+++ /dev/null
-/* 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;
-}
};
struct event_filter_query_internal {
- enum event_filter_log_type log_type_mask;
struct event_filter_node *expr;
void *context;
};
}
}
+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)
{
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)
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);
}
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 *
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;
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)
{
}
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:
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, ')');
}
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
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);