Files
Arquivotheca.Solaris-2.5/cmd/truss/pcontrol.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

1692 lines
40 KiB
C
Executable File

/*
* Copyright (c) 1992 Sun Microsystems, Inc. All Rights Reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
#pragma ident "@(#)pcontrol.c 1.17 94/01/17 SMI" /* SVr4.0 1.3 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <memory.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/fault.h>
#include <sys/syscall.h>
#include "pcontrol.h"
#include "ramdata.h"
#include "proto.h"
/* Process Management */
/* This module is carefully coded to contain only read-only data */
/* All read/write data is defined in ramdata.c (see also ramdata.h) */
/*
* Function prototypes for static routines in this module.
*/
#if defined(__STDC__)
static void prdump( process_t * );
static void deadcheck( process_t * );
static int execute( process_t * , int );
static int checksyscall( process_t * );
#else /* defined(__STDC__) */
static void prdump();
static void deadcheck();
static int execute();
static int checksyscall();
#endif /* defined(__STDC__) */
#if i386
CONST char * CONST regname[NREGS] = {
"gs", "fs", "es", "ds", "edi", "esi", "ebp", "esp", "ebx",
"edx", "ecx", "eax", "trapno", "err", "eip", "cs", "efl",
"uesp", "ss"
};
#endif /* i386 */
#if u3b2 || u3b15
CONST char * CONST regname[NREGS] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8",
"fp", "ap", "ps", "sp", "pcbp", "isp", "pc"
};
#endif
#if u3b
CONST char * CONST regname[NREGS] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8",
"ap", "fp", "sp", "r12", "r13", "r14", "epsw", "pc", "ps"
};
#endif
#if mc68k
CONST char * CONST regname[NREGS] = {
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
"a0", "a1", "a2", "a3", "a4", "a5", "fp", "sp",
"pc", "ps"
};
#endif
#if sparc
CONST char * CONST regname[NREGS] = {
"g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
"o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7",
"l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
"i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7",
"psr", "pc", "npc", "y",
};
#endif
int
Pcreate(P, args) /* create new controlled process */
register process_t *P; /* program table entry */
char **args; /* argument array, including the command name */
{
register int i;
register int upid;
register int fd;
char procname[100];
upid = fork();
if (upid == 0) { /* child process */
(void) pause(); /* wait for PRSABORT from parent */
/* if running setuid or setgid, reset credentials to normal */
if ((i = getgid()) != getegid())
(void) setgid(i);
if ((i = getuid()) != geteuid())
(void) setuid(i);
(void) execvp(*args, args); /* execute the command */
_exit(127);
}
if (upid == -1) { /* failure */
perror("Pcreate fork()");
return -1;
}
/* initialize the process structure */
(void) memset((char *)P, 0, sizeof(*P));
P->cntrl = TRUE;
P->child = TRUE;
P->state = PS_RUN;
P->upid = upid;
/* open the /proc/upid file */
(void) sprintf(procname, "%s/%d", procdir, upid);
/* exclusive open prevents others from interfering */
if ((fd = open(procname, (O_RDWR|O_EXCL))) < 0) {
perror("Pcreate open()");
(void) kill(upid, SIGKILL);
return -1;
}
/* make sure it's not one of 0, 1, or 2 */
/* this allows truss to work when spawned by init(1m) */
if (0 <= fd && fd <= 2) {
int dfd = fcntl(fd, F_DUPFD, 3);
(void) close(fd);
if (dfd < 0) {
perror("Pcreate fcntl()");
(void) kill(upid, SIGKILL);
return -1;
}
fd = dfd;
}
/* mark it close-on-exec so any created process doesn't inherit it */
(void) fcntl(fd, F_SETFD, 1);
/* mark it run-on-last-close so it runs even if we die on a signal */
if (Ioctl(fd, PIOCSRLC, 0) == -1)
perror("Pcreate PIOCSRLC");
P->pfd = fd;
(void) Pstop(P); /* stop the controlled process */
for (;;) { /* wait for process to sleep in pause() */
if (P->state == PS_STOP
&& P->why.pr_why == PR_REQUESTED
&& (P->why.pr_flags & PR_ASLEEP)
&& Pgetsysnum(P) == SYS_pause)
break;
if (P->state != PS_STOP /* interrupt or process died */
|| Psetrun(P, 0, PRSTOP) != 0) { /* can't restart */
int sig = SIGKILL;
(void) Ioctl(fd, PIOCKILL, (int)&sig); /* kill ! */
(void) close(fd);
(void) kill(upid, sig); /* kill !! */
P->state = PS_DEAD;
P->pfd = 0;
return -1;
}
(void) Pwait(P);
/* dumpwhy(P, 0); */
}
(void) Psysentry(P, SYS_exit, 1); /* catch these sys calls */
(void) Psysentry(P, SYS_exec, 1);
(void) Psysentry(P, SYS_execve, 1);
/* kick it off the pause() */
if (Psetrun(P, 0, PRSABORT) == -1) {
int sig = SIGKILL;
perror("Pcreate PIOCRUN");
(void) Ioctl(fd, PIOCKILL, (int)&sig);
(void) kill(upid, sig);
(void) close(fd);
P->state = PS_DEAD;
P->pfd = 0;
return -1;
}
(void) Pwait(P); /* wait for exec() or exit() */
return 0;
}
/* This is for the !?!*%! call to sleep() in execvp() */
unsigned int
sleep(n)
unsigned n;
{
if (n) {
(void) sighold(SIGALRM);
(void) alarm(n);
(void) sigpause(SIGALRM);
(void) alarm(0);
(void) sigrelse(SIGALRM);
timeout = FALSE;
}
return 0;
}
#if 0
static void
fddump(fd) /* debugging code -- dump fstat(fd) */
int fd;
{
struct stat statb;
CONST char * s;
(void) fprintf(stderr, "fd = %d", fd);
if (fstat(fd, &statb) == -1)
goto out;
switch (statb.st_mode & S_IFMT) {
case S_IFDIR: s="S_IFDIR"; break;
case S_IFCHR: s="S_IFCHR"; break;
case S_IFBLK: s="S_IFBLK"; break;
case S_IFREG: s="S_IFREG"; break;
case S_IFIFO: s="S_IFIFO"; break;
default: s="???"; break;
}
(void) fprintf(stderr, " %s mode = 0%o dev = 0x%.4X ino = %d",
s,
statb.st_mode & ~S_IFMT,
statb.st_dev,
statb.st_ino);
(void) fprintf(stderr, " uid = %d, gid = %d, size = %ld\n",
statb.st_uid,
statb.st_gid,
statb.st_size);
out:
(void) fputc('\n', stderr);
}
#endif
int
Pgrab(P, upid, force) /* grab existing process */
register process_t *P; /* program table entry */
register pid_t upid; /* UNIX process ID */
int force; /* if TRUE, grab regardless */
{
register int fd = -1;
int nmappings;
int ruid;
struct prcred prcred;
char procname[100];
again: /* Come back here if we lose it in the Window of Vulnerability */
if (fd >= 0) {
(void) close(fd);
fd = -1;
}
(void) memset((char *)P, 0, sizeof(*P));
if (upid <= 0)
return -1;
/* generate the /proc/upid filename */
(void) sprintf(procname, "%s/%ld", procdir, upid);
/* Request exclusive open to avoid grabbing someone else's */
/* process and to prevent others from interfering afterwards. */
/* If this fails and the 'force' flag is set, attempt to */
/* open non-exclusively (effective only for the super-user). */
if ((fd = open(procname, (O_RDWR|O_EXCL))) < 0
&& (fd = (force? open(procname, O_RDWR) : -1)) < 0) {
if (errno == EBUSY && !force)
return 1;
if (debugflag)
perror("Pgrab open()");
return -1;
}
/* ---------------------------------------------------- */
/* We are now in the Window of Vulnerability (WoV). */
/* The process may exec() a setuid/setgid or unreadable */
/* object file between the open() and the PIOCSTOP. */
/* We will get EAGAIN in this case and must start over. */
/* ---------------------------------------------------- */
/* If the process is a system process, we can't control it */
/* even if we are super-user. The number of mappings is the */
/* way to determine a system process. */
if (Ioctl(fd, PIOCNMAP, (int)&nmappings) == -1) {
if (errno == EAGAIN) /* WoV */
goto again;
if (errno != ENOENT) /* Don't complain about zombies */
perror("Pgrab PIOCNMAP");
nmappings = 0;
}
/* there must be at least text, data, and stack */
if (nmappings < 3) {
(void) close(fd);
return -1;
}
/* Verify process credentials in case we are running setuid root. */
/* We only verify that our real uid matches the process's real uid. */
/* This means that the user really did create the process, even */
/* if using a different group id (via newgrp(1) for example). */
if (Ioctl(fd, PIOCCRED, (int)&prcred) == -1) {
if (errno == EAGAIN) /* WoV */
goto again;
if (errno != ENOENT) /* Don't complain about zombies */
perror("Pgrab PIOCCRED");
(void) close(fd);
return -1;
}
if ((ruid = getuid()) != 0 /* super-user allowed anything */
&& ruid != prcred.pr_ruid) { /* credentials check failed */
(void) close(fd);
return -1;
}
/* make sure it's not one of 0, 1, or 2 */
/* this allows truss to work when spawned by init(1m) */
if (0 <= fd && fd <= 2) {
int dfd = fcntl(fd, F_DUPFD, 3);
(void) close(fd);
if (dfd < 0) {
perror("Pgrab fcntl()");
return -1;
}
fd = dfd;
}
/* mark it close-on-exec so any created process doesn't inherit it */
(void) fcntl(fd, F_SETFD, 1);
/* mark it run-on-last-close so it runs even if we die from SIGKILL */
if (Ioctl(fd, PIOCSRLC, 0) == -1) {
if (errno == EAGAIN) /* WoV */
goto again;
if (errno != ENOENT) /* Don't complain about zombies */
perror("Pgrab PIOCSRLC");
}
P->cntrl = TRUE;
P->child = FALSE;
P->state = PS_RUN;
P->upid = upid;
P->pfd = fd;
/* before stopping the process, make sure it's not ourself */
if (upid == getpid()) {
/* write a magic number, read it through /proc file */
/* and see if the results match. */
long magic1 = 0;
long magic2 = 2;
errno = 0;
if (Pread(P, (long)&magic1, (char *)&magic2, sizeof(magic2))
== sizeof(magic2)
&& magic2 == 0
&& (magic1 = 0xfeedbeef)
&& Pread(P, (long)&magic1, (char *)&magic2, sizeof(magic2))
== sizeof(magic2)
&& magic2 == 0xfeedbeef) {
(void) close(fd);
(void) fprintf(stderr,
"Pgrab(): process attempted to grab itself\n");
return -1;
}
}
/* Stop the process, get its status and its signal/syscall masks. */
if (Pstatus(P, PIOCSTOP, 2) != 0) {
if (P->state == PS_LOST) /* WoV */
goto again;
if ((errno != EINTR && errno != ERESTART)
|| (P->state != PS_STOP && !(P->why.pr_flags&PR_DSTOP))) {
if (P->state != PS_RUN) {
if (errno != ENOENT)
perror("Pgrab PIOCSTOP");
#if 0
fddump(fd);
#endif
}
(void) close(fd);
return -1;
}
}
/* Process is or will be stopped, these will "certainly" not fail */
if (Ioctl(fd, PIOCGTRACE, (int)&P->sigmask) == -1)
perror("Pgrab PIOCGTRACE");
if (Ioctl(fd, PIOCGFAULT, (int)&P->faultmask) == -1)
perror("Pgrab PIOCGFAULT");
if (Ioctl(fd, PIOCGENTRY, (int)&P->sysentry) == -1)
perror("Pgrab PIOCGENTRY");
if (Ioctl(fd, PIOCGEXIT, (int)&P->sysexit) == -1)
perror("Pgrab PIOCGEXIT");
return 0;
}
int
Preopen(P, force) /* reopen the /proc file (after PS_LOST) */
register process_t *P;
int force; /* if TRUE, grab regardless */
{
register int fd;
char procname[100];
/* reopen the /proc/upid file */
(void) sprintf(procname, "%s/%ld", procdir, P->upid);
if ((fd = open(procname, (O_RDWR|O_EXCL))) < 0
&& (fd = (force? open(procname, O_RDWR) : -1)) < 0) {
if (debugflag)
perror("Preopen open()");
return -1;
}
if (P->pfd == 0 /* close the old filedescriptor */
|| close(P->pfd) != 0)
P->pfd = 3;
/* make the new filedescriptor the same as the old */
if (fd != P->pfd) {
int dfd = fcntl(fd, F_DUPFD, P->pfd);
P->pfd = 0;
(void) close(fd);
if (dfd < 0) {
perror("Preopen fcntl()");
return -1;
}
fd = dfd;
}
/* mark it close-on-exec so any created process doesn't inherit it */
(void) fcntl(fd, F_SETFD, 1);
/* set run-on-last-close so it runs even if we die from SIGKILL */
if (Ioctl(fd, PIOCSRLC, 0) == -1)
perror("Preopen PIOCSRLC");
P->state = PS_RUN;
P->pfd = fd;
/* process should be stopped on exec (REQUESTED) */
/* or else should be stopped on exit from exec() (SYSEXIT) */
if (Pwait(P) == 0
&& P->state == PS_STOP
&& (P->why.pr_why == PR_REQUESTED
|| (P->why.pr_why == PR_SYSEXIT
&& (P->why.pr_what == SYS_exec || P->why.pr_what == SYS_execve)))) {
/* fake up stop-on-exit-from-execve */
if (P->why.pr_why == PR_REQUESTED) {
P->why.pr_why = PR_SYSEXIT;
P->why.pr_what = SYS_execve;
}
}
else {
(void) fprintf(stderr,
"Preopen: expected REQUESTED or SYSEXIT(SYS_execve) stop\n");
}
return 0;
}
int
Prelease(P) /* release process to run freely */
register process_t *P;
{
register int fd = P->pfd;
long flags;
if (fd == 0)
return -1;
if (debugflag)
(void) fprintf(stderr, "Prelease: releasing pid # %ld\n",
P->upid);
/* attempt to stop it if we have to reset its registers */
if (P->sethold || P->setregs) {
register int count;
for (count = 10;
count > 0 && (P->state == PS_RUN || P->state == PS_STEP);
count--) {
(void) Pstop(P);
}
}
/* if we lost control, all we can do is close the file */
if (P->state == PS_STOP) {
if (P->sethold
&& Ioctl(fd, PIOCSHOLD, (int)&P->why.pr_sighold) != 0)
perror("Prelease PIOCSHOLD");
if (P->setregs
&& Ioctl(fd, PIOCSREG, (int)&P->REG[0]) != 0)
perror("Prelease PIOCSREG");
}
flags = PR_FORK;
(void) Ioctl(fd, PIOCRESET, (int)&flags);
flags = PR_RLC;
(void) Ioctl(fd, PIOCSET, (int)&flags);
(void) close(fd); /* this sets the process running */
/* zap the process structure */
(void) memset((char *)P, 0, sizeof(*P));
return 0;
}
int
Phang(P) /* release process but leave it stopped */
register process_t *P;
{
register int fd = P->pfd;
register int count;
long flags;
sysset_t sysset;
sigset_t sigset;
fltset_t fltset;
if (fd == 0)
return -1;
if (debugflag)
(void) fprintf(stderr, "Phang: releasing pid # %ld\n",
P->upid);
/* attempt to stop it */
for (count = 10;
count > 0 && (P->state == PS_RUN || P->state == PS_STEP);
count--)
(void) Pstop(P);
/* if we lost control, all we can do is close the file */
if (P->state == PS_STOP) {
if (P->sethold
&& Ioctl(fd, PIOCSHOLD, (int)&P->why.pr_sighold) != 0)
perror("Phang PIOCSHOLD");
if (P->setregs
&& Ioctl(fd, PIOCSREG, (int)&P->REG[0]) != 0)
perror("Phang PIOCSREG");
/* no run-on-last-close or kill-on-last-close*/
flags = PR_RLC|PR_KLC| /* PR_ASYNC| */ PR_PCOMPAT;
if (Ioctl(fd, PIOCRESET, (int)&flags) != 0)
perror("Phang PIOCRESET");
/* inherit-on-fork */
flags = PR_FORK;
if (Ioctl(fd, PIOCSET, (int)&flags) != 0)
perror("Phang PIOCSET");
/* no signal tracing */
premptyset(&sigset);
if (ioctl(fd, PIOCSTRACE, (int)&sigset) != 0)
perror("Phang PIOCSTRACE");
/* no fault tracing */
premptyset(&fltset);
if (ioctl(fd, PIOCSFAULT, (int)&fltset) != 0)
perror("Phang PIOCSFAULT");
/* no syscall tracing */
premptyset(&sysset);
if (ioctl(fd, PIOCSENTRY, (int)&sysset) != 0)
perror("Phang PIOCSENTRY");
premptyset(&sysset);
if (ioctl(fd, PIOCSEXIT, (int)&sysset) != 0)
perror("Phang PIOCSENTRY");
}
(void) close(fd);
/* zap the process structure */
(void) memset((char *)P, 0, sizeof(*P));
return 0;
}
/* debugging */
static void
prdump(P)
process_t *P;
{
long bits = *((long *)&P->why.pr_sigpend);
if (P->why.pr_cursig)
(void) fprintf(stderr, " p_cursig = %d", P->why.pr_cursig);
if (bits)
(void) fprintf(stderr, " p_sigpend = 0x%.8lX", bits);
(void) fputc('\n', stderr);
}
#if 0
/* debugging */
static void
dumpregs(P)
register process_t *P;
{
register int i;
for (i = 0; i < NREGS; i++) {
(void) fprintf(stderr, " %%%s = 0x%.8X",
regname[i], P->REG[i]);
if ((i+1)%4 == 0)
(void) fprintf(stderr, "\n");
}
if (i%4 != 0)
(void) fprintf(stderr, "\n");
}
#endif
#if 0
/* debugging */
static void
dumpwhy(P, str)
register process_t *P;
char *str;
{
if (str)
(void) fprintf(stderr, "%s\n", str);
(void) fprintf(stderr, "pr_flags = 0x%.8X\n", P->why.pr_flags);
(void) fprintf(stderr, "pr_why = 0x%.8X\n", P->why.pr_why);
(void) fprintf(stderr, "pr_what = 0x%.8X\n", P->why.pr_what);
(void) fprintf(stderr, "pr_pid = 0x%.8X\n", P->why.pr_pid);
(void) fprintf(stderr, "pr_ppid = 0x%.8X\n", P->why.pr_ppid);
(void) fprintf(stderr, "pr_pgrp = 0x%.8X\n", P->why.pr_pgrp);
(void) fprintf(stderr, "pr_cursig = 0x%.8X\n", P->why.pr_cursig);
(void) fprintf(stderr, "pr_sigpend= 0x%.8X\n", P->why.pr_sigpend.bits[0]);
(void) fprintf(stderr, "pr_sighold= 0x%.8X\n", P->why.pr_sighold.bits[0]);
(void) fprintf(stderr, "pr_instr = 0x%.8X\n", P->why.pr_instr);
(void) fprintf(stderr, "pr_utime = 0x%.8X\n", P->why.pr_utime);
(void) fprintf(stderr, "pr_stime = 0x%.8X\n", P->why.pr_stime);
dumpregs(P);
}
#endif
int
Pwait(P) /* wait for process to stop for any reason */
register process_t *P;
{
return Pstatus(P, PIOCWSTOP, 0);
}
int
Pstop(P) /* direct process to stop; wait for it to stop */
register process_t *P;
{
return Pstatus(P, PIOCSTOP, 0);
}
int
Pstatus(P, request, sec) /* wait for specified process to stop or terminate */
register process_t *P; /* program table entry */
register int request; /* PIOCSTATUS, PIOCSTOP, PIOCWSTOP */
unsigned sec; /* if non-zero, alarm timeout in seconds */
{
register int status = 0;
int err = 0;
switch (P->state) {
case PS_NULL:
case PS_LOST:
case PS_DEAD:
return -1;
case PS_STOP:
if (request != PIOCSTATUS)
return 0;
}
switch (request) {
case PIOCSTATUS:
case PIOCSTOP:
case PIOCWSTOP:
break;
default:
/* programming error */
(void) fprintf(stderr, "Pstatus: illegal request\n");
return -1;
}
timeout = FALSE;
if (sec)
(void) alarm(sec);
if (Ioctl(P->pfd, request, (int)&P->why) != 0) {
err = errno;
if (sec)
(void) alarm(0);
if (request != PIOCSTATUS && (err == EINTR || err == ERESTART)
&& Ioctl(P->pfd, PIOCSTATUS, (int)&P->why) != 0)
err = errno;
}
else if (sec)
(void) alarm(0);
if (err) {
switch (err) {
case EINTR: /* timeout or user typed DEL */
case ERESTART:
if (debugflag)
(void) fprintf(stderr, "Pstatus: EINTR\n");
break;
case EAGAIN: /* we lost control of the the process */
if (debugflag)
(void) fprintf(stderr, "Pstatus: EAGAIN\n");
P->state = PS_LOST;
break;
default: /* check for dead process */
if (debugflag || err != ENOENT) {
CONST char * errstr;
switch (request) {
case PIOCSTATUS:
errstr = "Pstatus PIOCSTATUS"; break;
case PIOCSTOP:
errstr = "Pstatus PIOCSTOP"; break;
case PIOCWSTOP:
errstr = "Pstatus PIOCWSTOP"; break;
default:
errstr = "Pstatus PIOC???"; break;
}
perror(errstr);
}
deadcheck(P);
break;
}
if (!timeout || (err != EINTR && err != ERESTART)) {
errno = err;
return -1;
}
}
if (!(P->why.pr_flags&PR_STOPPED)) {
if (request == PIOCSTATUS || timeout) {
timeout = FALSE;
return 0;
}
(void) fprintf(stderr, "Pstatus: process is not stopped\n");
return -1;
}
P->state = PS_STOP;
timeout = FALSE;
#if 0 /* debugging */
{
struct prstatus pstats;
if (Ioctl(P->pfd, PIOCSTATUS, (int)&pstats) == -1)
perror("Pstatus: PIOCSTATUS");
else if (memcmp((char *)&P->why, (char *)&pstats, sizeof(pstats)) != 0)
(void) fprintf(stderr,
"Pstatus: PIOCWSTOP and PIOCSTATUS disagree!\n");
}
#endif
#if 0 /* debugging */
{
GREGSET regs; /* array of registers from process */
register int i;
/* read the process registers */
if (Ioctl(P->pfd, PIOCGREG, (int)&regs[0]) == -1)
perror("Pstatus: PIOCGREG failure");
else for (i = 0; i < NREGS; i++) {
if (debugflag && P->REG[i] != regs[i])
(void) fprintf(stderr,
" %%%s: 0x%.8X 0x%.8X\n",
regname[i], P->REG[i], regs[i]);
P->REG[i] = regs[i];
}
}
#endif
switch (P->why.pr_why) {
case PR_REQUESTED:
if (debugflag) {
(void) fprintf(stderr, "Pstatus: why: REQUESTED");
prdump(P);
}
break;
case PR_SIGNALLED:
if (debugflag) {
(void) fprintf(stderr, "Pstatus: why: SIGNALLED %s",
signame(P->why.pr_what));
prdump(P);
}
break;
case PR_FAULTED:
if (debugflag) {
(void) fprintf(stderr, "Pstatus: why: FAULTED %s",
fltname(P->why.pr_what));
prdump(P);
}
#if 0 /* debugging */
dumpregs(P);
#endif
break;
case PR_SYSENTRY:
/* remember syscall address */
#if sparc
P->sysaddr = P->REG[R_PC];
#else
P->sysaddr = P->REG[R_PC]-sizeof(syscall_t);
#endif
if (debugflag) {
(void) fprintf(stderr, "Pstatus: why: SYSENTRY %s",
sysname(P->why.pr_what, -1));
prdump(P);
}
break;
case PR_SYSEXIT:
/* remember syscall address */
P->sysaddr = P->REG[R_PC]-sizeof(syscall_t);
if (debugflag) {
(void) fprintf(stderr, "Pstatus: why: SYSEXIT %s",
sysname(P->why.pr_what, -1));
prdump(P);
}
break;
case PR_JOBCONTROL:
if (debugflag) {
(void) fprintf(stderr, "Pstatus: why: JOBCONTROL %s",
signame(P->why.pr_what));
prdump(P);
}
break;
case PR_SUSPENDED:
if (debugflag) {
(void) fprintf(stderr, "Pstatus: why: SUSPENDED");
prdump(P);
}
break;
default:
if (debugflag) {
(void) fprintf(stderr, "Pstatus: why: Unknown");
prdump(P);
}
status = -1;
break;
}
#if 0 /* debugging */
if (debugflag)
dumpwhy(P, 0);
#endif
return status;
}
int
Pgetsysnum(P) /* determine which syscall number we are at */
register process_t *P;
{
register int syscall = -1;
#if u3b2 || u3b5
if (Pgetareg(P,0)==0 && (P->REG[0]&0x7c)==4 && Pgetareg(P,1)==0)
syscall = (P->REG[1] & 0x7ff8) >> 3;
#endif
#if mc68k
if (Pgetareg(P,0) == 0)
syscall = P->REG[0] & 0xffff;
#endif
#if sparc || i386
# if defined(sparc) && defined(oldstuff)
if (Pgetareg(P,R_G1) == 0) {
syscall = P->REG[R_G1];
if (syscall == SYS_syscall /* indirect system call */
&& Pgetareg(P,R_O0) == 0)
syscall = P->REG[R_O0];
}
# else
syscall = P->why.pr_syscall;
# endif
#endif /* sparc || i386 */
return syscall;
}
int
Psetsysnum(P, syscall) /* we are at a syscall trap, prepare to issue syscall */
register process_t *P;
register int syscall;
{
#if defined(i386)
P->REG[R_R0] = syscall;
if (Pputareg(P,R_R0))
syscall = -1;
#endif /* i386 */
#if u3b2 || u3b5
P->REG[0] = 4;
P->REG[1] = syscall<<3;
if (Pputareg(P,0) || Pputareg(P,1))
syscall = -1;
#endif
#if mc68k
P->REG[0] = syscall;
if (Pputareg(P,0))
syscall = -1;
#endif
#if sparc
P->REG[R_G1] = syscall;
if (Pputareg(P,R_G1))
syscall = -1;
#endif
return syscall;
}
static void
deadcheck(P)
register process_t *P;
{
if (P->pfd == 0)
P->state = PS_DEAD;
else {
while (Ioctl(P->pfd, PIOCSTATUS, (int)&P->why) != 0) {
switch (errno) {
default:
/* process is dead */
if (P->pfd != 0)
(void) close(P->pfd);
P->pfd = 0;
P->state = PS_DEAD;
break;
case EINTR:
case ERESTART:
continue;
case EAGAIN:
P->state = PS_LOST;
break;
}
break;
}
}
}
int
Pgetregs(P) /* get values of registers from stopped process */
register process_t *P;
{
if (P->state != PS_STOP)
return -1;
return 0; /* registers are always available */
}
int
Pgetareg(P, reg) /* get the value of one register from stopped process */
register process_t *P;
register int reg; /* register number */
{
if (reg < 0 || reg >= NREGS) {
(void) fprintf(stderr,
"Pgetareg(): invalid register number, %d\n", reg);
return -1;
}
if (P->state != PS_STOP)
return -1;
return 0; /* registers are always available */
}
int
Pputregs(P) /* put values of registers into stopped process */
register process_t *P;
{
if (P->state != PS_STOP)
return -1;
P->setregs = TRUE; /* set registers before continuing */
return 0;
}
int
Pputareg(P, reg) /* put value of one register into stopped process */
register process_t *P;
register int reg; /* register number */
{
if (reg < 0 || reg >= NREGS) {
(void) fprintf(stderr,
"Pputareg(): invalid register number, %d\n", reg);
return -1;
}
if (P->state != PS_STOP)
return -1;
P->setregs = TRUE; /* set registers before continuing */
return 0;
}
int
Psetrun(P, sig, flags)
register process_t *P;
int sig; /* signal to pass to process */
register int flags; /* flags: PRCSIG|PRSTEP|PRSABORT|PRSTOP */
{
register int request; /* for setting signal */
register int why = P->why.pr_why;
siginfo_t info;
struct prrun prrun;
if (sig < 0 || sig > PRMAXSIG
|| P->state != PS_STOP)
return -1;
if (sig) {
if (flags & PRCSIG)
request = PIOCKILL;
else {
switch (why) {
case PR_REQUESTED:
case PR_SIGNALLED:
request = PIOCSSIG;
break;
default:
request = PIOCKILL;
break;
}
}
}
/* must be initialized to zero */
(void) memset((char *)&prrun, 0, sizeof(prrun));
(void) memset((char *)&info, 0, sizeof(info));
info.si_signo = sig;
prrun.pr_flags = flags & ~(PRSTRACE|PRSHOLD|PRSFAULT|PRSVADDR);
if (P->setsig) {
prrun.pr_flags |= PRSTRACE;
prrun.pr_trace = P->sigmask;
}
if (P->sethold) {
prrun.pr_flags |= PRSHOLD;
prrun.pr_sighold = P->why.pr_sighold;
}
if (P->setfault) {
prrun.pr_flags |= PRSFAULT;
prrun.pr_fault = P->faultmask;
}
while ((P->setentry && Ioctl(P->pfd, PIOCSENTRY, (int)&P->sysentry) == -1)
|| (P->setexit && Ioctl(P->pfd, PIOCSEXIT, (int)&P->sysexit) == -1)
|| (P->setregs && Ioctl(P->pfd, PIOCSREG, (int)&P->REG[0]) == -1)
|| (sig && Ioctl(P->pfd, request, (int)&info) == -1)) {
if (errno != EBUSY || Ioctl(P->pfd, PIOCWSTOP, 0) != 0)
goto bad;
}
P->setentry = FALSE;
P->setexit = FALSE;
P->setregs = FALSE;
if (Ioctl(P->pfd, PIOCRUN, prrun.pr_flags? (int)&prrun : 0) == -1) {
if ((why != PR_SIGNALLED && why != PR_JOBCONTROL)
|| errno != EBUSY)
goto bad;
goto out; /* ptrace()ed or jobcontrol stop -- back off */
}
P->setsig = FALSE;
P->sethold = FALSE;
P->setfault = FALSE;
out:
P->state = (flags&PRSTEP)? PS_STEP : PS_RUN;
return 0;
bad:
if (errno == ENOENT)
goto out;
perror("Psetrun");
return -1;
}
int
Pstart(P, sig)
register process_t *P;
int sig; /* signal to pass to process */
{
return Psetrun(P, sig, 0);
}
int
Pterm(P)
register process_t *P;
{
int sig = SIGKILL;
if (debugflag)
(void) fprintf(stderr,
"Pterm: terminating pid # %ld\n", P->upid);
if (P->state == PS_STOP)
(void) Pstart(P, SIGKILL);
(void) Ioctl(P->pfd, PIOCKILL, (int)&sig); /* make sure */
(void) kill((int)P->upid, SIGKILL); /* make double sure */
if (P->pfd != 0)
(void) close(P->pfd);
/* zap the process structure */
(void) memset((char *)P, 0, sizeof(*P));
return 0;
}
int
Pread(P, address, buf, nbyte)
register process_t *P;
long address; /* address in process */
char *buf; /* caller's buffer */
int nbyte; /* number of bytes to read */
{
if (nbyte <= 0)
return 0;
return pread(P->pfd, buf, (unsigned)nbyte, (off_t)address);
}
int
Pwrite(P, address, buf, nbyte)
register process_t *P;
long address; /* address in process */
CONST char *buf; /* caller's buffer */
int nbyte; /* number of bytes to write */
{
if (nbyte <= 0)
return 0;
return pwrite(P->pfd, buf, (unsigned)nbyte, (off_t)address);
}
int
Psignal(P, which, stop) /* action on specified signal */
register process_t *P; /* program table exit */
register int which; /* signal number */
register int stop; /* if TRUE, stop process; else let it go */
{
register int oldval;
if (which <= 0 || which > PRMAXSIG || (which == SIGKILL && stop))
return -1;
oldval = prismember(&P->sigmask, which)? TRUE : FALSE;
if (stop) { /* stop process on receipt of signal */
if (!oldval) {
praddset(&P->sigmask, which);
P->setsig = TRUE;
}
}
else { /* let process continue on receipt of signal */
if (oldval) {
prdelset(&P->sigmask, which);
P->setsig = TRUE;
}
}
return oldval;
}
int
Pfault(P, which, stop) /* action on specified fault */
register process_t *P; /* program table exit */
register int which; /* fault number */
register int stop; /* if TRUE, stop process; else let it go */
{
register int oldval;
if (which <= 0 || which > PRMAXFAULT)
return -1;
oldval = prismember(&P->faultmask, which)? TRUE : FALSE;
if (stop) { /* stop process on receipt of fault */
if (!oldval) {
praddset(&P->faultmask, which);
P->setfault = TRUE;
}
}
else { /* let process continue on receipt of fault */
if (oldval) {
prdelset(&P->faultmask, which);
P->setfault = TRUE;
}
}
return oldval;
}
int
Psysentry(P, which, stop) /* action on specified system call entry */
register process_t *P; /* program table entry */
register int which; /* system call number */
register int stop; /* if TRUE, stop process; else let it go */
{
register int oldval;
if (which <= 0 || which > PRMAXSYS)
return -1;
oldval = prismember(&P->sysentry, which)? TRUE : FALSE;
if (stop) { /* stop process on sys call */
if (!oldval) {
praddset(&P->sysentry, which);
P->setentry = TRUE;
}
}
else { /* don't stop process on sys call */
if (oldval) {
prdelset(&P->sysentry, which);
P->setentry = TRUE;
}
}
return oldval;
}
int
Psysexit(P, which, stop) /* action on specified system call exit */
register process_t *P; /* program table exit */
register int which; /* system call number */
register int stop; /* if TRUE, stop process; else let it go */
{
register int oldval;
if (which <= 0 || which > PRMAXSYS)
return -1;
oldval = prismember(&P->sysexit, which)? TRUE : FALSE;
if (stop) { /* stop process on sys call exit */
if (!oldval) {
praddset(&P->sysexit, which);
P->setexit = TRUE;
}
}
else { /* don't stop process on sys call exit */
if (oldval) {
prdelset(&P->sysexit, which);
P->setexit = TRUE;
}
}
return oldval;
}
static int
execute(P, sysindex) /* execute the syscall instruction */
register process_t *P; /* process control structure */
int sysindex; /* system call index */
{
sigset_t hold; /* mask of held signals */
int sentry; /* old value of stop-on-syscall-entry */
/* move current signal back to pending */
if (P->why.pr_cursig) {
int sig = P->why.pr_cursig;
(void) Ioctl(P->pfd, PIOCSSIG, 0);
(void) Ioctl(P->pfd, PIOCKILL, (int)&sig);
P->why.pr_cursig = 0;
}
sentry = Psysentry(P, sysindex, TRUE); /* set stop-on-syscall-entry */
hold = P->why.pr_sighold; /* remember signal hold mask */
prfillset(&P->why.pr_sighold); /* hold all signals */
P->sethold = TRUE;
if (Psetrun(P, 0, PRCSIG) == -1)
goto bad;
while (P->state == PS_RUN)
(void) Pwait(P);
if (P->state != PS_STOP)
goto bad;
P->why.pr_sighold = hold; /* restore hold mask */
P->sethold = TRUE;
(void) Psysentry(P, sysindex, sentry); /* restore sysentry stop */
if (P->why.pr_why == PR_SYSENTRY
&& P->why.pr_what == sysindex)
return 0;
bad:
return -1;
}
/* worst-case alignment for objects on the stack */
#if i386 /* stack grows down, non-aligned */
#define ALIGN(sp) (sp)
#define ARGOFF 1
#endif
#if u3b2 /* stack grows up, word-aligned */
#define ALIGN(sp) (((sp) + sizeof(int) - 1) & ~(sizeof(int) - 1))
#define ARGOFF 0
#endif
#if sparc /* stack grows down, doubleword-aligned */
#define ALIGN(sp) ((sp) & ~(2*sizeof(int) - 1))
#define ARGOFF 0
#endif
struct sysret /* perform system call in controlled process */
Psyscall(P, sysindex, nargs, argp)
register process_t *P; /* process control structure */
int sysindex; /* system call index */
register int nargs; /* number of arguments to system call */
struct argdes *argp; /* argument descriptor array */
{
register struct argdes *adp; /* pointer to argument descriptor */
struct sysret rval; /* return value */
register int i; /* general index value */
register int Perr = 0; /* local error number */
int sexit; /* old value of stop-on-syscall-exit */
GREG sp; /* adjusted stack pointer */
GREG ap; /* adjusted argument pointer */
GREGSET savedreg; /* remembered registers */
int arglist[MAXARGS+2]; /* syscall arglist */
int why = P->why.pr_why; /* reason for stopping */
int what = P->why.pr_what; /* detailed reason (syscall, signal) */
/* block (hold) all signals for the duration. */
sigset_t block, unblock;
(void) sigfillset(&block);
(void) sigemptyset(&unblock);
(void) sigprocmask(SIG_BLOCK, &block, &unblock);
rval.errno = 0; /* initialize return value */
rval.r0 = 0;
rval.r1 = 0;
premptyset(&psigs); /* no saved signals yet */
if (sysindex <= 0 || sysindex > PRMAXSYS /* programming error */
|| nargs < 0 || nargs > MAXARGS)
goto bad1;
if (P->state != PS_STOP /* check state of process */
|| (P->why.pr_flags & PR_ASLEEP)
|| Pgetregs(P) != 0)
goto bad2;
for (i = 0; i < NREGS; i++) /* remember registers */
savedreg[i] = P->REG[i];
if (checksyscall(P)) /* bad text ? */
goto bad3;
/* validate arguments and compute the stack frame parameters --- */
sp = savedreg[R_SP]; /* begin with the current stack pointer */
sp = ALIGN(sp);
for (i=0, adp=argp; i<nargs; i++, adp++) { /* for each argument */
rval.r0 = i; /* in case of error */
switch (adp->type) {
default: /* programming error */
goto bad4;
case AT_BYVAL: /* simple argument */
break;
case AT_BYREF: /* must allocate space */
switch (adp->inout) {
case AI_INPUT:
case AI_OUTPUT:
case AI_INOUT:
if (adp->object == NULL)
goto bad5; /* programming error */
break;
default: /* programming error */
goto bad6;
}
/* allocate stack space for BYREF argument */
if (adp->len <= 0 || adp->len > MAXARGL)
goto bad7; /* programming error */
#if u3b2 /* upward stack growth */
adp->value = sp; /* stack address for object */
sp = ALIGN(sp + adp->len);
#elif sparc || i386 /* downward stack growth */
sp = ALIGN(sp - adp->len);
adp->value = sp; /* stack address for object */
#endif
break;
}
}
rval.r0 = 0; /* in case of error */
#if u3b2
ap = sp; /* address of arg list */
sp += sizeof(int)*(nargs+2); /* space for arg list + CALL parms */
#endif
#if i386
ap = sp; /* address of arg list */
sp -= sizeof(int)*(nargs+2); /* space for arg list + CALL parms */
#endif
#if sparc
sp -= (nargs>6)? sizeof(int)*(16+1+nargs) : sizeof(int)*(16+1+6);
sp = ALIGN(sp);
ap = sp+(16+1)*sizeof(int); /* address of arg dump area */
#endif
/* point of no return */
/* special treatment of stopped-on-syscall-entry */
/* move the process to the stopped-on-syscall-exit state */
if (why == PR_SYSENTRY) {
/* arrange to reissue sys call */
#if !sparc
savedreg[R_PC] -= sizeof(syscall_t);
#endif
sexit = Psysexit(P, what, TRUE); /* catch this syscall exit */
if (Psetrun(P, 0, PRSABORT) != 0 /* abort sys call */
|| Pwait(P) != 0
|| P->state != PS_STOP
|| P->why.pr_why != PR_SYSEXIT
|| P->why.pr_what != what
|| Pgetareg(P, R_PS) != 0
|| Pgetareg(P, 0) != 0
|| (P->REG[R_PS] & ERRBIT) == 0
|| (P->REG[R_R0] != EINTR && P->REG[R_R0] != ERESTART)) {
(void) fprintf(stderr,
"Psyscall(): cannot abort sys call\n");
(void) Psysexit(P, what, sexit);
goto bad9;
}
(void) Psysexit(P, what, sexit);/* restore previous exit trap */
}
/* perform the system call entry, adjusting %sp */
/* this moves the process to the stopped-on-syscall-entry state */
/* just before the arguments to the sys call are fetched */
(void) Psetsysnum(P, sysindex);
P->REG[R_SP] = sp;
P->REG[R_PC] = P->sysaddr; /* address of syscall */
#if sparc
P->REG[R_nPC] = P->sysaddr+sizeof(syscall_t);
#elif u3b2
P->REG[R_AP] = ap;
#endif
(void) Pputregs(P);
if (execute(P, sysindex) != 0 /* execute the syscall instruction */
#if sparc
|| P->REG[R_PC] != P->sysaddr
|| P->REG[R_nPC] != P->sysaddr+sizeof(syscall_t)
#else
|| P->REG[R_PC] != P->sysaddr+sizeof(syscall_t)
#endif
)
goto bad10;
/* stopped at syscall entry; copy arguments to stack frame */
for (i=0, adp=argp; i<nargs; i++, adp++) { /* for each argument */
rval.r0 = i; /* in case of error */
if (adp->type != AT_BYVAL
&& adp->inout != AI_OUTPUT) {
/* copy input byref parameter to process */
if (Pwrite(P, (long)adp->value, adp->object, adp->len)
!= adp->len)
goto bad17;
}
arglist[ARGOFF+i] = adp->value;
#if sparc
if (i < 6) {
P->REG[R_O0+i] = adp->value;
(void) Pputareg(P, R_O0+i);
}
#endif
}
rval.r0 = 0; /* in case of error */
#if i386
arglist[0] = savedreg[R_PC]; /* CALL parameters */
if (Pwrite(P, (long)sp, (char *)&arglist[0], (int)sizeof(int)*(nargs+1))
!= sizeof(int)*(nargs+1))
goto bad18;
#endif
#if u3b2
arglist[nargs] = savedreg[R_PC]; /* CALL parameters */
arglist[nargs+1] = savedreg[R_AP];
if (Pwrite(P, (long)ap, (char *)&arglist[0], (int)sizeof(int)*(nargs+2))
!= sizeof(int)*(nargs+2))
goto bad18;
#endif
#if sparc
if (nargs > 6
&& Pwrite(P, (long)ap, (char *)&arglist[0], (int)sizeof(int)*nargs)
!= sizeof(int)*nargs)
goto bad18;
#endif
/* complete the system call */
/* this moves the process to the stopped-on-syscall-exit state */
sexit = Psysexit(P, sysindex, TRUE); /* catch this syscall exit */
do { /* allow process to receive signals in sys call */
if (Psetrun(P, 0, 0) == -1)
goto bad21;
while (P->state == PS_RUN)
(void) Pwait(P);
} while (P->state == PS_STOP && P->why.pr_why == PR_SIGNALLED);
(void) Psysexit(P, sysindex, sexit); /* restore original setting */
if (P->state != PS_STOP
|| P->why.pr_why != PR_SYSEXIT)
goto bad22;
if (P->why.pr_what != sysindex)
goto bad23;
#if sparc
if (P->REG[R_PC] != P->sysaddr+sizeof(syscall_t)
|| P->REG[R_nPC] != P->sysaddr+2*sizeof(syscall_t))
#else
if (P->REG[R_PC] != P->sysaddr+sizeof(syscall_t))
#endif
goto bad24;
/* fetch output arguments back from process */
#if u3b2
if (Pread(P, (long)ap, (char *)&arglist[0], (int)sizeof(int)*(nargs+2))
!= sizeof(int)*(nargs+2))
goto bad25;
#endif
#if i386
if (Pread(P, (long)sp, (char *)&arglist[0], (int)sizeof(int)*(nargs+1))
!= sizeof(int)*(nargs+1))
goto bad25;
#endif
for (i=0, adp=argp; i<nargs; i++, adp++) { /* for each argument */
rval.r0 = i; /* in case of error */
if (adp->type != AT_BYVAL
&& adp->inout != AI_INPUT) {
/* copy output byref parameter from process */
if (Pread(P, (long)adp->value, adp->object, adp->len)
!= adp->len)
goto bad26;
}
adp->value = arglist[ARGOFF+i];
}
/* get the return values from the syscall */
if (P->REG[R_PS] & ERRBIT) { /* error */
rval.errno = P->REG[R_R0];
rval.r0 = -1;
}
else { /* normal return */
rval.r0 = P->REG[R_R0];
rval.r1 = P->REG[R_R1];
}
goto good;
bad26: Perr++;
bad25: Perr++;
bad24: Perr++;
bad23: Perr++;
bad22: Perr++;
bad21: Perr++;
Perr++;
Perr++;
bad18: Perr++;
bad17: Perr++;
Perr++;
Perr++;
Perr++;
Perr++;
Perr++;
Perr++;
bad10: Perr++;
bad9: Perr++;
Perr += 8;
rval.errno = -Perr; /* local errors are negative */
good:
/* restore process to its previous state (almost) */
for (i = 0; i < NREGS; i++) /* restore remembered registers */
P->REG[i] = savedreg[i];
(void) Pputregs(P);
if (why == PR_SYSENTRY /* special treatment */
&& execute(P, what) != 0) { /* get back to the syscall */
(void) fprintf(stderr,
"Psyscall(): cannot reissue sys call\n");
if (Perr == 0)
rval.errno = -27;
}
P->why.pr_why = why;
P->why.pr_what = what;
goto out;
bad8: Perr++;
bad7: Perr++;
bad6: Perr++;
bad5: Perr++;
bad4: Perr++;
bad3: Perr++;
bad2: Perr++;
bad1: Perr++;
rval.errno = -Perr; /* local errors are negative */
out:
/* unblock (release) all signals before returning */
(void) sigprocmask(SIG_SETMASK, &unblock, (sigset_t *)NULL);
return rval;
}
static int
checksyscall(P) /* check syscall instruction in process */
process_t *P;
{
/* this should always succeed--we always have a good syscall address */
syscall_t instr; /* holds one syscall instruction */
return(
#if i386
(Pread(P, P->sysaddr, (char *)instr, sizeof(instr)) == sizeof(instr)
&& instr[0] == SYSCALL)?
#else
(Pread(P, P->sysaddr, (char *)&instr, sizeof(instr)) == sizeof(instr)
&& instr == SYSCALL)?
#endif
0 : -1 );
}
int
Ioctl(fd, request, arg) /* deal with RFS congestion */
int fd;
int request;
int arg;
{
register int rc;
char str[40];
for(;;) {
if ((rc = ioctl(fd, request, arg)) != -1
|| errno != ENOMEM)
return rc;
if (debugflag) {
(void) sprintf(str, "\t *** Ioctl(0x%X): ENOMEM\n",
request);
(void) write(2, str, (unsigned)strlen(str));
}
}
}
/* test for empty set */
/* support routine used by isemptyset() macro */
int
is_empty(sp, n)
register CONST long * sp; /* pointer to set (array of longs) */
register unsigned n; /* number of longs in set */
{
if (n) {
do {
if (*sp++)
return FALSE;
} while (--n);
}
return TRUE;
}