xref: /netbsd-src/bin/sh/trap.c (revision ae9172d6cd9432a6a1a56760d86b32c57a66c39c)
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 /*static char sccsid[] = "from: @(#)trap.c	8.1 (Berkeley) 5/31/93";*/
39 static char *rcsid = "$Id: trap.c,v 1.10 1994/12/05 19:07:57 cgd Exp $";
40 #endif /* not lint */
41 
42 #include "shell.h"
43 #include "main.h"
44 #include "nodes.h"	/* for other headers */
45 #include "eval.h"
46 #include "jobs.h"
47 #include "options.h"
48 #include "syntax.h"
49 #include "output.h"
50 #include "memalloc.h"
51 #include "error.h"
52 #include "trap.h"
53 #include "mystring.h"
54 #include <signal.h>
55 #include <unistd.h>
56 
57 
58 /*
59  * Sigmode records the current value of the signal handlers for the various
60  * modes.  A value of zero means that the current handler is not known.
61  * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
62  */
63 
64 #define S_DFL 1			/* default signal handling (SIG_DFL) */
65 #define S_CATCH 2		/* signal is caught */
66 #define S_IGN 3			/* signal is ignored (SIG_IGN) */
67 #define S_HARD_IGN 4		/* signal is ignored permenantly */
68 #define S_RESET 5		/* temporary - to reset a hard ignored sig */
69 
70 
71 extern char nullstr[1];		/* null string */
72 
73 char *trap[NSIG+1];		/* trap handler commands */
74 MKINIT char sigmode[NSIG];	/* current value of signal */
75 char gotsig[NSIG];		/* indicates specified signal received */
76 int pendingsigs;			/* indicates some signal received */
77 
78 /*
79  * The trap builtin.
80  */
81 
82 int
83 trapcmd(argc, argv)
84 	int argc;
85 	char **argv;
86 {
87 	char *action;
88 	char **ap;
89 	int signo;
90 
91 	if (argc <= 1) {
92 		for (signo = 0 ; signo <= NSIG ; signo++) {
93 			if (trap[signo] != NULL)
94 				out1fmt("%d: %s\n", signo, trap[signo]);
95 		}
96 		return 0;
97 	}
98 	ap = argv + 1;
99 	if (is_number(*ap))
100 		action = NULL;
101 	else
102 		action = *ap++;
103 	while (*ap) {
104 		if ((signo = number(*ap)) < 0 || signo > NSIG)
105 			error("%s: bad trap", *ap);
106 		INTOFF;
107 		if (action)
108 			action = savestr(action);
109 		if (trap[signo])
110 			ckfree(trap[signo]);
111 		trap[signo] = action;
112 		if (signo != 0)
113 			setsignal(signo);
114 		INTON;
115 		ap++;
116 	}
117 	return 0;
118 }
119 
120 
121 
122 /*
123  * Clear traps on a fork.
124  */
125 
126 void
127 clear_traps() {
128 	char **tp;
129 
130 	for (tp = trap ; tp <= &trap[NSIG] ; tp++) {
131 		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
132 			INTOFF;
133 			ckfree(*tp);
134 			*tp = NULL;
135 			if (tp != &trap[0])
136 				setsignal(tp - trap);
137 			INTON;
138 		}
139 	}
140 }
141 
142 
143 
144 /*
145  * Set the signal handler for the specified signal.  The routine figures
146  * out what it should be set to.
147  */
148 
149 int
150 setsignal(signo)
151 	int signo;
152 {
153 	int action;
154 	sig_t sigact;
155 	char *t;
156 	extern void onsig();
157 	extern sig_t getsigaction();
158 
159 	if ((t = trap[signo]) == NULL)
160 		action = S_DFL;
161 	else if (*t != '\0')
162 		action = S_CATCH;
163 	else
164 		action = S_IGN;
165 	if (rootshell && action == S_DFL) {
166 		switch (signo) {
167 		case SIGINT:
168 			if (iflag)
169 				action = S_CATCH;
170 			break;
171 		case SIGQUIT:
172 #ifdef DEBUG
173 			{
174 			extern int debug;
175 
176 			if (debug)
177 				break;
178 			}
179 #endif
180 			/* FALLTHROUGH */
181 		case SIGTERM:
182 			if (iflag)
183 				action = S_IGN;
184 			break;
185 #if JOBS
186 		case SIGTSTP:
187 		case SIGTTOU:
188 			if (mflag)
189 				action = S_IGN;
190 			break;
191 #endif
192 		}
193 	}
194 	t = &sigmode[signo - 1];
195 	if (*t == 0) {
196 		/*
197 		 * current setting unknown
198 		 */
199 		sigact = getsigaction(signo);
200 		if (sigact == SIG_IGN) {
201 			if (mflag && (signo == SIGTSTP ||
202 			     signo == SIGTTIN || signo == SIGTTOU)) {
203 				*t = S_IGN;	/* don't hard ignore these */
204 			} else
205 				*t = S_HARD_IGN;
206 		} else {
207 			*t = S_RESET;	/* force to be set */
208 		}
209 	}
210 	if (*t == S_HARD_IGN || *t == action)
211 		return 0;
212 	switch (action) {
213 		case S_DFL:	sigact = SIG_DFL;	break;
214 		case S_CATCH:  	sigact = onsig;		break;
215 		case S_IGN:	sigact = SIG_IGN;	break;
216 	}
217 	*t = action;
218 	return (int)signal(signo, sigact);
219 }
220 
221 /*
222  * Return the current setting for sig w/o changing it.
223  */
224 sig_t
225 getsigaction(signo)
226 	int signo;
227 {
228 	struct sigaction sa;
229 
230 	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
231 		error("Sigaction system call failed");
232 
233 	return sa.sa_handler;
234 }
235 
236 /*
237  * Ignore a signal.
238  */
239 
240 void
241 ignoresig(signo)
242 	int signo;
243 {
244 	if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
245 		signal(signo, SIG_IGN);
246 	}
247 	sigmode[signo - 1] = S_HARD_IGN;
248 }
249 
250 
251 #ifdef mkinit
252 INCLUDE <signal.h>
253 INCLUDE "trap.h"
254 
255 SHELLPROC {
256 	char *sm;
257 
258 	clear_traps();
259 	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
260 		if (*sm == S_IGN)
261 			*sm = S_HARD_IGN;
262 	}
263 }
264 #endif
265 
266 
267 
268 /*
269  * Signal handler.
270  */
271 
272 void
273 onsig(signo)
274 	int signo;
275 {
276 	signal(signo, onsig);
277 	if (signo == SIGINT && trap[SIGINT] == NULL) {
278 		onint();
279 		return;
280 	}
281 	gotsig[signo - 1] = 1;
282 	pendingsigs++;
283 }
284 
285 
286 
287 /*
288  * Called to execute a trap.  Perhaps we should avoid entering new trap
289  * handlers while we are executing a trap handler.
290  */
291 
292 void
293 dotrap() {
294 	int i;
295 	int savestatus;
296 
297 	for (;;) {
298 		for (i = 1 ; ; i++) {
299 			if (gotsig[i - 1])
300 				break;
301 			if (i >= NSIG)
302 				goto done;
303 		}
304 		gotsig[i - 1] = 0;
305 		savestatus=exitstatus;
306 		evalstring(trap[i]);
307 		exitstatus=savestatus;
308 	}
309 done:
310 	pendingsigs = 0;
311 }
312 
313 
314 
315 /*
316  * Controls whether the shell is interactive or not.
317  */
318 
319 
320 void
321 setinteractive(on)
322 	int on;
323 {
324 	static int is_interactive;
325 
326 	if (on == is_interactive)
327 		return;
328 	setsignal(SIGINT);
329 	setsignal(SIGQUIT);
330 	setsignal(SIGTERM);
331 	is_interactive = on;
332 }
333 
334 
335 
336 /*
337  * Called to exit the shell.
338  */
339 
340 void
341 exitshell(status)
342 	int status;
343 {
344 	struct jmploc loc1, loc2;
345 	char *p;
346 
347 	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
348 	if (setjmp(loc1.loc)) {
349 		goto l1;
350 	}
351 	if (setjmp(loc2.loc)) {
352 		goto l2;
353 	}
354 	handler = &loc1;
355 	if ((p = trap[0]) != NULL && *p != '\0') {
356 		trap[0] = NULL;
357 		evalstring(p);
358 	}
359 l1:   handler = &loc2;			/* probably unnecessary */
360 	flushall();
361 #if JOBS
362 	setjobctl(0);
363 #endif
364 l2:   _exit(status);
365 }
366