/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <errno.h>#include <sys/types.h> #include "event-parse.h"#include "event-utils.h" #define COMM "COMM" static struct format_field comm = { .name = "COMM",}; struct event_list { struct event_list *next; struct event_format *event;}; static void show_error(char *error_buf, const char *fmt, ...){ unsigned long long index; const char *input; va_list ap; int len; int i; input = pevent_get_input_buf(); index = pevent_get_input_buf_ptr(); len = input ? strlen(input) : 0; if (len) { strcpy(error_buf, input); error_buf[len] = '\n'; for (i = 1; i < len && i < index; i++) error_buf[len+i] = ' '; error_buf[len + i] = '^'; error_buf[len + i + 1] = '\n'; len += i+2; } va_start(ap, fmt); vsnprintf(error_buf + len, PEVENT_FILTER_ERROR_BUFSZ - len, fmt, ap); va_end(ap);} static void free_token(char *token){ pevent_free_token(token);} static enum event_type read_token(char **tok){ enum event_type type; char *token = NULL; do { free_token(token); type = pevent_read_token(&token); } while (type == EVENT_NEWLINE || type == EVENT_SPACE); /* If token is = or ! check to see if the next char is ~ */ if (token && (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && pevent_peek_char() == '~') { /* append it */ *tok = malloc(3); if (*tok == NULL) { free_token(token); return EVENT_ERROR; } sprintf(*tok, "%c%c", *token, '~'); free_token(token); /* Now remove the '~' from the buffer */ pevent_read_token(&token); free_token(token); } else *tok = token; return type;} static int filter_cmp(const void *a, const void *b){ const struct filter_type *ea = a; const struct filter_type *eb = b; if (ea->event_id < eb->event_id) return -1; if (ea->event_id > eb->event_id) return 1; return 0;} static struct filter_type *find_filter_type(struct event_filter *filter, int id){ struct filter_type *filter_type; struct filter_type key; key.event_id = id; filter_type = bsearch(&key, filter->event_filters, filter->filters, sizeof(*filter->event_filters), filter_cmp); return filter_type;} static struct filter_type *add_filter_type(struct event_filter *filter, int id){ struct filter_type *filter_type; int i; filter_type = find_filter_type(filter, id); if (filter_type) return filter_type; filter_type = realloc(filter->event_filters, sizeof(*filter->event_filters) * (filter->filters + 1)); if (!filter_type) return NULL; filter->event_filters = filter_type; for (i = 0; i < filter->filters; i++) { if (filter->event_filters[i].event_id > id) break; } if (i < filter->filters) memmove(&filter->event_filters[i+1], &filter->event_filters[i], sizeof(*filter->event_filters) * (filter->filters - i)); filter_type = &filter->event_filters[i]; filter_type->event_id = id; filter_type->event = pevent_find_event(filter->pevent, id); filter_type->filter = NULL; filter->filters++; return filter_type;} /** * pevent_filter_alloc - create a new event filter * @pevent: The pevent that this filter is associated with */struct event_filter *pevent_filter_alloc(struct pevent *pevent){ struct event_filter *filter; filter = malloc(sizeof(*filter)); if (filter == NULL) return NULL; memset(filter, 0, sizeof(*filter)); filter->pevent = pevent; pevent_ref(pevent); return filter;} static struct filter_arg *allocate_arg(void){ return calloc(1, sizeof(struct filter_arg));} static void free_arg(struct filter_arg *arg){ if (!arg) return; switch (arg->type) { case FILTER_ARG_NONE: case FILTER_ARG_BOOLEAN: break; case FILTER_ARG_NUM: free_arg(arg->num.left); free_arg(arg->num.right); break; case FILTER_ARG_EXP: free_arg(arg->exp.left); free_arg(arg->exp.right); break; case FILTER_ARG_STR: free(arg->str.val); regfree(&arg->str.reg); free(arg->str.buffer); break; case FILTER_ARG_VALUE: if (arg->value.type == FILTER_STRING || arg->value.type == FILTER_CHAR) free(arg->value.str); break; case FILTER_ARG_OP: free_arg(arg->op.left); free_arg(arg->op.right); default: break; } free(arg);} static int add_event(struct event_list **events, struct event_format *event){ struct event_list *list; list = malloc(sizeof(*list)); if (list == NULL) return -1; list->next = *events; *events = list; list->event = event; return 0;} static int event_match(struct event_format *event, regex_t *sreg, regex_t *ereg){ if (sreg) { return !regexec(sreg, event->system, 0, NULL, 0) && !regexec(ereg, event->name, 0, NULL, 0); } return !regexec(ereg, event->system, 0, NULL, 0) || !regexec(ereg, event->name, 0, NULL, 0);} static enum pevent_errnofind_event(struct pevent *pevent, struct event_list **events, char *sys_name, char *event_name){ struct event_format *event; regex_t ereg; regex_t sreg; int match = 0; int fail = 0; char *reg; int ret; int i; if (!event_name) { /* if no name is given, then swap sys and name */ event_name = sys_name; sys_name = NULL; } reg = malloc(strlen(event_name) + 3); if (reg == NULL) return PEVENT_ERRNO__MEM_ALLOC_FAILED; sprintf(reg, "^%s$", event_name); ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) return PEVENT_ERRNO__INVALID_EVENT_NAME; if (sys_name) { reg = malloc(strlen(sys_name) + 3); if (reg == NULL) { regfree(&ereg); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } sprintf(reg, "^%s$", sys_name); ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) { regfree(&ereg); return PEVENT_ERRNO__INVALID_EVENT_NAME; } } for (i = 0; i < pevent->nr_events; i++) { event = pevent->events[i]; if (event_match(event, sys_name ? &sreg : NULL, &ereg)) { match = 1; if (add_event(events, event) < 0) { fail = 1; break; } } } regfree(&ereg); if (sys_name) regfree(&sreg); if (!match) return PEVENT_ERRNO__EVENT_NOT_FOUND; if (fail) return PEVENT_ERRNO__MEM_ALLOC_FAILED; return 0;} static void free_events(struct event_list *events){ struct event_list *event; while (events) { event = events; events = events->next; free(event); }} static enum pevent_errnocreate_arg_item(struct event_format *event, const char *token, enum event_type type, struct filter_arg **parg, char *error_str){ struct format_field *field; struct filter_arg *arg; arg = allocate_arg(); if (arg == NULL) { show_error(error_str, "failed to allocate filter arg"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } switch (type) { case EVENT_SQUOTE: case EVENT_DQUOTE: arg->type = FILTER_ARG_VALUE; arg->value.type = type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR; arg->value.str = strdup(token); if (!arg->value.str) { free_arg(arg); show_error(error_str, "failed to allocate string filter arg"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } break; case EVENT_ITEM: /* if it is a number, then convert it */ if (isdigit(token[0])) { arg->type = FILTER_ARG_VALUE; arg->value.type = FILTER_NUMBER; arg->value.val = strtoull(token, NULL, 0); break; } /* Consider this a field */ field = pevent_find_any_field(event, token); if (!field) { if (strcmp(token, COMM) != 0) { /* not a field, Make it false */ arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = FILTER_FALSE; break; } /* If token is 'COMM' then it is special */ field = &comm; } arg->type = FILTER_ARG_FIELD; arg->field.field = field; break; default: free_arg(arg); show_error(error_str, "expected a value but found %s", token); return PEVENT_ERRNO__UNEXPECTED_TYPE; } *parg = arg; return 0;} static struct filter_arg *create_arg_op(enum filter_op_type btype){ struct filter_arg *arg; arg = allocate_arg(); if (!arg) return NULL; arg->type = FILTER_ARG_OP; arg->op.type = btype; return arg;} static struct filter_arg *create_arg_exp(enum filter_exp_type etype){ struct filter_arg *arg; arg = allocate_arg(); if (!arg) return NULL; arg->type = FILTER_ARG_EXP; arg->op.type = etype; return arg;} static struct filter_arg *create_arg_cmp(enum filter_exp_type etype){ struct filter_arg *arg; arg = allocate_arg(); if (!arg) return NULL; /* Use NUM and change if necessary */ arg->type = FILTER_ARG_NUM; arg->op.type = etype; return arg;} static enum pevent_errnoadd_right(struct filter_arg *op, struct filter_arg *arg, char *error_str){ struct filter_arg *left; char *str; int op_type; int ret; switch (op->type) { case FILTER_ARG_EXP: if (op->exp.right) goto out_fail; op->exp.right = arg; break; case FILTER_ARG_OP: if (op->op.right) goto out_fail; op->op.right = arg; break; case FILTER_ARG_NUM: if (op->op.right) goto out_fail; /* * The arg must be num, str, or field */ switch (arg->type) { case FILTER_ARG_VALUE: case FILTER_ARG_FIELD: break; default: show_error(error_str, "Illegal rvalue"); return PEVENT_ERRNO__ILLEGAL_RVALUE; } /* * Depending on the type, we may need to * convert this to a string or regex. */ switch (arg->value.type) { case FILTER_CHAR: /* * A char should be converted to number if * the string is 1 byte, and the compare * is not a REGEX. */ if (strlen(arg->value.str) == 1 && op->num.type != FILTER_CMP_REGEX &&Lines 1–500 of 2432. Load more
/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <errno.h> #include <sys/types.h> #include "event-parse.h" #include "event-utils.h" #define COMM "COMM" static struct format_field comm = { .name = "COMM", }; struct event_list { struct event_list *next; struct event_format *event; }; static void show_error(char *error_buf, const char *fmt, ...) { unsigned long long index; const char *input; va_list ap; int len; int i; input = pevent_get_input_buf(); index = pevent_get_input_buf_ptr(); len = input ? strlen(input) : 0; if (len) { strcpy(error_buf, input); error_buf[len] = '\n'; for (i = 1; i < len && i < index; i++) error_buf[len+i] = ' '; error_buf[len + i] = '^'; error_buf[len + i + 1] = '\n'; len += i+2; } va_start(ap, fmt); vsnprintf(error_buf + len, PEVENT_FILTER_ERROR_BUFSZ - len, fmt, ap); va_end(ap); } static void free_token(char *token) { pevent_free_token(token); } static enum event_type read_token(char **tok) { enum event_type type; char *token = NULL; do { free_token(token); type = pevent_read_token(&token); } while (type == EVENT_NEWLINE || type == EVENT_SPACE); /* If token is = or ! check to see if the next char is ~ */ if (token && (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && pevent_peek_char() == '~') { /* append it */ *tok = malloc(3); if (*tok == NULL) { free_token(token); return EVENT_ERROR; } sprintf(*tok, "%c%c", *token, '~'); free_token(token); /* Now remove the '~' from the buffer */ pevent_read_token(&token); free_token(token); } else *tok = token; return type; } static int filter_cmp(const void *a, const void *b) { const struct filter_type *ea = a; const struct filter_type *eb = b; if (ea->event_id < eb->event_id) return -1; if (ea->event_id > eb->event_id) return 1; return 0; } static struct filter_type * find_filter_type(struct event_filter *filter, int id) { struct filter_type *filter_type; struct filter_type key; key.event_id = id; filter_type = bsearch(&key, filter->event_filters, filter->filters, sizeof(*filter->event_filters), filter_cmp); return filter_type; } static struct filter_type * add_filter_type(struct event_filter *filter, int id) { struct filter_type *filter_type; int i; filter_type = find_filter_type(filter, id); if (filter_type) return filter_type; filter_type = realloc(filter->event_filters, sizeof(*filter->event_filters) * (filter->filters + 1)); if (!filter_type) return NULL; filter->event_filters = filter_type; for (i = 0; i < filter->filters; i++) { if (filter->event_filters[i].event_id > id) break; } if (i < filter->filters) memmove(&filter->event_filters[i+1], &filter->event_filters[i], sizeof(*filter->event_filters) * (filter->filters - i)); filter_type = &filter->event_filters[i]; filter_type->event_id = id; filter_type->event = pevent_find_event(filter->pevent, id); filter_type->filter = NULL; filter->filters++; return filter_type; } /** * pevent_filter_alloc - create a new event filter * @pevent: The pevent that this filter is associated with */ struct event_filter *pevent_filter_alloc(struct pevent *pevent) { struct event_filter *filter; filter = malloc(sizeof(*filter)); if (filter == NULL) return NULL; memset(filter, 0, sizeof(*filter)); filter->pevent = pevent; pevent_ref(pevent); return filter; } static struct filter_arg *allocate_arg(void) { return calloc(1, sizeof(struct filter_arg)); } static void free_arg(struct filter_arg *arg) { if (!arg) return; switch (arg->type) { case FILTER_ARG_NONE: case FILTER_ARG_BOOLEAN: break; case FILTER_ARG_NUM: free_arg(arg->num.left); free_arg(arg->num.right); break; case FILTER_ARG_EXP: free_arg(arg->exp.left); free_arg(arg->exp.right); break; case FILTER_ARG_STR: free(arg->str.val); regfree(&arg->str.reg); free(arg->str.buffer); break; case FILTER_ARG_VALUE: if (arg->value.type == FILTER_STRING || arg->value.type == FILTER_CHAR) free(arg->value.str); break; case FILTER_ARG_OP: free_arg(arg->op.left); free_arg(arg->op.right); default: break; } free(arg); } static int add_event(struct event_list **events, struct event_format *event) { struct event_list *list; list = malloc(sizeof(*list)); if (list == NULL) return -1; list->next = *events; *events = list; list->event = event; return 0; } static int event_match(struct event_format *event, regex_t *sreg, regex_t *ereg) { if (sreg) { return !regexec(sreg, event->system, 0, NULL, 0) && !regexec(ereg, event->name, 0, NULL, 0); } return !regexec(ereg, event->system, 0, NULL, 0) || !regexec(ereg, event->name, 0, NULL, 0); } static enum pevent_errno find_event(struct pevent *pevent, struct event_list **events, char *sys_name, char *event_name) { struct event_format *event; regex_t ereg; regex_t sreg; int match = 0; int fail = 0; char *reg; int ret; int i; if (!event_name) { /* if no name is given, then swap sys and name */ event_name = sys_name; sys_name = NULL; } reg = malloc(strlen(event_name) + 3); if (reg == NULL) return PEVENT_ERRNO__MEM_ALLOC_FAILED; sprintf(reg, "^%s$", event_name); ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) return PEVENT_ERRNO__INVALID_EVENT_NAME; if (sys_name) { reg = malloc(strlen(sys_name) + 3); if (reg == NULL) { regfree(&ereg); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } sprintf(reg, "^%s$", sys_name); ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) { regfree(&ereg); return PEVENT_ERRNO__INVALID_EVENT_NAME; } } for (i = 0; i < pevent->nr_events; i++) { event = pevent->events[i]; if (event_match(event, sys_name ? &sreg : NULL, &ereg)) { match = 1; if (add_event(events, event) < 0) { fail = 1; break; } } } regfree(&ereg); if (sys_name) regfree(&sreg); if (!match) return PEVENT_ERRNO__EVENT_NOT_FOUND; if (fail) return PEVENT_ERRNO__MEM_ALLOC_FAILED; return 0; } static void free_events(struct event_list *events) { struct event_list *event; while (events) { event = events; events = events->next; free(event); } } static enum pevent_errno create_arg_item(struct event_format *event, const char *token, enum event_type type, struct filter_arg **parg, char *error_str) { struct format_field *field; struct filter_arg *arg; arg = allocate_arg(); if (arg == NULL) { show_error(error_str, "failed to allocate filter arg"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } switch (type) { case EVENT_SQUOTE: case EVENT_DQUOTE: arg->type = FILTER_ARG_VALUE; arg->value.type = type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR; arg->value.str = strdup(token); if (!arg->value.str) { free_arg(arg); show_error(error_str, "failed to allocate string filter arg"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } break; case EVENT_ITEM: /* if it is a number, then convert it */ if (isdigit(token[0])) { arg->type = FILTER_ARG_VALUE; arg->value.type = FILTER_NUMBER; arg->value.val = strtoull(token, NULL, 0); break; } /* Consider this a field */ field = pevent_find_any_field(event, token); if (!field) { if (strcmp(token, COMM) != 0) { /* not a field, Make it false */ arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = FILTER_FALSE; break; } /* If token is 'COMM' then it is special */ field = &comm; } arg->type = FILTER_ARG_FIELD; arg->field.field = field; break; default: free_arg(arg); show_error(error_str, "expected a value but found %s", token); return PEVENT_ERRNO__UNEXPECTED_TYPE; } *parg = arg; return 0; } static struct filter_arg * create_arg_op(enum filter_op_type btype) { struct filter_arg *arg; arg = allocate_arg(); if (!arg) return NULL; arg->type = FILTER_ARG_OP; arg->op.type = btype; return arg; } static struct filter_arg * create_arg_exp(enum filter_exp_type etype) { struct filter_arg *arg; arg = allocate_arg(); if (!arg) return NULL; arg->type = FILTER_ARG_EXP; arg->op.type = etype; return arg; } static struct filter_arg * create_arg_cmp(enum filter_exp_type etype) { struct filter_arg *arg; arg = allocate_arg(); if (!arg) return NULL; /* Use NUM and change if necessary */ arg->type = FILTER_ARG_NUM; arg->op.type = etype; return arg; } static enum pevent_errno add_right(struct filter_arg *op, struct filter_arg *arg, char *error_str) { struct filter_arg *left; char *str; int op_type; int ret; switch (op->type) { case FILTER_ARG_EXP: if (op->exp.right) goto out_fail; op->exp.right = arg; break; case FILTER_ARG_OP: if (op->op.right) goto out_fail; op->op.right = arg; break; case FILTER_ARG_NUM: if (op->op.right) goto out_fail; /* * The arg must be num, str, or field */ switch (arg->type) { case FILTER_ARG_VALUE: case FILTER_ARG_FIELD: break; default: show_error(error_str, "Illegal rvalue"); return PEVENT_ERRNO__ILLEGAL_RVALUE; } /* * Depending on the type, we may need to * convert this to a string or regex. */ switch (arg->value.type) { case FILTER_CHAR: /* * A char should be converted to number if * the string is 1 byte, and the compare * is not a REGEX. */ if (strlen(arg->value.str) == 1 && op->num.type != FILTER_CMP_REGEX &&