1*d1c86d36Skre /* $NetBSD: trap.c,v 1.58 2024/10/09 13:43:33 kre Exp $ */ 249f0ad86Scgd 361f28255Scgd /*- 437ed7877Sjtc * Copyright (c) 1991, 1993 537ed7877Sjtc * The Regents of the University of California. All rights reserved. 661f28255Scgd * 761f28255Scgd * This code is derived from software contributed to Berkeley by 861f28255Scgd * Kenneth Almquist. 961f28255Scgd * 1061f28255Scgd * Redistribution and use in source and binary forms, with or without 1161f28255Scgd * modification, are permitted provided that the following conditions 1261f28255Scgd * are met: 1361f28255Scgd * 1. Redistributions of source code must retain the above copyright 1461f28255Scgd * notice, this list of conditions and the following disclaimer. 1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright 1661f28255Scgd * notice, this list of conditions and the following disclaimer in the 1761f28255Scgd * documentation and/or other materials provided with the distribution. 18b5b29542Sagc * 3. Neither the name of the University nor the names of its contributors 1961f28255Scgd * may be used to endorse or promote products derived from this software 2061f28255Scgd * without specific prior written permission. 2161f28255Scgd * 2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3261f28255Scgd * SUCH DAMAGE. 3361f28255Scgd */ 3461f28255Scgd 35cd799663Schristos #include <sys/cdefs.h> 3661f28255Scgd #ifndef lint 3749f0ad86Scgd #if 0 389d255ec4Schristos static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; 3949f0ad86Scgd #else 40*d1c86d36Skre __RCSID("$NetBSD: trap.c,v 1.58 2024/10/09 13:43:33 kre Exp $"); 4149f0ad86Scgd #endif 4261f28255Scgd #endif /* not lint */ 4361f28255Scgd 4407bae7edSchristos #include <signal.h> 4507bae7edSchristos #include <unistd.h> 4607bae7edSchristos #include <stdlib.h> 475791be97Skre #include <stdio.h> 4813fc5c1bSkre #include <errno.h> 49e904ec40Skre #include <limits.h> 50af2cfdd7Skre #include <termios.h> 5113fc5c1bSkre 52af2cfdd7Skre #undef CEOF /* from <termios.h> but concflicts with sh use */ 53af2cfdd7Skre 54af2cfdd7Skre #include <sys/ioctl.h> 5513fc5c1bSkre #include <sys/resource.h> 5607bae7edSchristos 5761f28255Scgd #include "shell.h" 5861f28255Scgd #include "main.h" 5961f28255Scgd #include "nodes.h" /* for other headers */ 6061f28255Scgd #include "eval.h" 6161f28255Scgd #include "jobs.h" 6207bae7edSchristos #include "show.h" 6361f28255Scgd #include "options.h" 644fc4fe2eSchristos #include "builtins.h" 6561f28255Scgd #include "syntax.h" 6661f28255Scgd #include "output.h" 6761f28255Scgd #include "memalloc.h" 6861f28255Scgd #include "error.h" 6961f28255Scgd #include "trap.h" 7061f28255Scgd #include "mystring.h" 712a32d39eSchristos #include "var.h" 7261f28255Scgd 73c591669fSkre #ifndef SMALL 74c591669fSkre #include "myhistedit.h" 75c591669fSkre #endif 7661f28255Scgd 7761f28255Scgd /* 7861f28255Scgd * Sigmode records the current value of the signal handlers for the various 7961f28255Scgd * modes. A value of zero means that the current handler is not known. 8061f28255Scgd * S_HARD_IGN indicates that the signal was ignored on entry to the shell, 8161f28255Scgd */ 8261f28255Scgd 8361f28255Scgd #define S_DFL 1 /* default signal handling (SIG_DFL) */ 8461f28255Scgd #define S_CATCH 2 /* signal is caught */ 8561f28255Scgd #define S_IGN 3 /* signal is ignored (SIG_IGN) */ 8661f28255Scgd #define S_HARD_IGN 4 /* signal is ignored permenantly */ 8737ed7877Sjtc #define S_RESET 5 /* temporary - to reset a hard ignored sig */ 8861f28255Scgd 8961f28255Scgd 901a3b3eb0Sjtc MKINIT char sigmode[NSIG]; /* current value of signal */ 9113fc5c1bSkre static volatile sig_atomic_t gotsig[NSIG];/* indicates specified signal received */ 9213fc5c1bSkre volatile sig_atomic_t pendingsigs; /* indicates some signal received */ 9313fc5c1bSkre 9413fc5c1bSkre int traps_invalid; /* in a subshell, but trap[] not yet cleared */ 9513fc5c1bSkre static char * volatile trap[NSIG]; /* trap handler commands */ 9613fc5c1bSkre static int in_dotrap; 9713fc5c1bSkre static int last_trapsig; 9813fc5c1bSkre 9913fc5c1bSkre static int exiting; /* exitshell() has been done */ 10013fc5c1bSkre static int exiting_status; /* the status to use for exit() */ 10161f28255Scgd 102c02b3bbdSchristos static int getsigaction(int, sig_t *); 1035791be97Skre STATIC const char *trap_signame(int); 10433a05de6Skre void printsignals(struct output *, int); 1058d23cb03Swulf 1068d23cb03Swulf /* 1078d23cb03Swulf * return the signal number described by `p' (as a number or a name) 1088d23cb03Swulf * or -1 if it isn't one 1098d23cb03Swulf */ 1108d23cb03Swulf 111*d1c86d36Skre int 112c02b3bbdSchristos signame_to_signum(const char *p) 1138d23cb03Swulf { 1148d23cb03Swulf int i; 1158d23cb03Swulf 1168d23cb03Swulf if (is_number(p)) 1178d23cb03Swulf return number(p); 1188d23cb03Swulf 1198d23cb03Swulf if (strcasecmp(p, "exit") == 0 ) 1208d23cb03Swulf return 0; 1218d23cb03Swulf 12233a05de6Skre i = signalnumber(p); 12333a05de6Skre if (i == 0) 12433a05de6Skre i = -1; 1258d23cb03Swulf return i; 1268d23cb03Swulf } 1278d23cb03Swulf 1288d23cb03Swulf /* 1295791be97Skre * return the name of a signal used by the "trap" command 1305791be97Skre */ 1315791be97Skre STATIC const char * 1325791be97Skre trap_signame(int signo) 1335791be97Skre { 1345791be97Skre static char nbuf[12]; 13533a05de6Skre const char *p; 1365791be97Skre 1375791be97Skre if (signo == 0) 1385791be97Skre return "EXIT"; 13933a05de6Skre p = signalname(signo); 1405791be97Skre if (p != NULL) 1415791be97Skre return p; 1425791be97Skre (void)snprintf(nbuf, sizeof nbuf, "%d", signo); 1435791be97Skre return nbuf; 1445791be97Skre } 1455791be97Skre 146af2cfdd7Skre #ifdef SMALL 1475791be97Skre /* 1488d23cb03Swulf * Print a list of valid signal names 1498d23cb03Swulf */ 15033a05de6Skre void 15133a05de6Skre printsignals(struct output *out, int len) 1528d23cb03Swulf { 1538d23cb03Swulf int n; 1548d23cb03Swulf 15533a05de6Skre if (len != 0) 15633a05de6Skre outc(' ', out); 1578d23cb03Swulf for (n = 1; n < NSIG; n++) { 15833a05de6Skre outfmt(out, "%s", trap_signame(n)); 1598d23cb03Swulf if ((n == NSIG/2) || n == (NSIG - 1)) 16033a05de6Skre outstr("\n", out); 1618d23cb03Swulf else 16233a05de6Skre outc(' ', out); 1638d23cb03Swulf } 1648d23cb03Swulf } 165af2cfdd7Skre #else /* !SMALL */ 166af2cfdd7Skre /* 167af2cfdd7Skre * Print the names of all the signals (neatly) to fp 168af2cfdd7Skre * "len" gives the number of chars already printed to 169af2cfdd7Skre * the current output line (in kill.c, always 0) 170af2cfdd7Skre */ 171af2cfdd7Skre void 172af2cfdd7Skre printsignals(struct output *out, int len) 173af2cfdd7Skre { 174af2cfdd7Skre int sig; 175af2cfdd7Skre int nl, pad; 176af2cfdd7Skre const char *name; 177af2cfdd7Skre int termwidth = 80; 178af2cfdd7Skre 179af2cfdd7Skre if ((name = bltinlookup("COLUMNS", 1)) != NULL) 180af2cfdd7Skre termwidth = (int)strtol(name, NULL, 10); 181af2cfdd7Skre else if (isatty(1)) { 182af2cfdd7Skre struct winsize win; 183af2cfdd7Skre 184af2cfdd7Skre if (ioctl(1, TIOCGWINSZ, &win) == 0 && win.ws_col > 0) 185af2cfdd7Skre termwidth = win.ws_col; 186af2cfdd7Skre } 187af2cfdd7Skre 188af2cfdd7Skre if (posix) 189af2cfdd7Skre pad = 1; 190af2cfdd7Skre else 191af2cfdd7Skre pad = (len | 7) + 1 - len; 192af2cfdd7Skre 193af2cfdd7Skre for (sig = 0; (sig = signalnext(sig)) != 0; ) { 194af2cfdd7Skre name = signalname(sig); 195af2cfdd7Skre if (name == NULL) 196af2cfdd7Skre continue; 197af2cfdd7Skre 198af2cfdd7Skre nl = strlen(name); 199af2cfdd7Skre 200af2cfdd7Skre if (len > 0 && nl + len + pad >= termwidth) { 201af2cfdd7Skre outc('\n', out); 202af2cfdd7Skre len = 0; 203af2cfdd7Skre pad = 0; 204af2cfdd7Skre } else if (pad > 0 && len != 0) 205af2cfdd7Skre outfmt(out, "%*s", pad, ""); 206af2cfdd7Skre else 207af2cfdd7Skre pad = 0; 208af2cfdd7Skre 209af2cfdd7Skre len += nl + pad; 210af2cfdd7Skre if (!posix) 211af2cfdd7Skre pad = (nl | 7) + 1 - nl; 212af2cfdd7Skre else 213af2cfdd7Skre pad = 1; 214af2cfdd7Skre 215af2cfdd7Skre outstr(name, out); 216af2cfdd7Skre } 217af2cfdd7Skre if (len != 0) 218af2cfdd7Skre outc('\n', out); 219af2cfdd7Skre } 220af2cfdd7Skre #endif /* SMALL */ 2219d255ec4Schristos 22261f28255Scgd /* 22361f28255Scgd * The trap builtin. 22461f28255Scgd */ 22561f28255Scgd 2264ce0d34aScgd int 227c02b3bbdSchristos trapcmd(int argc, char **argv) 2284ce0d34aScgd { 22961f28255Scgd char *action; 23061f28255Scgd char **ap; 23161f28255Scgd int signo; 2326f0d4805Skre int errs = 0; 2335791be97Skre int printonly = 0; 2346f0d4805Skre 2356f0d4805Skre ap = argv + 1; 2366f0d4805Skre 23713fc5c1bSkre CTRACE(DBG_TRAP, ("trapcmd: ")); 2384a370dceSkre if (argc == 3 && strcmp(ap[1], "--") == 0) 2394a370dceSkre argc--; 2406f0d4805Skre if (argc == 2 && strcmp(*ap, "-l") == 0) { 24113fc5c1bSkre CTRACE(DBG_TRAP, ("-l\n")); 24233a05de6Skre out1str("EXIT"); 24333a05de6Skre printsignals(out1, 4); 2446f0d4805Skre return 0; 2456f0d4805Skre } 2465791be97Skre if (argc == 2 && strcmp(*ap, "-") == 0) { 24713fc5c1bSkre CTRACE(DBG_TRAP, ("-\n")); 2485791be97Skre for (signo = 0; signo < NSIG; signo++) { 2495791be97Skre if (trap[signo] == NULL) 2505791be97Skre continue; 2515791be97Skre INTOFF; 2525791be97Skre ckfree(trap[signo]); 2535791be97Skre trap[signo] = NULL; 2545791be97Skre if (signo != 0) 2555791be97Skre setsignal(signo, 0); 2565791be97Skre INTON; 2575791be97Skre } 25813fc5c1bSkre traps_invalid = 0; 2595791be97Skre return 0; 2605791be97Skre } 2614a370dceSkre if (argc >= 2 && (strcmp(*ap, "-p") == 0 || strcmp(*ap, "-P") == 0)) { 2624a370dceSkre CTRACE(DBG_TRAP, ("%s ", *ap)); 2634a370dceSkre printonly = 1 + (ap[0][1] == 'p'); 2645791be97Skre ap++; 2655791be97Skre argc--; 2665791be97Skre } 2676f0d4805Skre 2686f0d4805Skre if (argc > 1 && strcmp(*ap, "--") == 0) { 2696f0d4805Skre argc--; 2706f0d4805Skre ap++; 2716f0d4805Skre } 27261f28255Scgd 2734a370dceSkre if (printonly == 1 && argc < 2) 2744a370dceSkre goto usage; 2754a370dceSkre 27661f28255Scgd if (argc <= 1) { 2775791be97Skre int count; 2785791be97Skre 27913fc5c1bSkre CTRACE(DBG_TRAP, ("*all*\n")); 2805791be97Skre if (printonly) { 281e904ec40Skre for (count = 0, signo = 0 ; signo < NSIG ; signo++) { 282e904ec40Skre if (signo == SIGKILL || signo == SIGSTOP) 283e904ec40Skre continue; 2845791be97Skre if (trap[signo] == NULL) { 2855791be97Skre if (count == 0) 2865791be97Skre out1str("trap -- -"); 2875791be97Skre out1fmt(" %s", trap_signame(signo)); 2885791be97Skre /* oh! unlucky 13 */ 2895791be97Skre if (++count >= 13) { 2905791be97Skre out1str("\n"); 2915791be97Skre count = 0; 29261f28255Scgd } 2935791be97Skre } 294e904ec40Skre } 2955791be97Skre if (count) 2965791be97Skre out1str("\n"); 2975791be97Skre } 2985791be97Skre 299e904ec40Skre /* 300e904ec40Skre * We don't need do deal with SIGSTOP or SIGKILL as a 301e904ec40Skre * special case anywhere here, as they cannot be 302e904ec40Skre * ignored or caught - the only possibility is default 303e904ec40Skre */ 3045791be97Skre for (count = 0, signo = 0 ; signo < NSIG ; signo++) 3055791be97Skre if (trap[signo] != NULL && trap[signo][0] == '\0') { 3065791be97Skre if (count == 0) 3075791be97Skre out1str("trap -- ''"); 3085791be97Skre out1fmt(" %s", trap_signame(signo)); 3095791be97Skre /* 3105791be97Skre * the prefix is 10 bytes, with 4 byte 3115791be97Skre * signal names (common) we have room in 3125791be97Skre * the 70 bytes left on a normal line for 3135791be97Skre * 70/(4+1) signals, that's 14, but to 3145791be97Skre * allow for the occasional longer sig name 3155791be97Skre * we output one less... 3165791be97Skre */ 3175791be97Skre if (++count >= 13) { 3185791be97Skre out1str("\n"); 3195791be97Skre count = 0; 3205791be97Skre } 3215791be97Skre } 3225791be97Skre if (count) 3235791be97Skre out1str("\n"); 3245791be97Skre 3255791be97Skre for (signo = 0 ; signo < NSIG ; signo++) 3265791be97Skre if (trap[signo] != NULL && trap[signo][0] != '\0') { 3275791be97Skre out1str("trap -- "); 3285791be97Skre print_quoted(trap[signo]); 3295791be97Skre out1fmt(" %s\n", trap_signame(signo)); 3305791be97Skre } 3315791be97Skre 33261f28255Scgd return 0; 33361f28255Scgd } 33413fc5c1bSkre CTRACE(DBG_TRAP, ("\n")); 3358d23cb03Swulf 33661f28255Scgd action = NULL; 3378d23cb03Swulf 33813fc5c1bSkre if (!printonly && traps_invalid) 33913fc5c1bSkre free_traps(); 34013fc5c1bSkre 3415791be97Skre if (!printonly && !is_number(*ap)) { 3426f0d4805Skre if ((*ap)[0] == '-' && (*ap)[1] == '\0') 3436f0d4805Skre ap++; /* reset to default */ 3446f0d4805Skre else 3456f0d4805Skre action = *ap++; /* can be '' for "ignore" */ 3466f0d4805Skre argc--; 3476f0d4805Skre } 3488d23cb03Swulf 3496f0d4805Skre if (argc < 2) { /* there must be at least 1 condition */ 3504a370dceSkre usage: 3516f0d4805Skre out2str("Usage: trap [-l]\n" 3525791be97Skre " trap -p [condition ...]\n" 3534a370dceSkre " trap -P condition ...\n" 3546f0d4805Skre " trap action condition ...\n" 3556f0d4805Skre " trap N condition ...\n"); 3566f0d4805Skre return 2; 3578d23cb03Swulf } 3586f0d4805Skre 3598d23cb03Swulf 36061f28255Scgd while (*ap) { 3618d23cb03Swulf signo = signame_to_signum(*ap); 3628d23cb03Swulf 3635791be97Skre if (signo < 0 || signo >= NSIG) { 3646f0d4805Skre /* This is not a fatal error, so sayeth posix */ 3656f0d4805Skre outfmt(out2, "trap: '%s' bad condition\n", *ap); 3666f0d4805Skre errs = 1; 3675791be97Skre ap++; 3685791be97Skre continue; 3695791be97Skre } 3705791be97Skre ap++; 3715791be97Skre 3725791be97Skre if (printonly) { 373e904ec40Skre /* 374e904ec40Skre * we allow SIGSTOP and SIGKILL to be obtained 375e904ec40Skre * (action will always be "-") here, if someone 376e904ec40Skre * really wants to get that particular output 377e904ec40Skre */ 3784a370dceSkre if (printonly == 1) { 3794a370dceSkre if (trap[signo] != NULL) 3804a370dceSkre out1fmt("%s\n", trap[signo]); 3814a370dceSkre } else { 3825791be97Skre out1str("trap -- "); 3835791be97Skre if (trap[signo] == NULL) 3845791be97Skre out1str("-"); 3855791be97Skre else 3865791be97Skre print_quoted(trap[signo]); 3875791be97Skre out1fmt(" %s\n", trap_signame(signo)); 3884a370dceSkre } 3895791be97Skre continue; 3906f0d4805Skre } 3918d23cb03Swulf 392e904ec40Skre if ((signo == SIGKILL || signo == SIGSTOP) && action != NULL) { 393e904ec40Skre #ifndef SMALL 394e904ec40Skre /* 395e904ec40Skre * Don't bother with the error message in a SMALL shell 396e904ec40Skre * just ignore req and return error status silently 397e904ec40Skre * (POSIX says this is an "undefined" operation so 398e904ec40Skre * whatever we do is OK!) 399e904ec40Skre * 400e904ec40Skre * When we do generate an error, make it attempt match 401e904ec40Skre * the user's operand, as best we can reasonably. 402e904ec40Skre */ 403e904ec40Skre outfmt(out2, "trap: '%s%s' cannot be %s\n", 404e904ec40Skre (!is_alpha(ap[-1][0]) || 405e904ec40Skre strncasecmp(ap[-1], "sig", 3) == 0) ? "" : 406e904ec40Skre is_upper(ap[-1][0]) ? "SIG" : "sig", 407e904ec40Skre ap[-1], *action ? "caught" : "ignored"); 408e904ec40Skre #endif 409e904ec40Skre errs = 1; 410e904ec40Skre continue; 411e904ec40Skre } 412e904ec40Skre 41361f28255Scgd INTOFF; 41461f28255Scgd if (action) 41561f28255Scgd action = savestr(action); 4168d23cb03Swulf 41713fc5c1bSkre VTRACE(DBG_TRAP, ("trap for %d from %s%s%s to %s%s%s\n", signo, 41813fc5c1bSkre trap[signo] ? "'" : "", trap[signo] ? trap[signo] : "-", 41913fc5c1bSkre trap[signo] ? "'" : "", action ? "'" : "", 42013fc5c1bSkre action ? action : "-", action ? "'" : "")); 42113fc5c1bSkre 42261f28255Scgd if (trap[signo]) 42361f28255Scgd ckfree(trap[signo]); 4248d23cb03Swulf 42561f28255Scgd trap[signo] = action; 4268d23cb03Swulf 42761f28255Scgd if (signo != 0) 428edcb4544Schristos setsignal(signo, 0); 42961f28255Scgd INTON; 43061f28255Scgd } 4316f0d4805Skre return errs; 43261f28255Scgd } 43361f28255Scgd 43461f28255Scgd 43561f28255Scgd 43661f28255Scgd /* 437edcb4544Schristos * Clear traps on a fork or vfork. 438edcb4544Schristos * Takes one arg vfork, to tell it to not be destructive of 439edcb4544Schristos * the parents variables. 44061f28255Scgd */ 44161f28255Scgd void 442c02b3bbdSchristos clear_traps(int vforked) 443edcb4544Schristos { 44413fc5c1bSkre char * volatile *tp; 44561f28255Scgd 44613fc5c1bSkre VTRACE(DBG_TRAP, ("clear_traps(%d)\n", vforked)); 44713fc5c1bSkre if (!vforked) 44813fc5c1bSkre traps_invalid = 1; 44913fc5c1bSkre 45013fc5c1bSkre for (tp = &trap[1] ; tp < &trap[NSIG] ; tp++) { 45161f28255Scgd if (*tp && **tp) { /* trap not NULL or SIG_IGN */ 45261f28255Scgd INTOFF; 45313fc5c1bSkre setsignal(tp - trap, vforked == 1); 45461f28255Scgd INTON; 45561f28255Scgd } 45661f28255Scgd } 45713fc5c1bSkre if (vforked == 2) 45813fc5c1bSkre free_traps(); 45913fc5c1bSkre } 46013fc5c1bSkre 46113fc5c1bSkre void 46213fc5c1bSkre free_traps(void) 46313fc5c1bSkre { 46413fc5c1bSkre char * volatile *tp; 46513fc5c1bSkre 46613fc5c1bSkre VTRACE(DBG_TRAP, ("free_traps%s\n", traps_invalid ? "(invalid)" : "")); 46713fc5c1bSkre INTOFF; 46813fc5c1bSkre for (tp = trap ; tp < &trap[NSIG] ; tp++) 46913fc5c1bSkre if (*tp && **tp) { 47013fc5c1bSkre ckfree(*tp); 47113fc5c1bSkre *tp = NULL; 47213fc5c1bSkre } 47313fc5c1bSkre traps_invalid = 0; 47413fc5c1bSkre INTON; 47561f28255Scgd } 47661f28255Scgd 4778a9a9619Skre /* 4788a9a9619Skre * See if there are any defined traps 4798a9a9619Skre */ 4808a9a9619Skre int 4818a9a9619Skre have_traps(void) 4828a9a9619Skre { 48313fc5c1bSkre char * volatile *tp; 48413fc5c1bSkre 48513fc5c1bSkre if (traps_invalid) 48613fc5c1bSkre return 0; 48761f28255Scgd 4888a9a9619Skre for (tp = trap ; tp < &trap[NSIG] ; tp++) 4898a9a9619Skre if (*tp && **tp) /* trap not NULL or SIG_IGN */ 4908a9a9619Skre return 1; 4918a9a9619Skre return 0; 4928a9a9619Skre } 49361f28255Scgd 49461f28255Scgd /* 49561f28255Scgd * Set the signal handler for the specified signal. The routine figures 49661f28255Scgd * out what it should be set to. 49761f28255Scgd */ 49813fc5c1bSkre void 499c02b3bbdSchristos setsignal(int signo, int vforked) 5004ce0d34aScgd { 50161f28255Scgd int action; 5023564ac65Schristos sig_t sigact = SIG_DFL, sig; 503edcb4544Schristos char *t, tsig; 50461f28255Scgd 505e8ed7c88Skre if (traps_invalid || (t = trap[signo]) == NULL) 50661f28255Scgd action = S_DFL; 50761f28255Scgd else if (*t != '\0') 50861f28255Scgd action = S_CATCH; 50961f28255Scgd else 51061f28255Scgd action = S_IGN; 51113fc5c1bSkre 51213fc5c1bSkre VTRACE(DBG_TRAP, ("setsignal(%d%s) -> %d", signo, 51313fc5c1bSkre vforked ? ", VF" : "", action)); 514edcb4544Schristos if (rootshell && !vforked && action == S_DFL) { 51561f28255Scgd switch (signo) { 51661f28255Scgd case SIGINT: 517832949b9Schristos if (iflag || minusc || sflag == 0) 51861f28255Scgd action = S_CATCH; 51961f28255Scgd break; 52061f28255Scgd case SIGQUIT: 52161f28255Scgd #ifdef DEBUG 52261f28255Scgd if (debug) 52361f28255Scgd break; 52461f28255Scgd #endif 52561f28255Scgd /* FALLTHROUGH */ 52661f28255Scgd case SIGTERM: 52713fc5c1bSkre if (rootshell && iflag) 52861f28255Scgd action = S_IGN; 52961f28255Scgd break; 53061f28255Scgd #if JOBS 53161f28255Scgd case SIGTSTP: 53261f28255Scgd case SIGTTOU: 53313fc5c1bSkre if (rootshell && mflag) 53461f28255Scgd action = S_IGN; 53561f28255Scgd break; 53661f28255Scgd #endif 53761f28255Scgd } 53861f28255Scgd } 539b842cc27Schristos 54013fc5c1bSkre /* 54113fc5c1bSkre * Never let users futz with SIGCHLD 54213fc5c1bSkre * instead we will give them pseudo SIGCHLD's 54313fc5c1bSkre * when background jobs complete. 54413fc5c1bSkre */ 54513fc5c1bSkre if (signo == SIGCHLD) 54613fc5c1bSkre action = S_DFL; 54713fc5c1bSkre 54813fc5c1bSkre VTRACE(DBG_TRAP, (" -> %d", action)); 54913fc5c1bSkre 55013fc5c1bSkre t = &sigmode[signo]; 551edcb4544Schristos tsig = *t; 552edcb4544Schristos if (tsig == 0) { 55361f28255Scgd /* 55437ed7877Sjtc * current setting unknown 55561f28255Scgd */ 5569d255ec4Schristos if (!getsigaction(signo, &sigact)) { 5579d255ec4Schristos /* 5589d255ec4Schristos * Pretend it worked; maybe we should give a warning 5599d255ec4Schristos * here, but other shells don't. We don't alter 5609d255ec4Schristos * sigmode, so that we retry every time. 5619d255ec4Schristos */ 56213fc5c1bSkre VTRACE(DBG_TRAP, (" getsigaction (%d)\n", errno)); 56313fc5c1bSkre return; 5649d255ec4Schristos } 56513fc5c1bSkre VTRACE(DBG_TRAP, (" [%s]%s%s", sigact==SIG_IGN ? "IGN" : 56613fc5c1bSkre sigact==SIG_DFL ? "DFL" : "caught", 56713fc5c1bSkre iflag ? "i" : "", mflag ? "m" : "")); 56813fc5c1bSkre 56961f28255Scgd if (sigact == SIG_IGN) { 5703564ac65Schristos /* 571e996b462Schristos * POSIX 3.14.13 states that non-interactive shells 572e996b462Schristos * should ignore trap commands for signals that were 573e996b462Schristos * ignored upon entry, and leaves the behavior 574e996b462Schristos * unspecified for interactive shells. On interactive 575e996b462Schristos * shells, or if job control is on, and we have a job 576e996b462Schristos * control related signal, we allow the trap to work. 577e996b462Schristos * 578e996b462Schristos * This change allows us to be POSIX compliant, and 579e996b462Schristos * at the same time override the default behavior if 580e996b462Schristos * we need to by setting the interactive flag. 5813564ac65Schristos */ 582e996b462Schristos if ((mflag && (signo == SIGTSTP || 583e996b462Schristos signo == SIGTTIN || signo == SIGTTOU)) || iflag) { 5843564ac65Schristos tsig = S_IGN; 585e996b462Schristos } else 586e996b462Schristos tsig = S_HARD_IGN; 58761f28255Scgd } else { 588edcb4544Schristos tsig = S_RESET; /* force to be set */ 58961f28255Scgd } 59061f28255Scgd } 59113fc5c1bSkre VTRACE(DBG_TRAP, (" tsig=%d\n", tsig)); 59213fc5c1bSkre 593edcb4544Schristos if (tsig == S_HARD_IGN || tsig == action) 59413fc5c1bSkre return; 59513fc5c1bSkre 59661f28255Scgd switch (action) { 59761f28255Scgd case S_DFL: sigact = SIG_DFL; break; 59861f28255Scgd case S_CATCH: sigact = onsig; break; 59961f28255Scgd case S_IGN: sigact = SIG_IGN; break; 60061f28255Scgd } 60113fc5c1bSkre 6023564ac65Schristos sig = signal(signo, sigact); 60313fc5c1bSkre 6043564ac65Schristos if (sig != SIG_ERR) { 6053564ac65Schristos sigset_t ss; 60613fc5c1bSkre 607edcb4544Schristos if (!vforked) 60861f28255Scgd *t = action; 60913fc5c1bSkre 6103564ac65Schristos if (action == S_CATCH) 6113564ac65Schristos (void)siginterrupt(signo, 1); 6123564ac65Schristos /* 6133564ac65Schristos * If our parent accidentally blocked signals for 6143564ac65Schristos * us make sure we unblock them 6153564ac65Schristos */ 6163564ac65Schristos (void)sigemptyset(&ss); 6173564ac65Schristos (void)sigaddset(&ss, signo); 6183564ac65Schristos (void)sigprocmask(SIG_UNBLOCK, &ss, NULL); 6193564ac65Schristos } 62013fc5c1bSkre return; 62161f28255Scgd } 62261f28255Scgd 62337ed7877Sjtc /* 62437ed7877Sjtc * Return the current setting for sig w/o changing it. 62537ed7877Sjtc */ 6269d255ec4Schristos static int 627c02b3bbdSchristos getsigaction(int signo, sig_t *sigact) 6284ce0d34aScgd { 62937ed7877Sjtc struct sigaction sa; 63037ed7877Sjtc 63137ed7877Sjtc if (sigaction(signo, (struct sigaction *)0, &sa) == -1) 6329d255ec4Schristos return 0; 6339d255ec4Schristos *sigact = (sig_t) sa.sa_handler; 6349d255ec4Schristos return 1; 63537ed7877Sjtc } 63661f28255Scgd 63761f28255Scgd /* 63861f28255Scgd * Ignore a signal. 63961f28255Scgd */ 64061f28255Scgd 64161f28255Scgd void 642c02b3bbdSchristos ignoresig(int signo, int vforked) 6434ce0d34aScgd { 64413fc5c1bSkre if (sigmode[signo] == 0) 64513fc5c1bSkre setsignal(signo, vforked); 64613fc5c1bSkre 64713fc5c1bSkre VTRACE(DBG_TRAP, ("ignoresig(%d%s)\n", signo, vforked ? ", VF" : "")); 64813fc5c1bSkre if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { 64961f28255Scgd signal(signo, SIG_IGN); 650edcb4544Schristos if (!vforked) 65113fc5c1bSkre sigmode[signo] = S_IGN; 65213fc5c1bSkre } 65313fc5c1bSkre } 65413fc5c1bSkre 65513fc5c1bSkre char * 65613fc5c1bSkre child_trap(void) 65713fc5c1bSkre { 65813fc5c1bSkre char * p; 65913fc5c1bSkre 66013fc5c1bSkre p = trap[SIGCHLD]; 66113fc5c1bSkre 662e8ed7c88Skre if (traps_invalid || (p != NULL && *p == '\0')) 66313fc5c1bSkre p = NULL; 66413fc5c1bSkre 66513fc5c1bSkre return p; 66661f28255Scgd } 66761f28255Scgd 66861f28255Scgd 66961f28255Scgd #ifdef mkinit 6701a3b3eb0Sjtc INCLUDE <signal.h> 67161f28255Scgd INCLUDE "trap.h" 67213fc5c1bSkre INCLUDE "shell.h" 67313fc5c1bSkre INCLUDE "show.h" 67461f28255Scgd 67561f28255Scgd SHELLPROC { 67661f28255Scgd char *sm; 67761f28255Scgd 67813fc5c1bSkre INTOFF; 67913fc5c1bSkre clear_traps(2); 6801a3b3eb0Sjtc for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { 68113fc5c1bSkre if (*sm == S_IGN) { 68261f28255Scgd *sm = S_HARD_IGN; 68378cb4ed2Skre VTRACE(DBG_TRAP, ("SHELLPROC: %d -> hard_ign\n", 68478cb4ed2Skre (sm - sigmode))); 68561f28255Scgd } 68661f28255Scgd } 68713fc5c1bSkre INTON; 68813fc5c1bSkre } 68961f28255Scgd #endif 69061f28255Scgd 69161f28255Scgd 69261f28255Scgd 69361f28255Scgd /* 69461f28255Scgd * Signal handler. 69561f28255Scgd */ 69661f28255Scgd 69761f28255Scgd void 698c02b3bbdSchristos onsig(int signo) 6994ce0d34aScgd { 700e904ec40Skre int sav_err = errno; 701e904ec40Skre 702e904ec40Skre CTRACE(DBG_SIG, ("onsig(%d), had: pendingsigs %d%s, gotsig[%d]=%d\n", 703e904ec40Skre signo, pendingsigs, intpending ? " (SIGINT-pending)" : "", 704e904ec40Skre signo, gotsig[signo])); 7052e1a04e9Skre 70613fc5c1bSkre /* This should not be needed. 70761f28255Scgd signal(signo, onsig); 70813fc5c1bSkre */ 70913fc5c1bSkre 710e8ed7c88Skre if (signo == SIGINT && (traps_invalid || trap[SIGINT] == NULL)) { 711e904ec40Skre VTRACE(DBG_SIG, ("onsig(SIGINT), doing it now\n")); 712e904ec40Skre if (suppressint && !in_dotrap) 713e904ec40Skre intpending = 1; 714e904ec40Skre else 71561f28255Scgd onint(); 716e904ec40Skre errno = sav_err; 71761f28255Scgd return; 71861f28255Scgd } 71913fc5c1bSkre 72013fc5c1bSkre /* 72113fc5c1bSkre * if the signal will do nothing, no point reporting it 72213fc5c1bSkre */ 723e8ed7c88Skre if (!traps_invalid && trap[signo] != NULL && trap[signo][0] != '\0' && 72413fc5c1bSkre signo != SIGCHLD) { 7255791be97Skre gotsig[signo] = 1; 72661f28255Scgd pendingsigs++; 727e904ec40Skre if (iflag && signo == SIGINT) { 728e904ec40Skre if (!suppressint) { 729e904ec40Skre VTRACE(DBG_SIG, 730e904ec40Skre ("onsig: -i gotsig[INT]->%d pendingsigs->%d BANG\n", 731e904ec40Skre gotsig[SIGINT], pendingsigs)); 732e904ec40Skre onint(); 733e904ec40Skre errno = sav_err; 734e904ec40Skre return; 73561f28255Scgd } 736e904ec40Skre intpending = 1; 737e904ec40Skre } 738e904ec40Skre VTRACE(DBG_SIG, ("onsig: gotsig[%d]->%d pendingsigs->%d%s\n", 739e904ec40Skre signo, gotsig[signo], pendingsigs, 740e904ec40Skre intpending ? " (SIGINT pending)":"")); 741e904ec40Skre } 742e904ec40Skre errno = sav_err; 74313fc5c1bSkre } 74461f28255Scgd 74561f28255Scgd 74661f28255Scgd 74761f28255Scgd /* 74861f28255Scgd * Called to execute a trap. Perhaps we should avoid entering new trap 74961f28255Scgd * handlers while we are executing a trap handler. 75061f28255Scgd */ 75161f28255Scgd 75261f28255Scgd void 753c02b3bbdSchristos dotrap(void) 754c02b3bbdSchristos { 75561f28255Scgd int i; 756c19b98a9Skre char *tr; 75713fc5c1bSkre int savestatus; 75813fc5c1bSkre struct skipsave saveskip; 75961f28255Scgd 760e904ec40Skre CTRACE(DBG_TRAP|DBG_SIG, ("dotrap[%d]: %d pending, traps %sinvalid\n", 76113fc5c1bSkre in_dotrap, pendingsigs, traps_invalid ? "" : "not ")); 762e904ec40Skre 763e904ec40Skre in_dotrap++; 76461f28255Scgd for (;;) { 7655791be97Skre pendingsigs = 0; 76613fc5c1bSkre for (i = 1 ; ; i++) { 767e904ec40Skre if (i >= NSIG) { 768e904ec40Skre in_dotrap--; 769e904ec40Skre VTRACE(DBG_TRAP|DBG_SIG, ("dotrap[%d] done\n", 770e904ec40Skre in_dotrap)); 7715791be97Skre return; 772e904ec40Skre } 7735791be97Skre if (gotsig[i]) 7745791be97Skre break; 7755791be97Skre } 7765791be97Skre gotsig[i] = 0; 77713fc5c1bSkre 77813fc5c1bSkre if (traps_invalid) 77913fc5c1bSkre continue; 78013fc5c1bSkre 78113fc5c1bSkre tr = trap[i]; 78213fc5c1bSkre 78313fc5c1bSkre CTRACE(DBG_TRAP|DBG_SIG, ("dotrap %d: %s%s%s\n", i, 78413fc5c1bSkre tr ? "\"" : "", tr ? tr : "NULL", tr ? "\"" : "")); 78513fc5c1bSkre 78613fc5c1bSkre if (tr != NULL) { 78713fc5c1bSkre last_trapsig = i; 78813fc5c1bSkre save_skipstate(&saveskip); 78961f28255Scgd savestatus = exitstatus; 79013fc5c1bSkre 791ee4a694aSkre tr = savestr(tr); /* trap code may free trap[i] */ 792c19b98a9Skre evalstring(tr, 0); 793c19b98a9Skre ckfree(tr); 79413fc5c1bSkre 79513fc5c1bSkre if (current_skipstate() == SKIPNONE || 79613fc5c1bSkre saveskip.state != SKIPNONE) { 79713fc5c1bSkre restore_skipstate(&saveskip); 79861f28255Scgd exitstatus = savestatus; 79961f28255Scgd } 80061f28255Scgd } 80113fc5c1bSkre } 80213fc5c1bSkre } 80361f28255Scgd 804f95d5940Schristos int 805f95d5940Schristos lastsig(void) 806f95d5940Schristos { 807f95d5940Schristos int i; 80861f28255Scgd 8095791be97Skre for (i = NSIG; --i > 0; ) 8105791be97Skre if (gotsig[i]) 811f95d5940Schristos return i; 812f95d5940Schristos return SIGINT; /* XXX */ 813f95d5940Schristos } 81461f28255Scgd 81561f28255Scgd /* 81661f28255Scgd * Controls whether the shell is interactive or not. 81761f28255Scgd */ 81861f28255Scgd 81961f28255Scgd 82061f28255Scgd void 821c02b3bbdSchristos setinteractive(int on) 8224ce0d34aScgd { 82337ed7877Sjtc static int is_interactive; 82437ed7877Sjtc 82561f28255Scgd if (on == is_interactive) 82661f28255Scgd return; 827edcb4544Schristos setsignal(SIGINT, 0); 828edcb4544Schristos setsignal(SIGQUIT, 0); 829edcb4544Schristos setsignal(SIGTERM, 0); 83061f28255Scgd is_interactive = on; 83161f28255Scgd } 83261f28255Scgd 83361f28255Scgd 83461f28255Scgd 83561f28255Scgd /* 83661f28255Scgd * Called to exit the shell. 83761f28255Scgd */ 83861f28255Scgd void 839c02b3bbdSchristos exitshell(int status) 8404ce0d34aScgd { 84113fc5c1bSkre CTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP, 84213fc5c1bSkre ("pid %d: exitshell(%d)\n", getpid(), status)); 84313fc5c1bSkre 84413fc5c1bSkre exiting = 1; 84513fc5c1bSkre exiting_status = status; 84613fc5c1bSkre exitshell_savedstatus(); 84713fc5c1bSkre } 84813fc5c1bSkre 84913fc5c1bSkre void 85013fc5c1bSkre exitshell_savedstatus(void) 85113fc5c1bSkre { 85213fc5c1bSkre struct jmploc loc; 85361f28255Scgd char *p; 85413fc5c1bSkre volatile int sig = 0; 85513fc5c1bSkre int s; 85613fc5c1bSkre sigset_t sigs; 85761f28255Scgd 8582e1a04e9Skre CTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP, 85913fc5c1bSkre ("pid %d: exitshell_savedstatus()%s $?=%d xs=%d dt=%d ts=%d\n", 86013fc5c1bSkre getpid(), exiting ? " exiting" : "", exitstatus, 86113fc5c1bSkre exiting_status, in_dotrap, last_trapsig)); 8622e1a04e9Skre 86313fc5c1bSkre if (!exiting) { 86413fc5c1bSkre if (in_dotrap && last_trapsig) { 86513fc5c1bSkre sig = last_trapsig; 86613fc5c1bSkre exiting_status = sig + 128; 86713fc5c1bSkre } else 86813fc5c1bSkre exiting_status = exitstatus; 86937ed7877Sjtc } 87013fc5c1bSkre exitstatus = exiting_status; 87113fc5c1bSkre 872ff46268cSkre if (pendingsigs && !setjmp(loc.loc)) { 873ff46268cSkre handler = &loc; 874ff46268cSkre 875ff46268cSkre dotrap(); 876ff46268cSkre } 877ff46268cSkre 87813fc5c1bSkre if (!setjmp(loc.loc)) { 87913fc5c1bSkre handler = &loc; 88013fc5c1bSkre 88113fc5c1bSkre if (!traps_invalid && (p = trap[0]) != NULL && *p != '\0') { 88213fc5c1bSkre reset_eval(); 88361f28255Scgd trap[0] = NULL; 8842e1a04e9Skre VTRACE(DBG_TRAP, ("exit trap: \"%s\"\n", p)); 885d6ac7627Schristos evalstring(p, 0); 88661f28255Scgd } 88713fc5c1bSkre } 88813fc5c1bSkre 88913fc5c1bSkre INTOFF; /* we're done, no more interrupts. */ 89013fc5c1bSkre 891c591669fSkre #ifndef SMALL 892c591669fSkre if (rootshell) 893c591669fSkre save_sh_history(); 894c591669fSkre #endif 895c591669fSkre 89613fc5c1bSkre if (!setjmp(loc.loc)) { 89713fc5c1bSkre handler = &loc; /* probably unnecessary */ 89861f28255Scgd flushall(); 89961f28255Scgd #if JOBS 90061f28255Scgd setjobctl(0); 90161f28255Scgd #endif 90213fc5c1bSkre } 90313fc5c1bSkre 90413fc5c1bSkre if ((s = sig) != 0 && s != SIGSTOP && s != SIGTSTP && s != SIGTTIN && 90513fc5c1bSkre s != SIGTTOU) { 90613fc5c1bSkre struct rlimit nocore; 90713fc5c1bSkre 90813fc5c1bSkre /* 90913fc5c1bSkre * if the signal is of the core dump variety, don't... 91013fc5c1bSkre */ 91113fc5c1bSkre nocore.rlim_cur = nocore.rlim_max = 0; 91213fc5c1bSkre (void) setrlimit(RLIMIT_CORE, &nocore); 91313fc5c1bSkre 91413fc5c1bSkre signal(s, SIG_DFL); 91513fc5c1bSkre sigemptyset(&sigs); 91613fc5c1bSkre sigaddset(&sigs, s); 91713fc5c1bSkre sigprocmask(SIG_UNBLOCK, &sigs, NULL); 91813fc5c1bSkre 919b8bee70dSkre VTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP, 920b8bee70dSkre ("exitshell_savedstatus(): pid %d Death by signal %d\n", 921b8bee70dSkre getpid(), s)); 92213fc5c1bSkre kill(getpid(), s); 92313fc5c1bSkre } 924b8bee70dSkre VTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP, 925b8bee70dSkre ("exitshell_savedstatus(): pid %d exiting(%d)\n", 926b8bee70dSkre getpid(), exiting_status)); 92713fc5c1bSkre _exit(exiting_status); 9289dc385beSmycroft /* NOTREACHED */ 92961f28255Scgd } 930