summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolay Marchuk <marchuk.nikolay.a@gmail.com>2017-08-28 11:28:57 +0700
committerDmitry V. Levin <ldv@altlinux.org>2017-12-21 13:12:36 +0000
commit2de045936fb1b6c3069a8d17d45ec6fcb6e7a9ba (patch)
tree4866ff78f9b0003be444aec09a72891a008f0753
parent7d93fbe00e2525601cf637aaed8787e6556e405d (diff)
downloadstrace-2de045936fb1b6c3069a8d17d45ec6fcb6e7a9ba.tar.gz
Implement new filtering language parsing
* basic_filters.c (lookup_class, qualify_syscall_class, qualify_syscall, qualify_syscall_tokens, parse_syscall_filter, qualify_tokens, parse_fd_filter, parse_path_filter): Add qualify_mode argument. (qualify_tokens, qualify_syscall_tokens): Use set inversion only in qualify mode. (lookup_class): Use deprecated class names only in qualify mode. * defs.h (qualify): Remove declaration. (filtering_parse): Add new declaration. * filter.c (struct filter_type, parse_filter): Add bool argument to parse_*_filter declarations. * filter.h (parse_filter_action, parse_qualify_action, parse_filter_expression): Add new declarations. (parse_filter, qualify_tokens, qualify_syscall_tokens): Add qualify_mode argument. (DECL_FILTER): Add bool argument to parse_*_filter declarations. * filter_action.c (parse_filter_action): Add new parsing function. (inject_path_tracing): Use filtering_parse instead of qualify. * filter_expression.c (parse_filter_expression): Implement parsing of filter expression. (parse_operator, push_operator, is_higher_priority): Add helper functions. (is_space_ascii, is_allowed_in_name): Add new declarations. * filter_parse.c: New file. * filter_qualify.c (qualify_read, qualify_write, qualify_signals, qualify_trace, qualify_abbrev, qualify_verbose, qualify_raw, qualify_inject_common, qualify_fault, qualify_inject): Use main_part and args arguments. * strace.c (init): Use filtering_parse instead of qualify. * Makefile.am (strace_SOURCES): Add filter_parse.c.
-rw-r--r--Makefile.am1
-rw-r--r--basic_filters.c67
-rw-r--r--defs.h2
-rw-r--r--filter.c6
-rw-r--r--filter.h14
-rw-r--r--filter_action.c15
-rw-r--r--filter_expression.c188
-rw-r--r--filter_parse.c252
-rw-r--r--filter_qualify.c94
-rw-r--r--strace.c6
10 files changed, 567 insertions, 78 deletions
diff --git a/Makefile.am b/Makefile.am
index 8663bd20e..342c639dd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -139,6 +139,7 @@ strace_SOURCES = \
file_ioctl.c \
filter_action.c \
filter_expression.c \
+ filter_parse.c \
filter_qualify.c \
filter.c \
filter.h \
diff --git a/basic_filters.c b/basic_filters.c
index 488510764..217cac748 100644
--- a/basic_filters.c
+++ b/basic_filters.c
@@ -96,7 +96,7 @@ qualify_syscall_regex(const char *s, struct number_set *set)
}
static unsigned int
-lookup_class(const char *s)
+lookup_class(const char *s, bool qualify_mode)
{
static const struct {
const char *name;
@@ -127,6 +127,8 @@ lookup_class(const char *s)
unsigned int i;
for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
+ if (!qualify_mode && *s != '%')
+ continue;
if (strcmp(s, syscall_class[i].name) == 0) {
return syscall_class[i].value;
}
@@ -136,9 +138,9 @@ lookup_class(const char *s)
}
static bool
-qualify_syscall_class(const char *s, struct number_set *set)
+qualify_syscall_class(const char *s, struct number_set *set, bool qualify_mode)
{
- const unsigned int n = lookup_class(s);
+ const unsigned int n = lookup_class(s, qualify_mode);
if (!n)
return false;
@@ -181,7 +183,7 @@ qualify_syscall_name(const char *s, struct number_set *set)
}
static bool
-qualify_syscall(const char *token, struct number_set *set)
+qualify_syscall(const char *token, struct number_set *set, bool qualify_mode)
{
bool ignore_fail = false;
@@ -193,7 +195,7 @@ qualify_syscall(const char *token, struct number_set *set)
return qualify_syscall_number(token, set) || ignore_fail;
if (*token == '/')
return qualify_syscall_regex(token + 1, set) || ignore_fail;
- return qualify_syscall_class(token, set)
+ return qualify_syscall_class(token, set, qualify_mode)
|| qualify_syscall_name(token, set)
|| ignore_fail;
}
@@ -204,7 +206,7 @@ qualify_syscall(const char *token, struct number_set *set)
*/
void
qualify_syscall_tokens(const char *const str, struct number_set *const set,
- const char *const name)
+ bool qualify_mode)
{
/* Clear all sets. */
clear_number_set_array(set, SUPPORTED_PERSONALITIES);
@@ -214,10 +216,15 @@ qualify_syscall_tokens(const char *const str, struct number_set *const set,
* of the remaining specification.
*/
const char *s = str;
-handle_inversion:
- while (*s == '!') {
- invert_number_set_array(set, SUPPORTED_PERSONALITIES);
- ++s;
+ if (qualify_mode) {
+ /*
+ * Each leading ! character means inversion
+ * of the remaining specification.
+ */
+ while (*s == '!') {
+ invert_number_set_array(set, SUPPORTED_PERSONALITIES);
+ ++s;
+ }
}
if (strcmp(s, "none") == 0) {
@@ -228,8 +235,8 @@ handle_inversion:
*/
return;
} else if (strcmp(s, "all") == 0) {
- s = "!none";
- goto handle_inversion;
+ invert_number_set_array(set, SUPPORTED_PERSONALITIES);
+ return;
}
/*
@@ -246,26 +253,26 @@ handle_inversion:
for (token = strtok_r(copy, ",", &saveptr); token;
token = strtok_r(NULL, ",", &saveptr)) {
- done = qualify_syscall(token, set);
+ done = qualify_syscall(token, set, qualify_mode);
if (!done) {
- error_msg_and_die("invalid %s '%s'", name, token);
+ error_msg_and_die("invalid system call '%s'", token);
}
}
free(copy);
if (!done) {
- error_msg_and_die("invalid %s '%s'", name, str);
+ error_msg_and_die("invalid system call '%s'", str);
}
}
void *
-parse_syscall_filter(const char *str)
+parse_syscall_filter(const char *str, bool qualify_mode)
{
struct number_set *set;
set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
- qualify_syscall_tokens(str, set, "system call");
+ qualify_syscall_tokens(str, set, qualify_mode);
return set;
}
@@ -290,7 +297,8 @@ free_syscall_filter(void *priv_data)
*/
void
qualify_tokens(const char *const str, struct number_set *const set,
- string_to_uint_func func, const char *const name)
+ string_to_uint_func func, const char *const name,
+ bool qualify_mode)
{
/* Clear the set. */
clear_number_set_array(set, 1);
@@ -300,10 +308,15 @@ qualify_tokens(const char *const str, struct number_set *const set,
* of the remaining specification.
*/
const char *s = str;
-handle_inversion:
- while (*s == '!') {
- invert_number_set_array(set, 1);
- ++s;
+ if (qualify_mode) {
+ /*
+ * Each leading ! character means inversion
+ * of the remaining specification.
+ */
+ while (*s == '!') {
+ invert_number_set_array(set, 1);
+ ++s;
+ }
}
if (strcmp(s, "none") == 0) {
@@ -314,8 +327,8 @@ handle_inversion:
*/
return;
} else if (strcmp(s, "all") == 0) {
- s = "!none";
- goto handle_inversion;
+ invert_number_set_array(set, 1);
+ return;
}
/*
@@ -348,12 +361,12 @@ handle_inversion:
}
void *
-parse_fd_filter(const char *str)
+parse_fd_filter(const char *str, bool qualify_mode)
{
struct number_set *set;
set = alloc_number_set_array(1);
- qualify_tokens(str, set, string_to_uint, "descriptor");
+ qualify_tokens(str, set, string_to_uint, "descriptor", qualify_mode);
return set;
}
@@ -394,7 +407,7 @@ free_fd_filter(void *priv_data)
}
void *
-parse_path_filter(const char *path)
+parse_path_filter(const char *path, bool qualify_mode)
{
struct path_set *set = xcalloc(1, sizeof(struct path_set));
diff --git a/defs.h b/defs.h
index 91ce153c9..b54303367 100644
--- a/defs.h
+++ b/defs.h
@@ -651,7 +651,7 @@ print_struct_statfs64(struct tcb *, kernel_ulong_t addr, kernel_ulong_t size);
extern void print_ifindex(unsigned int);
-extern void qualify(const char *);
+extern void filtering_parse(const char *);
extern void filtering_parsing_finish(void);
extern void filter_syscall(struct tcb *);
diff --git a/filter.c b/filter.c
index 9b996cf57..bd100355b 100644
--- a/filter.c
+++ b/filter.c
@@ -35,7 +35,7 @@
static const struct filter_type {
const char *name;
- void *(*parse_filter)(const char *);
+ void *(*parse_filter)(const char *, bool);
bool (*run_filter)(struct tcb *, void *);
void (*free_priv_data)(void *);
} filter_types[] = {
@@ -79,9 +79,9 @@ add_filter_to_array(struct filter **filters, unsigned int *nfilters,
}
void
-parse_filter(struct filter *filter, const char *str)
+parse_filter(struct filter *filter, const char *str, bool qualify_mode)
{
- filter->priv_data = filter->type->parse_filter(str);
+ filter->priv_data = filter->type->parse_filter(str, qualify_mode);
}
static bool
diff --git a/filter.h b/filter.h
index 1d8c5f7a6..249a2a93c 100644
--- a/filter.h
+++ b/filter.h
@@ -40,9 +40,10 @@ struct bool_expression;
typedef int (*string_to_uint_func)(const char *);
void qualify_tokens(const char *str, struct number_set *set,
- string_to_uint_func func, const char *name);
+ string_to_uint_func func, const char *name,
+ bool qualify_mode);
void qualify_syscall_tokens(const char *str, struct number_set *set,
- const char *name);
+ bool qualify_mode);
void parse_inject_common_args(char *, struct inject_opts *,
const bool fault_tokens_only, bool qualify_mode);
bool is_traced(struct tcb *);
@@ -54,7 +55,7 @@ int match_fd_common(struct tcb *, match_fd_func, void *);
/* filter api */
struct filter* add_filter_to_array(struct filter **, unsigned int *nfilters,
const char *name);
-void parse_filter(struct filter *, const char *str);
+void parse_filter(struct filter *, const char *str, bool qualify_mode);
void run_filters(struct tcb *, struct filter *, unsigned int, bool *);
void free_filter(struct filter *);
void set_filters_qualify_mode(struct filter **, unsigned int *nfilters,
@@ -64,6 +65,7 @@ void set_filter_priv_data(struct filter *, void *);
/* filter action api */
struct filter *create_filter(struct filter_action *, const char *name);
struct filter_action *find_or_add_action(const char *);
+void parse_filter_action(const char *, const char *, const char *);
void set_qualify_mode(struct filter_action *, unsigned int);
void set_filter_action_priv_data(struct filter_action *, void *);
@@ -72,10 +74,14 @@ struct bool_expression *create_expression();
bool run_expression(struct bool_expression *, bool *, unsigned int);
void set_expression_qualify_mode(struct bool_expression *, unsigned int);
void expression_add_filter_and(struct bool_expression *, unsigned int);
+void parse_filter_expression(struct bool_expression *, const char *,
+ struct filter_action *, unsigned int);
+
+void parse_qualify_action(const char *, const char *, const char *);
#define DECL_FILTER(name) \
extern void * \
-parse_ ## name ## _filter(const char *); \
+parse_ ## name ## _filter(const char *, bool); \
extern bool \
run_ ## name ## _filter(struct tcb *, void *); \
extern void \
diff --git a/filter_action.c b/filter_action.c
index 0a0b72f25..fea3e8775 100644
--- a/filter_action.c
+++ b/filter_action.c
@@ -94,7 +94,7 @@ inject_path_tracing(void)
struct filter *path_filter;
if (!action->nfilters)
- qualify("trace=all");
+ filtering_parse("trace=all");
path_filter = add_filter_to_array(&action->filters, &action->nfilters,
"path");
set_filter_priv_data(path_filter, &global_path_set);
@@ -175,6 +175,19 @@ find_or_add_action(const char *name)
return add_action(type);
}
+void
+parse_filter_action(const char *action_name, const char *expr, const char *args)
+{
+ struct filter_action *action = find_or_add_action(action_name);
+
+ parse_filter_expression(action->expr, expr, action, action->nfilters);
+ if (args && action->type->parse_args == &parse_null)
+ error_msg("%s action takes no arguments, ignored arguments "
+ "'%s'", action->type->name, args);
+ action->priv_data = action->type->parse_args(args);
+}
+
+
static void
run_filter_action(struct tcb *tcp, struct filter_action *action)
{
diff --git a/filter_expression.c b/filter_expression.c
index 73aaae3a0..98dbbcc54 100644
--- a/filter_expression.c
+++ b/filter_expression.c
@@ -29,6 +29,9 @@
#include <stdarg.h>
#include "filter.h"
+extern bool is_space_ascii(char);
+extern bool is_allowed_in_name(char);
+
struct expression_token {
enum token_type {
TOK_VARIABLE,
@@ -44,6 +47,9 @@ struct expression_token {
} data;
};
+/* Pseudo-operator used for parsing */
+#define OP_PARENTHESIS 3
+
struct bool_expression {
unsigned int ntokens;
struct expression_token *tokens;
@@ -279,3 +285,185 @@ run_expression(struct bool_expression *expr, bool *variables,
variables, variables_num);
return stack[0];
}
+
+/*
+ * Parse operator and add operator length to str and pos.
+ * Return -1 if no operator found.
+ */
+static int
+parse_operator(char **str, unsigned int *pos)
+{
+#define _OP(s, op) { s, sizeof(s) - 1, op }
+ struct {
+ const char *str;
+ int len;
+ enum operator_type op;
+ } ops[] = {
+ _OP("!", OP_NOT),
+ _OP("not", OP_NOT),
+ _OP("&&", OP_AND),
+ _OP("and", OP_AND),
+ _OP("||", OP_OR),
+ _OP("or", OP_OR),
+ };
+#undef _OP
+ char *p = *str;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ops); i++) {
+ if (!strncmp(p, ops[i].str, ops[i].len) &&
+ (!is_allowed_in_name(ops[i].str[0]) ||
+ !is_allowed_in_name(p[ops[i].len]))) {
+ *str += ops[i].len - 1;
+ *pos += ops[i].len - 1;
+ return ops[i].op;
+ }
+ }
+ return -1;
+}
+
+static char *
+unescape_argument(char **str)
+{
+ char *p;
+ char *p_new;
+ bool escaped = false;
+ unsigned int size = 1;
+ char *new_str = xcalloc(strlen(*str) + 1, 1);
+
+ for (p = *str, p_new = new_str; *p; ++p) {
+ if (!escaped) {
+ if (*p == '\\') {
+ escaped = true;
+ continue;
+ } else if (is_space_ascii(*p) || *p == ')' || *p == '|'
+ || *p == '&') {
+ break;
+ }
+ }
+ escaped = false;
+ *(p_new++) = *p;
+ size++;
+ }
+ *str = p - 1;
+ return xreallocarray(new_str, size, 1);
+}
+
+static void
+push_operator(int *stack, unsigned int *stack_size, int op)
+{
+ if (*stack_size == MAX_STACK_SIZE)
+ error_msg_and_die("stack overflow (expression is too complex)");
+ stack[*stack_size] = op;
+ (*stack_size)++;
+}
+
+static bool
+is_higher_priority(int op_a, int op_b)
+{
+ bool op_priority[] = {
+ [OP_NOT] = 2,
+ [OP_AND] = 1,
+ [OP_OR] = 0,
+ };
+ return op_priority[op_a] > op_priority[op_b];
+}
+
+void
+parse_filter_expression(struct bool_expression *expr, const char *str,
+ struct filter_action *action, unsigned int start_id)
+{
+ enum {
+ WAIT_FILTER,
+ FILTER_NAME,
+ FILTER_ARG,
+ WAIT_OPERATOR,
+ } state = WAIT_FILTER;
+ unsigned int variable_id = start_id;
+ /* Current stack stack_size */
+ unsigned int st_size = 0;
+ int stack[MAX_STACK_SIZE];
+ char *buf = xstrdup(str);
+ struct filter *cur_filter = NULL;
+ char *filter_name = NULL;
+ char *filter_arg = NULL;
+ int op;
+ char *p;
+ unsigned int pos = 0;
+
+ for (p = buf; *p; ++p, ++pos) {
+ switch (state) {
+ case WAIT_FILTER:
+ if (*p == '(') {
+ push_operator(stack, &st_size, OP_PARENTHESIS);
+ } else if ((op = parse_operator(&p, &pos)) >= 0) {
+ if (op == OP_NOT) {
+ push_operator(stack, &st_size, op);
+ } else {
+ error_msg_and_die("invalid operator "
+ "at '%s':%u",
+ str, pos);
+ }
+ } else if (!is_space_ascii(*p)) {
+ filter_name = p;
+ state = FILTER_NAME;
+ }
+ break;
+
+ case FILTER_NAME:
+ if (is_space_ascii(*p)) {
+ *p = '\0';
+ cur_filter = create_filter(action, filter_name);
+ filter_arg = NULL;
+ state = FILTER_ARG;
+ }
+ break;
+
+ case FILTER_ARG:
+ if (!filter_arg && is_space_ascii(*p))
+ break;
+ filter_arg = unescape_argument(&p);
+ parse_filter(cur_filter, filter_arg, false);
+ free(filter_arg);
+ add_variable_token(expr, variable_id++);
+ state = WAIT_OPERATOR;
+ break;
+
+ case WAIT_OPERATOR:
+ if (is_space_ascii(*p))
+ break;
+ if (*p == ')') {
+ while ((st_size > 0) &&
+ (stack[st_size - 1] != OP_PARENTHESIS)) {
+ op = stack[--st_size];
+ add_operator_token(expr, op);
+ }
+ --st_size;
+ break;
+ }
+ op = parse_operator(&p, &pos);
+ if (op < 0 || op == OP_NOT)
+ error_msg_and_die("invalid operator at '%s':%u",
+ str, pos);
+
+ /* Pop operators with higher priority. */
+ while ((st_size > 0) &&
+ (stack[st_size - 1] != OP_PARENTHESIS) &&
+ is_higher_priority(stack[st_size - 1], op))
+ add_operator_token(expr, stack[--st_size]);
+
+ push_operator(stack, &st_size, op);
+ state = WAIT_FILTER;
+ break;
+ }
+ }
+
+ free(buf);
+ if (state != WAIT_OPERATOR)
+ error_msg_and_die("unfinished filter expression '%s'", str);
+
+ while (st_size > 0)
+ add_operator_token(expr, stack[--st_size]);
+ if (start_id > 0)
+ add_operator_token(expr, OP_OR);
+}
diff --git a/filter_parse.c b/filter_parse.c
new file mode 100644
index 000000000..e0fd8e580
--- /dev/null
+++ b/filter_parse.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+#include "filter.h"
+
+bool
+is_space_ascii(char c)
+{
+ return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') ||
+ (c == '\v') || (c == '\f');
+}
+
+bool
+is_allowed_in_name(char c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9') || (c == '_');
+}
+
+/*
+ * Split expression into action name, filter expression or qualify set
+ * and action arguments.
+ */
+void
+filtering_parse(const char *str)
+{
+ enum parsing_states {
+ F_EMPTY,
+ F_BEGIN,
+ F_QUAL_SET,
+ F_FILT_EXPR,
+ F_QUAL_ARGS,
+ F_FILT_ARGS,
+ F_END,
+ } state = F_EMPTY;
+ const char *begin = NULL;
+ const char *action_name = "trace";
+ const char *main_part = NULL;
+ const char *args = NULL;
+ bool action_specified = false;
+ bool escaped = false;
+ int parentheses_count = 0;
+ /* Used to store position of last terminating parenthesis. */
+ char *expression_end = NULL;
+ /* Used to provide diagnostics. */
+ unsigned int pos = 0;
+ char *buf = xstrdup(str);
+ char *p;
+
+ for (p = buf; *p; ++p, ++pos) {
+ switch (state) {
+ case F_EMPTY:
+ switch (*p) {
+ /* trace(), action name omitted */
+ case '(':
+ parentheses_count++;
+ main_part = p;
+ state = F_FILT_EXPR;
+ break;
+ /* missing action name */
+ case '=':
+ *p = '\0';
+ error_msg_and_die("invalid filter action '%s'",
+ buf);
+ default:
+ if (is_space_ascii(*p)) {
+ break;
+ } else if (!strncmp(p, "not", 3) && *(p + 3) &&
+ (is_space_ascii(*(p + 3)) ||
+ *(p + 3) == '(')) {
+ main_part = p;
+ state = F_FILT_EXPR;
+ break;
+ } else {
+ begin = p;
+ state = F_BEGIN;
+ }
+ }
+ if (state != F_BEGIN)
+ break;
+ /* else fall through to check for qualify set */
+
+ case F_BEGIN:
+ switch (*p) {
+ /* action(...) */
+ case '(':
+ if (*begin == '!') {
+ main_part = begin;
+ } else {
+ action_name = begin;
+ action_specified = true;
+ *p = '\0';
+ main_part = p + 1;
+ }
+ state = F_FILT_EXPR;
+ parentheses_count++;
+ break;
+ /* action=... */
+ case '=':
+ action_name = begin;
+ action_specified = true;
+ *p = '\0';
+ main_part = p + 1;
+ state = F_QUAL_SET;
+ break;
+ case ':':
+ main_part = begin;
+ *p = '\0';
+ args = p + 1;
+ state = F_QUAL_ARGS;
+ break;
+ case ';':
+ error_msg_and_die("invalid arguments position "
+ "'%s':%u",
+ str, pos);
+ /* qualify set without action. */
+ case ',':
+ case '?':
+ case '/':
+ case '%':
+ case '-':
+ main_part = begin;
+ state = F_QUAL_SET;
+ break;
+ default:
+ /* new expression without action. */
+ if (is_space_ascii(*p)) {
+ main_part = begin;
+ state = F_FILT_EXPR;
+ }
+ }
+ break;
+
+ case F_QUAL_SET:
+ if (*p == ':') {
+ *p = '\0';
+ args = p + 1;
+ state = F_QUAL_ARGS;
+ }
+ break;
+
+ case F_FILT_EXPR:
+ if (!escaped) {
+ switch (*p) {
+ case ';':
+ if (parentheses_count != 1 ||
+ !action_specified)
+ error_msg_and_die("invalid "
+ "arguments "
+ "position "
+ "'%s':%u",
+ str, pos);
+ *p = '\0';
+ args = p + 1;
+ state = F_FILT_ARGS;
+ break;
+ case '(':
+ parentheses_count++;
+ break;
+ case ')':
+ if (parentheses_count <= 0)
+ error_msg_and_die("unexpected "
+ "')' at "
+ "'%s':%u",
+ str, pos);
+ parentheses_count--;
+ expression_end = p;
+ if (action_specified &&
+ parentheses_count == 0)
+ state = F_END;
+ break;
+ case '\\':
+ escaped = true;
+ break;
+ }
+ } else
+ escaped = false;
+ break;
+
+ case F_QUAL_ARGS:
+ break;
+ case F_FILT_ARGS:
+ if (!escaped) {
+ switch (*p) {
+ case ')':
+ parentheses_count--;
+ expression_end = p;
+ state = F_END;
+ break;
+ case '\\':
+ escaped = true;
+ break;
+ }
+ } else
+ escaped = false;
+ break;
+ case F_END:
+ if (!is_space_ascii(*p))
+ error_msg_and_die("unexpected '%c' at "
+ "'%s':%u", *p, str, pos);
+ }
+ }
+
+ switch (state) {
+ case F_EMPTY:
+ main_part = buf;
+ parse_qualify_action(action_name, main_part, args);
+ break;
+ case F_BEGIN:
+ main_part = begin;
+ /* fall through */
+ case F_QUAL_SET:
+ case F_QUAL_ARGS:
+ parse_qualify_action(action_name, main_part, args);
+ break;
+ case F_FILT_EXPR:
+ case F_FILT_ARGS:
+ case F_END:
+ if (parentheses_count != 0)
+ error_msg_and_die("missing ')' in '%s'", str);
+ if (action_specified && expression_end)
+ *expression_end = '\0';
+ parse_filter_action(action_name, main_part, args);
+ break;
+ }
+ free(buf);
+}
diff --git a/filter_qualify.c b/filter_qualify.c
index 4607afe76..1f9c02594 100644
--- a/filter_qualify.c
+++ b/filter_qualify.c
@@ -182,92 +182,110 @@ parse_inject_common_args(char *str, struct inject_opts *const opts,
}
static void
-qualify_read(const char *const str)
+qualify_read(const char *const main_part, const char *const args)
{
struct filter_action *action = find_or_add_action("read");
struct filter *filter = create_filter(action, "fd");
- parse_filter(filter, str);
+ parse_filter(filter, main_part, true);
+ if (args)
+ error_msg("read action takes no arguments, ignored arguments "
+ "'%s'", args);
set_qualify_mode(action, 1);
}
static void
-qualify_write(const char *const str)
+qualify_write(const char *const main_part, const char *const args)
{
struct filter_action *action = find_or_add_action("write");
struct filter *filter = create_filter(action, "fd");
- parse_filter(filter, str);
+ parse_filter(filter, main_part, true);
+ if (args)
+ error_msg("write action takes no arguments, ignored arguments "
+ "'%s'", args);
set_qualify_mode(action, 1);
}
static void
-qualify_signals(const char *const str)
+qualify_signals(const char *const main_part, const char *const args)
{
if (!signal_set)
signal_set = alloc_number_set_array(1);
- qualify_tokens(str, signal_set, sigstr_to_uint, "signal");
+
+ qualify_tokens(main_part, signal_set, sigstr_to_uint, "signal", true);
+ if (args)
+ error_msg("signal action takes no arguments, ignored arguments "
+ "'%s'", args);
}
static void
-qualify_trace(const char *const str)
+qualify_trace(const char *const main_part, const char *const args)
{
struct filter_action *action = find_or_add_action("trace");
struct filter *filter = create_filter(action, "syscall");
- parse_filter(filter, str);
+ parse_filter(filter, main_part, true);
+ if (args)
+ error_msg("trace action takes no arguments, ignored arguments "
+ "'%s'", args);
set_qualify_mode(action, 1);
}
static void
-qualify_abbrev(const char *const str)
+qualify_abbrev(const char *const main_part, const char *const args)
{
struct filter_action *action = find_or_add_action("abbrev");
struct filter *filter = create_filter(action, "syscall");
- parse_filter(filter, str);
+ parse_filter(filter, main_part, true);
+ if (args)
+ error_msg("abbrev action takes no arguments, ignored arguments "
+ "'%s'", args);
set_qualify_mode(action, 1);
}
static void
-qualify_verbose(const char *const str)
+qualify_verbose(const char *const main_part, const char *const args)
{
struct filter_action *action = find_or_add_action("verbose");
struct filter *filter = create_filter(action, "syscall");
- parse_filter(filter, str);
+ parse_filter(filter, main_part, true);
+ if (args)
+ error_msg("verbose action takes no arguments, ignored arguments"
+ " '%s'", args);
set_qualify_mode(action, 1);
}
static void
-qualify_raw(const char *const str)
+qualify_raw(const char *const main_part, const char *const args)
{
struct filter_action *action = find_or_add_action("raw");
struct filter *filter = create_filter(action, "syscall");
- parse_filter(filter, str);
+ parse_filter(filter, main_part, true);
+ if (args)
+ error_msg("raw action takes no arguments, ignored arguments "
+ "'%s'", args);
set_qualify_mode(action, 1);
}
static void
-qualify_inject_common(const char *const str,
+qualify_inject_common(const char *const main_part, const char *const args,
const bool fault_tokens_only,
const char *const description)
{
struct inject_opts *opts = xmalloc(sizeof(struct inject_opts));
- char *buf = xstrdup(str);
- char *args = strchr(buf, ':');
+ char *buf = xstrdup(args);
struct filter_action *action;
struct filter *filter;
- if (args)
- *(args++) = '\0';
-
action = find_or_add_action(fault_tokens_only ? "fault" : "inject");
filter = create_filter(action, "syscall");
- parse_filter(filter, buf);
+ parse_filter(filter, main_part, true);
set_qualify_mode(action, 1);
- parse_inject_common_args(args, opts, fault_tokens_only, true);
+ parse_inject_common_args(buf, opts, fault_tokens_only, true);
if (!opts->data.flags)
error_msg_and_die("invalid %s argument '%s'", description,
@@ -278,20 +296,20 @@ qualify_inject_common(const char *const str,
}
static void
-qualify_fault(const char *const str)
+qualify_fault(const char *const main_part, const char *const args)
{
- qualify_inject_common(str, true, "fault");
+ qualify_inject_common(main_part, args, true, "fault");
}
static void
-qualify_inject(const char *const str)
+qualify_inject(const char *const main_part, const char *const args)
{
- qualify_inject_common(str, false, "inject");
+ qualify_inject_common(main_part, args, false, "inject");
}
static const struct qual_options {
const char *name;
- void (*qualify)(const char *);
+ void (*qualify)(const char *, const char *);
} qual_options[] = {
{ "trace", qualify_trace },
{ "t", qualify_trace },
@@ -315,22 +333,20 @@ static const struct qual_options {
};
void
-qualify(const char *str)
+parse_qualify_action(const char *action_name, const char *main_part,
+ const char *args)
{
- const struct qual_options *opt = qual_options;
+ const struct qual_options *opt = NULL;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
- const char *name = qual_options[i].name;
- const size_t len = strlen(name);
- const char *val = str_strip_prefix_len(str, name, len);
-
- if (val == str || *val != '=')
- continue;
- str = val + 1;
- opt = &qual_options[i];
- break;
+ if (!strcmp(action_name, qual_options[i].name)) {
+ opt = &qual_options[i];
+ break;
+ }
}
- opt->qualify(str);
+ if (!opt)
+ error_msg_and_die("invalid filter action '%s'", action_name);
+ opt->qualify(main_part ? main_part : "", args);
}
diff --git a/strace.c b/strace.c
index 12f8e6dc2..7a262aa96 100644
--- a/strace.c
+++ b/strace.c
@@ -1573,7 +1573,7 @@ init(int argc, char *argv[])
#if DEFAULT_QUAL_FLAGS != (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE)
# error Bug in DEFAULT_QUAL_FLAGS
#endif
- qualify("signal=all");
+ filtering_parse("signal=all");
while ((c = getopt(argc, argv, "+"
#ifdef USE_LIBUNWIND
"k"
@@ -1638,7 +1638,7 @@ init(int argc, char *argv[])
show_fd_path++;
break;
case 'v':
- qualify("abbrev=none");
+ filtering_parse("abbrev=none");
break;
case 'V':
print_version();
@@ -1653,7 +1653,7 @@ init(int argc, char *argv[])
error_opt_arg(c, optarg);
break;
case 'e':
- qualify(optarg);
+ filtering_parse(optarg);
break;
case 'o':
outfname = optarg;