#include #if !defined (HAVE_WORKING_FORK) #include #include #include #include #include #include #include #include #include #include #include #include #include "nofork.h" #include "dospath.h" #include "command.h" #include "general.h" #include "error.h" #include "variables.h" #include "subst.h" #include "externs.h" #include "builtins/common.h" #include "hashcmd.h" #include "flags.h" #include "bashjmp.h" #define SAVESTRING(p) ((p != NULL) ? savestring (p) : NULL) #define FREE_AND_CLEAR(p) (free (p), p = NULL) #define CURRENT_PID ((pid_t) 1) pid_t nofork_current_pid = CURRENT_PID; int nofork_wait_status = -1; OLDENVBUF *current_environment = NULL; static void save_jmp_buf(jmp_buf save, jmp_buf now) { memcpy((char *)save, (char *)now, sizeof(jmp_buf)); } static void restore_jmp_buf(jmp_buf save, jmp_buf now) { memcpy((char *)now, (char *)save, sizeof(jmp_buf)); } extern jmp_buf top_level, subshell_top_level; extern int interactive, interactive_shell, login_shell; extern int subshell_environment; extern int subshell_exit_builtin, exit_immediately_on_error; extern int dollar_dollar_pid; extern int array_needs_making; extern SHELL_VAR *ifs_var; static void save_current_directory (OLDENVBUF *envp) { envp->pwd = SAVESTRING (get_string_value ("PWD")); envp->curdir = getcwd (NULL, PATH_MAX); } static void restore_current_directory(OLDENVBUF *envp) { /* change old directory */ if (envp->curdir != NULL) { chdir(envp->curdir); if (envp->pwd != NULL) set_working_directory(envp->pwd); else set_working_directory(envp->curdir); FREE_AND_CLEAR (envp->curdir); } else if (envp->pwd != NULL) { chdir(envp->pwd); set_working_directory(envp->pwd); } if (envp->pwd) FREE_AND_CLEAR(envp->pwd); } extern WORD_LIST *subst_assign_varlist; static void save_global_variables(OLDENVBUF *envp) { envp->interactive = interactive; envp->interactive_shell = interactive_shell; envp->login_shell = login_shell; envp->subshell_environment = subshell_environment; envp->subshell_exit_builtin = subshell_exit_builtin; envp->exit_immediately_on_error = exit_immediately_on_error; envp->variable_context = variable_context; envp->dollar_dollar_pid = dollar_dollar_pid; envp->subst_assign_varlist = subst_assign_varlist; envp->echo_command_at_execute = echo_command_at_execute; subst_assign_varlist = NULL; } static void restore_global_variables(OLDENVBUF *envp) { interactive = envp->interactive; interactive_shell = envp->interactive_shell; login_shell = envp->login_shell; subshell_environment = envp->subshell_environment; subshell_exit_builtin = envp->subshell_exit_builtin; exit_immediately_on_error = envp->exit_immediately_on_error; variable_context = envp->variable_context; dollar_dollar_pid = envp->dollar_dollar_pid; subst_assign_varlist = envp->subst_assign_varlist; echo_command_at_execute = envp->echo_command_at_execute; } static VAR_CONTEXT * copy_var_context(VAR_CONTEXT *fvc) { VAR_CONTEXT *vc; vc = (VAR_CONTEXT *)xmalloc(sizeof(VAR_CONTEXT)); vc->name = fvc->name ? savestring(fvc->name) : (char *)NULL; vc->scope = fvc->scope; vc->flags = fvc->flags & ~(VC_COPYONCHANGE); vc->table = hash_copy(fvc->table, (sh_string_func_t *)copy_variable); vc->up = vc->down = (VAR_CONTEXT *)NULL; return vc; } static void save_all_var_contexts(OLDENVBUF *envp) { VAR_CONTEXT *vc; VAR_CONTEXT *vc_copy, *sv, *vc_copy_prev; vc = shell_variables; sv = vc_copy = copy_var_context(vc); while (vc->down) { vc_copy_prev = vc_copy; vc = vc->down; vc_copy = copy_var_context(vc); vc_copy_prev->down = vc_copy; vc_copy->up = vc_copy_prev; } #if NOFORK_COPY_ON_CHANGE envp->global_variables = vc_copy; envp->shell_variables = sv; #else envp->shell_variables = shell_variables; envp->global_variables = global_variables; shell_variables = sv; global_variables = vc_copy; #endif } static void restore_all_var_contexts(OLDENVBUF *envp) { delete_all_contexts(shell_variables); dispose_var_context(global_variables); global_variables = envp->global_variables; shell_variables = envp->shell_variables; delete_all_variables(shell_functions); hash_dispose(shell_functions); shell_functions = envp->shell_functions; } void copy_var_contexts_on_change(void) { if (current_environment == NULL) return; save_all_var_contexts(current_environment); global_variables->flags &= ~(VC_COPYONCHANGE); } static void save_shell_variables(OLDENVBUF *envp) { #if NOFORK_COPY_ON_CHANGE SHELL_VAR *lastarg_var; #endif maybe_make_export_env(); envp->global_variables = NULL; envp->shell_variables = NULL; global_variables->flags |= VC_COPYONCHANGE; #if NOFORK_COPY_ON_CHANGE || 1 /* Must be copied to be restored later because the shell variables may be modified by the subshell call. */ save_all_var_contexts(envp); #endif envp->shell_functions = shell_functions; shell_functions = hash_copy(envp->shell_functions, (sh_string_func_t *)copy_variable); envp->temporary_env = temporary_env; if (temporary_env) temporary_env = hash_copy(envp->temporary_env, (sh_string_func_t *)copy_variable); #if NOFORK_COPY_ON_CHANGE lastarg_var = find_variable("_"); if (lastarg_var) envp->lastarg = savestring(value_cell(lastarg_var)); #endif envp->rest_of_args = list_rest_of_args(); } static void restore_shell_variables(OLDENVBUF *envp) { SHELL_VAR *temp_ifs; if (envp->global_variables) restore_all_var_contexts(envp); else global_variables->flags &= ~(VC_COPYONCHANGE); if (temporary_env) { delete_all_variables(temporary_env); hash_dispose(temporary_env); } temporary_env = envp->temporary_env; temp_ifs = find_variable("IFS"); setifs(temp_ifs); #if NOFORK_COPY_ON_CHANGE bind_variable("_", envp->lastarg); free(envp->lastarg); #endif remember_args(envp->rest_of_args, 1); array_needs_making = 1; maybe_make_export_env(); } extern int return_catch_flag; int nofork_save_all_environment(OLDENVBUF *envp) { save_jmp_buf(envp->return_catch, return_catch); envp->return_catch_flag = return_catch_flag; save_jmp_buf(envp->top_level, top_level); save_jmp_buf(envp->subshell_top_level, subshell_top_level); nofork_save_std_fds(envp->fds); save_current_directory(envp); save_global_variables(envp); save_shell_variables(envp); envp->prev_environment = current_environment; current_environment = envp; return 0; } int nofork_restore_all_environment(OLDENVBUF *envp) { restore_shell_variables(envp); restore_global_variables(envp); restore_current_directory(envp); nofork_restore_std_fds(envp->fds); restore_jmp_buf(envp->top_level, top_level); restore_jmp_buf(envp->subshell_top_level, subshell_top_level); restore_jmp_buf(envp->return_catch, return_catch); return_catch_flag = envp->return_catch_flag; current_environment = envp->prev_environment; return 0; } #include #if !defined (MAXPID) #define MAXPID ((pid_t) 30000) #endif static pid_t last_pid = CURRENT_PID; pid_t nofork_make_child_pid(void) { if (++last_pid > MAXPID) last_pid = CURRENT_PID + 1; return last_pid; } void nofork_save_std_fds(int fds[3]) { int i; /* save stdin/out/err */ for (i = 0; i < 3; i++) #if defined (F_DUPFD) if ((fds[i] = fcntl(i, F_DUPFD, 20)) < 0) #else if ((fds[i] = dup(i)) < 0) #endif internal_error("Cannot duplicate fd %d: %s", i, strerror(errno)); } void nofork_restore_std_fds(int fds[3]) { int i; /* restore stdin/out/err */ for (i = 0; i < 3; i++) if (fds[i] >= 0) { if (dup2(fds[i], i) < 0) internal_error("cannot duplicate fd %d to fd %d: %s", fds[i], i, strerror(errno)); close (fds[i]); fds[i] = -1; } } #endif /* !HAVE_WORKING_FORK */