1e1150663SDag-Erling Smørgrav /*-
2e1150663SDag-Erling Smørgrav * Copyright (c) 2026 Dag-Erling Smørgrav
3e1150663SDag-Erling Smørgrav *
4e1150663SDag-Erling Smørgrav * SPDX-License-Identifier: BSD-2-Clause
5e1150663SDag-Erling Smørgrav */
6e1150663SDag-Erling Smørgrav
7e1150663SDag-Erling Smørgrav #include <sys/wait.h>
8e1150663SDag-Erling Smørgrav
9e1150663SDag-Erling Smørgrav #include <errno.h>
10e1150663SDag-Erling Smørgrav #include <fcntl.h>
11e1150663SDag-Erling Smørgrav #include <poll.h>
12e1150663SDag-Erling Smørgrav #include <signal.h>
13e1150663SDag-Erling Smørgrav #include <stdbool.h>
14e1150663SDag-Erling Smørgrav #include <stdio.h>
15e1150663SDag-Erling Smørgrav #include <stdlib.h>
16e1150663SDag-Erling Smørgrav #include <unistd.h>
17e1150663SDag-Erling Smørgrav
18e1150663SDag-Erling Smørgrav #include <atf-c.h>
19e1150663SDag-Erling Smørgrav
20e1150663SDag-Erling Smørgrav /*
21e1150663SDag-Erling Smørgrav * Wait for a process to terminate, optionally reap it, and verify that it
22e1150663SDag-Erling Smørgrav * exited cleanly.
23e1150663SDag-Erling Smørgrav */
24e1150663SDag-Erling Smørgrav static void
wait_zero(pid_t pid,bool reap)25e1150663SDag-Erling Smørgrav wait_zero(pid_t pid, bool reap)
26e1150663SDag-Erling Smørgrav {
27e1150663SDag-Erling Smørgrav int wstatus;
28e1150663SDag-Erling Smørgrav pid_t ret;
29e1150663SDag-Erling Smørgrav
30e1150663SDag-Erling Smørgrav do {
31e1150663SDag-Erling Smørgrav ret = waitpid(pid, &wstatus, reap ? 0 : WNOWAIT);
32e1150663SDag-Erling Smørgrav ATF_REQUIRE(ret == pid || (ret < 0 && errno == EINTR));
33e1150663SDag-Erling Smørgrav } while (ret != pid);
34e1150663SDag-Erling Smørgrav ATF_CHECK(WIFEXITED(wstatus));
35e1150663SDag-Erling Smørgrav ATF_CHECK_EQ(WEXITSTATUS(wstatus), 0);
36e1150663SDag-Erling Smørgrav }
37e1150663SDag-Erling Smørgrav
38e1150663SDag-Erling Smørgrav /*
39e1150663SDag-Erling Smørgrav * Verify that pwait is still waiting for the target process.
40e1150663SDag-Erling Smørgrav */
41e1150663SDag-Erling Smørgrav static void
pwait_is_waiting(pid_t pwpid,pid_t tpid,int pwout,int pwerr)42e1150663SDag-Erling Smørgrav pwait_is_waiting(pid_t pwpid, pid_t tpid, int pwout, int pwerr)
43e1150663SDag-Erling Smørgrav {
44e1150663SDag-Erling Smørgrav struct pollfd pfds[2];
45e1150663SDag-Erling Smørgrav char line[64] = "", *eol, *rest;
46e1150663SDag-Erling Smørgrav ssize_t ret;
47e1150663SDag-Erling Smørgrav
48e1150663SDag-Erling Smørgrav pfds[0].fd = pwout;
49e1150663SDag-Erling Smørgrav pfds[0].events = POLLIN;
50e1150663SDag-Erling Smørgrav pfds[1].fd = pwerr;
51e1150663SDag-Erling Smørgrav pfds[1].events = POLLIN;
52e1150663SDag-Erling Smørgrav
53e1150663SDag-Erling Smørgrav /* loop until we have some output */
54e1150663SDag-Erling Smørgrav do {
55e1150663SDag-Erling Smørgrav /* send SIGINFO so pwait prints the target PID on stderr */
56e1150663SDag-Erling Smørgrav ATF_REQUIRE(kill(pwpid, SIGINFO) == 0);
57e1150663SDag-Erling Smørgrav } while ((ret = poll(pfds, 2, 100)) == 0);
58e1150663SDag-Erling Smørgrav ATF_REQUIRE_EQ(ret, 1);
59e1150663SDag-Erling Smørgrav ATF_REQUIRE_EQ(pfds[1].revents, POLLIN);
60e1150663SDag-Erling Smørgrav
61e1150663SDag-Erling Smørgrav /* read line from stderr */
62e1150663SDag-Erling Smørgrav ATF_REQUIRE((ret = read(pwerr, line, sizeof(line) - 1)) > 0);
63e1150663SDag-Erling Smørgrav
64e1150663SDag-Erling Smørgrav /* verify the line */
65e1150663SDag-Erling Smørgrav ATF_REQUIRE((eol = strchr(line, '\n')) != NULL);
66e1150663SDag-Erling Smørgrav *eol = '\0';
67e1150663SDag-Erling Smørgrav ATF_REQUIRE_EQ(strtol(line, &rest, 10), tpid);
68e1150663SDag-Erling Smørgrav ATF_REQUIRE_STREQ(rest, "");
69e1150663SDag-Erling Smørgrav }
70e1150663SDag-Erling Smørgrav
71e1150663SDag-Erling Smørgrav /*
72e1150663SDag-Erling Smørgrav * Verify that pwait has reported that the target process has terminated,
73e1150663SDag-Erling Smørgrav * then reap it.
74e1150663SDag-Erling Smørgrav */
75e1150663SDag-Erling Smørgrav static void
pwait_is_done(pid_t pwpid,pid_t tpid,int pwout,int pwerr)76e1150663SDag-Erling Smørgrav pwait_is_done(pid_t pwpid, pid_t tpid, int pwout, int pwerr)
77e1150663SDag-Erling Smørgrav {
78e1150663SDag-Erling Smørgrav struct pollfd pfds[2];
79e1150663SDag-Erling Smørgrav char line[64] = "", *eol, *rest;
80e1150663SDag-Erling Smørgrav ssize_t ret;
81e1150663SDag-Erling Smørgrav
82e1150663SDag-Erling Smørgrav pfds[0].fd = pwout;
83e1150663SDag-Erling Smørgrav pfds[0].events = POLLIN;
84e1150663SDag-Erling Smørgrav pfds[1].fd = pwerr;
85e1150663SDag-Erling Smørgrav pfds[1].events = POLLIN;
86e1150663SDag-Erling Smørgrav
87e1150663SDag-Erling Smørgrav /* loop until we have some output */
88e1150663SDag-Erling Smørgrav do {
89e1150663SDag-Erling Smørgrav /* nothing */
90e1150663SDag-Erling Smørgrav } while ((ret = poll(pfds, 2, 100)) == 0);
91e1150663SDag-Erling Smørgrav ATF_REQUIRE_EQ(ret, 1);
92e1150663SDag-Erling Smørgrav ATF_REQUIRE_EQ(pfds[0].revents, POLLIN);
93e1150663SDag-Erling Smørgrav
94e1150663SDag-Erling Smørgrav /* read line from stdout */
95e1150663SDag-Erling Smørgrav ATF_REQUIRE((ret = read(pwout, line, sizeof(line) - 1)) > 0);
96e1150663SDag-Erling Smørgrav
97e1150663SDag-Erling Smørgrav /* verify the line */
98e1150663SDag-Erling Smørgrav ATF_REQUIRE((eol = strchr(line, '\n')) != NULL);
99e1150663SDag-Erling Smørgrav *eol = '\0';
100e1150663SDag-Erling Smørgrav ATF_REQUIRE_EQ(strtol(line, &rest, 10), tpid);
101e1150663SDag-Erling Smørgrav ATF_REQUIRE_STREQ(rest, ": exited with status 0.");
102e1150663SDag-Erling Smørgrav
103e1150663SDag-Erling Smørgrav /* reap pwait */
104e1150663SDag-Erling Smørgrav wait_zero(pwpid, true);
105e1150663SDag-Erling Smørgrav }
106e1150663SDag-Erling Smørgrav
107e1150663SDag-Erling Smørgrav static void
pwait_reap_test(bool reap)108e1150663SDag-Erling Smørgrav pwait_reap_test(bool reap)
109e1150663SDag-Erling Smørgrav {
110e1150663SDag-Erling Smørgrav char pidstr[16];
111e1150663SDag-Erling Smørgrav pid_t tpid, pwpid;
112e1150663SDag-Erling Smørgrav int tpipe[2], pwout[2], pwerr[2];
113e1150663SDag-Erling Smørgrav
114e1150663SDag-Erling Smørgrav /* fork target process */
115e1150663SDag-Erling Smørgrav ATF_REQUIRE(pipe2(tpipe, O_CLOEXEC) == 0);
116e1150663SDag-Erling Smørgrav ATF_REQUIRE((tpid = fork()) >= 0);
117e1150663SDag-Erling Smørgrav if (tpid == 0) {
118e1150663SDag-Erling Smørgrav (void)close(tpipe[1]);
119e1150663SDag-Erling Smørgrav /* block until parent closes its end of the pipe */
120e1150663SDag-Erling Smørgrav (void)read(tpipe[0], pidstr, 1);
121e1150663SDag-Erling Smørgrav _exit(0);
122e1150663SDag-Erling Smørgrav }
123e1150663SDag-Erling Smørgrav (void)close(tpipe[0]);
124e1150663SDag-Erling Smørgrav
125e1150663SDag-Erling Smørgrav /* fork pwait */
126e1150663SDag-Erling Smørgrav snprintf(pidstr, sizeof(pidstr), "%ld", (long)tpid);
127e1150663SDag-Erling Smørgrav ATF_REQUIRE(pipe2(pwout, O_CLOEXEC) == 0);
128e1150663SDag-Erling Smørgrav ATF_REQUIRE(pipe2(pwerr, O_CLOEXEC) == 0);
129e1150663SDag-Erling Smørgrav ATF_REQUIRE((pwpid = fork()) >= 0);
130e1150663SDag-Erling Smørgrav if (pwpid == 0) {
131e1150663SDag-Erling Smørgrav (void)dup2(pwout[0], STDOUT_FILENO);
132e1150663SDag-Erling Smørgrav (void)dup2(pwerr[0], STDERR_FILENO);
133e1150663SDag-Erling Smørgrav execlp("pwait", "pwait", reap ? "-rv" : "-v", pidstr, NULL);
134e1150663SDag-Erling Smørgrav _exit(99);
135e1150663SDag-Erling Smørgrav }
136e1150663SDag-Erling Smørgrav
137e1150663SDag-Erling Smørgrav /* wait for pwait to become ready */
138e1150663SDag-Erling Smørgrav pwait_is_waiting(pwpid, tpid, pwout[1], pwerr[1]);
139e1150663SDag-Erling Smørgrav
140e1150663SDag-Erling Smørgrav /* unblock the target and wait for it to terminate */
141e1150663SDag-Erling Smørgrav (void)close(tpipe[1]);
142e1150663SDag-Erling Smørgrav wait_zero(tpid, false);
143e1150663SDag-Erling Smørgrav
144e1150663SDag-Erling Smørgrav if (reap) {
145e1150663SDag-Erling Smørgrav /* check that pwait is still waiting */
146e1150663SDag-Erling Smørgrav pwait_is_waiting(pwpid, tpid, pwout[1], pwerr[1]);
147e1150663SDag-Erling Smørgrav } else {
148e1150663SDag-Erling Smørgrav /* check that pwait has reported completion */
149e1150663SDag-Erling Smørgrav pwait_is_done(pwpid, tpid, pwout[1], pwerr[1]);
150e1150663SDag-Erling Smørgrav }
151e1150663SDag-Erling Smørgrav
152e1150663SDag-Erling Smørgrav /* reap the target */
153e1150663SDag-Erling Smørgrav wait_zero(tpid, true);
154e1150663SDag-Erling Smørgrav
155e1150663SDag-Erling Smørgrav if (reap) {
156e1150663SDag-Erling Smørgrav /* check that pwait has reported completion */
157e1150663SDag-Erling Smørgrav pwait_is_done(pwpid, tpid, pwout[1], pwerr[1]);
158e1150663SDag-Erling Smørgrav }
159e1150663SDag-Erling Smørgrav }
160e1150663SDag-Erling Smørgrav
161e1150663SDag-Erling Smørgrav ATF_TC(pwait_normal);
ATF_TC_HEAD(pwait_normal,tc)162e1150663SDag-Erling Smørgrav ATF_TC_HEAD(pwait_normal, tc)
163e1150663SDag-Erling Smørgrav {
164e1150663SDag-Erling Smørgrav atf_tc_set_md_var(tc, "descr", "Test that pwait returns before the "
165e1150663SDag-Erling Smørgrav "target process has been reaped");
166e1150663SDag-Erling Smørgrav }
ATF_TC_BODY(pwait_normal,tc)167e1150663SDag-Erling Smørgrav ATF_TC_BODY(pwait_normal, tc)
168e1150663SDag-Erling Smørgrav {
169*51c0cdb0SDag-Erling Smørgrav pwait_reap_test(false);
170e1150663SDag-Erling Smørgrav }
171e1150663SDag-Erling Smørgrav
172e1150663SDag-Erling Smørgrav ATF_TC(pwait_reap);
ATF_TC_HEAD(pwait_reap,tc)173e1150663SDag-Erling Smørgrav ATF_TC_HEAD(pwait_reap, tc)
174e1150663SDag-Erling Smørgrav {
175e1150663SDag-Erling Smørgrav atf_tc_set_md_var(tc, "descr", "Test that pwait -r does not return "
176e1150663SDag-Erling Smørgrav "until the target process has been reaped");
177e1150663SDag-Erling Smørgrav }
ATF_TC_BODY(pwait_reap,tc)178e1150663SDag-Erling Smørgrav ATF_TC_BODY(pwait_reap, tc)
179e1150663SDag-Erling Smørgrav {
180e1150663SDag-Erling Smørgrav pwait_reap_test(true);
181e1150663SDag-Erling Smørgrav }
182e1150663SDag-Erling Smørgrav
ATF_TP_ADD_TCS(tp)183e1150663SDag-Erling Smørgrav ATF_TP_ADD_TCS(tp)
184e1150663SDag-Erling Smørgrav {
185e1150663SDag-Erling Smørgrav ATF_TP_ADD_TC(tp, pwait_normal);
186e1150663SDag-Erling Smørgrav ATF_TP_ADD_TC(tp, pwait_reap);
187e1150663SDag-Erling Smørgrav return (atf_no_error());
188e1150663SDag-Erling Smørgrav }
189