trap.c revision 31098
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 *	$Id: trap.c,v 1.9 1997/11/05 23:33:58 ache Exp $
37 */
38
39#ifndef lint
40static char const sccsid[] = "@(#)trap.c	8.5 (Berkeley) 6/5/95";
41#endif /* not lint */
42
43#include <signal.h>
44#include <unistd.h>
45#include <stdlib.h>
46
47#include "shell.h"
48#include "main.h"
49#include "nodes.h"	/* for other headers */
50#include "eval.h"
51#include "jobs.h"
52#include "show.h"
53#include "options.h"
54#include "syntax.h"
55#include "output.h"
56#include "memalloc.h"
57#include "error.h"
58#include "trap.h"
59#include "mystring.h"
60
61
62/*
63 * Sigmode records the current value of the signal handlers for the various
64 * modes.  A value of zero means that the current handler is not known.
65 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
66 */
67
68#define S_DFL 1			/* default signal handling (SIG_DFL) */
69#define S_CATCH 2		/* signal is caught */
70#define S_IGN 3			/* signal is ignored (SIG_IGN) */
71#define S_HARD_IGN 4		/* signal is ignored permenantly */
72#define S_RESET 5		/* temporary - to reset a hard ignored sig */
73
74
75MKINIT char sigmode[NSIG];	/* current value of signal */
76int pendingsigs;			/* indicates some signal received */
77static char *trap[NSIG];	/* trap handler commands */
78static char gotsig[NSIG];	/* indicates specified signal received */
79static int ignore_sigchld;	/* Used while handling SIGCHLD traps. */
80
81static int getsigaction __P((int, sig_t *));
82
83
84/*
85 * Map a string to a signal number.
86 */
87static int
88sigstring_to_signum(sig)
89	char *sig;
90{
91
92	if (is_number(sig)) {
93		int signo;
94
95		signo = atoi(sig);
96		return ((signo >= 0 && signo < NSIG) ? signo : (-1));
97	} else if (strcasecmp(sig, "exit") == 0) {
98		return (0);
99	} else {
100		int n;
101
102		if (strncasecmp(sig, "sig", 3) == 0)
103			sig += 3;
104		for (n = 1; n < NSIG; n++)
105			if (strcasecmp(sys_signame[n], sig) == 0)
106				return (n);
107	}
108	return (-1);
109}
110
111
112/*
113 * Print a list of valid signal names.
114 */
115static void
116printsignals()
117{
118	int n;
119
120	for (n = 1; n < NSIG; n++) {
121		out1fmt("%s", sys_signame[n]);
122		if (n == (NSIG / 2) || n == (NSIG - 1))
123			out1str("\n");
124		else
125			out1c(' ');
126	}
127}
128
129
130/*
131 * The trap builtin.
132 */
133int
134trapcmd(argc, argv)
135	int argc;
136	char **argv;
137{
138	char *action;
139	int signo;
140
141	if (argc <= 1) {
142		for (signo = 0 ; signo < NSIG ; signo++) {
143			if (trap[signo] != NULL)
144				out1fmt("trap -- '%s' %s\n", trap[signo],
145					(signo) ? sys_signame[signo] : "exit");
146		}
147		return 0;
148	}
149	action = NULL;
150	if (*++argv && strcmp(*argv, "--") == 0)
151		argv++;
152	if (*argv && sigstring_to_signum(*argv) == -1) {
153		if ((*argv)[0] != '-') {
154			action = *argv;
155			argv++;
156		} else if ((*argv)[1] == '\0') {
157			argv++;
158		} else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
159			printsignals();
160			return 0;
161		} else {
162			error("bad option %s", *argv);
163		}
164	}
165	while (*argv) {
166		if ((signo = sigstring_to_signum(*argv)) == -1)
167			error("bad signal %s", *argv);
168		INTOFF;
169		if (action)
170			action = savestr(action);
171		if (trap[signo])
172			ckfree(trap[signo]);
173		trap[signo] = action;
174		if (signo != 0)
175			setsignal(signo);
176		INTON;
177		argv++;
178	}
179	return 0;
180}
181
182
183/*
184 * Clear traps on a fork.
185 */
186void
187clear_traps()
188{
189	char **tp;
190
191	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
192		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
193			INTOFF;
194			ckfree(*tp);
195			*tp = NULL;
196			if (tp != &trap[0])
197				setsignal(tp - trap);
198			INTON;
199		}
200	}
201}
202
203
204/*
205 * Set the signal handler for the specified signal.  The routine figures
206 * out what it should be set to.
207 */
208void
209setsignal(signo)
210	int signo;
211{
212	int action;
213	sig_t sig, sigact = SIG_DFL;
214	char *t;
215
216	if ((t = trap[signo]) == NULL)
217		action = S_DFL;
218	else if (*t != '\0')
219		action = S_CATCH;
220	else
221		action = S_IGN;
222	if (rootshell && action == S_DFL) {
223		switch (signo) {
224		case SIGINT:
225			if (iflag)
226				action = S_CATCH;
227			break;
228		case SIGQUIT:
229#ifdef DEBUG
230			{
231			extern int debug;
232
233			if (debug)
234				break;
235			}
236#endif
237			/* FALLTHROUGH */
238		case SIGTERM:
239			if (iflag)
240				action = S_IGN;
241			break;
242#if JOBS
243		case SIGTSTP:
244		case SIGTTOU:
245			if (mflag)
246				action = S_IGN;
247			break;
248#endif
249		}
250	}
251
252	t = &sigmode[signo];
253	if (*t == 0) {
254		/*
255		 * current setting unknown
256		 */
257		if (!getsigaction(signo, &sigact)) {
258			/*
259			 * Pretend it worked; maybe we should give a warning
260			 * here, but other shells don't. We don't alter
261			 * sigmode, so that we retry every time.
262			 */
263			return;
264		}
265		if (sigact == SIG_IGN) {
266			if (mflag && (signo == SIGTSTP ||
267			     signo == SIGTTIN || signo == SIGTTOU)) {
268				*t = S_IGN;	/* don't hard ignore these */
269			} else
270				*t = S_HARD_IGN;
271		} else {
272			*t = S_RESET;	/* force to be set */
273		}
274	}
275	if (*t == S_HARD_IGN || *t == action)
276		return;
277	switch (action) {
278		case S_DFL:	sigact = SIG_DFL;	break;
279		case S_CATCH:  	sigact = onsig;		break;
280		case S_IGN:	sigact = SIG_IGN;	break;
281	}
282	*t = action;
283	sig = signal(signo, sigact);
284#ifdef BSD
285	if (sig != SIG_ERR && action == S_CATCH)
286		siginterrupt(signo, 1);
287#endif
288}
289
290
291/*
292 * Return the current setting for sig w/o changing it.
293 */
294static int
295getsigaction(signo, sigact)
296	int signo;
297	sig_t *sigact;
298{
299	struct sigaction sa;
300
301	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
302		return 0;
303	*sigact = (sig_t) sa.sa_handler;
304	return 1;
305}
306
307
308/*
309 * Ignore a signal.
310 */
311void
312ignoresig(signo)
313	int signo;
314{
315
316	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
317		signal(signo, SIG_IGN);
318	}
319	sigmode[signo] = S_HARD_IGN;
320}
321
322
323#ifdef mkinit
324INCLUDE <signal.h>
325INCLUDE "trap.h"
326
327SHELLPROC {
328	char *sm;
329
330	clear_traps();
331	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
332		if (*sm == S_IGN)
333			*sm = S_HARD_IGN;
334	}
335}
336#endif
337
338
339/*
340 * Signal handler.
341 */
342void
343onsig(signo)
344	int signo;
345{
346
347#ifndef BSD
348	signal(signo, onsig);
349#endif
350	if (signo == SIGINT && trap[SIGINT] == NULL) {
351		onint();
352		return;
353	}
354	if (signo != SIGCHLD || !ignore_sigchld)
355		gotsig[signo] = 1;
356	pendingsigs++;
357}
358
359
360/*
361 * Called to execute a trap.  Perhaps we should avoid entering new trap
362 * handlers while we are executing a trap handler.
363 */
364void
365dotrap()
366{
367	int i;
368	int savestatus;
369
370	for (;;) {
371		for (i = 1; i < NSIG; i++) {
372			if (gotsig[i]) {
373				gotsig[i] = 0;
374				if (trap[i]) {
375					/*
376					 * Ignore SIGCHLD to avoid infinite recursion
377					 * if the trap action does a fork.
378					 */
379					if (i == SIGCHLD)
380						ignore_sigchld++;
381					savestatus = exitstatus;
382					evalstring(trap[i]);
383					exitstatus = savestatus;
384					if (i == SIGCHLD)
385						ignore_sigchld--;
386				}
387				break;
388			}
389		}
390		if (i >= NSIG)
391			break;
392	}
393	pendingsigs = 0;
394}
395
396
397/*
398 * Controls whether the shell is interactive or not.
399 */
400void
401setinteractive(on)
402	int on;
403{
404	static int is_interactive = 0;
405
406	if (on == is_interactive)
407		return;
408	setsignal(SIGINT);
409	setsignal(SIGQUIT);
410	setsignal(SIGTERM);
411	is_interactive = on;
412}
413
414
415/*
416 * Called to exit the shell.
417 */
418void
419exitshell(status)
420	int status;
421{
422	struct jmploc loc1, loc2;
423	char *p;
424
425	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
426	if (setjmp(loc1.loc)) {
427		goto l1;
428	}
429	if (setjmp(loc2.loc)) {
430		goto l2;
431	}
432	handler = &loc1;
433	if ((p = trap[0]) != NULL && *p != '\0') {
434		trap[0] = NULL;
435		evalstring(p);
436	}
437l1:   handler = &loc2;			/* probably unnecessary */
438	flushall();
439#if JOBS
440	setjobctl(0);
441#endif
442l2:   _exit(status);
443}
444