1 /* $NetBSD: trap.c,v 1.41 2017/07/05 19:47:11 kre Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kenneth Almquist. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; 39 #else 40 __RCSID("$NetBSD: trap.c,v 1.41 2017/07/05 19:47:11 kre Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 #include <signal.h> 45 #include <unistd.h> 46 #include <stdlib.h> 47 #include <stdio.h> 48 49 #include "shell.h" 50 #include "main.h" 51 #include "nodes.h" /* for other headers */ 52 #include "eval.h" 53 #include "jobs.h" 54 #include "show.h" 55 #include "options.h" 56 #include "builtins.h" 57 #include "syntax.h" 58 #include "output.h" 59 #include "memalloc.h" 60 #include "error.h" 61 #include "trap.h" 62 #include "mystring.h" 63 #include "var.h" 64 65 66 /* 67 * Sigmode records the current value of the signal handlers for the various 68 * modes. A value of zero means that the current handler is not known. 69 * S_HARD_IGN indicates that the signal was ignored on entry to the shell, 70 */ 71 72 #define S_DFL 1 /* default signal handling (SIG_DFL) */ 73 #define S_CATCH 2 /* signal is caught */ 74 #define S_IGN 3 /* signal is ignored (SIG_IGN) */ 75 #define S_HARD_IGN 4 /* signal is ignored permenantly */ 76 #define S_RESET 5 /* temporary - to reset a hard ignored sig */ 77 78 79 char *trap[NSIG]; /* trap handler commands */ 80 MKINIT char sigmode[NSIG]; /* current value of signal */ 81 static volatile char gotsig[NSIG];/* indicates specified signal received */ 82 volatile int pendingsigs; /* indicates some signal received */ 83 84 static int getsigaction(int, sig_t *); 85 STATIC const char *trap_signame(int); 86 87 /* 88 * return the signal number described by `p' (as a number or a name) 89 * or -1 if it isn't one 90 */ 91 92 static int 93 signame_to_signum(const char *p) 94 { 95 int i; 96 97 if (is_number(p)) 98 return number(p); 99 100 if (strcasecmp(p, "exit") == 0 ) 101 return 0; 102 103 if (strncasecmp(p, "sig", 3) == 0) 104 p += 3; 105 106 for (i = 0; i < NSIG; ++i) 107 if (strcasecmp (p, sys_signame[i]) == 0) 108 return i; 109 return -1; 110 } 111 112 /* 113 * return the name of a signal used by the "trap" command 114 */ 115 STATIC const char * 116 trap_signame(int signo) 117 { 118 static char nbuf[12]; 119 const char *p = NULL; 120 121 if (signo == 0) 122 return "EXIT"; 123 if (signo > 0 && signo < NSIG) 124 p = sys_signame[signo]; 125 if (p != NULL) 126 return p; 127 (void)snprintf(nbuf, sizeof nbuf, "%d", signo); 128 return nbuf; 129 } 130 131 /* 132 * Print a list of valid signal names 133 */ 134 static void 135 printsignals(void) 136 { 137 int n; 138 139 out1str("EXIT "); 140 141 for (n = 1; n < NSIG; n++) { 142 out1fmt("%s", trap_signame(n)); 143 if ((n == NSIG/2) || n == (NSIG - 1)) 144 out1str("\n"); 145 else 146 out1c(' '); 147 } 148 } 149 150 /* 151 * The trap builtin. 152 */ 153 154 int 155 trapcmd(int argc, char **argv) 156 { 157 char *action; 158 char **ap; 159 int signo; 160 int errs = 0; 161 int printonly = 0; 162 163 ap = argv + 1; 164 165 if (argc == 2 && strcmp(*ap, "-l") == 0) { 166 printsignals(); 167 return 0; 168 } 169 if (argc == 2 && strcmp(*ap, "-") == 0) { 170 for (signo = 0; signo < NSIG; signo++) { 171 if (trap[signo] == NULL) 172 continue; 173 INTOFF; 174 ckfree(trap[signo]); 175 trap[signo] = NULL; 176 if (signo != 0) 177 setsignal(signo, 0); 178 INTON; 179 } 180 return 0; 181 } 182 if (argc >= 2 && strcmp(*ap, "-p") == 0) { 183 printonly = 1; 184 ap++; 185 argc--; 186 } 187 188 if (argc > 1 && strcmp(*ap, "--") == 0) { 189 argc--; 190 ap++; 191 } 192 193 if (argc <= 1) { 194 int count; 195 196 if (printonly) { 197 for (count = 0, signo = 0 ; signo < NSIG ; signo++) 198 if (trap[signo] == NULL) { 199 if (count == 0) 200 out1str("trap -- -"); 201 out1fmt(" %s", trap_signame(signo)); 202 /* oh! unlucky 13 */ 203 if (++count >= 13) { 204 out1str("\n"); 205 count = 0; 206 } 207 } 208 if (count) 209 out1str("\n"); 210 } 211 212 for (count = 0, signo = 0 ; signo < NSIG ; signo++) 213 if (trap[signo] != NULL && trap[signo][0] == '\0') { 214 if (count == 0) 215 out1str("trap -- ''"); 216 out1fmt(" %s", trap_signame(signo)); 217 /* 218 * the prefix is 10 bytes, with 4 byte 219 * signal names (common) we have room in 220 * the 70 bytes left on a normal line for 221 * 70/(4+1) signals, that's 14, but to 222 * allow for the occasional longer sig name 223 * we output one less... 224 */ 225 if (++count >= 13) { 226 out1str("\n"); 227 count = 0; 228 } 229 } 230 if (count) 231 out1str("\n"); 232 233 for (signo = 0 ; signo < NSIG ; signo++) 234 if (trap[signo] != NULL && trap[signo][0] != '\0') { 235 out1str("trap -- "); 236 print_quoted(trap[signo]); 237 out1fmt(" %s\n", trap_signame(signo)); 238 } 239 240 return 0; 241 } 242 243 action = NULL; 244 245 if (!printonly && !is_number(*ap)) { 246 if ((*ap)[0] == '-' && (*ap)[1] == '\0') 247 ap++; /* reset to default */ 248 else 249 action = *ap++; /* can be '' for "ignore" */ 250 argc--; 251 } 252 253 if (argc < 2) { /* there must be at least 1 condition */ 254 out2str("Usage: trap [-l]\n" 255 " trap -p [condition ...]\n" 256 " trap action condition ...\n" 257 " trap N condition ...\n"); 258 return 2; 259 } 260 261 262 while (*ap) { 263 signo = signame_to_signum(*ap); 264 265 if (signo < 0 || signo >= NSIG) { 266 /* This is not a fatal error, so sayeth posix */ 267 outfmt(out2, "trap: '%s' bad condition\n", *ap); 268 errs = 1; 269 ap++; 270 continue; 271 } 272 ap++; 273 274 if (printonly) { 275 out1str("trap -- "); 276 if (trap[signo] == NULL) 277 out1str("-"); 278 else 279 print_quoted(trap[signo]); 280 out1fmt(" %s\n", trap_signame(signo)); 281 continue; 282 } 283 284 INTOFF; 285 if (action) 286 action = savestr(action); 287 288 if (trap[signo]) 289 ckfree(trap[signo]); 290 291 trap[signo] = action; 292 293 if (signo != 0) 294 setsignal(signo, 0); 295 INTON; 296 } 297 return errs; 298 } 299 300 301 302 /* 303 * Clear traps on a fork or vfork. 304 * Takes one arg vfork, to tell it to not be destructive of 305 * the parents variables. 306 */ 307 308 void 309 clear_traps(int vforked) 310 { 311 char **tp; 312 313 for (tp = trap ; tp < &trap[NSIG] ; tp++) { 314 if (*tp && **tp) { /* trap not NULL or SIG_IGN */ 315 INTOFF; 316 if (!vforked) { 317 ckfree(*tp); 318 *tp = NULL; 319 } 320 if (tp != &trap[0]) 321 setsignal(tp - trap, vforked); 322 INTON; 323 } 324 } 325 } 326 327 328 329 /* 330 * Set the signal handler for the specified signal. The routine figures 331 * out what it should be set to. 332 */ 333 334 sig_t 335 setsignal(int signo, int vforked) 336 { 337 int action; 338 sig_t sigact = SIG_DFL, sig; 339 char *t, tsig; 340 341 if ((t = trap[signo]) == NULL) 342 action = S_DFL; 343 else if (*t != '\0') 344 action = S_CATCH; 345 else 346 action = S_IGN; 347 if (rootshell && !vforked && action == S_DFL) { 348 switch (signo) { 349 case SIGINT: 350 if (iflag || minusc || sflag == 0) 351 action = S_CATCH; 352 break; 353 case SIGQUIT: 354 #ifdef DEBUG 355 if (debug) 356 break; 357 #endif 358 /* FALLTHROUGH */ 359 case SIGTERM: 360 if (iflag) 361 action = S_IGN; 362 break; 363 #if JOBS 364 case SIGTSTP: 365 case SIGTTOU: 366 if (mflag) 367 action = S_IGN; 368 break; 369 #endif 370 } 371 } 372 373 t = &sigmode[signo - 1]; 374 tsig = *t; 375 if (tsig == 0) { 376 /* 377 * current setting unknown 378 */ 379 if (!getsigaction(signo, &sigact)) { 380 /* 381 * Pretend it worked; maybe we should give a warning 382 * here, but other shells don't. We don't alter 383 * sigmode, so that we retry every time. 384 */ 385 return 0; 386 } 387 if (sigact == SIG_IGN) { 388 /* 389 * POSIX 3.14.13 states that non-interactive shells 390 * should ignore trap commands for signals that were 391 * ignored upon entry, and leaves the behavior 392 * unspecified for interactive shells. On interactive 393 * shells, or if job control is on, and we have a job 394 * control related signal, we allow the trap to work. 395 * 396 * This change allows us to be POSIX compliant, and 397 * at the same time override the default behavior if 398 * we need to by setting the interactive flag. 399 */ 400 if ((mflag && (signo == SIGTSTP || 401 signo == SIGTTIN || signo == SIGTTOU)) || iflag) { 402 tsig = S_IGN; 403 } else 404 tsig = S_HARD_IGN; 405 } else { 406 tsig = S_RESET; /* force to be set */ 407 } 408 } 409 if (tsig == S_HARD_IGN || tsig == action) 410 return 0; 411 switch (action) { 412 case S_DFL: sigact = SIG_DFL; break; 413 case S_CATCH: sigact = onsig; break; 414 case S_IGN: sigact = SIG_IGN; break; 415 } 416 sig = signal(signo, sigact); 417 if (sig != SIG_ERR) { 418 sigset_t ss; 419 if (!vforked) 420 *t = action; 421 if (action == S_CATCH) 422 (void)siginterrupt(signo, 1); 423 /* 424 * If our parent accidentally blocked signals for 425 * us make sure we unblock them 426 */ 427 (void)sigemptyset(&ss); 428 (void)sigaddset(&ss, signo); 429 (void)sigprocmask(SIG_UNBLOCK, &ss, NULL); 430 } 431 return sig; 432 } 433 434 /* 435 * Return the current setting for sig w/o changing it. 436 */ 437 static int 438 getsigaction(int signo, sig_t *sigact) 439 { 440 struct sigaction sa; 441 442 if (sigaction(signo, (struct sigaction *)0, &sa) == -1) 443 return 0; 444 *sigact = (sig_t) sa.sa_handler; 445 return 1; 446 } 447 448 /* 449 * Ignore a signal. 450 */ 451 452 void 453 ignoresig(int signo, int vforked) 454 { 455 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { 456 signal(signo, SIG_IGN); 457 } 458 if (!vforked) 459 sigmode[signo - 1] = S_HARD_IGN; 460 } 461 462 463 #ifdef mkinit 464 INCLUDE <signal.h> 465 INCLUDE "trap.h" 466 467 SHELLPROC { 468 char *sm; 469 470 clear_traps(0); 471 for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { 472 if (*sm == S_IGN) 473 *sm = S_HARD_IGN; 474 } 475 } 476 #endif 477 478 479 480 /* 481 * Signal handler. 482 */ 483 484 void 485 onsig(int signo) 486 { 487 CTRACE(DBG_SIG, ("Signal %d, had: pending %d, gotsig[%d]=%d\n", 488 signo, pendingsigs, signo, gotsig[signo])); 489 490 signal(signo, onsig); 491 if (signo == SIGINT && trap[SIGINT] == NULL) { 492 onint(); 493 return; 494 } 495 gotsig[signo] = 1; 496 pendingsigs++; 497 } 498 499 500 501 /* 502 * Called to execute a trap. Perhaps we should avoid entering new trap 503 * handlers while we are executing a trap handler. 504 */ 505 506 void 507 dotrap(void) 508 { 509 int i; 510 int savestatus; 511 char *tr; 512 513 for (;;) { 514 for (i = 1 ; ; i++) { 515 if (i >= NSIG) { 516 pendingsigs = 0; 517 return; 518 } 519 if (gotsig[i]) 520 break; 521 } 522 gotsig[i] = 0; 523 savestatus=exitstatus; 524 CTRACE(DBG_TRAP|DBG_SIG, ("dotrap %d: \"%s\"\n", i, 525 trap[i] ? trap[i] : "-NULL-")); 526 tr = savestr(trap[i]); /* trap code may free trap[i] */ 527 evalstring(tr, 0); 528 ckfree(tr); 529 exitstatus=savestatus; 530 } 531 } 532 533 int 534 lastsig(void) 535 { 536 int i; 537 538 for (i = NSIG; --i > 0; ) 539 if (gotsig[i]) 540 return i; 541 return SIGINT; /* XXX */ 542 } 543 544 /* 545 * Controls whether the shell is interactive or not. 546 */ 547 548 549 void 550 setinteractive(int on) 551 { 552 static int is_interactive; 553 554 if (on == is_interactive) 555 return; 556 setsignal(SIGINT, 0); 557 setsignal(SIGQUIT, 0); 558 setsignal(SIGTERM, 0); 559 is_interactive = on; 560 } 561 562 563 564 /* 565 * Called to exit the shell. 566 */ 567 568 void 569 exitshell(int status) 570 { 571 struct jmploc loc1, loc2; 572 char *p; 573 574 CTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP, 575 ("pid %d, exitshell(%d)\n", getpid(), status)); 576 577 if (setjmp(loc1.loc)) { 578 goto l1; 579 } 580 if (setjmp(loc2.loc)) { 581 goto l2; 582 } 583 handler = &loc1; 584 if ((p = trap[0]) != NULL && *p != '\0') { 585 trap[0] = NULL; 586 VTRACE(DBG_TRAP, ("exit trap: \"%s\"\n", p)); 587 evalstring(p, 0); 588 } 589 l1: handler = &loc2; /* probably unnecessary */ 590 flushall(); 591 #if JOBS 592 setjobctl(0); 593 #endif 594 l2: _exit(status); 595 /* NOTREACHED */ 596 } 597