home *** CD-ROM | disk | FTP | other *** search
- /*
- Copyright 1990,1991,1992 Eric R. Smith.
- Copyright 1992,1993 Atari Corporation.
- All rights reserved.
- */
-
- /*
- * GEMDOS emulation routines: these are for the GEMDOS system calls
- * concerning allocating/freeing memory, including Pexec() (since
- * this allocates memory) and Pterm() (since this, implicitly, frees
- * it).
- */
-
- #include "mint.h"
-
- #define DIRSEP(c) ((c) == '\\')
-
- int procdate, proctime; /* set when any processes are created/destroyed */
-
- static long do_vfork P_((int));
-
- /*
- * new call for TT TOS, for the user to inform DOS of alternate memory
- * FIXME: we really shouldn't trust the user so completely
- * FIXME: doesn't work if memory protection is on
- */
-
- long ARGS_ON_STACK
- m_addalt(start, size)
- long start, size;
- {
- extern int no_mem_prot; /* see main.c and memprot.c */
-
- if (!no_mem_prot) return 0; /* pretend to succeed */
- if (!add_region(alt, start, size, M_ALT))
- return EINTRN;
- else
- return 0;
- }
-
- /*
- * internal routine for doing Malloc on a particular memory map
- */
-
- long
- _do_malloc(map, size, mode)
- MMAP map;
- long size;
- int mode;
- {
- virtaddr v;
- MEMREGION *m;
- long maxsize, mleft;
-
- if (size == -1L) {
- maxsize = max_rsize(map);
- if (curproc->maxmem) {
- mleft = curproc->maxmem - memused(curproc);
- if (maxsize > mleft)
- maxsize = mleft;
- if (maxsize < 0)
- maxsize = 0;
- }
- /* make sure to round down */
- return maxsize & ~MASKBITS;
- }
-
- /* special case: Malloc(0) should always return 0 */
- if (size == 0)
- return 0;
-
- if (curproc->maxmem) { /* memory limit? */
- if (size > curproc->maxmem - memused(curproc)) {
- DEBUG(("malloc: memory request would exceed limit"));
- return 0;
- }
- }
-
- m = get_region(map, size, mode);
- if (!m) {
- return 0;
- }
- v = attach_region(curproc, m);
- if (!v) {
- m->links = 0;
- free_region(m);
- return 0;
- }
- /* NOTE: get_region returns a region with link count 1; since attach_region
- * increments the link count, we have to remember to decrement the count
- * to correct for this.
- */
- m->links--;
- if ((mode & F_KEEP)) { /* request for permanent memory */
- m->mflags |= M_KEEP;
- }
- return (long)v;
- }
-
- long ARGS_ON_STACK
- m_xalloc(size, mode)
- long size;
- int mode;
- {
- long r, r1;
- int protmode;
- #ifdef DEBUG_INFO
- int origmode = mode;
- #endif
-
- TRACE(("Mxalloc(%ld,%x)",size,mode));
-
- /*
- * AKP: Hack here: if the calling process' PC is in ROM, then this is a
- * Malloc call made by VDI's v_opnvwk routine. So we change mode to
- * include "super accessible." This is temporary, until VDI catches up
- * with multitasking TOS.
- */
-
- if (((mode & F_PROTMODE) == 0) &&
- (curproc->ctxt[SYSCALL].pc > 0x00e00000L) &&
- (curproc->ctxt[SYSCALL].pc < 0x00efffffL)) {
- mode |= (F_PROT_S + 0x10) | F_KEEP;
- TRACE(("m_xalloc: VDI special (call from ROM)"));
- }
- /*
- * If the mode argument comes in a zero, then set it to the default
- * value from prgflags. Otherwise subtract one from it to bring it
- * into line with the actual argument to alloc_region.
- */
- protmode = (mode & F_PROTMODE) >> F_PROTSHIFT;
-
- if (protmode == 0) {
- protmode = (curproc->memflags & F_PROTMODE) >> F_PROTSHIFT;
- }
- else --protmode;
-
- if (protmode > PROT_MAX_MODE) {
- DEBUG(("Mxalloc: invalid protection mode changed to private"));
- protmode = PROT_P;
- }
-
- #if 0
- /* I'm very suspicious of the 0x08 flag; I can't see how it could
- * work as the comment below seems to indicate -- ERS
- */
-
- /*
- * if the mode argument has the 0x08 bit set then you're trying to change
- * the protection mode of a block you already own. "size" is really its
- * base address. (new as of 2/6/92)
- */
- if (mode & 0x08) change_prot_status(curproc,size,protmode);
- #endif
-
- /*
- * Copy the F_KEEP attribute into protmode. We didn't do that
- * before now because change_prot_status don't want to see no
- * steenking nofree attributes.
- */
-
- protmode |= (mode & F_KEEP);
-
- /* mask off all but the ST/alternative RAM bits before further use */
- mode &= 3;
-
- if (mode == 0) {
- r = _do_malloc(core, size, protmode);
- goto ret;
- }
- else if (mode == 1) {
- r = _do_malloc(alt, size, protmode);
- goto ret;
- }
- else if (size == -1) {
- /* modes 2 and 3 are the same for for size -1 */
- r = _do_malloc(core, -1L, PROT_P);
- r1 = _do_malloc(alt, -1L, PROT_P);
- if (r1 > r) r = r1;
- goto ret;
- }
- else if (mode == 2) {
- r = _do_malloc(core, size, protmode);
- if (r == 0) r = _do_malloc(alt, size, protmode);
- goto ret;
- }
- else /* if (mode == 3) */ {
- r = _do_malloc(alt, size, protmode);
- if (r == 0) r = _do_malloc(core, size, protmode);
- goto ret;
- }
- ret:
- if (r == 0) {
- DEBUG(("m_xalloc(%lx,%x) returns 0",size,origmode));
- } else {
- TRACE(("m_xalloc(%lx,%x) returns %lx",size,origmode,r));
- }
- return r;
- }
-
- long ARGS_ON_STACK
- m_alloc(size)
- long size;
- {
- long r;
-
- TRACE(("Malloc(%lx)", size));
- if (curproc->memflags & F_ALTALLOC)
- r = m_xalloc(size, 3);
- else
- r = m_xalloc(size, 0);
- TRACE(("Malloc: returning %lx", r));
- return r;
- }
-
- long ARGS_ON_STACK
- m_free(block)
- virtaddr block;
- {
- MEMREGION *m;
- int i;
-
- TRACE(("Mfree(%lx)", block));
- if (!block) {
- DEBUG(("Mfree: null pointer"));
- return EIMBA;
- }
-
- /* search backwards so that most recently allocated incarnations of
- shared memory blocks are freed first (this doesn't matter very often)
- */
-
- for (i = curproc->num_reg - 1; i >= 0; i--) {
- if (curproc->addr[i] == block) {
- m = curproc->mem[i];
- assert(m != NULL);
- assert(m->loc == (long)block);
- curproc->mem[i] = 0;
- curproc->addr[i] = 0;
- m->links--;
- if (m->links == 0) {
- free_region(m);
- }
- return 0;
- }
- }
-
- /* hmmm... if we didn't find the region, perhaps it's a global
- * one (with the M_KEEP flag set) belonging to a process that
- * terminated
- */
- for (i = rootproc->num_reg - 1; i >= 0; i--) {
- if (rootproc->addr[i] == block) {
- m = rootproc->mem[i];
- assert(m != NULL);
- assert(m->loc == (long)block);
- if (!(m->mflags & M_KEEP))
- continue;
- TRACE(("Freeing M_KEPT memory"));
- rootproc->mem[i] = 0;
- rootproc->addr[i] = 0;
- m->links--;
- if (m->links == 0) {
- free_region(m);
- }
- return 0;
- }
- }
-
-
- DEBUG(("Mfree: bad address %lx", block));
- return EIMBA;
- }
-
- long ARGS_ON_STACK
- m_shrink(dummy, block, size)
- int dummy;
- virtaddr block;
- long size;
- {
- MEMREGION *m;
- int i;
-
- UNUSED(dummy);
- TRACE(("Mshrink: %lx to %ld", block, size));
- if (!block) {
- DEBUG(("Mshrink: null pointer"));
- return EIMBA;
- }
-
- for (i = 0; i < curproc->num_reg; i++) {
- if (curproc->addr[i] == block) {
- m = curproc->mem[i];
- assert(m != NULL);
- assert(m->loc == (long)block);
- return shrink_region(m, size);
- }
- }
- DEBUG(("Mshrink: bad address (%lx)", block));
- return EIMBA;
- }
-
- long ARGS_ON_STACK
- p_exec(mode, ptr1, ptr2, ptr3)
- int mode;
- void *ptr1, *ptr2, *ptr3;
- {
- MEMREGION *base,
- *env = 0; /* assignment suppresses spurious warning */
- MEMREGION *text = 0; /* for shared text regions */
- PROC *p;
- long r, flags = 0;
- int i;
- char mkbase = 0, mkload = 0, mkgo = 0, mkwait = 0, mkfree = 0;
- char overlay = 0;
- char thread = 0;
- char ptrace;
- char mkname = 0, *newname, *lastslash;
- char localname[PNAMSIZ+1];
- XATTR xattr;
- int newpid;
-
- #ifdef DEBUG_INFO
- /* tfmt and tail_offs are used for debugging only */
- const char *tfmt = "Pexec(%d,%s,\"%s\",%lx)";
- int tail_offs = 1;
- #endif
-
- /* the high bit of mode controls process tracing */
- switch(mode & 0x7fff) {
- case 0:
- mkwait = 1; /* fall through */
- case 100:
- mkload = mkgo = mkfree = 1;
- mkname = 1;
- break;
- case 200: /* overlay current process */
- mkload = mkgo = 1;
- overlay = mkname = 1;
- break;
- case 3:
- mkload = 1;
- break;
- case 6:
- mkfree = 1;
- /* fall through */
- case 4:
- mkwait = mkgo = 1;
- thread = (mode == 4);
- #ifdef DEBUG_INFO
- tfmt = "Pexec(%d,%lx,BP:%lx,%lx)";
- tail_offs = 0;
- #endif
- break;
- case 106:
- mkfree = 1; /* fall through */
- case 104:
- thread = (mode == 104);
- mkgo = 1;
- mkname = (ptr1 != 0);
- #ifdef DEBUG_INFO
- tfmt = "Pexec(%d,%s,BP:%lx,%lx)";
- tail_offs = 0;
- #endif
- break;
- case 206:
- #if 0
- /* mkfree has no effect when overlay is set, since
- * in this case the "parent" and "child" are the same
- * process; since the "child" will run in memory that the
- * "parent" allocated, we don't want that memory freed!
- */
- mkfree = 1;
- #endif
- /* fall through */
- case 204:
- mkgo = overlay = 1;
- mkname = (ptr1 != 0);
- #ifdef DEBUG_INFO
- tfmt = "Pexec(%d,%s,BP:%lx,%lx)";
- tail_offs = 0;
- #endif
- break;
- case 7:
- flags = (long)ptr1; /* set program flags */
- if (((flags & F_PROTMODE) >> F_PROTSHIFT) > PROT_MAX_MODE) {
- DEBUG (("Pexec: invalid protection mode changed to private"));
- flags = (flags & ~F_PROTMODE) | F_PROT_P;
- }
- /* and fall through */
- case 5:
- mkbase = 1;
- #ifdef DEBUG_INFO
- tfmt = "Pexec(%d,%lx,%s,%lx)";
- tail_offs = 0;
- #endif
- break;
- default:
- DEBUG(("Pexec(%d,%lx,%lx,%lx): bad mode",mode,ptr1,ptr2,ptr3));
- return EINVFN;
- }
-
- TRACE((tfmt,mode,ptr1,(char *)ptr2+tail_offs,ptr3));
-
- /* Pexec with mode 0x8000 indicates tracing should be active */
- ptrace = (!mkwait && (mode & 0x8000));
-
- /* in most cases, we'll want a process struct to exist,
- * so make sure one is around. Note that we must be
- * careful to free it later!
- */
-
- TRACE(("Checking for memory for new PROC structure"));
- p = 0;
- if (!overlay) {
- p = new_proc();
- if (!p) {
- DEBUG(("Pexec: couldn't get a PROC struct"));
- return ENSMEM;
- }
- }
-
- TRACE(("creating environment"));
-
- if (mkload || mkbase) {
- env = create_env((char *)ptr3, flags);
- if (!env) {
- DEBUG(("Pexec: unable to create environment"));
- if (p) dispose_proc(p);
- return ENSMEM;
- }
- }
-
- TRACE(("creating base page"));
-
- if (mkbase) {
- base = create_base((char *)ptr2, env, flags, 0L);
- if (!base) {
- DEBUG(("Pexec: unable to create basepage"));
- detach_region(curproc, env);
- if (p) dispose_proc(p);
- return ENSMEM;
- }
- TRACELOW(("Pexec: basepage region(%lx) is %ld bytes at %lx", base, base->len, base->loc));
- }
- else if (mkload) {
- base = load_region((char *)ptr1, env, (char *)ptr2,
- &xattr, &text, &flags);
- if (!base) {
- DEBUG(("Pexec: load_region failed"));
- detach_region(curproc, env);
- if (p) dispose_proc(p);
- return mint_errno;
- }
- TRACE(("Pexec: basepage region(%lx) is %ld bytes at %lx", base, base->len, base->loc));
- }
- else { /* mode == 4,6,104,106,204, or 206 -- just go */
- base = addr2mem((virtaddr)ptr2);
- if (base)
- env = addr2mem(*(void **)(base->loc + 0x2c));
- else
- env = 0;
- if (!env) {
- DEBUG(("Pexec: memory not owned by parent"));
- if (p) dispose_proc(p);
- return EIMBA;
- }
- #if 0
- /* make sure that the PC we are about to use is in a region which is
- * attached to the process; this is most commonly a problem for
- * shared text segment programs.
- * BUG: we should verify that the PC is in a region to which the
- * child process should legitimately have access.
- */
- text = addr2region(((BASEPAGE *)base->loc)->p_tbase);
- if (text == base) {
- /* text segment is part of base region */
- text = NULL;
- }
- #endif
- }
-
- /* make a local copy of the name, in case we are overlaying the current
- * process
- */
- if (mkname) {
- lastslash = 0;
- newname = ptr1;
- while (*newname) {
- if (*newname == '\\' || *newname == '/')
- lastslash = newname;
- ++newname;
- }
- if (!lastslash)
- lastslash = ptr1;
- else
- lastslash++;
-
- i = 0; newname = localname;
- while (i++ < PNAMSIZ) {
- if (*lastslash == '.' || *lastslash == 0) {
- *newname = 0; break;
- }
- else
- *newname++ = *lastslash++;
- }
- *newname = 0;
- }
-
- if (mkload || mkbase) {
- /*
- * Now that the file's loaded, flags is set to the prgflags
- * for the file. In the case of mkbase it's been right all along.
- * Here's where we change the protection on the environment to
- * match those flags.
- */
- mark_region(env,(short)((flags & F_PROTMODE) >> F_PROTSHIFT));
- }
-
- if (p) {
- /* free the PROC struct so fork_proc will succeed */
- /* FIXME: it would be much better to pass the PROC as a parameter
- * to fork_proc!!
- */
- dispose_proc(p);
- p = 0;
- }
-
- if (mkgo) {
- BASEPAGE *b;
-
- /* tell the child who the parent was */
- b = (BASEPAGE *)base->loc;
-
- if (overlay) {
- b->p_parent = curproc->base->p_parent;
- p = curproc;
- /* make sure that exec_region doesn't free the base and env */
- base->links++;
- env->links++;
- if (text) text->links++;
- }
- else {
- b->p_parent = curproc->base;
- p = fork_proc();
- }
- if (!p) {
- if (mkbase) {
- detach_region(curproc, base);
- detach_region(curproc, env);
- if (text) detach_region(curproc, text);
- }
- return mint_errno;
- }
-
- /* jr: add Pexec information to PROC struct */
- strncpy(p->cmdlin, b->p_cmdlin, 128);
- p->fname[0] = 0;
- if (mkload) {
- char tmp[PATH_MAX];
- char *source = ptr1;
- tmp[1] = ':';
- if (source[1] == ':') {
- tmp[0] = source[0];
- source += 2;
- } else
- tmp[0] = 'A' + curproc->curdrv;
- if (DIRSEP(source[0])) /* absolute path? */
- {
- strncpy (&tmp[2], &source[0], PATH_MAX-2);
- strcpy (p->fname, tmp);
- } else {
- if (! d_getcwd (&tmp[2], tmp[0] - 'A' + 1, PATH_MAX - 2))
- ksprintf (p->fname, "%s\\%s", tmp, source);
- }
- }
-
- if (ptrace)
- p->ptracer = pid2proc(p->ppid);
-
- /* Even though the file system won't allow unauthorized access
- * to setuid/setgid programs, it's better to err on the side of
- * caution and forbid them to be traced (since the parent can arrange
- * to share the child's address space, not all accesses need to
- * go through the file system.)
- */
- if (mkload && mkgo && !p->ptracer) { /* setuid/setgid is OK */
- if (xattr.mode & S_ISUID)
- p->euid = xattr.uid;
- if (xattr.mode & S_ISGID)
- p->egid = xattr.gid;
- }
- /* exec_region frees the memory attached to p; that's always what
- * we want, since fork_proc duplicates the memory, and since
- * if we didn't call fork_proc then we're overlaying.
- * NOTE: after this call, we may not be able to access the
- * original address space that the Pexec was taking place in
- * (if this is an overlaid Pexec, we just freed that memory).
- */
- (void)exec_region(p, base, thread);
- attach_region(p, env);
- attach_region(p, base);
- if (text) attach_region(p, text);
-
- if (mkname) {
- /* interesting coincidence -- if a process needs a name, it usually
- * needs to have its domain reset to DOM_TOS. Doing it this way
- * (instead of doing it in exec_region) means that Pexec(4,...)
- * can be used to create new threads of execution which retain
- * the same domain.
- */
- if (!thread)
- p->domain = DOM_TOS;
-
- /* put in the new process name we saved above */
- strcpy(p->name, localname);
- }
-
- /* turn on tracing for the new process */
- if (p->ptracer)
- p->ctxt[CURRENT].ptrace = 1;
-
- /* set the time/date stamp of u:\proc */
- proctime = timestamp;
- procdate = datestamp;
-
- if (overlay) {
- /* correct for temporary increase in links (see above) */
- base->links--;
- env->links--;
- if (text) text->links--;
- /* let our parent run, if it Vfork'd() */
- if ( (p = pid2proc(curproc->ppid)) != 0 ) {
- if (p->wait_q == WAIT_Q &&
- p->wait_cond == (long)curproc) {
- short sr = spl7();
- rm_q(WAIT_Q, p);
- add_q(READY_Q, p);
- spl(sr);
- }
- }
-
- /* OK, let's run our new code */
- /* we guarantee ourselves at least 2 timeslices to do an Mshrink */
- assert(curproc->magic == CTXT_MAGIC);
- fresh_slices(2);
- leave_kernel();
- change_context(&(curproc->ctxt[CURRENT]));
- }
- else {
- /* we want this process to run ASAP */
- /* so we temporarily give it high priority and put it first on the
- * run queue
- */
- run_next(p, 2);
- }
- }
-
- if (mkfree) {
- detach_region(curproc, base);
- detach_region(curproc, env);
- if (text) detach_region(curproc, text);
- }
-
- if (mkwait) {
- long oldsigint, oldsigquit;
-
- oldsigint = curproc->sighandle[SIGINT];
- oldsigquit = curproc->sighandle[SIGQUIT];
- curproc->sighandle[SIGINT] =
- curproc->sighandle[SIGQUIT] = SIG_IGN;
-
- newpid = p->pid;
- for(;;) {
- r = p_wait3(0, (long *)0);
- if (r < 0) {
- ALERT("p_exec: wait error");
- return EINTRN;
- }
- if ( newpid == ((r&0xffff0000L) >> 16) ) {
- TRACE(("leaving Pexec; child return code %ld", r));
- r = r & 0x0000ffffL;
- break;
- }
- if (curproc->pid)
- DEBUG(("Pexec: wrong child found"));
- }
- curproc->sighandle[SIGINT] = oldsigint;
- curproc->sighandle[SIGQUIT] = oldsigquit;
- return r;
- }
- else if (mkgo) {
- /* warning: after the yield() the "p" structure may not exist any more
- * (if the child exits right away)
- */
- newpid = p->pid;
- yield(); /* let the new process run */
- return newpid;
- } else {
- TRACE(("leaving Pexec with basepage address %lx", base->loc));
- return base->loc;
- }
- }
-
- /*
- * terminate a process, with return code "code". If que == ZOMBIE_Q, free
- * all resources attached to the child; if que == TSR_Q, free everything
- * but memory.
- * NOTE: terminate() should be called only when the process is to be
- * "terminated with extreme prejuidice". Most times, p_term or p_termres
- * are the functions to use, since they allow the user to do some cleaning
- * up, etc.
- */
-
- long
- terminate(code, que)
- int code, que;
- {
- extern PROC *dlockproc[]; /* in dosdir.c */
- PROC *p;
- FILEPTR *fp;
- MEMREGION *m;
- MEMREGION **hold_mem;
- virtaddr *hold_addr;
- int i, wakemint = 0;
- DIR *dirh, *nexth;
- extern short bconbsiz; /* in bios.c */
-
- if (bconbsiz)
- (void) bflush();
-
- assert(que == ZOMBIE_Q || que == TSR_Q);
-
- if (curproc->pid == 0) {
- FATAL("attempt to terminate MiNT");
- }
-
- /* cancel all pending timeouts for this process */
- cancelalltimeouts();
- /* cancel alarm clock */
- curproc->alarmtim = 0;
-
- /* release any drives locked by Dlock */
- for(i = 0; i < NUM_DRIVES; i++) {
- if (dlockproc[i] == curproc) {
- dlockproc[i] = 0;
- changedrv(i);
- }
- }
-
- /* release the controlling terminal, if we're a process group leader */
- fp = curproc->handle[-1];
- if (fp && is_terminal(fp) && curproc->pgrp == curproc->pid) {
- struct tty *tty = (struct tty *)fp->devinfo;
- if (curproc->pgrp == tty->pgrp)
- tty->pgrp = 0;
- }
-
- /* close all files */
- for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
- if ((fp = curproc->handle[i]) != 0)
- do_close(fp);
- curproc->handle[i] = 0;
- }
-
- /* close any unresolved Fsfirst/Fsnext directory searches */
- for (i = 0; i < NUM_SEARCH; i++) {
- if (curproc->srchdta[i]) {
- DIR *dirh = &curproc->srchdir[i];
- (*dirh->fc.fs->closedir)(dirh);
- release_cookie(&dirh->fc);
- dirh->fc.fs = 0;
- }
- }
-
- /* close pending opendir/readdir searches */
- for (dirh = curproc->searches; dirh; ) {
- if (dirh->fc.fs) {
- (*dirh->fc.fs->closedir)(dirh);
- release_cookie(&dirh->fc);
- }
- nexth = dirh->next;
- kfree(dirh);
- dirh = nexth;
- }
-
- /* release the directory cookies held by the process */
- for (i = 0; i < NUM_DRIVES; i++) {
- release_cookie(&curproc->curdir[i]);
- curproc->curdir[i].fs = 0;
- release_cookie(&curproc->root[i]);
- curproc->root[i].fs = 0;
- }
-
- /* release all semaphores owned by this process */
- free_semaphores(curproc->pid);
-
- /* free all memory */
- /* if mflags & M_KEEP then attach it to process 0 */
- if (que == ZOMBIE_Q) {
- for (i = curproc->num_reg - 1; i >=0; i--) {
- m = curproc->mem[i];
- curproc->mem[i] = 0; curproc->addr[i] = 0;
- if (m) {
- /* don't free specially allocated memory */
- if (m->mflags & M_KEEP) {
- if (curproc != rootproc)
- attach_region(rootproc, m);
- }
- m->links--;
- if (m->links == 0) {
- free_region(m);
- }
- }
- }
-
- /*
- * mark the mem & addr arrays as void so the memory
- * protection code won't try to walk them. Do this before
- * freeing them so we don't try to walk them when marking
- * those pages themselves as free!
- *
- * Note: when a process terminates, the MMU root pointer
- * still points to that process' page table, until the next
- * process is dispatched. This is OK, since the process'
- * page table is in system memory, and it isn't going to be
- * freed. It is going to wind up on the free process list,
- * though, after dispose_proc. This might be Not A Good
- * Thing.
- */
-
- hold_addr = curproc->addr;
- hold_mem = curproc->mem;
-
- curproc->mem = NULL;
- curproc->addr = NULL;
- curproc->num_reg = 0;
-
- kfree(hold_addr);
- kfree(hold_mem);
- }
- /* else
- make TSR process non-swappable */
-
- /*
- * make sure that any open files that refer to this process are
- * closed
- */
- changedrv(PROC_RDEV_BASE | curproc->pid);
-
- /* find our parent (if parent not found, then use process 0 as parent
- * since that process is constantly in a wait loop)
- */
-
- p = pid2proc(curproc->ppid);
- if (!p) {
- TRACE(("terminate: parent not found"));
- p = pid2proc(0);
- }
-
- /* NOTE: normally just post_sig is sufficient for sending a signal; but
- * in this particular case, we have to worry about processes that are
- * blocking all signals because they Vfork'd and are waiting for us to
- * finish (which is indicated by a wait_cond matching our PROC
- * structure), and also processes that are ignoring SIGCHLD but are
- * waiting for us.
- */
- if (p->wait_q == WAIT_Q &&
- (p->wait_cond == (long)curproc || p->wait_cond == (long)p_waitpid) ) {
- short sr = spl7();
- TRACE(("terminate: waking up parent"));
- rm_q(WAIT_Q, p);
- add_q(READY_Q, p);
- spl(sr);
- }
- if (curproc->ptracer && curproc->ptracer != p) {
- /* BUG: should we ensure curproc->ptracer is awake ? */
- post_sig(curproc->ptracer, SIGCHLD); /* tell tracing process */
- }
- post_sig(p, SIGCHLD); /* inform of process termination */
-
- /* find our children, and orphan them
- * also, check for processes we were tracing, and
- * cancel the trace
- */
- i = curproc->pid;
- for (p = proclist; p; p = p->gl_next) {
- if (p->ppid == i) {
- p->ppid = 0; /* have the system adopt it */
- if (p->wait_q == ZOMBIE_Q)
- wakemint = 1; /* we need to wake proc. 0 */
- }
- if (p->ptracer == curproc) {
- p->ptracer = 0;
- /*
- * `FEATURE': we terminate traced processes when the tracer terminates.
- * It might plausibly be argued that it would be better to let them
- * continue, to let some (new) tracer take them over. On the other hand,
- * if the tracer terminated normally, it should have used Fcntl(PTRACESFLAGS)
- * to reset the trace nicely, so something must be wrong for us to have
- * reached here.
- */
- post_sig(p, SIGTERM); /* arrange for termination */
- }
- }
-
- if (wakemint) {
- p = rootproc; /* pid 0 */
- if (p->wait_q == WAIT_Q) {
- short sr = spl7();
- rm_q(WAIT_Q, p);
- add_q(READY_Q, p);
- spl(sr);
- }
- }
-
- /* this makes sure that our children are inherited by the system;
- * plus, it may help avoid problems if somehow a signal gets
- * through to us
- */
- for(i = 0; i < NSIG; i++)
- curproc->sighandle[i] = SIG_IGN;
-
- /* finally, reset the time/date stamp for u:\proc */
- proctime = timestamp;
- procdate = datestamp;
-
- sleep(que, (long)(unsigned)code);
-
- /* we shouldn't ever get here */
- FATAL("terminate: sleep woke up when it shouldn't have");
- return 0;
- }
-
- /*
- * TOS process termination entry points:
- * p_term terminates the process, freeing its memory
- * p_termres lets the process hang around resident in memory, after
- * shrinking its transient program area to "save" bytes
- */
-
- long ARGS_ON_STACK
- p_term(code)
- int code;
- {
- CONTEXT *syscall;
-
- TRACE(("Pterm(%d)", code));
- /* call the process termination vector */
- syscall = &curproc->ctxt[SYSCALL];
-
- if (syscall->term_vec != (long)rts) {
- TRACE(("term_vec: user has something to do"));
- /*
- * we handle the termination vector just like Supexec(), by
- * sending signal 0 to the process. See supexec() in xbios.c for details.
- * Note that we _always_ want to unwind the signal stack, and setting
- * bit 1 of curproc->sigmask tells handle_sig to do that -- see signal.c.
- */
- curproc->sigmask |= 1L;
- (void)supexec((Func)syscall->term_vec, 0L, 0L, 0L, 0L,
- (long)code);
- /*
- * if we arrive here, continue with the termination...
- */
- }
- return terminate(code, ZOMBIE_Q);
- }
-
- long ARGS_ON_STACK
- p_term0()
- {
- return p_term(0);
- }
-
- long ARGS_ON_STACK
- p_termres(save, code)
- long save;
- int code;
- {
- MEMREGION *m;
- int i;
-
- TRACE(("Ptermres(%ld, %d)", save, code));
- m = curproc->mem[1]; /* should be the basepage (0 is env.) */
- if (m) {
- (void)shrink_region(m, save);
- }
- /*
- * make all of the TSR's private memory globally accessible;
- * this means that more TSR's will "do the right thing"
- * without having to have prgflags set.
- */
- for (i = 0; i < curproc->num_reg; i++) {
- m = curproc->mem[i];
- if (m && m->links == 1) { /* only the TSR is owner */
- if (get_prot_mode(m) == PROT_P) {
- mark_region(m, PROT_G);
- }
- }
- }
- return terminate(code, TSR_Q);
- }
-
- /*
- * routine for waiting for children to die. Return has the pid of the
- * found child in the high word, and the child's exit code in
- * the low word. If no children exist, return "File Not Found".
- * If (nohang & 1) is nonzero, then return a 0 immediately if we have
- * no dead children but some living ones that we still have to wait
- * for. If (nohang & 2) is nonzero, then we return any stopped
- * children; otherwise, only children that have exited or are stopped
- * due to a trace trap are returned.
- * If "rusage" is non-zero and a child is found, put the child's
- * resource usage into it (currently only the user and system time are
- * sent back).
- * The pid argument specifies a set of child processes for which status
- * is requested:
- * If pid is equal to -1, status is requested for any child process.
- *
- * If pid is greater than zero, it specifies the process ID of a
- * single child process for which status is requested.
- *
- * If pid is equal to zero, status is requested for any child
- * process whose process group ID is equal to that of the calling
- * process.
- *
- * If pid is less than -1, status is requested for any child process
- * whose process group ID is equal to the absolute value of pid.
- *
- * Note this call is a real standard crosser... POSIX.1 doesn't have the
- * rusage stuff, BSD doesn't have the pid stuff; both are useful, so why
- * not have it all!
- */
-
- long ARGS_ON_STACK
- p_waitpid(pid, nohang, rusage)
- int pid;
- int nohang;
- long *rusage;
- {
- long r;
- PROC *p, *q;
- int ourpid;
- int found;
-
- TRACE(("Pwaitpid(%d, %d, %lx)", pid, nohang, rusage));
- ourpid = curproc->pid;
-
- /* if there are terminated children, clean up and return their info;
- * if there are children, but still running, wait for them;
- * if there are no children, return an error
- */
-
- do {
- /* look for any children */
- found = 0;
- for (p = proclist; p; p = p->gl_next) {
- if ((p->ppid == ourpid || p->ptracer == curproc) &&
- (pid == -1 ||
- (pid > 0 && pid == p->pid) ||
- (pid == 0 && p->pgrp == ourpid) ||
- (pid < -1 && p->pgrp == -pid))) {
- found++;
- if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
- break;
-
- /* p->wait_cond == 0 if a stopped process has already been waited for */
- if (p->wait_q == STOP_Q && p->wait_cond) {
- if ((nohang & 2) ||
- ((p->wait_cond&0x1f00) == (SIGTRAP<<8)))
- break;
- }
- }
- }
- if (!p) {
- if (found) {
- if (nohang & 1)
- return 0;
- if (curproc->pid)
- TRACE(("Pwaitpid: going to sleep"));
- sleep(WAIT_Q, (long)p_waitpid);
- }
- else {
- DEBUG(("Pwaitpid: no children found"));
- return EFILNF;
- }
- }
- } while (!p);
-
- /* OK, we've found our child */
- /* calculate the return code from the child's exit code and pid */
- r = (((unsigned long)p->pid) << 16) | (p->wait_cond & 0x0000ffff);
-
- /* check resource usage */
- if (rusage) {
- *rusage++ = p->usrtime;
- *rusage = p->systime;
- }
-
- /* avoid adding adopted trace processes usage to the foster parent */
- if (curproc->pid == p->ppid) {
- /* add child's resource usage to parent's */
- if (p->wait_q == TSR_Q || p->wait_q == ZOMBIE_Q) {
- curproc->chldstime += p->systime;
- curproc->chldutime += p->usrtime;
- }
- }
-
- /* if it was stopped, mark it as having been found and again return */
- if (p->wait_q == STOP_Q) {
- p->wait_cond = 0;
- return r;
- }
-
- /* We have to worry about processes which attach themselves to running
- * processes which they want to trace. We fix things up so that the
- * second time the signal gets delivered we will go all the way to the
- * end of this function.
- */
- if (p->ptracer && p->ptracer->pid != p->ppid) {
- if (curproc == p->ptracer) {
- /* deliver the signal to the tracing process first */
- TRACE(("Pwaitpid(ptracer): returning status to tracing process"));
- p->ptracer = NULL;
- return r;
- }
- else {
- /* Hmmm, the real parent got here first */
- TRACE(("Pwaitpid(ptracer): returning status to parent process"));
- p->ppid = -1;
- return r;
- }
- }
-
- /* if it was a TSR, mark it as having been found and return */
- if (p->wait_q == TSR_Q) {
- p->ppid = -1;
- return r;
- }
-
- /* it better have been on the ZOMBIE queue from here on in... */
- assert(p->wait_q == ZOMBIE_Q);
- assert(p != curproc);
-
- /* take the child off both the global and ZOMBIE lists */
- { short sr = spl7();
- rm_q(ZOMBIE_Q, p);
- spl(sr);
- }
-
- if (proclist == p) {
- proclist = p->gl_next;
- p->gl_next = 0;
- }
- else {
- q = proclist;
- while(q && q->gl_next != p)
- q = q->gl_next;
- assert(q);
- q->gl_next = p->gl_next;
- p->gl_next = 0;
- }
-
- dispose_proc(p); /* free the PROC structure */
-
- return r;
- }
-
- /* p_wait3: BSD process termination primitive, here to maintain
- * compatibility with existing binaries.
- */
- long ARGS_ON_STACK
- p_wait3(nohang, rusage)
- int nohang;
- long *rusage;
- {
- return p_waitpid(-1, nohang, rusage);
- }
-
- /* p_wait: block until a child has exited, and don't worry about
- resource stats. this is provided as a convenience, and to maintain
- compatibility with existing binaries (yes, I'm lazy...). we could
- make do with Pwaitpid().
- */
-
- long ARGS_ON_STACK
- p_wait()
- {
- /*
- * BEWARE:
- * POSIX says that wait() should be implemented as
- * Pwaitpid(-1, 0, (long *)0). Pwait is really not
- * useful for much at all, but we'll keep it around
- * for a while (with it's old, crufty semantics)
- * for backwards compatibility. People implementing
- * POSIX style libraries should use Pwaitpid even
- * to implement wait().
- */
- return p_wait3(2, (long *)0);
- }
-
- /*
- * do_vfork(save): create a duplicate of the current process. This is
- * essentially a vfork() algorithm, except that if (save == 1) the
- * parent's address space is saved, and then restored when the process
- * is made runnable again. The parent is suspended until either the child
- * process (the duplicate) exits or does a Pexec which overlays its
- * memory space.
- *
- * "txtsize" is the size of the process' TEXT area, if it has a valid one;
- * this is part of the second memory region attached (the basepage one)
- * and need not be saved (we assume processes don't write on their own
- * code segment)
- */
-
- static long
- do_vfork(save)
- int save;
- {
- PROC *p;
- long sigmask;
- MEMREGION *m, *savemem = 0;
- long savesize, txtsize;
- int i, newpid;
- char *saveplace;
-
- p = fork_proc();
- if (!p) {
- DEBUG(("do_vfork: couldn't get new PROC struct"));
- return mint_errno;
- }
- /* set u:\proc time+date */
- proctime = timestamp;
- procdate = datestamp;
-
- /*
- * maybe save the parent's address space
- */
- txtsize = p->txtsize;
-
- if (save) {
- TRACE(("do_vfork: saving parent"));
- savesize = memused(curproc) - txtsize;
- assert(savesize >= 0);
-
- saveplace = (char *)alloc_region(alt, savesize, PROT_P);
- if (!saveplace)
- saveplace = (char *)alloc_region(core, savesize, PROT_P);
-
- if (!saveplace) {
- DEBUG(("do_vfork: can't save parent's memory"));
- p->ppid = 0; /* abandon the child */
- post_sig(p, SIGKILL); /* then kill it */
- p->ctxt[CURRENT].pc = (long)check_sigs;
- /* just to make sure it dies */
- p->ctxt[CURRENT].ssp = (long)(p->stack + ISTKSIZE);
- p->pri = MAX_NICE+1;
- run_next(p, 1);
- yield();
- return ENSMEM;
- }
- savemem = addr2mem((virtaddr)saveplace);
- assert(savemem);
- for (i = 0; i < curproc->num_reg; i++) {
- m = curproc->mem[i];
- if (m && m != savemem && !(m->mflags & M_SHTEXT)) {
- if (i != 1 || txtsize == 0) {
- quickmove(saveplace, (char *)m->loc, m->len);
- saveplace += m->len;
- }
- else {
- quickmove(saveplace, (char *)m->loc+txtsize,
- m->len - txtsize);
- saveplace += m->len - txtsize;
- }
- }
- }
- }
-
- p->ctxt[CURRENT] = p->ctxt[SYSCALL];
- p->ctxt[CURRENT].regs[0] = 0; /* child returns a 0 from call */
- p->ctxt[CURRENT].sr &= ~(0x2000); /* child must be in user mode */
- #if 0 /* set up in fork_proc() */
- p->ctxt[CURRENT].ssp = (long)(p->stack + ISTKSIZE);
- #endif
-
- /* watch out for job control signals, since our parent can never wake
- * up to respond to them. solution: block them; exec_region (in mem.c)
- * clears the signal mask, so an exec() will unblock them.
- */
- p->sigmask |= (1L << SIGTSTP) | (1L << SIGTTIN) | (1L << SIGTTOU);
-
- TRACE(("do_vfork: parent going to sleep, wait_cond == %lx",
- (long)p));
-
- /* WARNING: This sleep() must absolutely not wake up until the child
- * has released the memory space correctly. That's why we mask off
- * all signals.
- */
- sigmask = curproc->sigmask;
- curproc->sigmask = ~((unsigned long)1 << SIGKILL);
-
- { short sr = spl7();
- add_q(READY_Q, p); /* put it on the ready queue */
- sleep(WAIT_Q, (long)p); /* while we wait for it */
- spl(sr);
- }
- TRACE(("do_vfork: parent waking up"));
-
- if (save) {
- TRACE(("do_vfork: parent restoring memory"));
- saveplace = (char *)savemem->loc;
- for (i = 0; i < curproc->num_reg; i++) {
- m = curproc->mem[i];
- if (m && (m != savemem) && !(m->mflags & M_SHTEXT)) {
- if (i != 1 || txtsize == 0) {
- quickmove((char *)m->loc, saveplace, m->len);
- saveplace += m->len;
- }
- else {
- quickmove((char *)m->loc+txtsize, saveplace,
- m->len - txtsize);
- saveplace += m->len - txtsize;
- }
- }
- }
- detach_region(curproc, savemem);
- }
- curproc->sigmask = sigmask;
- /* note that the PROC structure pointed to by p may be freed during
- * the check_sigs call!
- */
- newpid = p->pid;
- check_sigs(); /* did we get any signals while sleeping? */
- return newpid;
- }
-
- /*
- * here are the interfaces that the user sees. Pvfork() doesn't save
- * the child's address space; Pfork() does. Someday Pfork() should
- * allow asynchronous execution of both child and parent, but this
- * will do for now.
- */
-
- long ARGS_ON_STACK
- p_vfork()
- {
- return do_vfork(0);
- }
-
- long ARGS_ON_STACK
- p_fork()
- {
- return do_vfork(1);
- }
-