/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file contains a set of generic routines for periodically
* sampling the state of another process, or tree of processes.
*
* It is built upon the infrastructure provided by libproc.
*/
#include <libproc.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
#include <ctype.h>
#include <libintl.h>
#include <libcpc.h>
#include <sys/cpc_impl.h>
#include "libpctx.h"
struct __pctx {
void *uarg;
int verbose;
int created;
int sigblocked;
int terminate;
};
static void
{
}
/*PRINTFLIKE3*/
static void
{
}
/*
* Create a new process and bind the user args for it
*/
pctx_t *
const char *filename,
char *const *argv,
void *arg,
int verbose,
{
int err;
switch (err) {
case C_PERM:
"unreadable program '%s'\n"), filename);
break;
case C_LP64:
"program '%s'\n"), filename);
break;
case C_NOEXEC:
"program '%s'\n"), filename);
break;
case C_NOENT:
"program '%s'\n"), filename);
break;
case C_FORK:
"program '%s'\n"), filename);
break;
default:
break;
}
return (NULL);
}
return (NULL);
}
/*
* Set kill-on-last-close so the controlled process
* dies if we die.
*/
return (pctx);
}
/*
* Capture an existing process and bind the user args for it
*/
pctx_t *
{
int err;
switch (err) {
case G_NOPROC:
break;
case G_ZOMB:
break;
case G_PERM:
break;
case G_BUSY:
gettext("pid %d is already being traced\n"),
(int)pid);
break;
case G_SYS:
break;
case G_SELF:
gettext("cannot capture self!\n"));
break;
case G_LP64:
"process, pid %d\n"), (int)pid);
break;
default:
break;
}
return (NULL);
}
return (NULL);
}
/*
* Set run-on-last-close so the controlled process
* runs even if we die on a signal. This is because
* we grabbed an existing process - it would be impolite
* to cause it to die if we exit prematurely.
*/
return (pctx);
}
/*ARGSUSED*/
static void
{}
/*ARGSUSED*/
static int
{
return (0);
}
int
{
int error = 0;
do {
case PCTX_NULL_EVENT:
break;
case PCTX_SYSC_EXEC_EVENT:
break;
case PCTX_SYSC_FORK_EVENT:
break;
case PCTX_SYSC_EXIT_EVENT: /* always intercepted */
break;
break;
case PCTX_INIT_LWP_EVENT:
break;
case PCTX_FINI_LWP_EVENT:
break;
case PCTX_SYSC_LWP_EXIT_EVENT:
break;
default:
error = -1;
break;
}
if (error != 0)
return (error);
error = -1;
} else {
error = -1;
}
/*
* exec causes termination of all but the exec-ing lwp,
* and resets the lwpid to one in the new address space.
*/
} else {
}
return (0);
}
static void
__libpctx_init(void)
{
/*
* Initialize the signal set used to shield ourselves from
* death-by-terminal-signal while the agent lwp is running.
*/
(void) sigemptyset(&termsig);
}
#pragma init(__libpctx_init)
static void
{
return;
if (pctx->sigblocked++ == 0) {
}
}
static void
{
return;
if (--pctx->sigblocked == 0) {
}
}
/*
* Iterate over the valid lwpids in the process, invoking the
* action function on each one.
*/
static int
{
int ret = 0;
return (0);
return (ret);
}
if (fd >= 0)
return (-1);
}
sizeof (prheader_t) + sizeof (lwpstatus_t)) {
return (-1);
}
/* LINTED pointer cast may result in improper alignment */
ret = -1;
/* LINTED pointer cast may result in improper alignment */
}
return (ret);
}
/*
* Free any associated state, but leave the process stopped if it
* is still under our control. (If it isn't under our control,
* it should just run to completion when we do our last close)
*/
static void
{
}
}
/*
* Completely release the process from our control and discard all our state
*/
void
{
}
}
static void
{
}
}
static uint_t
{
if (sdiff < 0)
return (0);
if (udiff < 0) {
sdiff--;
}
if (sdiff < 0)
return (0);
}
int
{
void (*sigsaved)();
int pstate;
if (msec == 0)
nsamples = 0;
if (nsamples == 0)
/*
* Casually discard any knowledge of the children we create
*/
/*
* Since we've just "discovered" this process which might have
* been running for weeks, deliver some init_lwp events so
* that our caller gets a handle on the process.
*/
goto bailout;
}
if (msec != 0) {
/*
* tvgoal represents the time at which the sample
* should next be taken.
*/
(void) gettimeofday(&tvgoal, 0);
}
/*
* The event handling loop continues while running is 1.
* running becomes 0 when either the controlled process has
* exited successfully or the number of time samples has expired.
* Otherwise, if an error has occurred, running becomes -1.
*/
break;
}
if (msec != 0) {
/*
* This timing loop attempts to estimate the number
* of milliseconds between our "goal" time (when
* we should stop the process and run the tick
* routine) and the current time.
*
* If we ever find ourselves running behind i.e. we
* missed our goal, then we skip ahead to the next
* goal instead.
*/
do {
(void) gettimeofday(&tvnow, 0);
/*
* Skip ahead to the next goal, unless
* there is only one more sample left
* to take.
*/
if (nsamples != 1)
nsamples--;
}
}
goto bailout;
else
case PS_RUN:
/*
* Try again, but wait for up to 5 seconds.
*/
}
break;
case PS_STOP:
break;
case PS_LOST:
/*
* executable. Try and get control back again,
* else bail ..
*/
goto checkstate;
gettext("%d: execed a program that cannot "
"be tracked\n"), (int)pid);
running = -1;
break;
case PS_UNDEAD:
case PS_DEAD:
gettext("%d: process terminated\n"),
(int)pid);
running = -1;
break;
default:
gettext("%d: process state 0x%x?\n"),
break;
}
break;
case PR_REQUESTED:
/*
* The process is in a vfork stupor until
* its child releases it via an exec.
* Don't sample it while it's in this state
* - we won't be able to create the agent.
*/
break;
}
running = -1;
running = 0;
break;
case PR_SYSENTRY:
case SYS_lwp_exit:
break;
case SYS_exit:
!= 0)
running = -1;
if (running == 1)
running = 0;
break;
case SYS_execve:
break;
default:
"warning - pid %d sysentry(%d)\n",
break;
}
break;
case PR_SYSEXIT:
case SYS_execve:
/*
* The exec failed completely.
* Reinstate the lwps we fini'd
* at exec entrance
*/
if (pctx_lwpiterate(pctx,
running = 1;
else
running = -1;
break;
}
default_int) {
running = 0;
break;
}
running = -1;
running = -1;
break;
case SYS_lwp_create:
break;
running = -1;
running = -1;
break;
case SYS_vfork:
case SYS_forksys:
break;
switch (fork1()) {
int wascreated;
case 0:
if (wascreated) {
/*
* Set kill on last
* close so -all-
* children die.
*/
(void) Psetflags(
}
_exit(0);
} else {
_exit(1);
}
/*NOTREACHED*/
case -1:
"cannot follow pid %d: %s\n",
break;
default:
break;
}
break;
default:
"warning - pid %d sysexit(%d)\n"),
break;
}
break;
case PR_SIGNALLED:
break;
case PR_JOBCONTROL:
gettext("pid %d - job control stop\n"),
(int)pid);
running = -1;
break;
case PR_FAULTED:
break;
case PR_SUSPENDED:
break;
case PR_CHECKPOINT:
gettext("pid %d - checkpoint\n"),
(int)pid);
break;
default:
gettext("pid %d - reason %d\n"),
running = -1;
break;
}
}
return (0);
switch (running) {
case 0:
return (0);
case -1:
return (-1);
default:
(int)pid);
return (-1);
}
}
/*
* Execute the private 'cpc' system call in the context of the
* controlled process.
*/
int
{
int error;
/*
* Keep track of the relationship between cpc_t and pctx_t here.
* We store the last cpc_t used by libpctx, so that when this pctx is
* destroyed, libpctx can notify libcpc.
*/
/*
* cmd and lwpid are passed in by value no matter what the command is.
*/
adp++;
adp++;
switch (cmd) {
case CPC_BIND:
adp++;
adp++;
break;
case CPC_SAMPLE:
adp++;
adp++;
break;
default:
adp->arg_object = 0;
adp++;
adp->arg_object = 0;
adp++;
adp->arg_object = 0;
break;
}
if (error) {
return (-1);
}
}
/*
* libcpc-private hook used to register a callback. The callback is used to
* notify libcpc when a pctx handle is invalidated.
*/
void
{
}
/*
* Tell pctx_run to bail out immediately
*/
void
{
}