trap.c revision 218285
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 218285 2011-02-04 16:40:50Z jilles $");
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;
82static int last_trapsig;
83
84static int exiting;		/* exitshell() has been called */
85static int exiting_exitstatus;	/* value passed to exitshell() */
86
87static int getsigaction(int, sig_t *);
88
89
90/*
91 * Map a string to a signal number.
92 *
93 * Note: the signal number may exceed NSIG.
94 */
95static int
96sigstring_to_signum(char *sig)
97{
98
99	if (is_number(sig)) {
100		int signo;
101
102		signo = atoi(sig);
103		return ((signo >= 0 && signo < NSIG) ? signo : (-1));
104	} else if (strcasecmp(sig, "EXIT") == 0) {
105		return (0);
106	} else {
107		int n;
108
109		if (strncasecmp(sig, "SIG", 3) == 0)
110			sig += 3;
111		for (n = 1; n < sys_nsig; n++)
112			if (sys_signame[n] &&
113			    strcasecmp(sys_signame[n], sig) == 0)
114				return (n);
115	}
116	return (-1);
117}
118
119
120/*
121 * Print a list of valid signal names.
122 */
123static void
124printsignals(void)
125{
126	int n, outlen;
127
128	outlen = 0;
129	for (n = 1; n < sys_nsig; n++) {
130		if (sys_signame[n]) {
131			out1fmt("%s", sys_signame[n]);
132			outlen += strlen(sys_signame[n]);
133		} else {
134			out1fmt("%d", n);
135			outlen += 3;	/* good enough */
136		}
137		++outlen;
138		if (outlen > 71 || n == sys_nsig - 1) {
139			out1str("\n");
140			outlen = 0;
141		} else {
142			out1c(' ');
143		}
144	}
145}
146
147
148/*
149 * The trap builtin.
150 */
151int
152trapcmd(int argc, char **argv)
153{
154	char *action;
155	int signo;
156	int errors = 0;
157	int i;
158
159	while ((i = nextopt("l")) != '\0') {
160		switch (i) {
161		case 'l':
162			printsignals();
163			return (0);
164		}
165	}
166	argv = argptr;
167
168	if (*argv == NULL) {
169		for (signo = 0 ; signo < sys_nsig ; signo++) {
170			if (signo < NSIG && trap[signo] != NULL) {
171				out1str("trap -- ");
172				out1qstr(trap[signo]);
173				if (signo == 0) {
174					out1str(" EXIT\n");
175				} else if (sys_signame[signo]) {
176					out1fmt(" %s\n", sys_signame[signo]);
177				} else {
178					out1fmt(" %d\n", signo);
179				}
180			}
181		}
182		return 0;
183	}
184	action = NULL;
185	if (*argv && sigstring_to_signum(*argv) == -1) {
186		if (strcmp(*argv, "-") == 0)
187			argv++;
188		else {
189			action = *argv;
190			argv++;
191		}
192	}
193	while (*argv) {
194		if ((signo = sigstring_to_signum(*argv)) == -1) {
195			warning("bad signal %s", *argv);
196			errors = 1;
197		}
198		INTOFF;
199		if (action)
200			action = savestr(action);
201		if (trap[signo])
202			ckfree(trap[signo]);
203		trap[signo] = action;
204		if (signo != 0)
205			setsignal(signo);
206		INTON;
207		argv++;
208	}
209	return errors;
210}
211
212
213/*
214 * Clear traps on a fork.
215 */
216void
217clear_traps(void)
218{
219	char *volatile *tp;
220
221	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
222		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
223			INTOFF;
224			ckfree(*tp);
225			*tp = NULL;
226			if (tp != &trap[0])
227				setsignal(tp - trap);
228			INTON;
229		}
230	}
231}
232
233
234/*
235 * Check if we have any traps enabled.
236 */
237int
238have_traps(void)
239{
240	char *volatile *tp;
241
242	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
243		if (*tp && **tp)	/* trap not NULL or SIG_IGN */
244			return 1;
245	}
246	return 0;
247}
248
249/*
250 * Set the signal handler for the specified signal.  The routine figures
251 * out what it should be set to.
252 */
253void
254setsignal(int signo)
255{
256	int action;
257	sig_t sigact = SIG_DFL;
258	struct sigaction sa;
259	char *t;
260
261	if ((t = trap[signo]) == NULL)
262		action = S_DFL;
263	else if (*t != '\0')
264		action = S_CATCH;
265	else
266		action = S_IGN;
267	if (action == S_DFL) {
268		switch (signo) {
269		case SIGINT:
270			action = S_CATCH;
271			break;
272		case SIGQUIT:
273#ifdef DEBUG
274			{
275			extern int debug;
276
277			if (debug)
278				break;
279			}
280#endif
281			action = S_CATCH;
282			break;
283		case SIGTERM:
284			if (rootshell && iflag)
285				action = S_IGN;
286			break;
287#if JOBS
288		case SIGTSTP:
289		case SIGTTOU:
290			if (rootshell && mflag)
291				action = S_IGN;
292			break;
293#endif
294#ifndef NO_HISTORY
295		case SIGWINCH:
296			if (rootshell && iflag)
297				action = S_CATCH;
298			break;
299#endif
300		}
301	}
302
303	t = &sigmode[signo];
304	if (*t == 0) {
305		/*
306		 * current setting unknown
307		 */
308		if (!getsigaction(signo, &sigact)) {
309			/*
310			 * Pretend it worked; maybe we should give a warning
311			 * here, but other shells don't. We don't alter
312			 * sigmode, so that we retry every time.
313			 */
314			return;
315		}
316		if (sigact == SIG_IGN) {
317			if (mflag && (signo == SIGTSTP ||
318			     signo == SIGTTIN || signo == SIGTTOU)) {
319				*t = S_IGN;	/* don't hard ignore these */
320			} else
321				*t = S_HARD_IGN;
322		} else {
323			*t = S_RESET;	/* force to be set */
324		}
325	}
326	if (*t == S_HARD_IGN || *t == action)
327		return;
328	switch (action) {
329		case S_DFL:	sigact = SIG_DFL;	break;
330		case S_CATCH:  	sigact = onsig;		break;
331		case S_IGN:	sigact = SIG_IGN;	break;
332	}
333	*t = action;
334	sa.sa_handler = sigact;
335	sa.sa_flags = 0;
336	sigemptyset(&sa.sa_mask);
337	sigaction(signo, &sa, NULL);
338}
339
340
341/*
342 * Return the current setting for sig w/o changing it.
343 */
344static int
345getsigaction(int signo, sig_t *sigact)
346{
347	struct sigaction sa;
348
349	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
350		return 0;
351	*sigact = (sig_t) sa.sa_handler;
352	return 1;
353}
354
355
356/*
357 * Ignore a signal.
358 */
359void
360ignoresig(int signo)
361{
362
363	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
364		signal(signo, SIG_IGN);
365	}
366	sigmode[signo] = S_HARD_IGN;
367}
368
369
370#ifdef mkinit
371INCLUDE <signal.h>
372INCLUDE "trap.h"
373
374SHELLPROC {
375	char *sm;
376
377	clear_traps();
378	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
379		if (*sm == S_IGN)
380			*sm = S_HARD_IGN;
381	}
382}
383#endif
384
385
386/*
387 * Signal handler.
388 */
389void
390onsig(int signo)
391{
392
393	if (signo == SIGINT && trap[SIGINT] == NULL) {
394		onint();
395		return;
396	}
397
398	if (signo != SIGCHLD || !ignore_sigchld)
399		gotsig[signo] = 1;
400	pendingsigs++;
401
402	/* If we are currently in a wait builtin, prepare to break it */
403	if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0)
404		breakwaitcmd = 1;
405	/*
406	 * If a trap is set, not ignored and not the null command, we need
407	 * to make sure traps are executed even when a child blocks signals.
408	 */
409	if (Tflag &&
410	    trap[signo] != NULL &&
411	    ! (trap[signo][0] == '\0') &&
412	    ! (trap[signo][0] == ':' && trap[signo][1] == '\0'))
413		breakwaitcmd = 1;
414
415#ifndef NO_HISTORY
416	if (signo == SIGWINCH)
417		gotwinch = 1;
418#endif
419}
420
421
422/*
423 * Called to execute a trap.  Perhaps we should avoid entering new trap
424 * handlers while we are executing a trap handler.
425 */
426void
427dotrap(void)
428{
429	int i;
430	int savestatus;
431
432	in_dotrap++;
433	for (;;) {
434		for (i = 1; i < NSIG; i++) {
435			if (gotsig[i]) {
436				gotsig[i] = 0;
437				if (trap[i]) {
438					/*
439					 * Ignore SIGCHLD to avoid infinite
440					 * recursion if the trap action does
441					 * a fork.
442					 */
443					if (i == SIGCHLD)
444						ignore_sigchld++;
445					last_trapsig = i;
446					savestatus = exitstatus;
447					evalstring(trap[i], 0);
448					exitstatus = savestatus;
449					if (i == SIGCHLD)
450						ignore_sigchld--;
451				}
452				break;
453			}
454		}
455		if (i >= NSIG)
456			break;
457	}
458	in_dotrap--;
459	pendingsigs = 0;
460}
461
462
463/*
464 * Controls whether the shell is interactive or not.
465 */
466void
467setinteractive(int on)
468{
469	static int is_interactive = -1;
470
471	if (on == is_interactive)
472		return;
473	setsignal(SIGINT);
474	setsignal(SIGQUIT);
475	setsignal(SIGTERM);
476#ifndef NO_HISTORY
477	setsignal(SIGWINCH);
478#endif
479	is_interactive = on;
480}
481
482
483/*
484 * Called to exit the shell.
485 */
486void
487exitshell(int status)
488{
489	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
490	exiting = 1;
491	exiting_exitstatus = status;
492	exitshell_savedstatus();
493}
494
495void
496exitshell_savedstatus(void)
497{
498	struct jmploc loc1, loc2;
499	char *p;
500	int sig = 0;
501	sigset_t sigs;
502
503	if (!exiting) {
504		if (in_dotrap && last_trapsig) {
505			sig = last_trapsig;
506			exiting_exitstatus = sig + 128;
507		} else
508			exiting_exitstatus = oexitstatus;
509	}
510	exitstatus = oexitstatus = exiting_exitstatus;
511	if (setjmp(loc1.loc)) {
512		goto l1;
513	}
514	if (setjmp(loc2.loc)) {
515		goto l2;
516	}
517	handler = &loc1;
518	if ((p = trap[0]) != NULL && *p != '\0') {
519		trap[0] = NULL;
520		evalstring(p, 0);
521	}
522l1:   handler = &loc2;			/* probably unnecessary */
523	flushall();
524#if JOBS
525	setjobctl(0);
526#endif
527l2:
528	if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN &&
529	    sig != SIGTTOU) {
530		signal(sig, SIG_DFL);
531		sigemptyset(&sigs);
532		sigaddset(&sigs, sig);
533		sigprocmask(SIG_UNBLOCK, &sigs, NULL);
534		kill(getpid(), sig);
535		/* If the default action is to ignore, fall back to _exit(). */
536	}
537	_exit(exiting_exitstatus);
538}
539