Minix1.1/usr/src/mm/signal.c
/* 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 signalled. The actual signalling is done by sig_proc().
*
* The entry points into this file are:
* do_signal: perform the SIGNAL system call
* do_kill: perform the KILL system call
* do_ksig: accept a signal originating in the kernel (e.g., SIGINT)
* sig_proc: interrupt or terminate a signalled process
* 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
* unpause: check to see if process is suspended on anything
*/
#include "../h/const.h"
#include "../h/type.h"
#include "../h/callnr.h"
#include "../h/com.h"
#include "../h/error.h"
#include "../h/signal.h"
#include "../h/stat.h"
#include "const.h"
#include "glo.h"
#include "mproc.h"
#include "param.h"
#define DUMP_SIZE 256 /* buffer size for core dumps */
#define CORE_MODE 0777 /* mode to use on core image files */
#define DUMPED 0200 /* bit set in status when core dumped */
PRIVATE message m_sig;
/*===========================================================================*
* do_signal *
*===========================================================================*/
PUBLIC int do_signal()
{
/* Perform the signal(sig, func) call by setting bits to indicate that a signal
* is to be caught or ignored.
*/
int mask;
if (sig < 1 || sig > NR_SIGS) return(EINVAL);
if (sig == SIGKILL) return(OK); /* SIGKILL may not ignored/caught */
mask = 1 << (sig - 1); /* singleton set with 'sig' bit on */
/* All this func does is set the bit maps for subsequent sig processing. */
if (func == SIG_IGN) {
mp->mp_ignore |= mask;
mp->mp_catch &= ~mask;
} else if (func == SIG_DFL) {
mp->mp_ignore &= ~mask;
mp->mp_catch &= ~mask;
} else {
mp->mp_ignore &= ~mask;
mp->mp_catch |= mask;
mp->mp_func = func;
}
return(OK);
}
/*===========================================================================*
* do_kill *
*===========================================================================*/
PUBLIC int do_kill()
{
/* Perform the kill(pid, kill_sig) system call. */
return check_sig(pid, kill_sig, mp->mp_effuid);
}
/*===========================================================================*
* 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 is 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).
*/
register struct mproc *rmp;
int i, proc_id, proc_nr, id;
unshort sig_map; /* bits 0 - 15 for sigs 1 - 16 */
/* Only kernel and FS may make this call. */
if (who != HARDWARE && who != FS_PROC_NR) return(EPERM);
proc_nr = mm_in.PROC1;
rmp = &mproc[proc_nr];
if ( (rmp->mp_flags & IN_USE) == 0 || (rmp->mp_flags & HANGING) ) return(OK);
proc_id = rmp->mp_pid;
sig_map = (unshort) mm_in.SIG_MAP;
mp = &mproc[0]; /* pretend kernel signals are from MM */
/* Stack faults are passed from kernel to MM as pseudo-signal 16. */
if (sig_map == 1 << (STACK_FAULT - 1)) {
stack_fault(proc_nr);
return(OK);
}
/* 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, since multiple processes may have to signalled.
*/
for (i = 0; i < NR_SIGS; i++) {
id = (i+1 == SIGINT || i+1 == SIGQUIT ? 0 : proc_id);
if ( (sig_map >> i) & 1) check_sig(id, i + 1, SUPER_USER);
}
dont_reply = TRUE; /* don't reply to the kernel */
return(OK);
}
/*===========================================================================*
* check_sig *
*===========================================================================*/
PRIVATE int check_sig(proc_id, sig_nr, send_uid)
int proc_id; /* pid of process to signal, or 0 or -1 */
int sig_nr; /* which signal to send (1-16) */
uid send_uid; /* identity of process sending the signal */
{
/* 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. SIGALRM too.
*/
register struct mproc *rmp;
int count, send_sig;
unshort mask;
extern unshort core_bits;
if (sig_nr < 1 || sig_nr > NR_SIGS) return(EINVAL);
count = 0; /* count # of signals sent */
mask = 1 << (sig_nr - 1);
/* Search the proc table for processes to signal. Several tests are made:
* - if proc's uid != sender's, and sender is not superuser, don't signal
* - if specific process requested (i.e., 'procpid' > 0, check for match
* - if a process has already exited, it can't receive signals
* - if 'proc_id' is 0 signal everyone in same process group except caller
*/
for (rmp = &mproc[INIT_PROC_NR + 1]; rmp < &mproc[NR_PROCS]; rmp++ ) {
if ( (rmp->mp_flags & IN_USE) == 0) continue;
send_sig = TRUE; /* if it's FALSE at end of loop, don't signal */
if (send_uid != rmp->mp_effuid && send_uid != SUPER_USER)send_sig=FALSE;
if (proc_id > 0 && proc_id != rmp->mp_pid) send_sig = FALSE;
if (rmp->mp_flags & HANGING) send_sig = FALSE; /*don't wake the dead*/
if (proc_id == 0 && mp->mp_procgrp != rmp->mp_procgrp) send_sig = FALSE;
if (send_uid == SUPER_USER && proc_id == -1) send_sig = TRUE;
/* SIGALARM is a little special. When a process exits, a clock signal
* can arrive just as the timer is being turned off. Also, turn off
* ALARM_ON bit when timer goes off to keep it accurate.
*/
if (sig_nr == SIGALRM) {
if ( (rmp->mp_flags & ALARM_ON) == 0) continue;
rmp->mp_flags &= ~ALARM_ON;
}
if (send_sig == FALSE || rmp->mp_ignore & mask) continue;
count++;
/* Send the signal or kill the process, possibly with core dump. */
sig_proc(rmp, sig_nr);
/* If process is hanging on PAUSE, WAIT, tty, pipe, etc. release it. */
unpause(rmp - mproc); /* check to see if process is paused */
if (proc_id > 0) break; /* only one process being signalled */
}
/* If the calling process has killed itself, don't reply. */
if ((mp->mp_flags & IN_USE) == 0 || (mp->mp_flags & HANGING))dont_reply =TRUE;
return(count > 0 ? OK : ESRCH);
}
/*===========================================================================*
* sig_proc *
*===========================================================================*/
PUBLIC sig_proc(rmp, sig_nr)
register struct mproc *rmp; /* pointer to the process to be signalled */
int sig_nr; /* signal to send to process (1-16) */
{
/* Send a signal to a process. Check to see if the signal is to be caught.
* If so, the pc, psw, and signal number are to be pushed onto the process'
* stack. If the stack cannot grow or the signal is not to be caught, kill
* the process.
*/
unshort mask;
int core_file;
vir_bytes new_sp;
extern unshort core_bits;
if ( (rmp->mp_flags & IN_USE) == 0) return; /* if already dead forget it */
mask = 1 << (sig_nr - 1);
if (rmp->mp_catch & mask) {
/* Signal should be caught. */
rmp->mp_catch &= ~mask; /* disable further signals */
sys_getsp(rmp - mproc, &new_sp);
new_sp -= SIG_PUSH_BYTES;
if (adjust(rmp, rmp->mp_seg[D].mem_len, new_sp) == OK) {
sys_sig(rmp - mproc, sig_nr, rmp->mp_func);
return; /* successful signal */
}
}
/* Signal should not or cannot be caught. Take default action. */
core_file = ( core_bits >> (sig_nr - 1 )) & 1;
rmp->mp_sigstatus = (char) sig_nr;
if (core_file) dump_core(rmp); /* dump core */
mm_exit(rmp, 0); /* terminate process */
}
/*===========================================================================*
* do_alarm *
*===========================================================================*/
PUBLIC int do_alarm()
{
/* Perform the alarm(seconds) system call. */
register int r;
unsigned sec;
sec = (unsigned) seconds;
r = set_alarm(who, sec);
return(r);
}
/*===========================================================================*
* set_alarm *
*===========================================================================*/
PUBLIC int set_alarm(proc_nr, sec)
int proc_nr; /* process that wants the alarm */
unsigned sec; /* how many seconds delay before the signal */
{
/* This routine is used by do_alarm() to set the alarm timer. It is also
* to turn the timer off when a process exits with the timer still on.
*/
int remaining;
m_sig.m_type = SET_ALARM;
m_sig.PROC_NR = proc_nr;
m_sig.DELTA_TICKS = HZ * sec;
if (sec != 0)
mproc[proc_nr].mp_flags |= ALARM_ON; /* turn ALARM_ON bit on */
else
mproc[proc_nr].mp_flags &= ~ALARM_ON; /* turn ALARM_ON bit off */
/* Tell the clock task to provide a signal message when the time comes. */
if (sendrec(CLOCK, &m_sig) != OK) panic("alarm er", NO_NUM);
remaining = (int) m_sig.SECONDS_LEFT;
return(remaining);
}
/*===========================================================================*
* do_pause *
*===========================================================================*/
PUBLIC int do_pause()
{
/* Perform the pause() system call. */
mp->mp_flags |= PAUSED; /* turn on PAUSE bit */
dont_reply = TRUE;
return(OK);
}
/*===========================================================================*
* unpause *
*===========================================================================*/
PUBLIC unpause(pro)
int pro; /* which process number */
{
/* A signal is to be sent to a process. It 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 PAUSE or WAIT. 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 PAUSE call. */
if ( (rmp->mp_flags & PAUSED) && (rmp->mp_flags & HANGING) == 0) {
rmp->mp_flags &= ~PAUSED; /* turn off PAUSED bit */
reply(pro, EINTR, 0, NIL_PTR);
return;
}
/* Check to see if process is hanging on a WAIT call. */
if ( (rmp->mp_flags & WAITING) && (rmp->mp_flags & HANGING) == 0) {
rmp->mp_flags &= ~ WAITING; /* turn off WAITING bit */
reply(pro, EINTR, 0, NIL_PTR);
return;
}
/* Process is not hanging on an MM call. Ask FS to take a look. */
tell_fs(UNPAUSE, pro, 0, 0);
return;
}
/*===========================================================================*
* dump_core *
*===========================================================================*/
PRIVATE dump_core(rmp)
register struct mproc *rmp; /* whose core is to be dumped */
{
/* Make a core dump on the file "core", if possible. */
struct stat s_buf, d_buf;
char buf[DUMP_SIZE];
int i, r, s, er1, er2, slot;
vir_bytes v_buf;
long len, a, c, ct, dest;
struct mproc *xmp;
extern char core_name[];
/* Change to working directory of dumpee. */
slot = rmp - mproc;
tell_fs(CHDIR, slot, 0, 0);
/* Can core file be written? */
if (rmp->mp_realuid != rmp->mp_effuid) return;
xmp = mp; /* allowed() looks at 'mp' */
mp = rmp;
r = allowed(core_name, &s_buf, W_BIT); /* is core_file writable */
s = allowed(".", &d_buf, W_BIT); /* is directory writable? */
mp = xmp;
if (r >= 0) close(r);
if (s >= 0) close(s);
if (rmp->mp_effuid == SUPER_USER) r = 0; /* su can always dump core */
if (s >= 0 && (r >= 0 || r == ENOENT)) {
/* Either file is writable or it doesn't exist & dir is writable */
r = creat(core_name, CORE_MODE);
tell_fs(CHDIR, 0, 1, 0); /* go back to MM's own dir */
if (r < 0) return;
rmp->mp_sigstatus |= DUMPED;
/* First loop through segments and write each length on core file. */
for (i = 0; i < NR_SEGS; i++) {
len = rmp->mp_seg[i].mem_len << CLICK_SHIFT;
if (write(r, (char *) &len, sizeof len) < 0) {
close(r);
return;
}
}
/* Now loop through segments and write the segments themselves out. */
v_buf = (vir_bytes) buf;
dest = (long) v_buf;
for (i = 0; i < NR_SEGS; i++) {
a = (phys_bytes) rmp->mp_seg[i].mem_vir << CLICK_SHIFT;
c = (phys_bytes) rmp->mp_seg[i].mem_len << CLICK_SHIFT;
/* Loop through a segment, dumping it. */
while (c > 0) {
ct = MIN(c, DUMP_SIZE);
er1 = mem_copy(slot, i, a, MM_PROC_NR, D, dest, ct);
er2 = write(r, buf, (int) ct);
if (er1 < 0 || er2 < 0) {
close(r);
return;
}
a += ct;
c -= ct;
}
}
} else {
tell_fs(CHDIR, 0, 1, 0); /* go back to MM's own dir */
close(r);
return;
}
close(r);
}