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
71static char sigmode[NSIG];	/* current value of signal */
72volatile sig_atomic_t pendingsig;	/* indicates some signal received */
73volatile sig_atomic_t pendingsig_waitcmd;	/* indicates wait builtin should be interrupted */
74static int in_dotrap;			/* do we execute in a trap handler? */
75static char *volatile trap[NSIG];	/* trap handler commands */
76static volatile sig_atomic_t gotsig[NSIG];
77				/* indicates specified signal received */
78static int ignore_sigchld;	/* Used while handling SIGCHLD traps. */
79static int last_trapsig;
80
81static int exiting;		/* exitshell() has been called */
82static int exiting_exitstatus;	/* value passed to exitshell() */
83
84static 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 */
92static int
93sigstring_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 */
120static void
121printsignals(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 */
148int
149trapcmd(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 */
213void
214clear_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 */
234int
235have_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 */
250void
251setsignal(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 */
331static int
332getsigaction(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 */
346void
347ignoresig(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
359int
360issigchldtrapped(void)
361{
362
363	return (trap[SIGCHLD] != NULL && *trap[SIGCHLD] != '\0');
364}
365
366
367/*
368 * Signal handler.
369 */
370void
371onsig(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 */
399void
400dotrap(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
467void
468trap_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 */
478void
479setinteractive(void)
480{
481	setsignal(SIGTERM);
482}
483
484
485/*
486 * Called to exit the shell.
487 */
488void
489exitshell(int status)
490{
491	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
492	exiting = 1;
493	exiting_exitstatus = status;
494	exitshell_savedstatus();
495}
496
497void
498exitshell_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