xref: /freebsd/bin/sh/trap.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
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.3 1995/05/30 00:07:23 rgrimes Exp $
37  */
38 
39 #ifndef lint
40 static char 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 
75 extern char nullstr[1];		/* null string */
76 
77 char *trap[NSIG+1];		/* trap handler commands */
78 MKINIT char sigmode[NSIG];	/* current value of signal */
79 char gotsig[NSIG];		/* indicates specified signal received */
80 int pendingsigs;			/* indicates some signal received */
81 
82 static int getsigaction __P((int, sig_t *));
83 
84 /*
85  * The trap builtin.
86  */
87 
88 int
89 trapcmd(argc, argv)
90 	int argc;
91 	char **argv;
92 {
93 	char *action;
94 	char **ap;
95 	int signo;
96 
97 	if (argc <= 1) {
98 		for (signo = 0 ; signo <= NSIG ; signo++) {
99 			if (trap[signo] != NULL)
100 				out1fmt("%d: %s\n", signo, trap[signo]);
101 		}
102 		return 0;
103 	}
104 	ap = argv + 1;
105 	if (is_number(*ap))
106 		action = NULL;
107 	else
108 		action = *ap++;
109 	while (*ap) {
110 		if ((signo = number(*ap)) < 0 || signo > NSIG)
111 			error("%s: bad trap", *ap);
112 		INTOFF;
113 		if (action)
114 			action = savestr(action);
115 		if (trap[signo])
116 			ckfree(trap[signo]);
117 		trap[signo] = action;
118 		if (signo != 0)
119 			setsignal(signo);
120 		INTON;
121 		ap++;
122 	}
123 	return 0;
124 }
125 
126 
127 
128 /*
129  * Clear traps on a fork.
130  */
131 
132 void
133 clear_traps() {
134 	char **tp;
135 
136 	for (tp = trap ; tp <= &trap[NSIG] ; tp++) {
137 		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
138 			INTOFF;
139 			ckfree(*tp);
140 			*tp = NULL;
141 			if (tp != &trap[0])
142 				setsignal(tp - trap);
143 			INTON;
144 		}
145 	}
146 }
147 
148 
149 
150 /*
151  * Set the signal handler for the specified signal.  The routine figures
152  * out what it should be set to.
153  */
154 
155 long
156 setsignal(signo)
157 	int signo;
158 {
159 	int action;
160 	sig_t sigact = SIG_DFL;
161 	char *t;
162 	extern void onsig();
163 
164 	if ((t = trap[signo]) == NULL)
165 		action = S_DFL;
166 	else if (*t != '\0')
167 		action = S_CATCH;
168 	else
169 		action = S_IGN;
170 	if (rootshell && action == S_DFL) {
171 		switch (signo) {
172 		case SIGINT:
173 			if (iflag)
174 				action = S_CATCH;
175 			break;
176 		case SIGQUIT:
177 #ifdef DEBUG
178 			{
179 			extern int debug;
180 
181 			if (debug)
182 				break;
183 			}
184 #endif
185 			/* FALLTHROUGH */
186 		case SIGTERM:
187 			if (iflag)
188 				action = S_IGN;
189 			break;
190 #if JOBS
191 		case SIGTSTP:
192 		case SIGTTOU:
193 			if (mflag)
194 				action = S_IGN;
195 			break;
196 #endif
197 		}
198 	}
199 
200 	t = &sigmode[signo - 1];
201 	if (*t == 0) {
202 		/*
203 		 * current setting unknown
204 		 */
205 		if (!getsigaction(signo, &sigact)) {
206 			/*
207 			 * Pretend it worked; maybe we should give a warning
208 			 * here, but other shells don't. We don't alter
209 			 * sigmode, so that we retry every time.
210 			 */
211 			return 0;
212 		}
213 		if (sigact == SIG_IGN) {
214 			if (mflag && (signo == SIGTSTP ||
215 			     signo == SIGTTIN || signo == SIGTTOU)) {
216 				*t = S_IGN;	/* don't hard ignore these */
217 			} else
218 				*t = S_HARD_IGN;
219 		} else {
220 			*t = S_RESET;	/* force to be set */
221 		}
222 	}
223 	if (*t == S_HARD_IGN || *t == action)
224 		return 0;
225 	switch (action) {
226 		case S_DFL:	sigact = SIG_DFL;	break;
227 		case S_CATCH:  	sigact = onsig;		break;
228 		case S_IGN:	sigact = SIG_IGN;	break;
229 	}
230 	*t = action;
231 	return (long)signal(signo, sigact);
232 }
233 
234 /*
235  * Return the current setting for sig w/o changing it.
236  */
237 static int
238 getsigaction(signo, sigact)
239 	int signo;
240 	sig_t *sigact;
241 {
242 	struct sigaction sa;
243 
244 	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
245 		return 0;
246 	*sigact = (sig_t) sa.sa_handler;
247 	return 1;
248 }
249 
250 /*
251  * Ignore a signal.
252  */
253 
254 void
255 ignoresig(signo)
256 	int signo;
257 {
258 	if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
259 		signal(signo, SIG_IGN);
260 	}
261 	sigmode[signo - 1] = S_HARD_IGN;
262 }
263 
264 
265 #ifdef mkinit
266 INCLUDE <signal.h>
267 INCLUDE "trap.h"
268 
269 SHELLPROC {
270 	char *sm;
271 
272 	clear_traps();
273 	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
274 		if (*sm == S_IGN)
275 			*sm = S_HARD_IGN;
276 	}
277 }
278 #endif
279 
280 
281 
282 /*
283  * Signal handler.
284  */
285 
286 void
287 onsig(signo)
288 	int signo;
289 {
290 	signal(signo, onsig);
291 	if (signo == SIGINT && trap[SIGINT] == NULL) {
292 		onint();
293 		return;
294 	}
295 	gotsig[signo - 1] = 1;
296 	pendingsigs++;
297 }
298 
299 
300 
301 /*
302  * Called to execute a trap.  Perhaps we should avoid entering new trap
303  * handlers while we are executing a trap handler.
304  */
305 
306 void
307 dotrap() {
308 	int i;
309 	int savestatus;
310 
311 	for (;;) {
312 		for (i = 1 ; ; i++) {
313 			if (gotsig[i - 1])
314 				break;
315 			if (i >= NSIG)
316 				goto done;
317 		}
318 		gotsig[i - 1] = 0;
319 		savestatus=exitstatus;
320 		evalstring(trap[i]);
321 		exitstatus=savestatus;
322 	}
323 done:
324 	pendingsigs = 0;
325 }
326 
327 
328 
329 /*
330  * Controls whether the shell is interactive or not.
331  */
332 
333 
334 void
335 setinteractive(on)
336 	int on;
337 {
338 	static int is_interactive;
339 
340 	if (on == is_interactive)
341 		return;
342 	setsignal(SIGINT);
343 	setsignal(SIGQUIT);
344 	setsignal(SIGTERM);
345 	is_interactive = on;
346 }
347 
348 
349 
350 /*
351  * Called to exit the shell.
352  */
353 
354 void
355 exitshell(status)
356 	int status;
357 {
358 	struct jmploc loc1, loc2;
359 	char *p;
360 
361 	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
362 	if (setjmp(loc1.loc)) {
363 		goto l1;
364 	}
365 	if (setjmp(loc2.loc)) {
366 		goto l2;
367 	}
368 	handler = &loc1;
369 	if ((p = trap[0]) != NULL && *p != '\0') {
370 		trap[0] = NULL;
371 		evalstring(p);
372 	}
373 l1:   handler = &loc2;			/* probably unnecessary */
374 	flushall();
375 #if JOBS
376 	setjobctl(0);
377 #endif
378 l2:   _exit(status);
379 }
380