9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

notify.c (4717B)


      1 /*
      2  * Signal handling for Plan 9 programs. 
      3  * We stubbornly use the strings from Plan 9 instead 
      4  * of the enumerated Unix constants.  
      5  * There are some weird translations.  In particular,
      6  * a "kill" note is the same as SIGTERM in Unix.
      7  * There is no equivalent note to Unix's SIGKILL, since
      8  * it's not a deliverable signal anyway.
      9  *
     10  * We do not handle SIGABRT or SIGSEGV, mainly because
     11  * the thread library queues its notes for later, and we want
     12  * to dump core with the state at time of delivery.
     13  *
     14  * We have to add some extra entry points to provide the
     15  * ability to tweak which signals are deliverable and which
     16  * are acted upon.  Notifydisable and notifyenable play with
     17  * the process signal mask.  Notifyignore enables the signal
     18  * but will not call notifyf when it comes in.  This is occasionally
     19  * useful.
     20  */
     21 
     22 #include <u.h>
     23 #include <signal.h>
     24 #define NOPLAN9DEFINES
     25 #include <libc.h>
     26 
     27 extern char *_p9sigstr(int, char*);
     28 extern int _p9strsig(char*);
     29 
     30 typedef struct Sig Sig;
     31 struct Sig
     32 {
     33 	int sig;			/* signal number */
     34 	int flags;
     35 };
     36 
     37 enum
     38 {
     39 	Restart = 1<<0,
     40 	Ignore = 1<<1,
     41 	NoNotify = 1<<2,
     42 };
     43 
     44 static Sig sigs[] = {
     45 	SIGHUP,		0,
     46 	SIGINT,		0,
     47 	SIGQUIT,		0,
     48 	SIGILL,		0,
     49 	SIGTRAP,		0,
     50 /*	SIGABRT, 		0, 	*/
     51 #ifdef SIGEMT
     52 	SIGEMT,		0,
     53 #endif
     54 	SIGFPE,		0,
     55 	SIGBUS,		0,
     56 /*	SIGSEGV, 		0, 	*/
     57 	SIGCHLD,		Restart|Ignore,
     58 	SIGSYS,		0,
     59 	SIGPIPE,		Ignore,
     60 	SIGALRM,		0,
     61 	SIGTERM,		0,
     62 	SIGTSTP,		Restart|Ignore|NoNotify,
     63 /*	SIGTTIN,		Restart|Ignore, */
     64 /*	SIGTTOU,		Restart|Ignore, */
     65 	SIGXCPU,		0,
     66 	SIGXFSZ,		0,
     67 	SIGVTALRM,	0,
     68 	SIGUSR1,		0,
     69 	SIGUSR2,		0,
     70 #ifdef SIGWINCH
     71 	SIGWINCH,	Restart|Ignore|NoNotify,
     72 #endif
     73 #ifdef SIGINFO
     74 	SIGINFO,		Restart|Ignore|NoNotify,
     75 #endif
     76 };
     77 
     78 static Sig*
     79 findsig(int s)
     80 {
     81 	int i;
     82 
     83 	for(i=0; i<nelem(sigs); i++)
     84 		if(sigs[i].sig == s)
     85 			return &sigs[i];
     86 	return nil;
     87 }
     88 
     89 /*
     90  * The thread library initializes _notejmpbuf to its own
     91  * routine which provides a per-pthread jump buffer.
     92  * If we're not using the thread library, we assume we are
     93  * single-threaded.
     94  */
     95 typedef struct Jmp Jmp;
     96 struct Jmp
     97 {
     98 	p9jmp_buf b;
     99 };
    100 
    101 static Jmp onejmp;
    102 
    103 static Jmp*
    104 getonejmp(void)
    105 {
    106 	return &onejmp;
    107 }
    108 
    109 Jmp *(*_notejmpbuf)(void) = getonejmp;
    110 static void noteinit(void);
    111 
    112 /*
    113  * Actual signal handler. 
    114  */
    115 
    116 static void (*notifyf)(void*, char*);	/* Plan 9 handler */
    117 
    118 static void
    119 signotify(int sig)
    120 {
    121 	char tmp[64];
    122 	Jmp *j;
    123 	Sig *s;
    124 
    125 	j = (*_notejmpbuf)();
    126 	switch(p9setjmp(j->b)){
    127 	case 0:
    128 		if(notifyf)
    129 			(*notifyf)(nil, _p9sigstr(sig, tmp));
    130 		/* fall through */
    131 	case 1:	/* noted(NDFLT) */
    132 		if(0)print("DEFAULT %d\n", sig);
    133 		s = findsig(sig);
    134 		if(s && (s->flags&Ignore))
    135 			return;
    136 		signal(sig, SIG_DFL);
    137 		raise(sig);
    138 		_exit(1);
    139 	case 2:	/* noted(NCONT) */
    140 		if(0)print("HANDLED %d\n", sig);
    141 		return;
    142 	}
    143 }
    144 
    145 static void
    146 signonotify(int sig)
    147 {
    148 	USED(sig);
    149 }
    150 
    151 int
    152 noted(int v)
    153 {
    154 	p9longjmp((*_notejmpbuf)()->b, v==NCONT ? 2 : 1);
    155 	abort();
    156 	return 0;
    157 }
    158 
    159 int
    160 notify(void (*f)(void*, char*))
    161 {
    162 	static int init;
    163 
    164 	notifyf = f;
    165 	if(!init){
    166 		init = 1;
    167 		noteinit();
    168 	}
    169 	return 0;
    170 }
    171 
    172 /*
    173  * Nonsense about enabling and disabling signals.
    174  */
    175 typedef void Sighandler(int);
    176 static Sighandler*
    177 handler(int s)
    178 {
    179 	struct sigaction sa;
    180 
    181 	sigaction(s, nil, &sa);
    182 	return sa.sa_handler;
    183 }
    184 
    185 static int
    186 notesetenable(int sig, int enabled)
    187 {
    188 	sigset_t mask, omask;
    189 
    190 	if(sig == 0)
    191 		return -1;
    192 
    193 	sigemptyset(&mask);
    194 	sigaddset(&mask, sig);
    195 	sigprocmask(enabled ? SIG_UNBLOCK : SIG_BLOCK, &mask, &omask);
    196 	return !sigismember(&omask, sig);	
    197 }
    198 
    199 int
    200 noteenable(char *msg)
    201 {
    202 	return notesetenable(_p9strsig(msg), 1);
    203 }
    204 
    205 int
    206 notedisable(char *msg)
    207 {
    208 	return notesetenable(_p9strsig(msg), 0);
    209 }
    210 
    211 static int
    212 notifyseton(int s, int on)
    213 {
    214 	Sig *sig;
    215 	struct sigaction sa, osa;
    216 
    217 	sig = findsig(s);
    218 	if(sig == nil)
    219 		return -1;
    220 	memset(&sa, 0, sizeof sa);
    221 	sa.sa_handler = on ? signotify : signonotify;
    222 	if(sig->flags&Restart)
    223 		sa.sa_flags |= SA_RESTART;
    224 
    225 	/*
    226 	 * We can't allow signals within signals because there's
    227 	 * only one jump buffer.
    228 	 */
    229 	sigfillset(&sa.sa_mask);
    230 
    231 	/*
    232 	 * Install handler.
    233 	 */
    234 	sigaction(sig->sig, &sa, &osa);
    235 	return osa.sa_handler == signotify;
    236 }
    237 
    238 int
    239 notifyon(char *msg)
    240 {
    241 	return notifyseton(_p9strsig(msg), 1);
    242 }
    243 
    244 int
    245 notifyoff(char *msg)
    246 {
    247 	return notifyseton(_p9strsig(msg), 0);
    248 }
    249 
    250 /*
    251  * Initialization follows sigs table.
    252  */
    253 static void
    254 noteinit(void)
    255 {
    256 	int i;
    257 	Sig *sig;
    258 
    259 	for(i=0; i<nelem(sigs); i++){
    260 		sig = &sigs[i];
    261 		/*
    262 		 * If someone has already installed a handler,
    263 		 * It's probably some ld preload nonsense,
    264 		 * like pct (a SIGVTALRM-based profiler).
    265 		 * Or maybe someone has already called notifyon/notifyoff.
    266 		 * Leave it alone.
    267 		 */
    268 		if(handler(sig->sig) != SIG_DFL)
    269 			continue;
    270 		notifyseton(sig->sig, !(sig->flags&NoNotify));
    271 	}
    272 }
    273