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