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