xref: /titanic_50/usr/src/lib/libtnfctl/continue.c (revision 8fd04b8338ed5093ec2d1e668fa620b7de44c177)
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 				&reg0, &reg1);
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