#include <sys/param.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define ATF_REQUIRE(a) assert(a)
#define CHILD_REQUIRE(a) assert(a)
#define FORKEE_ASSERTX(x) \
do { \
int ret = (x); \
if (!ret) \
errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
__FILE__, __LINE__, __func__, #x); \
} while (0)
#define FORKEE_ASSERT(x) \
do { \
int ret = (x); \
if (!ret) \
err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
__FILE__, __LINE__, __func__, #x); \
} while (0)
#ifndef nitemps
#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
#endif
#if defined(linux)
#define WALLSIG __WALL
#define sys_signame sys_siglist
#endif
static void
wait_for_zombie(pid_t pid)
{
printf("KR!!! %s(): %s:%d\n", __func__
, __FILE__
, __LINE__
);
/*
* Wait for a process to exit. This is kind of gross, but
* there is not a better way.
*/
for (;;) {
printf("KR!!! %s(): %s:%d\n", __func__
, __FILE__
, __LINE__
);
#if defined(__FreeBSD__) || defined(__NetBSD__)
#if defined(__NetBSD__)
struct kinfo_proc2 kp;
#elif defined(__FreeBSD__)
struct kinfo_proc kp;
#endif
size_t len;
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
#if defined(__NetBSD__)
mib[4] = sizeof(kp);
mib[5] = 1;
#endif
len = sizeof(kp);
if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) {
/* The KERN_PROC_PID sysctl fails for zombies. */
ATF_REQUIRE(errno == ESRCH);
break;
}
#elif defined(linux)
printf("KR!!! %s(): %s:%d\n", __func__
, __FILE__
, __LINE__
);
bool iszombie = false;
// open the /proc/*/stat file
char pbuf[32];
snprintf(pbuf
, sizeof(pbuf
), "/proc/%d/stat", (int)pid
);
FILE
* fpstat
= fopen(pbuf
, "r");
if (!fpstat) {
break;
};
{
int rpid =0; char rcmd[32]; char rstatc = 0;
fscanf(fpstat
, "%d %30s %c", &rpid
, rcmd
, &rstatc
);
iszombie = rstatc == 'Z';
}
if (iszombie == true)
break;
#endif
usleep(5000);
}
printf("KR!!! %s(): %s:%d pid=%d zombie!\n", __func__
, __FILE__
, __LINE__
, getpid
());
}
int
main(int argc, char **argv)
{
pid_t child, debugger, wpid;
int cpipe[2], dpipe[2], status;
char c = '0';
int tmp;
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
ATF_REQUIRE(pipe(cpipe) == 0);
ATF_REQUIRE((child = fork()) != -1);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
if (child == 0) {
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* Child process. */
close(cpipe[1]);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* Wait for parent to be ready. */
FORKEE_ASSERTX(read(cpipe[0], &c, sizeof(c)) == sizeof(c)); // assert
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
_exit(1);
}
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
ATF_REQUIRE(pipe(dpipe) == 0);
ATF_REQUIRE((debugger = fork()) != -1);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
if (debugger == 0) {
close(cpipe[0]);
close(cpipe[1]);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* Debugger process. */
FORKEE_ASSERT(ptrace(PT_ATTACH, child, NULL, 0) != -1); // assert
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
wpid = waitpid(child, &status, 0);
CHILD_REQUIRE(wpid == child);
CHILD_REQUIRE(WIFSTOPPED(status));
CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
FORKEE_ASSERT(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* Signal parent that debugger is attached. */
char debug1 = 'd';
printf("KR!!! %s(): %s:%d pid=%d trying to write to pipe character '%c'\n", __func__
, __FILE__
, __LINE__
, getpid
(), debug1
);
FORKEE_ASSERT(write(dpipe[1], &debug1, sizeof(debug1)) == sizeof(debug1));
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* Wait for parent's failed wait. */
sleep(1);
/*
tmp = read(dpipe[0], &c, sizeof(c));
if (tmp != 0)
printf("KR!!! %s(): %s:%d pid=%d got a character '%c'\n", __func__, __FILE__, __LINE__, getpid(), c);
else
printf("KR!!! %s(): %s:%d pid=%d no character!\n", __func__, __FILE__, __LINE__, getpid());
FORKEE_ASSERT(tmp == 0);
*/
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
wpid = waitpid(child, &status, 0);
CHILD_REQUIRE(wpid == child);
CHILD_REQUIRE(WIFEXITED(status));
CHILD_REQUIRE(WEXITSTATUS(status) == 1);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
_exit(0);
}
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
// close(dpipe[0]);
/* Parent process. */
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* Wait for the debugger to attach to the child. */
ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c));
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* Release the child. */
ATF_REQUIRE(write(cpipe[1], &c, sizeof(c)) == sizeof(c));
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
//ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
close(cpipe[0]);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
close(cpipe[1]);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
wait_for_zombie(child);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/*
* This wait should return a pid of 0 to indicate no status to
* report. The parent should see the child as non-exited
* until the debugger sees the exit.
*/
wpid = waitpid(child, &status, WNOHANG);
ATF_REQUIRE(wpid == 0);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* Signal the debugger to wait for the child. */
close(dpipe[0]);
close(dpipe[1]);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* Wait for the debugger. */
wpid = waitpid(debugger, &status, 0);
ATF_REQUIRE(wpid == debugger);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 0);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
/* The child process should now be ready. */
wpid = waitpid(child, &status, WNOHANG);
ATF_REQUIRE(wpid == child);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 1);
printf("KR!!! %s(): %s:%d pid=%d\n", __func__
, __FILE__
, __LINE__
, getpid
());
return 0;
}