trap.c revision 36150
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
41static const char rcsid[] =
42	"$Id$";
43#endif /* not lint */
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
63
64/*
65 * Sigmode records the current value of the signal handlers for the various
66 * modes.  A value of zero means that the current handler is not known.
67 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
68 */
69
70#define S_DFL 1			/* default signal handling (SIG_DFL) */
71#define S_CATCH 2		/* signal is caught */
72#define S_IGN 3			/* signal is ignored (SIG_IGN) */
73#define S_HARD_IGN 4		/* signal is ignored permenantly */
74#define S_RESET 5		/* temporary - to reset a hard ignored sig */
75
76
77MKINIT char sigmode[NSIG];	/* current value of signal */
78int pendingsigs;			/* indicates some signal received */
79static char *trap[NSIG];	/* trap handler commands */
80static char gotsig[NSIG];	/* indicates specified signal received */
81static int ignore_sigchld;	/* Used while handling SIGCHLD traps. */
82
83static int getsigaction __P((int, sig_t *));
84
85
86/*
87 * Map a string to a signal number.
88 */
89static int
90sigstring_to_signum(sig)
91	char *sig;
92{
93
94	if (is_number(sig)) {
95		int signo;
96
97		signo = atoi(sig);
98		return ((signo >= 0 && signo < NSIG) ? signo : (-1));
99	} else if (strcasecmp(sig, "exit") == 0) {
100		return (0);
101	} else {
102		int n;
103
104		if (strncasecmp(sig, "sig", 3) == 0)
105			sig += 3;
106		for (n = 1; n < NSIG; n++)
107			if (strcasecmp(sys_signame[n], sig) == 0)
108				return (n);
109	}
110	return (-1);
111}
112
113
114/*
115 * Print a list of valid signal names.
116 */
117static void
118printsignals()
119{
120	int n;
121
122	for (n = 1; n < NSIG; n++) {
123		out1fmt("%s", sys_signame[n]);
124		if (n == (NSIG / 2) || n == (NSIG - 1))
125			out1str("\n");
126		else
127			out1c(' ');
128	}
129}
130
131
132/*
133 * The trap builtin.
134 */
135int
136trapcmd(argc, argv)
137	int argc;
138	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()
190{
191	char **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(signo)
212	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 (rootshell && action == S_DFL) {
225		switch (signo) {
226		case SIGINT:
227			if (iflag)
228				action = S_CATCH;
229			break;
230		case SIGQUIT:
231#ifdef DEBUG
232			{
233			extern int debug;
234
235			if (debug)
236				break;
237			}
238#endif
239			/* FALLTHROUGH */
240		case SIGTERM:
241			if (iflag)
242				action = S_IGN;
243			break;
244#if JOBS
245		case SIGTSTP:
246		case SIGTTOU:
247			if (mflag)
248				action = S_IGN;
249			break;
250#endif
251		}
252	}
253
254	t = &sigmode[signo];
255	if (*t == 0) {
256		/*
257		 * current setting unknown
258		 */
259		if (!getsigaction(signo, &sigact)) {
260			/*
261			 * Pretend it worked; maybe we should give a warning
262			 * here, but other shells don't. We don't alter
263			 * sigmode, so that we retry every time.
264			 */
265			return;
266		}
267		if (sigact == SIG_IGN) {
268			if (mflag && (signo == SIGTSTP ||
269			     signo == SIGTTIN || signo == SIGTTOU)) {
270				*t = S_IGN;	/* don't hard ignore these */
271			} else
272				*t = S_HARD_IGN;
273		} else {
274			*t = S_RESET;	/* force to be set */
275		}
276	}
277	if (*t == S_HARD_IGN || *t == action)
278		return;
279	switch (action) {
280		case S_DFL:	sigact = SIG_DFL;	break;
281		case S_CATCH:  	sigact = onsig;		break;
282		case S_IGN:	sigact = SIG_IGN;	break;
283	}
284	*t = action;
285	sig = signal(signo, sigact);
286#ifdef BSD
287	if (sig != SIG_ERR && action == S_CATCH)
288		siginterrupt(signo, 1);
289#endif
290}
291
292
293/*
294 * Return the current setting for sig w/o changing it.
295 */
296static int
297getsigaction(signo, sigact)
298	int signo;
299	sig_t *sigact;
300{
301	struct sigaction sa;
302
303	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
304		return 0;
305	*sigact = (sig_t) sa.sa_handler;
306	return 1;
307}
308
309
310/*
311 * Ignore a signal.
312 */
313void
314ignoresig(signo)
315	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(signo)
346	int signo;
347{
348
349#ifndef BSD
350	signal(signo, onsig);
351#endif
352	if (signo == SIGINT && trap[SIGINT] == NULL) {
353		onint();
354		return;
355	}
356	if (signo != SIGCHLD || !ignore_sigchld)
357		gotsig[signo] = 1;
358	pendingsigs++;
359}
360
361
362/*
363 * Called to execute a trap.  Perhaps we should avoid entering new trap
364 * handlers while we are executing a trap handler.
365 */
366void
367dotrap()
368{
369	int i;
370	int savestatus;
371
372	for (;;) {
373		for (i = 1; i < NSIG; i++) {
374			if (gotsig[i]) {
375				gotsig[i] = 0;
376				if (trap[i]) {
377					/*
378					 * Ignore SIGCHLD to avoid infinite recursion
379					 * if the trap action does a fork.
380					 */
381					if (i == SIGCHLD)
382						ignore_sigchld++;
383					savestatus = exitstatus;
384					evalstring(trap[i]);
385					exitstatus = savestatus;
386					if (i == SIGCHLD)
387						ignore_sigchld--;
388				}
389				break;
390			}
391		}
392		if (i >= NSIG)
393			break;
394	}
395	pendingsigs = 0;
396}
397
398
399/*
400 * Controls whether the shell is interactive or not.
401 */
402void
403setinteractive(on)
404	int on;
405{
406	static int is_interactive = 0;
407
408	if (on == is_interactive)
409		return;
410	setsignal(SIGINT);
411	setsignal(SIGQUIT);
412	setsignal(SIGTERM);
413	is_interactive = on;
414}
415
416
417/*
418 * Called to exit the shell.
419 */
420void
421exitshell(status)
422	int status;
423{
424	struct jmploc loc1, loc2;
425	char *p;
426
427	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
428	if (setjmp(loc1.loc)) {
429		goto l1;
430	}
431	if (setjmp(loc2.loc)) {
432		goto l2;
433	}
434	handler = &loc1;
435	if ((p = trap[0]) != NULL && *p != '\0') {
436		trap[0] = NULL;
437		evalstring(p);
438	}
439l1:   handler = &loc2;			/* probably unnecessary */
440	flushall();
441#if JOBS
442	setjobctl(0);
443#endif
444l2:   _exit(status);
445}
446