home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
No Fragments Archive 10: Diskmags
/
nf_archive_10.iso
/
MAGS
/
PURE_B
/
PBMAG22A.MSA
/
MINT095S.ZIP
/
SRC
/
SIGNAL.C
< prev
next >
Wrap
C/C++ Source or Header
|
1987-04-22
|
14KB
|
522 lines
/*
Copyright 1990,1991,1992 Eric R. Smith. All rights reserved.
*/
/* signal.c:: signal handling routines */
#include "mint.h"
void (*sig_routine)(); /* used in intr.s */
/*
* killgroup(pgrp, sig): send a signal to all members of a process group
* returns 0 on success, or an error code on failure
*/
long
killgroup(pgrp, sig)
int pgrp, sig;
{
PROC *p;
int found = 0;
TRACE("killgroup %d %d", pgrp, sig);
if (pgrp < 0)
return EINTRN;
for (p = proclist; p; p = p->gl_next) {
if (p->pgrp == pgrp) {
post_sig(p, sig);
found++;
}
}
if (found) {
check_sigs(); /* see if the current process is affected */
return 0;
}
else {
DEBUG("killgroup: no processes found");
return EFILNF;
}
}
/* post_sig: post a signal as being pending. It is assumed that the
caller has already verified that "sig" is a valid signal, and
moreover it is the caller's responsibility to call check_sigs()
if it's possible that p == curproc
*/
void
post_sig(p, sig)
PROC *p;
int sig;
{
ulong sigm;
/* if process is ignoring this signal, do nothing
* also: signal 0 is SIGNULL, and should never be delivered through
* the normal channels (indeed, it's filtered out in dossig.c,
* but the extra sanity check here is harmless). The kernel uses
* signal 0 internally for some purposes, but it is handled
* specially (see supexec() in xbios.c, for example).
*/
if (p->sighandle[sig] == SIG_IGN || sig == 0)
return;
/* if the process is already dead, do nothing */
if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
return;
/* mark the signal as pending */
sigm = (1L << (unsigned long)sig);
p->sigpending |= sigm;
/* if the signal is masked, do nothing further */
if ( (p->sigmask & sigm) != 0 )
return;
/* otherwise, make sure the process is awake */
if (p->wait_q && p->wait_q != READY_Q) {
rm_q(p->wait_q, p);
add_q(READY_Q, p);
}
}
/*
* check_sigs: see if we have any signals pending. if so,
* handle them.
*/
void
check_sigs()
{
ulong sigs, sigm;
ulong i;
if (curproc->pid == 0) return;
sigs = curproc->sigpending & ~(curproc->sigmask);
if (sigs) {
sigm = 2;
for (i = 1; i < NSIG; i++) {
if (sigs & sigm) {
ulong omask;
curproc->sigpending &= ~sigm;
omask = curproc->sigmask;
/* sigextra gives which extra signals should also be masked */
curproc->sigmask |= curproc->sigextra[i] | sigm;
handle_sig(i);
/*
* POSIX.1-3.3.4.2(723) "If and when the user's signal handler returns
* normally, the original signal mask is restored."
*
* BUG?: This unmasking could unmask a pending signal which we will not
* see this time around (if the signal number is less than i) and which
* was not pending when we started; should we detect this condition and
* loop around for a second try? POSIX only guarantees delivery of
* one signal per kernel entry, so this shouldn't really be a problem.
*/
curproc->sigmask = omask; /* unmask signals */
}
sigm = sigm << 1;
}
}
}
/*
* raise: cause a signal to be raised in the current process
*/
void
raise(sig)
int sig;
{
post_sig(curproc, sig);
check_sigs();
}
#ifdef EXCEPTION_SIGS
/* exception numbers corresponding to signals */
char excep_num[NSIG] =
{ 0, 0, 0, 0,
4, /* SIGILL == illegal instruction */
9, /* SIGTRAP == trace trap */
4, /* pretend SIGABRT is also illegal instruction */
8, /* SIGPRIV == privileged instruction exception */
5, /* SIGFPE == divide by zero */
0, 2, /* SIGBUS == bus error */
3 /* SIGSEGV == address error */
/* everything else gets zeros */
};
/* a "0" means we don't print a message when it happens -- typically the
user is expecting a synchronous signal, so we don't need to report it
*/
const char *signames[NSIG] = { 0,
0, 0, 0, "ILLEGAL INSTRUCTION", "TRACE TRAP",
0, "PRIVILEGE VIOLATION", "DIVISION BY ZERO", 0, "BUS ERROR",
"ADDRESS ERROR", "BAD SYSTEM CALL", 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, "CPU TIME EXHAUSTED", "FILE TOO BIG",
0, 0, 0, 0, 0
};
/*
* replaces the TOS "show bombs" routine: for now, print the name of the
* interrupt on the console, and save info on the crash in the appropriate
* system area
*/
void
bombs(sig)
int sig;
{
long *procinfo = (long *)0x380L;
int i;
CONTEXT *crash;
if (signames[sig]) {
ALERT("%s: User PC=%lx (basepage=%lx)",
signames[sig],
curproc->ctxt[SYSCALL].pc, curproc->base);
/* save the processor state at crash time */
/* assumes that "crash time" is the context curproc->ctxt[SYSCALL] */
/* BUG: this is not true if the crash happened in the kernel; in the
* latter case, the crash context wasn't saved anywhere.
*/
crash = &curproc->ctxt[SYSCALL];
*procinfo++ = 0x12345678; /* magic flag for valid info */
for (i = 0; i < 15; i++)
*procinfo++ = crash->regs[i];
*procinfo++ = crash->ssp;
*procinfo++ = ((long)excep_num[sig]) << 24L;
*procinfo++ = crash->usp;
/* we're also supposed to save some info from the supervisor stack. it's not
* clear what we should do for MiNT, since most of the stuff that used to be
* on the stack has been put in the CONTXT struct. Moreover, we don't want
* to crash because of an attempt to access illegal memory. Hence, we do
* nothing here...
*/
}
}
#endif
/*
* handle_sig: do whatever is appropriate to handle a signal
*/
static long unwound_stack = 0;
void
handle_sig(sig)
int sig;
{
long oldstack, newstack;
long *stack;
CONTEXT *call, oldsysctxt, newcurrent;
extern void sig_return();
if (curproc->sighandle[sig] == SIG_IGN)
return;
else if (curproc->sighandle[sig] == SIG_DFL) {
_default:
switch(sig) {
#if 0
/* Note: SIGNULL is filtered out in dossig.c and is never actually
* delivered (its only purpose for the user is to test for the existence of
* a process, it isn't a real signal). The kernel uses SIGNULL
* internally, but all such code does the signal handling "by hand"
* and so no default handling is necessary.
*/
case SIGNULL:
#endif
case SIGWINCH:
case SIGCHLD:
return; /* do nothing */
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
stop(sig);
return;
case SIGCONT:
curproc->sigpending &= ~STOPSIGS;
return;
/* here are the fatal signals. for SIGINT, we use p_term() so that
* TOS programs that catch ^C via the vector at 0x400 will work. for
* other signals (especially SIGKILL) we don't want that to happen;
* TOS programs aren't prepared to deal with signals.
*/
case SIGINT: /* ^C */
if (curproc->domain == DOM_TOS) {
curproc->sigmask &= ~(1L<<SIGINT);
p_term(-32);
return;
}
/* otherwise, fall through */
default:
#ifdef EXCEPTION_SIGS
bombs(sig); /* tell the user what happened */
#endif
terminate(sig << 8, ZOMBIE_Q);
}
}
else { /* user wants to handle it himself */
/* another kludge: there is one case in which the p_sigreturn mechanism
* is invoked by the kernel, namely when the user calls Supexec()
* or when s/he installs a handler for the GEMDOS terminate vector (#0x102)
* and the program terminates. MiNT fakes the call to user code with
* signal 0 (SIGNULL); programs that longjmp out of the user function
* and are later sent back to it again (e.g. if ^C keeps getting pressed
* and a terminate vector has been installed) will grow the stack without
* bound unless we watch for this case.
*
* Solution (sort of): whenever Pterm() is called, we unwind the
* stack; otherwise, we let it grow, so that nested Supexec()
* calls work.
*
* Note that SIGNULL is thrown away when sent by user processes,
* and the user can't mask it (it's UNMASKABLE), so there is
* is no possibility of confusion with anything the user does.
*/
if (sig == 0) {
/* p_term() sets sigmask to let us know to do Psigreturn */
if (curproc->sigmask & 1L) {
p_sigreturn();
curproc->sigmask &= ~1L;
} else {
unwound_stack = 0;
}
}
call = &curproc->ctxt[SYSCALL];
/*
* what we do is build two fake stack frames; the bottom one is
* for a call to the user function, with (long)parameter being the
* signal number; the top one is for sig_return.
* When the user function returns, it returns to sig_return, which
* calls into the kernel to restore the context in prev_ctxt
* (thus putting us back here). We can then continue on our way.
*/
/* set a new system stack, with a bit of buffer space */
oldstack = curproc->sysstack;
newstack = ((long) ( (&newcurrent) - 3 )) - 12;
if (newstack < (long)curproc->stack + ISTKSIZE + 256) {
ALERT("stack overflow");
goto _default;
}
else if ((long) curproc->stack + STKSIZE < newstack) {
FATAL("system stack not in proc structure");
}
/* unwound_stack is set by p_sigreturn() */
if (sig == 0 && unwound_stack)
curproc->sysstack = unwound_stack;
else
curproc->sysstack = newstack;
oldsysctxt = *call;
stack = (long *)(call->sr & 0x2000 ? call->ssp :
call->usp);
/*
Hmmm... here's another potential problem for the signal 0 terminate
vector: if the program keeps returning back to user mode without
worrying about the supervisor stack, we'll eventually overflow it.
However, if the program is in supervisor mode itself, then we don't
want to stomp on its stack. Temporary solution: ignore the problem,
the stack's only growing 8 bytes at a time.
*/
*(--stack) = (long)sig;
*(--stack) = (long)sig_return;
if (call->sr & 0x2000)
call->ssp = ((long) stack);
else
call->usp = ((long) stack);
call->pc = (long) curproc->sighandle[sig];
((long *)curproc->sysstack)[1] = FRAME_MAGIC;
((long *)curproc->sysstack)[2] = oldstack;
((long *)curproc->sysstack)[3] = sig;
if (save_context(&newcurrent) == 0 ) {
/*
* go do the signal; eventually, we'll restore this context (unless the
* user longjmp'd out of his signal handler). while the user is handling
* the signal, it's masked out to prevent race conditions. p_sigreturn()
* will unmask it for us when the user is finished.
*/
newcurrent.regs[0] = CTXT_MAGIC;
/* set D0 so next return is different */
assert(curproc->magic == CTXT_MAGIC);
leave_kernel();
restore_context(call);
}
/*
* OK, we get here from p_sigreturn, via the user returning from
* the handler to sig_return. Restoring the stack and unmasking the
* signal have been done already for us by p_sigreturn.
* We should just restore the old system call context
* and continue with whatever it was we were doing.
*/
TRACE("done handling signal");
curproc->ctxt[SYSCALL] = oldsysctxt;
assert(curproc->magic == CTXT_MAGIC);
}
}
/*
* the p_sigreturn system call
* When called by the user from inside a signal handler, it indicates a
* desire to restore the old stack frame prior to a longjmp() out of
* the handler.
* When called from the sig_return module, it indicates that the user
* is finished a handler, and we should not only restore the stack
* frame but also the old context we were working in (which is on the
* system call stack -- see handle_sig).
* The "valid_return" variable is 0 in the first case, 1 in the second.
*/
short valid_return;
long
p_sigreturn()
{
CONTEXT *oldctxt;
long *frame;
long sig;
unwound_stack = 0;
top:
frame = (long *)curproc->sysstack;
frame++; /* frame should point at FRAME_MAGIC, now */
sig = frame[2];
if (*frame != FRAME_MAGIC || (sig < 0) || (sig >= NSIG)) {
FATAL("Psigreturn: system stack corrupted");
}
if (frame[1] == 0) {
DEBUG("Psigreturn: frame at %lx points to 0", frame-1);
return 0;
}
unwound_stack = curproc->sysstack;
TRACE("Psigreturn(%d)", (int)sig);
curproc->sysstack = frame[1]; /* restore frame */
curproc->sigmask &= ~(1L<<sig); /* unblock signal */
if (!valid_return) {
/* here, the user is telling us that a longjmp out of a signal handler is
* about to occur; so we should unwind *all* the signal frames
*/
goto top;
}
else {
valid_return = 0;
oldctxt = ((CONTEXT *)(&frame[2])) + 3;
if (oldctxt->regs[0] != CTXT_MAGIC) {
FATAL("p_sigreturn: corrupted context");
}
assert(curproc->magic == CTXT_MAGIC);
restore_context(oldctxt);
return 0; /* dummy -- this isn't reached */
}
}
/*
* stop a process because of signal "sig"
*/
void
stop(sig)
int sig;
{
unsigned int code;
unsigned long oldmask;
PROC *p;
code = sig << 8;
if (curproc->pid == 0) {
ALERT("attempt to stop MiNT");
return;
}
/* notify parent */
p = pid2proc(curproc->ppid);
if (p && !(p->sigflags[SIGCHLD] & SA_NOCLDSTOP)) {
post_sig(p, SIGCHLD);
}
oldmask = curproc->sigmask;
/* mask out most signals */
curproc->sigmask |= ~(UNMASKABLE | SIGTERM);
/* sleep until someone signals us awake */
sleep(STOP_Q, (long) code | 0177);
/* when we wake up, restore the signal mask */
curproc->sigmask = oldmask;
/* and discard any signals that would cause us to stop again */
curproc->sigpending &= ~STOPSIGS;
}
/*
* interrupt handlers to raise SIGBUS, SIGSEGV, etc. Note that for
* really fatal errors we reset the handler to SIG_DFL, so that
* a second such error kills us
*/
void
exception(sig)
int sig;
{
curproc->sighandle[sig] = SIG_DFL;
raise(sig);
}
void
sigbus()
{
exception(SIGBUS);
}
void
sigaddr()
{
exception(SIGSEGV);
}
void
sigill()
{
exception(SIGILL);
}
void
sigpriv()
{
raise(SIGPRIV);
}
void
sigfpe()
{
raise(SIGFPE);
}
void
sigtrap()
{
raise(SIGTRAP);
}