From: Daan De Meyer Date: Thu, 6 Nov 2025 13:30:06 +0000 (+0100) Subject: sd-event: Allow passing WNOWAIT to sd_event_add_child() X-Git-Tag: v259-rc1~59^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c11e1001dbd909130f789d3a2cfe888f29439f56;p=thirdparty%2Fsystemd.git sd-event: Allow passing WNOWAIT to sd_event_add_child() This allows doing the reaping outside of the callback, we'll use this when adding fibers in a later commit. --- diff --git a/man/sd_event_add_child.xml b/man/sd_event_add_child.xml index 4bf07baf592..28ff6245165 100644 --- a/man/sd_event_add_child.xml +++ b/man/sd_event_add_child.xml @@ -116,8 +116,9 @@ parameter specifies the PID of the process to watch, which must be a direct child process of the invoking process. The options parameter determines which state changes will be watched for. It must contain an OR-ed mask of WEXITED (watch for the child process terminating), - WSTOPPED (watch for the child process being stopped by a signal), and - WCONTINUED (watch for the child process being resumed by a signal). See + WSTOPPED (watch for the child process being stopped by a signal), + WCONTINUED (watch for the child process being resumed by a signal) and + WNOWAIT (Do not reap the child process after it exits). See waitid2 for further information. diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index fbe7656925e..5794b63aaaf 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -48,7 +48,7 @@ static bool EVENT_SOURCE_WATCH_PIDFD(const sd_event_source *s) { /* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */ return s && s->type == SOURCE_CHILD && - s->child.options == WEXITED; + (s->child.options & ~WNOWAIT) == WEXITED; } static bool event_source_is_online(sd_event_source *s) { @@ -1583,7 +1583,7 @@ _public_ int sd_event_add_child( assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); assert_return(pid > 1, -EINVAL); - assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL); + assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED|WNOWAIT)), -EINVAL); assert_return(options != 0, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_origin_changed(e), -ECHILD); @@ -1675,7 +1675,7 @@ _public_ int sd_event_add_child_pidfd( assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); assert_return(pidfd >= 0, -EBADF); - assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL); + assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED|WNOWAIT)), -EINVAL); assert_return(options != 0, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_origin_changed(e), -ECHILD); @@ -3689,7 +3689,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori zero(s->child.siginfo); if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, - WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options) < 0) + WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | (s->child.options & ~WNOWAIT)) < 0) return negative_errno(); if (s->child.siginfo.si_pid != 0) { @@ -3737,7 +3737,6 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) { /* Note that pidfd would also generate EPOLLHUP when the process gets reaped. But at this point we * only permit EPOLLIN, under the assumption that upon EPOLLHUP the child source should already * be set to pending, and we would have returned early above. */ - assert(!s->child.exited); zero(s->child.siginfo); if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0) @@ -4174,10 +4173,11 @@ static int source_dispatch(sd_event_source *s) { r = s->child.callback(s, &s->child.siginfo, s->userdata); - /* Now, reap the PID for good. */ + /* Now, reap the PID for good (unless WNOWAIT was specified by the caller). */ if (zombie) { - (void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED); - s->child.waited = true; + (void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED|(s->child.options & WNOWAIT)); + if (!FLAGS_SET(s->child.options, WNOWAIT)) + s->child.waited = true; } break; diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index 31931aea193..52bdcf7ae22 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -985,4 +985,84 @@ TEST(defer_add_post) { ASSERT_TRUE(dispatched_post); } +static int child_handler_wnowait(sd_event_source *s, const siginfo_t *si, void *userdata) { + int *counter = ASSERT_PTR(userdata); + + (*counter)++; + + if (*counter == 5) + ASSERT_OK(sd_event_exit(sd_event_source_get_event(s), 0)); + + return 0; +} + +TEST(child_wnowait) { + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + + ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD)); + + ASSERT_OK(sd_event_default(&e)); + + /* Fork a subprocess */ + pid_t pid; + ASSERT_OK_ERRNO(pid = fork()); + + if (pid == 0) + /* Child process - exit with a specific code */ + _exit(42); + + /* Add a child source with WNOWAIT flag */ + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + int counter = 0; + ASSERT_OK(sd_event_add_child(e, &s, pid, WEXITED|WNOWAIT, child_handler_wnowait, &counter)); + ASSERT_OK(sd_event_source_set_enabled(s, SD_EVENT_ON)); + + /* Run the event loop - this should call the handler */ + ASSERT_OK(sd_event_loop(e)); + ASSERT_EQ(counter, 5); + + /* Since we used WNOWAIT, the child should still be waitable */ + siginfo_t si = {}; + ASSERT_OK_ERRNO(waitid(P_PID, pid, &si, WEXITED|WNOHANG)); + ASSERT_EQ(si.si_pid, pid); + ASSERT_EQ(si.si_code, CLD_EXITED); + ASSERT_EQ(si.si_status, 42); +} + +TEST(child_pidfd_wnowait) { + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + + ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD)); + + ASSERT_OK(sd_event_default(&e)); + + /* Fork a subprocess */ + pid_t pid; + ASSERT_OK_ERRNO(pid = fork()); + + if (pid == 0) + /* Child process - exit with a specific code */ + _exit(42); + + _cleanup_close_ int pidfd = -EBADF; + ASSERT_OK_ERRNO(pidfd = pidfd_open(pid, 0)); + + /* Add a child source with WNOWAIT flag */ + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + int counter = 0; + ASSERT_OK(sd_event_add_child_pidfd(e, &s, pidfd, WEXITED|WNOWAIT, child_handler_wnowait, &counter)); + ASSERT_OK(sd_event_source_set_enabled(s, SD_EVENT_ON)); + + /* Run the event loop - this should call the handler */ + ASSERT_OK(sd_event_loop(e)); + ASSERT_EQ(counter, 5); + + /* Since we used WNOWAIT, the child should still be waitable */ + siginfo_t si = {}; + ASSERT_OK_ERRNO(waitid(P_PIDFD, pidfd, &si, WEXITED|WNOHANG)); + ASSERT_EQ(si.si_pid, pid); + ASSERT_EQ(si.si_code, CLD_EXITED); + ASSERT_EQ(si.si_status, 42); +} + DEFINE_TEST_MAIN(LOG_DEBUG);