chiark / gitweb /
udevd: track exit status of event process
[elogind.git] / udev_rules.c
index 6c6aa0f09140c136a7c34f31e259ba674fd146fd..a3100f8e143f7e051bd4102223772556e0367c28 100644 (file)
@@ -208,7 +208,7 @@ static int import_file_into_env(struct udevice *udev, const char *filename)
        size_t bufsize;
 
        if (file_map(filename, &buf, &bufsize) != 0) {
-               err("can't open '%s'", filename);
+               err("can't open '%s': %s", filename, strerror(errno));
                return -1;
        }
        import_keys_into_env(udev, buf, bufsize);
@@ -267,31 +267,78 @@ static int import_parent_into_env(struct udevice *udev, struct sysfs_class_devic
        return rc;
 }
 
-/* finds the lowest positive N such that <name>N isn't present in the udevdb
- * if <name> doesn't exist, 0 is returned, N otherwise
- */
-static int find_free_number(const char *name)
+static int match_name_and_get_number(const char *base, const char *devname)
 {
-       char devpath[PATH_SIZE];
+       size_t baselen;
+       char *endptr;
+       int num;
+
+       baselen = strlen(base);
+       if (strncmp(base, devname, baselen) != 0)
+               return -1;
+       if (devname[baselen] == '\0')
+               return 0;
+       if (!isdigit(devname[baselen]))
+               return -1;
+       num = strtoul(&devname[baselen], &endptr, 10);
+       if (endptr[0] != '\0')
+               return -1;
+       return num;
+}
+
+/* finds the lowest positive device number such that <name>N isn't present in the udevdb
+ * if <name> doesn't exist, 0 is returned, N otherwise */
+static int find_free_number(const char *base, const char *devpath)
+{
+       char db_devpath[PATH_SIZE];
        char filename[PATH_SIZE];
+       struct udevice udev_db;
        int num = 0;
 
-       strlcpy(filename, name, sizeof(filename));
+       /* check if the device already owns a matching name */
+       udev_init_device(&udev_db, NULL, NULL, NULL);
+       if (udev_db_get_device(&udev_db, devpath) == 0) {
+               struct name_entry *name_loop;
+               int devnum;
+
+               devnum = match_name_and_get_number(base, udev_db.name);
+               if (devnum >= 0) {
+                       num = devnum;
+                       dbg("device '%s', already has the node '%s' with num %u, use it", devpath, base, num);
+                       goto out;
+               }
+               list_for_each_entry(name_loop, &udev_db.symlink_list, node) {
+                       devnum = match_name_and_get_number(base, name_loop->name);
+                       if (devnum >= 0) {
+                               num = devnum;
+                               dbg("device '%s', already has a symlink '%s' with num %u, use it", devpath, base, num);
+                               goto out;
+                       }
+               }
+       }
+
+       /* just search the database again and again until a free name is found */
+       strlcpy(filename, base, sizeof(filename));
        while (1) {
                dbg("look for existing node '%s'", filename);
-               if (udev_db_search_name(devpath, sizeof(devpath), filename) != 0) {
+               if (udev_db_lookup_name(filename, db_devpath, sizeof(db_devpath)) != 0) {
                        dbg("free num=%d", num);
-                       return num;
+                       break;
                }
 
                num++;
-               if (num > 1000) {
-                       info("find_free_number gone crazy (num=%d), aborted", num);
-                       return -1;
+               if (num > 100000) {
+                       err("find_free_number aborted at num=%d", num);
+                       num = -1;
+                       break;
                }
-               snprintf(filename, sizeof(filename), "%s%d", name, num);
+               snprintf(filename, sizeof(filename), "%s%d", base, num);
                filename[sizeof(filename)-1] = '\0';
        }
+
+out:
+       udev_cleanup_device(&udev_db);
+       return num;
 }
 
 static int find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device,
@@ -323,13 +370,13 @@ static int find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sys
 
 attr_found:
        strlcpy(value, tmpattr->value, len);
-       remove_trailing_char(value, '\n');
+       remove_trailing_chars(value, '\n');
 
        dbg("found attribute '%s'", tmpattr->path);
        return 0;
 }
 
-#define WAIT_LOOP_PER_SECOND                   20
+#define WAIT_LOOP_PER_SECOND           50
 static int wait_for_sysfs(struct udevice *udev, const char *file, int timeout)
 {
        char filename[PATH_SIZE];
@@ -342,12 +389,13 @@ static int wait_for_sysfs(struct udevice *udev, const char *file, int timeout)
 
        while (--loop) {
                if (stat(filename, &stats) == 0) {
-                       dbg("file appeared after %i loops", (timeout * WAIT_LOOP_PER_SECOND) - loop-1);
+                       info("file appeared after %i loops", (timeout * WAIT_LOOP_PER_SECOND) - loop-1);
                        return 0;
                }
+               info("wait for %i mseconds", 1000 / WAIT_LOOP_PER_SECOND);
                usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
        }
-       dbg("waiting for '%s' failed", filename);
+       err("waiting for '%s' failed", filename);
        return -1;
 }
 
@@ -359,6 +407,7 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
        char *head, *tail, *pos, *cpos, *attr, *rest;
        int len;
        int i;
+       int count;
        unsigned int next_free_number;
        struct sysfs_class_device *class_dev_parent;
        enum subst_type {
@@ -541,12 +590,14 @@ found:
                        i = strlen(temp2);
                        while (i > 0 && isspace(temp2[i-1]))
                                temp2[--i] = '\0';
-                       replace_untrusted_chars(temp2);
+                       count = replace_untrusted_chars(temp2);
+                       if (count)
+                               info("%i untrusted character(s) replaced" , count);
                        strlcat(string, temp2, maxsize);
                        dbg("substitute sysfs value '%s'", temp2);
                        break;
                case SUBST_ENUM:
-                       next_free_number = find_free_number(string);
+                       next_free_number = find_free_number(string, udev->devpath);
                        if (next_free_number > 0) {
                                sprintf(temp2, "%d", next_free_number);
                                strlcat(string, temp2, maxsize);
@@ -598,7 +649,7 @@ found:
                        }
                        pos = getenv(attr);
                        if (pos == NULL) {
-                               dbg("env '%s' not avialable", attr);
+                               dbg("env '%s' not available", attr);
                                break;
                        }
                        dbg("substitute env '%s=%s'", attr, pos);
@@ -663,6 +714,7 @@ static int match_key(const char *key_name, struct udev_rule *rule, struct key *k
        return -1;
 }
 
+/* match a single rule against a given device and possibly its parent devices */
 static int match_rule(struct udevice *udev, struct udev_rule *rule,
                      struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device)
 {
@@ -809,9 +861,13 @@ try_parent:
                        if (rule->program.operation != KEY_OP_NOMATCH)
                                goto exit;
                } else {
+                       int count;
+
                        dbg("PROGRAM matches");
-                       remove_trailing_char(result, '\n');
-                       replace_untrusted_chars(result);
+                       remove_trailing_chars(result, '\n');
+                       count = replace_untrusted_chars(result);
+                       if (count)
+                               info("%i untrusted character(s) replaced" , count);
                        dbg("result is '%s'", result);
                        strlcpy(udev->program_result, result, sizeof(udev->program_result));
                        dbg("PROGRAM returned successful");
@@ -957,22 +1013,20 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
                        if (!udev->symlink_final && rule->symlink.operation != KEY_OP_UNSET) {
                                char temp[PATH_SIZE];
                                char *pos, *next;
+                               int count;
 
                                if (rule->symlink.operation == KEY_OP_ASSIGN_FINAL)
                                        udev->symlink_final = 1;
                                if (rule->symlink.operation == KEY_OP_ASSIGN || rule->symlink.operation == KEY_OP_ASSIGN_FINAL) {
-                                       struct name_entry *name_loop;
-                                       struct name_entry *temp_loop;
-
                                        info("reset symlink list");
-                                       list_for_each_entry_safe(name_loop, temp_loop, &udev->symlink_list, node) {
-                                               list_del(&name_loop->node);
-                                               free(name_loop);
-                                       }
+                                       name_list_cleanup(&udev->symlink_list);
                                }
                                strlcpy(temp, key_val(rule, &rule->symlink), sizeof(temp));
                                apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device);
-                               dbg("rule applied, added symlink '%s'", temp);
+                               count = replace_untrusted_chars(temp);
+                               if (count)
+                                       info("%i untrusted character(s) replaced" , count);
+                               dbg("rule applied, added symlink(s) '%s'", temp);
 
                                /* add multiple symlinks separated by spaces */
                                pos = temp;
@@ -996,9 +1050,13 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
 
                        /* set name, later rules with name set will be ignored */
                        if (rule->name.operation != KEY_OP_UNSET) {
+                               int count;
                                name_set = 1;
                                strlcpy(udev->name, key_val(rule, &rule->name), sizeof(udev->name));
                                apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device);
+                               count = replace_untrusted_chars(udev->name);
+                               if (count)
+                                       info("%i untrusted character(s) replaced", count);
 
                                info("rule applied, '%s' becomes '%s'", udev->kernel_name, udev->name);
                                if (udev->type != DEV_NET)
@@ -1012,14 +1070,8 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct s
                                if (rule->run.operation == KEY_OP_ASSIGN_FINAL)
                                        udev->run_final = 1;
                                if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) {
-                                       struct name_entry *name_loop;
-                                       struct name_entry *temp_loop;
-
                                        info("reset run list");
-                                       list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) {
-                                               list_del(&name_loop->node);
-                                               free(name_loop);
-                                       }
+                                       name_list_cleanup(&udev->run_list);
                                }
                                strlcpy(program, key_val(rule, &rule->run), sizeof(program));
                                apply_format(udev, program, sizeof(program), class_dev, sysfs_device);
@@ -1093,14 +1145,8 @@ int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev,
                                char program[PATH_SIZE];
 
                                if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) {
-                                       struct name_entry *name_loop;
-                                       struct name_entry *temp_loop;
-
                                        info("reset run list");
-                                       list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) {
-                                               list_del(&name_loop->node);
-                                               free(name_loop);
-                                       }
+                                       name_list_cleanup(&udev->run_list);
                                }
                                strlcpy(program, key_val(rule, &rule->run), sizeof(program));
                                apply_format(udev, program, sizeof(program), class_dev, sysfs_dev);