1/*	$OpenBSD: t_waitid.c,v 1.1 2022/10/26 23:18:02 kettenis Exp $	*/
2/* $NetBSD: t_wait.c,v 1.10 2021/07/17 14:03:35 martin Exp $ */
3
4/*-
5 * Copyright (c) 2016 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Christos Zoulas.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "macros.h"
34
35#include <sys/wait.h>
36#include <sys/resource.h>
37
38#include <errno.h>
39#include <inttypes.h>
40#include <limits.h>
41#include <pwd.h>
42#include <signal.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <unistd.h>
46
47#include "atf-c.h"
48
49ATF_TC(waitid_invalid);
50ATF_TC_HEAD(waitid_invalid, tc)
51{
52	atf_tc_set_md_var(tc, "descr",
53	    "Test that waitid(2) returns EINVAL with 0 options");
54}
55
56ATF_TC_BODY(waitid_invalid, tc)
57{
58	siginfo_t si;
59	ATF_REQUIRE(waitid(P_ALL, 0, &si, 0) == -1
60	    && errno == EINVAL);
61}
62
63ATF_TC(waitid_exited);
64ATF_TC_HEAD(waitid_exited, tc)
65{
66	atf_tc_set_md_var(tc, "descr",
67	    "Test that waitid(2) handled exiting process and code");
68}
69
70ATF_TC_BODY(waitid_exited, tc)
71{
72	siginfo_t si;
73	pid_t pid;
74
75	switch (pid = fork()) {
76	case 0:
77		exit(0x5a5a5a5a);
78		/*NOTREACHED*/
79	case -1:
80		ATF_REQUIRE(pid > 0);
81		__unreachable();
82	default:
83		ATF_REQUIRE(waitid(P_PID, pid, &si, WEXITED) == 0);
84		ATF_REQUIRE(si.si_status == 0x5a5a5a5a);
85		ATF_REQUIRE(si.si_pid == pid);
86		ATF_REQUIRE(si.si_uid == getuid());
87		ATF_REQUIRE(si.si_code == CLD_EXITED);
88		printf("user: %ju system: %ju\n", (uintmax_t)si.si_utime,
89		    (uintmax_t)si.si_utime);
90		break;
91	}
92}
93
94ATF_TC(waitid_terminated);
95ATF_TC_HEAD(waitid_terminated, tc)
96{
97	atf_tc_set_md_var(tc, "descr",
98	    "Test that waitid(2) handled terminated process and code");
99}
100
101ATF_TC_BODY(waitid_terminated, tc)
102{
103	siginfo_t si;
104	pid_t pid;
105
106	switch (pid = fork()) {
107	case 0:
108		sleep(100);
109		/*FALLTHROUGH*/
110	case -1:
111		ATF_REQUIRE(pid > 0);
112		__unreachable();
113	default:
114		ATF_REQUIRE(kill(pid, SIGTERM) == 0);
115		ATF_REQUIRE(waitid(P_PID, pid, &si, WEXITED) == 0);
116		ATF_REQUIRE(si.si_status == SIGTERM);
117		ATF_REQUIRE(si.si_pid == pid);
118		ATF_REQUIRE(si.si_uid == getuid());
119		ATF_REQUIRE(si.si_code == CLD_KILLED);
120		printf("user: %ju system: %ju\n", (uintmax_t)si.si_utime,
121		    (uintmax_t)si.si_utime);
122		break;
123	}
124}
125
126ATF_TC(waitid_coredumped);
127ATF_TC_HEAD(waitid_coredumped, tc)
128{
129	atf_tc_set_md_var(tc, "descr",
130	    "Test that waitid(2) handled coredumped process and code");
131}
132
133ATF_TC_BODY(waitid_coredumped, tc)
134{
135	siginfo_t si;
136	pid_t pid;
137	static const struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
138
139	switch (pid = fork()) {
140	case 0:
141		ATF_REQUIRE(setrlimit(RLIMIT_CORE, &rl) == 0);
142		*(char *)8 = 0;
143		/*FALLTHROUGH*/
144	case -1:
145		ATF_REQUIRE(pid > 0);
146		__unreachable();
147	default:
148		ATF_REQUIRE(waitid(P_PID, pid, &si, WEXITED) == 0);
149		ATF_REQUIRE(si.si_status == SIGSEGV);
150		ATF_REQUIRE(si.si_pid == pid);
151		ATF_REQUIRE(si.si_uid == getuid());
152		ATF_REQUIRE(si.si_code == CLD_DUMPED);
153		printf("user: %ju system: %ju\n", (uintmax_t)si.si_utime,
154		    (uintmax_t)si.si_utime);
155		break;
156	}
157}
158
159ATF_TC(waitid_stop_and_go);
160ATF_TC_HEAD(waitid_stop_and_go, tc)
161{
162	atf_tc_set_md_var(tc, "descr",
163	    "Test that waitid(2) handled stopped/continued process and code");
164}
165
166ATF_TC_BODY(waitid_stop_and_go, tc)
167{
168	siginfo_t si;
169	pid_t pid;
170	static const struct rlimit rl = { 0, 0 };
171
172	ATF_REQUIRE(setrlimit(RLIMIT_CORE, &rl) == 0);
173	switch (pid = fork()) {
174	case 0:
175		sleep(100);
176		/*FALLTHROUGH*/
177	case -1:
178		ATF_REQUIRE(pid > 0);
179		__unreachable();
180	default:
181		ATF_REQUIRE(kill(pid, SIGSTOP) == 0);
182		ATF_REQUIRE(waitid(P_PID, pid, &si, WSTOPPED) == 0);
183		ATF_REQUIRE(si.si_status == SIGSTOP);
184		ATF_REQUIRE(si.si_pid == pid);
185		ATF_REQUIRE(si.si_uid == getuid());
186		ATF_REQUIRE(si.si_code == CLD_STOPPED);
187		printf("user: %ju system: %ju\n", (uintmax_t)si.si_utime,
188		    (uintmax_t)si.si_utime);
189
190		ATF_REQUIRE(kill(pid, SIGCONT) == 0);
191		ATF_REQUIRE(waitid(P_PID, pid, &si, WCONTINUED) == 0);
192		ATF_REQUIRE(si.si_status == SIGCONT);
193		ATF_REQUIRE(si.si_pid == pid);
194		ATF_REQUIRE(si.si_uid == getuid());
195		ATF_REQUIRE(si.si_code == CLD_CONTINUED);
196		printf("user: %ju system: %ju\n", (uintmax_t)si.si_utime,
197		    (uintmax_t)si.si_utime);
198
199		ATF_REQUIRE(kill(pid, SIGQUIT) == 0);
200		ATF_REQUIRE(waitid(P_PID, pid, &si, WEXITED) == 0);
201		ATF_REQUIRE(si.si_status == SIGQUIT);
202		ATF_REQUIRE(si.si_pid == pid);
203		ATF_REQUIRE(si.si_uid == getuid());
204		ATF_REQUIRE(si.si_code == CLD_KILLED);
205		printf("user: %ju system: %ju\n", (uintmax_t)si.si_utime,
206		    (uintmax_t)si.si_utime);
207		break;
208	}
209}
210
211ATF_TC(waitid_stopgo_loop);
212ATF_TC_HEAD(waitid_stopgo_loop, tc)
213{
214	atf_tc_set_md_var(tc, "descr",
215	    "Test that waitid(2) handled stopped/continued process loop");
216}
217
218ATF_TC_BODY(waitid_stopgo_loop, tc)
219{
220	siginfo_t si;
221	pid_t pid;
222	static const struct rlimit rl = { 0, 0 };
223	size_t N = 100;
224
225	ATF_REQUIRE(setrlimit(RLIMIT_CORE, &rl) == 0);
226	switch (pid = fork()) {
227	case 0:
228		sleep(100);
229		/*FALLTHROUGH*/
230	case -1:
231		ATF_REQUIRE(pid > 0);
232		__unreachable();
233	}
234
235	printf("Before loop of SIGSTOP/SIGCONT sequence %zu times\n", N);
236	while (N --> 0) {
237		ATF_REQUIRE(kill(pid, SIGSTOP) == 0);
238		ATF_REQUIRE(waitid(P_PID, pid, &si, WSTOPPED) == 0);
239		ATF_REQUIRE(si.si_status == SIGSTOP);
240		ATF_REQUIRE(si.si_pid == pid);
241		ATF_REQUIRE(si.si_uid == getuid());
242		ATF_REQUIRE(si.si_code == CLD_STOPPED);
243
244		ATF_REQUIRE(kill(pid, SIGCONT) == 0);
245		ATF_REQUIRE(waitid(P_PID, pid, &si, WCONTINUED) == 0);
246		ATF_REQUIRE(si.si_status == SIGCONT);
247		ATF_REQUIRE(si.si_pid == pid);
248		ATF_REQUIRE(si.si_uid == getuid());
249		ATF_REQUIRE(si.si_code == CLD_CONTINUED);
250	}
251	ATF_REQUIRE(kill(pid, SIGQUIT) == 0);
252	ATF_REQUIRE(waitid(P_PID, pid, &si, WEXITED) == 0);
253	ATF_REQUIRE(si.si_status == SIGQUIT);
254	ATF_REQUIRE(si.si_pid == pid);
255	ATF_REQUIRE(si.si_uid == getuid());
256	ATF_REQUIRE(si.si_code == CLD_KILLED);
257	printf("user: %ju system: %ju\n", (uintmax_t)si.si_utime,
258	    (uintmax_t)si.si_utime);
259}
260
261ATF_TP_ADD_TCS(tp)
262{
263
264	ATF_TP_ADD_TC(tp, waitid_invalid);
265	ATF_TP_ADD_TC(tp, waitid_exited);
266	ATF_TP_ADD_TC(tp, waitid_terminated);
267	ATF_TP_ADD_TC(tp, waitid_coredumped);
268	ATF_TP_ADD_TC(tp, waitid_stop_and_go);
269	ATF_TP_ADD_TC(tp, waitid_stopgo_loop);
270
271	return atf_no_error();
272}
273