Hello. My idea is to allow user to specify simple filters for monitoring udev events. Events are printed only if pass all of filters. There is only GL_PLAIN comparison now. e.g.: $ udevadm monitor --filter='SUBSYSTEM=="bluetooth"' --filter='ACTION!="add"' This command prints only events which match "bluetooth" SUBSYSTEM and ACTION other than "add". I'll be glad to hear your opinions --- udev/udevadm-monitor.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 206 insertions(+), 3 deletions(-) diff --git a/udev/udevadm-monitor.c b/udev/udevadm-monitor.c index 77d98fa..0daae41 100644 --- a/udev/udevadm-monitor.c +++ b/udev/udevadm-monitor.c @@ -33,6 +33,18 @@ #include "udev.h" +enum operation_type { + OP_MATCH, + OP_NOMATCH +}; + +struct filter_rule { + char *key; + char *value; + enum operation_type op; + struct filter_rule *next; +}; + static int udev_exit; static void asmlinkage sig_handler(int signum) @@ -64,6 +76,174 @@ static void print_device(struct udev_device *device, const char *source, int env } } +static int add_filter_rule (struct filter_rule **rules, const char *filter) { + char *filterpos = NULL; + char *keypos; + + filterpos = strdup(filter); + if (filterpos == NULL) + return -1; + + keypos = filterpos; + while (*keypos != '\0') { + char *key; + char *value; + char *temp; + enum operation_type op; + struct filter_rule *new_rule; + + /* udev-rules.c:877 */ + /* skip whitespace */ + while (isspace(*keypos) || *keypos == ',') { + keypos++; + } + + /* end of filter, no conflict */ + if (*keypos == '\0') { + free(filterpos); + return 0; + } + + /* find end of key */ + key = keypos; + while (1) { + keypos++; + if (keypos[0] == '\0') { + goto error; + } + if (isspace(keypos[0])) + break; + if (keypos[0] == '=') + break; + if ((keypos[0] == '+') || (keypos[0] == '!') || (keypos[0] == ':')) + if (keypos[1] == '=') + break; + } + + temp = keypos; + while (isspace(keypos[0])) + keypos++; + if (keypos[0] == '\0') { + goto error; + } + + /* get operation type */ + if (keypos[0] == '=' && keypos[1] == '=') { + op = OP_MATCH; + keypos += 2; + } else if (keypos[0] == '!' && keypos[1] == '=') { + op = OP_NOMATCH; + keypos += 2; + } + else { + goto error; + } + + /* terminate key */ + temp[0] = '\0'; + + /* skip whitespace after operator */ + while (isspace(keypos[0])) + keypos++; + if (keypos[0] == '\0') { + goto error; + } + + /* get the value */ + if (keypos[0] == '"') + keypos++; + else { + goto error; + } + value = keypos; + + /* terminate */ + temp = strchr(keypos, '"'); + if (!temp) { + goto error; + } + temp[0] = '\0'; + keypos = temp + 1 ; + + new_rule = (struct filter_rule *)malloc(sizeof(struct filter_rule)); + if (new_rule == NULL) + goto error; + + new_rule->key = strdup(key); + if (new_rule->key == NULL) { + free(new_rule); + goto error; + } + + new_rule->value = strdup(value); + if (new_rule->value == NULL) { + free(new_rule->key); + free(new_rule); + goto error; + } + + new_rule->op = op; + + new_rule->next = *rules; + *rules = new_rule; + } + + free(filterpos); + return 0; + +error: + free(filterpos); + return -1; +} + +static int free_rules(struct filter_rule *rules) { + struct filter_rule *rulespos, *rulestmp; + + for (rulespos = rules; rulespos; rulespos = rulespos->next) { + rulestmp = rulespos; + free(rulestmp->key); + free(rulestmp->value); + free(rulestmp); + } + return 0; +} + +static int filter_device(struct udev_device *device, struct filter_rule *filter) { + struct filter_rule *filterpos; + struct udev_list_entry *list_entry; + + list_entry = udev_device_get_properties_list_entry(device); + if (list_entry == NULL) { + return 0; + } + + for (filterpos = filter; filterpos; filterpos = filterpos->next) { + int match; + struct udev_list_entry *entry; + + entry = udev_list_entry_get_by_name(list_entry, filterpos->key); + if (entry == NULL) { + return 0; + } + + /*udev-rules.c:1871 GL_PLAIN*/ + /* TODO: GL_GLOB, GL_SPLIT, GL_SPLIT_GLOB + * TODO: ATTR*/ + + match = ! strcasecmp(filterpos->value, udev_list_entry_get_value(entry)); + + /* udev-rules.c:1930 */ + if ( !match && (filterpos->op == OP_MATCH)) { + return 0; + } + if ( !match && (filterpos->op == OP_NOMATCH)) { + return 0; + } + } + return 1; +} + + int udevadm_monitor(struct udev *udev, int argc, char *argv[]) { struct sigaction act; @@ -76,16 +256,19 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[]) fd_set readfds; int rc = 0; + struct filter_rule *filter = NULL; + static const struct option options[] = { { "environment", no_argument, NULL, 'e' }, { "kernel", no_argument, NULL, 'k' }, { "udev", no_argument, NULL, 'u' }, + { "filter", required_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, {} }; while (1) { - option = getopt_long(argc, argv, "ekuh", options, NULL); + option = getopt_long(argc, argv, "ekuf:h", options, NULL); if (option == -1) break; @@ -99,11 +282,20 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[]) case 'u': print_udev = 1; break; + case 'f': + if (add_filter_rule(&filter, optarg) < 0) { + /* error */ + fprintf(stderr, "can't parse filter: %s\n", optarg); + goto out; + } + break; + case 'h': printf("Usage: udevadm monitor [--environment] [--kernel] [--udev] [--help]\n" " --env print the whole event environment\n" " --kernel print kernel uevents\n" " --udev print udev events\n" + " --filter=<udev rule> print udev events matching rule\n" " --help\n\n"); default: goto out; @@ -139,6 +331,7 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[]) rc = 2; goto out; } + printf("UDEV the event which udev sends out after rule processing\n"); } if (print_kernel) { @@ -178,7 +371,9 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[]) device = udev_monitor_receive_device(kernel_monitor); if (device == NULL) continue; - print_device(device, "UEVENT", env); + + if ( !filter || filter_device(device, filter)) + print_device(device, "UEVENT", env); udev_device_unref(device); } @@ -188,13 +383,21 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[]) device = udev_monitor_receive_device(udev_monitor); if (device == NULL) continue; - print_device(device, "UDEV", env); + + if ( !filter || filter_device(device, filter)) + print_device(device, "UDEV", env); udev_device_unref(device); } } + out: udev_monitor_unref(udev_monitor); udev_monitor_unref(kernel_monitor); + + if (filter) + /* clean filter */ + free_rules(filter); + return rc; } -- 1.6.0.6 -- To unsubscribe from this list: send the line "unsubscribe linux-hotplug" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html