diff options
| author | kay.sievers@vrfy.org <kay.sievers@vrfy.org> | 2005-04-02 17:45:35 +0200 | 
|---|---|---|
| committer | Greg KH <gregkh@suse.de> | 2005-04-26 23:55:00 -0700 | 
| commit | 821d0ec803a72841f173739f5b713fe847edab75 (patch) | |
| tree | 4866ffc8b8c367cd6e62fd0616fffde01757e3e7 | |
| parent | e03a196a0d0680868ea230ab8f8d100ee90d0fa4 (diff) | |
[PATCH] add RUN key to be able to run rule based notification
SUBSYSTEM=="block", RUN="/sbin/program"
  will execute the program only for block device events.
ACTION="remove", SUBSYSTEM=="block", RUN"/sbin/program"
  will execute the program, if a block device is removed.
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | test/udev-test.pl | 47 | ||||
| -rw-r--r-- | udev.8.in | 13 | ||||
| -rw-r--r-- | udev.c | 19 | ||||
| -rw-r--r-- | udev.h | 4 | ||||
| -rw-r--r-- | udev_add.c | 5 | ||||
| -rw-r--r-- | udev_config.c | 7 | ||||
| -rw-r--r-- | udev_rules.c | 148 | ||||
| -rw-r--r-- | udev_rules.h | 6 | ||||
| -rw-r--r-- | udev_rules_parse.c | 13 | ||||
| -rw-r--r-- | udev_utils.c | 4 | ||||
| -rw-r--r-- | udevd.c | 4 | ||||
| -rw-r--r-- | udevstart.c | 8 | 
13 files changed, 266 insertions, 13 deletions
| @@ -51,7 +51,6 @@ VERSION =	056  INSTALL_DIR =	/usr/local/bin  RELEASE_NAME =	$(ROOT)-$(VERSION)  LOCAL_CFG_DIR =	etc/udev -HOTPLUG_EXEC =	$(ROOT)  DESTDIR =  KERNEL_DIR = /lib/modules/${shell uname -r}/build diff --git a/test/udev-test.pl b/test/udev-test.pl index 27ca37115c..91e91bb2ae 100644 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1245,6 +1245,53 @@ KERNEL=="sda", SYSFS{vendor}!="", NAME="ok"  KERNEL=="sda", SYSFS{vendor}=="", NAME="not-3-ok"  EOF  	}, +	{ +		desc		=> "check ACTION value", +		subsys		=> "block", +		devpath		=> "/block/sda", +		exp_name	=> "ok", +		rules		=> <<EOF +ACTION=="unknown", KERNEL=="sda", NAME="unknown-not-ok" +ACTION=="add", KERNEL=="sda", NAME="ok" +EOF +	}, +	{ +		desc		=> "apply NAME only once", +		subsys		=> "block", +		devpath		=> "/block/sda", +		exp_name	=> "link", +		exp_target	=> "ok", +		rules		=> <<EOF +KERNEL=="sda", NAME="ok" +KERNEL=="sda", NAME="not-ok" +KERNEL=="sda", SYMLINK+="link" +EOF +	}, +	{ +		desc		=> "test RUN key", +		subsys		=> "block", +		devpath		=> "/block/sda", +		exp_name	=> "testsymlink", +		exp_target	=> "ok", +		exp_rem_error	=> "yes", +		option		=> "clean", +		rules		=> <<EOF +KERNEL=="sda", NAME="ok", RUN+="/bin/ln -s ok %r/testsymlink" +KERNEL=="sda", NAME="not-ok" +EOF +	}, +	{ +		desc		=> "test RUN key remove", +		subsys		=> "block", +		devpath		=> "/block/sda", +		exp_name	=> "testsymlink2", +		exp_target	=> "ok2", +		rules		=> <<EOF +KERNEL=="sda", NAME="ok2", RUN+="/bin/ln -s ok2 %r/testsymlink2" +KERNEL=="sda", ACTION=="remove", RUN+="/bin/rm -f %r/testsymlink2" +KERNEL=="sda", NAME="not-ok2" +EOF +	},  );  # set env @@ -111,6 +111,9 @@ Match the kernel device name.  .B SUBSYSTEM  Match the kernel subsystem name.  .TP +.B ACTION +Match the kernel action name. +.TP  .B DRIVER  Match the kernel driver name.  .TP @@ -170,6 +173,9 @@ distribution provided rules file.  The permissions for the device node. Every specified value overwrites the  compiled-in default value.  .TP +.B RUN +Add a program to the list of programs to be executed for a specific device. +.TP  .B OPTIONS  .B last_rule  will be the last rule applied. No later rules will have any effect. @@ -234,6 +240,9 @@ The node name of the parent device.  .BI %s{ filename }  The content of a sysfs attribute.  .TP +.B %r +The udev_root value. +.TP  .B %e  If a device node already exists with the name, the smallest positive  decimal integer N is substituted such that the resulting name doesn't @@ -344,6 +353,9 @@ config file.  .B UDEV_LOG  Overrides the log priority specified in the config file.  .TP +.B UDEV_RUN +If set to "0", it disables the execution of programs added by rules. +.TP  .B UDEV_NO_DEVD  The default behavior of  .B udev @@ -356,7 +368,6 @@ will skip this step.  .nf  /sbin/udev                           udev program  /etc/udev/*                          udev config files -/etc/hotplug.d/default/udev.hotplug  hotplug symlink to udev program  /etc/dev.d/*                         programs invoked by udev  .fi  .SH "SEE ALSO" @@ -157,6 +157,8 @@ int main(int argc, char *argv[], char *envp[])  	}  	if (udev.type == DEV_BLOCK || udev.type == DEV_CLASS || udev.type == DEV_NET) { +		udev_rules_init(); +  		if (strcmp(action, "add") == 0) {  			/* wait for sysfs and possibly add node */  			dbg("udev add"); @@ -178,9 +180,6 @@ int main(int argc, char *argv[], char *envp[])  			wait_for_class_device(class_dev, &error); -			/* init rules */ -			udev_rules_init(); -  			/* name, create node, store in db */  			retval = udev_add_device(&udev, class_dev); @@ -195,10 +194,24 @@ int main(int argc, char *argv[], char *envp[])  				goto hotplug;  			} +			udev_rules_get_run(&udev); +			if (udev.ignore_device) { +				dbg("device event will be ignored"); +				goto hotplug; +			} +  			/* get node from db, remove db-entry, delete created node */  			retval = udev_remove_device(&udev);  		} +		if (udev_run && !list_empty(&udev.run_list)) { +			struct name_entry *name_loop; + +			dbg("executing run list"); +			list_for_each_entry(name_loop, &udev.run_list, node) +				execute_command(name_loop->name, udev.subsystem); +		} +  		/* run dev.d/ scripts if we created/deleted a node or changed a netif name */  		if (udev.devname[0] != '\0') {  			setenv("DEVNAME", udev.devname, 1); @@ -58,6 +58,7 @@ enum device_type {  struct udevice {  	char devpath[PATH_SIZE];  	char subsystem[NAME_SIZE]; +	char action[NAME_SIZE];  	enum device_type type;  	char name[PATH_SIZE]; @@ -67,9 +68,11 @@ struct udevice {  	char group[USER_SIZE];  	mode_t mode;  	dev_t devt; +	struct list_head run_list;  	char tmp_node[PATH_SIZE];  	int partitions; +	int ignore_device;  	int ignore_remove;  	int config_line;  	char config_file[PATH_SIZE]; @@ -93,6 +96,7 @@ extern char udev_db_path[PATH_SIZE];  extern char udev_config_filename[PATH_SIZE];  extern char udev_rules_filename[PATH_SIZE];  extern int udev_log_priority; +extern int udev_run;  extern int udev_dev_d;  extern int udev_hotplug_d; diff --git a/udev_add.c b/udev_add.c index d0d9eab913..5fff3836e3 100644 --- a/udev_add.c +++ b/udev_add.c @@ -276,8 +276,11 @@ int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev)  		}  	} -	if (udev_rules_get_name(udev, class_dev) != 0) +	udev_rules_get_name(udev, class_dev); +	if (udev->ignore_device) { +		dbg("device event will be ignored");  		return 0; +	}  	dbg("adding name='%s'", udev->name); diff --git a/udev_config.c b/udev_config.c index b6f578b27d..6bc070d63a 100644 --- a/udev_config.c +++ b/udev_config.c @@ -44,6 +44,7 @@ char udev_db_path[PATH_SIZE];  char udev_config_filename[PATH_SIZE];  char udev_rules_filename[PATH_SIZE];  int udev_log_priority; +int udev_run;  int udev_dev_d;  int udev_hotplug_d; @@ -217,10 +218,16 @@ void udev_init_config(void)  	strcpy(udev_config_filename, UDEV_CONFIG_FILE);  	strcpy(udev_rules_filename, UDEV_RULES_FILE);  	udev_log_priority = LOG_ERR; +	udev_run = 1;  	udev_dev_d = 1;  	udev_hotplug_d = 1;  	sysfs_get_mnt_path(sysfs_path, sizeof(sysfs_path)); +	/* disable RUN key execution */ +	env = getenv("UDEV_RUN"); +	if (env && !string_is_true(env)) +		udev_run = 0; +  	env = getenv("UDEV_NO_DEVD");  	if (env && string_is_true(env))  		udev_dev_d = 0; diff --git a/udev_rules.c b/udev_rules.c index 069dec7d04..6f82fac841 100644 --- a/udev_rules.c +++ b/udev_rules.c @@ -474,6 +474,21 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule,  {  	struct sysfs_device *parent_device = sysfs_device; +	if (rule->action_operation != KEY_OP_UNSET) { +		dbg("check for " KEY_ACTION " rule->action='%s' udev->action='%s'", +		    rule->action, udev->action); +		if (strcmp_pattern(rule->action, udev->action) != 0) { +			dbg(KEY_ACTION " is not matching"); +			if (rule->action_operation != KEY_OP_NOMATCH) +				goto exit; +		} else { +			dbg(KEY_ACTION " matches"); +			if (rule->action_operation == KEY_OP_NOMATCH) +				goto exit; +		} +		dbg(KEY_ACTION " key is true"); +	} +  	if (rule->kernel_operation != KEY_OP_UNSET) {  		dbg("check for " KEY_KERNEL " rule->kernel='%s' udev_kernel_name='%s'",  		    rule->kernel, udev->kernel_name); @@ -716,12 +731,17 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d  	list_for_each_entry(rule, &udev_rule_list, node) {  		dbg("process rule");  		if (match_rule(udev, rule, class_dev, sysfs_device) == 0) { +			if (udev->name[0] != '\0' && rule->name[0] != '\0') { +				dbg("node name already set, rule ignored"); +				continue; +			}  			/* apply options */  			if (rule->ignore_device) {  				info("configured rule in '%s[%i]' applied, '%s' is ignored",  				     rule->config_file, rule->config_line, udev->kernel_name); -				return -1; +				udev->ignore_device = 1; +				return 0;  			}  			if (rule->ignore_remove) {  				udev->ignore_remove = 1; @@ -773,7 +793,7 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d  				name_list_add(&udev->symlink_list, pos, 0);  			} -			/* rule matches */ +			/* set name, later rules with name set will be ignored */  			if (rule->name[0] != '\0') {  				info("configured rule in '%s[%i]' applied, '%s' becomes '%s'",  				     rule->config_file, rule->config_line, udev->kernel_name, rule->name); @@ -786,20 +806,25 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d  				if (udev->type != DEV_NET)  					dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i",  					    udev->name, udev->owner, udev->group, udev->mode, udev->partitions); +			} -				break; +			if (rule->run[0] != '\0') { +				char program[PATH_SIZE]; + +				strlcpy(program, rule->run, sizeof(program)); +				apply_format(udev, program, sizeof(program), class_dev, sysfs_device); +				dbg("add run '%s'", program); +				name_list_add(&udev->run_list, program, 0);  			}  			if (rule->last_rule) {  				dbg("last rule to be applied");  				break;  			} -  		}  	}  	if (udev->name[0] == '\0') { -		/* no rule matched, so we use the kernel name */  		strlcpy(udev->name, udev->kernel_name, sizeof(udev->name));  		info("no rule found, use kernel name '%s'", udev->name);  	} @@ -812,3 +837,116 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d  	return 0;  } + +int udev_rules_get_run(struct udevice *udev) +{ +	struct udev_rule *rule; +	char program[PATH_SIZE]; + +	/* look for a matching rule to apply */ +	list_for_each_entry(rule, &udev_rule_list, node) { +		dbg("process rule"); + +		if (rule->run[0] == '\0') +			continue; + +		if (rule->name[0] != '\0' || rule->symlink[0] != '\0' || +		    rule->mode != 0000 || rule->owner[0] != '\0' || rule->group[0] != '\0') { +			dbg("skip rule that names a device"); +			continue; +		} + +		if (rule->action_operation != KEY_OP_UNSET) { +			dbg("check for " KEY_ACTION " rule->action='%s' udev->action='%s'", +			    rule->action, udev->action); +			if (strcmp_pattern(rule->action, udev->action) != 0) { +				dbg(KEY_ACTION " is not matching"); +				if (rule->action_operation != KEY_OP_NOMATCH) +					continue; +			} else { +				dbg(KEY_ACTION " matches"); +				if (rule->action_operation == KEY_OP_NOMATCH) +					continue; +			} +			dbg(KEY_ACTION " key is true"); +		} + +		if (rule->kernel_operation != KEY_OP_UNSET) { +			dbg("check for " KEY_KERNEL " rule->kernel='%s' udev->kernel_name='%s'", +			    rule->kernel, udev->kernel_name); +			if (strcmp_pattern(rule->kernel, udev->kernel_name) != 0) { +				dbg(KEY_KERNEL " is not matching"); +				if (rule->kernel_operation != KEY_OP_NOMATCH) +					continue; +		} else { +				dbg(KEY_KERNEL " matches"); +				if (rule->kernel_operation == KEY_OP_NOMATCH) +					continue; +			} +			dbg(KEY_KERNEL " key is true"); +		} + +		if (rule->subsystem_operation != KEY_OP_UNSET) { +			dbg("check for " KEY_SUBSYSTEM " rule->subsystem='%s' udev->subsystem='%s'", +			    rule->subsystem, udev->subsystem); +			if (strcmp_pattern(rule->subsystem, udev->subsystem) != 0) { +				dbg(KEY_SUBSYSTEM " is not matching"); +				if (rule->subsystem_operation != KEY_OP_NOMATCH) +					continue; +			} else { +				dbg(KEY_SUBSYSTEM " matches"); +				if (rule->subsystem_operation == KEY_OP_NOMATCH) +					continue; +			} +			dbg(KEY_SUBSYSTEM " key is true"); +		} + +		if (rule->env_pair_count) { +			int i; + +			dbg("check for " KEY_ENV " pairs"); +			for (i = 0; i < rule->env_pair_count; i++) { +				struct key_pair *pair; +				const char *value; + +				pair = &rule->env_pair[i]; +				value = getenv(pair->name); +				if (!value) { +					dbg(KEY_ENV "{'%s'} is not found", pair->name); +					continue; +				} +				if (strcmp_pattern(pair->value, value) != 0) { +					dbg(KEY_ENV "{'%s'} is not matching", pair->name); +					if (pair->operation != KEY_OP_NOMATCH) +						continue; +				} else { +					dbg(KEY_ENV "{'%s'} matches", pair->name); +					if (pair->operation == KEY_OP_NOMATCH) +						continue; +				} +			} +			dbg(KEY_ENV " key is true"); +		} + +		/* rule matches */ + +		if (rule->ignore_device) { +			info("configured rule in '%s[%i]' applied, '%s' is ignored", +			     rule->config_file, rule->config_line, udev->kernel_name); +			udev->ignore_device = 1; +			return 0; +		} + +		strlcpy(program, rule->run, sizeof(program)); +		apply_format(udev, program, sizeof(program), NULL, NULL); +		dbg("add run '%s'", program); +		name_list_add(&udev->run_list, program, 0); + +		if (rule->last_rule) { +			dbg("last rule to be applied"); +			break; +		} +	} + +	return 0; +} diff --git a/udev_rules.h b/udev_rules.h index 561ba1687d..5fba2d5571 100644 --- a/udev_rules.h +++ b/udev_rules.h @@ -30,6 +30,7 @@  #define KEY_KERNEL		"KERNEL"  #define KEY_SUBSYSTEM		"SUBSYSTEM" +#define KEY_ACTION		"ACTION"  #define KEY_BUS			"BUS"  #define KEY_ID			"ID"  #define KEY_PROGRAM		"PROGRAM" @@ -42,6 +43,7 @@  #define KEY_OWNER		"OWNER"  #define KEY_GROUP		"GROUP"  #define KEY_MODE		"MODE" +#define KEY_RUN			"RUN"  #define KEY_OPTIONS		"OPTIONS"  #define OPTION_LAST_RULE	"last_rule" @@ -75,6 +77,8 @@ struct udev_rule {  	enum key_operation kernel_operation;  	char subsystem[NAME_SIZE];  	enum key_operation subsystem_operation; +	char action[NAME_SIZE]; +	enum key_operation action_operation;  	char bus[NAME_SIZE];  	enum key_operation bus_operation;  	char id[NAME_SIZE]; @@ -95,6 +99,7 @@ struct udev_rule {  	char owner[USER_SIZE];  	char group[USER_SIZE];  	mode_t mode; +	char run[PATH_SIZE];  	int last_rule;  	int ignore_device; @@ -109,6 +114,7 @@ extern struct list_head udev_rule_list;  extern int udev_rules_init(void);  extern int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev); +extern int udev_rules_get_run(struct udevice *udev);  extern void udev_rules_close(void);  #endif diff --git a/udev_rules_parse.c b/udev_rules_parse.c index 3c1631ab49..e665957b81 100644 --- a/udev_rules_parse.c +++ b/udev_rules_parse.c @@ -256,6 +256,13 @@ static int rules_parse(const char *filename)  				continue;  			} +			if (strcasecmp(key, KEY_ACTION) == 0) { +				strlcpy(rule.action, value, sizeof(rule.action)); +				rule.action_operation = operation; +				valid = 1; +				continue; +			} +  			if (strcasecmp(key, KEY_BUS) == 0) {  				strlcpy(rule.bus, value, sizeof(rule.bus));  				rule.bus_operation = operation; @@ -379,6 +386,12 @@ static int rules_parse(const char *filename)  				continue;  			} +			if (strcasecmp(key, KEY_RUN) == 0) { +				strlcpy(rule.run, value, sizeof(rule.run)); +				valid = 1; +				continue; +			} +  			if (strcasecmp(key, KEY_OPTIONS) == 0) {  				if (strstr(value, OPTION_LAST_RULE) != NULL) {  					dbg("last rule to be applied"); diff --git a/udev_utils.c b/udev_utils.c index 4695ef0d72..5cdfb1421d 100644 --- a/udev_utils.c +++ b/udev_utils.c @@ -45,10 +45,14 @@ int udev_init_device(struct udevice *udev, const char* devpath, const char *subs  	memset(udev, 0x00, sizeof(struct udevice));  	INIT_LIST_HEAD(&udev->symlink_list); +	INIT_LIST_HEAD(&udev->run_list);  	if (subsystem)  		strlcpy(udev->subsystem, subsystem, sizeof(udev->subsystem)); +	if (action) +		strlcpy(udev->action, action, sizeof(udev->action)); +  	if (devpath) {  		strlcpy(udev->devpath, devpath, sizeof(udev->devpath));  		remove_trailing_char(udev->devpath, '/'); @@ -145,7 +145,7 @@ static void msg_queue_insert(struct hotplug_msg *msg)  }  /* forks event and removes event from run queue when finished */ -static void udev_run(struct hotplug_msg *msg) +static void execute_udev(struct hotplug_msg *msg)  {  	char *const argv[] = { "udev", msg->subsystem, NULL };  	pid_t pid; @@ -349,7 +349,7 @@ static void exec_queue_manager(void)  		if (!msg) {  			/* move event to run list */  			list_move_tail(&loop_msg->node, &running_list); -			udev_run(loop_msg); +			execute_udev(loop_msg);  			running++;  			dbg("moved seq %llu to running list", loop_msg->seqnum);  		} else { diff --git a/udevstart.c b/udevstart.c index 4bd4795874..d41702efd1 100644 --- a/udevstart.c +++ b/udevstart.c @@ -126,6 +126,14 @@ static int add_device(const char *path, const char *subsystem)  	udev_init_device(&udev, devpath, subsystem, "add");  	udev_add_device(&udev, class_dev); +	if (udev_run && !list_empty(&udev.run_list)) { +		struct name_entry *name_loop; + +		dbg("executing run list"); +		list_for_each_entry(name_loop, &udev.run_list, node) +			execute_command(name_loop->name, udev.subsystem); +	} +  	/* run dev.d/ scripts if we created a node or changed a netif name */  	if (udev_dev_d && udev.devname[0] != '\0') {  		setenv("DEVNAME", udev.devname, 1); | 
