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