chiark / gitweb /
release 099
[elogind.git] / udev_rules.c
index 49306295ff8ad85292c3f4031b9f43a8a2081a4c..edaaa71d12c7793c9716cb332ce5edaed41a2598 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * udev_rules.c
- *
  * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
  * Copyright (C) 2003-2006 Kay Sievers <kay.sievers@vrfy.org>
  *
@@ -15,7 +13,7 @@
  *
  *     You should have received a copy of the GNU General Public License along
  *     with this program; if not, write to the Free Software Foundation, Inc.,
- *     675 Mass Ave, Cambridge, MA 02139, USA.
+ *     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *
  */
 
@@ -28,6 +26,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <syslog.h>
+#include <fnmatch.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 
@@ -246,7 +245,7 @@ static int import_parent_into_env(struct udevice *udev, const char *filter)
                                if (pos) {
                                        pos[0] = '\0';
                                        pos++;
-                                       if (strcmp_pattern(filter, name) == 0) {
+                                       if (fnmatch(filter, name, 0) == 0) {
                                                dbg("import key '%s'", name_loop->name);
                                                name_list_add(&udev->env_list, name_loop->name, 0);
                                                setenv(name, pos, 1);
@@ -459,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);
@@ -583,7 +594,7 @@ static int match_key(const char *key_name, struct udev_rule *rule, struct key *k
                        pos++;
                }
                dbg("match %s '%s' <-> '%s'", key_name, key_value, val);
-               match = (strcmp_pattern(key_value, val) == 0);
+               match = (fnmatch(key_value, val, 0) == 0);
                if (match && (key->operation != KEY_OP_NOMATCH)) {
                        dbg("%s is true (matching value)", key_name);
                        return 0;
@@ -644,17 +655,16 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
                int found;
 
                found = (wait_for_sysfs(udev, key_val(rule, &rule->wait_for_sysfs), 3) == 0);
-               if (!found && (rule->wait_for_sysfs.operation != KEY_OP_NOMATCH)) {
-                       dbg("WAIT_FOR_SYSFS failed");
+               if (!found && (rule->wait_for_sysfs.operation != KEY_OP_NOMATCH))
                        goto nomatch;
-               }
        }
 
-       /* check for matching sysfs attrubute pairs */
-       if (rule->attr.count) {
-               dbg("check %i ATTR keys", rule->attr.count);
-               for (i = 0; i < rule->attr.count; i++) {
-                       struct key_pair *pair = &rule->attr.keys[i];
+       /* check for matching sysfs attribute pairs */
+       for (i = 0; i < rule->attr.count; i++) {
+               struct key_pair *pair = &rule->attr.keys[i];
+
+               if (pair->key.operation == KEY_OP_MATCH ||
+                   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;
@@ -678,7 +688,6 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
                        if (match_key("ATTR", rule, &pair->key, val))
                                goto nomatch;
                }
-               dbg("all %i ATTR keys matched", rule->attr.count);
        }
 
        /* walk up the chain of parent devices and find a match */
@@ -696,11 +705,12 @@ 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 */
-               if (rule->attrs.count) {
-                       dbg("check %i ATTRS keys", rule->attrs.count);
-                       for (i = 0; i < rule->attrs.count; i++) {
-                               struct key_pair *pair = &rule->attrs.keys[i];
+               /* check for matching sysfs attribute pairs */
+               for (i = 0; i < rule->attrs.count; i++) {
+                       struct key_pair *pair = &rule->attrs.keys[i];
+
+                       if (pair->key.operation == KEY_OP_MATCH ||
+                           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;
@@ -726,7 +736,6 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
                                if (match_key("ATTRS", rule, &pair->key, val))
                                        goto try_parent;
                        }
-                       dbg("all %i ATTRS keys matched", rule->attrs.count);
                }
 
                /* found matching device  */
@@ -805,18 +814,48 @@ try_parent:
                struct key_pair *pair = &rule->env.keys[i];
 
                if (pair->key.operation == KEY_OP_ASSIGN) {
+                       char temp_value[NAME_SIZE];
                        const char *key_name = key_pair_name(rule, pair);
                        const char *value = key_val(rule, &pair->key);
-                       char *key_value = name_list_key_add(&udev->env_list, key_name, value);
+                       char *key_value;
+
+                       /* make sure we don't write to the same string we possibly read from */
+                       strlcpy(temp_value, value, sizeof(temp_value));
+                       udev_rules_apply_format(udev, temp_value, NAME_SIZE);
+
+                       key_value = name_list_key_add(&udev->env_list, key_name, temp_value);
                        if (key_value == NULL)
                                break;
 
-                       udev_rules_apply_format(udev, key_value, NAME_SIZE);
                        putenv(key_value);
                        dbg("export ENV '%s'", key_value);
                }
        }
 
+       /* if we have ATTR assignements write value to sysfs file */
+       for (i = 0; i < rule->attr.count; i++) {
+               struct key_pair *pair = &rule->attr.keys[i];
+
+               if (pair->key.operation == KEY_OP_ASSIGN) {
+                       const char *key_name = key_pair_name(rule, pair);
+                       const char *key_value = key_val(rule, &pair->key);
+                       char attr[PATH_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));
+                       dbg("write '%s' to '%s'", key_value, attr);
+                       f = fopen(attr, "w");
+                       if (f != NULL) {
+                               if (fprintf(f, "%s\n", key_value) <= 0)
+                                       err("error writing ATTR{%s}: %s", attr, strerror(errno));
+                               fclose(f);
+                       } else
+                               err("error opening ATTR{%s} for writing: %s", attr, strerror(errno));
+               }
+       }
        return 0;
 
 nomatch: