diff options
-rwxr-xr-x | test/udev-test.pl | 18 | ||||
-rw-r--r-- | udev_rules.c | 56 | ||||
-rw-r--r-- | udev_sysfs.c | 56 |
3 files changed, 92 insertions, 38 deletions
diff --git a/test/udev-test.pl b/test/udev-test.pl index 464098fa65..54c3378392 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -582,6 +582,24 @@ SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", NAME="bad" EOF }, { + desc => "substitute attr with link target value (first match)", + subsys => "block", + devpath => "/block/sda", + exp_name => "driver-is-sd", + rules => <<EOF +SUBSYSTEMS=="scsi", NAME="driver-is-\$attr{driver}" +EOF + }, + { + desc => "substitute attr with link target value (currently selected device)", + subsys => "block", + devpath => "/block/sda", + exp_name => "driver-is-aic7xxx", + rules => <<EOF +SUBSYSTEMS=="pci", NAME="driver-is-\$attr{driver}" +EOF + }, + { desc => "ignore ATTRS attribute whitespace", subsys => "block", devpath => "/block/sda", diff --git a/udev_rules.c b/udev_rules.c index 1483f8f493..edaaa71d12 100644 --- a/udev_rules.c +++ b/udev_rules.c @@ -458,30 +458,42 @@ found: } break; case SUBST_ATTR: - if (attr == NULL) { - dbg("missing attribute"); - break; - } else { - struct sysfs_device *dev_parent; - const char *value; + if (attr == NULL) + err("missing file parameter for attr"); + else { + 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) + value = sysfs_attr_get_value(udev->dev_parent->devpath, attr); + + /* look at all devices along the chain of parents */ + if (value == NULL) { + struct sysfs_device *dev_parent = udev->dev; + + do { + dbg("looking at '%s'", dev_parent->devpath); + value = sysfs_attr_get_value(dev_parent->devpath, attr); + if (value != NULL) { + strlcpy(temp2, value, sizeof(temp2)); + break; + } + dev_parent = sysfs_device_get_parent(dev_parent); + } while (dev_parent != NULL); + } - dev_parent = udev->dev; - do { - dbg("looking at '%s'", dev_parent->devpath); - value = sysfs_attr_get_value(dev_parent->devpath, attr); - if (value != NULL) { - strlcpy(temp2, value, sizeof(temp2)); - break; - } - dev_parent = sysfs_device_get_parent(dev_parent); - } while (dev_parent != NULL); + if (value == NULL) + break; - /* strip trailing whitespace of sysfs value */ - i = strlen(temp2); - while (i > 0 && isspace(temp2[i-1])) - temp2[--i] = '\0'; + /* strip trailing whitespace and replace untrusted characters of sysfs value */ + size = strlcpy(temp2, value, sizeof(temp2)); + if (size >= sizeof(temp2)) + size = sizeof(temp2)-1; + while (size > 0 && isspace(temp2[size-1])) + temp2[--size] = '\0'; count = replace_untrusted_chars(temp2); - if (count) + if (count > 0) info("%i untrusted character(s) replaced" , count); strlcat(string, temp2, maxsize); dbg("substitute sysfs value '%s'", temp2); @@ -693,7 +705,7 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule) if (match_key("DRIVERS", rule, &rule->drivers, udev->dev_parent->driver)) goto try_parent; - /* check for matching sysfs attrubute pairs */ + /* check for matching sysfs attribute pairs */ for (i = 0; i < rule->attrs.count; i++) { struct key_pair *pair = &rule->attrs.keys[i]; diff --git a/udev_sysfs.c b/udev_sysfs.c index c192696689..e733d417c5 100644 --- a/udev_sysfs.c +++ b/udev_sysfs.c @@ -346,6 +346,7 @@ char *sysfs_attr_get_value(const char *devpath, const char *attr_name) char value[NAME_SIZE]; struct sysfs_attr *attr_loop; struct sysfs_attr *attr; + struct stat statbuf; int fd; ssize_t size; size_t sysfs_len; @@ -375,25 +376,48 @@ char *sysfs_attr_get_value(const char *devpath, const char *attr_name) dbg("add to cache '%s'", path_full); list_add(&attr->node, &attr_list); - /* read attribute value */ - fd = open(path_full, O_RDONLY); - if (fd < 0) { - dbg("attribute '%s' does not exist", path_full); + if (lstat(path_full, &statbuf) != 0) { + dbg("stat '%s' failed: %s", path_full, strerror(errno)); goto out; } - size = read(fd, value, sizeof(value)); - close(fd); - if (size < 0) - goto out; - if (size == sizeof(value)) - goto out; - /* got a valid value, store and return it */ - value[size] = '\0'; - remove_trailing_chars(value, '\n'); - dbg("cache '%s' with value '%s'", path_full, value); - strlcpy(attr->value_local, value, sizeof(attr->value_local)); - attr->value = attr->value_local; + if (S_ISLNK(statbuf.st_mode)) { + /* links return the last element of the target path */ + char link_target[PATH_SIZE]; + int len; + const char *pos; + + len = readlink(path_full, link_target, sizeof(link_target)); + if (len > 0) { + link_target[len] = '\0'; + pos = strrchr(link_target, '/'); + if (pos != NULL) { + dbg("cache '%s' with link value '%s'", path_full, value); + strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local)); + attr->value = attr->value_local; + } + } + } else { + /* read attribute value */ + fd = open(path_full, O_RDONLY); + if (fd < 0) { + dbg("attribute '%s' does not exist", path_full); + goto out; + } + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + goto out; + if (size == sizeof(value)) + goto out; + + /* got a valid value, store and return it */ + value[size] = '\0'; + remove_trailing_chars(value, '\n'); + dbg("cache '%s' with attribute value '%s'", path_full, value); + strlcpy(attr->value_local, value, sizeof(attr->value_local)); + attr->value = attr->value_local; + } out: return attr->value; |