* Removed all of the thread cancellation cruft that is no longer necessary
[citadel.git] / citadel / dynloader.c
1 /*******************************************************
2  *
3  * Citadel Dynamic Loading Module
4  * Written by Brian Costello
5  * btx@calyx.net
6  *
7  * $Id$
8  *
9  ******************************************************/
10
11
12 #include "sysdep.h"
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <dlfcn.h>
16 #include <sys/types.h>
17 #include <dirent.h>
18 #include <strings.h>
19 #include <syslog.h>
20 #include <limits.h>
21 #include <ctype.h>
22 #include "citadel.h"
23 #include "server.h"
24 #include "dynloader.h"
25 #include "sysdep_decls.h"
26 #include "msgbase.h"
27 #include "tools.h"
28
29 #ifndef HAVE_SNPRINTF
30 #include <stdarg.h>
31 #include "snprintf.h"
32 #endif
33
34 struct LogFunctionHook *LogHookTable = NULL;
35 struct CleanupFunctionHook *CleanupHookTable = NULL;
36 struct SessionFunctionHook *SessionHookTable = NULL;
37 struct UserFunctionHook *UserHookTable = NULL;
38 struct XmsgFunctionHook *XmsgHookTable = NULL;
39 struct MessageFunctionHook *MessageHookTable = NULL;
40
41 struct ProtoFunctionHook {
42         void (*handler) (char *cmdbuf);
43         char *cmd;
44         char *desc;
45         struct ProtoFunctionHook *next;
46 } *ProtoHookList = NULL;
47
48 void CtdlRegisterProtoHook(void (*handler) (char *), char *cmd, char *desc)
49 {
50         struct ProtoFunctionHook *p = mallok(sizeof *p);
51
52         if (p == NULL) {
53                 fprintf(stderr, "can't malloc new ProtoFunctionHook\n");
54                 exit(EXIT_FAILURE);
55         }
56         p->handler = handler;
57         p->cmd = cmd;
58         p->desc = desc;
59         p->next = ProtoHookList;
60         ProtoHookList = p;
61         lprintf(5, "Registered server command %s (%s)\n", cmd, desc);
62 }
63
64 int DLoader_Exec_Cmd(char *cmdbuf)
65 {
66         struct ProtoFunctionHook *p;
67
68         for (p = ProtoHookList; p; p = p->next) {
69                 if (!strncasecmp(cmdbuf, p->cmd, 4)) {
70                         p->handler(&cmdbuf[5]);
71                         return 1;
72                 }
73         }
74         return 0;
75 }
76
77 void DLoader_Init(char *pathname)
78 {
79         void *fcn_handle;
80         char dl_error[256];
81         DIR *dir;
82         int i;
83         struct dirent *dptr;
84         char *(*h_init_fcn) (void);
85         char *dl_info;
86
87         char pathbuf[PATH_MAX];
88
89         if ((dir = opendir(pathname)) == NULL) {
90                 perror("opendir");
91                 exit(1);
92         }
93         while ((dptr = readdir(dir)) != NULL) {
94                 if (strlen(dptr->d_name) < 4)
95                         continue;
96                 if (strcasecmp(&dptr->d_name[strlen(dptr->d_name)-3], ".so"))
97                         continue;
98
99                 snprintf(pathbuf, PATH_MAX, "%s/%s", pathname, dptr->d_name);
100 #ifdef RTLD_NOW
101                 if (!(fcn_handle = dlopen(pathbuf, RTLD_NOW)))
102 #else                           /* OpenBSD */
103                 if (!(fcn_handle = dlopen(pathbuf, DL_LAZY)))
104 #endif
105                 {
106                         safestrncpy(dl_error, dlerror(), sizeof dl_error);
107                         for (i=0; i<strlen(dl_error); ++i)
108                                 if (!isprint(dl_error[i]))
109                                         dl_error[i]='.';
110                         fprintf(stderr, "DLoader_Init dlopen failed: %s\n",
111                                 dl_error);
112                         continue;
113                 }
114                 h_init_fcn = (char * (*)(void))
115 #ifndef __OpenBSD__
116                     dlsym(fcn_handle, "Dynamic_Module_Init");
117 #else
118                     dlsym(fcn_handle, "_Dynamic_Module_Init");
119 #endif
120
121                 if (dlerror() != NULL) {
122                         fprintf(stderr, "DLoader_Init dlsym failed\n");
123                         continue;
124                 }
125                 dl_info = h_init_fcn();
126
127                 lprintf(3, "Loaded module: %s\n", dl_info);
128         }       /* While */
129 }
130
131
132
133 void CtdlRegisterLogHook(void (*fcn_ptr) (char *), int loglevel)
134 {
135
136         struct LogFunctionHook *newfcn;
137
138         newfcn = (struct LogFunctionHook *)
139             mallok(sizeof(struct LogFunctionHook));
140         newfcn->next = LogHookTable;
141         newfcn->h_function_pointer = fcn_ptr;
142         newfcn->loglevel = loglevel;
143         LogHookTable = newfcn;
144
145         lprintf(5, "Registered a new logging function\n");
146 }
147
148
149 void CtdlRegisterCleanupHook(void (*fcn_ptr) (void))
150 {
151
152         struct CleanupFunctionHook *newfcn;
153
154         newfcn = (struct CleanupFunctionHook *)
155             mallok(sizeof(struct CleanupFunctionHook));
156         newfcn->next = CleanupHookTable;
157         newfcn->h_function_pointer = fcn_ptr;
158         CleanupHookTable = newfcn;
159
160         lprintf(5, "Registered a new cleanup function\n");
161 }
162
163
164 void CtdlRegisterSessionHook(void (*fcn_ptr) (void), int EventType)
165 {
166
167         struct SessionFunctionHook *newfcn;
168
169         newfcn = (struct SessionFunctionHook *)
170             mallok(sizeof(struct SessionFunctionHook));
171         newfcn->next = SessionHookTable;
172         newfcn->h_function_pointer = fcn_ptr;
173         newfcn->eventtype = EventType;
174         SessionHookTable = newfcn;
175
176         lprintf(5, "Registered a new session function (type %d)\n",
177                 EventType);
178 }
179
180
181 void CtdlRegisterUserHook(void (*fcn_ptr) (char *, long), int EventType)
182 {
183
184         struct UserFunctionHook *newfcn;
185
186         newfcn = (struct UserFunctionHook *)
187             mallok(sizeof(struct UserFunctionHook));
188         newfcn->next = UserHookTable;
189         newfcn->h_function_pointer = fcn_ptr;
190         newfcn->eventtype = EventType;
191         UserHookTable = newfcn;
192
193         lprintf(5, "Registered a new user function (type %d)\n",
194                 EventType);
195 }
196
197
198 void CtdlRegisterMessageHook(int (*handler)(struct CtdlMessage *),
199                                 int EventType)
200 {
201
202         struct MessageFunctionHook *newfcn;
203
204         newfcn = (struct MessageFunctionHook *)
205             mallok(sizeof(struct MessageFunctionHook));
206         newfcn->next = MessageHookTable;
207         newfcn->h_function_pointer = handler;
208         newfcn->eventtype = EventType;
209         MessageHookTable = newfcn;
210
211         lprintf(5, "Registered a new message function (type %d)\n",
212                 EventType);
213 }
214
215
216 void CtdlRegisterXmsgHook(int (*fcn_ptr) (char *, char *, char *), int order)
217 {
218
219         struct XmsgFunctionHook *newfcn;
220
221         newfcn = (struct XmsgFunctionHook *)
222             mallok(sizeof(struct XmsgFunctionHook));
223         newfcn->next = XmsgHookTable;
224         newfcn->order = order;
225         newfcn->h_function_pointer = fcn_ptr;
226         XmsgHookTable = newfcn;
227         lprintf(5, "Registered a new x-msg function (priority %d)\n", order);
228 }
229
230
231 void PerformSessionHooks(int EventType)
232 {
233         struct SessionFunctionHook *fcn;
234
235         for (fcn = SessionHookTable; fcn != NULL; fcn = fcn->next) {
236                 if (fcn->eventtype == EventType) {
237                         (*fcn->h_function_pointer) ();
238                 }
239         }
240 }
241
242 void PerformLogHooks(int loglevel, char *logmsg)
243 {
244         struct LogFunctionHook *fcn;
245
246         for (fcn = LogHookTable; fcn != NULL; fcn = fcn->next) {
247                 if (fcn->loglevel >= loglevel) {
248                         (*fcn->h_function_pointer) (logmsg);
249                 }
250         }
251 }
252
253 void PerformUserHooks(char *username, long usernum, int EventType)
254 {
255         struct UserFunctionHook *fcn;
256
257         for (fcn = UserHookTable; fcn != NULL; fcn = fcn->next) {
258                 if (fcn->eventtype == EventType) {
259                         (*fcn->h_function_pointer) (username, usernum);
260                 }
261         }
262 }
263
264 int PerformMessageHooks(struct CtdlMessage *msg, int EventType)
265 {
266         struct MessageFunctionHook *fcn;
267         int total_retval = 0;
268
269         /* Other code may elect to protect this message from server-side
270          * handlers; if this is the case, don't do anything.
271          */
272         lprintf(9, "** Event type is %d, flags are %d\n",
273                 EventType, msg->cm_flags);
274         if (msg->cm_flags & CM_SKIP_HOOKS) {
275                 lprintf(9, "Skipping hooks\n");
276                 return(0);
277         }
278
279         /* Otherwise, run all the hooks appropriate to this event type.
280          */
281         for (fcn = MessageHookTable; fcn != NULL; fcn = fcn->next) {
282                 if (fcn->eventtype == EventType) {
283                         total_retval = total_retval +
284                                 (*fcn->h_function_pointer) (msg);
285                 }
286         }
287
288         /* Return the sum of the return codes from the hook functions.  If
289          * this is an EVT_BEFORESAVE event, a nonzero return code will cause
290          * the save operation to abort.
291          */
292         return total_retval;
293 }
294
295
296
297 int PerformXmsgHooks(char *sender, char *recp, char *msg)
298 {
299         struct XmsgFunctionHook *fcn;
300         int total_sent = 0;
301         int p;
302
303         for (p=0; p<MAX_XMSG_PRI; ++p) {
304                 for (fcn = XmsgHookTable; fcn != NULL; fcn = fcn->next) {
305                         if (fcn->order == p) {
306                                 total_sent +=
307                                         (*fcn->h_function_pointer)
308                                                 (sender, recp, msg);
309                         }
310                 }
311                 /* Break out of the loop if a higher-priority function
312                  * successfully delivered the message.  This prevents duplicate
313                  * deliveries to local users simultaneously signed onto
314                  * remote services.
315                  */
316                 if (total_sent) goto DONE;
317         }
318 DONE:   return total_sent;
319 }