#include <sys/wait.h>
#include <errno.h>
#include <ctype.h>
+#include <stdbool.h>
+// Spawn and trace child process, which spawn and trace its own child.
+//
+// Example taken from here
// 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 pid_t child_pid = 0;
+// Whether SIGSTOP send to tracee right after PTRACE_ATTACH was seen or not.
+static bool attach_stop = false;
+
+// Is tracee now inside syscall (syscall-enter-stop was seen) or outside
+// (the last syscall-stop was syscall-exit-stop).
+static bool in_syscall = false;
+
+// How to restart ptrace-stopped tracee.
+const enum __ptrace_request ptrace_restart = PTRACE_SYSCALL;
+//const enum __ptrace_request ptrace_restart = PTRACE_CONT;
+
+// Representation of each process.
+typedef struct worker {
+ char *name;
+ void (*work)(char*);
+} Worker;
+
+static void f(char *s){
+ while(1){
+ printf ("%s\n", s);
+ fflush(stdout);
+ sleep(1);
+ }
+}
+
+/*
static void a(){
while(1){
printf ("A\n");
sleep(1);
}
}
+*/
+// Will start following workers.
+static const Worker workers[] = {
+ {.name = "A", .work = f},
+ {.name = "B", .work = f},
+ //{.name = "C", .work = f},
+};
+static const size_t n_workers = sizeof(workers)/sizeof(Worker);
+
+// Child was stopped by ptrace. Determine why and restart it properly.
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);
+ snprintf(pref, n_pref, "[Child %d]", cpid);
if (WIFEXITED(status)) {
printf("%s: Child exited with status=%d\n", pref, WEXITSTATUS(status));
child_pid = 0;
+ // My child exited, so should i.
+ free(pref);
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;
+ // My child was killed, so i'm quitting now.
+ free(pref);
raise(SIGINT);
} else if (WIFSTOPPED(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);
+ 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);
+ if (csig == SIGTRAP|0x80) {
+ printf("%s: Child is traced and and is about to receive '%s' (%d|0x80 = %d)\n", pref, sys_siglist[csig^0x80], csig^0x80, csig);
+ } else {
+ printf("%s: Child is traced and and is about to receive '%s' (%d)\n", pref, sys_siglist[csig], csig);
+ }
+ siginfo_t csiginfo;
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 (!in_syscall) {
+ printf("%s: TRAP|0x80: Tracee is in syscall-ENTER-stop\n", pref);
+ } else {
+ printf("%s: TRAP|0x80: Tracee is in syscall-EXIT-stop\n", pref);
+ }
+
+ /*
+ 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);
+ printf("%s: TRAP|0x80: Tracee's syscall-stop confirmed\n", pref);
}
+ */
+
+ in_syscall = !in_syscall;
+ ptrace(ptrace_restart, cpid, 0, 0);
+
break;
case SIGTRAP:
- printf("%s: Tracee trapped\n", pref);
switch (status>>8) {
+ case ((PTRACE_EVENT_EXIT<<8) | SIGTRAP):
+ if (ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo) == -1) {
+ perror("PTRACE_GETSIGINFO:");
+ abort();
+ }
+
+ unsigned long exit_status;
+ if (ptrace(PTRACE_GETEVENTMSG, cpid, 0, &exit_status) == -1) {
+ perror("PTRACE_GETEVENTMSG:");
+ abort();
+ }
+
+ exit_status >>= 8;
+ printf("%s: TRAP: Tracee is in ptrace-event stop after EXIT with %d (si_code = %d, status = %d)\n", pref, exit_status, csiginfo.si_code, status>>8);
+ ptrace(ptrace_restart, cpid, 0, 0);
+ break;
+
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);
+ ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo);
+ // PTRACE_GETEVENTMSG .
+ printf("%s: TRAP: Tracee is in some ptrace-event stop (si_code = %d)\n", pref, csiginfo.si_code);
+ ptrace(ptrace_restart, cpid, 0, 0);
break;
default:
- ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo);
+ 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);
+ printf("%s: TRAP: Tracee is in signal-delivery stop: received from user-space\n", pref);
+ ptrace(ptrace_restart, cpid, 0, csig);
+
} else if (csiginfo.si_code == SI_KERNEL) {
- printf("%s: Tracee is in signal-delivery stop: received SIGTRAP from kernel\n", pref);
+ printf("%s: TRAP: Tracee is in signal-delivery stop: received from kernel\n", pref);
+ ptrace(ptrace_restart, cpid, 0, csig);
+
} else if (csiginfo.si_code == SIGTRAP) {
- printf("%s: Tracee is in syscall-stop\n", pref);
+ if (!in_syscall) {
+ printf("%s: TRAP: Tracee is in syscall-ENTER-stop\n", pref);
+ } else {
+ printf("%s: TRAP: Tracee is in syscall-EXIT-stop\n", pref);
+ }
+ in_syscall = !in_syscall;
+ ptrace(ptrace_restart, cpid, 0, 0);
+
} else {
- printf("%s: Tracee is in some other trap..\n", pref);
+ printf("%s: TRAP: Tracee is in some other trap..\n", pref);
+ ptrace(ptrace_restart, cpid, 0, 0);
}
break;
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
- ret = ptrace(PTRACE_GETSIGINFO, cpid, 0, &csiginfo);
+ ;
+ int 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);
+ printf("%s: STOP: Tracee is in group-stop (%s), ignoring stop signal %d\n", pref, strerror(errno), csig);
+
+ ptrace(ptrace_restart, 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);
+ printf("%s: STOP: Tracee is in signal-delivery-stop, injecting stop signal %d\n", pref, csig);
+
+ if (!attach_stop) {
+ printf("%s: STOP: stop signal after attach\n", pref);
+ attach_stop = true;
+ printf("%s: STOP: Enabling event tracing\n", pref);
+ //if(ptrace(PTRACE_SETOPTIONS, child_pid, NULL, PTRACE_O_TRACEEXIT) == -1) {
+ if(ptrace(PTRACE_SETOPTIONS, child_pid, NULL,
+ PTRACE_O_TRACEEXIT | PTRACE_O_TRACESYSGOOD) == -1) {
+ perror("ptrace setopts");
+ abort();
+ }
+ }
+
+ ptrace(ptrace_restart, 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);
+ ptrace(ptrace_restart, cpid, 0, csig);
break;
}
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);
+ printf("%s: SIGINT: No child left, exit with %d\n", pref, mypid%100);
+ free(pref);
+ _exit(mypid%100);
}
break;
}
}
printf("%s: --\n", pref);
+
+ free(pref);
}
-int main(int argc,
- char **argv){
+int main(int argc, char **argv){
- printf("pidA = %d\n", getpid());
+ setvbuf(stdout, NULL, _IONBF, 0);
struct sigaction sa;
sa.sa_sigaction = catcher;
sigemptyset(&sa.sa_mask);
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGTRAP, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL); // Ctrl+C
+
+ if (n_workers < 1) {
+ printf("Nikogo netu doma\n");
+ abort();
+ }
+
+ int i = 0;
+ char *myname = workers[i].name;
+ void (*mywork) (char*) = workers[i].work;
+ printf("pid%s = %d\n", myname, getpid());
+
+ for (++i; i < n_workers; i++) {
+ char *childname = workers[i].name;
+ printf("Forking %s\n", childname);
+ child_pid = fork();
+ if (child_pid) {
+ printf("pid%s = %d\n", childname, child_pid);
+
+ sleep(1);
+ printf("[%d]: attaching to %s..\n", getpid(), childname);
+ if(ptrace(PTRACE_ATTACH, child_pid, NULL, NULL) == -1){
+ perror("PTRACE_ATTACH");
+ }
+
+ break;
+
+ } else {
+ myname = workers[i].name;
+ mywork = workers[i].work;
+ printf("%s: My pid %d\n", myname, getpid());
+ }
+ }
- int result;
+ mywork(myname);
+ /*
printf("Forking B\n");
child_pid = fork();
if (child_pid) {
result = ptrace(PTRACE_ATTACH, child_pid, NULL, NULL);
if(result==-1){
perror("inner ptrace");
+ abort();
}
b();
c();
}
}
+ */
return 0;
}