main.c revision 32275
1189765Sgabor/*
2189765Sgabor * Copryight 1997 Sean Eric Fagan
3189765Sgabor *
4189765Sgabor * Redistribution and use in source and binary forms, with or without
5189765Sgabor * modification, are permitted provided that the following conditions
6189765Sgabor * are met:
7189765Sgabor * 1. Redistributions of source code must retain the above copyright
8189765Sgabor *    notice, this list of conditions and the following disclaimer.
9189765Sgabor * 2. Redistributions in binary form must reproduce the above copyright
10189765Sgabor *    notice, this list of conditions and the following disclaimer in the
11189765Sgabor *    documentation and/or other materials provided with the distribution.
12189765Sgabor * 3. All advertising materials mentioning features or use of this software
13189765Sgabor *    must display the following acknowledgement:
14189765Sgabor *	This product includes software developed by Sean Eric Fagan
15189765Sgabor * 4. Neither the name of the author may be used to endorse or promote
16189765Sgabor *    products derived from this software without specific prior written
17189765Sgabor *    permission.
18189765Sgabor *
19189765Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20189765Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21189765Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22189765Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23189765Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24189765Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25189765Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26189765Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27189765Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28189765Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29189765Sgabor * SUCH DAMAGE.
30189765Sgabor */
31189765Sgabor
32189765Sgabor#ifndef lint
33189765Sgaborstatic const char rcsid[] =
34189765Sgabor	"$Id$";
35189765Sgabor#endif /* not lint */
36189765Sgabor
37189765Sgabor/*
38189765Sgabor * The main module for truss.  Suprisingly simple, but, then, the other
39189765Sgabor * files handle the bulk of the work.  And, of course, the kernel has to
40189765Sgabor * do a lot of the work :).
41189765Sgabor */
42189765Sgabor
43189765Sgabor#include <err.h>
44189765Sgabor#include <errno.h>
45189765Sgabor#include <fcntl.h>
46189765Sgabor#include <signal.h>
47189765Sgabor#include <stdio.h>
48189765Sgabor#include <stdlib.h>
49189765Sgabor#include <string.h>
50189765Sgabor#include <unistd.h>
51189765Sgabor#include <sys/ioctl.h>
52189765Sgabor#include <sys/pioctl.h>
53189765Sgabor
54189765Sgaborextern int setup_and_wait(char **);
55189765Sgaborextern int start_tracing(int, int);
56189765Sgaborextern void i386_syscall_entry(int, int);
57189765Sgaborextern void i386_syscall_exit(int, int);
58189765Sgaborextern void i386_linux_syscall_entry(int, int);
59189765Sgaborextern void i386_linux_syscall_exit(int, int);
60189765Sgabor
61189765Sgabor/*
62189765Sgabor * These should really be parameterized -- I don't like having globals,
63189765Sgabor * but this is the easiest way, right now, to deal with them.
64189765Sgabor */
65189765Sgabor
66189765Sgaborint pid = 0;
67189765Sgaborint nosigs = 0;
68189765SgaborFILE *outfile = stderr;
69189765Sgaborint Procfd;
70189765Sgaborchar progtype[50];	/* OS and type of executable */
71189765Sgabor
72189765Sgaborstatic inline void
73189765Sgaborusage(void)
74189765Sgabor{
75189765Sgabor  fprintf(stderr, "%s\n%s\n",
76189765Sgabor	"usage: truss [-S] [-o file] -p pid",
77189765Sgabor	"       truss [-S] [-o file] command [args]");
78189765Sgabor  exit(1);
79189765Sgabor}
80189765Sgabor
81189765Sgaborstruct ex_types {
82189765Sgabor  char *type;
83189765Sgabor  void (*enter_syscall)(int, int);
84189765Sgabor  void (*exit_syscall)(int, int);
85189765Sgabor} ex_types[] = {
86189765Sgabor  { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit },
87189765Sgabor  { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit },
88189765Sgabor  { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit },
89189765Sgabor  { 0, 0, 0 },
90189765Sgabor};
91189765Sgabor
92189765Sgabor/*
93189765Sgabor * Set the execution type.  This is called after every exec, and when
94189765Sgabor * a process is first monitored.  The procfs pseudo-file "etype" has
95189765Sgabor * the execution module type -- see /proc/curproc/etype for an example.
96189765Sgabor */
97189765Sgabor
98189765Sgaborstatic struct ex_types *
99189765Sgaborset_etype() {
100189765Sgabor  struct ex_types *funcs;
101189765Sgabor  char etype[24];
102189765Sgabor  char progtype[32];
103189765Sgabor  int fd;
104189765Sgabor
105189765Sgabor  sprintf(etype, "/proc/%d/etype", pid);
106189765Sgabor  if ((fd = open(etype, O_RDONLY)) == -1) {
107189765Sgabor    strcpy(progtype, "FreeBSD a.out");
108189765Sgabor  } else {
109189765Sgabor    int len = read(fd, progtype, sizeof(progtype));
110189765Sgabor    progtype[len-1] = '\0';
111189765Sgabor    close(fd);
112189765Sgabor  }
113189765Sgabor
114189765Sgabor  for (funcs = ex_types; funcs->type; funcs++)
115189765Sgabor    if (!strcmp(funcs->type, progtype))
116189765Sgabor      break;
117189765Sgabor
118189765Sgabor  return funcs;
119189765Sgabor}
120189765Sgabor
121189765Sgaborint
122189765Sgabormain(int ac, char **av) {
123189765Sgabor  int c;
124189765Sgabor  int i;
125189765Sgabor  char **command;
126189765Sgabor  struct procfs_status pfs;
127189765Sgabor  struct ex_types *funcs;
128189765Sgabor  int in_exec = 0;
129189765Sgabor  char *fname = NULL;
130189765Sgabor
131189765Sgabor  while ((c = getopt(ac, av, "p:o:S")) != EOF) {
132189765Sgabor    switch (c) {
133189765Sgabor    case 'p':	/* specified pid */
134189765Sgabor      pid = atoi(optarg);
135189765Sgabor      break;
136189765Sgabor    case 'o':	/* Specified output file */
137189765Sgabor      fname = optarg;
138189765Sgabor      break;
139189765Sgabor    case 'S':	/* Don't trace signals */
140189765Sgabor      nosigs = 1;
141189765Sgabor      break;
142189765Sgabor    default:
143189765Sgabor      usage();
144189765Sgabor    }
145189765Sgabor  }
146189765Sgabor
147189765Sgabor  ac -= optind; av += optind;
148189765Sgabor  if ((pid == 0 && ac == 0) || (pid != 0 && ac != 0))
149189765Sgabor    usage();
150189765Sgabor
151189765Sgabor  if (fname != NULL) { /* Use output file */
152189765Sgabor    if ((outfile = fopen(fname, "w")) == NULL)
153189765Sgabor      errx(1, "cannot open %s", fname);
154189765Sgabor  }
155189765Sgabor
156189765Sgabor  /*
157189765Sgabor   * If truss starts the process itself, it will ignore some signals --
158189765Sgabor   * they should be passed off to the process, which may or may not
159189765Sgabor   * exit.  If, however, we are examining an already-running process,
160189765Sgabor   * then we restore the event mask on these same signals.
161189765Sgabor   */
162189765Sgabor
163189765Sgabor  if (pid == 0) {	/* Start a command ourselves */
164189765Sgabor    command = av;
165189765Sgabor    pid = setup_and_wait(command);
166189765Sgabor    signal(SIGINT, SIG_IGN);
167189765Sgabor    signal(SIGTERM, SIG_IGN);
168189765Sgabor    signal(SIGQUIT, SIG_IGN);
169189765Sgabor  } else {
170189765Sgabor    extern void restore_proc(int);
171189765Sgabor    signal(SIGINT, restore_proc);
172189765Sgabor    signal(SIGTERM, restore_proc);
173189765Sgabor    signal(SIGQUIT, restore_proc);
174189765Sgabor  }
175189765Sgabor
176189765Sgabor
177189765Sgabor  /*
178189765Sgabor   * At this point, if we started the process, it is stopped waiting to
179189765Sgabor   * be woken up, either in exit() or in execve().
180189765Sgabor   */
181189765Sgabor
182189765Sgabor  Procfd = start_tracing(pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT |
183189765Sgabor		     (nosigs ? 0 : S_SIG));
184189765Sgabor  pfs.why = 0;
185189765Sgabor
186189765Sgabor  funcs = set_etype();
187189765Sgabor  /*
188189765Sgabor   * At this point, it's a simple loop, waiting for the process to
189189765Sgabor   * stop, finding out why, printing out why, and then continuing it.
190189765Sgabor   * All of the grunt work is done in the support routines.
191189765Sgabor   */
192189765Sgabor
193189765Sgabor  do {
194189765Sgabor    int val = 0;
195189765Sgabor
196189765Sgabor    if (ioctl(Procfd, PIOCWAIT, &pfs) == -1)
197189765Sgabor      warn("PIOCWAIT top of loop");
198189765Sgabor    else {
199189765Sgabor      switch(i = pfs.why) {
200189765Sgabor      case S_SCE:
201189765Sgabor	funcs->enter_syscall(pid, pfs.val);
202189765Sgabor	break;
203189765Sgabor      case S_SCX:
204189765Sgabor	/*
205189765Sgabor	 * This is so we don't get two messages for an exec -- one
206189765Sgabor	 * for the S_EXEC, and one for the syscall exit.  It also,
207189765Sgabor	 * conveniently, ensures that the first message printed out
208189765Sgabor	 * isn't the return-from-syscall used to create the process.
209189765Sgabor	 */
210189765Sgabor
211189765Sgabor	if (in_exec) {
212189765Sgabor	  in_exec = 0;
213189765Sgabor	  break;
214189765Sgabor	}
215189765Sgabor	funcs->exit_syscall(pid, pfs.val);
216189765Sgabor	break;
217189765Sgabor      case S_SIG:
218189765Sgabor	fprintf(outfile, "SIGNAL %d\n", pfs.val);
219189765Sgabor	break;
220189765Sgabor      case S_EXIT:
221189765Sgabor	fprintf (outfile, "process exit, rval = %d\n", pfs.val);
222189765Sgabor	break;
223189765Sgabor      case S_EXEC:
224189765Sgabor	funcs = set_etype();
225189765Sgabor	in_exec = 1;
226189765Sgabor	break;
227189765Sgabor      default:
228189765Sgabor	fprintf (outfile, "Process stopped because of:  %d\n", i);
229189765Sgabor	break;
230189765Sgabor      }
231189765Sgabor    }
232189765Sgabor    if (ioctl(Procfd, PIOCCONT, val) == -1)
233189765Sgabor      warn("PIOCCONT");
234189765Sgabor  } while (pfs.why != S_EXIT);
235189765Sgabor  return 0;
236189765Sgabor}
237189765Sgabor