trap.c revision 46684
1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static char sccsid[] = "@(#)trap.c	8.5 (Berkeley) 6/5/95";
40#endif
41static const char rcsid[] =
42	"$Id: trap.c,v 1.18 1999/04/01 13:27:36 cracauer Exp $";
43#endif /* not lint */
44
45#include <signal.h>
46#include <unistd.h>
47#include <stdlib.h>
48
49#include "shell.h"
50#include "main.h"
51#include "nodes.h"	/* for other headers */
52#include "eval.h"
53#include "jobs.h"
54#include "show.h"
55#include "options.h"
56#include "syntax.h"
57#include "output.h"
58#include "memalloc.h"
59#include "error.h"
60#include "trap.h"
61#include "mystring.h"
62
63
64/*
65 * Sigmode records the current value of the signal handlers for the various
66 * modes.  A value of zero means that the current handler is not known.
67 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
68 */
69
70#define S_DFL 1			/* default signal handling (SIG_DFL) */
71#define S_CATCH 2		/* signal is caught */
72#define S_IGN 3			/* signal is ignored (SIG_IGN) */
73#define S_HARD_IGN 4		/* signal is ignored permanently */
74#define S_RESET 5		/* temporary - to reset a hard ignored sig */
75
76
77MKINIT char sigmode[NSIG];	/* current value of signal */
78int pendingsigs;		/* indicates some signal received */
79int in_dotrap;			/* do we execute in a trap handler? */
80static char *volatile trap[NSIG];	/* trap handler commands */
81static volatile sig_atomic_t gotsig[NSIG];
82				/* indicates specified signal received */
83static int ignore_sigchld;	/* Used while handling SIGCHLD traps. */
84
85static int getsigaction __P((int, sig_t *));
86
87
88/*
89 * Map a string to a signal number.
90 */
91static int
92sigstring_to_signum(sig)
93	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 < NSIG; n++)
109			if (strcasecmp(sys_signame[n], sig) == 0)
110				return (n);
111	}
112	return (-1);
113}
114
115
116/*
117 * Print a list of valid signal names.
118 */
119static void
120printsignals()
121{
122	int n;
123
124	for (n = 1; n < NSIG; n++) {
125		out1fmt("%s", sys_signame[n]);
126		if (n == (NSIG / 2) || n == (NSIG - 1))
127			out1str("\n");
128		else
129			out1c(' ');
130	}
131}
132
133
134/*
135 * The trap builtin.
136 */
137int
138trapcmd(argc, argv)
139	int argc;
140	char **argv;
141{
142	char *action;
143	int signo;
144
145	if (argc <= 1) {
146		for (signo = 0 ; signo < NSIG ; signo++) {
147			if (trap[signo] != NULL)
148				out1fmt("trap -- '%s' %s\n", trap[signo],
149					(signo) ? sys_signame[signo] : "exit");
150		}
151		return 0;
152	}
153	action = NULL;
154	if (*++argv && strcmp(*argv, "--") == 0)
155		argv++;
156	if (*argv && sigstring_to_signum(*argv) == -1) {
157		if ((*argv)[0] != '-') {
158			action = *argv;
159			argv++;
160		} else if ((*argv)[1] == '\0') {
161			argv++;
162		} else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
163			printsignals();
164			return 0;
165		} else {
166			error("bad option %s", *argv);
167		}
168	}
169	while (*argv) {
170		if ((signo = sigstring_to_signum(*argv)) == -1)
171			error("bad signal %s", *argv);
172		INTOFF;
173		if (action)
174			action = savestr(action);
175		if (trap[signo])
176			ckfree(trap[signo]);
177		trap[signo] = action;
178		if (signo != 0)
179			setsignal(signo);
180		INTON;
181		argv++;
182	}
183	return 0;
184}
185
186
187/*
188 * Clear traps on a fork.
189 */
190void
191clear_traps()
192{
193	char *volatile *tp;
194
195	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
196		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
197			INTOFF;
198			ckfree(*tp);
199			*tp = NULL;
200			if (tp != &trap[0])
201				setsignal(tp - trap);
202			INTON;
203		}
204	}
205}
206
207
208/*
209 * Set the signal handler for the specified signal.  The routine figures
210 * out what it should be set to.
211 */
212void
213setsignal(signo)
214	int signo;
215{
216	int action;
217	sig_t sig, sigact = SIG_DFL;
218	char *t;
219
220	if ((t = trap[signo]) == NULL)
221		action = S_DFL;
222	else if (*t != '\0')
223		action = S_CATCH;
224	else
225		action = S_IGN;
226	if (action == S_DFL) {
227		switch (signo) {
228		case SIGINT:
229			action = S_CATCH;
230			break;
231		case SIGQUIT:
232#ifdef DEBUG
233			{
234			extern int debug;
235
236			if (debug)
237				break;
238			}
239#endif
240			action = S_CATCH;
241			break;
242		case SIGTERM:
243			if (rootshell && iflag)
244				action = S_IGN;
245			break;
246#if JOBS
247		case SIGTSTP:
248		case SIGTTOU:
249			if (rootshell && mflag)
250				action = S_IGN;
251			break;
252#endif
253		}
254	}
255
256	t = &sigmode[signo];
257	if (*t == 0) {
258		/*
259		 * current setting unknown
260		 */
261		if (!getsigaction(signo, &sigact)) {
262			/*
263			 * Pretend it worked; maybe we should give a warning
264			 * here, but other shells don't. We don't alter
265			 * sigmode, so that we retry every time.
266			 */
267			return;
268		}
269		if (sigact == SIG_IGN) {
270			if (mflag && (signo == SIGTSTP ||
271			     signo == SIGTTIN || signo == SIGTTOU)) {
272				*t = S_IGN;	/* don't hard ignore these */
273			} else
274				*t = S_HARD_IGN;
275		} else {
276			*t = S_RESET;	/* force to be set */
277		}
278	}
279	if (*t == S_HARD_IGN || *t == action)
280		return;
281	switch (action) {
282		case S_DFL:	sigact = SIG_DFL;	break;
283		case S_CATCH:  	sigact = onsig;		break;
284		case S_IGN:	sigact = SIG_IGN;	break;
285	}
286	*t = action;
287	sig = signal(signo, sigact);
288#ifdef BSD
289	if (sig != SIG_ERR && action == S_CATCH)
290		siginterrupt(signo, 1);
291#endif
292}
293
294
295/*
296 * Return the current setting for sig w/o changing it.
297 */
298static int
299getsigaction(signo, sigact)
300	int signo;
301	sig_t *sigact;
302{
303	struct sigaction sa;
304
305	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
306		return 0;
307	*sigact = (sig_t) sa.sa_handler;
308	return 1;
309}
310
311
312/*
313 * Ignore a signal.
314 */
315void
316ignoresig(signo)
317	int signo;
318{
319
320	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
321		signal(signo, SIG_IGN);
322	}
323	sigmode[signo] = S_HARD_IGN;
324}
325
326
327#ifdef mkinit
328INCLUDE <signal.h>
329INCLUDE "trap.h"
330
331SHELLPROC {
332	char *sm;
333
334	clear_traps();
335	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
336		if (*sm == S_IGN)
337			*sm = S_HARD_IGN;
338	}
339}
340#endif
341
342
343/*
344 * Signal handler.
345 */
346void
347onsig(signo)
348	int signo;
349{
350
351#ifndef BSD
352	signal(signo, onsig);
353#endif
354	if (signo == SIGINT && trap[SIGINT] == NULL) {
355		onint();
356		return;
357	}
358
359	if (signo != SIGCHLD || !ignore_sigchld)
360		gotsig[signo] = 1;
361	pendingsigs++;
362
363	/* If we are currently in a wait builtin, prepare to break it */
364	if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0)
365		breakwaitcmd = 1;
366	/*
367	 * If a trap is set, not ignored and not the null command, we need
368	 * to make sure traps are executed even when a child blocks signals.
369	 */
370	if (Tflag &&
371	    trap[signo] != NULL &&
372	    ! trap[signo][0] == '\0' &&
373	    ! (trap[signo][0] == ':' && trap[signo][1] == '\0'))
374		breakwaitcmd = 1;
375}
376
377
378/*
379 * Called to execute a trap.  Perhaps we should avoid entering new trap
380 * handlers while we are executing a trap handler.
381 */
382void
383dotrap()
384{
385	int i;
386	int savestatus;
387
388	in_dotrap++;
389	for (;;) {
390		for (i = 1; i < NSIG; i++) {
391			if (gotsig[i]) {
392				gotsig[i] = 0;
393				if (trap[i]) {
394					/*
395					 * Ignore SIGCHLD to avoid infinite recursion
396					 * if the trap action does a fork.
397					 */
398					if (i == SIGCHLD)
399						ignore_sigchld++;
400					savestatus = exitstatus;
401					evalstring(trap[i]);
402					exitstatus = savestatus;
403					if (i == SIGCHLD)
404						ignore_sigchld--;
405				}
406				break;
407			}
408		}
409		if (i >= NSIG)
410			break;
411	}
412	in_dotrap--;
413	pendingsigs = 0;
414}
415
416
417/*
418 * Controls whether the shell is interactive or not.
419 */
420void
421setinteractive(on)
422	int on;
423{
424	static int is_interactive = -1;
425
426	if (on == is_interactive)
427		return;
428	setsignal(SIGINT);
429	setsignal(SIGQUIT);
430	setsignal(SIGTERM);
431	is_interactive = on;
432}
433
434
435/*
436 * Called to exit the shell.
437 */
438void
439exitshell(status)
440	int status;
441{
442	struct jmploc loc1, loc2;
443	char *p;
444
445	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
446	if (setjmp(loc1.loc)) {
447		goto l1;
448	}
449	if (setjmp(loc2.loc)) {
450		goto l2;
451	}
452	handler = &loc1;
453	if ((p = trap[0]) != NULL && *p != '\0') {
454		trap[0] = NULL;
455		evalstring(p);
456	}
457l1:   handler = &loc2;			/* probably unnecessary */
458	flushall();
459#if JOBS
460	setjobctl(0);
461#endif
462l2:   _exit(status);
463}
464