main.c revision 31567
1/* 2 * The main module for truss. Suprisingly simple, but, then, the other 3 * files handle the bulk of the work. And, of course, the kernel has to 4 * do a lot of the work :). 5 */ 6/* 7 * $Id$ 8 */ 9 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <errno.h> 14#include <err.h> 15#include <signal.h> 16#include <fcntl.h> 17#include <sys/ioctl.h> 18#include <sys/pioctl.h> 19 20extern int setup_and_wait(char **); 21extern int start_tracing(int, int); 22extern void i386_syscall_entry(int, int); 23extern void i386_syscall_exit(int, int); 24extern void i386_linux_syscall_entry(int, int); 25extern void i386_linux_syscall_exit(int, int); 26 27/* 28 * These should really be parameterized -- I don't like having globals, 29 * but this is the easiest way, right now, to deal with them. 30 */ 31 32int pid = 0; 33int nosigs = 0; 34FILE *outfile = stderr; 35char *prog; 36int Procfd; 37char progtype[50]; /* OS and type of executable */ 38 39static inline void 40usage(void) { 41 fprintf(stderr, "usage: %s [-o <file>] [-S] { [-p <pid> ] | " 42 "[ <command> <args>] }\n", prog); 43 exit(1); 44} 45 46struct ex_types { 47 char *type; 48 void (*enter_syscall)(int, int); 49 void (*exit_syscall)(int, int); 50} ex_types[] = { 51 { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 52 { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 53 { 0, 0, 0 }, 54}; 55 56/* 57 * Set the execution type. This is called after every exec, and when 58 * a process is first monitored. The procfs pseudo-file "etype" has 59 * the execution module type -- see /proc/curproc/etype for an example. 60 */ 61 62static struct ex_types * 63set_etype() { 64 struct ex_types *funcs; 65 char etype[24]; 66 char progtype[32]; 67 int fd; 68 69 sprintf(etype, "/proc/%d/etype", pid); 70 if ((fd = open(etype, O_RDONLY)) == -1) { 71 strcpy(progtype, "FreeBSD a.out"); 72 } else { 73 int len = read(fd, progtype, sizeof(progtype)); 74 progtype[len-1] = '\0'; 75 close(etype); 76 } 77 78 for (funcs = ex_types; funcs->type; funcs++) 79 if (!strcmp(funcs->type, progtype)) 80 break; 81 82 return funcs; 83} 84 85main(int ac, char **av) { 86 int mask; 87 int c; 88 int i; 89 char **command; 90 struct procfs_status pfs; 91 char etype[25]; 92 struct ex_types *funcs; 93 int fd; 94 int in_exec = 0; 95 96 prog = av[0]; 97 98 while ((c = getopt(ac, av, "p:o:S")) != EOF) { 99 switch (c) { 100 case 'p': /* specified pid */ 101 pid = atoi(optarg); 102 break; 103 case 'o': /* Specified output file */ 104 if ((outfile = fopen(optarg, "w")) == NULL) { 105 fprintf (stderr, "%s: cannot open %s\n", av[0], optarg); 106 exit(1); 107 } 108 break; 109 case 'S': /* Don't trace signals */ 110 nosigs = 1; 111 break; 112 default: 113 usage(); 114 } 115 } 116 117 ac -= optind; av += optind; 118 if (ac && pid != 0) 119 usage(); 120 121 /* 122 * If truss starts the process itself, it will ignore some signals -- 123 * they should be passed off to the process, which may or may not 124 * exit. If, however, we are examining an already-running process, 125 * then we restore the event mask on these same signals. 126 */ 127 128 if (pid == 0) { /* Start a command ourselves */ 129 command = av; 130 pid = setup_and_wait(command); 131 signal(SIGINT, SIG_IGN); 132 signal(SIGTERM, SIG_IGN); 133 signal(SIGQUIT, SIG_IGN); 134 } else { 135 extern void restore_proc(int); 136 signal(SIGINT, restore_proc); 137 signal(SIGTERM, restore_proc); 138 signal(SIGQUIT, restore_proc); 139 } 140 141 142 /* 143 * At this point, if we started the process, it is stopped waiting to 144 * be woken up, either in exit() or in execve(). 145 */ 146 147 Procfd = start_tracing(pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 148 (nosigs ? 0 : S_SIG)); 149 pfs.why = 0; 150 151 funcs = set_etype(); 152 /* 153 * At this point, it's a simple loop, waiting for the process to 154 * stop, finding out why, printing out why, and then continuing it. 155 * All of the grunt work is done in the support routines. 156 */ 157 158 do { 159 int val = 0; 160 161 if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 162 perror("PIOCWAIT top of loop"); 163 else { 164 switch(i = pfs.why) { 165 case S_SCE: 166 funcs->enter_syscall(pid, pfs.val); 167 break; 168 case S_SCX: 169 /* 170 * This is so we don't get two messages for an exec -- one 171 * for the S_EXEC, and one for the syscall exit. It also, 172 * conveniently, ensures that the first message printed out 173 * isn't the return-from-syscall used to create the process. 174 */ 175 176 if (in_exec) { 177 in_exec = 0; 178 break; 179 } 180 funcs->exit_syscall(pid, pfs.val); 181 break; 182 case S_SIG: 183 fprintf(outfile, "SIGNAL %d\n", pfs.val); 184 break; 185 case S_EXIT: 186 fprintf (outfile, "process exit, rval = %d\n", pfs.val); 187 break; 188 case S_EXEC: 189 funcs = set_etype(); 190 in_exec = 1; 191 break; 192 default: 193 fprintf (outfile, "Process stopped because of: %d\n", i); 194 break; 195 } 196 } 197 if (ioctl(Procfd, PIOCCONT, &val) == -1) 198 perror("PIOCCONT"); 199 } while (pfs.why != S_EXIT); 200 return 0; 201} 202