[comp.os.minix] MINIX 1.2 mm changes

ast@cs.vu.nl (Andy Tanenbaum) (07/01/87)

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin
echo Extracting \f\o\r\k\e\x\i\t\.\c
sed 's/^X//' > \f\o\r\k\e\x\i\t\.\c << '+ END-OF-FILE '\f\o\r\k\e\x\i\t\.\c
X/* This file deals with creating processes (via FORK) and deleting them (via
X * EXIT/WAIT).  When a process forks, a new slot in the 'mproc' table is
X * allocated for it, and a copy of the parent's core image is made for the
X * child.  Then the kernel and file system are informed.  A process is removed
X * from the 'mproc' table when two events have occurred: (1) it has exited or
X * been killed by a signal, and (2) the parent has done a WAIT.  If the process
X * exits first, it continues to occupy a slot until the parent does a WAIT.
X *
X * The entry points into this file are:
X *   do_fork:	perform the FORK system call
X *   do_mm_exit:	perform the EXIT system call (by calling mm_exit())
X *   mm_exit:	actually do the exiting
X *   do_wait:	perform the WAIT system call
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/callnr.h"
X#include "../h/error.h"
X#include "const.h"
X#include "glo.h"
X#include "mproc.h"
X#include "param.h"
X
X#define LAST_FEW            2	/* last few slots reserved for superuser */
X
XPRIVATE next_pid = INIT_PROC_NR+1;	/* next pid to be assigned */
XPRIVATE process_group = 1;		/* next process grp to be assigned */
X
X/* Some C compilers require static declarations to precede their first use. */
X
X/*===========================================================================*
X *				do_fork					     *
X *===========================================================================*/
XPUBLIC int do_fork()
X{
X/* The process pointed to by 'mp' has forked.  Create a child process. */
X
X  register struct mproc *rmp;	/* pointer to parent */
X  register struct mproc *rmc;	/* pointer to child */
X  int i, child_nr, t;
X  char *sptr, *dptr;
X  long prog_bytes;
X  phys_clicks prog_clicks, child_base;
X  long parent_abs, child_abs;
X  extern phys_clicks alloc_mem();
X
X /* If tables might fill up during FORK, don't even start since recovery half
X  * way through is such a nuisance.
X  */
X
X  rmp = mp;
X  if (procs_in_use == NR_PROCS) return(EAGAIN);
X  if (procs_in_use >= NR_PROCS - LAST_FEW && rmp->mp_effuid != 0)return(EAGAIN);
X
X  /* Determine how much memory to allocate. */
X  prog_clicks = (phys_clicks) rmp->mp_seg[T].mem_len + rmp->mp_seg[D].mem_len +
X						      rmp->mp_seg[S].mem_len;
X#ifdef i8088
X  prog_clicks += rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_len; /* gap too */
X#endif
X  prog_bytes = (long) prog_clicks << CLICK_SHIFT;
X  if ( (child_base = alloc_mem(prog_clicks)) == NO_MEM) return(EAGAIN);
X
X  /* Create a copy of the parent's core image for the child. */
X  child_abs = (long) child_base << CLICK_SHIFT;
X  parent_abs = (long) rmp->mp_seg[T].mem_phys << CLICK_SHIFT;
X  i = mem_copy(ABS, 0, parent_abs, ABS, 0, child_abs, prog_bytes);
X  if ( i < 0) panic("do_fork can't copy", i);
X
X  /* Find a slot in 'mproc' for the child process.  A slot must exist. */
X  for (rmc = &mproc[0]; rmc < &mproc[NR_PROCS]; rmc++)
X	if ( (rmc->mp_flags & IN_USE) == 0) break;
X
X  /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
X  child_nr = rmc - mproc;	/* slot number of the child */
X  procs_in_use++;
X  sptr = (char *) rmp;		/* pointer to parent's 'mproc' slot */
X  dptr = (char *) rmc;		/* pointer to child's 'mproc' slot */
X  i = sizeof(struct mproc);	/* number of bytes in a proc slot. */
X  while (i--) *dptr++ = *sptr++;/* copy from parent slot to child's */
X
X  /* Set process group. */
X  if (who == INIT_PROC_NR) rmc->mp_procgrp = process_group++;
X
X  rmc->mp_parent = who;		/* record child's parent */
X  rmc->mp_seg[T].mem_phys = child_base;
X  rmc->mp_seg[D].mem_phys = child_base + rmc->mp_seg[T].mem_len;
X  rmc->mp_seg[S].mem_phys = rmc->mp_seg[D].mem_phys + 
X			(rmp->mp_seg[S].mem_phys - rmp->mp_seg[D].mem_phys);
X  rmc->mp_exitstatus = 0;
X  rmc->mp_sigstatus = 0;
X
X  /* Find a free pid for the child and put it in the table. */
X  do {
X	t = 0;			/* 't' = 0 means pid still free */
X	next_pid = (next_pid < 30000 ? next_pid + 1 : INIT_PROC_NR + 1);
X	for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++)
X		if (rmp->mp_pid == next_pid) {
X			t = 1;
X			break;
X		}
X	rmc->mp_pid = next_pid;	/* assign pid to child */
X  } while (t);
X
X  /* Tell kernel and file system about the (now successful) FORK. */
X  sys_fork(who, child_nr, rmc->mp_pid);
X  tell_fs(FORK, who, child_nr, 0);
X
X  /* Report child's memory map to kernel. */
X  sys_newmap(child_nr, rmc->mp_seg);
X
X  /* Reply to child to wake it up. */
X  reply(child_nr, 0, 0, NIL_PTR);
X  return(next_pid);		 /* child's pid */
X}
X
X
X/*===========================================================================*
X *				do_mm_exit				     *
X *===========================================================================*/
XPUBLIC int do_mm_exit()
X{
X/* Perform the exit(status) system call. The real work is done by mm_exit(),
X * which is also called when a process is killed by a signal.
X */
X
X  mm_exit(mp, status);
X  dont_reply = TRUE;		/* don't reply to newly terminated process */
X  return(OK);			/* pro forma return code */
X}
X
X
X/*===========================================================================*
X *				mm_exit					     *
X *===========================================================================*/
XPUBLIC mm_exit(rmp, exit_status)
Xregister struct mproc *rmp;	/* pointer to the process to be terminated */
Xint exit_status;		/* the process' exit status (for parent) */
X{
X/* A process is done.  If parent is waiting for it, clean it up, else hang. */
X
X  /* How to terminate a process is determined by whether or not the
X   * parent process has already done a WAIT.  Test to see if it has.
X   */
X  rmp->mp_exitstatus = (char) exit_status;	/* store status in 'mproc' */
X
X  if (mproc[rmp->mp_parent].mp_flags & WAITING)
X	cleanup(rmp);		/* release parent and tell everybody */
X  else
X	rmp->mp_flags |= HANGING;	/* Parent not waiting.  Suspend proc */
X
X  /* If the exited process has a timer pending, kill it. */
X  if (rmp->mp_flags & ALARM_ON) set_alarm(rmp - mproc, (unsigned) 0);
X
X  /* Tell the kernel and FS that the process is no longer runnable. */
X  sys_xit(rmp->mp_parent, rmp - mproc);
X  tell_fs(EXIT, rmp - mproc, 0, 0);  /* file system can free the proc slot */
X}
X
X
X/*===========================================================================*
X *				do_wait					     *
X *===========================================================================*/
XPUBLIC int do_wait()
X{
X/* A process wants to wait for a child to terminate. If one is already waiting,
X * go clean it up and let this WAIT call terminate.  Otherwise, really wait.
X */
X
X  register struct mproc *rp;
X  register int children;
X
X  /* A process calling WAIT never gets a reply in the usual way via the
X   * reply() in the main loop.  If a child has already exited, the routine
X   * cleanup() sends the reply to awaken the caller.
X   */
X
X  /* Is there a child waiting to be collected? */
X  children = 0;
X  for (rp = &mproc[0]; rp < &mproc[NR_PROCS]; rp++) {
X	if ( (rp->mp_flags & IN_USE) && rp->mp_parent == who) {
X		children++;
X		if (rp->mp_flags & HANGING) {
X			cleanup(rp);	/* a child has already exited */
X			dont_reply = TRUE;
X			return(OK);
X		}
X	}
X  }
X
X  /* No child has exited.  Wait for one, unless none exists. */
X  if (children > 0) {		/* does this process have any children? */
X	mp->mp_flags |= WAITING;
X	dont_reply = TRUE;
X	return(OK);		/* yes - wait for one to exit */
X  } else
X	return(ECHILD);		/* no - parent has no children */
X}
X
X
X/*===========================================================================*
X *				cleanup					     *
X *===========================================================================*/
XPRIVATE cleanup(child)
Xregister struct mproc *child;	/* tells which process is exiting */
X{
X/* Clean up the remains of a process.  This routine is only called if two
X * conditions are satisfied:
X *     1. The process has done an EXIT or has been killed by a signal.
X *     2. The process' parent has done a WAIT.
X *
X * It releases the memory, if that has not been done yet.  Whether it has or
X * has not been done depends on the order of the EXIT and WAIT calls.
X */
X
X  register struct mproc *parent, *rp;
X  int init_waiting, child_nr;
X  unsigned int r;
X  phys_clicks s;
X
X  child_nr = child - mproc;
X  parent = &mproc[child->mp_parent];
X
X  /* Wakeup the parent. */
X  r = child->mp_sigstatus & 0377;
X  r = r | (child->mp_exitstatus << 8);
X  reply(child->mp_parent, child->mp_pid, r, NIL_PTR);
X
X  /* Release the memory occupied by the child. */
X  s = (phys_clicks) child->mp_seg[S].mem_vir + child->mp_seg[S].mem_len;
X  if (child->mp_flags & SEPARATE) s += child->mp_seg[T].mem_len;
X  free_mem(child->mp_seg[T].mem_phys, s);	/* free the memory */
X
X  /* Update flags. */
X  child->mp_flags  &= ~HANGING;	/* turn off HANGING bit */
X  child->mp_flags  &= ~PAUSED;	/* turn off PAUSED bit */
X  parent->mp_flags &= ~WAITING;	/* parent is no longer waiting */
X  child->mp_flags  &= ~IN_USE;	/* release the table slot */
X  procs_in_use--;
X
X  /* If exiting process has children, disinherit them.  INIT is new parent. */
X  init_waiting = (mproc[INIT_PROC_NR].mp_flags & WAITING ? 1 : 0);
X  for (rp = &mproc[0]; rp < &mproc[NR_PROCS]; rp++) {
X  	if (rp->mp_parent == child_nr) {
X  		/* 'rp' points to a child to be disinherited. */
X  		rp->mp_parent = INIT_PROC_NR;	/* init takes over */
X  		if (init_waiting && (rp->mp_flags & HANGING) ) {
X  			/* Init was waiting. */
X  			cleanup(rp);	/* recursive call */
X  			init_waiting = 0;
X  		}
X  	}
X  }
X}
+ END-OF-FILE forkexit.c
chmod 'u=rw,g=r,o=r' \f\o\r\k\e\x\i\t\.\c
set `sum \f\o\r\k\e\x\i\t\.\c`
sum=$1
case $sum in
48964)	:;;
*)	echo 'Bad sum in '\f\o\r\k\e\x\i\t\.\c >&2
esac
echo Extracting \s\i\g\n\a\l\.\c
sed 's/^X//' > \s\i\g\n\a\l\.\c << '+ END-OF-FILE '\s\i\g\n\a\l\.\c
X/* This file handles signals, which are asynchronous events and are generally
X * a messy and unpleasant business.  Signals can be generated by the KILL
X * system call, or from the keyboard (SIGINT) or from the clock (SIGALRM).
X * In all cases control eventually passes to check_sig() to see which processes
X * can be signalled.  The actual signalling is done by sig_proc().
X *
X * The entry points into this file are:
X *   do_signal:	perform the SIGNAL system call
X *   do_kill:	perform the KILL system call
X *   do_ksig:	accept a signal originating in the kernel (e.g., SIGINT)
X *   sig_proc:	interrupt or terminate a signalled process
X *   do_alarm:	perform the ALARM system call by calling set_alarm()
X *   set_alarm:	tell the clock task to start or stop a timer
X *   do_pause:	perform the PAUSE system call
X *   unpause:	check to see if process is suspended on anything
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/callnr.h"
X#include "../h/com.h"
X#include "../h/error.h"
X#include "../h/signal.h"
X#include "../h/stat.h"
X#include "const.h"
X#include "glo.h"
X#include "mproc.h"
X#include "param.h"
X
X#define DUMP_SIZE	 256	/* buffer size for core dumps */
X#define CORE_MODE	0777	/* mode to use on core image files */
X#define DUMPED          0200	/* bit set in status when core dumped */
X
XPRIVATE message m_sig;
X
X/*===========================================================================*
X *				do_signal				     *
X *===========================================================================*/
XPUBLIC int do_signal()
X{
X/* Perform the signal(sig, func) call by setting bits to indicate that a signal
X * is to be caught or ignored.
X */
X
X  int mask;
X
X  if (sig < 1 || sig > NR_SIGS) return(EINVAL);
X  if (sig == SIGKILL) return(OK);	/* SIGKILL may not ignored/caught */
X  mask = 1 << (sig - 1);	/* singleton set with 'sig' bit on */
X
X  /* All this func does is set the bit maps for subsequent sig processing. */
X  if (func == SIG_IGN) {
X	mp->mp_ignore |= mask;
X	mp->mp_catch &= ~mask;
X  } else if (func == SIG_DFL) {
X	mp->mp_ignore &= ~mask;
X	mp->mp_catch &= ~mask;
X  } else {
X	mp->mp_ignore &= ~mask;
X	mp->mp_catch |= mask;
X	mp->mp_func = func;
X  }
X  return(OK);
X}
X
X
X/*===========================================================================*
X *				do_kill					     *
X *===========================================================================*/
XPUBLIC int do_kill()
X{
X/* Perform the kill(pid, kill_sig) system call. */
X
X  return check_sig(pid, kill_sig, mp->mp_effuid);
X}
X
X
X/*===========================================================================*
X *				do_ksig					     *
X *===========================================================================*/
XPUBLIC int do_ksig()
X{
X/* Certain signals, such as segmentation violations and DEL, originate in the
X * kernel.  When the kernel detects such signals, it sets bits in a bit map.
X * As soon is MM is awaiting new work, the kernel sends MM a message containing
X * the process slot and bit map.  That message comes here.  The File System
X * also uses this mechanism to signal writing on broken pipes (SIGPIPE).
X */
X
X  register struct mproc *rmp;
X  int i, proc_id, proc_nr, id;
X  unshort sig_map;		/* bits 0 - 15 for sigs 1 - 16 */
X
X  /* Only kernel and FS may make this call. */
X  if (who != HARDWARE && who != FS_PROC_NR) return(EPERM);
X
X  proc_nr = mm_in.PROC1;
X  rmp = &mproc[proc_nr];
X  if ( (rmp->mp_flags & IN_USE) == 0 || (rmp->mp_flags & HANGING) ) return(OK);
X  proc_id = rmp->mp_pid;
X  sig_map = (unshort) mm_in.SIG_MAP;
X  mp = &mproc[0];		/* pretend kernel signals are from MM */
X  mp->mp_procgrp = rmp->mp_procgrp;	/* get process group right */
X
X  /* Stack faults are passed from kernel to MM as pseudo-signal 16. */
X  if (sig_map == 1 << (STACK_FAULT - 1)) {
X	stack_fault(proc_nr);
X	return(OK);
X  }
X
X  /* Check each bit in turn to see if a signal is to be sent.  Unlike
X   * kill(), the kernel may collect several unrelated signals for a process
X   * and pass them to MM in one blow.  Thus loop on the bit map. For SIGINT
X   * and SIGQUIT, use proc_id 0, since multiple processes may have to signalled.
X   */
X  for (i = 0; i < NR_SIGS; i++) {
X	id = (i+1 == SIGINT || i+1 == SIGQUIT ? 0 : proc_id);
X	if ( (sig_map >> i) & 1) check_sig(id, i + 1, SUPER_USER);
X  }
X
X  dont_reply = TRUE;		/* don't reply to the kernel */
X  return(OK);
X}
X
X
X/*===========================================================================*
X *				check_sig				     *
X *===========================================================================*/
XPRIVATE int check_sig(proc_id, sig_nr, send_uid)
Xint proc_id;			/* pid of process to signal, or 0 or -1 */
Xint sig_nr;			/* which signal to send (1-16) */
Xuid send_uid;			/* identity of process sending the signal */
X{
X/* Check to see if it is possible to send a signal.  The signal may have to be
X * sent to a group of processes.  This routine is invoked by the KILL system
X * call, and also when the kernel catches a DEL or other signal. SIGALRM too.
X */
X
X  register struct mproc *rmp;
X  int count, send_sig;
X  unshort mask;
X  extern unshort core_bits;
X
X  if (sig_nr < 1 || sig_nr > NR_SIGS) return(EINVAL);
X  count = 0;			/* count # of signals sent */
X  mask = 1 << (sig_nr - 1);
X
X  /* Search the proc table for processes to signal.  Several tests are made:
X   * 	- if proc's uid != sender's, and sender is not superuser, don't signal
X   *	- if specific process requested (i.e., 'procpid' > 0, check for match
X   *	- if a process has already exited, it can't receive signals
X   *	- if 'proc_id' is 0 signal everyone in same process group except caller
X   */
X  for (rmp = &mproc[INIT_PROC_NR + 1]; rmp < &mproc[NR_PROCS]; rmp++ ) {
X	if ( (rmp->mp_flags & IN_USE) == 0) continue;
X	send_sig = TRUE;	/* if it's FALSE at end of loop, don't signal */
X	if (send_uid != rmp->mp_effuid && send_uid != SUPER_USER)send_sig=FALSE;
X	if (proc_id > 0 && proc_id != rmp->mp_pid) send_sig = FALSE;
X	if (rmp->mp_flags & HANGING) send_sig = FALSE;   /*don't wake the dead*/
X	if (proc_id == 0 && mp->mp_procgrp != rmp->mp_procgrp) send_sig = FALSE;
X	if (send_uid == SUPER_USER && proc_id == -1) send_sig = TRUE;
X
X	/* SIGALARM is a little special.  When a process exits, a clock signal
X	 * can arrive just as the timer is being turned off.  Also, turn off
X	 * ALARM_ON bit when timer goes off to keep it accurate.
X	 */
X	if (sig_nr == SIGALRM) {
X		if ( (rmp->mp_flags & ALARM_ON) == 0) continue;
X		rmp->mp_flags &= ~ALARM_ON;
X	}
X
X	if (send_sig == FALSE || rmp->mp_ignore & mask) continue;
X	count++;
X
X	/* Send the signal or kill the process, possibly with core dump. */
X	sig_proc(rmp, sig_nr);
X
X	/* If process is hanging on PAUSE, WAIT, tty, pipe, etc. release it. */
X	unpause(rmp - mproc);	/* check to see if process is paused */
X	if (proc_id > 0) break;	/* only one process being signalled */
X  }
X
X  /* If the calling process has killed itself, don't reply. */
X  if ((mp->mp_flags & IN_USE) == 0 || (mp->mp_flags & HANGING))dont_reply =TRUE;
X  return(count > 0 ? OK : ESRCH);
X}
X
X
X/*===========================================================================*
X *				sig_proc				     *
X *===========================================================================*/
XPUBLIC sig_proc(rmp, sig_nr)
Xregister struct mproc *rmp;	/* pointer to the process to be signalled */
Xint sig_nr;			/* signal to send to process (1-16) */
X{
X/* Send a signal to a process.  Check to see if the signal is to be caught.
X * If so, the pc, psw, and signal number are to be pushed onto the process'
X * stack.  If the stack cannot grow or the signal is not to be caught, kill
X * the process.
X */
X
X  unshort mask;
X  int core_file;
X  vir_bytes new_sp;
X  extern unshort core_bits;
X
X  if ( (rmp->mp_flags & IN_USE) == 0) return;	/* if already dead forget it */
X  mask = 1 << (sig_nr - 1);
X  if (rmp->mp_catch & mask) {
X	/* Signal should be caught. */
X	rmp->mp_catch &= ~mask;		/* disable further signals */
X	sys_getsp(rmp - mproc, &new_sp);
X	new_sp -= SIG_PUSH_BYTES;
X	if (adjust(rmp, rmp->mp_seg[D].mem_len, new_sp) == OK) {
X		sys_sig(rmp - mproc, sig_nr, rmp->mp_func);
X		return;		/* successful signal */
X	}
X  }
X
X  /* Signal should not or cannot be caught.  Take default action. */
X  core_file = ( core_bits >> (sig_nr - 1 )) & 1;
X  rmp->mp_sigstatus = (char) sig_nr;
X  if (core_file) dump_core(rmp); /* dump core */
X  mm_exit(rmp, 0);		/* terminate process */
X}
X
X
X/*===========================================================================*
X *				do_alarm				     *
X *===========================================================================*/
XPUBLIC int do_alarm()
X{
X/* Perform the alarm(seconds) system call. */
X
X  register int r;
X  unsigned sec;
X
X  sec = (unsigned) seconds;
X  r = set_alarm(who, sec);
X  return(r);
X}
X
X
X/*===========================================================================*
X *				set_alarm				     *
X *===========================================================================*/
XPUBLIC int set_alarm(proc_nr, sec)
Xint proc_nr;			/* process that wants the alarm */
Xunsigned sec;			/* how many seconds delay before the signal */
X{
X/* This routine is used by do_alarm() to set the alarm timer.  It is also
X * to turn the timer off when a process exits with the timer still on.
X */
X
X  int remaining;
X
X  m_sig.m_type = SET_ALARM;
X  m_sig.CLOCK_PROC_NR = proc_nr;
X  m_sig.DELTA_TICKS = HZ * sec;
X  if (sec != 0)
X	mproc[proc_nr].mp_flags |= ALARM_ON;	/* turn ALARM_ON bit on */
X  else
X	mproc[proc_nr].mp_flags &= ~ALARM_ON;	/* turn ALARM_ON bit off */
X
X  /* Tell the clock task to provide a signal message when the time comes. */
X  if (sendrec(CLOCK, &m_sig) != OK) panic("alarm er", NO_NUM);
X  remaining = (int) m_sig.SECONDS_LEFT;
X  return(remaining);
X}
X
X
X/*===========================================================================*
X *				do_pause				     *
X *===========================================================================*/
XPUBLIC int do_pause()
X{
X/* Perform the pause() system call. */
X
X  mp->mp_flags |= PAUSED;	/* turn on PAUSE bit */
X  dont_reply = TRUE;
X  return(OK);
X}
X
X
X/*===========================================================================*
X *				unpause					     *
X *===========================================================================*/
XPUBLIC unpause(pro)
Xint pro;			/* which process number */
X{
X/* A signal is to be sent to a process.  It that process is hanging on a
X * system call, the system call must be terminated with EINTR.  Possible
X * calls are PAUSE, WAIT, READ and WRITE, the latter two for pipes and ttys.
X * First check if the process is hanging on PAUSE or WAIT.  If not, tell FS,
X * so it can check for READs and WRITEs from pipes, ttys and the like.
X */
X
X  register struct mproc *rmp;
X
X  rmp = &mproc[pro];
X
X  /* Check to see if process is hanging on PAUSE call. */
X  if ( (rmp->mp_flags & PAUSED) && (rmp->mp_flags & HANGING) == 0) {
X	rmp->mp_flags &= ~PAUSED;	/* turn off PAUSED bit */
X	reply(pro, EINTR, 0, NIL_PTR);
X	return;
X  }
X
X  /* Check to see if process is hanging on a WAIT call. */
X  if ( (rmp->mp_flags & WAITING) && (rmp->mp_flags & HANGING) == 0) {
X	rmp->mp_flags &= ~ WAITING;	/* turn off WAITING bit */
X	reply(pro, EINTR, 0, NIL_PTR);
X	return;
X  }
X
X  /* Process is not hanging on an MM call.  Ask FS to take a look. */
X  tell_fs(UNPAUSE, pro, 0, 0);
X
X  return;
X}
X
X
X/*===========================================================================*
X *				dump_core				     *
X *===========================================================================*/
XPRIVATE dump_core(rmp)
Xregister struct mproc *rmp;	/* whose core is to be dumped */
X{
X/* Make a core dump on the file "core", if possible. */
X
X  struct stat s_buf, d_buf;
X  char buf[DUMP_SIZE];
X  int i, r, s, er1, er2, slot;
X  vir_bytes v_buf;
X  long len, a, c, ct, dest;
X  struct mproc *xmp;
X  extern char core_name[];
X
X
X  /* Change to working directory of dumpee. */
X  slot = rmp - mproc;
X  tell_fs(CHDIR, slot, 0, 0);
X
X  /* Can core file be written? */
X  if (rmp->mp_realuid != rmp->mp_effuid) {
X	tell_fs(CHDIR, 0, 1, 0);	/* go back to MM's directory */
X	return;
X  }
X  xmp = mp;			/* allowed() looks at 'mp' */
X  mp = rmp;
X  r = allowed(core_name, &s_buf, W_BIT);	/* is core_file writable */
X  s = allowed(".", &d_buf, W_BIT);	/* is directory writable? */
X  mp = xmp;
X  if (r >= 0) close(r);
X  if (s >= 0) close(s);
X  if (rmp->mp_effuid == SUPER_USER) r = 0;	/* su can always dump core */
X
X  if (s >= 0 && (r >= 0 || r == ENOENT)) {
X	/* Either file is writable or it doesn't exist & dir is writable */
X	r = creat(core_name, CORE_MODE);
X	tell_fs(CHDIR, 0, 1, 0);	/* go back to MM's own dir */
X	if (r < 0) return;
X	rmp->mp_sigstatus |= DUMPED;
X
X	/* First loop through segments and write each length on core file. */
X	for (i = 0; i < NR_SEGS; i++) {
X		len = rmp->mp_seg[i].mem_len << CLICK_SHIFT;
X		if (write(r, (char *) &len, sizeof len) < 0) {
X			close(r);
X			return;
X		}
X	}
X
X	/* Now loop through segments and write the segments themselves out. */
X	v_buf = (vir_bytes) buf;
X	dest = (long) v_buf;
X	for (i = 0; i < NR_SEGS; i++) {
X		a = (phys_bytes) rmp->mp_seg[i].mem_vir << CLICK_SHIFT;
X		c = (phys_bytes) rmp->mp_seg[i].mem_len << CLICK_SHIFT;
X
X		/* Loop through a segment, dumping it. */
X		while (c > 0) {
X			ct = MIN(c, DUMP_SIZE);
X			er1 = mem_copy(slot, i, a, MM_PROC_NR, D, dest, ct);
X			er2 = write(r, buf, (int) ct);
X			if (er1 < 0 || er2 < 0) {
X				close(r);
X				return;
X			}
X			a += ct;
X			c -= ct;
X		}
X	}
X  } else {
X	tell_fs(CHDIR, 0, 1, 0);	/* go back to MM's own dir */
X	close(r);
X	return;
X  }
X
X  close(r);
X}
+ END-OF-FILE signal.c
chmod 'u=rw,g=r,o=r' \s\i\g\n\a\l\.\c
set `sum \s\i\g\n\a\l\.\c`
sum=$1
case $sum in
09347)	:;;
*)	echo 'Bad sum in '\s\i\g\n\a\l\.\c >&2
esac
exit 0