- port to Cygwin (DLL support, etc.)
[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 #ifdef HAVE_DLFCN_H
17 #include <dlfcn.h>
18 #endif
19 #ifdef HAVE_DL_H
20 #include <dl.h>
21 #include "hpsux.h"
22 #endif
23 #include <sys/types.h>
24 #include <dirent.h>
25 #include <string.h>
26 #include <syslog.h>
27 #include <limits.h>
28 #include <ctype.h>
29 #include "citadel.h"
30 #include "server.h"
31 #include "dynloader.h"
32 #include "sysdep_decls.h"
33 #include "msgbase.h"
34 #include "tools.h"
35 #include "config.h"
36
37 #ifndef HAVE_SNPRINTF
38 #include <stdarg.h>
39 #include "snprintf.h"
40 #endif
41
42 struct LogFunctionHook *LogHookTable = NULL;
43 struct CleanupFunctionHook *CleanupHookTable = NULL;
44 struct SessionFunctionHook *SessionHookTable = NULL;
45 struct UserFunctionHook *UserHookTable = NULL;
46 struct XmsgFunctionHook *XmsgHookTable = NULL;
47 struct MessageFunctionHook *MessageHookTable = NULL;
48 struct ServiceFunctionHook *ServiceHookTable = NULL;
49
50 struct ProtoFunctionHook {
51         void (*handler) (char *cmdbuf);
52         char *cmd;
53         char *desc;
54         struct ProtoFunctionHook *next;
55 } *ProtoHookList = NULL;
56
57 void CtdlRegisterProtoHook(void (*handler) (char *), char *cmd, char *desc)
58 {
59         struct ProtoFunctionHook *p = mallok(sizeof *p);
60
61         if (p == NULL) {
62                 fprintf(stderr, "can't malloc new ProtoFunctionHook\n");
63                 exit(EXIT_FAILURE);
64         }
65         p->handler = handler;
66         p->cmd = cmd;
67         p->desc = desc;
68         p->next = ProtoHookList;
69         ProtoHookList = p;
70         lprintf(5, "Registered server command %s (%s)\n", cmd, desc);
71 }
72
73 int DLoader_Exec_Cmd(char *cmdbuf)
74 {
75         struct ProtoFunctionHook *p;
76
77         for (p = ProtoHookList; p; p = p->next) {
78                 if (!strncasecmp(cmdbuf, p->cmd, 4)) {
79                         p->handler(&cmdbuf[5]);
80                         return 1;
81                 }
82         }
83         return 0;
84 }
85
86 void DLoader_Init(char *pathname)
87 {
88         void *fcn_handle;
89         char dl_error[SIZ];
90         DIR *dir;
91         int i;
92         struct dirent *dptr;
93         char *(*h_init_fcn) (void);
94         char *dl_info;
95
96         char pathbuf[PATH_MAX];
97
98         if ((dir = opendir(pathname)) == NULL) {
99                 perror("opendir");
100                 exit(1);
101         }
102         while ((dptr = readdir(dir)) != NULL) {
103                 if (strlen(dptr->d_name) < 4)
104                         continue;
105 #ifndef __CYGWIN__
106                 if (strcasecmp(&dptr->d_name[strlen(dptr->d_name)-3], ".so"))
107 #else
108                 if (strcasecmp(&dptr->d_name[strlen(dptr->d_name)-4], ".dll"))
109 #endif
110                         continue;
111
112                 snprintf(pathbuf, PATH_MAX, "%s/%s", pathname, dptr->d_name);
113                 lprintf(7, "Initializing %s...\n", pathbuf);
114
115 #ifdef RTLD_LAZY
116                 if (!(fcn_handle = dlopen(pathbuf, RTLD_LAZY)))
117 #else                           /* OpenBSD */
118                 if (!(fcn_handle = dlopen(pathbuf, DL_LAZY)))
119 #endif
120                 {
121                         safestrncpy(dl_error, dlerror(), sizeof dl_error);
122                         for (i=0; i<strlen(dl_error); ++i)
123                                 if (!isprint(dl_error[i]))
124                                         dl_error[i]='.';
125                         fprintf(stderr, "DLoader_Init dlopen failed: %s\n",
126                                 dl_error);
127                         continue;
128                 }
129                 h_init_fcn = (char * (*)(void))
130 #ifndef __OpenBSD__
131                     dlsym(fcn_handle, "Dynamic_Module_Init");
132 #else
133                     dlsym(fcn_handle, "_Dynamic_Module_Init");
134 #endif
135
136                 if (dlerror() != NULL) {
137                         fprintf(stderr, "DLoader_Init dlsym failed\n");
138                         continue;
139                 }
140                 dl_info = h_init_fcn();
141
142                 lprintf(3, "Loaded module: %s\n", dl_info);
143         }       /* While */
144 }
145
146
147
148 void CtdlRegisterLogHook(void (*fcn_ptr) (char *), int loglevel)
149 {
150
151         struct LogFunctionHook *newfcn;
152
153         newfcn = (struct LogFunctionHook *)
154             mallok(sizeof(struct LogFunctionHook));
155         newfcn->next = LogHookTable;
156         newfcn->h_function_pointer = fcn_ptr;
157         newfcn->loglevel = loglevel;
158         LogHookTable = newfcn;
159
160         lprintf(5, "Registered a new logging function\n");
161 }
162
163
164 void CtdlRegisterCleanupHook(void (*fcn_ptr) (void))
165 {
166
167         struct CleanupFunctionHook *newfcn;
168
169         newfcn = (struct CleanupFunctionHook *)
170             mallok(sizeof(struct CleanupFunctionHook));
171         newfcn->next = CleanupHookTable;
172         newfcn->h_function_pointer = fcn_ptr;
173         CleanupHookTable = newfcn;
174
175         lprintf(5, "Registered a new cleanup function\n");
176 }
177
178
179 void CtdlRegisterSessionHook(void (*fcn_ptr) (void), int EventType)
180 {
181
182         struct SessionFunctionHook *newfcn;
183
184         newfcn = (struct SessionFunctionHook *)
185             mallok(sizeof(struct SessionFunctionHook));
186         newfcn->next = SessionHookTable;
187         newfcn->h_function_pointer = fcn_ptr;
188         newfcn->eventtype = EventType;
189         SessionHookTable = newfcn;
190
191         lprintf(5, "Registered a new session function (type %d)\n",
192                 EventType);
193 }
194
195
196 void CtdlRegisterUserHook(void (*fcn_ptr) (char *, long), int EventType)
197 {
198
199         struct UserFunctionHook *newfcn;
200
201         newfcn = (struct UserFunctionHook *)
202             mallok(sizeof(struct UserFunctionHook));
203         newfcn->next = UserHookTable;
204         newfcn->h_function_pointer = fcn_ptr;
205         newfcn->eventtype = EventType;
206         UserHookTable = newfcn;
207
208         lprintf(5, "Registered a new user function (type %d)\n",
209                 EventType);
210 }
211
212
213 void CtdlRegisterMessageHook(int (*handler)(struct CtdlMessage *),
214                                 int EventType)
215 {
216
217         struct MessageFunctionHook *newfcn;
218
219         newfcn = (struct MessageFunctionHook *)
220             mallok(sizeof(struct MessageFunctionHook));
221         newfcn->next = MessageHookTable;
222         newfcn->h_function_pointer = handler;
223         newfcn->eventtype = EventType;
224         MessageHookTable = newfcn;
225
226         lprintf(5, "Registered a new message function (type %d)\n",
227                 EventType);
228 }
229
230
231 void CtdlRegisterXmsgHook(int (*fcn_ptr) (char *, char *, char *), int order)
232 {
233
234         struct XmsgFunctionHook *newfcn;
235
236         newfcn = (struct XmsgFunctionHook *)
237             mallok(sizeof(struct XmsgFunctionHook));
238         newfcn->next = XmsgHookTable;
239         newfcn->order = order;
240         newfcn->h_function_pointer = fcn_ptr;
241         XmsgHookTable = newfcn;
242         lprintf(5, "Registered a new x-msg function (priority %d)\n", order);
243 }
244
245 void CtdlRegisterServiceHook(int tcp_port,
246                         char *sockpath,
247                         void (*h_greeting_function) (void),
248                         void (*h_command_function) (void) )
249 {
250         struct ServiceFunctionHook *newfcn;
251         char message[SIZ];
252
253         newfcn = (struct ServiceFunctionHook *)
254             mallok(sizeof(struct ServiceFunctionHook));
255         newfcn->next = ServiceHookTable;
256         newfcn->tcp_port = tcp_port;
257         newfcn->sockpath = sockpath;
258         newfcn->h_greeting_function = h_greeting_function;
259         newfcn->h_command_function = h_command_function;
260
261         if (sockpath != NULL) {
262                 newfcn->msock = ig_uds_server(sockpath, config.c_maxsessions);
263                 sprintf(message, "Unix domain socket '%s': ", sockpath);
264         }
265         else if (tcp_port <= 0) {       /* port -1 to disable */
266                 lprintf(7, "Service has been manually disabled, skipping\n");
267                 phree(newfcn);
268                 return;
269         }
270         else {
271                 newfcn->msock = ig_tcp_server(tcp_port, config.c_maxsessions);
272                 sprintf(message, "TCP port %d: ", tcp_port);
273         }
274
275         if (newfcn->msock > 0) {
276                 ServiceHookTable = newfcn;
277                 strcat(message, "registered.");
278                 lprintf(5, "%s\n", message);
279         }
280         else {
281                 strcat(message, "FAILED.");
282                 lprintf(2, "%s\n", message);
283                 phree(newfcn);
284         }
285 }
286
287
288
289 void PerformSessionHooks(int EventType)
290 {
291         struct SessionFunctionHook *fcn;
292
293         for (fcn = SessionHookTable; fcn != NULL; fcn = fcn->next) {
294                 if (fcn->eventtype == EventType) {
295                         (*fcn->h_function_pointer) ();
296                 }
297         }
298 }
299
300 void PerformLogHooks(int loglevel, char *logmsg)
301 {
302         struct LogFunctionHook *fcn;
303
304         for (fcn = LogHookTable; fcn != NULL; fcn = fcn->next) {
305                 if (fcn->loglevel >= loglevel) {
306                         (*fcn->h_function_pointer) (logmsg);
307                 }
308         }
309 }
310
311 void PerformUserHooks(char *username, long usernum, int EventType)
312 {
313         struct UserFunctionHook *fcn;
314
315         for (fcn = UserHookTable; fcn != NULL; fcn = fcn->next) {
316                 if (fcn->eventtype == EventType) {
317                         (*fcn->h_function_pointer) (username, usernum);
318                 }
319         }
320 }
321
322 int PerformMessageHooks(struct CtdlMessage *msg, int EventType)
323 {
324         struct MessageFunctionHook *fcn;
325         int total_retval = 0;
326
327         /* Other code may elect to protect this message from server-side
328          * handlers; if this is the case, don't do anything.
329         lprintf(9, "** Event type is %d, flags are %d\n",
330                 EventType, msg->cm_flags);
331          */
332         if (msg->cm_flags & CM_SKIP_HOOKS) {
333                 lprintf(9, "Skipping hooks\n");
334                 return(0);
335         }
336
337         /* Otherwise, run all the hooks appropriate to this event type.
338          */
339         for (fcn = MessageHookTable; fcn != NULL; fcn = fcn->next) {
340                 if (fcn->eventtype == EventType) {
341                         total_retval = total_retval +
342                                 (*fcn->h_function_pointer) (msg);
343                 }
344         }
345
346         /* Return the sum of the return codes from the hook functions.  If
347          * this is an EVT_BEFORESAVE event, a nonzero return code will cause
348          * the save operation to abort.
349          */
350         return total_retval;
351 }
352
353
354
355 int PerformXmsgHooks(char *sender, char *recp, char *msg)
356 {
357         struct XmsgFunctionHook *fcn;
358         int total_sent = 0;
359         int p;
360
361         for (p=0; p<MAX_XMSG_PRI; ++p) {
362                 for (fcn = XmsgHookTable; fcn != NULL; fcn = fcn->next) {
363                         if (fcn->order == p) {
364                                 total_sent +=
365                                         (*fcn->h_function_pointer)
366                                                 (sender, recp, msg);
367                         }
368                 }
369                 /* Break out of the loop if a higher-priority function
370                  * successfully delivered the message.  This prevents duplicate
371                  * deliveries to local users simultaneously signed onto
372                  * remote services.
373                  */
374                 if (total_sent) goto DONE;
375         }
376 DONE:   return total_sent;
377 }