* [PATCH] udevadm monitor --filter= with GL_PLAIN comparison
@ 2009-03-18 19:34 Petr Lautrbach
2009-03-19 1:33 ` Kay Sievers
2009-03-19 9:38 ` Petr Lautrbach
0 siblings, 2 replies; 3+ messages in thread
From: Petr Lautrbach @ 2009-03-18 19:34 UTC (permalink / raw)
To: linux-hotplug
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
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] udevadm monitor --filter= with GL_PLAIN comparison
2009-03-18 19:34 [PATCH] udevadm monitor --filter= with GL_PLAIN comparison Petr Lautrbach
@ 2009-03-19 1:33 ` Kay Sievers
2009-03-19 9:38 ` Petr Lautrbach
1 sibling, 0 replies; 3+ messages in thread
From: Kay Sievers @ 2009-03-19 1:33 UTC (permalink / raw)
To: linux-hotplug
On Wed, Mar 18, 2009 at 20:34, Petr Lautrbach <plautrba@redhat.com> wrote:
> 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
Funny. What are you using the monitor for, that you want to filter on
events? It's a pure debugging tool, and you usually need to see all
events in the sequence, like the parent devices coming and going.
Thanks,
Kay
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] udevadm monitor --filter= with GL_PLAIN comparison
2009-03-18 19:34 [PATCH] udevadm monitor --filter= with GL_PLAIN comparison Petr Lautrbach
2009-03-19 1:33 ` Kay Sievers
@ 2009-03-19 9:38 ` Petr Lautrbach
1 sibling, 0 replies; 3+ messages in thread
From: Petr Lautrbach @ 2009-03-19 9:38 UTC (permalink / raw)
To: linux-hotplug
Kay Sievers wrote:
> On Wed, Mar 18, 2009 at 20:34, Petr Lautrbach <plautrba@redhat.com> wrote:
>> 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
>
> Funny. What are you using the monitor for, that you want to filter on
> events? It's a pure debugging tool, and you usually need to see all
> events in the sequence, like the parent devices coming and going.
>
One of goal of Fedora Powermanagement feature is framework that will allow services to start
depending on installed hardware or software and I'm using this filter to monitor only
events which are defined in my udev rules e.g. for bluetooth see [1].
At first, I was writing rules with RUN+="socket:..." and perl script waiting for events on socket but
this way is looks better for me and it's usable without need any other socket or script.
[1] https://bugzilla.redhat.com/show_bug.cgi?idH4345#c12
Regards,
Petr
--
Petr Lautrbach, Red Hat, Inc.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2009-03-19 9:38 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-03-18 19:34 [PATCH] udevadm monitor --filter= with GL_PLAIN comparison Petr Lautrbach
2009-03-19 1:33 ` Kay Sievers
2009-03-19 9:38 ` Petr Lautrbach
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).