xref: /onnv-gate/usr/src/lib/libtnfctl/continue.c (revision 11798:1e7f1f154004)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53235Sraf  * Common Development and Distribution License (the "License").
63235Sraf  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
213235Sraf 
220Sstevel@tonic-gate /*
23*11798SRoger.Faulkner@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * interface to continue a target process (DIRECT_MODE) and helper
290Sstevel@tonic-gate  * functions needed by this routine.
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include "tnfctl_int.h"
330Sstevel@tonic-gate #include "prb_proc.h"
340Sstevel@tonic-gate #include "dbg.h"
350Sstevel@tonic-gate 
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #include <stdlib.h>
380Sstevel@tonic-gate #include <errno.h>
390Sstevel@tonic-gate 
400Sstevel@tonic-gate static tnfctl_errcode_t _tnfctl_continue(tnfctl_handle_t *hndl,
410Sstevel@tonic-gate     tnfctl_event_t *evt, sigset_t *oldmask, boolean_t watch_forks);
420Sstevel@tonic-gate static tnfctl_errcode_t enable_target_state(tnfctl_handle_t *hndl,
430Sstevel@tonic-gate     boolean_t watch_forks);
440Sstevel@tonic-gate static tnfctl_errcode_t disable_target_state(tnfctl_handle_t *hndl);
450Sstevel@tonic-gate 
460Sstevel@tonic-gate /*
470Sstevel@tonic-gate  * continue the target process and return the evt it stopped on.
480Sstevel@tonic-gate  * If child_hndl is set and we see a fork, return a handle on child
490Sstevel@tonic-gate  * process.
500Sstevel@tonic-gate  */
510Sstevel@tonic-gate tnfctl_errcode_t
tnfctl_continue(tnfctl_handle_t * hndl,tnfctl_event_t * evt,tnfctl_handle_t ** child_hndl)520Sstevel@tonic-gate tnfctl_continue(tnfctl_handle_t *hndl, tnfctl_event_t *evt,
530Sstevel@tonic-gate 		tnfctl_handle_t **child_hndl)
540Sstevel@tonic-gate {
550Sstevel@tonic-gate 	tnfctl_errcode_t	prexstat;
560Sstevel@tonic-gate 	prb_status_t		prbstat;
570Sstevel@tonic-gate 	boolean_t		lmapok = B_FALSE;
580Sstevel@tonic-gate 	boolean_t		watch_forks;
590Sstevel@tonic-gate 	/* set my_evt to something other than TNFCTL_EVENT_TARGGONE */
600Sstevel@tonic-gate 	tnfctl_event_t		my_evt = TNFCTL_EVENT_EINTR;
610Sstevel@tonic-gate 	enum event_op_t		dl_evt;
620Sstevel@tonic-gate 	sigset_t		newmask, oldmask;
630Sstevel@tonic-gate 	prb_proc_ctl_t		*proc_p;
640Sstevel@tonic-gate 	prgreg_t		reg0, reg1;
650Sstevel@tonic-gate 
660Sstevel@tonic-gate 	/* this interface only works for DIRECT_MODE clients */
670Sstevel@tonic-gate 	if (hndl->mode != DIRECT_MODE)
680Sstevel@tonic-gate 		return (TNFCTL_ERR_BADARG);
690Sstevel@tonic-gate 
700Sstevel@tonic-gate 	proc_p = hndl->proc_p;
710Sstevel@tonic-gate 
720Sstevel@tonic-gate 	if (sigfillset(&newmask) == -1)
730Sstevel@tonic-gate 		return (tnfctl_status_map(errno));
740Sstevel@tonic-gate 
750Sstevel@tonic-gate 	watch_forks = (child_hndl != NULL);
760Sstevel@tonic-gate 
770Sstevel@tonic-gate 	/*
780Sstevel@tonic-gate 	 * XXXX block all signals.  Synchronous signals like SEGV that
790Sstevel@tonic-gate 	 * the user could catch and handle will now result in a core dump.
800Sstevel@tonic-gate 	 * But, this is very unlikely for 2 reasons - most users don't try
810Sstevel@tonic-gate 	 * to handle synchronous signals - it usually just aborts the process.
820Sstevel@tonic-gate 	 * And, secondly, the code until we return the original mask is the
830Sstevel@tonic-gate 	 * place where this synchronous signal would be generated - and, it
840Sstevel@tonic-gate 	 * is not very much code.
850Sstevel@tonic-gate 	 */
860Sstevel@tonic-gate 	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) == -1)
870Sstevel@tonic-gate 		return (tnfctl_status_map(errno));
880Sstevel@tonic-gate 
890Sstevel@tonic-gate 	/*
900Sstevel@tonic-gate 	 * Target is stopped on entry because tnfctl_continue()
910Sstevel@tonic-gate 	 * only returns with a stopped target.
920Sstevel@tonic-gate 	 */
930Sstevel@tonic-gate 
940Sstevel@tonic-gate 	/* target process shouldn't be stopped when link maps are incosistent */
950Sstevel@tonic-gate 	while (lmapok == B_FALSE) {
960Sstevel@tonic-gate 		prexstat = _tnfctl_continue(hndl, &my_evt, &oldmask,
970Sstevel@tonic-gate 		    watch_forks);
980Sstevel@tonic-gate 		if (prexstat) {
990Sstevel@tonic-gate 			if (my_evt == TNFCTL_EVENT_TARGGONE ||
1000Sstevel@tonic-gate 			    my_evt == TNFCTL_EVENT_EXIT) {
1010Sstevel@tonic-gate 				/*
1020Sstevel@tonic-gate 				 * target exited - free obj list and probe
1030Sstevel@tonic-gate 				 * list so that we keep our internal state
1040Sstevel@tonic-gate 				 * correct, else probe control interfaces will
1050Sstevel@tonic-gate 				 * have wrong information.
1060Sstevel@tonic-gate 				 */
1070Sstevel@tonic-gate 			    DBG(fprintf(stderr, "target is gone\n"));
1080Sstevel@tonic-gate 				_tnfctl_free_objs_and_probes(hndl);
1090Sstevel@tonic-gate 				*evt = my_evt;
1100Sstevel@tonic-gate 				return (TNFCTL_ERR_NONE);
1110Sstevel@tonic-gate 			} else if (my_evt == TNFCTL_EVENT_EXEC) {
1120Sstevel@tonic-gate 				*evt = my_evt;
1130Sstevel@tonic-gate 				return (TNFCTL_ERR_NONE);
1140Sstevel@tonic-gate 			} else if (prexstat == TNFCTL_ERR_FILENOTFOUND) {
1150Sstevel@tonic-gate 				return (TNFCTL_ERR_NOPROCESS);
1160Sstevel@tonic-gate 			} else {
1170Sstevel@tonic-gate 				return (prexstat);
1180Sstevel@tonic-gate 			}
1190Sstevel@tonic-gate 		}
1200Sstevel@tonic-gate 		if (my_evt == TNFCTL_EVENT_FORK) {
1210Sstevel@tonic-gate 	/*
1220Sstevel@tonic-gate 	 * sanity check.  we should only get here if child_hndl is set
1230Sstevel@tonic-gate 	 */
1240Sstevel@tonic-gate 		    if (child_hndl) {
1250Sstevel@tonic-gate 			    *evt = my_evt;
1260Sstevel@tonic-gate 			    prbstat = prb_proc_get_r0_r1(proc_p,
1270Sstevel@tonic-gate 				&reg0, &reg1);
1280Sstevel@tonic-gate 			    if (prbstat) {
1290Sstevel@tonic-gate 				prexstat = _tnfctl_map_to_errcode(prbstat);
1300Sstevel@tonic-gate 				return (prexstat);
1310Sstevel@tonic-gate 			    }
1320Sstevel@tonic-gate 			    prexstat = tnfctl_pid_open((pid_t)reg0,
1330Sstevel@tonic-gate 						child_hndl);
1340Sstevel@tonic-gate 			    disable_target_state(*child_hndl);
1350Sstevel@tonic-gate 			    return (prexstat);
1360Sstevel@tonic-gate 			}
1370Sstevel@tonic-gate 			return (TNFCTL_ERR_NONE);
1380Sstevel@tonic-gate 		}
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 		/*
1410Sstevel@tonic-gate 		 * update state in handle
1420Sstevel@tonic-gate 		 * REMIND: Only need to call _tnfctl_refresh_process on
1430Sstevel@tonic-gate 		 * dlopen or dlclose.  Need to take out other functionality
1440Sstevel@tonic-gate 		 * of refresh_process into a separate function that should
1450Sstevel@tonic-gate 		 * be called here.
1460Sstevel@tonic-gate 		 */
1470Sstevel@tonic-gate 		prexstat = _tnfctl_refresh_process(hndl, &lmapok, &dl_evt);
1480Sstevel@tonic-gate 		if (prexstat && (lmapok == B_TRUE))
1490Sstevel@tonic-gate 			return (prexstat);
1500Sstevel@tonic-gate 		prexstat = TNFCTL_ERR_NONE;
1510Sstevel@tonic-gate 	}
1520Sstevel@tonic-gate 	*evt = my_evt;
1530Sstevel@tonic-gate 	/* see if we have more detail about the event */
1540Sstevel@tonic-gate 	if (dl_evt == EVT_OPEN)
1550Sstevel@tonic-gate 		*evt = TNFCTL_EVENT_DLOPEN;
1560Sstevel@tonic-gate 	else if (dl_evt == EVT_CLOSE)
1570Sstevel@tonic-gate 		*evt = TNFCTL_EVENT_DLCLOSE;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	return (TNFCTL_ERR_NONE);
1600Sstevel@tonic-gate }
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate /*
1630Sstevel@tonic-gate  * Continues target and waits for it to stop.
1640Sstevel@tonic-gate  *	warning: This routine returns TNFCTL_EVENT_DLOPEN for any kind of
1650Sstevel@tonic-gate  *	dl activity.  Up to the caller to determine the actual DL event.
1660Sstevel@tonic-gate  */
1670Sstevel@tonic-gate static tnfctl_errcode_t
_tnfctl_continue(tnfctl_handle_t * hndl,tnfctl_event_t * evt,sigset_t * oldmask,boolean_t watch_forks)1680Sstevel@tonic-gate _tnfctl_continue(tnfctl_handle_t *hndl, tnfctl_event_t *evt, sigset_t *oldmask,
1690Sstevel@tonic-gate     boolean_t watch_forks)
1700Sstevel@tonic-gate {
1710Sstevel@tonic-gate 	tnfctl_errcode_t	prexstat;
1720Sstevel@tonic-gate 	tnfctl_errcode_t	ret_prexstat = TNFCTL_ERR_NONE;
1730Sstevel@tonic-gate 	prb_status_t		prbstat, prbstat2;
1740Sstevel@tonic-gate 	prb_proc_ctl_t		*proc_p;
1750Sstevel@tonic-gate 	prb_proc_state_t 	state;
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	proc_p = hndl->proc_p;
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	/* set up state before we run process */
1800Sstevel@tonic-gate 	prexstat = enable_target_state(hndl, watch_forks);
1810Sstevel@tonic-gate 	if (prexstat)
1820Sstevel@tonic-gate 		return (prexstat);
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate again:
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate 	/* resume target */
1870Sstevel@tonic-gate 	prbstat = prb_proc_cont(proc_p);
1880Sstevel@tonic-gate 	if (prbstat) {
1890Sstevel@tonic-gate 		ret_prexstat = _tnfctl_map_to_errcode(prbstat);
1900Sstevel@tonic-gate 		goto end_of_func;
1910Sstevel@tonic-gate 	}
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	/* wait on target to stop (standby) */
1940Sstevel@tonic-gate 	prbstat = prb_proc_wait(proc_p, B_TRUE, oldmask);
1950Sstevel@tonic-gate 	if (prbstat) {
1960Sstevel@tonic-gate 		if (prbstat == EINTR) {
1970Sstevel@tonic-gate 			*evt = TNFCTL_EVENT_EINTR;
1980Sstevel@tonic-gate 			prbstat2 = prb_proc_stop(proc_p);
1990Sstevel@tonic-gate 			if (prbstat2) {
2000Sstevel@tonic-gate 				ret_prexstat = _tnfctl_map_to_errcode(prbstat2);
2010Sstevel@tonic-gate 				goto end_of_func;
2020Sstevel@tonic-gate 			}
2030Sstevel@tonic-gate 		} else if (prbstat == ENOENT) {
2040Sstevel@tonic-gate 			/* target process finished */
2050Sstevel@tonic-gate 			if (hndl->called_exit)
2060Sstevel@tonic-gate 				*evt = TNFCTL_EVENT_EXIT;
2070Sstevel@tonic-gate 			else
2080Sstevel@tonic-gate 				*evt = TNFCTL_EVENT_TARGGONE;
2090Sstevel@tonic-gate 			/* return directly - process no longer around */
2100Sstevel@tonic-gate 			return (TNFCTL_ERR_INTERNAL);
2110Sstevel@tonic-gate 		} else {
2120Sstevel@tonic-gate 			ret_prexstat = _tnfctl_map_to_errcode(prbstat);
2130Sstevel@tonic-gate 			goto end_of_func;
2140Sstevel@tonic-gate 		}
2150Sstevel@tonic-gate 	}
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	prbstat = prb_proc_state(proc_p, &state);
2180Sstevel@tonic-gate 	if (prbstat) {
2190Sstevel@tonic-gate 		ret_prexstat = _tnfctl_map_to_errcode(prbstat);
2200Sstevel@tonic-gate 		goto end_of_func;
2210Sstevel@tonic-gate 	}
2220Sstevel@tonic-gate 	if (state.ps_isbptfault) {
2230Sstevel@tonic-gate 		/* dlopen or dlclose */
2240Sstevel@tonic-gate 		prbstat = prb_rtld_advance(proc_p);
2250Sstevel@tonic-gate 		if (prbstat) {
2260Sstevel@tonic-gate 			ret_prexstat = _tnfctl_map_to_errcode(prbstat);
2270Sstevel@tonic-gate 			goto end_of_func;
2280Sstevel@tonic-gate 		}
2290Sstevel@tonic-gate 		/*
2300Sstevel@tonic-gate 		 * actually don't know if it is a dlopen or dlclose yet.
2310Sstevel@tonic-gate 		 * But, we return dlopen here.  Up to the caller to determine
2320Sstevel@tonic-gate 		 * which one it actually is.
2330Sstevel@tonic-gate 		 */
2340Sstevel@tonic-gate 		*evt = TNFCTL_EVENT_DLOPEN;
2350Sstevel@tonic-gate 	} else
2360Sstevel@tonic-gate 	    if (state.ps_issysentry) {
2370Sstevel@tonic-gate 		switch (state.ps_syscallnum) {
2380Sstevel@tonic-gate 		case SYS_execve:
2390Sstevel@tonic-gate 		    *evt = TNFCTL_EVENT_EXEC;
2400Sstevel@tonic-gate 		    ret_prexstat = TNFCTL_ERR_INTERNAL;
2410Sstevel@tonic-gate 		    break;
2420Sstevel@tonic-gate 		case SYS_exit:
2430Sstevel@tonic-gate 		    hndl->called_exit = B_TRUE;
2440Sstevel@tonic-gate 		    goto again;
2450Sstevel@tonic-gate 		default:
2460Sstevel@tonic-gate 		    break;
2470Sstevel@tonic-gate 		}
2480Sstevel@tonic-gate 	    } else if (state.ps_issysexit) {
2490Sstevel@tonic-gate 		    switch (state.ps_syscallnum) {
2500Sstevel@tonic-gate 		    case SYS_vfork:
2513235Sraf 		    case SYS_forksys:
2520Sstevel@tonic-gate 			*evt = TNFCTL_EVENT_FORK;
2530Sstevel@tonic-gate 			break;
2540Sstevel@tonic-gate 		    default:
2550Sstevel@tonic-gate 			break;
2560Sstevel@tonic-gate 		    }
2570Sstevel@tonic-gate 		}
2580Sstevel@tonic-gate end_of_func:
2590Sstevel@tonic-gate 	/*
2600Sstevel@tonic-gate 	 * disable all our sycall tracing and bpt setup in process when it
2610Sstevel@tonic-gate 	 * is stopped, so that even if the controlling process aborts,
2620Sstevel@tonic-gate 	 * the target could continue running
2630Sstevel@tonic-gate 	 */
2640Sstevel@tonic-gate 	prexstat = disable_target_state(hndl);
2650Sstevel@tonic-gate 	if (prexstat)
2660Sstevel@tonic-gate 		return (prexstat);
2670Sstevel@tonic-gate 	return (ret_prexstat);
2680Sstevel@tonic-gate }
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate /*
2710Sstevel@tonic-gate  * enable the system call tracing and dl activity tracing
2720Sstevel@tonic-gate  */
2730Sstevel@tonic-gate static tnfctl_errcode_t
enable_target_state(tnfctl_handle_t * hndl,boolean_t watch_forks)2740Sstevel@tonic-gate enable_target_state(tnfctl_handle_t *hndl, boolean_t watch_forks)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	prb_status_t	prbstat;
2770Sstevel@tonic-gate 	prb_proc_ctl_t	*proc_p;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	proc_p = hndl->proc_p;
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	/* trace exec */
2820Sstevel@tonic-gate 	prbstat = prb_proc_entry(proc_p, SYS_execve, PRB_SYS_ADD);
2830Sstevel@tonic-gate 	if (prbstat)
2840Sstevel@tonic-gate 		return (_tnfctl_map_to_errcode(prbstat));
2850Sstevel@tonic-gate 	/* trace exit */
2860Sstevel@tonic-gate 	prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_ADD);
2870Sstevel@tonic-gate 	if (prbstat)
2880Sstevel@tonic-gate 		return (_tnfctl_map_to_errcode(prbstat));
2890Sstevel@tonic-gate 	/* trace fork if the caller requests */
2900Sstevel@tonic-gate 	if (watch_forks) {
2910Sstevel@tonic-gate 		prbstat = prb_proc_exit(proc_p, SYS_vfork, PRB_SYS_ADD);
2920Sstevel@tonic-gate 		if (prbstat)
2930Sstevel@tonic-gate 			return (_tnfctl_map_to_errcode(prbstat));
2940Sstevel@tonic-gate 
2953235Sraf 		prbstat = prb_proc_exit(proc_p, SYS_forksys, PRB_SYS_ADD);
2963235Sraf 		if (prbstat)
2973235Sraf 			return (_tnfctl_map_to_errcode(prbstat));
2983235Sraf 
2990Sstevel@tonic-gate 		prbstat = prb_proc_setfork(proc_p, B_TRUE);
3000Sstevel@tonic-gate 		if (prbstat)
3010Sstevel@tonic-gate 			return (_tnfctl_map_to_errcode(prbstat));
3020Sstevel@tonic-gate 	}
3030Sstevel@tonic-gate 	/*
3040Sstevel@tonic-gate 	 * tracing flags for fork and exec will get unset when
3050Sstevel@tonic-gate 	 * process stops. see disable_target_state()
3060Sstevel@tonic-gate 	 */
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 	/* setup process to stop during dlopen() or dlclose() */
3090Sstevel@tonic-gate 	prbstat = prb_rtld_stalk(proc_p);
3100Sstevel@tonic-gate 	return (_tnfctl_map_to_errcode(prbstat));
3110Sstevel@tonic-gate }
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate /*
3140Sstevel@tonic-gate  * disable the system call tracing and dl activity tracing
3150Sstevel@tonic-gate  */
3160Sstevel@tonic-gate static tnfctl_errcode_t
disable_target_state(tnfctl_handle_t * hndl)3170Sstevel@tonic-gate disable_target_state(tnfctl_handle_t *hndl)
3180Sstevel@tonic-gate {
3190Sstevel@tonic-gate 	prb_status_t	prbstat;
3200Sstevel@tonic-gate 	prb_proc_ctl_t	*proc_p;
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 	proc_p = hndl->proc_p;
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 	/* remove the stalking breakpoint while the process is stopped */
3250Sstevel@tonic-gate 	prbstat = prb_rtld_unstalk(proc_p);
3260Sstevel@tonic-gate 	if (prbstat)
3270Sstevel@tonic-gate 		return (_tnfctl_map_to_errcode(prbstat));
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	/* remove the exec, exit and fork tracing while stopped */
3300Sstevel@tonic-gate 	prbstat = prb_proc_entry(proc_p, SYS_execve, PRB_SYS_DEL);
3310Sstevel@tonic-gate 	if (prbstat)
3320Sstevel@tonic-gate 		return (_tnfctl_map_to_errcode(prbstat));
3330Sstevel@tonic-gate 	prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_DEL);
3340Sstevel@tonic-gate 	if (prbstat)
3350Sstevel@tonic-gate 		return (_tnfctl_map_to_errcode(prbstat));
3360Sstevel@tonic-gate 	prbstat = prb_proc_exit(proc_p, SYS_vfork, PRB_SYS_DEL);
3370Sstevel@tonic-gate 	if (prbstat)
3380Sstevel@tonic-gate 	    return (_tnfctl_map_to_errcode(prbstat));
3393235Sraf 	prbstat = prb_proc_exit(proc_p, SYS_forksys, PRB_SYS_DEL);
3403235Sraf 	if (prbstat)
3413235Sraf 	    return (_tnfctl_map_to_errcode(prbstat));
3420Sstevel@tonic-gate 	prbstat = prb_proc_setfork(proc_p, B_FALSE);
3430Sstevel@tonic-gate 	if (prbstat)
3440Sstevel@tonic-gate 	    return (_tnfctl_map_to_errcode(prbstat));
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	return (TNFCTL_ERR_NONE);
3470Sstevel@tonic-gate }
348