trap.c revision 230439
198937Sdes/*- 298937Sdes * Copyright (c) 1991, 1993 398937Sdes * The Regents of the University of California. All rights reserved. 498937Sdes * 598937Sdes * This code is derived from software contributed to Berkeley by 698937Sdes * Kenneth Almquist. 798937Sdes * 898937Sdes * Redistribution and use in source and binary forms, with or without 998937Sdes * modification, are permitted provided that the following conditions 1098937Sdes * are met: 1198937Sdes * 1. Redistributions of source code must retain the above copyright 1298937Sdes * notice, this list of conditions and the following disclaimer. 1398937Sdes * 2. Redistributions in binary form must reproduce the above copyright 1498937Sdes * notice, this list of conditions and the following disclaimer in the 1598937Sdes * documentation and/or other materials provided with the distribution. 1698937Sdes * 4. Neither the name of the University nor the names of its contributors 1798937Sdes * may be used to endorse or promote products derived from this software 1898937Sdes * without specific prior written permission. 1998937Sdes * 2098937Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2198937Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2298937Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2398937Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2498937Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2598937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2698937Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2798937Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2898937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2998937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3098937Sdes * SUCH DAMAGE. 3198937Sdes */ 3298937Sdes 3398937Sdes#ifndef lint 3498937Sdes#if 0 3598937Sdesstatic char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; 3698937Sdes#endif 3798937Sdes#endif /* not lint */ 3898937Sdes#include <sys/cdefs.h> 3998937Sdes__FBSDID("$FreeBSD: stable/9/bin/sh/trap.c 230439 2012-01-21 21:54:31Z jilles $"); 4098937Sdes 4198937Sdes#include <signal.h> 4298937Sdes#include <unistd.h> 4398937Sdes#include <stdlib.h> 4498937Sdes 4598937Sdes#include "shell.h" 4698937Sdes#include "main.h" 4798937Sdes#include "nodes.h" /* for other headers */ 4898937Sdes#include "eval.h" 4998937Sdes#include "jobs.h" 5098937Sdes#include "show.h" 5198937Sdes#include "options.h" 5298937Sdes#include "syntax.h" 5398937Sdes#include "output.h" 5498937Sdes#include "memalloc.h" 5598937Sdes#include "error.h" 5698937Sdes#include "trap.h" 5798937Sdes#include "mystring.h" 5898937Sdes#include "builtins.h" 5998937Sdes#include "myhistedit.h" 6098937Sdes 6198937Sdes 6298937Sdes/* 6398937Sdes * Sigmode records the current value of the signal handlers for the various 6498937Sdes * modes. A value of zero means that the current handler is not known. 6598937Sdes * S_HARD_IGN indicates that the signal was ignored on entry to the shell, 6698937Sdes */ 6798937Sdes 6898937Sdes#define S_DFL 1 /* default signal handling (SIG_DFL) */ 6998937Sdes#define S_CATCH 2 /* signal is caught */ 7098937Sdes#define S_IGN 3 /* signal is ignored (SIG_IGN) */ 7198937Sdes#define S_HARD_IGN 4 /* signal is ignored permanently */ 7298937Sdes#define S_RESET 5 /* temporary - to reset a hard ignored sig */ 7398937Sdes 7498937Sdes 7598937SdesMKINIT char sigmode[NSIG]; /* current value of signal */ 7698937Sdesint pendingsigs; /* indicates some signal received */ 7798937Sdesint in_dotrap; /* do we execute in a trap handler? */ 7898937Sdesstatic char *volatile trap[NSIG]; /* trap handler commands */ 7998937Sdesstatic volatile sig_atomic_t gotsig[NSIG]; 8098937Sdes /* indicates specified signal received */ 8198937Sdesstatic int ignore_sigchld; /* Used while handling SIGCHLD traps. */ 8298937Sdesvolatile sig_atomic_t gotwinch; 8398937Sdesstatic int last_trapsig; 8498937Sdes 8598937Sdesstatic int exiting; /* exitshell() has been called */ 8698937Sdesstatic int exiting_exitstatus; /* value passed to exitshell() */ 8798937Sdes 8898937Sdesstatic int getsigaction(int, sig_t *); 8998937Sdes 9098937Sdes 9198937Sdes/* 9298937Sdes * Map a string to a signal number. 9398937Sdes * 9498937Sdes * Note: the signal number may exceed NSIG. 9598937Sdes */ 9698937Sdesstatic int 9798937Sdessigstring_to_signum(char *sig) 9898937Sdes{ 9998937Sdes 10098937Sdes if (is_number(sig)) { 10198937Sdes int signo; 10298937Sdes 10398937Sdes signo = atoi(sig); 10498937Sdes return ((signo >= 0 && signo < NSIG) ? signo : (-1)); 10598937Sdes } else if (strcasecmp(sig, "EXIT") == 0) { 10698937Sdes return (0); 10798937Sdes } else { 10898937Sdes int n; 10998937Sdes 11098937Sdes if (strncasecmp(sig, "SIG", 3) == 0) 11198937Sdes sig += 3; 11298937Sdes for (n = 1; n < sys_nsig; n++) 11398937Sdes if (sys_signame[n] && 11498937Sdes strcasecmp(sys_signame[n], sig) == 0) 11598937Sdes return (n); 11698937Sdes } 11798937Sdes return (-1); 11898937Sdes} 11998937Sdes 12098937Sdes 12198937Sdes/* 12298937Sdes * Print a list of valid signal names. 12398937Sdes */ 12498937Sdesstatic void 12598937Sdesprintsignals(void) 12698937Sdes{ 12798937Sdes int n, outlen; 128126274Sdes 12998937Sdes outlen = 0; 13098937Sdes for (n = 1; n < sys_nsig; n++) { 13198937Sdes if (sys_signame[n]) { 13298937Sdes out1fmt("%s", sys_signame[n]); 13398937Sdes outlen += strlen(sys_signame[n]); 13498937Sdes } else { 13598937Sdes out1fmt("%d", n); 13698937Sdes outlen += 3; /* good enough */ 13798937Sdes } 13898937Sdes ++outlen; 13998937Sdes if (outlen > 71 || n == sys_nsig - 1) { 14098937Sdes out1str("\n"); 14198937Sdes outlen = 0; 14298937Sdes } else { 14398937Sdes out1c(' '); 14498937Sdes } 14598937Sdes } 14698937Sdes} 14798937Sdes 14898937Sdes 14998937Sdes/* 15098937Sdes * The trap builtin. 15198937Sdes */ 15298937Sdesint 15398937Sdestrapcmd(int argc, char **argv) 15498937Sdes{ 15598937Sdes char *action; 15698937Sdes int signo; 15798937Sdes int errors = 0; 15898937Sdes int i; 15998937Sdes 16098937Sdes while ((i = nextopt("l")) != '\0') { 16198937Sdes switch (i) { 16298937Sdes case 'l': 16398937Sdes printsignals(); 16498937Sdes return (0); 16598937Sdes } 16698937Sdes } 16798937Sdes argv = argptr; 16898937Sdes 16998937Sdes if (*argv == NULL) { 17098937Sdes for (signo = 0 ; signo < sys_nsig ; signo++) { 17198937Sdes if (signo < NSIG && trap[signo] != NULL) { 17298937Sdes out1str("trap -- "); 17398937Sdes out1qstr(trap[signo]); 17498937Sdes if (signo == 0) { 17598937Sdes out1str(" EXIT\n"); 17698937Sdes } else if (sys_signame[signo]) { 17798937Sdes out1fmt(" %s\n", sys_signame[signo]); 17898937Sdes } else { 17998937Sdes out1fmt(" %d\n", signo); 18098937Sdes } 18198937Sdes } 18298937Sdes } 183126274Sdes return 0; 18498937Sdes } 18598937Sdes action = NULL; 18698937Sdes if (*argv && sigstring_to_signum(*argv) == -1) { 18798937Sdes if (strcmp(*argv, "-") == 0) 18898937Sdes argv++; 18998937Sdes else { 19098937Sdes action = *argv; 19198937Sdes argv++; 19298937Sdes } 19398937Sdes } 19498937Sdes for (; *argv; argv++) { 19598937Sdes if ((signo = sigstring_to_signum(*argv)) == -1) { 19698937Sdes warning("bad signal %s", *argv); 19798937Sdes errors = 1; 19898937Sdes continue; 19998937Sdes } 20098937Sdes INTOFF; 20198937Sdes if (action) 20298937Sdes action = savestr(action); 20398937Sdes if (trap[signo]) 20498937Sdes ckfree(trap[signo]); 205126274Sdes trap[signo] = action; 20698937Sdes if (signo != 0) 20798937Sdes setsignal(signo); 20898937Sdes INTON; 209126274Sdes } 21098937Sdes return errors; 21198937Sdes} 21298937Sdes 21398937Sdes 21498937Sdes/* 215126274Sdes * Clear traps on a fork. 21698937Sdes */ 21798937Sdesvoid 21898937Sdesclear_traps(void) 21998937Sdes{ 22098937Sdes char *volatile *tp; 22198937Sdes 22298937Sdes for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) { 22398937Sdes if (*tp && **tp) { /* trap not NULL or SIG_IGN */ 22498937Sdes INTOFF; 22598937Sdes ckfree(*tp); 22698937Sdes *tp = NULL; 22798937Sdes if (tp != &trap[0]) 22898937Sdes setsignal(tp - trap); 22998937Sdes INTON; 23098937Sdes } 23198937Sdes } 23298937Sdes} 23398937Sdes 23498937Sdes 23598937Sdes/* 23698937Sdes * Check if we have any traps enabled. 23798937Sdes */ 23898937Sdesint 23998937Sdeshave_traps(void) 24098937Sdes{ 24198937Sdes char *volatile *tp; 24298937Sdes 24398937Sdes for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) { 24498937Sdes if (*tp && **tp) /* trap not NULL or SIG_IGN */ 24598937Sdes return 1; 246126274Sdes } 24798937Sdes return 0; 24898937Sdes} 24998937Sdes 25098937Sdes/* 25198937Sdes * Set the signal handler for the specified signal. The routine figures 252 * out what it should be set to. 253 */ 254void 255setsignal(int signo) 256{ 257 int action; 258 sig_t sigact = SIG_DFL; 259 struct sigaction sa; 260 char *t; 261 262 if ((t = trap[signo]) == NULL) 263 action = S_DFL; 264 else if (*t != '\0') 265 action = S_CATCH; 266 else 267 action = S_IGN; 268 if (action == S_DFL) { 269 switch (signo) { 270 case SIGINT: 271 action = S_CATCH; 272 break; 273 case SIGQUIT: 274#ifdef DEBUG 275 { 276 extern int debug; 277 278 if (debug) 279 break; 280 } 281#endif 282 action = S_CATCH; 283 break; 284 case SIGTERM: 285 if (rootshell && iflag) 286 action = S_IGN; 287 break; 288#if JOBS 289 case SIGTSTP: 290 case SIGTTOU: 291 if (rootshell && mflag) 292 action = S_IGN; 293 break; 294#endif 295#ifndef NO_HISTORY 296 case SIGWINCH: 297 if (rootshell && iflag) 298 action = S_CATCH; 299 break; 300#endif 301 } 302 } 303 304 t = &sigmode[signo]; 305 if (*t == 0) { 306 /* 307 * current setting unknown 308 */ 309 if (!getsigaction(signo, &sigact)) { 310 /* 311 * Pretend it worked; maybe we should give a warning 312 * here, but other shells don't. We don't alter 313 * sigmode, so that we retry every time. 314 */ 315 return; 316 } 317 if (sigact == SIG_IGN) { 318 if (mflag && (signo == SIGTSTP || 319 signo == SIGTTIN || signo == SIGTTOU)) { 320 *t = S_IGN; /* don't hard ignore these */ 321 } else 322 *t = S_HARD_IGN; 323 } else { 324 *t = S_RESET; /* force to be set */ 325 } 326 } 327 if (*t == S_HARD_IGN || *t == action) 328 return; 329 switch (action) { 330 case S_DFL: sigact = SIG_DFL; break; 331 case S_CATCH: sigact = onsig; break; 332 case S_IGN: sigact = SIG_IGN; break; 333 } 334 *t = action; 335 sa.sa_handler = sigact; 336 sa.sa_flags = 0; 337 sigemptyset(&sa.sa_mask); 338 sigaction(signo, &sa, NULL); 339} 340 341 342/* 343 * Return the current setting for sig w/o changing it. 344 */ 345static int 346getsigaction(int signo, sig_t *sigact) 347{ 348 struct sigaction sa; 349 350 if (sigaction(signo, (struct sigaction *)0, &sa) == -1) 351 return 0; 352 *sigact = (sig_t) sa.sa_handler; 353 return 1; 354} 355 356 357/* 358 * Ignore a signal. 359 */ 360void 361ignoresig(int signo) 362{ 363 364 if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { 365 signal(signo, SIG_IGN); 366 } 367 sigmode[signo] = S_HARD_IGN; 368} 369 370 371/* 372 * Signal handler. 373 */ 374void 375onsig(int signo) 376{ 377 378 if (signo == SIGINT && trap[SIGINT] == NULL) { 379 onint(); 380 return; 381 } 382 383 if (signo != SIGCHLD || !ignore_sigchld) 384 gotsig[signo] = 1; 385 pendingsigs++; 386 387 /* If we are currently in a wait builtin, prepare to break it */ 388 if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0) 389 breakwaitcmd = 1; 390 /* 391 * If a trap is set, not ignored and not the null command, we need 392 * to make sure traps are executed even when a child blocks signals. 393 */ 394 if (Tflag && 395 trap[signo] != NULL && 396 ! (trap[signo][0] == '\0') && 397 ! (trap[signo][0] == ':' && trap[signo][1] == '\0')) 398 breakwaitcmd = 1; 399 400#ifndef NO_HISTORY 401 if (signo == SIGWINCH) 402 gotwinch = 1; 403#endif 404} 405 406 407/* 408 * Called to execute a trap. Perhaps we should avoid entering new trap 409 * handlers while we are executing a trap handler. 410 */ 411void 412dotrap(void) 413{ 414 int i; 415 int savestatus; 416 417 in_dotrap++; 418 for (;;) { 419 for (i = 1; i < NSIG; i++) { 420 if (gotsig[i]) { 421 gotsig[i] = 0; 422 if (trap[i]) { 423 /* 424 * Ignore SIGCHLD to avoid infinite 425 * recursion if the trap action does 426 * a fork. 427 */ 428 if (i == SIGCHLD) 429 ignore_sigchld++; 430 last_trapsig = i; 431 savestatus = exitstatus; 432 evalstring(trap[i], 0); 433 exitstatus = savestatus; 434 if (i == SIGCHLD) 435 ignore_sigchld--; 436 } 437 break; 438 } 439 } 440 if (i >= NSIG) 441 break; 442 } 443 in_dotrap--; 444 pendingsigs = 0; 445} 446 447 448/* 449 * Controls whether the shell is interactive or not. 450 */ 451void 452setinteractive(int on) 453{ 454 static int is_interactive = -1; 455 456 if (on == is_interactive) 457 return; 458 setsignal(SIGINT); 459 setsignal(SIGQUIT); 460 setsignal(SIGTERM); 461#ifndef NO_HISTORY 462 setsignal(SIGWINCH); 463#endif 464 is_interactive = on; 465} 466 467 468/* 469 * Called to exit the shell. 470 */ 471void 472exitshell(int status) 473{ 474 TRACE(("exitshell(%d) pid=%d\n", status, getpid())); 475 exiting = 1; 476 exiting_exitstatus = status; 477 exitshell_savedstatus(); 478} 479 480void 481exitshell_savedstatus(void) 482{ 483 struct jmploc loc1, loc2; 484 char *p; 485 int sig = 0; 486 sigset_t sigs; 487 488 if (!exiting) { 489 if (in_dotrap && last_trapsig) { 490 sig = last_trapsig; 491 exiting_exitstatus = sig + 128; 492 } else 493 exiting_exitstatus = oexitstatus; 494 } 495 exitstatus = oexitstatus = exiting_exitstatus; 496 if (setjmp(loc1.loc)) { 497 goto l1; 498 } 499 if (setjmp(loc2.loc)) { 500 goto l2; 501 } 502 handler = &loc1; 503 if ((p = trap[0]) != NULL && *p != '\0') { 504 trap[0] = NULL; 505 evalstring(p, 0); 506 } 507l1: handler = &loc2; /* probably unnecessary */ 508 flushall(); 509#if JOBS 510 setjobctl(0); 511#endif 512l2: 513 if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN && 514 sig != SIGTTOU) { 515 signal(sig, SIG_DFL); 516 sigemptyset(&sigs); 517 sigaddset(&sigs, sig); 518 sigprocmask(SIG_UNBLOCK, &sigs, NULL); 519 kill(getpid(), sig); 520 /* If the default action is to ignore, fall back to _exit(). */ 521 } 522 _exit(exiting_exitstatus); 523} 524