Deleted Added
full compact
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$
7 * $Id: main.c,v 1.1 1997/12/06 05:23:03 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 { "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}