trap.c revision 125155
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 125155 2004-01-28 19:01:10Z njl $");
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. */
85volatile sig_atomic_t gotwinch;
86
87static int getsigaction(int, sig_t *);
88
89
90/*
91 * Map a string to a signal number.
92 */
93static int
94sigstring_to_signum(char *sig)
95{
96
97	if (is_number(sig)) {
98		int signo;
99
100		signo = atoi(sig);
101		return ((signo >= 0 && signo < NSIG) ? signo : (-1));
102	} else if (strcasecmp(sig, "exit") == 0) {
103		return (0);
104	} else {
105		int n;
106
107		if (strncasecmp(sig, "sig", 3) == 0)
108			sig += 3;
109		for (n = 1; n < sys_nsig; n++)
110			if (sys_signame[n] && strcasecmp(sys_signame[n], sig) == 0)
111				return (n);
112	}
113	return (-1);
114}
115
116
117/*
118 * Print a list of valid signal names.
119 */
120static void
121printsignals(void)
122{
123	int n;
124
125	for (n = 1; n < sys_nsig; n++) {
126		out1fmt("%s", sys_signame[n]);
127		if (n == (NSIG / 2) || n == (NSIG - 1))
128			out1str("\n");
129		else
130			out1c(' ');
131	}
132}
133
134
135/*
136 * The trap builtin.
137 */
138int
139trapcmd(int argc, char **argv)
140{
141	char *action;
142	int signo;
143
144	if (argc <= 1) {
145		for (signo = 0 ; signo < sys_nsig ; signo++) {
146			if (trap[signo] != NULL)
147				out1fmt("trap -- '%s' %s\n", trap[signo],
148					(signo) ? sys_signame[signo] : "exit");
149		}
150		return 0;
151	}
152	action = NULL;
153	if (*++argv && strcmp(*argv, "--") == 0)
154		argv++;
155	if (*argv && sigstring_to_signum(*argv) == -1) {
156		if ((*argv)[0] != '-') {
157			action = *argv;
158			argv++;
159		} else if ((*argv)[1] == '\0') {
160			argv++;
161		} else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
162			printsignals();
163			return 0;
164		} else {
165			error("bad option %s", *argv);
166		}
167	}
168	while (*argv) {
169		if ((signo = sigstring_to_signum(*argv)) == -1)
170			error("bad signal %s", *argv);
171		INTOFF;
172		if (action)
173			action = savestr(action);
174		if (trap[signo])
175			ckfree(trap[signo]);
176		trap[signo] = action;
177		if (signo != 0)
178			setsignal(signo);
179		INTON;
180		argv++;
181	}
182	return 0;
183}
184
185
186/*
187 * Clear traps on a fork.
188 */
189void
190clear_traps(void)
191{
192	char *volatile *tp;
193
194	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
195		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
196			INTOFF;
197			ckfree(*tp);
198			*tp = NULL;
199			if (tp != &trap[0])
200				setsignal(tp - trap);
201			INTON;
202		}
203	}
204}
205
206
207/*
208 * Set the signal handler for the specified signal.  The routine figures
209 * out what it should be set to.
210 */
211void
212setsignal(int signo)
213{
214	int action;
215	sig_t sig, sigact = SIG_DFL;
216	char *t;
217
218	if ((t = trap[signo]) == NULL)
219		action = S_DFL;
220	else if (*t != '\0')
221		action = S_CATCH;
222	else
223		action = S_IGN;
224	if (action == S_DFL) {
225		switch (signo) {
226		case SIGINT:
227			action = S_CATCH;
228			break;
229		case SIGQUIT:
230#ifdef DEBUG
231			{
232			extern int debug;
233
234			if (debug)
235				break;
236			}
237#endif
238			action = S_CATCH;
239			break;
240		case SIGTERM:
241			if (rootshell && iflag)
242				action = S_IGN;
243			break;
244#if JOBS
245		case SIGTSTP:
246		case SIGTTOU:
247			if (rootshell && mflag)
248				action = S_IGN;
249			break;
250#endif
251#ifndef NO_HISTORY
252		case SIGWINCH:
253			if (rootshell && iflag)
254				action = S_CATCH;
255			break;
256#endif
257		}
258	}
259
260	t = &sigmode[signo];
261	if (*t == 0) {
262		/*
263		 * current setting unknown
264		 */
265		if (!getsigaction(signo, &sigact)) {
266			/*
267			 * Pretend it worked; maybe we should give a warning
268			 * here, but other shells don't. We don't alter
269			 * sigmode, so that we retry every time.
270			 */
271			return;
272		}
273		if (sigact == SIG_IGN) {
274			if (mflag && (signo == SIGTSTP ||
275			     signo == SIGTTIN || signo == SIGTTOU)) {
276				*t = S_IGN;	/* don't hard ignore these */
277			} else
278				*t = S_HARD_IGN;
279		} else {
280			*t = S_RESET;	/* force to be set */
281		}
282	}
283	if (*t == S_HARD_IGN || *t == action)
284		return;
285	switch (action) {
286		case S_DFL:	sigact = SIG_DFL;	break;
287		case S_CATCH:  	sigact = onsig;		break;
288		case S_IGN:	sigact = SIG_IGN;	break;
289	}
290	*t = action;
291	sig = signal(signo, sigact);
292	if (sig != SIG_ERR && action == S_CATCH)
293		siginterrupt(signo, 1);
294}
295
296
297/*
298 * Return the current setting for sig w/o changing it.
299 */
300static int
301getsigaction(int signo, 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(int signo)
317{
318
319	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
320		signal(signo, SIG_IGN);
321	}
322	sigmode[signo] = S_HARD_IGN;
323}
324
325
326#ifdef mkinit
327INCLUDE <signal.h>
328INCLUDE "trap.h"
329
330SHELLPROC {
331	char *sm;
332
333	clear_traps();
334	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
335		if (*sm == S_IGN)
336			*sm = S_HARD_IGN;
337	}
338}
339#endif
340
341
342/*
343 * Signal handler.
344 */
345void
346onsig(int signo)
347{
348
349	if (signo == SIGINT && trap[SIGINT] == NULL) {
350		onint();
351		return;
352	}
353
354	if (signo != SIGCHLD || !ignore_sigchld)
355		gotsig[signo] = 1;
356	pendingsigs++;
357
358	/* If we are currently in a wait builtin, prepare to break it */
359	if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0)
360		breakwaitcmd = 1;
361	/*
362	 * If a trap is set, not ignored and not the null command, we need
363	 * to make sure traps are executed even when a child blocks signals.
364	 */
365	if (Tflag &&
366	    trap[signo] != NULL &&
367	    ! trap[signo][0] == '\0' &&
368	    ! (trap[signo][0] == ':' && trap[signo][1] == '\0'))
369		breakwaitcmd = 1;
370
371#ifndef NO_HISTORY
372	if (signo == SIGWINCH)
373		gotwinch = 1;
374#endif
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(void)
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(int on)
422{
423	static int is_interactive = -1;
424
425	if (on == is_interactive)
426		return;
427	setsignal(SIGINT);
428	setsignal(SIGQUIT);
429	setsignal(SIGTERM);
430#ifndef NO_HISTORY
431	setsignal(SIGWINCH);
432#endif
433	is_interactive = on;
434}
435
436
437/*
438 * Called to exit the shell.
439 */
440void
441exitshell(int status)
442{
443	struct jmploc loc1, loc2;
444	char *p;
445
446	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
447	if (setjmp(loc1.loc)) {
448		goto l1;
449	}
450	if (setjmp(loc2.loc)) {
451		goto l2;
452	}
453	handler = &loc1;
454	if ((p = trap[0]) != NULL && *p != '\0') {
455		trap[0] = NULL;
456		evalstring(p);
457	}
458l1:   handler = &loc2;			/* probably unnecessary */
459	flushall();
460#if JOBS
461	setjobctl(0);
462#endif
463l2:   _exit(status);
464}
465