1 /*- 2 * Copyright 1997 Sean Eric Fagan 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by Sean Eric Fagan 15 * 4. Neither the name of the author may be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 /* 36 * Various setup functions for truss. Not the cleanest-written code, 37 * I'm afraid. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/types.h> 42 #include <sys/ptrace.h> 43 #include <sys/wait.h> 44 45 #include <err.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <signal.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <time.h> 53 #include <unistd.h> 54 55 #include <machine/reg.h> 56 57 #include "truss.h" 58 #include "extern.h" 59 60 static sig_atomic_t detaching; 61 62 /* 63 * setup_and_wait() is called to start a process. All it really does 64 * is fork(), set itself up to stop on exec or exit, and then exec 65 * the given command. At that point, the child process stops, and 66 * the parent can wake up and deal with it. 67 */ 68 69 int 70 setup_and_wait(char *command[]) 71 { 72 pid_t pid; 73 74 pid = vfork(); 75 if (pid == -1) 76 err(1, "fork failed"); 77 if (pid == 0) { /* Child */ 78 ptrace(PT_TRACE_ME, 0, 0, 0); 79 execvp(command[0], command); 80 err(1, "execvp %s", command[0]); 81 } 82 83 /* Only in the parent here */ 84 if (waitpid(pid, NULL, 0) < 0) 85 err(1, "unexpect stop in waitpid"); 86 87 return (pid); 88 } 89 90 /* 91 * start_tracing picks up where setup_and_wait() dropped off -- namely, 92 * it sets the event mask for the given process id. Called for both 93 * monitoring an existing process and when we create our own. 94 */ 95 96 int 97 start_tracing(pid_t pid) 98 { 99 int ret, retry; 100 101 retry = 10; 102 do { 103 ret = ptrace(PT_ATTACH, pid, NULL, 0); 104 usleep(200); 105 } while (ret && retry-- > 0); 106 if (ret) 107 err(1, "can not attach to target process"); 108 109 if (waitpid(pid, NULL, 0) < 0) 110 err(1, "Unexpect stop in waitpid"); 111 112 return (0); 113 } 114 115 /* 116 * Restore a process back to it's pre-truss state. 117 * Called for SIGINT, SIGTERM, SIGQUIT. This only 118 * applies if truss was told to monitor an already-existing 119 * process. 120 */ 121 122 void 123 restore_proc(int signo __unused) 124 { 125 126 detaching = 1; 127 } 128 129 static int 130 detach_proc(pid_t pid) 131 { 132 int waitval; 133 134 /* stop the child so that we can detach */ 135 kill(pid, SIGSTOP); 136 if (waitpid(pid, &waitval, 0) < 0) 137 err(1, "Unexpected stop in waitpid"); 138 139 if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0) 140 err(1, "Can not detach the process"); 141 142 kill(pid, SIGCONT); 143 144 return (waitval); 145 } 146 147 /* 148 * Change curthread member based on lwpid. 149 * If it is a new thread, create a threadinfo structure 150 */ 151 static void 152 find_thread(struct trussinfo *info, lwpid_t lwpid) 153 { 154 struct threadinfo *np; 155 156 info->curthread = NULL; 157 SLIST_FOREACH(np, &info->threadlist, entries) { 158 if (np->tid == lwpid) { 159 info->curthread = np; 160 return; 161 } 162 } 163 164 np = (struct threadinfo *)calloc(1, sizeof(struct threadinfo)); 165 if (np == NULL) 166 err(1, "calloc() failed"); 167 np->tid = lwpid; 168 SLIST_INSERT_HEAD(&info->threadlist, np, entries); 169 info->curthread = np; 170 } 171 172 /* 173 * Start the traced process and wait until it stoped. 174 * Fill trussinfo structure. 175 * When this even returns, the traced process is in stop state. 176 */ 177 void 178 waitevent(struct trussinfo *info) 179 { 180 struct ptrace_lwpinfo lwpinfo; 181 static int pending_signal = 0; 182 int waitval; 183 184 ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal); 185 pending_signal = 0; 186 187 detach: 188 if (detaching) { 189 waitval = detach_proc(info->pid); 190 info->pr_why = S_DETACHED; 191 info->pr_data = WEXITSTATUS(waitval); 192 return; 193 } 194 195 if (waitpid(info->pid, &waitval, 0) == -1) { 196 if (errno == EINTR) 197 goto detach; 198 err(1, "Unexpected stop in waitpid"); 199 } 200 201 if (WIFCONTINUED(waitval)) { 202 info->pr_why = S_NONE; 203 return; 204 } 205 if (WIFEXITED(waitval)) { 206 info->pr_why = S_EXIT; 207 info->pr_data = WEXITSTATUS(waitval); 208 return; 209 } 210 if (WIFSTOPPED(waitval)) { 211 ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo, 212 sizeof(lwpinfo)); 213 find_thread(info, lwpinfo.pl_lwpid); 214 switch (WSTOPSIG(waitval)) { 215 case SIGTRAP: 216 if (lwpinfo.pl_flags & PL_FLAG_SCE) { 217 info->pr_why = S_SCE; 218 info->curthread->in_syscall = 1; 219 break; 220 } else if (lwpinfo.pl_flags & PL_FLAG_SCX) { 221 info->pr_why = S_SCX; 222 info->curthread->in_syscall = 0; 223 break; 224 } else { 225 errx(1, 226 "pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX", 227 lwpinfo.pl_flags); 228 } 229 default: 230 info->pr_why = S_SIG; 231 info->pr_data = WSTOPSIG(waitval); 232 pending_signal = info->pr_data; 233 break; 234 } 235 } 236 if (WIFSIGNALED(waitval)) { 237 info->pr_why = S_EXIT; 238 info->pr_data = 0; 239 return; 240 } 241 } 242