1 /*        $NetBSD: trap.c,v 1.58 2024/10/09 13:43:33 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.58 2024/10/09 13:43:33 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 #include <errno.h>
49 #include <limits.h>
50 #include <termios.h>
51 
52 #undef    CEOF      /* from <termios.h> but concflicts with sh use */
53 
54 #include <sys/ioctl.h>
55 #include <sys/resource.h>
56 
57 #include "shell.h"
58 #include "main.h"
59 #include "nodes.h"  /* for other headers */
60 #include "eval.h"
61 #include "jobs.h"
62 #include "show.h"
63 #include "options.h"
64 #include "builtins.h"
65 #include "syntax.h"
66 #include "output.h"
67 #include "memalloc.h"
68 #include "error.h"
69 #include "trap.h"
70 #include "mystring.h"
71 #include "var.h"
72 
73 #ifndef SMALL
74 #include "myhistedit.h"
75 #endif
76 
77 /*
78  * Sigmode records the current value of the signal handlers for the various
79  * modes.  A value of zero means that the current handler is not known.
80  * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
81  */
82 
83 #define S_DFL 1                         /* default signal handling (SIG_DFL) */
84 #define S_CATCH 2             /* signal is caught */
85 #define S_IGN 3                         /* signal is ignored (SIG_IGN) */
86 #define S_HARD_IGN 4                    /* signal is ignored permenantly */
87 #define S_RESET 5             /* temporary - to reset a hard ignored sig */
88 
89 
90 MKINIT char sigmode[NSIG];    /* current value of signal */
91 static volatile sig_atomic_t gotsig[NSIG];/* indicates specified signal received */
92 volatile sig_atomic_t pendingsigs;      /* indicates some signal received */
93 
94 int traps_invalid;            /* in a subshell, but trap[] not yet cleared */
95 static char * volatile trap[NSIG];      /* trap handler commands */
96 static int in_dotrap;
97 static int last_trapsig;
98 
99 static int exiting;           /* exitshell() has been done */
100 static int exiting_status;    /* the status to use for exit() */
101 
102 static int getsigaction(int, sig_t *);
103 STATIC const char *trap_signame(int);
104 void printsignals(struct output *, int);
105 
106 /*
107  * return the signal number described by `p' (as a number or a name)
108  * or -1 if it isn't one
109  */
110 
111 int
signame_to_signum(const char * p)112 signame_to_signum(const char *p)
113 {
114           int i;
115 
116           if (is_number(p))
117                     return number(p);
118 
119           if (strcasecmp(p, "exit") == 0 )
120                     return 0;
121 
122           i = signalnumber(p);
123           if (i == 0)
124                     i = -1;
125           return i;
126 }
127 
128 /*
129  * return the name of a signal used by the "trap" command
130  */
131 STATIC const char *
trap_signame(int signo)132 trap_signame(int signo)
133 {
134           static char nbuf[12];
135           const char *p;
136 
137           if (signo == 0)
138                     return "EXIT";
139           p = signalname(signo);
140           if (p != NULL)
141                     return p;
142           (void)snprintf(nbuf, sizeof nbuf, "%d", signo);
143           return nbuf;
144 }
145 
146 #ifdef SMALL
147 /*
148  * Print a list of valid signal names
149  */
150 void
printsignals(struct output * out,int len)151 printsignals(struct output *out, int len)
152 {
153           int n;
154 
155           if (len != 0)
156                     outc(' ', out);
157           for (n = 1; n < NSIG; n++) {
158                     outfmt(out, "%s", trap_signame(n));
159                     if ((n == NSIG/2) || n == (NSIG - 1))
160                               outstr("\n", out);
161                     else
162                               outc(' ', out);
163           }
164 }
165 #else /* !SMALL */
166 /*
167  * Print the names of all the signals (neatly) to fp
168  * "len" gives the number of chars already printed to
169  * the current output line (in kill.c, always 0)
170  */
171 void
printsignals(struct output * out,int len)172 printsignals(struct output *out, int len)
173 {
174           int sig;
175           int nl, pad;
176           const char *name;
177           int termwidth = 80;
178 
179           if ((name = bltinlookup("COLUMNS", 1)) != NULL)
180                     termwidth = (int)strtol(name, NULL, 10);
181           else if (isatty(1)) {
182                     struct winsize win;
183 
184                     if (ioctl(1, TIOCGWINSZ, &win) == 0 && win.ws_col > 0)
185                               termwidth = win.ws_col;
186           }
187 
188           if (posix)
189                     pad = 1;
190           else
191                     pad = (len | 7) + 1 - len;
192 
193           for (sig = 0; (sig = signalnext(sig)) != 0; ) {
194                     name = signalname(sig);
195                     if (name == NULL)
196                               continue;
197 
198                     nl = strlen(name);
199 
200                     if (len > 0 && nl + len + pad >= termwidth) {
201                               outc('\n', out);
202                               len = 0;
203                               pad = 0;
204                     } else if (pad > 0 && len != 0)
205                               outfmt(out, "%*s", pad, "");
206                     else
207                               pad = 0;
208 
209                     len += nl + pad;
210                     if (!posix)
211                               pad = (nl | 7) + 1 - nl;
212                     else
213                               pad = 1;
214 
215                     outstr(name, out);
216           }
217           if (len != 0)
218                     outc('\n', out);
219 }
220 #endif /* SMALL */
221 
222 /*
223  * The trap builtin.
224  */
225 
226 int
trapcmd(int argc,char ** argv)227 trapcmd(int argc, char **argv)
228 {
229           char *action;
230           char **ap;
231           int signo;
232           int errs = 0;
233           int printonly = 0;
234 
235           ap = argv + 1;
236 
237           CTRACE(DBG_TRAP, ("trapcmd: "));
238           if (argc == 3 && strcmp(ap[1], "--") == 0)
239                     argc--;
240           if (argc == 2 && strcmp(*ap, "-l") == 0) {
241                     CTRACE(DBG_TRAP, ("-l\n"));
242                     out1str("EXIT");
243                     printsignals(out1, 4);
244                     return 0;
245           }
246           if (argc == 2 && strcmp(*ap, "-") == 0) {
247                     CTRACE(DBG_TRAP, ("-\n"));
248                     for (signo = 0; signo < NSIG; signo++) {
249                               if (trap[signo] == NULL)
250                                         continue;
251                               INTOFF;
252                               ckfree(trap[signo]);
253                               trap[signo] = NULL;
254                               if (signo != 0)
255                                         setsignal(signo, 0);
256                               INTON;
257                     }
258                     traps_invalid = 0;
259                     return 0;
260           }
261           if (argc >= 2 && (strcmp(*ap, "-p") == 0 || strcmp(*ap, "-P") == 0)) {
262                     CTRACE(DBG_TRAP, ("%s ", *ap));
263                     printonly = 1 + (ap[0][1] == 'p');
264                     ap++;
265                     argc--;
266           }
267 
268           if (argc > 1 && strcmp(*ap, "--") == 0) {
269                     argc--;
270                     ap++;
271           }
272 
273           if (printonly == 1 && argc < 2)
274                     goto usage;
275 
276           if (argc <= 1) {
277                     int count;
278 
279                     CTRACE(DBG_TRAP, ("*all*\n"));
280                     if (printonly) {
281                               for (count = 0, signo = 0 ; signo < NSIG ; signo++) {
282                                         if (signo == SIGKILL || signo == SIGSTOP)
283                                                   continue;
284                                         if (trap[signo] == NULL) {
285                                                   if (count == 0)
286                                                             out1str("trap -- -");
287                                                   out1fmt(" %s", trap_signame(signo));
288                                                   /* oh! unlucky 13 */
289                                                   if (++count >= 13) {
290                                                             out1str("\n");
291                                                             count = 0;
292                                                   }
293                                         }
294                               }
295                               if (count)
296                                         out1str("\n");
297                     }
298 
299                     /*
300                      * We don't need do deal with SIGSTOP or SIGKILL as a
301                      * special case anywhere here, as they cannot be
302                      * ignored or caught - the only possibility is default
303                      */
304                     for (count = 0, signo = 0 ; signo < NSIG ; signo++)
305                               if (trap[signo] != NULL && trap[signo][0] == '\0') {
306                                         if (count == 0)
307                                                   out1str("trap -- ''");
308                                         out1fmt(" %s", trap_signame(signo));
309                                         /*
310                                          * the prefix is 10 bytes, with 4 byte
311                                          * signal names (common) we have room in
312                                          * the 70 bytes left on a normal line for
313                                          * 70/(4+1) signals, that's 14, but to
314                                          * allow for the occasional longer sig name
315                                          * we output one less...
316                                          */
317                                         if (++count >= 13) {
318                                                   out1str("\n");
319                                                   count = 0;
320                                         }
321                               }
322                     if (count)
323                               out1str("\n");
324 
325                     for (signo = 0 ; signo < NSIG ; signo++)
326                               if (trap[signo] != NULL && trap[signo][0] != '\0') {
327                                         out1str("trap -- ");
328                                         print_quoted(trap[signo]);
329                                         out1fmt(" %s\n", trap_signame(signo));
330                               }
331 
332                     return 0;
333           }
334           CTRACE(DBG_TRAP, ("\n"));
335 
336           action = NULL;
337 
338           if (!printonly && traps_invalid)
339                     free_traps();
340 
341           if (!printonly && !is_number(*ap)) {
342                     if ((*ap)[0] == '-' && (*ap)[1] == '\0')
343                               ap++;                         /* reset to default */
344                     else
345                               action = *ap++;               /* can be '' for "ignore" */
346                     argc--;
347           }
348 
349           if (argc < 2) {               /* there must be at least 1 condition */
350  usage:
351                     out2str("Usage: trap [-l]\n"
352                               "       trap -p [condition ...]\n"
353                               "       trap -P  condition ...\n"
354                               "       trap action condition ...\n"
355                               "       trap N condition ...\n");
356                     return 2;
357           }
358 
359 
360           while (*ap) {
361                     signo = signame_to_signum(*ap);
362 
363                     if (signo < 0 || signo >= NSIG) {
364                               /* This is not a fatal error, so sayeth posix */
365                               outfmt(out2, "trap: '%s' bad condition\n", *ap);
366                               errs = 1;
367                               ap++;
368                               continue;
369                     }
370                     ap++;
371 
372                     if (printonly) {
373                               /*
374                                * we allow SIGSTOP and SIGKILL to be obtained
375                                * (action will always be "-") here, if someone
376                                * really wants to get that particular output
377                                */
378                               if (printonly == 1) {
379                                         if (trap[signo] != NULL)
380                                                   out1fmt("%s\n", trap[signo]);
381                               } else {
382                                         out1str("trap -- ");
383                                         if (trap[signo] == NULL)
384                                                   out1str("-");
385                                         else
386                                                   print_quoted(trap[signo]);
387                                         out1fmt(" %s\n", trap_signame(signo));
388                               }
389                               continue;
390                     }
391 
392                     if ((signo == SIGKILL || signo == SIGSTOP) && action != NULL) {
393 #ifndef SMALL
394                               /*
395                                * Don't bother with the error message in a SMALL shell
396                                * just ignore req and  return error status silently
397                                * (POSIX says this is an "undefined" operation so
398                                * whatever we do is OK!)
399                                *
400                                * When we do generate an error, make it attempt match
401                                * the user's operand, as best we can reasonably.
402                                */
403                               outfmt(out2, "trap: '%s%s' cannot be %s\n",
404                                   (!is_alpha(ap[-1][0]) ||
405                                         strncasecmp(ap[-1], "sig", 3) == 0) ? "" :
406                                         is_upper(ap[-1][0]) ? "SIG" : "sig",
407                                   ap[-1], *action ? "caught" : "ignored");
408 #endif
409                               errs = 1;
410                               continue;
411                     }
412 
413                     INTOFF;
414                     if (action)
415                               action = savestr(action);
416 
417                     VTRACE(DBG_TRAP, ("trap for %d from %s%s%s to %s%s%s\n", signo,
418                               trap[signo] ? "'" : "", trap[signo] ? trap[signo] : "-",
419                               trap[signo] ? "'" : "", action ?  "'" : "",
420                               action ? action : "-", action ?  "'" : ""));
421 
422                     if (trap[signo])
423                               ckfree(trap[signo]);
424 
425                     trap[signo] = action;
426 
427                     if (signo != 0)
428                               setsignal(signo, 0);
429                     INTON;
430           }
431           return errs;
432 }
433 
434 
435 
436 /*
437  * Clear traps on a fork or vfork.
438  * Takes one arg vfork, to tell it to not be destructive of
439  * the parents variables.
440  */
441 void
clear_traps(int vforked)442 clear_traps(int vforked)
443 {
444           char * volatile *tp;
445 
446           VTRACE(DBG_TRAP, ("clear_traps(%d)\n", vforked));
447           if (!vforked)
448                     traps_invalid = 1;
449 
450           for (tp = &trap[1] ; tp < &trap[NSIG] ; tp++) {
451                     if (*tp && **tp) {  /* trap not NULL or SIG_IGN */
452                               INTOFF;
453                               setsignal(tp - trap, vforked == 1);
454                               INTON;
455                     }
456           }
457           if (vforked == 2)
458                     free_traps();
459 }
460 
461 void
free_traps(void)462 free_traps(void)
463 {
464           char * volatile *tp;
465 
466           VTRACE(DBG_TRAP, ("free_traps%s\n", traps_invalid ? "(invalid)" : ""));
467           INTOFF;
468           for (tp = trap ; tp < &trap[NSIG] ; tp++)
469                     if (*tp && **tp) {
470                               ckfree(*tp);
471                               *tp = NULL;
472                     }
473           traps_invalid = 0;
474           INTON;
475 }
476 
477 /*
478  * See if there are any defined traps
479  */
480 int
have_traps(void)481 have_traps(void)
482 {
483           char * volatile *tp;
484 
485           if (traps_invalid)
486                     return 0;
487 
488           for (tp = trap ; tp < &trap[NSIG] ; tp++)
489                     if (*tp && **tp)    /* trap not NULL or SIG_IGN */
490                               return 1;
491           return 0;
492 }
493 
494 /*
495  * Set the signal handler for the specified signal.  The routine figures
496  * out what it should be set to.
497  */
498 void
setsignal(int signo,int vforked)499 setsignal(int signo, int vforked)
500 {
501           int action;
502           sig_t sigact = SIG_DFL, sig;
503           char *t, tsig;
504 
505           if (traps_invalid || (t = trap[signo]) == NULL)
506                     action = S_DFL;
507           else if (*t != '\0')
508                     action = S_CATCH;
509           else
510                     action = S_IGN;
511 
512           VTRACE(DBG_TRAP, ("setsignal(%d%s) -> %d", signo,
513               vforked ? ", VF" : "", action));
514           if (rootshell && !vforked && action == S_DFL) {
515                     switch (signo) {
516                     case SIGINT:
517                               if (iflag || minusc || sflag == 0)
518                                         action = S_CATCH;
519                               break;
520                     case SIGQUIT:
521 #ifdef DEBUG
522                               if (debug)
523                                         break;
524 #endif
525                               /* FALLTHROUGH */
526                     case SIGTERM:
527                               if (rootshell && iflag)
528                                         action = S_IGN;
529                               break;
530 #if JOBS
531                     case SIGTSTP:
532                     case SIGTTOU:
533                               if (rootshell && mflag)
534                                         action = S_IGN;
535                               break;
536 #endif
537                     }
538           }
539 
540           /*
541            * Never let users futz with SIGCHLD
542            * instead we will give them pseudo SIGCHLD's
543            * when background jobs complete.
544            */
545           if (signo == SIGCHLD)
546                     action = S_DFL;
547 
548           VTRACE(DBG_TRAP, (" -> %d", action));
549 
550           t = &sigmode[signo];
551           tsig = *t;
552           if (tsig == 0) {
553                     /*
554                      * current setting unknown
555                      */
556                     if (!getsigaction(signo, &sigact)) {
557                               /*
558                                * Pretend it worked; maybe we should give a warning
559                                * here, but other shells don't. We don't alter
560                                * sigmode, so that we retry every time.
561                                */
562                               VTRACE(DBG_TRAP, (" getsigaction (%d)\n", errno));
563                               return;
564                     }
565                     VTRACE(DBG_TRAP, (" [%s]%s%s", sigact==SIG_IGN ? "IGN" :
566                         sigact==SIG_DFL ? "DFL" : "caught",
567                         iflag ? "i" : "", mflag ? "m" : ""));
568 
569                     if (sigact == SIG_IGN) {
570                               /*
571                                * POSIX 3.14.13 states that non-interactive shells
572                                * should ignore trap commands for signals that were
573                                * ignored upon entry, and leaves the behavior
574                                * unspecified for interactive shells. On interactive
575                                * shells, or if job control is on, and we have a job
576                                * control related signal, we allow the trap to work.
577                                *
578                                * This change allows us to be POSIX compliant, and
579                                * at the same time override the default behavior if
580                                * we need to by setting the interactive flag.
581                                */
582                               if ((mflag && (signo == SIGTSTP ||
583                                    signo == SIGTTIN || signo == SIGTTOU)) || iflag) {
584                                         tsig = S_IGN;
585                               } else
586                                         tsig = S_HARD_IGN;
587                     } else {
588                               tsig = S_RESET;     /* force to be set */
589                     }
590           }
591           VTRACE(DBG_TRAP, (" tsig=%d\n", tsig));
592 
593           if (tsig == S_HARD_IGN || tsig == action)
594                     return;
595 
596           switch (action) {
597                     case S_DFL:         sigact = SIG_DFL;   break;
598                     case S_CATCH:       sigact = onsig;               break;
599                     case S_IGN:         sigact = SIG_IGN;   break;
600           }
601 
602           sig = signal(signo, sigact);
603 
604           if (sig != SIG_ERR) {
605                     sigset_t ss;
606 
607                     if (!vforked)
608                               *t = action;
609 
610                     if (action == S_CATCH)
611                               (void)siginterrupt(signo, 1);
612                     /*
613                      * If our parent accidentally blocked signals for
614                      * us make sure we unblock them
615                      */
616                     (void)sigemptyset(&ss);
617                     (void)sigaddset(&ss, signo);
618                     (void)sigprocmask(SIG_UNBLOCK, &ss, NULL);
619           }
620           return;
621 }
622 
623 /*
624  * Return the current setting for sig w/o changing it.
625  */
626 static int
getsigaction(int signo,sig_t * sigact)627 getsigaction(int signo, sig_t *sigact)
628 {
629           struct sigaction sa;
630 
631           if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
632                     return 0;
633           *sigact = (sig_t) sa.sa_handler;
634           return 1;
635 }
636 
637 /*
638  * Ignore a signal.
639  */
640 
641 void
ignoresig(int signo,int vforked)642 ignoresig(int signo, int vforked)
643 {
644           if (sigmode[signo] == 0)
645                     setsignal(signo, vforked);
646 
647           VTRACE(DBG_TRAP, ("ignoresig(%d%s)\n", signo, vforked ? ", VF" : ""));
648           if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
649                     signal(signo, SIG_IGN);
650                     if (!vforked)
651                               sigmode[signo] = S_IGN;
652           }
653 }
654 
655 char *
child_trap(void)656 child_trap(void)
657 {
658           char * p;
659 
660           p = trap[SIGCHLD];
661 
662           if (traps_invalid || (p != NULL && *p == '\0'))
663                     p = NULL;
664 
665           return p;
666 }
667 
668 
669 #ifdef mkinit
670 INCLUDE <signal.h>
671 INCLUDE "trap.h"
672 INCLUDE "shell.h"
673 INCLUDE "show.h"
674 
675 SHELLPROC {
676           char *sm;
677 
678           INTOFF;
679           clear_traps(2);
680           for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
681                     if (*sm == S_IGN) {
682                               *sm = S_HARD_IGN;
683                               VTRACE(DBG_TRAP, ("SHELLPROC: %d -> hard_ign\n",
684                                   (sm - sigmode)));
685                     }
686           }
687           INTON;
688 }
689 #endif
690 
691 
692 
693 /*
694  * Signal handler.
695  */
696 
697 void
onsig(int signo)698 onsig(int signo)
699 {
700           int sav_err = errno;
701 
702           CTRACE(DBG_SIG, ("onsig(%d), had: pendingsigs %d%s, gotsig[%d]=%d\n",
703               signo, pendingsigs, intpending ? " (SIGINT-pending)" : "",
704               signo, gotsig[signo]));
705 
706           /*                            This should not be needed.
707           signal(signo, onsig);
708           */
709 
710           if (signo == SIGINT && (traps_invalid || trap[SIGINT] == NULL)) {
711                     VTRACE(DBG_SIG, ("onsig(SIGINT), doing it now\n"));
712                     if (suppressint && !in_dotrap)
713                               intpending = 1;
714                     else
715                               onint();
716                     errno = sav_err;
717                     return;
718           }
719 
720           /*
721            * if the signal will do nothing, no point reporting it
722            */
723           if (!traps_invalid && trap[signo] != NULL && trap[signo][0] != '\0' &&
724               signo != SIGCHLD) {
725                     gotsig[signo] = 1;
726                     pendingsigs++;
727                     if (iflag && signo == SIGINT) {
728                               if (!suppressint) {
729                                         VTRACE(DBG_SIG,
730                                  ("onsig: -i gotsig[INT]->%d pendingsigs->%d  BANG\n",
731                                             gotsig[SIGINT], pendingsigs));
732                                         onint();
733                                         errno = sav_err;
734                                         return;
735                               }
736                               intpending = 1;
737                     }
738                     VTRACE(DBG_SIG, ("onsig: gotsig[%d]->%d pendingsigs->%d%s\n",
739                         signo, gotsig[signo], pendingsigs,
740                         intpending ? " (SIGINT pending)":""));
741           }
742           errno = sav_err;
743 }
744 
745 
746 
747 /*
748  * Called to execute a trap.  Perhaps we should avoid entering new trap
749  * handlers while we are executing a trap handler.
750  */
751 
752 void
dotrap(void)753 dotrap(void)
754 {
755           int i;
756           char *tr;
757           int savestatus;
758           struct skipsave saveskip;
759 
760           CTRACE(DBG_TRAP|DBG_SIG, ("dotrap[%d]: %d pending, traps %sinvalid\n",
761               in_dotrap, pendingsigs, traps_invalid ? "" : "not "));
762 
763           in_dotrap++;
764           for (;;) {
765                     pendingsigs = 0;
766                     for (i = 1 ; ; i++) {
767                               if (i >= NSIG) {
768                                         in_dotrap--;
769                                         VTRACE(DBG_TRAP|DBG_SIG, ("dotrap[%d] done\n",
770                                             in_dotrap));
771                                         return;
772                               }
773                               if (gotsig[i])
774                                         break;
775                     }
776                     gotsig[i] = 0;
777 
778                     if (traps_invalid)
779                               continue;
780 
781                     tr = trap[i];
782 
783                     CTRACE(DBG_TRAP|DBG_SIG, ("dotrap %d: %s%s%s\n", i,
784                         tr ? "\"" : "", tr ? tr : "NULL", tr ? "\"" : ""));
785 
786                     if (tr != NULL) {
787                               last_trapsig = i;
788                               save_skipstate(&saveskip);
789                               savestatus = exitstatus;
790 
791                               tr = savestr(tr);   /* trap code may free trap[i] */
792                               evalstring(tr, 0);
793                               ckfree(tr);
794 
795                               if (current_skipstate() == SKIPNONE ||
796                                   saveskip.state != SKIPNONE) {
797                                         restore_skipstate(&saveskip);
798                                         exitstatus = savestatus;
799                               }
800                     }
801           }
802 }
803 
804 int
lastsig(void)805 lastsig(void)
806 {
807           int i;
808 
809           for (i = NSIG; --i > 0; )
810                     if (gotsig[i])
811                               return i;
812           return SIGINT;      /* XXX */
813 }
814 
815 /*
816  * Controls whether the shell is interactive or not.
817  */
818 
819 
820 void
setinteractive(int on)821 setinteractive(int on)
822 {
823           static int is_interactive;
824 
825           if (on == is_interactive)
826                     return;
827           setsignal(SIGINT, 0);
828           setsignal(SIGQUIT, 0);
829           setsignal(SIGTERM, 0);
830           is_interactive = on;
831 }
832 
833 
834 
835 /*
836  * Called to exit the shell.
837  */
838 void
exitshell(int status)839 exitshell(int status)
840 {
841           CTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP,
842               ("pid %d: exitshell(%d)\n", getpid(), status));
843 
844           exiting = 1;
845           exiting_status = status;
846           exitshell_savedstatus();
847 }
848 
849 void
exitshell_savedstatus(void)850 exitshell_savedstatus(void)
851 {
852           struct jmploc loc;
853           char *p;
854           volatile int sig = 0;
855           int s;
856           sigset_t sigs;
857 
858           CTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP,
859             ("pid %d: exitshell_savedstatus()%s $?=%d xs=%d dt=%d ts=%d\n",
860               getpid(), exiting ? " exiting" : "", exitstatus,
861               exiting_status, in_dotrap, last_trapsig));
862 
863           if (!exiting) {
864                     if (in_dotrap && last_trapsig) {
865                               sig = last_trapsig;
866                               exiting_status = sig + 128;
867                     } else
868                               exiting_status = exitstatus;
869           }
870           exitstatus = exiting_status;
871 
872           if (pendingsigs && !setjmp(loc.loc)) {
873                     handler = &loc;
874 
875                     dotrap();
876           }
877 
878           if (!setjmp(loc.loc)) {
879                     handler = &loc;
880 
881                     if (!traps_invalid && (p = trap[0]) != NULL && *p != '\0') {
882                               reset_eval();
883                               trap[0] = NULL;
884                               VTRACE(DBG_TRAP, ("exit trap: \"%s\"\n", p));
885                               evalstring(p, 0);
886                     }
887           }
888 
889           INTOFF;                       /*  we're done, no more interrupts. */
890 
891 #ifndef SMALL
892           if (rootshell)
893                     save_sh_history();
894 #endif
895 
896           if (!setjmp(loc.loc)) {
897                     handler = &loc;               /* probably unnecessary */
898                     flushall();
899 #if JOBS
900                     setjobctl(0);
901 #endif
902           }
903 
904           if ((s = sig) != 0 && s != SIGSTOP && s != SIGTSTP && s != SIGTTIN &&
905               s != SIGTTOU) {
906                     struct rlimit nocore;
907 
908                     /*
909                      * if the signal is of the core dump variety, don't...
910                      */
911                     nocore.rlim_cur = nocore.rlim_max = 0;
912                     (void) setrlimit(RLIMIT_CORE, &nocore);
913 
914                     signal(s, SIG_DFL);
915                     sigemptyset(&sigs);
916                     sigaddset(&sigs, s);
917                     sigprocmask(SIG_UNBLOCK, &sigs, NULL);
918 
919                     VTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP,
920                         ("exitshell_savedstatus(): pid %d Death by signal %d\n",
921                               getpid(), s));
922                     kill(getpid(), s);
923           }
924           VTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP,
925               ("exitshell_savedstatus(): pid %d exiting(%d)\n",
926                     getpid(), exiting_status));
927           _exit(exiting_status);
928           /* NOTREACHED */
929 }
930