xref: /freebsd/usr.bin/truss/setup.c (revision 3b3a8eb937bf8045231e8364bfd1b94cd4a95979)
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 
60 static 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 
69 int
70 setup_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 
96 int
97 start_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 
122 void
123 restore_proc(int signo __unused)
124 {
125 
126 	detaching = 1;
127 }
128 
129 static int
130 detach_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  */
151 static void
152 find_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 *)malloc(sizeof(struct threadinfo));
165 	if (np == NULL)
166 		errx(1, "malloc() failed");
167 	np->tid = lwpid;
168 	np->in_fork = 0;
169 	np->in_syscall = 0;
170 	SLIST_INSERT_HEAD(&info->threadlist, np, entries);
171 	info->curthread = np;
172 }
173 
174 /*
175  * Start the traced process and wait until it stoped.
176  * Fill trussinfo structure.
177  * When this even returns, the traced process is in stop state.
178  */
179 void
180 waitevent(struct trussinfo *info)
181 {
182 	struct ptrace_lwpinfo lwpinfo;
183 	static int pending_signal = 0;
184 	int waitval;
185 
186 	ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal);
187 	pending_signal = 0;
188 
189 detach:
190 	if (detaching) {
191 		waitval = detach_proc(info->pid);
192 		info->pr_why = S_DETACHED;
193 		info->pr_data = WEXITSTATUS(waitval);
194 		return;
195 	}
196 
197 	if (waitpid(info->pid, &waitval, 0) == -1) {
198 		if (errno == EINTR)
199 			goto detach;
200 		err(1, "Unexpected stop in waitpid");
201 	}
202 
203 	if (WIFCONTINUED(waitval)) {
204 		info->pr_why = S_NONE;
205 		return;
206 	}
207 	if (WIFEXITED(waitval)) {
208 		info->pr_why = S_EXIT;
209 		info->pr_data = WEXITSTATUS(waitval);
210 		return;
211 	}
212 	if (WIFSTOPPED(waitval)) {
213 		ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo,
214 		    sizeof(lwpinfo));
215 		find_thread(info, lwpinfo.pl_lwpid);
216 		switch (WSTOPSIG(waitval)) {
217 		case SIGTRAP:
218 			if (lwpinfo.pl_flags & PL_FLAG_SCE) {
219 				info->pr_why = S_SCE;
220 				info->curthread->in_syscall = 1;
221 				break;
222 			} else if (lwpinfo.pl_flags & PL_FLAG_SCX) {
223 				info->pr_why = S_SCX;
224 				info->curthread->in_syscall = 0;
225 				break;
226 			} else {
227 				errx(1,
228 		   "pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX",
229 				    lwpinfo.pl_flags);
230 			}
231 		default:
232 			info->pr_why = S_SIG;
233 			info->pr_data = WSTOPSIG(waitval);
234 			pending_signal = info->pr_data;
235 			break;
236 		}
237 	}
238 	if (WIFSIGNALED(waitval)) {
239 		info->pr_why = S_EXIT;
240 		info->pr_data = 0;
241 		return;
242 	}
243 }
244