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