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