* defs.h (parse_action): Add definition. * filter.c: Add filters parsing function. * filter.h: Add new definitions. * filter_action.c: Add action parsing function. * filter_expression.c: Add expression parsing function. * strace.c (init): Add strace -m option. --- defs.h | 1 + filter.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ filter.h | 3 ++ filter_action.c | 31 +++++++++++++++ filter_expression.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++- strace.c | 5 ++- 6 files changed, 253 insertions(+), 3 deletions(-)
diff --git a/defs.h b/defs.h index 3d991ae..8c3fbb0 100644 --- a/defs.h +++ b/defs.h @@ -660,6 +660,7 @@ extern bool is_number_in_set(const unsigned int, const struct number_set *); extern void sort_filter_actions(void); extern void filter_syscall(struct tcb *); extern void parse_qualify_filter(const char *); +extern void parse_action(const char *); #define DECL_IOCTL(name) \ extern int \ diff --git a/filter.c b/filter.c index 1f2d2eb..a7ec155 100644 --- a/filter.c +++ b/filter.c @@ -121,6 +121,110 @@ free_filter(struct filter *filter) filter->type->free_priv_data(filter->_priv_data); } +static unsigned int +escaped_argument_len(const char *str) { + unsigned int len = 0; + const char *p; + bool escaped = false; + for (p = str; *p && *p != ';'; ++p) { + if (!escaped) { + if (*p == '\\') { + escaped = true; + continue; + } else if (isspace(*p) || *p == ')' || *p == '|' + || *p == '&') { + break; + } + } + escaped = false; + ++len; + } + return len; +} + +static char * +unescape_argument(const char *str) +{ + const char *p; + char *p_new; + bool escaped = false; + char *new_str = xcalloc(escaped_argument_len(str) + 1, sizeof(char)); + for (p = str, p_new = new_str; *p && *p != ';'; ++p) { + if (!escaped) { + if (*p == '\\') { + escaped = true; + continue; + } else if (isspace(*p) || *p == ')' || *p == '|' + || *p == '&') { + break; + } + } + escaped = false; + *(p_new++) = *p; + } + return new_str; +} +/* + * Try to parse filter from str. Return positive number of characters + * to be skipped in str on success or negative number of characters + * to be copied to new string on failure. +*/ +static int +try_parse_filter(const char *str, struct filter **filters, + unsigned int *nfilters) +{ + int res = 0; + const char *p = str; + if (!lookup_filter_type(str)) { + for (; isalpha(*p); ++p, --res); + } else { + struct filter *filter = add_filter_to_array(filters, nfilters, + str); + /* + * Find and parse filter argument. + */ + for (; isalpha(*p); ++p, ++res); + for (; isspace(*p); ++p, ++res); + char *arg = unescape_argument(p); + res += strlen(arg); + parse_filter(filter, arg, filter->type->name); + free(arg); + } + return res; +} +/* Parses all filters from expression, replaces the filter + * with filter terminal and returns resulting string. +*/ +char * +parse_filters_from_expression(const char *str, struct filter **filters, + unsigned int *nfilters) +{ + char *new_expr = xcalloc(strlen(str) + 1, sizeof(char)); + const char *p; + char *p_new; + for (p = str, p_new = new_expr; *p && *p != ';'; ++p) { + if (isalpha(*p)) { + int res = try_parse_filter(p, filters, nfilters); + if (res > 0) { + /* Skip successfully parsed filter. */ + p += res - 1; + int rc = snprintf(p_new, 2, "F"); + p_new += rc; + } else { + /* Filter is not parsed. */ + strncpy(p_new, p, -res); + p += -res - 1; + p_new += -res; + } + } else { + *(p_new++) = *p; + } + } + unsigned int new_len = (p_new - new_expr) + 1; + new_expr = xreallocarray(new_expr, new_len, sizeof(char)); + return new_expr; +} + void * get_filter_priv_data(struct filter *filter) { diff --git a/filter.h b/filter.h index 7e03d75..8fcc8e8 100644 --- a/filter.h +++ b/filter.h @@ -48,6 +48,8 @@ struct filter* add_filter_to_array(struct filter **, unsigned int *nfilters, const char *name); void parse_filter(struct filter *, const char *str, const char *const name); bool *run_filters(struct tcb *, struct filter *, unsigned int); +char *parse_filters_from_expression(const char *str, struct filter **, + unsigned int *nfilters); void free_filter(struct filter *); void *get_filter_priv_data(struct filter *); void set_filter_priv_data(struct filter *, void *); @@ -62,5 +64,6 @@ void set_filter_action_priv_data(struct filter_action *, void *); struct bool_expression *create_expression(); void set_expression_qualify_mode(struct bool_expression *); bool run_expression(struct bool_expression *, unsigned int, bool *); +void parse_expression(const char *, struct bool_expression *, unsigned int); #endif diff --git a/filter_action.c b/filter_action.c index 0c235da..3663d49 100644 --- a/filter_action.c +++ b/filter_action.c @@ -169,6 +169,37 @@ find_or_add_action(const char *name) return add_action(type); } +void +parse_action(const char *str) +{ + const struct filter_action_type *type; + char *buf = xstrdup(str); + char *expr = strchr(buf, '('); + if (!expr || (expr == buf)) { + type = lookup_filter_action_type("trace"); + expr = buf; + } else if (expr[strlen(expr) - 1] == ')'){ + type = lookup_filter_action_type(buf); + ++expr; + expr[strlen(expr) - 1] = '\0'; + if (!type) + error_msg_and_die("invalid filter action '%s'", buf); + } else + error_msg_and_die("invalid filter expression"); + + struct filter_action *action = find_or_add_action(type->name); + const char *args = strchr(expr, ';'); + if (args) { + action->_priv_data = type->parse_args(args + 1); + } + unsigned int start_id = action->nfilters; + expr = parse_filters_from_expression(expr, &action->filters, + &action->nfilters); + parse_expression(expr, action->expr, start_id); + free(expr); + free(buf); +} + static void run_filter_action(struct tcb *tcp, struct filter_action *action) { diff --git a/filter_expression.c b/filter_expression.c index ca02280..81dde02 100644 --- a/filter_expression.c +++ b/filter_expression.c @@ -25,6 +25,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "defs.h" +#include <ctype.h> +#include "filter.h" struct expression_token{ enum token_type{ @@ -55,17 +57,45 @@ create_expression(void) } static void -reallocate_expression(struct bool_expression *const expr, const unsigned int new_ntokens) +reallocate_expression(struct bool_expression *expr, unsigned int new_ntokens) { if (new_ntokens <= expr->ntokens) return; expr->tokens = xreallocarray(expr->tokens, new_ntokens, - sizeof(*expr->tokens)); + sizeof(struct expression_token)); memset(expr->tokens + expr->ntokens, 0, sizeof(*expr->tokens) * (new_ntokens - expr->ntokens)); expr->ntokens = new_ntokens; } +static void +add_variable_token(struct bool_expression *expr, unsigned int id) +{ + struct expression_token token; + token.type = TOK_VARIABLE; + token.data.variable_id = id; + reallocate_expression(expr, expr->ntokens + 1); + expr->tokens[expr->ntokens - 1] = token; +} + +static void +add_operator_token(struct bool_expression *expr, char c) { + struct expression_token token; + token.type = TOK_OPERATOR; + switch (c){ + case '!': + token.data.operator_id = OP_NOT; + break; + case '&': + token.data.operator_id = OP_AND; + break; + case '|': + token.data.operator_id = OP_OR; + } + reallocate_expression(expr, expr->ntokens + 1); + expr->tokens[expr->ntokens - 1] = token; +} + #define STACK_SIZE 32 static bool stack[STACK_SIZE]; @@ -129,3 +159,81 @@ run_expression(struct bool_expression *expr, error_msg_and_die("corrupted filter expression"); return stack[0]; } + +void parse_expression(const char *str, struct bool_expression *expr, + unsigned int start_id) +{ + unsigned int variable_id = 0; + /* Current stack index */ + unsigned int index = 0; + char op_stack[STACK_SIZE]; + const char *p; + for (p = str; *p; ++p) { + if (isspace(*p)) + continue; + switch (*p) { + case 'F': + add_variable_token(expr, variable_id++); + break; + case '(': + if (index == STACK_SIZE) + error_msg_and_die("stack overflow"); + op_stack[index++] = '('; + break; + case ')': + while ((index > 0) && (op_stack[index - 1] != '(')) + add_operator_token(expr, op_stack[--index]); + if (index == 0) + error_msg_and_die("invalid filter expression"); + --index; + break; + case '!': + op_stack[index++] = '!'; + break; + case 'n': + if (*(p + 1) != 'o' || *(p + 2) != 't') + error_msg_and_die("invalid filter expression"); + p += 2; + op_stack[index++] = '!'; + break; + case '&': + if (*(p + 1) != '&') + error_msg_and_die("invalid filter expression"); + ++p; + while ((index > 0) && (op_stack[index - 1] == '!')) + add_operator_token(expr, op_stack[--index]); + op_stack[index++] = '&'; + break; + case 'a': + if (*(p + 1) != 'n' || *(p + 2) != 'd') + error_msg_and_die("invalid filter expression"); + p += 2; + while ((index > 0) && (op_stack[index - 1] == '!')) + add_operator_token(expr, op_stack[--index]); + op_stack[index++] = '&'; + break; + case '|': + if (*(p + 1) != '|') + error_msg_and_die("invalid filter expression"); + ++p; + while ((index > 0) && ((op_stack[index - 1] == '!') + || (op_stack[index - 1] == '&'))) + add_operator_token(expr, op_stack[--index]); + op_stack[index++] = '|'; + break; + case 'o': + if (*(p + 1) != 'r') + error_msg_and_die("invalid filter expression"); + ++p; + while ((index > 0) && ((op_stack[index - 1] == '!') + || (op_stack[index - 1] == '&'))) + add_operator_token(expr, op_stack[--index]); + op_stack[index++] = '|'; + break; + } + } + while (index > 0) + add_operator_token(expr, op_stack[--index]); + if (start_id > 0) + add_operator_token(expr, '&'); +} diff --git a/strace.c b/strace.c index 9795ae1..8f222f1 100644 --- a/strace.c +++ b/strace.c @@ -1639,7 +1639,7 @@ init(int argc, char *argv[]) "k" #endif "D" - "a:e:o:O:p:s:S:u:E:P:I:")) != EOF) { + "a:e:m:o:O:p:s:S:u:E:P:I:")) != EOF) { switch (c) { case 'b': if (strcmp(optarg, "execve") != 0) @@ -1716,6 +1716,9 @@ init(int argc, char *argv[]) case 'e': parse_qualify_filter(optarg); break; + case 'm': + parse_action(optarg); + break; case 'o': outfname = xstrdup(optarg); break; -- 2.1.4 ------------------------------------------------------------------------------ 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