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