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