1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1997-2005
5  *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <signal.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <string.h>
39 
40 #include "shell.h"
41 #include "main.h"
42 #include "nodes.h"	/* for other headers */
43 #include "eval.h"
44 #include "jobs.h"
45 #include "show.h"
46 #include "options.h"
47 #include "syntax.h"
48 #include "output.h"
49 #include "memalloc.h"
50 #include "error.h"
51 #include "trap.h"
52 #include "mystring.h"
53 
54 /*
55  * Sigmode records the current value of the signal handlers for the various
56  * modes.  A value of zero means that the current handler is not known.
57  * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
58  */
59 
60 #define S_DFL 1			/* default signal handling (SIG_DFL) */
61 #define S_CATCH 2		/* signal is caught */
62 #define S_IGN 3			/* signal is ignored (SIG_IGN) */
63 #define S_HARD_IGN 4		/* signal is ignored permenantly */
64 #define S_RESET 5		/* temporary - to reset a hard ignored sig */
65 
66 
67 /* trap handler commands */
68 static char *trap[NSIG];
69 /* number of non-null traps */
70 int trapcnt;
71 /* current value of signal */
72 char sigmode[NSIG - 1];
73 /* indicates specified signal received */
74 static char gotsig[NSIG - 1];
75 /* last pending signal */
76 volatile sig_atomic_t pendingsigs;
77 /* received SIGCHLD */
78 int gotsigchld;
79 
80 extern char *signal_names[];
81 
82 static int decode_signum(const char *);
83 
84 #ifdef mkinit
85 INCLUDE "trap.h"
86 INIT {
87 	sigmode[SIGCHLD - 1] = S_DFL;
88 	setsignal(SIGCHLD);
89 }
90 #endif
91 
92 /*
93  * The trap builtin.
94  */
95 
96 int
trapcmd(int argc,char ** argv)97 trapcmd(int argc, char **argv)
98 {
99 	char *action;
100 	char **ap;
101 	int signo;
102 
103 	nextopt(nullstr);
104 	ap = argptr;
105 	if (!*ap) {
106 		for (signo = 0 ; signo < NSIG ; signo++) {
107 			if (trap[signo] != NULL) {
108 				out1fmt(
109 					"trap -- %s %s\n",
110 					single_quote(trap[signo]),
111 					signal_names[signo]
112 				);
113 			}
114 		}
115 		return 0;
116 	}
117 	if (!ap[1] || decode_signum(*ap) >= 0)
118 		action = NULL;
119 	else
120 		action = *ap++;
121 	while (*ap) {
122 		if ((signo = decode_signal(*ap, 0)) < 0) {
123 			outfmt(out2, "trap: %s: bad trap\n", *ap);
124 			return 1;
125 		}
126 		INTOFF;
127 		if (action) {
128 			if (action[0] == '-' && action[1] == '\0')
129 				action = NULL;
130 			else {
131 				if (*action)
132 					trapcnt++;
133 				action = savestr(action);
134 			}
135 		}
136 		if (trap[signo]) {
137 			if (*trap[signo])
138 				trapcnt--;
139 			ckfree(trap[signo]);
140 		}
141 		trap[signo] = action;
142 		if (signo != 0)
143 			setsignal(signo);
144 		INTON;
145 		ap++;
146 	}
147 	return 0;
148 }
149 
150 
151 
152 /*
153  * Clear traps on a fork.
154  */
155 
156 void
clear_traps(void)157 clear_traps(void)
158 {
159 	char **tp;
160 
161 	INTOFF;
162 	for (tp = trap ; tp < &trap[NSIG] ; tp++) {
163 		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
164 			ckfree(*tp);
165 			*tp = NULL;
166 			if (tp != &trap[0])
167 				setsignal(tp - trap);
168 		}
169 	}
170 	trapcnt = 0;
171 	INTON;
172 }
173 
174 
175 
176 /*
177  * Set the signal handler for the specified signal.  The routine figures
178  * out what it should be set to.
179  */
180 
181 void
setsignal(int signo)182 setsignal(int signo)
183 {
184 #ifdef __Fuchsia__
185 	return;
186 #endif
187 	int action;
188 	char *t, tsig;
189 	struct sigaction act;
190 
191 	if ((t = trap[signo]) == NULL)
192 		action = S_DFL;
193 	else if (*t != '\0')
194 		action = S_CATCH;
195 	else
196 		action = S_IGN;
197 	if (rootshell && action == S_DFL) {
198 		switch (signo) {
199 		case SIGINT:
200 			if (iflag || minusc || sflag == 0)
201 				action = S_CATCH;
202 			break;
203 		case SIGQUIT:
204 #ifdef DEBUG
205 			if (debug)
206 				break;
207 #endif
208 			/* FALLTHROUGH */
209 		case SIGTERM:
210 			if (iflag)
211 				action = S_IGN;
212 			break;
213 #if JOBS
214 		case SIGTSTP:
215 		case SIGTTOU:
216 			if (mflag)
217 				action = S_IGN;
218 			break;
219 #endif
220 		}
221 	}
222 
223 	if (signo == SIGCHLD)
224 		action = S_CATCH;
225 
226 	t = &sigmode[signo - 1];
227 	tsig = *t;
228 	if (tsig == 0) {
229 		/*
230 		 * current setting unknown
231 		 */
232 		if (sigaction(signo, 0, &act) == -1) {
233 			/*
234 			 * Pretend it worked; maybe we should give a warning
235 			 * here, but other shells don't. We don't alter
236 			 * sigmode, so that we retry every time.
237 			 */
238 			return;
239 		}
240 		if (act.sa_handler == SIG_IGN) {
241 			if (mflag && (signo == SIGTSTP ||
242 			     signo == SIGTTIN || signo == SIGTTOU)) {
243 				tsig = S_IGN;	/* don't hard ignore these */
244 			} else
245 				tsig = S_HARD_IGN;
246 		} else {
247 			tsig = S_RESET;	/* force to be set */
248 		}
249 	}
250 	if (tsig == S_HARD_IGN || tsig == action)
251 		return;
252 	switch (action) {
253 	case S_CATCH:
254 		act.sa_handler = onsig;
255 		break;
256 	case S_IGN:
257 		act.sa_handler = SIG_IGN;
258 		break;
259 	default:
260 		act.sa_handler = SIG_DFL;
261 	}
262 	*t = action;
263 	act.sa_flags = 0;
264 	sigfillset(&act.sa_mask);
265 	sigaction(signo, &act, 0);
266 }
267 
268 /*
269  * Ignore a signal.
270  */
271 
272 void
ignoresig(int signo)273 ignoresig(int signo)
274 {
275 	if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
276 		signal(signo, SIG_IGN);
277 	}
278 	sigmode[signo - 1] = S_HARD_IGN;
279 }
280 
281 
282 
283 /*
284  * Signal handler.
285  */
286 
287 void
onsig(int signo)288 onsig(int signo)
289 {
290 	if (signo == SIGCHLD) {
291 		gotsigchld = 1;
292 		if (!trap[SIGCHLD])
293 			return;
294 	}
295 
296 	gotsig[signo - 1] = 1;
297 	pendingsigs = signo;
298 
299 	if (signo == SIGINT && !trap[SIGINT]) {
300 		if (!suppressint)
301 			onint();
302 		intpending = 1;
303 	}
304 }
305 
306 
307 
308 /*
309  * Called to execute a trap.  Perhaps we should avoid entering new trap
310  * handlers while we are executing a trap handler.
311  */
312 
dotrap(void)313 void dotrap(void)
314 {
315 	char *p;
316 	char *q;
317 	int i;
318 	int status, last_status;
319 
320 	if (!pendingsigs)
321 		return;
322 
323 	status = savestatus;
324 	last_status = status;
325 	if (likely(status < 0)) {
326 		status = exitstatus;
327 		savestatus = status;
328 	}
329 	pendingsigs = 0;
330 	barrier();
331 
332 	for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
333 		if (!*q)
334 			continue;
335 
336 		if (evalskip) {
337 			pendingsigs = i + 1;
338 			break;
339 		}
340 
341 		*q = 0;
342 
343 		p = trap[i + 1];
344 		if (!p)
345 			continue;
346 		evalstring(p, 0);
347 		if (evalskip != SKIPFUNC)
348 			exitstatus = status;
349 	}
350 
351 	savestatus = last_status;
352 }
353 
354 
355 
356 /*
357  * Controls whether the shell is interactive or not.
358  */
359 
360 
361 void
setinteractive(int on)362 setinteractive(int on)
363 {
364 	static int is_interactive;
365 
366 	if (++on == is_interactive)
367 		return;
368 	is_interactive = on;
369 	setsignal(SIGINT);
370 	setsignal(SIGQUIT);
371 	setsignal(SIGTERM);
372 }
373 
374 
375 
376 /*
377  * Called to exit the shell.
378  */
379 
380 void
exitshell(void)381 exitshell(void)
382 {
383 	struct jmploc loc;
384 	char *p;
385 
386 	savestatus = exitstatus;
387 	TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
388 	if (setjmp(loc.loc))
389 		goto out;
390 	handler = &loc;
391 	if ((p = trap[0])) {
392 		trap[0] = NULL;
393 		evalskip = 0;
394 		evalstring(p, 0);
395 	}
396 out:
397 	/*
398 	 * Disable job control so that whoever had the foreground before we
399 	 * started can get it back.
400 	 */
401 	if (likely(!setjmp(loc.loc)))
402 		setjobctl(0);
403 	flushall();
404 	_exit(savestatus);
405 	/* NOTREACHED */
406 }
407 
decode_signum(const char * string)408 static int decode_signum(const char *string)
409 {
410 	int signo = -1;
411 
412 	if (is_number(string)) {
413 		signo = atoi(string);
414 		if (signo >= NSIG)
415 			signo = -1;
416 	}
417 
418 	return signo;
419 }
420 
decode_signal(const char * string,int minsig)421 int decode_signal(const char *string, int minsig)
422 {
423 	int signo;
424 
425 	signo = decode_signum(string);
426 	if (signo >= 0)
427 		return signo;
428 
429 	for (signo = minsig; signo < NSIG; signo++) {
430 		if (!strcasecmp(string, signal_names[signo])) {
431 			return signo;
432 		}
433 	}
434 
435 	return -1;
436 }
437