main.c revision 101282
1/* 2 * Copryight 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#ifndef lint 33static const char rcsid[] = 34 "$FreeBSD: head/usr.bin/truss/main.c 101282 2002-08-04 00:46:48Z mdodd $"; 35#endif /* not lint */ 36 37/* 38 * The main module for truss. Suprisingly simple, but, then, the other 39 * files handle the bulk of the work. And, of course, the kernel has to 40 * do a lot of the work :). 41 */ 42 43#include <sys/param.h> 44#include <sys/ioctl.h> 45#include <sys/pioctl.h> 46 47#include <err.h> 48#include <errno.h> 49#include <fcntl.h> 50#include <signal.h> 51#include <stdio.h> 52#include <stdlib.h> 53#include <string.h> 54#include <unistd.h> 55 56#include "truss.h" 57#include "extern.h" 58 59/* 60 * It's difficult to parameterize this because it must be 61 * accessible in a signal handler. 62 */ 63 64int Procfd; 65 66static __inline void 67usage(void) 68{ 69 fprintf(stderr, "%s\n%s\n", 70 "usage: truss [-S] [-o file] -p pid", 71 " truss [-S] [-o file] command [args]"); 72 exit(1); 73} 74 75/* 76 * WARNING! "FreeBSD a.out" must be first, or set_etype will not 77 * work correctly. 78 */ 79struct ex_types { 80 const char *type; 81 void (*enter_syscall)(struct trussinfo *, int); 82 int (*exit_syscall)(struct trussinfo *, int); 83} ex_types[] = { 84#ifdef __alpha__ 85 { "FreeBSD ELF", alpha_syscall_entry, alpha_syscall_exit }, 86#endif 87#ifdef __i386__ 88 { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 89 { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 90 { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 91#endif 92 { 0, 0, 0 }, 93}; 94 95/* 96 * Set the execution type. This is called after every exec, and when 97 * a process is first monitored. The procfs pseudo-file "etype" has 98 * the execution module type -- see /proc/curproc/etype for an example. 99 */ 100 101static struct ex_types * 102set_etype(struct trussinfo *trussinfo) { 103 struct ex_types *funcs; 104 char etype[24]; 105 char progt[32]; 106 int fd; 107 108 sprintf(etype, "/proc/%d/etype", trussinfo->pid); 109 if ((fd = open(etype, O_RDONLY)) == -1) { 110 strcpy(progt, "FreeBSD a.out"); 111 } else { 112 int len = read(fd, progt, sizeof(progt)); 113 progt[len-1] = '\0'; 114 close(fd); 115 } 116 117 for (funcs = ex_types; funcs->type; funcs++) 118 if (!strcmp(funcs->type, progt)) 119 break; 120 121 if (funcs->type == NULL) { 122 funcs = &ex_types[0]; 123 warn("Execution type %s is not supported -- using %s\n", 124 progt, funcs->type); 125 } 126 return funcs; 127} 128 129int 130main(int ac, char **av) { 131 int c; 132 int i; 133 char **command; 134 struct procfs_status pfs; 135 struct ex_types *funcs; 136 int in_exec = 0; 137 char *fname = NULL; 138 int sigexit = 0; 139 struct trussinfo *trussinfo; 140 141 /* Initialize the trussinfo struct */ 142 trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); 143 if (trussinfo == NULL) 144 errx(1, "malloc() failed"); 145 bzero(trussinfo, sizeof(struct trussinfo)); 146 trussinfo->outfile = stderr; 147 148 while ((c = getopt(ac, av, "p:o:S")) != -1) { 149 switch (c) { 150 case 'p': /* specified pid */ 151 trussinfo->pid = atoi(optarg); 152 break; 153 case 'o': /* Specified output file */ 154 fname = optarg; 155 break; 156 case 'S': /* Don't trace signals */ 157 trussinfo->flags |= NOSIGS; 158 break; 159 default: 160 usage(); 161 } 162 } 163 164 ac -= optind; av += optind; 165 if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0)) 166 usage(); 167 168 if (fname != NULL) { /* Use output file */ 169 if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 170 errx(1, "cannot open %s", fname); 171 } 172 173 /* 174 * If truss starts the process itself, it will ignore some signals -- 175 * they should be passed off to the process, which may or may not 176 * exit. If, however, we are examining an already-running process, 177 * then we restore the event mask on these same signals. 178 */ 179 180 if (trussinfo->pid == 0) { /* Start a command ourselves */ 181 command = av; 182 trussinfo->pid = setup_and_wait(command); 183 signal(SIGINT, SIG_IGN); 184 signal(SIGTERM, SIG_IGN); 185 signal(SIGQUIT, SIG_IGN); 186 } else { 187 signal(SIGINT, restore_proc); 188 signal(SIGTERM, restore_proc); 189 signal(SIGQUIT, restore_proc); 190 } 191 192 193 /* 194 * At this point, if we started the process, it is stopped waiting to 195 * be woken up, either in exit() or in execve(). 196 */ 197 198 Procfd = start_tracing( 199 trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 200 ((trussinfo->flags & NOSIGS) ? 0 : S_SIG)); 201 if (Procfd == -1) 202 return 0; 203 204 pfs.why = 0; 205 206 funcs = set_etype(trussinfo); 207 /* 208 * At this point, it's a simple loop, waiting for the process to 209 * stop, finding out why, printing out why, and then continuing it. 210 * All of the grunt work is done in the support routines. 211 */ 212 213 do { 214 int val = 0; 215 216 if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 217 warn("PIOCWAIT top of loop"); 218 else { 219 switch(i = pfs.why) { 220 case S_SCE: 221 funcs->enter_syscall(trussinfo, pfs.val); 222 break; 223 case S_SCX: 224 /* 225 * This is so we don't get two messages for an exec -- one 226 * for the S_EXEC, and one for the syscall exit. It also, 227 * conveniently, ensures that the first message printed out 228 * isn't the return-from-syscall used to create the process. 229 */ 230 231 if (in_exec) { 232 in_exec = 0; 233 break; 234 } 235 funcs->exit_syscall(trussinfo, pfs.val); 236 break; 237 case S_SIG: 238 fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val); 239 sigexit = pfs.val; 240 break; 241 case S_EXIT: 242 fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val); 243 break; 244 case S_EXEC: 245 funcs = set_etype(trussinfo); 246 in_exec = 1; 247 break; 248 default: 249 fprintf (trussinfo->outfile, "Process stopped because of: %d\n", i); 250 break; 251 } 252 } 253 if (ioctl(Procfd, PIOCCONT, val) == -1) { 254 if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH) 255 break; 256 else 257 warn("PIOCCONT"); 258 } 259 } while (pfs.why != S_EXIT); 260 fflush(trussinfo->outfile); 261 if (sigexit) { 262 if (sigexit == SIGQUIT) 263 exit(sigexit); 264 (void) signal(sigexit, SIG_DFL); 265 (void) kill(getpid(), sigexit); 266 } 267 return 0; 268} 269