X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev_rules.c;h=cbd76e66ebe84f3f4f31a82fdffa3de0c1e69973;hp=2b5c411d963e7af71e7e66176a8d5e27657aac4c;hb=7628ff6e144a94c48e463d0188022f5661e0ba40;hpb=7ff56624f84d1616470ab6cbeab17c1367e775d7 diff --git a/udev_rules.c b/udev_rules.c index 2b5c411d9..cbd76e66e 100644 --- a/udev_rules.c +++ b/udev_rules.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "libsysfs/sysfs/libsysfs.h" #include "list.h" @@ -327,7 +326,7 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize, struct udevice udev_parent; dbg("found parent '%s', get the node name", class_dev_parent->path); - udev_init_device(&udev_parent, NULL, NULL); + udev_init_device(&udev_parent, NULL, NULL, NULL); /* lookup the name in the udev_db with the DEVPATH of the parent */ if (udev_db_get_device(&udev_parent, &class_dev_parent->path[strlen(sysfs_path)]) == 0) { strlcat(string, udev_parent.name, maxsize); @@ -364,21 +363,22 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize, } } -static int execute_program(struct udevice *udev, const char *path, char *value, int len) +static int execute_program_pipe(const char *command, const char *subsystem, char *value, int len) { int retval; int count; int status; - int fds[2]; + int pipefds[2]; pid_t pid; char *pos; char arg[PATH_SIZE]; char *argv[(sizeof(arg) / 2) + 1]; + int devnull; int i; - strlcpy(arg, path, sizeof(arg)); + strlcpy(arg, command, sizeof(arg)); i = 0; - if (strchr(path, ' ')) { + if (strchr(arg, ' ')) { pos = arg; while (pos != NULL) { if (pos[0] == '\'') { @@ -397,12 +397,12 @@ static int execute_program(struct udevice *udev, const char *path, char *value, dbg("execute '%s' with parsed arguments", arg); } else { argv[0] = arg; - argv[1] = udev->subsystem; + argv[1] = (char *) subsystem; argv[2] = NULL; dbg("execute '%s' with subsystem '%s' argument", arg, argv[1]); } - retval = pipe(fds); + retval = pipe(pipefds); if (retval != 0) { err("pipe failed"); return -1; @@ -412,22 +412,27 @@ static int execute_program(struct udevice *udev, const char *path, char *value, switch(pid) { case 0: /* child dup2 write side of pipe to STDOUT */ - dup2(fds[1], STDOUT_FILENO); + devnull = open("/dev/null", O_RDWR); + if (devnull >= 0) { + dup2(devnull, STDIN_FILENO); + dup2(devnull, STDERR_FILENO); + close(devnull); + } + dup2(pipefds[1], STDOUT_FILENO); retval = execv(arg, argv); - err("exec of program failed"); _exit(1); case -1: - err("fork of '%s' failed", path); + err("fork of '%s' failed", arg); retval = -1; break; default: - /* parent reads from fds[0] */ - close(fds[1]); + /* parent reads from pipefds[0] */ + close(pipefds[1]); retval = 0; i = 0; while (1) { - count = read(fds[0], value + i, len - i-1); + count = read(pipefds[0], value + i, len - i-1); if (count < 0) { err("read failed with '%s'", strerror(errno)); retval = -1; @@ -445,7 +450,7 @@ static int execute_program(struct udevice *udev, const char *path, char *value, } value[i] = '\0'; - close(fds[0]); + close(pipefds[0]); waitpid(pid, &status, 0); if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { @@ -469,10 +474,25 @@ 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' class_dev->name='%s'", - rule->kernel, class_dev->name); - if (strcmp_pattern(rule->kernel, class_dev->name) != 0) { + 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) goto exit; @@ -485,8 +505,8 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule, } if (rule->subsystem_operation != KEY_OP_UNSET) { - dbg("check for " KEY_SUBSYSTEM " rule->subsystem='%s' class_dev->name='%s'", - rule->subsystem, class_dev->name); + 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) @@ -642,7 +662,8 @@ try_parent: dbg("check " KEY_PROGRAM); strlcpy(program, rule->program, sizeof(program)); apply_format(udev, program, sizeof(program), class_dev, sysfs_device); - if (execute_program(udev, program, udev->program_result, sizeof(udev->program_result)) != 0) { + if (execute_program_pipe(program, udev->subsystem, + udev->program_result, sizeof(udev->program_result)) != 0) { dbg(KEY_PROGRAM " returned nonzero"); if (rule->program_operation != KEY_OP_NOMATCH) goto exit; @@ -710,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_set && rule->name_operation != KEY_OP_UNSET) { + 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; @@ -728,72 +754,116 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d } /* apply permissions */ - if (rule->mode != 0000) { + if (!udev->mode_final && rule->mode != 0000) { + if (rule->mode_operation == KEY_OP_ASSIGN_FINAL) + udev->mode_final = 1; udev->mode = rule->mode; dbg("applied mode=%#o to '%s'", udev->mode, udev->kernel_name); } - if (rule->owner[0] != '\0') { + if (!udev->owner_final && rule->owner[0] != '\0') { + if (rule->owner_operation == KEY_OP_ASSIGN_FINAL) + udev->owner_final = 1; strlcpy(udev->owner, rule->owner, sizeof(udev->owner)); apply_format(udev, udev->owner, sizeof(udev->owner), class_dev, sysfs_device); dbg("applied owner='%s' to '%s'", udev->owner, udev->kernel_name); } - if (rule->group[0] != '\0') { + if (!udev->group_final && rule->group[0] != '\0') { + if (rule->group_operation == KEY_OP_ASSIGN_FINAL) + udev->group_final = 1; strlcpy(udev->group, rule->group, sizeof(udev->group)); apply_format(udev, udev->group, sizeof(udev->group), class_dev, sysfs_device); dbg("applied group='%s' to '%s'", udev->group, udev->kernel_name); } /* collect symlinks */ - if (rule->symlink[0] != '\0') { + if (!udev->symlink_final && rule->symlink_operation != KEY_OP_UNSET) { char temp[PATH_SIZE]; char *pos, *next; - info("configured rule in '%s[%i]' applied, added symlink '%s'", - rule->config_file, rule->config_line, rule->symlink); - strlcpy(temp, rule->symlink, sizeof(temp)); - apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device); - - /* add multiple symlinks separated by spaces */ - pos = temp; - next = strchr(temp, ' '); - while (next) { - next[0] = '\0'; + if (rule->symlink_operation == KEY_OP_ASSIGN_FINAL) + udev->symlink_final = 1; + if (rule->symlink_operation == KEY_OP_ASSIGN || rule->symlink_operation == KEY_OP_ASSIGN_FINAL) { + struct name_entry *name_loop; + struct name_entry *temp_loop; + + info("reset symlink list"); + list_for_each_entry_safe(name_loop, temp_loop, &udev->symlink_list, node) { + list_del(&name_loop->node); + free(name_loop); + } + } + if (rule->symlink[0] != '\0') { + info("configured rule in '%s[%i]' applied, added symlink '%s'", + rule->config_file, rule->config_line, rule->symlink); + strlcpy(temp, rule->symlink, sizeof(temp)); + apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device); + + /* add multiple symlinks separated by spaces */ + pos = temp; + next = strchr(temp, ' '); + while (next) { + next[0] = '\0'; + info("add symlink '%s'", pos); + name_list_add(&udev->symlink_list, pos, 0); + pos = &next[1]; + next = strchr(pos, ' '); + } info("add symlink '%s'", pos); name_list_add(&udev->symlink_list, pos, 0); - pos = &next[1]; - next = strchr(pos, ' '); } - info("add symlink '%s'", pos); - name_list_add(&udev->symlink_list, pos, 0); } - /* rule matches */ - 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); + /* set name, later rules with name set will be ignored */ + if (rule->name_operation != KEY_OP_UNSET) { + udev->name_set = 1; + if (rule->name[0] == '\0') { + info("configured rule in '%s[%i]' applied, node handling for '%s' supressed", + rule->config_file, rule->config_line, udev->kernel_name); + } else { + strlcpy(udev->name, rule->name, sizeof(udev->name)); + apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device); + strlcpy(udev->config_file, rule->config_file, sizeof(udev->config_file)); + udev->config_line = rule->config_line; + + info("configured rule in '%s:%i' applied, '%s' becomes '%s'", + rule->config_file, rule->config_line, udev->kernel_name, rule->name); + 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); + } + } - strlcpy(udev->name, rule->name, sizeof(udev->name)); - apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device); - strlcpy(udev->config_file, rule->config_file, sizeof(udev->config_file)); - udev->config_line = rule->config_line; + if (!udev->run_final && rule->run_operation != KEY_OP_UNSET) { + char program[PATH_SIZE]; - 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); + if (rule->run_operation == KEY_OP_ASSIGN_FINAL) + udev->run_final = 1; + if (rule->run_operation == KEY_OP_ASSIGN || rule->run_operation == KEY_OP_ASSIGN_FINAL) { + struct name_entry *name_loop; + struct name_entry *temp_loop; - break; + info("reset run list"); + list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) { + list_del(&name_loop->node); + free(name_loop); + } + } + if (rule->run[0] != '\0') { + 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); } @@ -806,3 +876,61 @@ 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 sysfs_device *sysfs_device) +{ + struct udev_rule *rule; + + /* look for a matching rule to apply */ + list_for_each_entry(rule, &udev_rule_list, node) { + dbg("process rule"); + + if (rule->run_operation == KEY_OP_UNSET) + continue; + + if (rule->name_operation != KEY_OP_UNSET || rule->symlink_operation != KEY_OP_UNSET || + rule->mode != 0000 || rule->owner[0] != '\0' || rule->group[0] != '\0') { + dbg("skip rule that names a device"); + continue; + } + + if (match_rule(udev, rule, NULL, sysfs_device) == 0) { + 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; + } + + if (!udev->run_final && rule->run_operation != KEY_OP_UNSET) { + char program[PATH_SIZE]; + + if (rule->run_operation == KEY_OP_ASSIGN || rule->run_operation == KEY_OP_ASSIGN_FINAL) { + struct name_entry *name_loop; + struct name_entry *temp_loop; + + info("reset run list"); + list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) { + list_del(&name_loop->node); + free(name_loop); + } + } + if (rule->run[0] != '\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->run_operation == KEY_OP_ASSIGN_FINAL) + break; + } + + if (rule->last_rule) { + dbg("last rule to be applied"); + break; + } + } + } + + return 0; +}