xref: /original-bsd/bin/sh/trap.c (revision 00a25f5a)
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.4 (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 /*
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 
171 	if (signo == SIGKILL)
172 		/* Pretend it worked */
173 		return 0;
174 
175 	t = &sigmode[signo - 1];
176 	if (*t == 0) {
177 		/*
178 		 * current setting unknown
179 		 */
180 		sigact = getsigaction(signo);
181 		if (sigact == SIG_IGN) {
182 			if (mflag && (signo == SIGTSTP ||
183 			     signo == SIGTTIN || signo == SIGTTOU)) {
184 				*t = S_IGN;	/* don't hard ignore these */
185 			} else
186 				*t = S_HARD_IGN;
187 		} else {
188 			*t = S_RESET;	/* force to be set */
189 		}
190 	}
191 	if (*t == S_HARD_IGN || *t == action)
192 		return 0;
193 	switch (action) {
194 		case S_DFL:	sigact = SIG_DFL;	break;
195 		case S_CATCH:  	sigact = onsig;		break;
196 		case S_IGN:	sigact = SIG_IGN;	break;
197 	}
198 	*t = action;
199 	return (long)signal(signo, sigact);
200 }
201 
202 /*
203  * Return the current setting for sig w/o changing it.
204  */
205 sig_t
206 getsigaction(signo)
207 	int signo;
208 {
209 	struct sigaction sa;
210 
211 	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
212 		error("Sigaction system call failed");
213 
214 	return (sig_t) sa.sa_handler;
215 }
216 
217 /*
218  * Ignore a signal.
219  */
220 
221 void
222 ignoresig(signo)
223 	int signo;
224 {
225 	if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
226 		signal(signo, SIG_IGN);
227 	}
228 	sigmode[signo - 1] = S_HARD_IGN;
229 }
230 
231 
232 #ifdef mkinit
233 INCLUDE <signal.h>
234 INCLUDE "trap.h"
235 
236 SHELLPROC {
237 	char *sm;
238 
239 	clear_traps();
240 	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
241 		if (*sm == S_IGN)
242 			*sm = S_HARD_IGN;
243 	}
244 }
245 #endif
246 
247 
248 
249 /*
250  * Signal handler.
251  */
252 
253 void
254 onsig(signo)
255 	int signo;
256 {
257 	signal(signo, onsig);
258 	if (signo == SIGINT && trap[SIGINT] == NULL) {
259 		onint();
260 		return;
261 	}
262 	gotsig[signo - 1] = 1;
263 	pendingsigs++;
264 }
265 
266 
267 
268 /*
269  * Called to execute a trap.  Perhaps we should avoid entering new trap
270  * handlers while we are executing a trap handler.
271  */
272 
273 void
274 dotrap() {
275 	int i;
276 	int savestatus;
277 
278 	for (;;) {
279 		for (i = 1 ; ; i++) {
280 			if (gotsig[i - 1])
281 				break;
282 			if (i >= NSIG)
283 				goto done;
284 		}
285 		gotsig[i - 1] = 0;
286 		savestatus=exitstatus;
287 		evalstring(trap[i]);
288 		exitstatus=savestatus;
289 	}
290 done:
291 	pendingsigs = 0;
292 }
293 
294 
295 
296 /*
297  * Controls whether the shell is interactive or not.
298  */
299 
300 
301 void
302 setinteractive(on)
303 	int on;
304 {
305 	static int is_interactive;
306 
307 	if (on == is_interactive)
308 		return;
309 	setsignal(SIGINT);
310 	setsignal(SIGQUIT);
311 	setsignal(SIGTERM);
312 	is_interactive = on;
313 }
314 
315 
316 
317 /*
318  * Called to exit the shell.
319  */
320 
321 void
322 exitshell(status)
323 	int status;
324 {
325 	struct jmploc loc1, loc2;
326 	char *p;
327 
328 	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
329 	if (setjmp(loc1.loc)) {
330 		goto l1;
331 	}
332 	if (setjmp(loc2.loc)) {
333 		goto l2;
334 	}
335 	handler = &loc1;
336 	if ((p = trap[0]) != NULL && *p != '\0') {
337 		trap[0] = NULL;
338 		evalstring(p);
339 	}
340 l1:   handler = &loc2;			/* probably unnecessary */
341 	flushall();
342 #if JOBS
343 	setjobctl(0);
344 #endif
345 l2:   _exit(status);
346 }
347