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#include "trap.h"
36#include "builtins.h"
37#include "error.h"
38#include "eval.h"
39#include "jobs.h"
40#include "main.h"
41#include "memalloc.h"
42#include "mystring.h"
43#include "nodes.h" /* for other headers */
44#include "options.h"
45#include "output.h"
46#include "shell.h"
47#include "show.h"
48#include "syntax.h"
49#ifndef NO_HISTORY
50#include "lineedit.h"
51#endif
52
53#include <signal.h>
54#include <stdlib.h>
55#include <unistd.h>
56
57/*
58 * Sigmode records the current value of the signal handlers for the various
59 * modes. A value of zero means that the current handler is not known.
60 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
61 */
62
63#define S_DFL 1 /* default signal handling (SIG_DFL) */
64#define S_CATCH 2 /* signal is caught */
65#define S_IGN 3 /* signal is ignored (SIG_IGN) */
66#define S_HARD_IGN 4 /* signal is ignored permanently */
67#define S_RESET 5 /* temporary - to reset a hard ignored sig */
68
69static char sigmode[NSIG]; /* current value of signal */
70volatile sig_atomic_t pendingsig; /* indicates some signal received */
71volatile sig_atomic_t pendingsig_waitcmd; /* indicates wait builtin should be interrupted */
72static int in_dotrap; /* do we execute in a trap handler? */
73static char *volatile trap[NSIG]; /* trap handler commands */
74static volatile sig_atomic_t gotsig[NSIG];
75/* indicates specified signal received */
76static int ignore_sigchld; /* Used while handling SIGCHLD traps. */
77static int last_trapsig;
78
79static int exiting; /* exitshell() has been called */
80static int exiting_exitstatus; /* value passed to exitshell() */
81
82static int getsigaction(int, sig_t *);
83
84/*
85 * Map a string to a signal number.
86 *
87 * Note: the signal number may exceed NSIG.
88 */
89static int
90sigstring_to_signum(char *sig)
91{
92 if (is_number(sig)) {
93 int signo;
94
95 signo = atoi(sig);
96 return ((signo >= 0 && signo < NSIG) ? signo : (-1));
97 } else if (strcasecmp(sig, "EXIT") == 0) {
98 return (0);
99 } else {
100 int n;
101
102 if (strncasecmp(sig, "SIG", 3) == 0)
103 sig += 3;
104 for (n = 1; n < sys_nsig; n++)
105 if (sys_signame[n] && strcasecmp(sys_signame[n], sig) == 0)
106 return (n);
107 }
108 return (-1);
109}
110
111/*
112 * Print a list of valid signal names.
113 */
114static void
115printsignals(void)
116{
117 int n, outlen;
118
119 outlen = 0;
120 for (n = 1; n < sys_nsig; n++) {
121 if (sys_signame[n]) {
122 out1fmt("%s", sys_signame[n]);
123 outlen += strlen(sys_signame[n]);
124 } else {
125 out1fmt("%d", n);
126 outlen += 3; /* good enough */
127 }
128 ++outlen;
129 if (outlen > 71 || n == sys_nsig - 1) {
130 out1str("\n");
131 outlen = 0;
132 } else {
133 out1c(' ');
134 }
135 }
136}
137
138/*
139 * The trap builtin.
140 */
141int
142trapcmd(int argc __unused, char **argv)
143{
144 char *action;
145 int signo;
146 int errors = 0;
147 int i;
148
149 while ((i = nextopt("l")) != '\0') {
150 switch (i) {
151 case 'l':
152 printsignals();
153 return (0);
154 }
155 }
156 argv = argptr;
157
158 if (*argv == NULL) {
159 for (signo = 0; signo < sys_nsig; signo++) {
160 if (signo < NSIG && trap[signo] != NULL) {
161 out1str("trap -- ");
162 out1qstr(trap[signo]);
163 if (signo == 0) {
164 out1str(" EXIT\n");
165 } else if (sys_signame[signo]) {
166 out1fmt(" %s\n", sys_signame[signo]);
167 } else {
168 out1fmt(" %d\n", signo);
169 }
170 }
171 }
172 return 0;
173 }
174 action = NULL;
175 if (*argv && !is_number(*argv)) {
176 if (strcmp(*argv, "-") == 0)
177 argv++;
178 else {
179 action = *argv;
180 argv++;
181 }
182 }
183 for (; *argv; argv++) {
184 if ((signo = sigstring_to_signum(*argv)) == -1) {
185 warning("bad signal %s", *argv);
186 errors = 1;
187 continue;
188 }
189 INTOFF;
190 if (action)
191 action = savestr(action);
192 if (trap[signo])
193 ckfree(trap[signo]);
194 trap[signo] = action;
195 if (signo != 0)
196 setsignal(signo);
197 INTON;
198 }
199 return errors;
200}
201
202/*
203 * Clear traps on a fork.
204 */
205void
206clear_traps(void)
207{
208 char *volatile *tp;
209
210 for (tp = trap; tp <= &trap[NSIG - 1]; tp++) {
211 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
212 INTOFF;
213 ckfree(*tp);
214 *tp = NULL;
215 if (tp != &trap[0])
216 setsignal(tp - trap);
217 INTON;
218 }
219 }
220}
221
222/*
223 * Check if we have any traps enabled.
224 */
225int
226have_traps(void)
227{
228 char *volatile *tp;
229
230 for (tp = trap; tp <= &trap[NSIG - 1]; tp++) {
231 if (*tp && **tp) /* trap not NULL or SIG_IGN */
232 return 1;
233 }
234 return 0;
235}
236
237/*
238 * Set the signal handler for the specified signal. The routine figures
239 * out what it should be set to.
240 */
241void
242setsignal(int signo)
243{
244 int action;
245 sig_t sigact = SIG_DFL;
246 struct sigaction sa;
247 char *t;
248
249 if ((t = trap[signo]) == NULL)
250 action = S_DFL;
251 else if (*t != '\0')
252 action = S_CATCH;
253 else
254 action = S_IGN;
255 if (action == S_DFL) {
256 switch (signo) {
257 case SIGINT:
258 action = S_CATCH;
259 break;
260 case SIGQUIT:
261#ifdef DEBUG
262 if (debug)
263 break;
264#endif
265 action = S_CATCH;
266 break;
267 case SIGTERM:
268 if (rootshell && iflag)
269 action = S_IGN;
270 break;
271#if JOBS
272 case SIGTSTP:
273 case SIGTTOU:
274 if (rootshell && mflag)
275 action = S_IGN;
276 break;
277#endif
278 }
279 }
280
281 t = &sigmode[signo];
282 if (*t == 0) {
283 /*
284 * current setting unknown
285 */
286 if (!getsigaction(signo, &sigact)) {
287 /*
288 * Pretend it worked; maybe we should give a warning
289 * here, but other shells don't. We don't alter
290 * sigmode, so that we retry every time.
291 */
292 return;
293 }
294 if (sigact == SIG_IGN) {
295 if (mflag && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)) {
296 *t = S_IGN; /* don't hard ignore these */
297 } else
298 *t = S_HARD_IGN;
299 } else {
300 *t = S_RESET; /* force to be set */
301 }
302 }
303 if (*t == S_HARD_IGN || *t == action)
304 return;
305 switch (action) {
306 case S_DFL:
307 sigact = SIG_DFL;
308 break;
309 case S_CATCH:
310 sigact = onsig;
311 break;
312 case S_IGN:
313 sigact = SIG_IGN;
314 break;
315 }
316 *t = action;
317 sa.sa_handler = sigact;
318 sa.sa_flags = 0;
319 sigemptyset(&sa.sa_mask);
320 sigaction(signo, &sa, NULL);
321}
322
323/*
324 * Return the current setting for sig w/o changing it.
325 */
326static int
327getsigaction(int signo, sig_t *sigact)
328{
329 struct sigaction sa;
330
331 if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
332 return 0;
333 *sigact = (sig_t)sa.sa_handler;
334 return 1;
335}
336
337/*
338 * Ignore a signal.
339 */
340void
341ignoresig(int signo)
342{
343 if (sigmode[signo] == 0)
344 setsignal(signo);
345 if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
346 signal(signo, SIG_IGN);
347 sigmode[signo] = S_IGN;
348 }
349}
350
351int
352issigchldtrapped(void)
353{
354 return (trap[SIGCHLD] != NULL && *trap[SIGCHLD] != '\0');
355}
356
357/*
358 * Signal handler.
359 */
360void
361onsig(int signo)
362{
363 if (signo == SIGINT && trap[SIGINT] == NULL) {
364 if (suppressint)
365 SET_PENDING_INT;
366 else
367 onint();
368 return;
369 }
370
371 /* If we are currently in a wait builtin, prepare to break it */
372 if (signo == SIGINT || signo == SIGQUIT)
373 pendingsig_waitcmd = signo;
374
375 if (trap[signo] != NULL && trap[signo][0] != '\0' && (signo != SIGCHLD || !ignore_sigchld)) {
376 gotsig[signo] = 1;
377 pendingsig = signo;
378 pendingsig_waitcmd = signo;
379 }
380}
381
382/*
383 * Called to execute a trap. Perhaps we should avoid entering new trap
384 * handlers while we are executing a trap handler.
385 */
386void
387dotrap(void)
388{
389 struct stackmark smark;
390 int i;
391 int savestatus, prev_evalskip, prev_skipcount;
392
393 in_dotrap++;
394 for (;;) {
395 pendingsig = 0;
396 pendingsig_waitcmd = 0;
397 for (i = 1; i < NSIG; i++) {
398 if (gotsig[i]) {
399 gotsig[i] = 0;
400 if (trap[i]) {
401 /*
402 * Ignore SIGCHLD to avoid infinite
403 * recursion if the trap action does
404 * a fork.
405 */
406 if (i == SIGCHLD)
407 ignore_sigchld++;
408
409 /*
410 * Backup current evalskip
411 * state and reset it before
412 * executing a trap, so that the
413 * trap is not disturbed by an
414 * ongoing break/continue/return
415 * statement.
416 */
417 prev_evalskip = evalskip;
418 prev_skipcount = skipcount;
419 evalskip = 0;
420
421 last_trapsig = i;
422 savestatus = exitstatus;
423 setstackmark(&smark);
424 evalstring(stsavestr(trap[i]), 0);
425 popstackmark(&smark);
426
427 /*
428 * If such a command was not
429 * already in progress, allow a
430 * break/continue/return in the
431 * trap action to have an effect
432 * outside of it.
433 */
434 if (evalskip == 0 || prev_evalskip != 0) {
435 evalskip = prev_evalskip;
436 skipcount = prev_skipcount;
437 exitstatus = savestatus;
438 }
439
440 if (i == SIGCHLD)
441 ignore_sigchld--;
442 }
443 break;
444 }
445 }
446 if (i >= NSIG)
447 break;
448 }
449 in_dotrap--;
450}
451
452void
453trap_init(void)
454{
455 setsignal(SIGINT);
456 setsignal(SIGQUIT);
457}
458
459/*
460 * Controls whether the shell is interactive or not based on iflag.
461 */
462void
463setinteractive(void)
464{
465 setsignal(SIGTERM);
466}
467
468/*
469 * Called to exit the shell.
470 */
471void
472exitshell(int status)
473{
474 TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
475 exiting = 1;
476 exiting_exitstatus = status;
477 exitshell_savedstatus();
478}
479
480void
481exitshell_savedstatus(void)
482{
483 struct jmploc loc1, loc2;
484 char *p;
485 int sig = 0;
486 sigset_t sigs;
487
488 if (!exiting) {
489 if (in_dotrap && last_trapsig) {
490 sig = last_trapsig;
491 exiting_exitstatus = sig + 128;
492 } else
493 exiting_exitstatus = oexitstatus;
494 }
495 exitstatus = oexitstatus = exiting_exitstatus;
496 if (!setjmp(loc1.loc)) {
497 handler = &loc1;
498 if ((p = trap[0]) != NULL && *p != '\0') {
499 /*
500 * Reset evalskip, or the trap on EXIT could be
501 * interrupted if the last command was a "return".
502 */
503 evalskip = 0;
504 trap[0] = NULL;
505 FORCEINTON;
506 evalstring(p, 0);
507 }
508 }
509 if (!setjmp(loc2.loc)) {
510 handler = &loc2; /* probably unnecessary */
511 FORCEINTON;
512 flushall();
513#if JOBS
514 setjobctl(0);
515#endif
516#ifndef NO_HISTORY
517 histsave();
518#endif
519 }
520 if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN && sig != SIGTTOU) {
521 signal(sig, SIG_DFL);
522 sigemptyset(&sigs);
523 sigaddset(&sigs, sig);
524 sigprocmask(SIG_UNBLOCK, &sigs, NULL);
525 kill(getpid(), sig);
526 /* If the default action is to ignore, fall back to _exit(). */
527 }
528 _exit(exiting_exitstatus);
529}