1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; 36 #endif 37 #endif /* not lint */ 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #include <signal.h> 42 #include <unistd.h> 43 #include <stdlib.h> 44 45 #include "shell.h" 46 #include "main.h" 47 #include "nodes.h" /* for other headers */ 48 #include "eval.h" 49 #include "jobs.h" 50 #include "show.h" 51 #include "options.h" 52 #include "syntax.h" 53 #include "output.h" 54 #include "memalloc.h" 55 #include "error.h" 56 #include "trap.h" 57 #include "mystring.h" 58 #include "myhistedit.h" 59 60 61 /* 62 * Sigmode records the current value of the signal handlers for the various 63 * modes. A value of zero means that the current handler is not known. 64 * S_HARD_IGN indicates that the signal was ignored on entry to the shell, 65 */ 66 67 #define S_DFL 1 /* default signal handling (SIG_DFL) */ 68 #define S_CATCH 2 /* signal is caught */ 69 #define S_IGN 3 /* signal is ignored (SIG_IGN) */ 70 #define S_HARD_IGN 4 /* signal is ignored permanently */ 71 #define S_RESET 5 /* temporary - to reset a hard ignored sig */ 72 73 74 MKINIT char sigmode[NSIG]; /* current value of signal */ 75 int pendingsigs; /* indicates some signal received */ 76 int in_dotrap; /* do we execute in a trap handler? */ 77 static char *volatile trap[NSIG]; /* trap handler commands */ 78 static volatile sig_atomic_t gotsig[NSIG]; 79 /* indicates specified signal received */ 80 static int ignore_sigchld; /* Used while handling SIGCHLD traps. */ 81 volatile sig_atomic_t gotwinch; 82 83 static int getsigaction(int, sig_t *); 84 85 86 /* 87 * Map a string to a signal number. 88 * 89 * Note: the signal number may exceed NSIG. 90 */ 91 static int 92 sigstring_to_signum(char *sig) 93 { 94 95 if (is_number(sig)) { 96 int signo; 97 98 signo = atoi(sig); 99 return ((signo >= 0 && signo < NSIG) ? signo : (-1)); 100 } else if (strcasecmp(sig, "exit") == 0) { 101 return (0); 102 } else { 103 int n; 104 105 if (strncasecmp(sig, "sig", 3) == 0) 106 sig += 3; 107 for (n = 1; n < sys_nsig; n++) 108 if (sys_signame[n] && 109 strcasecmp(sys_signame[n], sig) == 0) 110 return (n); 111 } 112 return (-1); 113 } 114 115 116 /* 117 * Print a list of valid signal names. 118 */ 119 static void 120 printsignals(void) 121 { 122 int n, outlen; 123 124 outlen = 0; 125 for (n = 1; n < sys_nsig; n++) { 126 if (sys_signame[n]) { 127 out1fmt("%s", sys_signame[n]); 128 outlen += strlen(sys_signame[n]); 129 } else { 130 out1fmt("%d", n); 131 outlen += 3; /* good enough */ 132 } 133 ++outlen; 134 if (outlen > 70 || n == sys_nsig - 1) { 135 out1str("\n"); 136 outlen = 0; 137 } else { 138 out1c(' '); 139 } 140 } 141 } 142 143 144 /* 145 * The trap builtin. 146 */ 147 int 148 trapcmd(int argc, char **argv) 149 { 150 char *action; 151 int signo; 152 int errors = 0; 153 154 if (argc <= 1) { 155 for (signo = 0 ; signo < sys_nsig ; signo++) { 156 if (signo < NSIG && trap[signo] != NULL) { 157 out1str("trap -- "); 158 out1qstr(trap[signo]); 159 if (signo == 0) { 160 out1str(" exit\n"); 161 } else if (sys_signame[signo]) { 162 out1fmt(" %s\n", sys_signame[signo]); 163 } else { 164 out1fmt(" %d\n", signo); 165 } 166 } 167 } 168 return 0; 169 } 170 action = NULL; 171 if (*++argv && strcmp(*argv, "--") == 0) 172 argv++; 173 if (*argv && sigstring_to_signum(*argv) == -1) { 174 if ((*argv)[0] != '-') { 175 action = *argv; 176 argv++; 177 } else if ((*argv)[1] == '\0') { 178 argv++; 179 } else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') { 180 printsignals(); 181 return 0; 182 } else { 183 error("bad option %s", *argv); 184 } 185 } 186 while (*argv) { 187 if ((signo = sigstring_to_signum(*argv)) == -1) { 188 out2fmt_flush("trap: bad signal %s\n", *argv); 189 errors = 1; 190 } 191 INTOFF; 192 if (action) 193 action = savestr(action); 194 if (trap[signo]) 195 ckfree(trap[signo]); 196 trap[signo] = action; 197 if (signo != 0) 198 setsignal(signo); 199 INTON; 200 argv++; 201 } 202 return errors; 203 } 204 205 206 /* 207 * Clear traps on a fork. 208 */ 209 void 210 clear_traps(void) 211 { 212 char *volatile *tp; 213 214 for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) { 215 if (*tp && **tp) { /* trap not NULL or SIG_IGN */ 216 INTOFF; 217 ckfree(*tp); 218 *tp = NULL; 219 if (tp != &trap[0]) 220 setsignal(tp - trap); 221 INTON; 222 } 223 } 224 } 225 226 227 /* 228 * Check if we have any traps enabled. 229 */ 230 int 231 have_traps(void) 232 { 233 char *volatile *tp; 234 235 for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) { 236 if (*tp && **tp) /* trap not NULL or SIG_IGN */ 237 return 1; 238 } 239 return 0; 240 } 241 242 /* 243 * Set the signal handler for the specified signal. The routine figures 244 * out what it should be set to. 245 */ 246 void 247 setsignal(int signo) 248 { 249 int action; 250 sig_t sigact = SIG_DFL; 251 struct sigaction sa; 252 char *t; 253 254 if ((t = trap[signo]) == NULL) 255 action = S_DFL; 256 else if (*t != '\0') 257 action = S_CATCH; 258 else 259 action = S_IGN; 260 if (action == S_DFL) { 261 switch (signo) { 262 case SIGINT: 263 action = S_CATCH; 264 break; 265 case SIGQUIT: 266 #ifdef DEBUG 267 { 268 extern int debug; 269 270 if (debug) 271 break; 272 } 273 #endif 274 action = S_CATCH; 275 break; 276 case SIGTERM: 277 if (rootshell && iflag) 278 action = S_IGN; 279 break; 280 #if JOBS 281 case SIGTSTP: 282 case SIGTTOU: 283 if (rootshell && mflag) 284 action = S_IGN; 285 break; 286 #endif 287 #ifndef NO_HISTORY 288 case SIGWINCH: 289 if (rootshell && iflag) 290 action = S_CATCH; 291 break; 292 #endif 293 } 294 } 295 296 t = &sigmode[signo]; 297 if (*t == 0) { 298 /* 299 * current setting unknown 300 */ 301 if (!getsigaction(signo, &sigact)) { 302 /* 303 * Pretend it worked; maybe we should give a warning 304 * here, but other shells don't. We don't alter 305 * sigmode, so that we retry every time. 306 */ 307 return; 308 } 309 if (sigact == SIG_IGN) { 310 if (mflag && (signo == SIGTSTP || 311 signo == SIGTTIN || signo == SIGTTOU)) { 312 *t = S_IGN; /* don't hard ignore these */ 313 } else 314 *t = S_HARD_IGN; 315 } else { 316 *t = S_RESET; /* force to be set */ 317 } 318 } 319 if (*t == S_HARD_IGN || *t == action) 320 return; 321 switch (action) { 322 case S_DFL: sigact = SIG_DFL; break; 323 case S_CATCH: sigact = onsig; break; 324 case S_IGN: sigact = SIG_IGN; break; 325 } 326 *t = action; 327 sa.sa_handler = sigact; 328 sa.sa_flags = 0; 329 sigemptyset(&sa.sa_mask); 330 sigaction(signo, &sa, NULL); 331 } 332 333 334 /* 335 * Return the current setting for sig w/o changing it. 336 */ 337 static int 338 getsigaction(int signo, sig_t *sigact) 339 { 340 struct sigaction sa; 341 342 if (sigaction(signo, (struct sigaction *)0, &sa) == -1) 343 return 0; 344 *sigact = (sig_t) sa.sa_handler; 345 return 1; 346 } 347 348 349 /* 350 * Ignore a signal. 351 */ 352 void 353 ignoresig(int signo) 354 { 355 356 if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { 357 signal(signo, SIG_IGN); 358 } 359 sigmode[signo] = S_HARD_IGN; 360 } 361 362 363 #ifdef mkinit 364 INCLUDE <signal.h> 365 INCLUDE "trap.h" 366 367 SHELLPROC { 368 char *sm; 369 370 clear_traps(); 371 for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { 372 if (*sm == S_IGN) 373 *sm = S_HARD_IGN; 374 } 375 } 376 #endif 377 378 379 /* 380 * Signal handler. 381 */ 382 void 383 onsig(int signo) 384 { 385 386 if (signo == SIGINT && trap[SIGINT] == NULL) { 387 onint(); 388 return; 389 } 390 391 if (signo != SIGCHLD || !ignore_sigchld) 392 gotsig[signo] = 1; 393 pendingsigs++; 394 395 /* If we are currently in a wait builtin, prepare to break it */ 396 if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0) 397 breakwaitcmd = 1; 398 /* 399 * If a trap is set, not ignored and not the null command, we need 400 * to make sure traps are executed even when a child blocks signals. 401 */ 402 if (Tflag && 403 trap[signo] != NULL && 404 ! (trap[signo][0] == '\0') && 405 ! (trap[signo][0] == ':' && trap[signo][1] == '\0')) 406 breakwaitcmd = 1; 407 408 #ifndef NO_HISTORY 409 if (signo == SIGWINCH) 410 gotwinch = 1; 411 #endif 412 } 413 414 415 /* 416 * Called to execute a trap. Perhaps we should avoid entering new trap 417 * handlers while we are executing a trap handler. 418 */ 419 void 420 dotrap(void) 421 { 422 int i; 423 int savestatus; 424 425 in_dotrap++; 426 for (;;) { 427 for (i = 1; i < NSIG; i++) { 428 if (gotsig[i]) { 429 gotsig[i] = 0; 430 if (trap[i]) { 431 /* 432 * Ignore SIGCHLD to avoid infinite 433 * recursion if the trap action does 434 * a fork. 435 */ 436 if (i == SIGCHLD) 437 ignore_sigchld++; 438 savestatus = exitstatus; 439 evalstring(trap[i], 0); 440 exitstatus = savestatus; 441 if (i == SIGCHLD) 442 ignore_sigchld--; 443 } 444 break; 445 } 446 } 447 if (i >= NSIG) 448 break; 449 } 450 in_dotrap--; 451 pendingsigs = 0; 452 } 453 454 455 /* 456 * Controls whether the shell is interactive or not. 457 */ 458 void 459 setinteractive(int on) 460 { 461 static int is_interactive = -1; 462 463 if (on == is_interactive) 464 return; 465 setsignal(SIGINT); 466 setsignal(SIGQUIT); 467 setsignal(SIGTERM); 468 #ifndef NO_HISTORY 469 setsignal(SIGWINCH); 470 #endif 471 is_interactive = on; 472 } 473 474 475 /* 476 * Called to exit the shell. 477 */ 478 void 479 exitshell(int status) 480 { 481 struct jmploc loc1, loc2; 482 char *p; 483 484 TRACE(("exitshell(%d) pid=%d\n", status, getpid())); 485 if (setjmp(loc1.loc)) { 486 goto l1; 487 } 488 if (setjmp(loc2.loc)) { 489 goto l2; 490 } 491 handler = &loc1; 492 if ((p = trap[0]) != NULL && *p != '\0') { 493 trap[0] = NULL; 494 evalstring(p, 0); 495 } 496 l1: handler = &loc2; /* probably unnecessary */ 497 flushall(); 498 #if JOBS 499 setjobctl(0); 500 #endif 501 l2: _exit(status); 502 } 503