main.c revision 85301
1116742Ssam/* 2116904Ssam * Copryight 1997 Sean Eric Fagan 3139530Ssam * 4116742Ssam * Redistribution and use in source and binary forms, with or without 5116742Ssam * modification, are permitted provided that the following conditions 6116742Ssam * are met: 7116742Ssam * 1. Redistributions of source code must retain the above copyright 8116742Ssam * notice, this list of conditions and the following disclaimer. 9116742Ssam * 2. Redistributions in binary form must reproduce the above copyright 10116904Ssam * notice, this list of conditions and the following disclaimer in the 11116904Ssam * documentation and/or other materials provided with the distribution. 12116904Ssam * 3. All advertising materials mentioning features or use of this software 13116904Ssam * must display the following acknowledgement: 14116904Ssam * This product includes software developed by Sean Eric Fagan 15116904Ssam * 4. Neither the name of the author may be used to endorse or promote 16116742Ssam * products derived from this software without specific prior written 17116742Ssam * permission. 18116742Ssam * 19116742Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20116742Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21116904Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22116904Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23116904Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24116904Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25116904Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26116904Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27116904Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28116904Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29116904Ssam * SUCH DAMAGE. 30116904Ssam */ 31116742Ssam 32116742Ssam#ifndef lint 33116742Ssamstatic const char rcsid[] = 34116742Ssam "$FreeBSD: head/usr.bin/truss/main.c 85301 2001-10-22 02:02:00Z des $"; 35116742Ssam#endif /* not lint */ 36116742Ssam 37116742Ssam/* 38116742Ssam * The main module for truss. Suprisingly simple, but, then, the other 39116742Ssam * files handle the bulk of the work. And, of course, the kernel has to 40127646Ssam * do a lot of the work :). 41127646Ssam */ 42127646Ssam 43116742Ssam#include <sys/param.h> 44116742Ssam#include <sys/ioctl.h> 45116742Ssam#include <sys/pioctl.h> 46116742Ssam#include <sys/proc.h> 47116742Ssam 48116742Ssam#include <err.h> 49116742Ssam#include <errno.h> 50116742Ssam#include <fcntl.h> 51116742Ssam#include <signal.h> 52116742Ssam#include <stdio.h> 53116742Ssam#include <stdlib.h> 54116742Ssam#include <string.h> 55127646Ssam#include <unistd.h> 56127646Ssam 57127646Ssamextern int setup_and_wait(char **); 58127646Ssamextern int start_tracing(int, int); 59127646Ssam#ifdef __alpha__ 60127646Ssamextern void alpha_syscall_entry(int, int); 61127646Ssamextern void alpha_syscall_exit(int, int); 62127646Ssam#endif 63127646Ssam#ifdef __i386__ 64127646Ssamextern void i386_syscall_entry(int, int); 65116742Ssamextern void i386_syscall_exit(int, int); 66116742Ssamextern void i386_linux_syscall_entry(int, int); 67116742Ssamextern void i386_linux_syscall_exit(int, int); 68116742Ssam#endif 69116742Ssam 70138568Ssam/* 71138568Ssam * These should really be parameterized -- I don't like having globals, 72138568Ssam * but this is the easiest way, right now, to deal with them. 73138568Ssam */ 74138568Ssam 75116742Ssamint pid = 0; 76116742Ssamint nosigs = 0; 77116742SsamFILE *outfile; 78116742Ssamint Procfd; 79116742Ssamchar progtype[50]; /* OS and type of executable */ 80116742Ssam 81138568Ssamstatic inline void 82138568Ssamusage(void) 83138568Ssam{ 84138568Ssam fprintf(stderr, "%s\n%s\n", 85138568Ssam "usage: truss [-S] [-o file] -p pid", 86138568Ssam " truss [-S] [-o file] command [args]"); 87138568Ssam exit(1); 88138568Ssam} 89138568Ssam 90138568Ssam/* 91138568Ssam * WARNING! "FreeBSD a.out" must be first, or set_etype will not 92138568Ssam * work correctly. 93138568Ssam */ 94138568Ssamstruct ex_types { 95138568Ssam char *type; 96138568Ssam void (*enter_syscall)(int, int); 97138568Ssam void (*exit_syscall)(int, int); 98138568Ssam} ex_types[] = { 99138568Ssam#ifdef __alpha__ 100138568Ssam { "FreeBSD ELF", alpha_syscall_entry, alpha_syscall_exit }, 101138568Ssam#endif 102138568Ssam#ifdef __i386__ 103138568Ssam { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 104138568Ssam { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 105138568Ssam { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 106138568Ssam#endif 107138568Ssam { 0, 0, 0 }, 108138568Ssam}; 109138568Ssam 110138568Ssam/* 111138568Ssam * Set the execution type. This is called after every exec, and when 112138568Ssam * a process is first monitored. The procfs pseudo-file "etype" has 113138568Ssam * the execution module type -- see /proc/curproc/etype for an example. 114138568Ssam */ 115138568Ssam 116138568Ssamstatic struct ex_types * 117138568Ssamset_etype() { 118138568Ssam struct ex_types *funcs; 119138568Ssam char etype[24]; 120138568Ssam char progtype[32]; 121138568Ssam int fd; 122138568Ssam 123138568Ssam sprintf(etype, "/proc/%d/etype", pid); 124138568Ssam if ((fd = open(etype, O_RDONLY)) == -1) { 125138568Ssam strcpy(progtype, "FreeBSD a.out"); 126138568Ssam } else { 127138568Ssam int len = read(fd, progtype, sizeof(progtype)); 128138568Ssam progtype[len-1] = '\0'; 129138568Ssam close(fd); 130138568Ssam } 131138568Ssam 132138568Ssam for (funcs = ex_types; funcs->type; funcs++) 133138568Ssam if (!strcmp(funcs->type, progtype)) 134138568Ssam break; 135138568Ssam 136138568Ssam if (funcs == NULL) { 137138568Ssam warn("Execution type %s is not supported -- using FreeBSD a.out\n", 138138568Ssam progtype); 139138568Ssam funcs = &ex_types[0]; 140138568Ssam } 141138568Ssam return funcs; 142138568Ssam} 143138568Ssam 144138568Ssamint 145138568Ssammain(int ac, char **av) { 146138568Ssam int c; 147138568Ssam int i; 148138568Ssam char **command; 149138568Ssam struct procfs_status pfs; 150138568Ssam struct ex_types *funcs; 151138568Ssam int in_exec = 0; 152138568Ssam char *fname = NULL; 153138568Ssam int sigexit = 0; 154138568Ssam 155138568Ssam outfile = stdout; 156138568Ssam while ((c = getopt(ac, av, "p:o:S")) != -1) { 157138568Ssam switch (c) { 158138568Ssam case 'p': /* specified pid */ 159138568Ssam pid = atoi(optarg); 160138568Ssam break; 161138568Ssam case 'o': /* Specified output file */ 162138568Ssam fname = optarg; 163138568Ssam break; 164138568Ssam case 'S': /* Don't trace signals */ 165138568Ssam nosigs = 1; 166138568Ssam break; 167138568Ssam default: 168138568Ssam usage(); 169138568Ssam } 170138568Ssam } 171138568Ssam 172138568Ssam ac -= optind; av += optind; 173138568Ssam if ((pid == 0 && ac == 0) || (pid != 0 && ac != 0)) 174138568Ssam usage(); 175138568Ssam 176138568Ssam if (fname != NULL) { /* Use output file */ 177138568Ssam if ((outfile = fopen(fname, "w")) == NULL) 178138568Ssam errx(1, "cannot open %s", fname); 179138568Ssam } 180138568Ssam 181138568Ssam /* 182138568Ssam * If truss starts the process itself, it will ignore some signals -- 183138568Ssam * they should be passed off to the process, which may or may not 184138568Ssam * exit. If, however, we are examining an already-running process, 185116742Ssam * then we restore the event mask on these same signals. 186138568Ssam */ 187116742Ssam 188138568Ssam if (pid == 0) { /* Start a command ourselves */ 189116742Ssam command = av; 190116742Ssam pid = setup_and_wait(command); 191116742Ssam signal(SIGINT, SIG_IGN); 192116742Ssam signal(SIGTERM, SIG_IGN); 193116742Ssam signal(SIGQUIT, SIG_IGN); 194116742Ssam } else { 195116742Ssam extern void restore_proc(int); 196116742Ssam signal(SIGINT, restore_proc); 197116742Ssam signal(SIGTERM, restore_proc); 198116742Ssam signal(SIGQUIT, restore_proc); 199116742Ssam } 200116742Ssam 201116742Ssam 202116742Ssam /* 203116742Ssam * At this point, if we started the process, it is stopped waiting to 204116742Ssam * be woken up, either in exit() or in execve(). 205116742Ssam */ 206116742Ssam 207116742Ssam Procfd = start_tracing(pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 208116742Ssam (nosigs ? 0 : S_SIG)); 209116742Ssam if (Procfd == -1) 210116742Ssam return 0; 211116742Ssam 212116742Ssam pfs.why = 0; 213116742Ssam 214116742Ssam funcs = set_etype(); 215116742Ssam /* 216116742Ssam * At this point, it's a simple loop, waiting for the process to 217116742Ssam * stop, finding out why, printing out why, and then continuing it. 218116742Ssam * All of the grunt work is done in the support routines. 219116742Ssam */ 220116742Ssam 221116742Ssam do { 222116742Ssam int val = 0; 223116742Ssam 224116742Ssam if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 225116742Ssam warn("PIOCWAIT top of loop"); 226116742Ssam else { 227116742Ssam switch(i = pfs.why) { 228116742Ssam case S_SCE: 229116742Ssam funcs->enter_syscall(pid, pfs.val); 230116742Ssam break; 231116742Ssam case S_SCX: 232116742Ssam /* 233116742Ssam * This is so we don't get two messages for an exec -- one 234116742Ssam * for the S_EXEC, and one for the syscall exit. It also, 235116742Ssam * conveniently, ensures that the first message printed out 236116742Ssam * isn't the return-from-syscall used to create the process. 237116742Ssam */ 238116742Ssam 239116742Ssam if (in_exec) { 240116742Ssam in_exec = 0; 241116742Ssam break; 242116742Ssam } 243116742Ssam funcs->exit_syscall(pid, pfs.val); 244116742Ssam break; 245116742Ssam case S_SIG: 246116742Ssam fprintf(outfile, "SIGNAL %lu\n", pfs.val); 247116742Ssam sigexit = pfs.val; 248116742Ssam break; 249116742Ssam case S_EXIT: 250116742Ssam fprintf (outfile, "process exit, rval = %lu\n", pfs.val); 251116742Ssam break; 252116742Ssam case S_EXEC: 253116742Ssam funcs = set_etype(); 254116742Ssam in_exec = 1; 255116742Ssam break; 256116742Ssam default: 257116742Ssam fprintf (outfile, "Process stopped because of: %d\n", i); 258116742Ssam break; 259138568Ssam } 260116742Ssam } 261116742Ssam if (ioctl(Procfd, PIOCCONT, val) == -1) { 262116742Ssam if (kill(pid, 0) == -1 && errno == ESRCH) 263116742Ssam break; 264116742Ssam else 265116742Ssam warn("PIOCCONT"); 266116742Ssam } 267116742Ssam } while (pfs.why != S_EXIT); 268116742Ssam fflush(outfile); 269116742Ssam if (sigexit) { 270116742Ssam if (sigexit == SIGQUIT) 271116742Ssam exit(sigexit); 272116742Ssam (void) signal(sigexit, SIG_DFL); 273116742Ssam (void) kill(getpid(), sigexit); 274116742Ssam } 275116742Ssam return 0; 276116742Ssam} 277116742Ssam