udev: Ensure udev_event_spawn reads stdout

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.
This commit is contained in:
Paul Davey 2019-11-27 12:51:59 +13:00 committed by Lennart Poettering
parent eaadc03d61
commit 9f537ae310
1 changed files with 33 additions and 6 deletions

View 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);
}