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