Esempio n. 1
0
File: test.c Progetto: akpil/EDX
   void trace_me(int p1, int p2, int p3)
   {
     int i;
     int j = 7+p2;
     static int si = 3;
     static int sj;
     printf ("**\n");
     printf ("%d %d\n", p1, p2);

     if ((p1/3)*3 == p1) {
       i = j + 1;
       si--;
       sj = sj + p1 + p2 + p3;
       trace_me(p1-1, p2-1, p3-p2);
       g1 += 2;
       g2 = g2 - p1 - p2 -p3;
       printf("!\n");
     } else {
       if (((p1/3)*3+2) == p1) {
         i = p1;
         g1 -= (p2+p3);
         g2 += (p1 + p2 + p3);
         trace_me(p1-1, p2+1, p2 + p3);
         sj--;
         sj = sj - p1 - p2 -p3;
	 printf("@\n");
       } else {
         i = j + 1;
	 printf("#\n");
         printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", p1, p2, i, j,
             si, sj, g1, g2);
       }
     }
   }
Esempio n. 2
0
ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc)
{
    pid_t child, wpid;
    int status;

    ATF_REQUIRE((child = fork()) != -1);
    if (child == 0) {
        /* Child process. */
        trace_me();

        _exit(1);
    }

    /* Parent process. */

    /* The first wait() should report the stop from SIGSTOP. */
    wpid = waitpid(child, &status, 0);
    ATF_REQUIRE(wpid == child);
    ATF_REQUIRE(WIFSTOPPED(status));
    ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);

    /* Continue the child ignoring the SIGSTOP. */
    ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);

    /* The second wait() should report the exit status. */
    wpid = waitpid(child, &status, 0);
    ATF_REQUIRE(wpid == child);
    ATF_REQUIRE(WIFEXITED(status));
    ATF_REQUIRE(WEXITSTATUS(status) == 1);

    /* The child should no longer exist. */
    wpid = waitpid(child, &status, 0);
    ATF_REQUIRE(wpid == -1);
    ATF_REQUIRE(errno == ECHILD);
}
Esempio n. 3
0
void
execute_program(Process *sp, char **argv) {
    pid_t pid;

    debug(1, "Executing `%s'...", sp->filename);

    pid = fork();
    if (pid < 0) {
        perror("ltrace: fork");
        exit(1);
    } else if (!pid) {	/* child */
        change_uid(sp);
        trace_me();
        execvp(sp->filename, argv);
        fprintf(stderr, "Can't execute `%s': %s\n", sp->filename,
                strerror(errno));
        _exit(1);
    }

    debug(1, "PID=%d", pid);

    sp->pid = pid;

    return;
}
Esempio n. 4
0
static void fork_child_callback(void *user) {
	fork_child_data *data = user;
	if (data->runprofile) {
		char **argv = r_str_argv (data->cmd, NULL);
		if (!argv) {
			exit (1);
		}
		RRunProfile *rp = _get_run_profile (data->io, data->bits, argv);
		if (!rp) {
			r_str_argv_free (argv);
			exit (1);
		}
		trace_me ();
		r_run_start (rp);
		r_run_free (rp);
		r_str_argv_free (argv);
		exit (1);
	} else {
		char *_cmd = data->io->args ?
					 r_str_appendf (strdup (data->cmd), " %s", data->io->args) :
					 strdup (data->cmd);
		trace_me ();
		char **argv = r_str_argv (_cmd, NULL);
		if (!argv) {
			free (_cmd);
			return;
		}
		if (argv && *argv) {
			int i;
			for (i = 3; i < 1024; i++) {
				(void)close (i);
			}
			for (i = 0; argv[i]; i++) {
				r_str_arg_unescape (argv[i]);
			}
			if (execvp (argv[0], argv) == -1) {
				eprintf ("Could not execvp: %s\n", strerror (errno));
				exit (MAGIC_EXIT);
			}
		} else {
			eprintf ("Invalid execvp\n");
		}
		r_str_argv_free (argv);
		free (_cmd);
	}
}
ATF_TC_BODY(ptrace__new_child_pl_syscall_code_fork, tc)
{
	struct ptrace_lwpinfo pl[2];
	pid_t children[2], fpid, wpid;
	int status;

	ATF_REQUIRE((fpid = fork()) != -1);
	if (fpid == 0) {
		trace_me();
		follow_fork_parent(false);
	}

	/* Parent process. */
	children[0] = fpid;

	/* The first wait() should report the stop from SIGSTOP. */
	wpid = waitpid(children[0], &status, 0);
	ATF_REQUIRE(wpid == children[0]);
	ATF_REQUIRE(WIFSTOPPED(status));
	ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);

	ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);

	/* Continue the child ignoring the SIGSTOP. */
	ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);

	/* Wait for both halves of the fork event to get reported. */
	children[1] = handle_fork_events(children[0], pl);
	ATF_REQUIRE(children[1] > 0);

	ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_SCX) != 0);
	ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0);
	ATF_REQUIRE(pl[0].pl_syscall_code == SYS_fork);
	ATF_REQUIRE(pl[0].pl_syscall_code == pl[1].pl_syscall_code);
	ATF_REQUIRE(pl[0].pl_syscall_narg == pl[1].pl_syscall_narg);

	ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
	ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);

	/*
	 * The child can't exit until the grandchild reports status, so the
	 * grandchild should report its exit first to the debugger.
	 */
	wpid = wait(&status);
	ATF_REQUIRE(wpid == children[1]);
	ATF_REQUIRE(WIFEXITED(status));
	ATF_REQUIRE(WEXITSTATUS(status) == 2);

	wpid = wait(&status);
	ATF_REQUIRE(wpid == children[0]);
	ATF_REQUIRE(WIFEXITED(status));
	ATF_REQUIRE(WEXITSTATUS(status) == 1);

	wpid = wait(&status);
	ATF_REQUIRE(wpid == -1);
	ATF_REQUIRE(errno == ECHILD);
}
Esempio n. 6
0
ATF_TC_BODY(ptrace__follow_fork_parent_detached, tc)
{
    pid_t children[2], fpid, wpid;
    int status;

    ATF_REQUIRE((fpid = fork()) != -1);
    if (fpid == 0) {
        trace_me();
        follow_fork_parent();
    }

    /* Parent process. */
    children[0] = fpid;

    /* The first wait() should report the stop from SIGSTOP. */
    wpid = waitpid(children[0], &status, 0);
    ATF_REQUIRE(wpid == children[0]);
    ATF_REQUIRE(WIFSTOPPED(status));
    ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);

    ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);

    /* Continue the child ignoring the SIGSTOP. */
    ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);

    children[1] = handle_fork_events(children[0]);
    ATF_REQUIRE(children[1] > 0);

    ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1);
    ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);

    /*
     * The child can't exit until the grandchild reports status, so the
     * grandchild should report its exit first to the debugger.
     *
     * Even though the child process is detached, it is still a
     * child of the debugger, so it will still report it's exit
     * after the grandchild.
     */
    wpid = wait(&status);
    ATF_REQUIRE(wpid == children[1]);
    ATF_REQUIRE(WIFEXITED(status));
    ATF_REQUIRE(WEXITSTATUS(status) == 2);

    wpid = wait(&status);
    ATF_REQUIRE(wpid == children[0]);
    ATF_REQUIRE(WIFEXITED(status));
    ATF_REQUIRE(WEXITSTATUS(status) == 1);

    wpid = wait(&status);
    ATF_REQUIRE(wpid == -1);
    ATF_REQUIRE(errno == ECHILD);
}
Esempio n. 7
0
ATF_TC_BODY(ptrace__follow_fork_child_detached, tc)
{
    pid_t children[2], fpid, wpid;
    int status;

    ATF_REQUIRE((fpid = fork()) != -1);
    if (fpid == 0) {
        trace_me();
        follow_fork_parent();
    }

    /* Parent process. */
    children[0] = fpid;

    /* The first wait() should report the stop from SIGSTOP. */
    wpid = waitpid(children[0], &status, 0);
    ATF_REQUIRE(wpid == children[0]);
    ATF_REQUIRE(WIFSTOPPED(status));
    ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);

    ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);

    /* Continue the child ignoring the SIGSTOP. */
    ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);

    children[1] = handle_fork_events(children[0]);
    ATF_REQUIRE(children[1] > 0);

    ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
    ATF_REQUIRE(ptrace(PT_DETACH, children[1], (caddr_t)1, 0) != -1);

    /*
     * Should not see any status from the grandchild now, only the
     * child.
     */
    wpid = wait(&status);
    ATF_REQUIRE(wpid == children[0]);
    ATF_REQUIRE(WIFEXITED(status));
    ATF_REQUIRE(WEXITSTATUS(status) == 1);

    wpid = wait(&status);
    ATF_REQUIRE(wpid == -1);
    ATF_REQUIRE(errno == ECHILD);
}
Esempio n. 8
0
// __UNIX__ (not windows)
static int fork_and_ptraceme(RIO *io, int bits, const char *cmd) {
	bool runprofile = io->runprofile && *(io->runprofile);
	char **argv;
#if __APPLE__ && !__POWERPC__
	pid_t p = -1;
	posix_spawn_file_actions_t fileActions;
	ut32 ps_flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
	sigset_t no_signals;
	sigset_t all_signals;
	size_t copied = 1;
	cpu_type_t cpu = CPU_TYPE_ANY;
	posix_spawnattr_t attr = {0};
	posix_spawnattr_init (&attr);

	sigemptyset (&no_signals);
	sigfillset (&all_signals);
	posix_spawnattr_setsigmask (&attr, &no_signals);
	posix_spawnattr_setsigdefault (&attr, &all_signals);

	posix_spawn_file_actions_init (&fileActions);
	posix_spawn_file_actions_addinherit_np (&fileActions, STDIN_FILENO);
	posix_spawn_file_actions_addinherit_np (&fileActions, STDOUT_FILENO);
	posix_spawn_file_actions_addinherit_np (&fileActions, STDERR_FILENO);

	ps_flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
	ps_flags |= POSIX_SPAWN_START_SUSPENDED;
#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
	if (!runprofile) {
		int ret, useASLR = io->aslr;
		char *_cmd = io->args
			? r_str_appendf (strdup (cmd), " %s", io->args)
			: strdup (cmd);
		argv = r_str_argv (_cmd, NULL);
		if (!argv) {
			free (_cmd);
			return -1;
		}
		if (!*argv) {
			r_str_argv_free (argv);
			free (_cmd);
			eprintf ("Invalid execvp\n");
			return -1;
		}
		if (useASLR != -1) {
			if (!useASLR) {
				ps_flags |= _POSIX_SPAWN_DISABLE_ASLR;
			}
		}
		(void)posix_spawnattr_setflags (&attr, ps_flags);
#if __x86_64__
		if (bits == 32) {
			cpu = CPU_TYPE_I386;
			// cpu |= CPU_ARCH_ABI64;
		}
#endif
		posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &copied);
		{
			char *dst = r_file_readlink (argv[0]);
			if (dst) {
				argv[0] = dst;
			}
		}
		ret = posix_spawnp (&p, argv[0], NULL, &attr, argv, NULL);
		handle_posix_error (ret);
		posix_spawn_file_actions_destroy (&fileActions);
		r_str_argv_free (argv);
		free (_cmd);
		return p;
	} else {
		int ret;
		argv = r_str_argv (cmd, NULL);
		if (!argv) {
			posix_spawn_file_actions_destroy (&fileActions);
			return -1;
		}
		RRunProfile *rp = _get_run_profile (io, bits, argv);
		if (!rp) {
			r_str_argv_free (argv);
			posix_spawn_file_actions_destroy (&fileActions);
			return -1;
		}
		handle_posix_redirection (rp, &fileActions);
		if (rp->_args[0]) {
			if (!rp->_aslr) {
				ps_flags |= _POSIX_SPAWN_DISABLE_ASLR;
			}
#if __x86_64__
			if (rp->_bits == 32) {
				cpu = CPU_TYPE_I386;
			}
#endif
			(void)posix_spawnattr_setflags (&attr, ps_flags);
			posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &copied);
			ret = posix_spawnp (&p, rp->_args[0], &fileActions, &attr, rp->_args, NULL);
			handle_posix_error (ret);
		}
		r_str_argv_free (argv);
		r_run_free (rp);
		posix_spawn_file_actions_destroy (&fileActions);
		return p;
	}
	posix_spawn_file_actions_destroy (&fileActions);
	return -1;
#endif
	int ret, status, child_pid;
	child_pid = r_sys_fork ();
	switch (child_pid) {
	case -1:
		perror ("fork_and_ptraceme");
		break;
	case 0:
		if (runprofile) {
			argv = r_str_argv (cmd, NULL);
			if (!argv) {
				exit(1);
			}
			RRunProfile *rp = _get_run_profile (io, bits, argv);
			if (!rp) {
				r_str_argv_free (argv);
				exit (1);
			}
			trace_me ();
			r_run_start (rp);
			r_run_free (rp);
			r_str_argv_free (argv);
			exit (1);
		} else {
			char *_cmd = io->args ?
				r_str_appendf (strdup (cmd), " %s", io->args) :
				strdup (cmd);

			trace_me ();
			argv = r_str_argv (_cmd, NULL);
			if (!argv) {
				free (_cmd);
				return -1;
			}
			if (argv && *argv) {
				int i;
				for (i = 3; i < 1024; i++)
					(void)close (i);
				execvp (argv[0], argv);
			} else {
				eprintf ("Invalid execvp\n");
			}
			r_str_argv_free (argv);
			free (_cmd);
		}
		perror ("fork_and_attach: execv");
		//printf(stderr, "[%d] %s execv failed.\n", getpid(), ps.filename);
		exit (MAGIC_EXIT); /* error */
		return 0; // invalid pid // if exit is overriden.. :)
	default:
		/* XXX: clean this dirty code */
		do {
			ret = wait (&status);
			if (ret == -1) return -1;
			if (ret != child_pid) {
				eprintf ("Wait event received by "
					"different pid %d\n", ret);
			}
		} while (ret != child_pid);
		if (WIFSTOPPED (status)) {
			eprintf ("Process with PID %d started...\n", (int)child_pid);
		}
		if (WEXITSTATUS (status) == MAGIC_EXIT) {
			child_pid = -1;
		}
		// XXX kill (pid, SIGSTOP);
		break;
	}
	return child_pid;
}
Esempio n. 9
0
static void trace_me () {
#if __APPLE__
	signal (SIGTRAP, SIG_IGN); //NEED BY STEP
#endif
#if __APPLE__ || __BSD__
/* we can probably remove this #if..as long as PT_TRACE_ME is redefined for OSX in r_debug.h */
	signal (SIGABRT, inferior_abort_handler);
	if (ptrace (PT_TRACE_ME, 0, 0, 0) != 0) {
#else
	if (ptrace (PTRACE_TRACEME, 0, NULL, NULL) != 0) {
#endif
		r_sys_perror ("ptrace-traceme");
		exit (MAGIC_EXIT);
	}
}

// __UNIX__ (not windows)
static int fork_and_ptraceme(RIO *io, int bits, const char *cmd) {
	bool runprofile = io->runprofile && *(io->runprofile);
	char **argv;
#if __APPLE__ && !__POWERPC__
	if (!runprofile) {
#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
		posix_spawn_file_actions_t fileActions;
		ut32 ps_flags = POSIX_SPAWN_SETSIGDEF |
				POSIX_SPAWN_SETSIGMASK;
   		sigset_t no_signals;
    		sigset_t all_signals;
    		sigemptyset (&no_signals);
    		sigfillset (&all_signals);
		posix_spawnattr_t attr = {0};
		size_t copied = 1;
		cpu_type_t cpu;
		pid_t p = -1;
		int ret, useASLR = io->aslr;
		char *_cmd = io->args ?
				r_str_concatf (strdup (cmd), " %s", io->args) :
				strdup (cmd);
		argv = r_str_argv (_cmd, NULL);
		if (!argv) {
			free (_cmd);
			return -1;
		}
		if (!*argv) {
			r_str_argv_free (argv);
			free (_cmd);
			eprintf ("Invalid execvp\n");
			return -1;
		}
		posix_spawnattr_init (&attr);
		if (useASLR != -1) {
			if (!useASLR) ps_flags |= _POSIX_SPAWN_DISABLE_ASLR;
		}

		posix_spawn_file_actions_init (&fileActions);
		posix_spawn_file_actions_addinherit_np (&fileActions, STDIN_FILENO);
		posix_spawn_file_actions_addinherit_np (&fileActions, STDOUT_FILENO);
		posix_spawn_file_actions_addinherit_np (&fileActions, STDERR_FILENO);
		ps_flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
		ps_flags |= POSIX_SPAWN_START_SUSPENDED;

   		posix_spawnattr_setsigmask(&attr, &no_signals);
    		posix_spawnattr_setsigdefault(&attr, &all_signals);

		(void)posix_spawnattr_setflags (&attr, ps_flags);
#if __i386__ || __x86_64__
		cpu = CPU_TYPE_I386;
		if (bits == 64) cpu |= CPU_ARCH_ABI64;
#else
		cpu = CPU_TYPE_ANY;
#endif
		posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &copied);
		{
			char *dst = r_file_readlink (argv[0]);
			if (dst) {
				argv[0] = dst;
			}
		}
		ret = posix_spawnp (&p, argv[0], &fileActions, &attr, argv, NULL);
		switch (ret) {
		case 0:
			// eprintf ("Success\n");
			break;
		case 22:
			eprintf ("posix_spawnp: Invalid argument\n");
			break;
		case 86:
			eprintf ("Unsupported architecture\n");
			break;
		default:
			eprintf ("posix_spawnp: unknown error %d\n", ret);
			perror ("posix_spawnp");
			break;
		}
		posix_spawn_file_actions_destroy (&fileActions);
		r_str_argv_free (argv);
		free (_cmd);
		return p;
	}
#endif
	int ret, status, child_pid;

	child_pid = r_sys_fork ();
	switch (child_pid) {
	case -1:
		perror ("fork_and_ptraceme");
		break;
	case 0:
		if (runprofile) {
			char *expr = NULL;
			int i;
			RRunProfile *rp = r_run_new (NULL);
			argv = r_str_argv (cmd, NULL);
			for (i = 0; argv[i]; i++) {
				rp->_args[i] = argv[i];
			}
			rp->_args[i] = NULL;
			rp->_program = argv[0];
			rp->_dodebug = true;
			if (io->runprofile && *io->runprofile) {
				if (!r_run_parsefile (rp, io->runprofile)) {
					eprintf ("Can't find profile '%s'\n",
						io->runprofile);
					exit (MAGIC_EXIT);
				}
			}
			if (bits == 64)
				r_run_parseline (rp, expr=strdup ("bits=64"));
			else if (bits == 32)
				r_run_parseline (rp, expr=strdup ("bits=32"));
			free (expr);
			if (r_run_config_env (rp)) {
				eprintf ("Can't config the environment.\n");
				exit (1);
			}
			trace_me ();
			r_run_start (rp);
			r_run_free (rp);
			r_str_argv_free (argv);
			exit (1);
		} else {
			char *_cmd = io->args ?
				r_str_concatf (strdup (cmd), " %s", io->args) :
				strdup (cmd);

			trace_me ();
			argv = r_str_argv (_cmd, NULL);
			if (!argv) {
				free (_cmd);
				return -1;
			}
			if (argv && *argv) {
				int i;
				for (i = 3; i < 1024; i++)
					(void)close (i);
				execvp (argv[0], argv);
			} else {
				eprintf ("Invalid execvp\n");
			}
			r_str_argv_free (argv);
			free (_cmd);
		}
		perror ("fork_and_attach: execv");
		//printf(stderr, "[%d] %s execv failed.\n", getpid(), ps.filename);
		exit (MAGIC_EXIT); /* error */
		return 0; // invalid pid // if exit is overriden.. :)
	default:
		/* XXX: clean this dirty code */
		do {
			ret = wait (&status);
			if (ret == -1) return -1;
			if (ret != child_pid) {
				eprintf ("Wait event received by "
					"different pid %d\n", ret);
			}
		} while (ret != child_pid);
		if (WIFSTOPPED (status))
			eprintf ("Process with PID %d started...\n", (int)child_pid);
		if (WEXITSTATUS (status) == MAGIC_EXIT)
			child_pid = -1;
		// XXX kill (pid, SIGSTOP);
		break;
	}
	return child_pid;
}
Esempio n. 10
0
File: test.c Progetto: akpil/EDX
 int main()
 {
   trace_me(6, 4, 2);
   printf("%d\t%d\n", g1, g2);
   return 0;
 }
Esempio n. 11
0
static int fork_and_ptraceme(RIO *io, int bits, const char *cmd) {
#if __APPLE__ && !__POWERPC__
	return fork_and_ptraceme_for_mac(io, bits, cmd);
#else
	int ret, status, child_pid;
	bool runprofile = io->runprofile && *(io->runprofile);
	char **argv;
	child_pid = r_sys_fork ();
	switch (child_pid) {
	case -1:
		perror ("fork_and_ptraceme");
		break;
	case 0:
		if (runprofile) {
			argv = r_str_argv (cmd, NULL);
			if (!argv) {
				exit(1);
			}
			RRunProfile *rp = _get_run_profile (io, bits, argv);
			if (!rp) {
				r_str_argv_free (argv);
				exit (1);
			}
			trace_me ();
			r_run_start (rp);
			r_run_free (rp);
			r_str_argv_free (argv);
			exit (1);
		} else {
			char *_cmd = io->args ?
				r_str_appendf (strdup (cmd), " %s", io->args) :
				strdup (cmd);

			trace_me ();
			argv = r_str_argv (_cmd, NULL);
			if (!argv) {
				free (_cmd);
				return -1;
			}
			if (argv && *argv) {
				int i;
				for (i = 3; i < 1024; i++) {
					(void)close (i);
				}
				if (execvp (argv[0], argv) == -1) {
					eprintf ("Could not execvp: %s\n", strerror (errno));
					exit (MAGIC_EXIT);
				}
			} else {
				eprintf ("Invalid execvp\n");
			}
			r_str_argv_free (argv);
			free (_cmd);
		}
		perror ("fork_and_attach: execv");
		//printf(stderr, "[%d] %s execv failed.\n", getpid(), ps.filename);
		exit (MAGIC_EXIT); /* error */
		return 0; // invalid pid // if exit is overriden.. :)
	default:
		/* XXX: clean this dirty code */
		do {
			ret = wait (&status);
			if (ret == -1) return -1;
			if (ret != child_pid) {
				eprintf ("Wait event received by "
					"different pid %d\n", ret);
			}
		} while (ret != child_pid);
		if (WIFSTOPPED (status)) {
			eprintf ("Process with PID %d started...\n", (int)child_pid);
		}
		if (WEXITSTATUS (status) == MAGIC_EXIT) {
			child_pid = -1;
		}
		// XXX kill (pid, SIGSTOP);
		break;
	}
	return child_pid;
#endif
}