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