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