chiark / gitweb /
add missing ChangeLog
[elogind.git] / udev_rules.c
index 1139c99a7d0e84ff3b8e289a3c6af251d5aabf76..598c1ba7393a9d62828404abf88647dee4e8433e 100644 (file)
@@ -267,10 +267,48 @@ static int wait_for_sysfs(struct udevice *udev, const char *file, int timeout)
                info("wait for '%s' for %i mseconds", filepath, 1000 / WAIT_LOOP_PER_SECOND);
                usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
        }
-       err("waiting for '%s' failed", filepath);
+       info("waiting for '%s' failed", filepath);
        return -1;
 }
 
+/* handle "[$SUBSYSTEM/$KERNEL]<attribute>" lookup */
+static int attr_get_by_subsys_id(const char *attrstr, char *devpath, size_t len, char **attr)
+{
+       char subsys[NAME_SIZE];
+       char *attrib;
+       char *id;
+       int found = 0;
+
+       if (attrstr[0] != '[')
+               goto out;
+
+       strlcpy(subsys, &attrstr[1], sizeof(subsys));
+
+       attrib = strchr(subsys, ']');
+       if (attrib == NULL)
+               goto out;
+       attrib[0] = '\0';
+       attrib = &attrib[1];
+
+       id = strchr(subsys, '/');
+       if (id == NULL)
+               goto out;
+       id[0] = '\0';
+       id = &id[1];
+
+       if (sysfs_lookup_devpath_by_subsys_id(devpath, len, subsys, id)) {
+               if (attr != NULL) {
+                       if (attrib[0] != '\0')
+                               *attr = attrib;
+                       else
+                               *attr = NULL;
+               }
+               found = 1;
+       }
+out:
+       return found;
+}
+
 void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize)
 {
        char temp[PATH_SIZE];
@@ -292,6 +330,7 @@ void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize)
                SUBST_PARENT,
                SUBST_TEMP_NODE,
                SUBST_ROOT,
+               SUBST_SYS,
                SUBST_ENV,
        };
        static const struct subst_map {
@@ -311,6 +350,7 @@ void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize)
                { .name = "parent",     .fmt = 'P',     .type = SUBST_PARENT },
                { .name = "tempnode",   .fmt = 'N',     .type = SUBST_TEMP_NODE },
                { .name = "root",       .fmt = 'r',     .type = SUBST_ROOT },
+               { .name = "sys",        .fmt = 'S',     .type = SUBST_SYS },
                { .name = "env",        .fmt = 'E',     .type = SUBST_ENV },
                { NULL, '\0', 0 }
        };
@@ -441,11 +481,20 @@ found:
                        if (attr == NULL)
                                err("missing file parameter for attr");
                        else {
+                               char devpath[PATH_SIZE];
+                               char *attrib;
                                const char *value = NULL;
                                size_t size;
 
-                               /* first try the current device, other matches may have selected */
-                               if (udev->dev_parent != NULL && udev->dev_parent != udev->dev)
+                               if (attr_get_by_subsys_id(attr, devpath, sizeof(devpath), &attrib)) {
+                                       if (attrib != NULL)
+                                               value = sysfs_attr_get_value(devpath, attrib);
+                                       else
+                                               break;
+                               }
+
+                               /* try the current device, other matches may have selected */
+                               if (value == NULL && udev->dev_parent != NULL && udev->dev_parent != udev->dev)
                                        value = sysfs_attr_get_value(udev->dev_parent->devpath, attr);
 
                                /* look at all devices along the chain of parents */
@@ -516,6 +565,10 @@ found:
                        strlcat(string, udev_root, maxsize);
                        dbg("substitute udev_root '%s'", udev_root);
                        break;
+               case SUBST_SYS:
+                       strlcat(string, sysfs_path, maxsize);
+                       dbg("substitute sysfs_path '%s'", sysfs_path);
+                       break;
                case SUBST_ENV:
                        if (attr == NULL) {
                                dbg("missing attribute");
@@ -635,6 +688,40 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
                }
        }
 
+       if (rule->test.operation != KEY_OP_UNSET) {
+               char filename[PATH_SIZE];
+               char devpath[PATH_SIZE];
+               char *attr;
+               struct stat statbuf;
+               int match;
+
+               strlcpy(filename, key_val(rule, &rule->test), sizeof(filename));
+               udev_rules_apply_format(udev, filename, sizeof(filename));
+
+               if (attr_get_by_subsys_id(filename, devpath, sizeof(devpath), &attr)) {
+                       strlcpy(filename, sysfs_path, sizeof(filename));
+                       strlcat(filename, devpath, sizeof(filename));
+                       if (attr != NULL) {
+                               strlcat(filename, "/", sizeof(filename));
+                               strlcat(filename, attr, sizeof(filename));
+                       }
+               }
+
+               match = (stat(filename, &statbuf) == 0);
+               info("'%s' %s", filename, match ? "exists" : "does not exist");
+               if (match && rule->test_mode_mask > 0) {
+                       match = ((statbuf.st_mode & rule->test_mode_mask) > 0);
+                       info("'%s' has mode=%#o and %s %#o", filename, statbuf.st_mode,
+                            match ? "matches" : "does not match",
+                            rule->test_mode_mask);
+               }
+               if (match && rule->test.operation == KEY_OP_NOMATCH)
+                       goto nomatch;
+               if (!match && rule->test.operation == KEY_OP_MATCH)
+                       goto nomatch;
+               dbg("TEST key is true");
+       }
+
        if (rule->wait_for_sysfs.operation != KEY_OP_UNSET) {
                int found;
 
@@ -651,11 +738,20 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
                    pair->key.operation == KEY_OP_NOMATCH) {
                        const char *key_name = key_pair_name(rule, pair);
                        const char *key_value = key_val(rule, &pair->key);
-                       const char *value;
+                       char devpath[PATH_SIZE];
+                       char *attrib;
+                       const char *value = NULL;
                        char val[VALUE_SIZE];
                        size_t len;
 
-                       value = sysfs_attr_get_value(udev->dev->devpath, key_name);
+                       if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) {
+                               if (attrib != NULL)
+                                       value = sysfs_attr_get_value(devpath, attrib);
+                               else
+                                       goto nomatch;
+                       }
+                       if (value == NULL)
+                               value = sysfs_attr_get_value(udev->dev->devpath, key_name);
                        if (value == NULL)
                                goto nomatch;
                        strlcpy(val, value, sizeof(val));
@@ -1031,7 +1127,8 @@ int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev)
 
                dbg("process rule");
                if (rule->name.operation != KEY_OP_UNSET || rule->symlink.operation != KEY_OP_UNSET ||
-                   rule->mode_operation != KEY_OP_UNSET || rule->owner.operation != KEY_OP_UNSET || rule->group.operation != KEY_OP_UNSET) {
+                   rule->mode_operation != KEY_OP_UNSET || rule->owner.operation != KEY_OP_UNSET ||
+                   rule->group.operation != KEY_OP_UNSET) {
                        dbg("skip rule that names a device");
                        continue;
                }
@@ -1044,7 +1141,8 @@ int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev)
                        }
 
                        if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) {
-                               if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) {
+                               if (rule->run.operation == KEY_OP_ASSIGN ||
+                                   rule->run.operation == KEY_OP_ASSIGN_FINAL) {
                                        info("reset run list");
                                        name_list_cleanup(&udev->run_list);
                                }