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