Rename mm (memory manager) to pm (process manager), involved renaming
dir..
This commit is contained in:
parent
83ef55e301
commit
0e9261ed5a
21 changed files with 3402 additions and 1 deletions
|
@ -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) $@
|
||||
|
|
134
servers/pm/Makefile
Normal file
134
servers/pm/Makefile
Normal file
|
@ -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
|
428
servers/pm/alloc.c
Normal file
428
servers/pm/alloc.c
Normal file
|
@ -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 */
|
170
servers/pm/break.c
Normal file
170
servers/pm/break.c
Normal file
|
@ -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);
|
||||
}
|
14
servers/pm/const.h
Normal file
14
servers/pm/const.h
Normal file
|
@ -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 */
|
598
servers/pm/exec.c
Normal file
598
servers/pm/exec.c
Normal file
|
@ -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);
|
||||
}
|
288
servers/pm/forkexit.c
Normal file
288
servers/pm/forkexit.c
Normal file
|
@ -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--;
|
||||
}
|
78
servers/pm/getset.c
Normal file
78
servers/pm/getset.c
Normal file
|
@ -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);
|
||||
}
|
20
servers/pm/glo.h
Normal file
20
servers/pm/glo.h
Normal file
|
@ -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 */
|
||||
|
197
servers/pm/main.c
Normal file
197
servers/pm/main.c
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
221
servers/pm/misc.c
Normal file
221
servers/pm/misc.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
25
servers/pm/mm.h
Normal file
25
servers/pm/mm.h
Normal file
|
@ -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"
|
62
servers/pm/mproc.h
Normal file
62
servers/pm/mproc.h
Normal file
|
@ -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)
|
46
servers/pm/param.h
Normal file
46
servers/pm/param.h
Normal file
|
@ -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
|
||||
|
45
servers/pm/procutils.c
Normal file
45
servers/pm/procutils.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
91
servers/pm/proto.h
Normal file
91
servers/pm/proto.h
Normal file
|
@ -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) );
|
646
servers/pm/signal.c
Normal file
646
servers/pm/signal.c
Normal file
|
@ -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, ¤t_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);
|
||||
}
|
108
servers/pm/table.c
Normal file
108
servers/pm/table.c
Normal file
|
@ -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];
|
111
servers/pm/trace.c
Normal file
111
servers/pm/trace.c
Normal file
|
@ -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;
|
||||
}
|
5
servers/pm/type.h
Normal file
5
servers/pm/type.h
Normal file
|
@ -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.
|
||||
*/
|
||||
|
114
servers/pm/utility.c
Normal file
114
servers/pm/utility.c
Normal file
|
@ -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);
|
||||
}
|
Loading…
Reference in a new issue