--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <ctype.h>
+
+// https://stackoverflow.com/questions/2359581/calling-ptrace-inside-a-ptraced-linux-process
+// https://stackoverflow.com/questions/18437779/do-i-need-to-do-anything-with-a-sigchld-handler-if-i-am-just-using-wait-to-wai
+
+extern const char * const sys_siglist[];
+
+static pid_t child_pid;
+
+static void a(){
+ while(1){
+ printf ("A\n");
+ fflush(stdout);
+ sleep(1);
+ }
+}
+
+static void b(){
+ while(1){
+ printf ("B\n");
+ fflush(stdout);
+ sleep(1);
+ }
+}
+
+static void c(){
+ while(1){
+ printf ("C\n");
+ fflush(stdout);
+ sleep(1);
+ }
+}
+
+static void child_has_stopped(pid_t cpid, int status) {
+
+ size_t n_pref = 1 + snprintf(NULL, 0, "[Child %d]:", cpid);
+ char *pref = malloc(n_pref);
+ snprintf(pref, n_pref, "[Child %d]:", cpid);
+
+ if (WIFEXITED(status)) {
+ printf("%s: Child exited with status=%d\n", pref, WEXITSTATUS(status));
+ child_pid = 0;
+ raise(SIGINT);
+
+ } else if (WIFSIGNALED(status)) {
+ int termsig = WTERMSIG(status);
+ printf("%s: Child killed by signal '%s' (%d)\n", pref, sys_siglist[termsig], termsig);
+ child_pid = 0;
+ raise(SIGINT);
+
+ } else if (WIFSTOPPED(status)) {
+ int csig = WSTOPSIG(status);
+
+ n_pref = 1 + snprintf(pref, n_pref, "[Child %d] (%d):", cpid, csig);
+ pref = realloc(pref, n_pref);
+ snprintf(pref, n_pref, "[Child %d] (%d):", cpid, csig);
+
+ printf("%s: Child is traced and and is about to receive '%s' (%d)\n", pref, sys_siglist[csig], csig);
+
+ switch (csig) {
+ case SIGTRAP|0x80:
+ printf("%s: Tracee is in syscall-stop (SYSGOOD)\n", pref);
+ siginfo_t csiginfo;
+ int ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo);
+ if (csiginfo.si_signo == SIGTRAP && csiginfo.si_code == SIGTRAP|0x80) {
+ printf("%s: Tracee's syscall-stop confirmed (SYSGOOD)\n", pref);
+ }
+
+ break;
+
+ case SIGTRAP:
+ printf("%s: Tracee trapped\n", pref);
+ switch (status>>8) {
+ case ((PTRACE_EVENT_VFORK<<8) | SIGTRAP):
+ case ((PTRACE_EVENT_FORK<<8) | SIGTRAP):
+ case ((PTRACE_EVENT_CLONE<<8) | SIGTRAP):
+ case ((PTRACE_EVENT_VFORK_DONE<<8) | SIGTRAP):
+ case ((PTRACE_EVENT_EXEC<<8) | SIGTRAP):
+ case ((PTRACE_EVENT_EXIT<<8) | SIGTRAP):
+ case ((PTRACE_EVENT_STOP<<8) | SIGTRAP):
+ ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo);
+ printf("%s: Tracee is in ptrace-event stop (si_code = %d)\n", pref, csiginfo.si_code);
+ break;
+
+ default:
+ ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo);
+ if (csiginfo.si_code <= 0) {
+ printf("%s: Tracee is in signal-delivery stop: received SIGTRAP from user-space\n", pref);
+ } else if (csiginfo.si_code == SI_KERNEL) {
+ printf("%s: Tracee is in signal-delivery stop: received SIGTRAP from kernel\n", pref);
+ } else if (csiginfo.si_code == SIGTRAP) {
+ printf("%s: Tracee is in syscall-stop\n", pref);
+ } else {
+ printf("%s: Tracee is in some other trap..\n", pref);
+ }
+
+ break;
+ }
+
+ break;
+
+ case SIGSTOP:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo);
+ if (ret == -1 && errno == EINVAL) {
+ printf("%s: Tracee is in group-stop, ignoring stop signal %d\n", pref, csig);
+ ptrace(PTRACE_CONT, cpid, 0, 0);
+ } else {
+ printf("%s: Tracee is in STOP signal signal-delivery-stop, injecting stop signal %d\n", pref, csig);
+ ptrace(PTRACE_CONT, cpid, 0, csig);
+ }
+
+ break;
+
+ default:
+ printf("%s: Tracee is in signal-delivery-stop, injecting signal %d\n", pref, csig);
+ ptrace(PTRACE_CONT, cpid, 0, csig);
+ break;
+ }
+
+ } else if (WIFCONTINUED(status)) {
+ printf("%s: Child continued\n", pref);
+
+ } else {
+ printf("%s: Child is elsewhere\n", pref);
+ }
+
+ free(pref);
+}
+
+// FIXME: Try sigaction with SA_NOCLDSTOP .
+static void catcher(int sig, siginfo_t *info, void *vp) {
+ pid_t mypid = getpid();
+
+ size_t n_pref = 1 + snprintf(NULL, 0, "[%d] (%d):", mypid, sig);
+ char *pref = malloc(n_pref);
+
+ snprintf(pref, n_pref, "[%d] (%d)", mypid, sig);
+
+ printf("%s: Received signal '%s' (%d) on %d (reason %d)\n", pref, sys_siglist[sig], sig, info->si_pid, info->si_code);
+
+ switch (sig) {
+ case SIGINT:
+ if (child_pid != 0) {
+ printf("%s: SIGINT: Resend to child %d\n", pref, child_pid);
+ kill(child_pid, SIGINT);
+ } else {
+ printf("%s: SIGINT: No child left, exit\n", pref);
+ _exit(123);
+ }
+ break;
+
+ case SIGTRAP:
+ printf("%s: We're trapped\n", pref);
+ break;
+
+ case SIGCHLD: {
+ // *info - info about signal received by _parent_.
+ switch (info->si_code) {
+ case CLD_EXITED:
+ printf("%s: SIGCHLD: Child has exited with %d\n", pref, info->si_status);
+ break;
+ case CLD_KILLED:
+ printf("%s: SIGCHLD: Child was killed by '%s' (%d).\n", pref, sys_siglist[info->si_status], info->si_status);
+ break;
+ case CLD_DUMPED:
+ printf("%s: SIGCHLD: Child terminated abnormally.\n", pref);
+ break;
+ case CLD_TRAPPED:
+ printf("%s: SIGCHLD: Traced child has trapped by '%s' (%d)\n", pref, sys_siglist[info->si_status], info->si_status);
+ break;
+ case CLD_STOPPED:
+ printf("%s: SIGCHLD: Child has stopped by '%s' (%d)\n", pref, sys_siglist[info->si_status], info->si_status);
+ break;
+ case CLD_CONTINUED:
+ printf("%s: SIGCHLD: Stopped child has continued by '%s' (%d).\n", pref, sys_siglist[info->si_status], info->si_status);
+ break;
+ }
+
+ int wstatus;
+ pid_t child_pid;
+ while ((child_pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
+ printf("%s: Child pid %d\n", pref, child_pid);
+
+ child_has_stopped(child_pid, wstatus);
+ }
+ }
+ }
+ printf("%s: --\n", pref);
+}
+
+int main(int argc,
+ char **argv){
+
+ printf("pidA = %d\n", getpid());
+
+ struct sigaction sa;
+ sa.sa_sigaction = catcher;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGTRAP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ int result;
+
+ printf("Forking B\n");
+ child_pid = fork();
+ if (child_pid) {
+ printf("pidB = %d\n", child_pid);
+
+ sleep(1);
+ printf("[%d]: attaching to B..\n", getpid());
+ result = ptrace(PTRACE_ATTACH, child_pid, NULL, NULL);
+ if(result == -1){
+ perror("outer ptrace");
+ }
+
+ a();
+
+ } else {
+ printf("B Started\n");
+ sleep(3);
+ printf("Forking C\n");
+ child_pid = fork();
+
+ //b();
+ if (child_pid) {
+ printf("pidC = %d\n", child_pid);
+
+ printf("[%d]: attaching to pidC..\n", getpid());
+ result = ptrace(PTRACE_ATTACH, child_pid, NULL, NULL);
+ if(result==-1){
+ perror("inner ptrace");
+ }
+
+ b();
+
+ } else {
+ printf("C Started\n");
+ c();
+ }
+ }
+
+ return 0;
+}