11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Kenneth Almquist. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 4. Neither the name of the University nor the names of its contributors 171556Srgrimes * may be used to endorse or promote products derived from this software 181556Srgrimes * without specific prior written permission. 191556Srgrimes * 201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301556Srgrimes * SUCH DAMAGE. 311556Srgrimes */ 321556Srgrimes 331556Srgrimes#ifndef lint 3436150Scharnier#if 0 3536150Scharnierstatic char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; 3636150Scharnier#endif 371556Srgrimes#endif /* not lint */ 3899110Sobrien#include <sys/cdefs.h> 3999110Sobrien__FBSDID("$FreeBSD$"); 401556Srgrimes 4117987Speter#include <signal.h> 4217987Speter#include <unistd.h> 4317987Speter#include <stdlib.h> 4417987Speter 451556Srgrimes#include "shell.h" 461556Srgrimes#include "main.h" 471556Srgrimes#include "nodes.h" /* for other headers */ 481556Srgrimes#include "eval.h" 491556Srgrimes#include "jobs.h" 5017987Speter#include "show.h" 511556Srgrimes#include "options.h" 521556Srgrimes#include "syntax.h" 531556Srgrimes#include "output.h" 541556Srgrimes#include "memalloc.h" 551556Srgrimes#include "error.h" 561556Srgrimes#include "trap.h" 571556Srgrimes#include "mystring.h" 58223060Sjilles#include "builtins.h" 59100578Stjr#include "myhistedit.h" 601556Srgrimes 611556Srgrimes 621556Srgrimes/* 631556Srgrimes * Sigmode records the current value of the signal handlers for the various 641556Srgrimes * modes. A value of zero means that the current handler is not known. 651556Srgrimes * S_HARD_IGN indicates that the signal was ignored on entry to the shell, 661556Srgrimes */ 671556Srgrimes 681556Srgrimes#define S_DFL 1 /* default signal handling (SIG_DFL) */ 691556Srgrimes#define S_CATCH 2 /* signal is caught */ 701556Srgrimes#define S_IGN 3 /* signal is ignored (SIG_IGN) */ 7146684Skris#define S_HARD_IGN 4 /* signal is ignored permanently */ 721556Srgrimes#define S_RESET 5 /* temporary - to reset a hard ignored sig */ 731556Srgrimes 741556Srgrimes 7517987SpeterMKINIT char sigmode[NSIG]; /* current value of signal */ 7638521Scracauerint pendingsigs; /* indicates some signal received */ 7738536Scracauerint in_dotrap; /* do we execute in a trap handler? */ 7838950Scracauerstatic char *volatile trap[NSIG]; /* trap handler commands */ 79157811Sschweikhstatic volatile sig_atomic_t gotsig[NSIG]; 8038521Scracauer /* indicates specified signal received */ 8120902Sstevestatic int ignore_sigchld; /* Used while handling SIGCHLD traps. */ 82100588Stjrvolatile sig_atomic_t gotwinch; 83217472Sjillesstatic int last_trapsig; 841556Srgrimes 85217175Sjillesstatic int exiting; /* exitshell() has been called */ 86217175Sjillesstatic int exiting_exitstatus; /* value passed to exitshell() */ 87217175Sjilles 88213811Sobrienstatic int getsigaction(int, sig_t *); 8917987Speter 9020902Ssteve 911556Srgrimes/* 9220902Ssteve * Map a string to a signal number. 93125727Snjl * 94125727Snjl * Note: the signal number may exceed NSIG. 9520902Ssteve */ 96213811Sobrienstatic int 9790111Simpsigstring_to_signum(char *sig) 9820902Ssteve{ 9920902Ssteve 10020902Ssteve if (is_number(sig)) { 10120902Ssteve int signo; 10220902Ssteve 10320902Ssteve signo = atoi(sig); 10420902Ssteve return ((signo >= 0 && signo < NSIG) ? signo : (-1)); 105218285Sjilles } else if (strcasecmp(sig, "EXIT") == 0) { 10620902Ssteve return (0); 10720902Ssteve } else { 10820902Ssteve int n; 10920902Ssteve 110218285Sjilles if (strncasecmp(sig, "SIG", 3) == 0) 11120902Ssteve sig += 3; 112125155Snjl for (n = 1; n < sys_nsig; n++) 113125728Snjl if (sys_signame[n] && 114125728Snjl strcasecmp(sys_signame[n], sig) == 0) 11520902Ssteve return (n); 11620902Ssteve } 11720902Ssteve return (-1); 11820902Ssteve} 11920902Ssteve 12020902Ssteve 12120902Ssteve/* 12220902Ssteve * Print a list of valid signal names. 12320902Ssteve */ 124213811Sobrienstatic void 12590111Simpprintsignals(void) 12620902Ssteve{ 127125727Snjl int n, outlen; 12820902Ssteve 129125727Snjl outlen = 0; 130125155Snjl for (n = 1; n < sys_nsig; n++) { 131125727Snjl if (sys_signame[n]) { 132125727Snjl out1fmt("%s", sys_signame[n]); 133125727Snjl outlen += strlen(sys_signame[n]); 134125727Snjl } else { 135125727Snjl out1fmt("%d", n); 136125727Snjl outlen += 3; /* good enough */ 137125727Snjl } 138125727Snjl ++outlen; 139217425Sjilles if (outlen > 71 || n == sys_nsig - 1) { 14020902Ssteve out1str("\n"); 141125727Snjl outlen = 0; 142125727Snjl } else { 14320902Ssteve out1c(' '); 144125727Snjl } 14520902Ssteve } 14620902Ssteve} 14720902Ssteve 14820902Ssteve 14920902Ssteve/* 1501556Srgrimes * The trap builtin. 1511556Srgrimes */ 15217987Speterint 15390111Simptrapcmd(int argc, char **argv) 15417987Speter{ 1551556Srgrimes char *action; 1561556Srgrimes int signo; 157199641Sjilles int errors = 0; 158217461Sjilles int i; 1591556Srgrimes 160217461Sjilles while ((i = nextopt("l")) != '\0') { 161217461Sjilles switch (i) { 162217461Sjilles case 'l': 163217461Sjilles printsignals(); 164217461Sjilles return (0); 165217461Sjilles } 166217461Sjilles } 167217461Sjilles argv = argptr; 168217461Sjilles 169217461Sjilles if (*argv == NULL) { 170125155Snjl for (signo = 0 ; signo < sys_nsig ; signo++) { 171125727Snjl if (signo < NSIG && trap[signo] != NULL) { 172153244Sstefanf out1str("trap -- "); 173153244Sstefanf out1qstr(trap[signo]); 174125727Snjl if (signo == 0) { 175218285Sjilles out1str(" EXIT\n"); 176125727Snjl } else if (sys_signame[signo]) { 177153244Sstefanf out1fmt(" %s\n", sys_signame[signo]); 178125727Snjl } else { 179153244Sstefanf out1fmt(" %d\n", signo); 180125727Snjl } 181125727Snjl } 1821556Srgrimes } 1831556Srgrimes return 0; 1841556Srgrimes } 18520902Ssteve action = NULL; 18620902Ssteve if (*argv && sigstring_to_signum(*argv) == -1) { 187217461Sjilles if (strcmp(*argv, "-") == 0) 188217461Sjilles argv++; 189217461Sjilles else { 19020902Ssteve action = *argv; 19120902Ssteve argv++; 19220902Ssteve } 19320902Ssteve } 194230439Sjilles for (; *argv; argv++) { 195199641Sjilles if ((signo = sigstring_to_signum(*argv)) == -1) { 196216622Sjilles warning("bad signal %s", *argv); 197199641Sjilles errors = 1; 198230439Sjilles continue; 199199641Sjilles } 2001556Srgrimes INTOFF; 2011556Srgrimes if (action) 2021556Srgrimes action = savestr(action); 2031556Srgrimes if (trap[signo]) 2041556Srgrimes ckfree(trap[signo]); 2051556Srgrimes trap[signo] = action; 2061556Srgrimes if (signo != 0) 2071556Srgrimes setsignal(signo); 2081556Srgrimes INTON; 2091556Srgrimes } 210199641Sjilles return errors; 2111556Srgrimes} 2121556Srgrimes 2131556Srgrimes 2141556Srgrimes/* 2151556Srgrimes * Clear traps on a fork. 2161556Srgrimes */ 2171556Srgrimesvoid 21890111Simpclear_traps(void) 21920902Ssteve{ 22038950Scracauer char *volatile *tp; 2211556Srgrimes 22220902Ssteve for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) { 2231556Srgrimes if (*tp && **tp) { /* trap not NULL or SIG_IGN */ 2241556Srgrimes INTOFF; 2251556Srgrimes ckfree(*tp); 2261556Srgrimes *tp = NULL; 2271556Srgrimes if (tp != &trap[0]) 2281556Srgrimes setsignal(tp - trap); 2291556Srgrimes INTON; 2301556Srgrimes } 2311556Srgrimes } 2321556Srgrimes} 2331556Srgrimes 2341556Srgrimes 2351556Srgrimes/* 236194127Sjilles * Check if we have any traps enabled. 237194127Sjilles */ 238194127Sjillesint 239194127Sjilleshave_traps(void) 240194127Sjilles{ 241194127Sjilles char *volatile *tp; 242194127Sjilles 243194127Sjilles for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) { 244194127Sjilles if (*tp && **tp) /* trap not NULL or SIG_IGN */ 245194127Sjilles return 1; 246194127Sjilles } 247194127Sjilles return 0; 248194127Sjilles} 249194127Sjilles 250194127Sjilles/* 2511556Srgrimes * Set the signal handler for the specified signal. The routine figures 2521556Srgrimes * out what it should be set to. 2531556Srgrimes */ 25431098Sbdevoid 25590111Simpsetsignal(int signo) 25617987Speter{ 2571556Srgrimes int action; 258199205Sjilles sig_t sigact = SIG_DFL; 259199205Sjilles struct sigaction sa; 2601556Srgrimes char *t; 2611556Srgrimes 2621556Srgrimes if ((t = trap[signo]) == NULL) 2631556Srgrimes action = S_DFL; 2641556Srgrimes else if (*t != '\0') 2651556Srgrimes action = S_CATCH; 2661556Srgrimes else 2671556Srgrimes action = S_IGN; 26838521Scracauer if (action == S_DFL) { 2691556Srgrimes switch (signo) { 2701556Srgrimes case SIGINT: 27138521Scracauer action = S_CATCH; 2721556Srgrimes break; 2731556Srgrimes case SIGQUIT: 2741556Srgrimes#ifdef DEBUG 2751556Srgrimes { 2761556Srgrimes extern int debug; 2771556Srgrimes 2781556Srgrimes if (debug) 2791556Srgrimes break; 2801556Srgrimes } 2811556Srgrimes#endif 28238535Scracauer action = S_CATCH; 28338521Scracauer break; 2841556Srgrimes case SIGTERM: 28538521Scracauer if (rootshell && iflag) 2861556Srgrimes action = S_IGN; 2871556Srgrimes break; 2881556Srgrimes#if JOBS 2891556Srgrimes case SIGTSTP: 2901556Srgrimes case SIGTTOU: 29138521Scracauer if (rootshell && mflag) 2921556Srgrimes action = S_IGN; 2931556Srgrimes break; 2941556Srgrimes#endif 295100578Stjr#ifndef NO_HISTORY 296100578Stjr case SIGWINCH: 297100588Stjr if (rootshell && iflag) 298100578Stjr action = S_CATCH; 299100578Stjr break; 300100578Stjr#endif 3011556Srgrimes } 3021556Srgrimes } 30317987Speter 30420902Ssteve t = &sigmode[signo]; 3058855Srgrimes if (*t == 0) { 3068855Srgrimes /* 3078855Srgrimes * current setting unknown 3081556Srgrimes */ 30917987Speter if (!getsigaction(signo, &sigact)) { 31017987Speter /* 31117987Speter * Pretend it worked; maybe we should give a warning 31217987Speter * here, but other shells don't. We don't alter 31317987Speter * sigmode, so that we retry every time. 31417987Speter */ 31531098Sbde return; 31617987Speter } 3171556Srgrimes if (sigact == SIG_IGN) { 3188855Srgrimes if (mflag && (signo == SIGTSTP || 3191556Srgrimes signo == SIGTTIN || signo == SIGTTOU)) { 3201556Srgrimes *t = S_IGN; /* don't hard ignore these */ 3211556Srgrimes } else 3221556Srgrimes *t = S_HARD_IGN; 3231556Srgrimes } else { 3241556Srgrimes *t = S_RESET; /* force to be set */ 3251556Srgrimes } 3261556Srgrimes } 3271556Srgrimes if (*t == S_HARD_IGN || *t == action) 32831098Sbde return; 3291556Srgrimes switch (action) { 3301556Srgrimes case S_DFL: sigact = SIG_DFL; break; 3311556Srgrimes case S_CATCH: sigact = onsig; break; 3321556Srgrimes case S_IGN: sigact = SIG_IGN; break; 3331556Srgrimes } 3341556Srgrimes *t = action; 335199205Sjilles sa.sa_handler = sigact; 336199205Sjilles sa.sa_flags = 0; 337199205Sjilles sigemptyset(&sa.sa_mask); 338199205Sjilles sigaction(signo, &sa, NULL); 3391556Srgrimes} 3401556Srgrimes 34120902Ssteve 3421556Srgrimes/* 3431556Srgrimes * Return the current setting for sig w/o changing it. 3441556Srgrimes */ 345213811Sobrienstatic int 34690111Simpgetsigaction(int signo, sig_t *sigact) 34717987Speter{ 3481556Srgrimes struct sigaction sa; 3491556Srgrimes 3501556Srgrimes if (sigaction(signo, (struct sigaction *)0, &sa) == -1) 35117987Speter return 0; 35217987Speter *sigact = (sig_t) sa.sa_handler; 35317987Speter return 1; 3541556Srgrimes} 3551556Srgrimes 35620902Ssteve 3571556Srgrimes/* 3581556Srgrimes * Ignore a signal. 3591556Srgrimes */ 3601556Srgrimesvoid 36190111Simpignoresig(int signo) 36217987Speter{ 36320902Ssteve 36420902Ssteve if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { 3651556Srgrimes signal(signo, SIG_IGN); 3661556Srgrimes } 36720902Ssteve sigmode[signo] = S_HARD_IGN; 3681556Srgrimes} 3691556Srgrimes 3701556Srgrimes 3711556Srgrimes/* 3721556Srgrimes * Signal handler. 3731556Srgrimes */ 3741556Srgrimesvoid 37590111Simponsig(int signo) 37617987Speter{ 37731098Sbde 3781556Srgrimes if (signo == SIGINT && trap[SIGINT] == NULL) { 3791556Srgrimes onint(); 3801556Srgrimes return; 3811556Srgrimes } 38238950Scracauer 38320902Ssteve if (signo != SIGCHLD || !ignore_sigchld) 38420902Ssteve gotsig[signo] = 1; 3851556Srgrimes pendingsigs++; 38638950Scracauer 38738950Scracauer /* If we are currently in a wait builtin, prepare to break it */ 38838950Scracauer if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0) 38938521Scracauer breakwaitcmd = 1; 390157811Sschweikh /* 391157811Sschweikh * If a trap is set, not ignored and not the null command, we need 39239056Scracauer * to make sure traps are executed even when a child blocks signals. 39338950Scracauer */ 39445221Scracauer if (Tflag && 395157811Sschweikh trap[signo] != NULL && 396149825Srse ! (trap[signo][0] == '\0') && 39739049Scracauer ! (trap[signo][0] == ':' && trap[signo][1] == '\0')) 39839049Scracauer breakwaitcmd = 1; 399100578Stjr 400100578Stjr#ifndef NO_HISTORY 401100578Stjr if (signo == SIGWINCH) 402100588Stjr gotwinch = 1; 403100578Stjr#endif 4041556Srgrimes} 4051556Srgrimes 4061556Srgrimes 4071556Srgrimes/* 4081556Srgrimes * Called to execute a trap. Perhaps we should avoid entering new trap 4091556Srgrimes * handlers while we are executing a trap handler. 4101556Srgrimes */ 4111556Srgrimesvoid 41290111Simpdotrap(void) 41320902Ssteve{ 4141556Srgrimes int i; 415231085Sdumbbell int savestatus, prev_evalskip, prev_skipcount; 4161556Srgrimes 41738521Scracauer in_dotrap++; 4181556Srgrimes for (;;) { 41920902Ssteve for (i = 1; i < NSIG; i++) { 42020902Ssteve if (gotsig[i]) { 42120902Ssteve gotsig[i] = 0; 42220902Ssteve if (trap[i]) { 42320902Ssteve /* 424125728Snjl * Ignore SIGCHLD to avoid infinite 425125728Snjl * recursion if the trap action does 426125728Snjl * a fork. 42720902Ssteve */ 42820902Ssteve if (i == SIGCHLD) 42920902Ssteve ignore_sigchld++; 430231085Sdumbbell 431231085Sdumbbell /* 432231085Sdumbbell * Backup current evalskip 433231085Sdumbbell * state and reset it before 434231085Sdumbbell * executing a trap, so that the 435231085Sdumbbell * trap is not disturbed by an 436231085Sdumbbell * ongoing break/continue/return 437231085Sdumbbell * statement. 438231085Sdumbbell */ 439231085Sdumbbell prev_evalskip = evalskip; 440231085Sdumbbell prev_skipcount = skipcount; 441231085Sdumbbell evalskip = 0; 442231085Sdumbbell 443217472Sjilles last_trapsig = i; 44420902Ssteve savestatus = exitstatus; 445193169Sstefanf evalstring(trap[i], 0); 44620902Ssteve exitstatus = savestatus; 447231085Sdumbbell 448231085Sdumbbell /* 449231085Sdumbbell * If such a command was not 450231085Sdumbbell * already in progress, allow a 451231085Sdumbbell * break/continue/return in the 452231085Sdumbbell * trap action to have an effect 453231085Sdumbbell * outside of it. 454231085Sdumbbell */ 455231085Sdumbbell if (prev_evalskip != 0) { 456231085Sdumbbell evalskip = prev_evalskip; 457231085Sdumbbell skipcount = prev_skipcount; 458231085Sdumbbell } 459231085Sdumbbell 46020902Ssteve if (i == SIGCHLD) 46120902Ssteve ignore_sigchld--; 46220902Ssteve } 4631556Srgrimes break; 46420902Ssteve } 4651556Srgrimes } 46620902Ssteve if (i >= NSIG) 46720902Ssteve break; 4681556Srgrimes } 46938521Scracauer in_dotrap--; 4701556Srgrimes pendingsigs = 0; 4711556Srgrimes} 4721556Srgrimes 4731556Srgrimes 4741556Srgrimes/* 4751556Srgrimes * Controls whether the shell is interactive or not. 4761556Srgrimes */ 4771556Srgrimesvoid 47890111Simpsetinteractive(int on) 47917987Speter{ 48038521Scracauer static int is_interactive = -1; 4811556Srgrimes 4821556Srgrimes if (on == is_interactive) 4831556Srgrimes return; 4841556Srgrimes setsignal(SIGINT); 4851556Srgrimes setsignal(SIGQUIT); 4861556Srgrimes setsignal(SIGTERM); 487100578Stjr#ifndef NO_HISTORY 488100578Stjr setsignal(SIGWINCH); 489100578Stjr#endif 4901556Srgrimes is_interactive = on; 4911556Srgrimes} 4921556Srgrimes 4931556Srgrimes 4941556Srgrimes/* 4951556Srgrimes * Called to exit the shell. 4961556Srgrimes */ 4971556Srgrimesvoid 49890111Simpexitshell(int status) 49917987Speter{ 500217175Sjilles TRACE(("exitshell(%d) pid=%d\n", status, getpid())); 501217175Sjilles exiting = 1; 502217175Sjilles exiting_exitstatus = status; 503217175Sjilles exitshell_savedstatus(); 504217175Sjilles} 505217175Sjilles 506217175Sjillesvoid 507217175Sjillesexitshell_savedstatus(void) 508217175Sjilles{ 5091556Srgrimes struct jmploc loc1, loc2; 5101556Srgrimes char *p; 511217472Sjilles int sig = 0; 512217472Sjilles sigset_t sigs; 5131556Srgrimes 514217472Sjilles if (!exiting) { 515217472Sjilles if (in_dotrap && last_trapsig) { 516217472Sjilles sig = last_trapsig; 517217472Sjilles exiting_exitstatus = sig + 128; 518217472Sjilles } else 519217472Sjilles exiting_exitstatus = oexitstatus; 520217472Sjilles } 521217175Sjilles exitstatus = oexitstatus = exiting_exitstatus; 5221556Srgrimes if (setjmp(loc1.loc)) { 5231556Srgrimes goto l1; 5241556Srgrimes } 5251556Srgrimes if (setjmp(loc2.loc)) { 5261556Srgrimes goto l2; 5271556Srgrimes } 5281556Srgrimes handler = &loc1; 5291556Srgrimes if ((p = trap[0]) != NULL && *p != '\0') { 530231085Sdumbbell /* 531231085Sdumbbell * Reset evalskip, or the trap on EXIT could be 532231085Sdumbbell * interrupted if the last command was a "return". 533231085Sdumbbell */ 534231085Sdumbbell evalskip = 0; 5351556Srgrimes trap[0] = NULL; 536193169Sstefanf evalstring(p, 0); 5371556Srgrimes } 5381556Srgrimesl1: handler = &loc2; /* probably unnecessary */ 5391556Srgrimes flushall(); 5401556Srgrimes#if JOBS 5411556Srgrimes setjobctl(0); 5421556Srgrimes#endif 543217472Sjillesl2: 544217472Sjilles if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN && 545217472Sjilles sig != SIGTTOU) { 546217472Sjilles signal(sig, SIG_DFL); 547217472Sjilles sigemptyset(&sigs); 548217472Sjilles sigaddset(&sigs, sig); 549217472Sjilles sigprocmask(SIG_UNBLOCK, &sigs, NULL); 550217472Sjilles kill(getpid(), sig); 551217472Sjilles /* If the default action is to ignore, fall back to _exit(). */ 552217472Sjilles } 553217472Sjilles _exit(exiting_exitstatus); 5541556Srgrimes} 555