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$");
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 sig_atomic_t detaching;
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	pid_t pid;
73
74	pid = vfork();
75	if (pid == -1)
76		err(1, "fork failed");
77	if (pid == 0) {	/* Child */
78		ptrace(PT_TRACE_ME, 0, 0, 0);
79		execvp(command[0], command);
80		err(1, "execvp %s", command[0]);
81	}
82
83	/* Only in the parent here */
84	if (waitpid(pid, NULL, 0) < 0)
85		err(1, "unexpect stop in waitpid");
86
87	return (pid);
88}
89
90/*
91 * start_tracing picks up where setup_and_wait() dropped off -- namely,
92 * it sets the event mask for the given process id.  Called for both
93 * monitoring an existing process and when we create our own.
94 */
95
96int
97start_tracing(pid_t pid)
98{
99	int ret, retry;
100
101	retry = 10;
102	do {
103		ret = ptrace(PT_ATTACH, pid, NULL, 0);
104		usleep(200);
105	} while (ret && retry-- > 0);
106	if (ret)
107		err(1, "can not attach to target process");
108
109	if (waitpid(pid, NULL, 0) < 0)
110		err(1, "Unexpect stop in waitpid");
111
112	return (0);
113}
114
115/*
116 * Restore a process back to it's pre-truss state.
117 * Called for SIGINT, SIGTERM, SIGQUIT.  This only
118 * applies if truss was told to monitor an already-existing
119 * process.
120 */
121
122void
123restore_proc(int signo __unused)
124{
125
126	detaching = 1;
127}
128
129static int
130detach_proc(pid_t pid)
131{
132	int waitval;
133
134	/* stop the child so that we can detach */
135	kill(pid, SIGSTOP);
136	if (waitpid(pid, &waitval, 0) < 0)
137		err(1, "Unexpected stop in waitpid");
138
139	if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0)
140		err(1, "Can not detach the process");
141
142	kill(pid, SIGCONT);
143
144	return (waitval);
145}
146
147/*
148 * Change curthread member based on lwpid.
149 * If it is a new thread, create a threadinfo structure
150 */
151static void
152find_thread(struct trussinfo *info, lwpid_t lwpid)
153{
154	struct threadinfo *np;
155
156	info->curthread = NULL;
157	SLIST_FOREACH(np, &info->threadlist, entries) {
158		if (np->tid == lwpid) {
159			info->curthread = np;
160			return;
161		}
162	}
163
164	np = (struct threadinfo *)calloc(1, sizeof(struct threadinfo));
165	if (np == NULL)
166		err(1, "calloc() failed");
167	np->tid = lwpid;
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	struct ptrace_lwpinfo lwpinfo;
181	static int pending_signal = 0;
182	int waitval;
183
184	ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal);
185	pending_signal = 0;
186
187detach:
188	if (detaching) {
189		waitval = detach_proc(info->pid);
190		info->pr_why = S_DETACHED;
191		info->pr_data = WEXITSTATUS(waitval);
192		return;
193	}
194
195	if (waitpid(info->pid, &waitval, 0) == -1) {
196		if (errno == EINTR)
197			goto detach;
198		err(1, "Unexpected stop in waitpid");
199	}
200
201	if (WIFCONTINUED(waitval)) {
202		info->pr_why = S_NONE;
203		return;
204	}
205	if (WIFEXITED(waitval)) {
206		info->pr_why = S_EXIT;
207		info->pr_data = WEXITSTATUS(waitval);
208		return;
209	}
210	if (WIFSTOPPED(waitval)) {
211		ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo,
212		    sizeof(lwpinfo));
213		find_thread(info, lwpinfo.pl_lwpid);
214		switch (WSTOPSIG(waitval)) {
215		case SIGTRAP:
216			if (lwpinfo.pl_flags & PL_FLAG_SCE) {
217				info->pr_why = S_SCE;
218				info->curthread->in_syscall = 1;
219				break;
220			} else if (lwpinfo.pl_flags & PL_FLAG_SCX) {
221				info->pr_why = S_SCX;
222				info->curthread->in_syscall = 0;
223				break;
224			} else {
225				errx(1,
226		   "pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX",
227				    lwpinfo.pl_flags);
228			}
229		default:
230			info->pr_why = S_SIG;
231			info->pr_data = WSTOPSIG(waitval);
232			pending_signal = info->pr_data;
233			break;
234		}
235	}
236	if (WIFSIGNALED(waitval)) {
237		info->pr_why = S_EXIT;
238		info->pr_data = 0;
239		return;
240	}
241}
242