diff options
author | Chet Ramey <chet@caleb.ins.cwru.edu> | 2013-03-04 08:11:02 -0500 |
---|---|---|
committer | Chet Ramey <chet@caleb.ins.cwru.edu> | 2013-03-04 08:11:02 -0500 |
commit | 83509ab7ce6a469f1a9397fae60409aee5c11574 (patch) | |
tree | 9085413d20db28f5d2b1bba73fa13aa7548499d5 | |
parent | 73a146bec7f75da9f78f6d54329c980b75a2318d (diff) | |
download | bash-83509ab7ce6a469f1a9397fae60409aee5c11574.tar.gz |
commit bash-20130222 snapshot
-rw-r--r-- | CHANGES-4.3 | 25 | ||||
-rw-r--r-- | CWRU/CWRU.chlog | 37 | ||||
-rw-r--r-- | CWRU/CWRU.chlog~ | 46 | ||||
-rw-r--r-- | NEWS-4.3 | 6 | ||||
-rw-r--r-- | bashline.c | 4 | ||||
-rw-r--r-- | braces.c | 13 | ||||
-rw-r--r-- | eval.c~ | 292 | ||||
-rw-r--r-- | jobs.c | 10 | ||||
-rw-r--r-- | lib/readline/isearch.c | 1 | ||||
-rw-r--r-- | lib/readline/readline.c | 3 | ||||
-rw-r--r-- | lib/readline/search.c | 1 | ||||
-rw-r--r-- | nojobs.c | 9 | ||||
-rw-r--r-- | pcomplete.c | 2 | ||||
-rw-r--r-- | quit.h | 2 | ||||
-rw-r--r-- | quit.h~ | 2 | ||||
-rw-r--r-- | redir.c | 10 | ||||
-rw-r--r-- | sig.c | 18 | ||||
-rw-r--r-- | sig.c~ | 722 | ||||
-rw-r--r-- | sig.h | 7 | ||||
-rw-r--r-- | sig.h~ | 139 | ||||
-rw-r--r-- | unwind_prot.c | 2 | ||||
-rw-r--r-- | unwind_prot.c~ | 357 |
22 files changed, 1685 insertions, 23 deletions
diff --git a/CHANGES-4.3 b/CHANGES-4.3 index 33f721be..3ef84605 100644 --- a/CHANGES-4.3 +++ b/CHANGES-4.3 @@ -315,6 +315,22 @@ vvvv. Worked around a kernel problem that caused SIGCHLD to interrupt open(2) wwww. Fixed a problem that resulted in inconsistent expansion of $* and ${a[*]}. +xxxx. Fixed a problem that caused `read -t' to crash when interrupted by + SIGINT. + +yyyy. Fixed a problem that caused pattern removal to fail randomly because the + pattern matcher read beyond the end of a string. + +zzzz. Fixed a bug that caused core dumps when shell functions tried to create + local shadow copies of special variables like GROUPS. + +aaaaa. Fixed a bug that caused SIGTERM to be occasionally lost by children of + interactive shells when it arrived before the child process reset the + handler from SIG_DFL. + +bbbbb. Fixed a bug that caused redirections like <&n- to leave file descriptor + n closed if executed with a builtin command. + 2. Changes to Readline a. Fixed a bug that did not allow the `dd', `cc', or `yy' vi editing mode @@ -372,6 +388,9 @@ q. Fixed a bug that caused binding a macro to a multi-character key sequence r. Fixed several redisplay errors with multibyte characters and prompts containing invisible characters when using horizontal scrolling. +s. Fixed a bug that caused redisplay errors when trying to overwrite + existing characters using multibyte characters. + 3. New Features in Bash a. The `helptopic' completion action now maps to all the help topics, not just @@ -472,6 +491,12 @@ dd. The `printf' %(...)T format specifier now uses the current time if no ee. There is a new variable, BASH_COMPAT, that controls the current shell compatibility level. +ff. The `popd' builtin now treats additional arguments as errors. + +gg. The brace expansion code now treats a failed sequence expansion as a + simple string and will continue to expand brace terms in the remainder + of the word. + 4. New Features in Readline a. Readline is now more responsive to SIGHUP and other fatal signals when diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 47a0914c..67d406f4 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -4528,7 +4528,7 @@ trap.c lib/glob/xmbsrtowcs.c - xdupmbstowcs2: fixed but where end of string was not handled correctly, causing loop to go past end of string in a bunch of cases. - Fixes bug reported by + Fixes bug reported by "Dashing" <dashing@hushmail.com> 2/13 @@ -4615,3 +4615,38 @@ lib/readline/display.c - use new variable `bytes_to_insert' instead of overloading temp in some code blocks (nls - nfd, bytes that comprise the characters different in the new line from the old) + + 2/18 + ---- +redir.c + - do_redirection_internal: add undoable redirection for the implicit + close performed by the <&n- and >&n- redirections. Fixes bug + reported by Stephane Chazelas <stephane.chazelas@gmail.com> + + 2/19 + ---- +sig.c + - termsig_handler: an interactive shell killed by SIGHUP and keeping + command history will try to save the shell history before exiting. + This is an attempt to preserve the save-history-when-the-terminal- + window-is-closed behavior + + 2/21 + ---- +braces.c + - brace_expand: if a sequence expansion fails (e.g. because the + integers overflow), treat that expansion as a simple string, including + the braces, and try to process any remainder of the string. The + remainder may include brace expansions. Derived from SuSE bug + 804551 example (https://bugzilla.novell.com/show_bug.cgi?id=804551) + + 2/23 + ---- +{quit,sig}.h,sig.c + - sigterm_received declaration now in sig.h; type is sig_atomic_t + - sigwinch_received type now sig_atomic_t + - sig.h includes bashtypes.h and <signal.h> if SIG_DFL not defined + (same logic as trap.h) to pick up sig_atomic_t + +unwind_prot.c + - include sig.h before quit.h (reverse order) diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index ad8ff0d1..53f67c34 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -4528,7 +4528,7 @@ trap.c lib/glob/xmbsrtowcs.c - xdupmbstowcs2: fixed but where end of string was not handled correctly, causing loop to go past end of string in a bunch of cases. - Fixes bug reported by + Fixes bug reported by "Dashing" <dashing@hushmail.com> 2/13 @@ -4603,3 +4603,47 @@ execute_cmd.c lib/readline/display.c - open_some_spaces: new function, subset of insert_some_chars that just opens up a specified number of spaces to be overwritten + - insert_some_spaces: now just calls to open_some_spaces followed by + _rl_output_some_chars + - update_line: use col_temp instead of recalculating it using + _rl_col_width in the case where we use more columns with fewer bytes + - update_line: use open_some_spaces and then output the right number + of chars instead of trying to print new characters then overwrite + existing characters in two separate calls. This includes removing + some dodgy code and making things simpler. Fix from Egmont + Koblinger <egmont@gmail.com> + - use new variable `bytes_to_insert' instead of overloading temp in + some code blocks (nls - nfd, bytes that comprise the characters + different in the new line from the old) + + 2/18 + ---- +redir.c + - do_redirection_internal: add undoable redirection for the implicit + close performed by the <&n- and >&n- redirections. Fixes bug + reported by Stephane Chazelas <stephane.chazelas@gmail.com> + + 2/19 + ---- +sig.c + - termsig_handler: an interactive shell killed by SIGHUP and keeping + command history will try to save the shell history before exiting. + This is an attempt to preserve the save-history-when-the-terminal- + window-is-closed behavior + + 2/21 + ---- +braces.c + - brace_expand: if a sequence expansion fails (e.g. because the + integers overflow), treat that expansion as a simple string, including + the braces, and try to process any remainder of the string. The + remainder may include brace expansions. Derived from SuSE bug + 804551 example (https://bugzilla.novell.com/show_bug.cgi?id=804551) + + 2/23 + ---- +{quit,sig}.h,sig.c + - sigterm_received declaration now in sig.h; type is sig_atomic_t + - sigwinch_received type now sig_atomic_t + - sig.h includes bashtypes.h and <signal.h> if SIG_DFL not defined + (same logic as trap.h) to pick up sig_atomic_t @@ -102,6 +102,12 @@ dd. The `printf' %(...)T format specifier now uses the current time if no ee. There is a new variable, BASH_COMPAT, that controls the current shell compatibility level. +ff. The `popd' builtin now treats additional arguments as errors. + +gg. The brace expansion code now treats a failed sequence expansion as a + simple string and will continue to expand brace terms in the remainder + of the word. + 2. New Features in Readline a. Readline is now more responsive to SIGHUP and other fatal signals when @@ -4140,10 +4140,6 @@ bash_dequote_text (text) static int bash_event_hook () { -#if defined (DEBUG) -itrace("bash_event_hook"); -#endif - /* If we're going to longjmp to top_level, make sure we clean up readline */ if (interrupt_state && signal_is_trapped (SIGINT) == 0) rl_cleanup_after_signal (); @@ -228,6 +228,19 @@ brace_expand (text) tack = expand_seqterm (amble, alen); if (tack) goto add_tack; + else if (text[i + 1]) + { + /* If the sequence expansion fails (e.g., because the integers + overflow), but there is more in the string, try and process + the rest of the string, which may contain additional brace + expansions. Treat the unexpanded sequence term as a simple + string (including the braces). */ + tack = strvec_create (2); + tack[0] = savestring (text+start-1); + tack[0][i-start+2] = '\0'; + tack[1] = (char *)0; + goto add_tack; + } else { free (amble); diff --git a/eval.c~ b/eval.c~ new file mode 100644 index 00000000..2f96a068 --- /dev/null +++ b/eval.c~ @@ -0,0 +1,292 @@ +/* eval.c -- reading and evaluating commands. */ + +/* Copyright (C) 1996-2011 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include "bashansi.h" +#include <stdio.h> + +#include "bashintl.h" + + +#include "shell.h" +#include "flags.h" +#include "trap.h" + +#include "builtins/common.h" + +#include "input.h" +#include "execute_cmd.h" + +#if defined (HISTORY) +# include "bashhist.h" +#endif + +extern int EOF_reached; +extern int indirection_level; +extern int posixly_correct; +extern int subshell_environment, running_under_emacs; +extern int last_command_exit_value, stdin_redir; +extern int need_here_doc; +extern int current_command_number, current_command_line_count, line_number; +extern int expand_aliases; + +#if defined (HAVE_POSIX_SIGNALS) +extern sigset_t top_level_mask; +#endif + +static void send_pwd_to_eterm __P((void)); +static sighandler alrm_catcher __P((int)); + +/* Read and execute commands until EOF is reached. This assumes that + the input source has already been initialized. */ +int +reader_loop () +{ + int our_indirection_level; + COMMAND * volatile current_command; + + USE_VAR(current_command); + + current_command = (COMMAND *)NULL; + + our_indirection_level = ++indirection_level; + + while (EOF_Reached == 0) + { + int code; + + code = setjmp_nosigs (top_level); + +#if defined (PROCESS_SUBSTITUTION) + unlink_fifo_list (); +#endif /* PROCESS_SUBSTITUTION */ + + /* XXX - why do we set this every time through the loop? */ + if (interactive_shell && signal_is_ignored (SIGINT) == 0) + set_signal_handler (SIGINT, sigint_sighandler); + + if (code != NOT_JUMPED) + { + indirection_level = our_indirection_level; + + switch (code) + { + /* Some kind of throw to top_level has occured. */ + case FORCE_EOF: + case ERREXIT: + case EXITPROG: + current_command = (COMMAND *)NULL; + if (exit_immediately_on_error) + variable_context = 0; /* not in a function */ + EOF_Reached = EOF; + goto exec_done; + + case DISCARD: + /* Make sure the exit status is reset to a non-zero value, but + leave existing non-zero values (e.g., > 128 on signal) + alone. */ + if (last_command_exit_value == 0) + last_command_exit_value = EXECUTION_FAILURE; + if (subshell_environment) + { + current_command = (COMMAND *)NULL; + EOF_Reached = EOF; + goto exec_done; + } + /* Obstack free command elements, etc. */ + if (current_command) + { + dispose_command (current_command); + current_command = (COMMAND *)NULL; + } +#if defined (HAVE_POSIX_SIGNALS) + sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL); +#endif + break; + + default: + command_error ("reader_loop", CMDERR_BADJUMP, code, 0); + } + } + + executing = 0; + if (temporary_env) + dispose_used_env_vars (); + +#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA) + /* Attempt to reclaim memory allocated with alloca (). */ + (void) alloca (0); +#endif + + if (read_command () == 0) + { + if (interactive_shell == 0 && read_but_dont_execute) + { + last_command_exit_value = EXECUTION_SUCCESS; + dispose_command (global_command); + global_command = (COMMAND *)NULL; + } + else if (current_command = global_command) + { + global_command = (COMMAND *)NULL; + current_command_number++; + + executing = 1; + stdin_redir = 0; + execute_command (current_command); + + exec_done: + QUIT; + + if (current_command) + { + dispose_command (current_command); + current_command = (COMMAND *)NULL; + } + } + } + else + { + /* Parse error, maybe discard rest of stream if not interactive. */ + if (interactive == 0) + EOF_Reached = EOF; + } + if (just_one_command) + EOF_Reached = EOF; + } + indirection_level--; + return (last_command_exit_value); +} + +static sighandler +alrm_catcher(i) + int i; +{ + printf (_("\007timed out waiting for input: auto-logout\n")); + fflush (stdout); + bash_logout (); /* run ~/.bash_logout if this is a login shell */ + jump_to_top_level (EXITPROG); + SIGRETURN (0); +} + +/* Send an escape sequence to emacs term mode to tell it the + current working directory. */ +static void +send_pwd_to_eterm () +{ + char *pwd, *f; + + f = 0; + pwd = get_string_value ("PWD"); + if (pwd == 0) + f = pwd = get_working_directory ("eterm"); + fprintf (stderr, "\032/%s\n", pwd); + free (f); +} + +/* Call the YACC-generated parser and return the status of the parse. + Input is read from the current input stream (bash_input). yyparse + leaves the parsed command in the global variable GLOBAL_COMMAND. + This is where PROMPT_COMMAND is executed. */ +int +parse_command () +{ + int r; + char *command_to_execute; + + need_here_doc = 0; + run_pending_traps (); + + /* Allow the execution of a random command just before the printing + of each primary prompt. If the shell variable PROMPT_COMMAND + is set then the value of it is the command to execute. */ + if (interactive && bash_input.type != st_string) + { + command_to_execute = get_string_value ("PROMPT_COMMAND"); + if (command_to_execute) + execute_variable_command (command_to_execute, "PROMPT_COMMAND"); + + if (running_under_emacs == 2) + send_pwd_to_eterm (); /* Yuck */ + } + + current_command_line_count = 0; + r = yyparse (); + + if (need_here_doc) + gather_here_documents (); + + return (r); +} + +/* Read and parse a command, returning the status of the parse. The command + is left in the globval variable GLOBAL_COMMAND for use by reader_loop. + This is where the shell timeout code is executed. */ +int +read_command () +{ + SHELL_VAR *tmout_var; + int tmout_len, result; + SigHandler *old_alrm; + + set_current_prompt_level (1); + global_command = (COMMAND *)NULL; + + /* Only do timeouts if interactive. */ + tmout_var = (SHELL_VAR *)NULL; + tmout_len = 0; + old_alrm = (SigHandler *)NULL; + + if (interactive) + { + tmout_var = find_variable ("TMOUT"); + + if (tmout_var && var_isset (tmout_var)) + { + tmout_len = atoi (value_cell (tmout_var)); + if (tmout_len > 0) + { + old_alrm = set_signal_handler (SIGALRM, alrm_catcher); + alarm (tmout_len); + } + } + } + + QUIT; + + current_command_line_count = 0; + result = parse_command (); + + if (interactive && tmout_var && (tmout_len > 0)) + { + alarm(0); + set_signal_handler (SIGALRM, old_alrm); + } + + return (result); +} @@ -1717,6 +1717,8 @@ make_child (command, async_p) sigset_t set, oset; pid_t pid; + /* XXX - block SIGTERM here and unblock in child after fork resets the + set of pending signals? */ sigemptyset (&set); sigaddset (&set, SIGCHLD); sigaddset (&set, SIGINT); @@ -1747,11 +1749,16 @@ make_child (command, async_p) waitchld (-1, 0); sys_error ("fork: retry"); + RESET_SIGTERM; + if (sleep (forksleep) != 0) break; forksleep <<= 1; } + if (pid != 0) + RESET_SIGTERM; + if (pid < 0) { sys_error ("fork"); @@ -1767,9 +1774,6 @@ make_child (command, async_p) throw_to_top_level (); /* Reset signals, etc. */ } - if (pid != 0) - RESET_SIGTERM; - if (pid == 0) { /* In the child. Give this child the right process group, set the diff --git a/lib/readline/isearch.c b/lib/readline/isearch.c index fcc01d9b..dfd8ff8c 100644 --- a/lib/readline/isearch.c +++ b/lib/readline/isearch.c @@ -318,6 +318,7 @@ _rl_search_getchar (cxt) c = cxt->lastc = _rl_read_mbstring (cxt->lastc, cxt->mb, MB_LEN_MAX); #endif + RL_CHECK_SIGNALS (); return c; } diff --git a/lib/readline/readline.c b/lib/readline/readline.c index 1946ff14..947dfdbe 100644 --- a/lib/readline/readline.c +++ b/lib/readline/readline.c @@ -559,7 +559,8 @@ readline_internal_charloop () /* look at input.c:rl_getc() for the circumstances under which this will be returned; punt immediately on read error without converting it to - a newline. */ + a newline; assume that rl_read_key has already called the signal + handler. */ if (c == READERR) { #if defined (READLINE_CALLBACKS) diff --git a/lib/readline/search.c b/lib/readline/search.c index 45307465..aff6654e 100644 --- a/lib/readline/search.c +++ b/lib/readline/search.c @@ -468,6 +468,7 @@ rl_history_search_internal (count, dir) copy into the line buffer. */ while (count) { + RL_CHECK_SIGNALS (); ret = noninc_search_from_pos (history_search_string, rl_history_search_pos + dir, dir); if (ret == -1) break; @@ -501,11 +501,17 @@ make_child (command, async_p) sync_buffered_stream (default_buffered_input); #endif /* BUFFERED_INPUT */ + /* XXX - block SIGTERM here and unblock in child after fork resets the + set of pending signals? */ + RESET_SIGTERM; + /* Create the child, handle severe errors. Retry on EAGAIN. */ forksleep = 1; while ((pid = fork ()) < 0 && errno == EAGAIN && forksleep < FORKSLEEP_MAX) { sys_error ("fork: retry"); + RESET_SIGTERM; + #if defined (HAVE_WAITPID) /* Posix systems with a non-blocking waitpid () system call available get another chance after zombies are reaped. */ @@ -519,6 +525,9 @@ make_child (command, async_p) forksleep <<= 1; } + if (pid != 0) + RESET_SIGTERM; + if (pid < 0) { sys_error ("fork"); diff --git a/pcomplete.c b/pcomplete.c index 38faab1a..6b4e0332 100644 --- a/pcomplete.c +++ b/pcomplete.c @@ -729,7 +729,7 @@ pcomp_filename_completion_function (text, state) { FREE (dfn); /* remove backslashes quoting special characters in filenames. */ - /* There are roughtly three paths we can follow to get here: + /* There are roughly three paths we can follow to get here: 1. complete -f 2. compgen -f "$word" from a completion function 3. compgen -f "$word" from the command line @@ -25,8 +25,6 @@ extern volatile int interrupt_state; extern volatile int terminating_signal; -extern sig_atomic_t sigterm_received; - /* Macro to call a great deal. SIGINT just sets the interrupt_state variable. When it is safe, put QUIT in the code, and the "interrupt" will take place. The same scheme is used for terminating signals (e.g., SIGHUP) @@ -1,6 +1,6 @@ /* quit.h -- How to handle SIGINT gracefully. */ -/* Copyright (C) 1993-2012 Free Software Foundation, Inc. +/* Copyright (C) 1993-2013 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -1044,6 +1044,16 @@ do_redirection_internal (redirect, flags) r = add_undo_close_redirect (redirector); REDIRECTION_ERROR (r, errno, -1); } + if ((flags & RX_UNDOABLE) && (ri == r_move_input || ri == r_move_output)) + { + /* r_move_input and r_move_output add an additional close() + that needs to be undone */ + if (fcntl (redirector, F_GETFD, 0) != -1) + { + r = add_undo_redirect (redir_fd, r_close_this, -1); + REDIRECTION_ERROR (r, errno, -1); + } + } #if defined (BUFFERED_INPUT) check_bash_input (redirector); #endif @@ -71,13 +71,14 @@ extern void initialize_siglist (); volatile int interrupt_state = 0; /* Non-zero after SIGWINCH */ -volatile int sigwinch_received = 0; +sig_atomic_t sigwinch_received = 0; + +/* Non-zero after SIGTERM */ +sig_atomic_t sigterm_received = 0; /* Set to the value of any terminating signal received. */ volatile int terminating_signal = 0; -sig_atomic_t sigterm_received = 0; - /* The environment at the top-level R-E loop. We use this in the case of error return. */ procenv_t top_level; @@ -420,7 +421,8 @@ throw_to_top_level () #endif /* JOB_CONTROL */ #if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS) - /* This should not be necessary on systems using sigsetjmp/siglongjmp. */ + /* This needs to stay because jobs.c:make_child() uses it without resetting + the signal mask. */ sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL); #endif @@ -553,12 +555,14 @@ termsig_handler (sig) if (sig == SIGINT && signal_is_trapped (SIGINT)) run_interrupt_trap (); -#if 0 #if defined (HISTORY) - if (interactive_shell && (sig != SIGABRT && sig != SIGINT && sig != SIGHUP && sig != SIGTERM)) + /* If we don't do something like this, the history will not be saved when + an interactive shell is running in a terminal window that gets closed + with the `close' button. We can't test for RL_STATE_READCMD because + readline no longer handles SIGTERM synchronously. */ + if (interactive_shell && interactive && sig == SIGHUP && remember_on_history) maybe_save_shell_history (); #endif /* HISTORY */ -#endif #if defined (JOB_CONTROL) if (sig == SIGHUP && (interactive || (subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB)))) @@ -0,0 +1,722 @@ +/* sig.c - interface for shell signal handlers and signal initialization. */ + +/* Copyright (C) 1994-2013 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include <sys/types.h> +# endif +# include <unistd.h> +#endif + +#include <stdio.h> +#include <signal.h> + +#include "bashintl.h" + +#include "shell.h" +#if defined (JOB_CONTROL) +#include "jobs.h" +#endif /* JOB_CONTROL */ +#include "siglist.h" +#include "sig.h" +#include "trap.h" + +#include "builtins/common.h" + +#if defined (READLINE) +# include "bashline.h" +# include <readline/readline.h> +#endif + +#if defined (HISTORY) +# include "bashhist.h" +#endif + +extern int last_command_exit_value; +extern int last_command_exit_signal; +extern int return_catch_flag; +extern int loop_level, continuing, breaking, funcnest; +extern int executing_list; +extern int comsub_ignore_return; +extern int parse_and_execute_level, shell_initialized; +#if defined (HISTORY) +extern int history_lines_this_session; +#endif +extern int no_line_editing; + +extern void initialize_siglist (); + +/* Non-zero after SIGINT. */ +volatile int interrupt_state = 0; + +/* Non-zero after SIGWINCH */ +volatile int sigwinch_received = 0; + +/* Set to the value of any terminating signal received. */ +volatile int terminating_signal = 0; + +sig_atomic_t sigterm_received = 0; + +/* The environment at the top-level R-E loop. We use this in + the case of error return. */ +procenv_t top_level; + +#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS) +/* The signal masks that this shell runs with. */ +sigset_t top_level_mask; +#endif /* JOB_CONTROL */ + +/* When non-zero, we throw_to_top_level (). */ +int interrupt_immediately = 0; + +/* When non-zero, we call the terminating signal handler immediately. */ +int terminate_immediately = 0; + +#if defined (SIGWINCH) +static SigHandler *old_winch = (SigHandler *)SIG_DFL; +#endif + +static void initialize_shell_signals __P((void)); + +void +initialize_signals (reinit) + int reinit; +{ + initialize_shell_signals (); + initialize_job_signals (); +#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL) + if (reinit == 0) + initialize_siglist (); +#endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */ +} + +/* A structure describing a signal that terminates the shell if not + caught. The orig_handler member is present so children can reset + these signals back to their original handlers. */ +struct termsig { + int signum; + SigHandler *orig_handler; + int orig_flags; +}; + +#define NULL_HANDLER (SigHandler *)SIG_DFL + +/* The list of signals that would terminate the shell if not caught. + We catch them, but just so that we can write the history file, + and so forth. */ +static struct termsig terminating_signals[] = { +#ifdef SIGHUP +{ SIGHUP, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGINT +{ SIGINT, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGILL +{ SIGILL, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGTRAP +{ SIGTRAP, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGIOT +{ SIGIOT, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGDANGER +{ SIGDANGER, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGEMT +{ SIGEMT, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGFPE +{ SIGFPE, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGBUS +{ SIGBUS, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGSEGV +{ SIGSEGV, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGSYS +{ SIGSYS, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGPIPE +{ SIGPIPE, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGALRM +{ SIGALRM, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGTERM +{ SIGTERM, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGXCPU +{ SIGXCPU, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGXFSZ +{ SIGXFSZ, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGVTALRM +{ SIGVTALRM, NULL_HANDLER, 0 }, +#endif + +#if 0 +#ifdef SIGPROF +{ SIGPROF, NULL_HANDLER, 0 }, +#endif +#endif + +#ifdef SIGLOST +{ SIGLOST, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGUSR1 +{ SIGUSR1, NULL_HANDLER, 0 }, +#endif + +#ifdef SIGUSR2 +{ SIGUSR2, NULL_HANDLER, 0 }, +#endif +}; + +#define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig)) + +#define XSIG(x) (terminating_signals[x].signum) +#define XHANDLER(x) (terminating_signals[x].orig_handler) +#define XSAFLAGS(x) (terminating_signals[x].orig_flags) + +static int termsigs_initialized = 0; + +/* Initialize signals that will terminate the shell to do some + unwind protection. For non-interactive shells, we only call + this when a trap is defined for EXIT (0) or when trap is run + to display signal dispositions. */ +void +initialize_terminating_signals () +{ + register int i; +#if defined (HAVE_POSIX_SIGNALS) + struct sigaction act, oact; +#endif + + if (termsigs_initialized) + return; + + /* The following code is to avoid an expensive call to + set_signal_handler () for each terminating_signals. Fortunately, + this is possible in Posix. Unfortunately, we have to call signal () + on non-Posix systems for each signal in terminating_signals. */ +#if defined (HAVE_POSIX_SIGNALS) + act.sa_handler = termsig_sighandler; + act.sa_flags = 0; + sigemptyset (&act.sa_mask); + sigemptyset (&oact.sa_mask); + for (i = 0; i < TERMSIGS_LENGTH; i++) + sigaddset (&act.sa_mask, XSIG (i)); + for (i = 0; i < TERMSIGS_LENGTH; i++) + { + /* If we've already trapped it, don't do anything. */ + if (signal_is_trapped (XSIG (i))) + continue; + + sigaction (XSIG (i), &act, &oact); + XHANDLER(i) = oact.sa_handler; + XSAFLAGS(i) = oact.sa_flags; + /* Don't do anything with signals that are ignored at shell entry + if the shell is not interactive. */ + /* XXX - should we do this for interactive shells, too? */ + if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN) + { + sigaction (XSIG (i), &oact, &act); + set_signal_ignored (XSIG (i)); + } +#if defined (SIGPROF) && !defined (_MINIX) + if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN) + sigaction (XSIG (i), &oact, (struct sigaction *)NULL); +#endif /* SIGPROF && !_MINIX */ + } + +#else /* !HAVE_POSIX_SIGNALS */ + + for (i = 0; i < TERMSIGS_LENGTH; i++) + { + /* If we've already trapped it, don't do anything. */ + if (signal_is_trapped (XSIG (i))) + continue; + + XHANDLER(i) = signal (XSIG (i), termsig_sighandler); + XSAFLAGS(i) = 0; + /* Don't do anything with signals that are ignored at shell entry + if the shell is not interactive. */ + /* XXX - should we do this for interactive shells, too? */ + if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN) + { + signal (XSIG (i), SIG_IGN); + set_signal_ignored (XSIG (i)); + } +#ifdef SIGPROF + if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN) + signal (XSIG (i), XHANDLER (i)); +#endif + } + +#endif /* !HAVE_POSIX_SIGNALS */ + + termsigs_initialized = 1; +} + +static void +initialize_shell_signals () +{ + if (interactive) + initialize_terminating_signals (); + +#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS) + /* All shells use the signal mask they inherit, and pass it along + to child processes. Children will never block SIGCHLD, though. */ + sigemptyset (&top_level_mask); + sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask); +# if defined (SIGCHLD) + sigdelset (&top_level_mask, SIGCHLD); +# endif +#endif /* JOB_CONTROL || HAVE_POSIX_SIGNALS */ + + /* And, some signals that are specifically ignored by the shell. */ + set_signal_handler (SIGQUIT, SIG_IGN); + + if (interactive) + { + set_signal_handler (SIGINT, sigint_sighandler); + get_original_signal (SIGTERM); + if (signal_is_hard_ignored (SIGTERM) == 0) + set_signal_handler (SIGTERM, sigterm_sighandler); + set_sigwinch_handler (); + } +} + +void +reset_terminating_signals () +{ + register int i; +#if defined (HAVE_POSIX_SIGNALS) + struct sigaction act; +#endif + + if (termsigs_initialized == 0) + return; + +#if defined (HAVE_POSIX_SIGNALS) + act.sa_flags = 0; + sigemptyset (&act.sa_mask); + for (i = 0; i < TERMSIGS_LENGTH; i++) + { + /* Skip a signal if it's trapped or handled specially, because the + trap code will restore the correct value. */ + if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i))) + continue; + + act.sa_handler = XHANDLER (i); + act.sa_flags = XSAFLAGS (i); + sigaction (XSIG (i), &act, (struct sigaction *) NULL); + } +#else /* !HAVE_POSIX_SIGNALS */ + for (i = 0; i < TERMSIGS_LENGTH; i++) + { + if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i))) + continue; + + signal (XSIG (i), XHANDLER (i)); + } +#endif /* !HAVE_POSIX_SIGNALS */ + + termsigs_initialized = 0; +} +#undef XSIG +#undef XHANDLER + +/* Run some of the cleanups that should be performed when we run + jump_to_top_level from a builtin command context. XXX - might want to + also call reset_parser here. */ +void +top_level_cleanup () +{ + /* Clean up string parser environment. */ + while (parse_and_execute_level) + parse_and_execute_cleanup (); + +#if defined (PROCESS_SUBSTITUTION) + unlink_fifo_list (); +#endif /* PROCESS_SUBSTITUTION */ + + run_unwind_protects (); + loop_level = continuing = breaking = funcnest = 0; + executing_list = comsub_ignore_return = return_catch_flag = 0; +} + +/* What to do when we've been interrupted, and it is safe to handle it. */ +void +throw_to_top_level () +{ + int print_newline = 0; + + if (interrupt_state) + { + if (last_command_exit_value < 128) + last_command_exit_value = 128 + SIGINT; + print_newline = 1; + DELINTERRUPT; + } + + if (interrupt_state) + return; + + last_command_exit_signal = (last_command_exit_value > 128) ? + (last_command_exit_value - 128) : 0; + last_command_exit_value |= 128; + + /* Run any traps set on SIGINT. */ + run_interrupt_trap (); + + /* Clean up string parser environment. */ + while (parse_and_execute_level) + parse_and_execute_cleanup (); + +#if defined (JOB_CONTROL) + give_terminal_to (shell_pgrp, 0); +#endif /* JOB_CONTROL */ + +#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS) + /* This needs to stay because jobs.c:make_child() uses it without resetting + the signal mask. */ + sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL); +#endif + + reset_parser (); + +#if defined (READLINE) + if (interactive) + bashline_reset (); +#endif /* READLINE */ + +#if defined (PROCESS_SUBSTITUTION) + unlink_fifo_list (); +#endif /* PROCESS_SUBSTITUTION */ + + run_unwind_protects (); + loop_level = continuing = breaking = funcnest = 0; + executing_list = comsub_ignore_return = return_catch_flag = 0; + + if (interactive && print_newline) + { + fflush (stdout); + fprintf (stderr, "\n"); + fflush (stderr); + } + + /* An interrupted `wait' command in a script does not exit the script. */ + if (interactive || (interactive_shell && !shell_initialized) || + (print_newline && signal_is_trapped (SIGINT))) + jump_to_top_level (DISCARD); + else + jump_to_top_level (EXITPROG); +} + +/* This is just here to isolate the longjmp calls. */ +void +jump_to_top_level (value) + int value; +{ + longjmp (top_level, value); +} + +sighandler +termsig_sighandler (sig) + int sig; +{ + /* If we get called twice with the same signal before handling it, + terminate right away. */ + if ( +#ifdef SIGHUP + sig != SIGHUP && +#endif +#ifdef SIGINT + sig != SIGINT && +#endif +#ifdef SIGDANGER + sig != SIGDANGER && +#endif +#ifdef SIGPIPE + sig != SIGPIPE && +#endif +#ifdef SIGALRM + sig != SIGALRM && +#endif +#ifdef SIGTERM + sig != SIGTERM && +#endif +#ifdef SIGXCPU + sig != SIGXCPU && +#endif +#ifdef SIGXFSZ + sig != SIGXFSZ && +#endif +#ifdef SIGVTALRM + sig != SIGVTALRM && +#endif +#ifdef SIGLOST + sig != SIGLOST && +#endif +#ifdef SIGUSR1 + sig != SIGUSR1 && +#endif +#ifdef SIGUSR2 + sig != SIGUSR2 && +#endif + sig == terminating_signal) + terminate_immediately = 1; + + terminating_signal = sig; + + /* XXX - should this also trigger when interrupt_immediately is set? */ + if (terminate_immediately) + { +#if defined (HISTORY) + /* XXX - will inhibit history file being written */ +# if defined (READLINE) + if (interactive_shell == 0 || interactive == 0 || (sig != SIGHUP && sig != SIGTERM) || no_line_editing || (RL_ISSTATE (RL_STATE_READCMD) == 0)) +# endif + history_lines_this_session = 0; +#endif + terminate_immediately = 0; + termsig_handler (sig); + } + +#if defined (READLINE) + /* Set the event hook so readline will call it after the signal handlers + finish executing, so if this interrupted character input we can get + quick response. */ + if (interactive_shell && interactive && no_line_editing == 0) + bashline_set_event_hook (); +#endif + + SIGRETURN (0); +} + +void +termsig_handler (sig) + int sig; +{ + static int handling_termsig = 0; + + /* Simple semaphore to keep this function from being executed multiple + times. Since we no longer are running as a signal handler, we don't + block multiple occurrences of the terminating signals while running. */ + if (handling_termsig) + return; + handling_termsig = 1; + terminating_signal = 0; /* keep macro from re-testing true. */ + + /* I don't believe this condition ever tests true. */ + if (sig == SIGINT && signal_is_trapped (SIGINT)) + run_interrupt_trap (); + +#if defined (HISTORY) + /* If we don't do something like this, the history will not be saved when + an interactive shell is running in a terminal window that gets closed + with the `close' button. We can't test for RL_STATE_READCMD because + readline no longer handles SIGTERM synchronously. */ + if (interactive_shell && interactive && sig == SIGHUP && remember_on_history) + maybe_save_shell_history (); +#endif /* HISTORY */ + +#if defined (JOB_CONTROL) + if (sig == SIGHUP && (interactive || (subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB)))) + hangup_all_jobs (); + end_job_control (); +#endif /* JOB_CONTROL */ + +#if defined (PROCESS_SUBSTITUTION) + unlink_fifo_list (); +#endif /* PROCESS_SUBSTITUTION */ + + /* Reset execution context */ + loop_level = continuing = breaking = funcnest = 0; + executing_list = comsub_ignore_return = return_catch_flag = 0; + + run_exit_trap (); + set_signal_handler (sig, SIG_DFL); + kill (getpid (), sig); +} + +/* What we really do when SIGINT occurs. */ +sighandler +sigint_sighandler (sig) + int sig; +{ +#if defined (MUST_REINSTALL_SIGHANDLERS) + signal (sig, sigint_sighandler); +#endif + + /* interrupt_state needs to be set for the stack of interrupts to work + right. Should it be set unconditionally? */ + if (interrupt_state == 0) + ADDINTERRUPT; + + if (interrupt_immediately) + { + interrupt_immediately = 0; + last_command_exit_value = 128 + sig; + throw_to_top_level (); + } +#if defined (READLINE) + /* Set the event hook so readline will call it after the signal handlers + finish executing, so if this interrupted character input we can get + quick response. */ + else if (RL_ISSTATE (RL_STATE_SIGHANDLER)) + bashline_set_event_hook (); +#endif + + SIGRETURN (0); +} + +#if defined (SIGWINCH) +sighandler +sigwinch_sighandler (sig) + int sig; +{ +#if defined (MUST_REINSTALL_SIGHANDLERS) + set_signal_handler (SIGWINCH, sigwinch_sighandler); +#endif /* MUST_REINSTALL_SIGHANDLERS */ + sigwinch_received = 1; + SIGRETURN (0); +} +#endif /* SIGWINCH */ + +void +set_sigwinch_handler () +{ +#if defined (SIGWINCH) + old_winch = set_signal_handler (SIGWINCH, sigwinch_sighandler); +#endif +} + +void +unset_sigwinch_handler () +{ +#if defined (SIGWINCH) + set_signal_handler (SIGWINCH, old_winch); +#endif +} + +sighandler +sigterm_sighandler (sig) + int sig; +{ + sigterm_received = 1; /* XXX - counter? */ + SIGRETURN (0); +} + +/* Signal functions used by the rest of the code. */ +#if !defined (HAVE_POSIX_SIGNALS) + +/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */ +sigprocmask (operation, newset, oldset) + int operation, *newset, *oldset; +{ + int old, new; + + if (newset) + new = *newset; + else + new = 0; + + switch (operation) + { + case SIG_BLOCK: + old = sigblock (new); + break; + + case SIG_SETMASK: + old = sigsetmask (new); + break; + + default: + internal_error (_("sigprocmask: %d: invalid operation"), operation); + } + + if (oldset) + *oldset = old; +} + +#else + +#if !defined (SA_INTERRUPT) +# define SA_INTERRUPT 0 +#endif + +#if !defined (SA_RESTART) +# define SA_RESTART 0 +#endif + +SigHandler * +set_signal_handler (sig, handler) + int sig; + SigHandler *handler; +{ + struct sigaction act, oact; + + act.sa_handler = handler; + act.sa_flags = 0; + + /* XXX - bash-4.2 */ + /* We don't want a child death to interrupt interruptible system calls, even + if we take the time to reap children */ +#if defined (SIGCHLD) + if (sig == SIGCHLD) + act.sa_flags |= SA_RESTART; /* XXX */ +#endif + /* If we're installing a SIGTERM handler for interactive shells, we want + it to be as close to SIG_IGN as possible. */ + if (sig == SIGTERM && handler == sigterm_sighandler) + act.sa_flags |= SA_RESTART; /* XXX */ + + sigemptyset (&act.sa_mask); + sigemptyset (&oact.sa_mask); + sigaction (sig, &act, &oact); + return (oact.sa_handler); +} +#endif /* HAVE_POSIX_SIGNALS */ @@ -25,6 +25,10 @@ #include "stdc.h" +#if !defined (SIG_DFL) +# include <signal.h> /* for sig_atomic_t */ +#endif + #if !defined (SIGABRT) && defined (SIGIOT) # define SIGABRT SIGIOT #endif @@ -104,7 +108,8 @@ do { \ #endif /* !HAVE_POSIX_SIGNALS */ /* Extern variables */ -extern volatile int sigwinch_received; +extern sig_atomic_t sigwinch_received; +extern sig_atomic_t sigterm_received; extern int interrupt_immediately; extern int terminate_immediately; @@ -0,0 +1,139 @@ +/* sig.h -- header file for signal handler definitions. */ + +/* Copyright (C) 1994-2013 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Make sure that this is included *after* config.h! */ + +#if !defined (_SIG_H_) +# define _SIG_H_ + +#include "stdc.h" + +#if !defined (SIG_DFL) +# include <signal.h> +#endif + +#if !defined (SIGABRT) && defined (SIGIOT) +# define SIGABRT SIGIOT +#endif + +#define sighandler RETSIGTYPE +typedef RETSIGTYPE SigHandler __P((int)); + +#if defined (VOID_SIGHANDLER) +# define SIGRETURN(n) return +#else +# define SIGRETURN(n) return(n) +#endif /* !VOID_SIGHANDLER */ + +/* Here is a definition for set_signal_handler () which simply expands to + a call to signal () for non-Posix systems. The code for set_signal_handler + in the Posix case resides in general.c. */ +#if !defined (HAVE_POSIX_SIGNALS) +# define set_signal_handler(sig, handler) (SigHandler *)signal (sig, handler) +#else +extern SigHandler *set_signal_handler __P((int, SigHandler *)); /* in sig.c */ +#endif /* _POSIX_VERSION */ + +#if !defined (SIGCHLD) && defined (SIGCLD) +# define SIGCHLD SIGCLD +#endif + +#if !defined (HAVE_POSIX_SIGNALS) && !defined (sigmask) +# define sigmask(x) (1 << ((x)-1)) +#endif /* !HAVE_POSIX_SIGNALS && !sigmask */ + +#if !defined (HAVE_POSIX_SIGNALS) +# if !defined (SIG_BLOCK) +# define SIG_BLOCK 2 +# define SIG_SETMASK 3 +# endif /* SIG_BLOCK */ + +/* sigset_t defined in config.h */ + +/* Make sure there is nothing inside the signal set. */ +# define sigemptyset(set) (*(set) = 0) + +/* Initialize the signal set to hold all signals. */ +# define sigfillset(set) (*set) = sigmask (NSIG) - 1 + +/* Add SIG to the contents of SET. */ +# define sigaddset(set, sig) *(set) |= sigmask (sig) + +/* Delete SIG from signal set SET. */ +# define sigdelset(set, sig) *(set) &= ~sigmask (sig) + +/* Is SIG a member of the signal set SET? */ +# define sigismember(set, sig) ((*(set) & sigmask (sig)) != 0) + +/* Suspend the process until the reception of one of the signals + not present in SET. */ +# define sigsuspend(set) sigpause (*(set)) +#endif /* !HAVE_POSIX_SIGNALS */ + +/* These definitions are used both in POSIX and non-POSIX implementations. */ + +#define BLOCK_SIGNAL(sig, nvar, ovar) \ +do { \ + sigemptyset (&nvar); \ + sigaddset (&nvar, sig); \ + sigemptyset (&ovar); \ + sigprocmask (SIG_BLOCK, &nvar, &ovar); \ +} while (0) + +#define UNBLOCK_SIGNAL(ovar) sigprocmask (SIG_SETMASK, &ovar, (sigset_t *) NULL) + +#if defined (HAVE_POSIX_SIGNALS) +# define BLOCK_CHILD(nvar, ovar) BLOCK_SIGNAL (SIGCHLD, nvar, ovar) +# define UNBLOCK_CHILD(ovar) UNBLOCK_SIGNAL(ovar) +#else /* !HAVE_POSIX_SIGNALS */ +# define BLOCK_CHILD(nvar, ovar) ovar = sigblock (sigmask (SIGCHLD)) +# define UNBLOCK_CHILD(ovar) sigsetmask (ovar) +#endif /* !HAVE_POSIX_SIGNALS */ + +/* Extern variables */ +extern sig_atomic_t sigwinch_received; +extern sig_atomic_t sigterm_received; + +extern int interrupt_immediately; +extern int terminate_immediately; + +/* Functions from sig.c. */ +extern sighandler termsig_sighandler __P((int)); +extern void termsig_handler __P((int)); +extern sighandler sigint_sighandler __P((int)); +extern void initialize_signals __P((int)); +extern void initialize_terminating_signals __P((void)); +extern void reset_terminating_signals __P((void)); +extern void top_level_cleanup __P((void)); +extern void throw_to_top_level __P((void)); +extern void jump_to_top_level __P((int)) __attribute__((__noreturn__)); + +extern sighandler sigwinch_sighandler __P((int)); +extern void set_sigwinch_handler __P((void)); +extern void unset_sigwinch_handler __P((void)); + +extern sighandler sigterm_sighandler __P((int)); + +/* Functions defined in trap.c. */ +extern SigHandler *set_sigint_handler __P((void)); +extern SigHandler *trap_to_sighandler __P((int)); +extern sighandler trap_handler __P((int)); + +#endif /* _SIG_H_ */ diff --git a/unwind_prot.c b/unwind_prot.c index a477c03a..85a10b57 100644 --- a/unwind_prot.c +++ b/unwind_prot.c @@ -46,8 +46,8 @@ #include "command.h" #include "general.h" #include "unwind_prot.h" -#include "quit.h" #include "sig.h" +#include "quit.h" #include "error.h" /* for internal_warning */ /* Structure describing a saved variable and the value to restore it to. */ diff --git a/unwind_prot.c~ b/unwind_prot.c~ new file mode 100644 index 00000000..a477c03a --- /dev/null +++ b/unwind_prot.c~ @@ -0,0 +1,357 @@ +/* unwind_prot.c - a simple unwind-protect system for internal variables */ + +/* I can't stand it anymore! Please can't we just write the + whole Unix system in lisp or something? */ + +/* Copyright (C) 1987-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* **************************************************************** */ +/* */ +/* Unwind Protection Scheme for Bash */ +/* */ +/* **************************************************************** */ +#include "config.h" + +#include "bashtypes.h" +#include "bashansi.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#if STDC_HEADERS +# include <stddef.h> +#endif + +#ifndef offsetof +# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#include "command.h" +#include "general.h" +#include "unwind_prot.h" +#include "quit.h" +#include "sig.h" +#include "error.h" /* for internal_warning */ + +/* Structure describing a saved variable and the value to restore it to. */ +typedef struct { + char *variable; + int size; + char desired_setting[1]; /* actual size is `size' */ +} SAVED_VAR; + +/* If HEAD.CLEANUP is null, then ARG.V contains a tag to throw back to. + If HEAD.CLEANUP is restore_variable, then SV.V contains the saved + variable. Otherwise, call HEAD.CLEANUP (ARG.V) to clean up. */ +typedef union uwp { + struct uwp_head { + union uwp *next; + Function *cleanup; + } head; + struct { + struct uwp_head uwp_head; + char *v; + } arg; + struct { + struct uwp_head uwp_head; + SAVED_VAR v; + } sv; +} UNWIND_ELT; + + +static void without_interrupts __P((VFunction *, char *, char *)); +static void unwind_frame_discard_internal __P((char *, char *)); +static void unwind_frame_run_internal __P((char *, char *)); +static void add_unwind_protect_internal __P((Function *, char *)); +static void remove_unwind_protect_internal __P((char *, char *)); +static void run_unwind_protects_internal __P((char *, char *)); +static void clear_unwind_protects_internal __P((char *, char *)); +static inline void restore_variable __P((SAVED_VAR *)); +static void unwind_protect_mem_internal __P((char *, char *)); + +static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL; + +#define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT)) +#define uwpfree(elt) free(elt) + +/* Run a function without interrupts. This relies on the fact that the + FUNCTION cannot change the value of interrupt_immediately. (I.e., does + not call QUIT (). */ +static void +without_interrupts (function, arg1, arg2) + VFunction *function; + char *arg1, *arg2; +{ + int old_interrupt_immediately; + + old_interrupt_immediately = interrupt_immediately; + interrupt_immediately = 0; + + (*function)(arg1, arg2); + + interrupt_immediately = old_interrupt_immediately; +} + +/* Start the beginning of a region. */ +void +begin_unwind_frame (tag) + char *tag; +{ + add_unwind_protect ((Function *)NULL, tag); +} + +/* Discard the unwind protects back to TAG. */ +void +discard_unwind_frame (tag) + char *tag; +{ + if (unwind_protect_list) + without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL); +} + +/* Run the unwind protects back to TAG. */ +void +run_unwind_frame (tag) + char *tag; +{ + if (unwind_protect_list) + without_interrupts (unwind_frame_run_internal, tag, (char *)NULL); +} + +/* Add the function CLEANUP with ARG to the list of unwindable things. */ +void +add_unwind_protect (cleanup, arg) + Function *cleanup; + char *arg; +{ + without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg); +} + +/* Remove the top unwind protect from the list. */ +void +remove_unwind_protect () +{ + if (unwind_protect_list) + without_interrupts + (remove_unwind_protect_internal, (char *)NULL, (char *)NULL); +} + +/* Run the list of cleanup functions in unwind_protect_list. */ +void +run_unwind_protects () +{ + if (unwind_protect_list) + without_interrupts + (run_unwind_protects_internal, (char *)NULL, (char *)NULL); +} + +/* Erase the unwind-protect list. If flags is 1, free the elements. */ +void +clear_unwind_protect_list (flags) + int flags; +{ + char *flag; + + if (unwind_protect_list) + { + flag = flags ? "" : (char *)NULL; + without_interrupts + (clear_unwind_protects_internal, flag, (char *)NULL); + } +} + +int +have_unwind_protects () +{ + return (unwind_protect_list != 0); +} + +/* **************************************************************** */ +/* */ +/* The Actual Functions */ +/* */ +/* **************************************************************** */ + +static void +add_unwind_protect_internal (cleanup, arg) + Function *cleanup; + char *arg; +{ + UNWIND_ELT *elt; + + uwpalloc (elt); + elt->head.next = unwind_protect_list; + elt->head.cleanup = cleanup; + elt->arg.v = arg; + unwind_protect_list = elt; +} + +static void +remove_unwind_protect_internal (ignore1, ignore2) + char *ignore1, *ignore2; +{ + UNWIND_ELT *elt; + + elt = unwind_protect_list; + if (elt) + { + unwind_protect_list = unwind_protect_list->head.next; + uwpfree (elt); + } +} + +static void +run_unwind_protects_internal (ignore1, ignore2) + char *ignore1, *ignore2; +{ + unwind_frame_run_internal ((char *) NULL, (char *) NULL); +} + +static void +clear_unwind_protects_internal (flag, ignore) + char *flag, *ignore; +{ + if (flag) + { + while (unwind_protect_list) + remove_unwind_protect_internal ((char *)NULL, (char *)NULL); + } + unwind_protect_list = (UNWIND_ELT *)NULL; +} + +static void +unwind_frame_discard_internal (tag, ignore) + char *tag, *ignore; +{ + UNWIND_ELT *elt; + int found; + + found = 0; + while (elt = unwind_protect_list) + { + unwind_protect_list = unwind_protect_list->head.next; + if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag))) + { + uwpfree (elt); + found = 1; + break; + } + else + uwpfree (elt); + } + + if (found == 0) + internal_warning ("unwind_frame_discard: %s: frame not found", tag); +} + +/* Restore the value of a variable, based on the contents of SV. + sv->desired_setting is a block of memory SIZE bytes long holding the + value itself. This block of memory is copied back into the variable. */ +static inline void +restore_variable (sv) + SAVED_VAR *sv; +{ + FASTCOPY (sv->desired_setting, sv->variable, sv->size); +} + +static void +unwind_frame_run_internal (tag, ignore) + char *tag, *ignore; +{ + UNWIND_ELT *elt; + int found; + + found = 0; + while (elt = unwind_protect_list) + { + unwind_protect_list = elt->head.next; + + /* If tag, then compare. */ + if (elt->head.cleanup == 0) + { + if (tag && STREQ (elt->arg.v, tag)) + { + uwpfree (elt); + found = 1; + break; + } + } + else + { + if (elt->head.cleanup == (Function *) restore_variable) + restore_variable (&elt->sv.v); + else + (*(elt->head.cleanup)) (elt->arg.v); + } + + uwpfree (elt); + } + if (tag && found == 0) + internal_warning ("unwind_frame_run: %s: frame not found", tag); +} + +static void +unwind_protect_mem_internal (var, psize) + char *var; + char *psize; +{ + int size, allocated; + UNWIND_ELT *elt; + + size = *(int *) psize; + allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]); + elt = (UNWIND_ELT *)xmalloc (allocated); + elt->head.next = unwind_protect_list; + elt->head.cleanup = (Function *) restore_variable; + elt->sv.v.variable = var; + elt->sv.v.size = size; + FASTCOPY (var, elt->sv.v.desired_setting, size); + unwind_protect_list = elt; +} + +/* Save the value of a variable so it will be restored when unwind-protects + are run. VAR is a pointer to the variable. SIZE is the size in + bytes of VAR. */ +void +unwind_protect_mem (var, size) + char *var; + int size; +{ + without_interrupts (unwind_protect_mem_internal, var, (char *) &size); +} + +#if defined (DEBUG) +#include <stdio.h> + +void +print_unwind_protect_tags () +{ + UNWIND_ELT *elt; + + elt = unwind_protect_list; + while (elt) + { + unwind_protect_list = unwind_protect_list->head.next; + if (elt->head.cleanup == 0) + fprintf(stderr, "tag: %s\n", elt->arg.v); + elt = unwind_protect_list; + } +} +#endif |