/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 (c) 1994-1999 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains interfaces that are wrappers over the basic
* /proc ioctls
*/
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include "prb_proc_int.h"
#include "dbg.h"
/*
* Declarations
*/
static prb_status_t
/*
* prb_proc_open_general() - function to open the process file
* system entry for the supplied process. Opens with different
* options based on the 'oflg'.
* Returns a pointer to an opaque structure that contains the fd
* needed for /proc control.
*/
{
int retval;
if (retval == -1) {
"proc_open: open of \"%s\" failed: %s\n",
return (prb_status_map(errno));
}
/* allocate proc_p and zero fill */
return (PRB_STATUS_ALLOCFAIL);
return (PRB_STATUS_OK);
}
/*
* prb_proc_open() - a wrapper which opens the process file system
* entry for the supplied process. Returns a pointer to an opaque
* structure that contains the fd needed for /proc control.
*/
{
return (prb_proc_open_general(pid,
}
/*
* Read the last section of /proc man page for details.
* re-open should not use O_EXCL flag.
*/
{
return (prb_proc_open_general(pid,
}
/*
* prob_proc_close() - close the proc fd and free the memory taken up
* by proc_p
*/
{
return (PRB_STATUS_OK);
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_pid_get() - gets the pid of the proc
*/
{
}
/*
* prb_proc_stop() - stops the target process
*/
{
int retval;
"sunw%verbosity 2; sunw%debug 'stopping the target process'");
if (retval == -1) {
goto again;
"prb_proc_stop: PIOCSTOP failed: %s\n",
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_prstop() - runs and stops the process, used to clear a target
* process out of a system call state.
*/
{
int procfd;
int retval;
"sunw%verbosity 2; sunw%debug 'stepping the target process'");
if (retval == -1) {
goto again1;
"prb_proc_prstop: PIOCRUN failed: %s\n",
return (prb_status_map(errno));
}
if (retval == -1) {
goto again2;
"prb_proc_prstop: PIOCWSTOP failed: %s\n",
return (prb_status_map(errno));
}
/*
* if we didn't stop because we requested it (eg. if there was a
* signal in the target ?), we might need to try again
*/
goto again1;
return (PRB_STATUS_OK);
}
/*
* prb_proc_state() - returns the status pf the process
*/
{
int procfd;
int retval;
"sunw%verbosity 2; sunw%debug 'getting the status'");
if (retval == -1) {
goto again;
"prb_proc_status: PIOCSTATUS failed: %s\n",
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_wait() - waits for the target process to stop
*/
{
int procfd;
int retval;
int i, mask_size;
"sunw%verbosity 2;"
"sunw%debug 'waiting for the target process to stop'");
/*
* This one of the places where we do not resubmit the ioctl if
* if it is terminated by an EINTR (interrupted system call). In
* this case, the caller knows best ...
*/
/* if we blocked signals... */
if (use_sigmask) {
return (prb_status_map(errno));
return (prb_status_map(errno));
/*
* check if there were any signals pending -
* XXXX libc should provide this interface
*/
for (i = 0; i < mask_size; i++) {
}
/* return to original signal mask */
return (prb_status_map(errno));
/* if there was a pending signal, don't call PIOCWSTOP ioctl */
if (pending_signal)
return (prb_status_map(EINTR));
/*
* XXXX - there is a a race between now and when we call
* the PIOCWSTOP ioctl. One solution, is for the user to
* call an interface in libtnfctl from their signal handler.
* This interface will do a longjmp such that it never
* calls the ioctl (the setjmp would be before we restore
* the signal mask above)
*/
}
if (retval == -1) {
#ifdef DEBUG
"prb_proc_wait: PIOCWSTOP failed: %s\n",
#endif
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_cont() - continues the target process
*/
{
int procfd;
int retval;
"sunw%verbosity 2; sunw%debug 'starting the target process'");
if (retval == -1) {
goto again;
"prb_proc_cont: PIOCRUN failed: %s\n",
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_istepbpt() - step the target process one instruction
*
* CAUTION!!!! - this routine is specialized to only be able to single
* step over the breakpoint location.
*/
{
int procfd;
int retval;
"sunw%verbosity 2; "
"sunw%debug 'single stepping over breakpoint'");
/* add trace fault to the list of current traced faults */
if (retval == -1) {
goto again1;
"prb_proc_istepbpt: PIOCGFAULT failed: %s\n",
return (prb_status_map(errno));
}
/* issue the run command with the single-step option */
/* load the location of the breakpoint */
if (retval == -1) {
goto again2;
"prb_proc_istepbpt: PIOCRUN failed: %s\n",
return (prb_status_map(errno));
}
if (retval == -1) {
goto again3;
"prb_proc_istepbpt: PIOCWSTOP failed: %s\n",
return (prb_status_map(errno));
}
/* clear any current faults */
if (retval == -1) {
goto again4;
"prb_proc_clrbptflt: PIOCCFAULT failed: %s\n",
return (prb_status_map(errno));
}
/* remove the trace fault from the current traced faults */
if (retval == -1) {
goto again5;
"prb_proc_istepbpt: PIOCSFAULT failed: %s\n",
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_clrbptflt() - clear an encountered breakpoint fault
*/
{
int retval;
int procfd;
/* clear any current faults */
if (retval == -1) {
goto again;
"prb_proc_clrbptflt: PIOCCFAULT failed: %s\n",
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_tracebpt() - sets the bpt tracing state.
*/
{
int procfd;
int retval;
/* get the current set of traced faults */
if (retval == -1) {
goto again1;
"prb_proc_tracebpt: PIOCGFAULT failed: %s\n",
return (prb_status_map(errno));
}
/* set or clear the breakpoint flag */
if (bpt)
else
/* write the fault set back */
if (retval == -1) {
goto again2;
"prb_proc_tracebpt: PIOCSFAULT failed: %s\n",
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/* Note - the following 3 functions should be combined */
/*
* prb_proc_setrlc() - sets or clears the run-on-last-close flag.
*/
{
int procfd;
long mode;
int retval;
if (rlc) {
if (retval == -1) {
goto again1;
"prb_proc_setrlc: PIOCSET failed: %s\n",
return (prb_status_map(errno));
}
} else {
if (retval == -1) {
goto again2;
"prb_proc_setrlc: PIOCRESET failed: %s\n",
return (prb_status_map(errno));
}
}
return (PRB_STATUS_OK);
} /* end prb_proc_setrlc */
/*
* prb_proc_setklc() - sets or clears the kill-on-last-close flag.
*/
{
int procfd;
long mode;
int retval;
if (klc) {
if (retval == -1) {
goto again1;
"prb_proc_setklc: PIOCSET failed: %s\n",
return (prb_status_map(errno));
}
} else {
if (retval == -1) {
goto again2;
"prb_proc_setklc: PIOCRESET failed: %s\n",
return (prb_status_map(errno));
}
}
return (PRB_STATUS_OK);
} /* end prb_proc_setklc */
/*
* prb_proc_setfork() - sets or clears the inherit-on-fork flag
*/
{
int procfd;
long mode;
int retval;
if (inhfork) {
if (retval == -1) {
goto again1;
"prb_proc_setfork: PIOCSET failed: %s\n",
return (prb_status_map(errno));
}
} else {
if (retval == -1) {
goto again2;
"prb_proc_setfork: PIOCRESET failed: %s\n",
return (prb_status_map(errno));
}
}
return (PRB_STATUS_OK);
} /* end prb_proc_setfork */
/*
* prb_proc_exit() - if op is PRB_SYS_ALL, sets up the target process to stop
* on exit from all system calls. If op is PRB_SYS_NONE, sets up the target
* process so that it will not stop on exit from any system call.
* PRB_SYS_ADD and PRB_SYS_DEL adds or deletes a particular system call from
* the mask of "interested" system calls respectively. This function can be
* called multiple times to build up the mask.
*/
{
int procfd;
int retval;
"sunw%verbosity 2; "
"sunw%debug 'setting up target to stop on exit of syscall'");
switch (op) {
case PRB_SYS_ALL:
break;
case PRB_SYS_NONE:
break;
case PRB_SYS_ADD:
if (retval == -1) {
goto again1;
"prb_proc_exit: PIOCGEXIT failed: %s\n",
return (prb_status_map(errno));
}
break;
case PRB_SYS_DEL:
if (retval == -1) {
goto again2;
"prb_proc_exit: PIOCGEXIT failed: %s\n",
return (prb_status_map(errno));
}
break;
default:
return (PRB_STATUS_BADARG);
}
if (retval == -1) {
goto again3;
"prb_proc_exit: PIOCSEXIT failed: %s\n",
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
} /* end prb_proc_exit */
/*
* prb_proc_entry() - if op is PRB_SYS_ALL, sets up the target process to
* stop on entry from all system calls. If op is PRB_SYS_NONE, sets up the
* target process so that it will not stop on entry from any system call.
* PRB_SYS_ADD and PRB_SYS_DEL adds or deletes a particular system call from
* the mask of "interested" system calls respectively. This function can be
* called multiple times to build up the mask.
*/
{
int procfd;
int retval;
"sunw%verbosity 2; "
"sunw%debug 'setting up target to stop on entry of syscall'");
switch (op) {
case PRB_SYS_ALL:
break;
case PRB_SYS_NONE:
break;
case PRB_SYS_ADD:
if (retval == -1) {
goto again1;
"prb_proc_entry: PIOCGENTRY failed: %s\n",
return (prb_status_map(errno));
}
break;
case PRB_SYS_DEL:
if (retval == -1) {
goto again2;
"prb_proc_entry: PIOCGENTRY failed: %s\n",
return (prb_status_map(errno));
}
break;
default:
return (PRB_STATUS_BADARG);
}
if (retval == -1) {
goto again3;
"prb_proc_entry: PIOCSENTRY failed: %s\n",
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_read() - reads a block of memory from a processes address space.
*/
{
int procfd;
return (prb_status_map(errno));
}
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_write() - writes a block of memory from a processes address
* space.
*/
{
int procfd;
return (prb_status_map(errno));
}
return (prb_status_map(errno));
}
return (PRB_STATUS_OK);
}
/*
* prb_proc_readstr() - dereferences a string in the target
* NOTE: There is a similar routine called _tnfctl_readstr_targ()
* used by tnfctl layer. It would be better if there was only
* one of these functions defined.
*/
{
offset = 0;
/* allocate an inital return buffer */
if (!ptr) {
"prb_proc_readstr: malloc failed\n"));
return (PRB_STATUS_ALLOCFAIL);
}
/*LINTED constant in conditional context*/
while (1) {
int i;
/* read a chunk into our buffer */
if (prbstat) {
/*
* if we get into trouble with a large read, try again
* with a single byte. Subsequent failure is real ...
*/
if (bufsz > 1) {
bufsz = 1;
continue;
}
"prb_proc_readstr: prb_proc_read failed: %s\n",
return (prbstat);
}
/* copy the chracters into the return buffer */
for (i = 0; i < bufsz; i++) {
char c = buffer[i];
if (c == '\0') {
/* hooray! we saw the end of the string */
return (PRB_STATUS_OK);
}
}
/* bummer, need to grab another bufsz characters */
if (!ptr) {
"prb_proc_readstr: realloc failed\n"));
return (PRB_STATUS_ALLOCFAIL);
}
}
#if defined(lint)
return (PRB_STATUS_OK);
#endif
}
{
int retval;
int procfd;
if (retval == -1) {
goto again;
return (prb_status_map(errno));
}
/*
* Use R_Rn register definitions for some uniformity
* sparc: define R_R0 R_O0
* define R_R1 R_O1
* x86: define R_R0 EAX
* define R_R1 EDX
*/
return (PRB_STATUS_OK);
}