]> git.saurik.com Git - apple/shell_cmds.git/blame_incremental - sh/trap.c
shell_cmds-216.60.1.tar.gz
[apple/shell_cmds.git] / sh / trap.c
... / ...
CommitLineData
1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36#if 0
37static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95";
38#endif
39#endif /* not lint */
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: head/bin/sh/trap.c 326025 2017-11-20 19:49:47Z pfg $");
42
43#include <signal.h>
44#include <unistd.h>
45#include <stdlib.h>
46
47#include "shell.h"
48#include "main.h"
49#include "nodes.h" /* for other headers */
50#include "eval.h"
51#include "jobs.h"
52#include "show.h"
53#include "options.h"
54#include "syntax.h"
55#include "output.h"
56#include "memalloc.h"
57#include "error.h"
58#include "trap.h"
59#include "mystring.h"
60#include "builtins.h"
61#include "myhistedit.h"
62
63#ifdef __APPLE__
64#define sys_nsig (NSIG)
65#endif /* __APPLE__ */
66
67/*
68 * Sigmode records the current value of the signal handlers for the various
69 * modes. A value of zero means that the current handler is not known.
70 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
71 */
72
73#define S_DFL 1 /* default signal handling (SIG_DFL) */
74#define S_CATCH 2 /* signal is caught */
75#define S_IGN 3 /* signal is ignored (SIG_IGN) */
76#define S_HARD_IGN 4 /* signal is ignored permanently */
77#define S_RESET 5 /* temporary - to reset a hard ignored sig */
78
79
80static char sigmode[NSIG]; /* current value of signal */
81volatile sig_atomic_t pendingsig; /* indicates some signal received */
82volatile sig_atomic_t pendingsig_waitcmd; /* indicates wait builtin should be interrupted */
83static int in_dotrap; /* do we execute in a trap handler? */
84static char *volatile trap[NSIG]; /* trap handler commands */
85static volatile sig_atomic_t gotsig[NSIG];
86 /* indicates specified signal received */
87static int ignore_sigchld; /* Used while handling SIGCHLD traps. */
88static int last_trapsig;
89
90static int exiting; /* exitshell() has been called */
91static int exiting_exitstatus; /* value passed to exitshell() */
92
93static int getsigaction(int, sig_t *);
94
95
96/*
97 * Map a string to a signal number.
98 *
99 * Note: the signal number may exceed NSIG.
100 */
101static int
102sigstring_to_signum(char *sig)
103{
104
105 if (is_number(sig)) {
106 int signo;
107
108 signo = atoi(sig);
109 return ((signo >= 0 && signo < NSIG) ? signo : (-1));
110 } else if (strcasecmp(sig, "EXIT") == 0) {
111 return (0);
112 } else {
113 int n;
114
115 if (strncasecmp(sig, "SIG", 3) == 0)
116 sig += 3;
117 for (n = 1; n < sys_nsig; n++)
118 if (sys_signame[n] &&
119 strcasecmp(sys_signame[n], sig) == 0)
120 return (n);
121 }
122 return (-1);
123}
124
125
126/*
127 * Print a list of valid signal names.
128 */
129static void
130printsignals(void)
131{
132 int n, outlen;
133
134 outlen = 0;
135 for (n = 1; n < sys_nsig; n++) {
136 if (sys_signame[n]) {
137 out1fmt("%s", sys_signame[n]);
138 outlen += strlen(sys_signame[n]);
139 } else {
140 out1fmt("%d", n);
141 outlen += 3; /* good enough */
142 }
143 ++outlen;
144 if (outlen > 71 || n == sys_nsig - 1) {
145 out1str("\n");
146 outlen = 0;
147 } else {
148 out1c(' ');
149 }
150 }
151}
152
153
154/*
155 * The trap builtin.
156 */
157int
158trapcmd(int argc __unused, char **argv)
159{
160 char *action;
161 int signo;
162 int errors = 0;
163 int i;
164
165 while ((i = nextopt("l")) != '\0') {
166 switch (i) {
167 case 'l':
168 printsignals();
169 return (0);
170 }
171 }
172 argv = argptr;
173
174 if (*argv == NULL) {
175 for (signo = 0 ; signo < sys_nsig ; signo++) {
176 if (signo < NSIG && trap[signo] != NULL) {
177 out1str("trap -- ");
178 out1qstr(trap[signo]);
179 if (signo == 0) {
180 out1str(" EXIT\n");
181 } else if (sys_signame[signo]) {
182 out1fmt(" %s\n", sys_signame[signo]);
183 } else {
184 out1fmt(" %d\n", signo);
185 }
186 }
187 }
188 return 0;
189 }
190 action = NULL;
191 if (*argv && !is_number(*argv)) {
192 if (strcmp(*argv, "-") == 0)
193 argv++;
194 else {
195 action = *argv;
196 argv++;
197 }
198 }
199 for (; *argv; argv++) {
200 if ((signo = sigstring_to_signum(*argv)) == -1) {
201 warning("bad signal %s", *argv);
202 errors = 1;
203 continue;
204 }
205 INTOFF;
206 if (action)
207 action = savestr(action);
208 if (trap[signo])
209 ckfree(trap[signo]);
210 trap[signo] = action;
211 if (signo != 0)
212 setsignal(signo);
213 INTON;
214 }
215 return errors;
216}
217
218
219/*
220 * Clear traps on a fork.
221 */
222void
223clear_traps(void)
224{
225 char *volatile *tp;
226
227 for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
228 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
229 INTOFF;
230 ckfree(*tp);
231 *tp = NULL;
232 if (tp != &trap[0])
233 setsignal(tp - trap);
234 INTON;
235 }
236 }
237}
238
239
240/*
241 * Check if we have any traps enabled.
242 */
243int
244have_traps(void)
245{
246 char *volatile *tp;
247
248 for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
249 if (*tp && **tp) /* trap not NULL or SIG_IGN */
250 return 1;
251 }
252 return 0;
253}
254
255/*
256 * Set the signal handler for the specified signal. The routine figures
257 * out what it should be set to.
258 */
259void
260setsignal(int signo)
261{
262 int action;
263 sig_t sigact = SIG_DFL;
264 struct sigaction sa;
265 char *t;
266
267 if ((t = trap[signo]) == NULL)
268 action = S_DFL;
269 else if (*t != '\0')
270 action = S_CATCH;
271 else
272 action = S_IGN;
273 if (action == S_DFL) {
274 switch (signo) {
275 case SIGINT:
276 action = S_CATCH;
277 break;
278 case SIGQUIT:
279#ifdef DEBUG
280 {
281 extern int debug;
282
283 if (debug)
284 break;
285 }
286#endif
287 action = S_CATCH;
288 break;
289 case SIGTERM:
290 if (rootshell && iflag)
291 action = S_IGN;
292 break;
293#if JOBS
294 case SIGTSTP:
295 case SIGTTOU:
296 if (rootshell && mflag)
297 action = S_IGN;
298 break;
299#endif
300 }
301 }
302
303 t = &sigmode[signo];
304 if (*t == 0) {
305 /*
306 * current setting unknown
307 */
308 if (!getsigaction(signo, &sigact)) {
309 /*
310 * Pretend it worked; maybe we should give a warning
311 * here, but other shells don't. We don't alter
312 * sigmode, so that we retry every time.
313 */
314 return;
315 }
316 if (sigact == SIG_IGN) {
317 if (mflag && (signo == SIGTSTP ||
318 signo == SIGTTIN || signo == SIGTTOU)) {
319 *t = S_IGN; /* don't hard ignore these */
320 } else
321 *t = S_HARD_IGN;
322 } else {
323 *t = S_RESET; /* force to be set */
324 }
325 }
326 if (*t == S_HARD_IGN || *t == action)
327 return;
328 switch (action) {
329 case S_DFL: sigact = SIG_DFL; break;
330 case S_CATCH: sigact = onsig; break;
331 case S_IGN: sigact = SIG_IGN; break;
332 }
333 *t = action;
334 sa.sa_handler = sigact;
335 sa.sa_flags = 0;
336 sigemptyset(&sa.sa_mask);
337 sigaction(signo, &sa, NULL);
338}
339
340
341/*
342 * Return the current setting for sig w/o changing it.
343 */
344static int
345getsigaction(int signo, sig_t *sigact)
346{
347 struct sigaction sa;
348
349 if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
350 return 0;
351 *sigact = (sig_t) sa.sa_handler;
352 return 1;
353}
354
355
356/*
357 * Ignore a signal.
358 */
359void
360ignoresig(int signo)
361{
362
363 if (sigmode[signo] == 0)
364 setsignal(signo);
365 if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
366 signal(signo, SIG_IGN);
367 sigmode[signo] = S_IGN;
368 }
369}
370
371
372int
373issigchldtrapped(void)
374{
375
376 return (trap[SIGCHLD] != NULL && *trap[SIGCHLD] != '\0');
377}
378
379
380/*
381 * Signal handler.
382 */
383void
384onsig(int signo)
385{
386
387 if (signo == SIGINT && trap[SIGINT] == NULL) {
388 /*
389 * The !in_dotrap here is safe. The only way we can arrive
390 * here with in_dotrap set is that a trap handler set SIGINT to
391 * SIG_DFL and killed itself.
392 */
393 if (suppressint && !in_dotrap)
394 SET_PENDING_INT;
395 else
396 onint();
397 return;
398 }
399
400 /* If we are currently in a wait builtin, prepare to break it */
401 if (signo == SIGINT || signo == SIGQUIT)
402 pendingsig_waitcmd = signo;
403
404 if (trap[signo] != NULL && trap[signo][0] != '\0' &&
405 (signo != SIGCHLD || !ignore_sigchld)) {
406 gotsig[signo] = 1;
407 pendingsig = signo;
408 pendingsig_waitcmd = signo;
409 }
410}
411
412
413/*
414 * Called to execute a trap. Perhaps we should avoid entering new trap
415 * handlers while we are executing a trap handler.
416 */
417void
418dotrap(void)
419{
420 struct stackmark smark;
421 int i;
422 int savestatus, prev_evalskip, prev_skipcount;
423
424 in_dotrap++;
425 for (;;) {
426 pendingsig = 0;
427 pendingsig_waitcmd = 0;
428 for (i = 1; i < NSIG; i++) {
429 if (gotsig[i]) {
430 gotsig[i] = 0;
431 if (trap[i]) {
432 /*
433 * Ignore SIGCHLD to avoid infinite
434 * recursion if the trap action does
435 * a fork.
436 */
437 if (i == SIGCHLD)
438 ignore_sigchld++;
439
440 /*
441 * Backup current evalskip
442 * state and reset it before
443 * executing a trap, so that the
444 * trap is not disturbed by an
445 * ongoing break/continue/return
446 * statement.
447 */
448 prev_evalskip = evalskip;
449 prev_skipcount = skipcount;
450 evalskip = 0;
451
452 last_trapsig = i;
453 savestatus = exitstatus;
454 setstackmark(&smark);
455 evalstring(stsavestr(trap[i]), 0);
456 popstackmark(&smark);
457
458 /*
459 * If such a command was not
460 * already in progress, allow a
461 * break/continue/return in the
462 * trap action to have an effect
463 * outside of it.
464 */
465 if (evalskip == 0 ||
466 prev_evalskip != 0) {
467 evalskip = prev_evalskip;
468 skipcount = prev_skipcount;
469 exitstatus = savestatus;
470 }
471
472 if (i == SIGCHLD)
473 ignore_sigchld--;
474 }
475 break;
476 }
477 }
478 if (i >= NSIG)
479 break;
480 }
481 in_dotrap--;
482}
483
484
485/*
486 * Controls whether the shell is interactive or not based on iflag.
487 */
488void
489setinteractive(void)
490{
491 setsignal(SIGINT);
492 setsignal(SIGQUIT);
493 setsignal(SIGTERM);
494}
495
496
497/*
498 * Called to exit the shell.
499 */
500void
501exitshell(int status)
502{
503 TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
504 exiting = 1;
505 exiting_exitstatus = status;
506 exitshell_savedstatus();
507}
508
509void
510exitshell_savedstatus(void)
511{
512 struct jmploc loc1, loc2;
513 char *p;
514 int sig = 0;
515 sigset_t sigs;
516
517 if (!exiting) {
518 if (in_dotrap && last_trapsig) {
519 sig = last_trapsig;
520 exiting_exitstatus = sig + 128;
521 } else
522 exiting_exitstatus = oexitstatus;
523 }
524 exitstatus = oexitstatus = exiting_exitstatus;
525 if (!setjmp(loc1.loc)) {
526 handler = &loc1;
527 if ((p = trap[0]) != NULL && *p != '\0') {
528 /*
529 * Reset evalskip, or the trap on EXIT could be
530 * interrupted if the last command was a "return".
531 */
532 evalskip = 0;
533 trap[0] = NULL;
534 FORCEINTON;
535 evalstring(p, 0);
536 }
537 }
538 if (!setjmp(loc2.loc)) {
539 handler = &loc2; /* probably unnecessary */
540 FORCEINTON;
541 flushall();
542#if JOBS
543 setjobctl(0);
544#endif
545 }
546 if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN &&
547 sig != SIGTTOU) {
548 signal(sig, SIG_DFL);
549 sigemptyset(&sigs);
550 sigaddset(&sigs, sig);
551 sigprocmask(SIG_UNBLOCK, &sigs, NULL);
552 kill(getpid(), sig);
553 /* If the default action is to ignore, fall back to _exit(). */
554 }
555 _exit(exiting_exitstatus);
556}