]> code.citadel.org Git - citadel.git/blob - citadel/dynloader.c
* file_ops.c: fixed bug in cmd_read() which could cause server to report
[citadel.git] / citadel / dynloader.c
1 /*
2  * $Id$
3  *
4  * Citadel Dynamic Loading Module
5  * Written by Brian Costello <btx@calyx.net>
6  *
7  */
8
9 #ifdef DLL_EXPORT
10 #define IN_LIBCIT
11 #endif
12
13 #include "sysdep.h"
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #ifdef HAVE_DLFCN_H
18 #include <dlfcn.h>
19 #endif
20 #ifdef HAVE_DL_H
21 #include <dl.h>
22 #include "hpsux.h"
23 #endif
24 #include <sys/types.h>
25 #include <dirent.h>
26 #include <string.h>
27 #include <syslog.h>
28 #include <limits.h>
29 #include <ctype.h>
30 #include "citadel.h"
31 #include "server.h"
32 #include "dynloader.h"
33 #include "sysdep_decls.h"
34 #include "msgbase.h"
35 #include "tools.h"
36 #include "config.h"
37
38 #ifndef HAVE_SNPRINTF
39 #include <stdarg.h>
40 #include "snprintf.h"
41 #endif
42
43 struct LogFunctionHook *LogHookTable = NULL;
44 struct CleanupFunctionHook *CleanupHookTable = NULL;
45 struct SessionFunctionHook *SessionHookTable = NULL;
46 struct UserFunctionHook *UserHookTable = NULL;
47 struct XmsgFunctionHook *XmsgHookTable = NULL;
48 struct MessageFunctionHook *MessageHookTable = NULL;
49 struct NetprocFunctionHook *NetprocHookTable = NULL;
50 struct ServiceFunctionHook *ServiceHookTable = NULL;
51
52 struct ProtoFunctionHook {
53         void (*handler) (char *cmdbuf);
54         char *cmd;
55         char *desc;
56         struct ProtoFunctionHook *next;
57 } *ProtoHookList = NULL;
58
59 void CtdlRegisterProtoHook(void (*handler) (char *), char *cmd, char *desc)
60 {
61         struct ProtoFunctionHook *p;
62
63         p = (struct ProtoFunctionHook *)
64                 mallok(sizeof(struct ProtoFunctionHook));
65
66         if (p == NULL) {
67                 fprintf(stderr, "can't malloc new ProtoFunctionHook\n");
68                 exit(EXIT_FAILURE);
69         }
70         p->handler = handler;
71         p->cmd = cmd;
72         p->desc = desc;
73         p->next = ProtoHookList;
74         ProtoHookList = p;
75         lprintf(5, "Registered server command %s (%s)\n", cmd, desc);
76 }
77
78
79 void CtdlUnregisterProtoHook(void (*handler) (char *), char *cmd)
80 {
81         struct ProtoFunctionHook *cur, *p;
82
83         for (cur = ProtoHookList; cur != NULL; cur = cur->next) {
84                 /* This will also remove duplicates if any */
85                 while (cur != NULL &&
86                                 handler == cur->handler &&
87                                 !strcmp(cmd, cur->cmd)) {
88                         lprintf(5, "Unregistered server command %s (%s)\n",
89                                         cmd, cur->desc);
90                         p = cur->next;
91                         if (cur == ProtoHookList) {
92                                 ProtoHookList = p;
93                         }
94                         phree(cur);
95                         cur = p;
96                 }
97         }
98 }
99
100
101 int DLoader_Exec_Cmd(char *cmdbuf)
102 {
103         struct ProtoFunctionHook *p;
104
105         for (p = ProtoHookList; p; p = p->next) {
106                 if (!strncasecmp(cmdbuf, p->cmd, 4)) {
107                         p->handler(&cmdbuf[5]);
108                         return 1;
109                 }
110         }
111         return 0;
112 }
113
114 void DLoader_Init(char *pathname)
115 {
116         void *fcn_handle;
117         char dl_error[SIZ];
118         DIR *dir;
119         int i;
120         struct dirent *dptr;
121         char *(*h_init_fcn) (void);
122         char *dl_info;
123
124         char pathbuf[PATH_MAX];
125
126         if ((dir = opendir(pathname)) == NULL) {
127                 perror("opendir");
128                 exit(1);
129         }
130         while ((dptr = readdir(dir)) != NULL) {
131                 if (strlen(dptr->d_name) < 4)
132                         continue;
133 #ifndef __CYGWIN__
134                 if (strcasecmp(&dptr->d_name[strlen(dptr->d_name)-3], ".so"))
135 #else
136                 if (strcasecmp(&dptr->d_name[strlen(dptr->d_name)-4], ".dll"))
137 #endif
138                         continue;
139
140                 snprintf(pathbuf, PATH_MAX, "%s/%s", pathname, dptr->d_name);
141                 lprintf(7, "Initializing %s...\n", pathbuf);
142
143 #ifdef RTLD_LAZY
144                 if (!(fcn_handle = dlopen(pathbuf, RTLD_LAZY)))
145 #else                           /* OpenBSD */
146                 if (!(fcn_handle = dlopen(pathbuf, DL_LAZY)))
147 #endif
148                 {
149                         safestrncpy(dl_error, dlerror(), sizeof dl_error);
150                         for (i=0; i<strlen(dl_error); ++i)
151                                 if (!isprint(dl_error[i]))
152                                         dl_error[i]='.';
153                         fprintf(stderr, "DLoader_Init dlopen failed: %s\n",
154                                 dl_error);
155                         continue;
156                 }
157                 h_init_fcn = (char * (*)(void))
158 #ifndef __OpenBSD__
159                     dlsym(fcn_handle, "Dynamic_Module_Init");
160 #else
161                     dlsym(fcn_handle, "_Dynamic_Module_Init");
162 #endif
163
164                 if (dlerror() != NULL) {
165                         fprintf(stderr, "DLoader_Init dlsym failed\n");
166                         continue;
167                 }
168                 dl_info = h_init_fcn();
169
170                 lprintf(3, "Loaded module: %s\n", dl_info);
171         }       /* While */
172 }
173
174
175
176 void CtdlRegisterLogHook(void (*fcn_ptr) (char *), int loglevel)
177 {
178
179         struct LogFunctionHook *newfcn;
180
181         newfcn = (struct LogFunctionHook *)
182             mallok(sizeof(struct LogFunctionHook));
183         newfcn->next = LogHookTable;
184         newfcn->h_function_pointer = fcn_ptr;
185         newfcn->loglevel = loglevel;
186         LogHookTable = newfcn;
187
188         lprintf(5, "Registered a new logging function\n");
189 }
190
191
192 void CtdlUnregisterLogHook(void (*fcn_ptr) (char *), int loglevel)
193 {
194         struct LogFunctionHook *cur, *p;
195
196         for (cur = LogHookTable; cur != NULL; cur = cur->next) {
197                 /* This will also remove duplicates if any */
198                 while (cur != NULL &&
199                                 fcn_ptr == cur->h_function_pointer &&
200                                 loglevel == cur->loglevel) {
201                         lprintf(5, "Unregistered logging function\n");
202                         p = cur->next;
203                         if (cur == LogHookTable) {
204                                 LogHookTable = p;
205                         }
206                         phree(cur);
207                         cur = p;
208                 }
209         }
210 }
211
212
213 void CtdlRegisterCleanupHook(void (*fcn_ptr) (void))
214 {
215
216         struct CleanupFunctionHook *newfcn;
217
218         newfcn = (struct CleanupFunctionHook *)
219             mallok(sizeof(struct CleanupFunctionHook));
220         newfcn->next = CleanupHookTable;
221         newfcn->h_function_pointer = fcn_ptr;
222         CleanupHookTable = newfcn;
223
224         lprintf(5, "Registered a new cleanup function\n");
225 }
226
227
228 void CtdlUnregisterCleanupHook(void (*fcn_ptr) (void))
229 {
230         struct CleanupFunctionHook *cur, *p;
231
232         for (cur = CleanupHookTable; cur != NULL; cur = cur->next) {
233                 /* This will also remove duplicates if any */
234                 while (cur != NULL &&
235                                 fcn_ptr == cur->h_function_pointer) {
236                         lprintf(5, "Unregistered cleanup function\n");
237                         p = cur->next;
238                         if (cur == CleanupHookTable) {
239                                 CleanupHookTable = p;
240                         }
241                         phree(cur);
242                         cur = p;
243                 }
244         }
245 }
246
247
248 void CtdlRegisterSessionHook(void (*fcn_ptr) (void), int EventType)
249 {
250
251         struct SessionFunctionHook *newfcn;
252
253         newfcn = (struct SessionFunctionHook *)
254             mallok(sizeof(struct SessionFunctionHook));
255         newfcn->next = SessionHookTable;
256         newfcn->h_function_pointer = fcn_ptr;
257         newfcn->eventtype = EventType;
258         SessionHookTable = newfcn;
259
260         lprintf(5, "Registered a new session function (type %d)\n",
261                 EventType);
262 }
263
264
265 void CtdlUnregisterSessionHook(void (*fcn_ptr) (void), int EventType)
266 {
267         struct SessionFunctionHook *cur, *p;
268
269         for (cur = SessionHookTable; cur != NULL; cur = cur->next) {
270                 /* This will also remove duplicates if any */
271                 while (cur != NULL &&
272                                 fcn_ptr == cur->h_function_pointer &&
273                                 EventType == cur->eventtype) {
274                         lprintf(5, "Unregistered session function (type %d)\n",
275                                         EventType);
276                         p = cur->next;
277                         if (cur == SessionHookTable) {
278                                 SessionHookTable = p;
279                         }
280                         phree(cur);
281                         cur = p;
282                 }
283         }
284 }
285
286
287 void CtdlRegisterUserHook(void (*fcn_ptr) (char *, long), int EventType)
288 {
289
290         struct UserFunctionHook *newfcn;
291
292         newfcn = (struct UserFunctionHook *)
293             mallok(sizeof(struct UserFunctionHook));
294         newfcn->next = UserHookTable;
295         newfcn->h_function_pointer = fcn_ptr;
296         newfcn->eventtype = EventType;
297         UserHookTable = newfcn;
298
299         lprintf(5, "Registered a new user function (type %d)\n",
300                 EventType);
301 }
302
303
304 void CtdlUnregisterUserHook(void (*fcn_ptr) (char *, long), int EventType)
305 {
306         struct UserFunctionHook *cur, *p;
307
308         for (cur = UserHookTable; cur != NULL; cur = cur->next) {
309                 /* This will also remove duplicates if any */
310                 while (cur != NULL &&
311                                 fcn_ptr == cur->h_function_pointer &&
312                                 EventType == cur->eventtype) {
313                         lprintf(5, "Unregistered user function (type %d)\n",
314                                         EventType);
315                         p = cur->next;
316                         if (cur == UserHookTable) {
317                                 UserHookTable = p;
318                         }
319                         phree(cur);
320                         cur = p;
321                 }
322         }
323 }
324
325
326 void CtdlRegisterMessageHook(int (*handler)(struct CtdlMessage *),
327                                 int EventType)
328 {
329
330         struct MessageFunctionHook *newfcn;
331
332         newfcn = (struct MessageFunctionHook *)
333             mallok(sizeof(struct MessageFunctionHook));
334         newfcn->next = MessageHookTable;
335         newfcn->h_function_pointer = handler;
336         newfcn->eventtype = EventType;
337         MessageHookTable = newfcn;
338
339         lprintf(5, "Registered a new message function (type %d)\n",
340                 EventType);
341 }
342
343
344 void CtdlUnregisterMessageHook(int (*handler)(struct CtdlMessage *),
345                 int EventType)
346 {
347         struct MessageFunctionHook *cur, *p;
348
349         for (cur = MessageHookTable; cur != NULL; cur = cur->next) {
350                 /* This will also remove duplicates if any */
351                 while (cur != NULL &&
352                                 handler == cur->h_function_pointer &&
353                                 EventType == cur->eventtype) {
354                         lprintf(5, "Unregistered message function (type %d)\n",
355                                         EventType);
356                         p = cur->next;
357                         if (cur == MessageHookTable) {
358                                 MessageHookTable = p;
359                         }
360                         phree(cur);
361                         cur = p;
362                 }
363         }
364 }
365
366
367 void CtdlRegisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
368 {
369         struct NetprocFunctionHook *newfcn;
370
371         newfcn = (struct NetprocFunctionHook *)
372             mallok(sizeof(struct NetprocFunctionHook));
373         newfcn->next = NetprocHookTable;
374         newfcn->h_function_pointer = handler;
375         NetprocHookTable = newfcn;
376
377         lprintf(5, "Registered a new netproc function\n");
378 }
379
380
381 void CtdlUnregisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
382 {
383         struct NetprocFunctionHook *cur, *p;
384
385         for (cur = NetprocHookTable; cur != NULL; cur = cur->next) {
386                 /* This will also remove duplicates if any */
387                 while (cur != NULL &&
388                                 handler == cur->h_function_pointer ) {
389                         lprintf(5, "Unregistered netproc function\n");
390                         p = cur->next;
391                         if (cur == NetprocHookTable) {
392                                 NetprocHookTable = p;
393                         }
394                         phree(cur);
395                         cur = p;
396                 }
397         }
398 }
399
400
401 void CtdlRegisterXmsgHook(int (*fcn_ptr) (char *, char *, char *), int order)
402 {
403
404         struct XmsgFunctionHook *newfcn;
405
406         newfcn = (struct XmsgFunctionHook *)
407             mallok(sizeof(struct XmsgFunctionHook));
408         newfcn->next = XmsgHookTable;
409         newfcn->order = order;
410         newfcn->h_function_pointer = fcn_ptr;
411         XmsgHookTable = newfcn;
412         lprintf(5, "Registered a new x-msg function (priority %d)\n", order);
413 }
414
415
416 void CtdlUnregisterXmsgHook(int (*fcn_ptr) (char *, char *, char *), int order)
417 {
418         struct XmsgFunctionHook *cur, *p;
419
420         for (cur = XmsgHookTable; cur != NULL; cur = cur->next) {
421                 /* This will also remove duplicates if any */
422                 while (cur != NULL &&
423                                 fcn_ptr == cur->h_function_pointer &&
424                                 order == cur->order) {
425                         lprintf(5, "Unregistered x-msg function "
426                                         "(priority %d)\n", order);
427                         p = cur->next;
428                         if (cur == XmsgHookTable) {
429                                 XmsgHookTable = p;
430                         }
431                         phree(cur);
432                         cur = p;
433                 }
434         }
435 }
436
437
438 void CtdlRegisterServiceHook(int tcp_port,
439                         char *sockpath,
440                         void (*h_greeting_function) (void),
441                         void (*h_command_function) (void) )
442 {
443         struct ServiceFunctionHook *newfcn;
444         char message[SIZ];
445
446         newfcn = (struct ServiceFunctionHook *)
447             mallok(sizeof(struct ServiceFunctionHook));
448         newfcn->next = ServiceHookTable;
449         newfcn->tcp_port = tcp_port;
450         newfcn->sockpath = sockpath;
451         newfcn->h_greeting_function = h_greeting_function;
452         newfcn->h_command_function = h_command_function;
453
454         if (sockpath != NULL) {
455                 newfcn->msock = ig_uds_server(sockpath, config.c_maxsessions);
456                 sprintf(message, "Unix domain socket '%s': ", sockpath);
457         }
458         else if (tcp_port <= 0) {       /* port -1 to disable */
459                 lprintf(7, "Service has been manually disabled, skipping\n");
460                 phree(newfcn);
461                 return;
462         }
463         else {
464                 newfcn->msock = ig_tcp_server(tcp_port, config.c_maxsessions);
465                 sprintf(message, "TCP port %d: ", tcp_port);
466         }
467
468         if (newfcn->msock > 0) {
469                 ServiceHookTable = newfcn;
470                 strcat(message, "registered.");
471                 lprintf(5, "%s\n", message);
472         }
473         else {
474                 strcat(message, "FAILED.");
475                 lprintf(2, "%s\n", message);
476                 phree(newfcn);
477         }
478 }
479
480
481 void CtdlUnregisterServiceHook(int tcp_port, char *sockpath,
482                         void (*h_greeting_function) (void),
483                         void (*h_command_function) (void) )
484 {
485         struct ServiceFunctionHook *cur, *p;
486
487         for (cur = ServiceHookTable; cur != NULL; cur = cur->next) {
488                 /* This will also remove duplicates if any */
489                 while (cur != NULL &&
490                                 !(sockpath && cur->sockpath &&
491                                         strcmp(sockpath, cur->sockpath)) &&
492                                 h_greeting_function == cur->h_greeting_function &&
493                                 h_command_function == cur->h_command_function &&
494                                 tcp_port == cur->tcp_port) {
495                         close(cur->msock);
496                         if (sockpath) {
497                                 lprintf(5, "Closed UNIX domain socket %s\n",
498                                                 sockpath);
499                         } else if (tcp_port) {
500                                 lprintf(5, "Closed TCP port %d\n", tcp_port);
501                         } else {
502                                 lprintf(5, "Unregistered unknown service\n");
503                         }
504                         p = cur->next;
505                         if (cur == ServiceHookTable) {
506                                 ServiceHookTable = p;
507                         }
508                         phree(cur);
509                         cur = p;
510                 }
511         }
512 }
513
514
515 void PerformSessionHooks(int EventType)
516 {
517         struct SessionFunctionHook *fcn;
518
519         for (fcn = SessionHookTable; fcn != NULL; fcn = fcn->next) {
520                 if (fcn->eventtype == EventType) {
521                         (*fcn->h_function_pointer) ();
522                 }
523         }
524 }
525
526 void PerformLogHooks(int loglevel, char *logmsg)
527 {
528         struct LogFunctionHook *fcn;
529
530         for (fcn = LogHookTable; fcn != NULL; fcn = fcn->next) {
531                 if (fcn->loglevel >= loglevel) {
532                         (*fcn->h_function_pointer) (logmsg);
533                 }
534         }
535 }
536
537 void PerformUserHooks(char *username, long usernum, int EventType)
538 {
539         struct UserFunctionHook *fcn;
540
541         for (fcn = UserHookTable; fcn != NULL; fcn = fcn->next) {
542                 if (fcn->eventtype == EventType) {
543                         (*fcn->h_function_pointer) (username, usernum);
544                 }
545         }
546 }
547
548 int PerformMessageHooks(struct CtdlMessage *msg, int EventType)
549 {
550         struct MessageFunctionHook *fcn;
551         int total_retval = 0;
552
553         /* Other code may elect to protect this message from server-side
554          * handlers; if this is the case, don't do anything.
555         lprintf(9, "** Event type is %d, flags are %d\n",
556                 EventType, msg->cm_flags);
557          */
558         if (msg->cm_flags & CM_SKIP_HOOKS) {
559                 lprintf(9, "Skipping hooks\n");
560                 return(0);
561         }
562
563         /* Otherwise, run all the hooks appropriate to this event type.
564          */
565         for (fcn = MessageHookTable; fcn != NULL; fcn = fcn->next) {
566                 if (fcn->eventtype == EventType) {
567                         total_retval = total_retval +
568                                 (*fcn->h_function_pointer) (msg);
569                 }
570         }
571
572         /* Return the sum of the return codes from the hook functions.  If
573          * this is an EVT_BEFORESAVE event, a nonzero return code will cause
574          * the save operation to abort.
575          */
576         return total_retval;
577 }
578
579
580
581 int PerformNetprocHooks(struct CtdlMessage *msg, char *target_room)
582 {
583         struct NetprocFunctionHook *fcn;
584         int total_retval = 0;
585
586         for (fcn = NetprocHookTable; fcn != NULL; fcn = fcn->next) {
587                 total_retval = total_retval +
588                         (*fcn->h_function_pointer) (msg, target_room);
589         }
590
591         /* Return the sum of the return codes from the hook functions.
592          * A nonzero return code will cause the message to *not* be imported.
593          */
594         return total_retval;
595 }
596
597
598
599 int PerformXmsgHooks(char *sender, char *recp, char *msg)
600 {
601         struct XmsgFunctionHook *fcn;
602         int total_sent = 0;
603         int p;
604
605         for (p=0; p<MAX_XMSG_PRI; ++p) {
606                 for (fcn = XmsgHookTable; fcn != NULL; fcn = fcn->next) {
607                         if (fcn->order == p) {
608                                 total_sent +=
609                                         (*fcn->h_function_pointer)
610                                                 (sender, recp, msg);
611                         }
612                 }
613                 /* Break out of the loop if a higher-priority function
614                  * successfully delivered the message.  This prevents duplicate
615                  * deliveries to local users simultaneously signed onto
616                  * remote services.
617                  */
618                 if (total_sent) break;
619         }
620         return total_sent;
621 }