/*- * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Various setup functions for truss. Not the cleanest-written code, * I'm afraid. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "truss.h" #include "extern.h" static sig_atomic_t detaching; /* * setup_and_wait() is called to start a process. All it really does * is fork(), set itself up to stop on exec or exit, and then exec * the given command. At that point, the child process stops, and * the parent can wake up and deal with it. */ int setup_and_wait(char *command[]) { pid_t pid; pid = vfork(); if (pid == -1) err(1, "fork failed"); if (pid == 0) { /* Child */ ptrace(PT_TRACE_ME, 0, 0, 0); execvp(command[0], command); err(1, "execvp %s", command[0]); } /* Only in the parent here */ if (waitpid(pid, NULL, 0) < 0) err(1, "unexpect stop in waitpid"); return (pid); } /* * start_tracing picks up where setup_and_wait() dropped off -- namely, * it sets the event mask for the given process id. Called for both * monitoring an existing process and when we create our own. */ int start_tracing(pid_t pid) { int ret, retry; retry = 10; do { ret = ptrace(PT_ATTACH, pid, NULL, 0); usleep(200); } while (ret && retry-- > 0); if (ret) err(1, "can not attach to target process"); if (waitpid(pid, NULL, 0) < 0) err(1, "Unexpect stop in waitpid"); return (0); } /* * Restore a process back to it's pre-truss state. * Called for SIGINT, SIGTERM, SIGQUIT. This only * applies if truss was told to monitor an already-existing * process. */ void restore_proc(int signo __unused) { detaching = 1; } static int detach_proc(pid_t pid) { int waitval; /* stop the child so that we can detach */ kill(pid, SIGSTOP); if (waitpid(pid, &waitval, 0) < 0) err(1, "Unexpected stop in waitpid"); if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0) err(1, "Can not detach the process"); kill(pid, SIGCONT); return (waitval); } /* * Change curthread member based on lwpid. * If it is a new thread, create a threadinfo structure */ static void find_thread(struct trussinfo *info, lwpid_t lwpid) { struct threadinfo *np; info->curthread = NULL; SLIST_FOREACH(np, &info->threadlist, entries) { if (np->tid == lwpid) { info->curthread = np; return; } } np = (struct threadinfo *)calloc(1, sizeof(struct threadinfo)); if (np == NULL) err(1, "calloc() failed"); np->tid = lwpid; SLIST_INSERT_HEAD(&info->threadlist, np, entries); info->curthread = np; } /* * Start the traced process and wait until it stoped. * Fill trussinfo structure. * When this even returns, the traced process is in stop state. */ void waitevent(struct trussinfo *info) { struct ptrace_lwpinfo lwpinfo; static int pending_signal = 0; int waitval; ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal); pending_signal = 0; detach: if (detaching) { waitval = detach_proc(info->pid); info->pr_why = S_DETACHED; info->pr_data = WEXITSTATUS(waitval); return; } if (waitpid(info->pid, &waitval, 0) == -1) { if (errno == EINTR) goto detach; err(1, "Unexpected stop in waitpid"); } if (WIFCONTINUED(waitval)) { info->pr_why = S_NONE; return; } if (WIFEXITED(waitval)) { info->pr_why = S_EXIT; info->pr_data = WEXITSTATUS(waitval); return; } if (WIFSTOPPED(waitval)) { ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)); find_thread(info, lwpinfo.pl_lwpid); switch (WSTOPSIG(waitval)) { case SIGTRAP: if (lwpinfo.pl_flags & PL_FLAG_SCE) { info->pr_why = S_SCE; info->curthread->in_syscall = 1; break; } else if (lwpinfo.pl_flags & PL_FLAG_SCX) { info->pr_why = S_SCX; info->curthread->in_syscall = 0; break; } else { errx(1, "pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX", lwpinfo.pl_flags); } default: info->pr_why = S_SIG; info->pr_data = WSTOPSIG(waitval); pending_signal = info->pr_data; break; } } if (WIFSIGNALED(waitval)) { info->pr_why = S_EXIT; info->pr_data = 0; return; } }