Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
kccdist/lib/usys/forkex.c
There are 5 other files named forkex.c in the archive. Click here to see a list.
/* FORKEXEC - Forking and program execution
**
** forkexec() - KCC-specific function
**
** execl(), execle(), execv(), execve(), execlp(), execvp()
** fork(), vfork()
*/
#include "c-env.h"
#include "errno.h"
#include "frkxec.h"
#include "sys/usysig.h"
#include "sys/file.h"
#if SYS_T20+SYS_10X
#include "sys/usysio.h"
#include "jsys.h"
#endif
#ifndef NULL
#define NULL 0
#endif
/* External URT data refs */
extern int _vfrkf; /* Non-zero if mem shared by vfork() */
extern int _nfork; /* Bumped every time a child is created */
#if (SYS_T20+SYS_10X)==0
#error exec*() and *fork() not supported for this system.
#endif
/* exec[lv][ ep]
** l - takes argv as variable number of args in call
** v - takes argv as pointer to array of args
** - environment pointer taken from current environment
** e - environment pointer furnished as last arg in call
** p - program name lookup uses shell search rules
**
** Matrix: - p e
** l execl execlp execle
** v execv execvp execve
**
** execl (prog, arg0, arg1, ..., 0);
** execlp(prog, arg0, arg1, ..., 0); - program lookup
** execle(prog, arg0, arg1, ..., 0, envp);
** execv (prog, argv);
** execvp(prog, argv); - program lookup
** execve(prog, argv, envp); - Fundamental form of exec call
*/
static int doexec();
static char **revstack();
int
execl(prog, arg0) /* (prog, arg0, arg1, ..., 0) */
char *prog, *arg0;
{
return doexec(0, prog, revstack(&arg0), NULL);
}
int
execlp(prog, arg0) /* (prog, arg0, arg1, ..., 0) - lookup program */
char *prog, *arg0;
{
return doexec(FX_PGMSRCH, prog, revstack(&arg0), NULL);
}
int
execle(prog, arg0) /* (prog, arg0, arg1, ..., 0, envp) */
char *prog, *arg0;
{
return doexec(FX_PGMSRCH, prog, revstack(&arg0), NULL);
}
int
execv(prog, argv) /* (prog, argv) */
char *prog, **argv;
{
return doexec(0, prog, argv, NULL);
}
int
execvp(prog, argv) /* (prog, argv) - look up program */
char *prog, **argv;
{
return doexec(FX_PGMSRCH, prog, argv, NULL);
}
int
execve(prog, argv, envp) /* (prog, argv, envp) */
char *prog, *argv[], *envp[];
{
return doexec(0, prog, argv, envp);
}
/* REVSTACK - auxiliary for execl*() routines.
** Reverses an array of pointers on the stack.
** On the PDP-10 an arg list like arg1, arg2, ... 0 is stored on
** the stack such that 0 is at the lowest address, arg1 at highest.
** We need to reverse this ordering so we can treat it like a normal
** argv array (with a null pointer as the last element).
*/
static char **
revstack(av)
char **av;
{
register char **t, **b, *tmp; /* Top and bottom pointers */
register int i;
for (t = b = av; *t; --t); /* Back up to top of array (a null ptr) */
av = t; /* Remember where top is */
i = (1 + b - t) / 2; /* Find # of elements to swap in array */
for (; i > 0; --i) {
tmp = *t; /* Swap elements, and bump ptrs closer. */
*t-- = *b;
*b++ = tmp;
}
return av; /* Return ptr to top of reversed array */
}
/* DOEXEC - auxiliary for the exec*() routines.
*/
static int
doexec(flags, prog, argv, envp)
int flags;
char *prog, **argv, **envp;
{
struct frkxec f;
f.fx_flags = FX_NOFORK | (flags & FX_PGMSRCH);
f.fx_name = prog;
f.fx_argv = argv;
f.fx_envp = envp;
return forkexec(&f);
}
/*
** vfork()
**
** This keeps the same map as the superior, but makes the superior
** wait for an exec() call before continuing. Thus if all that is
** done is a store of the return value, that will get done again
** and nothing will be the worse.
**
** This cannot however be implemented merely by leaving the maps
** of the two the same, because then we wouldn't know which pid
** would be stored last. UNIX claims to borrow the thread of control
** of the superior up to the exec(), but we fake it by making the
** superior wait until a HALTF% in exec(), triggered by _vfrkf != 0.
*/
static int vferno = EAGAIN; /* Error # to use if CFORK% fails */
int
vfork()
{
#if SYS_T20+SYS_10X
asm(USYS_BEG_ASM); /* Disable interrupts */
#asm
SEARCH MONSYM /* Uppercase to avoid monsym() clash */
pop 17,16 /* Get ret addr off stack for safety */
move 1,[cr%map!cr%cap!cr%acs!cr%st!vforkr] /* Set fork going */
setz 2, /* Copying registers from ours */
cfork% /* Make a fork */
erjmpa [move 1,vferno /* Lose lose */
movem 1,errno
jrst vfrker]
wfork% /* Wait for it to synch in exec() */
move 4,1 /* Save handle (T20 may clobber AC3) */
rfsts% /* Read fork status to verify synch */
exch 1,4 /* Restore fork handle */
hlrzs 4 /* Get status code in RH */
tlz 2,-1 /* Clear LH of PC */
cain 4,.rfhlt /* Must be HALTF%'d and unfrozen */
caie 2,vfksyn /* at location of synch */
jrst vfork2
hrli 1,(sf%con) /* OK, continue child after synch! */
sfork% /* Start it again */
vfork2: setzm .vfrkf /* No longer inside vfork */
aos .nfork /* Bump count of inferiors created */
lsh 1,11 /* Shift fork handle up by 9 bits */
andi 1,777000 /* Zap all but low 9 bits of handle */
push 17,1 /* Save that */
gjinf% /* Get job # */
dpb 3,[001100,,(17)] /* Store in low 9 bits of PID */
caia
vfrker: push 17,[-1] /* CFORK failed, return -1 as error */
#endasm
asm(USYS_END_ASM);
#asm
pop 17,1
jrst (16) /* All done! */
/* Child fork of vfork() starts here!
** Note we do NOT re-enable interrupts, even though the child process
** doesn't have its PSI system on, because our memory is still shared
** and clearing .sigusys would permit the parent to handle signals.
** This could cause unexpected changes to the shared memory before
** the child gets to its exec() call.
*/
vforkr: setom .vfrkf /* Set flag saying inside vfork */
setz 1, /* This is inferior fork */
jrst (16) /* Return */
#endasm
#endif /* T20+10X */
}
/*
** fork()
** This does safe map copy before starting inferior
*/
static int _fork();
int
fork()
{
int ret;
USYS_BEG(); /* Disable interrupts */
ret = _fork(); /* Try to fork and see what we get */
if (ret == -1)
USYS_RETERR(EAGAIN); /* Assume no more processes */
_nfork++; /* Won, bump count of inferiors created! */
if (ret != 0)
USYS_RET(ret); /* We're parent, just return fork handle */
/* Here, we're the child process. May need to do some cleanup or
** init stuff here eventually, such as enabling the PSI system!
** As it is, we're in big trouble if a signal happens between now
** and an exec() call.
*/
_nfork = 0; /* Child has no inferiors yet */
USYS_RET(0);
}
static int
_fork()
{
#if SYS_T20+SYS_10X
#asm
extern $mapsc
#if SYS_T20
nsects==40
#else
nsects==1
#endif
move 1,[cr%cap!cr%acs] /* Same caps and regs as us */
setz 2, /* Copying registers from ours */
cfork% /* Make a fork */
erjmpa .+2 /* Lose lose, return -1 */
jrst fork1
seto 1,
popj 17,
fork1: move 6,1 /* Copy handle to safer place */
movei 7,.fhslf /* Mapping from self */
/*
** Set inferior map looking like ours. Can't just do fork-to-fork
** PMAP% or CR%MAP CFORK% because then any changes
** in impure data in the mother fork would be reflected in
** the daughter (but not vice versa if PM%CPY set)
*/
movei 5,nsects /* Counting off all sections */
setzb 10,11 /* Start with sect zero in each fork */
pushj 17,$mapsc /* Map section across */
sojg 5,.-1 /* Until we are done */
/* Map all set, now we can safely start the subfork and return. */
move 1,6 /* Pages all mapped, recover handle */
xmovei 2,forkst /* Set subfork to start here */
tlne 2,-1 /* If addr is in non-zero section, */
jrst fork8 /* must start using extended call */
sfork%
jrst fork9
fork8: move 3,2
setz 2,
xsfrk% /* Start it at extended address. */
fork9: /* Return PID. Fork handle in AC1 */
move 6,1 /* Save handle */
gjinf% /* Get job # in AC3 */
dpb 6,[111100,,3] /* Put low 9 bits of handle inside PID */
hrrz 1,3 /* Clear LH and return the PID */
popj 17,
/* Child fork starts here. */
extern .pidslf, .pidpar /* From GETPID */
forkst: move 1,.pidslf
movem 1,.pidpar /* If have a PID, remember as parent's */
setzb 1,.pidslf /* Zap own PID to force gen of new one */
popj 17, /* Return 0 as child's result for fork() */
#endasm
#endif /* T20+10X */
} /* End of fork() */
/* FORKEXEC - combine fork() with exec() for highest efficiency
**
*/
static int findpgm();
static char *cvtargv();
static void setpio(), _setpio(), _exec();
int
forkexec(f)
struct frkxec *f;
{
int flags;
#if SYS_T20+SYS_10X
#define RSCANLEN 1000
char rscanbuf[RSCANLEN];
char *rscanp;
int i, acs[5], startpc, stoff;
int frk = 0, jfn = 0;
#endif
USYS_BEG();
if (f == NULL)
USYS_RETERR(EINVAL);
flags = f->fx_flags;
/* Check out new standard i/o FDs, if any were given */
if ((flags & FX_FDMAP) && (
(f->fx_fdin >= 0 && (f->fx_fdin >= OPEN_MAX || !_uioufx[f->fx_fdin]))
|| (f->fx_fdout >= 0 && (f->fx_fdout >= OPEN_MAX || !_uioufx[f->fx_fdout]))
))
USYS_RETERR(EBADF);
stoff = (flags & FX_STARTOFF) ? f->fx_startoff : 0;
#if SYS_T20+SYS_10X
#if SYS_T20 /* Get RSCAN buffer into shape */
if (flags & FX_T20_RSCAN)
rscanp = f->fx_blkadr;
else if (f->fx_argv)
rscanp = cvtargv(f->fx_argv, rscanbuf, RSCANLEN);
else rscanp = NULL;
if (flags & FX_T20_PRARG) {
if (f->fx_blkadr == NULL)
USYS_RETERR(EINVAL);
}
#endif
if (flags & FX_T20_PGMJFN) /* Use JFN directly? */
jfn = (int)f->fx_name;
else if (flags & FX_T20_PGMNAME) { /* Use GTJFN% on name? */
acs[1] = GJ_OLD|GJ_SHT;
acs[2] = (int)(f->fx_name - 1);
if (jsys(GTJFN, acs) <= 0)
USYS_RETERR(ENOENT);
jfn = acs[1] & 0777777;
}
else if ((jfn = findpgm(f->fx_name, (flags & FX_PGMSRCH))) <= 0)
USYS_RETERR(ENOENT); /* Couldn't find program */
/* From this point on, must be careful to free up "jfn" if we fail. */
if (flags & FX_NOFORK) { /* Simple exec()? */
frk = _FHSLF; /* Fork is ourself */
} else {
/* Do a fork(), then apply exec() to that fork. */
acs[1] = CR_CAP;
if (jsys(CFORK, acs) <= 0) {
errno = EAGAIN; /* If fail, claim no more procs */
goto fail;
}
frk = acs[1]; /* Remember fork handle */
_nfork++; /* Bump count of forks created! */
acs[1] = (frk << 18) | jfn;
if (jsys(GET, acs) <= 0) {
errno = ENOEXEC; /* Perhaps not executable file? */
goto fail;
}
acs[1] = frk;
#if SYS_T20
if (jsys(XGVEC, acs) > 0) /* Get extended entry vector */
startpc = acs[3];
else
#endif
if (jsys(GEVEC, acs) > 0) /* Get entry vector */
startpc = (acs[2] & 0777777);
else {
errno = ENOEXEC;
goto fail;
}
}
/* This code applies whether chaining or not. */
if (rscanp) { /* Set RSCAN buffer if need to */
acs[1] = (int)(rscanp-1);
if (jsys(RSCAN, acs) <= 0) {
errno = E2BIG;
goto fail;
}
}
if (flags & FX_T20_PRARG) { /* Set PRARG% arg block */
acs[1] = (_PRAST<<18) | frk;
acs[2] = (int)(int *)f->fx_blkadr;
acs[3] = f->fx_blklen;
if (jsys(PRARG, acs) <= 0) {
errno = E2BIG;
goto fail;
}
}
/* Now must do hack to ensure that stdin and stdout are preserved
** over the map, and work even if new program is not a C program.
** This is done by setting the primary JFNs .PRIIN and .PRIOU to
** whatever stdin and stdout should be for the new fork/program.
** This is normally whatever they are for the current program.
*/
setpio(frk, (flags&FX_FDMAP) ? f->fx_fdin : -1,
(flags&FX_FDMAP) ? f->fx_fdout : -1);
/* Now if chaining, ready to load program! */
if (flags & FX_NOFORK) /* Simple exec()? */
_exec(jfn, stoff);
/* Starting inferior process! */
acs[1] = frk;
acs[2] = startpc + stoff;
if (jsys(SFORK, acs) <= 0) {
errno = EFAULT; /* What else to use? */
goto fail;
}
if (flags & FX_WAIT) { /* Wait until fork done? */
(void)USYS_END(); /* Allow interrupts while waiting */
if (jsys(WFORK, acs) > 0) { /* Wait for that fork (in AC1) */
jsys(KFORK, acs); /* if done, kill fork. */
f->fx_pid = 0;
f->fx_waitres = 0; /* Assume result is OK */
_nfork--;
}
return 0; /* Say we won */
}
jsys(GJINF, acs);
f->fx_pid = ((frk & 0777)<<9) | (acs[3] & 0777);
USYS_RET(0); /* Won! */
fail: /* Failure, clean up any debris */
if (frk) {
acs[1] = frk;
if (jsys(KFORK, acs) > 0)
_nfork--;
}
if (jfn) {
acs[1] = jfn;
jsys(RLJFN, acs);
}
#endif /* T20+10X */
USYS_RET(-1); /* Fail */
}
/* Auxiliaries for forkexec() */
#define PROGLEN 400 /* Max length of program name */
#if SYS_T20
#define PROGPRE "SYS:" /* Prefix to use when searching */
#define PROGPOST ".EXE" /* Postfix for ditto */
#elif SYS_10X
#define PROGPRE "<SYSTEM>" /* Prefix to use when searching */
#define PROGPOST ".SAV" /* Postfix for ditto */
#endif
static int
findpgm(prog, srchflg)
char *prog; /* Program name */
int srchflg; /* True if want to search for program */
{
char buf[PROGLEN], *bprog, *eprog;
int jfn, i;
if ((i = strlen(prog)) > PROGLEN-15) /* Ensure not too big; if so, give */
return _gtjfn(prog, O_RDONLY); /* up, but try using string directly */
strcpy(buf, PROGPRE); /* Put prefix at top */
bprog = buf + sizeof(PROGPRE)-1; /* Remember ptr to beg of progname */
strcpy(bprog, prog); /* Add program name */
eprog = bprog + i; /* Remember ptr to end of progname */
strcpy(eprog, PROGPOST); /* Add postfix */
if (srchflg && (jfn = _gtjfn(buf, O_RDONLY)) > 0) /* try SYS:prog.EXE */
return jfn;
if ((jfn = _gtjfn(bprog, O_RDONLY)) > 0) /* try prog.EXE */
return jfn;
*eprog = '\0'; /* Insert null to drop .EXE or .SAV */
if (srchflg && (jfn = _gtjfn(buf, O_RDONLY)) > 0) /* try SYS:prog */
return jfn;
if ((jfn = _gtjfn(prog, O_RDONLY)) > 0) /* try just prog */
return jfn;
return 0; /* All failed, give up */
}
static char *
cvtargv(av, buf, len)
char **av, *buf;
int len;
{
register char *t, *f;
if (av == NULL || *av == NULL)
return NULL; /* No args */
for (t = buf-1; *av; ++av) {
f = *av-1;
while (--len > 0 && (*++t = *++f));
*t = ' '; /* Space between each arg */
}
*t = '\n'; /* Last arg ends with linefeed, not space */
if (len > 0) ++t;
*t = '\0'; /* and a null */
return buf;
}
#if SYS_T20+SYS_10X
static void
setpio(frk, infd, outfd)
int infd, outfd;
{
int injfn, outjfn;
if (infd < 0) infd = STDIN_CH;
if (outfd < 0) outfd = STDOUT_CH;
injfn = _uioufx[infd] ? _uioch[_uioufx[infd]] : -1;
outjfn = _uioufx[outfd] ? _uioch[_uioufx[outfd]] : -1;
_setpio(frk, injfn, outjfn);
}
static void
_setpio(frk, in, out)
int frk, in, out;
{
#asm
move 1,-1(17) ; Get fork handle to use
gpjfn% ; Get current settings for primary i/o
move 5,2 ; Save them elsewhere
hrre 4,-2(17) ; Check input arg
jumpl 4,skipin ; If no setting, skip input stuff.
caie 4,.priin
hrl 2,4 ; If non-standard, set LH to new value!
skipin: hrre 4,-3(17) ; Check output arg
jumpl 4,skipo ; If no setting, skip output stuff.
caie 4,.priou
hrr 2,4 ; If non-standard, set RH to new value!
skipo:
camn 2,5 ; OK, is new setting any different from old?
popj 17,
move 1,-1(17) ; One is different, must map!
spjfn% ; Do it
#endasm
}
#endif /* T20+10X */
#if SYS_T20+SYS_10X
/*
** Low level support for exec()
**
** _exec(jfn, offset)
** Chains to the program in the file that the jfn identifies.
** Never returns if successful.
**
** If we were in vfork() we HALTF% to give the superior a chance to synch.
*/
static void
_exec(jfn, offset)
int jfn, offset;
{
#asm
extern .vfrkf /* in URT */
#if SYS_T20
movei 1,.fhslf
setz 2,
scvec% /* Reset compat package vector! */
#endif
/* NOTE!! We cannot use SFRKV% to start ourselves if the start offset
** is anything but 0, because SFRKV% does funny things if the entry
** vector is JRST FOO (as it is for program like LINK). In that case
** it starts the program at .JBREN rather than .JBSA+offset, and things
** blow up. Hence the extra code to get the entry vector explicitly,
** and we have to use XGVEC% in case the program is extended to begin
** with! Space is sure getting tight in the ACs...
*/
#if SYS_T20
movei 1,.fhslf
xgvec% /* Must find out whether this will work */
erjmp exec4 /* If failed, jump to handle old system */
/* Set up ACs for multi-section system */
move 0,[acsext,,1] /* Get BLT pointer to set regs */
blt 0,16 /* Copy all of ACs 1-16 across */
hrr acoffx,-2(17) /* Set start offset in RH of proper AC */
jrst exec5
#endif
/* Set up ACs for single-section system */
exec4: move 0,[acsone,,1] /* Get BLT pointer to set regs */
blt 0,16 /* Copy all of ACs 1-16 across */
hrr acoff1,-2(17) /* Set start offset in RH of proper AC */
exec5: move 0,-1(17) /* Save JFN for GET in AC0 */
hrli 0,.fhslf /* <handle>,,<jfn> */
/* Now no longer need memory for anything, all is in the ACs. */
skipe .vfrkf /* If in vfork() */
haltf% /* Stop and let superior catch up */
vfksyn: /* This PC is checked to ensure HALTF% is right one! */
jrst @[4] /* Jump to section 0 regs */
#if SYS_T20
/* ACs for multi-section TOPS-20 */
acsext: /* where to copy regs from */
-1 /* AC1 unmapping */
.fhslf,,0 /* AC2 from self starting page 0 */
pm%cnt!pm%epn!1000 /* AC3 to bottom of the section */
pmap% /* AC4 unmap section zero */
hrri 2,1 /* AC5 make pointer to section 1 */
movei 3,37 /* AC6 count of sections to unmap */
smap% /* AC7 unmap all nonzero sections */
erjmp 11 /* AC10 win with pre-rel-5 TOPS-20 */
move 1,0 /* AC11 get back GET% argument */
get% /* AC12 map new program into ourself */
movei 1,.fhslf /* AC13 this process again */
xgvec% /* AC14 get extended entry vector */
acoffx==15
addi 3,0 /* AC15 add start offset */
xjrstf 2 /* AC16 start there! */
/* Note that PC flags get restored from junk in AC2, but
** since the LH will normally be zero, we'll risk it.
*/
#endif /* T20 */
/* ACs for single-section system (TOPS-20 or TENEX) */
acsone:
-1 /* AC1 unmapping */
.fhslf,,0 /* AC2 from self starting page 0 */
#if SYS_T20
pm%cnt!pm%epn!1000 /* AC3 to bottom of the section */
pmap% /* AC4 unmap section zero */
jfcl /* AC5 */
jfcl /* AC6 */
#else
1000 /* AC3 TENEX has no PM%CNT */
pmap% /* AC4 unmap a page */
aoj 2, /* AC5 point to next */
sojg 3,4 /* AC6 loop until all unmapped */
#endif
move 1,0 /* AC7 get back GET% argument */
get% /* AC10 map new program into ourself */
movei 1,.fhslf /* AC11 this process again */
gevec% /* AC12 Get entry vector, and */
acoff1==13
jrst (2) /* AC13 start there, plus offset */
block 3 /* AC14, AC15, AC16 */
#endasm
}
#endif /* T20+10X */