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 
66 char *TCID = "waitpid09";
67 int TST_TOTAL = 1;
68 volatile int intintr;
69 
70 static void setup(void);
71 static void cleanup(void);
72 static void inthandlr();
73 static void do_exit(void);
74 static void setup_sigint(void);
75 #ifdef UCLINUX
76 static void do_exit_uclinux(void);
77 #endif
78 
main(int argc,char ** argv)79 int 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  */
setup_sigint(void)257 static 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 
setup(void)265 static void setup(void)
266 {
267 	TEST_PAUSE;
268 }
269 
cleanup(void)270 static void cleanup(void)
271 {
272 }
273 
inthandlr(void)274 static void inthandlr(void)
275 {
276 	intintr++;
277 }
278 
wait_for_parent(void)279 static void wait_for_parent(void)
280 {
281 	int testvar;
282 	while (!intintr)
283 		testvar = 0;
284 }
285 
do_exit(void)286 static 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  */
do_exit_uclinux(void)297 static void do_exit_uclinux(void)
298 {
299 	setup_sigint();
300 	do_exit();
301 }
302 #endif
303