1 /* $NetBSD: trap.c,v 1.5 2003/06/23 11:39:05 agc Exp $ */ 2 3 /* 4 * signal handling 5 */ 6 #include <sys/cdefs.h> 7 8 #ifndef lint 9 __RCSID("$NetBSD: trap.c,v 1.5 2003/06/23 11:39:05 agc Exp $"); 10 #endif 11 12 13 /* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */ 14 #define FROM_TRAP_C 15 #include "sh.h" 16 17 /* Table is indexed by signal number 18 * 19 * The script siglist.sh generates siglist.out, which is a sorted, complete 20 * list of signals 21 */ 22 Trap sigtraps[SIGNALS+1] = { 23 { SIGEXIT_, "EXIT", "Signal 0" }, 24 #include "siglist.out" /* generated by siglist.sh */ 25 { SIGERR_, "ERR", "Error handler" }, 26 }; 27 28 static struct sigaction Sigact_ign, Sigact_trap; 29 30 void 31 inittraps() 32 { 33 #ifdef HAVE_SYS_SIGLIST 34 # ifndef SYS_SIGLIST_DECLARED 35 extern char *sys_siglist[]; 36 # endif 37 int i; 38 39 /* Use system description, if available, for unknown signals... */ 40 for (i = 0; i < NSIG; i++) 41 if (!sigtraps[i].name && sys_siglist[i] && sys_siglist[i][0]) 42 sigtraps[i].mess = sys_siglist[i]; 43 #endif /* HAVE_SYS_SIGLIST */ 44 45 sigemptyset(&Sigact_ign.sa_mask); 46 Sigact_ign.sa_flags = KSH_SA_FLAGS; 47 Sigact_ign.sa_handler = SIG_IGN; 48 Sigact_trap = Sigact_ign; 49 Sigact_trap.sa_handler = trapsig; 50 51 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR; 52 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR; 53 sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */ 54 sigtraps[SIGHUP].flags |= TF_FATAL; 55 sigtraps[SIGCHLD].flags |= TF_SHELL_USES; 56 57 /* these are always caught so we can clean up any temproary files. */ 58 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG); 59 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG); 60 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG); 61 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG); 62 } 63 64 #ifdef KSH 65 static RETSIGTYPE alarm_catcher ARGS((int sig)); 66 67 void 68 alarm_init() 69 { 70 sigtraps[SIGALRM].flags |= TF_SHELL_USES; 71 setsig(&sigtraps[SIGALRM], alarm_catcher, 72 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); 73 } 74 75 static RETSIGTYPE 76 alarm_catcher(sig) 77 int sig; 78 { 79 if (ksh_tmout_state == TMOUT_READING) { 80 int left = alarm(0); 81 82 if (left == 0) { 83 ksh_tmout_state = TMOUT_LEAVING; 84 intrsig = 1; 85 } else 86 alarm(left); 87 } 88 return RETSIGVAL; 89 } 90 #endif /* KSH */ 91 92 Trap * 93 gettrap(name, igncase) 94 const char *name; 95 int igncase; 96 { 97 int i; 98 register Trap *p; 99 100 if (digit(*name)) { 101 int n; 102 103 if (getn(name, &n) && 0 <= n && n < SIGNALS) 104 return &sigtraps[n]; 105 return NULL; 106 } 107 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) 108 if (p->name && (igncase ? strcasecmp(p->name, name) == 0 109 : strcmp(p->name, name) == 0)) 110 return p; 111 return NULL; 112 } 113 114 /* 115 * trap signal handler 116 */ 117 RETSIGTYPE 118 trapsig(i) 119 int i; 120 { 121 Trap *p = &sigtraps[i]; 122 123 trap = p->set = 1; 124 if (p->flags & TF_DFL_INTR) 125 intrsig = 1; 126 if ((p->flags & TF_FATAL) && !p->trap) { 127 fatal_trap = 1; 128 intrsig = 1; 129 } 130 if (p->shtrap) 131 (*p->shtrap)(i); 132 #ifdef V7_SIGNALS 133 if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */ 134 sigaction(i, &Sigact_trap, (struct sigaction *) 0); 135 #endif /* V7_SIGNALS */ 136 return RETSIGVAL; 137 } 138 139 /* called when we want to allow the user to ^C out of something - won't 140 * work if user has trapped SIGINT. 141 */ 142 void 143 intrcheck() 144 { 145 if (intrsig) 146 runtraps(TF_DFL_INTR|TF_FATAL); 147 } 148 149 /* called after EINTR to check if a signal with normally causes process 150 * termination has been received. 151 */ 152 int 153 fatal_trap_check() 154 { 155 int i; 156 Trap *p; 157 158 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */ 159 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) 160 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) 161 /* return value is used as an exit code */ 162 return 128 + p->signal; 163 return 0; 164 } 165 166 /* Returns the signal number of any pending traps: ie, a signal which has 167 * occurred for which a trap has been set or for which the TF_DFL_INTR flag 168 * is set. 169 */ 170 int 171 trap_pending() 172 { 173 int i; 174 Trap *p; 175 176 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) 177 if (p->set && ((p->trap && p->trap[0]) 178 || ((p->flags & (TF_DFL_INTR|TF_FATAL)) 179 && !p->trap))) 180 return p->signal; 181 return 0; 182 } 183 184 /* 185 * run any pending traps. If intr is set, only run traps that 186 * can interrupt commands. 187 */ 188 void 189 runtraps(flag) 190 int flag; 191 { 192 int i; 193 register Trap *p; 194 195 #ifdef KSH 196 if (ksh_tmout_state == TMOUT_LEAVING) { 197 ksh_tmout_state = TMOUT_EXECUTING; 198 warningf(FALSE, "timed out waiting for input"); 199 unwind(LEXIT); 200 } else 201 /* XXX: this means the alarm will have no effect if a trap 202 * is caught after the alarm() was started...not good. 203 */ 204 ksh_tmout_state = TMOUT_EXECUTING; 205 #endif /* KSH */ 206 if (!flag) 207 trap = 0; 208 if (flag & TF_DFL_INTR) 209 intrsig = 0; 210 if (flag & TF_FATAL) 211 fatal_trap = 0; 212 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) 213 if (p->set && (!flag 214 || ((p->flags & flag) && p->trap == (char *) 0))) 215 runtrap(p); 216 } 217 218 void 219 runtrap(p) 220 Trap *p; 221 { 222 int i = p->signal; 223 char *trapstr = p->trap; 224 int oexstat; 225 int UNINITIALIZED(old_changed); 226 227 p->set = 0; 228 if (trapstr == (char *) 0) { /* SIG_DFL */ 229 if (p->flags & TF_FATAL) { 230 /* eg, SIGHUP */ 231 exstat = 128 + i; 232 unwind(LLEAVE); 233 } 234 if (p->flags & TF_DFL_INTR) { 235 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */ 236 exstat = 128 + i; 237 unwind(LINTR); 238 } 239 return; 240 } 241 if (trapstr[0] == '\0') /* SIG_IGN */ 242 return; 243 if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */ 244 old_changed = p->flags & TF_CHANGED; 245 p->flags &= ~TF_CHANGED; 246 p->trap = (char *) 0; 247 } 248 oexstat = exstat; 249 /* Note: trapstr is fully parsed before anything is executed, thus 250 * no problem with afree(p->trap) in settrap() while still in use. 251 */ 252 command(trapstr); 253 exstat = oexstat; 254 if (i == SIGEXIT_ || i == SIGERR_) { 255 if (p->flags & TF_CHANGED) 256 /* don't clear TF_CHANGED */ 257 afree(trapstr, APERM); 258 else 259 p->trap = trapstr; 260 p->flags |= old_changed; 261 } 262 } 263 264 /* clear pending traps and reset user's trap handlers; used after fork(2) */ 265 void 266 cleartraps() 267 { 268 int i; 269 Trap *p; 270 271 trap = 0; 272 intrsig = 0; 273 fatal_trap = 0; 274 for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) { 275 p->set = 0; 276 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) 277 settrap(p, (char *) 0); 278 } 279 } 280 281 /* restore signals just before an exec(2) */ 282 void 283 restoresigs() 284 { 285 int i; 286 Trap *p; 287 288 for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) 289 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL)) 290 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL, 291 SS_RESTORE_CURR|SS_FORCE); 292 } 293 294 void 295 settrap(p, s) 296 Trap *p; 297 char *s; 298 { 299 handler_t f; 300 301 if (p->trap) 302 afree(p->trap, APERM); 303 p->trap = str_save(s, APERM); /* handles s == 0 */ 304 p->flags |= TF_CHANGED; 305 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN; 306 307 p->flags |= TF_USER_SET; 308 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL) 309 f = trapsig; 310 else if (p->flags & TF_SHELL_USES) { 311 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) { 312 /* do what user wants at exec time */ 313 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 314 if (f == SIG_IGN) 315 p->flags |= TF_EXEC_IGN; 316 else 317 p->flags |= TF_EXEC_DFL; 318 } 319 /* assumes handler already set to what shell wants it 320 * (normally trapsig, but could be j_sigchld() or SIG_IGN) 321 */ 322 return; 323 } 324 325 /* todo: should we let user know signal is ignored? how? */ 326 setsig(p, f, SS_RESTORE_CURR|SS_USER); 327 } 328 329 /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't 330 * kill shell (unless user catches it and exits) 331 */ 332 int 333 block_pipe() 334 { 335 int restore_dfl = 0; 336 Trap *p = &sigtraps[SIGPIPE]; 337 338 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 339 setsig(p, SIG_IGN, SS_RESTORE_CURR); 340 if (p->flags & TF_ORIG_DFL) 341 restore_dfl = 1; 342 } else if (p->cursig == SIG_DFL) { 343 setsig(p, SIG_IGN, SS_RESTORE_CURR); 344 restore_dfl = 1; /* restore to SIG_DFL */ 345 } 346 return restore_dfl; 347 } 348 349 /* Called by c_print() to undo whatever block_pipe() did */ 350 void 351 restore_pipe(restore_dfl) 352 int restore_dfl; 353 { 354 if (restore_dfl) 355 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); 356 } 357 358 /* Set action for a signal. Action may not be set if original 359 * action was SIG_IGN, depending on the value of flags and 360 * FTALKING. 361 */ 362 int 363 setsig(p, f, flags) 364 Trap *p; 365 handler_t f; 366 int flags; 367 { 368 struct sigaction sigact; 369 370 if (p->signal == SIGEXIT_ || p->signal == SIGERR_) 371 return 1; 372 373 /* First time setting this signal? If so, get and note the current 374 * setting. 375 */ 376 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 377 sigaction(p->signal, &Sigact_ign, &sigact); 378 p->flags |= sigact.sa_handler == SIG_IGN ? 379 TF_ORIG_IGN : TF_ORIG_DFL; 380 p->cursig = SIG_IGN; 381 } 382 383 /* Generally, an ignored signal stays ignored, except if 384 * - the user of an interactive shell wants to change it 385 * - the shell wants for force a change 386 */ 387 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) 388 && (!(flags & SS_USER) || !Flag(FTALKING))) 389 return 0; 390 391 setexecsig(p, flags & SS_RESTORE_MASK); 392 393 /* This is here 'cause there should be a way of clearing shtraps, but 394 * don't know if this is a sane way of doing it. At the moment, 395 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH). 396 */ 397 if (!(flags & SS_USER)) 398 p->shtrap = (handler_t) 0; 399 if (flags & SS_SHTRAP) { 400 p->shtrap = f; 401 f = trapsig; 402 } 403 404 if (p->cursig != f) { 405 p->cursig = f; 406 sigemptyset(&sigact.sa_mask); 407 sigact.sa_flags = KSH_SA_FLAGS; 408 sigact.sa_handler = f; 409 sigaction(p->signal, &sigact, (struct sigaction *) 0); 410 } 411 412 return 1; 413 } 414 415 /* control what signal is set to before an exec() */ 416 void 417 setexecsig(p, restore) 418 Trap *p; 419 int restore; 420 { 421 /* XXX debugging */ 422 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) 423 internal_errorf(1, "setexecsig: unset signal %d(%s)", 424 p->signal, p->name); 425 426 /* restore original value for exec'd kids */ 427 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 428 switch (restore & SS_RESTORE_MASK) { 429 case SS_RESTORE_CURR: /* leave things as they currently are */ 430 break; 431 case SS_RESTORE_ORIG: 432 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL; 433 break; 434 case SS_RESTORE_DFL: 435 p->flags |= TF_EXEC_DFL; 436 break; 437 case SS_RESTORE_IGN: 438 p->flags |= TF_EXEC_IGN; 439 break; 440 } 441 } 442