1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * interface to continue a target process (DIRECT_MODE) and helper
29 * functions needed by this routine.
30 */
31
32 #include "tnfctl_int.h"
33 #include "prb_proc.h"
34 #include "dbg.h"
35
36
37 #include <stdlib.h>
38 #include <errno.h>
39
40 static tnfctl_errcode_t _tnfctl_continue(tnfctl_handle_t *hndl,
41 tnfctl_event_t *evt, sigset_t *oldmask, boolean_t watch_forks);
42 static tnfctl_errcode_t enable_target_state(tnfctl_handle_t *hndl,
43 boolean_t watch_forks);
44 static tnfctl_errcode_t disable_target_state(tnfctl_handle_t *hndl);
45
46 /*
47 * continue the target process and return the evt it stopped on.
48 * If child_hndl is set and we see a fork, return a handle on child
49 * process.
50 */
51 tnfctl_errcode_t
tnfctl_continue(tnfctl_handle_t * hndl,tnfctl_event_t * evt,tnfctl_handle_t ** child_hndl)52 tnfctl_continue(tnfctl_handle_t *hndl, tnfctl_event_t *evt,
53 tnfctl_handle_t **child_hndl)
54 {
55 tnfctl_errcode_t prexstat;
56 prb_status_t prbstat;
57 boolean_t lmapok = B_FALSE;
58 boolean_t watch_forks;
59 /* set my_evt to something other than TNFCTL_EVENT_TARGGONE */
60 tnfctl_event_t my_evt = TNFCTL_EVENT_EINTR;
61 enum event_op_t dl_evt;
62 sigset_t newmask, oldmask;
63 prb_proc_ctl_t *proc_p;
64 prgreg_t reg0, reg1;
65
66 /* this interface only works for DIRECT_MODE clients */
67 if (hndl->mode != DIRECT_MODE)
68 return (TNFCTL_ERR_BADARG);
69
70 proc_p = hndl->proc_p;
71
72 if (sigfillset(&newmask) == -1)
73 return (tnfctl_status_map(errno));
74
75 watch_forks = (child_hndl != NULL);
76
77 /*
78 * XXXX block all signals. Synchronous signals like SEGV that
79 * the user could catch and handle will now result in a core dump.
80 * But, this is very unlikely for 2 reasons - most users don't try
81 * to handle synchronous signals - it usually just aborts the process.
82 * And, secondly, the code until we return the original mask is the
83 * place where this synchronous signal would be generated - and, it
84 * is not very much code.
85 */
86 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) == -1)
87 return (tnfctl_status_map(errno));
88
89 /*
90 * Target is stopped on entry because tnfctl_continue()
91 * only returns with a stopped target.
92 */
93
94 /* target process shouldn't be stopped when link maps are incosistent */
95 while (lmapok == B_FALSE) {
96 prexstat = _tnfctl_continue(hndl, &my_evt, &oldmask,
97 watch_forks);
98 if (prexstat) {
99 if (my_evt == TNFCTL_EVENT_TARGGONE ||
100 my_evt == TNFCTL_EVENT_EXIT) {
101 /*
102 * target exited - free obj list and probe
103 * list so that we keep our internal state
104 * correct, else probe control interfaces will
105 * have wrong information.
106 */
107 DBG(fprintf(stderr, "target is gone\n"));
108 _tnfctl_free_objs_and_probes(hndl);
109 *evt = my_evt;
110 return (TNFCTL_ERR_NONE);
111 } else if (my_evt == TNFCTL_EVENT_EXEC) {
112 *evt = my_evt;
113 return (TNFCTL_ERR_NONE);
114 } else if (prexstat == TNFCTL_ERR_FILENOTFOUND) {
115 return (TNFCTL_ERR_NOPROCESS);
116 } else {
117 return (prexstat);
118 }
119 }
120 if (my_evt == TNFCTL_EVENT_FORK) {
121 /*
122 * sanity check. we should only get here if child_hndl is set
123 */
124 if (child_hndl) {
125 *evt = my_evt;
126 prbstat = prb_proc_get_r0_r1(proc_p,
127 ®0, ®1);
128 if (prbstat) {
129 prexstat = _tnfctl_map_to_errcode(prbstat);
130 return (prexstat);
131 }
132 prexstat = tnfctl_pid_open((pid_t)reg0,
133 child_hndl);
134 disable_target_state(*child_hndl);
135 return (prexstat);
136 }
137 return (TNFCTL_ERR_NONE);
138 }
139
140 /*
141 * update state in handle
142 * REMIND: Only need to call _tnfctl_refresh_process on
143 * dlopen or dlclose. Need to take out other functionality
144 * of refresh_process into a separate function that should
145 * be called here.
146 */
147 prexstat = _tnfctl_refresh_process(hndl, &lmapok, &dl_evt);
148 if (prexstat && (lmapok == B_TRUE))
149 return (prexstat);
150 prexstat = TNFCTL_ERR_NONE;
151 }
152 *evt = my_evt;
153 /* see if we have more detail about the event */
154 if (dl_evt == EVT_OPEN)
155 *evt = TNFCTL_EVENT_DLOPEN;
156 else if (dl_evt == EVT_CLOSE)
157 *evt = TNFCTL_EVENT_DLCLOSE;
158
159 return (TNFCTL_ERR_NONE);
160 }
161
162 /*
163 * Continues target and waits for it to stop.
164 * warning: This routine returns TNFCTL_EVENT_DLOPEN for any kind of
165 * dl activity. Up to the caller to determine the actual DL event.
166 */
167 static tnfctl_errcode_t
_tnfctl_continue(tnfctl_handle_t * hndl,tnfctl_event_t * evt,sigset_t * oldmask,boolean_t watch_forks)168 _tnfctl_continue(tnfctl_handle_t *hndl, tnfctl_event_t *evt, sigset_t *oldmask,
169 boolean_t watch_forks)
170 {
171 tnfctl_errcode_t prexstat;
172 tnfctl_errcode_t ret_prexstat = TNFCTL_ERR_NONE;
173 prb_status_t prbstat, prbstat2;
174 prb_proc_ctl_t *proc_p;
175 prb_proc_state_t state;
176
177 proc_p = hndl->proc_p;
178
179 /* set up state before we run process */
180 prexstat = enable_target_state(hndl, watch_forks);
181 if (prexstat)
182 return (prexstat);
183
184 again:
185
186 /* resume target */
187 prbstat = prb_proc_cont(proc_p);
188 if (prbstat) {
189 ret_prexstat = _tnfctl_map_to_errcode(prbstat);
190 goto end_of_func;
191 }
192
193 /* wait on target to stop (standby) */
194 prbstat = prb_proc_wait(proc_p, B_TRUE, oldmask);
195 if (prbstat) {
196 if (prbstat == EINTR) {
197 *evt = TNFCTL_EVENT_EINTR;
198 prbstat2 = prb_proc_stop(proc_p);
199 if (prbstat2) {
200 ret_prexstat = _tnfctl_map_to_errcode(prbstat2);
201 goto end_of_func;
202 }
203 } else if (prbstat == ENOENT) {
204 /* target process finished */
205 if (hndl->called_exit)
206 *evt = TNFCTL_EVENT_EXIT;
207 else
208 *evt = TNFCTL_EVENT_TARGGONE;
209 /* return directly - process no longer around */
210 return (TNFCTL_ERR_INTERNAL);
211 } else {
212 ret_prexstat = _tnfctl_map_to_errcode(prbstat);
213 goto end_of_func;
214 }
215 }
216
217 prbstat = prb_proc_state(proc_p, &state);
218 if (prbstat) {
219 ret_prexstat = _tnfctl_map_to_errcode(prbstat);
220 goto end_of_func;
221 }
222 if (state.ps_isbptfault) {
223 /* dlopen or dlclose */
224 prbstat = prb_rtld_advance(proc_p);
225 if (prbstat) {
226 ret_prexstat = _tnfctl_map_to_errcode(prbstat);
227 goto end_of_func;
228 }
229 /*
230 * actually don't know if it is a dlopen or dlclose yet.
231 * But, we return dlopen here. Up to the caller to determine
232 * which one it actually is.
233 */
234 *evt = TNFCTL_EVENT_DLOPEN;
235 } else
236 if (state.ps_issysentry) {
237 switch (state.ps_syscallnum) {
238 case SYS_execve:
239 *evt = TNFCTL_EVENT_EXEC;
240 ret_prexstat = TNFCTL_ERR_INTERNAL;
241 break;
242 case SYS_exit:
243 hndl->called_exit = B_TRUE;
244 goto again;
245 default:
246 break;
247 }
248 } else if (state.ps_issysexit) {
249 switch (state.ps_syscallnum) {
250 case SYS_vfork:
251 case SYS_forksys:
252 *evt = TNFCTL_EVENT_FORK;
253 break;
254 default:
255 break;
256 }
257 }
258 end_of_func:
259 /*
260 * disable all our sycall tracing and bpt setup in process when it
261 * is stopped, so that even if the controlling process aborts,
262 * the target could continue running
263 */
264 prexstat = disable_target_state(hndl);
265 if (prexstat)
266 return (prexstat);
267 return (ret_prexstat);
268 }
269
270 /*
271 * enable the system call tracing and dl activity tracing
272 */
273 static tnfctl_errcode_t
enable_target_state(tnfctl_handle_t * hndl,boolean_t watch_forks)274 enable_target_state(tnfctl_handle_t *hndl, boolean_t watch_forks)
275 {
276 prb_status_t prbstat;
277 prb_proc_ctl_t *proc_p;
278
279 proc_p = hndl->proc_p;
280
281 /* trace exec */
282 prbstat = prb_proc_entry(proc_p, SYS_execve, PRB_SYS_ADD);
283 if (prbstat)
284 return (_tnfctl_map_to_errcode(prbstat));
285 /* trace exit */
286 prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_ADD);
287 if (prbstat)
288 return (_tnfctl_map_to_errcode(prbstat));
289 /* trace fork if the caller requests */
290 if (watch_forks) {
291 prbstat = prb_proc_exit(proc_p, SYS_vfork, PRB_SYS_ADD);
292 if (prbstat)
293 return (_tnfctl_map_to_errcode(prbstat));
294
295 prbstat = prb_proc_exit(proc_p, SYS_forksys, PRB_SYS_ADD);
296 if (prbstat)
297 return (_tnfctl_map_to_errcode(prbstat));
298
299 prbstat = prb_proc_setfork(proc_p, B_TRUE);
300 if (prbstat)
301 return (_tnfctl_map_to_errcode(prbstat));
302 }
303 /*
304 * tracing flags for fork and exec will get unset when
305 * process stops. see disable_target_state()
306 */
307
308 /* setup process to stop during dlopen() or dlclose() */
309 prbstat = prb_rtld_stalk(proc_p);
310 return (_tnfctl_map_to_errcode(prbstat));
311 }
312
313 /*
314 * disable the system call tracing and dl activity tracing
315 */
316 static tnfctl_errcode_t
disable_target_state(tnfctl_handle_t * hndl)317 disable_target_state(tnfctl_handle_t *hndl)
318 {
319 prb_status_t prbstat;
320 prb_proc_ctl_t *proc_p;
321
322 proc_p = hndl->proc_p;
323
324 /* remove the stalking breakpoint while the process is stopped */
325 prbstat = prb_rtld_unstalk(proc_p);
326 if (prbstat)
327 return (_tnfctl_map_to_errcode(prbstat));
328
329 /* remove the exec, exit and fork tracing while stopped */
330 prbstat = prb_proc_entry(proc_p, SYS_execve, PRB_SYS_DEL);
331 if (prbstat)
332 return (_tnfctl_map_to_errcode(prbstat));
333 prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_DEL);
334 if (prbstat)
335 return (_tnfctl_map_to_errcode(prbstat));
336 prbstat = prb_proc_exit(proc_p, SYS_vfork, PRB_SYS_DEL);
337 if (prbstat)
338 return (_tnfctl_map_to_errcode(prbstat));
339 prbstat = prb_proc_exit(proc_p, SYS_forksys, PRB_SYS_DEL);
340 if (prbstat)
341 return (_tnfctl_map_to_errcode(prbstat));
342 prbstat = prb_proc_setfork(proc_p, B_FALSE);
343 if (prbstat)
344 return (_tnfctl_map_to_errcode(prbstat));
345
346 return (TNFCTL_ERR_NONE);
347 }
348