setup.c revision 31597
1/*
2 * Various setup functions for truss.  Not the cleanest-written code,
3 * I'm afraid.
4 */
5/*
6 * $Id: setup.c,v 1.3 1997/12/06 14:42:58 peter Exp $
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13#include <errno.h>
14#include <err.h>
15#include <fcntl.h>
16#include <signal.h>
17#include <sys/ioctl.h>
18#include <sys/pioctl.h>
19#include <sys/types.h>
20#include <sys/wait.h>
21
22static int evflags = 0;
23
24/*
25 * setup_and_wait() is called to start a process.  All it really does
26 * is vfork(), set itself up to stop on exec or exit, and then exec
27 * the given command.  At that point, the child process stops, and
28 * the parent can wake up and deal with it.
29 */
30
31int
32setup_and_wait(char *command[]) {
33  struct procfs_status pfs;
34  char buf[32];
35  int fd;
36  int pid;
37  extern char *prog;
38  int flags;
39
40  pid = vfork();
41  if (pid == -1) {
42    err(1, "vfork failed");
43  }
44  if (pid == 0) {	/* Child */
45    int mask = S_EXEC | S_EXIT;
46    fd = open("/proc/curproc/mem", O_WRONLY);
47    if (fd == -1)
48      err(2, "cannot open /proc/curproc/mem");
49    fcntl(fd, F_SETFD, 1);
50    if (ioctl(fd, PIOCBIS, &mask) == -1)
51      err(3, "PIOCBIS");
52    flags = PF_LINGER;
53    /*
54     * The PF_LINGER flag tells procfs not to wake up the
55     * process on last close; normally, this is the behaviour
56     * we want.
57     */
58    if (ioctl(fd, PIOCSFL, &flags) == -1)
59      perror("cannot set PF_LINGER");
60    execvp(command[0], command);
61    mask = ~0;
62    ioctl(fd, PIOCBIC, &mask);
63    err(4, "execvp %s", command[0]);
64  }
65  /* Only in the parent here */
66
67  if (waitpid(pid, NULL, WNOHANG) != 0) {
68    /*
69     * Process exited before it got to us -- meaning the exec failed
70     * miserably -- so we just quietly exit.
71     */
72    exit(1);
73  }
74
75  sprintf(buf, "/proc/%d/mem", pid);
76  if ((fd = open(buf, O_RDWR)) == -1)
77    err(5, "cannot open %s", buf);
78  if (ioctl(fd, PIOCWAIT, &pfs) == -1)
79    err(6, "PIOCWAIT");
80  if (pfs.why == S_EXIT) {
81    int zero = 0;
82    fprintf(stderr, "process exited before exec'ing\n");
83    ioctl(fd, PIOCCONT, &zero);
84    wait(0);
85    exit(7);
86  }
87  close(fd);
88  return pid;
89}
90
91/*
92 * start_tracing picks up where setup_and_wait() dropped off -- namely,
93 * it sets the event mask for the given process id.  Called for both
94 * monitoring an existing process and when we create our own.
95 */
96
97int
98start_tracing(int pid, int flags) {
99  int fd;
100  char buf[32];
101  struct procfs_status tmp;
102  sprintf(buf, "/proc/%d/mem", pid);
103
104  fd = open(buf, O_RDWR);
105  if (fd == -1)
106    err(8, "cannot open %s", buf);
107
108  if (ioctl(fd, PIOCSTATUS, &tmp) == -1) {
109    err(10, "cannot get procfs status struct");
110  }
111  evflags = tmp.events;
112
113  if (ioctl(fd, PIOCBIS, &flags) == -1)
114    err(9, "cannot set procfs event bit mask");
115
116  /*
117   * This clears the PF_LINGER set above in setup_and_wait();
118   * if truss happens to die before this, then the process
119   * needs to be woken up via procctl.
120   */
121
122  flags = 0;
123  if (ioctl(fd, PIOCSFL, &flags) == -1)
124    perror("cannot clear PF_LINGER");
125
126  return fd;
127}
128
129/*
130 * Restore a process back to it's pre-truss state.
131 * Called for SIGINT, SIGTERM, SIGQUIT.  This only
132 * applies if truss was told to monitor an already-existing
133 * process.
134 */
135void
136restore_proc(int signo) {
137  extern int Procfd;
138  int i;
139
140  i = ~0;
141  ioctl(Procfd, PIOCBIC, &i);
142  if (evflags)
143    ioctl(Procfd, PIOCBIS, &evflags);
144  exit(0);
145}
146