xref: /onnv-gate/usr/src/lib/libpctx/common/libpctx.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
52235Skk112340  * Common Development and Distribution License (the "License").
62235Skk112340  * 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  * This file contains a set of generic routines for periodically
290Sstevel@tonic-gate  * sampling the state of another process, or tree of processes.
300Sstevel@tonic-gate  *
310Sstevel@tonic-gate  * It is built upon the infrastructure provided by libproc.
320Sstevel@tonic-gate  */
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include <sys/wait.h>
350Sstevel@tonic-gate #include <sys/syscall.h>
360Sstevel@tonic-gate #include <sys/time.h>
370Sstevel@tonic-gate #include <libproc.h>
380Sstevel@tonic-gate #include <stdio.h>
390Sstevel@tonic-gate #include <stdlib.h>
400Sstevel@tonic-gate #include <errno.h>
410Sstevel@tonic-gate #include <unistd.h>
420Sstevel@tonic-gate #include <signal.h>
430Sstevel@tonic-gate #include <string.h>
440Sstevel@tonic-gate #include <strings.h>
450Sstevel@tonic-gate #include <limits.h>
460Sstevel@tonic-gate #include <ctype.h>
470Sstevel@tonic-gate #include <libintl.h>
480Sstevel@tonic-gate #include <libcpc.h>
490Sstevel@tonic-gate #include <sys/cpc_impl.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #include "libpctx.h"
520Sstevel@tonic-gate 
530Sstevel@tonic-gate struct __pctx {
540Sstevel@tonic-gate 	pctx_errfn_t *errfn;
550Sstevel@tonic-gate 	struct ps_prochandle *Pr;
560Sstevel@tonic-gate 	void *uarg;
570Sstevel@tonic-gate 	pctx_sysc_execfn_t *exec;
580Sstevel@tonic-gate 	pctx_sysc_forkfn_t *fork;
590Sstevel@tonic-gate 	pctx_sysc_exitfn_t *exit;
600Sstevel@tonic-gate 	pctx_sysc_lwp_createfn_t *lwp_create;
610Sstevel@tonic-gate 	pctx_init_lwpfn_t *init_lwp;
620Sstevel@tonic-gate 	pctx_fini_lwpfn_t *fini_lwp;
630Sstevel@tonic-gate 	pctx_sysc_lwp_exitfn_t *lwp_exit;
640Sstevel@tonic-gate 	int verbose;
650Sstevel@tonic-gate 	int created;
660Sstevel@tonic-gate 	int sigblocked;
6711389SAlexander.Kolbasov@Sun.COM 	int terminate;
680Sstevel@tonic-gate 	sigset_t savedset;
690Sstevel@tonic-gate 	cpc_t *cpc;
700Sstevel@tonic-gate };
710Sstevel@tonic-gate 
720Sstevel@tonic-gate static void (*pctx_cpc_callback)(cpc_t *cpc, struct __pctx *pctx);
730Sstevel@tonic-gate 
740Sstevel@tonic-gate static void
pctx_default_errfn(const char * fn,const char * fmt,va_list ap)750Sstevel@tonic-gate pctx_default_errfn(const char *fn, const char *fmt, va_list ap)
760Sstevel@tonic-gate {
770Sstevel@tonic-gate 	(void) fprintf(stderr, "libpctx: pctx_%s: ", fn);
780Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, ap);
790Sstevel@tonic-gate }
800Sstevel@tonic-gate 
810Sstevel@tonic-gate /*PRINTFLIKE3*/
820Sstevel@tonic-gate static void
pctx_error(pctx_t * pctx,const char * fn,const char * fmt,...)830Sstevel@tonic-gate pctx_error(pctx_t *pctx, const char *fn, const char *fmt, ...)
840Sstevel@tonic-gate {
850Sstevel@tonic-gate 	va_list ap;
860Sstevel@tonic-gate 
870Sstevel@tonic-gate 	va_start(ap, fmt);
880Sstevel@tonic-gate 	pctx->errfn(fn, fmt, ap);
890Sstevel@tonic-gate 	va_end(ap);
900Sstevel@tonic-gate }
910Sstevel@tonic-gate 
920Sstevel@tonic-gate /*
930Sstevel@tonic-gate  * Create a new process and bind the user args for it
940Sstevel@tonic-gate  */
950Sstevel@tonic-gate pctx_t *
pctx_create(const char * filename,char * const * argv,void * arg,int verbose,pctx_errfn_t * errfn)960Sstevel@tonic-gate pctx_create(
970Sstevel@tonic-gate     const char *filename,
980Sstevel@tonic-gate     char *const *argv,
990Sstevel@tonic-gate     void *arg,
1000Sstevel@tonic-gate     int verbose,
1010Sstevel@tonic-gate     pctx_errfn_t *errfn)
1020Sstevel@tonic-gate {
1030Sstevel@tonic-gate 	static const char fn[] = "create";
1040Sstevel@tonic-gate 	int err;
1050Sstevel@tonic-gate 	pctx_t *pctx;
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	pctx = calloc(1, sizeof (*pctx));
1080Sstevel@tonic-gate 	pctx->uarg = arg;
1090Sstevel@tonic-gate 	pctx->verbose = verbose;
11011389SAlexander.Kolbasov@Sun.COM 	pctx->terminate = 0;
1110Sstevel@tonic-gate 	pctx->errfn = errfn ? errfn : pctx_default_errfn;
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	if ((pctx->Pr = Pcreate(filename, argv, &err, 0, 0)) == NULL) {
1140Sstevel@tonic-gate 		switch (err) {
1150Sstevel@tonic-gate 		case C_PERM:
1160Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot trace set-id or "
1170Sstevel@tonic-gate 			    "unreadable program '%s'\n"), filename);
1180Sstevel@tonic-gate 			break;
1190Sstevel@tonic-gate 		case C_LP64:
1200Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot control LP64 "
1210Sstevel@tonic-gate 			    "program '%s'\n"), filename);
1220Sstevel@tonic-gate 			break;
1230Sstevel@tonic-gate 		case C_NOEXEC:
1240Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot execute "
1250Sstevel@tonic-gate 			    "program '%s'\n"), filename);
1260Sstevel@tonic-gate 			break;
1270Sstevel@tonic-gate 		case C_NOENT:
1280Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot find"
1290Sstevel@tonic-gate 			    "program '%s'\n"), filename);
1300Sstevel@tonic-gate 			break;
1310Sstevel@tonic-gate 		case C_FORK:
1320Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot fork, "
1330Sstevel@tonic-gate 			    "program '%s'\n"), filename);
1340Sstevel@tonic-gate 			break;
1350Sstevel@tonic-gate 		default:
1360Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("%s, program '%s'\n"),
1370Sstevel@tonic-gate 			    Pcreate_error(err), filename);
1380Sstevel@tonic-gate 			break;
1390Sstevel@tonic-gate 		}
1400Sstevel@tonic-gate 		free(pctx);
1410Sstevel@tonic-gate 		return (NULL);
1420Sstevel@tonic-gate 	}
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 	if (Psysentry(pctx->Pr, SYS_exit, 1) == -1) {
1450Sstevel@tonic-gate 		pctx_error(pctx, fn,
1460Sstevel@tonic-gate 		    gettext("can't stop-on-exit() program '%s'\n"), filename);
1470Sstevel@tonic-gate 		Prelease(pctx->Pr, PRELEASE_KILL);
1480Sstevel@tonic-gate 		free(pctx);
1490Sstevel@tonic-gate 		return (NULL);
1500Sstevel@tonic-gate 	}
1510Sstevel@tonic-gate 	/*
1520Sstevel@tonic-gate 	 * Set kill-on-last-close so the controlled process
1530Sstevel@tonic-gate 	 * dies if we die.
1540Sstevel@tonic-gate 	 */
1550Sstevel@tonic-gate 	pctx->created = 1;
1560Sstevel@tonic-gate 	(void) Psetflags(pctx->Pr, PR_KLC);
1570Sstevel@tonic-gate 	(void) pctx_set_events(pctx, PCTX_NULL_EVENT);
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	return (pctx);
1600Sstevel@tonic-gate }
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate /*
1630Sstevel@tonic-gate  * Capture an existing process and bind the user args for it
1640Sstevel@tonic-gate  */
1650Sstevel@tonic-gate pctx_t *
pctx_capture(pid_t pid,void * arg,int verbose,pctx_errfn_t * errfn)1660Sstevel@tonic-gate pctx_capture(pid_t pid, void *arg, int verbose, pctx_errfn_t *errfn)
1670Sstevel@tonic-gate {
1680Sstevel@tonic-gate 	static const char fn[] = "capture";
1690Sstevel@tonic-gate 	int err;
1700Sstevel@tonic-gate 	pctx_t *pctx;
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 	pctx = calloc(1, sizeof (*pctx));
1730Sstevel@tonic-gate 	pctx->uarg = arg;
1740Sstevel@tonic-gate 	pctx->verbose = verbose;
1750Sstevel@tonic-gate 	pctx->errfn = errfn ? errfn : pctx_default_errfn;
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	if ((pctx->Pr = Pgrab(pid, 0, &err)) == NULL) {
1780Sstevel@tonic-gate 		switch (err) {
1790Sstevel@tonic-gate 		case G_NOPROC:
1800Sstevel@tonic-gate 			pctx_error(pctx, fn,
1810Sstevel@tonic-gate 			    gettext("pid %d doesn't exist\n"), (int)pid);
1820Sstevel@tonic-gate 			break;
1830Sstevel@tonic-gate 		case G_ZOMB:
1840Sstevel@tonic-gate 			pctx_error(pctx, fn,
1850Sstevel@tonic-gate 			    gettext("pid %d is a zombie\n"), (int)pid);
1860Sstevel@tonic-gate 			break;
1870Sstevel@tonic-gate 		case G_PERM:
1880Sstevel@tonic-gate 			pctx_error(pctx, fn,
1890Sstevel@tonic-gate 			    gettext("pid %d: permission denied\n"), (int)pid);
1900Sstevel@tonic-gate 			break;
1910Sstevel@tonic-gate 		case G_BUSY:
1920Sstevel@tonic-gate 			pctx_error(pctx, fn,
1930Sstevel@tonic-gate 			    gettext("pid %d is already being traced\n"),
1940Sstevel@tonic-gate 			    (int)pid);
1950Sstevel@tonic-gate 			break;
1960Sstevel@tonic-gate 		case G_SYS:
1970Sstevel@tonic-gate 			pctx_error(pctx, fn,
1980Sstevel@tonic-gate 			    gettext("pid %d is a system process\n"), (int)pid);
1990Sstevel@tonic-gate 			break;
2000Sstevel@tonic-gate 		case G_SELF:
2010Sstevel@tonic-gate 			pctx_error(pctx, fn,
2020Sstevel@tonic-gate 			    gettext("cannot capture self!\n"));
2030Sstevel@tonic-gate 			break;
2040Sstevel@tonic-gate 		case G_LP64:
2050Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot control LP64 "
2060Sstevel@tonic-gate 			    "process, pid %d\n"), (int)pid);
2070Sstevel@tonic-gate 			break;
2080Sstevel@tonic-gate 		default:
2090Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("%s: pid %d\n"),
2100Sstevel@tonic-gate 			    Pgrab_error(err), (int)pid);
2110Sstevel@tonic-gate 			break;
2120Sstevel@tonic-gate 		}
2130Sstevel@tonic-gate 		free(pctx);
2140Sstevel@tonic-gate 		return (NULL);
2150Sstevel@tonic-gate 	}
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	if (Psysentry(pctx->Pr, SYS_exit, 1) == -1) {
2180Sstevel@tonic-gate 		pctx_error(pctx, fn,
2190Sstevel@tonic-gate 		    gettext("can't stop-on-exit() pid %d\n"), (int)pid);
2200Sstevel@tonic-gate 		Prelease(pctx->Pr, PRELEASE_CLEAR);
2210Sstevel@tonic-gate 		free(pctx);
2220Sstevel@tonic-gate 		return (NULL);
2230Sstevel@tonic-gate 	}
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	/*
2260Sstevel@tonic-gate 	 * Set run-on-last-close so the controlled process
2270Sstevel@tonic-gate 	 * runs even if we die on a signal.  This is because
2280Sstevel@tonic-gate 	 * we grabbed an existing process - it would be impolite
2290Sstevel@tonic-gate 	 * to cause it to die if we exit prematurely.
2300Sstevel@tonic-gate 	 */
2310Sstevel@tonic-gate 	pctx->created = 0;
2320Sstevel@tonic-gate 	(void) Psetflags(pctx->Pr, PR_RLC);
2330Sstevel@tonic-gate 	(void) pctx_set_events(pctx, PCTX_NULL_EVENT);
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	return (pctx);
2360Sstevel@tonic-gate }
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate /*ARGSUSED*/
2390Sstevel@tonic-gate static void
default_void(pctx_t * pctx)2400Sstevel@tonic-gate default_void(pctx_t *pctx)
2410Sstevel@tonic-gate {}
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate /*ARGSUSED*/
2440Sstevel@tonic-gate static int
default_int(pctx_t * pctx)2450Sstevel@tonic-gate default_int(pctx_t *pctx)
2460Sstevel@tonic-gate {
2470Sstevel@tonic-gate 	return (0);
2480Sstevel@tonic-gate }
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate int
pctx_set_events(pctx_t * pctx,...)2510Sstevel@tonic-gate pctx_set_events(pctx_t *pctx, ...)
2520Sstevel@tonic-gate {
2530Sstevel@tonic-gate 	static const char fn[] = "set_events";
2540Sstevel@tonic-gate 	va_list pvar;
2550Sstevel@tonic-gate 	int error = 0;
2560Sstevel@tonic-gate 	pctx_event_t event;
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	va_start(pvar, pctx);
2590Sstevel@tonic-gate 	do {
2600Sstevel@tonic-gate 		switch (event = (pctx_event_t)va_arg(pvar, pctx_event_t)) {
2610Sstevel@tonic-gate 		case PCTX_NULL_EVENT:
2620Sstevel@tonic-gate 			break;
2630Sstevel@tonic-gate 		case PCTX_SYSC_EXEC_EVENT:
2640Sstevel@tonic-gate 			pctx->exec = (pctx_sysc_execfn_t *)
2650Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_execfn_t *);
2660Sstevel@tonic-gate 			break;
2670Sstevel@tonic-gate 		case PCTX_SYSC_FORK_EVENT:
2680Sstevel@tonic-gate 			pctx->fork = (pctx_sysc_forkfn_t *)
2690Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_forkfn_t *);
2700Sstevel@tonic-gate 			break;
2710Sstevel@tonic-gate 		case PCTX_SYSC_EXIT_EVENT:	/* always intercepted */
2720Sstevel@tonic-gate 			pctx->exit = (pctx_sysc_exitfn_t *)
2730Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_exitfn_t *);
2740Sstevel@tonic-gate 			break;
2750Sstevel@tonic-gate 		case PCTX_SYSC_LWP_CREATE_EVENT:
2760Sstevel@tonic-gate 			pctx->lwp_create = (pctx_sysc_lwp_createfn_t *)
2770Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_lwp_createfn_t *);
2780Sstevel@tonic-gate 			break;
2790Sstevel@tonic-gate 		case PCTX_INIT_LWP_EVENT:
2800Sstevel@tonic-gate 			pctx->init_lwp = (pctx_init_lwpfn_t *)
2810Sstevel@tonic-gate 			    va_arg(pvar, pctx_init_lwpfn_t *);
2820Sstevel@tonic-gate 			break;
2830Sstevel@tonic-gate 		case PCTX_FINI_LWP_EVENT:
2840Sstevel@tonic-gate 			pctx->fini_lwp = (pctx_fini_lwpfn_t *)
2850Sstevel@tonic-gate 			    va_arg(pvar, pctx_fini_lwpfn_t *);
2860Sstevel@tonic-gate 			break;
2870Sstevel@tonic-gate 		case PCTX_SYSC_LWP_EXIT_EVENT:
2880Sstevel@tonic-gate 			pctx->lwp_exit = (pctx_sysc_lwp_exitfn_t *)
2890Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_lwp_exitfn_t *);
2900Sstevel@tonic-gate 			break;
2910Sstevel@tonic-gate 		default:
2920Sstevel@tonic-gate 			pctx_error(pctx, fn,
2930Sstevel@tonic-gate 			    gettext("unknown event type %x\n"), event);
2940Sstevel@tonic-gate 			error = -1;
2950Sstevel@tonic-gate 			break;
2960Sstevel@tonic-gate 		}
2970Sstevel@tonic-gate 	} while (event != PCTX_NULL_EVENT && error == 0);
2980Sstevel@tonic-gate 	va_end(pvar);
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	if (error != 0)
3010Sstevel@tonic-gate 		return (error);
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	if (pctx->exec == NULL)
3040Sstevel@tonic-gate 		pctx->exec = (pctx_sysc_execfn_t *)default_int;
3050Sstevel@tonic-gate 	if (pctx->fork == NULL)
3060Sstevel@tonic-gate 		pctx->fork = (pctx_sysc_forkfn_t *)default_void;
3070Sstevel@tonic-gate 	if (pctx->exit == NULL)
3080Sstevel@tonic-gate 		pctx->exit = (pctx_sysc_exitfn_t *)default_void;
3090Sstevel@tonic-gate 	if (pctx->lwp_create == NULL)
3100Sstevel@tonic-gate 		pctx->lwp_create = (pctx_sysc_lwp_createfn_t *)default_int;
3110Sstevel@tonic-gate 	if (pctx->init_lwp == NULL)
3120Sstevel@tonic-gate 		pctx->init_lwp = (pctx_init_lwpfn_t *)default_int;
3130Sstevel@tonic-gate 	if (pctx->fini_lwp == NULL)
3140Sstevel@tonic-gate 		pctx->fini_lwp = (pctx_fini_lwpfn_t *)default_int;
3150Sstevel@tonic-gate 	if (pctx->lwp_exit == NULL)
3160Sstevel@tonic-gate 		pctx->lwp_exit = (pctx_sysc_lwp_exitfn_t *)default_int;
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	if (pctx->fork != (pctx_sysc_forkfn_t *)default_void) {
3190Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_vfork, 1);
3203235Sraf 		(void) Psysexit(pctx->Pr, SYS_forksys, 1);
3210Sstevel@tonic-gate 		if (Psetflags(pctx->Pr, PR_FORK) == -1)
3220Sstevel@tonic-gate 			error = -1;
3230Sstevel@tonic-gate 	} else {
3240Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_vfork, 0);
3253235Sraf 		(void) Psysexit(pctx->Pr, SYS_forksys, 0);
3260Sstevel@tonic-gate 		if (Punsetflags(pctx->Pr, PR_FORK) == -1)
3270Sstevel@tonic-gate 			error = -1;
3280Sstevel@tonic-gate 	}
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	/*
3310Sstevel@tonic-gate 	 * exec causes termination of all but the exec-ing lwp,
3320Sstevel@tonic-gate 	 * and resets the lwpid to one in the new address space.
3330Sstevel@tonic-gate 	 */
3340Sstevel@tonic-gate 	if (pctx->exec != (pctx_sysc_execfn_t *)default_int ||
3350Sstevel@tonic-gate 	    pctx->fini_lwp != (pctx_fini_lwpfn_t *)default_int ||
3360Sstevel@tonic-gate 	    pctx->init_lwp != (pctx_init_lwpfn_t *)default_int) {
3370Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_execve, 1);
3380Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_execve, 1);
3390Sstevel@tonic-gate 	} else {
3400Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_execve, 0);
3410Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_execve, 0);
3420Sstevel@tonic-gate 	}
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	(void) Psysexit(pctx->Pr, SYS_lwp_create,
3450Sstevel@tonic-gate 	    pctx->lwp_create != (pctx_sysc_lwp_createfn_t *)default_int ||
3460Sstevel@tonic-gate 	    pctx->init_lwp != (pctx_init_lwpfn_t *)default_int);
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	(void) Psysentry(pctx->Pr, SYS_lwp_exit,
3490Sstevel@tonic-gate 	    pctx->lwp_exit != (pctx_sysc_lwp_exitfn_t *)default_int ||
3500Sstevel@tonic-gate 	    pctx->fini_lwp != (pctx_fini_lwpfn_t *)default_int);
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	return (0);
3530Sstevel@tonic-gate }
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate static sigset_t termsig;
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate static void
__libpctx_init(void)3580Sstevel@tonic-gate __libpctx_init(void)
3590Sstevel@tonic-gate {
3600Sstevel@tonic-gate 	/*
3610Sstevel@tonic-gate 	 * Initialize the signal set used to shield ourselves from
3620Sstevel@tonic-gate 	 * death-by-terminal-signal while the agent lwp is running.
3630Sstevel@tonic-gate 	 */
3640Sstevel@tonic-gate 	(void) sigemptyset(&termsig);
3650Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGHUP);
3660Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGTERM);
3670Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGINT);
3680Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGQUIT);
3690Sstevel@tonic-gate }
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate #pragma init(__libpctx_init)
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate static void
pctx_begin_syscalls(pctx_t * pctx)3740Sstevel@tonic-gate pctx_begin_syscalls(pctx_t *pctx)
3750Sstevel@tonic-gate {
3760Sstevel@tonic-gate 	if (pctx->Pr == NULL)
3770Sstevel@tonic-gate 		return;
3780Sstevel@tonic-gate 	if (pctx->sigblocked++ == 0) {
3790Sstevel@tonic-gate 		(void) sigprocmask(SIG_BLOCK, &termsig, &pctx->savedset);
3800Sstevel@tonic-gate 		(void) Pcreate_agent(pctx->Pr);
3810Sstevel@tonic-gate 	}
3820Sstevel@tonic-gate }
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate static void
pctx_end_syscalls(pctx_t * pctx)3850Sstevel@tonic-gate pctx_end_syscalls(pctx_t *pctx)
3860Sstevel@tonic-gate {
3870Sstevel@tonic-gate 	if (pctx->Pr == NULL)
3880Sstevel@tonic-gate 		return;
3890Sstevel@tonic-gate 	if (--pctx->sigblocked == 0) {
3900Sstevel@tonic-gate 		(void) Pdestroy_agent(pctx->Pr);
3910Sstevel@tonic-gate 		(void) sigprocmask(SIG_SETMASK, &pctx->savedset, NULL);
3920Sstevel@tonic-gate 	}
3930Sstevel@tonic-gate }
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate /*
3960Sstevel@tonic-gate  * Iterate over the valid lwpids in the process, invoking the
3970Sstevel@tonic-gate  * action function on each one.
3980Sstevel@tonic-gate  */
3990Sstevel@tonic-gate static int
pctx_lwpiterate(pctx_t * pctx,int (* action)(pctx_t *,pid_t,id_t,void *))4000Sstevel@tonic-gate pctx_lwpiterate(pctx_t *pctx, int (*action)(pctx_t *, pid_t, id_t, void *))
4010Sstevel@tonic-gate {
4020Sstevel@tonic-gate 	const pstatus_t *pstatus;
4030Sstevel@tonic-gate 	char lstatus[64];
4040Sstevel@tonic-gate 	struct stat statb;
4050Sstevel@tonic-gate 	lwpstatus_t *lwps;
4060Sstevel@tonic-gate 	prheader_t *prh;
4070Sstevel@tonic-gate 	int fd, nlwp;
4080Sstevel@tonic-gate 	int ret = 0;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	if (action == (int (*)(pctx_t *, pid_t, id_t, void *))default_int)
4110Sstevel@tonic-gate 		return (0);
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	pstatus = Pstatus(pctx->Pr);
4140Sstevel@tonic-gate 	if (pstatus->pr_nlwp <= 1) {
4150Sstevel@tonic-gate 		pctx_begin_syscalls(pctx);
4160Sstevel@tonic-gate 		ret = action(pctx, pstatus->pr_pid, 1, pctx->uarg);
4170Sstevel@tonic-gate 		pctx_end_syscalls(pctx);
4180Sstevel@tonic-gate 		return (ret);
4190Sstevel@tonic-gate 	}
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 	(void) snprintf(lstatus, sizeof (lstatus),
4220Sstevel@tonic-gate 	    "/proc/%d/lstatus", (int)pstatus->pr_pid);
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	if ((fd = open(lstatus, O_RDONLY)) < 0 ||
4250Sstevel@tonic-gate 	    fstat(fd, &statb) != 0) {
4260Sstevel@tonic-gate 		if (fd >= 0)
4270Sstevel@tonic-gate 			(void) close(fd);
4280Sstevel@tonic-gate 		return (-1);
4290Sstevel@tonic-gate 	}
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	prh = malloc(statb.st_size);
4320Sstevel@tonic-gate 	if (read(fd, prh, statb.st_size) <
4330Sstevel@tonic-gate 	    sizeof (prheader_t) + sizeof (lwpstatus_t)) {
4340Sstevel@tonic-gate 		(void) close(fd);
4350Sstevel@tonic-gate 		free(prh);
4360Sstevel@tonic-gate 		return (-1);
4370Sstevel@tonic-gate 	}
4380Sstevel@tonic-gate 	(void) close(fd);
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate 	/* LINTED pointer cast may result in improper alignment */
4410Sstevel@tonic-gate 	lwps = (lwpstatus_t *)(prh + 1);
4420Sstevel@tonic-gate 	pctx_begin_syscalls(pctx);
4430Sstevel@tonic-gate 	for (nlwp = prh->pr_nent; nlwp > 0; nlwp--) {
4440Sstevel@tonic-gate 		if (action(pctx,
4450Sstevel@tonic-gate 		    pstatus->pr_pid, lwps->pr_lwpid, pctx->uarg) != 0)
4460Sstevel@tonic-gate 			ret = -1;
4470Sstevel@tonic-gate 		/* LINTED pointer cast may result in improper alignment */
4480Sstevel@tonic-gate 		lwps = (lwpstatus_t *)((char *)lwps + prh->pr_entsize);
4490Sstevel@tonic-gate 	}
4500Sstevel@tonic-gate 	pctx_end_syscalls(pctx);
4510Sstevel@tonic-gate 	free(prh);
4520Sstevel@tonic-gate 	return (ret);
4530Sstevel@tonic-gate }
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate /*
4560Sstevel@tonic-gate  * Free any associated state, but leave the process stopped if it
4570Sstevel@tonic-gate  * is still under our control.  (If it isn't under our control,
4580Sstevel@tonic-gate  * it should just run to completion when we do our last close)
4590Sstevel@tonic-gate  */
4600Sstevel@tonic-gate static void
pctx_free(pctx_t * pctx)4610Sstevel@tonic-gate pctx_free(pctx_t *pctx)
4620Sstevel@tonic-gate {
4630Sstevel@tonic-gate 	if (pctx->cpc != NULL && pctx_cpc_callback != NULL)
4640Sstevel@tonic-gate 		(*pctx_cpc_callback)(pctx->cpc, pctx);
4650Sstevel@tonic-gate 	if (pctx->Pr) {
4660Sstevel@tonic-gate 		Pfree(pctx->Pr);
4670Sstevel@tonic-gate 		pctx->Pr = NULL;
4680Sstevel@tonic-gate 	}
4690Sstevel@tonic-gate 	pctx->errfn = pctx_default_errfn;
4700Sstevel@tonic-gate }
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate /*
4730Sstevel@tonic-gate  * Completely release the process from our control and discard all our state
4740Sstevel@tonic-gate  */
4750Sstevel@tonic-gate void
pctx_release(pctx_t * pctx)4760Sstevel@tonic-gate pctx_release(pctx_t *pctx)
4770Sstevel@tonic-gate {
4780Sstevel@tonic-gate 	if (pctx->Pr) {
4790Sstevel@tonic-gate 		Prelease(pctx->Pr, PRELEASE_CLEAR);
4800Sstevel@tonic-gate 		pctx->Pr = NULL;
4810Sstevel@tonic-gate 	}
48211389SAlexander.Kolbasov@Sun.COM 
4830Sstevel@tonic-gate 	pctx_free(pctx);
4840Sstevel@tonic-gate 	bzero(pctx, sizeof (*pctx));
4850Sstevel@tonic-gate 	free(pctx);
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate static void
msincr(struct timeval * tv,uint_t msec)4890Sstevel@tonic-gate msincr(struct timeval *tv, uint_t msec)
4900Sstevel@tonic-gate {
4910Sstevel@tonic-gate 	tv->tv_sec += msec / MILLISEC;
4920Sstevel@tonic-gate 	tv->tv_usec += (msec % MILLISEC) * MILLISEC;
4930Sstevel@tonic-gate 	if (tv->tv_usec > MICROSEC) {
4940Sstevel@tonic-gate 		tv->tv_sec++;
4950Sstevel@tonic-gate 		tv->tv_usec -= MICROSEC;
4960Sstevel@tonic-gate 	}
4970Sstevel@tonic-gate }
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate static uint_t
msdiff(struct timeval * tva,struct timeval * tvb)5000Sstevel@tonic-gate msdiff(struct timeval *tva, struct timeval *tvb)
5010Sstevel@tonic-gate {
5020Sstevel@tonic-gate 	time_t sdiff = tva->tv_sec - tvb->tv_sec;
5030Sstevel@tonic-gate 	suseconds_t udiff = tva->tv_usec - tvb->tv_usec;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	if (sdiff < 0)
5060Sstevel@tonic-gate 		return (0);
5070Sstevel@tonic-gate 	if (udiff < 0) {
5080Sstevel@tonic-gate 		udiff += MICROSEC;
5090Sstevel@tonic-gate 		sdiff--;
5100Sstevel@tonic-gate 	}
5110Sstevel@tonic-gate 	if (sdiff < 0)
5120Sstevel@tonic-gate 		return (0);
5130Sstevel@tonic-gate 	if (sdiff >= (INT_MAX / MILLISEC))
5140Sstevel@tonic-gate 		return ((uint_t)INT_MAX);
5150Sstevel@tonic-gate 	return ((uint_t)(sdiff * MILLISEC + udiff / MILLISEC));
5160Sstevel@tonic-gate }
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate int
pctx_run(pctx_t * pctx,uint_t msec,uint_t nsamples,int (* tick)(pctx_t *,pid_t,id_t,void *))5190Sstevel@tonic-gate pctx_run(
5200Sstevel@tonic-gate 	pctx_t *pctx,
5210Sstevel@tonic-gate 	uint_t msec,
5220Sstevel@tonic-gate 	uint_t nsamples,
5230Sstevel@tonic-gate 	int (*tick)(pctx_t *, pid_t, id_t, void *))
5240Sstevel@tonic-gate {
5250Sstevel@tonic-gate 	static const char fn[] = "run";
5260Sstevel@tonic-gate 	struct timeval tvgoal, tvnow;
5270Sstevel@tonic-gate 	uint_t mswait = 0;
5280Sstevel@tonic-gate 	int running = 1;
5290Sstevel@tonic-gate 	const pstatus_t *pstatus;
5300Sstevel@tonic-gate 	psinfo_t psinfo;
5310Sstevel@tonic-gate 	void (*sigsaved)();
5320Sstevel@tonic-gate 	id_t lwpid;
5330Sstevel@tonic-gate 	pid_t pid = Pstatus(pctx->Pr)->pr_pid;
5340Sstevel@tonic-gate 	int pstate;
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	if (msec == 0)
5370Sstevel@tonic-gate 		nsamples = 0;
5380Sstevel@tonic-gate 	if (nsamples == 0)
5390Sstevel@tonic-gate 		nsamples = UINT_MAX;
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	/*
5420Sstevel@tonic-gate 	 * Casually discard any knowledge of the children we create
5430Sstevel@tonic-gate 	 */
5440Sstevel@tonic-gate 	sigsaved = signal(SIGCHLD, SIG_IGN);
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 	/*
5470Sstevel@tonic-gate 	 * Since we've just "discovered" this process which might have
5480Sstevel@tonic-gate 	 * been running for weeks, deliver some init_lwp events so
5490Sstevel@tonic-gate 	 * that our caller gets a handle on the process.
5500Sstevel@tonic-gate 	 */
5510Sstevel@tonic-gate 	if (pctx_lwpiterate(pctx, pctx->init_lwp) != 0) {
5520Sstevel@tonic-gate 		if (pctx->verbose)
5530Sstevel@tonic-gate 			pctx_error(pctx, fn,
5540Sstevel@tonic-gate 			    gettext("%d: lwp discovery failed\n"), (int)pid);
5550Sstevel@tonic-gate 		goto bailout;
5560Sstevel@tonic-gate 	}
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	if (msec != 0) {
5590Sstevel@tonic-gate 		/*
5600Sstevel@tonic-gate 		 * tvgoal represents the time at which the sample
5610Sstevel@tonic-gate 		 * should next be taken.
5620Sstevel@tonic-gate 		 */
5630Sstevel@tonic-gate 		(void) gettimeofday(&tvgoal, 0);
5640Sstevel@tonic-gate 		msincr(&tvgoal, msec);
5650Sstevel@tonic-gate 	}
5660Sstevel@tonic-gate 
5672235Skk112340 	/*
5682235Skk112340 	 * The event handling loop continues while running is 1.
5692235Skk112340 	 * running becomes 0 when either the controlled process has
5702235Skk112340 	 * exited successfully or the number of time samples has expired.
5712235Skk112340 	 * Otherwise, if an error has occurred, running becomes -1.
5722235Skk112340 	 */
57311389SAlexander.Kolbasov@Sun.COM 	while (running == 1 && !pctx->terminate) {
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 		if (Psetrun(pctx->Pr, 0, 0) != 0) {
5760Sstevel@tonic-gate 			if (pctx->verbose)
5770Sstevel@tonic-gate 				pctx_error(pctx, fn,
5780Sstevel@tonic-gate 				    gettext("%d: Psetrun\n"), (int)pid);
5790Sstevel@tonic-gate 			break;
5800Sstevel@tonic-gate 		}
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 		if (msec != 0) {
5830Sstevel@tonic-gate 			/*
5840Sstevel@tonic-gate 			 * This timing loop attempts to estimate the number
5850Sstevel@tonic-gate 			 * of milliseconds between our "goal" time (when
5860Sstevel@tonic-gate 			 * we should stop the process and run the tick
5870Sstevel@tonic-gate 			 * routine) and the current time.
5880Sstevel@tonic-gate 			 *
5890Sstevel@tonic-gate 			 * If we ever find ourselves running behind i.e. we
5900Sstevel@tonic-gate 			 * missed our goal, then we skip ahead to the next
5910Sstevel@tonic-gate 			 * goal instead.
5920Sstevel@tonic-gate 			 */
5930Sstevel@tonic-gate 			do {
5940Sstevel@tonic-gate 				(void) gettimeofday(&tvnow, 0);
5950Sstevel@tonic-gate 				if ((mswait = msdiff(&tvgoal, &tvnow)) == 0) {
5960Sstevel@tonic-gate 					msincr(&tvgoal, msec);
5970Sstevel@tonic-gate 					/*
5980Sstevel@tonic-gate 					 * Skip ahead to the next goal, unless
5990Sstevel@tonic-gate 					 * there is only one more sample left
6000Sstevel@tonic-gate 					 * to take.
6010Sstevel@tonic-gate 					 */
6020Sstevel@tonic-gate 					if (nsamples != 1)
6030Sstevel@tonic-gate 						nsamples--;
6040Sstevel@tonic-gate 				}
60511389SAlexander.Kolbasov@Sun.COM 			} while (mswait == 0 && !pctx->terminate);
6060Sstevel@tonic-gate 		}
6070Sstevel@tonic-gate 
60811389SAlexander.Kolbasov@Sun.COM 		if (pctx->terminate)
60911389SAlexander.Kolbasov@Sun.COM 			goto bailout;
61011389SAlexander.Kolbasov@Sun.COM 		else
61111389SAlexander.Kolbasov@Sun.COM 			(void) Pwait(pctx->Pr, mswait);
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate checkstate:
6140Sstevel@tonic-gate 		switch (pstate = Pstate(pctx->Pr)) {
6150Sstevel@tonic-gate 		case PS_RUN:
6160Sstevel@tonic-gate 			/*
6170Sstevel@tonic-gate 			 * Try again, but wait for up to 5 seconds.
6180Sstevel@tonic-gate 			 */
6190Sstevel@tonic-gate 			if (Pstop(pctx->Pr, 5 * MILLISEC) == -1 ||
6200Sstevel@tonic-gate 			    (pstate = Pstate(pctx->Pr)) != PS_STOP) {
6210Sstevel@tonic-gate 				pctx_error(pctx, fn,
6220Sstevel@tonic-gate 				    gettext("%d: won't stop\n"), (int)pid);
6230Sstevel@tonic-gate 			}
6240Sstevel@tonic-gate 			break;
6250Sstevel@tonic-gate 		case PS_STOP:
6260Sstevel@tonic-gate 			break;
6270Sstevel@tonic-gate 		case PS_LOST:
6280Sstevel@tonic-gate 			/*
6290Sstevel@tonic-gate 			 * Lost control - probably execed a setuid/setgid
6300Sstevel@tonic-gate 			 * executable.  Try and get control back again,
6310Sstevel@tonic-gate 			 * else bail ..
6320Sstevel@tonic-gate 			 */
6330Sstevel@tonic-gate 			(void) Preopen(pctx->Pr);
6340Sstevel@tonic-gate 			if ((pstate = Pstate(pctx->Pr)) != PS_LOST)
6350Sstevel@tonic-gate 				goto checkstate;
6360Sstevel@tonic-gate 			pctx_error(pctx, fn,
6370Sstevel@tonic-gate 			    gettext("%d: execed a program that cannot "
6380Sstevel@tonic-gate 			    "be tracked\n"), (int)pid);
6392235Skk112340 			running = -1;
6400Sstevel@tonic-gate 			break;
6410Sstevel@tonic-gate 		case PS_UNDEAD:
6420Sstevel@tonic-gate 		case PS_DEAD:
6430Sstevel@tonic-gate 			if (pctx->verbose)
6440Sstevel@tonic-gate 				pctx_error(pctx, fn,
6450Sstevel@tonic-gate 				    gettext("%d: process terminated\n"),
6460Sstevel@tonic-gate 				    (int)pid);
6472235Skk112340 			running = -1;
6480Sstevel@tonic-gate 			break;
6490Sstevel@tonic-gate 		default:
6500Sstevel@tonic-gate 			if (pctx->verbose)
6510Sstevel@tonic-gate 				pctx_error(pctx, fn,
6520Sstevel@tonic-gate 				    gettext("%d: process state 0x%x?\n"),
6530Sstevel@tonic-gate 				    (int)pid, pstate);
6540Sstevel@tonic-gate 			break;
6550Sstevel@tonic-gate 		}
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 		if (pstate != PS_STOP)
6580Sstevel@tonic-gate 			break;
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 		pstatus = Pstatus(pctx->Pr);
6610Sstevel@tonic-gate 		lwpid = pstatus->pr_lwp.pr_lwpid;
6620Sstevel@tonic-gate 		switch (pstatus->pr_lwp.pr_why) {
6630Sstevel@tonic-gate 		case PR_REQUESTED:
6640Sstevel@tonic-gate 			msincr(&tvgoal, msec);
6650Sstevel@tonic-gate 			if (pstatus->pr_flags & PR_VFORKP) {
6660Sstevel@tonic-gate 				/*
6670Sstevel@tonic-gate 				 * The process is in a vfork stupor until
6680Sstevel@tonic-gate 				 * its child releases it via an exec.
6690Sstevel@tonic-gate 				 * Don't sample it while it's in this state
6700Sstevel@tonic-gate 				 * - we won't be able to create the agent.
6710Sstevel@tonic-gate 				 */
6720Sstevel@tonic-gate 				break;
6730Sstevel@tonic-gate 			}
6740Sstevel@tonic-gate 			if (pctx_lwpiterate(pctx, tick) != 0)
6752235Skk112340 				running = -1;
6762235Skk112340 			if (running == 1 && --nsamples == 0)
6770Sstevel@tonic-gate 				running = 0;
6780Sstevel@tonic-gate 			break;
6790Sstevel@tonic-gate 		case PR_SYSENTRY:
6800Sstevel@tonic-gate 			switch (pstatus->pr_lwp.pr_what) {
6810Sstevel@tonic-gate 			case SYS_lwp_exit:
6820Sstevel@tonic-gate 				pctx_begin_syscalls(pctx);
6830Sstevel@tonic-gate 				(void) pctx->fini_lwp(pctx,
6840Sstevel@tonic-gate 				    pid, lwpid, pctx->uarg);
6850Sstevel@tonic-gate 				(void) pctx->lwp_exit(pctx,
6860Sstevel@tonic-gate 				    pid, lwpid, pctx->uarg);
6870Sstevel@tonic-gate 				pctx_end_syscalls(pctx);
6880Sstevel@tonic-gate 				break;
6890Sstevel@tonic-gate 			case SYS_exit:
6902235Skk112340 				if (pctx_lwpiterate(pctx, pctx->fini_lwp)
6912235Skk112340 				    != 0)
6922235Skk112340 					running = -1;
6930Sstevel@tonic-gate 				pctx->exit(pctx, pid, lwpid,
6940Sstevel@tonic-gate 				    (int)pstatus->pr_lwp.pr_sysarg[0],
6950Sstevel@tonic-gate 				    pctx->uarg);
6962235Skk112340 				if (running == 1)
6972235Skk112340 					running = 0;
6980Sstevel@tonic-gate 				break;
6990Sstevel@tonic-gate 			case SYS_execve:
7000Sstevel@tonic-gate 				(void) pctx_lwpiterate(pctx, pctx->fini_lwp);
7010Sstevel@tonic-gate 				break;
7020Sstevel@tonic-gate 			default:
7030Sstevel@tonic-gate 				pctx_error(pctx, fn,
7040Sstevel@tonic-gate 				    "warning - pid %d sysentry(%d)\n",
7050Sstevel@tonic-gate 				    (int)pid, pstatus->pr_lwp.pr_what);
7060Sstevel@tonic-gate 				break;
7070Sstevel@tonic-gate 			}
7080Sstevel@tonic-gate 			break;
7090Sstevel@tonic-gate 		case PR_SYSEXIT:
7100Sstevel@tonic-gate 			switch (pstatus->pr_lwp.pr_what) {
7110Sstevel@tonic-gate 			case SYS_execve:
7120Sstevel@tonic-gate 				if (pstatus->pr_lwp.pr_errno) {
7130Sstevel@tonic-gate 					/*
7140Sstevel@tonic-gate 					 * The exec failed completely.
7150Sstevel@tonic-gate 					 * Reinstate the lwps we fini'd
7160Sstevel@tonic-gate 					 * at exec entrance
7170Sstevel@tonic-gate 					 */
7182235Skk112340 					if (pctx_lwpiterate(pctx,
7192235Skk112340 					    pctx->init_lwp) == 0)
7202235Skk112340 						running = 1;
7212235Skk112340 					else
7222235Skk112340 						running = -1;
7230Sstevel@tonic-gate 					break;
7240Sstevel@tonic-gate 				}
7250Sstevel@tonic-gate 				if (pctx->exec == (pctx_sysc_execfn_t *)
7260Sstevel@tonic-gate 				    default_int) {
7270Sstevel@tonic-gate 					running = 0;
7280Sstevel@tonic-gate 					break;
7290Sstevel@tonic-gate 				}
7300Sstevel@tonic-gate 				(void) memcpy(&psinfo,
7310Sstevel@tonic-gate 				    Ppsinfo(pctx->Pr), sizeof (psinfo));
7320Sstevel@tonic-gate 				proc_unctrl_psinfo(&psinfo);
7330Sstevel@tonic-gate 				pctx_begin_syscalls(pctx);
7342235Skk112340 				if (pctx->exec(pctx, pid, lwpid,
7352235Skk112340 				    psinfo.pr_psargs, pctx->uarg) != 0)
7362235Skk112340 					running = -1;
7372235Skk112340 				if (running == 1 && pctx->init_lwp(pctx,
7382235Skk112340 				    pid, 1, pctx->uarg) != 0)
7392235Skk112340 					running = -1;
7400Sstevel@tonic-gate 				pctx_end_syscalls(pctx);
7410Sstevel@tonic-gate 				break;
7420Sstevel@tonic-gate 			case SYS_lwp_create:
7430Sstevel@tonic-gate 				if (pstatus->pr_lwp.pr_errno ||
7440Sstevel@tonic-gate 				    pstatus->pr_lwp.pr_rval1)
7450Sstevel@tonic-gate 					break;
7460Sstevel@tonic-gate 				pctx_begin_syscalls(pctx);
7472235Skk112340 				if (pctx->init_lwp(pctx, pid, lwpid,
7482235Skk112340 				    pctx->uarg) != 0)
7492235Skk112340 					running = -1;
7502235Skk112340 				if (running == 1 && pctx->lwp_create(pctx,
7512235Skk112340 				    pid, lwpid, pctx->uarg) != 0)
7522235Skk112340 					running = -1;
7530Sstevel@tonic-gate 				pctx_end_syscalls(pctx);
7540Sstevel@tonic-gate 				break;
7550Sstevel@tonic-gate 			case SYS_vfork:
7563235Sraf 			case SYS_forksys:
7570Sstevel@tonic-gate 				if (pstatus->pr_lwp.pr_errno)
7580Sstevel@tonic-gate 					break;
7590Sstevel@tonic-gate 				(void) fflush(NULL);
7600Sstevel@tonic-gate 				switch (fork1()) {
7610Sstevel@tonic-gate 					pid_t ppid;
7620Sstevel@tonic-gate 					int wascreated;
7630Sstevel@tonic-gate 					pctx_sysc_forkfn_t *forkfn;
7640Sstevel@tonic-gate 				case 0:
7650Sstevel@tonic-gate 					ppid = pid;
7660Sstevel@tonic-gate 					pid = pstatus->pr_lwp.pr_rval1;
7670Sstevel@tonic-gate 					wascreated = pctx->created;
7680Sstevel@tonic-gate 					forkfn = pctx->fork;
7690Sstevel@tonic-gate 					pctx_free(pctx);
7700Sstevel@tonic-gate 					pctx = pctx_capture(pid, pctx->uarg,
7710Sstevel@tonic-gate 					    pctx->verbose, pctx->errfn);
7720Sstevel@tonic-gate 					if (pctx != NULL) {
7730Sstevel@tonic-gate 						if (wascreated) {
7740Sstevel@tonic-gate 							/*
7750Sstevel@tonic-gate 							 * Set kill on last
7760Sstevel@tonic-gate 							 * close so -all-
7770Sstevel@tonic-gate 							 * children die.
7780Sstevel@tonic-gate 							 */
7790Sstevel@tonic-gate 							pctx->created = 1;
7800Sstevel@tonic-gate 							(void) Psetflags(
7810Sstevel@tonic-gate 							    pctx->Pr, PR_KLC);
7820Sstevel@tonic-gate 						}
7830Sstevel@tonic-gate 						(*forkfn)(pctx, ppid, pid,
7840Sstevel@tonic-gate 						    lwpid, pctx->uarg);
7850Sstevel@tonic-gate 						pctx_release(pctx);
7862235Skk112340 						_exit(0);
7872235Skk112340 					} else {
7882235Skk112340 						_exit(1);
7890Sstevel@tonic-gate 					}
7900Sstevel@tonic-gate 					/*NOTREACHED*/
7910Sstevel@tonic-gate 				case -1:
7920Sstevel@tonic-gate 					pctx_error(pctx, fn,
7930Sstevel@tonic-gate 					    "cannot follow pid %d: %s\n",
7940Sstevel@tonic-gate 					    (int)pstatus->pr_lwp.pr_rval1,
7950Sstevel@tonic-gate 					    strerror(errno));
7960Sstevel@tonic-gate 					break;
7970Sstevel@tonic-gate 				default:
7980Sstevel@tonic-gate 					break;
7990Sstevel@tonic-gate 				}
8000Sstevel@tonic-gate 				break;
8010Sstevel@tonic-gate 			default:
8020Sstevel@tonic-gate 				pctx_error(pctx, fn, gettext(
8030Sstevel@tonic-gate 				    "warning - pid %d sysexit(%d)\n"),
8040Sstevel@tonic-gate 				    (int)pid, pstatus->pr_lwp.pr_what);
8050Sstevel@tonic-gate 				break;
8060Sstevel@tonic-gate 			}
8070Sstevel@tonic-gate 			break;
8080Sstevel@tonic-gate 		case PR_SIGNALLED:
8090Sstevel@tonic-gate 			if (pctx->verbose)
8100Sstevel@tonic-gate 				pctx_error(pctx, fn,
8110Sstevel@tonic-gate 				    gettext("pid %d - signalled\n"), (int)pid);
8120Sstevel@tonic-gate 			break;
8130Sstevel@tonic-gate 		case PR_JOBCONTROL:
8140Sstevel@tonic-gate 			if (pctx->verbose)
8150Sstevel@tonic-gate 				pctx_error(pctx, fn,
8160Sstevel@tonic-gate 				    gettext("pid %d - job control stop\n"),
8170Sstevel@tonic-gate 				    (int)pid);
8182235Skk112340 			running = -1;
8190Sstevel@tonic-gate 			break;
8200Sstevel@tonic-gate 		case PR_FAULTED:
8210Sstevel@tonic-gate 			if (pctx->verbose)
8220Sstevel@tonic-gate 				pctx_error(pctx, fn,
8230Sstevel@tonic-gate 				    gettext("pid %d - faulted\n"), (int)pid);
8240Sstevel@tonic-gate 			break;
8250Sstevel@tonic-gate 		case PR_SUSPENDED:
8260Sstevel@tonic-gate 			if (pctx->verbose)
8270Sstevel@tonic-gate 				pctx_error(pctx, fn,
8280Sstevel@tonic-gate 				    gettext("pid %d - suspended\n"), (int)pid);
8290Sstevel@tonic-gate 			break;
8300Sstevel@tonic-gate 		case PR_CHECKPOINT:
8310Sstevel@tonic-gate 			if (pctx->verbose)
8320Sstevel@tonic-gate 				pctx_error(pctx, fn,
8330Sstevel@tonic-gate 				    gettext("pid %d - checkpoint\n"),
8340Sstevel@tonic-gate 				    (int)pid);
8350Sstevel@tonic-gate 			break;
8360Sstevel@tonic-gate 		default:
8370Sstevel@tonic-gate 			if (pctx->verbose)
8380Sstevel@tonic-gate 				pctx_error(pctx, fn,
8390Sstevel@tonic-gate 				    gettext("pid %d - reason %d\n"),
8400Sstevel@tonic-gate 				    (int)pid, pstatus->pr_lwp.pr_why);
8412235Skk112340 			running = -1;
8420Sstevel@tonic-gate 			break;
8430Sstevel@tonic-gate 		}
8440Sstevel@tonic-gate 	}
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate bailout:
8470Sstevel@tonic-gate 	(void) signal(SIGCHLD, sigsaved);
8482235Skk112340 
84911389SAlexander.Kolbasov@Sun.COM 	if (pctx->terminate)
85011389SAlexander.Kolbasov@Sun.COM 		return (0);
85111389SAlexander.Kolbasov@Sun.COM 
8522235Skk112340 	switch (running) {
8532235Skk112340 	case 0:
8540Sstevel@tonic-gate 		return (0);
8552235Skk112340 	case -1:
8562235Skk112340 		return (-1);
8572235Skk112340 	default:
8582235Skk112340 		pctx_error(pctx, fn, gettext("lost control of pid %d\n"),
8592235Skk112340 		    (int)pid);
8602235Skk112340 		pctx_free(pctx);
8612235Skk112340 		return (-1);
8622235Skk112340 	}
8630Sstevel@tonic-gate }
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate /*
8660Sstevel@tonic-gate  * Execute the private 'cpc' system call in the context of the
8670Sstevel@tonic-gate  * controlled process.
8680Sstevel@tonic-gate  */
8690Sstevel@tonic-gate int
__pctx_cpc(pctx_t * pctx,cpc_t * cpc,int cmd,id_t lwpid,void * data1,void * data2,void * data3,int bufsize)8700Sstevel@tonic-gate __pctx_cpc(pctx_t *pctx, cpc_t *cpc,
8710Sstevel@tonic-gate     int cmd, id_t lwpid, void *data1, void *data2, void *data3, int bufsize)
8720Sstevel@tonic-gate {
8730Sstevel@tonic-gate 	sysret_t rval;
8740Sstevel@tonic-gate 	argdes_t argd[5];
8750Sstevel@tonic-gate 	argdes_t *adp = &argd[0];
8760Sstevel@tonic-gate 	int error;
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	/*
8790Sstevel@tonic-gate 	 * Keep track of the relationship between cpc_t and pctx_t here.
8800Sstevel@tonic-gate 	 * We store the last cpc_t used by libpctx, so that when this pctx is
8810Sstevel@tonic-gate 	 * destroyed, libpctx can notify libcpc.
8820Sstevel@tonic-gate 	 */
88311389SAlexander.Kolbasov@Sun.COM 
8840Sstevel@tonic-gate 	if (pctx->cpc != NULL && pctx->cpc != cpc && pctx_cpc_callback != NULL)
8850Sstevel@tonic-gate 		(*pctx_cpc_callback)(pctx->cpc, pctx);
8860Sstevel@tonic-gate 	pctx->cpc = cpc;
8870Sstevel@tonic-gate 
8880Sstevel@tonic-gate 	/*
8890Sstevel@tonic-gate 	 * cmd and lwpid are passed in by value no matter what the command is.
8900Sstevel@tonic-gate 	 */
8910Sstevel@tonic-gate 	adp->arg_value = cmd;
8920Sstevel@tonic-gate 	adp->arg_object = NULL;
8930Sstevel@tonic-gate 	adp->arg_type = AT_BYVAL;
8940Sstevel@tonic-gate 	adp->arg_inout = AI_INPUT;
8950Sstevel@tonic-gate 	adp->arg_size = 0;
8960Sstevel@tonic-gate 	adp++;
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 	adp->arg_value = lwpid;
8990Sstevel@tonic-gate 	adp->arg_object = NULL;
9000Sstevel@tonic-gate 	adp->arg_type = AT_BYVAL;
9010Sstevel@tonic-gate 	adp->arg_inout = AI_INPUT;
9020Sstevel@tonic-gate 	adp->arg_size = 0;
9030Sstevel@tonic-gate 	adp++;
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 	switch (cmd) {
9060Sstevel@tonic-gate 	case CPC_BIND:
9070Sstevel@tonic-gate 		adp->arg_value = 0;
9080Sstevel@tonic-gate 		adp->arg_object = data1;
9090Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
9100Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
9110Sstevel@tonic-gate 		adp->arg_size = (size_t)data2;
9120Sstevel@tonic-gate 		adp++;
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 		adp->arg_value = (size_t)data2;
9150Sstevel@tonic-gate 		adp->arg_object = NULL;
9160Sstevel@tonic-gate 		adp->arg_type = AT_BYVAL;
9170Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
9180Sstevel@tonic-gate 		adp->arg_size = 0;
9190Sstevel@tonic-gate 		adp++;
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 		adp->arg_value = 0;
9220Sstevel@tonic-gate 		adp->arg_object = data3;
9230Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
9240Sstevel@tonic-gate 		adp->arg_inout = AI_INOUT;
9250Sstevel@tonic-gate 		adp->arg_size = sizeof (int);
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 		break;
9280Sstevel@tonic-gate 	case CPC_SAMPLE:
9290Sstevel@tonic-gate 		adp->arg_value = 0;
9300Sstevel@tonic-gate 		adp->arg_object = data1;
9310Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
9320Sstevel@tonic-gate 		adp->arg_inout = AI_OUTPUT;
9330Sstevel@tonic-gate 		adp->arg_size = bufsize;
9340Sstevel@tonic-gate 		adp++;
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate 		adp->arg_value = 0;
9370Sstevel@tonic-gate 		adp->arg_object = data2;
9380Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
9390Sstevel@tonic-gate 		adp->arg_inout = AI_OUTPUT;
9400Sstevel@tonic-gate 		adp->arg_size = sizeof (hrtime_t);
9410Sstevel@tonic-gate 		adp++;
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate 		adp->arg_value = 0;
9440Sstevel@tonic-gate 		adp->arg_object = data3;
9450Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
9460Sstevel@tonic-gate 		adp->arg_inout = AI_OUTPUT;
9470Sstevel@tonic-gate 		adp->arg_size = sizeof (uint64_t);
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate 		break;
9500Sstevel@tonic-gate 	default:
9510Sstevel@tonic-gate 		adp->arg_value = 0;
9520Sstevel@tonic-gate 		adp->arg_object = 0;
9530Sstevel@tonic-gate 		adp->arg_type = AT_BYVAL;
9540Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
9550Sstevel@tonic-gate 		adp->arg_size = 0;
9560Sstevel@tonic-gate 		adp++;
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 		adp->arg_value = 0;
9590Sstevel@tonic-gate 		adp->arg_object = 0;
9600Sstevel@tonic-gate 		adp->arg_type = AT_BYVAL;
9610Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
9620Sstevel@tonic-gate 		adp->arg_size = 0;
9630Sstevel@tonic-gate 		adp++;
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 		adp->arg_value = 0;
9660Sstevel@tonic-gate 		adp->arg_object = 0;
9670Sstevel@tonic-gate 		adp->arg_type = AT_BYVAL;
9680Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
9690Sstevel@tonic-gate 		adp->arg_size = 0;
9700Sstevel@tonic-gate 
9710Sstevel@tonic-gate 		break;
9720Sstevel@tonic-gate 	}
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 	error = Psyscall(pctx->Pr, &rval, SYS_cpc, 5, &argd[0]);
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate 	if (error) {
9770Sstevel@tonic-gate 		errno = error > 0 ? error : ENOSYS;
9780Sstevel@tonic-gate 		return (-1);
9790Sstevel@tonic-gate 	}
9800Sstevel@tonic-gate 	return (rval.sys_rval1);
9810Sstevel@tonic-gate }
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate /*
9840Sstevel@tonic-gate  * libcpc-private hook used to register a callback. The callback is used to
9850Sstevel@tonic-gate  * notify libcpc when a pctx handle is invalidated.
9860Sstevel@tonic-gate  */
9870Sstevel@tonic-gate void
__pctx_cpc_register_callback(void (* arg)(struct __cpc *,struct __pctx *))9880Sstevel@tonic-gate __pctx_cpc_register_callback(void (*arg)(struct __cpc *, struct __pctx *))
9890Sstevel@tonic-gate {
9900Sstevel@tonic-gate 	pctx_cpc_callback = arg;
9910Sstevel@tonic-gate }
99211389SAlexander.Kolbasov@Sun.COM 
99311389SAlexander.Kolbasov@Sun.COM /*
99411389SAlexander.Kolbasov@Sun.COM  * Tell pctx_run to bail out immediately
99511389SAlexander.Kolbasov@Sun.COM  */
99611389SAlexander.Kolbasov@Sun.COM void
pctx_terminate(struct __pctx * pctx)99711389SAlexander.Kolbasov@Sun.COM pctx_terminate(struct __pctx *pctx)
99811389SAlexander.Kolbasov@Sun.COM {
99911389SAlexander.Kolbasov@Sun.COM 	pctx->terminate = 1;
100011389SAlexander.Kolbasov@Sun.COM }
1001