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