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