Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / callback.c
1 /* $XConsortium: callback.c /main/5 1996/10/03 10:40:51 drk $ */
2 /*
3  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
4  *  (c) Copyright 1993, 1994 International Business Machines Corp.
5  *  (c) Copyright 1993, 1994 Novell, Inc.
6  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
7  */
8
9 #include <EUSCompat.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <sys/time.h>
16 #include <rpc/rpc.h>
17 #include "agent.h"
18 #include "cmcb.h"
19 #include "callback.h"
20 #include "appt4.h"
21 #include "utility.h"
22 #include "lutil.h"
23
24 extern int debug;
25 extern char *pgname;
26
27 /*
28  * forward declaration of static functions used within the file
29  */
30 static _DtCmsRegistrationInfo * _DtCmsDoCallback(
31         _DtCmsRegistrationInfo  *rlist,
32         uint                    type,
33         char                    *source,
34         int                     pid,
35         int                     version,
36         void                    *args);
37
38 /*****************************************************************************
39  * extern functions
40  *****************************************************************************/
41
42 extern _DtCmsRegistrationInfo *
43 _DtCmsMakeRegistrationInfo(
44         char    *client,
45         int     types,
46         u_long  prognum,
47         u_long  versnum,
48         u_long  procnum,
49         int     pid)
50 {
51         _DtCmsRegistrationInfo *rinfo;
52
53         if ((rinfo = (_DtCmsRegistrationInfo *)calloc(1,
54             sizeof(_DtCmsRegistrationInfo))) == NULL)
55                 return (NULL);
56
57         if ((rinfo->client = strdup(client)) == NULL) {
58                 free(rinfo);
59                 return (NULL);
60         }
61
62         rinfo->types    = types;
63         rinfo->prognum  = prognum;
64         rinfo->versnum  = versnum;
65         rinfo->procnum  = procnum;
66         rinfo->pid      = pid;
67         rinfo->next     = NULL;
68         return(rinfo);
69 }
70
71 extern void
72 _DtCmsFreeRegistrationInfo(_DtCmsRegistrationInfo *rinfo)
73 {
74         if (rinfo==NULL) return;
75         if (rinfo->client != NULL)
76                 free(rinfo->client);
77         free(rinfo);
78 }
79
80 /*
81  * this routine is for v4, so type is not checked
82  */
83 extern _DtCmsRegistrationInfo *
84 _DtCmsGetRegistration(
85         _DtCmsRegistrationInfo  **_rlist,
86         char                    *client,
87         u_long                  prognum,
88         u_long                  versnum,
89         u_long                  procnum,
90         int                     pid)
91 {
92         _DtCmsRegistrationInfo  *rlist = *_rlist, 
93                                 *prev, 
94                                 *ptr, 
95                                 *tmp_ptr;
96
97         for (prev = ptr = rlist; ptr != NULL; ) {
98                 if ((strcmp(ptr->client, client)==0) &&
99                     (ptr->prognum == prognum)  &&
100                     (ptr->versnum == versnum)  &&
101                     (ptr->procnum == procnum))
102                 {
103                         if (ptr->pid == pid) {
104                                 *_rlist = rlist;
105                                 return (ptr);
106                         } else {
107                                 /* ptr points to a stale record
108                                  * remove it from the linked list
109                                  * and update ptr and prev appropriately
110                                  */
111                                 if (ptr == prev) { /* top of list */
112                                         rlist = rlist->next;
113                                         _DtCmsFreeRegistrationInfo(ptr);
114                                         ptr = prev = rlist;
115                                 } else {
116                                         prev->next = ptr->next;
117                                         tmp_ptr = ptr;
118                                         ptr = ptr->next;
119                                         _DtCmsFreeRegistrationInfo(tmp_ptr);
120                                 }
121                         }
122                 } else {
123                         prev = ptr;
124                         ptr = ptr->next;
125                 }
126         }
127
128         *_rlist = rlist;
129         return (NULL);
130 }
131
132 /*
133  * Go through the registration list and ping each client.
134  * Deregister clients that do not respond.
135  */
136 extern _DtCmsRegistrationInfo *
137 _DtCmsCheckRegistrationList(_DtCmsRegistrationInfo *rlist)
138 {
139         int nclients=0, ndereg=0;
140         char *sourcehost=NULL;
141         _DtCmsRegistrationInfo *p_next;
142         _DtCmsRegistrationInfo *p_prev;
143         _DtCmsRegistrationInfo *head;
144         struct timeval timeout_tv;
145         CLIENT *cl;
146         boolean_t advance = B_TRUE;
147
148         timeout_tv.tv_sec = 10;
149         timeout_tv.tv_usec = 0;
150
151         /* loop through the registration list */
152
153         head = p_prev = rlist;
154         while (rlist != NULL) {
155
156                 p_next = rlist->next;
157
158                 if (debug) {
159                         fprintf(stderr,
160                           "%s: pinging %s on prog: %ld, vers: %ld, proc: %ld\n",
161                           pgname, rlist->client, rlist->prognum, rlist->versnum,
162                           rlist->procnum);
163                 }
164
165                 sourcehost = _DtCmsTarget2Location(rlist->client);
166                 cl = clnt_create(sourcehost, rlist->prognum, rlist->versnum,
167                         "udp");
168
169                 if (cl != NULL) {
170                         clnt_control(cl, CLSET_TIMEOUT, (char *)&timeout_tv);
171                         timeout_tv.tv_sec = 5;
172                         clnt_control(cl, CLSET_RETRY_TIMEOUT,
173                                 (char*)&timeout_tv);
174                 }
175                                 
176                 /* no client or client not responding */
177                 if (cl == NULL || clnt_call(cl, 0, (xdrproc_t)xdr_void, (char *)NULL,
178                         (xdrproc_t)xdr_void, (char *)NULL, timeout_tv) != RPC_SUCCESS)
179                 {
180                         if (debug) {
181                                 clnt_pcreateerror(sourcehost);
182                                 fprintf(stderr, "%s: %s deregistered, pid %d\n",
183                                         pgname, rlist->client, rlist->pid);
184                         }
185
186                         if (rlist == p_prev) { /* top of list */
187                                 head = p_next;
188                                 p_prev = p_next;
189                                 advance = B_FALSE;
190                         } else {
191                                 p_prev->next = p_next;
192                         }
193
194                         /* deregister client */
195                         _DtCmsFreeRegistrationInfo(rlist);
196
197                         rlist = p_prev;
198
199                         ndereg++;
200                 }
201
202                 if (cl)
203                         clnt_destroy(cl);
204
205                 free(sourcehost);
206                 nclients++;
207
208                 if (advance) {
209                         p_prev = rlist;
210                         rlist = p_next;
211                 } else
212                         advance = B_TRUE;
213         }
214
215         if (debug) {
216                 fprintf(stderr, "%s: number of clients before cleanup = %d\n",
217                         pgname, nclients);
218                 fprintf(stderr, "%s: number of clients deregistered = %d\n",
219                         pgname, ndereg);
220         }
221
222         return (head);
223 }
224
225 extern _DtCmsRegistrationInfo *
226 _DtCmsDoV1CbForV4Data(
227         _DtCmsRegistrationInfo  *rlist,
228         char                    *source,
229         int                     pid,
230         cms_key                 *key1,
231         cms_key                 *key2)
232 {
233         Appt_4  appt1, appt2;
234
235         if (rlist == NULL)
236                 return (rlist);
237
238         appt1.appt_id.tick = key1->time;
239         appt1.appt_id.key = key1->id;
240
241         if (key2) {
242                 appt2.appt_id.tick = key2->time;
243                 appt2.appt_id.key = key2->id;
244                 appt2.next = NULL;
245                 appt1.next = &appt2;
246         } else
247                 appt1.next = NULL;
248
249         return (_DtCmsDoV1Callback(rlist, source, pid, &appt1));
250 }
251
252 /*
253  * this routine takes care of callbacks to clients using v1 of the callback
254  * protocol.
255  */
256 extern _DtCmsRegistrationInfo *
257 _DtCmsDoV1Callback(
258         _DtCmsRegistrationInfo  *rlist,
259         char                    *source,
260         int                     pid,
261         Appt_4                  *a)
262 {
263         Uid_4 *k, *ids = NULL;
264         Table_Res_4 res;
265         int nclients=0, ncallbacks=0;
266         char *sourcehost=NULL;
267         _DtCmsRegistrationInfo *ptr;
268         _DtCmsRegistrationInfo *prev;
269         struct timeval timeout_tv;
270         CLIENT *cl;
271         boolean_t advance = B_TRUE;
272
273         if (rlist == NULL)
274                 return (rlist);
275
276         /* Callback with appointment ids only for security reason. */
277         while (a != NULL)
278         {
279                 if ((k = (Uid_4 *)malloc(sizeof(Uid_4))) == NULL) {
280                         _DtCm_free_keyentry4(ids);
281                         return (rlist);
282                 }
283
284                 k->appt_id = a->appt_id;
285                 k->next = ids;
286                 ids = k;
287                 a = a->next;
288         }
289
290         res.status = access_ok_4;
291         res.res.tag = ID_4;
292         res.res.Table_Res_List_4_u.i = ids;
293
294         rlist = _DtCmsDoCallback(rlist, 0, source, pid, AGENTVERS, (void *)&res);
295
296         if (ids != NULL)
297                 _DtCm_free_keyentry4(ids);
298
299         return (rlist);
300 }
301
302 extern void
303 _DtCmsListRegistration(_DtCmsRegistrationInfo *rlist, char *cal)
304 {
305         int n = 0;
306
307         fprintf(stderr, "registration list of calendar %s\n", cal);
308         while(rlist != NULL) {
309                 n++;
310                 fprintf(stderr, "\t%s (pid %d)\n", rlist->client, rlist->pid);
311                 rlist = rlist->next;
312         }
313         fprintf(stderr, "\tnumber of registered clients = %d\n", n);
314 }
315
316 extern _DtCmsRegistrationInfo *
317 _DtCmsRemoveRegistration(
318         _DtCmsRegistrationInfo *rlist,
319         _DtCmsRegistrationInfo *rinfo)
320 {
321         _DtCmsRegistrationInfo *ptr, *prev;
322
323
324         for (ptr = prev = rlist; ptr != NULL; prev = ptr, ptr = ptr->next) {
325                 if (ptr == rinfo) {
326                         if (ptr == rlist)
327                                 rlist = ptr->next;
328                         else
329                                 prev->next = ptr->next;
330
331                         _DtCmsFreeRegistrationInfo(ptr);
332                         break;
333                 }
334         }
335
336         return (rlist);
337 }
338
339 extern _DtCmsRegistrationInfo *
340 _DtCmsDoOpenCalCallback(
341         _DtCmsRegistrationInfo  *rlist,
342         char                    *cal,
343         char                    *user,
344         int                     pid)
345 {
346         cmcb_update_callback_args args;
347         char calendar[BUFSIZ];
348
349         if (rlist == NULL)
350                 return (rlist);
351
352         sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
353         args.calendar = calendar;
354         args.user = user;
355         args.data.reason = CSA_CB_CALENDAR_LOGON;
356
357         return (_DtCmsDoCallback(rlist, CSA_CB_CALENDAR_LOGON, user, pid,
358                 AGENTVERS_2, (void *)&args));
359 }
360
361
362 extern _DtCmsRegistrationInfo *
363 _DtCmsDoRemoveCalCallback(
364         _DtCmsRegistrationInfo  *rlist,
365         char                    *cal,
366         char                    *user,
367         int                     pid)
368 {
369         cmcb_update_callback_args args;
370         char calendar[BUFSIZ];
371
372         if (rlist == NULL)
373                 return (rlist);
374
375         sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
376         args.calendar = calendar;
377         args.user = user;
378         args.data.reason = CSA_CB_CALENDAR_DELETED;
379
380         return (_DtCmsDoCallback(rlist, CSA_CB_CALENDAR_DELETED, user, pid,
381                 AGENTVERS_2, (void *)&args));
382 }
383
384 extern _DtCmsRegistrationInfo *
385 _DtCmsDoUpdateCalAttrsCallback(
386         _DtCmsRegistrationInfo  *rlist,
387         char                    *cal,
388         char                    *user,
389         uint                    num_attrs,
390         cms_attribute           *attrs,
391         int                     pid)
392 {
393         cmcb_update_callback_args       args;
394         cmcb_cal_attr_data              cdata; 
395         _DtCmsRegistrationInfo          *res;
396         char buf[80];
397         char calendar[BUFSIZ];
398         int i;
399
400         if (rlist == NULL)
401                 return (rlist);
402
403         sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
404
405         /* set up update info */
406         if (num_attrs > 0 &&
407             (cdata.names = (char **)calloc(1, sizeof(char *)*num_attrs)))
408         {
409                 for (i = 0; i < num_attrs; i++)
410                         cdata.names[i] = attrs[i].name.name;
411         } else {
412                 cdata.num_names = 0;
413                 cdata.names = NULL;
414         }
415
416         args.calendar = calendar;
417         args.user = user;
418         args.data.reason = CSA_CB_CALENDAR_ATTRIBUTE_UPDATED;
419         args.data.data.cdata = &cdata;
420
421         res = _DtCmsDoCallback(rlist, CSA_CB_CALENDAR_ATTRIBUTE_UPDATED, user,
422                 pid, AGENTVERS_2, (void *)&args);
423
424         if (num_attrs > 0) free(cdata.names);
425
426         return (res);
427 }
428
429 extern _DtCmsRegistrationInfo *
430 _DtCmsDoInsertEntryCallback(
431         _DtCmsRegistrationInfo  *rlist,
432         char                    *cal,
433         char                    *source,
434         long                    id,
435         int                     pid)
436 {
437         cmcb_update_callback_args args;
438         cmcb_add_entry_data adata; 
439         char buf[80];
440         char calendar[BUFSIZ];
441
442         if (rlist == NULL)
443                 return (rlist);
444
445         sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
446
447         /* set up update info */
448         sprintf(buf, "%ld", id);
449         adata.id = buf;
450         args.calendar = calendar;
451         args.user = source;
452         args.data.reason = CSA_CB_ENTRY_ADDED;
453         args.data.data.adata = &adata;
454
455         return (_DtCmsDoCallback(rlist, CSA_CB_ENTRY_ADDED, source, pid,
456                 AGENTVERS_2, (void *)&args));
457 }
458
459 extern _DtCmsRegistrationInfo *
460 _DtCmsDoDeleteEntryCallback(
461         _DtCmsRegistrationInfo  *rlist,
462         char                    *cal,
463         char                    *source,
464         long                    id,
465         int                     scope,
466         time_t time,
467         int                     pid)
468 {
469         cmcb_update_callback_args args;
470         cmcb_delete_entry_data ddata; 
471         char buf[80];
472         char calendar[BUFSIZ];
473
474         if (rlist == NULL)
475                 return (rlist);
476
477         sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
478
479         /* set up update info */
480         sprintf(buf, "%ld", id);
481         ddata.id = buf;
482         ddata.scope = scope;
483         ddata.time = time;
484         args.calendar = calendar;
485         args.user = source;
486         args.data.reason = CSA_CB_ENTRY_DELETED;
487         args.data.data.ddata = &ddata;
488
489         return (_DtCmsDoCallback(rlist, CSA_CB_ENTRY_DELETED, source, pid,
490                 AGENTVERS_2, (void *)&args));
491 }
492
493 extern _DtCmsRegistrationInfo *
494 _DtCmsDoUpdateEntryCallback(
495         _DtCmsRegistrationInfo  *rlist,
496         char                    *cal,
497         char                    *source,
498         long                    newid,
499         long                    oldid,
500         int                     scope,
501         long                    time,
502         int                     pid)
503 {
504         cmcb_update_callback_args args;
505         cmcb_update_entry_data udata; 
506         char nbuf[80], obuf[80];
507         char calendar[BUFSIZ];
508
509         if (rlist == NULL)
510                 return (rlist);
511
512         sprintf(calendar, "%s@%s", cal, _DtCmGetLocalHost());
513
514         /* set up update info */
515         sprintf(obuf, "%ld", oldid);
516         udata.oldid = obuf;
517         if (newid > 0)
518                 sprintf(nbuf, "%ld", newid);
519         else
520                 nbuf[0] = NULL;
521         udata.newid = nbuf;
522         udata.scope = scope;
523         udata.time = time;
524         args.calendar = calendar;
525         args.user = source;
526         args.data.reason = CSA_CB_ENTRY_UPDATED;
527         args.data.data.udata = &udata;
528
529         return (_DtCmsDoCallback(rlist, CSA_CB_ENTRY_UPDATED, source, pid,
530                 AGENTVERS_2, (void *)&args));
531 }
532
533 /*
534  * this routine takes care of callbacks to clients using either
535  * v1 or v2 of the callback protocol.
536  */
537 static _DtCmsRegistrationInfo *
538 _DtCmsDoCallback(
539         _DtCmsRegistrationInfo  *rlist,
540         uint                    type,
541         char                    *source,
542         int                     pid,
543         int                     version,
544         void                    *args)
545 {
546         int nclients=0, ncallbacks=0;
547         char *sourcehost=NULL;
548         _DtCmsRegistrationInfo *ptr;
549         _DtCmsRegistrationInfo *prev;
550         struct timeval timeout_tv;
551         CLIENT *cl;
552         boolean_t advance = B_TRUE;
553         
554         /*
555          * loop through the registration list looking for parties
556          * interested in this transaction.
557          */
558
559         for (ptr = prev = rlist; ptr != NULL; ) {
560
561                 /* The caller will get the results of the rpc call.
562                  * If he's registered on the callback list, don't call him -
563                  * UNLESS the process id of his client differs from the
564                  * original ticket. However, if the pid is a VOIDPID (-1),
565                  * a version 2 client has registered and there's no way
566                  * of telling which instance of the client it is.  So,
567                  * to be safe (avoid deadlock) we won't callback version
568                  * 2 clients registered on version 3 daemons if their
569                  * registration name entry matches the caller's. [Nanno]
570                  */
571
572                 if (version != ptr->versnum ||
573                     (type && !(type & ptr->types)) ||
574                     ((strcmp(source, ptr->client) == 0) &&
575                     ((pid == ptr->pid) || (pid == -1 ) || (ptr->pid == -1)))) {
576                         prev = ptr;
577                         ptr = ptr->next;
578                         nclients++;
579                         continue;
580                 }
581
582                 sourcehost = _DtCmsTarget2Location(ptr->client);
583                 if (debug) {
584                         fprintf(stderr,
585                           "%s: calling back %s on prog: %ld, vers: %ld, proc: %ld\n",
586                           pgname, ptr->client, ptr->prognum, ptr->versnum,
587                           ptr->procnum);
588                 }
589                 cl = clnt_create(sourcehost, ptr->prognum, ptr->versnum, "udp");
590
591                 /* deregister client if fails to create handle */
592                 if (cl == NULL) {
593                         if (debug) {
594                                 clnt_pcreateerror(sourcehost);
595                         }
596
597                         if (ptr == rlist) { /* top of list */
598                                 rlist = ptr->next;
599                                 prev = rlist;
600                                 advance = B_FALSE;
601                         } else {
602                                 prev->next = ptr->next;
603                         }
604
605                         /* deregister client */
606                         _DtCmsFreeRegistrationInfo(ptr);
607
608                         ptr = prev;
609
610                 } else {
611                         /* Set timeout to zero so that the call
612                          * returns right away.
613                          */
614                         timeout_tv.tv_sec = 0;
615                         timeout_tv.tv_usec = 0;
616
617 #ifndef SunOS
618                         /* for non-sun systems, clnt_call won't
619                          * return right away unless timeout is set
620                          * to zero using clnt_control(), (rpc bug?)
621                          */
622                         clnt_control(cl, CLSET_TIMEOUT,
623                                         (char *)&timeout_tv);
624 #endif
625                         if (version == AGENTVERS) {
626                                 (void)clnt_call(cl, ptr->procnum,
627                                         (xdrproc_t)_DtCm_xdr_Table_Res_4, (char *)args,
628                                         (xdrproc_t)xdr_void, (char *)0, timeout_tv);
629                         } else if (version == AGENTVERS_2) {
630                                 (void)clnt_call(cl, ptr->procnum,
631                                         (xdrproc_t)xdr_cmcb_update_callback_args,
632                                         (char *)args, (xdrproc_t)xdr_void, (char *)0,
633                                         timeout_tv);
634                         }
635                         ncallbacks++;
636                         nclients++;
637                 }
638
639                 if (cl)
640                         clnt_destroy(cl);
641
642                 free(sourcehost);
643
644                 if (advance) {
645                         prev = ptr;
646                         ptr = ptr->next;
647                 } else
648                         advance = B_TRUE;
649         }
650
651         if (debug) {
652                 fprintf(stderr, "%s: number of registered clients = %d\n",
653                         pgname, nclients);
654                 fprintf(stderr, "%s: number of clients called back= %d\n",
655                         pgname, ncallbacks);
656         }
657
658         return (rlist);
659 }
660