]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/truss/setup.c
Merge ATF 0.16 from vendor/atf/dist.
[FreeBSD/FreeBSD.git] / usr.bin / truss / setup.c
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 pid_t 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
69 int
70 setup_and_wait(char *command[])
71 {
72         pid_t pid;
73         int waitval;
74
75         pid = vfork();
76         if (pid == -1)
77                 err(1, "fork failed");
78         if (pid == 0) { /* Child */
79                 ptrace(PT_TRACE_ME, 0, 0, 0);
80                 execvp(command[0], command);
81                 err(1, "execvp %s", command[0]);
82         }
83
84         /* Only in the parent here */
85         if (waitpid(pid, &waitval, 0) < 0) {
86                 err(1, "unexpect stop in waitpid");
87                 return 0;
88         }
89
90         child_pid = pid;
91
92         return (pid);
93 }
94
95 /*
96  * start_tracing picks up where setup_and_wait() dropped off -- namely,
97  * it sets the event mask for the given process id.  Called for both
98  * monitoring an existing process and when we create our own.
99  */
100
101 int
102 start_tracing(pid_t pid)
103 {
104         int ret, retry, waitval;
105
106         retry = 10;
107         do {
108                 ret = ptrace(PT_ATTACH, pid, NULL, 0);
109                 usleep(200);
110         } while (ret && retry-- > 0);
111         if (ret)
112                 err(1, "can not attach to target process");
113
114         child_pid = pid;
115         if (waitpid(pid, &waitval, 0) < 0)
116                 err(1, "Unexpect stop in waitpid");
117
118         return (0);
119 }
120
121 /*
122  * Restore a process back to it's pre-truss state.
123  * Called for SIGINT, SIGTERM, SIGQUIT.  This only
124  * applies if truss was told to monitor an already-existing
125  * process.
126  */
127 void
128 restore_proc(int signo __unused)
129 {
130         int waitval;
131
132         /* stop the child so that we can detach */
133         kill(child_pid, SIGSTOP);
134         if (waitpid(child_pid, &waitval, 0) < 0)
135                 err(1, "Unexpected stop in waitpid");
136
137         if (ptrace(PT_DETACH, child_pid, (caddr_t)1, 0) < 0)
138                 err(1, "Can not detach the process");
139
140         kill(child_pid, SIGCONT);
141         exit(0);
142 }
143
144 /*
145  * Change curthread member based on lwpid.
146  * If it is a new thread, create a threadinfo structure
147  */
148 static void
149 find_thread(struct trussinfo *info, lwpid_t lwpid)
150 {
151         struct threadinfo *np;
152
153         info->curthread = NULL;
154         SLIST_FOREACH(np, &info->threadlist, entries) {
155                 if (np->tid == lwpid) {
156                         info->curthread = np;
157                         return;
158                 }
159         }
160
161         np = (struct threadinfo *)malloc(sizeof(struct threadinfo));
162         if (np == NULL)
163                 errx(1, "malloc() failed");
164         np->tid = lwpid;
165         np->in_fork = 0;
166         np->in_syscall = 0;
167         SLIST_INSERT_HEAD(&info->threadlist, np, entries);
168         info->curthread = np;
169 }
170
171 /*
172  * Start the traced process and wait until it stoped.
173  * Fill trussinfo structure.
174  * When this even returns, the traced process is in stop state.
175  */
176 void
177 waitevent(struct trussinfo *info)
178 {
179         struct ptrace_lwpinfo lwpinfo;
180         static int pending_signal = 0;
181         int waitval;
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         if (WIFCONTINUED(waitval)) {
190                 info->pr_why = S_NONE;
191                 return;
192         }
193         if (WIFEXITED(waitval)) {
194                 info->pr_why = S_EXIT;
195                 info->pr_data = WEXITSTATUS(waitval);
196                 return;
197         }
198         if (WIFSTOPPED(waitval)) {
199                 ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo,
200                     sizeof(lwpinfo));
201                 find_thread(info, lwpinfo.pl_lwpid);
202                 switch (WSTOPSIG(waitval)) {
203                 case SIGTRAP:
204                         if (lwpinfo.pl_flags & PL_FLAG_SCE) {
205                                 info->pr_why = S_SCE;
206                                 info->curthread->in_syscall = 1;
207                                 break;
208                         } else if (lwpinfo.pl_flags & PL_FLAG_SCX) {
209                                 info->pr_why = S_SCX;
210                                 info->curthread->in_syscall = 0;
211                                 break;
212                         } else {
213                                 errx(1,
214                    "pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX",
215                                     lwpinfo.pl_flags);
216                         }
217                 default:
218                         info->pr_why = S_SIG;
219                         info->pr_data = WSTOPSIG(waitval);
220                         pending_signal = info->pr_data;
221                         break;
222                 }
223         }
224         if (WIFSIGNALED(waitval)) {
225                 info->pr_why = S_EXIT;
226                 info->pr_data = 0;
227                 return;
228         }
229 }