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