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