/* $Id: scescx.c,v 1.7 2012/01/29 06:18:38 kostik Exp kostik $ */ #include #include #include #include #include #include #include #include #include #include #include #define TRACE ">>>> " static const char * decode_wait_status(int status) { static char c[128]; char b[32]; int first; c[0] = '\0'; first = 1; if (WIFCONTINUED(status)) { first = 0; strlcat(c, "CONT", sizeof(c)); } if (WIFEXITED(status)) { if (first) first = 0; else strlcat(c, ",", sizeof(c)); snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status)); strlcat(c, b, sizeof(c)); } if (WIFSIGNALED(status)) { if (first) first = 0; else strlcat(c, ",", sizeof(c)); snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status))); strlcat(c, b, sizeof(c)); if (WCOREDUMP(status)) strlcat(c, ",CORE", sizeof(c)); } if (WIFSTOPPED(status)) { if (first) first = 0; else strlcat(c, ",", sizeof(c)); snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status))); strlcat(c, b, sizeof(c)); } return (c); } static const char * decode_pl_flags(struct ptrace_lwpinfo *lwpinfo) { static char c[128]; static struct decode_tag { int flag; const char *desc; } decode[] = { { PL_FLAG_SA, "SA" }, { PL_FLAG_BOUND, "BOUND" }, { PL_FLAG_SCE, "SCE" }, { PL_FLAG_SCX, "SCX" }, { PL_FLAG_EXEC, "EXEC" }, { PL_FLAG_SI, "SI" }, { PL_FLAG_FORKED, "FORKED" }, }; unsigned first, i; c[0] = '\0'; first = 1; for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) { if ((lwpinfo->pl_flags & decode[i].flag) != 0) { if (first) first = 0; else strlcat(c, ",", sizeof(c)); strlcat(c, decode[i].desc, sizeof(c)); } } return (c); } static const char * decode_pl_event(struct ptrace_lwpinfo *lwpinfo) { switch (lwpinfo->pl_event) { case PL_EVENT_NONE: return "NONE"; case PL_EVENT_SIGNAL: return "SIG"; default: return "UNKNOWN"; } } static void get_pathname(pid_t pid) { char pathname[PATH_MAX]; int error, name[4]; size_t len; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_PATHNAME; name[3] = pid; len = sizeof(pathname); error = sysctl(name, 4, pathname, &len, NULL, 0); if (error < 0) { if (errno != ESRCH) { fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n", pid, strerror(errno)); return; } fprintf(stderr, "pid %d exited\n", pid); return; } if (len == 0 || strlen(pathname) == 0) { fprintf(stderr, "No cached pathname for process %d\n", pid); return; } printf(TRACE "pid %d path %s\n", pid, pathname); } static void wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo) { printf(TRACE "pid %d wait %s", pid, decode_wait_status(status)); if (lwpinfo != NULL) { printf("event %s flags %s", decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo)); } printf("\n"); } static int trace_sc(int pid) { struct ptrace_lwpinfo lwpinfo; int status; if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) { perror("PT_TO_SCE"); ptrace(PT_KILL, pid, NULL, 0); return (-1); } if (waitpid(pid, &status, 0) == -1) { perror("waitpid"); return (-1); } if (WIFEXITED(status) || WIFSIGNALED(status)) { wait_info(pid, status, NULL); return (-1); } assert(WIFSTOPPED(status)); assert(WSTOPSIG(status) == SIGTRAP); if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof lwpinfo) < 0) { perror("PT_LWPINFO"); ptrace(PT_KILL, pid, NULL, 0); return (-1); } wait_info(pid, status, &lwpinfo); assert(lwpinfo.pl_flags & PL_FLAG_SCE); if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) { perror("PT_TO_SCX"); ptrace(PT_KILL, pid, NULL, 0); return (-1); } if (waitpid(pid, &status, 0) == -1) { perror("waitpid"); return (-1); } if (WIFEXITED(status) || WIFSIGNALED(status)) { wait_info(pid, status, NULL); return (-1); } assert(WIFSTOPPED(status)); assert(WSTOPSIG(status) == SIGTRAP); if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof lwpinfo) < 0) { perror("PT_LWPINFO"); ptrace(PT_KILL, pid, NULL, 0); return (-1); } wait_info(pid, status, &lwpinfo); assert(lwpinfo.pl_flags & PL_FLAG_SCX); if (lwpinfo.pl_flags & PL_FLAG_EXEC) get_pathname(pid); if (lwpinfo.pl_flags & PL_FLAG_FORKED) { printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid); return (lwpinfo.pl_child_pid); } return (0); } static int trace_cont(int pid) { struct ptrace_lwpinfo lwpinfo; int status; if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) { perror("PT_CONTINUE"); ptrace(PT_KILL, pid, NULL, 0); return (-1); } if (waitpid(pid, &status, 0) == -1) { perror("waitpid"); return (-1); } if (WIFEXITED(status) || WIFSIGNALED(status)) { wait_info(pid, status, NULL); return (-1); } assert(WIFSTOPPED(status)); assert(WSTOPSIG(status) == SIGTRAP); if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof lwpinfo) < 0) { perror("PT_LWPINFO"); ptrace(PT_KILL, pid, NULL, 0); return (-1); } wait_info(pid, status, &lwpinfo); if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) == (PL_FLAG_EXEC | PL_FLAG_SCX)) get_pathname(pid); if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) == (PL_FLAG_FORKED | PL_FLAG_SCX)) { printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid); return (lwpinfo.pl_child_pid); } return (0); } static int trace_syscalls = 1; static int trace(pid_t pid) { return (trace_syscalls ? trace_sc(pid) : trace_cont(pid)); } int main(int argc, char *argv[]) { int c, status; pid_t pid, pid1; trace_syscalls = 1; while ((c = getopt(argc, argv, "cs")) != -1) { switch (c) { case 'c': trace_syscalls = 0; break; case 's': trace_syscalls = 1; break; default: case '?': fprintf(stderr, "Usage: %s [-c] [-s]\n", argv[0]); return (2); } } if ((pid = fork()) < 0) { perror("fork"); return 1; } else if (pid == 0) { if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) { perror("PT_TRACE_ME"); _exit(1); } kill(getpid(), SIGSTOP); getpid(); if ((pid1 = fork()) < 0) { perror("fork1"); return (1); } else if (pid1 == 0) { printf("Hi from child %d\n", getpid()); execl("/bin/ls", "ls", "/", (char *)NULL); } } else { /* parent */ if (waitpid(pid, &status, 0) == -1) { perror("waitpid"); return (-1); } assert(WIFSTOPPED(status)); assert(WSTOPSIG(status) == SIGSTOP); if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) { perror("PT_FOLLOW_FORK"); ptrace(PT_KILL, pid, NULL, 0); return (2); } while ((pid1 = trace(pid)) >= 0) { if (pid1 != 0) { printf(TRACE "attached to pid %d\n", pid1); #if 0 kill(pid1, SIGCONT); #endif if (waitpid(pid1, &status, 0) == -1) { perror("waitpid"); return (-1); } printf(TRACE "nested loop, pid %d status %s\n", pid1, decode_wait_status(status)); assert(WIFSTOPPED(status)); assert(WSTOPSIG(status) == SIGSTOP); while (trace(pid1) >= 0) ; } } ptrace(PT_CONTINUE, pid, (caddr_t)1, 0); } return (0); }