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