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. [ldv: simplify *_qualify_mode] [ldv: eliminate parse_null] [ldv: optimize lookup_class] [ldv: use loop initial declarations] [ldv: ATTRIBUTE_FALLTHROUGH]
This commit is contained in:
@ -149,6 +149,7 @@ strace_SOURCES = \
|
||||
file_ioctl.c \
|
||||
filter_action.c \
|
||||
filter_expression.c \
|
||||
filter_parse.c \
|
||||
filter_qualify.c \
|
||||
filter.c \
|
||||
filter.h \
|
||||
|
@ -91,7 +91,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;
|
||||
@ -123,6 +123,8 @@ lookup_class(const char *s)
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
|
||||
if (!qualify_mode && *s != '%')
|
||||
break;
|
||||
if (strcmp(s, syscall_class[i].name) == 0)
|
||||
return syscall_class[i].value;
|
||||
}
|
||||
@ -131,9 +133,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)
|
||||
{
|
||||
/* Clear all sets. */
|
||||
clear_number_set_array(set, SUPPORTED_PERSONALITIES);
|
||||
@ -213,9 +216,15 @@ qualify_syscall_tokens(const char *const str, struct number_set *const set)
|
||||
* of the remaining specification.
|
||||
*/
|
||||
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) {
|
||||
@ -244,7 +253,7 @@ qualify_syscall_tokens(const char *const str, struct number_set *const set)
|
||||
|
||||
for (const char *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,10 +265,10 @@ 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 = alloc_number_set_array(SUPPORTED_PERSONALITIES);
|
||||
qualify_syscall_tokens(str, set);
|
||||
qualify_syscall_tokens(str, set, qualify_mode);
|
||||
return set;
|
||||
}
|
||||
|
||||
@ -280,7 +289,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);
|
||||
@ -290,9 +300,15 @@ qualify_tokens(const char *const str, struct number_set *const set,
|
||||
* of the remaining specification.
|
||||
*/
|
||||
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) {
|
||||
@ -335,10 +351,10 @@ 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 = 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;
|
||||
}
|
||||
|
||||
@ -360,7 +376,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));
|
||||
|
||||
|
2
defs.h
2
defs.h
@ -917,7 +917,7 @@ extern void print_ifindex(unsigned int);
|
||||
|
||||
extern void print_bpf_filter_code(const uint16_t code, bool extended);
|
||||
|
||||
extern void qualify(const char *);
|
||||
extern void filtering_parse(const char *);
|
||||
extern void filtering_parsing_finish(void);
|
||||
extern void filter_syscall(struct tcb *);
|
||||
|
||||
|
6
filter.c
6
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[] = {
|
||||
@ -77,9 +77,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
|
||||
|
15
filter.h
15
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);
|
||||
bool is_traced(struct tcb *);
|
||||
@ -53,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);
|
||||
@ -63,6 +65,7 @@ void set_filter_priv_data(struct filter *, void *);
|
||||
struct filter *create_filter(struct filter_action *, const char *name);
|
||||
struct filter_action *find_or_add_action(const char *);
|
||||
void set_qualify_mode(struct filter_action *);
|
||||
void parse_filter_action(const char *, const char *, const char *);
|
||||
void set_filter_action_priv_data(struct filter_action *, void *);
|
||||
|
||||
/* filter expression api */
|
||||
@ -70,10 +73,14 @@ struct bool_expression *create_expression();
|
||||
bool run_expression(struct bool_expression *, bool *, unsigned int);
|
||||
void set_expression_qualify_mode(struct bool_expression *);
|
||||
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 \
|
||||
|
@ -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);
|
||||
@ -171,6 +171,20 @@ 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)
|
||||
error_msg("%s action takes no arguments, ignored arguments "
|
||||
"'%s'", action->type->name, args);
|
||||
action->priv_data = action->type->parse_args
|
||||
? action->type->parse_args(args) : NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run_filter_action(struct tcb *tcp, struct filter_action *action)
|
||||
{
|
||||
|
@ -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;
|
||||
@ -270,3 +276,183 @@ 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;
|
||||
|
||||
for (unsigned int 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 *copy = xstrdup(str);
|
||||
struct filter *cur_filter = NULL;
|
||||
char *filter_name = NULL;
|
||||
char *filter_arg = NULL;
|
||||
int op;
|
||||
unsigned int pos = 0;
|
||||
|
||||
for (char *p = copy; *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(copy);
|
||||
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);
|
||||
}
|
||||
|
252
filter_parse.c
Normal file
252
filter_parse.c
Normal file
@ -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 *copy = xstrdup(str);
|
||||
|
||||
for (char *p = copy; *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'",
|
||||
copy);
|
||||
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 */
|
||||
ATTRIBUTE_FALLTHROUGH;
|
||||
|
||||
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 = copy;
|
||||
parse_qualify_action(action_name, main_part, args);
|
||||
break;
|
||||
case F_BEGIN:
|
||||
main_part = begin;
|
||||
ATTRIBUTE_FALLTHROUGH;
|
||||
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(copy);
|
||||
}
|
@ -250,79 +250,82 @@ parse_inject_common_args(char *const str, struct inject_opts *const opts,
|
||||
}
|
||||
|
||||
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_filter(const char *const str, const char *const action_name,
|
||||
const char *const filter_type)
|
||||
qualify_filter(const char *const main_part, const char *const args,
|
||||
const char *const action_name, const char *const filter_type)
|
||||
{
|
||||
struct filter_action *action = find_or_add_action(action_name);
|
||||
struct filter *filter = create_filter(action, filter_type);
|
||||
|
||||
parse_filter(filter, str);
|
||||
parse_filter(filter, main_part, true);
|
||||
if (args)
|
||||
error_msg("%s action takes no arguments, ignored arguments "
|
||||
"'%s'", action_name, args);
|
||||
set_qualify_mode(action);
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_read(const char *const str)
|
||||
qualify_read(const char *const main_part, const char *const args)
|
||||
{
|
||||
qualify_filter(str, "read", "fd");
|
||||
qualify_filter(main_part, args, "read", "fd");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_write(const char *const str)
|
||||
qualify_write(const char *const main_part, const char *const args)
|
||||
{
|
||||
qualify_filter(str, "write", "fd");
|
||||
qualify_filter(main_part, args, "write", "fd");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_trace(const char *const str)
|
||||
qualify_trace(const char *const main_part, const char *const args)
|
||||
{
|
||||
qualify_filter(str, "trace", "syscall");
|
||||
qualify_filter(main_part, args, "trace", "syscall");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_abbrev(const char *const str)
|
||||
qualify_abbrev(const char *const main_part, const char *const args)
|
||||
{
|
||||
qualify_filter(str, "abbrev", "syscall");
|
||||
qualify_filter(main_part, args, "abbrev", "syscall");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_verbose(const char *const str)
|
||||
qualify_verbose(const char *const main_part, const char *const args)
|
||||
{
|
||||
qualify_filter(str, "verbose", "syscall");
|
||||
qualify_filter(main_part, args, "verbose", "syscall");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_raw(const char *const str)
|
||||
qualify_raw(const char *const main_part, const char *const args)
|
||||
{
|
||||
qualify_filter(str, "raw", "syscall");
|
||||
qualify_filter(main_part, args, "raw", "syscall");
|
||||
}
|
||||
|
||||
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 *copy = xstrdup(str);
|
||||
char *args = strchr(copy, ':');
|
||||
char *copy = 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, copy);
|
||||
parse_filter(filter, main_part, true);
|
||||
set_qualify_mode(action);
|
||||
parse_inject_common_args(args, opts, fault_tokens_only, true);
|
||||
parse_inject_common_args(copy, opts, fault_tokens_only, true);
|
||||
|
||||
if (!opts->data.flags)
|
||||
error_msg_and_die("invalid %s argument '%s'", description,
|
||||
@ -333,20 +336,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 },
|
||||
@ -370,21 +373,19 @@ 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;
|
||||
|
||||
for (unsigned int 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);
|
||||
}
|
||||
|
6
strace.c
6
strace.c
@ -1589,7 +1589,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 ENABLE_STACKTRACE
|
||||
"k"
|
||||
@ -1629,7 +1629,7 @@ init(int argc, char *argv[])
|
||||
daemonized_tracer = 1;
|
||||
break;
|
||||
case 'e':
|
||||
qualify(optarg);
|
||||
filtering_parse(optarg);
|
||||
break;
|
||||
case 'E':
|
||||
if (putenv(optarg) < 0)
|
||||
@ -1697,7 +1697,7 @@ init(int argc, char *argv[])
|
||||
username = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
qualify("abbrev=none");
|
||||
filtering_parse("abbrev=none");
|
||||
break;
|
||||
case 'V':
|
||||
print_version();
|
||||
|
Reference in New Issue
Block a user