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