[PATCH] udevadm monitor --filter= with GL_PLAIN comparison

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Kernel]     [Linux DVB]     [Asterisk Internet PBX]     [DCCP]     [Netdev]     [X.org]     [Util Linux NG]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux