]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #8066 from LittleCVR/udevadm-trigger-and-settle
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 9 Feb 2018 16:09:42 +0000 (17:09 +0100)
committerGitHub <noreply@github.com>
Fri, 9 Feb 2018 16:09:42 +0000 (17:09 +0100)
udevadm: allow trigger command to be synchronous

man/udevadm.xml
src/udev/udevadm-trigger.c

index 753ddf98daedd1d812585cc29e8def7bec2e4553..1495f556f3b9584541620daf6554e77e23e2eb3a 100644 (file)
             device.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><option>-w</option></term>
+          <term><option>--settle</option></term>
+          <listitem>
+            <para>Apart from triggering events, also waits for those events to
+            finish. Note that this is different from calling <command>udevadm
+            settle</command>. <command>udevadm settle</command> waits for all
+            events to finish. This option only waits for events triggered by
+            the same command to finish.</para>
+          </listitem>
+        </varlistentry>
 
         <xi:include href="standard-options.xml" xpointer="version" />
         <xi:include href="standard-options.xml" xpointer="help" />
index f78a2ba4373eb11466750831ac5dcf4c99d94791..05ddcdf48b76a0846b67271cdb633680a9096f51 100644 (file)
@@ -24,6 +24,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "fd-util.h"
+#include "set.h"
 #include "string-util.h"
 #include "udev-util.h"
 #include "udev.h"
 static int verbose;
 static int dry_run;
 
-static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) {
+static int exec_list(struct udev_enumerate *udev_enumerate, const char *action, Set *settle_set) {
         struct udev_list_entry *entry;
+        int r;
 
         udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) {
                 char filename[UTIL_PATH_SIZE];
+                const char *syspath;
                 int fd;
 
+                syspath = udev_list_entry_get_name(entry);
                 if (verbose)
-                        printf("%s\n", udev_list_entry_get_name(entry));
+                        printf("%s\n", syspath);
                 if (dry_run)
                         continue;
-                strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL);
+
+                strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
                 fd = open(filename, O_WRONLY|O_CLOEXEC);
                 if (fd < 0)
                         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);
                 close(fd);
         }
+
+        return 0;
 }
 
 static const char *keyval(const char *str, const char **val, char *buf, size_t size) {
@@ -87,6 +100,7 @@ static void help(void) {
                "  -y --sysname-match=NAME           Trigger devices with this /sys path\n"
                "     --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"
                , program_invocation_short_name);
 }
 
@@ -109,6 +123,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
                 { "sysname-match",     required_argument, NULL, 'y'      },
                 { "name-match",        required_argument, NULL, ARG_NAME },
                 { "parent-match",      required_argument, NULL, 'b'      },
+                { "settle",            no_argument,       NULL, 'w'      },
                 { "version",           no_argument,       NULL, 'V'      },
                 { "help",              no_argument,       NULL, 'h'      },
                 {}
@@ -119,13 +134,19 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
         } device_type = TYPE_DEVICES;
         const char *action = "change";
         _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate = NULL;
+        _cleanup_udev_monitor_unref_ 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_set_free_free_ Set *settle_set = NULL;
         int c, r;
 
         udev_enumerate = udev_enumerate_new(udev);
-        if (udev_enumerate == NULL)
+        if (!udev_enumerate)
                 return 1;
 
-        while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:Vh", options, NULL)) >= 0) {
+        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];
@@ -211,7 +232,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
                         _cleanup_udev_device_unref_ struct udev_device *dev;
 
                         dev = find_device(udev, optarg, "/sys");
-                        if (dev == NULL) {
+                        if (!dev) {
                                 log_error("unable to open the device '%s'", optarg);
                                 return 2;
                         }
@@ -223,12 +244,15 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
                         }
                         break;
                 }
+                case 'w':
+                        settle = true;
+                        break;
 
                 case ARG_NAME: {
                         _cleanup_udev_device_unref_ struct udev_device *dev;
 
                         dev = find_device(udev, optarg, "/dev/");
-                        if (dev == NULL) {
+                        if (!dev) {
                                 log_error("unable to open the device '%s'", optarg);
                                 return 2;
                         }
@@ -258,7 +282,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
                 _cleanup_udev_device_unref_ struct udev_device *dev;
 
                 dev = find_device(udev, argv[optind], NULL);
-                if (dev == NULL) {
+                if (!dev) {
                         log_error("unable to open the device '%s'", argv[optind]);
                         return 2;
                 }
@@ -270,18 +294,83 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
                 }
         }
 
+        if (settle) {
+                fd_ep = epoll_create1(EPOLL_CLOEXEC);
+                if (fd_ep < 0) {
+                        log_error_errno(errno, "error creating epoll fd: %m");
+                        return 1;
+                }
+
+                udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
+                if (!udev_monitor) {
+                        log_error("error: unable to create netlink socket");
+                        return 3;
+                }
+                fd_udev = udev_monitor_get_fd(udev_monitor);
+
+                if (udev_monitor_enable_receiving(udev_monitor) < 0) {
+                        log_error("error: unable to subscribe to udev events");
+                        return 4;
+                }
+
+                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;
+                }
+
+                settle_set = set_new(&string_hash_ops);
+                if (!settle_set) {
+                        log_oom();
+                        return 1;
+                }
+        }
+
         switch (device_type) {
         case TYPE_SUBSYSTEMS:
                 udev_enumerate_scan_subsystems(udev_enumerate);
-                exec_list(udev_enumerate, action);
-                return 0;
+                break;
         case TYPE_DEVICES:
                 udev_enumerate_scan_devices(udev_enumerate);
-                exec_list(udev_enumerate, action);
-                return 0;
+                break;
         default:
                 assert_not_reached("device_type");
         }
+        r = exec_list(udev_enumerate, action, settle_set);
+        if (r < 0)
+                return 1;
+
+        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_unref_ 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);
+                        }
+                }
+        }
+
+        return 0;
 }
 
 const struct udevadm_cmd udevadm_trigger = {