scsh-0.5/scsh/sighandlers1.c

293 lines
7.4 KiB
C

/* Need to define sig2interrupt vector.
** Interrupt-system mutators should probably hold interrupts while they
** operate.
*/
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include "cstuff.h"
/* Make sure our exports match up w/the implementation: */
#include "sighandlers1.h"
extern int errno;
extern scheme_value Spending_interruptsS, Sinterrupt_handlersS;
/* Translate Unix signal numbers to S48 interrupt numbers.
** alarm, keyboard (^C, SIGINT), and memory shortage are 0, 1, and 2.
*/
static int sig2interrupt(int signal)
{
switch (signal) {
case SIGALRM: return 0; /* Already defined by S48. */
case SIGCHLD: return 3;
case SIGCONT: return 4;
case SIGHUP: return 5;
case SIGINT: return 1; /* Already defined by S48. */
case SIGQUIT: return 6;
case SIGTERM: return 7;
case SIGTSTP: return 8;
case SIGUSR1: return 9;
case SIGUSR2: return 10;
#ifdef SIGINFO
case SIGINFO: return 11;
#endif
#ifdef SIGIO
case SIGIO: return 12;
#endif
#ifdef SIGPOLL
case SIGPOLL: return 13;
#endif
#ifdef SIGPROF
case SIGPROF: return 14;
#endif
#ifdef SIGPWR
case SIGPWR: return 15;
#endif
#ifdef SIGURG
case SIGURG: return 16;
#endif
#ifdef SIGVTALRM
case SIGVTALRM: return 17;
#endif
#ifdef SIGWINCH
case SIGWINCH: return 18;
#endif
#ifdef SIGXCPU
case SIGXCPU: return 19;
#endif
#ifdef SIGXFSZ
case SIGXFSZ: return 20;
#endif
default: return -1;
}
}
/* Hack the blocked-signal mask.
*******************************************************************************
*/
#include "machine/sigset.h"
int set_procmask(int hi, int lo, int *old_lo_p)
{
sigset_t mask, old_mask;
int old_hi;
make_sigset(&mask, hi, lo);
sigprocmask(SIG_SETMASK, &mask, &old_mask);
split_sigset(old_mask, &old_hi, old_lo_p);
return old_hi;
}
int get_procmask(int *old_lo_p)
{
sigset_t old_mask;
int old_hi;
sigprocmask(SIG_SETMASK, NULL, &old_mask);
split_sigset(old_mask, &old_hi, old_lo_p);
return old_hi;
}
/* Set/Get signal handlers
*******************************************************************************
*/
static void scm_handle_sig(int sig)
{
Spending_interruptsS |= (1<<sig2interrupt(sig));
}
scheme_value set_sig_handler(int sig, scheme_value handler, int flags,
scheme_value *ohandler, int *oflags)
{
struct sigaction new, old;
int intnum = sig2interrupt(sig);
scheme_value old_scsh_handler;
/* intnum in range? */
if( intnum >= VECTOR_LENGTH(Sinterrupt_handlersS) ) {
*ohandler = SCHFALSE;
return ENTER_FIXNUM(-1);
}
/* We may need this for ohandler later, but it may get clobbered
** when we set the new handler, so stash it away for now.
*/
old_scsh_handler = VECTOR_REF(Sinterrupt_handlersS, intnum);
sigemptyset(&new.sa_mask); /* WTF */
new.sa_flags = flags;
if( handler == SCHFALSE ) {
new.sa_handler = SIG_IGN;
VECTOR_REF(Sinterrupt_handlersS, intnum) = SCHFALSE;
}
/* This *really* sets the Unix signal handler to SIG_DFL.
** What usually happens isn't this -- what usually happens is that
** we establish a special Scheme handler that does the default, so
** that it is subject to S48's blocking machinery.
*/
else if( handler == SCHTRUE ) {
new.sa_handler = SIG_DFL;
VECTOR_REF(Sinterrupt_handlersS, intnum) = SCHFALSE;
}
else {
new.sa_handler = scm_handle_sig;
VECTOR_REF(Sinterrupt_handlersS, intnum) = handler;
/* Do other stuff. */
}
if( sigaction(sig, &new, &old) ) {
*ohandler = SCHFALSE;
return ENTER_FIXNUM(errno);
}
*oflags = old.sa_flags;
if( old.sa_handler == SIG_IGN ) *ohandler = SCHFALSE;
else if( old.sa_handler == SIG_DFL ) *ohandler = SCHTRUE;
else if( old.sa_handler == scm_handle_sig ) *ohandler = old_scsh_handler;
else *ohandler = ENTER_FIXNUM(-1); /* Unknown signal handler. */
return SCHFALSE;
}
scheme_value get_sig_handler(int signal, scheme_value *handler, int *flags)
{
struct sigaction old;
if( sigaction(signal, NULL, &old) ) {
*handler = SCHFALSE;
return ENTER_FIXNUM(errno);
}
*flags = old.sa_flags;
if( old.sa_handler == SIG_IGN ) *handler = SCHFALSE;
else if( old.sa_handler == SIG_DFL ) *handler = SCHTRUE;
else if( old.sa_handler == scm_handle_sig ) {
int intnum = sig2interrupt(signal);
/* intnum in range? */
if( intnum >= VECTOR_LENGTH(Sinterrupt_handlersS) ) {
*handler = SCHFALSE;
return ENTER_FIXNUM(-1);
}
*handler = VECTOR_REF(Sinterrupt_handlersS, intnum);
}
else *handler = ENTER_FIXNUM(-1); /* Unknown signal handler. */
return SCHFALSE;
}
/* Return true if SIGNAL's default action is definitely to be ignored. */
/* This should be inlined by a good compiler. */
static int sig_def_is_ignored(int signal)
{
return
/* Posix signals */
signal == SIGALRM || signal == SIGHUP ||
signal == SIGINT || signal == SIGQUIT ||
signal == SIGTERM || signal == SIGUSR1 ||
signal == SIGUSR2
/* Non-Posix signals, when present. */
#ifdef SIGINFO
|| signal == SIGINFO
#endif
#ifdef SIGPOLL
|| signal == SIGPOLL
#endif
#ifdef SIGPROF
|| signal == SIGPROF
#endif
#ifdef SIGVTALRM
|| signal == SIGVTALRM
#endif
#ifdef SIGXCPU
|| signal == SIGXCPU
#endif
#ifdef SIGXFSZ
|| signal == SIGXFSZ
#endif
#ifdef SIGIO
|| signal == SIGIO /* BSD => ignore; SVR4 => terminate */
#endif
;
}
/* This guy is responsible for making the default action for a
** Unix signal happen. Because S48's signal handler system is
** interposed between delivery-to-the-process and
** delivery-to-the-scheme-handler, when the user sets a signal
** handler to default, we install a Scheme proc that calls this
** guy, instead of just slapping a SIGDFL in as the Unix handler.
** We only have to do this for signals whose default isn't "ignore," i.e.:
** Posix: SIGALRM SIGHUP SIGINT SIGQUIT SIGTERM SIGUSR1 SIGUSR2
** Non-Posix: SIGINFO SIGPOLL SIGPROF SIGVTALRM SIGXCPU SIGXFSZ SIGIO
** This way, the S48 signal-blocking mechanism can work.
**
** Weird, I know.
*/
void do_default_sigaction(int signal)
{
sigset_t ss, old_ss;
struct sigaction default_action, old_action;
if( !sig_def_is_ignored(signal) ) {
/* OK -- signal's default *isn't* "ignore," so we have to do it. */
sigfillset(&ss); /* Block everyone. */
sigprocmask(SIG_SETMASK, &ss, &old_ss);
default_action.sa_handler = SIG_DFL; /* Set for default. */
sigemptyset(&default_action.sa_mask);
default_action.sa_flags = 0;
sigaction(signal, &default_action, &old_action);
raise(signal); /* Raise the signal. */
sigdelset(&ss, signal);
sigprocmask(SIG_SETMASK, &ss, 0); /* Handle it. */
/* Most likely, we'll never get to here, as the default for
** the signals we're handling is "terminate," but we'll play it safe.
*/
sigaction(signal, &old_action, 0); /* Restore old handler, */
sigprocmask(SIG_SETMASK, &old_ss, 0); /* and mask. */
}
}
/* Install a new signal-handler vector.
** I use this because the default one is only 3 entries long, and I
** don't want to modify the S48 source. So I'll just install my own
** at run-time.
** It's not a hack, it's a kluge.
*/
void install_new_handler_vector(scheme_value handlers)
{
extern scheme_value Sinterrupt_handlersS;
Sinterrupt_handlersS = handlers;
}