]> Zhao Yanbai Git Server - minix.git/commitdiff
Rename mm (memory manager) to pm (process manager), involved renaming
authorBen Gras <ben@minix3.org>
Tue, 3 May 2005 15:35:52 +0000 (15:35 +0000)
committerBen Gras <ben@minix3.org>
Tue, 3 May 2005 15:35:52 +0000 (15:35 +0000)
dir..

21 files changed:
servers/Makefile
servers/pm/Makefile [new file with mode: 0644]
servers/pm/alloc.c [new file with mode: 0644]
servers/pm/break.c [new file with mode: 0644]
servers/pm/const.h [new file with mode: 0644]
servers/pm/exec.c [new file with mode: 0644]
servers/pm/forkexit.c [new file with mode: 0644]
servers/pm/getset.c [new file with mode: 0644]
servers/pm/glo.h [new file with mode: 0644]
servers/pm/main.c [new file with mode: 0644]
servers/pm/misc.c [new file with mode: 0644]
servers/pm/mm.h [new file with mode: 0644]
servers/pm/mproc.h [new file with mode: 0644]
servers/pm/param.h [new file with mode: 0644]
servers/pm/procutils.c [new file with mode: 0644]
servers/pm/proto.h [new file with mode: 0644]
servers/pm/signal.c [new file with mode: 0644]
servers/pm/table.c [new file with mode: 0644]
servers/pm/trace.c [new file with mode: 0644]
servers/pm/type.h [new file with mode: 0644]
servers/pm/utility.c [new file with mode: 0644]

index 904f03282c7873c1c1ce9a6ebb51739d54851ee0..d21521037ea08e7a7f9ec6997e1f6e38163e0dc9 100644 (file)
@@ -14,7 +14,7 @@ usage:
 
 build: all
 all install clean:
-       cd ./mm && $(MAKE) $@
+       cd ./pm && $(MAKE) $@
        cd ./fs && $(MAKE) $@
        cd ./is && $(MAKE) $@
        cd ./init && $(MAKE) $@
diff --git a/servers/pm/Makefile b/servers/pm/Makefile
new file mode 100644 (file)
index 0000000..df71cce
--- /dev/null
@@ -0,0 +1,134 @@
+# Makefile for Process Manager (PM)
+SERVER = pm
+
+# directories
+u = /usr
+i = $u/include
+s = $i/sys
+h = $i/minix
+k = $u/src/kernel
+
+# programs, flags, etc.
+CC =   exec cc
+CFLAGS = -I$i
+LDFLAGS = -i
+
+OBJ =  main.o forkexit.o break.o exec.o procutils.o \
+       signal.o alloc.o utility.o table.o trace.o getset.o misc.o
+
+# build local binary
+all build:     $(SERVER)
+$(SERVER):     $(OBJ)
+       $(CC) -o $@ $(LDFLAGS) $(OBJ) -lsys -lutils
+       install -S 256w $@
+
+# install with other servers
+install:       /usr/sbin/servers/$(SERVER)
+/usr/sbin/servers/$(SERVER):   $(SERVER)
+       install -o root -cs $? $@
+
+# clean up local files
+clean:
+       rm -f $(SERVER) *.o *.bak 
+
+# dependencies
+a =    mm.h $h/config.h $s/types.h $h/const.h $h/type.h \
+       $i/ansi.h $i/fcntl.h $i/unistd.h $h/syslib.h \
+       $i/limits.h $i/errno.h const.h type.h proto.h glo.h
+
+alloc.o:       $a
+alloc.o:       $i/signal.h
+alloc.o:       $h/com.h
+alloc.o:       $h/callnr.h
+alloc.o:       mproc.h
+alloc.o:       $k/type.h
+alloc.o:       $k/const.h
+
+break.o:       $a
+break.o:       $i/signal.h
+break.o:       mproc.h
+break.o:       param.h
+
+exec.o:        $a
+exec.o:        $s/stat.h
+exec.o:        $h/callnr.h
+exec.o:        $h/com.h
+exec.o:        $i/a.out.h
+exec.o:        $i/signal.h
+exec.o:        $i/string.h
+exec.o:        mproc.h
+exec.o:        param.h
+
+forkexit.o:    $a
+forkexit.o:    $s/wait.h
+forkexit.o:    $h/callnr.h
+forkexit.o:    $h/com.h
+forkexit.o:    $h/utils.h
+forkexit.o:    $i/signal.h
+forkexit.o:    mproc.h
+forkexit.o:    param.h
+
+getset.o:      $a
+getset.o:      $h/callnr.h
+getset.o:      $i/signal.h
+getset.o:      mproc.h
+getset.o:      param.h
+
+main.o:        $a
+main.o:        $h/callnr.h
+main.o:        $h/com.h
+main.o:        $i/signal.h
+main.o:        $i/fcntl.h
+main.o:        $h/ioctl.h
+main.o:        $s/ioc_memory.h
+main.o:        $h/utils.h
+main.o:        mproc.h
+main.o:        param.h
+
+misc.o:        $a
+misc.o:        $h/callnr.h
+misc.o:        $h/utils.h
+misc.o:        $i/signal.h
+misc.o:        $h/ioctl.h
+misc.o:        $s/svrctl.h
+misc.o:        mproc.h
+misc.o:        param.h
+
+procutils.o:   $a
+procutils.o:   $i/timers.h
+procutils.o:   $i/string.h
+procutils.o:   $k/const.h
+procutils.o:   $k/type.h
+procutils.o:   $k/proc.h
+
+signal.o:      $a
+signal.o:      $s/stat.h
+signal.o:      $h/callnr.h
+signal.o:      $h/utils.h
+signal.o:      $h/com.h
+signal.o:      $i/signal.h
+signal.o:      $s/sigcontext.h
+signal.o:      $i/string.h
+signal.o:      mproc.h
+signal.o:      param.h
+
+table.o:       $a
+table.o:       $h/callnr.h
+table.o:       $i/signal.h
+table.o:       mproc.h
+table.o:       param.h
+
+trace.o:       $a
+trace.o:       $h/com.h
+trace.o:       $s/ptrace.h
+trace.o:       $i/signal.h
+trace.o:       mproc.h
+trace.o:       param.h
+
+utility.o:     $a
+utility.o:     $s/stat.h
+utility.o:     $h/callnr.h
+utility.o:     $h/com.h
+utility.o:     $i/fcntl.h
+utility.o:     $i/signal.h
+utility.o:     mproc.h
diff --git a/servers/pm/alloc.c b/servers/pm/alloc.c
new file mode 100644 (file)
index 0000000..91d0e32
--- /dev/null
@@ -0,0 +1,428 @@
+/* This file is concerned with allocating and freeing arbitrary-size blocks of
+ * physical memory on behalf of the FORK and EXEC system calls.  The key data
+ * structure used is the hole table, which maintains a list of holes in memory.
+ * It is kept sorted in order of increasing memory address. The addresses
+ * it contains refers to physical memory, starting at absolute address 0
+ * (i.e., they are not relative to the start of MM).  During system
+ * initialization, that part of memory containing the interrupt vectors,
+ * kernel, and MM are "allocated" to mark them as not available and to
+ * remove them from the hole list.
+ *
+ * The entry points into this file are:
+ *   alloc_mem:        allocate a given sized chunk of memory
+ *   free_mem: release a previously allocated chunk of memory
+ *   mem_init: initialize the tables when MM start up
+ *   max_hole: returns the largest hole currently available
+ */
+
+#include "mm.h"
+#include <minix/com.h>
+#include <minix/callnr.h>
+#include <signal.h>
+#include "mproc.h"
+#include "../../kernel/const.h"
+#include "../../kernel/type.h"
+
+#define NR_HOLES  (2*NR_PROCS) /* max # entries in hole table */
+#define NIL_HOLE (struct hole *) 0
+
+PRIVATE struct hole {
+  struct hole *h_next;         /* pointer to next entry on the list */
+  phys_clicks h_base;          /* where does the hole begin? */
+  phys_clicks h_len;           /* how big is the hole? */
+} hole[NR_HOLES];
+
+PRIVATE struct hole *hole_head;        /* pointer to first hole */
+PRIVATE struct hole *free_slots;/* ptr to list of unused table slots */
+#if ENABLE_SWAP
+PRIVATE int swap_fd = -1;      /* file descriptor of open swap file/device */
+PRIVATE u32_t swap_offset;     /* offset to start of swap area on swap file */
+PRIVATE phys_clicks swap_base; /* memory offset chosen as swap base */
+PRIVATE phys_clicks swap_maxsize;/* maximum amount of swap "memory" possible */
+PRIVATE struct mproc *in_queue;        /* queue of processes wanting to swap in */
+PRIVATE struct mproc *outswap = &mproc[LOW_USER];  /* outswap candidate? */
+#else /* !SWAP */
+#define swap_base ((phys_clicks) -1)
+#endif /* !SWAP */
+
+FORWARD _PROTOTYPE( void del_slot, (struct hole *prev_ptr, struct hole *hp) );
+FORWARD _PROTOTYPE( void merge, (struct hole *hp)                          );
+#if ENABLE_SWAP
+FORWARD _PROTOTYPE( int swap_out, (void)                                   );
+#else
+#define swap_out()     (0)
+#endif
+
+/*===========================================================================*
+ *                             alloc_mem                                    *
+ *===========================================================================*/
+PUBLIC phys_clicks alloc_mem(clicks)
+phys_clicks clicks;            /* amount of memory requested */
+{
+/* Allocate a block of memory from the free list using first fit. The block
+ * consists of a sequence of contiguous bytes, whose length in clicks is
+ * given by 'clicks'.  A pointer to the block is returned.  The block is
+ * always on a click boundary.  This procedure is called when memory is
+ * needed for FORK or EXEC.  Swap other processes out if needed.
+ */
+
+  register struct hole *hp, *prev_ptr;
+  phys_clicks old_base;
+
+  do {
+       hp = hole_head;
+       while (hp != NIL_HOLE && hp->h_base < swap_base) {
+               if (hp->h_len >= clicks) {
+                       /* We found a hole that is big enough.  Use it. */
+                       old_base = hp->h_base;  /* remember where it started */
+                       hp->h_base += clicks;   /* bite a piece off */
+                       hp->h_len -= clicks;    /* ditto */
+
+                       /* Delete the hole if used up completely. */
+                       if (hp->h_len == 0) del_slot(prev_ptr, hp);
+
+                       /* Return the start address of the acquired block. */
+                       return(old_base);
+               }
+
+               prev_ptr = hp;
+               hp = hp->h_next;
+       }
+  } while (swap_out());                /* try to swap some other process out */
+  return(NO_MEM);
+}
+
+/*===========================================================================*
+ *                             free_mem                                     *
+ *===========================================================================*/
+PUBLIC void free_mem(base, clicks)
+phys_clicks base;              /* base address of block to free */
+phys_clicks clicks;            /* number of clicks to free */
+{
+/* Return a block of free memory to the hole list.  The parameters tell where
+ * the block starts in physical memory and how big it is.  The block is added
+ * to the hole list.  If it is contiguous with an existing hole on either end,
+ * it is merged with the hole or holes.
+ */
+
+  register struct hole *hp, *new_ptr, *prev_ptr;
+
+  if (clicks == 0) return;
+  if ( (new_ptr = free_slots) == NIL_HOLE) panic("Hole table full", NO_NUM);
+  new_ptr->h_base = base;
+  new_ptr->h_len = clicks;
+  free_slots = new_ptr->h_next;
+  hp = hole_head;
+
+  /* If this block's address is numerically less than the lowest hole currently
+   * available, or if no holes are currently available, put this hole on the
+   * front of the hole list.
+   */
+  if (hp == NIL_HOLE || base <= hp->h_base) {
+       /* Block to be freed goes on front of the hole list. */
+       new_ptr->h_next = hp;
+       hole_head = new_ptr;
+       merge(new_ptr);
+       return;
+  }
+
+  /* Block to be returned does not go on front of hole list. */
+  while (hp != NIL_HOLE && base > hp->h_base) {
+       prev_ptr = hp;
+       hp = hp->h_next;
+  }
+
+  /* We found where it goes.  Insert block after 'prev_ptr'. */
+  new_ptr->h_next = prev_ptr->h_next;
+  prev_ptr->h_next = new_ptr;
+  merge(prev_ptr);             /* sequence is 'prev_ptr', 'new_ptr', 'hp' */
+}
+
+/*===========================================================================*
+ *                             del_slot                                     *
+ *===========================================================================*/
+PRIVATE void del_slot(prev_ptr, hp)
+register struct hole *prev_ptr;        /* pointer to hole entry just ahead of 'hp' */
+register struct hole *hp;      /* pointer to hole entry to be removed */
+{
+/* Remove an entry from the hole list.  This procedure is called when a
+ * request to allocate memory removes a hole in its entirety, thus reducing
+ * the numbers of holes in memory, and requiring the elimination of one
+ * entry in the hole list.
+ */
+
+  if (hp == hole_head)
+       hole_head = hp->h_next;
+  else
+       prev_ptr->h_next = hp->h_next;
+
+  hp->h_next = free_slots;
+  free_slots = hp;
+}
+
+/*===========================================================================*
+ *                             merge                                        *
+ *===========================================================================*/
+PRIVATE void merge(hp)
+register struct hole *hp;      /* ptr to hole to merge with its successors */
+{
+/* Check for contiguous holes and merge any found.  Contiguous holes can occur
+ * when a block of memory is freed, and it happens to abut another hole on
+ * either or both ends.  The pointer 'hp' points to the first of a series of
+ * three holes that can potentially all be merged together.
+ */
+
+  register struct hole *next_ptr;
+
+  /* If 'hp' points to the last hole, no merging is possible.  If it does not,
+   * try to absorb its successor into it and free the successor's table entry.
+   */
+  if ( (next_ptr = hp->h_next) == NIL_HOLE) return;
+  if (hp->h_base + hp->h_len == next_ptr->h_base) {
+       hp->h_len += next_ptr->h_len;   /* first one gets second one's mem */
+       del_slot(hp, next_ptr);
+  } else {
+       hp = next_ptr;
+  }
+
+  /* If 'hp' now points to the last hole, return; otherwise, try to absorb its
+   * successor into it.
+   */
+  if ( (next_ptr = hp->h_next) == NIL_HOLE) return;
+  if (hp->h_base + hp->h_len == next_ptr->h_base) {
+       hp->h_len += next_ptr->h_len;
+       del_slot(hp, next_ptr);
+  }
+}
+
+/*===========================================================================*
+ *                             mem_init                                     *
+ *===========================================================================*/
+PUBLIC void mem_init(free)
+phys_clicks *free;             /* memory size summaries */
+{
+/* Initialize hole lists.  There are two lists: 'hole_head' points to a linked
+ * list of all the holes (unused memory) in the system; 'free_slots' points to
+ * a linked list of table entries that are not in use.  Initially, the former
+ * list has one entry for each chunk of physical memory, and the second
+ * list links together the remaining table slots.  As memory becomes more
+ * fragmented in the course of time (i.e., the initial big holes break up into
+ * smaller holes), new table slots are needed to represent them.  These slots
+ * are taken from the list headed by 'free_slots'.
+ */
+  struct memory mem[NR_MEMS];  /* chunks of physical memory */
+  int i;
+  register struct hole *hp;
+  phys_clicks base;            /* base address of chunk */
+  phys_clicks size;            /* size of chunk */
+  message mess;
+
+  /* Get a copy of the physical memory chunks found at the kernel. */
+  if ((i=sys_getmemchunks(mem)) != OK)
+       panic("MM couldn't get mem chunks",i);
+
+  /* Put all holes on the free list. */
+  for (hp = &hole[0]; hp < &hole[NR_HOLES]; hp++) hp->h_next = hp + 1;
+  hole[NR_HOLES-1].h_next = NIL_HOLE;
+  hole_head = NIL_HOLE;
+  free_slots = &hole[0];
+
+  /* Ask the kernel for chunks of physical memory and allocate holes. */
+  *free = 0;
+  for (i=0; i<NR_MEMS; i++) {
+       if (mem[i].size > 0) {
+               free_mem(mem[i].base, mem[i].size);
+               *free += mem[i].size;
+#if ENABLE_SWAP
+               if (swap_base < mem[i].base + mem[i].size) 
+                       swap_base = mem[i].base+mem[i].size;
+#endif
+       }
+  }
+
+#if ENABLE_SWAP
+  /* The swap area is represented as a hole above and separate of regular
+   * memory.  A hole at the size of the swap file is allocated on "swapon".
+   */
+  swap_base++;                         /* make separate */
+  swap_maxsize = 0 - swap_base;                /* maximum we can possibly use */
+#endif
+}
+
+#if ENABLE_SWAP
+/*===========================================================================*
+ *                             swap_on                                      *
+ *===========================================================================*/
+PUBLIC int swap_on(file, offset, size)
+char *file;                            /* file to swap on */
+u32_t offset, size;                    /* area on swap file to use */
+{
+/* Turn swapping on. */
+
+  if (swap_fd != -1) return(EBUSY);    /* already have swap? */
+
+  tell_fs(CHDIR, who, FALSE, 0);       /* be like the caller for open() */
+  if ((swap_fd = open(file, O_RDWR)) < 0) return(-errno);
+  swap_offset = offset;
+  size >>= CLICK_SHIFT;
+  if (size > swap_maxsize) size = swap_maxsize;
+  if (size > 0) free_mem(swap_base, (phys_clicks) size);
+}
+
+/*===========================================================================*
+ *                             swap_off                                     *
+ *===========================================================================*/
+PUBLIC int swap_off()
+{
+/* Turn swapping off. */
+  struct mproc *rmp;
+  struct hole *hp, *prev_ptr;
+
+  if (swap_fd == -1) return(OK);       /* can't turn off what isn't on */
+
+  /* Put all swapped out processes on the inswap queue and swap in. */
+  for (rmp = &mproc[LOW_USER]; rmp < &mproc[NR_PROCS]; rmp++) {
+       if (rmp->mp_flags & ONSWAP) swap_inqueue(rmp);
+  }
+  swap_in();
+
+  /* All in memory? */
+  for (rmp = &mproc[LOW_USER]; rmp < &mproc[NR_PROCS]; rmp++) {
+       if (rmp->mp_flags & ONSWAP) return(ENOMEM);
+  }
+
+  /* Yes.  Remove the swap hole and close the swap file descriptor. */
+  for (hp = hole_head; hp != NIL_HOLE; prev_ptr = hp, hp = hp->h_next) {
+       if (hp->h_base >= swap_base) {
+               del_slot(prev_ptr, hp);
+               hp = hole_head;
+       }
+  }
+  close(swap_fd);
+  swap_fd = -1;
+  return(OK);
+}
+
+/*===========================================================================*
+ *                             swap_inqueue                                 *
+ *===========================================================================*/
+PUBLIC void swap_inqueue(rmp)
+register struct mproc *rmp;            /* process to add to the queue */
+{
+/* Put a swapped out process on the queue of processes to be swapped in.  This
+ * happens when such a process gets a signal, or if a reply message must be
+ * sent, like when a process doing a wait() has a child that exits.
+ */
+  struct mproc **pmp;
+
+  if (rmp->mp_flags & SWAPIN) return;  /* already queued */
+
+  
+  for (pmp = &in_queue; *pmp != NULL; pmp = &(*pmp)->mp_swapq) {}
+  *pmp = rmp;
+  rmp->mp_swapq = NULL;
+  rmp->mp_flags |= SWAPIN;
+}
+
+/*===========================================================================*
+ *                             swap_in                                      *
+ *===========================================================================*/
+PUBLIC void swap_in()
+{
+/* Try to swap in a process on the inswap queue.  We want to send it a message,
+ * interrupt it, or something.
+ */
+  struct mproc **pmp, *rmp;
+  phys_clicks old_base, new_base, size;
+  off_t off;
+  int proc_nr;
+
+  pmp = &in_queue;
+  while ((rmp = *pmp) != NULL) {
+       proc_nr = (rmp - mproc);
+       size = rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len
+               - rmp->mp_seg[D].mem_vir;
+
+       if (!(rmp->mp_flags & SWAPIN)) {
+               /* Guess it got killed.  (Queue is cleaned here.) */
+               *pmp = rmp->mp_swapq;
+               continue;
+       } else
+       if ((new_base = alloc_mem(size)) == NO_MEM) {
+               /* No memory for this one, try the next. */
+               pmp = &rmp->mp_swapq;
+       } else {
+               /* We've found memory.  Update map and swap in. */
+               old_base = rmp->mp_seg[D].mem_phys;
+               rmp->mp_seg[D].mem_phys = new_base;
+               rmp->mp_seg[S].mem_phys = rmp->mp_seg[D].mem_phys + 
+                       (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
+               sys_newmap(proc_nr, rmp->mp_seg);
+               off = swap_offset + ((off_t) (old_base-swap_base)<<CLICK_SHIFT);
+               lseek(swap_fd, off, SEEK_SET);
+               rw_seg(0, swap_fd, proc_nr, D, (phys_bytes)size << CLICK_SHIFT);
+               free_mem(old_base, size);
+               rmp->mp_flags &= ~(ONSWAP|SWAPIN);
+               *pmp = rmp->mp_swapq;
+               check_pending(rmp);     /* a signal may have waked this one */
+       }
+  }
+}
+
+/*===========================================================================*
+ *                             swap_out                                     *
+ *===========================================================================*/
+PRIVATE int swap_out()
+{
+/* Try to find a process that can be swapped out.  Candidates are those blocked
+ * on a system call that MM handles, like wait(), pause() or sigsuspend().
+ */
+  struct mproc *rmp;
+  struct hole *hp, *prev_ptr;
+  phys_clicks old_base, new_base, size;
+  off_t off;
+  int proc_nr;
+
+  rmp = outswap;
+  do {
+       if (++rmp == &mproc[NR_PROCS]) rmp = &mproc[LOW_USER];
+
+       /* A candidate? */
+       if (!(rmp->mp_flags & (PAUSED | WAITING | SIGSUSPENDED))) continue;
+
+       /* Already on swap or otherwise to be avoided? */
+       if (rmp->mp_flags & (TRACED | REPLY | ONSWAP)) continue;
+
+       /* Got one, find a swap hole and swap it out. */
+       proc_nr = (rmp - mproc);
+       size = rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len
+               - rmp->mp_seg[D].mem_vir;
+
+       prev_ptr = NIL_HOLE;
+       for (hp = hole_head; hp != NIL_HOLE; prev_ptr = hp, hp = hp->h_next) {
+               if (hp->h_base >= swap_base && hp->h_len >= size) break;
+       }
+       if (hp == NIL_HOLE) continue;   /* oops, not enough swapspace */
+       new_base = hp->h_base;
+       hp->h_base += size;
+       hp->h_len -= size;
+       if (hp->h_len == 0) del_slot(prev_ptr, hp);
+
+       off = swap_offset + ((off_t) (new_base - swap_base) << CLICK_SHIFT);
+       lseek(swap_fd, off, SEEK_SET);
+       rw_seg(1, swap_fd, proc_nr, D, (phys_bytes)size << CLICK_SHIFT);
+       old_base = rmp->mp_seg[D].mem_phys;
+       rmp->mp_seg[D].mem_phys = new_base;
+       rmp->mp_seg[S].mem_phys = rmp->mp_seg[D].mem_phys + 
+               (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
+       sys_newmap(proc_nr, rmp->mp_seg);
+       free_mem(old_base, size);
+       rmp->mp_flags |= ONSWAP;
+
+       outswap = rmp;          /* next time start here */
+       return(TRUE);
+  } while (rmp != outswap);
+
+  return(FALSE);       /* no candidate found */
+}
+#endif /* SWAP */
diff --git a/servers/pm/break.c b/servers/pm/break.c
new file mode 100644 (file)
index 0000000..fbbf27d
--- /dev/null
@@ -0,0 +1,170 @@
+/* The MINIX model of memory allocation reserves a fixed amount of memory for
+ * the combined text, data, and stack segments.  The amount used for a child
+ * process created by FORK is the same as the parent had.  If the child does
+ * an EXEC later, the new size is taken from the header of the file EXEC'ed.
+ *
+ * The layout in memory consists of the text segment, followed by the data
+ * segment, followed by a gap (unused memory), followed by the stack segment.
+ * The data segment grows upward and the stack grows downward, so each can
+ * take memory from the gap.  If they meet, the process must be killed.  The
+ * procedures in this file deal with the growth of the data and stack segments.
+ *
+ * The entry points into this file are:
+ *   do_brk:     BRK/SBRK system calls to grow or shrink the data segment
+ *   adjust:     see if a proposed segment adjustment is allowed
+ *   size_ok:    see if the segment sizes are feasible
+ */
+
+#include "mm.h"
+#include <signal.h>
+#include "mproc.h"
+#include "param.h"
+
+#define DATA_CHANGED       1   /* flag value when data segment size changed */
+#define STACK_CHANGED      2   /* flag value when stack size changed */
+
+/*===========================================================================*
+ *                             do_brk                                       *
+ *===========================================================================*/
+PUBLIC int do_brk()
+{
+/* Perform the brk(addr) system call.
+ *
+ * The call is complicated by the fact that on some machines (e.g., 8088),
+ * the stack pointer can grow beyond the base of the stack segment without
+ * anybody noticing it.
+ * The parameter, 'addr' is the new virtual address in D space.
+ */
+
+  register struct mproc *rmp;
+  int r;
+  vir_bytes v, new_sp;
+  vir_clicks new_clicks;
+
+  rmp = mp;
+  v = (vir_bytes) m_in.addr;
+  new_clicks = (vir_clicks) ( ((long) v + CLICK_SIZE - 1) >> CLICK_SHIFT);
+  if (new_clicks < rmp->mp_seg[D].mem_vir) {
+       rmp->mp_reply.reply_ptr = (char *) -1;
+       return(ENOMEM);
+  }
+  new_clicks -= rmp->mp_seg[D].mem_vir;
+  if ((r=p_getsp(who, &new_sp)) != OK) /* ask kernel for current sp value */
+       panic("MM couldn't get stack pointer", r);
+  r = adjust(rmp, new_clicks, new_sp);
+  rmp->mp_reply.reply_ptr = (r == OK ? m_in.addr : (char *) -1);
+  return(r);                   /* return new address or -1 */
+}
+
+
+/*===========================================================================*
+ *                             adjust                                       *
+ *===========================================================================*/
+PUBLIC int adjust(rmp, data_clicks, sp)
+register struct mproc *rmp;    /* whose memory is being adjusted? */
+vir_clicks data_clicks;                /* how big is data segment to become? */
+vir_bytes sp;                  /* new value of sp */
+{
+/* See if data and stack segments can coexist, adjusting them if need be.
+ * Memory is never allocated or freed.  Instead it is added or removed from the
+ * gap between data segment and stack segment.  If the gap size becomes
+ * negative, the adjustment of data or stack fails and ENOMEM is returned.
+ */
+
+  register struct mem_map *mem_sp, *mem_dp;
+  vir_clicks sp_click, gap_base, lower, old_clicks;
+  int changed, r, ft;
+  long base_of_stack, delta;   /* longs avoid certain problems */
+
+  mem_dp = &rmp->mp_seg[D];    /* pointer to data segment map */
+  mem_sp = &rmp->mp_seg[S];    /* pointer to stack segment map */
+  changed = 0;                 /* set when either segment changed */
+
+  if (mem_sp->mem_len == 0) return(OK);        /* don't bother init */
+
+  /* See if stack size has gone negative (i.e., sp too close to 0xFFFF...) */
+  base_of_stack = (long) mem_sp->mem_vir + (long) mem_sp->mem_len;
+  sp_click = sp >> CLICK_SHIFT;        /* click containing sp */
+  if (sp_click >= base_of_stack) return(ENOMEM);       /* sp too high */
+
+  /* Compute size of gap between stack and data segments. */
+  delta = (long) mem_sp->mem_vir - (long) sp_click;
+  lower = (delta > 0 ? sp_click : mem_sp->mem_vir);
+
+  /* Add a safety margin for future stack growth. Impossible to do right. */
+#define SAFETY_BYTES  (384 * sizeof(char *))
+#define SAFETY_CLICKS ((SAFETY_BYTES + CLICK_SIZE - 1) / CLICK_SIZE)
+  gap_base = mem_dp->mem_vir + data_clicks + SAFETY_CLICKS;
+  if (lower < gap_base) return(ENOMEM);        /* data and stack collided */
+
+  /* Update data length (but not data orgin) on behalf of brk() system call. */
+  old_clicks = mem_dp->mem_len;
+  if (data_clicks != mem_dp->mem_len) {
+       mem_dp->mem_len = data_clicks;
+       changed |= DATA_CHANGED;
+  }
+
+  /* Update stack length and origin due to change in stack pointer. */
+  if (delta > 0) {
+       mem_sp->mem_vir -= delta;
+       mem_sp->mem_phys -= delta;
+       mem_sp->mem_len += delta;
+       changed |= STACK_CHANGED;
+  }
+
+  /* Do the new data and stack segment sizes fit in the address space? */
+  ft = (rmp->mp_flags & SEPARATE);
+  r = size_ok(ft, rmp->mp_seg[T].mem_len, rmp->mp_seg[D].mem_len, 
+       rmp->mp_seg[S].mem_len, rmp->mp_seg[D].mem_vir, rmp->mp_seg[S].mem_vir);
+  if (r == OK) {
+       if (changed) sys_newmap((int)(rmp - mproc), rmp->mp_seg);
+       return(OK);
+  }
+
+  /* New sizes don't fit or require too many page/segment registers. Restore.*/
+  if (changed & DATA_CHANGED) mem_dp->mem_len = old_clicks;
+  if (changed & STACK_CHANGED) {
+       mem_sp->mem_vir += delta;
+       mem_sp->mem_phys += delta;
+       mem_sp->mem_len -= delta;
+  }
+  return(ENOMEM);
+}
+
+
+/*===========================================================================*
+ *                             size_ok                                      *
+ *===========================================================================*/
+PUBLIC int size_ok(file_type, tc, dc, sc, dvir, s_vir)
+int file_type;                 /* SEPARATE or 0 */
+vir_clicks tc;                 /* text size in clicks */
+vir_clicks dc;                 /* data size in clicks */
+vir_clicks sc;                 /* stack size in clicks */
+vir_clicks dvir;               /* virtual address for start of data seg */
+vir_clicks s_vir;              /* virtual address for start of stack seg */
+{
+/* Check to see if the sizes are feasible and enough segmentation registers
+ * exist.  On a machine with eight 8K pages, text, data, stack sizes of
+ * (32K, 16K, 16K) will fit, but (33K, 17K, 13K) will not, even though the
+ * former is bigger (64K) than the latter (63K).  Even on the 8088 this test
+ * is needed, since the data and stack may not exceed 4096 clicks.
+ */
+
+#if (CHIP == INTEL && _WORD_SIZE == 2)
+  int pt, pd, ps;              /* segment sizes in pages */
+
+  pt = ( (tc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE;
+  pd = ( (dc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE;
+  ps = ( (sc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE;
+
+  if (file_type == SEPARATE) {
+       if (pt > MAX_PAGES || pd + ps > MAX_PAGES) return(ENOMEM);
+  } else {
+       if (pt + pd + ps > MAX_PAGES) return(ENOMEM);
+  }
+#endif
+
+  if (dvir + dc > s_vir) return(ENOMEM);
+
+  return(OK);
+}
diff --git a/servers/pm/const.h b/servers/pm/const.h
new file mode 100644 (file)
index 0000000..fa4d1c8
--- /dev/null
@@ -0,0 +1,14 @@
+/* Constants used by the Memory Manager. */
+
+#define NO_MEM ((phys_clicks) 0)  /* returned by alloc_mem() with mem is up */
+
+#if (CHIP == INTEL && _WORD_SIZE == 2)
+/* These definitions are used in size_ok and are not needed for 386.
+ * The 386 segment granularity is 1 for segments smaller than 1M and 4096
+ * above that.  
+ */
+#define PAGE_SIZE        16    /* how many bytes in a page (s.b.HCLICK_SIZE)*/
+#define MAX_PAGES       4096   /* how many pages in the virtual addr space */
+#endif
+
+#define INIT_PID          1    /* init's process id number */
diff --git a/servers/pm/exec.c b/servers/pm/exec.c
new file mode 100644 (file)
index 0000000..9bda417
--- /dev/null
@@ -0,0 +1,598 @@
+/* This file handles the EXEC system call.  It performs the work as follows:
+ *    - see if the permissions allow the file to be executed
+ *    - read the header and extract the sizes
+ *    - fetch the initial args and environment from the user space
+ *    - allocate the memory for the new process
+ *    - copy the initial stack from MM to the process
+ *    - read in the text and data segments and copy to the process
+ *    - take care of setuid and setgid bits
+ *    - fix up 'mproc' table
+ *    - tell kernel about EXEC
+ *    - save offset to initial argc (for ps)
+ *
+ * The entry points into this file are:
+ *   do_exec:   perform the EXEC system call
+ *   rw_seg:    read or write a segment from or to a file
+ *   find_share: find a process whose text segment can be shared
+ */
+
+#include "mm.h"
+#include <sys/stat.h>
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include <a.out.h>
+#include <signal.h>
+#include <string.h>
+#include "mproc.h"
+#include "param.h"
+
+FORWARD _PROTOTYPE( int new_mem, (struct mproc *sh_mp, vir_bytes text_bytes,
+               vir_bytes data_bytes, vir_bytes bss_bytes,
+               vir_bytes stk_bytes, phys_bytes tot_bytes)              );
+FORWARD _PROTOTYPE( void patch_ptr, (char stack[ARG_MAX], vir_bytes base) );
+FORWARD _PROTOTYPE( int insert_arg, (char stack[ARG_MAX],
+               vir_bytes *stk_bytes, char *arg, int replace)           );
+FORWARD _PROTOTYPE( char *patch_stack, (int fd, char stack[ARG_MAX],
+               vir_bytes *stk_bytes, char *script)                     );
+FORWARD _PROTOTYPE( int read_header, (int fd, int *ft, vir_bytes *text_bytes,
+               vir_bytes *data_bytes, vir_bytes *bss_bytes,
+               phys_bytes *tot_bytes, long *sym_bytes, vir_clicks sc,
+               vir_bytes *pc)                                          );
+
+#define ESCRIPT        (-2000)         /* Returned by read_header for a #! script. */
+#define PTRSIZE        sizeof(char *)  /* Size of pointers in argv[] and envp[]. */
+
+/*===========================================================================*
+ *                             do_exec                                      *
+ *===========================================================================*/
+PUBLIC int do_exec()
+{
+/* Perform the execve(name, argv, envp) call.  The user library builds a
+ * complete stack image, including pointers, args, environ, etc.  The stack
+ * is copied to a buffer inside MM, and then to the new core image.
+ */
+
+  register struct mproc *rmp;
+  struct mproc *sh_mp;
+  int m, r, fd, ft, sn;
+  static char mbuf[ARG_MAX];   /* buffer for stack and zeroes */
+  static char name_buf[PATH_MAX]; /* the name of the file to exec */
+  char *new_sp, *name, *basename;
+  vir_bytes src, dst, text_bytes, data_bytes, bss_bytes, stk_bytes, vsp;
+  phys_bytes tot_bytes;                /* total space for program, including gap */
+  long sym_bytes;
+  vir_clicks sc;
+  struct stat s_buf[2], *s_p;
+  vir_bytes pc;
+
+  /* Do some validity checks. */
+  rmp = mp;
+  stk_bytes = (vir_bytes) m_in.stack_bytes;
+  if (stk_bytes > ARG_MAX) return(ENOMEM);     /* stack too big */
+  if (m_in.exec_len <= 0 || m_in.exec_len > PATH_MAX) return(EINVAL);
+
+  /* Get the exec file name and see if the file is executable. */
+  src = (vir_bytes) m_in.exec_name;
+  dst = (vir_bytes) name_buf;
+  r = sys_datacopy(who, (vir_bytes) src,
+               PM_PROC_NR, (vir_bytes) dst, (phys_bytes) m_in.exec_len);
+  if (r != OK) return(r);      /* file name not in user data segment */
+
+  /* Fetch the stack from the user before destroying the old core image. */
+  src = (vir_bytes) m_in.stack_ptr;
+  dst = (vir_bytes) mbuf;
+  r = sys_datacopy(who, (vir_bytes) src,
+                       PM_PROC_NR, (vir_bytes) dst, (phys_bytes)stk_bytes);
+
+  if (r != OK) return(EACCES); /* can't fetch stack (e.g. bad virtual addr) */
+
+  r = 0;       /* r = 0 (first attempt), or 1 (interpreted script) */
+  name = name_buf;     /* name of file to exec. */
+  do {
+       s_p = &s_buf[r];
+       tell_fs(CHDIR, who, FALSE, 0);  /* switch to the user's FS environ */
+       fd = allowed(name, s_p, X_BIT); /* is file executable? */
+       if (fd < 0)  return(fd);                /* file was not executable */
+
+       /* Read the file header and extract the segment sizes. */
+       sc = (stk_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
+
+       m = read_header(fd, &ft, &text_bytes, &data_bytes, &bss_bytes, 
+                                       &tot_bytes, &sym_bytes, sc, &pc);
+       if (m != ESCRIPT || ++r > 1) break;
+  } while ((name = patch_stack(fd, mbuf, &stk_bytes, name_buf)) != NULL);
+
+  if (m < 0) {
+       close(fd);              /* something wrong with header */
+       return(stk_bytes > ARG_MAX ? ENOMEM : ENOEXEC);
+  }
+
+  /* Can the process' text be shared with that of one already running? */
+  sh_mp = find_share(rmp, s_p->st_ino, s_p->st_dev, s_p->st_ctime);
+
+  /* Allocate new memory and release old memory.  Fix map and tell kernel. */
+  r = new_mem(sh_mp, text_bytes, data_bytes, bss_bytes, stk_bytes, tot_bytes);
+  if (r != OK) {
+       close(fd);              /* insufficient core or program too big */
+       return(r);
+  }
+
+  /* Save file identification to allow it to be shared. */
+  rmp->mp_ino = s_p->st_ino;
+  rmp->mp_dev = s_p->st_dev;
+  rmp->mp_ctime = s_p->st_ctime;
+
+  /* Patch up stack and copy it from MM to new core image. */
+  vsp = (vir_bytes) rmp->mp_seg[S].mem_vir << CLICK_SHIFT;
+  vsp += (vir_bytes) rmp->mp_seg[S].mem_len << CLICK_SHIFT;
+  vsp -= stk_bytes;
+  patch_ptr(mbuf, vsp);
+  src = (vir_bytes) mbuf;
+  r = sys_datacopy(PM_PROC_NR, (vir_bytes) src,
+                       who, (vir_bytes) vsp, (phys_bytes)stk_bytes);
+  if (r != OK) panic("do_exec stack copy err on", who);
+
+  /* Read in text and data segments. */
+  if (sh_mp != NULL) {
+       lseek(fd, (off_t) text_bytes, SEEK_CUR);  /* shared: skip text */
+  } else {
+       rw_seg(0, fd, who, T, text_bytes);
+  }
+  rw_seg(0, fd, who, D, data_bytes);
+
+  close(fd);                   /* don't need exec file any more */
+
+  /* Take care of setuid/setgid bits. */
+  if ((rmp->mp_flags & TRACED) == 0) { /* suppress if tracing */
+       if (s_buf[0].st_mode & I_SET_UID_BIT) {
+               rmp->mp_effuid = s_buf[0].st_uid;
+               tell_fs(SETUID,who, (int)rmp->mp_realuid, (int)rmp->mp_effuid);
+       }
+       if (s_buf[0].st_mode & I_SET_GID_BIT) {
+               rmp->mp_effgid = s_buf[0].st_gid;
+               tell_fs(SETGID,who, (int)rmp->mp_realgid, (int)rmp->mp_effgid);
+       }
+  }
+
+  /* Save offset to initial argc (for ps) */
+  rmp->mp_procargs = vsp;
+
+  /* Fix 'mproc' fields, tell kernel that exec is done,  reset caught sigs. */
+  for (sn = 1; sn <= _NSIG; sn++) {
+       if (sigismember(&rmp->mp_catch, sn)) {
+               sigdelset(&rmp->mp_catch, sn);
+               rmp->mp_sigact[sn].sa_handler = SIG_DFL;
+               sigemptyset(&rmp->mp_sigact[sn].sa_mask);
+       }
+  }
+
+  rmp->mp_flags &= ~SEPARATE;  /* turn off SEPARATE bit */
+  rmp->mp_flags |= ft;         /* turn it on for separate I & D files */
+  new_sp = (char *) vsp;
+
+  tell_fs(EXEC, who, 0, 0);    /* allow FS to handle FD_CLOEXEC files */
+
+  /* System will save command line for debugging, ps(1) output, etc. */
+  basename = strrchr(name, '/');
+  if (basename == NULL) basename = name; else basename++;
+  strncpy(rmp->mp_name, basename, PROC_NAME_LEN-1);
+  rmp->mp_name[PROC_NAME_LEN] = '\0';
+  sys_exec(who, new_sp, rmp->mp_flags & TRACED, basename, pc);
+
+  return(SUSPEND);             /* no reply, new program just runs */
+}
+
+
+/*===========================================================================*
+ *                             read_header                                  *
+ *===========================================================================*/
+PRIVATE int read_header(fd, ft, text_bytes, data_bytes, bss_bytes, 
+                                               tot_bytes, sym_bytes, sc, pc)
+int fd;                                /* file descriptor for reading exec file */
+int *ft;                       /* place to return ft number */
+vir_bytes *text_bytes;         /* place to return text size */
+vir_bytes *data_bytes;         /* place to return initialized data size */
+vir_bytes *bss_bytes;          /* place to return bss size */
+phys_bytes *tot_bytes;         /* place to return total size */
+long *sym_bytes;               /* place to return symbol table size */
+vir_clicks sc;                 /* stack size in clicks */
+vir_bytes *pc;                 /* program entry point (initial PC) */
+{
+/* Read the header and extract the text, data, bss and total sizes from it. */
+
+  int m, ct;
+  vir_clicks tc, dc, s_vir, dvir;
+  phys_clicks totc;
+  struct exec hdr;             /* a.out header is read in here */
+
+  /* Read the header and check the magic number.  The standard MINIX header 
+   * is defined in <a.out.h>.  It consists of 8 chars followed by 6 longs.
+   * Then come 4 more longs that are not used here.
+   *   Byte 0: magic number 0x01
+   *   Byte 1: magic number 0x03
+   *   Byte 2: normal = 0x10 (not checked, 0 is OK), separate I/D = 0x20
+   *   Byte 3: CPU type, Intel 16 bit = 0x04, Intel 32 bit = 0x10, 
+   *            Motorola = 0x0B, Sun SPARC = 0x17
+   *   Byte 4: Header length = 0x20
+   *   Bytes 5-7 are not used.
+   *
+   *   Now come the 6 longs
+   *   Bytes  8-11: size of text segments in bytes
+   *   Bytes 12-15: size of initialized data segment in bytes
+   *   Bytes 16-19: size of bss in bytes
+   *   Bytes 20-23: program entry point
+   *   Bytes 24-27: total memory allocated to program (text, data + stack)
+   *   Bytes 28-31: size of symbol table in bytes
+   * The longs are represented in a machine dependent order,
+   * little-endian on the 8088, big-endian on the 68000.
+   * The header is followed directly by the text and data segments, and the 
+   * symbol table (if any). The sizes are given in the header. Only the 
+   * text and data segments are copied into memory by exec. The header is 
+   * used here only. The symbol table is for the benefit of a debugger and 
+   * is ignored here.
+   */
+
+  if ((m= read(fd, &hdr, A_MINHDR)) < 2) return(ENOEXEC);
+
+  /* Interpreted script? */
+  if (((char *) &hdr)[0] == '#' && ((char *) &hdr)[1] == '!') return(ESCRIPT);
+
+  if (m != A_MINHDR) return(ENOEXEC);
+
+  /* Check magic number, cpu type, and flags. */
+  if (BADMAG(hdr)) return(ENOEXEC);
+#if (CHIP == INTEL && _WORD_SIZE == 2)
+  if (hdr.a_cpu != A_I8086) return(ENOEXEC);
+#endif
+#if (CHIP == INTEL && _WORD_SIZE == 4)
+  if (hdr.a_cpu != A_I80386) return(ENOEXEC);
+#endif
+  if ((hdr.a_flags & ~(A_NSYM | A_EXEC | A_SEP)) != 0) return(ENOEXEC);
+
+  *ft = ( (hdr.a_flags & A_SEP) ? SEPARATE : 0);    /* separate I & D or not */
+
+  /* Get text and data sizes. */
+  *text_bytes = (vir_bytes) hdr.a_text;        /* text size in bytes */
+  *data_bytes = (vir_bytes) hdr.a_data;        /* data size in bytes */
+  *bss_bytes  = (vir_bytes) hdr.a_bss; /* bss size in bytes */
+  *tot_bytes  = hdr.a_total;           /* total bytes to allocate for prog */
+  *sym_bytes  = hdr.a_syms;            /* symbol table size in bytes */
+  if (*tot_bytes == 0) return(ENOEXEC);
+
+  if (*ft != SEPARATE) {
+       /* If I & D space is not separated, it is all considered data. Text=0*/
+       *data_bytes += *text_bytes;
+       *text_bytes = 0;
+  }
+  *pc = hdr.a_entry;   /* initial address to start execution */
+
+  /* Check to see if segment sizes are feasible. */
+  tc = ((unsigned long) *text_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
+  dc = (*data_bytes + *bss_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
+  totc = (*tot_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
+  if (dc >= totc) return(ENOEXEC);     /* stack must be at least 1 click */
+  dvir = (*ft == SEPARATE ? 0 : tc);
+  s_vir = dvir + (totc - sc);
+  m = size_ok(*ft, tc, dc, sc, dvir, s_vir);
+  ct = hdr.a_hdrlen & BYTE;            /* header length */
+  if (ct > A_MINHDR) lseek(fd, (off_t) ct, SEEK_SET); /* skip unused hdr */
+  return(m);
+}
+
+
+/*===========================================================================*
+ *                             new_mem                                      *
+ *===========================================================================*/
+PRIVATE int new_mem(sh_mp, text_bytes, data_bytes,bss_bytes,stk_bytes,tot_bytes)
+struct mproc *sh_mp;           /* text can be shared with this process */
+vir_bytes text_bytes;          /* text segment size in bytes */
+vir_bytes data_bytes;          /* size of initialized data in bytes */
+vir_bytes bss_bytes;           /* size of bss in bytes */
+vir_bytes stk_bytes;           /* size of initial stack segment in bytes */
+phys_bytes tot_bytes;          /* total memory to allocate, including gap */
+{
+/* Allocate new memory and release the old memory.  Change the map and report
+ * the new map to the kernel.  Zero the new core image's bss, gap and stack.
+ */
+
+  register struct mproc *rmp;
+  vir_clicks text_clicks, data_clicks, gap_clicks, stack_clicks, tot_clicks;
+  phys_clicks new_base;
+  static char zero[1024];              /* used to zero bss */
+  phys_bytes bytes, base, count, bss_offset;
+
+  /* No need to allocate text if it can be shared. */
+  if (sh_mp != NULL) text_bytes = 0;
+
+  /* Allow the old data to be swapped out to make room.  (Which is really a
+   * waste of time, because we are going to throw it away anyway.)
+   */
+  rmp->mp_flags |= WAITING;
+
+  /* Acquire the new memory.  Each of the 4 parts: text, (data+bss), gap,
+   * and stack occupies an integral number of clicks, starting at click
+   * boundary.  The data and bss parts are run together with no space.
+   */
+  text_clicks = ((unsigned long) text_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
+  data_clicks = (data_bytes + bss_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
+  stack_clicks = (stk_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
+  tot_clicks = (tot_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT;
+  gap_clicks = tot_clicks - data_clicks - stack_clicks;
+  if ( (int) gap_clicks < 0) return(ENOMEM);
+
+  /* Try to allocate memory for the new process. */
+  new_base = alloc_mem(text_clicks + tot_clicks);
+  if (new_base == NO_MEM) return(ENOMEM);
+
+  /* We've got memory for the new core image.  Release the old one. */
+  rmp = mp;
+
+  if (find_share(rmp, rmp->mp_ino, rmp->mp_dev, rmp->mp_ctime) == NULL) {
+       /* No other process shares the text segment, so free it. */
+       free_mem(rmp->mp_seg[T].mem_phys, rmp->mp_seg[T].mem_len);
+  }
+  /* Free the data and stack segments. */
+  free_mem(rmp->mp_seg[D].mem_phys,
+      rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len - rmp->mp_seg[D].mem_vir);
+
+  /* We have now passed the point of no return.  The old core image has been
+   * forever lost, memory for a new core image has been allocated.  Set up
+   * and report new map.
+   */
+  if (sh_mp != NULL) {
+       /* Share the text segment. */
+       rmp->mp_seg[T] = sh_mp->mp_seg[T];
+  } else {
+       rmp->mp_seg[T].mem_phys = new_base;
+       rmp->mp_seg[T].mem_vir = 0;
+       rmp->mp_seg[T].mem_len = text_clicks;
+  }
+  rmp->mp_seg[D].mem_phys = new_base + text_clicks;
+  rmp->mp_seg[D].mem_vir = 0;
+  rmp->mp_seg[D].mem_len = data_clicks;
+  rmp->mp_seg[S].mem_phys = rmp->mp_seg[D].mem_phys + data_clicks + gap_clicks;
+  rmp->mp_seg[S].mem_vir = rmp->mp_seg[D].mem_vir + data_clicks + gap_clicks;
+  rmp->mp_seg[S].mem_len = stack_clicks;
+
+#if (CHIP == M68000)
+  rmp->mp_seg[T].mem_vir = 0;
+  rmp->mp_seg[D].mem_vir = rmp->mp_seg[T].mem_len;
+  rmp->mp_seg[S].mem_vir = rmp->mp_seg[D].mem_vir + rmp->mp_seg[D].mem_len + gap_clicks;
+#endif
+
+  sys_newmap(who, rmp->mp_seg);   /* report new map to the kernel */
+
+  /* The old memory may have been swapped out, but the new memory is real. */
+  rmp->mp_flags &= ~(WAITING|ONSWAP|SWAPIN);
+
+  /* Zero the bss, gap, and stack segment. */
+  bytes = (phys_bytes)(data_clicks + gap_clicks + stack_clicks) << CLICK_SHIFT;
+  base = (phys_bytes) rmp->mp_seg[D].mem_phys << CLICK_SHIFT;
+  bss_offset = (data_bytes >> CLICK_SHIFT) << CLICK_SHIFT;
+  base += bss_offset;
+  bytes -= bss_offset;
+
+  while (bytes > 0) {
+       count = MIN(bytes, (phys_bytes) sizeof(zero));
+       if (sys_physcopy(PM_PROC_NR, D, (phys_bytes) zero,
+                               NONE, PHYS_SEG, base, count) != OK) {
+               panic("new_mem can't zero", NO_NUM);
+       }
+       base += count;
+       bytes -= count;
+  }
+  return(OK);
+}
+
+
+/*===========================================================================*
+ *                             patch_ptr                                    *
+ *===========================================================================*/
+PRIVATE void patch_ptr(stack, base)
+char stack[ARG_MAX];   /* pointer to stack image within MM */
+vir_bytes base;                        /* virtual address of stack base inside user */
+{
+/* When doing an exec(name, argv, envp) call, the user builds up a stack
+ * image with arg and env pointers relative to the start of the stack.  Now
+ * these pointers must be relocated, since the stack is not positioned at
+ * address 0 in the user's address space.
+ */
+
+  char **ap, flag;
+  vir_bytes v;
+
+  flag = 0;                    /* counts number of 0-pointers seen */
+  ap = (char **) stack;                /* points initially to 'nargs' */
+  ap++;                                /* now points to argv[0] */
+  while (flag < 2) {
+       if (ap >= (char **) &stack[ARG_MAX]) return;    /* too bad */
+       if (*ap != NULL) {
+               v = (vir_bytes) *ap;    /* v is relative pointer */
+               v += base;              /* relocate it */
+               *ap = (char *) v;       /* put it back */
+       } else {
+               flag++;
+       }
+       ap++;
+  }
+}
+
+
+/*===========================================================================*
+ *                             insert_arg                                   *
+ *===========================================================================*/
+PRIVATE int insert_arg(stack, stk_bytes, arg, replace)
+char stack[ARG_MAX];           /* pointer to stack image within MM */
+vir_bytes *stk_bytes;          /* size of initial stack */
+char *arg;                     /* argument to prepend/replace as new argv[0] */
+int replace;
+{
+/* Patch the stack so that arg will become argv[0].  Be careful, the stack may
+ * be filled with garbage, although it normally looks like this:
+ *     nargs argv[0] ... argv[nargs-1] NULL envp[0] ... NULL
+ * followed by the strings "pointed" to by the argv[i] and the envp[i].  The
+ * pointers are really offsets from the start of stack.
+ * Return true iff the operation succeeded.
+ */
+  int offset, a0, a1, old_bytes = *stk_bytes;
+
+  /* Prepending arg adds at least one string and a zero byte. */
+  offset = strlen(arg) + 1;
+
+  a0 = (int) ((char **) stack)[1];     /* argv[0] */
+  if (a0 < 4 * PTRSIZE || a0 >= old_bytes) return(FALSE);
+
+  a1 = a0;             /* a1 will point to the strings to be moved */
+  if (replace) {
+       /* Move a1 to the end of argv[0][] (argv[1] if nargs > 1). */
+       do {
+               if (a1 == old_bytes) return(FALSE);
+               --offset;
+       } while (stack[a1++] != 0);
+  } else {
+       offset += PTRSIZE;      /* new argv[0] needs new pointer in argv[] */
+       a0 += PTRSIZE;          /* location of new argv[0][]. */
+  }
+
+  /* stack will grow by offset bytes (or shrink by -offset bytes) */
+  if ((*stk_bytes += offset) > ARG_MAX) return(FALSE);
+
+  /* Reposition the strings by offset bytes */
+  memmove(stack + a1 + offset, stack + a1, old_bytes - a1);
+
+  strcpy(stack + a0, arg);     /* Put arg in the new space. */
+
+  if (!replace) {
+       /* Make space for a new argv[0]. */
+       memmove(stack + 2 * PTRSIZE, stack + 1 * PTRSIZE, a0 - 2 * PTRSIZE);
+
+       ((char **) stack)[0]++; /* nargs++; */
+  }
+  /* Now patch up argv[] and envp[] by offset. */
+  patch_ptr(stack, (vir_bytes) offset);
+  ((char **) stack)[1] = (char *) a0;  /* set argv[0] correctly */
+  return(TRUE);
+}
+
+
+/*===========================================================================*
+ *                             patch_stack                                  *
+ *===========================================================================*/
+PRIVATE char *patch_stack(fd, stack, stk_bytes, script)
+int fd;                                /* file descriptor to open script file */
+char stack[ARG_MAX];           /* pointer to stack image within MM */
+vir_bytes *stk_bytes;          /* size of initial stack */
+char *script;                  /* name of script to interpret */
+{
+/* Patch the argument vector to include the path name of the script to be
+ * interpreted, and all strings on the #! line.  Returns the path name of
+ * the interpreter.
+ */
+  char *sp, *interp = NULL;
+  int n;
+  enum { INSERT=FALSE, REPLACE=TRUE };
+
+  /* Make script[] the new argv[0]. */
+  if (!insert_arg(stack, stk_bytes, script, REPLACE)) return(NULL);
+
+  if (lseek(fd, 2L, 0) == -1                   /* just behind the #! */
+    || (n= read(fd, script, PATH_MAX)) < 0     /* read line one */
+    || (sp= memchr(script, '\n', n)) == NULL)  /* must be a proper line */
+       return(NULL);
+
+  /* Move sp backwards through script[], prepending each string to stack. */
+  for (;;) {
+       /* skip spaces behind argument. */
+       while (sp > script && (*--sp == ' ' || *sp == '\t')) {}
+       if (sp == script) break;
+
+       sp[1] = 0;
+       /* Move to the start of the argument. */
+       while (sp > script && sp[-1] != ' ' && sp[-1] != '\t') --sp;
+
+       interp = sp;
+       if (!insert_arg(stack, stk_bytes, sp, INSERT)) return(NULL);
+  }
+
+  /* Round *stk_bytes up to the size of a pointer for alignment contraints. */
+  *stk_bytes= ((*stk_bytes + PTRSIZE - 1) / PTRSIZE) * PTRSIZE;
+
+  close(fd);
+  return(interp);
+}
+
+
+/*===========================================================================*
+ *                             rw_seg                                       *
+ *===========================================================================*/
+PUBLIC void rw_seg(rw, fd, proc, seg, seg_bytes0)
+int rw;                                /* 0 = read, 1 = write */
+int fd;                                /* file descriptor to read from / write to */
+int proc;                      /* process number */
+int seg;                       /* T, D, or S */
+phys_bytes seg_bytes0;         /* how much is to be transferred? */
+{
+/* Transfer text or data from/to a file and copy to/from a process segment.
+ * This procedure is a little bit tricky.  The logical way to transfer a
+ * segment would be block by block and copying each block to/from the user
+ * space one at a time.  This is too slow, so we do something dirty here,
+ * namely send the user space and virtual address to the file system in the
+ * upper 10 bits of the file descriptor, and pass it the user virtual address
+ * instead of a MM address.  The file system extracts these parameters when 
+ * gets a read or write call from the memory manager, which is the only process
+ * that is permitted to use this trick.  The file system then copies the whole 
+ * segment directly to/from user space, bypassing MM completely.
+ *
+ * The byte count on read is usually smaller than the segment count, because
+ * a segment is padded out to a click multiple, and the data segment is only
+ * partially initialized.
+ */
+
+  int new_fd, bytes, r;
+  char *ubuf_ptr;
+  struct mem_map *sp = &mproc[proc].mp_seg[seg];
+  phys_bytes seg_bytes = seg_bytes0;
+
+  new_fd = (proc << 7) | (seg << 5) | fd;
+  ubuf_ptr = (char *) ((vir_bytes) sp->mem_vir << CLICK_SHIFT);
+
+  while (seg_bytes != 0) {
+#define MM_CHUNK_SIZE 8192
+       bytes = MIN((INT_MAX / MM_CHUNK_SIZE) * MM_CHUNK_SIZE, seg_bytes);
+       if (rw == 0) {
+               r = read(new_fd, ubuf_ptr, bytes);
+       } else {
+               r = write(new_fd, ubuf_ptr, bytes);
+       }
+       if (r != bytes) break;
+       ubuf_ptr += bytes;
+       seg_bytes -= bytes;
+  }
+}
+
+
+/*===========================================================================*
+ *                             find_share                                   *
+ *===========================================================================*/
+PUBLIC struct mproc *find_share(mp_ign, ino, dev, ctime)
+struct mproc *mp_ign;          /* process that should not be looked at */
+ino_t ino;                     /* parameters that uniquely identify a file */
+dev_t dev;
+time_t ctime;
+{
+/* Look for a process that is the file <ino, dev, ctime> in execution.  Don't
+ * accidentally "find" mp_ign, because it is the process on whose behalf this
+ * call is made.
+ */
+  struct mproc *sh_mp;
+
+  for (sh_mp = &mproc[INIT_PROC_NR]; sh_mp < &mproc[NR_PROCS]; sh_mp++) {
+       if (!(sh_mp->mp_flags & SEPARATE)) continue;
+       if (sh_mp == mp_ign) continue;
+       if (sh_mp->mp_ino != ino) continue;
+       if (sh_mp->mp_dev != dev) continue;
+       if (sh_mp->mp_ctime != ctime) continue;
+       return sh_mp;
+  }
+  return(NULL);
+}
diff --git a/servers/pm/forkexit.c b/servers/pm/forkexit.c
new file mode 100644 (file)
index 0000000..3de7128
--- /dev/null
@@ -0,0 +1,288 @@
+/* This file deals with creating processes (via FORK) and deleting them (via
+ * EXIT/WAIT).  When a process forks, a new slot in the 'mproc' table is
+ * allocated for it, and a copy of the parent's core image is made for the
+ * child.  Then the kernel and file system are informed.  A process is removed
+ * from the 'mproc' table when two events have occurred: (1) it has exited or
+ * been killed by a signal, and (2) the parent has done a WAIT.  If the process
+ * exits first, it continues to occupy a slot until the parent does a WAIT.
+ *
+ * The entry points into this file are:
+ *   do_fork:   perform the FORK system call
+ *   do_mm_exit: perform the EXIT system call (by calling mm_exit())
+ *   mm_exit:   actually do the exiting
+ *   do_wait:   perform the WAITPID or WAIT system call
+ */
+
+
+#include "mm.h"
+#include <sys/wait.h>
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include <minix/utils.h>
+#include <signal.h>
+#include "mproc.h"
+#include "param.h"
+
+#define LAST_FEW            2  /* last few slots reserved for superuser */
+
+PRIVATE pid_t next_pid = INIT_PID+1;   /* next pid to be assigned */
+
+FORWARD _PROTOTYPE (void cleanup, (register struct mproc *child) );
+
+/*===========================================================================*
+ *                             do_fork                                      *
+ *===========================================================================*/
+PUBLIC int do_fork()
+{
+/* The process pointed to by 'mp' has forked.  Create a child process. */
+
+  register struct mproc *rmp;  /* pointer to parent */
+  register struct mproc *rmc;  /* pointer to child */
+  int i, child_nr, t;
+  phys_clicks prog_clicks, child_base;
+  phys_bytes prog_bytes, parent_abs, child_abs;        /* Intel only */
+
+ /* If tables might fill up during FORK, don't even start since recovery half
+  * way through is such a nuisance.
+  */
+  rmp = mp;
+  if ((procs_in_use == NR_PROCS) || 
+               (procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0))
+  {
+       printf("MM: proc table full!\n");
+       return(EAGAIN);
+  }
+
+  /* Determine how much memory to allocate.  Only the data and stack need to
+   * be copied, because the text segment is either shared or of zero length.
+   */
+  prog_clicks = (phys_clicks) rmp->mp_seg[S].mem_len;
+  prog_clicks += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
+  prog_bytes = (phys_bytes) prog_clicks << CLICK_SHIFT;
+  if ( (child_base = alloc_mem(prog_clicks)) == NO_MEM) return(ENOMEM);
+
+  /* Create a copy of the parent's core image for the child. */
+  child_abs = (phys_bytes) child_base << CLICK_SHIFT;
+  parent_abs = (phys_bytes) rmp->mp_seg[D].mem_phys << CLICK_SHIFT;
+  i = sys_abscopy(parent_abs, child_abs, prog_bytes);
+  if (i < 0) panic("do_fork can't copy", i);
+
+  /* Find a slot in 'mproc' for the child process.  A slot must exist. */
+  for (rmc = &mproc[0]; rmc < &mproc[NR_PROCS]; rmc++)
+       if ( (rmc->mp_flags & IN_USE) == 0) break;
+
+  /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
+  child_nr = (int)(rmc - mproc);       /* slot number of the child */
+  procs_in_use++;
+  *rmc = *rmp;                 /* copy parent's process slot to child's */
+
+  rmc->mp_parent = who;                        /* record child's parent */
+  rmc->mp_flags &= (IN_USE|SEPARATE);  /* inherit only these flags */
+
+  /* A separate I&D child keeps the parents text segment.  The data and stack
+   * segments must refer to the new copy.
+   */
+  if (!(rmc->mp_flags & SEPARATE)) rmc->mp_seg[T].mem_phys = child_base;
+  rmc->mp_seg[D].mem_phys = child_base;
+  rmc->mp_seg[S].mem_phys = rmc->mp_seg[D].mem_phys + 
+                       (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir);
+  rmc->mp_exitstatus = 0;
+  rmc->mp_sigstatus = 0;
+
+  /* Find a free pid for the child and put it in the table. */
+  do {
+       t = 0;                  /* 't' = 0 means pid still free */
+       next_pid = (next_pid < 30000 ? next_pid + 1 : INIT_PID + 1);
+       for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++)
+               if (rmp->mp_pid == next_pid || rmp->mp_procgrp == next_pid) {
+                       t = 1;
+                       break;
+               }
+       rmc->mp_pid = next_pid; /* assign pid to child */
+  } while (t);
+
+  /* Tell kernel and file system about the (now successful) FORK. */
+  sys_fork(who, child_nr, rmc->mp_pid);
+  tell_fs(FORK, who, child_nr, rmc->mp_pid);
+
+  /* Report child's memory map to kernel. */
+  sys_newmap(child_nr, rmc->mp_seg);
+
+  /* Reply to child to wake it up. */
+  setreply(child_nr, 0);
+  return(next_pid);             /* child's pid */
+}
+
+
+/*===========================================================================*
+ *                             do_mm_exit                                   *
+ *===========================================================================*/
+PUBLIC int do_mm_exit()
+{
+/* Perform the exit(status) system call. The real work is done by mm_exit(),
+ * which is also called when a process is killed by a signal.
+ */
+
+  mm_exit(mp, m_in.status);
+  return(SUSPEND);             /* can't communicate from beyond the grave */
+}
+
+
+/*===========================================================================*
+ *                             mm_exit                                      *
+ *===========================================================================*/
+PUBLIC void mm_exit(rmp, exit_status)
+register struct mproc *rmp;    /* pointer to the process to be terminated */
+int exit_status;               /* the process' exit status (for parent) */
+{
+/* A process is done.  Release most of the process' possessions.  If its
+ * parent is waiting, release the rest, else keep the process slot and
+ * become a zombie.
+ */
+
+  register int proc_nr;
+  int parent_waiting, right_child;
+  pid_t pidarg, procgrp;
+  struct mproc *p_mp;
+
+  proc_nr = (int) (rmp - mproc);       /* get process slot number */
+
+  /* Remember a session leader's process group. */
+  procgrp = (rmp->mp_pid == mp->mp_procgrp) ? mp->mp_procgrp : 0;
+
+  /* If the exited process has a timer pending, kill it. */
+  if (rmp->mp_flags & ALARM_ON) set_alarm(proc_nr, (unsigned) 0);
+
+  /* Tell the kernel and FS that the process is no longer runnable. */
+  tell_fs(EXIT, proc_nr, 0, 0);  /* file system can free the proc slot */
+  sys_xit(rmp->mp_parent, proc_nr);
+
+  /* Release the memory occupied by the child. */
+  if (find_share(rmp, rmp->mp_ino, rmp->mp_dev, rmp->mp_ctime) == NULL) {
+       /* No other process shares the text segment, so free it. */
+       free_mem(rmp->mp_seg[T].mem_phys, rmp->mp_seg[T].mem_len);
+  }
+  /* Free the data and stack segments. */
+  free_mem(rmp->mp_seg[D].mem_phys,
+      rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len - rmp->mp_seg[D].mem_vir);
+
+  /* The process slot can only be freed if the parent has done a WAIT. */
+  rmp->mp_exitstatus = (char) exit_status;
+
+  p_mp = &mproc[rmp->mp_parent];       /* process' parent */
+  pidarg = p_mp->mp_wpid;              /* who's being waited for? */
+  parent_waiting = p_mp->mp_flags & WAITING;
+
+  right_child =                                /* child meets one of the 3 tests? */
+       (pidarg == -1 || pidarg == rmp->mp_pid || -pidarg == rmp->mp_procgrp);
+
+  if (parent_waiting && right_child) {
+       cleanup(rmp);                   /* tell parent and release child slot */
+  } else {
+       rmp->mp_flags = IN_USE|ZOMBIE;  /* parent not waiting, zombify child */
+       sig_proc(p_mp, SIGCHLD);        /* send parent a "child died" signal */
+  }
+
+  /* If the process has children, disinherit them.  INIT is the new parent. */
+  for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
+       if (rmp->mp_flags & IN_USE && rmp->mp_parent == proc_nr) {
+               /* 'rmp' now points to a child to be disinherited. */
+               rmp->mp_parent = INIT_PROC_NR;
+               parent_waiting = mproc[INIT_PROC_NR].mp_flags & WAITING;
+               if (parent_waiting && (rmp->mp_flags & ZOMBIE)) cleanup(rmp);
+       }
+  }
+
+  /* Send a hangup to the process' process group if it was a session leader. */
+  if (procgrp != 0) check_sig(-procgrp, SIGHUP);
+}
+
+
+/*===========================================================================*
+ *                             do_waitpid                                   *
+ *===========================================================================*/
+PUBLIC int do_waitpid()
+{
+/* A process wants to wait for a child to terminate. If one is already waiting,
+ * go clean it up and let this WAIT call terminate.  Otherwise, really wait.
+ * Both WAIT and WAITPID are handled by this code.
+ */
+
+  register struct mproc *rp;
+  int pidarg, options, children;
+
+  /* A process calling WAIT never gets a reply in the usual way at the end
+   * of the main loop (unless WNOHANG is set or no qualifying child exists).
+   * If a child has already exited, the routine cleanup() sends the reply
+   * to awaken the caller.
+   */
+
+  /* Set internal variables, depending on whether this is WAIT or WAITPID. */
+  pidarg  = (call_nr == WAIT ? -1 : m_in.pid);    /* 1st param of waitpid */
+  options = (call_nr == WAIT ?  0 : m_in.sig_nr);  /* 3rd param of waitpid */
+  if (pidarg == 0) pidarg = -mp->mp_procgrp;   /* pidarg < 0 ==> proc grp */
+
+  /* Is there a child waiting to be collected? At this point, pidarg != 0:
+   *   pidarg  >  0 means pidarg is pid of a specific process to wait for
+   *   pidarg == -1 means wait for any child
+   *   pidarg  < -1 means wait for any child whose process group = -pidarg
+   */
+  children = 0;
+  for (rp = &mproc[0]; rp < &mproc[NR_PROCS]; rp++) {
+       if ( (rp->mp_flags & IN_USE) && rp->mp_parent == who) {
+               /* The value of pidarg determines which children qualify. */
+               if (pidarg  > 0 && pidarg != rp->mp_pid) continue;
+               if (pidarg < -1 && -pidarg != rp->mp_procgrp) continue;
+
+               children++;             /* this child is acceptable */
+               if (rp->mp_flags & ZOMBIE) {
+                       /* This child meets the pid test and has exited. */
+                       cleanup(rp);    /* this child has already exited */
+                       return(SUSPEND);
+               }
+               if ((rp->mp_flags & STOPPED) && rp->mp_sigstatus) {
+                       /* This child meets the pid test and is being traced.*/
+                       mp->mp_reply.reply_res2 = 0177|(rp->mp_sigstatus << 8);
+                       rp->mp_sigstatus = 0;
+                       return(rp->mp_pid);
+               }
+       }
+  }
+
+  /* No qualifying child has exited.  Wait for one, unless none exists. */
+  if (children > 0) {
+       /* At least 1 child meets the pid test exists, but has not exited. */
+       if (options & WNOHANG) return(0);    /* parent does not want to wait */
+       mp->mp_flags |= WAITING;             /* parent wants to wait */
+       mp->mp_wpid = (pid_t) pidarg;        /* save pid for later */
+       return(SUSPEND);                     /* do not reply, let it wait */
+  } else {
+       /* No child even meets the pid test.  Return error immediately. */
+       return(ECHILD);                      /* no - parent has no children */
+  }
+}
+
+
+/*===========================================================================*
+ *                             cleanup                                      *
+ *===========================================================================*/
+PRIVATE void cleanup(child)
+register struct mproc *child;  /* tells which process is exiting */
+{
+/* Finish off the exit of a process.  The process has exited or been killed
+ * by a signal, and its parent is waiting.
+ */
+
+  struct mproc *parent = &mproc[child->mp_parent];
+  int exitstatus;
+
+  /* Wake up the parent. */
+  exitstatus = (child->mp_exitstatus << 8) | (child->mp_sigstatus & 0377);
+  parent->mp_reply.reply_res2 = exitstatus;
+  setreply(child->mp_parent, child->mp_pid);
+  parent->mp_flags &= ~WAITING;                /* parent no longer waiting */
+
+  /* Release the process table entry. */
+  child->mp_flags = 0;
+  procs_in_use--;
+}
diff --git a/servers/pm/getset.c b/servers/pm/getset.c
new file mode 100644 (file)
index 0000000..cd8d6d5
--- /dev/null
@@ -0,0 +1,78 @@
+/* This file handles the 4 system calls that get and set uids and gids.
+ * It also handles getpid(), setsid(), and getpgrp().  The code for each
+ * one is so tiny that it hardly seemed worthwhile to make each a separate
+ * function.
+ */
+
+#include "mm.h"
+#include <minix/callnr.h>
+#include <signal.h>
+#include "mproc.h"
+#include "param.h"
+
+/*===========================================================================*
+ *                             do_getset                                    *
+ *===========================================================================*/
+PUBLIC int do_getset()
+{
+/* Handle GETUID, GETGID, GETPID, GETPGRP, SETUID, SETGID, SETSID.  The four
+ * GETs and SETSID return their primary results in 'r'.  GETUID, GETGID, and
+ * GETPID also return secondary results (the effective IDs, or the parent
+ * process ID) in 'reply_res2', which is returned to the user.
+ */
+
+  register struct mproc *rmp = mp;
+  register int r;
+
+  switch(call_nr) {
+       case GETUID:
+               r = rmp->mp_realuid;
+               rmp->mp_reply.reply_res2 = rmp->mp_effuid;
+               break;
+
+       case GETGID:
+               r = rmp->mp_realgid;
+               rmp->mp_reply.reply_res2 = rmp->mp_effgid;
+               break;
+
+       case GETPID:
+               r = mproc[who].mp_pid;
+               rmp->mp_reply.reply_res2 = mproc[rmp->mp_parent].mp_pid;
+               break;
+
+       case SETUID:
+               if (rmp->mp_realuid != (uid_t) m_in.usr_id && 
+                               rmp->mp_effuid != SUPER_USER)
+                       return(EPERM);
+               rmp->mp_realuid = (uid_t) m_in.usr_id;
+               rmp->mp_effuid = (uid_t) m_in.usr_id;
+               tell_fs(SETUID, who, rmp->mp_realuid, rmp->mp_effuid);
+               r = OK;
+               break;
+
+       case SETGID:
+               if (rmp->mp_realgid != (gid_t) m_in.grp_id && 
+                               rmp->mp_effuid != SUPER_USER)
+                       return(EPERM);
+               rmp->mp_realgid = (gid_t) m_in.grp_id;
+               rmp->mp_effgid = (gid_t) m_in.grp_id;
+               tell_fs(SETGID, who, rmp->mp_realgid, rmp->mp_effgid);
+               r = OK;
+               break;
+
+       case SETSID:
+               if (rmp->mp_procgrp == rmp->mp_pid) return(EPERM);
+               rmp->mp_procgrp = rmp->mp_pid;
+               tell_fs(SETSID, who, 0, 0);
+               /*FALL THROUGH*/
+
+       case GETPGRP:
+               r = rmp->mp_procgrp;
+               break;
+
+       default:
+               r = EINVAL;
+               break;  
+  }
+  return(r);
+}
diff --git a/servers/pm/glo.h b/servers/pm/glo.h
new file mode 100644 (file)
index 0000000..6f99a59
--- /dev/null
@@ -0,0 +1,20 @@
+/* EXTERN should be extern except in table.c */
+#ifdef _TABLE
+#undef EXTERN
+#define EXTERN
+#endif
+
+/* Global variables. */
+EXTERN struct mproc *mp;       /* ptr to 'mproc' slot of current process */
+EXTERN int procs_in_use;       /* how many processes are marked as IN_USE */
+
+/* The parameters of the call are kept here. */
+EXTERN message m_in;           /* the incoming message itself is kept here. */
+EXTERN int who;                        /* caller's proc number */
+EXTERN int call_nr;            /* system call number */
+
+extern _PROTOTYPE (int (*call_vec[]), (void) );        /* system call handlers */
+extern char core_name[];       /* file name where core images are produced */
+EXTERN sigset_t core_sset;     /* which signals cause core images */
+EXTERN sigset_t ign_sset;      /* which signals are by default ignored */
+
diff --git a/servers/pm/main.c b/servers/pm/main.c
new file mode 100644 (file)
index 0000000..7b72773
--- /dev/null
@@ -0,0 +1,197 @@
+/* This file contains the main program of the memory manager and some related
+ * procedures.  When MINIX starts up, the kernel runs for a little while,
+ * initializing itself and its tasks, and then it runs MM and FS.  Both MM
+ * and FS initialize themselves as far as they can.  FS then makes a call to
+ * MM, because MM has to wait for FS to acquire a RAM disk.  MM asks the
+ * kernel for all free memory and starts serving requests.
+ *
+ * The entry points into this file are:
+ *   main:     starts MM running
+ *   setreply: set the reply to be sent to process making an MM system call
+ */
+
+#include "mm.h"
+#include <minix/utils.h>
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/ioc_memory.h>
+#include "mproc.h"
+#include "param.h"
+
+FORWARD _PROTOTYPE( void get_work, (void)                              );
+FORWARD _PROTOTYPE( void mm_init, (void)                               );
+
+#define click_to_round_k(n) \
+       ((unsigned) ((((unsigned long) (n) << CLICK_SHIFT) + 512) / 1024))
+
+/*===========================================================================*
+ *                             main                                         *
+ *===========================================================================*/
+PUBLIC void main()
+{
+/* Main routine of the memory manager. */
+
+  int result, proc_nr;
+  struct mproc *rmp;
+
+  mm_init();                   /* initialize memory manager tables */
+
+  /* This is MM's main loop-  get work and do it, forever and forever. */
+  while (TRUE) {
+       get_work();             /* wait for an MM system call */
+
+       /* Check for system notifications first. Special cases. */
+       if (call_nr == HARD_STOP) {             /* MINIX is shutting down */
+               check_sig(-1, SIGKILL);         /* kill all processes */
+               sys_exit(0);
+               /* never reached */
+       } else if (call_nr == KSIG_PENDING) {   /* signals pending */
+               (void) ksig_pending();
+               result = SUSPEND;               /* don't reply */
+       }
+       /* Else, if the system call number is valid, perform the call. */
+       else if ((unsigned) call_nr >= NCALLS) {
+               result = ENOSYS;
+       } else {
+               result = (*call_vec[call_nr])();
+       }
+
+       /* Send the results back to the user to indicate completion. */
+       if (result != SUSPEND) setreply(who, result);
+
+       swap_in();              /* maybe a process can be swapped in? */
+
+       /* Send out all pending reply messages, including the answer to
+        * the call just made above.  The processes must not be swapped out.
+        */
+       for (proc_nr=0, rmp=mproc; proc_nr < NR_PROCS; proc_nr++, rmp++) {
+               if ((rmp->mp_flags & (REPLY | ONSWAP)) == REPLY) {
+                       if (send(proc_nr, &rmp->mp_reply) != OK)
+                               panic("MM can't reply to", proc_nr);
+                       rmp->mp_flags &= ~REPLY;
+               }
+       }
+  }
+}
+
+
+/*===========================================================================*
+ *                             get_work                                     *
+ *===========================================================================*/
+PRIVATE void get_work()
+{
+/* Wait for the next message and extract useful information from it. */
+
+  if (receive(ANY, &m_in) != OK) panic("MM receive error", NO_NUM);
+  who = m_in.m_source;         /* who sent the message */
+  call_nr = m_in.m_type;       /* system call number */
+
+  /* Process slot of caller. Misuse MM's own process slot if the kernel is
+   * calling. The can happen in case of pending kernel signals.
+   */
+  mp = &mproc[who < 0 ? PM_PROC_NR : who];
+}
+
+
+/*===========================================================================*
+ *                             setreply                                     *
+ *===========================================================================*/
+PUBLIC void setreply(proc_nr, result)
+int proc_nr;                   /* process to reply to */
+int result;                    /* result of the call (usually OK or error #)*/
+{
+/* Fill in a reply message to be sent later to a user process.  System calls
+ * may occasionally fill in other fields, this is only for the main return
+ * value, and for setting the "must send reply" flag.
+ */
+
+  register struct mproc *rmp = &mproc[proc_nr];
+
+  rmp->mp_reply.reply_res = result;
+  rmp->mp_flags |= REPLY;      /* reply pending */
+
+  if (rmp->mp_flags & ONSWAP)
+       swap_inqueue(rmp);      /* must swap this process back in */
+}
+
+
+/*===========================================================================*
+ *                             mm_init                                      *
+ *===========================================================================*/
+PRIVATE void mm_init()
+{
+/* Initialize the memory manager. */
+  int s;
+  static char core_sigs[] = { SIGQUIT, SIGILL, SIGTRAP, SIGABRT,
+                       SIGEMT, SIGFPE, SIGUSR1, SIGSEGV, SIGUSR2 };
+  static char ign_sigs[] = { SIGCHLD };
+  register int proc_nr;
+  register struct mproc *rmp;
+  register char *sig_ptr;
+  phys_clicks ram_clicks, total_clicks, minix_clicks, free_clicks;
+  message mess;
+  struct mem_map kernel_map[NR_LOCAL_SEGS];
+  int mem;
+
+  /* Build the set of signals which cause core dumps, and the set of signals
+   * that are by default ignored.
+   */
+  sigemptyset(&core_sset);
+  for (sig_ptr = core_sigs; sig_ptr < core_sigs+sizeof(core_sigs); sig_ptr++)
+       sigaddset(&core_sset, *sig_ptr);
+  sigemptyset(&ign_sset);
+  for (sig_ptr = ign_sigs; sig_ptr < ign_sigs+sizeof(ign_sigs); sig_ptr++)
+       sigaddset(&ign_sset, *sig_ptr);
+
+  /* Get the memory map of the kernel to see how much memory it uses. */
+  if ((s=p_getmap(SYSTASK, kernel_map)) != OK)
+       panic("MM couldn't get proc entry of SYSTASK",s);
+  minix_clicks = (kernel_map[S].mem_phys + kernel_map[S].mem_len)
+                               - kernel_map[T].mem_phys;
+
+  /* Initialize MM's tables. Request a copy of the system image table that
+   * is defined at the kernel level to see which slots to fill in.
+   */
+  for (proc_nr = 0; proc_nr <= INIT_PROC_NR; proc_nr++) {
+       rmp = &mproc[proc_nr];
+       rmp->mp_flags |= IN_USE;
+       if ((s=p_getmap(proc_nr, rmp->mp_seg)) != OK)
+               panic("MM couldn't get proc entry",s);
+       if (rmp->mp_seg[T].mem_len != 0) rmp->mp_flags |= SEPARATE;
+       minix_clicks += (rmp->mp_seg[S].mem_phys + rmp->mp_seg[S].mem_len)
+                               - rmp->mp_seg[T].mem_phys;
+  }
+  mproc[INIT_PROC_NR].mp_pid = INIT_PID;
+  sigemptyset(&mproc[INIT_PROC_NR].mp_ignore);
+  sigemptyset(&mproc[INIT_PROC_NR].mp_catch);
+  procs_in_use = LOW_USER + 1;
+
+  /* Wait for FS to send a message telling the RAM disk size then go "on-line".
+   */
+  if (receive(FS_PROC_NR, &mess) != OK)
+       panic("MM can't obtain RAM disk size from FS", NO_NUM);
+
+  ram_clicks = mess.MEM_CHUNK_SIZE;
+
+  /* Initialize tables to all physical mem. */
+  mem_init(&free_clicks);
+  total_clicks = minix_clicks + ram_clicks + free_clicks;
+
+  /* Print memory information. */
+  printf("Memory size=%uK   ", click_to_round_k(total_clicks));
+  printf("MINIX=%uK   ", click_to_round_k(minix_clicks));
+  printf("RAM disk=%uK   ", click_to_round_k(ram_clicks));
+  printf("Available=%uK\n\n", click_to_round_k(free_clicks));
+
+  /* Tell FS to continue. */
+  if (send(FS_PROC_NR, &mess) != OK)
+       panic("MM can't sync up with FS", NO_NUM);
+
+  /* Tell the memory task where my process table is for the sake of ps(1). */
+  if ((mem = open("/dev/ram", O_RDWR)) != -1) {
+       ioctl(mem, MIOCSPSINFO, (void *) mproc);
+       close(mem);
+  }
+}
diff --git a/servers/pm/misc.c b/servers/pm/misc.c
new file mode 100644 (file)
index 0000000..cbf1cb7
--- /dev/null
@@ -0,0 +1,221 @@
+/* Miscellaneous system calls.                         Author: Kees J. Bot
+ *                                                             31 Mar 2000
+ * The entry points into this file are:
+ *   do_reboot: kill all processes, then reboot system
+ *   do_svrctl: memory manager control
+ *   do_getsysinfo: request copy of MM data structure
+ */
+
+#include "mm.h"
+#include <minix/callnr.h>
+#include <signal.h>
+#include <sys/svrctl.h>
+#include <minix/com.h>
+#include <minix/utils.h>
+#include <string.h>
+#include "mproc.h"
+#include "param.h"
+
+FORWARD _PROTOTYPE( char *find_key, (const char *params, const char *key));
+
+/* MM gets a copy of all boot monitor parameters. */
+PRIVATE char monitor_params[128*sizeof(char *)];
+
+/*=====================================================================*
+ *                         do_getsysinfo                              *
+ *=====================================================================*/
+PUBLIC int do_getsysinfo()
+{
+  return(OK);
+}
+
+
+/*=====================================================================*
+ *                         do_reboot                                  *
+ *=====================================================================*/
+PUBLIC int do_reboot()
+{
+  register struct mproc *rmp = mp;
+  char monitor_code[32*sizeof(char *)];
+
+  if (rmp->mp_effuid != SUPER_USER) return(EPERM);
+
+  switch (m_in.reboot_flag) {
+  case RBT_HALT:
+  case RBT_REBOOT:
+  case RBT_PANIC:
+  case RBT_RESET:
+       break;
+  case RBT_MONITOR:
+       if (m_in.reboot_size >= sizeof(monitor_code)) return(EINVAL);
+       if (sys_datacopy(who, (vir_bytes) m_in.reboot_code,
+               PM_PROC_NR, (vir_bytes) monitor_code,
+               (phys_bytes) (m_in.reboot_size+1)) != OK) return(EFAULT);
+       if (monitor_code[m_in.reboot_size] != 0) return(EINVAL);
+       break;
+  default:
+       return(EINVAL);
+  }
+
+  check_sig(-1, SIGKILL);              /* kill all processes except init */
+  tell_fs(REBOOT,0,0,0);               /* tell FS to prepare for shutdown */
+
+  sys_abort(m_in.reboot_flag, PM_PROC_NR, monitor_code, m_in.reboot_size);
+  sys_exit(0);
+}
+
+/*=====================================================================*
+ *                         do_svrctl                                  *
+ *=====================================================================*/
+PUBLIC int do_svrctl()
+{
+  static int initialized = 0;
+  int s, req;
+  vir_bytes ptr;
+  req = m_in.svrctl_req;
+  ptr = (vir_bytes) m_in.svrctl_argp;
+
+  /* Initialize private copy of monitor parameters on first call. */
+  if (! initialized) {
+      if ((s=sys_getmonparams(monitor_params, sizeof(monitor_params))) != OK)
+          printf("MM: Warning couldn't get copy of monitor params: %d\n",s);
+      else
+          initialized = 1;
+  }
+
+  /* Binary compatibility check. */
+  if (req == SYSGETENV) {
+#if DEAD_CODE
+       printf("SYSGETENV by %d (fix!)\n", who);
+#endif
+       req = MMGETPARAM;
+  }
+
+  /* Is the request for the kernel? Forward it, except for SYSGETENV. */
+  if (((req >> 8) & 0xFF) == 'S') {
+
+      /* Simply forward call to the SYSTEM task. */
+      return(sys_svrctl(who, req, mp->mp_effuid == SUPER_USER, ptr));
+  }
+
+  /* Control operations local to the MM. */
+  switch(req) {
+  case MMGETPARAM: {
+      struct sysgetenv sysgetenv;
+      char search_key[64];
+      char *val_start;
+      size_t val_len;
+      size_t copy_len;
+
+      /* Check if boot monitor parameters are in place. */
+      if (! initialized) return(EAGAIN);
+
+      /* Copy sysgetenv structure to MM. */
+      if (sys_datacopy(who, ptr, SELF, (vir_bytes) &sysgetenv, 
+              sizeof(sysgetenv)) != OK) return(EFAULT);  
+
+      if (sysgetenv.keylen == 0) {     /* copy all parameters */
+          val_start = monitor_params;
+          val_len = sizeof(monitor_params);
+      } 
+      else {                           /* lookup value for key */
+          /* Try to get a copy of the requested key. */
+          if (sysgetenv.keylen > sizeof(search_key)) return(EINVAL);
+          if ((s = sys_datacopy(who, (vir_bytes) sysgetenv.key,
+                  SELF, (vir_bytes) search_key, sysgetenv.keylen)) != OK)
+              return(s);
+
+          /* Make sure key is null-terminated and lookup value. */
+          search_key[sysgetenv.keylen-1]= '\0';
+          if ((val_start = find_key(monitor_params, search_key)) == NULL)
+               return(ESRCH);
+          val_len = strlen(val_start) + 1;
+      }
+
+      /* Value found, make the actual copy (as far as possible). */
+      copy_len = MAX(val_len, sysgetenv.vallen); 
+      if ((s=sys_datacopy(SELF, (vir_bytes) val_start, 
+              who, (vir_bytes) sysgetenv.val, copy_len)) != OK)
+          return(s);
+
+      /* See if it fits in the client's buffer. */
+      return (copy_len > sysgetenv.vallen) ? E2BIG : OK;
+  }
+  case MMSIGNON: {
+       /* A user process becomes a task.  Simulate an exit by
+        * releasing a waiting parent and disinheriting children.
+        */
+       struct mproc *rmp;
+       pid_t pidarg;
+
+       if (mp->mp_effuid != SUPER_USER) return(EPERM);
+
+       rmp = &mproc[mp->mp_parent];
+       tell_fs(EXIT, who, 0, 0);
+
+       pidarg = rmp->mp_wpid;
+       if ((rmp->mp_flags & WAITING) && (pidarg == -1
+               || pidarg == mp->mp_pid || -pidarg == mp->mp_procgrp))
+       {
+               /* Wake up the parent. */
+               rmp->mp_reply.reply_res2 = 0;
+               setreply(mp->mp_parent, mp->mp_pid);
+               rmp->mp_flags &= ~WAITING;
+       }
+
+       /* Disinherit children. */
+       for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
+               if (rmp->mp_flags & IN_USE && rmp->mp_parent == who) {
+                       rmp->mp_parent = INIT_PROC_NR;
+               }
+       }
+
+       /* Become like MM and FS. */
+       mp->mp_pid = mp->mp_procgrp = 0;
+       mp->mp_parent = 0;
+       return(OK); }
+
+#if ENABLE_SWAP
+  case MMSWAPON: {
+       struct mmswapon swapon;
+
+       if (mp->mp_effuid != SUPER_USER) return(EPERM);
+
+       if (sys_datacopy(who, (phys_bytes) ptr,
+               PM_PROC_NR, (phys_bytes) &swapon,
+               (phys_bytes) sizeof(swapon)) != OK) return(EFAULT);
+
+       return(swap_on(swapon.file, swapon.offset, swapon.size)); }
+
+  case MMSWAPOFF: {
+       if (mp->mp_effuid != SUPER_USER) return(EPERM);
+
+       return(swap_off()); }
+#endif /* SWAP */
+
+  default:
+       return(EINVAL);
+  }
+}
+
+/*==========================================================================*
+ *                             find_key                                            *
+ *==========================================================================*/
+PRIVATE char *find_key(params,name)
+const char *params;
+const char *name;
+{
+  register const char *namep;
+  register char *envp;
+
+  for (envp = (char *) params; *envp != 0;) {
+       for (namep = name; *namep != 0 && *namep == *envp; namep++, envp++)
+               ;
+       if (*namep == '\0' && *envp == '=') 
+               return(envp + 1);
+       while (*envp++ != 0)
+               ;
+  }
+  return(NULL);
+}
+
diff --git a/servers/pm/mm.h b/servers/pm/mm.h
new file mode 100644 (file)
index 0000000..9170251
--- /dev/null
@@ -0,0 +1,25 @@
+/* This is the master header for mm.  It includes some other files
+ * and defines the principal constants.
+ */
+#define _POSIX_SOURCE      1   /* tell headers to include POSIX stuff */
+#define _MINIX             1   /* tell headers to include MINIX stuff */
+#define _SYSTEM            1   /* tell headers that this is the kernel */
+
+/* The following are so basic, all the *.c files get them automatically. */
+#include <minix/config.h>      /* MUST be first */
+#include <ansi.h>              /* MUST be second */
+#include <sys/types.h>
+#include <minix/const.h>
+#include <minix/type.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <minix/syslib.h>
+
+#include <limits.h>
+#include <errno.h>
+
+#include "const.h"
+#include "type.h"
+#include "proto.h"
+#include "glo.h"
diff --git a/servers/pm/mproc.h b/servers/pm/mproc.h
new file mode 100644 (file)
index 0000000..2973cfe
--- /dev/null
@@ -0,0 +1,62 @@
+/* This table has one slot per process.  It contains all the memory management
+ * information for each process.  Among other things, it defines the text, data
+ * and stack segments, uids and gids, and various flags.  The kernel and file
+ * systems have tables that are also indexed by process, with the contents
+ * of corresponding slots referring to the same process in all three.
+ */
+
+EXTERN struct mproc {
+  struct mem_map mp_seg[NR_LOCAL_SEGS]; /* points to text, data, stack */
+  char mp_exitstatus;          /* storage for status when process exits */
+  char mp_sigstatus;           /* storage for signal # for killed procs */
+  pid_t mp_pid;                        /* process id */
+  pid_t mp_procgrp;            /* pid of process group (used for signals) */
+  pid_t mp_wpid;               /* pid this process is waiting for */
+  int mp_parent;               /* index of parent process */
+
+  /* Real and effective uids and gids. */
+  uid_t mp_realuid;            /* process' real uid */
+  uid_t mp_effuid;             /* process' effective uid */
+  gid_t mp_realgid;            /* process' real gid */
+  gid_t mp_effgid;             /* process' effective gid */
+
+  /* File identification for sharing. */
+  ino_t mp_ino;                        /* inode number of file */
+  dev_t mp_dev;                        /* device number of file system */
+  time_t mp_ctime;             /* inode changed time */
+
+  /* Signal handling information. */
+  sigset_t mp_ignore;          /* 1 means ignore the signal, 0 means don't */
+  sigset_t mp_catch;           /* 1 means catch the signal, 0 means don't */
+  sigset_t mp_sigmask;         /* signals to be blocked */
+  sigset_t mp_sigmask2;                /* saved copy of mp_sigmask */
+  sigset_t mp_sigpending;      /* signals being blocked */
+  struct sigaction mp_sigact[_NSIG + 1]; /* as in sigaction(2) */
+  vir_bytes mp_sigreturn;      /* address of C library __sigreturn function */
+
+  /* Backwards compatibility for signals. */
+  sighandler_t mp_func;                /* all sigs vectored to a single user fcn */
+
+  unsigned mp_flags;           /* flag bits */
+  vir_bytes mp_procargs;        /* ptr to proc's initial stack arguments */
+  struct mproc *mp_swapq;      /* queue of procs waiting to be swapped in */
+  message mp_reply;            /* reply message to be sent to one */
+
+  char mp_name[PROC_NAME_LEN]; /* process name */
+} mproc[NR_PROCS];
+
+/* Flag values */
+#define IN_USE          0x001  /* set when 'mproc' slot in use */
+#define WAITING         0x002  /* set by WAIT system call */
+#define ZOMBIE          0x004  /* set by EXIT, cleared by WAIT */
+#define PAUSED          0x008  /* set by PAUSE system call */
+#define ALARM_ON        0x010  /* set when SIGALRM timer started */
+#define SEPARATE       0x020   /* set if file is separate I & D space */
+#define        TRACED          0x040   /* set if process is to be traced */
+#define STOPPED                0x080   /* set if process stopped for tracing */
+#define SIGSUSPENDED   0x100   /* set by SIGSUSPEND system call */
+#define REPLY          0x200   /* set if a reply message is pending */
+#define ONSWAP         0x400   /* set if data segment is swapped out */
+#define SWAPIN         0x800   /* set if on the "swap this in" queue */
+
+#define NIL_MPROC ((struct mproc *) 0)
diff --git a/servers/pm/param.h b/servers/pm/param.h
new file mode 100644 (file)
index 0000000..e576481
--- /dev/null
@@ -0,0 +1,46 @@
+/* The following names are synonyms for the variables in the input message. */
+#define addr            m1_p1
+#define exec_name      m1_p1
+#define exec_len       m1_i1
+#define func           m6_f1
+#define grp_id         m1_i1
+#define namelen                m1_i1
+#define pid            m1_i1
+#define seconds                m1_i1
+#define sig            m6_i1
+#define stack_bytes    m1_i2
+#define stack_ptr      m1_p2
+#define status         m1_i1
+#define usr_id         m1_i1
+#define request                m2_i2
+#define taddr          m2_l1
+#define data           m2_l2
+#define sig_nr         m1_i2
+#define sig_nsa                m1_p1
+#define sig_osa                m1_p2
+#define sig_ret                m1_p3
+#define sig_set                m2_l1
+#define sig_how                m2_i1
+#define sig_flags      m2_i2
+#define sig_context    m2_p1
+#ifdef _SIGMESSAGE
+#define sig_msg                m1_i1
+#endif
+#define reboot_flag    m1_i1
+#define reboot_code    m1_p1
+#define reboot_size    m1_i2
+#define svrctl_req     m2_i1
+#define svrctl_argp    m2_p1
+
+/* The following names are synonyms for the variables in a reply message. */
+#define reply_res      m_type
+#define reply_res2     m2_i1
+#define reply_ptr      m2_p1
+#define reply_mask     m2_l1   
+#define reply_trace    m2_l2   
+
+/* The following names are used to inform the FS about certain events. */
+#define tell_fs_arg1    m1_i1
+#define tell_fs_arg2    m1_i2
+#define tell_fs_arg3    m1_i3
+
diff --git a/servers/pm/procutils.c b/servers/pm/procutils.c
new file mode 100644 (file)
index 0000000..d478b5f
--- /dev/null
@@ -0,0 +1,45 @@
+#include "mm.h"
+#include <minix/config.h>
+#include <timers.h>
+#include <string.h>
+#include "../../kernel/const.h"
+#include "../../kernel/type.h"
+#include "../../kernel/proc.h"
+
+/* The entry points into this file are:
+ *   p_getmap: get memory map of given process
+ *   p_getsp:  get stack pointer of given process      
+ */
+
+/*===========================================================================*
+ *                             p_getmap                                             *
+ *===========================================================================*/
+PUBLIC int p_getmap(proc_nr, mem_map)
+int proc_nr;                                   /* process to get map of */
+struct mem_map *mem_map;                       /* put memory map here */
+{
+  struct proc p;
+  int s;
+
+  if ((s=sys_getproc(&p, proc_nr)) != OK)
+       return(s);
+  memcpy(mem_map, p.p_memmap, sizeof(p.p_memmap));
+  return(OK);
+}
+
+/*===========================================================================*
+ *                             p_getsp                                      *
+ *===========================================================================*/
+PUBLIC int p_getsp(proc_nr, sp)
+int proc_nr;                                   /* process to get sp of */
+vir_bytes *sp;                                 /* put stack pointer here */
+{
+  struct proc p;
+  int s;
+
+  if ((s=sys_getproc(&p, proc_nr)) != OK)
+       return(s);
+  *sp = p.p_reg.sp;
+  return(OK);
+}
+
diff --git a/servers/pm/proto.h b/servers/pm/proto.h
new file mode 100644 (file)
index 0000000..9f755ac
--- /dev/null
@@ -0,0 +1,91 @@
+/* Function prototypes. */
+
+struct mproc;
+struct stat;
+struct mem_map;
+
+/* alloc.c */
+_PROTOTYPE( phys_clicks alloc_mem, (phys_clicks clicks)                        );
+_PROTOTYPE( void free_mem, (phys_clicks base, phys_clicks clicks)      );
+_PROTOTYPE( void mem_init, (phys_clicks *free) );
+#if ENABLE_SWAP
+_PROTOTYPE( int swap_on, (char *file, u32_t offset, u32_t size)        );
+_PROTOTYPE( int swap_off, (void)                                       );
+_PROTOTYPE( void swap_in, (void)                                       );
+_PROTOTYPE( void swap_inqueue, (struct mproc *rmp)                     );
+#else /* !SWAP */
+#define swap_in()                      ((void)0)
+#define swap_inqueue(rmp)              ((void)0)
+#endif /* !SWAP */
+
+/* break.c */
+_PROTOTYPE( int adjust, (struct mproc *rmp,
+                       vir_clicks data_clicks, vir_bytes sp)           );
+_PROTOTYPE( int do_brk, (void)                                         );
+_PROTOTYPE( int size_ok, (int file_type, vir_clicks tc, vir_clicks dc,
+                       vir_clicks sc, vir_clicks dvir, vir_clicks s_vir) );
+
+/* devio.c */
+_PROTOTYPE( int do_dev_io, (void) );
+_PROTOTYPE( int do_dev_io, (void) );
+
+/* exec.c */
+_PROTOTYPE( int do_exec, (void)                                                );
+_PROTOTYPE( void rw_seg, (int rw, int fd, int proc, int seg,
+                                               phys_bytes seg_bytes)   );
+_PROTOTYPE( struct mproc *find_share, (struct mproc *mp_ign, Ino_t ino,
+                       Dev_t dev, time_t ctime)                        );
+
+/* forkexit.c */
+_PROTOTYPE( int do_fork, (void)                                                );
+_PROTOTYPE( int do_mm_exit, (void)                                     );
+_PROTOTYPE( int do_waitpid, (void)                                     );
+_PROTOTYPE( void mm_exit, (struct mproc *rmp, int exit_status)         );
+
+/* getset.c */
+_PROTOTYPE( int do_getset, (void)                                      );
+
+/* main.c */
+_PROTOTYPE( void main, (void)                                          );
+
+/* misc.c */
+_PROTOTYPE( int do_reboot, (void)                                      );
+_PROTOTYPE( int do_getsysinfo, (void)                                  );
+_PROTOTYPE( int do_svrctl, (void)                                      );
+_PROTOTYPE( int do_mstats, (void)                                      );
+
+#if (MACHINE == MACINTOSH)
+_PROTOTYPE( phys_clicks start_click, (void)                            );
+#endif
+
+_PROTOTYPE( void setreply, (int proc_nr, int result)                   );
+
+/* signal.c */
+_PROTOTYPE( int do_alarm, (void)                                       );
+_PROTOTYPE( int do_kill, (void)                                                );
+_PROTOTYPE( int ksig_pending, (void)                                           );
+_PROTOTYPE( int do_ksig, (void)                                                );
+_PROTOTYPE( int do_pause, (void)                                       );
+_PROTOTYPE( int set_alarm, (int proc_nr, int sec)                      );
+_PROTOTYPE( int check_sig, (pid_t proc_id, int signo)                  );
+_PROTOTYPE( void sig_proc, (struct mproc *rmp, int sig_nr)             );
+_PROTOTYPE( int do_sigaction, (void)                                   );
+_PROTOTYPE( int do_sigpending, (void)                                  );
+_PROTOTYPE( int do_sigprocmask, (void)                                 );
+_PROTOTYPE( int do_sigreturn, (void)                                   );
+_PROTOTYPE( int do_sigsuspend, (void)                                  );
+_PROTOTYPE( void check_pending, (struct mproc *rmp)                    );
+
+/* trace.c */
+_PROTOTYPE( int do_trace, (void)                                       );
+_PROTOTYPE( void stop_proc, (struct mproc *rmp, int sig_nr)            );
+
+/* utility.c */
+_PROTOTYPE( int allowed, (char *name_buf, struct stat *s_buf, int mask)        );
+_PROTOTYPE( int no_sys, (void)                                         );
+_PROTOTYPE( void panic, (char *format, int num)                                );
+_PROTOTYPE( void tell_fs, (int what, int p1, int p2, int p3)           );
+
+/* procutils.c */
+_PROTOTYPE( int p_getsp, (int proc_nr, vir_bytes *sp)                  );
+_PROTOTYPE( int p_getmap, (int proc_nr, struct mem_map *mem_map)       );
diff --git a/servers/pm/signal.c b/servers/pm/signal.c
new file mode 100644 (file)
index 0000000..96e7b05
--- /dev/null
@@ -0,0 +1,646 @@
+/* This file handles signals, which are asynchronous events and are generally
+ * a messy and unpleasant business.  Signals can be generated by the KILL
+ * system call, or from the keyboard (SIGINT) or from the clock (SIGALRM).
+ * In all cases control eventually passes to check_sig() to see which processes
+ * can be signaled.  The actual signaling is done by sig_proc().
+ *
+ * The entry points into this file are:
+ *   do_sigaction:   perform the SIGACTION system call
+ *   do_sigpending:  perform the SIGPENDING system call
+ *   do_sigprocmask: perform the SIGPROCMASK system call
+ *   do_sigreturn:   perform the SIGRETURN system call
+ *   do_sigsuspend:  perform the SIGSUSPEND system call
+ *   do_kill:  perform the KILL system call
+ *   do_ksig:  accept a signal originating in the kernel (e.g., SIGINT)
+ *   do_alarm: perform the ALARM system call by calling set_alarm()
+ *   set_alarm:        tell the clock task to start or stop a timer
+ *   do_pause: perform the PAUSE system call
+ *   ksig_pending: the kernel notified about pending signals
+ *   sig_proc: interrupt or terminate a signaled process
+ *   check_sig: check which processes to signal with sig_proc()
+ *   check_pending:  check if a pending signal can now be delivered
+ */
+
+#include "mm.h"
+#include <minix/utils.h>
+#include <sys/stat.h>
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include <signal.h>
+#include <sys/sigcontext.h>
+#include <string.h>
+#include "mproc.h"
+#include "param.h"
+
+#define CORE_MODE      0777    /* mode to use on core image files */
+#define DUMPED          0200   /* bit set in status when core dumped */
+
+FORWARD _PROTOTYPE( void dump_core, (struct mproc *rmp)                        );
+FORWARD _PROTOTYPE( void unpause, (int pro)                            );
+FORWARD _PROTOTYPE( void handle_ksig, (int proc_nr, sigset_t sig_map)  );
+
+
+/*===========================================================================*
+ *                            do_sigaction                                  *
+ *===========================================================================*/
+PUBLIC int do_sigaction()
+{
+  int r;
+  struct sigaction svec;
+  struct sigaction *svp;
+
+  if (m_in.sig_nr == SIGKILL) return(OK);
+  if (m_in.sig_nr < 1 || m_in.sig_nr > _NSIG) return (EINVAL);
+  svp = &mp->mp_sigact[m_in.sig_nr];
+  if ((struct sigaction *) m_in.sig_osa != (struct sigaction *) NULL) {
+       r = sys_datacopy(PM_PROC_NR,(vir_bytes) svp,
+               who, (vir_bytes) m_in.sig_osa, (phys_bytes) sizeof(svec));
+       if (r != OK) return(r);
+  }
+
+  if ((struct sigaction *) m_in.sig_nsa == (struct sigaction *) NULL) 
+       return(OK);
+
+  /* Read in the sigaction structure. */
+  r = sys_datacopy(who, (vir_bytes) m_in.sig_nsa,
+               PM_PROC_NR, (vir_bytes) &svec, (phys_bytes) sizeof(svec));
+  if (r != OK) return(r);
+
+  if (svec.sa_handler == SIG_IGN) {
+       sigaddset(&mp->mp_ignore, m_in.sig_nr);
+       sigdelset(&mp->mp_sigpending, m_in.sig_nr);
+       sigdelset(&mp->mp_catch, m_in.sig_nr);
+  } else {
+       sigdelset(&mp->mp_ignore, m_in.sig_nr);
+       if (svec.sa_handler == SIG_DFL)
+               sigdelset(&mp->mp_catch, m_in.sig_nr);
+       else
+               sigaddset(&mp->mp_catch, m_in.sig_nr);
+  }
+  mp->mp_sigact[m_in.sig_nr].sa_handler = svec.sa_handler;
+  sigdelset(&svec.sa_mask, SIGKILL);
+  mp->mp_sigact[m_in.sig_nr].sa_mask = svec.sa_mask;
+  mp->mp_sigact[m_in.sig_nr].sa_flags = svec.sa_flags;
+  mp->mp_sigreturn = (vir_bytes) m_in.sig_ret;
+  return(OK);
+}
+
+/*===========================================================================*
+ *                            do_sigpending                                  *
+ *===========================================================================*/
+PUBLIC int do_sigpending()
+{
+  mp->mp_reply.reply_mask = (long) mp->mp_sigpending;
+  return OK;
+}
+
+/*===========================================================================*
+ *                            do_sigprocmask                                 *
+ *===========================================================================*/
+PUBLIC int do_sigprocmask()
+{
+/* Note that the library interface passes the actual mask in sigmask_set,
+ * not a pointer to the mask, in order to save a copy.  Similarly,
+ * the old mask is placed in the return message which the library
+ * interface copies (if requested) to the user specified address.
+ *
+ * The library interface must set SIG_INQUIRE if the 'act' argument
+ * is NULL.
+ */
+
+  int i;
+
+  mp->mp_reply.reply_mask = (long) mp->mp_sigmask;
+
+  switch (m_in.sig_how) {
+      case SIG_BLOCK:
+       sigdelset((sigset_t *)&m_in.sig_set, SIGKILL);
+       for (i = 1; i <= _NSIG; i++) {
+               if (sigismember((sigset_t *)&m_in.sig_set, i))
+                       sigaddset(&mp->mp_sigmask, i);
+       }
+       break;
+
+      case SIG_UNBLOCK:
+       for (i = 1; i <= _NSIG; i++) {
+               if (sigismember((sigset_t *)&m_in.sig_set, i))
+                       sigdelset(&mp->mp_sigmask, i);
+       }
+       check_pending(mp);
+       break;
+
+      case SIG_SETMASK:
+       sigdelset((sigset_t *) &m_in.sig_set, SIGKILL);
+       mp->mp_sigmask = (sigset_t) m_in.sig_set;
+       check_pending(mp);
+       break;
+
+      case SIG_INQUIRE:
+       break;
+
+      default:
+       return(EINVAL);
+       break;
+  }
+  return OK;
+}
+
+/*===========================================================================*
+ *                            do_sigsuspend                                  *
+ *===========================================================================*/
+PUBLIC int do_sigsuspend()
+{
+  mp->mp_sigmask2 = mp->mp_sigmask;    /* save the old mask */
+  mp->mp_sigmask = (sigset_t) m_in.sig_set;
+  sigdelset(&mp->mp_sigmask, SIGKILL);
+  mp->mp_flags |= SIGSUSPENDED;
+  check_pending(mp);
+  return(SUSPEND);
+}
+
+
+/*===========================================================================*
+ *                               do_sigreturn                               *
+ *===========================================================================*/
+PUBLIC int do_sigreturn()
+{
+/* A user signal handler is done.  Restore context and check for
+ * pending unblocked signals.
+ */
+
+  int r;
+
+  mp->mp_sigmask = (sigset_t) m_in.sig_set;
+  sigdelset(&mp->mp_sigmask, SIGKILL);
+
+  r = sys_sigreturn(who, (struct sigmsg *) m_in.sig_context, m_in.sig_flags);
+  check_pending(mp);
+  return(r);
+}
+
+/*===========================================================================*
+ *                             do_kill                                      *
+ *===========================================================================*/
+PUBLIC int do_kill()
+{
+/* Perform the kill(pid, signo) system call. */
+
+  return check_sig(m_in.pid, m_in.sig_nr);
+}
+
+/*===========================================================================*
+ *                             do_ksig_pending                              *
+ *===========================================================================*/
+PUBLIC int ksig_pending()
+{
+/* The kernel has notified the MM about pending signals. Request pending
+ * signals until all signals are handled. If there are no more signals,
+ * NONE is returned in the process number field.
+ */ 
+ int proc_nr;
+ sigset_t sig_map;
+
+ while (TRUE) {
+   sys_getsig(&proc_nr, &sig_map);     /* get an arbitrary pending signal */
+   if (NONE == proc_nr) {              /* stop if no more pending signals */
+       break;
+   } else {
+       handle_ksig(proc_nr, sig_map);  /* handle the receive signal */
+   }
+ } 
+ return(SUSPEND);                      /* prevents sending reply */
+}
+
+/*===========================================================================*
+ *                             do_ksig                                      *
+ *===========================================================================*/
+PUBLIC int do_ksig()
+{
+/* Certain signals, such as segmentation violations and DEL, originate in the
+ * kernel.  When the kernel detects such signals, it sets bits in a bit map.
+ * As soon as MM is awaiting new work, the kernel sends MM a message containing
+ * the process slot and bit map.  That message comes here.  The File System
+ * also uses this mechanism to signal writing on broken pipes (SIGPIPE).
+ */
+  int proc_nr;
+  sigset_t sig_map;
+
+  /* Only kernel may make this call. */
+  if (who != HARDWARE) return(EPERM);
+  proc_nr = m_in.SIG_PROC;
+  sig_map = (sigset_t) m_in.SIG_MAP;
+  handle_ksig(proc_nr, sig_map);
+  return(SUSPEND);
+}
+
+/*===========================================================================*
+ *                             handle_ksig                                          *
+ *===========================================================================*/
+PRIVATE void handle_ksig(proc_nr, sig_map)
+int proc_nr;
+sigset_t sig_map;
+{
+  register struct mproc *rmp;
+  int i;
+  pid_t proc_id, id;
+
+  rmp = &mproc[proc_nr];
+  if ((rmp->mp_flags & (IN_USE | ZOMBIE)) != IN_USE) return;
+  proc_id = rmp->mp_pid;
+  mp = &mproc[0];              /* pretend kernel signals are from MM */
+  mp->mp_procgrp = rmp->mp_procgrp;    /* get process group right */
+
+  /* Check each bit in turn to see if a signal is to be sent.  Unlike
+   * kill(), the kernel may collect several unrelated signals for a
+   * process and pass them to MM in one blow.  Thus loop on the bit
+   * map. For SIGINT and SIGQUIT, use proc_id 0 to indicate a broadcast
+   * to the recipient's process group.  For SIGKILL, use proc_id -1 to
+   * indicate a systemwide broadcast.
+   */
+  for (i = 1; i <= _NSIG; i++) {
+       if (!sigismember(&sig_map, i)) continue;
+       switch (i) {
+           case SIGINT:
+           case SIGQUIT:
+               id = 0; break;  /* broadcast to process group */
+           case SIGKILL:
+               id = -1; break; /* broadcast to all except INIT */
+           case SIGALRM:
+               /* Disregard SIGALRM when the target process has not
+                * requested an alarm.  This only applies for a KERNEL
+                * generated signal.
+                */
+               if ((rmp->mp_flags & ALARM_ON) == 0) continue;
+               rmp->mp_flags &= ~ALARM_ON;
+               /* fall through */
+           default:
+               id = proc_id;
+               break;
+       }
+       check_sig(id, i);
+       sys_endsig(proc_nr);    /* tell kernel it's done */
+  }
+}
+
+
+/*===========================================================================*
+ *                             do_alarm                                     *
+ *===========================================================================*/
+PUBLIC int do_alarm()
+{
+/* Perform the alarm(seconds) system call. */
+  return(set_alarm(who, m_in.seconds));
+}
+
+
+/*===========================================================================*
+ *                             set_alarm                                    *
+ *===========================================================================*/
+PUBLIC int set_alarm(proc_nr, sec)
+int proc_nr;                   /* process that wants the alarm */
+int sec;                       /* how many seconds delay before the signal */
+{
+/* This routine is used by do_alarm() to set the alarm timer.  It is also used
+ * to turn the timer off when a process exits with the timer still on.
+ */
+  clock_t ticks;       /* number of ticks for alarm */
+  int remaining;       /* previous time left in seconds */
+  int s;
+
+  if (sec != 0) mproc[proc_nr].mp_flags |=  ALARM_ON;
+  else                 mproc[proc_nr].mp_flags &= ~ALARM_ON;
+
+  /* Tell the clock task to provide a signal message when the time comes.
+   *
+   * Large delays cause a lot of problems.  First, the alarm system call
+   * takes an unsigned seconds count and the library has cast it to an int.
+   * That probably works, but on return the library will convert "negative"
+   * unsigneds to errors.  Presumably no one checks for these errors, so
+   * force this call through.  Second, If unsigned and long have the same
+   * size, converting from seconds to ticks can easily overflow.  Finally,
+   * the kernel has similar overflow bugs adding ticks.
+   *
+   * Fixing this requires a lot of ugly casts to fit the wrong interface
+   * types and to avoid overflow traps.  ALRM_EXP_TIME has the right type
+   * (clock_t) although it is declared as long.  How can variables like
+   * this be declared properly without combinatorial explosion of message
+   * types?
+   */
+  ticks = (clock_t) (HZ * (unsigned long) (unsigned) sec);
+  if ( (unsigned long) ticks / HZ != (unsigned) sec)
+       ticks = LONG_MAX;       /* eternity (really TMR_NEVER) */
+
+  if ((s=sys_signalrm(proc_nr, &ticks)) != OK) 
+       panic("MM couldn't set signal alarm", s);
+
+  remaining = (int) ((ticks + (HZ-1))/HZ);
+  if (remaining < 0) remaining = INT_MAX;      /* true value is too large */
+  return(remaining);
+}
+
+
+/*===========================================================================*
+ *                             do_pause                                     *
+ *===========================================================================*/
+PUBLIC int do_pause()
+{
+/* Perform the pause() system call. */
+
+  mp->mp_flags |= PAUSED;
+  return(SUSPEND);
+}
+
+
+/*===========================================================================*
+ *                             sig_proc                                     *
+ *===========================================================================*/
+PUBLIC void sig_proc(rmp, signo)
+register struct mproc *rmp;    /* pointer to the process to be signaled */
+int signo;                     /* signal to send to process (1 to _NSIG) */
+{
+/* Send a signal to a process.  Check to see if the signal is to be caught,
+ * ignored, or blocked.  If the signal is to be caught, coordinate with
+ * KERNEL to push a sigcontext structure and a sigframe structure onto
+ * the catcher's stack.  Also, KERNEL will reset the program counter and
+ * stack pointer, so that when the process next runs, it will be executing
+ * the signal handler.  When the signal handler returns,  sigreturn(2)
+ * will be called.  Then KERNEL will restore the signal context from the
+ * sigcontext structure.
+ *
+ * If there is insufficient stack space, kill the process.
+ */
+
+  vir_bytes new_sp;
+  int s;
+  int slot;
+  int sigflags;
+  struct sigmsg sm;
+
+  slot = (int) (rmp - mproc);
+  if ((rmp->mp_flags & (IN_USE | ZOMBIE)) != IN_USE) {
+       printf("MM: signal %d sent to %s process %d\n",
+               (rmp->mp_flags & ZOMBIE) ? "zombie" : "dead", signo, slot);
+       panic("", NO_NUM);
+  }
+  if ((rmp->mp_flags & TRACED) && signo != SIGKILL) {
+       /* A traced process has special handling. */
+       unpause(slot);
+       stop_proc(rmp, signo);  /* a signal causes it to stop */
+       return;
+  }
+  /* Some signals are ignored by default. */
+  if (sigismember(&rmp->mp_ignore, signo)) return; 
+
+  if (sigismember(&rmp->mp_sigmask, signo)) {
+       /* Signal should be blocked. */
+       sigaddset(&rmp->mp_sigpending, signo);
+       return;
+  }
+  sigflags = rmp->mp_sigact[signo].sa_flags;
+  if (sigismember(&rmp->mp_catch, signo)) {
+       if (rmp->mp_flags & ONSWAP) {
+               /* Process is swapped out, leave signal pending. */
+               sigaddset(&rmp->mp_sigpending, signo);
+               swap_inqueue(rmp);
+               return;
+       }
+       if (rmp->mp_flags & SIGSUSPENDED)
+               sm.sm_mask = rmp->mp_sigmask2;
+       else
+               sm.sm_mask = rmp->mp_sigmask;
+       sm.sm_signo = signo;
+       sm.sm_sighandler = (vir_bytes) rmp->mp_sigact[signo].sa_handler;
+       sm.sm_sigreturn = rmp->mp_sigreturn;
+       if ((s=p_getsp(slot, &new_sp)) != OK)
+               panic("MM couldn't get new stack pointer",s);
+       sm.sm_stkptr = new_sp;
+
+       /* Make room for the sigcontext and sigframe struct. */
+       new_sp -= sizeof(struct sigcontext)
+                                + 3 * sizeof(char *) + 2 * sizeof(int);
+
+       if (adjust(rmp, rmp->mp_seg[D].mem_len, new_sp) != OK)
+               goto doterminate;
+
+       rmp->mp_sigmask |= rmp->mp_sigact[signo].sa_mask;
+       if (sigflags & SA_NODEFER)
+               sigdelset(&rmp->mp_sigmask, signo);
+       else
+               sigaddset(&rmp->mp_sigmask, signo);
+
+       if (sigflags & SA_RESETHAND) {
+               sigdelset(&rmp->mp_catch, signo);
+               rmp->mp_sigact[signo].sa_handler = SIG_DFL;
+       }
+
+       sys_sigsend(slot, &sm);
+       sigdelset(&rmp->mp_sigpending, signo);
+       /* If process is hanging on PAUSE, WAIT, SIGSUSPEND, tty, pipe, etc.,
+        * release it.
+        */
+       unpause(slot);
+       return;
+  }
+doterminate:
+  /* Signal should not or cannot be caught.  Take default action. */
+  if (sigismember(&ign_sset, signo)) return;
+
+  rmp->mp_sigstatus = (char) signo;
+  if (sigismember(&core_sset, signo)) {
+       if (rmp->mp_flags & ONSWAP) {
+               /* Process is swapped out, leave signal pending. */
+               sigaddset(&rmp->mp_sigpending, signo);
+               swap_inqueue(rmp);
+               return;
+       }
+       /* Switch to the user's FS environment and dump core. */
+       tell_fs(CHDIR, slot, FALSE, 0);
+       dump_core(rmp);
+  }
+  mm_exit(rmp, 0);             /* terminate process */
+}
+
+
+/*===========================================================================*
+ *                             check_sig                                    *
+ *===========================================================================*/
+PUBLIC int check_sig(proc_id, signo)
+pid_t proc_id;                 /* pid of proc to sig, or 0 or -1, or -pgrp */
+int signo;                     /* signal to send to process (0 to _NSIG) */
+{
+/* Check to see if it is possible to send a signal.  The signal may have to be
+ * sent to a group of processes.  This routine is invoked by the KILL system
+ * call, and also when the kernel catches a DEL or other signal.
+ */
+
+  register struct mproc *rmp;
+  int count;                   /* count # of signals sent */
+  int error_code;
+
+  if (signo < 0 || signo > _NSIG) return(EINVAL);
+
+  /* Return EINVAL for attempts to send SIGKILL to INIT alone. */
+  if (proc_id == INIT_PID && signo == SIGKILL) return(EINVAL);
+
+  /* Search the proc table for processes to signal.  (See forkexit.c about
+   * pid magic.)
+   */
+  count = 0;
+  error_code = ESRCH;
+  for (rmp = &mproc[INIT_PROC_NR]; rmp < &mproc[NR_PROCS]; rmp++) {
+       if (!(rmp->mp_flags & IN_USE)) continue;
+       if ((rmp->mp_flags & ZOMBIE) && signo != 0) continue;
+
+       /* Check for selection. */
+       if (proc_id > 0 && proc_id != rmp->mp_pid) continue;
+       if (proc_id == 0 && mp->mp_procgrp != rmp->mp_procgrp) continue;
+       if (proc_id == -1 && rmp->mp_pid <= INIT_PID) continue;
+       if (proc_id < -1 && rmp->mp_procgrp != -proc_id) continue;
+
+       /* Check for permission. */
+       if (mp->mp_effuid != SUPER_USER
+           && mp->mp_realuid != rmp->mp_realuid
+           && mp->mp_effuid != rmp->mp_realuid
+           && mp->mp_realuid != rmp->mp_effuid
+           && mp->mp_effuid != rmp->mp_effuid) {
+               error_code = EPERM;
+               continue;
+       }
+
+       count++;
+       if (signo == 0) continue;
+
+       /* 'sig_proc' will handle the disposition of the signal.  The
+        * signal may be caught, blocked, ignored, or cause process
+        * termination, possibly with core dump.
+        */
+       sig_proc(rmp, signo);
+
+       if (proc_id > 0) break; /* only one process being signaled */
+  }
+
+  /* If the calling process has killed itself, don't reply. */
+  if ((mp->mp_flags & (IN_USE | ZOMBIE)) != IN_USE) return(SUSPEND);
+  return(count > 0 ? OK : error_code);
+}
+
+
+/*===========================================================================*
+ *                               check_pending                              *
+ *===========================================================================*/
+PUBLIC void check_pending(rmp)
+register struct mproc *rmp;
+{
+  /* Check to see if any pending signals have been unblocked.  The
+   * first such signal found is delivered.
+   *
+   * If multiple pending unmasked signals are found, they will be
+   * delivered sequentially.
+   *
+   * There are several places in this file where the signal mask is
+   * changed.  At each such place, check_pending() should be called to
+   * check for newly unblocked signals.
+   */
+
+  int i;
+
+  for (i = 1; i <= _NSIG; i++) {
+       if (sigismember(&rmp->mp_sigpending, i) &&
+               !sigismember(&rmp->mp_sigmask, i)) {
+               sigdelset(&rmp->mp_sigpending, i);
+               sig_proc(rmp, i);
+               break;
+       }
+  }
+}
+
+
+/*===========================================================================*
+ *                             unpause                                      *
+ *===========================================================================*/
+PRIVATE void unpause(pro)
+int pro;                       /* which process number */
+{
+/* A signal is to be sent to a process.  If that process is hanging on a
+ * system call, the system call must be terminated with EINTR.  Possible
+ * calls are PAUSE, WAIT, READ and WRITE, the latter two for pipes and ttys.
+ * First check if the process is hanging on an MM call.  If not, tell FS,
+ * so it can check for READs and WRITEs from pipes, ttys and the like.
+ */
+
+  register struct mproc *rmp;
+
+  rmp = &mproc[pro];
+
+  /* Check to see if process is hanging on a PAUSE, WAIT or SIGSUSPEND call. */
+  if (rmp->mp_flags & (PAUSED | WAITING | SIGSUSPENDED)) {
+       rmp->mp_flags &= ~(PAUSED | WAITING | SIGSUSPENDED);
+       setreply(pro, EINTR);
+       return;
+  }
+
+  /* Process is not hanging on an MM call.  Ask FS to take a look. */
+  tell_fs(UNPAUSE, pro, 0, 0);
+}
+
+
+/*===========================================================================*
+ *                             dump_core                                    *
+ *===========================================================================*/
+PRIVATE void dump_core(rmp)
+register struct mproc *rmp;    /* whose core is to be dumped */
+{
+/* Make a core dump on the file "core", if possible. */
+
+  int s, fd, fake_fd, nr_written, seg, slot;
+  char *buf;
+  vir_bytes current_sp;
+  phys_bytes left;             /* careful; 64K might overflow vir_bytes */
+  unsigned nr_to_write;                /* unsigned for arg to write() but < INT_MAX */
+  long trace_data, trace_off;
+
+  slot = (int) (rmp - mproc);
+
+  /* Can core file be written?  We are operating in the user's FS environment,
+   * so no special permission checks are needed.
+   */
+  if (rmp->mp_realuid != rmp->mp_effuid) return;
+  if ( (fd = open(core_name, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK,
+                                               CORE_MODE)) < 0) return;
+  rmp->mp_sigstatus |= DUMPED;
+
+  /* Make sure the stack segment is up to date.
+   * We don't want adjust() to fail unless current_sp is preposterous,
+   * but it might fail due to safety checking.  Also, we don't really want 
+   * the adjust() for sending a signal to fail due to safety checking.  
+   * Maybe make SAFETY_BYTES a parameter.
+   */
+  if ((s=p_getsp(slot, &current_sp)) != OK)
+       panic("MM couldn't get new stack pointer",s);
+  adjust(rmp, rmp->mp_seg[D].mem_len, current_sp);
+
+  /* Write the memory map of all segments to begin the core file. */
+  if (write(fd, (char *) rmp->mp_seg, (unsigned) sizeof rmp->mp_seg)
+      != (unsigned) sizeof rmp->mp_seg) {
+       close(fd);
+       return;
+  }
+
+  /* Write out the whole kernel process table entry to get the regs. */
+  trace_off = 0;
+  while (sys_trace(3, slot, trace_off, &trace_data) == OK) {
+       if (write(fd, (char *) &trace_data, (unsigned) sizeof (long))
+           != (unsigned) sizeof (long)) {
+               close(fd);
+               return;
+       }
+       trace_off += sizeof (long);
+  }
+
+  /* Loop through segments and write the segments themselves out. */
+  for (seg = 0; seg < NR_LOCAL_SEGS; seg++) {
+       rw_seg(1, fd, slot, seg,
+               (phys_bytes) rmp->mp_seg[seg].mem_len << CLICK_SHIFT);
+  }
+  close(fd);
+}
diff --git a/servers/pm/table.c b/servers/pm/table.c
new file mode 100644 (file)
index 0000000..1962678
--- /dev/null
@@ -0,0 +1,108 @@
+/* This file contains the table used to map system call numbers onto the
+ * routines that perform them.
+ */
+
+#define _TABLE
+
+#include "mm.h"
+#include <minix/callnr.h>
+#include <signal.h>
+#include "mproc.h"
+#include "param.h"
+
+/* Miscellaneous */
+char core_name[] = "core";     /* file name where core images are produced */
+
+_PROTOTYPE (int (*call_vec[NCALLS]), (void) ) = {
+       no_sys,         /*  0 = unused  */
+       do_mm_exit,     /*  1 = exit    */
+       do_fork,        /*  2 = fork    */
+       no_sys,         /*  3 = read    */
+       no_sys,         /*  4 = write   */
+       no_sys,         /*  5 = open    */
+       no_sys,         /*  6 = close   */
+       do_waitpid,     /*  7 = wait    */
+       no_sys,         /*  8 = creat   */
+       no_sys,         /*  9 = link    */
+       no_sys,         /* 10 = unlink  */
+       do_waitpid,     /* 11 = waitpid */
+       no_sys,         /* 12 = chdir   */
+       no_sys,         /* 13 = time    */
+       no_sys,         /* 14 = mknod   */
+       no_sys,         /* 15 = chmod   */
+       no_sys,         /* 16 = chown   */
+       do_brk,         /* 17 = break   */
+       no_sys,         /* 18 = stat    */
+       no_sys,         /* 19 = lseek   */
+       do_getset,      /* 20 = getpid  */
+       no_sys,         /* 21 = mount   */
+       no_sys,         /* 22 = umount  */
+       do_getset,      /* 23 = setuid  */
+       do_getset,      /* 24 = getuid  */
+       no_sys,         /* 25 = stime   */
+       do_trace,       /* 26 = ptrace  */
+       do_alarm,       /* 27 = alarm   */
+       no_sys,         /* 28 = fstat   */
+       do_pause,       /* 29 = pause   */
+       no_sys,         /* 30 = utime   */
+       no_sys,         /* 31 = (stty)  */
+       no_sys,         /* 32 = (gtty)  */
+       no_sys,         /* 33 = access  */
+       no_sys,         /* 34 = (nice)  */
+       no_sys,         /* 35 = (ftime) */
+       no_sys,         /* 36 = sync    */
+       do_kill,        /* 37 = kill    */
+       no_sys,         /* 38 = rename  */
+       no_sys,         /* 39 = mkdir   */
+       no_sys,         /* 40 = rmdir   */
+       no_sys,         /* 41 = dup     */
+       no_sys,         /* 42 = pipe    */
+       no_sys,         /* 43 = times   */
+       no_sys,         /* 44 = (prof)  */
+       no_sys,         /* 45 = unused  */
+       do_getset,      /* 46 = setgid  */
+       do_getset,      /* 47 = getgid  */
+       no_sys,         /* 48 = (signal)*/
+       no_sys,         /* 49 = unused  */
+       no_sys,         /* 50 = unused  */
+       no_sys,         /* 51 = (acct)  */
+       no_sys,         /* 52 = (phys)  */
+       no_sys,         /* 53 = (lock)  */
+       no_sys,         /* 54 = ioctl   */
+       no_sys,         /* 55 = fcntl   */
+       no_sys,         /* 56 = (mpx)   */
+       no_sys,         /* 57 = unused  */
+       no_sys,         /* 58 = unused  */
+       do_exec,        /* 59 = execve  */
+       no_sys,         /* 60 = umask   */
+       no_sys,         /* 61 = chroot  */
+       do_getset,      /* 62 = setsid  */
+       do_getset,      /* 63 = getpgrp */
+
+       do_ksig,        /* 64 = KSIG: signals originating in the kernel */
+       no_sys,         /* 65 = UNPAUSE */
+       no_sys,         /* 66 = unused  */
+       no_sys,         /* 67 = REVIVE  */
+       no_sys,         /* 68 = TASK_REPLY  */
+       no_sys,         /* 69 = unused  */
+       no_sys,         /* 70 = unused  */
+       do_sigaction,   /* 71 = sigaction   */
+       do_sigsuspend,  /* 72 = sigsuspend  */
+       do_sigpending,  /* 73 = sigpending  */
+       do_sigprocmask, /* 74 = sigprocmask */
+       do_sigreturn,   /* 75 = sigreturn   */
+       do_reboot,      /* 76 = reboot  */
+       do_svrctl,      /* 77 = svrctl  */
+
+       no_sys,         /* 78 = cmostime */
+       do_getsysinfo,  /* 79 = getsysinfo */
+#if ENABLE_MESSAGE_STATS
+       do_mstats,      /* 80 = mstats */
+#else
+       no_sys,
+#endif
+       no_sys,         /* 81 = unused */
+       no_sys,         /* 82 = unused */
+};
+/* This should not fail with "array size is negative": */
+extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1];
diff --git a/servers/pm/trace.c b/servers/pm/trace.c
new file mode 100644 (file)
index 0000000..731324e
--- /dev/null
@@ -0,0 +1,111 @@
+/* This file handles the memory manager's part of debugging, using the 
+ * ptrace system call. Most of the commands are passed on to the system
+ * task for completion.
+ *
+ * The debugging commands available are:
+ * T_STOP      stop the process 
+ * T_OK                enable tracing by parent for this process
+ * T_GETINS    return value from instruction space 
+ * T_GETDATA   return value from data space 
+ * T_GETUSER   return value from user process table
+ * T_SETINS    set value in instruction space
+ * T_SETDATA   set value in data space
+ * T_SETUSER   set value in user process table 
+ * T_RESUME    resume execution 
+ * T_EXIT      exit
+ * T_STEP      set trace bit 
+ * 
+ * The T_OK and T_EXIT commands are handled here, and the T_RESUME and
+ * T_STEP commands are partially handled here and completed by the system
+ * task. The rest are handled entirely by the system task. 
+ */
+
+#include "mm.h"
+#include <minix/com.h>
+#include <sys/ptrace.h>
+#include <signal.h>
+#include "mproc.h"
+#include "param.h"
+
+#define NIL_MPROC      ((struct mproc *) 0)
+
+FORWARD _PROTOTYPE( struct mproc *findproc, (pid_t lpid) );
+
+/*===========================================================================*
+ *                             do_trace                                     *
+ *===========================================================================*/
+PUBLIC int do_trace()
+{
+  register struct mproc *child;
+
+  /* the T_OK call is made by the child fork of the debugger before it execs  
+   * the process to be traced
+   */
+  if (m_in.request == T_OK) {  /* enable tracing by parent for this proc */
+       mp->mp_flags |= TRACED;
+       mp->mp_reply.reply_trace = 0;
+       return(OK);
+  }
+  if ((child=findproc(m_in.pid))==NIL_MPROC || !(child->mp_flags & STOPPED)) {
+       return(ESRCH);
+  }
+  /* all the other calls are made by the parent fork of the debugger to 
+   * control execution of the child
+   */
+  switch (m_in.request) {
+  case T_EXIT:         /* exit */
+       mm_exit(child, (int) m_in.data);
+       mp->mp_reply.reply_trace = 0;
+       return(OK);
+  case T_RESUME: 
+  case T_STEP:                 /* resume execution */
+       if (m_in.data < 0 || m_in.data > _NSIG) return(EIO);
+       if (m_in.data > 0) {            /* issue signal */
+               child->mp_flags &= ~TRACED;  /* so signal is not diverted */
+               sig_proc(child, (int) m_in.data);
+               child->mp_flags |= TRACED;
+       }
+       child->mp_flags &= ~STOPPED;
+       break;
+  }
+  if (sys_trace(m_in.request,(int)(child-mproc),m_in.taddr,&m_in.data) != OK)
+       return(-errno);
+  mp->mp_reply.reply_trace = m_in.data;
+  return(OK);
+}
+
+/*===========================================================================*
+ *                             findproc                                     *
+ *===========================================================================*/
+PRIVATE struct mproc *findproc(lpid)
+pid_t lpid;
+{
+  register struct mproc *rmp;
+
+  for (rmp = &mproc[INIT_PROC_NR + 1]; rmp < &mproc[NR_PROCS]; rmp++)
+       if (rmp->mp_flags & IN_USE && rmp->mp_pid == lpid) return(rmp);
+  return(NIL_MPROC);
+}
+
+/*===========================================================================*
+ *                             stop_proc                                    *
+ *===========================================================================*/
+PUBLIC void stop_proc(rmp, signo)
+register struct mproc *rmp;
+int signo;
+{
+/* A traced process got a signal so stop it. */
+
+  register struct mproc *rpmp = mproc + rmp->mp_parent;
+
+  if (sys_trace(-1, (int) (rmp - mproc), 0L, (long *) 0) != OK) return;
+  rmp->mp_flags |= STOPPED;
+  if (rpmp->mp_flags & WAITING) {
+       rpmp->mp_flags &= ~WAITING;     /* parent is no longer waiting */
+       rpmp->mp_reply.reply_res2 = 0177 | (signo << 8);
+       setreply(rmp->mp_parent, rmp->mp_pid);
+  } else {
+       rmp->mp_sigstatus = signo;
+  }
+  return;
+}
diff --git a/servers/pm/type.h b/servers/pm/type.h
new file mode 100644 (file)
index 0000000..65d9c84
--- /dev/null
@@ -0,0 +1,5 @@
+/* If there were any type definitions local to the Memory Manager, they would
+ * be here.  This file is included only for symmetry with the kernel and File
+ * System, which do have some local type definitions.
+ */
+
diff --git a/servers/pm/utility.c b/servers/pm/utility.c
new file mode 100644 (file)
index 0000000..e82f4f9
--- /dev/null
@@ -0,0 +1,114 @@
+/* This file contains some utility routines for MM.
+ *
+ * The entry points are:
+ *   allowed:  see if an access is permitted
+ *   no_sys:   this routine is called for invalid system call numbers
+ *   panic:    MM has run aground of a fatal error and cannot continue
+ *   tell_fs:  interface to FS
+ */
+
+#include "mm.h"
+#include <sys/stat.h>
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include <fcntl.h>
+#include <signal.h>            /* needed only because mproc.h needs it */
+#include "mproc.h"
+#include "param.h"
+
+/*===========================================================================*
+ *                             allowed                                      *
+ *===========================================================================*/
+PUBLIC int allowed(name_buf, s_buf, mask)
+char *name_buf;                        /* pointer to file name to be EXECed */
+struct stat *s_buf;            /* buffer for doing and returning stat struct*/
+int mask;                      /* R_BIT, W_BIT, or X_BIT */
+{
+/* Check to see if file can be accessed.  Return EACCES or ENOENT if the access
+ * is prohibited.  If it is legal open the file and return a file descriptor.
+ */
+
+  int fd;
+  int save_errno;
+
+  /* Use the fact that mask for access() is the same as the permissions mask.
+   * E.g., X_BIT in <minix/const.h> is the same as X_OK in <unistd.h> and
+   * S_IXOTH in <sys/stat.h>.  tell_fs(DO_CHDIR, ...) has set MM's real ids
+   * to the user's effective ids, so access() works right for setuid programs.
+   */
+  if (access(name_buf, mask) < 0) return(-errno);
+
+  /* The file is accessible but might not be readable.  Make it readable. */
+  tell_fs(SETUID, PM_PROC_NR, (int) SUPER_USER, (int) SUPER_USER);
+
+  /* Open the file and fstat it.  Restore the ids early to handle errors. */
+  fd = open(name_buf, O_RDONLY | O_NONBLOCK);
+  save_errno = errno;          /* open might fail, e.g. from ENFILE */
+  tell_fs(SETUID, PM_PROC_NR, (int) mp->mp_effuid, (int) mp->mp_effuid);
+  if (fd < 0) return(-save_errno);
+  if (fstat(fd, s_buf) < 0) panic("allowed: fstat failed", NO_NUM);
+
+  /* Only regular files can be executed. */
+  if (mask == X_BIT && (s_buf->st_mode & I_TYPE) != I_REGULAR) {
+       close(fd);
+       return(EACCES);
+  }
+  return(fd);
+}
+
+
+/*===========================================================================*
+ *                             no_sys                                       *
+ *===========================================================================*/
+PUBLIC int no_sys()
+{
+/* A system call number not implemented by MM has been requested. */
+
+  return(EINVAL);
+}
+
+
+/*===========================================================================*
+ *                             panic                                        *
+ *===========================================================================*/
+PUBLIC void panic(format, num)
+char *format;                  /* format string */
+int num;                       /* number to go with format string */
+{
+/* Something awful has happened.  Panics are caused when an internal
+ * inconsistency is detected, e.g., a programming error or illegal value of a
+ * defined constant.
+ */
+
+  printf("Memory manager panic: %s ", format);
+  if (num != NO_NUM) printf("%d",num);
+  printf("\n");
+  tell_fs(SYNC, 0, 0, 0);      /* flush the cache to the disk */
+  sys_abort(RBT_PANIC);
+}
+
+
+/*===========================================================================*
+ *                             tell_fs                                      *
+ *===========================================================================*/
+PUBLIC void tell_fs(what, p1, p2, p3)
+int what, p1, p2, p3;
+{
+/* This routine is only used by MM to inform FS of certain events:
+ *      tell_fs(CHDIR, slot, dir, 0)
+ *      tell_fs(EXEC, proc, 0, 0)
+ *      tell_fs(EXIT, proc, 0, 0)
+ *      tell_fs(FORK, parent, child, pid)
+ *      tell_fs(SETGID, proc, realgid, effgid)
+ *      tell_fs(SETSID, proc, 0, 0)
+ *      tell_fs(SETUID, proc, realuid, effuid)
+ *      tell_fs(SYNC, 0, 0, 0)
+ *      tell_fs(UNPAUSE, proc, signr, 0)
+ */
+  message m;
+
+  m.tell_fs_arg1 = p1;
+  m.tell_fs_arg2 = p2;
+  m.tell_fs_arg3 = p3;
+  _taskcall(FS_PROC_NR, what, &m);
+}