trap.c revision 38950
1120442Ssam/*-
2120442Ssam * Copyright (c) 1991, 1993
360317Sdarrenr *	The Regents of the University of California.  All rights reserved.
460317Sdarrenr *
560317Sdarrenr * This code is derived from software contributed to Berkeley by
660317Sdarrenr * Kenneth Almquist.
760317Sdarrenr *
860317Sdarrenr * Redistribution and use in source and binary forms, with or without
960317Sdarrenr * modification, are permitted provided that the following conditions
1060317Sdarrenr * are met:
1160317Sdarrenr * 1. Redistributions of source code must retain the above copyright
1260317Sdarrenr *    notice, this list of conditions and the following disclaimer.
1360317Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright
1460317Sdarrenr *    notice, this list of conditions and the following disclaimer in the
1560317Sdarrenr *    documentation and/or other materials provided with the distribution.
1660317Sdarrenr * 3. All advertising materials mentioning features or use of this software
1760317Sdarrenr *    must display the following acknowledgement:
1860317Sdarrenr *	This product includes software developed by the University of
1960317Sdarrenr *	California, Berkeley and its contributors.
2060317Sdarrenr * 4. Neither the name of the University nor the names of its contributors
2160317Sdarrenr *    may be used to endorse or promote products derived from this software
2260317Sdarrenr *    without specific prior written permission.
2360317Sdarrenr *
2460317Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2560317Sdarrenr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2660317Sdarrenr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2760317Sdarrenr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2860317Sdarrenr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2960342Sdarrenr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30130582Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31254772Sandre * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3260317Sdarrenr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3360317Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3460317Sdarrenr * SUCH DAMAGE.
3560317Sdarrenr */
36120442Ssam
37120442Ssam#ifndef lint
38120442Ssam#if 0
3960317Sdarrenrstatic char sccsid[] = "@(#)trap.c	8.5 (Berkeley) 6/5/95";
40120442Ssam#endif
41241888Smelifarostatic const char rcsid[] =
42241888Smelifaro	"$Id: trap.c,v 1.14 1998/08/25 09:33:34 cracauer Exp $";
43241888Smelifaro#endif /* not lint */
44241888Smelifaro
45241888Smelifaro#include <signal.h>
4660317Sdarrenr#include <unistd.h>
4760317Sdarrenr#include <stdlib.h>
4884306Sru
4984306Sru#include "shell.h"
5084306Sru#include "main.h"
5184306Sru#include "nodes.h"	/* for other headers */
52254775Sandre#include "eval.h"
53254775Sandre#include "jobs.h"
5488509Sdavidc#include "show.h"
55120442Ssam#include "options.h"
5688509Sdavidc#include "syntax.h"
57120442Ssam#include "output.h"
58130582Sru#include "memalloc.h"
59120442Ssam#include "error.h"
60120442Ssam#include "trap.h"
61254775Sandre#include "mystring.h"
62120442Ssam
63254775Sandre
64120442Ssam/*
65135920Smlaier * Sigmode records the current value of the signal handlers for the various
66241888Smelifaro * modes.  A value of zero means that the current handler is not known.
67241888Smelifaro * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
68241888Smelifaro */
69241888Smelifaro
70241888Smelifaro#define S_DFL 1			/* default signal handling (SIG_DFL) */
71241888Smelifaro#define S_CATCH 2		/* signal is caught */
72241888Smelifaro#define S_IGN 3			/* signal is ignored (SIG_IGN) */
73241888Smelifaro#define S_HARD_IGN 4		/* signal is ignored permenantly */
74254825Sjoel#define S_RESET 5		/* temporary - to reset a hard ignored sig */
7560317Sdarrenr
7660317Sdarrenr
7760317SdarrenrMKINIT char sigmode[NSIG];	/* current value of signal */
78120442Ssamint pendingsigs;		/* indicates some signal received */
79120442Ssamint in_dotrap;			/* do we execute in a trap handler? */
80120442Ssamstatic char *volatile trap[NSIG];	/* trap handler commands */
81120442Ssamstatic volatile sig_atomic_t gotsig[NSIG];
82120442Ssam				/* indicates specified signal received */
83120442Ssamstatic int ignore_sigchld;	/* Used while handling SIGCHLD traps. */
84120442Ssam
85130582Srustatic int getsigaction __P((int, sig_t *));
86130582Sru
87130582Sru
88130582Sru/*
89130582Sru * Map a string to a signal number.
90130582Sru */
91120442Ssamstatic int
92120442Ssamsigstring_to_signum(sig)
93120442Ssam	char *sig;
94120442Ssam{
95120442Ssam
96120442Ssam	if (is_number(sig)) {
97120442Ssam		int signo;
98120442Ssam
99241888Smelifaro		signo = atoi(sig);
100241888Smelifaro		return ((signo >= 0 && signo < NSIG) ? signo : (-1));
101241888Smelifaro	} else if (strcasecmp(sig, "exit") == 0) {
102241888Smelifaro		return (0);
103241888Smelifaro	} else {
104241888Smelifaro		int n;
105241888Smelifaro
106241888Smelifaro		if (strncasecmp(sig, "sig", 3) == 0)
107241888Smelifaro			sig += 3;
108241888Smelifaro		for (n = 1; n < NSIG; n++)
109120442Ssam			if (strcasecmp(sys_signame[n], sig) == 0)
110120442Ssam				return (n);
111120442Ssam	}
112120442Ssam	return (-1);
113120442Ssam}
114120442Ssam
11560317Sdarrenr
11689207Sru/*
11760317Sdarrenr * Print a list of valid signal names.
118120442Ssam */
119120442Ssamstatic void
120120442Ssamprintsignals()
121120442Ssam{
122120442Ssam	int n;
123120442Ssam
124120442Ssam	for (n = 1; n < NSIG; n++) {
12560317Sdarrenr		out1fmt("%s", sys_signame[n]);
126120442Ssam		if (n == (NSIG / 2) || n == (NSIG - 1))
127120442Ssam			out1str("\n");
128120442Ssam		else
129120442Ssam			out1c(' ');
130130582Sru	}
131130582Sru}
132130582Sru
133130582Sru
134130582Sru/*
135130582Sru * The trap builtin.
136130582Sru */
137130582Sruint
138130582Srutrapcmd(argc, argv)
139130582Sru	int argc;
140130582Sru	char **argv;
141130582Sru{
142120442Ssam	char *action;
143120442Ssam	int signo;
144120442Ssam
145241888Smelifaro	if (argc <= 1) {
146241888Smelifaro		for (signo = 0 ; signo < NSIG ; signo++) {
147241888Smelifaro			if (trap[signo] != NULL)
148241888Smelifaro				out1fmt("trap -- '%s' %s\n", trap[signo],
149241888Smelifaro					(signo) ? sys_signame[signo] : "exit");
150241888Smelifaro		}
151241888Smelifaro		return 0;
152241888Smelifaro	}
153241888Smelifaro	action = NULL;
154241888Smelifaro	if (*++argv && strcmp(*argv, "--") == 0)
155241888Smelifaro		argv++;
156241888Smelifaro	if (*argv && sigstring_to_signum(*argv) == -1) {
157241888Smelifaro		if ((*argv)[0] != '-') {
158241888Smelifaro			action = *argv;
159241888Smelifaro			argv++;
160241888Smelifaro		} else if ((*argv)[1] == '\0') {
161241888Smelifaro			argv++;
162241888Smelifaro		} else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
163241888Smelifaro			printsignals();
164241888Smelifaro			return 0;
165241888Smelifaro		} else {
166241888Smelifaro			error("bad option %s", *argv);
167241888Smelifaro		}
168241888Smelifaro	}
169241888Smelifaro	while (*argv) {
170240561Smelifaro		if ((signo = sigstring_to_signum(*argv)) == -1)
171240561Smelifaro			error("bad signal %s", *argv);
172240561Smelifaro		INTOFF;
173240561Smelifaro		if (action)
174240561Smelifaro			action = savestr(action);
175241245Sglebius		if (trap[signo])
176240561Smelifaro			ckfree(trap[signo]);
177241245Sglebius		trap[signo] = action;
178240561Smelifaro		if (signo != 0)
179240573Sjoel			setsignal(signo);
180240561Smelifaro		INTON;
18160317Sdarrenr		argv++;
18289207Sru	}
183130582Sru	return 0;
184130582Sru}
185130582Sru
186130582Sru
187130582Sru/*
188130582Sru * Clear traps on a fork.
18960317Sdarrenr */
19060317Sdarrenrvoid
191130582Sruclear_traps()
192130582Sru{
193130582Sru	char *volatile *tp;
194130582Sru
195120442Ssam	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
196120442Ssam		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
197120442Ssam			INTOFF;
198130582Sru			ckfree(*tp);
199120442Ssam			*tp = NULL;
200130582Sru			if (tp != &trap[0])
201120442Ssam				setsignal(tp - trap);
202140561Sru			INTON;
203140561Sru		}
204242989Spluknet	}
205242989Spluknet}
20660317Sdarrenr
20760317Sdarrenr
20860317Sdarrenr/*
20960317Sdarrenr * Set the signal handler for the specified signal.  The routine figures
21060317Sdarrenr * out what it should be set to.
21160317Sdarrenr */
21260317Sdarrenrvoid
21360317Sdarrenrsetsignal(signo)
214130582Sru	int signo;
21560317Sdarrenr{
21660317Sdarrenr	int action;
21760317Sdarrenr	sig_t sig, sigact = SIG_DFL;
21860317Sdarrenr	char *t;
21960317Sdarrenr
22060317Sdarrenr	if ((t = trap[signo]) == NULL)
22189207Sru		action = S_DFL;
222120442Ssam	else if (*t != '\0')
223120442Ssam		action = S_CATCH;
22460317Sdarrenr	else
22560317Sdarrenr		action = S_IGN;
22660317Sdarrenr	if (action == S_DFL) {
22760317Sdarrenr		switch (signo) {
22860317Sdarrenr		case SIGINT:
22960317Sdarrenr			action = S_CATCH;
23060342Sdarrenr			break;
23189207Sru		case SIGQUIT:
232120442Ssam#ifdef DEBUG
233120442Ssam			{
234120442Ssam			extern int debug;
235120442Ssam
236120442Ssam			if (debug)
237120442Ssam				break;
238120442Ssam			}
239126468Ssimon#endif
240126468Ssimon			action = S_CATCH;
241241888Smelifaro			break;
242241888Smelifaro		case SIGTERM:
243241888Smelifaro			if (rootshell && iflag)
24460317Sdarrenr				action = S_IGN;
245254825Sjoel			break;
246130582Sru#if JOBS
247130582Sru		case SIGTSTP:
248120442Ssam		case SIGTTOU:
249136258Smlaier			if (rootshell && mflag)
250120442Ssam				action = S_IGN;
251136258Smlaier			break;
252136258Smlaier#endif
253140140Sru		}
254136258Smlaier	}
255
256	t = &sigmode[signo];
257	if (*t == 0) {
258		/*
259		 * current setting unknown
260		 */
261		if (!getsigaction(signo, &sigact)) {
262			/*
263			 * Pretend it worked; maybe we should give a warning
264			 * here, but other shells don't. We don't alter
265			 * sigmode, so that we retry every time.
266			 */
267			return;
268		}
269		if (sigact == SIG_IGN) {
270			if (mflag && (signo == SIGTSTP ||
271			     signo == SIGTTIN || signo == SIGTTOU)) {
272				*t = S_IGN;	/* don't hard ignore these */
273			} else
274				*t = S_HARD_IGN;
275		} else {
276			*t = S_RESET;	/* force to be set */
277		}
278	}
279	if (*t == S_HARD_IGN || *t == action)
280		return;
281	switch (action) {
282		case S_DFL:	sigact = SIG_DFL;	break;
283		case S_CATCH:  	sigact = onsig;		break;
284		case S_IGN:	sigact = SIG_IGN;	break;
285	}
286	*t = action;
287	sig = signal(signo, sigact);
288#ifdef BSD
289	if (sig != SIG_ERR && action == S_CATCH)
290		siginterrupt(signo, 1);
291#endif
292}
293
294
295/*
296 * Return the current setting for sig w/o changing it.
297 */
298static int
299getsigaction(signo, sigact)
300	int signo;
301	sig_t *sigact;
302{
303	struct sigaction sa;
304
305	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
306		return 0;
307	*sigact = (sig_t) sa.sa_handler;
308	return 1;
309}
310
311
312/*
313 * Ignore a signal.
314 */
315void
316ignoresig(signo)
317	int signo;
318{
319
320	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
321		signal(signo, SIG_IGN);
322	}
323	sigmode[signo] = S_HARD_IGN;
324}
325
326
327#ifdef mkinit
328INCLUDE <signal.h>
329INCLUDE "trap.h"
330
331SHELLPROC {
332	char *sm;
333
334	clear_traps();
335	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
336		if (*sm == S_IGN)
337			*sm = S_HARD_IGN;
338	}
339}
340#endif
341
342
343/*
344 * Signal handler.
345 */
346void
347onsig(signo)
348	int signo;
349{
350	int i;
351
352#ifndef BSD
353	signal(signo, onsig);
354#endif
355	if (signo == SIGINT && trap[SIGINT] == NULL) {
356		onint();
357		return;
358	}
359
360	if (signo != SIGCHLD || !ignore_sigchld)
361		gotsig[signo] = 1;
362	pendingsigs++;
363
364	/* If we are currently in a wait builtin, prepare to break it */
365	if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0)
366		breakwaitcmd = 1;
367	/*
368	 * If a trap is set, we need to make sure it is executed even
369	 * when a childs blocks all signals.
370	 */
371	for (i = 0; i < NSIG; i++) {
372		if (signo == i && trap[i] != NULL &&
373		    ! (trap[i][0] == ':' && trap[i][1] == '\0')) {
374			breakwaitcmd = 1;
375			break;
376		}
377	}
378}
379
380
381/*
382 * Called to execute a trap.  Perhaps we should avoid entering new trap
383 * handlers while we are executing a trap handler.
384 */
385void
386dotrap()
387{
388	int i;
389	int savestatus;
390
391	in_dotrap++;
392	for (;;) {
393		for (i = 1; i < NSIG; i++) {
394			if (gotsig[i]) {
395				gotsig[i] = 0;
396				if (trap[i]) {
397					/*
398					 * Ignore SIGCHLD to avoid infinite recursion
399					 * if the trap action does a fork.
400					 */
401					if (i == SIGCHLD)
402						ignore_sigchld++;
403					savestatus = exitstatus;
404					evalstring(trap[i]);
405					exitstatus = savestatus;
406					if (i == SIGCHLD)
407						ignore_sigchld--;
408				}
409				break;
410			}
411		}
412		if (i >= NSIG)
413			break;
414	}
415	in_dotrap--;
416	pendingsigs = 0;
417}
418
419
420/*
421 * Controls whether the shell is interactive or not.
422 */
423void
424setinteractive(on)
425	int on;
426{
427	static int is_interactive = -1;
428
429	if (on == is_interactive)
430		return;
431	setsignal(SIGINT);
432	setsignal(SIGQUIT);
433	setsignal(SIGTERM);
434	is_interactive = on;
435}
436
437
438/*
439 * Called to exit the shell.
440 */
441void
442exitshell(status)
443	int status;
444{
445	struct jmploc loc1, loc2;
446	char *p;
447
448	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
449	if (setjmp(loc1.loc)) {
450		goto l1;
451	}
452	if (setjmp(loc2.loc)) {
453		goto l2;
454	}
455	handler = &loc1;
456	if ((p = trap[0]) != NULL && *p != '\0') {
457		trap[0] = NULL;
458		evalstring(p);
459	}
460l1:   handler = &loc2;			/* probably unnecessary */
461	flushall();
462#if JOBS
463	setjobctl(0);
464#endif
465l2:   _exit(status);
466}
467