summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xtest/udev-test.pl18
-rw-r--r--udev_rules.c56
-rw-r--r--udev_sysfs.c56
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;