aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-10-23 02:59:33 +0000
committerrsc <devnull@localhost>2004-10-23 02:59:33 +0000
commit9acaad45703b1be15147b461c6edf90b765414cd (patch)
treeec7a1f07df3fa253c977bbd8594dc8051895722d
parentf6c9a57869036b93e912cb333057d6fc706ea4df (diff)
downloadplan9port-9acaad45703b1be15147b461c6edf90b765414cd.tar.gz
plan9port-9acaad45703b1be15147b461c6edf90b765414cd.tar.bz2
plan9port-9acaad45703b1be15147b461c6edf90b765414cd.zip
clean up signal handling
-rw-r--r--src/lib9/notify.c283
1 files changed, 194 insertions, 89 deletions
diff --git a/src/lib9/notify.c b/src/lib9/notify.c
index da075d71..2b95e099 100644
--- a/src/lib9/notify.c
+++ b/src/lib9/notify.c
@@ -1,46 +1,91 @@
+/*
+ * 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 so that
+ * stack traces show the original source of the signal instead
+ * of notifysigf.
+ *
+ * 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*);
-static struct {
- int sig;
- int restart;
- int dumb;
-} sigs[] = {
- SIGHUP, 0, 0,
- SIGINT, 0, 0,
- SIGQUIT, 0, 0,
- SIGILL, 0, 0,
- SIGTRAP, 0, 0,
-/* SIGABRT, */
+typedef struct Sig Sig;
+struct Sig
+{
+ int sig; /* signal number */
+ int restart; /* do we restart the system call after this signal is handled? */
+ int enabled; /* is this signal enabled (not masked)? */
+ int notified; /* do we call the notify function for this signal? */
+};
+
+/* initial settings; for current status, ask the kernel */
+static Sig sigs[] = {
+ SIGHUP, 0, 1, 1,
+ SIGINT, 0, 1, 1,
+ SIGQUIT, 0, 1, 1,
+ SIGILL, 0, 1, 1,
+ SIGTRAP, 0, 1, 1,
+/* SIGABRT, 0, 1, 1, */
#ifdef SIGEMT
- SIGEMT, 0, 0,
+ SIGEMT, 0, 1, 1,
#endif
- SIGFPE, 0, 0,
- SIGBUS, 0, 0,
-/* SIGSEGV, */
- SIGCHLD, 1, 1,
- SIGSYS, 0, 0,
- SIGPIPE, 0, 1,
- SIGALRM, 0, 0,
- SIGTERM, 0, 0,
- SIGTSTP, 1, 1,
- SIGTTIN, 1, 1,
- SIGTTOU, 1, 1,
- SIGXCPU, 0, 0,
- SIGXFSZ, 0, 0,
- SIGVTALRM, 0, 0,
- SIGUSR1, 0, 0,
- SIGUSR2, 0, 0,
- SIGWINCH, 1, 1,
+ SIGFPE, 0, 1, 1,
+ SIGBUS, 0, 1, 1,
+/* SIGSEGV, 0, 1, 1, */
+ SIGCHLD, 1, 0, 1,
+ SIGSYS, 0, 1, 1,
+ SIGPIPE, 0, 0, 1,
+ SIGALRM, 0, 1, 1,
+ SIGTERM, 0, 1, 1,
+ SIGTSTP, 1, 0, 1,
+ SIGTTIN, 1, 0, 1,
+ SIGTTOU, 1, 0, 1,
+ SIGXCPU, 0, 1, 1,
+ SIGXFSZ, 0, 1, 1,
+ SIGVTALRM, 0, 1, 1,
+ SIGUSR1, 0, 1, 1,
+ SIGUSR2, 0, 1, 1,
+ SIGWINCH, 1, 0, 1,
#ifdef SIGINFO
- SIGINFO, 0, 0,
+ SIGINFO, 1, 1, 1,
#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
{
@@ -56,33 +101,42 @@ getonejmp(void)
}
Jmp *(*_notejmpbuf)(void) = getonejmp;
-static void (*notifyf)(void*, char*);
-static int alldumb;
+static void noteinit(void);
-static void
-nop(int sig)
-{
- USED(sig);
-}
+/*
+ * Actual signal handler.
+ */
+
+static void (*notifyf)(void*, char*); /* Plan 9 handler */
static void
-notifysigf(int sig)
+signotify(int sig)
{
int v;
char tmp[64];
Jmp *j;
j = (*_notejmpbuf)();
- v = p9setjmp(j->b);
- if(v == 0 && notifyf)
- (*notifyf)(nil, _p9sigstr(sig, tmp));
- else if(v == 2){
+ 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);
+ signal(sig, SIG_DFL);
+ raise(sig);
+ _exit(1);
+ case 2: /* noted(NCONT) */
if(0)print("HANDLED %d\n", sig);
return;
}
- if(0)print("DEFAULT %d\n", sig);
- signal(sig, SIG_DFL);
- raise(sig);
+}
+
+static void
+signonotify(int sig)
+{
+ USED(sig);
}
int
@@ -93,71 +147,122 @@ noted(int v)
return 0;
}
+int
+notify(void (*f)(void*, char*))
+{
+ int i;
+ static int init;
+
+ notifyf = f;
+ if(!init){
+ init = 1;
+ noteinit();
+ }
+ return 0;
+}
+
+/*
+ * Nonsense about enabling and disabling signals.
+ */
+static void(*)(int)
+handler(int s)
+{
+ struct sigaction sa;
+
+ sigaction(s, nil, &sa);
+ return sa.sa_handler;
+}
+
static void
-handlesig(int s, int r, int skip)
+notifysetenable(int sig, int enabled)
{
- struct sigaction sa, osa;
+ sigset_t mask;
- /*
- * If someone has already installed a handler,
- * It's probably some ld preload nonsense,
- * like pct (a SIGVTALRM-based profiler).
- * Leave it alone.
- */
- sigaction(s, nil, &osa);
- if(osa.sa_handler != SIG_DFL && osa.sa_handler != notifysigf && osa.sa_handler != nop)
+ if(sig == 0)
return;
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigprocmask(enabled ? SIG_UNBLOCK : SIG_BLOCK, &mask, nil);
+}
+
+void
+notifyenable(char *msg)
+{
+ notifyenablex(_p9strsig(msg), 1);
+}
+
+void
+notifydisable(char *msg)
+{
+ notifyenablex(_p9strsig(msg), 0);
+}
+
+static void
+notifyseton(int s, int on)
+{
+ Sig *sig;
+ struct sigaction sa;
+
+ sig = findsig(s);
+ if(sig == nil)
+ return;
+ notifyenable(msg);
memset(&sa, 0, sizeof sa);
- if(skip)
- sa.sa_handler = nop;
- else if(notifyf == 0)
- sa.sa_handler = SIG_DFL;
- else
- sa.sa_handler = notifysigf;
+ sa.sa_handler = on ? signotify : signonotify;
+ if(sig->restart)
+ sa.sa_flags |= SA_RESTART;
/*
- * We assume that one jump buffer per thread
- * is okay, which means that we can't deal with
- * signal handlers called during signal handlers.
+ * We can't allow signals within signals because there's
+ * only one jump buffer.
*/
sigfillset(&sa.sa_mask);
- if(r)
- sa.sa_flags |= SA_RESTART;
- else
- sa.sa_flags &= ~SA_RESTART;
- sigaction(s, &sa, nil);
+
+ /*
+ * Install handler.
+ */
+ sigaction(sig->sig, &sa, nil);
}
-int
-notify(void (*f)(void*, char*))
+void
+notifyon(char *msg)
{
- int i;
-
- notifyf = f;
- for(i=0; i<nelem(sigs); i++)
- handlesig(sigs[i].sig, sigs[i].restart, sigs[i].dumb && !alldumb);
- return 0;
+ notifyseton(_p9strsig(msg), 1);
}
void
-notifyall(int all)
+notifyoff(char *msg)
{
- int i;
-
- alldumb = all;
- for(i=0; i<nelem(sigs); i++)
- if(sigs[i].dumb)
- handlesig(sigs[i].sig, sigs[i].restart, !all);
+ notifysetoff(_p9strsig(msg), 0);
}
-void
-notifyatsig(int sig, int use)
+/*
+ * Initialization follows sigs table.
+ */
+static void
+noteinit(void)
{
int i;
+ Sig *sig;
- for(i=0; i<nelem(sigs); i++)
- if(sigs[i].sig == sig)
- handlesig(sigs[i].sig, sigs[i].restart, 0);
+ 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).
+ * Leave it alone.
+ */
+ if(handler(sig->sig) != SIG_DFL)
+ continue;
+ /*
+ * Should we only disable and not enable signals?
+ * (I.e. if parent has disabled for us, should we still enable?)
+ * Right now we always initialize to the state we want.
+ */
+ notifysetenable(sig->sig, sig->enabled);
+ notifyseton(sig->sig, sig->notified);
+ }
}