Coverage Report

Created: 2026-09-01 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/lib-event.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "lib-event-private.h"
5
#include "event-filter.h"
6
#include "array.h"
7
#include "hash.h"
8
#include "llist.h"
9
#include "time-util.h"
10
#include "str.h"
11
#include "strescape.h"
12
#include "ioloop-private.h"
13
14
#include <ctype.h>
15
16
HASH_TABLE_DEFINE_TYPE(category_set, void *, const struct event_category *);
17
18
enum event_code {
19
  EVENT_CODE_ALWAYS_LOG_SOURCE  = 'a',
20
  EVENT_CODE_CATEGORY   = 'c',
21
  EVENT_CODE_TV_LAST_SENT   = 'l',
22
  EVENT_CODE_SENDING_NAME   = 'n',
23
  EVENT_CODE_SOURCE   = 's',
24
25
  EVENT_CODE_FIELD_INTMAX   = 'I',
26
  EVENT_CODE_FIELD_STR    = 'S',
27
  EVENT_CODE_FIELD_TIMEVAL  = 'T',
28
  EVENT_CODE_FIELD_IP   = 'P',
29
  EVENT_CODE_FIELD_STRLIST  = 'L',
30
};
31
32
/* Internal event category state.
33
34
   Each (unique) event category maps to one internal category.  (I.e., if
35
   two places attempt to register the same category, they will share the
36
   internal state.)
37
38
   This is required in order to support multiple registrations of the same
39
   category.  Currently, the only situation in which this occurs is the
40
   stats process receiving categories from other processes and also using
41
   the same categories internally.
42
43
   During registration, we look up the internal state based on the new
44
   category's name.  If found, we use it after sanity checking that the two
45
   are identical (i.e., they both have the same name and parent).  If not
46
   found, we allocate a new internal state and use it.
47
48
   We stash a pointer to the internal state in struct event_category (the
49
   "internal" member).  As a result, all category structs for the same
50
   category point to the same internal state. */
51
struct event_internal_category {
52
  /* More than one category can be represented by the internal state.
53
     To give consumers a unique but consistent category pointer, we
54
     return a pointer to this 'representative' category structure.
55
     Because we allocated it, we know that it will live exactly as
56
     long as we need it to. */
57
  struct event_category representative;
58
59
  struct event_internal_category *parent;
60
  char *name;
61
  int refcount;
62
};
63
64
struct event_reason {
65
  struct event *event;
66
};
67
68
struct event_category_iterator {
69
  HASH_TABLE_TYPE(category_set) hash;
70
  struct hash_iterate_context *iter;
71
};
72
73
extern struct event_passthrough event_passthrough_vfuncs;
74
75
static struct event *events = NULL;
76
static struct event *current_global_event = NULL;
77
static struct event *event_last_passthrough = NULL;
78
static ARRAY(event_callback_t *) event_handlers;
79
static ARRAY(event_category_callback_t *) event_category_callbacks;
80
static ARRAY(struct event_internal_category *) event_registered_categories_internal;
81
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
82
static ARRAY(struct event_category *) event_registered_categories;
83
#endif
84
static ARRAY(struct event_category *) event_registered_categories_representative;
85
static ARRAY(struct event *) global_event_stack;
86
static uint64_t event_id_counter = 0;
87
88
static void get_self_rusage(struct rusage *ru_r)
89
0
{
90
0
  if (getrusage(RUSAGE_SELF, ru_r) < 0)
91
0
    i_fatal("getrusage() failed: %m");
92
0
}
93
94
static struct event *
95
event_create_internal(struct event *parent, const char *source_filename,
96
          unsigned int source_linenum);
97
static struct event_internal_category *
98
event_category_find_internal(const char *name);
99
100
static struct event *last_passthrough_event(void)
101
873k
{
102
873k
  i_assert(event_last_passthrough != NULL);
103
873k
  return event_last_passthrough;
104
873k
}
105
106
static void event_copy_parent_defaults(struct event *event,
107
               const struct event *parent)
108
512k
{
109
512k
  event->always_log_source = parent->always_log_source;
110
512k
  event->passthrough = parent->passthrough;
111
512k
  event->min_log_level = parent->min_log_level;
112
512k
  event->forced_debug = parent->forced_debug;
113
512k
  event->forced_never_debug = parent->forced_never_debug;
114
512k
  event->disable_callbacks = parent->disable_callbacks;
115
512k
}
116
117
static bool
118
event_find_category(const struct event *event,
119
        const struct event_category *category);
120
121
static void event_set_changed(struct event *event)
122
1.14M
{
123
1.14M
  event->change_id++;
124
  /* It's unlikely that change_id will ever wrap, but lets be safe
125
     anyway. */
126
1.14M
  if (event->change_id == 0 ||
127
1.14M
      event->change_id == event->sent_to_stats_id)
128
0
    event->change_id++;
129
1.14M
}
130
131
static bool
132
event_call_callbacks(struct event *event, enum event_callback_type type,
133
         struct failure_context *ctx, const char *fmt, va_list args)
134
1.03M
{
135
1.03M
  event_callback_t *callback;
136
137
1.03M
  if (event->disable_callbacks)
138
0
    return TRUE;
139
1.03M
  if (!array_is_created(&event_handlers))
140
0
    return TRUE;
141
142
1.03M
  array_foreach_elem(&event_handlers, callback) {
143
0
    bool ret;
144
145
0
    T_BEGIN {
146
0
      ret = callback(event, type, ctx, fmt, args);
147
0
    } T_END;
148
0
    if (!ret) {
149
      /* event sending was stopped */
150
0
      return FALSE;
151
0
    }
152
0
  }
153
1.03M
  return TRUE;
154
1.03M
}
155
156
static void
157
event_call_callbacks_noargs(struct event *event,
158
          enum event_callback_type type, ...)
159
1.03M
{
160
1.03M
  va_list args;
161
162
  /* the args are empty and not used for anything, but there doesn't seem
163
     to be any nice and standard way of passing an initialized va_list
164
     as a parameter without va_start(). */
165
1.03M
  va_start(args, type);
166
1.03M
  (void)event_call_callbacks(event, type, NULL, NULL, args);
167
1.03M
  va_end(args);
168
1.03M
}
169
170
void event_copy_categories(struct event *to, struct event *from)
171
0
{
172
0
  unsigned int cat_count;
173
0
  struct event_category *const *categories =
174
0
    event_get_categories(from, &cat_count);
175
0
  for (unsigned int i = 1; i <= cat_count; i++)
176
0
    event_add_category(to, categories[cat_count-i]);
177
0
}
178
179
void event_copy_fields(struct event *to, struct event *from)
180
0
{
181
0
  const struct event_field *fld;
182
0
  unsigned int count;
183
0
  const char *const *values;
184
185
0
  if (!array_is_created(&from->fields))
186
0
    return;
187
0
  array_foreach(&from->fields, fld) {
188
0
    switch (fld->value_type) {
189
0
    case EVENT_FIELD_VALUE_TYPE_STR:
190
0
      event_add_str(to, fld->key, fld->value.str);
191
0
      break;
192
0
    case EVENT_FIELD_VALUE_TYPE_INTMAX:
193
0
      event_add_int(to, fld->key, fld->value.intmax);
194
0
      break;
195
0
    case EVENT_FIELD_VALUE_TYPE_TIMEVAL:
196
0
      event_add_timeval(to, fld->key, &fld->value.timeval);
197
0
      break;
198
0
    case EVENT_FIELD_VALUE_TYPE_IP:
199
0
      event_add_ip(to, fld->key, &fld->value.ip);
200
0
      break;
201
0
    case EVENT_FIELD_VALUE_TYPE_STRLIST:
202
0
      values = array_get(&fld->value.strlist, &count);
203
0
      for (unsigned int i = 0; i < count; i++)
204
0
        event_strlist_append(to, fld->key, values[i]);
205
0
      break;
206
0
    default:
207
0
      break;
208
0
    }
209
0
  }
210
0
}
211
212
bool event_has_all_categories(struct event *event, const struct event *other)
213
0
{
214
0
  struct event_category **cat;
215
0
  if (!array_is_created(&other->categories))
216
0
    return TRUE;
217
0
  if (!array_is_created(&event->categories))
218
0
    return FALSE;
219
0
  array_foreach_modifiable(&other->categories, cat) {
220
0
    if (!event_find_category(event, *cat))
221
0
      return FALSE;
222
0
  }
223
0
  return TRUE;
224
0
}
225
226
bool event_has_all_fields(struct event *event, const struct event *other)
227
0
{
228
0
  struct event_field *fld;
229
0
  if (!array_is_created(&other->fields))
230
0
    return TRUE;
231
0
  array_foreach_modifiable(&other->fields, fld) {
232
0
    if (event_find_field_nonrecursive(event, fld->key) == NULL)
233
0
      return FALSE;
234
0
  }
235
0
  return TRUE;
236
0
}
237
238
struct event *event_dup(const struct event *source)
239
0
{
240
0
  struct event *ret =
241
0
    event_create_internal(source->parent, source->source_filename,
242
0
              source->source_linenum);
243
0
  string_t *str = t_str_new(256);
244
0
  const char *err;
245
0
  event_export(source, str);
246
0
  if (!event_import(ret, str_c(str), &err))
247
0
    i_panic("event_import(%s) failed: %s", str_c(str), err);
248
0
  ret->tv_created_ioloop = source->tv_created_ioloop;
249
0
  return ret;
250
0
}
251
252
/*
253
 * Copy the source's categories and fields recursively.
254
 *
255
 * We recurse to the parent before copying this event's data because we may
256
 * be overriding a field.
257
 */
258
static void event_flatten_recurse(struct event *dst, struct event *src,
259
          struct event *limit)
260
0
{
261
0
  if (src->parent != limit)
262
0
    event_flatten_recurse(dst, src->parent, limit);
263
264
0
  event_copy_categories(dst, src);
265
0
  event_copy_fields(dst, src);
266
0
}
267
268
struct event *event_flatten(struct event *src)
269
0
{
270
0
  struct event *dst;
271
272
  /* If we don't have a parent or a global event,
273
     we have nothing to flatten. */
274
0
  if (src->parent == NULL && current_global_event == NULL)
275
0
    return event_ref(src);
276
277
  /* We have to flatten the event. */
278
279
0
  dst = event_create_internal(NULL, src->source_filename,
280
0
            src->source_linenum);
281
0
  dst = event_set_name(dst, src->sending_name);
282
283
0
  if (current_global_event != NULL)
284
0
    event_flatten_recurse(dst, current_global_event, NULL);
285
0
  event_flatten_recurse(dst, src, NULL);
286
287
0
  dst->tv_created_ioloop = src->tv_created_ioloop;
288
0
  dst->tv_created = src->tv_created;
289
0
  dst->tv_last_sent = src->tv_last_sent;
290
291
0
  return dst;
292
0
}
293
294
static inline void replace_parent_ref(struct event *event, struct event *new)
295
0
{
296
0
  if (event->parent == new)
297
0
    return; /* no-op */
298
299
0
  if (new != NULL)
300
0
    event_ref(new);
301
302
0
  event_unref(&event->parent);
303
304
0
  event->parent = new;
305
0
}
306
307
/*
308
 * Minimize the event and its ancestry.
309
 *
310
 * In general, the chain of parents starting from this event can be divided
311
 * up into four consecutive ranges:
312
 *
313
 *  1. the event itself
314
 *  2. a range of events that should be flattened into the event itself
315
 *  3. a range of trivial (i.e., no categories or fields) events that should
316
 *     be skipped
317
 *  4. the rest of the chain
318
 *
319
 * Except for the first range, the event itself, the remaining ranges can
320
 * have zero events.
321
 *
322
 * As the names of these ranges imply, we want to flatten certain parts of
323
 * the ancestry, skip other parts of the ancestry and leave the remainder
324
 * untouched.
325
 *
326
 * For example, suppose that we have an event (A) with ancestors forming the
327
 * following graph:
328
 *
329
 *  A -> B -> C -> D -> E -> F
330
 *
331
 * Further, suppose that B, C, and F contain some categories or fields but
332
 * have not yet been sent to an external process that knows how to reference
333
 * previously encountered events, and D contains no fields or categories of
334
 * its own (but it inherits some from E and F).
335
 *
336
 * We can define the 4 ranges:
337
 *
338
 *  A:     the event
339
 *  B-C:   flattening
340
 *  D:     skipping
341
 *  E-end: the rest
342
 *
343
 * The output would therefore be:
344
 *
345
 *  G -> E -> F
346
 *
347
 * where G contains the fields and categories of A, B, and C (and trivially
348
 * D because D was empty).
349
 *
350
 * Note that even though F has not yet been sent out, we send it now because
351
 * it is part of the "rest" range.
352
 *
353
 * TODO: We could likely apply this function recursively on the "rest"
354
 * range, but further investigation is required to determine whether it is
355
 * worth it.
356
 */
357
struct event *event_minimize(struct event *event)
358
0
{
359
0
  struct event *flatten_bound;
360
0
  struct event *skip_bound;
361
0
  struct event *new_event;
362
0
  struct event *cur;
363
364
0
  if (event->parent == NULL)
365
0
    return event_ref(event);
366
367
  /* find the bound for field/category flattening */
368
0
  flatten_bound = NULL;
369
0
  for (cur = event->parent; cur != NULL; cur = cur->parent) {
370
0
    if (cur->sent_to_stats_id == 0 &&
371
0
        timeval_cmp(&cur->tv_created_ioloop,
372
0
        &event->tv_created_ioloop) == 0)
373
0
      continue;
374
375
0
    flatten_bound = cur;
376
0
    break;
377
0
  }
378
379
  /* continue to find the bound for empty event skipping */
380
0
  skip_bound = NULL;
381
0
  for (; cur != NULL; cur = cur->parent) {
382
0
    if (cur->sent_to_stats_id == 0 &&
383
0
        (!array_is_created(&cur->fields) ||
384
0
         array_is_empty(&cur->fields)) &&
385
0
        (!array_is_created(&cur->categories) ||
386
0
         array_is_empty(&cur->categories)))
387
0
      continue;
388
389
0
    skip_bound = cur;
390
0
    break;
391
0
  }
392
393
  /* fast path - no flattening and no skipping to do */
394
0
  if ((event->parent == flatten_bound) &&
395
0
      (event->parent == skip_bound))
396
0
    return event_ref(event);
397
398
0
  new_event = event_dup(event);
399
400
  /* flatten */
401
0
  event_flatten_recurse(new_event, event, flatten_bound);
402
0
  replace_parent_ref(new_event, flatten_bound);
403
404
  /* skip */
405
0
  replace_parent_ref(new_event, skip_bound);
406
407
0
  return new_event;
408
0
}
409
410
static struct event *
411
event_create_internal(struct event *parent, const char *source_filename,
412
          unsigned int source_linenum)
413
518k
{
414
518k
  struct event *event;
415
518k
  pool_t pool = pool_alloconly_create(MEMPOOL_GROWING"event", 1024);
416
417
518k
  event = p_new(pool, struct event, 1);
418
518k
  event->refcount = 1;
419
518k
  event->id = ++event_id_counter;
420
518k
  event->pool = pool;
421
518k
  event->tv_created_ioloop = ioloop_timeval;
422
518k
  event->min_log_level = LOG_TYPE_INFO;
423
518k
  i_gettimeofday(&event->tv_created);
424
518k
  event->source_filename = p_strdup(pool, source_filename);
425
518k
  event->source_linenum = source_linenum;
426
518k
  event->change_id = 1;
427
518k
  if (parent != NULL) {
428
512k
    event->parent = parent;
429
512k
    event_ref(event->parent);
430
512k
    event_copy_parent_defaults(event, parent);
431
512k
  }
432
518k
  DLLIST_PREPEND(&events, event);
433
518k
  return event;
434
518k
}
435
436
#undef event_create
437
struct event *event_create(struct event *parent, const char *source_filename,
438
         unsigned int source_linenum)
439
518k
{
440
518k
  struct event *event;
441
442
518k
  event = event_create_internal(parent, source_filename, source_linenum);
443
518k
  (void)event_call_callbacks_noargs(event, EVENT_CALLBACK_TYPE_CREATE);
444
518k
  return event;
445
518k
}
446
447
#undef event_create_passthrough
448
struct event_passthrough *
449
event_create_passthrough(struct event *parent, const char *source_filename,
450
       unsigned int source_linenum)
451
250k
{
452
250k
  if (!parent->passthrough) {
453
250k
    if (event_last_passthrough != NULL) {
454
      /* API is being used in a wrong or dangerous way */
455
0
      i_panic("Can't create multiple passthrough events - "
456
0
        "finish the earlier event (%s:%d) with ->event()",
457
0
        event_last_passthrough->source_filename,
458
0
        event_last_passthrough->source_linenum);
459
0
    }
460
250k
    struct event *event =
461
250k
      event_create(parent, source_filename, source_linenum);
462
250k
    event->passthrough = TRUE;
463
    /* This event only intends to extend the parent event.
464
       Use the parent's creation timestamp. */
465
250k
    event->tv_created_ioloop = parent->tv_created_ioloop;
466
250k
    event->tv_created = parent->tv_created;
467
250k
    memcpy(&event->ru_last, &parent->ru_last, sizeof(parent->ru_last));
468
250k
    event_last_passthrough = event;
469
250k
  } else {
470
0
    event_last_passthrough = parent;
471
0
  }
472
250k
  return &event_passthrough_vfuncs;
473
250k
}
474
475
struct event *event_ref(struct event *event)
476
512k
{
477
512k
  i_assert(event->refcount > 0);
478
479
512k
  event->refcount++;
480
512k
  return event;
481
512k
}
482
483
void event_unref(struct event **_event)
484
1.04M
{
485
1.04M
  struct event *event = *_event;
486
487
1.04M
  if (event == NULL)
488
18.1k
    return;
489
1.03M
  *_event = NULL;
490
491
1.03M
  i_assert(event->refcount > 0);
492
1.03M
  if (--event->refcount > 0)
493
512k
    return;
494
1.03M
  i_assert(event != current_global_event);
495
496
518k
  event_call_callbacks_noargs(event, EVENT_CALLBACK_TYPE_FREE);
497
498
518k
  if (event_last_passthrough == event)
499
0
    event_last_passthrough = NULL;
500
518k
  if (event->log_prefix_from_system_pool)
501
518k
    i_free(event->log_prefix);
502
518k
  i_free(event->sending_name);
503
518k
  event_unref(&event->parent);
504
505
518k
  DLLIST_REMOVE(&events, event);
506
518k
  pool_unref(&event->pool);
507
518k
}
508
509
struct event *events_get_head(void)
510
0
{
511
0
  return events;
512
0
}
513
514
struct event *event_push_global(struct event *event)
515
0
{
516
0
  i_assert(event != NULL);
517
518
0
  if (current_global_event != NULL) {
519
0
    if (!array_is_created(&global_event_stack))
520
0
      i_array_init(&global_event_stack, 4);
521
0
    array_push_back(&global_event_stack, &current_global_event);
522
0
  }
523
0
  current_global_event = event;
524
0
  return event;
525
0
}
526
527
struct event *event_pop_global(struct event *event)
528
0
{
529
0
  i_assert(event != NULL);
530
0
  i_assert(event == current_global_event);
531
  /* If the active context's root event is popped, we'll assert-crash
532
     later on when deactivating the context and the root event no longer
533
     exists. */
534
0
  i_assert(event != io_loop_get_active_global_root());
535
536
0
  if (!array_is_created(&global_event_stack) ||
537
0
      array_count(&global_event_stack) == 0)
538
0
    current_global_event = NULL;
539
0
  else {
540
0
    unsigned int event_count;
541
0
    struct event *const *events =
542
0
      array_get(&global_event_stack, &event_count);
543
544
0
    i_assert(event_count > 0);
545
0
    current_global_event = events[event_count-1];
546
0
    array_delete(&global_event_stack, event_count-1, 1);
547
0
  }
548
0
  return current_global_event;
549
0
}
550
551
struct event *event_get_global(void)
552
14.8k
{
553
14.8k
  return current_global_event;
554
14.8k
}
555
556
struct event *event_get_global_root(void)
557
0
{
558
0
  if (array_is_created(&global_event_stack) &&
559
0
      array_count(&global_event_stack) > 0) {
560
0
    struct event *const *events =
561
0
      array_front(&global_event_stack);
562
0
    return events[0];
563
0
  }
564
0
  return current_global_event;
565
0
}
566
567
#undef event_reason_begin
568
struct event_reason *
569
event_reason_begin(const char *reason_code, const char *source_filename,
570
       unsigned int source_linenum)
571
0
{
572
0
  struct event_reason *reason;
573
574
0
  reason = i_new(struct event_reason, 1);
575
0
  reason->event = event_create(event_get_global(),
576
0
             source_filename, source_linenum);
577
0
  event_strlist_append(reason->event, EVENT_REASON_CODE, reason_code);
578
0
  event_push_global(reason->event);
579
0
  return reason;
580
0
}
581
582
void event_reason_end(struct event_reason **_reason)
583
25.6k
{
584
25.6k
  struct event_reason *reason = *_reason;
585
586
25.6k
  if (reason == NULL)
587
25.6k
    return;
588
0
  event_pop_global(reason->event);
589
  /* This event was created only for global use. It shouldn't be
590
     permanently stored anywhere. This assert could help catch bugs. */
591
0
  i_assert(reason->event->refcount == 1);
592
0
  event_unref(&reason->event);
593
0
  i_free(reason);
594
0
}
595
596
const char *event_reason_code(const char *module, const char *name)
597
0
{
598
0
  return event_reason_code_prefix(module, "", name);
599
0
}
600
601
static bool event_reason_code_module_validate(const char *module)
602
0
{
603
0
  const char *p;
604
605
0
  for (p = module; *p != '\0'; p++) {
606
0
    if (*p == ' ' || *p == '-' || *p == ':')
607
0
      return FALSE;
608
0
    if (i_isupper(*p))
609
0
      return FALSE;
610
0
  }
611
0
  return TRUE;
612
0
}
613
614
const char *event_reason_code_prefix(const char *module,
615
             const char *name_prefix, const char *name)
616
0
{
617
0
  const char *p;
618
619
0
  i_assert(module[0] != '\0');
620
0
  i_assert(name[0] != '\0');
621
622
0
  if (!event_reason_code_module_validate(module)) {
623
0
    i_panic("event_reason_code_prefix(): "
624
0
      "Invalid module '%s'", module);
625
0
  }
626
0
  if (!event_reason_code_module_validate(name_prefix)) {
627
0
    i_panic("event_reason_code_prefix(): "
628
0
      "Invalid name_prefix '%s'", name_prefix);
629
0
  }
630
631
0
  string_t *str = t_str_new(strlen(module) + 1 +
632
0
          strlen(name_prefix) + strlen(name));
633
0
  str_append(str, module);
634
0
  str_append_c(str, ':');
635
0
  str_append(str, name_prefix);
636
637
0
  for (p = name; *p != '\0'; p++) {
638
0
    switch (*p) {
639
0
    case ' ':
640
0
    case '-':
641
0
      str_append_c(str, '_');
642
0
      break;
643
0
    case ':':
644
0
      i_panic("event_reason_code_prefix(): "
645
0
        "name has ':' (%s, %s%s)",
646
0
        module, name_prefix, name);
647
0
    default:
648
0
      str_append_c(str, i_tolower(*p));
649
0
      break;
650
0
    }
651
0
  }
652
0
  return str_c(str);
653
0
}
654
655
static struct event *
656
event_set_log_prefix(struct event *event, const char *prefix, bool append)
657
277k
{
658
277k
  event->log_prefix_callback = NULL;
659
277k
  event->log_prefix_callback_context = NULL;
660
277k
  if (event->log_prefix == NULL) {
661
    /* allocate the first log prefix from the pool */
662
261k
    event->log_prefix = p_strdup(event->pool, prefix);
663
261k
  } else {
664
    /* log prefix is being updated multiple times -
665
       switch to system pool so we don't keep leaking memory */
666
15.3k
    if (event->log_prefix_from_system_pool)
667
15.3k
      i_free(event->log_prefix);
668
15.3k
    else
669
15.3k
      event->log_prefix_from_system_pool = TRUE;
670
15.3k
    event->log_prefix = i_strdup(prefix);
671
15.3k
  }
672
277k
  event->log_prefix_replace = !append;
673
277k
  return event;
674
277k
}
675
676
struct event *
677
event_set_append_log_prefix(struct event *event, const char *prefix)
678
277k
{
679
277k
  return event_set_log_prefix(event, prefix, TRUE);
680
277k
}
681
682
struct event *event_replace_log_prefix(struct event *event, const char *prefix)
683
0
{
684
0
  return event_set_log_prefix(event, prefix, FALSE);
685
0
}
686
687
struct event *
688
event_drop_parent_log_prefixes(struct event *event, unsigned int count)
689
12.7k
{
690
12.7k
  event->log_prefixes_dropped = count;
691
12.7k
  return event;
692
12.7k
}
693
694
#undef event_set_log_prefix_callback
695
struct event *
696
event_set_log_prefix_callback(struct event *event,
697
            bool replace,
698
            event_log_prefix_callback_t *callback,
699
            void *context)
700
0
{
701
0
  if (event->log_prefix_from_system_pool)
702
0
    i_free(event->log_prefix);
703
0
  else
704
0
    event->log_prefix = NULL;
705
0
  event->log_prefix_replace = replace;
706
0
  event->log_prefix_callback = callback;
707
0
  event->log_prefix_callback_context = context;
708
0
  return event;
709
0
}
710
711
#undef event_set_log_message_callback
712
struct event *
713
event_set_log_message_callback(struct event *event,
714
             event_log_message_callback_t *callback,
715
             void *context)
716
0
{
717
0
  event->log_message_callback = callback;
718
0
  event->log_message_callback_context = context;
719
0
  return event;
720
0
}
721
722
void event_disable_callbacks(struct event *event)
723
0
{
724
0
  event->disable_callbacks = TRUE;
725
0
}
726
727
#undef event_unset_log_message_callback
728
void event_unset_log_message_callback(struct event *event,
729
              event_log_message_callback_t *callback,
730
              void *context)
731
0
{
732
0
  i_assert(event->log_message_callback == callback);
733
0
  i_assert(event->log_message_callback_context == context);
734
735
0
  event->log_message_callback = NULL;
736
0
  event->log_message_callback_context = NULL;
737
0
}
738
739
struct event *
740
event_set_name(struct event *event, const char *name)
741
251k
{
742
251k
  i_free(event->sending_name);
743
251k
  event->sending_name = i_strdup(name);
744
251k
  return event;
745
251k
}
746
747
struct event *
748
event_set_source(struct event *event, const char *filename,
749
     unsigned int linenum, bool literal_fname)
750
0
{
751
0
  if (strcmp(event->source_filename, filename) != 0) {
752
0
    event->source_filename = literal_fname ? filename :
753
0
      p_strdup(event->pool, filename);
754
0
  }
755
0
  event->source_linenum = linenum;
756
0
  return event;
757
0
}
758
759
struct event *event_set_always_log_source(struct event *event)
760
0
{
761
0
  event->always_log_source = TRUE;
762
0
  return event;
763
0
}
764
765
struct event *event_set_min_log_level(struct event *event, enum log_type level)
766
0
{
767
0
  event->min_log_level = level;
768
0
  event_recalculate_debug_level(event);
769
0
  return event;
770
0
}
771
772
enum log_type event_get_min_log_level(const struct event *event)
773
0
{
774
0
  return event->min_log_level;
775
0
}
776
777
struct event *event_set_ptr(struct event *event, const char *key, void *value)
778
0
{
779
0
  struct event_pointer *p;
780
781
0
  if (!array_is_created(&event->pointers))
782
0
    p_array_init(&event->pointers, event->pool, 4);
783
0
  else {
784
    /* replace existing pointer if the key already exists */
785
0
    array_foreach_modifiable(&event->pointers, p) {
786
0
      if (strcmp(p->key, key) == 0) {
787
0
        p->value = value;
788
0
        return event;
789
0
      }
790
0
    }
791
0
  }
792
0
  p = array_append_space(&event->pointers);
793
0
  p->key = p_strdup(event->pool, key);
794
0
  p->value = value;
795
0
  return event;
796
0
}
797
798
void *event_get_ptr(const struct event *event, const char *key)
799
0
{
800
0
  const struct event_pointer *p;
801
802
0
  if (!array_is_created(&event->pointers))
803
0
    return NULL;
804
0
  array_foreach(&event->pointers, p) {
805
0
    if (strcmp(p->key, key) == 0)
806
0
      return p->value;
807
0
  }
808
0
  return NULL;
809
0
}
810
811
struct event_category *event_category_find_registered(const char *name)
812
0
{
813
0
  struct event_category *cat;
814
815
0
  array_foreach_elem(&event_registered_categories_representative, cat) {
816
0
    if (strcmp(cat->name, name) == 0)
817
0
      return cat;
818
0
  }
819
0
  return NULL;
820
0
}
821
822
static struct event_internal_category *
823
event_category_find_internal(const char *name)
824
6.04k
{
825
6.04k
  struct event_internal_category *internal;
826
827
6.04k
  array_foreach_elem(&event_registered_categories_internal, internal) {
828
0
    if (strcmp(internal->name, name) == 0)
829
0
      return internal;
830
0
  }
831
832
6.04k
  return NULL;
833
6.04k
}
834
835
struct event_category *const *
836
event_get_registered_categories(unsigned int *count_r)
837
0
{
838
0
  return array_get(&event_registered_categories_representative, count_r);
839
0
}
840
841
static void
842
event_category_add_to_array(struct event_internal_category *internal)
843
6.04k
{
844
6.04k
  struct event_category *representative = &internal->representative;
845
846
6.04k
  array_push_back(&event_registered_categories_internal, &internal);
847
6.04k
  array_push_back(&event_registered_categories_representative,
848
6.04k
      &representative);
849
6.04k
}
850
851
static struct event_category *
852
event_category_register(struct event_category *category)
853
6.04k
{
854
6.04k
  struct event_internal_category *internal = category->internal;
855
6.04k
  event_category_callback_t *callback;
856
6.04k
  bool allocated;
857
858
6.04k
  if (internal != NULL)
859
0
    return &internal->representative; /* case 2 - see below */
860
861
  /* register parent categories first */
862
6.04k
  if (category->parent != NULL)
863
0
    (void) event_category_register(category->parent);
864
865
  /* There are four cases we need to handle:
866
867
     1) a new category is registered
868
     2) same category struct is re-registered - already handled above
869
        internal NULL check
870
     3) different category struct is registered, but it is identical
871
        to the previously registered one
872
     4) different category struct is registered, and it is different
873
        from the previously registered one - a programming error */
874
6.04k
  internal = event_category_find_internal(category->name);
875
6.04k
  if (internal == NULL) {
876
    /* case 1: first time we saw this name - allocate new */
877
6.04k
    internal = i_new(struct event_internal_category, 1);
878
6.04k
    if (category->parent != NULL)
879
0
      internal->parent = category->parent->internal;
880
6.04k
    internal->name = i_strdup(category->name);
881
6.04k
    internal->refcount = 1;
882
6.04k
    internal->representative.name = internal->name;
883
6.04k
    internal->representative.parent = category->parent;
884
6.04k
    internal->representative.internal = internal;
885
886
6.04k
    event_category_add_to_array(internal);
887
888
6.04k
    allocated = TRUE;
889
6.04k
  } else {
890
    /* case 3 or 4: someone registered this name before - share */
891
0
    if ((category->parent != NULL) &&
892
0
        (internal->parent != category->parent->internal)) {
893
      /* case 4 */
894
0
      struct event_internal_category *other =
895
0
        category->parent->internal;
896
897
0
      i_panic("event category parent mismatch detected: "
898
0
        "category %p internal %p (%s), "
899
0
        "internal parent %p (%s), public parent %p (%s)",
900
0
        category, internal, internal->name,
901
0
        internal->parent, internal->parent->name,
902
0
        other, other->name);
903
0
    }
904
905
0
    internal->refcount++;
906
907
0
    allocated = FALSE;
908
0
  }
909
910
6.04k
  category->internal = internal;
911
6.04k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
912
  /* Record the registered category for later cleanup upon lib_deinit().
913
     For normal operation, calling lib_init() a second time never occurs,
914
     so this cleanup (clearing state) is not necessary normally. However,
915
     some fuzzers rely on fuzzing some sub-system from the very inception
916
     of the Dovecot library, so cycling lib_init() and lib_deinit()
917
     becomes a concern. Sadly, clearing event category state will only
918
     work when that is not located in some loaded plugin module, which
919
     would be unloaded long before lib_deinit(). Since Dovecot supports
920
     loading modules at many subsystems, this would cause crashes during
921
     normal operation. Therefore, the cleanup of category state is omitted
922
     entirely for normal operation and is only compiled in for the benefit
923
     of fuzzing where loading such modules is either omitted or handled
924
     specially. */
925
6.04k
  array_push_back(&event_registered_categories, &category);
926
6.04k
#endif
927
928
6.04k
  if (!allocated) {
929
    /* not the first registration of this category */
930
0
    return &internal->representative;
931
0
  }
932
933
12.0k
  array_foreach_elem(&event_category_callbacks, callback) T_BEGIN {
934
12.0k
    callback(&internal->representative);
935
12.0k
  } T_END;
936
937
6.04k
  return &internal->representative;
938
6.04k
}
939
940
static bool
941
event_find_category(const struct event *event,
942
        const struct event_category *category)
943
6.04k
{
944
6.04k
  struct event_internal_category *internal = category->internal;
945
946
  /* make sure we're always looking for a representative */
947
6.04k
  i_assert(category == &internal->representative);
948
949
6.04k
  return array_lsearch_ptr(&event->categories, category) != NULL;
950
6.04k
}
951
952
struct event *
953
event_add_categories(struct event *event,
954
         struct event_category *const *categories)
955
6.04k
{
956
6.04k
  struct event_category *representative;
957
958
6.04k
  if (!array_is_created(&event->categories))
959
6.04k
    p_array_init(&event->categories, event->pool, 4);
960
961
12.0k
  for (unsigned int i = 0; categories[i] != NULL; i++) {
962
6.04k
    representative = event_category_register(categories[i]);
963
6.04k
    if (!event_find_category(event, representative))
964
6.04k
      array_push_back(&event->categories, &representative);
965
6.04k
  }
966
6.04k
  event_set_changed(event);
967
6.04k
  event_recalculate_debug_level(event);
968
6.04k
  return event;
969
6.04k
}
970
971
struct event *
972
event_add_category(struct event *event, struct event_category *category)
973
6.04k
{
974
6.04k
  struct event_category *const categories[] = { category, NULL };
975
6.04k
  return event_add_categories(event, categories);
976
6.04k
}
977
978
struct event_field *
979
event_find_field_nonrecursive(const struct event *event, const char *key)
980
1.20M
{
981
1.20M
  struct event_field *field;
982
983
1.20M
  if (!array_is_created(&event->fields))
984
415k
    return NULL;
985
986
1.48M
  array_foreach_modifiable(&event->fields, field) {
987
1.48M
    if (strcmp(field->key, key) == 0)
988
25.1k
      return field;
989
1.48M
  }
990
761k
  return NULL;
991
787k
}
992
993
const struct event_field *
994
event_find_field_recursive(const struct event *event, const char *key)
995
14.8k
{
996
14.8k
  const struct event_field *field;
997
998
59.3k
  do {
999
59.3k
    if ((field = event_find_field_nonrecursive(event, key)) != NULL)
1000
0
      return field;
1001
59.3k
    event = event->parent;
1002
59.3k
  } while (event != NULL);
1003
1004
  /* check also the global event and its parents */
1005
14.8k
  event = event_get_global();
1006
14.8k
  while (event != NULL) {
1007
0
    if ((field = event_find_field_nonrecursive(event, key)) != NULL)
1008
0
      return field;
1009
0
    event = event->parent;
1010
0
  }
1011
14.8k
  return NULL;
1012
14.8k
}
1013
1014
static void
1015
event_get_recursive_strlist(const struct event *event, pool_t pool,
1016
          const char *key, ARRAY_TYPE(const_string) *dest)
1017
0
{
1018
0
  const struct event_field *field;
1019
0
  const char *str;
1020
1021
0
  if (event == NULL)
1022
0
    return;
1023
1024
0
  field = event_find_field_nonrecursive(event, key);
1025
0
  if (field != NULL) {
1026
0
    if (field->value_type != EVENT_FIELD_VALUE_TYPE_STRLIST) {
1027
      /* Value type unexpectedly changed. Stop recursing. */
1028
0
      return;
1029
0
    }
1030
0
    array_foreach_elem(&field->value.strlist, str) {
1031
0
      if (array_lsearch(dest, &str, i_strcmp_p) == NULL) {
1032
0
        if (pool != NULL)
1033
0
          str = p_strdup(pool, str);
1034
0
        array_push_back(dest, &str);
1035
0
      }
1036
0
    }
1037
0
  }
1038
0
  event_get_recursive_strlist(event->parent, pool, key, dest);
1039
0
}
1040
1041
const char *
1042
event_find_field_recursive_str(const struct event *event, const char *key)
1043
0
{
1044
0
  const struct event_field *field;
1045
1046
0
  field = event_find_field_recursive(event, key);
1047
0
  if (field == NULL)
1048
0
    return NULL;
1049
1050
0
  switch (field->value_type) {
1051
0
  case EVENT_FIELD_VALUE_TYPE_STR:
1052
0
    return field->value.str;
1053
0
  case EVENT_FIELD_VALUE_TYPE_INTMAX:
1054
0
    return t_strdup_printf("%jd", field->value.intmax);
1055
0
  case EVENT_FIELD_VALUE_TYPE_TIMEVAL:
1056
0
    return t_strdup_printf("%"PRIdTIME_T".%u",
1057
0
      field->value.timeval.tv_sec,
1058
0
      (unsigned int)field->value.timeval.tv_usec);
1059
0
  case EVENT_FIELD_VALUE_TYPE_IP:
1060
0
    return net_ip2addr(&field->value.ip);
1061
0
  case EVENT_FIELD_VALUE_TYPE_STRLIST: {
1062
0
    ARRAY_TYPE(const_string) list;
1063
0
    t_array_init(&list, 8);
1064
    /* This is a bit different, because it needs to be merging
1065
       all of the parent events' and global events' lists
1066
       together. */
1067
0
    event_get_recursive_strlist(event, NULL, key, &list);
1068
0
    event_get_recursive_strlist(event_get_global(), NULL,
1069
0
              key, &list);
1070
0
    return t_array_const_string_join(&list, ",");
1071
0
  }
1072
0
  }
1073
0
  i_unreached();
1074
0
}
1075
1076
static struct event_field *
1077
event_get_field(struct event *event, const char *key, bool clear)
1078
1.14M
{
1079
1.14M
  struct event_field *field;
1080
1081
1.14M
  field = event_find_field_nonrecursive(event, key);
1082
1.14M
  if (field == NULL) {
1083
1.11M
    if (!array_is_created(&event->fields))
1084
1.11M
      p_array_init(&event->fields, event->pool, 8);
1085
1.11M
    field = array_append_space(&event->fields);
1086
1.11M
    field->key = p_strdup(event->pool, key);
1087
1.11M
  } else if (clear) {
1088
25.1k
    i_zero(&field->value);
1089
25.1k
  }
1090
1.14M
  event_set_changed(event);
1091
1.14M
  return field;
1092
1.14M
}
1093
1094
struct event *
1095
event_add_str(struct event *event, const char *key, const char *value)
1096
712k
{
1097
712k
  struct event_field *field;
1098
1099
712k
  if (value == NULL) {
1100
    /* Silently ignoring is perhaps better than assert-crashing?
1101
       However, if the field already exists, this should be the
1102
       same as event_field_clear() */
1103
14.8k
    if (event_find_field_recursive(event, key) == NULL)
1104
14.8k
      return event;
1105
0
    value = "";
1106
0
  }
1107
1108
698k
  field = event_get_field(event, key, TRUE);
1109
698k
  field->value_type = EVENT_FIELD_VALUE_TYPE_STR;
1110
698k
  field->value.str = p_strdup(event->pool, value);
1111
698k
  return event;
1112
712k
}
1113
1114
struct event *
1115
event_strlist_append(struct event *event, const char *key, const char *value)
1116
0
{
1117
0
  struct event_field *field = event_get_field(event, key, FALSE);
1118
1119
0
  if (field->value_type != EVENT_FIELD_VALUE_TYPE_STRLIST ||
1120
0
      !array_is_created(&field->value.strlist)) {
1121
0
    field->value_type = EVENT_FIELD_VALUE_TYPE_STRLIST;
1122
0
    p_array_init(&field->value.strlist, event->pool, 1);
1123
0
  }
1124
1125
  /* lets not add empty values there though */
1126
0
  if (value == NULL)
1127
0
    return event;
1128
1129
0
  const char *str = p_strdup(event->pool, value);
1130
0
  if (array_lsearch(&field->value.strlist, &str, i_strcmp_p) == NULL)
1131
0
    array_push_back(&field->value.strlist, &str);
1132
0
  return event;
1133
0
}
1134
1135
struct event *
1136
event_strlist_replace(struct event *event, const char *key,
1137
          const char *const *values, unsigned int count)
1138
0
{
1139
0
  struct event_field *field = event_get_field(event, key, TRUE);
1140
0
  field->value_type = EVENT_FIELD_VALUE_TYPE_STRLIST;
1141
1142
0
  for (unsigned int i = 0; i < count; i++)
1143
0
    event_strlist_append(event, key, values[i]);
1144
0
  return event;
1145
0
}
1146
1147
struct event *
1148
event_strlist_copy_recursive(struct event *dest, const struct event *src,
1149
           const char *key)
1150
0
{
1151
0
  event_strlist_append(dest, key, NULL);
1152
0
  struct event_field *field = event_get_field(dest, key, FALSE);
1153
0
  i_assert(field != NULL);
1154
0
  event_get_recursive_strlist(src, dest->pool, key,
1155
0
            &field->value.strlist);
1156
0
  return dest;
1157
0
}
1158
1159
struct event *
1160
event_add_int(struct event *event, const char *key, intmax_t num)
1161
445k
{
1162
445k
  struct event_field *field;
1163
1164
445k
  field = event_get_field(event, key, TRUE);
1165
445k
  field->value_type = EVENT_FIELD_VALUE_TYPE_INTMAX;
1166
445k
  field->value.intmax = num;
1167
445k
  return event;
1168
445k
}
1169
1170
struct event *
1171
event_add_int_nonzero(struct event *event, const char *key, intmax_t num)
1172
0
{
1173
0
  if (num != 0)
1174
0
    return event_add_int(event, key, num);
1175
0
  return event;
1176
0
}
1177
1178
struct event *
1179
event_inc_int(struct event *event, const char *key, intmax_t num)
1180
0
{
1181
0
  struct event_field *field;
1182
1183
0
  field = event_find_field_nonrecursive(event, key);
1184
0
  if (field == NULL || field->value_type != EVENT_FIELD_VALUE_TYPE_INTMAX)
1185
0
    return event_add_int(event, key, num);
1186
1187
0
  field->value.intmax += num;
1188
0
  event_set_changed(event);
1189
0
  return event;
1190
0
}
1191
1192
struct event *
1193
event_add_timeval(struct event *event, const char *key,
1194
      const struct timeval *tv)
1195
0
{
1196
0
  struct event_field *field;
1197
1198
0
  field = event_get_field(event, key, TRUE);
1199
0
  field->value_type = EVENT_FIELD_VALUE_TYPE_TIMEVAL;
1200
0
  field->value.timeval = *tv;
1201
0
  return event;
1202
0
}
1203
1204
struct event *
1205
event_add_ip(struct event *event, const char *key, const struct ip_addr *ip)
1206
0
{
1207
0
  struct event_field *field;
1208
1209
0
  if (ip->family == 0) {
1210
    /* ignore nonexistent IP (similar to
1211
       event_add_str(value=NULL)) */
1212
0
    if (event_find_field_recursive(event, key) != NULL)
1213
0
      event_field_clear(event, key);
1214
0
    return event;
1215
0
  }
1216
1217
0
  field = event_get_field(event, key, TRUE);
1218
0
  field->value_type = EVENT_FIELD_VALUE_TYPE_IP;
1219
0
  field->value.ip = *ip;
1220
0
  return event;
1221
0
}
1222
1223
struct event *
1224
event_add_fields(struct event *event,
1225
     const struct event_add_field *fields)
1226
0
{
1227
0
  for (unsigned int i = 0; fields[i].key != NULL; i++) {
1228
0
    if (fields[i].value != NULL)
1229
0
      event_add_str(event, fields[i].key, fields[i].value);
1230
0
    else if (fields[i].value_timeval.tv_sec != 0) {
1231
0
      event_add_timeval(event, fields[i].key,
1232
0
            &fields[i].value_timeval);
1233
0
    } else if (fields[i].value_ip.family != 0) {
1234
0
      event_add_ip(event, fields[i].key, &fields[i].value_ip);
1235
0
    } else {
1236
0
      event_add_int(event, fields[i].key,
1237
0
              fields[i].value_intmax);
1238
0
    }
1239
0
  }
1240
0
  return event;
1241
0
}
1242
1243
void event_field_clear(struct event *event, const char *key)
1244
0
{
1245
0
  event_add_str(event, key, "");
1246
0
}
1247
1248
struct event *event_get_parent(const struct event *event)
1249
0
{
1250
0
  return event->parent;
1251
0
}
1252
1253
pool_t event_get_pool(const struct event *event)
1254
0
{
1255
0
  return event->pool;
1256
0
}
1257
1258
void event_get_create_time(const struct event *event, struct timeval *tv_r)
1259
0
{
1260
0
  *tv_r = event->tv_created;
1261
0
}
1262
1263
bool event_get_last_send_time(const struct event *event, struct timeval *tv_r)
1264
0
{
1265
0
  *tv_r = event->tv_last_sent;
1266
0
  return tv_r->tv_sec != 0;
1267
0
}
1268
1269
void event_get_last_duration(const struct event *event,
1270
           uintmax_t *duration_usecs_r)
1271
0
{
1272
0
  if (event->tv_last_sent.tv_sec == 0) {
1273
0
    *duration_usecs_r = 0;
1274
0
    return;
1275
0
  }
1276
0
  long long diff = timeval_diff_usecs(&event->tv_last_sent,
1277
0
              &event->tv_created);
1278
0
  i_assert(diff >= 0);
1279
0
  *duration_usecs_r = diff;
1280
0
}
1281
1282
const struct event_field *
1283
event_get_fields(const struct event *event, unsigned int *count_r)
1284
0
{
1285
0
  if (!array_is_created(&event->fields)) {
1286
0
    *count_r = 0;
1287
0
    return NULL;
1288
0
  }
1289
0
  return array_get(&event->fields, count_r);
1290
0
}
1291
1292
struct event_category *const *
1293
event_get_categories(const struct event *event, unsigned int *count_r)
1294
0
{
1295
0
  if (!array_is_created(&event->categories)) {
1296
0
    *count_r = 0;
1297
0
    return NULL;
1298
0
  }
1299
0
  return array_get(&event->categories, count_r);
1300
0
}
1301
1302
static void
1303
insert_category(HASH_TABLE_TYPE(category_set) hash,
1304
    const struct event_category *const cat)
1305
0
{
1306
  /* insert this category (key == the unique internal pointer) */
1307
0
  hash_table_update(hash, cat->internal, cat);
1308
1309
  /* insert parent's categories */
1310
0
  if (cat->parent != NULL)
1311
0
    insert_category(hash, cat->parent);
1312
0
}
1313
1314
struct event_category_iterator *
1315
event_categories_iterate_init(const struct event *event)
1316
0
{
1317
0
  struct event_category_iterator *iter;
1318
0
  struct event_category *const *cats;
1319
0
  unsigned int count, i;
1320
1321
0
  cats = event_get_categories(event, &count);
1322
0
  if (count == 0)
1323
0
    return NULL;
1324
1325
0
  iter = i_new(struct event_category_iterator, 1);
1326
1327
0
  hash_table_create_direct(&iter->hash, default_pool,
1328
0
         3 * count /* estimate */);
1329
1330
  /* Insert all the categories into the hash table */
1331
0
  for (i = 0; i < count; i++)
1332
0
    insert_category(iter->hash, cats[i]);
1333
1334
0
  iter->iter = hash_table_iterate_init(iter->hash);
1335
1336
0
  return iter;
1337
0
}
1338
1339
bool event_categories_iterate(struct event_category_iterator *iter,
1340
            const struct event_category **cat_r)
1341
0
{
1342
0
  void *key ATTR_UNUSED;
1343
1344
0
  if (iter == NULL) {
1345
0
    *cat_r = NULL;
1346
0
    return FALSE;
1347
0
  }
1348
0
  return hash_table_iterate(iter->iter, iter->hash, &key, cat_r);
1349
0
}
1350
1351
void event_categories_iterate_deinit(struct event_category_iterator **_iter)
1352
0
{
1353
0
  struct event_category_iterator *iter = *_iter;
1354
1355
0
  if (iter == NULL)
1356
0
    return;
1357
0
  *_iter = NULL;
1358
1359
0
  hash_table_iterate_deinit(&iter->iter);
1360
0
  hash_table_destroy(&iter->hash);
1361
0
  i_free(iter);
1362
0
}
1363
1364
void event_send(struct event *event, struct failure_context *ctx,
1365
    const char *fmt, ...)
1366
0
{
1367
0
  va_list args;
1368
1369
0
  va_start(args, fmt);
1370
0
  event_vsend(event, ctx, fmt, args);
1371
0
  va_end(args);
1372
0
}
1373
1374
void event_vsend(struct event *event, struct failure_context *ctx,
1375
     const char *fmt, va_list args)
1376
0
{
1377
0
  i_gettimeofday(&event->tv_last_sent);
1378
1379
  /* Skip adding user_cpu_usecs if not enabled. */
1380
0
  if (event->ru_last.ru_utime.tv_sec != 0 ||
1381
0
      event->ru_last.ru_utime.tv_usec != 0) {
1382
0
    struct rusage ru_current;
1383
0
    get_self_rusage(&ru_current);
1384
0
    long long udiff = timeval_diff_usecs(&ru_current.ru_utime,
1385
0
                 &event->ru_last.ru_utime);
1386
0
    event_add_int(event, "user_cpu_usecs", udiff > 0 ? udiff : 0);
1387
0
  }
1388
0
  if (event_call_callbacks(event, EVENT_CALLBACK_TYPE_SEND,
1389
0
         ctx, fmt, args)) {
1390
0
    if (ctx->type != LOG_TYPE_DEBUG ||
1391
0
        event->sending_debug_log)
1392
0
      i_log_typev(ctx, fmt, args);
1393
0
  }
1394
0
  event_send_abort(event);
1395
0
}
1396
1397
void event_send_abort(struct event *event)
1398
1.92M
{
1399
  /* if the event is sent again, it needs a new name */
1400
1.92M
  i_free(event->sending_name);
1401
1.92M
  if (event->passthrough)
1402
250k
    event_unref(&event);
1403
1.92M
}
1404
1405
static void
1406
event_export_field_value(string_t *dest, const struct event_field *field)
1407
0
{
1408
0
  switch (field->value_type) {
1409
0
  case EVENT_FIELD_VALUE_TYPE_STR:
1410
0
    str_append_c(dest, EVENT_CODE_FIELD_STR);
1411
0
    str_append_tabescaped(dest, field->key);
1412
0
    str_append_c(dest, '\t');
1413
0
    str_append_tabescaped(dest, field->value.str);
1414
0
    break;
1415
0
  case EVENT_FIELD_VALUE_TYPE_INTMAX:
1416
0
    str_append_c(dest, EVENT_CODE_FIELD_INTMAX);
1417
0
    str_append_tabescaped(dest, field->key);
1418
0
    str_printfa(dest, "\t%jd", field->value.intmax);
1419
0
    break;
1420
0
  case EVENT_FIELD_VALUE_TYPE_TIMEVAL:
1421
0
    str_append_c(dest, EVENT_CODE_FIELD_TIMEVAL);
1422
0
    str_append_tabescaped(dest, field->key);
1423
0
    str_printfa(dest, "\t%"PRIdTIME_T"\t%u",
1424
0
          field->value.timeval.tv_sec,
1425
0
          (unsigned int)field->value.timeval.tv_usec);
1426
0
    break;
1427
0
  case EVENT_FIELD_VALUE_TYPE_IP:
1428
0
    str_append_c(dest, EVENT_CODE_FIELD_IP);
1429
0
    str_append_tabescaped(dest, field->key);
1430
0
    str_printfa(dest, "\t%s", net_ip2addr(&field->value.ip));
1431
0
    break;
1432
0
  case EVENT_FIELD_VALUE_TYPE_STRLIST: {
1433
0
    unsigned int count;
1434
0
    const char *const *strlist =
1435
0
      array_get(&field->value.strlist, &count);
1436
0
    str_append_c(dest, EVENT_CODE_FIELD_STRLIST);
1437
0
    str_append_tabescaped(dest, field->key);
1438
0
    str_printfa(dest, "\t%u", count);
1439
0
    for (unsigned int i = 0; i < count; i++) {
1440
0
      str_append_c(dest, '\t');
1441
0
      str_append_tabescaped(dest, strlist[i]);
1442
0
    }
1443
0
  }
1444
0
  }
1445
0
}
1446
1447
void event_export(const struct event *event, string_t *dest)
1448
0
{
1449
  /* required fields: */
1450
0
  str_printfa(dest, "%"PRIdTIME_T"\t%u",
1451
0
        event->tv_created.tv_sec,
1452
0
        (unsigned int)event->tv_created.tv_usec);
1453
1454
  /* optional fields: */
1455
0
  if (event->source_filename != NULL) {
1456
0
    str_append_c(dest, '\t');
1457
0
    str_append_c(dest, EVENT_CODE_SOURCE);
1458
0
    str_append_tabescaped(dest, event->source_filename);
1459
0
    str_printfa(dest, "\t%u", event->source_linenum);
1460
0
  }
1461
0
  if (event->always_log_source) {
1462
0
    str_append_c(dest, '\t');
1463
0
    str_append_c(dest, EVENT_CODE_ALWAYS_LOG_SOURCE);
1464
0
  }
1465
0
  if (event->tv_last_sent.tv_sec != 0) {
1466
0
    str_printfa(dest, "\t%c%"PRIdTIME_T"\t%u",
1467
0
          EVENT_CODE_TV_LAST_SENT,
1468
0
          event->tv_last_sent.tv_sec,
1469
0
          (unsigned int)event->tv_last_sent.tv_usec);
1470
0
  }
1471
0
  if (event->sending_name != NULL) {
1472
0
    str_append_c(dest, '\t');
1473
0
    str_append_c(dest, EVENT_CODE_SENDING_NAME);
1474
0
    str_append_tabescaped(dest, event->sending_name);
1475
0
  }
1476
1477
0
  if (array_is_created(&event->categories)) {
1478
0
    struct event_category *cat;
1479
0
    array_foreach_elem(&event->categories, cat) {
1480
0
      str_append_c(dest, '\t');
1481
0
      str_append_c(dest, EVENT_CODE_CATEGORY);
1482
0
      str_append_tabescaped(dest, cat->name);
1483
0
    }
1484
0
  }
1485
1486
0
  if (array_is_created(&event->fields)) {
1487
0
    const struct event_field *field;
1488
0
    array_foreach(&event->fields, field) {
1489
0
      str_append_c(dest, '\t');
1490
0
      event_export_field_value(dest, field);
1491
0
    }
1492
0
  }
1493
0
}
1494
1495
bool event_import(struct event *event, const char *str, const char **error_r)
1496
0
{
1497
0
  return event_import_unescaped(event, t_strsplit_tabescaped(str),
1498
0
              error_r);
1499
0
}
1500
1501
static bool event_import_tv(const char *arg_secs, const char *arg_usecs,
1502
          struct timeval *tv_r, const char **error_r)
1503
0
{
1504
0
  unsigned int usecs;
1505
1506
0
  if (str_to_time(arg_secs, &tv_r->tv_sec) < 0) {
1507
0
    *error_r = "Invalid timeval seconds parameter";
1508
0
    return FALSE;
1509
0
  }
1510
1511
0
  if (arg_usecs == NULL) {
1512
0
    *error_r = "Timeval missing microseconds parameter";
1513
0
    return FALSE;
1514
0
  }
1515
0
  if (str_to_uint(arg_usecs, &usecs) < 0 || usecs >= 1000000) {
1516
0
    *error_r = "Invalid timeval microseconds parameter";
1517
0
    return FALSE;
1518
0
  }
1519
0
  tv_r->tv_usec = usecs;
1520
0
  return TRUE;
1521
0
}
1522
1523
static bool
1524
event_import_strlist(struct event *event, struct event_field *field,
1525
         const char *const **_args, const char **error_r)
1526
0
{
1527
0
  const char *const *args = *_args;
1528
0
  unsigned int count, i;
1529
1530
0
  field->value_type = EVENT_FIELD_VALUE_TYPE_STRLIST;
1531
0
  if (str_to_uint(args[0], &count) < 0) {
1532
0
    *error_r = t_strdup_printf("Field '%s' has invalid count: '%s'",
1533
0
             field->key, args[0]);
1534
0
    return FALSE;
1535
0
  }
1536
0
  p_array_init(&field->value.strlist, event->pool, count);
1537
0
  for (i = 1; i <= count && args[i] != NULL; i++) {
1538
0
    const char *str = p_strdup(event->pool, args[i]);
1539
0
    array_push_back(&field->value.strlist, &str);
1540
0
  }
1541
0
  if (i < count) {
1542
0
    *error_r = t_strdup_printf("Field '%s' has too few values",
1543
0
             field->key);
1544
0
    return FALSE;
1545
0
  }
1546
0
  *_args += count;
1547
0
  return TRUE;
1548
0
}
1549
1550
static bool
1551
event_import_field(struct event *event, enum event_code code, const char *arg,
1552
       const char *const **_args, const char **error_r)
1553
0
{
1554
0
  const char *const *args = *_args;
1555
0
  const char *error;
1556
1557
0
  if (*arg == '\0') {
1558
0
    *error_r = "Field name is missing";
1559
0
    return FALSE;
1560
0
  }
1561
0
  struct event_field *field = event_get_field(event, arg, TRUE);
1562
0
  if (args[0] == NULL) {
1563
0
    *error_r = "Field value is missing";
1564
0
    return FALSE;
1565
0
  }
1566
0
  switch (code) {
1567
0
  case EVENT_CODE_FIELD_INTMAX:
1568
0
    field->value_type = EVENT_FIELD_VALUE_TYPE_INTMAX;
1569
0
    if (str_to_intmax(*args, &field->value.intmax) < 0) {
1570
0
      *error_r = t_strdup_printf(
1571
0
        "Invalid field value '%s' number for '%s'",
1572
0
        *args, field->key);
1573
0
      return FALSE;
1574
0
    }
1575
0
    break;
1576
0
  case EVENT_CODE_FIELD_STR:
1577
0
    if (field->value_type == EVENT_FIELD_VALUE_TYPE_STR &&
1578
0
        null_strcmp(field->value.str, *args) == 0) {
1579
      /* already identical value */
1580
0
      break;
1581
0
    }
1582
0
    field->value_type = EVENT_FIELD_VALUE_TYPE_STR;
1583
0
    field->value.str = p_strdup(event->pool, *args);
1584
0
    break;
1585
0
  case EVENT_CODE_FIELD_TIMEVAL:
1586
0
    field->value_type = EVENT_FIELD_VALUE_TYPE_TIMEVAL;
1587
0
    if (!event_import_tv(args[0], args[1],
1588
0
             &field->value.timeval, &error)) {
1589
0
      *error_r = t_strdup_printf("Field '%s' value '%s': %s",
1590
0
               field->key, args[1], error);
1591
0
      return FALSE;
1592
0
    }
1593
0
    args++;
1594
0
    break;
1595
0
  case EVENT_CODE_FIELD_IP:
1596
0
    field->value_type = EVENT_FIELD_VALUE_TYPE_IP;
1597
0
    if (net_addr2ip(*args, &field->value.ip) < 0) {
1598
0
      *error_r = t_strdup_printf(
1599
0
        "Invalid field value '%s' IP for '%s'",
1600
0
        *args, field->key);
1601
0
      return FALSE;
1602
0
    }
1603
0
    break;
1604
0
  case EVENT_CODE_FIELD_STRLIST:
1605
0
    if (!event_import_strlist(event, field, &args, error_r))
1606
0
      return FALSE;
1607
0
    break;
1608
0
  default:
1609
0
    i_unreached();
1610
0
  }
1611
0
  *_args = args;
1612
0
  return TRUE;
1613
0
}
1614
1615
1616
static bool
1617
event_import_arg(struct event *event, const char *const **_args,
1618
     const char **error_r)
1619
0
{
1620
0
  const char *const *args = *_args;
1621
0
  const char *error, *arg = *args;
1622
0
  enum event_code code = arg[0];
1623
1624
0
  arg++;
1625
0
  switch (code) {
1626
0
  case EVENT_CODE_ALWAYS_LOG_SOURCE:
1627
0
    event->always_log_source = TRUE;
1628
0
    break;
1629
0
  case EVENT_CODE_CATEGORY: {
1630
0
    struct event_category *category =
1631
0
      event_category_find_registered(arg);
1632
0
    if (category == NULL) {
1633
0
      *error_r = t_strdup_printf(
1634
0
        "Unregistered category: '%s'", arg);
1635
0
      return FALSE;
1636
0
    }
1637
0
    if (!array_is_created(&event->categories))
1638
0
      p_array_init(&event->categories, event->pool, 4);
1639
0
    if (!event_find_category(event, category))
1640
0
      array_push_back(&event->categories, &category);
1641
0
    break;
1642
0
  }
1643
0
  case EVENT_CODE_TV_LAST_SENT:
1644
0
    if (!event_import_tv(arg, args[1], &event->tv_last_sent,
1645
0
             &error)) {
1646
0
      *error_r = t_strdup_printf(
1647
0
        "Invalid tv_last_sent: %s", error);
1648
0
      return FALSE;
1649
0
    }
1650
0
    args++;
1651
0
    break;
1652
0
  case EVENT_CODE_SENDING_NAME:
1653
0
    i_free(event->sending_name);
1654
0
    event->sending_name = i_strdup(arg);
1655
0
    break;
1656
0
  case EVENT_CODE_SOURCE: {
1657
0
    unsigned int linenum;
1658
1659
0
    if (args[1] == NULL) {
1660
0
      *error_r = "Source line number missing";
1661
0
      return FALSE;
1662
0
    }
1663
0
    if (str_to_uint(args[1], &linenum) < 0) {
1664
0
      *error_r = "Invalid Source line number";
1665
0
      return FALSE;
1666
0
    }
1667
0
    event_set_source(event, arg, linenum, FALSE);
1668
0
    args++;
1669
0
    break;
1670
0
  }
1671
0
  case EVENT_CODE_FIELD_INTMAX:
1672
0
  case EVENT_CODE_FIELD_STR:
1673
0
  case EVENT_CODE_FIELD_STRLIST:
1674
0
  case EVENT_CODE_FIELD_TIMEVAL:
1675
0
  case EVENT_CODE_FIELD_IP: {
1676
0
    args++;
1677
0
    if (!event_import_field(event, code, arg, &args, error_r))
1678
0
      return FALSE;
1679
0
    break;
1680
0
  }
1681
0
  }
1682
0
  *_args = args;
1683
0
  return TRUE;
1684
0
}
1685
1686
bool event_import_unescaped(struct event *event, const char *const *args,
1687
          const char **error_r)
1688
0
{
1689
0
  const char *error;
1690
1691
  /* Event's create callback has already added service:<name> category.
1692
     This imported event may be coming from another service process
1693
     though, so clear it out. */
1694
0
  if (array_is_created(&event->categories))
1695
0
    array_clear(&event->categories);
1696
1697
  /* required fields: */
1698
0
  if (args[0] == NULL) {
1699
0
    *error_r = "Missing required fields";
1700
0
    return FALSE;
1701
0
  }
1702
0
  if (!event_import_tv(args[0], args[1], &event->tv_created, &error)) {
1703
0
    *error_r = t_strdup_printf("Invalid tv_created: %s", error);
1704
0
    return FALSE;
1705
0
  }
1706
0
  args += 2;
1707
1708
  /* optional fields: */
1709
0
  while (*args != NULL) {
1710
0
    if (!event_import_arg(event, &args, error_r))
1711
0
      return FALSE;
1712
0
    args++;
1713
0
  }
1714
0
  return TRUE;
1715
0
}
1716
1717
void event_register_callback(event_callback_t *callback)
1718
0
{
1719
0
  array_push_back(&event_handlers, &callback);
1720
0
}
1721
1722
void event_register_callback_prepend(event_callback_t *callback)
1723
0
{
1724
0
  array_push_front(&event_handlers, &callback);
1725
0
}
1726
1727
void event_unregister_callback(event_callback_t *callback)
1728
0
{
1729
0
  unsigned int idx;
1730
1731
0
  if (!array_lsearch_ptr_idx(&event_handlers, callback, &idx))
1732
0
    i_unreached();
1733
0
  array_delete(&event_handlers, idx, 1);
1734
0
}
1735
1736
void event_category_register_callback(event_category_callback_t *callback)
1737
6.04k
{
1738
6.04k
  array_push_back(&event_category_callbacks, &callback);
1739
6.04k
}
1740
1741
void event_category_unregister_callback(event_category_callback_t *callback)
1742
6.04k
{
1743
6.04k
  unsigned int idx;
1744
1745
6.04k
  if (!array_lsearch_ptr_idx(&event_category_callbacks, callback, &idx))
1746
0
    i_unreached();
1747
6.04k
  array_delete(&event_category_callbacks, idx, 1);
1748
6.04k
}
1749
1750
static struct event_passthrough *
1751
event_passthrough_set_append_log_prefix(const char *prefix)
1752
0
{
1753
0
  event_set_append_log_prefix(last_passthrough_event(), prefix);
1754
0
  return &event_passthrough_vfuncs;
1755
0
}
1756
1757
static struct event_passthrough *
1758
event_passthrough_replace_log_prefix(const char *prefix)
1759
0
{
1760
0
  event_replace_log_prefix(last_passthrough_event(), prefix);
1761
0
  return &event_passthrough_vfuncs;
1762
0
}
1763
1764
static struct event_passthrough *
1765
event_passthrough_set_name(const char *name)
1766
250k
{
1767
250k
  event_set_name(last_passthrough_event(), name);
1768
250k
  return &event_passthrough_vfuncs;
1769
250k
}
1770
1771
static struct event_passthrough *
1772
event_passthrough_set_source(const char *filename,
1773
           unsigned int linenum, bool literal_fname)
1774
0
{
1775
0
  event_set_source(last_passthrough_event(), filename,
1776
0
       linenum, literal_fname);
1777
0
  return &event_passthrough_vfuncs;
1778
0
}
1779
1780
static struct event_passthrough *
1781
event_passthrough_set_always_log_source(void)
1782
0
{
1783
0
  event_set_always_log_source(last_passthrough_event());
1784
0
  return &event_passthrough_vfuncs;
1785
0
}
1786
1787
static struct event_passthrough *
1788
event_passthrough_add_categories(struct event_category *const *categories)
1789
0
{
1790
0
  event_add_categories(last_passthrough_event(), categories);
1791
0
  return &event_passthrough_vfuncs;
1792
0
}
1793
1794
static struct event_passthrough *
1795
event_passthrough_add_category(struct event_category *category)
1796
0
{
1797
0
  event_add_category(last_passthrough_event(), category);
1798
0
  return &event_passthrough_vfuncs;
1799
0
}
1800
1801
static struct event_passthrough *
1802
event_passthrough_add_fields(const struct event_add_field *fields)
1803
0
{
1804
0
  event_add_fields(last_passthrough_event(), fields);
1805
0
  return &event_passthrough_vfuncs;
1806
0
}
1807
1808
static struct event_passthrough *
1809
event_passthrough_add_str(const char *key, const char *value)
1810
216k
{
1811
216k
  event_add_str(last_passthrough_event(), key, value);
1812
216k
  return &event_passthrough_vfuncs;
1813
216k
}
1814
1815
static struct event_passthrough *
1816
event_passthrough_strlist_append(const char *key, const char *value)
1817
0
{
1818
0
  event_strlist_append(last_passthrough_event(), key, value);
1819
0
  return &event_passthrough_vfuncs;
1820
0
}
1821
1822
static struct event_passthrough *
1823
event_passthrough_strlist_replace(const char *key, const char *const *values,
1824
          unsigned int count)
1825
0
{
1826
0
  event_strlist_replace(last_passthrough_event(), key, values, count);
1827
0
  return &event_passthrough_vfuncs;
1828
0
}
1829
1830
static struct event_passthrough *
1831
event_passthrough_add_int(const char *key, intmax_t num)
1832
155k
{
1833
155k
  event_add_int(last_passthrough_event(), key, num);
1834
155k
  return &event_passthrough_vfuncs;
1835
155k
}
1836
1837
static struct event_passthrough *
1838
event_passthrough_add_int_nonzero(const char *key, intmax_t num)
1839
0
{
1840
0
  event_add_int_nonzero(last_passthrough_event(), key, num);
1841
0
  return &event_passthrough_vfuncs;
1842
0
}
1843
1844
static struct event_passthrough *
1845
event_passthrough_add_timeval(const char *key, const struct timeval *tv)
1846
0
{
1847
0
  event_add_timeval(last_passthrough_event(), key, tv);
1848
0
  return &event_passthrough_vfuncs;
1849
0
}
1850
1851
static struct event_passthrough *
1852
event_passthrough_add_ip(const char *key, const struct ip_addr *ip)
1853
0
{
1854
0
  event_add_ip(last_passthrough_event(), key, ip);
1855
0
  return &event_passthrough_vfuncs;
1856
0
}
1857
1858
static struct event_passthrough *
1859
event_passthrough_inc_int(const char *key, intmax_t num)
1860
0
{
1861
0
  event_inc_int(last_passthrough_event(), key, num);
1862
0
  return &event_passthrough_vfuncs;
1863
0
}
1864
1865
static struct event_passthrough *
1866
event_passthrough_clear_field(const char *key)
1867
0
{
1868
0
  event_field_clear(last_passthrough_event(), key);
1869
0
  return &event_passthrough_vfuncs;
1870
0
}
1871
1872
static struct event *event_passthrough_event(void)
1873
250k
{
1874
250k
  struct event *event = last_passthrough_event();
1875
250k
  event_last_passthrough = NULL;
1876
250k
  return event;
1877
250k
}
1878
1879
struct event_passthrough event_passthrough_vfuncs = {
1880
  .append_log_prefix = event_passthrough_set_append_log_prefix,
1881
  .replace_log_prefix = event_passthrough_replace_log_prefix,
1882
  .set_name = event_passthrough_set_name,
1883
  .set_source = event_passthrough_set_source,
1884
  .set_always_log_source = event_passthrough_set_always_log_source,
1885
  .add_categories = event_passthrough_add_categories,
1886
  .add_category = event_passthrough_add_category,
1887
  .add_fields = event_passthrough_add_fields,
1888
  .add_str = event_passthrough_add_str,
1889
  .add_int = event_passthrough_add_int,
1890
  .add_int_nonzero = event_passthrough_add_int_nonzero,
1891
  .add_timeval = event_passthrough_add_timeval,
1892
  .add_ip = event_passthrough_add_ip,
1893
  .inc_int = event_passthrough_inc_int,
1894
  .strlist_append = event_passthrough_strlist_append,
1895
  .strlist_replace = event_passthrough_strlist_replace,
1896
  .clear_field = event_passthrough_clear_field,
1897
  .event = event_passthrough_event,
1898
};
1899
1900
void event_enable_user_cpu_usecs(struct event *event)
1901
0
{
1902
0
  get_self_rusage(&event->ru_last);
1903
0
}
1904
1905
void lib_event_init(void)
1906
6.04k
{
1907
6.04k
  events = NULL;
1908
6.04k
  current_global_event = NULL;
1909
6.04k
  event_last_passthrough = NULL;
1910
6.04k
  event_id_counter = 0;
1911
1912
6.04k
  i_array_init(&event_handlers, 4);
1913
6.04k
  i_array_init(&event_category_callbacks, 4);
1914
6.04k
  i_array_init(&event_registered_categories_internal, 16);
1915
6.04k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1916
  /* Refer to earlier commment in event_category_register(). */
1917
6.04k
  i_array_init(&event_registered_categories, 32);
1918
6.04k
#endif
1919
6.04k
  i_array_init(&event_registered_categories_representative, 16);
1920
6.04k
  event_log_init();
1921
6.04k
}
1922
1923
void lib_event_deinit(void)
1924
6.04k
{
1925
6.04k
  struct event_internal_category *internal;
1926
6.04k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1927
6.04k
  struct event_category *category;
1928
6.04k
#endif
1929
1930
6.04k
  event_log_deinit();
1931
6.04k
  event_unset_global_debug_log_filter();
1932
6.04k
  event_unset_global_core_log_filter();
1933
6.04k
  for (struct event *event = events; event != NULL; event = event->next) {
1934
0
    i_warning("Event %p leaked (parent=%p): %s:%u",
1935
0
        event, event->parent,
1936
0
        event->source_filename, event->source_linenum);
1937
0
  }
1938
  /* categories cannot be unregistered, so just free them here */
1939
6.04k
  array_foreach_elem(&event_registered_categories_internal, internal) {
1940
6.04k
    i_free(internal->name);
1941
6.04k
    i_free(internal);
1942
6.04k
  }
1943
6.04k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1944
  /* Refer to earlier commment in event_category_register(). */
1945
6.04k
  array_foreach_elem(&event_registered_categories, category)
1946
6.04k
    category->internal = NULL;
1947
6.04k
  array_free(&event_registered_categories);
1948
6.04k
#endif
1949
6.04k
  array_free(&event_handlers);
1950
6.04k
  array_free(&event_category_callbacks);
1951
6.04k
  array_free(&event_registered_categories_internal);
1952
6.04k
  array_free(&event_registered_categories_representative);
1953
6.04k
  array_free(&global_event_stack);
1954
6.04k
}