• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 28259826111

26 Jun 2026 07:20PM UTC coverage: 93.032% (+0.6%) from 92.469%
28259826111

push

github

51363 of 55210 relevant lines covered (93.03%)

200.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.32
/src/event.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2003-2026 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, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, The ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23

24
/* Event management code */
25

26
#include "conf.h"
27

28
/* Note: as more events are added, and as this API grows more and more used
29
 * by the core code, look into using a different ADT for storage/retrieval
30
 * of these objects, such as hash tables.
31
 */
32

33
struct event_handler {
34
  struct event_handler *next, *prev;
35
  module *module;
36
  void (*cb)(const void *, void *);
37
  void *user_data;
38
  unsigned long flags;
39
};
40

41
struct event_list {
42
  struct event_list *next;
43
  pool *pool;
44
  const char *event;
45
  size_t event_len;
46
  struct event_handler *handlers;
47
};
48

49
static pool *event_pool = NULL;
50
static struct event_list *events = NULL;
51

52
static const char *curr_event = NULL;
53
static struct event_list *curr_evl = NULL;
54
static struct event_handler *curr_evh = NULL;
55

56
/* Certain events are NOT logged via Trace logging (in order to prevent
57
 * event/trace loops).
58
 */
59
static const char *untraced_events[] = {
60
  PR_LOG_NAME_UNSPEC,
61
  PR_LOG_NAME_XFERLOG,
62
  PR_LOG_NAME_SYSLOG,
63
  PR_LOG_NAME_SYSTEMLOG,
64
  PR_LOG_NAME_EXTLOG,
65
  PR_LOG_NAME_TRACELOG,
66
  NULL
67
};
68

69
#define PR_EVENT_FL_UNTRACED                0x001
70

71
static const char *trace_channel = "event";
72

73
#define EVENT_POOL_SZ        256
74

75
static void event_cleanup_cb(void *user_data) {
76
  event_pool = NULL;
679✔
77
  events = NULL;
679✔
78

679✔
79
  curr_event = NULL;
80
  curr_evl = NULL;
679✔
81
  curr_evh = NULL;
679✔
82
}
679✔
83

679✔
84
int pr_event_register(module *m, const char *event,
85
    void (*cb)(const void *, void *), void *user_data) {
780✔
86
  register unsigned int i;
87
  struct event_handler *evh;
780✔
88
  struct event_list *evl;
780✔
89
  pool *evl_pool;
780✔
90
  unsigned long flags = 0;
780✔
91

780✔
92
  if (event == NULL ||
93
      cb == NULL) {
780✔
94
    errno = EINVAL;
780✔
95
    return -1;
3✔
96
  }
3✔
97

98
  if (event_pool == NULL) {
99
    event_pool = make_sub_pool(permanent_pool);
777✔
100
    pr_pool_tag(event_pool, "Event Pool");
743✔
101

743✔
102
    register_cleanup2(event_pool, NULL, event_cleanup_cb);
103
  }
743✔
104

105
  pr_trace_msg(trace_channel, 3,
106
    "module '%s' (%p) registering handler for event '%s' (at %p)",
777✔
107
    m ? m->name : "(none)", m, event, cb);
108

109
  evh = pcalloc(event_pool, sizeof(struct event_handler));
110

777✔
111
  evh->module = m;
112
  evh->cb = cb;
777✔
113
  evh->user_data = user_data;
777✔
114

777✔
115
  /* Is this an untraced event? */
116
  for (i = 0; untraced_events[i] != NULL; i++) {
117
    if (strcmp(event, untraced_events[i]) == 0) {
5,439✔
118
      flags = PR_EVENT_FL_UNTRACED;
4,662✔
119
      break;
120
    }
121
  }
122

123
  evh->flags = flags;
124

777✔
125
  /* Scan the currently registered lists, looking for where to add this
126
   * registration.
127
   */
128

129
  for (evl = events; evl; evl = evl->next) {
130
    if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
805✔
131
      struct event_handler *evhi, *evhl = NULL;
48✔
132

20✔
133
      evhi = evl->handlers;
134
      if (evhi) {
20✔
135
        /* Make sure this event handler is added to the START of the list,
20✔
136
         * in order to preserve module load order handling of events (i.e.
137
         * last module loaded, first module handled).  The exception to this
138
         * rule are core callbacks (i.e. where m == NULL); these will always
139
         * be invoked last.
140
         *
141
         * Before that, though, check for duplicate registration/subscription.
142
         */
143
        while (evhi) {
144
          pr_signals_handle();
17✔
145

17✔
146
          if (evhi->cb == evh->cb) {
147
            /* Duplicate callback */
17✔
148
            errno = EEXIST;
149
            return -1;
1✔
150
          }
1✔
151

152
          evhl = evhi;
153

16✔
154
          if (evhi->next == NULL) {
155
            break;
16✔
156
          }
157

158
          evhi = evhi->next;
159
        }
160

161
        if (evh->module != NULL) {
162
          if (evl->handlers != NULL) {
16✔
163
            evl->handlers->prev = evh;
1✔
164
          }
1✔
165

166
          evh->next = evl->handlers;
167
          evl->handlers = evh;
1✔
168

1✔
169
        } else {
170
          /* Core event listeners go at the end. */
171
          evhl->next = evh;
172
          evh->prev = evhl;
15✔
173
        }
15✔
174

175
      } else {
176
        evl->handlers = evh;
177
      }
3✔
178

179
      /* All done */
180
      return 0;
181
    }
19✔
182
  }
183

184
  evl_pool = pr_pool_create_sz(event_pool, EVENT_POOL_SZ);
185
  pr_pool_tag(evl_pool, "Event listener list pool");
757✔
186

757✔
187
  evl = pcalloc(evl_pool, sizeof(struct event_list));
188
  evl->pool = evl_pool;
757✔
189
  evl->event = pstrdup(evl->pool, event);
757✔
190
  evl->event_len = strlen(evl->event);
757✔
191
  evl->handlers = evh;
757✔
192
  evl->next = events;
757✔
193

757✔
194
  events = evl;
195

757✔
196
  /* Clear any cached data. */
197
  curr_event = NULL;
198
  curr_evl = NULL;
757✔
199
  curr_evh = NULL;
757✔
200

757✔
201
  return 0;
202
}
757✔
203

204
int pr_event_unregister(module *m, const char *event,
205
    void (*cb)(const void *, void *)) {
24✔
206
  struct event_list *evl;
207
  int unregistered = FALSE;
24✔
208

24✔
209
  if (events == NULL) {
210
    return 0;
24✔
211
  }
212

213
  pr_trace_msg(trace_channel, 3,
214
    "module '%s' (%p) unregistering handler for event '%s'",
21✔
215
    m ? m->name : "(none)", m, event ? event : "(all)");
216

217
  /* For now, simply remove the event_handler entry for this callback.  In
218
   * the future, add a static counter, and churn the event pool after a
219
   * certain number of unregistrations, so that the memory pool doesn't
220
   * grow unnecessarily.
221
   */
222

223
  for (evl = events; evl; evl = evl->next) {
224
    pr_signals_handle();
35✔
225

19✔
226
    if (event == NULL ||
227
        strncmp(evl->event, event, evl->event_len + 1) == 0) {
19✔
228
      struct event_handler *evh;
14✔
229

15✔
230
      /* If there are no handlers for this event, there is nothing to
231
       * unregister.  Skip on to the next list.
232
       */
233
      if (evl->handlers == NULL) {
234
        continue;
15✔
235
      }
1✔
236

237
      for (evh = evl->handlers; evh;) {
238

31✔
239
        if ((m == NULL || evh->module == m) &&
240
            (cb == NULL || evh->cb == cb)) {
17✔
241
          struct event_handler *tmp = evh->next;
8✔
242

12✔
243
          if (evh->next) {
244
            evh->next->prev = evh->prev;
12✔
245
          }
2✔
246

247
          if (evh->prev) {
248
            evh->prev->next = evh->next;
12✔
249

1✔
250
          } else {
251
            /* This is the head of the list. */
252
            evl->handlers = evh->next;
253
          }
11✔
254

255
          evh->module = NULL;
256
          evh = tmp;
12✔
257
          unregistered = TRUE;
12✔
258

12✔
259
        } else {
260
          evh = evh->next;
261
        }
5✔
262
      }
263
    }
264
  }
265

266
  /* Clear any cached data. */
267
  curr_event = NULL;
268
  curr_evl = NULL;
16✔
269
  curr_evh = NULL;
16✔
270

16✔
271
  if (!unregistered) {
272
    errno = ENOENT;
16✔
273
    return -1;
5✔
274
  }
5✔
275

276
  return 0;
277
}
278

279
int pr_event_listening(const char *event) {
280
  struct event_list *evl;
6✔
281
  int count = 0;
6✔
282

6✔
283
  if (event == NULL) {
284
    errno = EINVAL;
6✔
285
    return -1;
1✔
286
  }
1✔
287

288
  if (events == NULL) {
289
    /* No registered listeners at all. */
5✔
290
    return 0;
291
  }
292

293
  /* Lookup callbacks for this event. */
294
  for (evl = events; evl; evl = evl->next) {
295

8✔
296
    if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
297
      struct event_handler *evh;
8✔
298

4✔
299
      /* If there are no registered callbacks for this event, be done. */
300
      if (evl->handlers == NULL) {
301
        return 0;
4✔
302
      }
303

304
      for (evh = evl->handlers; evh; evh = evh->next) {
305
        count++;
5✔
306
      }
3✔
307

308
      break;
309
    }
310
  }
311

312
  return count;
313
}
314

315
void pr_event_generate(const char *event, const void *event_data) {
316
  int use_cache = FALSE;
366✔
317
  struct event_list *evl;
366✔
318

366✔
319
  if (event == NULL) {
320
    return;
366✔
321
  }
322

323
  /* If there are no registered callbacks, be done. */
324
  if (events == NULL) {
325
    return;
365✔
326
  }
327

328
  /* If there is a cached event, see if the given event matches. */
329
  if (curr_event != NULL &&
330
      strcmp(curr_event, event) == 0) {
361✔
331
    use_cache = TRUE;
×
332
  }
×
333

334
  /* Lookup callbacks for this event. */
335
  for (evl = use_cache ? curr_evl : events; evl; evl = evl->next) {
336

717✔
337
    if (strncmp(evl->event, event, evl->event_len + 1) == 0) {
338
      struct event_handler *evh;
362✔
339

6✔
340
      /* If there are no registered callbacks for this event, be done. */
341
      if (!evl->handlers) {
342
        pr_trace_msg(trace_channel, 8, "no event handlers registered for '%s'",
6✔
343
          event);
1✔
344
        return;
345
      }
1✔
346

347
      curr_event = event;
348
      curr_evl = evl;
5✔
349

5✔
350
      for (evh = use_cache ? curr_evh : evl->handlers; evh; evh = evh->next) {
351
        /* Make sure that if the same event is generated by the current
16✔
352
         * listener, the next time through we go to the next listener, rather
353
         * sending the same event against to the same listener (Bug#3619).
354
         */
355
        curr_evh = evh->next;
356

6✔
357
        if (!(evh->flags & PR_EVENT_FL_UNTRACED)) {
358
          if (evh->module) {
6✔
359
            pr_trace_msg(trace_channel, 8,
6✔
360
              "dispatching event '%s' to mod_%s (at %p, use cache = %s)", event,
×
361
              evh->module->name, evh->cb, use_cache ? "true" : "false");
362

363
          } else {
364
            pr_trace_msg(trace_channel, 8,
365
              "dispatching event '%s' to core (at %p, use cache = %s)", event,
12✔
366
              evh->cb, use_cache ? "true" : "false");
367
          }
368
        }
369

370
        evh->cb(event_data, evh->user_data);
371
      }
6✔
372

373
      break;
374
    }
375
  }
376

377
  /* Clear any cached data after publishing the event to all interested
378
   * listeners.
379
   */
380
  curr_event = NULL;
381
  curr_evl = NULL;
360✔
382
  curr_evh = NULL;
360✔
383
}
360✔
384

385
void pr_event_dump(void (*dumpf)(const char *, ...)) {
386
  struct event_list *evl;
4✔
387

4✔
388
  if (dumpf == NULL) {
389
    return;
4✔
390
  }
391

392
  if (events == NULL) {
393
    dumpf("%s", "No events registered");
3✔
394
    return;
1✔
395
  }
1✔
396

397
  for (evl = events; evl; evl = evl->next) {
398
    pr_signals_handle();
5✔
399

3✔
400
    if (evl->handlers == NULL) {
401
      dumpf("No handlers registered for '%s'", evl->event);
3✔
402

×
403
    } else {
404
      struct event_handler *evh;
405

3✔
406
      dumpf("Registered for '%s':", evl->event);
407
      for (evh = evl->handlers; evh; evh = evh->next) {
3✔
408
        if (evh->module != NULL) {
7✔
409
          dumpf("  mod_%s.c", evh->module->name);
4✔
410

2✔
411
        } else {
412
          dumpf("  (core)");
413
        }
2✔
414
      }
415
    }
416
  }
417
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc