From: Josef 'Jeff' Sipek Date: Tue, 26 May 2020 15:35:38 +0000 (-0400) Subject: lib: Switch event filtering to the new filter language X-Git-Tag: 2.3.13~489 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fcba1d530737813de11d9d730d5db6496e2e574b;p=thirdparty%2Fdovecot%2Fcore.git lib: Switch event filtering to the new filter language --- diff --git a/src/lib-master/stats-client.c b/src/lib-master/stats-client.c index 281b876b09..489b0e1c59 100644 --- a/src/lib-master/stats-client.c +++ b/src/lib-master/stats-client.c @@ -40,7 +40,7 @@ client_handshake_filter(const char *const *args, struct event_filter **filter_r, } *filter_r = event_filter_create(); - if (!event_filter_import_unescaped(*filter_r, args+1, error_r)) { + if (!event_filter_import(*filter_r, t_str_tabunescape(args[1]), error_r)) { event_filter_unref(filter_r); return -1; } diff --git a/src/lib-master/test-event-stats.c b/src/lib-master/test-event-stats.c index b0b9837525..3699a363b9 100644 --- a/src/lib-master/test-event-stats.c +++ b/src/lib-master/test-event-stats.c @@ -158,7 +158,8 @@ static void stats_conn_input(struct connection *_conn) struct ostream *stats_data_out; struct server_connection *conn = (struct server_connection *)_conn; const char *handshake = "VERSION\tstats-server\t3\t0\n" - "FILTER\tctest1\t\tctest2\t\tctest3\t\tctest4\t\tctest5\t\n"; + "FILTER\tcategory=test1 OR category=test2 OR category=test3 OR " + "category=test4 OR category=test5\n"; const char *line = NULL; if (!conn->handshake_sent) { conn->handshake_sent = TRUE; diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index be675f1a24..150327c376 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -55,7 +55,6 @@ liblib_la_SOURCES = \ eacces-error.c \ env-util.c \ event-filter.c \ - event-filter-parser.c \ event-filter-parser-lexer.l \ event-filter-parser-parser.y \ event-log.c \ diff --git a/src/lib/event-filter-parser.c b/src/lib/event-filter-parser.c deleted file mode 100644 index 6c7b3aa459..0000000000 --- a/src/lib/event-filter-parser.c +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "array.h" -#include "lib-event-private.h" -#include "event-filter.h" - -static void add_category(ARRAY_TYPE(const_string) *categories, const char *name) -{ - if (!array_is_created(categories)) - t_array_init(categories, 4); - array_push_back(categories, &name); -} - -static int parse_query(const char *str, struct event_filter_query *query_r, - const char **error_r) -{ - ARRAY_TYPE(const_string) categories = ARRAY_INIT; - ARRAY(struct event_filter_field) fields = ARRAY_INIT; - - i_zero(query_r); - do { - while (*str == ' ') - str++; - const char *p = strchr(str, ' '); - if (p != NULL) - str = t_strdup_until(str, p++); - - if (strncmp(str, "event:", 6) == 0) { - query_r->name = str+6; - } else if (strncmp(str, "source:", 7) == 0) { - const char *linep = strchr(str+7, ':'); - if (linep == NULL) { - /* filename only - match to all line numbers */ - query_r->source_filename = str+7; - } else { - query_r->source_filename = t_strdup_until(str+7, linep); - if (str_to_uint(linep+1, &query_r->source_linenum) < 0) { - *error_r = t_strdup_printf( - "Invalid line number in '%s'", str); - return -1; - } - } - } else if (strncmp(str, "field:", 6) == 0) { - const char *value = strchr(str+6, '='); - if (value == NULL) { - *error_r = t_strdup_printf( - "Missing '=' in '%s'", str); - return -1; - } - if (!array_is_created(&fields)) - t_array_init(&fields, 4); - struct event_filter_field *field = - array_append_space(&fields); - field->key = t_strdup_until(str+6, value); - field->value = value+1; - } else if (str_begins(str, "cat:")) - add_category(&categories, str+4); - else if (str_begins(str, "category:")) - add_category(&categories, str+9); - else if (str_begins(str, "service:")) { - /* service:name is short for category:service:name */ - add_category(&categories, str); - } else { - *error_r = t_strdup_printf("Unknown event '%s'", str); - return -1; - } - str = p; - } while (str != NULL); - - if (array_is_created(&categories)) { - array_append_zero(&categories); - query_r->categories = array_front(&categories); - } - if (array_is_created(&fields)) { - array_append_zero(&fields); - query_r->fields = array_front(&fields); - } - return 0; -} - -int event_filter_parse(const char *str, struct event_filter *filter, - const char **error_r) -{ - struct event_filter_query query; - const char *p; - - while (*str != '\0') { - if (*str == ' ') { - str++; - continue; - } - - if (*str == '(') { - /* everything inside (...) is a single query */ - str++; - p = strchr(str, ')'); - if (p == NULL) { - *error_r = "Missing ')'"; - return -1; - } - if (parse_query(t_strdup_until(str, p), &query, error_r) < 0) - return -1; - str = p+1; - } else if ((p = strchr(str, ' ')) != NULL) { - /* parse a single-word query in the middle */ - if (parse_query(t_strdup_until(str, p), &query, error_r) < 0) - return -1; - str = p+1; - } else { - /* single-word last query */ - if (parse_query(str, &query, error_r) < 0) - return -1; - str = ""; - } - - event_filter_add(filter, &query); - } - - *error_r = NULL; - - return 0; -} diff --git a/src/lib/event-filter.c b/src/lib/event-filter.c index b1ba2c827b..46882e0811 100644 --- a/src/lib/event-filter.c +++ b/src/lib/event-filter.c @@ -35,7 +35,6 @@ static const struct log_type_map { }; struct event_filter_query_internal { - enum event_filter_log_type log_type_mask; struct event_filter_node *expr; void *context; }; @@ -104,6 +103,51 @@ void event_filter_unref(struct event_filter **_filter) } } +int event_filter_parse(const char *str, struct event_filter *filter, + const char **error_r) +{ + struct event_filter_query_internal *int_query; + struct event_filter_parser_state state; + int ret; + + i_zero(&state); + state.input = str; + state.len = strlen(str); + state.pos = 0; + state.pool = filter->pool; + + event_filter_parser_lex_init(&state.scanner); + event_filter_parser_set_extra(&state, state.scanner); + + ret = event_filter_parser_parse(&state); + + event_filter_parser_lex_destroy(state.scanner); + + if ((ret == 0) && (state.output != NULL)) { + /* success - non-NULL expression */ + i_assert(state.error == NULL); + + int_query = array_append_space(&filter->queries); + int_query->context = NULL; + int_query->expr = state.output; + + filter->named_queries_only = filter->named_queries_only && state.has_event_name; + } else if (ret != 0) { + /* error */ + i_assert(state.output == NULL); + i_assert(state.error != NULL); + + *error_r = state.error; + } + + /* + * Note that success with a NULL expression output is possible, but + * turns into a no-op. + */ + + return (ret != 0) ? -1 : 0; +} + bool event_filter_category_to_log_type(const char *name, enum event_filter_log_type *log_type_r) { @@ -155,7 +199,6 @@ event_filter_add_categories(pool_t pool, const char *const *categories) { unsigned int categories_count = str_array_length(categories); - enum event_filter_log_type log_type; unsigned int i; if (categories_count == 0) @@ -164,16 +207,13 @@ event_filter_add_categories(pool_t pool, for (i = 0; i < categories_count; i++) { struct event_filter_node *node; - if (event_filter_category_to_log_type(categories[i], &log_type)) { - int_query->log_type_mask |= log_type; - continue; - } - node = p_new(pool, struct event_filter_node, 1); node->type = EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY; node->op = EVENT_FILTER_OP_CMP_EQ; - node->category.name = p_strdup(pool, categories[i]); - node->category.ptr = event_category_find_registered(categories[i]); + if (!event_filter_category_to_log_type(categories[i], &node->category.log_type)) { + node->category.name = p_strdup(pool, categories[i]); + node->category.ptr = event_category_find_registered(categories[i]); + } add_node(pool, &int_query->expr, node); } @@ -246,11 +286,6 @@ void event_filter_add(struct event_filter *filter, event_filter_add_categories(filter->pool, int_query, query->categories); event_filter_add_fields(filter->pool, int_query, query->fields); - - if (int_query->log_type_mask == 0) { - /* no explicit log types given. default to all. */ - int_query->log_type_mask = EVENT_FILTER_LOG_TYPE_ALL; - } } static struct event_filter_node * @@ -291,7 +326,6 @@ event_filter_merge_with_context_internal(struct event_filter *dest, struct event_filter_query_internal *new; new = array_append_space(&dest->queries); - new->log_type_mask = int_query->log_type_mask; new->expr = clone_expr(dest->pool, int_query->expr); new->context = with_context ? new_context : int_query->context; } T_END; @@ -310,46 +344,6 @@ void event_filter_merge_with_context(struct event_filter *dest, event_filter_merge_with_context_internal(dest, src, new_context, TRUE); } -static void -event_filter_export_query_expr(const struct event_filter_query_internal *query, - struct event_filter_node *node, - string_t *dest) -{ - if (node == NULL) - return; - - switch (node->type) { - case EVENT_FILTER_NODE_TYPE_LOGIC: - /* currently only AND is supported */ - i_assert(node->op == EVENT_FILTER_OP_AND); - event_filter_export_query_expr(query, node->children[0], dest); - event_filter_export_query_expr(query, node->children[1], dest); - break; - case EVENT_FILTER_NODE_TYPE_EVENT_NAME: - str_append_c(dest, EVENT_FILTER_CODE_NAME); - str_append_tabescaped(dest, node->str); - str_append_c(dest, '\t'); - break; - case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION: - str_append_c(dest, EVENT_FILTER_CODE_SOURCE); - str_append_tabescaped(dest, node->str); - str_printfa(dest, "\t%ju\t", node->intmax); - break; - case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY: - str_append_c(dest, EVENT_FILTER_CODE_CATEGORY); - str_append_tabescaped(dest, node->category.name); - str_append_c(dest, '\t'); - break; - case EVENT_FILTER_NODE_TYPE_EVENT_FIELD: - str_append_c(dest, EVENT_FILTER_CODE_FIELD); - str_append_tabescaped(dest, node->field.key); - str_append_c(dest, '\t'); - str_append_tabescaped(dest, node->field.value.str); - str_append_c(dest, '\t'); - break; - } -} - static const char * event_filter_export_query_expr_op(enum event_filter_node_op op) { @@ -374,27 +368,27 @@ event_filter_export_query_expr_op(enum event_filter_node_op op) } static void -event_filter_export_query_expr_new(const struct event_filter_query_internal *query, - struct event_filter_node *node, - string_t *dest) +event_filter_export_query_expr(const struct event_filter_query_internal *query, + struct event_filter_node *node, + string_t *dest) { switch (node->type) { case EVENT_FILTER_NODE_TYPE_LOGIC: str_append_c(dest, '('); switch (node->op) { case EVENT_FILTER_OP_AND: - event_filter_export_query_expr_new(query, node->children[0], dest); + event_filter_export_query_expr(query, node->children[0], dest); str_append(dest, " AND "); - event_filter_export_query_expr_new(query, node->children[1], dest); + event_filter_export_query_expr(query, node->children[1], dest); break; case EVENT_FILTER_OP_OR: - event_filter_export_query_expr_new(query, node->children[0], dest); + event_filter_export_query_expr(query, node->children[0], dest); str_append(dest, " OR "); - event_filter_export_query_expr_new(query, node->children[1], dest); + event_filter_export_query_expr(query, node->children[1], dest); break; case EVENT_FILTER_OP_NOT: str_append(dest, "NOT "); - event_filter_export_query_expr_new(query, node->children[0], dest); + event_filter_export_query_expr(query, node->children[0], dest); break; case EVENT_FILTER_OP_CMP_EQ: case EVENT_FILTER_OP_CMP_GT: @@ -446,28 +440,9 @@ event_filter_export_query_expr_new(const struct event_filter_query_internal *que static void event_filter_export_query(const struct event_filter_query_internal *query, string_t *dest) -{ - unsigned int i; - - event_filter_export_query_expr(query, query->expr, dest); - - if (query->log_type_mask != EVENT_FILTER_LOG_TYPE_ALL) { - for (i = 0; i < N_ELEMENTS(event_filter_log_type_map); i++) { - if ((query->log_type_mask & event_filter_log_type_map[i].log_type) == 0) - continue; - str_append_c(dest, EVENT_FILTER_CODE_CATEGORY); - str_append_tabescaped(dest, event_filter_log_type_map[i].name); - str_append_c(dest, '\t'); - } - } -} - -static void -event_filter_export_query_new(const struct event_filter_query_internal *query, - string_t *dest) { str_append_c(dest, '('); - event_filter_export_query_expr_new(query, query->expr, dest); + event_filter_export_query_expr(query, query->expr, dest); str_append_c(dest, ')'); } @@ -476,112 +451,12 @@ void event_filter_export(struct event_filter *filter, string_t *dest) const struct event_filter_query_internal *query; bool first = TRUE; - array_foreach(&filter->queries, query) { - if (!first) - str_append_c(dest, '\t'); - first = FALSE; - event_filter_export_query(query, dest); - } -} - -void event_filter_export_new(struct event_filter *filter, string_t *dest) -{ - const struct event_filter_query_internal *query; - bool first = TRUE; - array_foreach(&filter->queries, query) { if (!first) str_append(dest, " OR "); first = FALSE; - event_filter_export_query_new(query, dest); - } -} - -bool event_filter_import(struct event_filter *filter, const char *str, - const char **error_r) -{ - return event_filter_import_unescaped(filter, - t_strsplit_tabescaped(str), error_r); -} - -bool event_filter_import_unescaped(struct event_filter *filter, - const char *const *args, - const char **error_r) -{ - struct event_filter_query query; - ARRAY_TYPE(const_string) categories; - ARRAY(struct event_filter_field) fields; - bool changed; - - t_array_init(&categories, 8); - t_array_init(&fields, 8); - i_zero(&query); - changed = FALSE; - for (unsigned int i = 0; args[i] != NULL; i++) { - const char *arg = args[i]; - - if (arg[0] == '\0') { - /* finish the query */ - if (array_count(&categories) > 0) { - array_append_zero(&categories); - query.categories = array_front(&categories); - } - if (array_count(&fields) > 0) { - array_append_zero(&fields); - query.fields = array_front(&fields); - } - event_filter_add(filter, &query); - - /* start the next query */ - i_zero(&query); - array_clear(&categories); - array_clear(&fields); - changed = FALSE; - continue; - } - - enum event_filter_code code = arg[0]; - arg++; - switch (code) { - case EVENT_FILTER_CODE_NAME: - query.name = arg; - break; - case EVENT_FILTER_CODE_SOURCE: - query.source_filename = arg; - i++; - if (args[i] == NULL) { - *error_r = "Source line number missing"; - return FALSE; - } - if (str_to_uint(args[i], &query.source_linenum) < 0) { - *error_r = "Invalid Source line number"; - return FALSE; - } - break; - case EVENT_FILTER_CODE_CATEGORY: - array_push_back(&categories, &arg); - break; - case EVENT_FILTER_CODE_FIELD: { - struct event_filter_field *field; - - field = array_append_space(&fields); - field->key = arg; - i++; - if (args[i] == NULL) { - *error_r = "Field value missing"; - return FALSE; - } - field->value = args[i]; - break; - } - } - changed = TRUE; - } - if (changed) { - *error_r = "Expected TAB at the end"; - return FALSE; + event_filter_export_query(query, dest); } - return TRUE; } static bool @@ -764,13 +639,6 @@ event_filter_query_match(const struct event_filter_query_internal *query, i_assert(ctx->type < N_ELEMENTS(event_filter_log_type_map)); log_type = event_filter_log_type_map[ctx->type].log_type; - if ((query->log_type_mask & log_type) == 0) - return FALSE; - - /* Nothing to evaluate - this can happen if the filter consists - solely of log types (e.g., cat:debug) */ - if (query->expr == NULL) - return TRUE; return event_filter_query_match_eval(query->expr, event, source_filename, source_linenum, log_type); diff --git a/src/lib/event-filter.h b/src/lib/event-filter.h index edc48a8771..b402ea2100 100644 --- a/src/lib/event-filter.h +++ b/src/lib/event-filter.h @@ -45,20 +45,13 @@ void event_filter_merge_with_context(struct event_filter *dest, const struct event_filter *src, void *new_context); -/* Export the filter into a tabescaped string, so its fields are separated - with TABs and there are no NUL, CR or LF characters. The context pointers - aren't exported. */ +/* Export the filter into a string. The context pointers aren't exported. */ void event_filter_export(struct event_filter *filter, string_t *dest); /* Add queries to the filter from the given string. The string is expected to be generated by event_filter_export(). Returns TRUE on success, FALSE on invalid string. */ -bool event_filter_import(struct event_filter *filter, const char *str, - const char **error_r); -/* Same as event_filter_import(), but string is already split into an array - of strings via *_strsplit_tabescaped(). */ -bool event_filter_import_unescaped(struct event_filter *filter, - const char *const *args, - const char **error_r); +#define event_filter_import(filter, str, error_r) \ + (event_filter_parse((str), (filter), (error_r)) == 0) /* Parse a string-ified query, filling the passed in filter */ int event_filter_parse(const char *str, struct event_filter *filter, diff --git a/src/stats/client-writer.c b/src/stats/client-writer.c index c0e790237f..0fc80c2f3a 100644 --- a/src/stats/client-writer.c +++ b/src/stats/client-writer.c @@ -5,6 +5,7 @@ #include "llist.h" #include "hash.h" #include "str.h" +#include "strescape.h" #include "lib-event-private.h" #include "event-filter.h" #include "ostream.h" @@ -33,10 +34,13 @@ static struct connection_list *writer_clients = NULL; static void client_writer_send_handshake(struct writer_client *client) { + string_t *filter = t_str_new(128); string_t *str = t_str_new(128); + event_filter_export(stats_metrics_get_event_filter(stats_metrics), filter); + str_append(str, "FILTER\t"); - event_filter_export(stats_metrics_get_event_filter(stats_metrics), str); + str_append_tabescaped(str, str_c(filter)); str_append_c(str, '\n'); o_stream_nsend(client->conn.output, str_data(str), str_len(str)); } diff --git a/src/stats/test-client-reader.c b/src/stats/test-client-reader.c index bf5a15471f..37cfe43b15 100644 --- a/src/stats/test-client-reader.c +++ b/src/stats/test-client-reader.c @@ -47,7 +47,7 @@ bool test_stats_callback(struct event *event, static const char *settings_blob_1 = "metric=test\n" "metric/test/metric_name=test\n" -"metric/test/filter=event:test\n" +"metric/test/filter=event=test\n" "\n"; static int test_reader_server_input_args(struct connection *conn ATTR_UNUSED, @@ -126,7 +126,7 @@ static void test_client_reader(void) static const char *settings_blob_2 = "metric=test\n" "metric/test/metric_name=test\n" -"metric/test/filter=event:test\n" +"metric/test/filter=event=test\n" "metric/test/group_by=test_name\n" "\n"; diff --git a/src/stats/test-client-writer.c b/src/stats/test-client-writer.c index 743846a9fe..608febdbca 100644 --- a/src/stats/test-client-writer.c +++ b/src/stats/test-client-writer.c @@ -20,7 +20,7 @@ static int test_writer_server_input_args(struct connection *conn, { /* check filter */ test_assert_strcmp(args[0], "FILTER"); - test_assert_strcmp(args[1], "ntest"); + test_assert_strcmp(args[1], "(event=\"test\")"); /* send commands now */ string_t *send_buf = t_str_new(128); o_stream_nsend_str(conn->output, "CATEGORY\ttest\n"); @@ -100,7 +100,7 @@ bool test_stats_callback(struct event *event, static const char *settings_blob_1 = "metric=test\n" "metric/test/metric_name=test\n" -"metric/test/filter=event:test\n" +"metric/test/filter=event=test\n" "\n"; static void test_client_writer(void) diff --git a/src/stats/test-stats-metrics.c b/src/stats/test-stats-metrics.c index bb1232e823..c384014b63 100644 --- a/src/stats/test-stats-metrics.c +++ b/src/stats/test-stats-metrics.c @@ -20,7 +20,7 @@ bool test_stats_callback(struct event *event, static const char *settings_blob_1 = "metric=test\n" "metric/test/metric_name=test\n" -"metric/test/filter=event:test\n" +"metric/test/filter=event=test\n" "\n"; static void test_stats_metrics(void) @@ -47,7 +47,7 @@ static void test_stats_metrics(void) static const char *settings_blob_2 = "metric=test\n" "metric/test/metric_name=test\n" -"metric/test/filter=(event:test field:test_field=value)\n" +"metric/test/filter=(event=test AND test_field=value)\n" "\n"; static void test_stats_metrics_filter(void) @@ -61,7 +61,7 @@ static void test_stats_metrics_filter(void) stats_metrics_get_event_filter(stats_metrics); string_t *str_filter = t_str_new(64); event_filter_export(filter, str_filter); - test_assert_strcmp("ntest ftest_field value ", + test_assert_strcmp("((event=\"test\" AND \"test_field\"=\"value\"))", str_c(str_filter)); /* send event */ @@ -167,7 +167,7 @@ static void test_stats_metrics_group_by_discrete_real(const struct discrete_test test_init(t_strdup_printf("metric=test\n" "metric/test/metric_name=test\n" - "metric/test/filter=event:test\n" + "metric/test/filter=event=test\n" "metric/test/group_by=%s\n" "\n", test->settings_blob)); @@ -343,7 +343,7 @@ static void test_stats_metrics_group_by_quantized_real(const struct quantized_te test_init(t_strdup_printf("metric=test\n" "metric/test/metric_name=test\n" - "metric/test/filter=event:test\n" + "metric/test/filter=event=test\n" "metric/test/group_by=test_name foobar:%s\n" "\n", test->settings_blob));