Coverage Report

Created: 2024-09-30 06:24

/src/proftpd/src/event.c
Line
Count
Source (jump to first uncovered line)
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
0
#define PR_EVENT_FL_UNTRACED    0x001
71
72
static const char *trace_channel = "event";
73
74
0
#define EVENT_POOL_SZ 256
75
76
0
static void event_cleanup_cb(void *user_data) {
77
0
  event_pool = NULL;
78
0
  events = NULL;
79
80
0
  curr_event = NULL;
81
0
  curr_evl = NULL;
82
0
  curr_evh = NULL;
83
0
}
84
85
int pr_event_register(module *m, const char *event,
86
0
    void (*cb)(const void *, void *), void *user_data) {
87
0
  register unsigned int i;
88
0
  struct event_handler *evh;
89
0
  struct event_list *evl;
90
0
  pool *evl_pool;
91
0
  unsigned long flags = 0;
92
93
0
  if (event == NULL ||
94
0
      cb == NULL) {
95
0
    errno = EINVAL;
96
0
    return -1;
97
0
  }
98
99
0
  if (event_pool == NULL) {
100
0
    event_pool = make_sub_pool(permanent_pool);
101
0
    pr_pool_tag(event_pool, "Event Pool");
102
103
0
    register_cleanup2(event_pool, NULL, event_cleanup_cb);
104
0
  }
105
106
0
  pr_trace_msg(trace_channel, 3,
107
0
    "module '%s' (%p) registering handler for event '%s' (at %p)",
108
0
    m ? m->name : "(none)", m, event, cb);
109
110
0
  evh = pcalloc(event_pool, sizeof(struct event_handler));
111
112
0
  evh->module = m;
113
0
  evh->cb = cb;
114
0
  evh->user_data = user_data;
115
116
  /* Is this an untraced event? */
117
0
  for (i = 0; untraced_events[i] != NULL; i++) {
118
0
    if (strcmp(event, untraced_events[i]) == 0) {
119
0
      flags = PR_EVENT_FL_UNTRACED;
120
0
      break;
121
0
    }
122
0
  }
123
124
0
  evh->flags = flags;
125
126
  /* Scan the currently registered lists, looking for where to add this
127
   * registration.
128
   */
129
130
0
  for (evl = events; evl; evl = evl->next) {
131
0
    if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
132
0
      struct event_handler *evhi, *evhl = NULL;
133
134
0
      evhi = evl->handlers;
135
0
      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
0
        while (evhi) {
145
0
          pr_signals_handle();
146
147
0
          if (evhi->cb == evh->cb) {
148
            /* Duplicate callback */
149
0
            errno = EEXIST;
150
0
            return -1;
151
0
          }
152
153
0
          evhl = evhi;
154
155
0
          if (evhi->next == NULL) {
156
0
            break;
157
0
          }
158
159
0
          evhi = evhi->next;
160
0
        }
161
162
0
        if (evh->module != NULL) {
163
0
          if (evl->handlers != NULL) {
164
0
            evl->handlers->prev = evh;
165
0
          }
166
167
0
          evh->next = evl->handlers;
168
0
          evl->handlers = evh;
169
170
0
        } else {
171
          /* Core event listeners go at the end. */
172
0
          evhl->next = evh;
173
0
          evh->prev = evhl;
174
0
        }
175
176
0
      } else {
177
0
        evl->handlers = evh;
178
0
      }
179
180
      /* All done */
181
0
      return 0;
182
0
    }
183
0
  }
184
185
0
  evl_pool = pr_pool_create_sz(event_pool, EVENT_POOL_SZ);
186
0
  pr_pool_tag(evl_pool, "Event listener list pool");
187
188
0
  evl = pcalloc(evl_pool, sizeof(struct event_list));
189
0
  evl->pool = evl_pool;
190
0
  evl->event = pstrdup(evl->pool, event);
191
0
  evl->event_len = strlen(evl->event);
192
0
  evl->handlers = evh;
193
0
  evl->next = events;
194
195
0
  events = evl;
196
197
  /* Clear any cached data. */
198
0
  curr_event = NULL;
199
0
  curr_evl = NULL;
200
0
  curr_evh = NULL;
201
202
0
  return 0;
203
0
}
204
205
int pr_event_unregister(module *m, const char *event,
206
0
    void (*cb)(const void *, void *)) {
207
0
  struct event_list *evl;
208
0
  int unregistered = FALSE;
209
210
0
  if (events == NULL) {
211
0
    return 0;
212
0
  }
213
214
0
  pr_trace_msg(trace_channel, 3,
215
0
    "module '%s' (%p) unregistering handler for event '%s'",
216
0
    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
0
  for (evl = events; evl; evl = evl->next) {
225
0
    pr_signals_handle();
226
227
0
    if (event == NULL ||
228
0
        strncmp(evl->event, event, evl->event_len + 1) == 0) {
229
0
      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
0
      if (evl->handlers == NULL) {
235
0
        continue;
236
0
      }
237
238
0
      for (evh = evl->handlers; evh;) {
239
240
0
        if ((m == NULL || evh->module == m) &&
241
0
            (cb == NULL || evh->cb == cb)) {
242
0
          struct event_handler *tmp = evh->next;
243
244
0
          if (evh->next) {
245
0
            evh->next->prev = evh->prev;
246
0
          }
247
248
0
          if (evh->prev) {
249
0
            evh->prev->next = evh->next;
250
251
0
          } else {
252
            /* This is the head of the list. */
253
0
            evl->handlers = evh->next;
254
0
          }
255
256
0
          evh->module = NULL;
257
0
          evh = tmp;
258
0
          unregistered = TRUE;
259
260
0
        } else {
261
0
          evh = evh->next;
262
0
        }
263
0
      }
264
0
    }
265
0
  }
266
267
  /* Clear any cached data. */
268
0
  curr_event = NULL;
269
0
  curr_evl = NULL;
270
0
  curr_evh = NULL;
271
272
0
  if (!unregistered) {
273
0
    errno = ENOENT;
274
0
    return -1;
275
0
  }
276
277
0
  return 0;
278
0
}
279
280
1.42k
int pr_event_listening(const char *event) {
281
1.42k
  struct event_list *evl;
282
1.42k
  int count = 0;
283
284
1.42k
  if (event == NULL) {
285
0
    errno = EINVAL;
286
0
    return -1;
287
0
  }
288
289
1.42k
  if (events == NULL) {
290
    /* No registered listeners at all. */
291
1.42k
    return 0;
292
1.42k
  }
293
294
  /* Lookup callbacks for this event. */
295
0
  for (evl = events; evl; evl = evl->next) {
296
297
0
    if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
298
0
      struct event_handler *evh;
299
300
      /* If there are no registered callbacks for this event, be done. */
301
0
      if (evl->handlers == NULL) {
302
0
        return 0;
303
0
      }
304
305
0
      for (evh = evl->handlers; evh; evh = evh->next) {
306
0
        count++;
307
0
      }
308
309
0
      break;
310
0
    }
311
0
  }
312
313
0
  return count;
314
0
}
315
316
0
void pr_event_generate(const char *event, const void *event_data) {
317
0
  int use_cache = FALSE;
318
0
  struct event_list *evl;
319
320
0
  if (event == NULL) {
321
0
    return;
322
0
  }
323
324
  /* If there are no registered callbacks, be done. */
325
0
  if (events == NULL) {
326
0
    return;
327
0
  }
328
329
  /* If there is a cached event, see if the given event matches. */
330
0
  if (curr_event != NULL &&
331
0
      strcmp(curr_event, event) == 0) {
332
0
    use_cache = TRUE;
333
0
  }
334
335
  /* Lookup callbacks for this event. */
336
0
  for (evl = use_cache ? curr_evl : events; evl; evl = evl->next) {
337
338
0
    if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
339
0
      struct event_handler *evh;
340
341
      /* If there are no registered callbacks for this event, be done. */
342
0
      if (!evl->handlers) {
343
0
        pr_trace_msg(trace_channel, 8, "no event handlers registered for '%s'",
344
0
          event);
345
0
        return;
346
0
      }
347
348
0
      curr_event = event;
349
0
      curr_evl = evl;
350
351
0
      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
0
        curr_evh = evh->next;
357
358
0
        if (!(evh->flags & PR_EVENT_FL_UNTRACED)) {
359
0
          if (evh->module) {
360
0
            pr_trace_msg(trace_channel, 8,
361
0
              "dispatching event '%s' to mod_%s (at %p, use cache = %s)", event,
362
0
              evh->module->name, evh->cb, use_cache ? "true" : "false");
363
364
0
          } else {
365
0
            pr_trace_msg(trace_channel, 8,
366
0
              "dispatching event '%s' to core (at %p, use cache = %s)", event,
367
0
              evh->cb, use_cache ? "true" : "false");
368
0
          }
369
0
        }
370
371
0
        evh->cb(event_data, evh->user_data);
372
0
      }
373
374
0
      break;
375
0
    }
376
0
  }
377
378
  /* Clear any cached data after publishing the event to all interested
379
   * listeners.
380
   */
381
0
  curr_event = NULL;
382
0
  curr_evl = NULL;
383
0
  curr_evh = NULL;
384
0
}
385
386
0
void pr_event_dump(void (*dumpf)(const char *, ...)) {
387
0
  struct event_list *evl;
388
389
0
  if (dumpf == NULL) {
390
0
    return;
391
0
  }
392
393
0
  if (events == NULL) {
394
0
    dumpf("%s", "No events registered");
395
0
    return;
396
0
  }
397
398
0
  for (evl = events; evl; evl = evl->next) {
399
0
    pr_signals_handle();
400
401
0
    if (evl->handlers == NULL) {
402
0
      dumpf("No handlers registered for '%s'", evl->event);
403
404
0
    } else {
405
0
      struct event_handler *evh;
406
407
0
      dumpf("Registered for '%s':", evl->event);
408
0
      for (evh = evl->handlers; evh; evh = evh->next) {
409
0
        if (evh->module != NULL) {
410
0
          dumpf("  mod_%s.c", evh->module->name);
411
412
0
        } else {
413
0
          dumpf("  (core)");
414
0
        }
415
0
      }
416
0
    }
417
0
  }
418
0
}