bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenextern const struct event_passthrough event_passthrough_vfuncs;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic struct event *current_global_event = NULL;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic struct event_passthrough *event_last_passthrough = NULL;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic ARRAY(event_callback_t *) event_handlers;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic ARRAY(event_category_callback_t *) event_category_callbacks;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic ARRAY(struct event_category *) event_registered_categories;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic ARRAY(struct event *) global_event_stack;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic struct event *last_passthrough_event(void)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic void event_copy_parent_defaults(struct event *event,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event->always_log_source = parent->always_log_source;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstruct event *event_create(struct event *parent, const char *source_filename,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen pool_t pool = pool_alloconly_create(MEMPOOL_GROWING"event", 64);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event->event_passthrough = event_passthrough_vfuncs;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_create_passthrough(struct event *parent, const char *source_filename,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen /* API is being used in a wrong or dangerous way */
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen i_panic("Can't create multiple passthrough events - finish the earlier with ->event()");
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_create(parent, source_filename, source_linenum);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen /* This event only intends to extend the parent event.
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen Use the parent's creation timestamp. */
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_last_passthrough = &event->event_passthrough;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_last_passthrough = &parent->event_passthrough;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_send_callbacks(struct event *event, enum event_callback_type type,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen struct failure_context *ctx, const char *fmt, va_list args)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen ret = (*callbackp)(event, type, ctx, fmt, args);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen /* event sending was stopped */
ec61c407caf0ade72e3c8ad881cc445653ccfa1fTimo Sirainenstatic void event_send_free(struct event *event, ...)
ec61c407caf0ade72e3c8ad881cc445653ccfa1fTimo Sirainen /* the args are empty and not used for anything, but there doesn't seem
ec61c407caf0ade72e3c8ad881cc445653ccfa1fTimo Sirainen to be any nice and standard way of passing an initialized va_list
ec61c407caf0ade72e3c8ad881cc445653ccfa1fTimo Sirainen as a parameter without va_start(). */
ec61c407caf0ade72e3c8ad881cc445653ccfa1fTimo Sirainen (void)event_send_callbacks(event, EVENT_CALLBACK_TYPE_FREE,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstruct event *event_push_global(struct event *event)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_append(&global_event_stack, ¤t_global_event, 1);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstruct event *event_pop_global(struct event *event)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_delete(&global_event_stack, event_count-1, 1);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic struct event *
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_set_log_prefix(struct event *event, const char *prefix, bool append)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen /* allocate the first log prefix from the pool */
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event->log_prefix = p_strdup(event->pool, prefix);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen /* log prefix is being updated multiple times -
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen switch to system pool so we don't keep leaking memory */
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_set_append_log_prefix(struct event *event, const char *prefix)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen return event_set_log_prefix(event, prefix, TRUE);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstruct event *event_replace_log_prefix(struct event *event, const char *prefix)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen return event_set_log_prefix(event, prefix, FALSE);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_set_name(struct event *event, const char *name)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_set_source(struct event *event, const char *filename,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event->source_filename = literal_fname ? filename :
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstruct event *event_set_always_log_source(struct event *event)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstruct event_category *event_category_find_registered(const char *name)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach(&event_registered_categories, catp) {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_get_registered_categories(unsigned int *count_r)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen return array_get(&event_registered_categories, count_r);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic void event_category_register(struct event_category *category)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen /* register parent categories first */
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_append(&event_registered_categories, &category, 1);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach(&event_category_callbacks, callbackp) T_BEGIN {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_find_category(struct event *event, const struct event_category *category)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach(&event->categories, categoryp) {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen p_array_init(&event->categories, event->pool, 4);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen for (unsigned int i = 0; categories[i] != NULL; i++) {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen if (!event_find_category(event, categories[i]))
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_append(&event->categories, &categories[i], 1);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_add_category(struct event *event, struct event_category *category)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen struct event_category *const categories[] = { category, NULL };
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen return event_add_categories(event, categories);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic struct event_field *
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_find_field_int(struct event *event, const char *key)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach_modifiable(&event->fields, field) {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_find_field(struct event *event, const char *key)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen const struct event_field *field = event_find_field_int(event, key);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_find_field_str(struct event *event, const char *key)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic struct event_field *
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_get_field(struct event *event, const char *key)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_add_str(struct event *event, const char *key, const char *value)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen field->value_type = EVENT_FIELD_VALUE_TYPE_STR;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen field->value.str = p_strdup(event->pool, value);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_add_int(struct event *event, const char *key, intmax_t num)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen field->value_type = EVENT_FIELD_VALUE_TYPE_INTMAX;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_add_timeval(struct event *event, const char *key,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen field->value_type = EVENT_FIELD_VALUE_TYPE_TIMEVAL;
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen for (unsigned int i = 0; fields[i].key != NULL; i++) {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_add_str(event, fields[i].key, fields[i].value);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_add_timeval(event, fields[i].key, &fields[i].value_timeval);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_add_int(event, fields[i].key, fields[i].value_intmax);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstruct event *event_get_parent(struct event *event)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenvoid event_get_create_time(struct event *event, struct timeval *tv_r)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenbool event_get_last_send_time(struct event *event, struct timeval *tv_r)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_get_fields(struct event *event, unsigned int *count_r)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_get_categories(struct event *event, unsigned int *count_r)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen return array_get(&event->categories, count_r);
ddd5a0932db39f7336123f5089fdd77f6a38bce6Timo Sirainenvoid event_send(struct event *event, struct failure_context *ctx,
ddd5a0932db39f7336123f5089fdd77f6a38bce6Timo Sirainen const char *fmt, ...)
68f5234e8c0a79b72546e1b1013aae4b86f394a4Timo Sirainenvoid event_vsend(struct event *event, struct failure_context *ctx,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen if (event_send_callbacks(event, EVENT_CALLBACK_TYPE_EVENT,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen /* if the event is sent again, it needs a new name */
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainenevent_export_field_value(string_t *dest, const struct event_field *field)
918384d458981e7db4aadc283212eb19912d5709Timo Sirainen str_append_tabescaped(dest, field->value.str);
918384d458981e7db4aadc283212eb19912d5709Timo Sirainen str_printfa(dest, "\t%jd", field->value.intmax);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainenvoid event_export(const struct event *event, string_t *dest)
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen /* required fields: */
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen /* optional fields: */
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen str_append_tabescaped(dest, event->source_filename);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen str_printfa(dest, "\t%u", event->source_linenum);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen str_append_c(dest, EVENT_CODE_ALWAYS_LOG_SOURCE);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen str_append_tabescaped(dest, event->sending_name);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainenbool event_import(struct event *event, const char *str, const char **error_r)
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen return event_import_unescaped(event, t_strsplit_tabescaped(str), error_r);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainenstatic bool event_import_tv(const char *arg_secs, const char *arg_usecs,
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen if (str_to_time(arg_secs, &tv_r->tv_sec) < 0) {
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen *error_r = "Invalid timeval seconds parameter";
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen *error_r = "Timeval missing microseconds parameter";
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen if (str_to_uint(arg_usecs, &usecs) < 0 || usecs >= 1000000) {
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen *error_r = "Invalid timeval microseconds parameter";
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainenbool event_import_unescaped(struct event *event, const char *const *args,
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen const char **error_r)
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen /* required fields: */
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen if (!event_import_tv(args[0], args[1], &event->tv_created, &error)) {
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen *error_r = t_strdup_printf("Invalid tv_created: %s", error);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen /* optional fields: */
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen *error_r = t_strdup_printf("Unregistered category: '%s'", arg);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen p_array_init(&event->categories, event->pool, 4);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen array_append(&event->categories, &category, 1);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen if (!event_import_tv(arg, args[1], &event->tv_last_sent, &error)) {
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen *error_r = t_strdup_printf("Invalid tv_last_sent: %s", error);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen event->source_filename = p_strdup(event->pool, arg);
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen if (str_to_uint(args[1], &event->source_linenum) < 0) {
918384d458981e7db4aadc283212eb19912d5709Timo Sirainen field->value_type = EVENT_FIELD_VALUE_TYPE_INTMAX;
918384d458981e7db4aadc283212eb19912d5709Timo Sirainen if (str_to_intmax(*args, &field->value.intmax) < 0) {
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen "Invalid field value '%s' number for '%s'",
918384d458981e7db4aadc283212eb19912d5709Timo Sirainen field->value_type = EVENT_FIELD_VALUE_TYPE_STR;
918384d458981e7db4aadc283212eb19912d5709Timo Sirainen field->value.str = p_strdup(event->pool, *args);
918384d458981e7db4aadc283212eb19912d5709Timo Sirainen field->value_type = EVENT_FIELD_VALUE_TYPE_TIMEVAL;
53ea4ad7b37e6c78a834151cdc30374316da4e34Timo Sirainen "Field '%s' value '%s': %s",
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenvoid event_register_callback(event_callback_t *callback)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenvoid event_unregister_callback(event_callback_t *callback)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach_idx(&event_handlers, callbackp), 1);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenvoid event_category_register_callback(event_category_callback_t *callback)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_append(&event_category_callbacks, &callback, 1);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenvoid event_category_unregister_callback(event_category_callback_t *callback)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach(&event_category_callbacks, callbackp) {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach_idx(&event_category_callbacks, callbackp), 1);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic void event_category_remove_from_array(struct event_category *category)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach(&event_registered_categories, catp) {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach_idx(&event_registered_categories, catp), 1);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenvoid event_category_unregister(struct event_category *category)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen /* it was never registered in the first place - ignore */
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen array_foreach(&event_category_callbacks, callbackp) T_BEGIN {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_passthrough_set_append_log_prefix(const char *prefix)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_set_append_log_prefix(last_passthrough_event(), prefix);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_passthrough_replace_log_prefix(const char *prefix)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_replace_log_prefix(last_passthrough_event(), prefix);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_set_name(last_passthrough_event(), name);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_passthrough_set_source(const char *filename,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_set_source(last_passthrough_event(), filename,
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_set_always_log_source(last_passthrough_event());
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_passthrough_add_categories(struct event_category *const *categories)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_add_categories(last_passthrough_event(), categories);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_passthrough_add_category(struct event_category *category)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_add_category(last_passthrough_event(), category);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_passthrough_add_fields(const struct event_add_field *fields)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_add_fields(last_passthrough_event(), fields);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_passthrough_add_str(const char *key, const char *value)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_add_str(last_passthrough_event(), key, value);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_passthrough_add_int(const char *key, intmax_t num)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_add_int(last_passthrough_event(), key, num);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenevent_passthrough_add_timeval(const char *key, const struct timeval *tv)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen event_add_timeval(last_passthrough_event(), key, tv);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenstatic struct event *event_passthrough_event(void)
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen struct event *event = last_passthrough_event();
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainenconst struct event_passthrough event_passthrough_vfuncs = {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen i_array_init(&event_registered_categories, 16);
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen for (struct event *event = events; event != NULL; event = event->next) {
b8a1347a9ea2cecf0d16f24748c2600fea8c7158Timo Sirainen i_warning("Event %p leaked (parent=%p): %s:%u",