]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: Ensure udev_event_spawn reads stdout
authorPaul Davey <paul.davey@alliedtelesis.co.nz>
Tue, 26 Nov 2019 23:51:59 +0000 (12:51 +1300)
committerLennart Poettering <lennart@poettering.net>
Wed, 4 Dec 2019 09:31:37 +0000 (10:31 +0100)
When running the program with udev_event_spawn it is possible to miss
output in stdout when the program exits causing the result to be empty
which can cause rules using the result to not function correctly.

This is due to the on_spawn_sigchld callback being processed while IO is
still pending and causing the event loop to exit.

To correct this the sigchld event source is made a lower priority than
the other event sources to ensure it is processed after IO.  This
requires changing the IO event source to oneshot and re-enabling it when
valid data is read but not for EOF, this prevents the empty pipes
constantly generating IO events.

src/udev/udev-event.c

index 58d484280aa5afcbc756b29b675111fbf4bb28b7..eb51139e519c9cbbeef18850dde01396a94a1125 100644 (file)
@@ -534,6 +534,7 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd
         char buf[4096], *p;
         size_t size;
         ssize_t l;
+        int r;
 
         assert(spawn);
         assert(fd == spawn->fd_stdout || fd == spawn->fd_stderr);
@@ -549,9 +550,11 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd
 
         l = read(fd, p, size - 1);
         if (l < 0) {
-                if (errno != EAGAIN)
-                        log_device_error_errno(spawn->device, errno,
-                                               "Failed to read stdout of '%s': %m", spawn->cmd);
+                if (errno == EAGAIN)
+                        goto reenable;
+
+                log_device_error_errno(spawn->device, errno,
+                                       "Failed to read stdout of '%s': %m", spawn->cmd);
 
                 return 0;
         }
@@ -574,6 +577,16 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd
                                          fd == spawn->fd_stdout ? "out" : "err", *q);
         }
 
+
+        if (l == 0)
+                return 0;
+
+        /* Re-enable the event source if we did not encounter EOF */
+reenable:
+        r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
+        if (r < 0)
+                log_device_error_errno(spawn->device, r,
+                                       "Failed to reactivate IO source of '%s'", spawn->cmd);
         return 0;
 }
 
@@ -634,6 +647,9 @@ static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userd
 
 static int spawn_wait(Spawn *spawn) {
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *sigchld_source = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *stdout_source = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *stderr_source = NULL;
         int r;
 
         assert(spawn);
@@ -670,20 +686,31 @@ static int spawn_wait(Spawn *spawn) {
         }
 
         if (spawn->fd_stdout >= 0) {
-                r = sd_event_add_io(e, NULL, spawn->fd_stdout, EPOLLIN, on_spawn_io, spawn);
+                r = sd_event_add_io(e, &stdout_source, spawn->fd_stdout, EPOLLIN, on_spawn_io, spawn);
+                if (r < 0)
+                        return r;
+                r = sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT);
                 if (r < 0)
                         return r;
         }
 
         if (spawn->fd_stderr >= 0) {
-                r = sd_event_add_io(e, NULL, spawn->fd_stderr, EPOLLIN, on_spawn_io, spawn);
+                r = sd_event_add_io(e, &stderr_source, spawn->fd_stderr, EPOLLIN, on_spawn_io, spawn);
+                if (r < 0)
+                        return r;
+                r = sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT);
                 if (r < 0)
                         return r;
         }
 
-        r = sd_event_add_child(e, NULL, spawn->pid, WEXITED, on_spawn_sigchld, spawn);
+        r = sd_event_add_child(e, &sigchld_source, spawn->pid, WEXITED, on_spawn_sigchld, spawn);
         if (r < 0)
                 return r;
+        /* SIGCHLD should be processed after IO is complete */
+        r = sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1);
+        if (r < 0)
+                return r;
+
 
         return sd_event_loop(e);
 }