| /* |
| * 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)); |
| } |
| } |
| |