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