[PATCH v3 6/6] Implement new filtering language parsing

Nikolay Marchuk marchuk.nikolay.a at gmail.com
Thu Jun 29 07:46:15 UTC 2017


* 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 4b3516b..372c7f8 100644
--- a/defs.h
+++ b/defs.h
@@ -674,6 +674,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,40 @@ 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 && type->parse_args != &parse_null) {
+		action->_priv_data = type->parse_args(NULL);
+	}
+	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 0733b84..7d7ac1b 100644
--- a/strace.c
+++ b/strace.c
@@ -1632,7 +1632,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)
@@ -1709,6 +1709,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





More information about the Strace-devel mailing list