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