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