xref: /freebsd/usr.bin/truss/setup.c (revision 94355cfdfd3f9c482aae46b832836caf9a0a191a)
1f0bcd5c3SXin LI /*-
20a6c71f8SWarner Losh  * Copyright 1997 Sean Eric Fagan
309d64da3SSean Eric Fagan  *
409d64da3SSean Eric Fagan  * Redistribution and use in source and binary forms, with or without
509d64da3SSean Eric Fagan  * modification, are permitted provided that the following conditions
609d64da3SSean Eric Fagan  * are met:
709d64da3SSean Eric Fagan  * 1. Redistributions of source code must retain the above copyright
809d64da3SSean Eric Fagan  *    notice, this list of conditions and the following disclaimer.
909d64da3SSean Eric Fagan  * 2. Redistributions in binary form must reproduce the above copyright
1009d64da3SSean Eric Fagan  *    notice, this list of conditions and the following disclaimer in the
1109d64da3SSean Eric Fagan  *    documentation and/or other materials provided with the distribution.
1209d64da3SSean Eric Fagan  * 3. All advertising materials mentioning features or use of this software
1309d64da3SSean Eric Fagan  *    must display the following acknowledgement:
1409d64da3SSean Eric Fagan  *	This product includes software developed by Sean Eric Fagan
1509d64da3SSean Eric Fagan  * 4. Neither the name of the author may be used to endorse or promote
1609d64da3SSean Eric Fagan  *    products derived from this software without specific prior written
1709d64da3SSean Eric Fagan  *    permission.
1809d64da3SSean Eric Fagan  *
1909d64da3SSean Eric Fagan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2009d64da3SSean Eric Fagan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2109d64da3SSean Eric Fagan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2209d64da3SSean Eric Fagan  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2309d64da3SSean Eric Fagan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2409d64da3SSean Eric Fagan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2509d64da3SSean Eric Fagan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2609d64da3SSean Eric Fagan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2709d64da3SSean Eric Fagan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2809d64da3SSean Eric Fagan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2909d64da3SSean Eric Fagan  * SUCH DAMAGE.
3009d64da3SSean Eric Fagan  */
3109d64da3SSean Eric Fagan 
32b956c13cSPhilippe Charnier #include <sys/cdefs.h>
33b956c13cSPhilippe Charnier __FBSDID("$FreeBSD$");
343cf51049SPhilippe Charnier 
3509d64da3SSean Eric Fagan /*
36bbeaf6c0SSean Eric Fagan  * Various setup functions for truss.  Not the cleanest-written code,
37bbeaf6c0SSean Eric Fagan  * I'm afraid.
38bbeaf6c0SSean Eric Fagan  */
39bbeaf6c0SSean Eric Fagan 
40580e0a2bSDag-Erling Smørgrav #include <sys/param.h>
415d2d083cSXin LI #include <sys/types.h>
425d2d083cSXin LI #include <sys/ptrace.h>
43580e0a2bSDag-Erling Smørgrav #include <sys/wait.h>
44580e0a2bSDag-Erling Smørgrav 
453cf51049SPhilippe Charnier #include <err.h>
46821df508SXin LI #include <errno.h>
47821df508SXin LI #include <fcntl.h>
483cf51049SPhilippe Charnier #include <signal.h>
49bbeaf6c0SSean Eric Fagan #include <stdio.h>
50bbeaf6c0SSean Eric Fagan #include <stdlib.h>
51821df508SXin LI #include <string.h>
52821df508SXin LI #include <time.h>
53bbeaf6c0SSean Eric Fagan #include <unistd.h>
54bbeaf6c0SSean Eric Fagan 
555d2d083cSXin LI #include <machine/reg.h>
565d2d083cSXin LI 
57ec0bed25SMatthew N. Dodd #include "truss.h"
581be5d704SMark Murray #include "extern.h"
591be5d704SMark Murray 
60be305c9cSAndrey Zonov static pid_t child_pid;
61bbeaf6c0SSean Eric Fagan 
62bbeaf6c0SSean Eric Fagan /*
63bbeaf6c0SSean Eric Fagan  * setup_and_wait() is called to start a process.  All it really does
641fd98d7dSDag-Erling Smørgrav  * is fork(), set itself up to stop on exec or exit, and then exec
65bbeaf6c0SSean Eric Fagan  * the given command.  At that point, the child process stops, and
66bbeaf6c0SSean Eric Fagan  * the parent can wake up and deal with it.
67bbeaf6c0SSean Eric Fagan  */
68bbeaf6c0SSean Eric Fagan 
69bbeaf6c0SSean Eric Fagan int
705321ae86SAlfred Perlstein setup_and_wait(char *command[])
715321ae86SAlfred Perlstein {
72be305c9cSAndrey Zonov 	pid_t pid;
735d2d083cSXin LI 	int waitval;
74bbeaf6c0SSean Eric Fagan 
755d2d083cSXin LI 	pid = vfork();
76*94355cfdSAndrey Zonov 	if (pid == -1)
771fd98d7dSDag-Erling Smørgrav 		err(1, "fork failed");
78bbeaf6c0SSean Eric Fagan 	if (pid == 0) {	/* Child */
795d2d083cSXin LI 		ptrace(PT_TRACE_ME, 0, 0, 0);
80bbeaf6c0SSean Eric Fagan 		execvp(command[0], command);
815d2d083cSXin LI 		err(1, "execvp %s", command[0]);
82bbeaf6c0SSean Eric Fagan 	}
835d2d083cSXin LI 
84bbeaf6c0SSean Eric Fagan 	/* Only in the parent here */
85ad018914SJohn Baldwin 	if (waitpid(pid, &waitval, 0) < 0) {
865d2d083cSXin LI 		err(1, "unexpect stop in waitpid");
875d2d083cSXin LI 		return 0;
88bbeaf6c0SSean Eric Fagan 	}
89bbeaf6c0SSean Eric Fagan 
905d2d083cSXin LI 	child_pid = pid;
91081e5c48SPav Lucistnik 
925321ae86SAlfred Perlstein 	return (pid);
93bbeaf6c0SSean Eric Fagan }
94bbeaf6c0SSean Eric Fagan 
95bbeaf6c0SSean Eric Fagan /*
96bbeaf6c0SSean Eric Fagan  * start_tracing picks up where setup_and_wait() dropped off -- namely,
97bbeaf6c0SSean Eric Fagan  * it sets the event mask for the given process id.  Called for both
98bbeaf6c0SSean Eric Fagan  * monitoring an existing process and when we create our own.
99bbeaf6c0SSean Eric Fagan  */
100bbeaf6c0SSean Eric Fagan 
101bbeaf6c0SSean Eric Fagan int
102be305c9cSAndrey Zonov start_tracing(pid_t pid)
1035321ae86SAlfred Perlstein {
104*94355cfdSAndrey Zonov 	int ret, retry, waitval;
1055321ae86SAlfred Perlstein 
106*94355cfdSAndrey Zonov 	retry = 10;
1075d2d083cSXin LI 	do {
1085d2d083cSXin LI 		ret = ptrace(PT_ATTACH, pid, NULL, 0);
1095d2d083cSXin LI 		usleep(200);
1105d2d083cSXin LI 	} while (ret && retry-- > 0);
1115d2d083cSXin LI 	if (ret)
1125d2d083cSXin LI 		err(1, "can not attach to target process");
11320fa828fSSean Eric Fagan 
1145d2d083cSXin LI 	child_pid = pid;
115ad018914SJohn Baldwin 	if (waitpid(pid, &waitval, 0) < 0)
1165d2d083cSXin LI 		err(1, "Unexpect stop in waitpid");
117bbeaf6c0SSean Eric Fagan 
1185d2d083cSXin LI 	return (0);
119bbeaf6c0SSean Eric Fagan }
120bbeaf6c0SSean Eric Fagan 
121bbeaf6c0SSean Eric Fagan /*
122bbeaf6c0SSean Eric Fagan  * Restore a process back to it's pre-truss state.
123bbeaf6c0SSean Eric Fagan  * Called for SIGINT, SIGTERM, SIGQUIT.  This only
124bbeaf6c0SSean Eric Fagan  * applies if truss was told to monitor an already-existing
125bbeaf6c0SSean Eric Fagan  * process.
126bbeaf6c0SSean Eric Fagan  */
127bbeaf6c0SSean Eric Fagan void
1285d2d083cSXin LI restore_proc(int signo __unused)
1295d2d083cSXin LI {
1305d2d083cSXin LI 	int waitval;
131bbeaf6c0SSean Eric Fagan 
1325d2d083cSXin LI 	/* stop the child so that we can detach */
1335d2d083cSXin LI 	kill(child_pid, SIGSTOP);
134ad018914SJohn Baldwin 	if (waitpid(child_pid, &waitval, 0) < 0)
1355d2d083cSXin LI 		err(1, "Unexpected stop in waitpid");
1365d2d083cSXin LI 
1375d2d083cSXin LI 	if (ptrace(PT_DETACH, child_pid, (caddr_t)1, 0) < 0)
1385d2d083cSXin LI 		err(1, "Can not detach the process");
1395d2d083cSXin LI 
1405d2d083cSXin LI 	kill(child_pid, SIGCONT);
141bbeaf6c0SSean Eric Fagan 	exit(0);
142bbeaf6c0SSean Eric Fagan }
1435d2d083cSXin LI 
1445d2d083cSXin LI /*
1455d2d083cSXin LI  * Change curthread member based on lwpid.
1465d2d083cSXin LI  * If it is a new thread, create a threadinfo structure
1475d2d083cSXin LI  */
1485d2d083cSXin LI static void
1495d2d083cSXin LI find_thread(struct trussinfo *info, lwpid_t lwpid)
1505d2d083cSXin LI {
1515d2d083cSXin LI 	struct threadinfo *np;
152*94355cfdSAndrey Zonov 
153*94355cfdSAndrey Zonov 	info->curthread = NULL;
1545d2d083cSXin LI 	SLIST_FOREACH(np, &info->threadlist, entries) {
1555d2d083cSXin LI 		if (np->tid == lwpid) {
1565d2d083cSXin LI 			info->curthread = np;
1575d2d083cSXin LI 			return;
1585d2d083cSXin LI 		}
1595d2d083cSXin LI 	}
1605d2d083cSXin LI 
1615d2d083cSXin LI 	np = (struct threadinfo *)malloc(sizeof(struct threadinfo));
1625d2d083cSXin LI 	if (np == NULL)
1635d2d083cSXin LI 		errx(1, "malloc() failed");
1645d2d083cSXin LI 	np->tid = lwpid;
1655d2d083cSXin LI 	np->in_fork = 0;
1665d2d083cSXin LI 	np->in_syscall = 0;
1675d2d083cSXin LI 	SLIST_INSERT_HEAD(&info->threadlist, np, entries);
1685d2d083cSXin LI 	info->curthread = np;
1695d2d083cSXin LI }
1705d2d083cSXin LI 
1715d2d083cSXin LI /*
1725d2d083cSXin LI  * Start the traced process and wait until it stoped.
1735d2d083cSXin LI  * Fill trussinfo structure.
1745d2d083cSXin LI  * When this even returns, the traced process is in stop state.
1755d2d083cSXin LI  */
1765d2d083cSXin LI void
1775d2d083cSXin LI waitevent(struct trussinfo *info)
1785d2d083cSXin LI {
179*94355cfdSAndrey Zonov 	struct ptrace_lwpinfo lwpinfo;
1805d2d083cSXin LI 	static int pending_signal = 0;
181*94355cfdSAndrey Zonov 	int waitval;
1825d2d083cSXin LI 
1835d2d083cSXin LI 	ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal);
1845d2d083cSXin LI 	pending_signal = 0;
1855d2d083cSXin LI 
186*94355cfdSAndrey Zonov 	if (waitpid(info->pid, &waitval, 0) < 0)
1875d2d083cSXin LI 		err(1, "Unexpected stop in waitpid");
1885d2d083cSXin LI 
1895d2d083cSXin LI 	if (WIFCONTINUED(waitval)) {
1905d2d083cSXin LI 		info->pr_why = S_NONE;
1915d2d083cSXin LI 		return;
1925d2d083cSXin LI 	}
1935d2d083cSXin LI 	if (WIFEXITED(waitval)) {
1945d2d083cSXin LI 		info->pr_why = S_EXIT;
1955d2d083cSXin LI 		info->pr_data = WEXITSTATUS(waitval);
1965d2d083cSXin LI 		return;
1975d2d083cSXin LI 	}
198ef29ac7fSXin LI 	if (WIFSTOPPED(waitval)) {
199*94355cfdSAndrey Zonov 		ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo,
200*94355cfdSAndrey Zonov 		    sizeof(lwpinfo));
2015d2d083cSXin LI 		find_thread(info, lwpinfo.pl_lwpid);
2025d2d083cSXin LI 		switch (WSTOPSIG(waitval)) {
2035d2d083cSXin LI 		case SIGTRAP:
20497695ad4SKonstantin Belousov 			if (lwpinfo.pl_flags & PL_FLAG_SCE) {
20597695ad4SKonstantin Belousov 				info->pr_why = S_SCE;
20697695ad4SKonstantin Belousov 				info->curthread->in_syscall = 1;
2075d2d083cSXin LI 				break;
20897695ad4SKonstantin Belousov 			} else if (lwpinfo.pl_flags & PL_FLAG_SCX) {
20997695ad4SKonstantin Belousov 				info->pr_why = S_SCX;
21097695ad4SKonstantin Belousov 				info->curthread->in_syscall = 0;
21197695ad4SKonstantin Belousov 				break;
21297695ad4SKonstantin Belousov 			} else {
21397695ad4SKonstantin Belousov 				errx(1,
21497695ad4SKonstantin Belousov 		   "pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX",
21597695ad4SKonstantin Belousov 				    lwpinfo.pl_flags);
21697695ad4SKonstantin Belousov 			}
2175d2d083cSXin LI 		default:
2185d2d083cSXin LI 			info->pr_why = S_SIG;
2195d2d083cSXin LI 			info->pr_data = WSTOPSIG(waitval);
2205d2d083cSXin LI 			pending_signal = info->pr_data;
2215d2d083cSXin LI 			break;
2225d2d083cSXin LI 		}
2235d2d083cSXin LI 	}
224ef29ac7fSXin LI 	if (WIFSIGNALED(waitval)) {
225ef29ac7fSXin LI 		info->pr_why = S_EXIT;
226f0bcd5c3SXin LI 		info->pr_data = 0;
227ef29ac7fSXin LI 		return;
228ef29ac7fSXin LI 	}
2295d2d083cSXin LI }
230