setup.c revision 218707
1/*-
2 * Copyright 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#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/usr.bin/truss/setup.c 218707 2011-02-15 12:42:18Z jhb $");
34
35/*
36 * Various setup functions for truss.  Not the cleanest-written code,
37 * I'm afraid.
38 */
39
40#include <sys/param.h>
41#include <sys/types.h>
42#include <sys/ptrace.h>
43#include <sys/wait.h>
44
45#include <err.h>
46#include <errno.h>
47#include <fcntl.h>
48#include <signal.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <time.h>
53#include <unistd.h>
54
55#include <machine/reg.h>
56
57#include "truss.h"
58#include "extern.h"
59
60static int child_pid;
61
62/*
63 * setup_and_wait() is called to start a process.  All it really does
64 * is fork(), set itself up to stop on exec or exit, and then exec
65 * the given command.  At that point, the child process stops, and
66 * the parent can wake up and deal with it.
67 */
68
69int
70setup_and_wait(char *command[])
71{
72	int pid;
73	int waitval;
74
75	pid = vfork();
76	if (pid == -1) {
77		err(1, "fork failed");
78	}
79	if (pid == 0) {	/* Child */
80		ptrace(PT_TRACE_ME, 0, 0, 0);
81		execvp(command[0], command);
82		err(1, "execvp %s", command[0]);
83	}
84
85	/* Only in the parent here */
86	if (waitpid(pid, &waitval, 0) < 0) {
87		err(1, "unexpect stop in waitpid");
88		return 0;
89	}
90
91	child_pid = pid;
92
93	return (pid);
94}
95
96/*
97 * start_tracing picks up where setup_and_wait() dropped off -- namely,
98 * it sets the event mask for the given process id.  Called for both
99 * monitoring an existing process and when we create our own.
100 */
101
102int
103start_tracing(int pid)
104{
105	int waitval;
106	int ret;
107	int retry = 10;
108
109	do {
110		ret = ptrace(PT_ATTACH, pid, NULL, 0);
111		usleep(200);
112	} while(ret && retry-- > 0);
113	if (ret)
114		err(1, "can not attach to target process");
115
116	child_pid = pid;
117	if (waitpid(pid, &waitval, 0) < 0)
118		err(1, "Unexpect stop in waitpid");
119
120	return (0);
121}
122
123/*
124 * Restore a process back to it's pre-truss state.
125 * Called for SIGINT, SIGTERM, SIGQUIT.  This only
126 * applies if truss was told to monitor an already-existing
127 * process.
128 */
129void
130restore_proc(int signo __unused)
131{
132	int waitval;
133
134	/* stop the child so that we can detach */
135	kill(child_pid, SIGSTOP);
136	if (waitpid(child_pid, &waitval, 0) < 0)
137		err(1, "Unexpected stop in waitpid");
138
139	if (ptrace(PT_DETACH, child_pid, (caddr_t)1, 0) < 0)
140		err(1, "Can not detach the process");
141
142	kill(child_pid, SIGCONT);
143	exit(0);
144}
145
146/*
147 * Change curthread member based on lwpid.
148 * If it is a new thread, create a threadinfo structure
149 */
150static void
151find_thread(struct trussinfo *info, lwpid_t lwpid)
152{
153	info->curthread = NULL;
154	struct threadinfo *np;
155	SLIST_FOREACH(np, &info->threadlist, entries) {
156	if (np->tid == lwpid) {
157		info->curthread = np;
158		return;
159		}
160	}
161
162	np = (struct threadinfo *)malloc(sizeof(struct threadinfo));
163	if (np == NULL)
164		errx(1, "malloc() failed");
165	np->tid = lwpid;
166	np->in_fork = 0;
167	np->in_syscall = 0;
168	SLIST_INSERT_HEAD(&info->threadlist, np, entries);
169	info->curthread = np;
170}
171
172/*
173 * Start the traced process and wait until it stoped.
174 * Fill trussinfo structure.
175 * When this even returns, the traced process is in stop state.
176 */
177void
178waitevent(struct trussinfo *info)
179{
180	int waitval;
181	static int pending_signal = 0;
182
183	ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal);
184	pending_signal = 0;
185
186	if (waitpid(info->pid, &waitval, 0) < 0) {
187		err(1, "Unexpected stop in waitpid");
188	}
189
190	if (WIFCONTINUED(waitval)) {
191		info->pr_why = S_NONE;
192		return;
193	}
194	if (WIFEXITED(waitval)) {
195		info->pr_why = S_EXIT;
196		info->pr_data = WEXITSTATUS(waitval);
197		return;
198	}
199	if (WIFSTOPPED(waitval)) {
200		struct ptrace_lwpinfo lwpinfo;
201		ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo, sizeof(lwpinfo));
202		find_thread(info, lwpinfo.pl_lwpid);
203		switch(WSTOPSIG(waitval)) {
204		case SIGTRAP:
205			info->pr_why = info->curthread->in_syscall?S_SCX:S_SCE;
206			info->curthread->in_syscall = 1 - info->curthread->in_syscall;
207			break;
208		default:
209			info->pr_why = S_SIG;
210			info->pr_data = WSTOPSIG(waitval);
211			pending_signal = info->pr_data;
212			break;
213		}
214	}
215	if (WIFSIGNALED(waitval)) {
216		info->pr_why = S_EXIT;
217		info->pr_data = 0;
218		return;
219	}
220}
221