$(top_builddir)/src/common/relayd/librelayd.la \
$(top_builddir)/src/common/testpoint/libtestpoint.la \
$(top_builddir)/src/common/health/libhealth.la \
- $(top_builddir)/src/common/config/libconfig.la
+ $(top_builddir)/src/common/config/libconfig.la \
+ $(top_builddir)/src/common/string-utils/libstring-utils.la
if HAVE_LIBLTTNG_UST_CTL
#include <lttng/condition/condition.h>
#include <lttng/action/action.h>
#include <lttng/channel-internal.h>
+#include <common/string-utils/string-utils.h>
#include "channel.h"
#include "consumer.h"
static pthread_mutex_t relayd_net_seq_idx_lock = PTHREAD_MUTEX_INITIALIZER;
static uint64_t relayd_net_seq_idx;
-static int validate_event_name(const char *);
static int validate_ust_event_name(const char *);
static int cmd_enable_event_internal(struct ltt_session *session,
struct lttng_domain *domain,
DBG("Disable event command for event \'%s\'", event->name);
event_name = event->name;
- if (validate_event_name(event_name)) {
- ret = LTTNG_ERR_INVALID_EVENT_NAME;
- goto error;
- }
/* Error out on unhandled search criteria */
if (event->loglevel_type || event->loglevel != -1 || event->enabled
return ret;
}
-static int validate_event_name(const char *name)
-{
- int ret = 0;
- const char *c = name;
- const char *event_name_end = c + LTTNG_SYMBOL_NAME_LEN;
- bool null_terminated = false;
-
- /*
- * Make sure that unescaped wildcards are only used as the last
- * character of the event name.
- */
- while (c < event_name_end) {
- switch (*c) {
- case '\0':
- null_terminated = true;
- goto end;
- case '\\':
- c++;
- break;
- case '*':
- if ((c + 1) < event_name_end && *(c + 1)) {
- /* Wildcard is not the last character */
- ret = LTTNG_ERR_INVALID_EVENT_NAME;
- goto end;
- }
- default:
- break;
- }
- c++;
- }
-end:
- if (!ret && !null_terminated) {
- ret = LTTNG_ERR_INVALID_EVENT_NAME;
- }
- return ret;
-}
-
static inline bool name_starts_with(const char *name, const char *prefix)
{
const size_t max_cmp_len = min(strlen(prefix), LTTNG_SYMBOL_NAME_LEN);
struct lttng_event_exclusion *exclusion,
int wpipe, bool internal_event)
{
- int ret, channel_created = 0;
+ int ret = 0, channel_created = 0;
struct lttng_channel *attr = NULL;
assert(session);
/* If we have a filter, we must have its filter expression */
assert(!(!!filter_expression ^ !!filter));
- DBG("Enable event command for event \'%s\'", event->name);
+ /* Normalize event name as a globbing pattern */
+ strutils_normalize_star_glob_pattern(event->name);
- rcu_read_lock();
+ /* Normalize exclusion names as globbing patterns */
+ if (exclusion) {
+ size_t i;
- ret = validate_event_name(event->name);
- if (ret) {
- goto error;
+ for (i = 0; i < exclusion->count; i++) {
+ char *name = LTTNG_EVENT_EXCLUSION_NAME_AT(exclusion, i);
+
+ strutils_normalize_star_glob_pattern(name);
+ }
}
+ DBG("Enable event command for event \'%s\'", event->name);
+
+ rcu_read_lock();
+
switch (domain->type) {
case LTTNG_DOMAIN_KERNEL:
{
lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
$(top_builddir)/src/common/libcommon.la \
- $(top_builddir)/src/common/config/libconfig.la
+ $(top_builddir)/src/common/config/libconfig.la \
+ $(top_builddir)/src/common/string-utils/libstring-utils.la
#include <src/common/sessiond-comm/sessiond-comm.h>
#include <common/compat/string.h>
+#include <common/string-utils/string-utils.h>
/* Mi dependancy */
#include <common/mi-lttng.h>
* Mi print exlcusion list
*/
static
-int mi_print_exclusion(int count, char **names)
+int mi_print_exclusion(char **names)
{
int i, ret;
+ int count = names ? strutils_array_of_strings_len(names) : 0;
assert(writer);
* Return allocated string for pretty-printing exclusion names.
*/
static
-char *print_exclusions(int count, char **names)
+char *print_exclusions(char **names)
{
int length = 0;
int i;
const char *preamble = " excluding ";
char *ret;
+ int count = names ? strutils_array_of_strings_len(names) : 0;
if (count == 0) {
return strdup("");
/* calculate total required length */
for (i = 0; i < count; i++) {
- length += strlen(names[i]) + 1;
+ length += strlen(names[i]) + 4;
}
/* add length of preamble + one for NUL - one for last (missing) comma */
length += strlen(preamble);
- ret = zmalloc(length);
+ ret = zmalloc(length + 1);
if (!ret) {
return NULL;
}
strncpy(ret, preamble, length);
for (i = 0; i < count; i++) {
+ strcat(ret, "\"");
strcat(ret, names[i]);
+ strcat(ret, "\"");
if (i != count - 1) {
- strcat(ret, ",");
+ strcat(ret, ", ");
}
}
return ret;
}
-/*
- * Compare list of exclusions against an event name.
- * Return a list of legal exclusion names.
- * Produce an error or a warning about others (depending on the situation)
- */
static
-int check_exclusion_subsets(const char *event_name,
- const char *exclusions,
- int *exclusion_count_ptr,
- char ***exclusion_list_ptr)
+int check_exclusion_subsets(const char *event_name, const char *exclusion)
{
- const char *excluder_ptr;
- const char *event_ptr;
- const char *next_excluder;
- int excluder_length;
- int exclusion_count = 0;
- char **exclusion_list = NULL;
- int ret = CMD_SUCCESS;
+ bool warn = false;
+ int ret = 0;
+ const char *e = event_name;
+ const char *x = exclusion;
+
+ /* Scan both the excluder and the event letter by letter */
+ while (true) {
+ if (*e == '\\') {
+ if (*x != *e) {
+ warn = true;
+ goto end;
+ }
- if (event_name[strlen(event_name) - 1] != '*') {
- ERR("Event %s: Excluders can only be used with wildcarded events", event_name);
- goto error;
+ e++;
+ x++;
+ goto cmp_chars;
+ }
+
+ if (*x == '*') {
+ /* Event is a subset of the excluder */
+ ERR("Event %s: %s excludes all events from %s",
+ event_name, exclusion, event_name);
+ goto error;
+ }
+
+ if (*e == '*') {
+ /*
+ * Reached the end of the event name before the
+ * end of the exclusion: this is valid.
+ */
+ goto end;
+ }
+
+cmp_chars:
+ if (*x != *e) {
+ warn = true;
+ break;
+ }
+
+ x++;
+ e++;
}
- next_excluder = exclusions;
- while (*next_excluder != 0) {
- event_ptr = event_name;
- excluder_ptr = next_excluder;
- excluder_length = strcspn(next_excluder, ",");
+ goto end;
- /* Scan both the excluder and the event letter by letter */
- while (1) {
- char e, x;
+error:
+ ret = -1;
- e = *event_ptr;
- x = *excluder_ptr;
+end:
+ if (warn) {
+ WARN("Event %s: %s does not exclude any events from %s",
+ event_name, exclusion, event_name);
+ }
- if (x == '*') {
- /* Event is a subset of the excluder */
- ERR("Event %s: %.*s excludes all events from %s",
- event_name,
- excluder_length,
- next_excluder,
- event_name);
- goto error;
- }
- if (e == '*') {
- char *string;
- char **new_exclusion_list;
-
- /* Excluder is a proper subset of event */
- string = lttng_strndup(next_excluder, excluder_length);
- if (!string) {
- PERROR("lttng_strndup error");
- goto error;
- }
- new_exclusion_list = realloc(exclusion_list,
- sizeof(char *) * (exclusion_count + 1));
- if (!new_exclusion_list) {
- PERROR("realloc");
- free(string);
+ return ret;
+}
+
+static
+int check_exclusions_subsets(const char *event_name,
+ char * const *exclusions)
+{
+ int ret = 0;
+ char * const *item;
+
+ for (item = exclusions; *item; item++) {
+ ret = check_exclusion_subsets(event_name, *item);
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static
+int create_exclusion_list_and_validate(const char *event_name,
+ const char *exclusions_arg,
+ char ***exclusion_list)
+{
+ int ret = 0;
+ char **exclusions = NULL;
+
+ /* Event name must be a valid globbing pattern to allow exclusions. */
+ if (!strutils_is_star_glob_pattern(event_name)) {
+ ERR("Event %s: Exclusions can only be used with a globbing pattern",
+ event_name);
+ goto error;
+ }
+
+ /* Split exclusions. */
+ exclusions = strutils_split(exclusions_arg, ',', true);
+ if (!exclusions) {
+ goto error;
+ }
+
+ /*
+ * If the event name is a star-at-end only globbing pattern,
+ * then we can validate the individual exclusions. Otherwise
+ * all exclusions are passed to the session daemon.
+ */
+ if (strutils_is_star_at_the_end_only_glob_pattern(event_name)) {
+ char * const *exclusion;
+
+ for (exclusion = exclusions; *exclusion; exclusion++) {
+ if (!strutils_is_star_glob_pattern(*exclusion) ||
+ strutils_is_star_at_the_end_only_glob_pattern(*exclusion)) {
+ ret = check_exclusions_subsets(
+ event_name, exclusion);
+ if (ret) {
goto error;
}
- exclusion_list = new_exclusion_list;
- exclusion_count++;
- exclusion_list[exclusion_count - 1] = string;
- break;
}
- if (x != e) {
- /* Excluder and event sets have no common elements */
- WARN("Event %s: %.*s does not exclude any events from %s",
- event_name,
- excluder_length,
- next_excluder,
- event_name);
- break;
- }
- excluder_ptr++;
- event_ptr++;
- }
- /* next excluder */
- next_excluder += excluder_length;
- if (*next_excluder == ',') {
- next_excluder++;
}
}
+
+ *exclusion_list = exclusions;
+
goto end;
+
error:
- while (exclusion_count--) {
- free(exclusion_list[exclusion_count]);
- }
- if (exclusion_list != NULL) {
- free(exclusion_list);
- }
- exclusion_list = NULL;
- exclusion_count = 0;
- ret = CMD_ERROR;
+ ret = -1;
+ strutils_free_null_terminated_array_of_strings(exclusions);
+
end:
- *exclusion_count_ptr = exclusion_count;
- *exclusion_list_ptr = exclusion_list;
return ret;
}
-static void warn_on_truncated_exclusion_names(char **exclusion_list,
- int exclusion_count, int *warn)
+static void warn_on_truncated_exclusion_names(char * const *exclusion_list,
+ int *warn)
{
- size_t i = 0;
-
- for (i = 0; i < exclusion_count; ++i) {
- const char *name = exclusion_list[i];
- size_t len = strlen(name);
+ char * const *exclusion;
- if (len >= LTTNG_SYMBOL_NAME_LEN) {
+ for (exclusion = exclusion_list; *exclusion; exclusion++) {
+ if (strlen(*exclusion) >= LTTNG_SYMBOL_NAME_LEN) {
WARN("Event exclusion \"%s\" will be truncated",
- name);
+ *exclusion);
*warn = 1;
}
}
char *event_name, *channel_name = NULL;
struct lttng_event ev;
struct lttng_domain dom;
- int exclusion_count = 0;
char **exclusion_list = NULL;
memset(&ev, 0, sizeof(ev));
}
if (opt_exclude) {
- ret = check_exclusion_subsets("*", opt_exclude,
- &exclusion_count, &exclusion_list);
- if (ret == CMD_ERROR) {
+ ret = create_exclusion_list_and_validate("*",
+ opt_exclude, &exclusion_list);
+ if (ret) {
+ ret = CMD_ERROR;
goto error;
}
- ev.exclusion = 1;
+ ev.exclusion = 1;
warn_on_truncated_exclusion_names(exclusion_list,
- exclusion_count, &warn);
+ &warn);
}
if (!opt_filter) {
ret = lttng_enable_event_with_exclusions(handle,
&ev, channel_name,
NULL,
- exclusion_count, exclusion_list);
+ exclusion_list ? strutils_array_of_strings_len(exclusion_list) : 0,
+ exclusion_list);
if (ret < 0) {
switch (-ret) {
case LTTNG_ERR_KERN_EVENT_EXIST:
switch (opt_event_type) {
case LTTNG_EVENT_TRACEPOINT:
if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) {
- char *exclusion_string = print_exclusions(exclusion_count, exclusion_list);
+ char *exclusion_string = print_exclusions(exclusion_list);
if (!exclusion_string) {
PERROR("Cannot allocate exclusion_string");
opt_loglevel);
free(exclusion_string);
} else {
- char *exclusion_string = print_exclusions(exclusion_count, exclusion_list);
+ char *exclusion_string = print_exclusions(exclusion_list);
if (!exclusion_string) {
PERROR("Cannot allocate exclusion_string");
break;
case LTTNG_EVENT_ALL:
if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) {
- char *exclusion_string = print_exclusions(exclusion_count, exclusion_list);
+ char *exclusion_string = print_exclusions(exclusion_list);
if (!exclusion_string) {
PERROR("Cannot allocate exclusion_string");
opt_loglevel);
free(exclusion_string);
} else {
- char *exclusion_string = print_exclusions(exclusion_count, exclusion_list);
+ char *exclusion_string = print_exclusions(exclusion_list);
if (!exclusion_string) {
PERROR("Cannot allocate exclusion_string");
if (opt_filter) {
command_ret = lttng_enable_event_with_exclusions(handle, &ev, channel_name,
- opt_filter, exclusion_count, exclusion_list);
+ opt_filter,
+ exclusion_list ? strutils_array_of_strings_len(exclusion_list) : 0,
+ exclusion_list);
if (command_ret < 0) {
switch (-command_ret) {
case LTTNG_ERR_FILTER_EXIST:
}
/* print exclusion */
- ret = mi_print_exclusion(exclusion_count, exclusion_list);
+ ret = mi_print_exclusion(exclusion_list);
if (ret) {
ret = CMD_ERROR;
goto error;
goto error;
}
/* Free previously allocated items */
- if (exclusion_list != NULL) {
- while (exclusion_count--) {
- free(exclusion_list[exclusion_count]);
- }
- free(exclusion_list);
- exclusion_list = NULL;
- }
- /* Check for proper subsets */
- ret = check_exclusion_subsets(event_name, opt_exclude,
- &exclusion_count, &exclusion_list);
- if (ret == CMD_ERROR) {
+ strutils_free_null_terminated_array_of_strings(
+ exclusion_list);
+ exclusion_list = NULL;
+ ret = create_exclusion_list_and_validate(
+ event_name, opt_exclude,
+ &exclusion_list);
+ if (ret) {
+ ret = CMD_ERROR;
goto error;
}
warn_on_truncated_exclusion_names(
- exclusion_list, exclusion_count, &warn);
+ exclusion_list, &warn);
}
ev.loglevel_type = opt_loglevel_type;
command_ret = lttng_enable_event_with_exclusions(handle,
&ev, channel_name,
- NULL, exclusion_count, exclusion_list);
- exclusion_string = print_exclusions(exclusion_count, exclusion_list);
+ NULL,
+ exclusion_list ? strutils_array_of_strings_len(exclusion_list) : 0,
+ exclusion_list);
+ exclusion_string = print_exclusions(exclusion_list);
if (!exclusion_string) {
PERROR("Cannot allocate exclusion_string");
error = 1;
ev.filter = 1;
command_ret = lttng_enable_event_with_exclusions(handle, &ev, channel_name,
- opt_filter, exclusion_count, exclusion_list);
- exclusion_string = print_exclusions(exclusion_count, exclusion_list);
+ opt_filter,
+ exclusion_list ? strutils_array_of_strings_len(exclusion_list) : 0,
+ exclusion_list);
+ exclusion_string = print_exclusions(exclusion_list);
if (!exclusion_string) {
PERROR("Cannot allocate exclusion_string");
error = 1;
}
/* print exclusion */
- ret = mi_print_exclusion(exclusion_count, exclusion_list);
+ ret = mi_print_exclusion(exclusion_list);
if (ret) {
ret = CMD_ERROR;
goto error;
ret = CMD_ERROR;
}
lttng_destroy_handle(handle);
-
- if (exclusion_list != NULL) {
- while (exclusion_count--) {
- free(exclusion_list[exclusion_count]);
- }
- free(exclusion_list);
- }
+ strutils_free_null_terminated_array_of_strings(exclusion_list);
/* Overwrite ret with error_holder if there was an actual error with
* enabling an event.
poptFreeContext(pc);
return ret;
}
+
filter-visitor-generate-ir.c \
filter-visitor-ir-check-binary-op-nesting.c \
filter-visitor-ir-validate-string.c \
+ filter-visitor-ir-validate-globbing.c \
+ filter-visitor-ir-normalize-glob-patterns.c \
filter-visitor-generate-bytecode.c \
filter-ast.h \
filter-bytecode.h \
filter-ir.h \
memstream.h
libfilter_la_CFLAGS = -include filter-symbols.h
+libfilter_la_LIBADD = $(top_builddir)/src/common/string-utils/libstring-utils.la
AM_YFLAGS = -t -d -v
int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx);
int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx);
int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx);
#endif /* _FILTER_AST_H */
FILTER_OP_GE = 16,
FILTER_OP_LE = 17,
- /* string binary comparator */
+ /* string binary comparator: apply to */
FILTER_OP_EQ_STRING = 18,
FILTER_OP_NE_STRING = 19,
FILTER_OP_GT_STRING = 20,
FILTER_OP_LOAD_FIELD_REF_USER_STRING = 74,
FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE = 75,
+ /*
+ * load immediate star globbing pattern (literal string)
+ * from immediate
+ */
+ FILTER_OP_LOAD_STAR_GLOB_STRING = 76,
+
+ /* globbing pattern binary operator: apply to */
+ FILTER_OP_EQ_STAR_GLOB_STRING = 77,
+ FILTER_OP_NE_STAR_GLOB_STRING = 78,
+
NR_FILTER_OPS,
};
IR_RIGHT,
};
+enum ir_load_string_type {
+ /* Plain, no globbing at all: `hello world`. */
+ IR_LOAD_STRING_TYPE_PLAIN = 0,
+
+ /* Star at the end only: `hello *`. */
+ IR_LOAD_STRING_TYPE_GLOB_STAR_END,
+
+ /* At least one star, anywhere, but not at the end only: `he*wor*`. */
+ IR_LOAD_STRING_TYPE_GLOB_STAR,
+};
+
struct ir_op_root {
struct ir_op *child;
};
struct ir_op_load {
union {
- char *string;
+ struct {
+ enum ir_load_string_type type;
+ char *value;
+ } string;
int64_t num;
double flt;
char *ref;
{
struct load_op *insn;
uint32_t insn_len = sizeof(struct load_op)
- + strlen(node->u.load.u.string) + 1;
+ + strlen(node->u.load.u.string.value) + 1;
insn = calloc(insn_len, 1);
if (!insn)
return -ENOMEM;
- insn->op = FILTER_OP_LOAD_STRING;
- strcpy(insn->data, node->u.load.u.string);
+
+ switch (node->u.load.u.string.type) {
+ case IR_LOAD_STRING_TYPE_GLOB_STAR:
+ /*
+ * We explicitly tell the interpreter here that
+ * this load is a full star globbing pattern so
+ * that the appropriate matching function can be
+ * called. Also, see comment below.
+ */
+ insn->op = FILTER_OP_LOAD_STAR_GLOB_STRING;
+ break;
+ default:
+ /*
+ * This is the "legacy" string, which includes
+ * star globbing patterns with a star only at
+ * the end. Both "plain" and "star at the end"
+ * literal strings are handled at the same place
+ * by the tracer's filter bytecode interpreter,
+ * whereas full star globbing patterns (stars
+ * can be anywhere in the string) is a special
+ * case.
+ */
+ insn->op = FILTER_OP_LOAD_STRING;
+ break;
+ }
+
+ strcpy(insn->data, node->u.load.u.string.value);
ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
free(insn);
return ret;
#include "filter-ir.h"
#include <common/macros.h>
+#include <common/string-utils/string-utils.h>
static
struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx,
return op;
}
+static
+enum ir_load_string_type get_literal_string_type(const char *string)
+{
+ assert(string);
+
+ if (strutils_is_star_glob_pattern(string)) {
+ if (strutils_is_star_at_the_end_only_glob_pattern(string)) {
+ return IR_LOAD_STRING_TYPE_GLOB_STAR_END;
+ }
+
+ return IR_LOAD_STRING_TYPE_GLOB_STAR;
+ }
+
+ return IR_LOAD_STRING_TYPE_PLAIN;
+}
+
static
struct ir_op *make_op_load_string(char *string, enum ir_side side)
{
op->data_type = IR_DATA_STRING;
op->signedness = IR_SIGN_UNKNOWN;
op->side = side;
- op->u.load.u.string = strdup(string);
- if (!op->u.load.u.string) {
+ op->u.load.u.string.type = get_literal_string_type(string);
+ op->u.load.u.string.value = strdup(string);
+ if (!op->u.load.u.string.value) {
free(op);
return NULL;
}
case IR_OP_LOAD:
switch (op->data_type) {
case IR_DATA_STRING:
- free(op->u.load.u.string);
+ free(op->u.load.u.string.value);
break;
case IR_DATA_FIELD_REF: /* fall-through */
case IR_DATA_GET_CONTEXT_REF:
--- /dev/null
+/*
+ * filter-visitor-ir-normalize-glob-patterns.c
+ *
+ * LTTng filter IR normalize string
+ *
+ * Copyright 2017 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <common/macros.h>
+#include <common/string-utils/string-utils.h>
+
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+static
+int normalize_glob_patterns(struct ir_op *node)
+{
+ switch (node->op) {
+ case IR_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+ return -EINVAL;
+
+ case IR_OP_ROOT:
+ return normalize_glob_patterns(node->u.root.child);
+ case IR_OP_LOAD:
+ {
+ if (node->data_type == IR_DATA_STRING) {
+ enum ir_load_string_type type =
+ node->u.load.u.string.type;
+ if (type == IR_LOAD_STRING_TYPE_GLOB_STAR_END ||
+ type == IR_LOAD_STRING_TYPE_GLOB_STAR) {
+ assert(node->u.load.u.string.value);
+ strutils_normalize_star_glob_pattern(
+ node->u.load.u.string.value);
+ }
+ }
+
+ return 0;
+ }
+ case IR_OP_UNARY:
+ return normalize_glob_patterns(node->u.unary.child);
+ case IR_OP_BINARY:
+ {
+ int ret = normalize_glob_patterns(node->u.binary.left);
+
+ if (ret)
+ return ret;
+ return normalize_glob_patterns(node->u.binary.right);
+ }
+ case IR_OP_LOGICAL:
+ {
+ int ret;
+
+ ret = normalize_glob_patterns(node->u.logical.left);
+ if (ret)
+ return ret;
+ return normalize_glob_patterns(node->u.logical.right);
+ }
+ }
+}
+
+/*
+ * This function normalizes all the globbing literal strings with
+ * utils_normalize_glob_pattern(). See the documentation of
+ * utils_normalize_glob_pattern() for more details.
+ */
+LTTNG_HIDDEN
+int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx)
+{
+ return normalize_glob_patterns(ctx->ir_root);
+}
--- /dev/null
+/*
+ * filter-visitor-ir-validate-globbing.c
+ *
+ * LTTng filter IR validate globbing
+ *
+ * Copyright 2017 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <common/macros.h>
+
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+static
+int validate_globbing(struct ir_op *node)
+{
+ int ret;
+
+ switch (node->op) {
+ case IR_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+ return -EINVAL;
+
+ case IR_OP_ROOT:
+ return validate_globbing(node->u.root.child);
+ case IR_OP_LOAD:
+ return 0;
+ case IR_OP_UNARY:
+ return validate_globbing(node->u.unary.child);
+ case IR_OP_BINARY:
+ {
+ struct ir_op *left = node->u.binary.left;
+ struct ir_op *right = node->u.binary.right;
+
+ if (left->op == IR_OP_LOAD && right->op == IR_OP_LOAD &&
+ left->data_type == IR_DATA_STRING &&
+ right->data_type == IR_DATA_STRING) {
+ /* Test 1. */
+ if (left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
+ right->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
+ fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
+ return -1;
+ }
+
+ if (right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
+ left->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
+ fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
+ return -1;
+ }
+ }
+
+ if ((left->op == IR_OP_LOAD && left->data_type == IR_DATA_STRING) ||
+ (right->op == IR_OP_LOAD && right->data_type == IR_DATA_STRING)) {
+ if ((left->op == IR_OP_LOAD && left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR) ||
+ (right->op == IR_OP_LOAD && right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR)) {
+ /* Test 2. */
+ if (node->u.binary.type != AST_OP_EQ &&
+ node->u.binary.type != AST_OP_NE) {
+ fprintf(stderr, "[error] Only the `==` and `!=` operators are allowed with a globbing pattern\n");
+ return -1;
+ }
+ }
+ }
+
+ ret = validate_globbing(left);
+ if (ret) {
+ return ret;
+ }
+
+ return validate_globbing(right);
+ }
+ case IR_OP_LOGICAL:
+ ret = validate_globbing(node->u.logical.left);
+ if (ret)
+ return ret;
+ return validate_globbing(node->u.logical.right);
+ }
+}
+
+/*
+ * This function recursively validates that:
+ *
+ * 1. When there's a binary operation between two literal strings,
+ * if one of them has the IR_LOAD_STRING_TYPE_GLOB_STAR type,
+ * the other one has the IR_LOAD_STRING_TYPE_PLAIN type.
+ *
+ * In other words, you cannot compare two globbing patterns, except
+ * for two globbing patterns with only a star at the end for backward
+ * compatibility reasons.
+ *
+ * 2. When there's a binary operation between two literal strings, if
+ * one of them is a (full) star globbing pattern, the binary
+ * operation is either == or !=.
+ */
+LTTNG_HIDDEN
+int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx)
+{
+ return validate_globbing(ctx->ir_root);
+}
if (node->data_type == IR_DATA_STRING) {
const char *str;
- assert(node->u.load.u.string);
- str = node->u.load.u.string;
+ assert(node->u.load.u.string.value);
+ str = node->u.load.u.string.value;
- /*
- * Make sure that if a non-escaped wildcard is
- * present, it is the last character of the string.
- */
for (;;) {
enum parse_char_result res;
str++;
switch (res) {
- case PARSE_CHAR_WILDCARD:
- {
- if (*str) {
- /*
- * Found a wildcard followed by non-null
- * character; unsupported.
- */
- ret = -EINVAL;
- fprintf(stderr,
- "Wildcards may only be used as the last character of a string in a filter.\n");
- goto end_load;
- }
- break;
- }
case PARSE_CHAR_UNKNOWN:
ret = -EINVAL;
fprintf(stderr,
assert(ev);
/* Don't add filter for the '*' event. */
- if (ev->name[0] != '*') {
+ if (strcmp(ev->name, "*") != 0) {
if (filter) {
err = asprintf(&agent_filter, "(%s) && (logger_name == \"%s\")", filter,
ev->name);
ret = -LTTNG_ERR_FILTER_INVAL;
goto parse_error;
}
+
+ /* Normalize globbing patterns in the expression. */
+ ret = filter_visitor_ir_normalize_glob_patterns(ctx);
+ if (ret) {
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+
/* Validate strings used as literals in the expression. */
ret = filter_visitor_ir_validate_string(ctx);
if (ret) {
ret = -LTTNG_ERR_FILTER_INVAL;
goto parse_error;
}
+
+ /* Validate globbing patterns in the expression. */
+ ret = filter_visitor_ir_validate_globbing(ctx);
+ if (ret) {
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+
dbg_printf("done\n");
dbg_printf("Generating bytecode... ");