END_TEST START_TEST(t_trace_syscall_signal) { int status; pid_t pid; if ((pid = fork()) < 0) fail("fork failed: %s", strerror(errno)); else if (!pid) { /* child */ if (!pink_trace_me()) { perror("pink_trace_me"); _exit(EXIT_FAILURE); } kill(getpid(), SIGSTOP); _exit(13); } else { /* parent */ fail_if(waitpid(pid, &status, 0) < 0, "%d(%s)", errno, strerror(errno)); fail_unless(WIFSTOPPED(status), "%#x", status); fail_unless(WSTOPSIG(status) == SIGSTOP, "%#x", status); fail_unless(pink_trace_syscall(pid, SIGTERM), "%d(%s)", errno, strerror(errno)); fail_if(waitpid(pid, &status, 0) < 0, "%d(%s)", errno, strerror(errno)); fail_unless(WIFSIGNALED(status), "%#x", status); fail_unless(WTERMSIG(status) == SIGTERM, "%#x", status); } }
END_TEST START_TEST(t_event_syscall) { int status; pid_t pid; pink_event_t event; if ((pid = fork()) < 0) fail("fork: %s", strerror(errno)); else if (!pid) { /* child */ if (!pink_trace_me()) { perror("pink_trace_me"); _exit(-1); } kill(getpid(), SIGSTOP); /* At this point Glibc may have cached getpid() so we call it * using syscall(2). */ syscall(SYS_getpid); } else { /* parent */ fail_if(waitpid(pid, &status, 0) < 0, "%d(%s)", strerror(errno)); fail_unless(WIFSTOPPED(status), "%#x", status); fail_unless(WSTOPSIG(status) == SIGSTOP, "%#x", status); fail_unless(pink_trace_setup(pid, PINK_TRACE_OPTION_SYSGOOD), "%d(%s)", errno, strerror(errno)); /* Resume the child and arrange it to be stopped at the next * system call. */ fail_unless(pink_trace_syscall(pid, 0), "%d(%s)", errno, strerror(errno)); fail_if(waitpid(pid, &status, 0) < 0, "%d(%s)", errno, strerror(errno)); event = pink_event_decide(status); fail_unless(event == PINK_EVENT_SYSCALL, "%d != %d", PINK_EVENT_SYSCALL, event); pink_trace_kill(pid); } }
/* * Document-method: PinkTrace::Trace.syscall * call-seq: * PinkTrace::Trace.syscall(pid, [sig=0]) -> nil * * Restarts the stopped child process and arranges it to be stopped after * the entry or exit of the next system call. * * The +sig+ argument is treated as the same way as the +sig+ argument of * PinkTrace::Trace.cont. */ VALUE pinkrb_trace_syscall(int argc, VALUE *argv, VALUE mod) { pid_t pid; long sig; VALUE vpid, vsig; switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { case 1: sig = 0; break; case 2: sig = NUM2LONG(vsig); break; default: abort(); } pid = NUM2PIDT(vpid); if (!pink_trace_syscall(pid, sig)) rb_sys_fail("pink_trace_syscall()"); return Qnil; }
int main(int argc, char **argv) { int sig, status, exit_code; pink_event_t event; struct child son; /* Parse arguments */ if (argc < 2) { fprintf(stderr, "Usage: %s program [argument...]\n", argv[0]); return EXIT_FAILURE; } /* Fork */ if ((son.pid = fork()) < 0) { perror("fork"); return EXIT_FAILURE; } else if (!son.pid) { /* child */ /* Set up for tracing */ if (!pink_trace_me()) { perror("pink_trace_me"); _exit(EXIT_FAILURE); } /* Stop and let the parent continue the execution. */ kill(getpid(), SIGSTOP); ++argv; execvp(argv[0], argv); perror("execvp"); _exit(-1); } else { waitpid(son.pid, &status, 0); event = pink_event_decide(status); assert(event == PINK_EVENT_STOP); /* Set up the tracing options. */ if (!pink_trace_setup(son.pid, PINK_TRACE_OPTION_SYSGOOD | PINK_TRACE_OPTION_EXEC)) { perror("pink_trace_setup"); pink_trace_kill(son.pid); return EXIT_FAILURE; } /* Figure out the bitness of the traced child. */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); printf("Child %i runs in %s mode\n", son.pid, pink_bitness_name(son.bitness)); son.dead = son.insyscall = false; sig = exit_code = 0; for (;;) { /* At this point the traced child is stopped and needs * to be resumed. */ if (!pink_trace_syscall(son.pid, sig)) { perror("pink_trace_syscall"); return (errno == ESRCH) ? 0 : 1; } sig = 0; /* Wait for the child */ if ((son.pid = waitpid(son.pid, &status, 0)) < 0) { perror("waitpid"); return (errno == ECHILD) ? 0 : 1; } /* Check the event. */ event = pink_event_decide(status); switch (event) { case PINK_EVENT_SYSCALL: handle_syscall(&son); break; break; case PINK_EVENT_EXEC: /* Update bitness */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); else printf(" (Updating the bitness of child %i to %s mode)\n", son.pid, pink_bitness_name(son.bitness)); break; case PINK_EVENT_GENUINE: case PINK_EVENT_UNKNOWN: /* Send the signal to the traced child as it * was a genuine signal. */ sig = WSTOPSIG(status); break; case PINK_EVENT_EXIT_GENUINE: exit_code = WEXITSTATUS(status); printf("Child %i exited normally with return code %d\n", son.pid, exit_code); son.dead = true; break; case PINK_EVENT_EXIT_SIGNAL: exit_code = 128 + WTERMSIG(status); printf("Child %i exited with signal %d\n", son.pid, WTERMSIG(status)); son.dead = true; break; default: /* Nothing */ ; } if (son.dead) break; } return exit_code; } }
int main(int argc, char **argv) { int sig, status, exit_code; struct child son; /* Parse arguments */ if (argc < 2) { fprintf(stderr, "Usage: %s program [argument...]\n", argv[0]); return EXIT_FAILURE; } /* Fork */ if ((son.pid = fork()) < 0) { perror("fork"); return EXIT_FAILURE; } else if (!son.pid) { /* child */ /* Set up for tracing */ if (!pink_trace_me()) { perror("pink_trace_me"); _exit(EXIT_FAILURE); } /* Stop and let the parent continue the execution. */ kill(getpid(), SIGSTOP); ++argv; execvp(argv[0], argv); perror("execvp"); _exit(-1); } else { waitpid(son.pid, &status, 0); assert(WIFSTOPPED(status)); assert(WSTOPSIG(status) == SIGSTOP); /* Figure out the bitness of the traced child. */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); printf("Child %i runs in %s mode\n", son.pid, pink_bitness_name(son.bitness)); son.inexecve = son.insyscall = false; son.printret = true; sig = exit_code = 0; for (;;) { /* At this point the traced child is stopped and needs * to be resumed. */ if (!pink_trace_syscall(son.pid, sig)) { perror("pink_trace_syscall"); return (errno == ESRCH) ? 0 : 1; } sig = 0; /* Wait for the child */ if ((son.pid = waitpid(son.pid, &status, 0)) < 0) { perror("waitpid"); return (errno == ECHILD) ? 0 : 1; } /* Check the event */ if (WIFSTOPPED(status)) { if (WSTOPSIG(status) == SIGTRAP) handle_sigtrap(&son); else { /* Child received a genuine signal. * Send it to the child. */ sig = WSTOPSIG(status); } } else if (WIFEXITED(status)) { exit_code = WEXITSTATUS(status); printf("Child %i exited normally with return code %d\n", son.pid, exit_code); break; } else if (WIFSIGNALED(status)) { exit_code = 128 + WTERMSIG(status); printf("Child %i exited with signal %d\n", son.pid, WTERMSIG(status)); break; } } return exit_code; } }