Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/lib5/usys/old-fork.c
There are no other files named old-fork.c in the archive.
/*
** Forking and program execution
**
** execl(), execle(), execv(), execve(), execlp(), execvp()
** fork(), vfork()
*/

#include "c-env.h"
#include "errno.h"
#include "sys/usysig.h"
#include "sys/file.h"
#if SYS_T20+SYS_10X
#include "sys/usysio.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 */
/*
** Stubs for non-forking operating system execution.
** Failure return for any of the routines herein is -1.
*/

#if (SYS_T20+SYS_10X)==0
#error exec*() and *fork() not supported for this system.
#endif
/*
** Rest of file is in SYS_T20+SYS_10X conditionals.
**
** First the outer exec() calls.  These all call a common
** handler routine, doexec(), which in turn calls the
** assembler routine _exec() to actually run the program.
*/
#define SRCH_ON 1
#define SRCH_OFF 0

#if SYS_T20+SYS_10X
static int doexec();
static void _exec();

execl(prog, arg)			/* prog name, arg, arg, ..., 0 */
{
    return doexec(prog, &arg, -1, SRCH_OFF);
}

execlp (prog, arg)			/* ditto but look in SYS: */
{
    return doexec(prog, &arg, -1, SRCH_ON);
}

execle(prog, arg)			/* prog name, arg, arg, ..., 0, envp */
{
    return doexec(prog, &arg, -1, SRCH_OFF);
}

execv(prog, argv)			/* prog name, arglist */
{
    return doexec(prog, argv, 1, SRCH_OFF);
}

execvp(prog, argv)			/* ditto but look in SYS: */
{
    return doexec(prog, argv, 1, SRCH_ON);
}

execve(prog, argv)			/* prog name, arglist, envp */
{
    return doexec(prog, argv, 1, SRCH_OFF);
}
/*
** Find executable file and set up command line buffer
** Intermediate handler between execX() and _exec()
*/

static int
doexec(prog, arg, inc, srchflg)
char *prog, **arg;
int srchflg;			/* TRUE if should search for file */
{
    char buf[400], *s, *bprog;
    int jfn;

    USYS_BEG();

    /* first find the program we want to run */
    s = buf;				/* start at top */
#if SYS_T20
	*s = 'S'; *++s = 'Y'; *++s = 'S'; *++s = ':'; /* make SYS: */
#else
	strcpy(buf, "<SUBSYS>");
	s += strlen(buf)-1;
#endif
	bprog = s+1;

    while ((*++s = *prog++) && *s != '|' && *s != ' ' && *s != '\n'
			    && *s != '\r' && *s != '\0') ;
    *s = '.';				/* and ".EXE" */
#if SYS_T20
    *++s = 'E'; *++s = 'X'; *++s = 'E';
#else
    *++s = 'S'; *++s = 'A'; *++s = 'V';
#endif
    *++s = '\0';
    if (!srchflg || (jfn = _gtjfn(buf, O_RDONLY)) == 0) {/* try SYS:prog.EXE */
	if ((jfn = _gtjfn(bprog, O_RDONLY)) == 0) {	/* no, prog.EXE */
	    *(s - 4) = '\0';		/* drop null to block .EXE or .SAV */
	    if (!srchflg || (jfn = _gtjfn(buf, O_RDONLY)) == 0)	/* and try SYS:prog */
		if ((jfn = _gtjfn(bprog, O_RDONLY)) == 0)	/* try prog */
			USYS_RETERR(ENOENT);	/* _gtjfn screws errno */
	}
    }

    /* now we have a program, build its argument string */
    for (s = buf; *arg != NULL; arg += inc) if (*s = **arg) {
    	while (*++s = *++*arg) ;	/* add each non-null arg to string */
	*s++ = ' ';			/* and delimit with a space */
    }
    if (s != buf) *(s-1) = '\n';	/* tie off with linefeed over space */
    *s = '\0';				/* and a null */

    /* 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 currently are.
    */
    {
	int ijfn = -1, ojfn = -1;
	if (_uioufx[STDIN_CH])  ijfn = _uioch[_uioufx[STDIN_CH]];
	if (_uioufx[STDOUT_CH]) ojfn = _uioch[_uioufx[STDOUT_CH]];
	/* Map the primary JFNs if either JFN is non-standard! */
	_set_pio((ijfn << 18) | (ojfn & 0777777));
    }

    /* all set up, go run the program */
    _exec(buf, jfn);			/* set up RSCAN and chain to program */

    USYS_RETERR(ENOEXEC);
}
/*
** Low level support for exec()
**
** _exec (buf, jfn)
**    sets the RSCAN% command line to buf, then chains to the program
**    found at the file pointed to by jfn.  never returns if successful.
**
**    if we were in vfork() we HALTF% to give the superior a chance to synch.
*/

static void
_exec(buf, jfn)
int buf, jfn;
{
#asm
	search monsym			/* Get JSYS, flag defs */
	extern .vfrkf			/* in URT */
	extern $retz, $retn		/* in CRT */
#if SYS_T20
	%chrbp	1,-1(17)		/* Get RSCAN buffer as ILDB pointer */
	rscan%				/* Set it */
	 erjmp	$retz			/* Lost */
#endif /* T20 */

	move	0,[rconts,,1]		/* Get BLT pointer to set regs */
	blt 	0,nregs			/* Copy all the registers across */
	hrr	4,-2(17)		/* Get JFN in right half */

	/* 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	@[5]			/* Jump to section 0 regs */


rconts:					/* where to copy regs from */
	-1				/* AC1  unmapping */
	.fhslf,,0			/* AC2  into self starting page 0 */
#if SYS_T20
	pm%cnt!pm%epn!1000		/* AC3  to bottom of the section */
	.fhslf,,0			/* AC4  doing GET% into this process */
	pmap%				/* AC5  unmap section zero */
	hrri	2,1			/* AC6  make pointer to section 1 */
	movei	3,37			/* AC7  count of sections to unmap */
	smap%				/* AC10 unmap all nonzero sections */
	erjmp	12			/* AC11 win with pre-rel-5 TOPS-20 */
	move	1,4			/* AC12 get back GET% argument */
	get%				/* AC13 map new program into ourself */
	movei	1,.fhslf		/* AC14 this process again */
	setz	2,			/* AC15 main entry point */
	sfrkv%				/* AC16 start ourself */
#else /* end T20, begin 10X */
	1000				/* AC3  TENEX has no PM%CNT */
	.fhslf,,0			/* AC4  doing GET% into this process */
	pmap%				/* AC5  unmap a page */
	addi	2,1			/* AC6  point to next */
	sojg	3,5			/* AC7  loop until all unmapped */
	move	1,4			/* AC10 get back GET% argument */
	get%				/* AC11 map new program into ourself */
	movei	1,.fhslf		/* AC12 this process again */
	setz	2,			/* AC13 main entry point */
	sfrkv%				/* AC14 start ourself */
#endif /* 10X */
	nregs==.-rconts			/* remember how many to copy */
#endasm
}	/* End of _exec() */

static
_set_pio(jfns)
int jfns;
{
#asm
	movei 1,.fhslf
	gpjfn%			; Get current settings for primary i/o
	move 5,2		; Save them elsewhere
	hlre 4,-1(17)		; Check input half of 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,-1(17)		; Check output half of 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,
	movei 1,.fhslf		; One is different, must map!
	spjfn%			; Do it
#endasm
}
/*
** 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.
*/

int
vfork()
{
	asm(USYS_BEG_ASM);		/* Disable interrupts */
#asm
	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	vfrker			/* Lose lose */
	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\n	jrst (16)\n");		/* 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.
	*/
#asm
vforkr:	setom	.vfrkf			/* Set flag saying inside vfork */
	setz	1,			/* This is inferior fork */
	jrst	(16)			/* Return */
#endasm
}
/*
** 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()
{
#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	$retn			/* Lose lose, return -1 */
	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
}	/* End of fork() */
#endif /* T20+10X */