trap.c revision 125155
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 125155 2004-01-28 19:01:10Z njl $");
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. */
85100588Stjrvolatile sig_atomic_t gotwinch;
861556Srgrimes
8790111Simpstatic int getsigaction(int, sig_t *);
8817987Speter
8920902Ssteve
901556Srgrimes/*
9120902Ssteve * Map a string to a signal number.
9220902Ssteve */
9320902Sstevestatic int
9490111Simpsigstring_to_signum(char *sig)
9520902Ssteve{
9620902Ssteve
9720902Ssteve	if (is_number(sig)) {
9820902Ssteve		int signo;
9920902Ssteve
10020902Ssteve		signo = atoi(sig);
10120902Ssteve		return ((signo >= 0 && signo < NSIG) ? signo : (-1));
10220902Ssteve	} else if (strcasecmp(sig, "exit") == 0) {
10320902Ssteve		return (0);
10420902Ssteve	} else {
10520902Ssteve		int n;
10620902Ssteve
10720902Ssteve		if (strncasecmp(sig, "sig", 3) == 0)
10820902Ssteve			sig += 3;
109125155Snjl		for (n = 1; n < sys_nsig; n++)
110125155Snjl			if (sys_signame[n] && strcasecmp(sys_signame[n], sig) == 0)
11120902Ssteve				return (n);
11220902Ssteve	}
11320902Ssteve	return (-1);
11420902Ssteve}
11520902Ssteve
11620902Ssteve
11720902Ssteve/*
11820902Ssteve * Print a list of valid signal names.
11920902Ssteve */
12020902Sstevestatic void
12190111Simpprintsignals(void)
12220902Ssteve{
12320902Ssteve	int n;
12420902Ssteve
125125155Snjl	for (n = 1; n < sys_nsig; n++) {
12620902Ssteve		out1fmt("%s", sys_signame[n]);
12720902Ssteve		if (n == (NSIG / 2) || n == (NSIG - 1))
12820902Ssteve			out1str("\n");
12920902Ssteve		else
13020902Ssteve			out1c(' ');
13120902Ssteve	}
13220902Ssteve}
13320902Ssteve
13420902Ssteve
13520902Ssteve/*
1361556Srgrimes * The trap builtin.
1371556Srgrimes */
13817987Speterint
13990111Simptrapcmd(int argc, char **argv)
14017987Speter{
1411556Srgrimes	char *action;
1421556Srgrimes	int signo;
1431556Srgrimes
1441556Srgrimes	if (argc <= 1) {
145125155Snjl		for (signo = 0 ; signo < sys_nsig ; signo++) {
1461556Srgrimes			if (trap[signo] != NULL)
14720902Ssteve				out1fmt("trap -- '%s' %s\n", trap[signo],
14820902Ssteve					(signo) ? sys_signame[signo] : "exit");
1491556Srgrimes		}
1501556Srgrimes		return 0;
1511556Srgrimes	}
15220902Ssteve	action = NULL;
15320902Ssteve	if (*++argv && strcmp(*argv, "--") == 0)
15420902Ssteve		argv++;
15520902Ssteve	if (*argv && sigstring_to_signum(*argv) == -1) {
15620902Ssteve		if ((*argv)[0] != '-') {
15720902Ssteve			action = *argv;
15820902Ssteve			argv++;
15920902Ssteve		} else if ((*argv)[1] == '\0') {
16020902Ssteve			argv++;
16120902Ssteve		} else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
16220902Ssteve			printsignals();
16320902Ssteve			return 0;
16420902Ssteve		} else {
16520902Ssteve			error("bad option %s", *argv);
16620902Ssteve		}
16720902Ssteve	}
16820902Ssteve	while (*argv) {
16920902Ssteve		if ((signo = sigstring_to_signum(*argv)) == -1)
17020902Ssteve			error("bad signal %s", *argv);
1711556Srgrimes		INTOFF;
1721556Srgrimes		if (action)
1731556Srgrimes			action = savestr(action);
1741556Srgrimes		if (trap[signo])
1751556Srgrimes			ckfree(trap[signo]);
1761556Srgrimes		trap[signo] = action;
1771556Srgrimes		if (signo != 0)
1781556Srgrimes			setsignal(signo);
1791556Srgrimes		INTON;
18020902Ssteve		argv++;
1811556Srgrimes	}
1821556Srgrimes	return 0;
1831556Srgrimes}
1841556Srgrimes
1851556Srgrimes
1861556Srgrimes/*
1871556Srgrimes * Clear traps on a fork.
1881556Srgrimes */
1891556Srgrimesvoid
19090111Simpclear_traps(void)
19120902Ssteve{
19238950Scracauer	char *volatile *tp;
1931556Srgrimes
19420902Ssteve	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
1951556Srgrimes		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
1961556Srgrimes			INTOFF;
1971556Srgrimes			ckfree(*tp);
1981556Srgrimes			*tp = NULL;
1991556Srgrimes			if (tp != &trap[0])
2001556Srgrimes				setsignal(tp - trap);
2011556Srgrimes			INTON;
2021556Srgrimes		}
2031556Srgrimes	}
2041556Srgrimes}
2051556Srgrimes
2061556Srgrimes
2071556Srgrimes/*
2081556Srgrimes * Set the signal handler for the specified signal.  The routine figures
2091556Srgrimes * out what it should be set to.
2101556Srgrimes */
21131098Sbdevoid
21290111Simpsetsignal(int signo)
21317987Speter{
2141556Srgrimes	int action;
21531098Sbde	sig_t sig, sigact = SIG_DFL;
2161556Srgrimes	char *t;
2171556Srgrimes
2181556Srgrimes	if ((t = trap[signo]) == NULL)
2191556Srgrimes		action = S_DFL;
2201556Srgrimes	else if (*t != '\0')
2211556Srgrimes		action = S_CATCH;
2221556Srgrimes	else
2231556Srgrimes		action = S_IGN;
22438521Scracauer	if (action == S_DFL) {
2251556Srgrimes		switch (signo) {
2261556Srgrimes		case SIGINT:
22738521Scracauer			action = S_CATCH;
2281556Srgrimes			break;
2291556Srgrimes		case SIGQUIT:
2301556Srgrimes#ifdef DEBUG
2311556Srgrimes			{
2321556Srgrimes			extern int debug;
2331556Srgrimes
2341556Srgrimes			if (debug)
2351556Srgrimes				break;
2361556Srgrimes			}
2371556Srgrimes#endif
23838535Scracauer			action = S_CATCH;
23938521Scracauer			break;
2401556Srgrimes		case SIGTERM:
24138521Scracauer			if (rootshell && iflag)
2421556Srgrimes				action = S_IGN;
2431556Srgrimes			break;
2441556Srgrimes#if JOBS
2451556Srgrimes		case SIGTSTP:
2461556Srgrimes		case SIGTTOU:
24738521Scracauer			if (rootshell && mflag)
2481556Srgrimes				action = S_IGN;
2491556Srgrimes			break;
2501556Srgrimes#endif
251100578Stjr#ifndef NO_HISTORY
252100578Stjr		case SIGWINCH:
253100588Stjr			if (rootshell && iflag)
254100578Stjr				action = S_CATCH;
255100578Stjr			break;
256100578Stjr#endif
2571556Srgrimes		}
2581556Srgrimes	}
25917987Speter
26020902Ssteve	t = &sigmode[signo];
2618855Srgrimes	if (*t == 0) {
2628855Srgrimes		/*
2638855Srgrimes		 * current setting unknown
2641556Srgrimes		 */
26517987Speter		if (!getsigaction(signo, &sigact)) {
26617987Speter			/*
26717987Speter			 * Pretend it worked; maybe we should give a warning
26817987Speter			 * here, but other shells don't. We don't alter
26917987Speter			 * sigmode, so that we retry every time.
27017987Speter			 */
27131098Sbde			return;
27217987Speter		}
2731556Srgrimes		if (sigact == SIG_IGN) {
2748855Srgrimes			if (mflag && (signo == SIGTSTP ||
2751556Srgrimes			     signo == SIGTTIN || signo == SIGTTOU)) {
2761556Srgrimes				*t = S_IGN;	/* don't hard ignore these */
2771556Srgrimes			} else
2781556Srgrimes				*t = S_HARD_IGN;
2791556Srgrimes		} else {
2801556Srgrimes			*t = S_RESET;	/* force to be set */
2811556Srgrimes		}
2821556Srgrimes	}
2831556Srgrimes	if (*t == S_HARD_IGN || *t == action)
28431098Sbde		return;
2851556Srgrimes	switch (action) {
2861556Srgrimes		case S_DFL:	sigact = SIG_DFL;	break;
2871556Srgrimes		case S_CATCH:  	sigact = onsig;		break;
2881556Srgrimes		case S_IGN:	sigact = SIG_IGN;	break;
2891556Srgrimes	}
2901556Srgrimes	*t = action;
29131098Sbde	sig = signal(signo, sigact);
29231098Sbde	if (sig != SIG_ERR && action == S_CATCH)
29331098Sbde		siginterrupt(signo, 1);
2941556Srgrimes}
2951556Srgrimes
29620902Ssteve
2971556Srgrimes/*
2981556Srgrimes * Return the current setting for sig w/o changing it.
2991556Srgrimes */
30017987Speterstatic int
30190111Simpgetsigaction(int signo, sig_t *sigact)
30217987Speter{
3031556Srgrimes	struct sigaction sa;
3041556Srgrimes
3051556Srgrimes	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
30617987Speter		return 0;
30717987Speter	*sigact = (sig_t) sa.sa_handler;
30817987Speter	return 1;
3091556Srgrimes}
3101556Srgrimes
31120902Ssteve
3121556Srgrimes/*
3131556Srgrimes * Ignore a signal.
3141556Srgrimes */
3151556Srgrimesvoid
31690111Simpignoresig(int signo)
31717987Speter{
31820902Ssteve
31920902Ssteve	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
3201556Srgrimes		signal(signo, SIG_IGN);
3211556Srgrimes	}
32220902Ssteve	sigmode[signo] = S_HARD_IGN;
3231556Srgrimes}
3241556Srgrimes
3251556Srgrimes
3261556Srgrimes#ifdef mkinit
32717987SpeterINCLUDE <signal.h>
3281556SrgrimesINCLUDE "trap.h"
3291556Srgrimes
3301556SrgrimesSHELLPROC {
3311556Srgrimes	char *sm;
3321556Srgrimes
3331556Srgrimes	clear_traps();
33417987Speter	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
3351556Srgrimes		if (*sm == S_IGN)
3361556Srgrimes			*sm = S_HARD_IGN;
3371556Srgrimes	}
3381556Srgrimes}
3391556Srgrimes#endif
3401556Srgrimes
3411556Srgrimes
3421556Srgrimes/*
3431556Srgrimes * Signal handler.
3441556Srgrimes */
3451556Srgrimesvoid
34690111Simponsig(int signo)
34717987Speter{
34831098Sbde
3491556Srgrimes	if (signo == SIGINT && trap[SIGINT] == NULL) {
3501556Srgrimes		onint();
3511556Srgrimes		return;
3521556Srgrimes	}
35338950Scracauer
35420902Ssteve	if (signo != SIGCHLD || !ignore_sigchld)
35520902Ssteve		gotsig[signo] = 1;
3561556Srgrimes	pendingsigs++;
35738950Scracauer
35838950Scracauer	/* If we are currently in a wait builtin, prepare to break it */
35938950Scracauer	if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0)
36038521Scracauer		breakwaitcmd = 1;
36138950Scracauer	/*
36239056Scracauer	 * If a trap is set, not ignored and not the null command, we need
36339056Scracauer	 * to make sure traps are executed even when a child blocks signals.
36438950Scracauer	 */
36545221Scracauer	if (Tflag &&
36645221Scracauer	    trap[signo] != NULL &&
36739056Scracauer	    ! trap[signo][0] == '\0' &&
36839049Scracauer	    ! (trap[signo][0] == ':' && trap[signo][1] == '\0'))
36939049Scracauer		breakwaitcmd = 1;
370100578Stjr
371100578Stjr#ifndef NO_HISTORY
372100578Stjr	if (signo == SIGWINCH)
373100588Stjr		gotwinch = 1;
374100578Stjr#endif
3751556Srgrimes}
3761556Srgrimes
3771556Srgrimes
3781556Srgrimes/*
3791556Srgrimes * Called to execute a trap.  Perhaps we should avoid entering new trap
3801556Srgrimes * handlers while we are executing a trap handler.
3811556Srgrimes */
3821556Srgrimesvoid
38390111Simpdotrap(void)
38420902Ssteve{
3851556Srgrimes	int i;
3861556Srgrimes	int savestatus;
3871556Srgrimes
38838521Scracauer	in_dotrap++;
3891556Srgrimes	for (;;) {
39020902Ssteve		for (i = 1; i < NSIG; i++) {
39120902Ssteve			if (gotsig[i]) {
39220902Ssteve				gotsig[i] = 0;
39320902Ssteve				if (trap[i]) {
39420902Ssteve					/*
39520902Ssteve					 * Ignore SIGCHLD to avoid infinite recursion
39620902Ssteve					 * if the trap action does a fork.
39720902Ssteve					 */
39820902Ssteve					if (i == SIGCHLD)
39920902Ssteve						ignore_sigchld++;
40020902Ssteve					savestatus = exitstatus;
40120902Ssteve					evalstring(trap[i]);
40220902Ssteve					exitstatus = savestatus;
40320902Ssteve					if (i == SIGCHLD)
40420902Ssteve						ignore_sigchld--;
40520902Ssteve				}
4061556Srgrimes				break;
40720902Ssteve			}
4081556Srgrimes		}
40920902Ssteve		if (i >= NSIG)
41020902Ssteve			break;
4111556Srgrimes	}
41238521Scracauer	in_dotrap--;
4131556Srgrimes	pendingsigs = 0;
4141556Srgrimes}
4151556Srgrimes
4161556Srgrimes
4171556Srgrimes/*
4181556Srgrimes * Controls whether the shell is interactive or not.
4191556Srgrimes */
4201556Srgrimesvoid
42190111Simpsetinteractive(int on)
42217987Speter{
42338521Scracauer	static int is_interactive = -1;
4241556Srgrimes
4251556Srgrimes	if (on == is_interactive)
4261556Srgrimes		return;
4271556Srgrimes	setsignal(SIGINT);
4281556Srgrimes	setsignal(SIGQUIT);
4291556Srgrimes	setsignal(SIGTERM);
430100578Stjr#ifndef NO_HISTORY
431100578Stjr	setsignal(SIGWINCH);
432100578Stjr#endif
4331556Srgrimes	is_interactive = on;
4341556Srgrimes}
4351556Srgrimes
4361556Srgrimes
4371556Srgrimes/*
4381556Srgrimes * Called to exit the shell.
4391556Srgrimes */
4401556Srgrimesvoid
44190111Simpexitshell(int status)
44217987Speter{
4431556Srgrimes	struct jmploc loc1, loc2;
4441556Srgrimes	char *p;
4451556Srgrimes
4461556Srgrimes	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
4471556Srgrimes	if (setjmp(loc1.loc)) {
4481556Srgrimes		goto l1;
4491556Srgrimes	}
4501556Srgrimes	if (setjmp(loc2.loc)) {
4511556Srgrimes		goto l2;
4521556Srgrimes	}
4531556Srgrimes	handler = &loc1;
4541556Srgrimes	if ((p = trap[0]) != NULL && *p != '\0') {
4551556Srgrimes		trap[0] = NULL;
4561556Srgrimes		evalstring(p);
4571556Srgrimes	}
4581556Srgrimesl1:   handler = &loc2;			/* probably unnecessary */
4591556Srgrimes	flushall();
4601556Srgrimes#if JOBS
4611556Srgrimes	setjobctl(0);
4621556Srgrimes#endif
4631556Srgrimesl2:   _exit(status);
4641556Srgrimes}
465