/* * Signal handling for Plan 9 programs. * We stubbornly use the strings from Plan 9 instead * of the enumerated Unix constants. * There are some weird translations. In particular, * a "kill" note is the same as SIGTERM in Unix. * There is no equivalent note to Unix's SIGKILL, since * it's not a deliverable signal anyway. * * We do not handle SIGABRT or SIGSEGV, mainly because * the thread library queues its notes for later, and we want * to dump core with the state at time of delivery. * * We have to add some extra entry points to provide the * ability to tweak which signals are deliverable and which * are acted upon. Notifydisable and notifyenable play with * the process signal mask. Notifyignore enables the signal * but will not call notifyf when it comes in. This is occasionally * useful. */ #include <u.h>#include <signal.h>#define NOPLAN9DEFINES#include <libc.h> extern char *_p9sigstr(int, char*);extern int _p9strsig(char*); typedef struct Sig Sig;struct Sig{ int sig; /* signal number */ int flags;}; enum{ Restart = 1<<0, Ignore = 1<<1, NoNotify = 1<<2,}; static Sig sigs[] = { SIGHUP, 0, SIGINT, 0, SIGQUIT, 0, SIGILL, 0, SIGTRAP, 0,/* SIGABRT, 0, */#ifdef SIGEMT SIGEMT, 0,#endif SIGFPE, 0, SIGBUS, 0,/* SIGSEGV, 0, */ SIGCHLD, Restart|Ignore, SIGSYS, 0, SIGPIPE, Ignore, SIGALRM, 0, SIGTERM, 0, SIGTSTP, Restart|Ignore|NoNotify,/* SIGTTIN, Restart|Ignore, *//* SIGTTOU, Restart|Ignore, */ SIGXCPU, 0, SIGXFSZ, 0, SIGVTALRM, 0, SIGUSR1, 0, SIGUSR2, 0,#ifdef SIGWINCH SIGWINCH, Restart|Ignore|NoNotify,#endif#ifdef SIGINFO SIGINFO, Restart|Ignore|NoNotify,#endif}; static Sig*findsig(int s){ int i; for(i=0; i<nelem(sigs); i++) if(sigs[i].sig == s) return &sigs[i]; return nil;} /* * The thread library initializes _notejmpbuf to its own * routine which provides a per-pthread jump buffer. * If we're not using the thread library, we assume we are * single-threaded. */typedef struct Jmp Jmp;struct Jmp{ p9jmp_buf b;}; static Jmp onejmp; static Jmp*getonejmp(void){ return &onejmp;} Jmp *(*_notejmpbuf)(void) = getonejmp;static void noteinit(void); /* * Actual signal handler. */ static void (*notifyf)(void*, char*); /* Plan 9 handler */ static voidsignotify(int sig){ char tmp[64]; Jmp *j; Sig *s; j = (*_notejmpbuf)(); switch(p9setjmp(j->b)){ case 0: if(notifyf) (*notifyf)(nil, _p9sigstr(sig, tmp)); /* fall through */ case 1: /* noted(NDFLT) */ if(0)print("DEFAULT %d\n", sig); s = findsig(sig); if(s && (s->flags&Ignore)) return; signal(sig, SIG_DFL); raise(sig); _exit(1); case 2: /* noted(NCONT) */ if(0)print("HANDLED %d\n", sig); return; }} static voidsignonotify(int sig){ USED(sig);} intnoted(int v){ p9longjmp((*_notejmpbuf)()->b, v==NCONT ? 2 : 1); abort(); return 0;} intnotify(void (*f)(void*, char*)){ static int init; notifyf = f; if(!init){ init = 1; noteinit(); } return 0;} /* * Nonsense about enabling and disabling signals. */typedef void Sighandler(int);static Sighandler*handler(int s){ struct sigaction sa; sigaction(s, nil, &sa); return sa.sa_handler;} static intnotesetenable(int sig, int enabled){ sigset_t mask, omask; if(sig == 0) return -1; sigemptyset(&mask); sigaddset(&mask, sig); sigprocmask(enabled ? SIG_UNBLOCK : SIG_BLOCK, &mask, &omask); return !sigismember(&omask, sig); } intnoteenable(char *msg){ return notesetenable(_p9strsig(msg), 1);} intnotedisable(char *msg){ return notesetenable(_p9strsig(msg), 0);} static intnotifyseton(int s, int on){ Sig *sig; struct sigaction sa, osa; sig = findsig(s); if(sig == nil) return -1; memset(&sa, 0, sizeof sa); sa.sa_handler = on ? signotify : signonotify; if(sig->flags&Restart) sa.sa_flags |= SA_RESTART; /* * We can't allow signals within signals because there's * only one jump buffer. */ sigfillset(&sa.sa_mask); /* * Install handler. */ sigaction(sig->sig, &sa, &osa); return osa.sa_handler == signotify;} intnotifyon(char *msg){ return notifyseton(_p9strsig(msg), 1);} intnotifyoff(char *msg){ return notifyseton(_p9strsig(msg), 0);} /* * Initialization follows sigs table. */static voidnoteinit(void){ int i; Sig *sig; for(i=0; i<nelem(sigs); i++){ sig = &sigs[i]; /* * If someone has already installed a handler, * It's probably some ld preload nonsense, * like pct (a SIGVTALRM-based profiler). * Or maybe someone has already called notifyon/notifyoff. * Leave it alone. */ if(handler(sig->sig) != SIG_DFL) continue; notifyseton(sig->sig, !(sig->flags&NoNotify)); }}
/*
* Signal handling for Plan 9 programs.
* We stubbornly use the strings from Plan 9 instead
* of the enumerated Unix constants.
* There are some weird translations. In particular,
* a "kill" note is the same as SIGTERM in Unix.
* There is no equivalent note to Unix's SIGKILL, since
* it's not a deliverable signal anyway.
*
* We do not handle SIGABRT or SIGSEGV, mainly because
* the thread library queues its notes for later, and we want
* to dump core with the state at time of delivery.
*
* We have to add some extra entry points to provide the
* ability to tweak which signals are deliverable and which
* are acted upon. Notifydisable and notifyenable play with
* the process signal mask. Notifyignore enables the signal
* but will not call notifyf when it comes in. This is occasionally
* useful.
*/
#include <u.h>
#include <signal.h>
#define NOPLAN9DEFINES
#include <libc.h>
extern char *_p9sigstr(int, char*);
extern int _p9strsig(char*);
typedef struct Sig Sig;
struct Sig
{
int sig; /* signal number */
int flags;
};
enum
{
Restart = 1<<0,
Ignore = 1<<1,
NoNotify = 1<<2,
};
static Sig sigs[] = {
SIGHUP, 0,
SIGINT, 0,
SIGQUIT, 0,
SIGILL, 0,
SIGTRAP, 0,
/* SIGABRT, 0, */
#ifdef SIGEMT
SIGEMT, 0,
#endif
SIGFPE, 0,
SIGBUS, 0,
/* SIGSEGV, 0, */
SIGCHLD, Restart|Ignore,
SIGSYS, 0,
SIGPIPE, Ignore,
SIGALRM, 0,
SIGTERM, 0,
SIGTSTP, Restart|Ignore|NoNotify,
/* SIGTTIN, Restart|Ignore, */
/* SIGTTOU, Restart|Ignore, */
SIGXCPU, 0,
SIGXFSZ, 0,
SIGVTALRM, 0,
SIGUSR1, 0,
SIGUSR2, 0,
#ifdef SIGWINCH
SIGWINCH, Restart|Ignore|NoNotify,
#endif
#ifdef SIGINFO
SIGINFO, Restart|Ignore|NoNotify,
#endif
};
static Sig*
findsig(int s)
{
int i;
for(i=0; i<nelem(sigs); i++)
if(sigs[i].sig == s)
return &sigs[i];
return nil;
}
/*
* The thread library initializes _notejmpbuf to its own
* routine which provides a per-pthread jump buffer.
* If we're not using the thread library, we assume we are
* single-threaded.
*/
typedef struct Jmp Jmp;
struct Jmp
{
p9jmp_buf b;
};
static Jmp onejmp;
static Jmp*
getonejmp(void)
{
return &onejmp;
}
Jmp *(*_notejmpbuf)(void) = getonejmp;
static void noteinit(void);
/*
* Actual signal handler.
*/
static void (*notifyf)(void*, char*); /* Plan 9 handler */
static void
signotify(int sig)
{
char tmp[64];
Jmp *j;
Sig *s;
j = (*_notejmpbuf)();
switch(p9setjmp(j->b)){
case 0:
if(notifyf)
(*notifyf)(nil, _p9sigstr(sig, tmp));
/* fall through */
case 1: /* noted(NDFLT) */
if(0)print("DEFAULT %d\n", sig);
s = findsig(sig);
if(s && (s->flags&Ignore))
return;
signal(sig, SIG_DFL);
raise(sig);
_exit(1);
case 2: /* noted(NCONT) */
if(0)print("HANDLED %d\n", sig);
return;
}
}
static void
signonotify(int sig)
{
USED(sig);
}
int
noted(int v)
{
p9longjmp((*_notejmpbuf)()->b, v==NCONT ? 2 : 1);
abort();
return 0;
}
int
notify(void (*f)(void*, char*))
{
static int init;
notifyf = f;
if(!init){
init = 1;
noteinit();
}
return 0;
}
/*
* Nonsense about enabling and disabling signals.
*/
typedef void Sighandler(int);
static Sighandler*
handler(int s)
{
struct sigaction sa;
sigaction(s, nil, &sa);
return sa.sa_handler;
}
static int
notesetenable(int sig, int enabled)
{
sigset_t mask, omask;
if(sig == 0)
return -1;
sigemptyset(&mask);
sigaddset(&mask, sig);
sigprocmask(enabled ? SIG_UNBLOCK : SIG_BLOCK, &mask, &omask);
return !sigismember(&omask, sig);
}
int
noteenable(char *msg)
{
return notesetenable(_p9strsig(msg), 1);
}
int
notedisable(char *msg)
{
return notesetenable(_p9strsig(msg), 0);
}
static int
notifyseton(int s, int on)
{
Sig *sig;
struct sigaction sa, osa;
sig = findsig(s);
if(sig == nil)
return -1;
memset(&sa, 0, sizeof sa);
sa.sa_handler = on ? signotify : signonotify;
if(sig->flags&Restart)
sa.sa_flags |= SA_RESTART;
/*
* We can't allow signals within signals because there's
* only one jump buffer.
*/
sigfillset(&sa.sa_mask);
/*
* Install handler.
*/
sigaction(sig->sig, &sa, &osa);
return osa.sa_handler == signotify;
}
int
notifyon(char *msg)
{
return notifyseton(_p9strsig(msg), 1);
}
int
notifyoff(char *msg)
{
return notifyseton(_p9strsig(msg), 0);
}
/*
* Initialization follows sigs table.
*/
static void
noteinit(void)
{
int i;
Sig *sig;
for(i=0; i<nelem(sigs); i++){
sig = &sigs[i];
/*
* If someone has already installed a handler,
* It's probably some ld preload nonsense,
* like pct (a SIGVTALRM-based profiler).
* Or maybe someone has already called notifyon/notifyoff.
* Leave it alone.
*/
if(handler(sig->sig) != SIG_DFL)
continue;
notifyseton(sig->sig, !(sig->flags&NoNotify));
}
}