trap.c revision 100578
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
351556Srgrimes */
361556Srgrimes
371556Srgrimes#ifndef lint
3836150Scharnier#if 0
3936150Scharnierstatic char sccsid[] = "@(#)trap.c	8.5 (Berkeley) 6/5/95";
4036150Scharnier#endif
411556Srgrimes#endif /* not lint */
4299110Sobrien#include <sys/cdefs.h>
4399110Sobrien__FBSDID("$FreeBSD: head/bin/sh/trap.c 100578 2002-07-23 15:05:00Z tjr $");
441556Srgrimes
4517987Speter#include <signal.h>
4617987Speter#include <unistd.h>
4717987Speter#include <stdlib.h>
4817987Speter
491556Srgrimes#include "shell.h"
501556Srgrimes#include "main.h"
511556Srgrimes#include "nodes.h"	/* for other headers */
521556Srgrimes#include "eval.h"
531556Srgrimes#include "jobs.h"
5417987Speter#include "show.h"
551556Srgrimes#include "options.h"
561556Srgrimes#include "syntax.h"
571556Srgrimes#include "output.h"
581556Srgrimes#include "memalloc.h"
591556Srgrimes#include "error.h"
601556Srgrimes#include "trap.h"
611556Srgrimes#include "mystring.h"
62100578Stjr#include "myhistedit.h"
631556Srgrimes
641556Srgrimes
651556Srgrimes/*
661556Srgrimes * Sigmode records the current value of the signal handlers for the various
671556Srgrimes * modes.  A value of zero means that the current handler is not known.
681556Srgrimes * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
691556Srgrimes */
701556Srgrimes
711556Srgrimes#define S_DFL 1			/* default signal handling (SIG_DFL) */
721556Srgrimes#define S_CATCH 2		/* signal is caught */
731556Srgrimes#define S_IGN 3			/* signal is ignored (SIG_IGN) */
7446684Skris#define S_HARD_IGN 4		/* signal is ignored permanently */
751556Srgrimes#define S_RESET 5		/* temporary - to reset a hard ignored sig */
761556Srgrimes
771556Srgrimes
7817987SpeterMKINIT char sigmode[NSIG];	/* current value of signal */
7938521Scracauerint pendingsigs;		/* indicates some signal received */
8038536Scracauerint in_dotrap;			/* do we execute in a trap handler? */
8138950Scracauerstatic char *volatile trap[NSIG];	/* trap handler commands */
8238521Scracauerstatic volatile sig_atomic_t gotsig[NSIG];
8338521Scracauer				/* indicates specified signal received */
8420902Sstevestatic int ignore_sigchld;	/* Used while handling SIGCHLD traps. */
851556Srgrimes
8690111Simpstatic int getsigaction(int, sig_t *);
8717987Speter
8820902Ssteve
891556Srgrimes/*
9020902Ssteve * Map a string to a signal number.
9120902Ssteve */
9220902Sstevestatic int
9390111Simpsigstring_to_signum(char *sig)
9420902Ssteve{
9520902Ssteve
9620902Ssteve	if (is_number(sig)) {
9720902Ssteve		int signo;
9820902Ssteve
9920902Ssteve		signo = atoi(sig);
10020902Ssteve		return ((signo >= 0 && signo < NSIG) ? signo : (-1));
10120902Ssteve	} else if (strcasecmp(sig, "exit") == 0) {
10220902Ssteve		return (0);
10320902Ssteve	} else {
10420902Ssteve		int n;
10520902Ssteve
10620902Ssteve		if (strncasecmp(sig, "sig", 3) == 0)
10720902Ssteve			sig += 3;
10820902Ssteve		for (n = 1; n < NSIG; n++)
10920902Ssteve			if (strcasecmp(sys_signame[n], sig) == 0)
11020902Ssteve				return (n);
11120902Ssteve	}
11220902Ssteve	return (-1);
11320902Ssteve}
11420902Ssteve
11520902Ssteve
11620902Ssteve/*
11720902Ssteve * Print a list of valid signal names.
11820902Ssteve */
11920902Sstevestatic void
12090111Simpprintsignals(void)
12120902Ssteve{
12220902Ssteve	int n;
12320902Ssteve
12420902Ssteve	for (n = 1; n < NSIG; n++) {
12520902Ssteve		out1fmt("%s", sys_signame[n]);
12620902Ssteve		if (n == (NSIG / 2) || n == (NSIG - 1))
12720902Ssteve			out1str("\n");
12820902Ssteve		else
12920902Ssteve			out1c(' ');
13020902Ssteve	}
13120902Ssteve}
13220902Ssteve
13320902Ssteve
13420902Ssteve/*
1351556Srgrimes * The trap builtin.
1361556Srgrimes */
13717987Speterint
13890111Simptrapcmd(int argc, char **argv)
13917987Speter{
1401556Srgrimes	char *action;
1411556Srgrimes	int signo;
1421556Srgrimes
1431556Srgrimes	if (argc <= 1) {
14420902Ssteve		for (signo = 0 ; signo < NSIG ; signo++) {
1451556Srgrimes			if (trap[signo] != NULL)
14620902Ssteve				out1fmt("trap -- '%s' %s\n", trap[signo],
14720902Ssteve					(signo) ? sys_signame[signo] : "exit");
1481556Srgrimes		}
1491556Srgrimes		return 0;
1501556Srgrimes	}
15120902Ssteve	action = NULL;
15220902Ssteve	if (*++argv && strcmp(*argv, "--") == 0)
15320902Ssteve		argv++;
15420902Ssteve	if (*argv && sigstring_to_signum(*argv) == -1) {
15520902Ssteve		if ((*argv)[0] != '-') {
15620902Ssteve			action = *argv;
15720902Ssteve			argv++;
15820902Ssteve		} else if ((*argv)[1] == '\0') {
15920902Ssteve			argv++;
16020902Ssteve		} else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
16120902Ssteve			printsignals();
16220902Ssteve			return 0;
16320902Ssteve		} else {
16420902Ssteve			error("bad option %s", *argv);
16520902Ssteve		}
16620902Ssteve	}
16720902Ssteve	while (*argv) {
16820902Ssteve		if ((signo = sigstring_to_signum(*argv)) == -1)
16920902Ssteve			error("bad signal %s", *argv);
1701556Srgrimes		INTOFF;
1711556Srgrimes		if (action)
1721556Srgrimes			action = savestr(action);
1731556Srgrimes		if (trap[signo])
1741556Srgrimes			ckfree(trap[signo]);
1751556Srgrimes		trap[signo] = action;
1761556Srgrimes		if (signo != 0)
1771556Srgrimes			setsignal(signo);
1781556Srgrimes		INTON;
17920902Ssteve		argv++;
1801556Srgrimes	}
1811556Srgrimes	return 0;
1821556Srgrimes}
1831556Srgrimes
1841556Srgrimes
1851556Srgrimes/*
1861556Srgrimes * Clear traps on a fork.
1871556Srgrimes */
1881556Srgrimesvoid
18990111Simpclear_traps(void)
19020902Ssteve{
19138950Scracauer	char *volatile *tp;
1921556Srgrimes
19320902Ssteve	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
1941556Srgrimes		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
1951556Srgrimes			INTOFF;
1961556Srgrimes			ckfree(*tp);
1971556Srgrimes			*tp = NULL;
1981556Srgrimes			if (tp != &trap[0])
1991556Srgrimes				setsignal(tp - trap);
2001556Srgrimes			INTON;
2011556Srgrimes		}
2021556Srgrimes	}
2031556Srgrimes}
2041556Srgrimes
2051556Srgrimes
2061556Srgrimes/*
2071556Srgrimes * Set the signal handler for the specified signal.  The routine figures
2081556Srgrimes * out what it should be set to.
2091556Srgrimes */
21031098Sbdevoid
21190111Simpsetsignal(int signo)
21217987Speter{
2131556Srgrimes	int action;
21431098Sbde	sig_t sig, sigact = SIG_DFL;
2151556Srgrimes	char *t;
2161556Srgrimes
2171556Srgrimes	if ((t = trap[signo]) == NULL)
2181556Srgrimes		action = S_DFL;
2191556Srgrimes	else if (*t != '\0')
2201556Srgrimes		action = S_CATCH;
2211556Srgrimes	else
2221556Srgrimes		action = S_IGN;
22338521Scracauer	if (action == S_DFL) {
2241556Srgrimes		switch (signo) {
2251556Srgrimes		case SIGINT:
22638521Scracauer			action = S_CATCH;
2271556Srgrimes			break;
2281556Srgrimes		case SIGQUIT:
2291556Srgrimes#ifdef DEBUG
2301556Srgrimes			{
2311556Srgrimes			extern int debug;
2321556Srgrimes
2331556Srgrimes			if (debug)
2341556Srgrimes				break;
2351556Srgrimes			}
2361556Srgrimes#endif
23738535Scracauer			action = S_CATCH;
23838521Scracauer			break;
2391556Srgrimes		case SIGTERM:
24038521Scracauer			if (rootshell && iflag)
2411556Srgrimes				action = S_IGN;
2421556Srgrimes			break;
2431556Srgrimes#if JOBS
2441556Srgrimes		case SIGTSTP:
2451556Srgrimes		case SIGTTOU:
24638521Scracauer			if (rootshell && mflag)
2471556Srgrimes				action = S_IGN;
2481556Srgrimes			break;
2491556Srgrimes#endif
250100578Stjr#ifndef NO_HISTORY
251100578Stjr		case SIGWINCH:
252100578Stjr			if (rootshell && iflag && el != NULL)
253100578Stjr				action = S_CATCH;
254100578Stjr			break;
255100578Stjr#endif
2561556Srgrimes		}
2571556Srgrimes	}
25817987Speter
25920902Ssteve	t = &sigmode[signo];
2608855Srgrimes	if (*t == 0) {
2618855Srgrimes		/*
2628855Srgrimes		 * current setting unknown
2631556Srgrimes		 */
26417987Speter		if (!getsigaction(signo, &sigact)) {
26517987Speter			/*
26617987Speter			 * Pretend it worked; maybe we should give a warning
26717987Speter			 * here, but other shells don't. We don't alter
26817987Speter			 * sigmode, so that we retry every time.
26917987Speter			 */
27031098Sbde			return;
27117987Speter		}
2721556Srgrimes		if (sigact == SIG_IGN) {
2738855Srgrimes			if (mflag && (signo == SIGTSTP ||
2741556Srgrimes			     signo == SIGTTIN || signo == SIGTTOU)) {
2751556Srgrimes				*t = S_IGN;	/* don't hard ignore these */
2761556Srgrimes			} else
2771556Srgrimes				*t = S_HARD_IGN;
2781556Srgrimes		} else {
2791556Srgrimes			*t = S_RESET;	/* force to be set */
2801556Srgrimes		}
2811556Srgrimes	}
2821556Srgrimes	if (*t == S_HARD_IGN || *t == action)
28331098Sbde		return;
2841556Srgrimes	switch (action) {
2851556Srgrimes		case S_DFL:	sigact = SIG_DFL;	break;
2861556Srgrimes		case S_CATCH:  	sigact = onsig;		break;
2871556Srgrimes		case S_IGN:	sigact = SIG_IGN;	break;
2881556Srgrimes	}
2891556Srgrimes	*t = action;
29031098Sbde	sig = signal(signo, sigact);
29131098Sbde	if (sig != SIG_ERR && action == S_CATCH)
29231098Sbde		siginterrupt(signo, 1);
2931556Srgrimes}
2941556Srgrimes
29520902Ssteve
2961556Srgrimes/*
2971556Srgrimes * Return the current setting for sig w/o changing it.
2981556Srgrimes */
29917987Speterstatic int
30090111Simpgetsigaction(int signo, sig_t *sigact)
30117987Speter{
3021556Srgrimes	struct sigaction sa;
3031556Srgrimes
3041556Srgrimes	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
30517987Speter		return 0;
30617987Speter	*sigact = (sig_t) sa.sa_handler;
30717987Speter	return 1;
3081556Srgrimes}
3091556Srgrimes
31020902Ssteve
3111556Srgrimes/*
3121556Srgrimes * Ignore a signal.
3131556Srgrimes */
3141556Srgrimesvoid
31590111Simpignoresig(int signo)
31617987Speter{
31720902Ssteve
31820902Ssteve	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
3191556Srgrimes		signal(signo, SIG_IGN);
3201556Srgrimes	}
32120902Ssteve	sigmode[signo] = S_HARD_IGN;
3221556Srgrimes}
3231556Srgrimes
3241556Srgrimes
3251556Srgrimes#ifdef mkinit
32617987SpeterINCLUDE <signal.h>
3271556SrgrimesINCLUDE "trap.h"
3281556Srgrimes
3291556SrgrimesSHELLPROC {
3301556Srgrimes	char *sm;
3311556Srgrimes
3321556Srgrimes	clear_traps();
33317987Speter	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
3341556Srgrimes		if (*sm == S_IGN)
3351556Srgrimes			*sm = S_HARD_IGN;
3361556Srgrimes	}
3371556Srgrimes}
3381556Srgrimes#endif
3391556Srgrimes
3401556Srgrimes
3411556Srgrimes/*
3421556Srgrimes * Signal handler.
3431556Srgrimes */
3441556Srgrimesvoid
34590111Simponsig(int signo)
34617987Speter{
34731098Sbde
3481556Srgrimes	if (signo == SIGINT && trap[SIGINT] == NULL) {
3491556Srgrimes		onint();
3501556Srgrimes		return;
3511556Srgrimes	}
35238950Scracauer
35320902Ssteve	if (signo != SIGCHLD || !ignore_sigchld)
35420902Ssteve		gotsig[signo] = 1;
3551556Srgrimes	pendingsigs++;
35638950Scracauer
35738950Scracauer	/* If we are currently in a wait builtin, prepare to break it */
35838950Scracauer	if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0)
35938521Scracauer		breakwaitcmd = 1;
36038950Scracauer	/*
36139056Scracauer	 * If a trap is set, not ignored and not the null command, we need
36239056Scracauer	 * to make sure traps are executed even when a child blocks signals.
36338950Scracauer	 */
36445221Scracauer	if (Tflag &&
36545221Scracauer	    trap[signo] != NULL &&
36639056Scracauer	    ! trap[signo][0] == '\0' &&
36739049Scracauer	    ! (trap[signo][0] == ':' && trap[signo][1] == '\0'))
36839049Scracauer		breakwaitcmd = 1;
369100578Stjr
370100578Stjr#ifndef NO_HISTORY
371100578Stjr	if (signo == SIGWINCH)
372100578Stjr		el_resize(el);
373100578Stjr#endif
3741556Srgrimes}
3751556Srgrimes
3761556Srgrimes
3771556Srgrimes/*
3781556Srgrimes * Called to execute a trap.  Perhaps we should avoid entering new trap
3791556Srgrimes * handlers while we are executing a trap handler.
3801556Srgrimes */
3811556Srgrimesvoid
38290111Simpdotrap(void)
38320902Ssteve{
3841556Srgrimes	int i;
3851556Srgrimes	int savestatus;
3861556Srgrimes
38738521Scracauer	in_dotrap++;
3881556Srgrimes	for (;;) {
38920902Ssteve		for (i = 1; i < NSIG; i++) {
39020902Ssteve			if (gotsig[i]) {
39120902Ssteve				gotsig[i] = 0;
39220902Ssteve				if (trap[i]) {
39320902Ssteve					/*
39420902Ssteve					 * Ignore SIGCHLD to avoid infinite recursion
39520902Ssteve					 * if the trap action does a fork.
39620902Ssteve					 */
39720902Ssteve					if (i == SIGCHLD)
39820902Ssteve						ignore_sigchld++;
39920902Ssteve					savestatus = exitstatus;
40020902Ssteve					evalstring(trap[i]);
40120902Ssteve					exitstatus = savestatus;
40220902Ssteve					if (i == SIGCHLD)
40320902Ssteve						ignore_sigchld--;
40420902Ssteve				}
4051556Srgrimes				break;
40620902Ssteve			}
4071556Srgrimes		}
40820902Ssteve		if (i >= NSIG)
40920902Ssteve			break;
4101556Srgrimes	}
41138521Scracauer	in_dotrap--;
4121556Srgrimes	pendingsigs = 0;
4131556Srgrimes}
4141556Srgrimes
4151556Srgrimes
4161556Srgrimes/*
4171556Srgrimes * Controls whether the shell is interactive or not.
4181556Srgrimes */
4191556Srgrimesvoid
42090111Simpsetinteractive(int on)
42117987Speter{
42238521Scracauer	static int is_interactive = -1;
4231556Srgrimes
4241556Srgrimes	if (on == is_interactive)
4251556Srgrimes		return;
4261556Srgrimes	setsignal(SIGINT);
4271556Srgrimes	setsignal(SIGQUIT);
4281556Srgrimes	setsignal(SIGTERM);
429100578Stjr#ifndef NO_HISTORY
430100578Stjr	setsignal(SIGWINCH);
431100578Stjr#endif
4321556Srgrimes	is_interactive = on;
4331556Srgrimes}
4341556Srgrimes
4351556Srgrimes
4361556Srgrimes/*
4371556Srgrimes * Called to exit the shell.
4381556Srgrimes */
4391556Srgrimesvoid
44090111Simpexitshell(int status)
44117987Speter{
4421556Srgrimes	struct jmploc loc1, loc2;
4431556Srgrimes	char *p;
4441556Srgrimes
4451556Srgrimes	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
4461556Srgrimes	if (setjmp(loc1.loc)) {
4471556Srgrimes		goto l1;
4481556Srgrimes	}
4491556Srgrimes	if (setjmp(loc2.loc)) {
4501556Srgrimes		goto l2;
4511556Srgrimes	}
4521556Srgrimes	handler = &loc1;
4531556Srgrimes	if ((p = trap[0]) != NULL && *p != '\0') {
4541556Srgrimes		trap[0] = NULL;
4551556Srgrimes		evalstring(p);
4561556Srgrimes	}
4571556Srgrimesl1:   handler = &loc2;			/* probably unnecessary */
4581556Srgrimes	flushall();
4591556Srgrimes#if JOBS
4601556Srgrimes	setjobctl(0);
4611556Srgrimes#endif
4621556Srgrimesl2:   _exit(status);
4631556Srgrimes}
464