1 /*
2  * ProFTPD - FTP server daemon
3  * Copyright (c) 2003-2020 The ProFTPD Project team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, The ProFTPD Project team and other respective
20  * copyright holders give permission to link this program with OpenSSL, and
21  * distribute the resulting executable, without including the source code for
22  * OpenSSL in the source distribution.
23  */
24 
25 /* Event management code */
26 
27 #include "conf.h"
28 
29 /* Note: as more events are added, and as this API grows more and more used
30  * by the core code, look into using a different ADT for storage/retrieval
31  * of these objects, such as hash tables.
32  */
33 
34 struct event_handler {
35   struct event_handler *next, *prev;
36   module *module;
37   void (*cb)(const void *, void *);
38   void *user_data;
39   unsigned long flags;
40 };
41 
42 struct event_list {
43   struct event_list *next;
44   pool *pool;
45   const char *event;
46   size_t event_len;
47   struct event_handler *handlers;
48 };
49 
50 static pool *event_pool = NULL;
51 static struct event_list *events = NULL;
52 
53 static const char *curr_event = NULL;
54 static struct event_list *curr_evl = NULL;
55 static struct event_handler *curr_evh = NULL;
56 
57 /* Certain events are NOT logged via Trace logging (in order to prevent
58  * event/trace loops).
59  */
60 static const char *untraced_events[] = {
61   PR_LOG_NAME_UNSPEC,
62   PR_LOG_NAME_XFERLOG,
63   PR_LOG_NAME_SYSLOG,
64   PR_LOG_NAME_SYSTEMLOG,
65   PR_LOG_NAME_EXTLOG,
66   PR_LOG_NAME_TRACELOG,
67   NULL
68 };
69 
70 #define PR_EVENT_FL_UNTRACED		0x001
71 
72 static const char *trace_channel = "event";
73 
74 #define EVENT_POOL_SZ	256
75 
event_cleanup_cb(void * user_data)76 static void event_cleanup_cb(void *user_data) {
77   event_pool = NULL;
78   events = NULL;
79 
80   curr_event = NULL;
81   curr_evl = NULL;
82   curr_evh = NULL;
83 }
84 
pr_event_register(module * m,const char * event,void (* cb)(const void *,void *),void * user_data)85 int pr_event_register(module *m, const char *event,
86     void (*cb)(const void *, void *), void *user_data) {
87   register unsigned int i;
88   struct event_handler *evh;
89   struct event_list *evl;
90   pool *evl_pool;
91   unsigned long flags = 0;
92 
93   if (event == NULL ||
94       cb == NULL) {
95     errno = EINVAL;
96     return -1;
97   }
98 
99   if (event_pool == NULL) {
100     event_pool = make_sub_pool(permanent_pool);
101     pr_pool_tag(event_pool, "Event Pool");
102 
103     register_cleanup2(event_pool, NULL, event_cleanup_cb);
104   }
105 
106   pr_trace_msg(trace_channel, 3,
107     "module '%s' (%p) registering handler for event '%s' (at %p)",
108     m ? m->name : "(none)", m, event, cb);
109 
110   evh = pcalloc(event_pool, sizeof(struct event_handler));
111 
112   evh->module = m;
113   evh->cb = cb;
114   evh->user_data = user_data;
115 
116   /* Is this an untraced event? */
117   for (i = 0; untraced_events[i] != NULL; i++) {
118     if (strcmp(event, untraced_events[i]) == 0) {
119       flags = PR_EVENT_FL_UNTRACED;
120       break;
121     }
122   }
123 
124   evh->flags = flags;
125 
126   /* Scan the currently registered lists, looking for where to add this
127    * registration.
128    */
129 
130   for (evl = events; evl; evl = evl->next) {
131     if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
132       struct event_handler *evhi, *evhl = NULL;
133 
134       evhi = evl->handlers;
135       if (evhi) {
136         /* Make sure this event handler is added to the START of the list,
137          * in order to preserve module load order handling of events (i.e.
138          * last module loaded, first module handled).  The exception to this
139          * rule are core callbacks (i.e. where m == NULL); these will always
140          * be invoked last.
141          *
142          * Before that, though, check for duplicate registration/subscription.
143          */
144         while (evhi) {
145           pr_signals_handle();
146 
147           if (evhi->cb == evh->cb) {
148             /* Duplicate callback */
149             errno = EEXIST;
150             return -1;
151           }
152 
153           evhl = evhi;
154 
155           if (evhi->next == NULL) {
156             break;
157           }
158 
159           evhi = evhi->next;
160         }
161 
162         if (evh->module != NULL) {
163           if (evl->handlers->next != NULL) {
164             evl->handlers->next->prev = evh;
165           }
166 
167           evh->next = evl->handlers;
168           evl->handlers = evh;
169 
170         } else {
171           /* Core event listeners go at the end. */
172           evhl->next = evh;
173           evh->prev = evhl;
174         }
175 
176       } else {
177         evl->handlers = evh;
178       }
179 
180       /* All done */
181       return 0;
182     }
183   }
184 
185   evl_pool = pr_pool_create_sz(event_pool, EVENT_POOL_SZ);
186   pr_pool_tag(evl_pool, "Event listener list pool");
187 
188   evl = pcalloc(evl_pool, sizeof(struct event_list));
189   evl->pool = evl_pool;
190   evl->event = pstrdup(evl->pool, event);
191   evl->event_len = strlen(evl->event);
192   evl->handlers = evh;
193   evl->next = events;
194 
195   events = evl;
196 
197   /* Clear any cached data. */
198   curr_event = NULL;
199   curr_evl = NULL;
200   curr_evh = NULL;
201 
202   return 0;
203 }
204 
pr_event_unregister(module * m,const char * event,void (* cb)(const void *,void *))205 int pr_event_unregister(module *m, const char *event,
206     void (*cb)(const void *, void *)) {
207   struct event_list *evl;
208   int unregistered = FALSE;
209 
210   if (events == NULL) {
211     return 0;
212   }
213 
214   pr_trace_msg(trace_channel, 3,
215     "module '%s' (%p) unregistering handler for event '%s'",
216     m ? m->name : "(none)", m, event ? event : "(all)");
217 
218   /* For now, simply remove the event_handler entry for this callback.  In
219    * the future, add a static counter, and churn the event pool after a
220    * certain number of unregistrations, so that the memory pool doesn't
221    * grow unnecessarily.
222    */
223 
224   for (evl = events; evl; evl = evl->next) {
225     pr_signals_handle();
226 
227     if (event == NULL ||
228         strncmp(evl->event, event, evl->event_len + 1) == 0) {
229       struct event_handler *evh;
230 
231       /* If there are no handlers for this event, there is nothing to
232        * unregister.  Skip on to the next list.
233        */
234       if (evl->handlers == NULL) {
235         continue;
236       }
237 
238       for (evh = evl->handlers; evh;) {
239 
240         if ((m == NULL || evh->module == m) &&
241             (cb == NULL || evh->cb == cb)) {
242           struct event_handler *tmp = evh->next;
243 
244           if (evh->next) {
245             evh->next->prev = evh->prev;
246           }
247 
248           if (evh->prev) {
249             evh->prev->next = evh->next;
250 
251           } else {
252             /* This is the head of the list. */
253             evl->handlers = evh->next;
254           }
255 
256           evh->module = NULL;
257           evh = tmp;
258           unregistered = TRUE;
259 
260         } else {
261           evh = evh->next;
262         }
263       }
264     }
265   }
266 
267   /* Clear any cached data. */
268   curr_event = NULL;
269   curr_evl = NULL;
270   curr_evh = NULL;
271 
272   if (!unregistered) {
273     errno = ENOENT;
274     return -1;
275   }
276 
277   return 0;
278 }
279 
pr_event_listening(const char * event)280 int pr_event_listening(const char *event) {
281   struct event_list *evl;
282   int count = 0;
283 
284   if (event == NULL) {
285     errno = EINVAL;
286     return -1;
287   }
288 
289   if (events == NULL) {
290     /* No registered listeners at all. */
291     return 0;
292   }
293 
294   /* Lookup callbacks for this event. */
295   for (evl = events; evl; evl = evl->next) {
296 
297     if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
298       struct event_handler *evh;
299 
300       /* If there are no registered callbacks for this event, be done. */
301       if (evl->handlers == NULL) {
302         return 0;
303       }
304 
305       for (evh = evl->handlers; evh; evh = evh->next) {
306         count++;
307       }
308 
309       break;
310     }
311   }
312 
313   return count;
314 }
315 
pr_event_generate(const char * event,const void * event_data)316 void pr_event_generate(const char *event, const void *event_data) {
317   int use_cache = FALSE;
318   struct event_list *evl;
319 
320   if (event == NULL) {
321     return;
322   }
323 
324   /* If there are no registered callbacks, be done. */
325   if (events == NULL) {
326     return;
327   }
328 
329   /* If there is a cached event, see if the given event matches. */
330   if (curr_event != NULL &&
331       strcmp(curr_event, event) == 0) {
332     use_cache = TRUE;
333   }
334 
335   /* Lookup callbacks for this event. */
336   for (evl = use_cache ? curr_evl : events; evl; evl = evl->next) {
337 
338     if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
339       struct event_handler *evh;
340 
341       /* If there are no registered callbacks for this event, be done. */
342       if (!evl->handlers) {
343         pr_trace_msg(trace_channel, 8, "no event handlers registered for '%s'",
344           event);
345         return;
346       }
347 
348       curr_event = event;
349       curr_evl = evl;
350 
351       for (evh = use_cache ? curr_evh : evl->handlers; evh; evh = evh->next) {
352         /* Make sure that if the same event is generated by the current
353          * listener, the next time through we go to the next listener, rather
354          * sending the same event against to the same listener (Bug#3619).
355          */
356         curr_evh = evh->next;
357 
358         if (!(evh->flags & PR_EVENT_FL_UNTRACED)) {
359           if (evh->module) {
360             pr_trace_msg(trace_channel, 8,
361               "dispatching event '%s' to mod_%s (at %p, use cache = %s)", event,
362               evh->module->name, evh->cb, use_cache ? "true" : "false");
363 
364           } else {
365             pr_trace_msg(trace_channel, 8,
366               "dispatching event '%s' to core (at %p, use cache = %s)", event,
367               evh->cb, use_cache ? "true" : "false");
368           }
369         }
370 
371         evh->cb(event_data, evh->user_data);
372       }
373 
374       break;
375     }
376   }
377 
378   /* Clear any cached data after publishing the event to all interested
379    * listeners.
380    */
381   curr_event = NULL;
382   curr_evl = NULL;
383   curr_evh = NULL;
384 
385   return;
386 }
387 
pr_event_dump(void (* dumpf)(const char *,...))388 void pr_event_dump(void (*dumpf)(const char *, ...)) {
389   struct event_list *evl;
390 
391   if (dumpf == NULL) {
392     return;
393   }
394 
395   if (events == NULL) {
396     dumpf("%s", "No events registered");
397     return;
398   }
399 
400   for (evl = events; evl; evl = evl->next) {
401     pr_signals_handle();
402 
403     if (evl->handlers == NULL) {
404       dumpf("No handlers registered for '%s'", evl->event);
405 
406     } else {
407       struct event_handler *evh;
408 
409       dumpf("Registered for '%s':", evl->event);
410       for (evh = evl->handlers; evh; evh = evh->next) {
411         if (evh->module != NULL) {
412           dumpf("  mod_%s.c", evh->module->name);
413 
414         } else {
415           dumpf("  (core)");
416         }
417       }
418     }
419   }
420 
421   return;
422 }
423