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