xref: /onnv-gate/usr/src/lib/libpctx/common/libpctx.c (revision 2235:f47c7004764d)
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
5*2235Skk112340  * Common Development and Distribution License (the "License").
6*2235Skk112340  * 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  */
210Sstevel@tonic-gate /*
22*2235Skk112340  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * This file contains a set of generic routines for periodically
300Sstevel@tonic-gate  * sampling the state of another process, or tree of processes.
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  * It is built upon the infrastructure provided by libproc.
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include <sys/wait.h>
360Sstevel@tonic-gate #include <sys/syscall.h>
370Sstevel@tonic-gate #include <sys/time.h>
380Sstevel@tonic-gate #include <libproc.h>
390Sstevel@tonic-gate #include <stdio.h>
400Sstevel@tonic-gate #include <stdlib.h>
410Sstevel@tonic-gate #include <errno.h>
420Sstevel@tonic-gate #include <unistd.h>
430Sstevel@tonic-gate #include <signal.h>
440Sstevel@tonic-gate #include <string.h>
450Sstevel@tonic-gate #include <strings.h>
460Sstevel@tonic-gate #include <limits.h>
470Sstevel@tonic-gate #include <ctype.h>
480Sstevel@tonic-gate #include <libintl.h>
490Sstevel@tonic-gate #include <libcpc.h>
500Sstevel@tonic-gate #include <sys/cpc_impl.h>
510Sstevel@tonic-gate 
520Sstevel@tonic-gate #include "libpctx.h"
530Sstevel@tonic-gate 
540Sstevel@tonic-gate struct __pctx {
550Sstevel@tonic-gate 	pctx_errfn_t *errfn;
560Sstevel@tonic-gate 	struct ps_prochandle *Pr;
570Sstevel@tonic-gate 	void *uarg;
580Sstevel@tonic-gate 	pctx_sysc_execfn_t *exec;
590Sstevel@tonic-gate 	pctx_sysc_forkfn_t *fork;
600Sstevel@tonic-gate 	pctx_sysc_exitfn_t *exit;
610Sstevel@tonic-gate 	pctx_sysc_lwp_createfn_t *lwp_create;
620Sstevel@tonic-gate 	pctx_init_lwpfn_t *init_lwp;
630Sstevel@tonic-gate 	pctx_fini_lwpfn_t *fini_lwp;
640Sstevel@tonic-gate 	pctx_sysc_lwp_exitfn_t *lwp_exit;
650Sstevel@tonic-gate 	int verbose;
660Sstevel@tonic-gate 	int created;
670Sstevel@tonic-gate 	int sigblocked;
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
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
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 *
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;
1100Sstevel@tonic-gate 	pctx->errfn = errfn ? errfn : pctx_default_errfn;
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	if ((pctx->Pr = Pcreate(filename, argv, &err, 0, 0)) == NULL) {
1130Sstevel@tonic-gate 		switch (err) {
1140Sstevel@tonic-gate 		case C_PERM:
1150Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot trace set-id or "
1160Sstevel@tonic-gate 			    "unreadable program '%s'\n"), filename);
1170Sstevel@tonic-gate 			break;
1180Sstevel@tonic-gate 		case C_LP64:
1190Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot control LP64 "
1200Sstevel@tonic-gate 			    "program '%s'\n"), filename);
1210Sstevel@tonic-gate 			break;
1220Sstevel@tonic-gate 		case C_NOEXEC:
1230Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot execute "
1240Sstevel@tonic-gate 			    "program '%s'\n"), filename);
1250Sstevel@tonic-gate 			break;
1260Sstevel@tonic-gate 		case C_NOENT:
1270Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot find"
1280Sstevel@tonic-gate 			    "program '%s'\n"), filename);
1290Sstevel@tonic-gate 			break;
1300Sstevel@tonic-gate 		case C_FORK:
1310Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot fork, "
1320Sstevel@tonic-gate 			    "program '%s'\n"), filename);
1330Sstevel@tonic-gate 			break;
1340Sstevel@tonic-gate 		default:
1350Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("%s, program '%s'\n"),
1360Sstevel@tonic-gate 			    Pcreate_error(err), filename);
1370Sstevel@tonic-gate 			break;
1380Sstevel@tonic-gate 		}
1390Sstevel@tonic-gate 		free(pctx);
1400Sstevel@tonic-gate 		return (NULL);
1410Sstevel@tonic-gate 	}
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	if (Psysentry(pctx->Pr, SYS_exit, 1) == -1) {
1440Sstevel@tonic-gate 		pctx_error(pctx, fn,
1450Sstevel@tonic-gate 		    gettext("can't stop-on-exit() program '%s'\n"), filename);
1460Sstevel@tonic-gate 		Prelease(pctx->Pr, PRELEASE_KILL);
1470Sstevel@tonic-gate 		free(pctx);
1480Sstevel@tonic-gate 		return (NULL);
1490Sstevel@tonic-gate 	}
1500Sstevel@tonic-gate 	/*
1510Sstevel@tonic-gate 	 * Set kill-on-last-close so the controlled process
1520Sstevel@tonic-gate 	 * dies if we die.
1530Sstevel@tonic-gate 	 */
1540Sstevel@tonic-gate 	pctx->created = 1;
1550Sstevel@tonic-gate 	(void) Psetflags(pctx->Pr, PR_KLC);
1560Sstevel@tonic-gate 	(void) pctx_set_events(pctx, PCTX_NULL_EVENT);
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 	return (pctx);
1590Sstevel@tonic-gate }
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate /*
1620Sstevel@tonic-gate  * Capture an existing process and bind the user args for it
1630Sstevel@tonic-gate  */
1640Sstevel@tonic-gate pctx_t *
1650Sstevel@tonic-gate pctx_capture(pid_t pid, void *arg, int verbose, pctx_errfn_t *errfn)
1660Sstevel@tonic-gate {
1670Sstevel@tonic-gate 	static const char fn[] = "capture";
1680Sstevel@tonic-gate 	int err;
1690Sstevel@tonic-gate 	pctx_t *pctx;
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	pctx = calloc(1, sizeof (*pctx));
1720Sstevel@tonic-gate 	pctx->uarg = arg;
1730Sstevel@tonic-gate 	pctx->verbose = verbose;
1740Sstevel@tonic-gate 	pctx->errfn = errfn ? errfn : pctx_default_errfn;
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 	if ((pctx->Pr = Pgrab(pid, 0, &err)) == NULL) {
1770Sstevel@tonic-gate 		switch (err) {
1780Sstevel@tonic-gate 		case G_NOPROC:
1790Sstevel@tonic-gate 			pctx_error(pctx, fn,
1800Sstevel@tonic-gate 			    gettext("pid %d doesn't exist\n"), (int)pid);
1810Sstevel@tonic-gate 			break;
1820Sstevel@tonic-gate 		case G_ZOMB:
1830Sstevel@tonic-gate 			pctx_error(pctx, fn,
1840Sstevel@tonic-gate 			    gettext("pid %d is a zombie\n"), (int)pid);
1850Sstevel@tonic-gate 			break;
1860Sstevel@tonic-gate 		case G_PERM:
1870Sstevel@tonic-gate 			pctx_error(pctx, fn,
1880Sstevel@tonic-gate 			    gettext("pid %d: permission denied\n"), (int)pid);
1890Sstevel@tonic-gate 			break;
1900Sstevel@tonic-gate 		case G_BUSY:
1910Sstevel@tonic-gate 			pctx_error(pctx, fn,
1920Sstevel@tonic-gate 			    gettext("pid %d is already being traced\n"),
1930Sstevel@tonic-gate 			    (int)pid);
1940Sstevel@tonic-gate 			break;
1950Sstevel@tonic-gate 		case G_SYS:
1960Sstevel@tonic-gate 			pctx_error(pctx, fn,
1970Sstevel@tonic-gate 			    gettext("pid %d is a system process\n"), (int)pid);
1980Sstevel@tonic-gate 			break;
1990Sstevel@tonic-gate 		case G_SELF:
2000Sstevel@tonic-gate 			pctx_error(pctx, fn,
2010Sstevel@tonic-gate 			    gettext("cannot capture self!\n"));
2020Sstevel@tonic-gate 			break;
2030Sstevel@tonic-gate 		case G_LP64:
2040Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot control LP64 "
2050Sstevel@tonic-gate 			    "process, pid %d\n"), (int)pid);
2060Sstevel@tonic-gate 			break;
2070Sstevel@tonic-gate 		default:
2080Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("%s: pid %d\n"),
2090Sstevel@tonic-gate 			    Pgrab_error(err), (int)pid);
2100Sstevel@tonic-gate 			break;
2110Sstevel@tonic-gate 		}
2120Sstevel@tonic-gate 		free(pctx);
2130Sstevel@tonic-gate 		return (NULL);
2140Sstevel@tonic-gate 	}
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 	if (Psysentry(pctx->Pr, SYS_exit, 1) == -1) {
2170Sstevel@tonic-gate 		pctx_error(pctx, fn,
2180Sstevel@tonic-gate 		    gettext("can't stop-on-exit() pid %d\n"), (int)pid);
2190Sstevel@tonic-gate 		Prelease(pctx->Pr, PRELEASE_CLEAR);
2200Sstevel@tonic-gate 		free(pctx);
2210Sstevel@tonic-gate 		return (NULL);
2220Sstevel@tonic-gate 	}
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	/*
2250Sstevel@tonic-gate 	 * Set run-on-last-close so the controlled process
2260Sstevel@tonic-gate 	 * runs even if we die on a signal.  This is because
2270Sstevel@tonic-gate 	 * we grabbed an existing process - it would be impolite
2280Sstevel@tonic-gate 	 * to cause it to die if we exit prematurely.
2290Sstevel@tonic-gate 	 */
2300Sstevel@tonic-gate 	pctx->created = 0;
2310Sstevel@tonic-gate 	(void) Psetflags(pctx->Pr, PR_RLC);
2320Sstevel@tonic-gate 	(void) pctx_set_events(pctx, PCTX_NULL_EVENT);
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	return (pctx);
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate /*ARGSUSED*/
2380Sstevel@tonic-gate static void
2390Sstevel@tonic-gate default_void(pctx_t *pctx)
2400Sstevel@tonic-gate {}
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate /*ARGSUSED*/
2430Sstevel@tonic-gate static int
2440Sstevel@tonic-gate default_int(pctx_t *pctx)
2450Sstevel@tonic-gate {
2460Sstevel@tonic-gate 	return (0);
2470Sstevel@tonic-gate }
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate int
2500Sstevel@tonic-gate pctx_set_events(pctx_t *pctx, ...)
2510Sstevel@tonic-gate {
2520Sstevel@tonic-gate 	static const char fn[] = "set_events";
2530Sstevel@tonic-gate 	va_list pvar;
2540Sstevel@tonic-gate 	int error = 0;
2550Sstevel@tonic-gate 	pctx_event_t event;
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	va_start(pvar, pctx);
2580Sstevel@tonic-gate 	do {
2590Sstevel@tonic-gate 		switch (event = (pctx_event_t)va_arg(pvar, pctx_event_t)) {
2600Sstevel@tonic-gate 		case PCTX_NULL_EVENT:
2610Sstevel@tonic-gate 			break;
2620Sstevel@tonic-gate 		case PCTX_SYSC_EXEC_EVENT:
2630Sstevel@tonic-gate 			pctx->exec = (pctx_sysc_execfn_t *)
2640Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_execfn_t *);
2650Sstevel@tonic-gate 			break;
2660Sstevel@tonic-gate 		case PCTX_SYSC_FORK_EVENT:
2670Sstevel@tonic-gate 			pctx->fork = (pctx_sysc_forkfn_t *)
2680Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_forkfn_t *);
2690Sstevel@tonic-gate 			break;
2700Sstevel@tonic-gate 		case PCTX_SYSC_EXIT_EVENT:	/* always intercepted */
2710Sstevel@tonic-gate 			pctx->exit = (pctx_sysc_exitfn_t *)
2720Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_exitfn_t *);
2730Sstevel@tonic-gate 			break;
2740Sstevel@tonic-gate 		case PCTX_SYSC_LWP_CREATE_EVENT:
2750Sstevel@tonic-gate 			pctx->lwp_create = (pctx_sysc_lwp_createfn_t *)
2760Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_lwp_createfn_t *);
2770Sstevel@tonic-gate 			break;
2780Sstevel@tonic-gate 		case PCTX_INIT_LWP_EVENT:
2790Sstevel@tonic-gate 			pctx->init_lwp = (pctx_init_lwpfn_t *)
2800Sstevel@tonic-gate 			    va_arg(pvar, pctx_init_lwpfn_t *);
2810Sstevel@tonic-gate 			break;
2820Sstevel@tonic-gate 		case PCTX_FINI_LWP_EVENT:
2830Sstevel@tonic-gate 			pctx->fini_lwp = (pctx_fini_lwpfn_t *)
2840Sstevel@tonic-gate 			    va_arg(pvar, pctx_fini_lwpfn_t *);
2850Sstevel@tonic-gate 			break;
2860Sstevel@tonic-gate 		case PCTX_SYSC_LWP_EXIT_EVENT:
2870Sstevel@tonic-gate 			pctx->lwp_exit = (pctx_sysc_lwp_exitfn_t *)
2880Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_lwp_exitfn_t *);
2890Sstevel@tonic-gate 			break;
2900Sstevel@tonic-gate 		default:
2910Sstevel@tonic-gate 			pctx_error(pctx, fn,
2920Sstevel@tonic-gate 			    gettext("unknown event type %x\n"), event);
2930Sstevel@tonic-gate 			error = -1;
2940Sstevel@tonic-gate 			break;
2950Sstevel@tonic-gate 		}
2960Sstevel@tonic-gate 	} while (event != PCTX_NULL_EVENT && error == 0);
2970Sstevel@tonic-gate 	va_end(pvar);
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	if (error != 0)
3000Sstevel@tonic-gate 		return (error);
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	if (pctx->exec == NULL)
3030Sstevel@tonic-gate 		pctx->exec = (pctx_sysc_execfn_t *)default_int;
3040Sstevel@tonic-gate 	if (pctx->fork == NULL)
3050Sstevel@tonic-gate 		pctx->fork = (pctx_sysc_forkfn_t *)default_void;
3060Sstevel@tonic-gate 	if (pctx->exit == NULL)
3070Sstevel@tonic-gate 		pctx->exit = (pctx_sysc_exitfn_t *)default_void;
3080Sstevel@tonic-gate 	if (pctx->lwp_create == NULL)
3090Sstevel@tonic-gate 		pctx->lwp_create = (pctx_sysc_lwp_createfn_t *)default_int;
3100Sstevel@tonic-gate 	if (pctx->init_lwp == NULL)
3110Sstevel@tonic-gate 		pctx->init_lwp = (pctx_init_lwpfn_t *)default_int;
3120Sstevel@tonic-gate 	if (pctx->fini_lwp == NULL)
3130Sstevel@tonic-gate 		pctx->fini_lwp = (pctx_fini_lwpfn_t *)default_int;
3140Sstevel@tonic-gate 	if (pctx->lwp_exit == NULL)
3150Sstevel@tonic-gate 		pctx->lwp_exit = (pctx_sysc_lwp_exitfn_t *)default_int;
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	if (pctx->fork != (pctx_sysc_forkfn_t *)default_void) {
3180Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_forkall, 1);
3190Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_vfork, 1);
3200Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_fork1, 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_forkall, 0);
3250Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_vfork, 0);
3260Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_fork1, 0);
3270Sstevel@tonic-gate 		if (Punsetflags(pctx->Pr, PR_FORK) == -1)
3280Sstevel@tonic-gate 			error = -1;
3290Sstevel@tonic-gate 	}
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	/*
3320Sstevel@tonic-gate 	 * exec causes termination of all but the exec-ing lwp,
3330Sstevel@tonic-gate 	 * and resets the lwpid to one in the new address space.
3340Sstevel@tonic-gate 	 */
3350Sstevel@tonic-gate 	if (pctx->exec != (pctx_sysc_execfn_t *)default_int ||
3360Sstevel@tonic-gate 	    pctx->fini_lwp != (pctx_fini_lwpfn_t *)default_int ||
3370Sstevel@tonic-gate 	    pctx->init_lwp != (pctx_init_lwpfn_t *)default_int) {
3380Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_exec, 1);
3390Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_execve, 1);
3400Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_exec, 1);
3410Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_execve, 1);
3420Sstevel@tonic-gate 	} else {
3430Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_exec, 0);
3440Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_execve, 0);
3450Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_exec, 0);
3460Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_execve, 0);
3470Sstevel@tonic-gate 	}
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	(void) Psysexit(pctx->Pr, SYS_lwp_create,
3500Sstevel@tonic-gate 	    pctx->lwp_create != (pctx_sysc_lwp_createfn_t *)default_int ||
3510Sstevel@tonic-gate 	    pctx->init_lwp != (pctx_init_lwpfn_t *)default_int);
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	(void) Psysentry(pctx->Pr, SYS_lwp_exit,
3540Sstevel@tonic-gate 	    pctx->lwp_exit != (pctx_sysc_lwp_exitfn_t *)default_int ||
3550Sstevel@tonic-gate 	    pctx->fini_lwp != (pctx_fini_lwpfn_t *)default_int);
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 	return (0);
3580Sstevel@tonic-gate }
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate static sigset_t termsig;
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate static void
3630Sstevel@tonic-gate __libpctx_init(void)
3640Sstevel@tonic-gate {
3650Sstevel@tonic-gate 	/*
3660Sstevel@tonic-gate 	 * Initialize the signal set used to shield ourselves from
3670Sstevel@tonic-gate 	 * death-by-terminal-signal while the agent lwp is running.
3680Sstevel@tonic-gate 	 */
3690Sstevel@tonic-gate 	(void) sigemptyset(&termsig);
3700Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGHUP);
3710Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGTERM);
3720Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGINT);
3730Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGQUIT);
3740Sstevel@tonic-gate }
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate #pragma init(__libpctx_init)
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate static void
3790Sstevel@tonic-gate pctx_begin_syscalls(pctx_t *pctx)
3800Sstevel@tonic-gate {
3810Sstevel@tonic-gate 	if (pctx->Pr == NULL)
3820Sstevel@tonic-gate 		return;
3830Sstevel@tonic-gate 	if (pctx->sigblocked++ == 0) {
3840Sstevel@tonic-gate 		(void) sigprocmask(SIG_BLOCK, &termsig, &pctx->savedset);
3850Sstevel@tonic-gate 		(void) Pcreate_agent(pctx->Pr);
3860Sstevel@tonic-gate 	}
3870Sstevel@tonic-gate }
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate static void
3900Sstevel@tonic-gate pctx_end_syscalls(pctx_t *pctx)
3910Sstevel@tonic-gate {
3920Sstevel@tonic-gate 	if (pctx->Pr == NULL)
3930Sstevel@tonic-gate 		return;
3940Sstevel@tonic-gate 	if (--pctx->sigblocked == 0) {
3950Sstevel@tonic-gate 		(void) Pdestroy_agent(pctx->Pr);
3960Sstevel@tonic-gate 		(void) sigprocmask(SIG_SETMASK, &pctx->savedset, NULL);
3970Sstevel@tonic-gate 	}
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate /*
4010Sstevel@tonic-gate  * Iterate over the valid lwpids in the process, invoking the
4020Sstevel@tonic-gate  * action function on each one.
4030Sstevel@tonic-gate  */
4040Sstevel@tonic-gate static int
4050Sstevel@tonic-gate pctx_lwpiterate(pctx_t *pctx, int (*action)(pctx_t *, pid_t, id_t, void *))
4060Sstevel@tonic-gate {
4070Sstevel@tonic-gate 	const pstatus_t *pstatus;
4080Sstevel@tonic-gate 	char lstatus[64];
4090Sstevel@tonic-gate 	struct stat statb;
4100Sstevel@tonic-gate 	lwpstatus_t *lwps;
4110Sstevel@tonic-gate 	prheader_t *prh;
4120Sstevel@tonic-gate 	int fd, nlwp;
4130Sstevel@tonic-gate 	int ret = 0;
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	if (action == (int (*)(pctx_t *, pid_t, id_t, void *))default_int)
4160Sstevel@tonic-gate 		return (0);
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 	pstatus = Pstatus(pctx->Pr);
4190Sstevel@tonic-gate 	if (pstatus->pr_nlwp <= 1) {
4200Sstevel@tonic-gate 		pctx_begin_syscalls(pctx);
4210Sstevel@tonic-gate 		ret = action(pctx, pstatus->pr_pid, 1, pctx->uarg);
4220Sstevel@tonic-gate 		pctx_end_syscalls(pctx);
4230Sstevel@tonic-gate 		return (ret);
4240Sstevel@tonic-gate 	}
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	(void) snprintf(lstatus, sizeof (lstatus),
4270Sstevel@tonic-gate 	    "/proc/%d/lstatus", (int)pstatus->pr_pid);
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	if ((fd = open(lstatus, O_RDONLY)) < 0 ||
4300Sstevel@tonic-gate 	    fstat(fd, &statb) != 0) {
4310Sstevel@tonic-gate 		if (fd >= 0)
4320Sstevel@tonic-gate 			(void) close(fd);
4330Sstevel@tonic-gate 		return (-1);
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	prh = malloc(statb.st_size);
4370Sstevel@tonic-gate 	if (read(fd, prh, statb.st_size) <
4380Sstevel@tonic-gate 	    sizeof (prheader_t) + sizeof (lwpstatus_t)) {
4390Sstevel@tonic-gate 		(void) close(fd);
4400Sstevel@tonic-gate 		free(prh);
4410Sstevel@tonic-gate 		return (-1);
4420Sstevel@tonic-gate 	}
4430Sstevel@tonic-gate 	(void) close(fd);
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	/* LINTED pointer cast may result in improper alignment */
4460Sstevel@tonic-gate 	lwps = (lwpstatus_t *)(prh + 1);
4470Sstevel@tonic-gate 	pctx_begin_syscalls(pctx);
4480Sstevel@tonic-gate 	for (nlwp = prh->pr_nent; nlwp > 0; nlwp--) {
4490Sstevel@tonic-gate 		if (action(pctx,
4500Sstevel@tonic-gate 		    pstatus->pr_pid, lwps->pr_lwpid, pctx->uarg) != 0)
4510Sstevel@tonic-gate 			ret = -1;
4520Sstevel@tonic-gate 		/* LINTED pointer cast may result in improper alignment */
4530Sstevel@tonic-gate 		lwps = (lwpstatus_t *)((char *)lwps + prh->pr_entsize);
4540Sstevel@tonic-gate 	}
4550Sstevel@tonic-gate 	pctx_end_syscalls(pctx);
4560Sstevel@tonic-gate 	free(prh);
4570Sstevel@tonic-gate 	return (ret);
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate /*
4610Sstevel@tonic-gate  * Free any associated state, but leave the process stopped if it
4620Sstevel@tonic-gate  * is still under our control.  (If it isn't under our control,
4630Sstevel@tonic-gate  * it should just run to completion when we do our last close)
4640Sstevel@tonic-gate  */
4650Sstevel@tonic-gate static void
4660Sstevel@tonic-gate pctx_free(pctx_t *pctx)
4670Sstevel@tonic-gate {
4680Sstevel@tonic-gate 	if (pctx->cpc != NULL && pctx_cpc_callback != NULL)
4690Sstevel@tonic-gate 		(*pctx_cpc_callback)(pctx->cpc, pctx);
4700Sstevel@tonic-gate 	if (pctx->Pr) {
4710Sstevel@tonic-gate 		Pfree(pctx->Pr);
4720Sstevel@tonic-gate 		pctx->Pr = NULL;
4730Sstevel@tonic-gate 	}
4740Sstevel@tonic-gate 	pctx->errfn = pctx_default_errfn;
4750Sstevel@tonic-gate }
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate /*
4780Sstevel@tonic-gate  * Completely release the process from our control and discard all our state
4790Sstevel@tonic-gate  */
4800Sstevel@tonic-gate void
4810Sstevel@tonic-gate pctx_release(pctx_t *pctx)
4820Sstevel@tonic-gate {
4830Sstevel@tonic-gate 	if (pctx->Pr) {
4840Sstevel@tonic-gate 		Prelease(pctx->Pr, PRELEASE_CLEAR);
4850Sstevel@tonic-gate 		pctx->Pr = NULL;
4860Sstevel@tonic-gate 	}
4870Sstevel@tonic-gate 	pctx_free(pctx);
4880Sstevel@tonic-gate 	bzero(pctx, sizeof (*pctx));
4890Sstevel@tonic-gate 	free(pctx);
4900Sstevel@tonic-gate }
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate static void
4930Sstevel@tonic-gate msincr(struct timeval *tv, uint_t msec)
4940Sstevel@tonic-gate {
4950Sstevel@tonic-gate 	tv->tv_sec += msec / MILLISEC;
4960Sstevel@tonic-gate 	tv->tv_usec += (msec % MILLISEC) * MILLISEC;
4970Sstevel@tonic-gate 	if (tv->tv_usec > MICROSEC) {
4980Sstevel@tonic-gate 		tv->tv_sec++;
4990Sstevel@tonic-gate 		tv->tv_usec -= MICROSEC;
5000Sstevel@tonic-gate 	}
5010Sstevel@tonic-gate }
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate static uint_t
5040Sstevel@tonic-gate msdiff(struct timeval *tva, struct timeval *tvb)
5050Sstevel@tonic-gate {
5060Sstevel@tonic-gate 	time_t sdiff = tva->tv_sec - tvb->tv_sec;
5070Sstevel@tonic-gate 	suseconds_t udiff = tva->tv_usec - tvb->tv_usec;
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	if (sdiff < 0)
5100Sstevel@tonic-gate 		return (0);
5110Sstevel@tonic-gate 	if (udiff < 0) {
5120Sstevel@tonic-gate 		udiff += MICROSEC;
5130Sstevel@tonic-gate 		sdiff--;
5140Sstevel@tonic-gate 	}
5150Sstevel@tonic-gate 	if (sdiff < 0)
5160Sstevel@tonic-gate 		return (0);
5170Sstevel@tonic-gate 	if (sdiff >= (INT_MAX / MILLISEC))
5180Sstevel@tonic-gate 		return ((uint_t)INT_MAX);
5190Sstevel@tonic-gate 	return ((uint_t)(sdiff * MILLISEC + udiff / MILLISEC));
5200Sstevel@tonic-gate }
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate int
5230Sstevel@tonic-gate pctx_run(
5240Sstevel@tonic-gate 	pctx_t *pctx,
5250Sstevel@tonic-gate 	uint_t msec,
5260Sstevel@tonic-gate 	uint_t nsamples,
5270Sstevel@tonic-gate 	int (*tick)(pctx_t *, pid_t, id_t, void *))
5280Sstevel@tonic-gate {
5290Sstevel@tonic-gate 	static const char fn[] = "run";
5300Sstevel@tonic-gate 	struct timeval tvgoal, tvnow;
5310Sstevel@tonic-gate 	uint_t mswait = 0;
5320Sstevel@tonic-gate 	int running = 1;
5330Sstevel@tonic-gate 	const pstatus_t *pstatus;
5340Sstevel@tonic-gate 	psinfo_t psinfo;
5350Sstevel@tonic-gate 	void (*sigsaved)();
5360Sstevel@tonic-gate 	id_t lwpid;
5370Sstevel@tonic-gate 	pid_t pid = Pstatus(pctx->Pr)->pr_pid;
5380Sstevel@tonic-gate 	int pstate;
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	if (msec == 0)
5410Sstevel@tonic-gate 		nsamples = 0;
5420Sstevel@tonic-gate 	if (nsamples == 0)
5430Sstevel@tonic-gate 		nsamples = UINT_MAX;
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	/*
5460Sstevel@tonic-gate 	 * Casually discard any knowledge of the children we create
5470Sstevel@tonic-gate 	 */
5480Sstevel@tonic-gate 	sigsaved = signal(SIGCHLD, SIG_IGN);
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 	/*
5510Sstevel@tonic-gate 	 * Since we've just "discovered" this process which might have
5520Sstevel@tonic-gate 	 * been running for weeks, deliver some init_lwp events so
5530Sstevel@tonic-gate 	 * that our caller gets a handle on the process.
5540Sstevel@tonic-gate 	 */
5550Sstevel@tonic-gate 	if (pctx_lwpiterate(pctx, pctx->init_lwp) != 0) {
5560Sstevel@tonic-gate 		if (pctx->verbose)
5570Sstevel@tonic-gate 			pctx_error(pctx, fn,
5580Sstevel@tonic-gate 			    gettext("%d: lwp discovery failed\n"), (int)pid);
5590Sstevel@tonic-gate 		goto bailout;
5600Sstevel@tonic-gate 	}
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	if (msec != 0) {
5630Sstevel@tonic-gate 		/*
5640Sstevel@tonic-gate 		 * tvgoal represents the time at which the sample
5650Sstevel@tonic-gate 		 * should next be taken.
5660Sstevel@tonic-gate 		 */
5670Sstevel@tonic-gate 		(void) gettimeofday(&tvgoal, 0);
5680Sstevel@tonic-gate 		msincr(&tvgoal, msec);
5690Sstevel@tonic-gate 	}
5700Sstevel@tonic-gate 
571*2235Skk112340 	/*
572*2235Skk112340 	 * The event handling loop continues while running is 1.
573*2235Skk112340 	 * running becomes 0 when either the controlled process has
574*2235Skk112340 	 * exited successfully or the number of time samples has expired.
575*2235Skk112340 	 * Otherwise, if an error has occurred, running becomes -1.
576*2235Skk112340 	 */
577*2235Skk112340 	while (running == 1) {
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 		if (Psetrun(pctx->Pr, 0, 0) != 0) {
5800Sstevel@tonic-gate 			if (pctx->verbose)
5810Sstevel@tonic-gate 				pctx_error(pctx, fn,
5820Sstevel@tonic-gate 				    gettext("%d: Psetrun\n"), (int)pid);
5830Sstevel@tonic-gate 			break;
5840Sstevel@tonic-gate 		}
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 		if (msec != 0) {
5870Sstevel@tonic-gate 			/*
5880Sstevel@tonic-gate 			 * This timing loop attempts to estimate the number
5890Sstevel@tonic-gate 			 * of milliseconds between our "goal" time (when
5900Sstevel@tonic-gate 			 * we should stop the process and run the tick
5910Sstevel@tonic-gate 			 * routine) and the current time.
5920Sstevel@tonic-gate 			 *
5930Sstevel@tonic-gate 			 * If we ever find ourselves running behind i.e. we
5940Sstevel@tonic-gate 			 * missed our goal, then we skip ahead to the next
5950Sstevel@tonic-gate 			 * goal instead.
5960Sstevel@tonic-gate 			 */
5970Sstevel@tonic-gate 			do {
5980Sstevel@tonic-gate 				(void) gettimeofday(&tvnow, 0);
5990Sstevel@tonic-gate 				if ((mswait = msdiff(&tvgoal, &tvnow)) == 0) {
6000Sstevel@tonic-gate 					msincr(&tvgoal, msec);
6010Sstevel@tonic-gate 					/*
6020Sstevel@tonic-gate 					 * Skip ahead to the next goal, unless
6030Sstevel@tonic-gate 					 * there is only one more sample left
6040Sstevel@tonic-gate 					 * to take.
6050Sstevel@tonic-gate 					 */
6060Sstevel@tonic-gate 					if (nsamples != 1)
6070Sstevel@tonic-gate 						nsamples--;
6080Sstevel@tonic-gate 				}
6090Sstevel@tonic-gate 			} while (mswait == 0);
6100Sstevel@tonic-gate 		}
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 		(void) Pwait(pctx->Pr, mswait);
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate checkstate:
6150Sstevel@tonic-gate 		switch (pstate = Pstate(pctx->Pr)) {
6160Sstevel@tonic-gate 		case PS_RUN:
6170Sstevel@tonic-gate 			/*
6180Sstevel@tonic-gate 			 * Try again, but wait for up to 5 seconds.
6190Sstevel@tonic-gate 			 */
6200Sstevel@tonic-gate 			if (Pstop(pctx->Pr, 5 * MILLISEC) == -1 ||
6210Sstevel@tonic-gate 			    (pstate = Pstate(pctx->Pr)) != PS_STOP) {
6220Sstevel@tonic-gate 				pctx_error(pctx, fn,
6230Sstevel@tonic-gate 				    gettext("%d: won't stop\n"), (int)pid);
6240Sstevel@tonic-gate 			}
6250Sstevel@tonic-gate 			break;
6260Sstevel@tonic-gate 		case PS_STOP:
6270Sstevel@tonic-gate 			break;
6280Sstevel@tonic-gate 		case PS_LOST:
6290Sstevel@tonic-gate 			/*
6300Sstevel@tonic-gate 			 * Lost control - probably execed a setuid/setgid
6310Sstevel@tonic-gate 			 * executable.  Try and get control back again,
6320Sstevel@tonic-gate 			 * else bail ..
6330Sstevel@tonic-gate 			 */
6340Sstevel@tonic-gate 			(void) Preopen(pctx->Pr);
6350Sstevel@tonic-gate 			if ((pstate = Pstate(pctx->Pr)) != PS_LOST)
6360Sstevel@tonic-gate 				goto checkstate;
6370Sstevel@tonic-gate 			pctx_error(pctx, fn,
6380Sstevel@tonic-gate 			    gettext("%d: execed a program that cannot "
6390Sstevel@tonic-gate 			    "be tracked\n"), (int)pid);
640*2235Skk112340 			running = -1;
6410Sstevel@tonic-gate 			break;
6420Sstevel@tonic-gate 		case PS_UNDEAD:
6430Sstevel@tonic-gate 		case PS_DEAD:
6440Sstevel@tonic-gate 			if (pctx->verbose)
6450Sstevel@tonic-gate 				pctx_error(pctx, fn,
6460Sstevel@tonic-gate 				    gettext("%d: process terminated\n"),
6470Sstevel@tonic-gate 				    (int)pid);
648*2235Skk112340 			running = -1;
6490Sstevel@tonic-gate 			break;
6500Sstevel@tonic-gate 		default:
6510Sstevel@tonic-gate 			if (pctx->verbose)
6520Sstevel@tonic-gate 				pctx_error(pctx, fn,
6530Sstevel@tonic-gate 				    gettext("%d: process state 0x%x?\n"),
6540Sstevel@tonic-gate 				    (int)pid, pstate);
6550Sstevel@tonic-gate 			break;
6560Sstevel@tonic-gate 		}
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 		if (pstate != PS_STOP)
6590Sstevel@tonic-gate 			break;
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 		pstatus = Pstatus(pctx->Pr);
6620Sstevel@tonic-gate 		lwpid = pstatus->pr_lwp.pr_lwpid;
6630Sstevel@tonic-gate 		switch (pstatus->pr_lwp.pr_why) {
6640Sstevel@tonic-gate 		case PR_REQUESTED:
6650Sstevel@tonic-gate 			msincr(&tvgoal, msec);
6660Sstevel@tonic-gate 			if (pstatus->pr_flags & PR_VFORKP) {
6670Sstevel@tonic-gate 				/*
6680Sstevel@tonic-gate 				 * The process is in a vfork stupor until
6690Sstevel@tonic-gate 				 * its child releases it via an exec.
6700Sstevel@tonic-gate 				 * Don't sample it while it's in this state
6710Sstevel@tonic-gate 				 * - we won't be able to create the agent.
6720Sstevel@tonic-gate 				 */
6730Sstevel@tonic-gate 				break;
6740Sstevel@tonic-gate 			}
6750Sstevel@tonic-gate 			if (pctx_lwpiterate(pctx, tick) != 0)
676*2235Skk112340 				running = -1;
677*2235Skk112340 			if (running == 1 && --nsamples == 0)
6780Sstevel@tonic-gate 				running = 0;
6790Sstevel@tonic-gate 			break;
6800Sstevel@tonic-gate 		case PR_SYSENTRY:
6810Sstevel@tonic-gate 			switch (pstatus->pr_lwp.pr_what) {
6820Sstevel@tonic-gate 			case SYS_lwp_exit:
6830Sstevel@tonic-gate 				pctx_begin_syscalls(pctx);
6840Sstevel@tonic-gate 				(void) pctx->fini_lwp(pctx,
6850Sstevel@tonic-gate 				    pid, lwpid, pctx->uarg);
6860Sstevel@tonic-gate 				(void) pctx->lwp_exit(pctx,
6870Sstevel@tonic-gate 				    pid, lwpid, pctx->uarg);
6880Sstevel@tonic-gate 				pctx_end_syscalls(pctx);
6890Sstevel@tonic-gate 				break;
6900Sstevel@tonic-gate 			case SYS_exit:
691*2235Skk112340 				if (pctx_lwpiterate(pctx, pctx->fini_lwp)
692*2235Skk112340 				    != 0)
693*2235Skk112340 					running = -1;
6940Sstevel@tonic-gate 				pctx->exit(pctx, pid, lwpid,
6950Sstevel@tonic-gate 				    (int)pstatus->pr_lwp.pr_sysarg[0],
6960Sstevel@tonic-gate 				    pctx->uarg);
697*2235Skk112340 				if (running == 1)
698*2235Skk112340 					running = 0;
6990Sstevel@tonic-gate 				break;
7000Sstevel@tonic-gate 			case SYS_exec:
7010Sstevel@tonic-gate 			case SYS_execve:
7020Sstevel@tonic-gate 				(void) pctx_lwpiterate(pctx, pctx->fini_lwp);
7030Sstevel@tonic-gate 				break;
7040Sstevel@tonic-gate 			default:
7050Sstevel@tonic-gate 				pctx_error(pctx, fn,
7060Sstevel@tonic-gate 				    "warning - pid %d sysentry(%d)\n",
7070Sstevel@tonic-gate 				    (int)pid, pstatus->pr_lwp.pr_what);
7080Sstevel@tonic-gate 				break;
7090Sstevel@tonic-gate 			}
7100Sstevel@tonic-gate 			break;
7110Sstevel@tonic-gate 		case PR_SYSEXIT:
7120Sstevel@tonic-gate 			switch (pstatus->pr_lwp.pr_what) {
7130Sstevel@tonic-gate 			case SYS_exec:
7140Sstevel@tonic-gate 			case SYS_execve:
7150Sstevel@tonic-gate 				if (pstatus->pr_lwp.pr_errno) {
7160Sstevel@tonic-gate 					/*
7170Sstevel@tonic-gate 					 * The exec failed completely.
7180Sstevel@tonic-gate 					 * Reinstate the lwps we fini'd
7190Sstevel@tonic-gate 					 * at exec entrance
7200Sstevel@tonic-gate 					 */
721*2235Skk112340 					if (pctx_lwpiterate(pctx,
722*2235Skk112340 					    pctx->init_lwp) == 0)
723*2235Skk112340 						running = 1;
724*2235Skk112340 					else
725*2235Skk112340 						running = -1;
7260Sstevel@tonic-gate 					break;
7270Sstevel@tonic-gate 				}
7280Sstevel@tonic-gate 				if (pctx->exec == (pctx_sysc_execfn_t *)
7290Sstevel@tonic-gate 				    default_int) {
7300Sstevel@tonic-gate 					running = 0;
7310Sstevel@tonic-gate 					break;
7320Sstevel@tonic-gate 				}
7330Sstevel@tonic-gate 				(void) memcpy(&psinfo,
7340Sstevel@tonic-gate 				    Ppsinfo(pctx->Pr), sizeof (psinfo));
7350Sstevel@tonic-gate 				proc_unctrl_psinfo(&psinfo);
7360Sstevel@tonic-gate 				pctx_begin_syscalls(pctx);
737*2235Skk112340 				if (pctx->exec(pctx, pid, lwpid,
738*2235Skk112340 				    psinfo.pr_psargs, pctx->uarg) != 0)
739*2235Skk112340 					running = -1;
740*2235Skk112340 				if (running == 1 && pctx->init_lwp(pctx,
741*2235Skk112340 				    pid, 1, pctx->uarg) != 0)
742*2235Skk112340 					running = -1;
7430Sstevel@tonic-gate 				pctx_end_syscalls(pctx);
7440Sstevel@tonic-gate 				break;
7450Sstevel@tonic-gate 			case SYS_lwp_create:
7460Sstevel@tonic-gate 				if (pstatus->pr_lwp.pr_errno ||
7470Sstevel@tonic-gate 				    pstatus->pr_lwp.pr_rval1)
7480Sstevel@tonic-gate 					break;
7490Sstevel@tonic-gate 				pctx_begin_syscalls(pctx);
750*2235Skk112340 				if (pctx->init_lwp(pctx, pid, lwpid,
751*2235Skk112340 				    pctx->uarg) != 0)
752*2235Skk112340 					running = -1;
753*2235Skk112340 				if (running == 1 && pctx->lwp_create(pctx,
754*2235Skk112340 				    pid, lwpid, pctx->uarg) != 0)
755*2235Skk112340 					running = -1;
7560Sstevel@tonic-gate 				pctx_end_syscalls(pctx);
7570Sstevel@tonic-gate 				break;
7580Sstevel@tonic-gate 			case SYS_forkall:
7590Sstevel@tonic-gate 			case SYS_vfork:
7600Sstevel@tonic-gate 			case SYS_fork1:
7610Sstevel@tonic-gate 				if (pstatus->pr_lwp.pr_errno)
7620Sstevel@tonic-gate 					break;
7630Sstevel@tonic-gate 				(void) fflush(NULL);
7640Sstevel@tonic-gate 				switch (fork1()) {
7650Sstevel@tonic-gate 					pid_t ppid;
7660Sstevel@tonic-gate 					int wascreated;
7670Sstevel@tonic-gate 					pctx_sysc_forkfn_t *forkfn;
7680Sstevel@tonic-gate 				case 0:
7690Sstevel@tonic-gate 					ppid = pid;
7700Sstevel@tonic-gate 					pid = pstatus->pr_lwp.pr_rval1;
7710Sstevel@tonic-gate 					wascreated = pctx->created;
7720Sstevel@tonic-gate 					forkfn = pctx->fork;
7730Sstevel@tonic-gate 					pctx_free(pctx);
7740Sstevel@tonic-gate 					pctx = pctx_capture(pid, pctx->uarg,
7750Sstevel@tonic-gate 					    pctx->verbose, pctx->errfn);
7760Sstevel@tonic-gate 					if (pctx != NULL) {
7770Sstevel@tonic-gate 						if (wascreated) {
7780Sstevel@tonic-gate 							/*
7790Sstevel@tonic-gate 							 * Set kill on last
7800Sstevel@tonic-gate 							 * close so -all-
7810Sstevel@tonic-gate 							 * children die.
7820Sstevel@tonic-gate 							 */
7830Sstevel@tonic-gate 							pctx->created = 1;
7840Sstevel@tonic-gate 							(void) Psetflags(
7850Sstevel@tonic-gate 							    pctx->Pr, PR_KLC);
7860Sstevel@tonic-gate 						}
7870Sstevel@tonic-gate 						(*forkfn)(pctx, ppid, pid,
7880Sstevel@tonic-gate 						    lwpid, pctx->uarg);
7890Sstevel@tonic-gate 						pctx_release(pctx);
790*2235Skk112340 						_exit(0);
791*2235Skk112340 					} else {
792*2235Skk112340 						_exit(1);
7930Sstevel@tonic-gate 					}
7940Sstevel@tonic-gate 					/*NOTREACHED*/
7950Sstevel@tonic-gate 				case -1:
7960Sstevel@tonic-gate 					pctx_error(pctx, fn,
7970Sstevel@tonic-gate 					    "cannot follow pid %d: %s\n",
7980Sstevel@tonic-gate 					    (int)pstatus->pr_lwp.pr_rval1,
7990Sstevel@tonic-gate 					    strerror(errno));
8000Sstevel@tonic-gate 					break;
8010Sstevel@tonic-gate 				default:
8020Sstevel@tonic-gate 					break;
8030Sstevel@tonic-gate 				}
8040Sstevel@tonic-gate 				break;
8050Sstevel@tonic-gate 			default:
8060Sstevel@tonic-gate 				pctx_error(pctx, fn, gettext(
8070Sstevel@tonic-gate 				    "warning - pid %d sysexit(%d)\n"),
8080Sstevel@tonic-gate 				    (int)pid, pstatus->pr_lwp.pr_what);
8090Sstevel@tonic-gate 				break;
8100Sstevel@tonic-gate 			}
8110Sstevel@tonic-gate 			break;
8120Sstevel@tonic-gate 		case PR_SIGNALLED:
8130Sstevel@tonic-gate 			if (pctx->verbose)
8140Sstevel@tonic-gate 				pctx_error(pctx, fn,
8150Sstevel@tonic-gate 				    gettext("pid %d - signalled\n"), (int)pid);
8160Sstevel@tonic-gate 			break;
8170Sstevel@tonic-gate 		case PR_JOBCONTROL:
8180Sstevel@tonic-gate 			if (pctx->verbose)
8190Sstevel@tonic-gate 				pctx_error(pctx, fn,
8200Sstevel@tonic-gate 				    gettext("pid %d - job control stop\n"),
8210Sstevel@tonic-gate 				    (int)pid);
822*2235Skk112340 			running = -1;
8230Sstevel@tonic-gate 			break;
8240Sstevel@tonic-gate 		case PR_FAULTED:
8250Sstevel@tonic-gate 			if (pctx->verbose)
8260Sstevel@tonic-gate 				pctx_error(pctx, fn,
8270Sstevel@tonic-gate 				    gettext("pid %d - faulted\n"), (int)pid);
8280Sstevel@tonic-gate 			break;
8290Sstevel@tonic-gate 		case PR_SUSPENDED:
8300Sstevel@tonic-gate 			if (pctx->verbose)
8310Sstevel@tonic-gate 				pctx_error(pctx, fn,
8320Sstevel@tonic-gate 				    gettext("pid %d - suspended\n"), (int)pid);
8330Sstevel@tonic-gate 			break;
8340Sstevel@tonic-gate 		case PR_CHECKPOINT:
8350Sstevel@tonic-gate 			if (pctx->verbose)
8360Sstevel@tonic-gate 				pctx_error(pctx, fn,
8370Sstevel@tonic-gate 				    gettext("pid %d - checkpoint\n"),
8380Sstevel@tonic-gate 				    (int)pid);
8390Sstevel@tonic-gate 			break;
8400Sstevel@tonic-gate 		default:
8410Sstevel@tonic-gate 			if (pctx->verbose)
8420Sstevel@tonic-gate 				pctx_error(pctx, fn,
8430Sstevel@tonic-gate 				    gettext("pid %d - reason %d\n"),
8440Sstevel@tonic-gate 				    (int)pid, pstatus->pr_lwp.pr_why);
845*2235Skk112340 			running = -1;
8460Sstevel@tonic-gate 			break;
8470Sstevel@tonic-gate 		}
8480Sstevel@tonic-gate 	}
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate bailout:
8510Sstevel@tonic-gate 	(void) signal(SIGCHLD, sigsaved);
852*2235Skk112340 
853*2235Skk112340 	switch (running) {
854*2235Skk112340 	case 0:
8550Sstevel@tonic-gate 		return (0);
856*2235Skk112340 	case -1:
857*2235Skk112340 		return (-1);
858*2235Skk112340 	default:
859*2235Skk112340 		pctx_error(pctx, fn, gettext("lost control of pid %d\n"),
860*2235Skk112340 		    (int)pid);
861*2235Skk112340 		pctx_free(pctx);
862*2235Skk112340 		return (-1);
863*2235Skk112340 	}
8640Sstevel@tonic-gate }
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate /*
8670Sstevel@tonic-gate  * Execute the private 'cpc' system call in the context of the
8680Sstevel@tonic-gate  * controlled process.
8690Sstevel@tonic-gate  */
8700Sstevel@tonic-gate int
8710Sstevel@tonic-gate __pctx_cpc(pctx_t *pctx, cpc_t *cpc,
8720Sstevel@tonic-gate     int cmd, id_t lwpid, void *data1, void *data2, void *data3, int bufsize)
8730Sstevel@tonic-gate {
8740Sstevel@tonic-gate 	sysret_t rval;
8750Sstevel@tonic-gate 	argdes_t argd[5];
8760Sstevel@tonic-gate 	argdes_t *adp = &argd[0];
8770Sstevel@tonic-gate 	int error;
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 	/*
8800Sstevel@tonic-gate 	 * Keep track of the relationship between cpc_t and pctx_t here.
8810Sstevel@tonic-gate 	 * We store the last cpc_t used by libpctx, so that when this pctx is
8820Sstevel@tonic-gate 	 * destroyed, libpctx can notify libcpc.
8830Sstevel@tonic-gate 	 */
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
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 }
992