* 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 (DECL_FILTER, 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. * 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. --- Makefile.am | 1 + basic_filters.c | 54 +++++++---- defs.h | 2 +- filter.c | 8 +- filter.h | 13 ++- filter_action.c | 14 ++- filter_expression.c | 189 +++++++++++++++++++++++++++++++++++++++ filter_parse.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++ filter_qualify.c | 97 +++++++++++--------- strace.c | 6 +- 10 files changed, 565 insertions(+), 71 deletions(-) create mode 100644 filter_parse.c
diff --git a/Makefile.am b/Makefile.am index b928eb28..81a46616 100644 --- a/Makefile.am +++ b/Makefile.am @@ -136,6 +136,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 6c0648c6..db4776ac 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; } @@ -203,7 +205,8 @@ qualify_syscall(const char *token, struct number_set *set) * according to STR specification. */ void -qualify_syscall_tokens(const char *const str, struct number_set *const set) +qualify_syscall_tokens(const char *const str, struct number_set *const set, + bool qualify_mode) { /* * Each leading ! character means inversion @@ -211,9 +214,15 @@ qualify_syscall_tokens(const char *const str, struct number_set *const set) */ const char *s = str; - 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) { @@ -242,7 +251,7 @@ qualify_syscall_tokens(const char *const str, struct number_set *const set) 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 system call '%s'", token); } @@ -256,12 +265,12 @@ qualify_syscall_tokens(const char *const str, struct number_set *const set) } 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); + qualify_syscall_tokens(str, set, qualify_mode); return set; } @@ -286,7 +295,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) { /* * Each leading ! character means inversion @@ -294,9 +304,15 @@ qualify_tokens(const char *const str, struct number_set *const set, */ const char *s = str; - 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) { @@ -341,12 +357,12 @@ qualify_tokens(const char *const str, struct number_set *const set, } 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; } @@ -387,7 +403,7 @@ free_fd_filter(void *_priv_data) } void * -parse_path_filter(const char *path, const char *const name) +parse_path_filter(const char *path, const char *const name, bool qualify_mode) { struct path_set *set = xcalloc(1, sizeof(struct path_set)); diff --git a/defs.h b/defs.h index 4bd8d9e0..de9d60e2 100644 --- a/defs.h +++ b/defs.h @@ -650,7 +650,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 9c9b7e0d..7aab4a59 100644 --- a/filter.c +++ b/filter.c @@ -30,7 +30,7 @@ #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 \ @@ -49,7 +49,7 @@ DECL_FILTER(path); 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[] = { @@ -93,9 +93,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 36c16066..f85acb21 100644 --- a/filter.h +++ b/filter.h @@ -40,8 +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); -void qualify_syscall_tokens(const char *str, struct number_set *set); + string_to_uint_func func, const char *name, + bool qualify_mode); +void qualify_syscall_tokens(const char *str, struct number_set *set, + bool qualify_mode); void parse_inject_common_args(char *, struct inject_opts *, const bool fault_tokens_only, bool qualify_mode); typedef bool (*match_fd_func)(struct tcb *, int, void *); @@ -50,7 +52,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_filter_priv_data(struct filter *, void *); @@ -60,6 +62,7 @@ void set_filters_qualify_mode(struct filter **, unsigned int *nfilters, /* 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_filter_action_priv_data(struct filter_action *, void *); void set_qualify_mode(struct filter_action *, unsigned int); @@ -68,5 +71,9 @@ 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 *); #endif /* !STRACE_FILTER_H */ diff --git a/filter_action.c b/filter_action.c index f424bd35..697a71ce 100644 --- a/filter_action.c +++ b/filter_action.c @@ -122,7 +122,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); @@ -203,6 +203,18 @@ 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 73aaae3a..09d0c4c7 100644 --- a/filter_expression.c +++ b/filter_expression.c @@ -29,6 +29,10 @@ #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 +48,9 @@ struct expression_token { } data; }; +/* Pseudo-operator for parsing */ +#define OP_PARENTHESIS 3 + struct bool_expression { unsigned int ntokens; struct expression_token *tokens; @@ -279,3 +286,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 00000000..e0fd8e58 --- /dev/null +++ b/filter_parse.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2017 Nikolay Marchuk <marchuk.nikola...@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 f195743a..a6c60266 100644 --- a/filter_qualify.c +++ b/filter_qualify.c @@ -175,118 +175,137 @@ 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); else clear_number_set_array(signal_set, 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 *buf = args ? xstrdup(args) : NULL; struct filter_action *action; struct filter *filter; - char *args = strchr(buf, ':'); - - 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->init) error_msg_and_die("invalid %s argument '%s'", description, args ? args : ""); - free(buf); + if (buf) + free(buf); set_filter_action_priv_data(action, opts); } 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 }, @@ -310,22 +329,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 17896f0d..eb2665a5 100644 --- a/strace.c +++ b/strace.c @@ -1581,7 +1581,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, "+b:cCdfFhiqrtTvVwxyz" #ifdef USE_LIBUNWIND @@ -1648,7 +1648,7 @@ init(int argc, char *argv[]) show_fd_path++; break; case 'v': - qualify("abbrev=none"); + filtering_parse("abbrev=none"); break; case 'V': print_version(); @@ -1663,7 +1663,7 @@ init(int argc, char *argv[]) error_opt_arg(c, optarg); break; case 'e': - qualify(optarg); + filtering_parse(optarg); break; case 'o': outfname = optarg; -- 2.11.0 ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Strace-devel mailing list Strace-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/strace-devel