trap.c revision 218306
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 218306 2011-02-04 22:47:55Z 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/*
371 * Signal handler.
372 */
373void
374onsig(int signo)
375{
376
377	if (signo == SIGINT && trap[SIGINT] == NULL) {
378		onint();
379		return;
380	}
381
382	if (signo != SIGCHLD || !ignore_sigchld)
383		gotsig[signo] = 1;
384	pendingsigs++;
385
386	/* If we are currently in a wait builtin, prepare to break it */
387	if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0)
388		breakwaitcmd = 1;
389	/*
390	 * If a trap is set, not ignored and not the null command, we need
391	 * to make sure traps are executed even when a child blocks signals.
392	 */
393	if (Tflag &&
394	    trap[signo] != NULL &&
395	    ! (trap[signo][0] == '\0') &&
396	    ! (trap[signo][0] == ':' && trap[signo][1] == '\0'))
397		breakwaitcmd = 1;
398
399#ifndef NO_HISTORY
400	if (signo == SIGWINCH)
401		gotwinch = 1;
402#endif
403}
404
405
406/*
407 * Called to execute a trap.  Perhaps we should avoid entering new trap
408 * handlers while we are executing a trap handler.
409 */
410void
411dotrap(void)
412{
413	int i;
414	int savestatus;
415
416	in_dotrap++;
417	for (;;) {
418		for (i = 1; i < NSIG; i++) {
419			if (gotsig[i]) {
420				gotsig[i] = 0;
421				if (trap[i]) {
422					/*
423					 * Ignore SIGCHLD to avoid infinite
424					 * recursion if the trap action does
425					 * a fork.
426					 */
427					if (i == SIGCHLD)
428						ignore_sigchld++;
429					last_trapsig = i;
430					savestatus = exitstatus;
431					evalstring(trap[i], 0);
432					exitstatus = savestatus;
433					if (i == SIGCHLD)
434						ignore_sigchld--;
435				}
436				break;
437			}
438		}
439		if (i >= NSIG)
440			break;
441	}
442	in_dotrap--;
443	pendingsigs = 0;
444}
445
446
447/*
448 * Controls whether the shell is interactive or not.
449 */
450void
451setinteractive(int on)
452{
453	static int is_interactive = -1;
454
455	if (on == is_interactive)
456		return;
457	setsignal(SIGINT);
458	setsignal(SIGQUIT);
459	setsignal(SIGTERM);
460#ifndef NO_HISTORY
461	setsignal(SIGWINCH);
462#endif
463	is_interactive = on;
464}
465
466
467/*
468 * Called to exit the shell.
469 */
470void
471exitshell(int status)
472{
473	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
474	exiting = 1;
475	exiting_exitstatus = status;
476	exitshell_savedstatus();
477}
478
479void
480exitshell_savedstatus(void)
481{
482	struct jmploc loc1, loc2;
483	char *p;
484	int sig = 0;
485	sigset_t sigs;
486
487	if (!exiting) {
488		if (in_dotrap && last_trapsig) {
489			sig = last_trapsig;
490			exiting_exitstatus = sig + 128;
491		} else
492			exiting_exitstatus = oexitstatus;
493	}
494	exitstatus = oexitstatus = exiting_exitstatus;
495	if (setjmp(loc1.loc)) {
496		goto l1;
497	}
498	if (setjmp(loc2.loc)) {
499		goto l2;
500	}
501	handler = &loc1;
502	if ((p = trap[0]) != NULL && *p != '\0') {
503		trap[0] = NULL;
504		evalstring(p, 0);
505	}
506l1:   handler = &loc2;			/* probably unnecessary */
507	flushall();
508#if JOBS
509	setjobctl(0);
510#endif
511l2:
512	if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN &&
513	    sig != SIGTTOU) {
514		signal(sig, SIG_DFL);
515		sigemptyset(&sigs);
516		sigaddset(&sigs, sig);
517		sigprocmask(SIG_UNBLOCK, &sigs, NULL);
518		kill(getpid(), sig);
519		/* If the default action is to ignore, fall back to _exit(). */
520	}
521	_exit(exiting_exitstatus);
522}
523