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