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