xref: /freebsd/bin/pwait/tests/pwait_reap.c (revision 51c0cdb04919f776516a7fac8529e7279ea5efad)
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