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); |