1 /* trap.c -- Not the trap command, but useful functions for manipulating
2 those objects. The trap command is in builtins/trap.def. */
4 /* Copyright (C) 1987, 1991 Free Software Foundation, Inc.
6 This file is part of GNU Bash, the Bourne Again SHell.
8 Bash is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 1, or (at your option) any later
13 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with Bash; see the file COPYING. If not, write to the Free Software
20 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
24 #include "bashtypes.h"
27 #if defined (HAVE_STRING_H)
29 #else /* !HAVE_STRING_H */
31 #endif /* !HAVE_STRING_H */
36 /* Flags which describe the current handling state of a signal. */
37 #define SIG_INHERITED 0x0 /* Value inherited from parent. */
38 #define SIG_TRAPPED 0x1 /* Currently trapped. */
39 #define SIG_HARD_IGNORE 0x2 /* Signal was ignored on shell entry. */
40 #define SIG_SPECIAL 0x4 /* Treat this signal specially. */
41 #define SIG_NO_TRAP 0x8 /* Signal cannot be trapped. */
42 #define SIG_INPROGRESS 0x10 /* Signal handler currently executing. */
43 #define SIG_CHANGED 0x20 /* Trap value changed in trap handler. */
44 #define SIG_IGNORED 0x40 /* The signal is currently being ignored. */
46 /* An array of such flags, one for each signal, describing what the
47 shell will do with a signal. */
48 static int sigmodes[NSIG];
50 static void change_signal (), restore_signal ();
52 /* Variables used here but defined in other files. */
53 extern int interactive_shell, interactive;
54 extern int interrupt_immediately;
55 extern int last_command_exit_value;
57 /* The list of things to do originally, before we started trapping. */
58 SigHandler *original_signals[NSIG];
60 /* For each signal, a slot for a string, which is a command to be
61 executed when that signal is recieved. The slot can also contain
62 DEFAULT_SIG, which means do whatever you were going to do before
63 you were so rudely interrupted, or IGNORE_SIG, which says ignore
65 char *trap_list[NSIG];
67 /* A bitmap of signals received for which we have trap handlers. */
68 int pending_traps[NSIG];
70 /* A value which can never be the target of a trap handler. */
71 #define IMPOSSIBLE_TRAP_HANDLER (SigHandler *)initialize_traps
78 trap_list[0] = (char *)NULL;
79 sigmodes[0] = SIG_INHERITED; /* On EXIT trap handler. */
81 for (i = 1; i < NSIG; i++)
84 trap_list[i] = (char *)DEFAULT_SIG;
85 sigmodes[i] = SIG_INHERITED;
86 original_signals[i] = IMPOSSIBLE_TRAP_HANDLER;
89 /* Show which signals are treated specially by the shell. */
91 original_signals[SIGCHLD] = (SigHandler *) set_signal_handler (SIGCHLD, SIG_DFL);
92 set_signal_handler (SIGCHLD, original_signals[SIGCHLD]);
93 sigmodes[SIGCHLD] |= (SIG_SPECIAL | SIG_NO_TRAP);
96 original_signals[SIGINT] =
97 (SigHandler *) set_signal_handler (SIGINT, SIG_DFL);
98 set_signal_handler (SIGINT, original_signals[SIGINT]);
99 sigmodes[SIGINT] |= SIG_SPECIAL;
101 original_signals[SIGQUIT] =
102 (SigHandler *) set_signal_handler (SIGQUIT, SIG_DFL);
103 set_signal_handler (SIGQUIT, original_signals[SIGQUIT]);
104 sigmodes[SIGQUIT] |= SIG_SPECIAL;
108 original_signals[SIGTERM] = (SigHandler *)set_signal_handler (SIGTERM, SIG_DFL);
109 set_signal_handler (SIGTERM, original_signals[SIGTERM]);
110 sigmodes[SIGTERM] |= SIG_SPECIAL;
114 /* Return the print name of this signal. */
119 if (sig >= NSIG || sig < 0)
120 return ("bad signal number");
122 return (signal_names[sig]);
125 /* Turn a string into a signal number, or a number into
126 a signal number. If STRING is "2", "SIGINT", or "INT",
127 then (int)2 is returned. Return NO_SIG if STRING doesn't
128 contain a valid signal descriptor. */
130 decode_signal (string)
135 if (sscanf (string, "%d", &sig) == 1)
137 if (sig < NSIG && sig >= 0)
143 for (sig = 0; sig < NSIG; sig++)
144 if (STREQ (string, signal_names[sig]) ||
145 STREQ (string, &(signal_names[sig])[3]))
151 /* Non-zero when we catch a trapped signal. */
152 static int catch_flag = 0;
154 #if !defined (USG) && !defined (USGr4)
155 #define HAVE_BSD_SIGNALS
164 if (catch_flag == 0) /* simple optimization */
169 /* Preserve $? when running trap. */
170 old_exit_value = last_command_exit_value;
172 for (sig = 1; sig < NSIG; sig++)
174 /* XXX this could be made into a counter by using
175 while (pending_traps[sig]--) instead of the if statement. */
176 if (pending_traps[sig])
178 #if defined (_POSIX_VERSION)
184 sigaddset (&set, sig);
185 sigprocmask (SIG_BLOCK, &set, &oset);
187 # if defined (HAVE_BSD_SIGNALS)
188 int oldmask = sigblock (sigmask (sig));
190 #endif /* POSIX_VERSION */
194 run_interrupt_trap ();
198 parse_and_execute (savestring (trap_list[sig]), "trap", 0);
200 pending_traps[sig] = 0;
202 #if defined (_POSIX_VERSION)
203 sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
205 # if defined (HAVE_BSD_SIGNALS)
206 sigsetmask (oldmask);
208 #endif /* POSIX_VERSION */
212 last_command_exit_value = old_exit_value;
220 (trap_list[sig] == (char *)DEFAULT_SIG) ||
221 (trap_list[sig] == (char *)IGNORE_SIG))
222 programming_error ("trap_handler: Bad signal %d", sig);
225 #if defined (USG) && !defined (HAVE_BSD_SIGNALS) && !defined (_POSIX_VERSION)
226 set_signal_handler (sig, trap_handler);
227 #endif /* USG && !HAVE_BSD_SIGNALS && !_POSIX_VERSION */
230 pending_traps[sig]++;
232 if (interrupt_immediately)
233 run_pending_traps ();
235 #if !defined (VOID_SIGHANDLER)
237 #endif /* VOID_SIGHANDLER */
240 #if defined (JOB_CONTROL) && defined (SIGCHLD)
241 /* Make COMMAND_STRING be executed when SIGCHLD is caught. */
243 set_sigchld_trap (command_string)
244 char *command_string;
248 set_signal (SIGCHLD, command_string);
251 /* Make COMMAND_STRING be executed when SIGCHLD is caught iff the current
252 SIGCHLD trap handler is DEFAULT_SIG. */
254 maybe_set_sigchld_trap (command_string)
255 char *command_string;
259 if ((sigmodes[SIGCHLD] & SIG_TRAPPED) == 0)
260 set_signal (SIGCHLD, command_string);
262 #endif /* JOB_CONTROL && SIGCHLD */
265 set_sigint_trap (command)
270 set_signal (SIGINT, command);
273 /* Reset the SIGINT handler so that subshells that are doing `shellsy'
274 things, like waiting for command substitution or executing commands
275 in explicit subshells ( ( cmd ) ), can catch interrupts properly. */
277 set_sigint_handler ()
279 if (sigmodes[SIGINT] & SIG_HARD_IGNORE)
280 return ((SigHandler *)SIG_IGN);
282 else if (sigmodes[SIGINT] & SIG_IGNORED)
283 return ((SigHandler *)set_signal_handler (SIGINT, SIG_IGN));
285 else if (sigmodes[SIGINT] & SIG_TRAPPED)
286 return ((SigHandler *)set_signal_handler (SIGINT, trap_handler));
288 /* The signal is not trapped, so set the handler to the shell's special
289 interrupt handler. */
290 else if (interactive) /* XXX - was interactive_shell */
291 return (set_signal_handler (SIGINT, sigint_sighandler));
293 return (set_signal_handler (SIGINT, termination_unwind_protect));
296 /* Set SIG to call STRING as a command. */
298 set_signal (sig, string)
302 /* A signal ignored on entry to the shell cannot be trapped or reset, but
303 no error is reported when attempting to do so. -- Posix.2 */
304 if (sigmodes[sig] & SIG_HARD_IGNORE)
307 /* Make sure we have original_signals[sig] if the signal has not yet
309 if ((sigmodes[sig] & SIG_TRAPPED) == 0)
311 /* If we aren't sure of the original value, check it. */
312 if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER)
314 original_signals[sig] = (SigHandler *)set_signal_handler (sig, SIG_DFL);
315 set_signal_handler (sig, original_signals[sig]);
318 /* Signals ignored on entry to the shell cannot be trapped or reset. */
319 if (original_signals[sig] == SIG_IGN)
321 sigmodes[sig] |= SIG_HARD_IGNORE;
326 /* Only change the system signal handler if SIG_NO_TRAP is not set.
327 The trap command string is changed in either case. The shell signal
328 handlers for SIGINT and SIGCHLD run the user specified traps in an
329 environment in which it is safe to do so. */
330 if ((sigmodes[sig] & SIG_NO_TRAP) == 0)
332 set_signal_handler (sig, SIG_IGN);
333 change_signal (sig, savestring (string));
334 set_signal_handler (sig, trap_handler);
337 change_signal (sig, savestring (string));
341 free_trap_command (sig)
344 if ((sigmodes[sig] & SIG_TRAPPED) && trap_list[sig] &&
345 (trap_list[sig] != (char *)IGNORE_SIG) &&
346 (trap_list[sig] != (char *)DEFAULT_SIG) &&
347 (trap_list[sig] != (char *)IMPOSSIBLE_TRAP_HANDLER))
348 free (trap_list[sig]);
351 /* If SIG has a string assigned to it, get rid of it. Then give it
354 change_signal (sig, value)
358 free_trap_command (sig);
359 trap_list[sig] = value;
361 sigmodes[sig] |= SIG_TRAPPED;
362 if (value == (char *)IGNORE_SIG)
363 sigmodes[sig] |= SIG_IGNORED;
365 sigmodes[sig] &= ~SIG_IGNORED;
366 if (sigmodes[sig] & SIG_INPROGRESS)
367 sigmodes[sig] |= SIG_CHANGED;
370 #define GET_ORIGINAL_SIGNAL(sig) \
371 if (sig && sig < NSIG && original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) \
372 get_original_signal (sig)
375 get_original_signal (sig)
378 /* If we aren't sure the of the original value, then get it. */
379 if (original_signals[sig] == (SigHandler *)IMPOSSIBLE_TRAP_HANDLER)
381 original_signals[sig] =
382 (SigHandler *) set_signal_handler (sig, SIG_DFL);
383 set_signal_handler (sig, original_signals[sig]);
385 /* Signals ignored on entry to the shell cannot be trapped. */
386 if (original_signals[sig] == SIG_IGN)
387 sigmodes[sig] |= SIG_HARD_IGNORE;
391 /* Restore the default action for SIG; i.e., the action the shell
392 would have taken before you used the trap command. This is called
393 from trap_builtin (), which takes care to restore the handlers for
394 the signals the shell treats specially. */
396 restore_default_signal (sig)
401 free_trap_command (sig);
402 trap_list[sig] = (char *)NULL;
403 sigmodes[sig] &= ~SIG_TRAPPED;
407 GET_ORIGINAL_SIGNAL (sig);
409 /* A signal ignored on entry to the shell cannot be trapped or reset, but
410 no error is reported when attempting to do so. Thanks Posix.2. */
411 if (sigmodes[sig] & SIG_HARD_IGNORE)
414 /* If we aren't trapping this signal, don't bother doing anything else. */
415 if (!(sigmodes[sig] & SIG_TRAPPED))
418 /* Only change the signal handler for SIG if it allows it. */
419 if (!(sigmodes[sig] & SIG_NO_TRAP))
420 set_signal_handler (sig, original_signals[sig]);
422 /* Change the trap command in either case. */
423 change_signal (sig, (char *)DEFAULT_SIG);
425 /* Mark the signal as no longer trapped. */
426 sigmodes[sig] &= ~SIG_TRAPPED;
429 /* Make this signal be ignored. */
434 GET_ORIGINAL_SIGNAL (sig);
436 /* A signal ignored on entry to the shell cannot be trapped or reset.
437 No error is reported when the user attempts to do so.
438 Thanks to Posix.2. */
439 if (sigmodes[sig] & SIG_HARD_IGNORE)
442 /* If already trapped and ignored, no change necessary. */
443 if ((sigmodes[sig] & SIG_TRAPPED) && (trap_list[sig] == (char *)IGNORE_SIG))
446 /* Only change the signal handler for SIG if it allows it. */
447 if (!(sigmodes[sig] & SIG_NO_TRAP))
448 set_signal_handler (sig, SIG_IGN);
450 /* Change the trap command in either case. */
451 change_signal (sig, (char *)IGNORE_SIG);
454 /* Handle the calling of "trap 0". The only sticky situation is when
455 the command to be executed includes an "exit". This is why we have
456 to provide our own place for top_level to jump to. */
462 old_exit_value = last_command_exit_value;
464 /* Run the trap only if signal 0 is trapped and not ignored. */
465 if ((sigmodes[0] & SIG_TRAPPED) &&
466 (trap_list[0] != (char *)IGNORE_SIG) &&
467 (sigmodes[0] & SIG_INPROGRESS) == 0)
472 trap_command= savestring (trap_list[0]);
473 sigmodes[0] &= ~SIG_TRAPPED;
474 sigmodes[0] |= SIG_INPROGRESS;
476 code = setjmp (top_level);
479 parse_and_execute (trap_command, "trap", 0);
480 else if (code == EXITPROG)
481 return (last_command_exit_value);
483 return (old_exit_value);
486 return (old_exit_value);
489 /* Set the handler signal SIG to the original and free any trap
490 command associated with it. */
495 set_signal_handler (sig, original_signals[sig]);
496 change_signal (sig, (char *)DEFAULT_SIG);
497 sigmodes[sig] &= ~SIG_TRAPPED;
500 /* Free all the allocated strings in the list of traps and reset the trap
501 values to the default. */
507 for (i = 0; i < NSIG; i++)
509 free_trap_command (i);
510 trap_list[i] = (char *)DEFAULT_SIG;
511 sigmodes[i] &= ~SIG_TRAPPED;
515 /* Reset the handler for SIG to the original value. */
520 set_signal_handler (sig, original_signals[sig]);
523 /* Reset the handlers for all trapped signals to the values they had when
524 the shell was started. */
526 reset_signal_handlers ()
530 if (sigmodes[0] & SIG_TRAPPED)
532 free_trap_command (0);
533 trap_list[0] = (char *)NULL;
534 sigmodes[0] &= ~SIG_TRAPPED;
537 for (i = 1; i < NSIG; i++)
539 if (sigmodes[i] & SIG_SPECIAL)
541 else if (sigmodes[i] & SIG_TRAPPED)
543 if (trap_list[i] == (char *)IGNORE_SIG)
544 set_signal_handler (i, SIG_IGN);
551 /* Reset all trapped signals to their original values. Signals set to be
552 ignored with trap '' SIGNAL should be ignored, so we make sure that they
553 are. Called by child processes after they are forked. */
555 restore_original_signals ()
559 reset_terminating_signals (); /* in shell.c */
561 if (sigmodes[0] & SIG_TRAPPED)
563 free_trap_command (0);
564 trap_list[0] = (char *)NULL;
565 sigmodes[0] &= ~SIG_TRAPPED;
568 for (i = 1; i < NSIG; i++)
570 if (sigmodes[i] & SIG_SPECIAL)
572 else if (sigmodes[i] & SIG_TRAPPED)
574 if (trap_list[i] == (char *)IGNORE_SIG)
575 set_signal_handler (i, SIG_IGN);
582 /* Run a trap set on SIGINT. This is called from throw_to_top_level (), and
583 declared here to localize the trap functions. */
585 run_interrupt_trap ()
587 char *command, *saved_command;
590 /* Run the interrupt trap if SIGINT is trapped and not ignored, and if
591 we are not currently running in the interrupt trap handler. */
592 if ((sigmodes[SIGINT] & SIG_TRAPPED) &&
593 (trap_list[SIGINT] != (char *)IGNORE_SIG) &&
594 (trap_list[SIGINT] != (char *)IMPOSSIBLE_TRAP_HANDLER) &&
595 ((sigmodes[SIGINT] & SIG_INPROGRESS) == 0))
597 saved_command = trap_list[SIGINT];
598 sigmodes[SIGINT] |= SIG_INPROGRESS;
599 sigmodes[SIGINT] &= ~SIG_CHANGED;
601 command = savestring (saved_command);
603 old_exit_value = last_command_exit_value;
604 parse_and_execute (command, "interrupt trap", 0);
605 last_command_exit_value = old_exit_value;
607 sigmodes[SIGINT] &= ~SIG_INPROGRESS;
609 if (sigmodes[SIGINT] & SIG_CHANGED)
611 free (saved_command);
612 sigmodes[SIGINT] &= ~SIG_CHANGED;
617 /* If a trap handler exists for signal SIG, then call it; otherwise just
620 maybe_call_trap_handler (sig)
623 /* Call the trap handler for SIG if the signal is trapped and not ignored. */
624 if ((sigmodes[sig] & SIG_TRAPPED) &&
625 (trap_list[sig] != (char *)IGNORE_SIG))
630 run_interrupt_trap ();
646 signal_is_trapped (sig)
649 return (sigmodes[sig] & SIG_TRAPPED);
653 signal_is_special (sig)
656 return (sigmodes[sig] & SIG_SPECIAL);
660 signal_is_ignored (sig)
663 return (sigmodes[sig] & SIG_IGNORED);
667 set_signal_ignored (sig)
670 sigmodes[sig] |= SIG_HARD_IGNORE;
671 original_signals[sig] = SIG_IGN;