main.c revision 32275
1189765Sgabor/* 2189765Sgabor * Copryight 1997 Sean Eric Fagan 3189765Sgabor * 4189765Sgabor * Redistribution and use in source and binary forms, with or without 5189765Sgabor * modification, are permitted provided that the following conditions 6189765Sgabor * are met: 7189765Sgabor * 1. Redistributions of source code must retain the above copyright 8189765Sgabor * notice, this list of conditions and the following disclaimer. 9189765Sgabor * 2. Redistributions in binary form must reproduce the above copyright 10189765Sgabor * notice, this list of conditions and the following disclaimer in the 11189765Sgabor * documentation and/or other materials provided with the distribution. 12189765Sgabor * 3. All advertising materials mentioning features or use of this software 13189765Sgabor * must display the following acknowledgement: 14189765Sgabor * This product includes software developed by Sean Eric Fagan 15189765Sgabor * 4. Neither the name of the author may be used to endorse or promote 16189765Sgabor * products derived from this software without specific prior written 17189765Sgabor * permission. 18189765Sgabor * 19189765Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20189765Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21189765Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22189765Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23189765Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24189765Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25189765Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26189765Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27189765Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28189765Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29189765Sgabor * SUCH DAMAGE. 30189765Sgabor */ 31189765Sgabor 32189765Sgabor#ifndef lint 33189765Sgaborstatic const char rcsid[] = 34189765Sgabor "$Id$"; 35189765Sgabor#endif /* not lint */ 36189765Sgabor 37189765Sgabor/* 38189765Sgabor * The main module for truss. Suprisingly simple, but, then, the other 39189765Sgabor * files handle the bulk of the work. And, of course, the kernel has to 40189765Sgabor * do a lot of the work :). 41189765Sgabor */ 42189765Sgabor 43189765Sgabor#include <err.h> 44189765Sgabor#include <errno.h> 45189765Sgabor#include <fcntl.h> 46189765Sgabor#include <signal.h> 47189765Sgabor#include <stdio.h> 48189765Sgabor#include <stdlib.h> 49189765Sgabor#include <string.h> 50189765Sgabor#include <unistd.h> 51189765Sgabor#include <sys/ioctl.h> 52189765Sgabor#include <sys/pioctl.h> 53189765Sgabor 54189765Sgaborextern int setup_and_wait(char **); 55189765Sgaborextern int start_tracing(int, int); 56189765Sgaborextern void i386_syscall_entry(int, int); 57189765Sgaborextern void i386_syscall_exit(int, int); 58189765Sgaborextern void i386_linux_syscall_entry(int, int); 59189765Sgaborextern void i386_linux_syscall_exit(int, int); 60189765Sgabor 61189765Sgabor/* 62189765Sgabor * These should really be parameterized -- I don't like having globals, 63189765Sgabor * but this is the easiest way, right now, to deal with them. 64189765Sgabor */ 65189765Sgabor 66189765Sgaborint pid = 0; 67189765Sgaborint nosigs = 0; 68189765SgaborFILE *outfile = stderr; 69189765Sgaborint Procfd; 70189765Sgaborchar progtype[50]; /* OS and type of executable */ 71189765Sgabor 72189765Sgaborstatic inline void 73189765Sgaborusage(void) 74189765Sgabor{ 75189765Sgabor fprintf(stderr, "%s\n%s\n", 76189765Sgabor "usage: truss [-S] [-o file] -p pid", 77189765Sgabor " truss [-S] [-o file] command [args]"); 78189765Sgabor exit(1); 79189765Sgabor} 80189765Sgabor 81189765Sgaborstruct ex_types { 82189765Sgabor char *type; 83189765Sgabor void (*enter_syscall)(int, int); 84189765Sgabor void (*exit_syscall)(int, int); 85189765Sgabor} ex_types[] = { 86189765Sgabor { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 87189765Sgabor { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 88189765Sgabor { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 89189765Sgabor { 0, 0, 0 }, 90189765Sgabor}; 91189765Sgabor 92189765Sgabor/* 93189765Sgabor * Set the execution type. This is called after every exec, and when 94189765Sgabor * a process is first monitored. The procfs pseudo-file "etype" has 95189765Sgabor * the execution module type -- see /proc/curproc/etype for an example. 96189765Sgabor */ 97189765Sgabor 98189765Sgaborstatic struct ex_types * 99189765Sgaborset_etype() { 100189765Sgabor struct ex_types *funcs; 101189765Sgabor char etype[24]; 102189765Sgabor char progtype[32]; 103189765Sgabor int fd; 104189765Sgabor 105189765Sgabor sprintf(etype, "/proc/%d/etype", pid); 106189765Sgabor if ((fd = open(etype, O_RDONLY)) == -1) { 107189765Sgabor strcpy(progtype, "FreeBSD a.out"); 108189765Sgabor } else { 109189765Sgabor int len = read(fd, progtype, sizeof(progtype)); 110189765Sgabor progtype[len-1] = '\0'; 111189765Sgabor close(fd); 112189765Sgabor } 113189765Sgabor 114189765Sgabor for (funcs = ex_types; funcs->type; funcs++) 115189765Sgabor if (!strcmp(funcs->type, progtype)) 116189765Sgabor break; 117189765Sgabor 118189765Sgabor return funcs; 119189765Sgabor} 120189765Sgabor 121189765Sgaborint 122189765Sgabormain(int ac, char **av) { 123189765Sgabor int c; 124189765Sgabor int i; 125189765Sgabor char **command; 126189765Sgabor struct procfs_status pfs; 127189765Sgabor struct ex_types *funcs; 128189765Sgabor int in_exec = 0; 129189765Sgabor char *fname = NULL; 130189765Sgabor 131189765Sgabor while ((c = getopt(ac, av, "p:o:S")) != EOF) { 132189765Sgabor switch (c) { 133189765Sgabor case 'p': /* specified pid */ 134189765Sgabor pid = atoi(optarg); 135189765Sgabor break; 136189765Sgabor case 'o': /* Specified output file */ 137189765Sgabor fname = optarg; 138189765Sgabor break; 139189765Sgabor case 'S': /* Don't trace signals */ 140189765Sgabor nosigs = 1; 141189765Sgabor break; 142189765Sgabor default: 143189765Sgabor usage(); 144189765Sgabor } 145189765Sgabor } 146189765Sgabor 147189765Sgabor ac -= optind; av += optind; 148189765Sgabor if ((pid == 0 && ac == 0) || (pid != 0 && ac != 0)) 149189765Sgabor usage(); 150189765Sgabor 151189765Sgabor if (fname != NULL) { /* Use output file */ 152189765Sgabor if ((outfile = fopen(fname, "w")) == NULL) 153189765Sgabor errx(1, "cannot open %s", fname); 154189765Sgabor } 155189765Sgabor 156189765Sgabor /* 157189765Sgabor * If truss starts the process itself, it will ignore some signals -- 158189765Sgabor * they should be passed off to the process, which may or may not 159189765Sgabor * exit. If, however, we are examining an already-running process, 160189765Sgabor * then we restore the event mask on these same signals. 161189765Sgabor */ 162189765Sgabor 163189765Sgabor if (pid == 0) { /* Start a command ourselves */ 164189765Sgabor command = av; 165189765Sgabor pid = setup_and_wait(command); 166189765Sgabor signal(SIGINT, SIG_IGN); 167189765Sgabor signal(SIGTERM, SIG_IGN); 168189765Sgabor signal(SIGQUIT, SIG_IGN); 169189765Sgabor } else { 170189765Sgabor extern void restore_proc(int); 171189765Sgabor signal(SIGINT, restore_proc); 172189765Sgabor signal(SIGTERM, restore_proc); 173189765Sgabor signal(SIGQUIT, restore_proc); 174189765Sgabor } 175189765Sgabor 176189765Sgabor 177189765Sgabor /* 178189765Sgabor * At this point, if we started the process, it is stopped waiting to 179189765Sgabor * be woken up, either in exit() or in execve(). 180189765Sgabor */ 181189765Sgabor 182189765Sgabor Procfd = start_tracing(pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 183189765Sgabor (nosigs ? 0 : S_SIG)); 184189765Sgabor pfs.why = 0; 185189765Sgabor 186189765Sgabor funcs = set_etype(); 187189765Sgabor /* 188189765Sgabor * At this point, it's a simple loop, waiting for the process to 189189765Sgabor * stop, finding out why, printing out why, and then continuing it. 190189765Sgabor * All of the grunt work is done in the support routines. 191189765Sgabor */ 192189765Sgabor 193189765Sgabor do { 194189765Sgabor int val = 0; 195189765Sgabor 196189765Sgabor if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 197189765Sgabor warn("PIOCWAIT top of loop"); 198189765Sgabor else { 199189765Sgabor switch(i = pfs.why) { 200189765Sgabor case S_SCE: 201189765Sgabor funcs->enter_syscall(pid, pfs.val); 202189765Sgabor break; 203189765Sgabor case S_SCX: 204189765Sgabor /* 205189765Sgabor * This is so we don't get two messages for an exec -- one 206189765Sgabor * for the S_EXEC, and one for the syscall exit. It also, 207189765Sgabor * conveniently, ensures that the first message printed out 208189765Sgabor * isn't the return-from-syscall used to create the process. 209189765Sgabor */ 210189765Sgabor 211189765Sgabor if (in_exec) { 212189765Sgabor in_exec = 0; 213189765Sgabor break; 214189765Sgabor } 215189765Sgabor funcs->exit_syscall(pid, pfs.val); 216189765Sgabor break; 217189765Sgabor case S_SIG: 218189765Sgabor fprintf(outfile, "SIGNAL %d\n", pfs.val); 219189765Sgabor break; 220189765Sgabor case S_EXIT: 221189765Sgabor fprintf (outfile, "process exit, rval = %d\n", pfs.val); 222189765Sgabor break; 223189765Sgabor case S_EXEC: 224189765Sgabor funcs = set_etype(); 225189765Sgabor in_exec = 1; 226189765Sgabor break; 227189765Sgabor default: 228189765Sgabor fprintf (outfile, "Process stopped because of: %d\n", i); 229189765Sgabor break; 230189765Sgabor } 231189765Sgabor } 232189765Sgabor if (ioctl(Procfd, PIOCCONT, val) == -1) 233189765Sgabor warn("PIOCCONT"); 234189765Sgabor } while (pfs.why != S_EXIT); 235189765Sgabor return 0; 236189765Sgabor} 237189765Sgabor