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