• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) International Business Machines  Corp., 2008
3  * Author: Matt Helsley <matthltc@us.ibm.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *
20  * Usage: $0 <num>
21  *
22  * vfork <num> times, stopping after each vfork. TODO: Requires an external process
23  * to send SIGCONT to goto the next vfork. <num> SIGCONT signals must be
24  * received before exiting.
25  *
26  * We can't do anything but execve or _exit in vfork'd processes
27  * so we use ptrace vfork'd processes in order to pause then during each
28  * vfork. This places the parent process in "TASK_UNINTERRUPTIBLE" state
29  * until vfork returns. This can delay delivery of signals to the parent
30  * process, even delay or stop system suspend.
31  */
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <time.h>
38 
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <sys/socket.h>
42 #include "test.h"
43 #include "config.h"
44 #include "../../syscalls/ptrace/ptrace.h"
45 
46 #define str_expand(s) str(s)
47 #define str(s) #s
48 
49 #define debug(s) \
50 perror("ERROR at " __FILE__ ":" str_expand(__LINE__) ": " s )
51 
52 char *filename = NULL;
53 FILE *fp = NULL;
54 int psync[2];
55 pid_t child = -1;
56 
57 int TST_TOTAL = 1;
58 char *TCID = "vfork";
59 
60 /* for signal handlers */
parent_cleanup(void)61 void parent_cleanup(void)
62 {
63 	close(psync[1]);
64 	if (fp) {
65 		fflush(fp);
66 		if (filename) {
67 			fclose(fp);
68 			(void)unlink(filename);
69 		}
70 	}
71 	tst_exit();
72 }
73 
kill_child(void)74 void kill_child(void)
75 {
76 
77 	/* Avoid killing all processes at the current user's level, and the
78 	 * test app as well =].
79 	 */
80 	if (0 < child && kill(child, 0) == 0) {
81 		/* Shouldn't happen, but I've seen it before... */
82 		if (ptrace(PTRACE_KILL, child, NULL, NULL) < 0) {
83 			tst_resm(TBROK | TERRNO,
84 				 "ptrace(PTRACE_KILL, %d, ..) failed", child);
85 		}
86 		(void)waitpid(child, NULL, WNOHANG);	/* Zombie children are bad. */
87 	}
88 
89 }
90 
child_cleanup(void)91 void child_cleanup(void)
92 {
93 	close(psync[0]);
94 	tst_exit();
95 }
96 
do_vfork(int count)97 int do_vfork(int count)
98 {
99 	pid_t child;
100 
101 	while (count) {
102 		child = vfork();
103 		if (child == 0)
104 			_exit(0);
105 		else if (child > 0)
106 			count--;
107 		else {
108 			tst_brkm(TFAIL | TERRNO, NULL, "vfork failed");
109 		}
110 	}
111 
112 	return EXIT_SUCCESS;
113 }
114 
115 /* Options */
116 int num_vforks = 1;
117 int do_pause = 0;
118 int do_sleep = 0;
119 struct timespec sleep_duration;
120 
sleepy_time(void)121 void sleepy_time(void)
122 {
123 	do {
124 		int rc = nanosleep(&sleep_duration, &sleep_duration);
125 
126 		if ((rc == -1) && (errno != EINTR))
127 			continue;
128 		break;
129 	} while (1);
130 }
131 
usage(void)132 void usage(void)
133 {
134 	tst_resm(TBROK, "usage: %s [-f [FILE]] [-s [NUM]] [-p] [NUM]\n\n"
135 		 "\t-f FILE\t\tFile to output trace data to.\n"
136 		 "\t-s NUM\t\tSleep for NUM seconds. [Default: 1 second]\n"
137 		 "\t\t\t\tSuffixes ms, us, s, m, and h correspond to\n"
138 		 "\t\t\t\tmilliseconds, microseconds, seconds [Default],\n"
139 		 "\t\t\t\tminutes, and hours respectively.\n\n"
140 		 "\t-p\t\tPause.\n\n"
141 		 "\tNUM\t\tExecute vfork NUM times.\n", TCID);
142 }
143 
_parse_opts(int argc,char ** argv)144 void _parse_opts(int argc, char **argv)
145 {
146 	int opt;
147 	char *units;
148 	unsigned long long duration = 1U;
149 
150 	sleep_duration.tv_sec = 0U;
151 	sleep_duration.tv_nsec = 0U;
152 
153 	while ((opt = getopt(argc, argv, "f:ps::")) != -1) {
154 		switch (opt) {
155 		case 'f':
156 			if ((fp = fopen(optarg, "w")) != NULL) {
157 				filename = optarg;
158 			}
159 			break;
160 		case 'p':
161 			do_pause = 1;
162 			break;
163 		case 's':
164 			if (optarg == NULL) {
165 				sleep_duration.tv_sec = 1;
166 				do_sleep = 1;
167 				break;
168 			}
169 			opt = sscanf(optarg, "%Ld%as", &duration, &units);
170 			if (opt < 1)
171 				break;
172 
173 			if ((opt != 2) || !strcmp(units, "s"))
174 				sleep_duration.tv_sec = duration;
175 			else if (!strcmp(units, "ms"))
176 				sleep_duration.tv_nsec = duration * 1000000U;
177 			else if (!strcmp(units, "us"))
178 				sleep_duration.tv_nsec = duration * 1000U;
179 			else if (!strcmp(units, "m"))
180 				sleep_duration.tv_sec = duration * 60U;
181 			else if (!strcmp(units, "h"))
182 				sleep_duration.tv_sec = duration * 3600U;
183 			else {
184 				tst_resm(TBROK, "Unrecognized time units: %s",
185 					 units);
186 				usage();
187 			}
188 			do_sleep = 1;
189 			break;
190 		default:
191 			usage();
192 		}
193 	}
194 
195 	if (optind >= argc)
196 		return;
197 	if (!strcmp(argv[optind], "--"))
198 		return;
199 	sscanf(argv[optind], "%d", &num_vforks);
200 }
201 
trace_grandchild(pid_t gchild)202 int trace_grandchild(pid_t gchild)
203 {
204 #if HAVE_DECL_PTRACE_GETSIGINFO
205 	siginfo_t info;
206 
207 	if (ptrace(PTRACE_GETSIGINFO, gchild, NULL, &info) == -1) {
208 		debug("ptrace(): ");
209 		return 0;
210 	}
211 	/*dump_siginfo(gchild, &info); */
212 	if ((info.si_code != 0) || (info.si_signo != SIGSTOP))
213 		return 0;
214 
215 	tst_resm(TINFO, "Grandchild spawn's pid=%d", gchild);
216 	fprintf(fp, "\t%d\n", gchild);
217 	fflush(fp);
218 	if (do_pause)
219 		pause();
220 	if (do_sleep)
221 		sleepy_time();
222 	if (ptrace(PTRACE_DETACH, gchild, NULL, NULL) == -1)
223 		debug("ptrace(): ");
224 	return -1;		/* don't wait for gchild */
225 #else
226 	return 0;
227 #endif
228 }
229 
do_trace(pid_t child,int num_children)230 int do_trace(pid_t child, int num_children)
231 {
232 	int my_exit_status = EXIT_SUCCESS;
233 	int status;
234 	pid_t process;
235 
236 	while (num_children > 0) {
237 		int died = 0;
238 
239 		/*printf("waiting for %d processes to exit\n", num_children); */
240 		process = waitpid(-1, &status, WUNTRACED);
241 		if (process < 1)
242 			continue;
243 		/*dump_status(process, status); */
244 		died = (WIFEXITED(status) || WIFSIGNALED(status));
245 		if (died)
246 			num_children--;
247 		if (process == child)
248 			my_exit_status = WEXITSTATUS(status);
249 		if (died || !WIFSTOPPED(status))
250 			continue;
251 
252 		if (process == child) {
253 			/* trace_child(process); */
254 			if (ptrace(PTRACE_CONT, process, NULL, NULL) == -1)
255 				debug("ptrace(): ");
256 		} else
257 			num_children += trace_grandchild(process);
258 
259 	}
260 
261 	return my_exit_status;
262 }
263 
send_mutex(int fd)264 void send_mutex(int fd)
265 {
266 	ssize_t nbytes = 0;
267 
268 	do {
269 		nbytes = write(fd, "r", 1);
270 		if (nbytes == 1)
271 			break;
272 		if (nbytes != -1)
273 			continue;
274 		if ((errno == EAGAIN) || (errno == EINTR))
275 			continue;
276 		else
277 			exit(EXIT_FAILURE);
278 		debug("write: ");
279 	} while (1);
280 }
281 
await_mutex(int fd)282 void await_mutex(int fd)
283 {
284 	char buffer[1];
285 	ssize_t nbytes = 0;
286 
287 	do {
288 		nbytes = read(fd, buffer, sizeof(buffer));
289 		if (nbytes == 1)
290 			break;
291 		if (nbytes != -1)
292 			continue;
293 		if ((errno == EAGAIN) || (errno == EINTR))
294 			continue;
295 		else
296 			exit(EXIT_FAILURE);
297 	} while (1);
298 }
299 
main(int argc,char ** argv)300 int main(int argc, char **argv)
301 {
302 
303 #if HAVE_DECL_PTRACE_SETOPTIONS && HAVE_DECL_PTRACE_O_TRACEVFORKDONE
304 	int exit_status;
305 
306 	_parse_opts(argc, argv);
307 
308 	if (fp == NULL) {
309 		fp = stderr;
310 	}
311 
312 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, psync) == -1) {
313 		tst_resm(TBROK | TERRNO, "socketpair() failed");
314 	} else {
315 
316 		child = fork();
317 		if (child == -1) {
318 			tst_resm(TBROK | TERRNO, "fork() failed");
319 		} else if (child == 0) {
320 
321 			int rc = EXIT_FAILURE;
322 
323 			tst_sig(FORK, DEF_HANDLER, child_cleanup);
324 
325 			if (close(psync[1])) {
326 				tst_resm(TBROK, "close(psync[1]) failed)");
327 			} else {
328 				/* sleep until the parent wakes us up */
329 				await_mutex(psync[0]);
330 				rc = do_vfork(num_vforks);
331 			}
332 			_exit(rc);
333 
334 		} else {
335 
336 			tst_sig(FORK, kill_child, parent_cleanup);
337 
338 			close(psync[0]);
339 
340 			/* Set up ptrace */
341 			if (ptrace(PTRACE_ATTACH, child, NULL, NULL) == -1) {
342 				tst_brkm(TBROK | TERRNO,
343 					 NULL, "ptrace(ATTACH) failed");
344 			}
345 			if (waitpid(child, NULL, 0) != child) {
346 				tst_resm(TBROK | TERRNO, "waitpid(%d) failed",
347 					 child);
348 				kill_child();
349 			} else {
350 
351 				if (ptrace(PTRACE_SETOPTIONS, child, NULL,
352 					   PTRACE_O_TRACEVFORK) == -1) {
353 					tst_resm(TINFO | TERRNO,
354 						 "ptrace(PTRACE_SETOPTIONS) "
355 						 "failed.");
356 				}
357 				if (ptrace(PTRACE_CONT, child, NULL, NULL) ==
358 				    -1) {
359 					tst_resm(TINFO | TERRNO,
360 						 "ptrace(PTRACE_CONT) failed.");
361 				}
362 
363 				send_mutex(psync[1]);
364 
365 				close(psync[1]);
366 
367 				tst_resm(TINFO, "Child spawn's pid=%d", child);
368 				fprintf(fp, "%d\n", child);
369 				fflush(fp);
370 
371 				exit_status = do_trace(child, ++num_vforks);
372 
373 				tst_resm(exit_status == 0 ? TPASS : TFAIL,
374 					 "do_trace %s",
375 					 (exit_status ==
376 					  0 ? "succeeded" : "failed"));
377 
378 				parent_cleanup();
379 
380 			}
381 
382 		}
383 
384 	}
385 
386 #else
387 	tst_resm(TCONF, "System doesn't support have required ptrace "
388 		 "capabilities.");
389 #endif
390 	tst_resm(TINFO, "Exiting...");
391 	tst_exit();
392 
393 }
394