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