1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*
21 * NAME
22 *	waitpid09.c
23 *
24 * DESCRIPTION
25 *	Check ability of parent to wait until child returns, and that the
26 *	child's process id is returned through the waitpid. Check that
27 *	waitpid returns immediately if no child is present.
28 *
29 * ALGORITHM
30 *	case 0:
31 *		Parent forks a child and waits. Parent should do nothing
32 *		further until child returns. The pid of the forked child
33 *		should match the returned value from the waitpid.
34 *
35 *	case 1:
36 *		Parent calls a waitpid with no children waiting. Waitpid
37 *		should return a -1 since there are no children to wait for.
38 *
39 * USAGE:  <for command-line>
40 *      waitpid09 [-c n] [-e] [-i n] [-I x] [-P x] [-t]
41 *      where,  -c n : Run n copies concurrently.
42 *              -e   : Turn on errno logging.
43 *              -i n : Execute test n times.
44 *              -I x : Execute test for x seconds.
45 *              -P x : Pause for x seconds between iterations.
46 *              -t   : Turn on syscall timing.
47 *
48 * History
49 *	07/2001 John George
50 *		-Ported
51 *      04/2002 wjhuie sigset cleanups
52 *
53 * Restrictions
54 *	None
55 */
56
57#define _GNU_SOURCE 1
58#include <sys/types.h>
59#include <signal.h>
60#include <errno.h>
61#include <sys/wait.h>
62#include <stdlib.h>
63
64#include "test.h"
65
66char *TCID = "waitpid09";
67int TST_TOTAL = 1;
68volatile int intintr;
69
70static void setup(void);
71static void cleanup(void);
72static void inthandlr();
73static void do_exit(void);
74static void setup_sigint(void);
75#ifdef UCLINUX
76static void do_exit_uclinux(void);
77#endif
78
79int main(int argc, char **argv)
80{
81	int lc;
82
83	int fail, pid, status, ret;
84
85	tst_parse_opts(argc, argv, NULL, NULL);
86
87#ifdef UCLINUX
88	maybe_run_child(&do_exit_uclinux, "");
89#endif
90
91	setup();
92
93	pid = FORK_OR_VFORK();
94	if (pid < 0) {
95		tst_brkm(TFAIL, cleanup, "Fork Failed");
96	} else if (pid == 0) {
97		/*
98		 * Child:
99		 * Set up to catch SIGINT.  The kids will wait till a
100		 * SIGINT has been received before they proceed.
101		 */
102		setup_sigint();
103
104		/* check for looping state if -i option is given */
105		for (lc = 0; TEST_LOOPING(lc); lc++) {
106			/* reset tst_count in case we are looping */
107			tst_count = 0;
108
109			intintr = 0;
110
111			fail = 0;
112			pid = FORK_OR_VFORK();
113			if (pid < 0) {
114				tst_brkm(TFAIL, cleanup, "Fork failed.");
115			} else if (pid == 0) {	/* child */
116#ifdef UCLINUX
117				if (self_exec(argv[0], "") < 0) {
118					tst_brkm(TFAIL, cleanup,
119						 "self_exec failed");
120				}
121#else
122				do_exit();
123#endif
124			} else {	/* parent */
125
126				/*
127				 *Check that waitpid with WNOHANG returns zero
128				 */
129				while (((ret = waitpid(pid, &status, WNOHANG))
130					!= 0) || (errno == EINTR)) {
131					if (ret == -1)
132						continue;
133
134					tst_resm(TFAIL, "return value for "
135						 "WNOHANG expected 0 got %d",
136						 ret);
137					fail = 1;
138				}
139#ifdef UCLINUX
140				/* Give the kids a chance to setup SIGINT again, since
141				 * this is cleared by exec().
142				 */
143				sleep(3);
144#endif
145
146				/* send SIGINT to child to tell it to proceed */
147				if (kill(pid, SIGINT) < 0) {
148					tst_resm(TFAIL, "Kill of child failed, "
149						 "errno = %d", errno);
150					fail = 1;
151				}
152
153				while (((ret = waitpid(pid, &status, 0)) != -1)
154				       || (errno == EINTR)) {
155					if (ret == -1)
156						continue;
157
158					if (ret != pid) {
159						tst_resm(TFAIL, "Expected %d "
160							 "got %d as proc id of "
161							 "child", pid, ret);
162						fail = 1;
163					}
164
165					if (status != 0) {
166						tst_resm(TFAIL, "status value "
167							 "got %d expected 0",
168							 status);
169						fail = 1;
170					}
171				}
172			}
173
174			pid = FORK_OR_VFORK();
175			if (pid < 0) {
176				tst_brkm(TFAIL, cleanup, "Second fork failed.");
177			} else if (pid == 0) {	/* child */
178				exit(0);
179			} else {	/* parent */
180				/* Give the child time to startup and exit */
181				sleep(2);
182
183				while (((ret = waitpid(pid, &status, WNOHANG))
184					!= -1) || (errno == EINTR)) {
185					if (ret == -1)
186						continue;
187
188					if (ret != pid) {
189						tst_resm(TFAIL, "proc id %d "
190							 "and retval %d do not "
191							 "match", pid, ret);
192						fail = 1;
193					}
194
195					if (status != 0) {
196						tst_resm(TFAIL, "non zero "
197							 "status received %d",
198							 status);
199						fail = 1;
200					}
201				}
202			}
203
204			if (fail)
205				tst_resm(TFAIL, "case 1 FAILED");
206			else
207				tst_resm(TPASS, "case 1 PASSED");
208
209			fail = 0;
210			ret = waitpid(pid, &status, 0);
211
212			if (ret != -1) {
213				tst_resm(TFAIL, "Expected -1 got %d", ret);
214				fail = 1;
215			}
216			if (errno != ECHILD) {
217				tst_resm(TFAIL, "Expected ECHILD got %d",
218					 errno);
219				fail = 1;
220			}
221
222			ret = waitpid(pid, &status, WNOHANG);
223			if (ret != -1) {
224				tst_resm(TFAIL, "WNOHANG: Expected -1 got %d",
225					 ret);
226				fail = 1;
227			}
228			if (errno != ECHILD) {
229				tst_resm(TFAIL, "WNOHANG: Expected ECHILD got "
230					 "%d", errno);
231				fail = 1;
232			}
233
234			if (fail)
235				tst_resm(TFAIL, "case 2 FAILED");
236			else
237				tst_resm(TPASS, "case 2 PASSED");
238		}
239
240		cleanup();
241	} else {
242		/* wait for the child to return */
243		waitpid(pid, &status, 0);
244		if (WEXITSTATUS(status) != 0) {
245			tst_brkm(TBROK, cleanup, "child returned bad "
246				 "status");
247		}
248	}
249
250	tst_exit();
251}
252
253/*
254 * setup_sigint()
255 *	sets up a SIGINT handler
256 */
257static void setup_sigint(void)
258{
259	if ((sig_t) signal(SIGINT, inthandlr) == SIG_ERR) {
260		tst_brkm(TFAIL, cleanup, "signal SIGINT failed, errno = %d",
261			 errno);
262	}
263}
264
265static void setup(void)
266{
267	TEST_PAUSE;
268}
269
270static void cleanup(void)
271{
272}
273
274static void inthandlr(void)
275{
276	intintr++;
277}
278
279static void wait_for_parent(void)
280{
281	int testvar;
282	while (!intintr)
283		testvar = 0;
284}
285
286static void do_exit(void)
287{
288	wait_for_parent();
289	exit(0);
290}
291
292#ifdef UCLINUX
293/*
294 * do_exit_uclinux()
295 *	Sets up SIGINT handler again, then calls do_exit
296 */
297static void do_exit_uclinux(void)
298{
299	setup_sigint();
300	do_exit();
301}
302#endif
303