trap.c revision 20902
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.5 1996/12/14 06:19:32 steve 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 */
208long
209setsignal(signo)
210	int signo;
211{
212	int action;
213	sig_t 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 0;
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 0;
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	return (long)signal(signo, sigact);
284}
285
286
287/*
288 * Return the current setting for sig w/o changing it.
289 */
290static int
291getsigaction(signo, sigact)
292	int signo;
293	sig_t *sigact;
294{
295	struct sigaction sa;
296
297	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
298		return 0;
299	*sigact = (sig_t) sa.sa_handler;
300	return 1;
301}
302
303
304/*
305 * Ignore a signal.
306 */
307void
308ignoresig(signo)
309	int signo;
310{
311
312	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
313		signal(signo, SIG_IGN);
314	}
315	sigmode[signo] = S_HARD_IGN;
316}
317
318
319#ifdef mkinit
320INCLUDE <signal.h>
321INCLUDE "trap.h"
322
323SHELLPROC {
324	char *sm;
325
326	clear_traps();
327	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
328		if (*sm == S_IGN)
329			*sm = S_HARD_IGN;
330	}
331}
332#endif
333
334
335/*
336 * Signal handler.
337 */
338void
339onsig(signo)
340	int signo;
341{
342
343	signal(signo, onsig);
344	if (signo == SIGINT && trap[SIGINT] == NULL) {
345		onint();
346		return;
347	}
348	if (signo != SIGCHLD || !ignore_sigchld)
349		gotsig[signo] = 1;
350	pendingsigs++;
351}
352
353
354/*
355 * Called to execute a trap.  Perhaps we should avoid entering new trap
356 * handlers while we are executing a trap handler.
357 */
358void
359dotrap()
360{
361	int i;
362	int savestatus;
363
364	for (;;) {
365		for (i = 1; i < NSIG; i++) {
366			if (gotsig[i]) {
367				gotsig[i] = 0;
368				if (trap[i]) {
369					/*
370					 * Ignore SIGCHLD to avoid infinite recursion
371					 * if the trap action does a fork.
372					 */
373					if (i == SIGCHLD)
374						ignore_sigchld++;
375					savestatus = exitstatus;
376					evalstring(trap[i]);
377					exitstatus = savestatus;
378					if (i == SIGCHLD)
379						ignore_sigchld--;
380				}
381				break;
382			}
383		}
384		if (i >= NSIG)
385			break;
386	}
387	pendingsigs = 0;
388}
389
390
391/*
392 * Controls whether the shell is interactive or not.
393 */
394void
395setinteractive(on)
396	int on;
397{
398	static int is_interactive = 0;
399
400	if (on == is_interactive)
401		return;
402	setsignal(SIGINT);
403	setsignal(SIGQUIT);
404	setsignal(SIGTERM);
405	is_interactive = on;
406}
407
408
409/*
410 * Called to exit the shell.
411 */
412void
413exitshell(status)
414	int status;
415{
416	struct jmploc loc1, loc2;
417	char *p;
418
419	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
420	if (setjmp(loc1.loc)) {
421		goto l1;
422	}
423	if (setjmp(loc2.loc)) {
424		goto l2;
425	}
426	handler = &loc1;
427	if ((p = trap[0]) != NULL && *p != '\0') {
428		trap[0] = NULL;
429		evalstring(p);
430	}
431l1:   handler = &loc2;			/* probably unnecessary */
432	flushall();
433#if JOBS
434	setjobctl(0);
435#endif
436l2:   _exit(status);
437}
438