trap.c revision 30969
133965Sjdp/*-
2218822Sdim * Copyright (c) 1991, 1993
3130561Sobrien *	The Regents of the University of California.  All rights reserved.
477298Sobrien *
533965Sjdp * This code is derived from software contributed to Berkeley by
633965Sjdp * Kenneth Almquist.
733965Sjdp *
833965Sjdp * Redistribution and use in source and binary forms, with or without
933965Sjdp * modification, are permitted provided that the following conditions
1033965Sjdp * are met:
1133965Sjdp * 1. Redistributions of source code must retain the above copyright
1233965Sjdp *    notice, this list of conditions and the following disclaimer.
1333965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1433965Sjdp *    notice, this list of conditions and the following disclaimer in the
1533965Sjdp *    documentation and/or other materials provided with the distribution.
1633965Sjdp * 3. All advertising materials mentioning features or use of this software
1733965Sjdp *    must display the following acknowledgement:
1833965Sjdp *	This product includes software developed by the University of
1933965Sjdp *	California, Berkeley and its contributors.
20218822Sdim * 4. Neither the name of the University nor the names of its contributors
2133965Sjdp *    may be used to endorse or promote products derived from this software
2277298Sobrien *    without specific prior written permission.
2333965Sjdp *
24104834Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2533965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2633965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27104834Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2833965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2933965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3033965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3133965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32218822Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3333965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3433965Sjdp * SUCH DAMAGE.
3533965Sjdp *
3633965Sjdp *	$Id: trap.c,v 1.8 1997/02/22 13:58:46 peter Exp $
3733965Sjdp */
3833965Sjdp
39218822Sdim#ifndef lint
40130561Sobrienstatic char const sccsid[] = "@(#)trap.c	8.5 (Berkeley) 6/5/95";
41130561Sobrien#endif /* not lint */
42130561Sobrien
4333965Sjdp#include <signal.h>
4433965Sjdp#include <unistd.h>
4533965Sjdp#include <stdlib.h>
4633965Sjdp
4733965Sjdp#include "shell.h"
4833965Sjdp#include "main.h"
4933965Sjdp#include "nodes.h"	/* for other headers */
5033965Sjdp#include "eval.h"
5133965Sjdp#include "jobs.h"
5277298Sobrien#include "show.h"
5333965Sjdp#include "options.h"
5433965Sjdp#include "syntax.h"
55218822Sdim#include "output.h"
56218822Sdim#include "memalloc.h"
5733965Sjdp#include "error.h"
5833965Sjdp#include "trap.h"
5933965Sjdp#include "mystring.h"
6033965Sjdp
6133965Sjdp
6233965Sjdp/*
63130561Sobrien * Sigmode records the current value of the signal handlers for the various
64130561Sobrien * modes.  A value of zero means that the current handler is not known.
65130561Sobrien * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
66218822Sdim */
67218822Sdim
6833965Sjdp#define S_DFL 1			/* default signal handling (SIG_DFL) */
6933965Sjdp#define S_CATCH 2		/* signal is caught */
7033965Sjdp#define S_IGN 3			/* signal is ignored (SIG_IGN) */
7133965Sjdp#define S_HARD_IGN 4		/* signal is ignored permenantly */
72130561Sobrien#define S_RESET 5		/* temporary - to reset a hard ignored sig */
7333965Sjdp
7489857Sobrien
7589857SobrienMKINIT char sigmode[NSIG];	/* current value of signal */
7689857Sobrienint pendingsigs;			/* indicates some signal received */
7789857Sobrienstatic char *trap[NSIG];	/* trap handler commands */
78218822Sdimstatic char gotsig[NSIG];	/* indicates specified signal received */
7989857Sobrienstatic int ignore_sigchld;	/* Used while handling SIGCHLD traps. */
8089857Sobrien
81218822Sdimstatic int getsigaction __P((int, sig_t *));
82218822Sdim
8389857Sobrien
8489857Sobrien/*
8589857Sobrien * Map a string to a signal number.
8689857Sobrien */
8789857Sobrienstatic int
8889857Sobriensigstring_to_signum(sig)
8989857Sobrien	char *sig;
9033965Sjdp{
91218822Sdim
9260484Sobrien	if (is_number(sig)) {
9333965Sjdp		int signo;
9433965Sjdp
9533965Sjdp		signo = atoi(sig);
9633965Sjdp		return ((signo >= 0 && signo < NSIG) ? signo : (-1));
9733965Sjdp	} else if (strcasecmp(sig, "exit") == 0) {
9833965Sjdp		return (0);
99130561Sobrien	} else {
10033965Sjdp		int n;
10133965Sjdp
102130561Sobrien		if (strncasecmp(sig, "sig", 3) == 0)
10333965Sjdp			sig += 3;
10433965Sjdp		for (n = 1; n < NSIG; n++)
10533965Sjdp			if (strcasecmp(sys_signame[n], sig) == 0)
10633965Sjdp				return (n);
107130561Sobrien	}
108130561Sobrien	return (-1);
109130561Sobrien}
11033965Sjdp
11133965Sjdp
11233965Sjdp/*
11333965Sjdp * Print a list of valid signal names.
11433965Sjdp */
11533965Sjdpstatic void
11633965Sjdpprintsignals()
11733965Sjdp{
11833965Sjdp	int n;
11933965Sjdp
12033965Sjdp	for (n = 1; n < NSIG; n++) {
12133965Sjdp		out1fmt("%s", sys_signame[n]);
122130561Sobrien		if (n == (NSIG / 2) || n == (NSIG - 1))
12333965Sjdp			out1str("\n");
12433965Sjdp		else
12533965Sjdp			out1c(' ');
12633965Sjdp	}
12733965Sjdp}
128130561Sobrien
129130561Sobrien
13033965Sjdp/*
13133965Sjdp * The trap builtin.
13260484Sobrien */
13333965Sjdpint
13433965Sjdptrapcmd(argc, argv)
13533965Sjdp	int argc;
13633965Sjdp	char **argv;
13733965Sjdp{
13833965Sjdp	char *action;
13933965Sjdp	int signo;
14033965Sjdp
14133965Sjdp	if (argc <= 1) {
14233965Sjdp		for (signo = 0 ; signo < NSIG ; signo++) {
14333965Sjdp			if (trap[signo] != NULL)
144218822Sdim				out1fmt("trap -- '%s' %s\n", trap[signo],
14560484Sobrien					(signo) ? sys_signame[signo] : "exit");
14660484Sobrien		}
14760484Sobrien		return 0;
14833965Sjdp	}
14933965Sjdp	action = NULL;
15033965Sjdp	if (*++argv && strcmp(*argv, "--") == 0)
15133965Sjdp		argv++;
152218822Sdim	if (*argv && sigstring_to_signum(*argv) == -1) {
153218822Sdim		if ((*argv)[0] != '-') {
154218822Sdim			action = *argv;
155218822Sdim			argv++;
156218822Sdim		} else if ((*argv)[1] == '\0') {
157218822Sdim			argv++;
158218822Sdim		} else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
159218822Sdim			printsignals();
160218822Sdim			return 0;
161218822Sdim		} else {
162218822Sdim			error("bad option %s", *argv);
163218822Sdim		}
164218822Sdim	}
165218822Sdim	while (*argv) {
166218822Sdim		if ((signo = sigstring_to_signum(*argv)) == -1)
167218822Sdim			error("bad signal %s", *argv);
168218822Sdim		INTOFF;
169218822Sdim		if (action)
170218822Sdim			action = savestr(action);
171218822Sdim		if (trap[signo])
172218822Sdim			ckfree(trap[signo]);
17333965Sjdp		trap[signo] = action;
17433965Sjdp		if (signo != 0)
17533965Sjdp			setsignal(signo);
17633965Sjdp		INTON;
177218822Sdim		argv++;
17833965Sjdp	}
17933965Sjdp	return 0;
18033965Sjdp}
18133965Sjdp
18233965Sjdp
18333965Sjdp/*
18433965Sjdp * Clear traps on a fork.
18533965Sjdp */
18633965Sjdpvoid
18733965Sjdpclear_traps()
18833965Sjdp{
18938889Sjdp	char **tp;
19033965Sjdp
19133965Sjdp	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
19233965Sjdp		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
19333965Sjdp			INTOFF;
19433965Sjdp			ckfree(*tp);
19533965Sjdp			*tp = NULL;
19638889Sjdp			if (tp != &trap[0])
19733965Sjdp				setsignal(tp - trap);
19833965Sjdp			INTON;
199130561Sobrien		}
200218822Sdim	}
201218822Sdim}
202218822Sdim
203218822Sdim
20433965Sjdp/*
20533965Sjdp * Set the signal handler for the specified signal.  The routine figures
20633965Sjdp * out what it should be set to.
20733965Sjdp */
20833965Sjdplong
20933965Sjdpsetsignal(signo)
21033965Sjdp	int signo;
21133965Sjdp{
21233965Sjdp	int action;
213218822Sdim	sig_t sigact = SIG_DFL;
214218822Sdim	char *t;
215218822Sdim	long sig;
216218822Sdim
217218822Sdim	if ((t = trap[signo]) == NULL)
218104834Sobrien		action = S_DFL;
219218822Sdim	else if (*t != '\0')
220218822Sdim		action = S_CATCH;
221218822Sdim	else
222218822Sdim		action = S_IGN;
223218822Sdim	if (rootshell && action == S_DFL) {
224218822Sdim		switch (signo) {
225218822Sdim		case SIGINT:
226218822Sdim			if (iflag)
227218822Sdim				action = S_CATCH;
22833965Sjdp			break;
229218822Sdim		case SIGQUIT:
230104834Sobrien#ifdef DEBUG
231218822Sdim			{
232218822Sdim			extern int debug;
233218822Sdim
23433965Sjdp			if (debug)
235218822Sdim				break;
236218822Sdim			}
237218822Sdim#endif
23833965Sjdp			/* FALLTHROUGH */
239218822Sdim		case SIGTERM:
240218822Sdim			if (iflag)
241218822Sdim				action = S_IGN;
242218822Sdim			break;
24333965Sjdp#if JOBS
244218822Sdim		case SIGTSTP:
245218822Sdim		case SIGTTOU:
246218822Sdim			if (mflag)
247218822Sdim				action = S_IGN;
248218822Sdim			break;
249218822Sdim#endif
250218822Sdim		}
25133965Sjdp	}
25233965Sjdp
25333965Sjdp	t = &sigmode[signo];
25433965Sjdp	if (*t == 0) {
25533965Sjdp		/*
25633965Sjdp		 * current setting unknown
25733965Sjdp		 */
25833965Sjdp		if (!getsigaction(signo, &sigact)) {
25933965Sjdp			/*
26033965Sjdp			 * Pretend it worked; maybe we should give a warning
261218822Sdim			 * here, but other shells don't. We don't alter
26233965Sjdp			 * sigmode, so that we retry every time.
263218822Sdim			 */
264218822Sdim			return 0;
265218822Sdim		}
26633965Sjdp		if (sigact == SIG_IGN) {
26733965Sjdp			if (mflag && (signo == SIGTSTP ||
268218822Sdim			     signo == SIGTTIN || signo == SIGTTOU)) {
26933965Sjdp				*t = S_IGN;	/* don't hard ignore these */
27033965Sjdp			} else
271130561Sobrien				*t = S_HARD_IGN;
272218822Sdim		} else {
273130561Sobrien			*t = S_RESET;	/* force to be set */
274104834Sobrien		}
27533965Sjdp	}
276104834Sobrien	if (*t == S_HARD_IGN || *t == action)
27733965Sjdp		return 0;
27833965Sjdp	switch (action) {
279218822Sdim		case S_DFL:	sigact = SIG_DFL;	break;
28033965Sjdp		case S_CATCH:  	sigact = onsig;		break;
28133965Sjdp		case S_IGN:	sigact = SIG_IGN;	break;
28233965Sjdp	}
28333965Sjdp	*t = action;
28433965Sjdp	sig = (long)signal(signo, sigact);
28533965Sjdp#ifdef BSD
28633965Sjdp	if (sig != -1 && action == S_CATCH)
28733965Sjdp		sig = siginterrupt(signo, 1);
28833965Sjdp#endif
28933965Sjdp	return sig;
29033965Sjdp}
29133965Sjdp
292218822Sdim
293218822Sdim/*
294218822Sdim * Return the current setting for sig w/o changing it.
295218822Sdim */
296218822Sdimstatic int
297218822Sdimgetsigaction(signo, sigact)
298218822Sdim	int signo;
299218822Sdim	sig_t *sigact;
300218822Sdim{
30133965Sjdp	struct sigaction sa;
30233965Sjdp
303218822Sdim	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
30433965Sjdp		return 0;
30533965Sjdp	*sigact = (sig_t) sa.sa_handler;
30633965Sjdp	return 1;
30733965Sjdp}
30833965Sjdp
30933965Sjdp
31033965Sjdp/*
31133965Sjdp * Ignore a signal.
312218822Sdim */
313218822Sdimvoid
31433965Sjdpignoresig(signo)
31533965Sjdp	int signo;
31633965Sjdp{
317130561Sobrien
31833965Sjdp	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
319104834Sobrien		signal(signo, SIG_IGN);
320218822Sdim	}
32133965Sjdp	sigmode[signo] = S_HARD_IGN;
32233965Sjdp}
32333965Sjdp
32460484Sobrien
32560484Sobrien#ifdef mkinit
32660484SobrienINCLUDE <signal.h>
32789857SobrienINCLUDE "trap.h"
32889857Sobrien
32989857SobrienSHELLPROC {
33060484Sobrien	char *sm;
33160484Sobrien
33260484Sobrien	clear_traps();
33333965Sjdp	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
33433965Sjdp		if (*sm == S_IGN)
33533965Sjdp			*sm = S_HARD_IGN;
336218822Sdim	}
337218822Sdim}
33833965Sjdp#endif
33933965Sjdp
34033965Sjdp
341104834Sobrien/*
342218822Sdim * Signal handler.
34333965Sjdp */
344218822Sdimvoid
34533965Sjdponsig(signo)
34633965Sjdp	int signo;
34733965Sjdp{
34833965Sjdp#ifndef BSD
34933965Sjdp	signal(signo, onsig);
35089857Sobrien#endif
35133965Sjdp	if (signo == SIGINT && trap[SIGINT] == NULL) {
35233965Sjdp		onint();
35333965Sjdp		return;
35433965Sjdp	}
355130561Sobrien	if (signo != SIGCHLD || !ignore_sigchld)
35677298Sobrien		gotsig[signo] = 1;
35777298Sobrien	pendingsigs++;
35877298Sobrien}
359104834Sobrien
36077298Sobrien
361104834Sobrien/*
36277298Sobrien * Called to execute a trap.  Perhaps we should avoid entering new trap
36377298Sobrien * handlers while we are executing a trap handler.
364104834Sobrien */
36577298Sobrienvoid
366104834Sobriendotrap()
36733965Sjdp{
36833965Sjdp	int i;
369104834Sobrien	int savestatus;
37033965Sjdp
37133965Sjdp	for (;;) {
372130561Sobrien		for (i = 1; i < NSIG; i++) {
37333965Sjdp			if (gotsig[i]) {
37433965Sjdp				gotsig[i] = 0;
375130561Sobrien				if (trap[i]) {
37633965Sjdp					/*
37789857Sobrien					 * Ignore SIGCHLD to avoid infinite recursion
37833965Sjdp					 * if the trap action does a fork.
37933965Sjdp					 */
38033965Sjdp					if (i == SIGCHLD)
38189857Sobrien						ignore_sigchld++;
38233965Sjdp					savestatus = exitstatus;
38333965Sjdp					evalstring(trap[i]);
38433965Sjdp					exitstatus = savestatus;
385218822Sdim					if (i == SIGCHLD)
386218822Sdim						ignore_sigchld--;
387218822Sdim				}
388218822Sdim				break;
389218822Sdim			}
390218822Sdim		}
39133965Sjdp		if (i >= NSIG)
39233965Sjdp			break;
39333965Sjdp	}
39433965Sjdp	pendingsigs = 0;
39533965Sjdp}
39633965Sjdp
397104834Sobrien
398104834Sobrien/*
39933965Sjdp * Controls whether the shell is interactive or not.
40033965Sjdp */
40133965Sjdpvoid
40233965Sjdpsetinteractive(on)
403218822Sdim	int on;
40433965Sjdp{
405	static int is_interactive = 0;
406
407	if (on == is_interactive)
408		return;
409	setsignal(SIGINT);
410	setsignal(SIGQUIT);
411	setsignal(SIGTERM);
412	is_interactive = on;
413}
414
415
416/*
417 * Called to exit the shell.
418 */
419void
420exitshell(status)
421	int status;
422{
423	struct jmploc loc1, loc2;
424	char *p;
425
426	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
427	if (setjmp(loc1.loc)) {
428		goto l1;
429	}
430	if (setjmp(loc2.loc)) {
431		goto l2;
432	}
433	handler = &loc1;
434	if ((p = trap[0]) != NULL && *p != '\0') {
435		trap[0] = NULL;
436		evalstring(p);
437	}
438l1:   handler = &loc2;			/* probably unnecessary */
439	flushall();
440#if JOBS
441	setjobctl(0);
442#endif
443l2:   _exit(status);
444}
445