]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/udev/udev-event.c
Merge pull request #7608 from poettering/more-news-v236
[thirdparty/systemd.git] / src / udev / udev-event.c
index 5d6542d3ad4854e60be9817429b3d5e0fb5e778a..8f7c28f03d4f6879a64856577e2c1403a822be98 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org>
  *
@@ -31,9 +32,8 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
-#include "event-util.h"
 #include "fd-util.h"
-#include "formats-util.h"
+#include "format-util.h"
 #include "netlink-util.h"
 #include "process-util.h"
 #include "signal-util.h"
@@ -59,7 +59,7 @@ struct udev_event *udev_event_new(struct udev_device *dev) {
         event->udev = udev;
         udev_list_init(udev, &event->run_list, false);
         udev_list_init(udev, &event->seclabel_list, false);
-        event->birth_usec = clock_boottime_or_monotonic();
+        event->birth_usec = now(CLOCK_MONOTONIC);
         return event;
 }
 
@@ -74,27 +74,223 @@ void udev_event_unref(struct udev_event *event) {
         free(event);
 }
 
-size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) {
+enum subst_type {
+        SUBST_UNKNOWN,
+        SUBST_DEVNODE,
+        SUBST_ATTR,
+        SUBST_ENV,
+        SUBST_KERNEL,
+        SUBST_KERNEL_NUMBER,
+        SUBST_DRIVER,
+        SUBST_DEVPATH,
+        SUBST_ID,
+        SUBST_MAJOR,
+        SUBST_MINOR,
+        SUBST_RESULT,
+        SUBST_PARENT,
+        SUBST_NAME,
+        SUBST_LINKS,
+        SUBST_ROOT,
+        SUBST_SYS,
+};
+
+static size_t subst_format_var(struct udev_event *event, struct udev_device *dev,
+                               enum subst_type type, char *attr,
+                               char *dest, size_t l) {
+        char *s = dest;
+
+        switch (type) {
+        case SUBST_DEVPATH:
+                l = strpcpy(&s, l, udev_device_get_devpath(dev));
+                break;
+        case SUBST_KERNEL:
+                l = strpcpy(&s, l, udev_device_get_sysname(dev));
+                break;
+        case SUBST_KERNEL_NUMBER:
+                if (udev_device_get_sysnum(dev) == NULL)
+                        break;
+                l = strpcpy(&s, l, udev_device_get_sysnum(dev));
+                break;
+        case SUBST_ID:
+                if (event->dev_parent == NULL)
+                        break;
+                l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent));
+                break;
+        case SUBST_DRIVER: {
+                const char *driver;
+
+                if (event->dev_parent == NULL)
+                        break;
+
+                driver = udev_device_get_driver(event->dev_parent);
+                if (driver == NULL)
+                        break;
+                l = strpcpy(&s, l, driver);
+                break;
+        }
+        case SUBST_MAJOR: {
+                char num[UTIL_PATH_SIZE];
+
+                sprintf(num, "%u", major(udev_device_get_devnum(dev)));
+                l = strpcpy(&s, l, num);
+                break;
+        }
+        case SUBST_MINOR: {
+                char num[UTIL_PATH_SIZE];
+
+                sprintf(num, "%u", minor(udev_device_get_devnum(dev)));
+                l = strpcpy(&s, l, num);
+                break;
+        }
+        case SUBST_RESULT: {
+                char *rest;
+                int i;
+
+                if (event->program_result == NULL)
+                        break;
+                /* get part of the result string */
+                i = 0;
+                if (attr != NULL)
+                        i = strtoul(attr, &rest, 10);
+                if (i > 0) {
+                        char result[UTIL_PATH_SIZE];
+                        char tmp[UTIL_PATH_SIZE];
+                        char *cpos;
+
+                        strscpy(result, sizeof(result), event->program_result);
+                        cpos = result;
+                        while (--i) {
+                                while (cpos[0] != '\0' && !isspace(cpos[0]))
+                                        cpos++;
+                                while (isspace(cpos[0]))
+                                        cpos++;
+                                if (cpos[0] == '\0')
+                                        break;
+                        }
+                        if (i > 0) {
+                                log_error("requested part of result string not found");
+                                break;
+                        }
+                        strscpy(tmp, sizeof(tmp), cpos);
+                        /* %{2+}c copies the whole string from the second part on */
+                        if (rest[0] != '+') {
+                                cpos = strchr(tmp, ' ');
+                                if (cpos)
+                                        cpos[0] = '\0';
+                        }
+                        l = strpcpy(&s, l, tmp);
+                } else {
+                        l = strpcpy(&s, l, event->program_result);
+                }
+                break;
+        }
+        case SUBST_ATTR: {
+                const char *value = NULL;
+                char vbuf[UTIL_NAME_SIZE];
+                size_t len;
+                int count;
+
+                if (attr == NULL) {
+                        log_error("missing file parameter for attr");
+                        break;
+                }
+
+                /* try to read the value specified by "[dmi/id]product_name" */
+                if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0)
+                        value = vbuf;
+
+                /* try to read the attribute the device */
+                if (value == NULL)
+                        value = udev_device_get_sysattr_value(event->dev, attr);
+
+                /* try to read the attribute of the parent device, other matches have selected */
+                if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev)
+                        value = udev_device_get_sysattr_value(event->dev_parent, attr);
+
+                if (value == NULL)
+                        break;
+
+                /* strip trailing whitespace, and replace unwanted characters */
+                if (value != vbuf)
+                        strscpy(vbuf, sizeof(vbuf), value);
+                len = strlen(vbuf);
+                while (len > 0 && isspace(vbuf[--len]))
+                        vbuf[len] = '\0';
+                count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
+                if (count > 0)
+                        log_debug("%i character(s) replaced" , count);
+                l = strpcpy(&s, l, vbuf);
+                break;
+        }
+        case SUBST_PARENT: {
+                struct udev_device *dev_parent;
+                const char *devnode;
+
+                dev_parent = udev_device_get_parent(event->dev);
+                if (dev_parent == NULL)
+                        break;
+                devnode = udev_device_get_devnode(dev_parent);
+                if (devnode != NULL)
+                        l = strpcpy(&s, l, devnode + STRLEN("/dev/"));
+                break;
+        }
+        case SUBST_DEVNODE:
+                if (udev_device_get_devnode(dev) != NULL)
+                        l = strpcpy(&s, l, udev_device_get_devnode(dev));
+                break;
+        case SUBST_NAME:
+                if (event->name != NULL)
+                        l = strpcpy(&s, l, event->name);
+                else if (udev_device_get_devnode(dev) != NULL)
+                        l = strpcpy(&s, l,
+                                    udev_device_get_devnode(dev) + STRLEN("/dev/"));
+                else
+                        l = strpcpy(&s, l, udev_device_get_sysname(dev));
+                break;
+        case SUBST_LINKS: {
+                struct udev_list_entry *list_entry;
+
+                list_entry = udev_device_get_devlinks_list_entry(dev);
+                if (list_entry == NULL)
+                        break;
+                l = strpcpy(&s, l,
+                            udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
+                udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
+                        l = strpcpyl(&s, l, " ",
+                                     udev_list_entry_get_name(list_entry) + STRLEN("/dev/"),
+                                     NULL);
+                break;
+        }
+        case SUBST_ROOT:
+                l = strpcpy(&s, l, "/dev");
+                break;
+        case SUBST_SYS:
+                l = strpcpy(&s, l, "/sys");
+                break;
+        case SUBST_ENV:
+                if (attr == NULL) {
+                        break;
+                } else {
+                        const char *value;
+
+                        value = udev_device_get_property_value(event->dev, attr);
+                        if (value == NULL)
+                                break;
+                        l = strpcpy(&s, l, value);
+                        break;
+                }
+        default:
+                log_error("unknown substitution type=%i", type);
+                break;
+        }
+
+        return s - dest;
+}
+
+size_t udev_event_apply_format(struct udev_event *event,
+                               const char *src, char *dest, size_t size,
+                               bool replace_whitespace) {
         struct udev_device *dev = event->dev;
-        enum subst_type {
-                SUBST_UNKNOWN,
-                SUBST_DEVNODE,
-                SUBST_ATTR,
-                SUBST_ENV,
-                SUBST_KERNEL,
-                SUBST_KERNEL_NUMBER,
-                SUBST_DRIVER,
-                SUBST_DEVPATH,
-                SUBST_ID,
-                SUBST_MAJOR,
-                SUBST_MINOR,
-                SUBST_RESULT,
-                SUBST_PARENT,
-                SUBST_NAME,
-                SUBST_LINKS,
-                SUBST_ROOT,
-                SUBST_SYS,
-        };
         static const struct subst_map {
                 const char *name;
                 const char fmt;
@@ -133,6 +329,7 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char *
                 enum subst_type type = SUBST_UNKNOWN;
                 char attrbuf[UTIL_PATH_SIZE];
                 char *attr = NULL;
+                size_t subst_len;
 
                 while (from[0] != '\0') {
                         if (from[0] == '$') {
@@ -170,7 +367,7 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char *
                         }
 copy:
                         /* copy char */
-                        if (l == 0)
+                        if (l < 2) /* need space for this char and the terminating NUL */
                                 goto out;
                         s[0] = from[0];
                         from++;
@@ -185,12 +382,12 @@ subst:
                         unsigned int i;
 
                         from++;
-                        for (i = 0; from[i] != '}'; i++) {
+                        for (i = 0; from[i] != '}'; i++)
                                 if (from[i] == '\0') {
                                         log_error("missing closing brace for format '%s'", src);
                                         goto out;
                                 }
-                        }
+
                         if (i >= sizeof(attrbuf))
                                 goto out;
                         memcpy(attrbuf, from, i);
@@ -201,189 +398,21 @@ subst:
                         attr = NULL;
                 }
 
-                switch (type) {
-                case SUBST_DEVPATH:
-                        l = strpcpy(&s, l, udev_device_get_devpath(dev));
-                        break;
-                case SUBST_KERNEL:
-                        l = strpcpy(&s, l, udev_device_get_sysname(dev));
-                        break;
-                case SUBST_KERNEL_NUMBER:
-                        if (udev_device_get_sysnum(dev) == NULL)
-                                break;
-                        l = strpcpy(&s, l, udev_device_get_sysnum(dev));
-                        break;
-                case SUBST_ID:
-                        if (event->dev_parent == NULL)
-                                break;
-                        l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent));
-                        break;
-                case SUBST_DRIVER: {
-                        const char *driver;
-
-                        if (event->dev_parent == NULL)
-                                break;
-
-                        driver = udev_device_get_driver(event->dev_parent);
-                        if (driver == NULL)
-                                break;
-                        l = strpcpy(&s, l, driver);
-                        break;
-                }
-                case SUBST_MAJOR: {
-                        char num[UTIL_PATH_SIZE];
-
-                        sprintf(num, "%u", major(udev_device_get_devnum(dev)));
-                        l = strpcpy(&s, l, num);
-                        break;
-                }
-                case SUBST_MINOR: {
-                        char num[UTIL_PATH_SIZE];
-
-                        sprintf(num, "%u", minor(udev_device_get_devnum(dev)));
-                        l = strpcpy(&s, l, num);
-                        break;
-                }
-                case SUBST_RESULT: {
-                        char *rest;
-                        int i;
-
-                        if (event->program_result == NULL)
-                                break;
-                        /* get part part of the result string */
-                        i = 0;
-                        if (attr != NULL)
-                                i = strtoul(attr, &rest, 10);
-                        if (i > 0) {
-                                char result[UTIL_PATH_SIZE];
-                                char tmp[UTIL_PATH_SIZE];
-                                char *cpos;
-
-                                strscpy(result, sizeof(result), event->program_result);
-                                cpos = result;
-                                while (--i) {
-                                        while (cpos[0] != '\0' && !isspace(cpos[0]))
-                                                cpos++;
-                                        while (isspace(cpos[0]))
-                                                cpos++;
-                                        if (cpos[0] == '\0')
-                                                break;
-                                }
-                                if (i > 0) {
-                                        log_error("requested part of result string not found");
-                                        break;
-                                }
-                                strscpy(tmp, sizeof(tmp), cpos);
-                                /* %{2+}c copies the whole string from the second part on */
-                                if (rest[0] != '+') {
-                                        cpos = strchr(tmp, ' ');
-                                        if (cpos)
-                                                cpos[0] = '\0';
-                                }
-                                l = strpcpy(&s, l, tmp);
-                        } else {
-                                l = strpcpy(&s, l, event->program_result);
-                        }
-                        break;
-                }
-                case SUBST_ATTR: {
-                        const char *value = NULL;
-                        char vbuf[UTIL_NAME_SIZE];
-                        size_t len;
-                        int count;
-
-                        if (attr == NULL) {
-                                log_error("missing file parameter for attr");
-                                break;
-                        }
-
-                        /* try to read the value specified by "[dmi/id]product_name" */
-                        if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0)
-                                value = vbuf;
+                subst_len = subst_format_var(event, dev, type, attr, s, l);
 
-                        /* try to read the attribute the device */
-                        if (value == NULL)
-                                value = udev_device_get_sysattr_value(event->dev, attr);
-
-                        /* try to read the attribute of the parent device, other matches have selected */
-                        if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev)
-                                value = udev_device_get_sysattr_value(event->dev_parent, attr);
-
-                        if (value == NULL)
-                                break;
+                /* SUBST_RESULT handles spaces itself */
+                if (replace_whitespace && type != SUBST_RESULT)
+                        /* util_replace_whitespace can replace in-place,
+                         * and does nothing if subst_len == 0
+                         */
+                        subst_len = util_replace_whitespace(s, s, subst_len);
 
-                        /* strip trailing whitespace, and replace unwanted characters */
-                        if (value != vbuf)
-                                strscpy(vbuf, sizeof(vbuf), value);
-                        len = strlen(vbuf);
-                        while (len > 0 && isspace(vbuf[--len]))
-                                vbuf[len] = '\0';
-                        count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
-                        if (count > 0)
-                                log_debug("%i character(s) replaced" , count);
-                        l = strpcpy(&s, l, vbuf);
-                        break;
-                }
-                case SUBST_PARENT: {
-                        struct udev_device *dev_parent;
-                        const char *devnode;
-
-                        dev_parent = udev_device_get_parent(event->dev);
-                        if (dev_parent == NULL)
-                                break;
-                        devnode = udev_device_get_devnode(dev_parent);
-                        if (devnode != NULL)
-                                l = strpcpy(&s, l, devnode + strlen("/dev/"));
-                        break;
-                }
-                case SUBST_DEVNODE:
-                        if (udev_device_get_devnode(dev) != NULL)
-                                l = strpcpy(&s, l, udev_device_get_devnode(dev));
-                        break;
-                case SUBST_NAME:
-                        if (event->name != NULL)
-                                l = strpcpy(&s, l, event->name);
-                        else if (udev_device_get_devnode(dev) != NULL)
-                                l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/"));
-                        else
-                                l = strpcpy(&s, l, udev_device_get_sysname(dev));
-                        break;
-                case SUBST_LINKS: {
-                        struct udev_list_entry *list_entry;
-
-                        list_entry = udev_device_get_devlinks_list_entry(dev);
-                        if (list_entry == NULL)
-                                break;
-                        l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/"));
-                        udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
-                                l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL);
-                        break;
-                }
-                case SUBST_ROOT:
-                        l = strpcpy(&s, l, "/dev");
-                        break;
-                case SUBST_SYS:
-                        l = strpcpy(&s, l, "/sys");
-                        break;
-                case SUBST_ENV:
-                        if (attr == NULL) {
-                                break;
-                        } else {
-                                const char *value;
-
-                                value = udev_device_get_property_value(event->dev, attr);
-                                if (value == NULL)
-                                        break;
-                                l = strpcpy(&s, l, value);
-                                break;
-                        }
-                default:
-                        log_error("unknown substitution type=%i", type);
-                        break;
-                }
+                s += subst_len;
+                l -= subst_len;
         }
 
 out:
+        assert(l >= 1);
         s[0] = '\0';
         return l;
 }
@@ -497,7 +526,7 @@ static void spawn_read(struct udev_event *event,
                 if (timeout_usec > 0) {
                         usec_t age_usec;
 
-                        age_usec = clock_boottime_or_monotonic() - event->birth_usec;
+                        age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
                         if (age_usec >= timeout_usec) {
                                 log_error("timeout '%s'", cmd);
                                 return;
@@ -638,7 +667,7 @@ static int spawn_wait(struct udev_event *event,
                 .pid = pid,
                 .accept_failure = accept_failure,
         };
-        _cleanup_event_unref_ sd_event *e = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         int r, ret;
 
         r = sd_event_new(&e);
@@ -648,13 +677,13 @@ static int spawn_wait(struct udev_event *event,
         if (timeout_usec > 0) {
                 usec_t usec, age_usec;
 
-                usec = now(clock_boottime_or_monotonic());
+                usec = now(CLOCK_MONOTONIC);
                 age_usec = usec - event->birth_usec;
                 if (age_usec < timeout_usec) {
                         if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) {
                                 spawn.timeout_warn = timeout_warn_usec - age_usec;
 
-                                r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
+                                r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
                                                       usec + spawn.timeout_warn, USEC_PER_SEC,
                                                       on_spawn_timeout_warning, &spawn);
                                 if (r < 0)
@@ -663,7 +692,7 @@ static int spawn_wait(struct udev_event *event,
 
                         spawn.timeout = timeout_usec - age_usec;
 
-                        r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
+                        r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
                                               usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn);
                         if (r < 0)
                                 return r;
@@ -696,10 +725,12 @@ int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) {
 
         pos = cmd;
         while (pos != NULL && pos[0] != '\0') {
-                if (pos[0] == '\'') {
-                        /* do not separate quotes */
+                if (IN_SET(pos[0], '\'', '"')) {
+                        /* do not separate quotes or double quotes */
+                        char delim[2] = { pos[0], '\0' };
+
                         pos++;
-                        argv[i] = strsep(&pos, "\'");
+                        argv[i] = strsep(&pos, delim);
                         if (pos != NULL)
                                 while (pos[0] == ' ')
                                         pos++;
@@ -849,11 +880,11 @@ void udev_event_execute_rules(struct udev_event *event,
                         /* disable watch during event processing */
                         if (major(udev_device_get_devnum(dev)) != 0)
                                 udev_watch_end(event->udev, event->dev_db);
-                }
 
-                if (major(udev_device_get_devnum(dev)) == 0 &&
-                    streq(udev_device_get_action(dev), "move"))
-                        udev_device_copy_properties(dev, event->dev_db);
+                        if (major(udev_device_get_devnum(dev)) == 0 &&
+                            streq(udev_device_get_action(dev), "move"))
+                                udev_device_copy_properties(dev, event->dev_db);
+                }
 
                 udev_rules_apply_to_event(rules, event,
                                           timeout_usec, timeout_warn_usec,
@@ -928,7 +959,7 @@ void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_
                 const char *cmd = udev_list_entry_get_name(list_entry);
                 enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry);
 
-                udev_event_apply_format(event, cmd, command, sizeof(command));
+                udev_event_apply_format(event, cmd, command, sizeof(command), false);
 
                 if (builtin_cmd < UDEV_BUILTIN_MAX)
                         udev_builtin_run(event->dev, builtin_cmd, command, false);