parameter specifies the PID of the process to watch, which must be a direct child process of the invoking
process. The <parameter>options</parameter> parameter determines which state changes will be watched for.
It must contain an OR-ed mask of <constant>WEXITED</constant> (watch for the child process terminating),
- <constant>WSTOPPED</constant> (watch for the child process being stopped by a signal), and
- <constant>WCONTINUED</constant> (watch for the child process being resumed by a signal). See
+ <constant>WSTOPPED</constant> (watch for the child process being stopped by a signal),
+ <constant>WCONTINUED</constant> (watch for the child process being resumed by a signal) and
+ <constant>WNOWAIT</constant> (Do not reap the child process after it exits). See
<citerefentry project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
for further information.</para>
/* 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) {
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);
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);
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) {
/* 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)
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;
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);