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