chiark / gitweb /
always unlink temporary file before creating new one
[elogind.git] / udev_rules.c
index e61c9a69b062c02fa2fe083911cc964ba8789d3e..cb74da56e37ae0da284a5f67b1692ce5965b11b0 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];
@@ -443,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 */
@@ -641,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;
 
@@ -657,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));
@@ -747,7 +837,8 @@ try_parent:
 
                strlcpy(program, key_val(rule, &rule->program), sizeof(program));
                udev_rules_apply_format(udev, program, sizeof(program));
-               if (run_program(program, udev->dev->subsystem, result, sizeof(result), NULL, (udev_log_priority >= LOG_INFO)) != 0) {
+               if (run_program(program, udev->dev->subsystem, result, sizeof(result),
+                               NULL, (udev_log_priority >= LOG_INFO)) != 0) {
                        dbg("PROGRAM is false");
                        udev->program_result[0] = '\0';
                        if (rule->program.operation != KEY_OP_NOMATCH)
@@ -757,9 +848,12 @@ try_parent:
 
                        dbg("PROGRAM matches");
                        remove_trailing_chars(result, '\n');
-                       count = replace_chars(result, ALLOWED_CHARS_INPUT);
-                       if (count)
-                               info("%i character(s) replaced" , count);
+                       if (rule->string_escape == ESCAPE_UNSET ||
+                           rule->string_escape == ESCAPE_REPLACE) {
+                               count = replace_chars(result, ALLOWED_CHARS_INPUT);
+                               if (count > 0)
+                                       info("%i character(s) replaced" , count);
+                       }
                        dbg("result is '%s'", result);
                        strlcpy(udev->program_result, result, sizeof(udev->program_result));
                        dbg("PROGRAM returned successful");
@@ -833,14 +927,28 @@ try_parent:
 
                if (pair->key.operation == KEY_OP_ASSIGN) {
                        const char *key_name = key_pair_name(rule, pair);
-                       char attr[PATH_SIZE];
+                       char devpath[PATH_SIZE];
+                       char *attrib;
+                       char attr[PATH_SIZE] = "";
                        char value[NAME_SIZE];
                        FILE *f;
 
-                       strlcpy(attr, sysfs_path, sizeof(attr));
-                       strlcat(attr, udev->dev->devpath, sizeof(attr));
-                       strlcat(attr, "/", sizeof(attr));
-                       strlcat(attr, key_name, sizeof(attr));
+                       if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) {
+                               if (attrib != NULL) {
+                                       strlcpy(attr, sysfs_path, sizeof(attr));
+                                       strlcat(attr, devpath, sizeof(attr));
+                                       strlcat(attr, "/", sizeof(attr));
+                                       strlcat(attr, attrib, sizeof(attr));
+                               }
+                       }
+
+                       if (attr[0] == '\0') {
+                               strlcpy(attr, sysfs_path, sizeof(attr));
+                               strlcat(attr, udev->dev->devpath, sizeof(attr));
+                               strlcat(attr, "/", sizeof(attr));
+                               strlcat(attr, key_name, sizeof(attr));
+                       }
+
                        strlcpy(value, key_val(rule, &pair->key), sizeof(value));
                        udev_rules_apply_format(udev, value, sizeof(value));
                        info("writing '%s' to sysfs file '%s'", value, attr);
@@ -943,9 +1051,12 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev)
                                /* allow  multiple symlinks separated by spaces */
                                strlcpy(temp, key_val(rule, &rule->symlink), sizeof(temp));
                                udev_rules_apply_format(udev, temp, sizeof(temp));
-                               count = replace_chars(temp, ALLOWED_CHARS_FILE " ");
-                               if (count)
-                                       info("%i character(s) replaced" , count);
+                               if (rule->string_escape == ESCAPE_UNSET ||
+                                   rule->string_escape == ESCAPE_REPLACE) {
+                                       count = replace_chars(temp, ALLOWED_CHARS_FILE " ");
+                                       if (count > 0)
+                                               info("%i character(s) replaced" , count);
+                               }
                                dbg("rule applied, added symlink(s) '%s'", temp);
                                pos = temp;
                                while (isspace(pos[0]))
@@ -975,9 +1086,12 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev)
                                name_set = 1;
                                strlcpy(udev->name, key_val(rule, &rule->name), sizeof(udev->name));
                                udev_rules_apply_format(udev, udev->name, sizeof(udev->name));
-                               count = replace_chars(udev->name, ALLOWED_CHARS_FILE);
-                               if (count)
-                                       info("%i character(s) replaced", count);
+                               if (rule->string_escape == ESCAPE_UNSET ||
+                                   rule->string_escape == ESCAPE_REPLACE) {
+                                       count = replace_chars(udev->name, ALLOWED_CHARS_FILE);
+                                       if (count > 0)
+                                               info("%i character(s) replaced", count);
+                               }
 
                                info("rule applied, '%s' becomes '%s'", udev->dev->kernel, udev->name);
                                if (strcmp(udev->dev->subsystem, "net") != 0)
@@ -1037,7 +1151,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;
                }
@@ -1050,7 +1165,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);
                                }