main.c revision 31899
1/*
2 * Copryight 1997 Sean Eric Fagan
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. All advertising materials mentioning features or use of this software
13 *    must display the following acknowledgement:
14 *	This product includes software developed by Sean Eric Fagan
15 * 4. Neither the name of the author may be used to endorse or promote
16 *    products derived from this software without specific prior written
17 *    permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 */
32
33/*
34 * The main module for truss.  Suprisingly simple, but, then, the other
35 * files handle the bulk of the work.  And, of course, the kernel has to
36 * do a lot of the work :).
37 */
38/*
39 * $Id: main.c,v 1.5 1997/12/13 03:13:47 sef Exp $
40 */
41
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <errno.h>
46#include <err.h>
47#include <signal.h>
48#include <fcntl.h>
49#include <unistd.h>
50#include <sys/ioctl.h>
51#include <sys/pioctl.h>
52
53extern int setup_and_wait(char **);
54extern int start_tracing(int, int);
55extern void i386_syscall_entry(int, int);
56extern void i386_syscall_exit(int, int);
57extern void i386_linux_syscall_entry(int, int);
58extern void i386_linux_syscall_exit(int, int);
59
60/*
61 * These should really be parameterized -- I don't like having globals,
62 * but this is the easiest way, right now, to deal with them.
63 */
64
65int pid = 0;
66int nosigs = 0;
67FILE *outfile = stderr;
68char *prog;
69int Procfd;
70char progtype[50];	/* OS and type of executable */
71
72static inline void
73usage(void) {
74  fprintf(stderr, "usage:  %s [-o <file>] [-S] { [-p <pid> ] | "
75	  "[ <command> <args>] }\n", prog);
76  exit(1);
77}
78
79struct ex_types {
80  char *type;
81  void (*enter_syscall)(int, int);
82  void (*exit_syscall)(int, int);
83} ex_types[] = {
84  { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit },
85  { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit },
86  { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit },
87  { 0, 0, 0 },
88};
89
90/*
91 * Set the execution type.  This is called after every exec, and when
92 * a process is first monitored.  The procfs pseudo-file "etype" has
93 * the execution module type -- see /proc/curproc/etype for an example.
94 */
95
96static struct ex_types *
97set_etype() {
98  struct ex_types *funcs;
99  char etype[24];
100  char progtype[32];
101  int fd;
102
103  sprintf(etype, "/proc/%d/etype", pid);
104  if ((fd = open(etype, O_RDONLY)) == -1) {
105    strcpy(progtype, "FreeBSD a.out");
106  } else {
107    int len = read(fd, progtype, sizeof(progtype));
108    progtype[len-1] = '\0';
109    close(fd);
110  }
111
112  for (funcs = ex_types; funcs->type; funcs++)
113    if (!strcmp(funcs->type, progtype))
114      break;
115
116  return funcs;
117}
118
119main(int ac, char **av) {
120  int mask;
121  int c;
122  int i;
123  char **command;
124  struct procfs_status pfs;
125  char etype[25];
126  struct ex_types *funcs;
127  int fd;
128  int in_exec = 0;
129
130  prog = av[0];
131
132  while ((c = getopt(ac, av, "p:o:S")) != EOF) {
133    switch (c) {
134    case 'p':	/* specified pid */
135      pid = atoi(optarg);
136      break;
137    case 'o':	/* Specified output file */
138      if ((outfile = fopen(optarg, "w")) == NULL) {
139	fprintf (stderr, "%s:  cannot open %s\n", av[0], optarg);
140	exit(1);
141      }
142      break;
143    case 'S':	/* Don't trace signals */
144      nosigs = 1;
145      break;
146    default:
147      usage();
148    }
149  }
150
151  ac -= optind; av += optind;
152  if ((pid == 0 && ac == 0) || (pid != 0 && ac != 0))
153    usage();
154
155  /*
156   * If truss starts the process itself, it will ignore some signals --
157   * they should be passed off to the process, which may or may not
158   * exit.  If, however, we are examining an already-running process,
159   * then we restore the event mask on these same signals.
160   */
161
162  if (pid == 0) {	/* Start a command ourselves */
163    command = av;
164    pid = setup_and_wait(command);
165    signal(SIGINT, SIG_IGN);
166    signal(SIGTERM, SIG_IGN);
167    signal(SIGQUIT, SIG_IGN);
168  } else {
169    extern void restore_proc(int);
170    signal(SIGINT, restore_proc);
171    signal(SIGTERM, restore_proc);
172    signal(SIGQUIT, restore_proc);
173  }
174
175
176  /*
177   * At this point, if we started the process, it is stopped waiting to
178   * be woken up, either in exit() or in execve().
179   */
180
181  Procfd = start_tracing(pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT |
182		     (nosigs ? 0 : S_SIG));
183  pfs.why = 0;
184
185  funcs = set_etype();
186  /*
187   * At this point, it's a simple loop, waiting for the process to
188   * stop, finding out why, printing out why, and then continuing it.
189   * All of the grunt work is done in the support routines.
190   */
191
192  do {
193    int val = 0;
194
195    if (ioctl(Procfd, PIOCWAIT, &pfs) == -1)
196      perror("PIOCWAIT top of loop");
197    else {
198      switch(i = pfs.why) {
199      case S_SCE:
200	funcs->enter_syscall(pid, pfs.val);
201	break;
202      case S_SCX:
203	/*
204	 * This is so we don't get two messages for an exec -- one
205	 * for the S_EXEC, and one for the syscall exit.  It also,
206	 * conveniently, ensures that the first message printed out
207	 * isn't the return-from-syscall used to create the process.
208	 */
209
210	if (in_exec) {
211	  in_exec = 0;
212	  break;
213	}
214	funcs->exit_syscall(pid, pfs.val);
215	break;
216      case S_SIG:
217	fprintf(outfile, "SIGNAL %d\n", pfs.val);
218	break;
219      case S_EXIT:
220	fprintf (outfile, "process exit, rval = %d\n", pfs.val);
221	break;
222      case S_EXEC:
223	funcs = set_etype();
224	in_exec = 1;
225	break;
226      default:
227	fprintf (outfile, "Process stopped because of:  %d\n", i);
228	break;
229      }
230    }
231    if (ioctl(Procfd, PIOCCONT, val) == -1)
232      perror("PIOCCONT");
233  } while (pfs.why != S_EXIT);
234  return 0;
235}
236