X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev_rules.c;h=d08edf7782fda5460153f9569efdfaa0623c5f21;hp=48397ac1c7bf3b9a6a74069b46ebff8683629f35;hb=0bfb84e1edd96b1e82ffd74c43b865e5d6dbf204;hpb=8bd41f36f793f7fc208ef6beb4b2b84e35a5e728 diff --git a/udev_rules.c b/udev_rules.c index 48397ac1c..d08edf778 100644 --- a/udev_rules.c +++ b/udev_rules.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "libsysfs/sysfs/libsysfs.h" #include "list.h" @@ -280,6 +281,46 @@ static int import_program_into_env(struct udevice *udev, const char *program) return import_keys_into_env(udev, result, reslen); } +static int import_parent_into_env(struct udevice *udev, struct sysfs_class_device *class_dev, const char *filter) +{ + struct sysfs_class_device *parent = sysfs_get_classdev_parent(class_dev); + int rc = -1; + + if (parent != NULL) { + struct udevice udev_parent; + struct name_entry *name_loop; + + dbg("found parent '%s', get the node name", parent->path); + udev_init_device(&udev_parent, NULL, NULL, NULL); + /* import the udev_db of the parent */ + if (udev_db_get_device(&udev_parent, &parent->path[strlen(sysfs_path)]) == 0) { + dbg("import stored parent env '%s'", udev_parent.name); + list_for_each_entry(name_loop, &udev_parent.env_list, node) { + char name[NAME_SIZE]; + char *pos; + + strlcpy(name, name_loop->name, sizeof(name)); + pos = strchr(name, '='); + if (pos) { + pos[0] = '\0'; + pos++; + if (strcmp_pattern(filter, name) == 0) { + dbg("import key '%s'", name_loop->name); + name_list_add(&udev->env_list, name_loop->name, 0); + setenv(name, pos, 1); + } else + dbg("skip key '%s'", name_loop->name); + } + } + rc = 0; + } else + dbg("parent not found in database"); + udev_cleanup_device(&udev_parent); + } + + return rc; +} + /* finds the lowest positive N such that N isn't present in the udevdb * if doesn't exist, 0 is returned, N otherwise */ @@ -335,6 +376,28 @@ attr_found: return 0; } +#define WAIT_LOOP_PER_SECOND 20 +static int wait_for_sysfs(struct udevice *udev, const char *file, int timeout) +{ + char filename[PATH_SIZE]; + struct stat stats; + int loop = timeout * WAIT_LOOP_PER_SECOND; + + snprintf(filename, sizeof(filename), "%s%s/%s", sysfs_path, udev->devpath, file); + filename[sizeof(filename)-1] = '\0'; + dbg("wait %i sec for '%s'", timeout, filename); + + while (--loop) { + if (stat(filename, &stats) == 0) { + dbg("file appeared after %i loops", (timeout * WAIT_LOOP_PER_SECOND) - loop-1); + return 0; + } + usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); + } + dbg("waiting for '%s' failed", filename); + return -1; +} + static void apply_format(struct udevice *udev, char *string, size_t maxsize, struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) { @@ -581,10 +644,12 @@ found: break; } pos = getenv(attr); - if (pos == NULL) + if (pos == NULL) { + dbg("env '%s' not avialable", attr); break; - strlcat(string, pos, maxsize); + } dbg("substitute env '%s=%s'", attr, pos); + strlcat(string, pos, maxsize); break; default: err("unknown substitution type=%i", type); @@ -612,25 +677,36 @@ static char *key_pair_name(struct udev_rule *rule, struct key_pair *pair) static int match_key(const char *key_name, struct udev_rule *rule, struct key *key, const char *val) { int match; + char value[PATH_SIZE]; char *key_value; + char *pos; if (key->operation == KEY_OP_UNSET) return 0; - key_value = rule->buf + key->val_off; + strlcpy(value, rule->buf + key->val_off, sizeof(value)); + key_value = value; - dbg("check for %s '%s' <-> '%s'", key_name, key_value, val); - match = (strcmp_pattern(key_value, val) == 0); - if (match && (key->operation != KEY_OP_NOMATCH)) { - dbg("%s key is matching (matching value)", key_name); - return 0; - } - if (!match && (key->operation == KEY_OP_NOMATCH)) { - dbg("%s key is matching, (non matching value)", key_name); - return 0; + dbg("key %s value='%s'", key_name, key_value); + while (key_value) { + pos = strchr(key_value, '|'); + if (pos) { + pos[0] = '\0'; + pos++; + } + dbg("match %s '%s' <-> '%s'", key_name, key_value, val); + match = (strcmp_pattern(key_value, val) == 0); + if (match && (key->operation != KEY_OP_NOMATCH)) { + dbg("%s is true (matching value)", key_name); + return 0; + } + if (!match && (key->operation == KEY_OP_NOMATCH)) { + dbg("%s is true (non-matching value)", key_name); + return 0; + } + key_value = pos; } - - dbg("%s key is not matching", key_name); + dbg("%s is false", key_name); return -1; } @@ -681,6 +757,22 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule, dbg("all %i ENV keys matched", rule->env.count); } + if (rule->wait_for_sysfs.operation != KEY_OP_UNSET) { + int match; + + match = (wait_for_sysfs(udev, key_val(rule, &rule->wait_for_sysfs), 3) == 0); + if (match && (rule->wait_for_sysfs.operation != KEY_OP_NOMATCH)) { + dbg("WAIT_FOR_SYSFS is true (matching value)"); + return 0; + } + if (!match && (rule->wait_for_sysfs.operation == KEY_OP_NOMATCH)) { + dbg("WAIT_FOR_SYSFS is true, (non matching value)"); + return 0; + } + dbg("WAIT_FOR_SYSFS is false"); + return -1; + } + /* walk up the chain of physical devices and find a match */ while (1) { /* check for matching driver */ @@ -762,12 +854,15 @@ try_parent: strlcpy(import, key_val(rule, &rule->import), sizeof(import)); apply_format(udev, import, sizeof(import), class_dev, sysfs_device); dbg("check for IMPORT import='%s'", import); - if (rule->import_exec) { + if (rule->import_type == IMPORT_PROGRAM) { dbg("run executable file import='%s'", import); rc = import_program_into_env(udev, import); - } else { + } else if (rule->import_type == IMPORT_FILE) { dbg("import file import='%s'", import); rc = import_file_into_env(udev, import); + } else if (rule->import_type == IMPORT_PARENT && class_dev) { + dbg("import parent import='%s'", import); + rc = import_parent_into_env(udev, class_dev, import); } if (rc) { dbg("IMPORT failed"); @@ -787,7 +882,7 @@ try_parent: apply_format(udev, program, sizeof(program), class_dev, sysfs_device); dbg("check for PROGRAM program='%s", program); if (execute_program(program, udev->subsystem, result, sizeof(result), NULL) != 0) { - dbg("PROGRAM is not matching"); + dbg("PROGRAM is false"); if (rule->program.operation != KEY_OP_NOMATCH) goto exit; } else { @@ -819,6 +914,7 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s struct sysfs_class_device *class_dev_parent; struct sysfs_device *sysfs_device = NULL; struct udev_rule *rule; + int name_set = 0; dbg("class_dev->name='%s'", class_dev->name); @@ -850,7 +946,7 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s if (rule == NULL) break; - if (udev->name_set && rule->name.operation != KEY_OP_UNSET) { + if (name_set && rule->name.operation != KEY_OP_UNSET) { dbg("node name already set, rule ignored"); continue; } @@ -918,21 +1014,27 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s /* add multiple symlinks separated by spaces */ pos = temp; - next = strchr(temp, ' '); + while (isspace(pos[0])) + pos++; + next = strchr(pos, ' '); while (next) { next[0] = '\0'; info("add symlink '%s'", pos); name_list_add(&udev->symlink_list, pos, 0); + while (isspace(next[1])) + next++; pos = &next[1]; next = strchr(pos, ' '); } - info("add symlink '%s'", pos); - name_list_add(&udev->symlink_list, pos, 0); + if (pos[0] != '\0') { + info("add symlink '%s'", pos); + name_list_add(&udev->symlink_list, pos, 0); + } } /* set name, later rules with name set will be ignored */ if (rule->name.operation != KEY_OP_UNSET) { - udev->name_set = 1; + name_set = 1; strlcpy(udev->name, key_val(rule, &rule->name), sizeof(udev->name)); apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device); @@ -970,7 +1072,7 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s } } - if (udev->name[0] == '\0') { + if (!name_set) { strlcpy(udev->name, udev->kernel_name, sizeof(udev->name)); info("no rule found, will use kernel name '%s'", udev->name); }