trap.c revision 100578
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
41#endif /* not lint */
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD: head/bin/sh/trap.c 100578 2002-07-23 15:05:00Z tjr $");
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#include "myhistedit.h"
63
64
65/*
66 * Sigmode records the current value of the signal handlers for the various
67 * modes.  A value of zero means that the current handler is not known.
68 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
69 */
70
71#define S_DFL 1			/* default signal handling (SIG_DFL) */
72#define S_CATCH 2		/* signal is caught */
73#define S_IGN 3			/* signal is ignored (SIG_IGN) */
74#define S_HARD_IGN 4		/* signal is ignored permanently */
75#define S_RESET 5		/* temporary - to reset a hard ignored sig */
76
77
78MKINIT char sigmode[NSIG];	/* current value of signal */
79int pendingsigs;		/* indicates some signal received */
80int in_dotrap;			/* do we execute in a trap handler? */
81static char *volatile trap[NSIG];	/* trap handler commands */
82static volatile sig_atomic_t gotsig[NSIG];
83				/* indicates specified signal received */
84static int ignore_sigchld;	/* Used while handling SIGCHLD traps. */
85
86static int getsigaction(int, sig_t *);
87
88
89/*
90 * Map a string to a signal number.
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 < 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(void)
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(int argc, char **argv)
139{
140	char *action;
141	int signo;
142
143	if (argc <= 1) {
144		for (signo = 0 ; signo < NSIG ; signo++) {
145			if (trap[signo] != NULL)
146				out1fmt("trap -- '%s' %s\n", trap[signo],
147					(signo) ? sys_signame[signo] : "exit");
148		}
149		return 0;
150	}
151	action = NULL;
152	if (*++argv && strcmp(*argv, "--") == 0)
153		argv++;
154	if (*argv && sigstring_to_signum(*argv) == -1) {
155		if ((*argv)[0] != '-') {
156			action = *argv;
157			argv++;
158		} else if ((*argv)[1] == '\0') {
159			argv++;
160		} else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
161			printsignals();
162			return 0;
163		} else {
164			error("bad option %s", *argv);
165		}
166	}
167	while (*argv) {
168		if ((signo = sigstring_to_signum(*argv)) == -1)
169			error("bad signal %s", *argv);
170		INTOFF;
171		if (action)
172			action = savestr(action);
173		if (trap[signo])
174			ckfree(trap[signo]);
175		trap[signo] = action;
176		if (signo != 0)
177			setsignal(signo);
178		INTON;
179		argv++;
180	}
181	return 0;
182}
183
184
185/*
186 * Clear traps on a fork.
187 */
188void
189clear_traps(void)
190{
191	char *volatile *tp;
192
193	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
194		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
195			INTOFF;
196			ckfree(*tp);
197			*tp = NULL;
198			if (tp != &trap[0])
199				setsignal(tp - trap);
200			INTON;
201		}
202	}
203}
204
205
206/*
207 * Set the signal handler for the specified signal.  The routine figures
208 * out what it should be set to.
209 */
210void
211setsignal(int signo)
212{
213	int action;
214	sig_t sig, sigact = SIG_DFL;
215	char *t;
216
217	if ((t = trap[signo]) == NULL)
218		action = S_DFL;
219	else if (*t != '\0')
220		action = S_CATCH;
221	else
222		action = S_IGN;
223	if (action == S_DFL) {
224		switch (signo) {
225		case SIGINT:
226			action = S_CATCH;
227			break;
228		case SIGQUIT:
229#ifdef DEBUG
230			{
231			extern int debug;
232
233			if (debug)
234				break;
235			}
236#endif
237			action = S_CATCH;
238			break;
239		case SIGTERM:
240			if (rootshell && iflag)
241				action = S_IGN;
242			break;
243#if JOBS
244		case SIGTSTP:
245		case SIGTTOU:
246			if (rootshell && mflag)
247				action = S_IGN;
248			break;
249#endif
250#ifndef NO_HISTORY
251		case SIGWINCH:
252			if (rootshell && iflag && el != NULL)
253				action = S_CATCH;
254			break;
255#endif
256		}
257	}
258
259	t = &sigmode[signo];
260	if (*t == 0) {
261		/*
262		 * current setting unknown
263		 */
264		if (!getsigaction(signo, &sigact)) {
265			/*
266			 * Pretend it worked; maybe we should give a warning
267			 * here, but other shells don't. We don't alter
268			 * sigmode, so that we retry every time.
269			 */
270			return;
271		}
272		if (sigact == SIG_IGN) {
273			if (mflag && (signo == SIGTSTP ||
274			     signo == SIGTTIN || signo == SIGTTOU)) {
275				*t = S_IGN;	/* don't hard ignore these */
276			} else
277				*t = S_HARD_IGN;
278		} else {
279			*t = S_RESET;	/* force to be set */
280		}
281	}
282	if (*t == S_HARD_IGN || *t == action)
283		return;
284	switch (action) {
285		case S_DFL:	sigact = SIG_DFL;	break;
286		case S_CATCH:  	sigact = onsig;		break;
287		case S_IGN:	sigact = SIG_IGN;	break;
288	}
289	*t = action;
290	sig = signal(signo, sigact);
291	if (sig != SIG_ERR && action == S_CATCH)
292		siginterrupt(signo, 1);
293}
294
295
296/*
297 * Return the current setting for sig w/o changing it.
298 */
299static int
300getsigaction(int signo, sig_t *sigact)
301{
302	struct sigaction sa;
303
304	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
305		return 0;
306	*sigact = (sig_t) sa.sa_handler;
307	return 1;
308}
309
310
311/*
312 * Ignore a signal.
313 */
314void
315ignoresig(int signo)
316{
317
318	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
319		signal(signo, SIG_IGN);
320	}
321	sigmode[signo] = S_HARD_IGN;
322}
323
324
325#ifdef mkinit
326INCLUDE <signal.h>
327INCLUDE "trap.h"
328
329SHELLPROC {
330	char *sm;
331
332	clear_traps();
333	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
334		if (*sm == S_IGN)
335			*sm = S_HARD_IGN;
336	}
337}
338#endif
339
340
341/*
342 * Signal handler.
343 */
344void
345onsig(int signo)
346{
347
348	if (signo == SIGINT && trap[SIGINT] == NULL) {
349		onint();
350		return;
351	}
352
353	if (signo != SIGCHLD || !ignore_sigchld)
354		gotsig[signo] = 1;
355	pendingsigs++;
356
357	/* If we are currently in a wait builtin, prepare to break it */
358	if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0)
359		breakwaitcmd = 1;
360	/*
361	 * If a trap is set, not ignored and not the null command, we need
362	 * to make sure traps are executed even when a child blocks signals.
363	 */
364	if (Tflag &&
365	    trap[signo] != NULL &&
366	    ! trap[signo][0] == '\0' &&
367	    ! (trap[signo][0] == ':' && trap[signo][1] == '\0'))
368		breakwaitcmd = 1;
369
370#ifndef NO_HISTORY
371	if (signo == SIGWINCH)
372		el_resize(el);
373#endif
374}
375
376
377/*
378 * Called to execute a trap.  Perhaps we should avoid entering new trap
379 * handlers while we are executing a trap handler.
380 */
381void
382dotrap(void)
383{
384	int i;
385	int savestatus;
386
387	in_dotrap++;
388	for (;;) {
389		for (i = 1; i < NSIG; i++) {
390			if (gotsig[i]) {
391				gotsig[i] = 0;
392				if (trap[i]) {
393					/*
394					 * Ignore SIGCHLD to avoid infinite recursion
395					 * if the trap action does a fork.
396					 */
397					if (i == SIGCHLD)
398						ignore_sigchld++;
399					savestatus = exitstatus;
400					evalstring(trap[i]);
401					exitstatus = savestatus;
402					if (i == SIGCHLD)
403						ignore_sigchld--;
404				}
405				break;
406			}
407		}
408		if (i >= NSIG)
409			break;
410	}
411	in_dotrap--;
412	pendingsigs = 0;
413}
414
415
416/*
417 * Controls whether the shell is interactive or not.
418 */
419void
420setinteractive(int on)
421{
422	static int is_interactive = -1;
423
424	if (on == is_interactive)
425		return;
426	setsignal(SIGINT);
427	setsignal(SIGQUIT);
428	setsignal(SIGTERM);
429#ifndef NO_HISTORY
430	setsignal(SIGWINCH);
431#endif
432	is_interactive = on;
433}
434
435
436/*
437 * Called to exit the shell.
438 */
439void
440exitshell(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