293 lines
7.4 KiB
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;
|
|
}
|