]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/udev/udevadm-trigger.c
Merge pull request #12753 from jrouleau/fix/hibernate-resume-timeout
[thirdparty/systemd.git] / src / udev / udevadm-trigger.c
index 8a936cc94135d7c1781698e2312e3c20ec1880bb..f14010a2d0de3b368729856bb8917e9b552a72b5 100644 (file)
 /* SPDX-License-Identifier: GPL-2.0+ */
 
 #include <errno.h>
-#include <fcntl.h>
 #include <getopt.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
 
+#include "sd-device.h"
+#include "sd-event.h"
+
+#include "device-enumerator-private.h"
+#include "device-private.h"
 #include "fd-util.h"
+#include "fileio.h"
+#include "path-util.h"
+#include "process-util.h"
 #include "set.h"
 #include "string-util.h"
-#include "udev.h"
+#include "strv.h"
+#include "udevadm.h"
 #include "udevadm-util.h"
-#include "util.h"
+#include "udev-ctrl.h"
+#include "virt.h"
 
-static int verbose;
-static int dry_run;
+static bool arg_verbose = false;
+static bool arg_dry_run = false;
 
-static int exec_list(struct udev_enumerate *udev_enumerate, const char *action, Set *settle_set) {
-        struct udev_list_entry *entry;
-        int r;
+static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) {
+        sd_device *d;
+        int r, ret = 0;
 
-        udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) {
-                char filename[UTIL_PATH_SIZE];
+        FOREACH_DEVICE_AND_SUBSYSTEM(e, d) {
+                _cleanup_free_ char *filename = NULL;
                 const char *syspath;
-                _cleanup_close_ int fd = -1;
 
-                syspath = udev_list_entry_get_name(entry);
-                if (verbose)
+                if (sd_device_get_syspath(d, &syspath) < 0)
+                        continue;
+
+                if (arg_verbose)
                         printf("%s\n", syspath);
-                if (dry_run)
+                if (arg_dry_run)
                         continue;
 
-                strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
-                fd = open(filename, O_WRONLY|O_CLOEXEC);
-                if (fd < 0)
+                filename = path_join(syspath, "uevent");
+                if (!filename)
+                        return log_oom();
+
+                r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER);
+                if (r < 0) {
+                        log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
+                                       "Failed to write '%s' to '%s': %m", action, filename);
+                        if (ret == 0 && r != -ENOENT)
+                                ret = r;
                         continue;
+                }
 
                 if (settle_set) {
                         r = set_put_strdup(settle_set, syspath);
                         if (r < 0)
                                 return log_oom();
                 }
-
-                if (write(fd, action, strlen(action)) < 0)
-                        log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename);
         }
 
+        return ret;
+}
+
+static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *userdata) {
+        _cleanup_free_ char *val = NULL;
+        Set *settle_set = userdata;
+        const char *syspath;
+
+        assert(dev);
+        assert(settle_set);
+
+        if (sd_device_get_syspath(dev, &syspath) < 0)
+                return 0;
+
+        if (arg_verbose)
+                printf("settle %s\n", syspath);
+
+        val = set_remove(settle_set, syspath);
+        if (!val)
+                log_debug("Got epoll event on syspath %s not present in syspath set", syspath);
+
+        if (set_isempty(settle_set))
+                return sd_event_exit(sd_device_monitor_get_event(m), 0);
+
         return 0;
 }
 
-static const char *keyval(const char *str, const char **val, char *buf, size_t size) {
-        char *pos;
+static char* keyval(const char *str, const char **key, const char **val) {
+        char *buf, *pos;
+
+        buf = strdup(str);
+        if (!buf)
+                return NULL;
 
-        strscpy(buf, size,str);
         pos = strchr(buf, '=');
-        if (pos != NULL) {
+        if (pos) {
                 pos[0] = 0;
                 pos++;
         }
+
+        *key = buf;
         *val = pos;
+
         return buf;
 }
 
-static void help(void) {
+static int help(void) {
         printf("%s trigger [OPTIONS] DEVPATH\n\n"
                "Request events from the kernel.\n\n"
                "  -h --help                         Show this help\n"
@@ -85,12 +126,17 @@ static void help(void) {
                "     --name-match=NAME              Trigger devices with this /dev name\n"
                "  -b --parent-match=NAME            Trigger devices with that parent device\n"
                "  -w --settle                       Wait for the triggered events to complete\n"
+               "     --wait-daemon[=SECONDS]        Wait for udevd daemon to be initialized\n"
+               "                                    before triggering uevents\n"
                , program_invocation_short_name);
+
+        return 0;
 }
 
-static int adm_trigger(int argc, char *argv[]) {
+int trigger_main(int argc, char *argv[], void *userdata) {
         enum {
                 ARG_NAME = 0x100,
+                ARG_PING,
         };
 
         static const struct option options[] = {
@@ -108,6 +154,7 @@ static int adm_trigger(int argc, char *argv[]) {
                 { "name-match",        required_argument, NULL, ARG_NAME },
                 { "parent-match",      required_argument, NULL, 'b'      },
                 { "settle",            no_argument,       NULL, 'w'      },
+                { "wait-daemon",       optional_argument, NULL, ARG_PING },
                 { "version",           no_argument,       NULL, 'V'      },
                 { "help",              no_argument,       NULL, 'h'      },
                 {}
@@ -117,115 +164,106 @@ static int adm_trigger(int argc, char *argv[]) {
                 TYPE_SUBSYSTEMS,
         } device_type = TYPE_DEVICES;
         const char *action = "change";
-        _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *udev_enumerate = NULL;
-        _cleanup_(udev_monitor_unrefp) struct udev_monitor *udev_monitor = NULL;
-        _cleanup_close_ int fd_ep = -1;
-        int fd_udev = -1;
-        struct epoll_event ep_udev;
-        bool settle = false;
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         _cleanup_set_free_free_ Set *settle_set = NULL;
+        usec_t ping_timeout_usec = 5 * USEC_PER_SEC;
+        bool settle = false, ping = false;
         int c, r;
 
-        udev_enumerate = udev_enumerate_new(NULL);
-        if (!udev_enumerate)
-                return 1;
+        if (running_in_chroot() > 0) {
+                log_info("Running in chroot, ignoring request.");
+                return 0;
+        }
+
+        r = sd_device_enumerator_new(&e);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_allow_uninitialized(e);
+        if (r < 0)
+                return r;
 
         while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) {
-                const char *key;
-                const char *val;
-                char buf[UTIL_PATH_SIZE];
+                _cleanup_free_ char *buf = NULL;
+                const char *key, *val;
 
                 switch (c) {
                 case 'v':
-                        verbose = 1;
+                        arg_verbose = true;
                         break;
                 case 'n':
-                        dry_run = 1;
+                        arg_dry_run = true;
                         break;
                 case 't':
                         if (streq(optarg, "devices"))
                                 device_type = TYPE_DEVICES;
                         else if (streq(optarg, "subsystems"))
                                 device_type = TYPE_SUBSYSTEMS;
-                        else {
-                                log_error("unknown type --type=%s", optarg);
-                                return 2;
-                        }
+                        else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg);
                         break;
                 case 'c':
-                        if (!STR_IN_SET(optarg, "add", "remove", "change")) {
-                                log_error("unknown action '%s'", optarg);
-                                return 2;
-                        } else
-                                action = optarg;
+                        if (device_action_from_string(optarg) < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown action '%s'", optarg);
 
+                        action = optarg;
                         break;
                 case 's':
-                        r = udev_enumerate_add_match_subsystem(udev_enumerate, optarg);
-                        if (r < 0) {
-                                log_error_errno(r, "could not add subsystem match '%s': %m", optarg);
-                                return 2;
-                        }
+                        r = sd_device_enumerator_add_match_subsystem(e, optarg, true);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add subsystem match '%s': %m", optarg);
                         break;
                 case 'S':
-                        r = udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg);
-                        if (r < 0) {
-                                log_error_errno(r, "could not add negative subsystem match '%s': %m", optarg);
-                                return 2;
-                        }
+                        r = sd_device_enumerator_add_match_subsystem(e, optarg, false);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add negative subsystem match '%s': %m", optarg);
                         break;
                 case 'a':
-                        key = keyval(optarg, &val, buf, sizeof(buf));
-                        r = udev_enumerate_add_match_sysattr(udev_enumerate, key, val);
-                        if (r < 0) {
-                                log_error_errno(r, "could not add sysattr match '%s=%s': %m", key, val);
-                                return 2;
-                        }
+                        buf = keyval(optarg, &key, &val);
+                        if (!buf)
+                                return log_oom();
+                        r = sd_device_enumerator_add_match_sysattr(e, key, val, true);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add sysattr match '%s=%s': %m", key, val);
                         break;
                 case 'A':
-                        key = keyval(optarg, &val, buf, sizeof(buf));
-                        r = udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val);
-                        if (r < 0) {
-                                log_error_errno(r, "could not add negative sysattr match '%s=%s': %m", key, val);
-                                return 2;
-                        }
+                        buf = keyval(optarg, &key, &val);
+                        if (!buf)
+                                return log_oom();
+                        r = sd_device_enumerator_add_match_sysattr(e, key, val, false);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add negative sysattr match '%s=%s': %m", key, val);
                         break;
                 case 'p':
-                        key = keyval(optarg, &val, buf, sizeof(buf));
-                        r = udev_enumerate_add_match_property(udev_enumerate, key, val);
-                        if (r < 0) {
-                                log_error_errno(r, "could not add property match '%s=%s': %m", key, val);
-                                return 2;
-                        }
+                        buf = keyval(optarg, &key, &val);
+                        if (!buf)
+                                return log_oom();
+                        r = sd_device_enumerator_add_match_property(e, key, val);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add property match '%s=%s': %m", key, val);
                         break;
                 case 'g':
-                        r = udev_enumerate_add_match_tag(udev_enumerate, optarg);
-                        if (r < 0) {
-                                log_error_errno(r, "could not add tag match '%s': %m", optarg);
-                                return 2;
-                        }
+                        r = sd_device_enumerator_add_match_tag(e, optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add tag match '%s': %m", optarg);
                         break;
                 case 'y':
-                        r = udev_enumerate_add_match_sysname(udev_enumerate, optarg);
-                        if (r < 0) {
-                                log_error_errno(r, "could not add sysname match '%s': %m", optarg);
-                                return 2;
-                        }
+                        r = sd_device_enumerator_add_match_sysname(e, optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add sysname match '%s': %m", optarg);
                         break;
                 case 'b': {
-                        _cleanup_(udev_device_unrefp) struct udev_device *dev;
+                        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
 
-                        dev = find_device(optarg, "/sys");
-                        if (!dev) {
-                                log_error("unable to open the device '%s'", optarg);
-                                return 2;
-                        }
+                        r = find_device(optarg, "/sys", &dev);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to open the device '%s': %m", optarg);
 
-                        r = udev_enumerate_add_match_parent(udev_enumerate, dev);
-                        if (r < 0) {
-                                log_error_errno(r, "could not add parent match '%s': %m", optarg);
-                                return 2;
-                        }
+                        r = device_enumerator_add_match_parent_incremental(e, dev);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
                         break;
                 }
                 case 'w':
@@ -233,132 +271,112 @@ static int adm_trigger(int argc, char *argv[]) {
                         break;
 
                 case ARG_NAME: {
-                        _cleanup_(udev_device_unrefp) struct udev_device *dev;
+                        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
 
-                        dev = find_device(optarg, "/dev/");
-                        if (!dev) {
-                                log_error("unable to open the device '%s'", optarg);
-                                return 2;
-                        }
+                        r = find_device(optarg, "/dev/", &dev);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to open the device '%s': %m", optarg);
 
-                        r = udev_enumerate_add_match_parent(udev_enumerate, dev);
-                        if (r < 0) {
-                                log_error_errno(r, "could not add parent match '%s': %m", optarg);
-                                return 2;
+                        r = device_enumerator_add_match_parent_incremental(e, dev);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
+                        break;
+                }
+
+                case ARG_PING: {
+                        ping = true;
+                        if (optarg) {
+                                r = parse_sec(optarg, &ping_timeout_usec);
+                                if (r < 0)
+                                        log_error_errno(r, "Failed to parse timeout value '%s', ignoring: %m", optarg);
                         }
                         break;
                 }
 
                 case 'V':
-                        print_version();
-                        return 0;
+                        return print_version();
                 case 'h':
-                        help();
-                        return 0;
+                        return help();
                 case '?':
-                        return 1;
+                        return -EINVAL;
                 default:
                         assert_not_reached("Unknown option");
                 }
         }
 
+        if (ping) {
+                _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
+
+                r = udev_ctrl_new(&uctrl);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to initialize udev control: %m");
+
+                r = udev_ctrl_send_ping(uctrl);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to connect to udev daemon: %m");
+
+                r = udev_ctrl_wait(uctrl, ping_timeout_usec);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to wait for daemon to reply: %m");
+        }
+
         for (; optind < argc; optind++) {
-                _cleanup_(udev_device_unrefp) struct udev_device *dev;
+                _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
 
-                dev = find_device(argv[optind], NULL);
-                if (!dev) {
-                        log_error("unable to open the device '%s'", argv[optind]);
-                        return 2;
-                }
+                r = find_device(argv[optind], NULL, &dev);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to open the device '%s': %m", argv[optind]);
 
-                r = udev_enumerate_add_match_parent(udev_enumerate, dev);
-                if (r < 0) {
-                        log_error_errno(r, "could not add tag match '%s': %m", optarg);
-                        return 2;
-                }
+                r = device_enumerator_add_match_parent_incremental(e, dev);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add parent match '%s': %m", argv[optind]);
         }
 
         if (settle) {
-                fd_ep = epoll_create1(EPOLL_CLOEXEC);
-                if (fd_ep < 0) {
-                        log_error_errno(errno, "error creating epoll fd: %m");
-                        return 1;
-                }
+                settle_set = set_new(&string_hash_ops);
+                if (!settle_set)
+                        return log_oom();
 
-                udev_monitor = udev_monitor_new_from_netlink(NULL, "udev");
-                if (!udev_monitor) {
-                        log_error("error: unable to create netlink socket");
-                        return 3;
-                }
-                fd_udev = udev_monitor_get_fd(udev_monitor);
+                r = sd_event_default(&event);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get default event: %m");
 
-                if (udev_monitor_enable_receiving(udev_monitor) < 0) {
-                        log_error("error: unable to subscribe to udev events");
-                        return 4;
-                }
+                r = sd_device_monitor_new(&m);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create device monitor object: %m");
 
-                ep_udev = (struct epoll_event) { .events = EPOLLIN, .data.fd = fd_udev };
-                if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
-                        log_error_errno(errno, "fail to add fd to epoll: %m");
-                        return 5;
-                }
+                r = sd_device_monitor_attach_event(m, event);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to attach event to device monitor: %m");
 
-                settle_set = set_new(&string_hash_ops);
-                if (!settle_set) {
-                        log_oom();
-                        return 1;
-                }
+                r = sd_device_monitor_start(m, device_monitor_handler, settle_set);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to start device monitor: %m");
         }
 
         switch (device_type) {
         case TYPE_SUBSYSTEMS:
-                udev_enumerate_scan_subsystems(udev_enumerate);
+                r = device_enumerator_scan_subsystems(e);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to scan subsystems: %m");
                 break;
         case TYPE_DEVICES:
-                udev_enumerate_scan_devices(udev_enumerate);
+                r = device_enumerator_scan_devices(e);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to scan devices: %m");
                 break;
         default:
-                assert_not_reached("device_type");
+                assert_not_reached("Unknown device type");
         }
-        r = exec_list(udev_enumerate, action, settle_set);
+        r = exec_list(e, action, settle_set);
         if (r < 0)
-                return 1;
+                return r;
 
-        while (!set_isempty(settle_set)) {
-                int fdcount;
-                struct epoll_event ev[4];
-                int i;
-
-                fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
-                if (fdcount < 0) {
-                        if (errno != EINTR)
-                                log_error_errno(errno, "error receiving uevent message: %m");
-                        continue;
-                }
-
-                for (i = 0; i < fdcount; i++) {
-                        if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
-                                _cleanup_(udev_device_unrefp) struct udev_device *device;
-                                const char *syspath = NULL;
-
-                                device = udev_monitor_receive_device(udev_monitor);
-                                if (!device)
-                                        continue;
-
-                                syspath = udev_device_get_syspath(device);
-                                if (verbose)
-                                        printf("settle %s\n", syspath);
-                                if (!set_remove(settle_set, syspath))
-                                        log_debug("Got epoll event on syspath %s not present in syspath set", syspath);
-                        }
-                }
+        if (event && !set_isempty(settle_set)) {
+                r = sd_event_loop(event);
+                if (r < 0)
+                        return log_error_errno(r, "Event loop failed: %m");
         }
 
         return 0;
 }
-
-const struct udevadm_cmd udevadm_trigger = {
-        .name = "trigger",
-        .cmd = adm_trigger,
-        .help = "Request events from the kernel",
-};