#ident "@(#)pcontrol.c 1.2 95/06/26 SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcontrol.h" #include "ramdata.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 deadcheck( process_t * ); static int execute( process_t * , int ); static int checksyscall( process_t * ); #else /* defined(__STDC__) */ static void deadcheck(); static int execute(); static int checksyscall(); #endif /* defined(__STDC__) */ static int dupfd(fd, dfd) register int fd; register int dfd; { /* * Make sure fd not one of 0, 1, or 2. * This allows the program to work when spawned by init(1m). * Also, if dfd is non-zero, dup the fd to be dfd. */ if (dfd > 0 || (0 <= fd && fd <= 2)) { if (dfd <= 0) dfd = 3; dfd = fcntl(fd, F_DUPFD, dfd); (void) close(fd); fd = dfd; } /* * Mark it close-on-exec so any created process doesn't inherit it. */ if (fd >= 0) (void) fcntl(fd, F_SETFD, 1); return (fd); } 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; P->pfd = -1; P->lwpfd = -1; /* 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 || (fd = dupfd(fd, 0)) < 0) { perror("Pcreate open()"); (void) kill(upid, SIGKILL); return -1; } P->pfd = fd; /* 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"); (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 = -1; 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 = -1; return -1; } (void) Pwait(P); /* wait for exec() or exit() */ return 0; } 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 pfd = -1; int err; int rc; prstatus_t prstat; int ruid; struct prcred prcred; char procname[100]; again: /* Come back here if we lose it in the Window of Vulnerability */ if (pfd >= 0) { (void) close(pfd); pfd = -1; } (void) memset((char *)P, 0, sizeof(*P)); P->pfd = -1; P->lwpfd = -1; /* generate the /proc/upid filename */ (void) sprintf(procname, "%s/%d", 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 (((pfd = open(procname, (O_RDWR|O_EXCL))) < 0 && (pfd = (force? open(procname, O_RDWR) : -1)) < 0) || (pfd = dupfd(pfd, 0)) < 0) { switch (errno) { case EBUSY: return G_BUSY; case ENOENT: return G_NOPROC; case EACCES: case EPERM: return G_PERM; default: perror("Pgrab open()"); return G_STRANGE; } } /* ---------------------------------------------------- */ /* 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. */ /* ---------------------------------------------------- */ /* 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(pfd, PIOCCRED, (int)&prcred) == -1) { if (errno == EAGAIN) /* WoV */ goto again; if (errno == ENOENT) /* Don't complain about zombies */ rc = G_NOPROC; else { perror("Pgrab PIOCCRED"); rc = G_STRANGE; } (void) close(pfd); return rc; } if ((ruid = getuid()) != 0 /* super-user allowed anything */ && ruid != prcred.pr_ruid) { /* credentials check failed */ (void) close(pfd); errno = EACCES; return G_PERM; } /* get the ps information, even if it is a system process or ourself */ if (Ioctl(pfd, PIOCPSINFO, (int)&P->psinfo) == -1) { if (errno == EAGAIN) /* WoV */ goto again; if (errno == ENOENT) /* Don't complain about zombies */ rc = G_NOPROC; else { perror("Pgrab PIOCPSINFO"); rc = G_STRANGE; } (void) close(pfd); return rc; } /* * Get the process's status. */ if (Ioctl(pfd, PIOCSTATUS, (int)&prstat) != 0) { if (errno == EAGAIN) /* WoV */ goto again; if (errno == ENOENT) /* Don't complain about zombies */ rc = G_ZOMB; else { perror("Pgrab PIOCSTATUS"); rc = G_STRANGE; } (void) close(pfd); return rc; } /* * If the process is a system process, we can't control it. */ if (prstat.pr_flags & PR_ISSYS) { (void) close(pfd); return G_SYS; } P->cntrl = TRUE; P->child = FALSE; P->state = PS_RUN; P->upid = upid; P->pfd = pfd; /* before stopping the process, make sure it's not ourself */ if (upid == getpid()) { /* * Verify that the process is really ourself: * Set a magic number, read it through the * /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(pfd); (void) memset((char *)P, 0, sizeof(*P)); P->pfd = -1; P->lwpfd = -1; return G_SELF; } } /* * If the process is already stopped or has been directed * to stop via /proc, do not set run-on-last-close. */ if (prstat.pr_flags & (PR_ISTOP|PR_DSTOP)) { (void) Pstatus(P, PIOCSTATUS, 0); goto out; } /* * Mark the process run-on-last-close so * it runs even if we die from SIGKILL. */ if (Ioctl(pfd, PIOCSRLC, 0) != 0) { if (errno == EAGAIN) /* WoV */ goto again; if (errno == ENOENT) /* Don't complain about zombies */ rc = G_ZOMB; else { perror("Pgrab PIOCSRLC"); rc = G_STRANGE; } (void) close(pfd); (void) memset((char *)P, 0, sizeof(*P)); P->pfd = -1; P->lwpfd = -1; return rc; } /* 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 && errno != ENOENT) { perror("Pgrab PIOCSTOP"); rc = G_STRANGE; } else { rc = G_ZOMB; } (void) close(pfd); (void) memset((char *)P, 0, sizeof(*P)); P->pfd = -1; P->lwpfd = -1; return rc; } } out: /* * Process should either be stopped via /proc or * there should be an outstanding stop directive. */ if (P->state != PS_STOP && (P->why.pr_flags & (PR_ISTOP|PR_DSTOP)) == 0) { (void) fprintf(stderr, "Pgrab: process is not stopped\n"); (void) close(pfd); return G_STRANGE; } /* Process is or will be stopped, these will "certainly" not fail */ rc = 0; if (Ioctl(pfd, PIOCGTRACE, (int)&P->sigmask) == -1) { perror("Pgrab PIOCGTRACE"); rc = G_STRANGE; } else if (Ioctl(pfd, PIOCGFAULT, (int)&P->faultmask) == -1) { perror("Pgrab PIOCGFAULT"); rc = G_STRANGE; } else if (Ioctl(pfd, PIOCGENTRY, (int)&P->sysentry) == -1) { perror("Pgrab PIOCGENTRY"); rc = G_STRANGE; } else if (Ioctl(pfd, PIOCGEXIT, (int)&P->sysexit) == -1) { perror("Pgrab PIOCGEXIT"); rc = G_STRANGE; } if (rc) { (void) close(pfd); (void) memset((char *)P, 0, sizeof(*P)); P->pfd = -1; P->lwpfd = -1; } return rc; } /* * Ensure that the lwp's signal mask and the * lwp registers are flushed to the lwp. */ static int Psync(P) register process_t *P; { int fd = (P->lwpfd >= 0)? P->lwpfd : P->pfd; int rc = 0; if (P->sethold && Ioctl(fd, PIOCSHOLD, (int)&P->why.pr_sighold) != 0) rc = -1; if (P->setregs && Ioctl(fd, PIOCSREG, (int)&P->REG[0]) != 0) rc = -1; P->sethold = FALSE; P->setregs = FALSE; return rc; } int Pchoose(P) /* choose an lwp for further operations */ register process_t *P; { int fd; id_t *lwpid; int nlwp; int i; Punchoose(P); nlwp = P->why.pr_nlwp; if (nlwp <= 0) nlwp = 1; lwpid = malloc((nlwp+1)*sizeof(id_t)); if (Ioctl(P->pfd, PIOCLWPIDS, (int)lwpid) != 0) { free(lwpid); return -1; } /* search for an lwp --- */ for (i = 0; i < nlwp; i++) { /* open the lwp file */ if ((fd = Ioctl(P->pfd, PIOCOPENLWP, (int)&lwpid[i])) < 0 || (fd = dupfd(fd, 0)) < 0) continue; P->lwpfd = fd; /* get the lwp status */ if (Pstatus(P, PIOCSTATUS, 0) != 0) break; /* avoid the aslwp, if possible */ if ((P->why.pr_flags & PR_ASLWP) && P->why.pr_nlwp > 1) { Punchoose(P); continue; } /* we have the lwp we want */ free(lwpid); return 0; } Punchoose(P); free(lwpid); return -1; } /* undo Pchoose() */ void Punchoose(P) register process_t *P; { (void) Psync(P); if (P->lwpfd >= 0) (void) close(P->lwpfd); P->lwpfd = -1; /* refresh the process status */ (void) Pstatus(P, PIOCSTATUS, 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]; if (P->lwpfd >= 0) (void) close(P->lwpfd); P->lwpfd = -1; /* 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) || close(P->pfd) < 0 || (fd = dupfd(fd, P->pfd)) < 0) { if (debugflag) perror("Preopen open()"); (void) close(P->pfd); P->pfd = -1; return -1; } P->pfd = fd; /* 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; /* 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; { int jsig = P->jsig; if (P->pfd < 0) return -1; /* 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) { /* we didn't lose it, set the registers */ if (Psync(P) != 0) perror("Prelease PIOCSHOLD/PIOCSREG"); } (void) Ioctl(P->pfd, PIOCRFORK, 0); (void) Ioctl(P->pfd, PIOCSRLC, 0); if (jsig) (void) Ioctl(P->pfd, PIOCKILL, (int)&jsig); /* closing the files sets the process running */ if (P->lwpfd >= 0) (void) close(P->lwpfd); if (P->pfd >= 0) (void) close(P->pfd); /* zap the process structure */ (void) memset((char *)P, 0, sizeof(*P)); P->pfd = -1; P->lwpfd = -1; return 0; } int Phang(P) /* release process but leave it stopped */ register process_t *P; { register int count; long flags; sysset_t sysset; sigset_t sigset; fltset_t fltset; if (P->pfd < 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) { /* we didn't lose it; do more */ if (Psync(P) != 0) perror("Phang PIOCSHOLD/PIOCSREG"); /* no run-on-last-close or kill-on-last-close*/ flags = PR_RLC|PR_KLC| /* PR_ASYNC| */ PR_PCOMPAT; if (Ioctl(P->pfd, PIOCRESET, (int)&flags) != 0) perror("Phang PIOCRESET"); /* inherit-on-fork */ flags = PR_FORK; if (Ioctl(P->pfd, PIOCSET, (int)&flags) != 0) perror("Phang PIOCSET"); /* no signal tracing */ premptyset(&sigset); if (ioctl(P->pfd, PIOCSTRACE, (int)&sigset) != 0) perror("Phang PIOCSTRACE"); /* no fault tracing */ premptyset(&fltset); if (ioctl(P->pfd, PIOCSFAULT, (int)&fltset) != 0) perror("Phang PIOCSFAULT"); /* no syscall tracing */ premptyset(&sysset); if (ioctl(P->pfd, PIOCSENTRY, (int)&sysset) != 0) perror("Phang PIOCSENTRY"); premptyset(&sysset); if (ioctl(P->pfd, PIOCSEXIT, (int)&sysset) != 0) perror("Phang PIOCSENTRY"); } if (P->lwpfd >= 0) (void) close(P->lwpfd); if (P->pfd >= 0) (void) close(P->pfd); /* zap the process structure */ (void) memset((char *)P, 0, sizeof(*P)); P->pfd = -1; P->lwpfd = -1; return 0; } 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; int pfd = (P->lwpfd >= 0)? P->lwpfd : P->pfd; 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(pfd, request, (int)&P->why) != 0) { err = errno; if (sec) (void) alarm(0); if (request != PIOCSTATUS && (err == EINTR || err == ERESTART) && Ioctl(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: break; case EAGAIN: /* we lost control of the the process */ P->state = PS_LOST; break; default: /* check for dead process */ if (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; switch (P->why.pr_why) { case PR_REQUESTED: case PR_SIGNALLED: case PR_FAULTED: case PR_JOBCONTROL: #ifdef PR_SUSPENDED case PR_SUSPENDED: #endif break; case PR_SYSENTRY: /* remember syscall address */ #if sparc #if nono P->sysaddr = P->REG[R_PC]; #endif break; #endif case PR_SYSEXIT: #if nono P->sysaddr = P->REG[R_PC]-sizeof(syscall_t); #endif break; default: status = -1; break; } 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 syscall = P->why.pr_syscall; #endif return syscall; } int Psetsysnum(P, syscall) /* we are at a syscall trap, prepare to issue syscall */ register process_t *P; register int syscall; { #if i386 P->REG[R_R0] = syscall; if (Pputareg(P,R_R0)) syscall = -1; #endif #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; { int pfd = (P->lwpfd >= 0)? P->lwpfd : P->pfd; if (pfd < 0) P->state = PS_DEAD; else { while (Ioctl(pfd, PIOCSTATUS, (int)&P->why) != 0) { switch (errno) { default: /* process or lwp is dead */ 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 >= NGREG) { (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 >= NGREG) { (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|PRCFAULT|PRSTEP|PRSABORT|PRSTOP */ { register int request; /* for setting signal */ register int why = P->why.pr_why; int pfd = (P->lwpfd >= 0)? P->lwpfd : P->pfd; 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(pfd, PIOCSENTRY, (int)&P->sysentry) == -1) || (P->setexit && Ioctl(pfd, PIOCSEXIT, (int)&P->sysexit) == -1) || (P->setregs && Ioctl(pfd, PIOCSREG, (int)&P->REG[0]) == -1) || (sig && Ioctl(pfd, request, (int)&info) == -1)) { if (errno != EBUSY || Ioctl(pfd, PIOCWSTOP, 0) != 0) goto bad; } P->setentry = FALSE; P->setexit = FALSE; P->setregs = FALSE; if (Ioctl(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 (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->lwpfd >= 0) (void) close(P->lwpfd); if (P->pfd >= 0) (void) close(P->pfd); /* zap the process structure */ (void) memset((char *)P, 0, sizeof(*P)); P->pfd = -1; P->lwpfd = -1; 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 */ { int pfd = (P->lwpfd >= 0)? P->lwpfd : P->pfd; sigset_t hold; /* mask of held signals */ int sentry; /* old value of stop-on-syscall-entry */ sysset_t entryset; /* move current signal back to pending */ if (P->why.pr_cursig) { int sig = P->why.pr_cursig; (void) Ioctl(pfd, PIOCSSIG, 0); (void) Ioctl(pfd, PIOCKILL, (int)&sig); P->why.pr_cursig = 0; } #if 1 entryset = P->sysentry; prfillset(&P->sysentry); P->setentry = TRUE; #else sentry = Psysentry(P, sysindex, TRUE); /* set stop-on-syscall-entry */ #endif 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; #if 1 P->sysentry = entryset; P->setentry = TRUE; #else (void) Psysentry(P, sysindex, sentry); /* restore sysentry stop */ #endif if (P->why.pr_why == PR_SYSENTRY && P->why.pr_what == sysindex) return 0; bad: fprintf(stderr, "execute(): expected %d/%d, got %d/%d\n", PR_SYSENTRY, sysindex, P->why.pr_why, P->why.pr_what); 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_t sp; /* adjusted stack pointer */ greg_t ap; /* adjusted argument pointer */ gregset_t savedreg; /* remembered registers */ int arglist[MAXARGS+2]; /* syscall arglist */ int why; /* reason for stopping */ int what; /* detailed reason (syscall, signal) */ sigset_t block, unblock; int waschosen = FALSE; /* block (hold) all signals for the duration. */ (void) sigfillset(&block); (void) sigemptyset(&unblock); (void) sigprocmask(SIG_BLOCK, &block, &unblock); rval.errno = 0; /* initialize return value */ rval.r0 = 0; rval.r1 = 0; /* if necessary, choose an lwp from the process to do all the work */ if (P->lwpfd < 0) { if (Pchoose(P) != 0) goto bad8; waschosen = TRUE; } why = P->why.pr_why; what = P->why.pr_what; 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 < NGREG; 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; itype) { 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; itype != 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; 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; itype != 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 < NGREG; 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: /* if we chose an lwp for the operation, unchoose it now */ if (waschosen) Punchoose(P); /* 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( (Pread(P,P->sysaddr,(char *)&instr,sizeof(instr)) == sizeof(instr) #if i386 && instr[0] == SYSCALL #else && instr == (syscall_t)SYSCALL #endif )? 0 : -1 ); } int Ioctl(fd, request, arg) /* deal with RFS congestion */ int fd; int request; int arg; { register int rc; for(;;) { if ((rc = ioctl(fd, request, arg)) != -1 || errno != ENOMEM) return rc; } }