Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/old/lib/fork.c
There are 2 other files named fork.c in the archive. Click here to see a list.
/*
** Forking and program execution
**
** execl(), execle(), execv(), execve(), execlp(), execvp()
** fork(), vfork()
*/

#include "c-env.h"

entry execl, execle, execv, execve, execlp, execvp;
entry fork, vfork;
/*
** Stubs for non-forking operating system execution.
** Failure return for any of the routines herein is -1.
*/

#if (SYS_T20+SYS_10X)==0
execl()  { execv(); }
execle() { execv(); }
execve() { execv(); }
execlp() { execv(); }
execvp() { execv(); }
execv()  { return -1; }

vfork()  { fork(); }
fork()   { return -1; }
#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.
*/

#if SYS_T20+SYS_10X

#define	GJOLD	0100000000000		/* get JFN for existing file */
#define NULL 0

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

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

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

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

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

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

static doexec(prog, arg, inc)
char *prog, **arg;
{
    char buf[400], *s, *bprog;
    int jfn;

    /* 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 != '|') ; /* Add prog name, break on vbar */
    *s = '.';				/* and ".EXE" */
#if SYS_T20
    *++s = 'E'; *++s = 'X'; *++s = 'E';
#else
    *++s = 'S'; *++s = 'A'; *++s = 'V';
#endif
    *++s = '\0';
    jfn = _gtjfn(buf, GJOLD);		/* try SYS:prog.EXE */
    if (jfn < 1) {
	jfn = _gtjfn(bprog, GJOLD);	/* no, prog.EXE */
	if (jfn < 1) {			/* neither worked */
	    *(s - 4) = '\0';		/* drop null to block .EXE or .SAV */
	    jfn = _gtjfn(buf, GJOLD);	/* and try SYS:prog */
	    if (jfn < 1) {
		jfn = _gtjfn(bprog, GJOLD); /* still no, just try prog */
		if (jfn < 1) return;	/* still no, lose */
	    }
	}
    }

    /* 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 */

    /* all set up, go run the program */
    _exec(buf, jfn);			/* set up RSCAN and chain to program */
}
/*
** 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 _exec();				/* trick into no extern */

#asm
	SEARCH MONSYM			/* get JSYS defs */

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 */

.EXEC:
#if SYS_T20
	%CHRBP	1,-1(17)		/* Get RSCAN buffer as ILDB pointer */
	RSCAN%				/* Set it */
	 ERJMP	$RETF			/* Lost */
#endif /* T20 */
	SKIPE	vforkf			/* If in vfork() */
	 HALTF%				/* Stop and let superior catch up */
	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 */
	JRST	@[5]			/* Jump to section 0 regs */
#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 vforkf != 0.
*/

static int vforkf = 0;

#asm
vfork:
	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	$RETN			/* Lose lose */
	WFORK%				/* Wait for it to do exec */
	HRLI	1,(SF%CON)		/* Continuing after HALTF% */
	SFORK%				/* Start it again */
	SETZM	vforkf			/* No longer inside vfork */
	HRRZ	1,1			/* Just get fork handle */
	JRST	(16)			/* All done */

vforkr:
	SETOM	vforkf			/* Set flag for inside vfork */
	SETZ	1,			/* This is inferior fork */
	JRST	(16)			/* Return */
#endasm
/*
** fork()
** This does safe map copy before starting inferior
*/

#asm
	EXTERN $MAPSC			/* in CRT */

#if SYS_T20
	NSECTS==40
#else
	NSECTS==1
#endif

fork:
	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 */
	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,$RETF			/* Set subfork to return zero */
	TLNE	2,-1			/* If addr is in non-zero section, */
	 JRST	fork8			/* must start using extended call */
	SFORK%
	POPJ	17,			/* Return with fork handle in AC1 */
fork8:	MOVE	3,2
	SETZ	2,
	XSFRK%				/*  Start it at extended address. */
	POPJ	17,
#endasm
#endif /* T20+10X */