diff --git a/FishsFish.xcodeproj/project.pbxproj b/FishsFish.xcodeproj/project.pbxproj index 320528b5a..e4987a9c0 100644 --- a/FishsFish.xcodeproj/project.pbxproj +++ b/FishsFish.xcodeproj/project.pbxproj @@ -28,14 +28,14 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + D025C02A15D1FEA100B9DB63 /* completions in Resources */ = {isa = PBXBuildFile; fileRef = D025C02715D1FEA100B9DB63 /* completions */; }; + D025C02B15D1FEA100B9DB63 /* functions in Resources */ = {isa = PBXBuildFile; fileRef = D025C02815D1FEA100B9DB63 /* functions */; }; + D025C02C15D1FEA100B9DB63 /* tools in Resources */ = {isa = PBXBuildFile; fileRef = D025C02915D1FEA100B9DB63 /* tools */; }; D07A7D3C15A7A38100811FC6 /* builtin_scripts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0F5E28215A7A32D00315DFF /* builtin_scripts.cpp */; }; D07B247315BCC15700D4ADB4 /* add-shell in Resources */ = {isa = PBXBuildFile; fileRef = D07B247215BCC15700D4ADB4 /* add-shell */; }; D07B247615BCC4BE00D4ADB4 /* install.sh in Resources */ = {isa = PBXBuildFile; fileRef = D07B247515BCC4BE00D4ADB4 /* install.sh */; }; D0C4FD9515A7D80700212EF1 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0C4FD9415A7D7EE00212EF1 /* config.fish */; }; D0C4FD9615A7D80C00212EF1 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0CBD580159EE48F0024809C /* config.fish */; }; - D0CBD57B159EE4640024809C /* completions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0CBD578159EE4600024809C /* completions */; }; - D0CBD57C159EE4640024809C /* functions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0CBD579159EE4600024809C /* functions */; }; - D0CBD57D159EE4640024809C /* tools in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0CBD57A159EE4600024809C /* tools */; }; D0CBD587159EF0E10024809C /* launch_fish.scpt in Resources */ = {isa = PBXBuildFile; fileRef = D0CBD586159EF0E10024809C /* launch_fish.scpt */; }; D0D02A67159837AD008E62BD /* complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853713B3ACEE0099B651 /* complete.cpp */; }; D0D02A69159837B2008E62BD /* env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853A13B3ACEE0099B651 /* env.cpp */; }; @@ -121,9 +121,6 @@ D0F019F415A9772C0034B3B1 /* fish_pager in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0D02AE415986537008E62BD /* fish_pager */; }; D0F019F615A977360034B3B1 /* set_color in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0F019DC15A969970034B3B1 /* set_color */; }; D0F019F815A977AB0034B3B1 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0CBD580159EE48F0024809C /* config.fish */; }; - D0F019F915A977AD0034B3B1 /* completions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0CBD578159EE4600024809C /* completions */; }; - D0F019FA15A977AE0034B3B1 /* functions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0CBD579159EE4600024809C /* functions */; }; - D0F019FB15A977B00034B3B1 /* tools in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0CBD57A159EE4600024809C /* tools */; }; D0F019FD15A977CA0034B3B1 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0C4FD9415A7D7EE00212EF1 /* config.fish */; }; D0F01A0315A978910034B3B1 /* osx_fish_launcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D02AFA159871B2008E62BD /* osx_fish_launcher.m */; }; D0F01A0515A978A10034B3B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0CBD583159EEE010024809C /* Foundation.framework */; }; @@ -210,9 +207,6 @@ dstSubfolderSpec = 7; files = ( D0C4FD9615A7D80C00212EF1 /* config.fish in CopyFiles */, - D0CBD57B159EE4640024809C /* completions in CopyFiles */, - D0CBD57C159EE4640024809C /* functions in CopyFiles */, - D0CBD57D159EE4640024809C /* tools in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -261,9 +255,6 @@ dstSubfolderSpec = 1; files = ( D0F019F815A977AB0034B3B1 /* config.fish in CopyFiles */, - D0F019F915A977AD0034B3B1 /* completions in CopyFiles */, - D0F019FA15A977AE0034B3B1 /* functions in CopyFiles */, - D0F019FB15A977B00034B3B1 /* tools in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -280,6 +271,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + D025C02715D1FEA100B9DB63 /* completions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = completions; path = share/completions; sourceTree = ""; }; + D025C02815D1FEA100B9DB63 /* functions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = functions; path = share/functions; sourceTree = ""; }; + D025C02915D1FEA100B9DB63 /* tools */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tools; path = share/tools; sourceTree = ""; }; D03EE83814DF88B200FC7150 /* lru.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lru.h; sourceTree = ""; }; D07B247215BCC15700D4ADB4 /* add-shell */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "add-shell"; path = "build_tools/osx_package_scripts/add-shell"; sourceTree = ""; }; D07B247515BCC4BE00D4ADB4 /* install.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = install.sh; path = osx/install.sh; sourceTree = ""; }; @@ -387,9 +381,6 @@ D0C4FD9415A7D7EE00212EF1 /* config.fish */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.fish; path = etc/config.fish; sourceTree = ""; }; D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = autoload.cpp; sourceTree = ""; }; D0C6FCCB14CFA4B7004CE8AD /* autoload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autoload.h; sourceTree = ""; }; - D0CBD578159EE4600024809C /* completions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = completions; path = share/completions; sourceTree = ""; }; - D0CBD579159EE4600024809C /* functions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = functions; path = share/functions; sourceTree = ""; }; - D0CBD57A159EE4600024809C /* tools */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tools; path = share/tools; sourceTree = ""; }; D0CBD580159EE48F0024809C /* config.fish */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.fish; path = share/config.fish; sourceTree = ""; }; D0CBD583159EEE010024809C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; D0CBD586159EF0E10024809C /* launch_fish.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; name = launch_fish.scpt; path = osx/launch_fish.scpt; sourceTree = ""; }; @@ -608,10 +599,10 @@ D0CBD580159EE48F0024809C /* config.fish */, D0C4FD9415A7D7EE00212EF1 /* config.fish */, D07B247515BCC4BE00D4ADB4 /* install.sh */, - D0CBD578159EE4600024809C /* completions */, - D0CBD579159EE4600024809C /* functions */, - D0CBD57A159EE4600024809C /* tools */, D0D02AA915985C0C008E62BD /* Info.plist */, + D025C02715D1FEA100B9DB63 /* completions */, + D025C02815D1FEA100B9DB63 /* functions */, + D025C02915D1FEA100B9DB63 /* tools */, ); name = Resources; sourceTree = ""; @@ -813,6 +804,9 @@ D0CBD587159EF0E10024809C /* launch_fish.scpt in Resources */, D07B247315BCC15700D4ADB4 /* add-shell in Resources */, D07B247615BCC4BE00D4ADB4 /* install.sh in Resources */, + D025C02A15D1FEA100B9DB63 /* completions in Resources */, + D025C02B15D1FEA100B9DB63 /* functions in Resources */, + D025C02C15D1FEA100B9DB63 /* tools in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/builtin.cpp b/builtin.cpp index f910486c2..f9a42f979 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -155,7 +155,7 @@ static int builtin_stdin; should normally not be used - sb_out and friends are already configured to handle everything. */ -static io_data_t *real_io; +static const io_chain_t *real_io; /** Counts the number of non null pointers in the specified array @@ -2752,7 +2752,7 @@ static int builtin_contains( parser_t &parser, wchar_t ** argv ) switch( opt ) { case 0: - assert(opt_index >= 0 && opt_index < sizeof long_options / sizeof *long_options); + assert(opt_index >= 0 && (size_t)opt_index < sizeof long_options / sizeof *long_options); if(long_options[opt_index].flag != 0) break; append_format(stderr_buffer, @@ -2878,7 +2878,7 @@ static int builtin_source( parser_t &parser, wchar_t ** argv ) parse_util_set_argv( (argc>2)?(argv+2):(argv+1), wcstring_list_t()); - res = reader_read( fd, real_io ); + res = reader_read( fd, real_io ? *real_io : io_chain_t() ); parser.pop_block(); @@ -3470,7 +3470,7 @@ static int builtin_breakpoint( parser_t &parser, wchar_t **argv ) { parser.push_block( BREAKPOINT ); - reader_read( STDIN_FILENO, real_io ); + reader_read( STDIN_FILENO, real_io ? *real_io : io_chain_t() ); parser.pop_block(); @@ -3861,10 +3861,10 @@ static int internal_help( const wchar_t *cmd ) } -int builtin_run( parser_t &parser, const wchar_t * const *argv, io_data_t *io ) +int builtin_run( parser_t &parser, const wchar_t * const *argv, const io_chain_t &io ) { int (*cmd)(parser_t &parser, const wchar_t * const *argv)=0; - real_io = io; + real_io = &io; CHECK( argv, STATUS_BUILTIN_ERROR ); CHECK( argv[0], STATUS_BUILTIN_ERROR ); diff --git a/builtin.h b/builtin.h index a34c83cbb..eee03b90c 100644 --- a/builtin.h +++ b/builtin.h @@ -135,7 +135,7 @@ int builtin_exists( const wcstring &cmd ); \return the exit status of the builtin command */ -int builtin_run( parser_t &parser, const wchar_t * const *argv, io_data_t *io ); +int builtin_run( parser_t &parser, const wchar_t * const *argv, const io_chain_t &io ); /** Returns a list of all builtin names */ wcstring_list_t builtin_get_names(void); diff --git a/builtin_set.cpp b/builtin_set.cpp index 35dea7a12..c9b0b4b68 100644 --- a/builtin_set.cpp +++ b/builtin_set.cpp @@ -313,7 +313,7 @@ static void erase_values(wcstring_list_t &list, const std::vector &indexes std::set::const_reverse_iterator iter; for (iter = indexes_set.rbegin(); iter != indexes_set.rend(); iter++) { long val = *iter; - if (val > 0 && val <= list.size()) { + if (val > 0 && (size_t)val <= list.size()) { // One-based indexing! list.erase(list.begin() + val - 1); } diff --git a/common.h b/common.h index 05c795bb5..2028a5d28 100644 --- a/common.h +++ b/common.h @@ -135,9 +135,9 @@ extern const wchar_t *program_name; */ #define FATAL_EXIT() \ { \ - ssize_t exit_read_count;char exit_read_buff; \ + char exit_read_buff; \ show_stackframe(); \ - exit_read_count=read( 0, &exit_read_buff, 1 ); \ + read( 0, &exit_read_buff, 1 ); \ exit_without_destructors( 1 ); \ } \ diff --git a/configure.ac b/configure.ac index 3da41794d..ddb8a9158 100644 --- a/configure.ac +++ b/configure.ac @@ -39,6 +39,8 @@ AC_SUBST(XSEL_MAN) AC_SUBST(XSEL_BIN) AC_SUBST(XSEL_MAN_PATH) + + # # If needed, run autoconf to regenerate the configure file # @@ -559,7 +561,7 @@ LIBS=$LIBS_COMMON # Check presense of various header files # -AC_CHECK_HEADERS([getopt.h termio.h sys/resource.h term.h ncurses/term.h ncurses.h curses.h stropts.h siginfo.h sys/select.h sys/ioctl.h sys/termios.h libintl.h execinfo.h]) +AC_CHECK_HEADERS([getopt.h termio.h sys/resource.h term.h ncurses/term.h ncurses.h curses.h stropts.h siginfo.h sys/select.h sys/ioctl.h sys/termios.h libintl.h execinfo.h spawn.h]) AC_CHECK_HEADER( [regex.h], diff --git a/env.cpp b/env.cpp index 26658379a..387d50c24 100644 --- a/env.cpp +++ b/env.cpp @@ -101,6 +101,7 @@ struct var_entry_t typedef std::map var_table_t; bool g_log_forks = false; +bool g_use_posix_spawn = false; //will usually be set to true /** @@ -187,7 +188,11 @@ static null_terminated_array_t export_array; Flag for checking if we need to regenerate the exported variable array */ -static bool has_changed = true; +static bool has_changed_exported = true; +static void mark_changed_exported() +{ + has_changed_exported = true; +} /** This string is used to store the value of dynamically @@ -257,7 +262,7 @@ static void start_fishd() } parser_t &parser = parser_t::principal_parser(); - parser.eval( cmd, 0, TOP ); + parser.eval( cmd, io_chain_t(), TOP ); } /** @@ -394,7 +399,7 @@ static void universal_callback( int type, if( str ) { - has_changed=true; + mark_changed_exported(); event_t ev = event_t::variable_event(name); ev.arguments.reset(new wcstring_list_t()); @@ -682,6 +687,10 @@ void env_init(const struct config_paths_t *paths /* or NULL */) /* Set g_log_forks */ env_var_t log_forks = env_get_string(L"fish_log_forks"); g_log_forks = ! log_forks.missing_or_empty() && from_string(log_forks); + + /* Set g_use_posix_spawn. Default to true. */ + env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn"); + g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string(use_posix_spawn)); } void env_destroy() @@ -702,7 +711,7 @@ void env_destroy() var_entry_t *entry = iter->second; if( entry->exportv ) { - has_changed = true; + mark_changed_exported(); } delete entry; @@ -741,12 +750,12 @@ int env_set(const wcstring &key, const wchar_t *val, int var_mode) { ASSERT_IS_MAIN_THREAD(); env_node_t *node = NULL; - bool has_changed_old = has_changed; + bool has_changed_old = has_changed_exported; bool has_changed_new = false; var_entry_t *e=0; int done=0; - int is_universal = 0; + int is_universal = 0; if( val && contains( key, L"PWD", L"HOME" ) ) { @@ -935,7 +944,8 @@ int env_set(const wcstring &key, const wchar_t *val, int var_mode) node->exportv=1; } - has_changed = has_changed_old || has_changed_new; + if (has_changed_old || has_changed_new) + mark_changed_exported(); } } @@ -982,7 +992,7 @@ static int try_remove( env_node_t *n, if( v->exportv ) { - has_changed = true; + mark_changed_exported(); } n->env.erase(result); @@ -1279,7 +1289,8 @@ void env_push( int new_scope ) if( new_scope ) { - has_changed |= local_scope_exports(top); + if (local_scope_exports(top)) + mark_changed_exported(); } top = node; @@ -1307,7 +1318,8 @@ void env_pop() if( killme->new_scope ) { - has_changed |= killme->exportv || local_scope_exports( killme->next ); + if (killme->exportv || local_scope_exports( killme->next )) + mark_changed_exported(); } top = top->next; @@ -1318,7 +1330,7 @@ void env_pop() var_entry_t *entry = iter->second; if( entry->exportv ) { - has_changed = true; + mark_changed_exported(); } delete entry; } @@ -1489,7 +1501,7 @@ static void update_export_array_if_necessary(bool recalc) { env_universal_barrier(); } - if( has_changed ) + if( has_changed_exported ) { std::map vals; size_t i; @@ -1515,22 +1527,22 @@ static void update_export_array_if_necessary(bool recalc) { std::vector local_export_buffer; export_func(vals, local_export_buffer ); export_array.set(local_export_buffer); - has_changed=false; + has_changed_exported=false; } } -char **env_export_arr( int recalc ) +char **env_export_arr( bool recalc ) { ASSERT_IS_MAIN_THREAD(); - update_export_array_if_necessary(recalc != 0); + update_export_array_if_necessary(recalc); return export_array.get(); } void env_export_arr(bool recalc, null_terminated_array_t &output) { ASSERT_IS_MAIN_THREAD(); - update_export_array_if_necessary(recalc != 0); + update_export_array_if_necessary(recalc); output = export_array; } diff --git a/env.h b/env.h index bd597f1b9..2c32b6a08 100644 --- a/env.h +++ b/env.h @@ -165,7 +165,7 @@ void env_push( int new_scope ); void env_pop(); /** Returns an array containing all exported variables in a format suitable for execv. */ -char **env_export_arr( int recalc ); +char **env_export_arr( bool recalc ); void env_export_arr(bool recalc, null_terminated_array_t &result); /** @@ -201,5 +201,7 @@ public: extern bool g_log_forks; extern int g_fork_count; +extern bool g_use_posix_spawn; + #endif diff --git a/event.cpp b/event.cpp index 619200edf..176bc0a0d 100644 --- a/event.cpp +++ b/event.cpp @@ -454,7 +454,7 @@ static void event_fire_internal( const event_t *event ) parser_t &parser = parser_t::principal_parser(); parser.push_block( EVENT ); parser.current_block->state1() = event; - parser.eval( buffer, 0, TOP ); + parser.eval( buffer, io_chain_t(), TOP ); parser.pop_block(); proc_pop_interactive(); proc_set_last_status( prev_status ); diff --git a/exec.cpp b/exec.cpp index e20c48263..499250923 100644 --- a/exec.cpp +++ b/exec.cpp @@ -49,6 +49,7 @@ #include "expand.h" #include "signal.h" + #include "parse_util.h" /** @@ -148,24 +149,25 @@ int exec_pipe( int fd[2]) /** Check if the specified fd is used as a part of a pipeline in the specidied set of IO redirections. + This is called after fork(). \param fd the fd to search for - \param io the set of io redirections to search in + \param io_chain the set of io redirections to search in */ -static int use_fd_in_pipe( int fd, io_data_t *io ) -{ - if( !io ) - return 0; - - if( ( io->io_mode == IO_BUFFER ) || - ( io->io_mode == IO_PIPE ) ) - { - if( io->param1.pipe_fd[0] == fd || - io->param1.pipe_fd[1] == fd ) - return 1; - } - - return use_fd_in_pipe( fd, io->next ); +static bool use_fd_in_pipe(int fd, const io_chain_t &io_chain ) +{ + for (size_t idx = 0; idx < io_chain.size(); idx++) + { + const io_data_t *io = io_chain.at(idx); + if( ( io->io_mode == IO_BUFFER ) || + ( io->io_mode == IO_PIPE ) ) + { + if( io->param1.pipe_fd[0] == fd || + io->param1.pipe_fd[1] == fd ) + return true; + } + } + return false; } @@ -177,13 +179,13 @@ static int use_fd_in_pipe( int fd, io_data_t *io ) \param io the list of io redirections for this job. Pipes mentioned here should not be closed. */ -void close_unused_internal_pipes( io_data_t *io ) +void close_unused_internal_pipes( const io_chain_t &io ) { /* A call to exec_close will modify open_fds, so be careful how we walk */ for (size_t i=0; i < open_fds.size(); i++) { if (open_fds[i]) { int fd = (int)i; - if( !use_fd_in_pipe( fd, io) ) + if( !use_fd_in_pipe(fd, io)) { debug( 4, L"Close fd %d, used in other context", fd ); exec_close( fd ); @@ -193,11 +195,24 @@ void close_unused_internal_pipes( io_data_t *io ) } } +void get_unused_internal_pipes(std::vector &fds, const io_chain_t &io) +{ + for (size_t i=0; i < open_fds.size(); i++) { + if (open_fds[i]) { + int fd = (int)i; + if( !use_fd_in_pipe(fd, io)) + { + fds.push_back(fd); + } + } + } +} + /** Returns the interpreter for the specified script. Returns NULL if file is not a script with a shebang. */ -static char *get_interpreter( const char *command, char *interpreter, size_t buff_size ) +char *get_interpreter( const char *command, char *interpreter, size_t buff_size ) { // OK to not use CLO_EXEC here because this is only called after fork int fd = open( command, O_RDONLY ); @@ -239,9 +254,8 @@ static void safe_launch_process( process_t *p, const char *actual_cmd, char **ar // debug( 1, L"exec '%ls'", p->argv[0] ); - execve ( wcs2str(p->actual_cmd.c_str()), - argv, - envv ); + // Wow, this wcs2str call totally allocates memory + execve ( actual_cmd, argv, envv ); err = errno; @@ -275,88 +289,8 @@ static void safe_launch_process( process_t *p, const char *actual_cmd, char **ar } errno = err; - debug_safe( 0, "Failed to execute process '%s'. Reason:", actual_cmd ); - - switch( errno ) - { - - case E2BIG: - { - char sz1[128], sz2[128]; - - long arg_max = -1; - - size_t sz = 0; - char **p; - for(p=argv; *p; p++) - { - sz += strlen(*p)+1; - } - - for(p=envv; *p; p++) - { - sz += strlen(*p)+1; - } - - format_size_safe(sz1, sz); - arg_max = sysconf( _SC_ARG_MAX ); - - if( arg_max > 0 ) - { - format_size_safe(sz2, sz); - debug_safe(0, "The total size of the argument and environment lists %s exceeds the operating system limit of %s.", sz1, sz2); - } - else - { - debug_safe( 0, "The total size of the argument and environment lists (%s) exceeds the operating system limit.", sz1); - } - - debug_safe(0, "Try running the command again with fewer arguments."); - exit_without_destructors(STATUS_EXEC_FAIL); - break; - } - - case ENOEXEC: - { - /* Hope strerror doesn't allocate... */ - const char *err = strerror(errno); - debug_safe(0, "exec: %s", err); - - debug_safe(0, "The file '%ls' is marked as an executable but could not be run by the operating system.", actual_cmd); - exit_without_destructors(STATUS_EXEC_FAIL); - } - - case ENOENT: - { - char interpreter_buff[128] = {}, *interpreter; - interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff); - if( interpreter && 0 != access( interpreter, X_OK ) ) - { - debug_safe(0, "The file '%s' specified the interpreter '%s', which is not an executable command.", actual_cmd, interpreter ); - } - else - { - debug_safe(0, "The file '%s' or a script or ELF interpreter does not exist, or a shared library needed for file or interpreter cannot be found.", actual_cmd); - } - exit_without_destructors(STATUS_EXEC_FAIL); - } - - case ENOMEM: - { - debug_safe(0, "Out of memory"); - exit_without_destructors(STATUS_EXEC_FAIL); - } - - default: - { - /* Hope strerror doesn't allocate... */ - const char *err = strerror(errno); - debug_safe(0, "exec: %s", err); - - // debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd); - exit_without_destructors(STATUS_EXEC_FAIL); - } - } + safe_report_exec_error(errno, actual_cmd, argv, envv); + exit_without_destructors(STATUS_EXEC_FAIL); } /** @@ -368,7 +302,7 @@ static void launch_process_nofork( process_t *p ) ASSERT_IS_NOT_FORKED_CHILD(); char **argv = wcsv2strv(p->get_argv()); - char **envv = env_export_arr( 0 ); + char **envv = env_export_arr( false ); char *actual_cmd = wcs2str(p->actual_cmd.c_str()); /* Bounce to launch_process. This never returns. */ @@ -380,102 +314,9 @@ static void launch_process_nofork( process_t *p ) Check if the IO redirection chains contains redirections for the specified file descriptor */ -static int has_fd( io_data_t *d, int fd ) +static int has_fd( const io_chain_t &d, int fd ) { - return io_get( d, fd ) != 0; -} - - -/** - Free a transmogrified io chain. Only the chain itself and resources - used by a transmogrified IO_FILE redirection are freed, since the - original chain may still be needed. -*/ -static void io_untransmogrify( io_data_t * in, io_data_t *out ) -{ - if( !out ) - return; - assert(in != NULL); - io_untransmogrify( in->next, out->next ); - switch( in->io_mode ) - { - case IO_FILE: - exec_close( out->param1.old_fd ); - break; - } - delete out; -} - - -/** - Make a copy of the specified io redirection chain, but change file - redirection into fd redirection. This makes the redirection chain - suitable for use as block-level io, since the file won't be - repeatedly reopened for every command in the block, which would - reset the cursor position. - - \return the transmogrified chain on sucess, or 0 on failiure -*/ -static io_data_t *io_transmogrify( io_data_t * in ) -{ - ASSERT_IS_MAIN_THREAD(); - if( !in ) - return 0; - - std::auto_ptr out(new io_data_t); - - out->fd = in->fd; - out->io_mode = IO_FD; - out->param2.close_old = 1; - out->next=0; - - switch( in->io_mode ) - { - /* - These redirections don't need transmogrification. They can be passed through. - */ - case IO_FD: - case IO_CLOSE: - case IO_BUFFER: - case IO_PIPE: - { - out.reset(new io_data_t(*in)); - break; - } - - /* - Transmogrify file redirections - */ - case IO_FILE: - { - int fd; - - if( (fd=open( in->filename_cstr, in->param2.flags, OPEN_MASK ) )==-1 ) - { - debug( 1, - FILE_ERROR, - in->filename_cstr ); - - wperror( L"open" ); - return NULL; - } - - out->param1.old_fd = fd; - break; - } - } - - if( in->next) - { - out->next = io_transmogrify( in->next ); - if( !out->next ) - { - io_untransmogrify( in, out.release() ); - return NULL; - } - } - - return out.release(); + return io_chain_get( d, fd ) != NULL; } /** @@ -490,28 +331,17 @@ static io_data_t *io_transmogrify( io_data_t * in ) static void internal_exec_helper( parser_t &parser, const wchar_t *def, enum block_type_t block_type, - io_data_t *io ) + io_chain_t &ios ) { - io_data_t *io_internal = io_transmogrify( io ); int is_block_old=is_block; is_block=1; - - /* - Did the transmogrification fail - if so, set error status and return - */ - if( io && !io_internal ) - { - proc_set_last_status( STATUS_EXEC_FAIL ); - return; - } - + signal_unblock(); - parser.eval( def, io_internal, block_type ); + parser.eval( def, ios, block_type ); signal_block(); - io_untransmogrify( io, io_internal ); job_reap( 0 ); is_block=is_block_old; } @@ -552,7 +382,6 @@ void exec( parser_t &parser, job_t *j ) sigset_t chldset; io_data_t pipe_read, pipe_write; - io_data_t *tmp; io_data_t *io_buffer =0; @@ -577,23 +406,16 @@ void exec( parser_t &parser, job_t *j ) debug( 4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id ); - if( parser.block_io ) + if( ! parser.block_io.empty() ) { - if( j->io ) - { - j->io = io_add( io_duplicate(parser.block_io), j->io ); - } - else - { - j->io=io_duplicate(parser.block_io); - } + io_duplicate_append(parser.block_io, j->io); } - - io_data_t *input_redirect; - - for( input_redirect = j->io; input_redirect; input_redirect = input_redirect->next ) + const io_data_t *input_redirect = NULL; + for (size_t idx = 0; idx < j->io.size(); idx++) { + input_redirect = j->io.at(idx); + if( (input_redirect->io_mode == IO_BUFFER) && input_redirect->is_input ) { @@ -647,11 +469,9 @@ void exec( parser_t &parser, job_t *j ) pipe_write.io_mode=IO_PIPE; pipe_write.is_input = 0; - pipe_read.next=0; - pipe_write.next=0; pipe_write.param1.pipe_fd[0]=pipe_write.param1.pipe_fd[1]=-1; - j->io = io_add( j->io, &pipe_write ); + j->io.push_back(&pipe_write); signal_block(); @@ -737,7 +557,7 @@ void exec( parser_t &parser, job_t *j ) uniprocessor systems. */ if( p->type == EXTERNAL ) - env_export_arr( 1 ); + env_export_arr( true ); /* @@ -746,7 +566,7 @@ void exec( parser_t &parser, job_t *j ) if( p == j->first_process->next ) { - j->io = io_add( j->io, &pipe_read ); + j->io.push_back(&pipe_read); } if( p_wants_pipe ) @@ -769,8 +589,9 @@ void exec( parser_t &parser, job_t *j ) This is the last element of the pipeline. Remove the io redirection for pipe output. */ - j->io = io_remove( j->io, &pipe_write ); - + io_chain_t::iterator where = std::find(j->io.begin(), j->io.end(), &pipe_write); + if (where != j->io.end()) + j->io.erase(where); } switch( p->type ) @@ -825,8 +646,8 @@ void exec( parser_t &parser, job_t *j ) if( p->next ) { - io_buffer = io_buffer_create( 0 ); - j->io = io_add( j->io, io_buffer ); + io_buffer = io_buffer_create( 0 ); + j->io.push_back(io_buffer); } internal_exec_helper( parser, def, TOP, j->io ); @@ -842,8 +663,8 @@ void exec( parser_t &parser, job_t *j ) { if( p->next ) { - io_buffer = io_buffer_create( 0 ); - j->io = io_add( j->io, io_buffer ); + io_buffer = io_buffer_create( 0 ); + j->io.push_back(io_buffer); } internal_exec_helper( parser, p->argv0(), TOP, j->io ); @@ -864,7 +685,7 @@ void exec( parser_t &parser, job_t *j ) */ if( p == j->first_process ) { - io_data_t *in = io_get( j->io, 0 ); + const io_data_t *in = io_chain_get( j->io, 0 ); if( in ) { @@ -1040,7 +861,7 @@ void exec( parser_t &parser, job_t *j ) break; } - j->io = io_remove( j->io, io_buffer ); + io_remove( j->io, io_buffer ); io_buffer_read( io_buffer ); @@ -1159,7 +980,7 @@ void exec( parser_t &parser, job_t *j ) performance quite a bit in complex completion code. */ - io_data_t *io = io_get( j->io, 1 ); + io_data_t *io = io_chain_get( j->io, 1 ); bool buffer_stdout = io && io->io_mode == IO_BUFFER; if( ( get_stderr_buffer().empty() ) && @@ -1172,10 +993,10 @@ void exec( parser_t &parser, job_t *j ) skip_fork = 1; } - if (! skip_fork && ! j->io) { + if (! skip_fork && j->io.empty()) { /* PCA for some reason, fish forks a lot, even for basic builtins like echo just to write out their buffers. I'm certain a lot of this is unnecessary, but I am not sure exactly when. If j->io is NULL, then it means there's no pipes or anything, so we can certainly just write out our data. Beyond that, we may be able to do the same if io_get returns 0 for STDOUT_FILENO and STDERR_FILENO. */ if (g_log_forks) { - printf("fork #-: Skipping fork for internal builtin for '%ls' (io is %p, job_io is %p)\n", p->argv0(), io, j->io); + printf("fork #-: Skipping fork for internal builtin for '%ls'\n", p->argv0()); } const wcstring &out = get_stdout_buffer(), &err = get_stderr_buffer(); char *outbuff = wcs2str(out.c_str()), *errbuff = wcs2str(err.c_str()); @@ -1185,8 +1006,9 @@ void exec( parser_t &parser, job_t *j ) skip_fork = 1; } - for( io_data_t *tmp_io = j->io; tmp_io != NULL; tmp_io=tmp_io->next ) + for( io_chain_t::iterator iter = j->io.begin(); iter != j->io.end(); iter++ ) { + io_data_t *tmp_io = *iter; if( tmp_io->io_mode == IO_FILE && strcmp(tmp_io->filename_cstr, "/dev/null") != 0) { skip_fork = 0; @@ -1218,8 +1040,8 @@ void exec( parser_t &parser, job_t *j ) fflush(stdout); fflush(stderr); if (g_log_forks) { - printf("fork #%d: Executing fork for internal builtin for '%ls' (io is %p, job_io is %p)\n", g_fork_count, p->argv0(), io, j->io); - io_print(io); + printf("fork #%d: Executing fork for internal builtin for '%ls'\n", g_fork_count, p->argv0()); + io_print(io_chain_t(io)); } pid = execute_fork(false); if( pid == 0 ) @@ -1274,33 +1096,65 @@ void exec( parser_t &parser, job_t *j ) const wchar_t *file = reader_current_filename(); const wchar_t *func = parser_t::principal_parser().is_function(); printf("fork #%d: forking for '%s' in '%ls:%ls'\n", g_fork_count, actual_cmd, file ? file : L"", func ? func : L"?"); + + fprintf(stderr, "IO chain for %s:\n", actual_cmd); + io_print(j->io); + } + +#if FISH_USE_POSIX_SPAWN + /* Prefer to use posix_spawn, since it's faster on some systems like OS X */ + bool use_posix_spawn = g_use_posix_spawn; + if (use_posix_spawn) + { + /* Create posix spawn attributes and actions */ + posix_spawnattr_t attr = posix_spawnattr_t(); + posix_spawn_file_actions_t actions = posix_spawn_file_actions_t(); + bool made_it = fork_actions_make_spawn_properties(&attr, &actions, j, p); + if (made_it) + { + /* We successfully made the attributes and actions; actually call posix_spawn */ + int spawn_ret = posix_spawn(&pid, actual_cmd, &actions, &attr, argv, envv); + if (spawn_ret != 0) + { + safe_report_exec_error(spawn_ret, actual_cmd, argv, envv); + /* Make sure our pid isn't set */ + pid = 0; + } + + /* Clean up our actions */ + posix_spawn_file_actions_destroy(&actions); + posix_spawnattr_destroy(&attr); + } + } + else +#endif + { + pid = execute_fork(false); + if (pid == 0) + { + /* + This is the child process. + */ + p->pid = getpid(); + setup_child_process( j, p ); + safe_launch_process( p, actual_cmd, argv, envv ); + + /* + safe_launch_process _never_ returns... + */ + } } - pid = execute_fork(false); - if( pid == 0 ) - { - /* - This is the child process. - */ - p->pid = getpid(); - setup_child_process( j, p ); - safe_launch_process( p, actual_cmd, argv, envv ); - - /* - safe_launch_process _never_ returns... - */ - } - else - { - /* - This is the parent process. Store away - information on the child, and possibly fice - it control over the terminal. - */ - p->pid = pid; - set_child_group( j, p, 0 ); - - } + + /* + This is the parent process. Store away + information on the child, and possibly fice + it control over the terminal. + */ + p->pid = pid; + + set_child_group( j, p, 0 ); + break; } @@ -1348,10 +1202,12 @@ void exec( parser_t &parser, job_t *j ) debug( 3, L"Job is constructed" ); - j->io = io_remove( j->io, &pipe_read ); - - for( tmp = parser.block_io; tmp; tmp=tmp->next ) - j->io = io_remove( j->io, tmp ); + io_remove( j->io, &pipe_read ); + + for (io_chain_t::const_iterator iter = parser.block_io.begin(); iter != parser.block_io.end(); iter++) + { + io_remove( j->io, *iter ); + } job_set_flag( j, JOB_CONSTRUCTED, 1 ); @@ -1400,7 +1256,7 @@ static int exec_subshell_internal( const wcstring &cmd, wcstring_list_t *lst ) prev_status = proc_get_last_status(); parser_t &parser = parser_t::principal_parser(); - if( parser.eval( cmd, io_buffer, SUBST ) ) + if( parser.eval( cmd, io_chain_t(io_buffer), SUBST ) ) { status = -1; } diff --git a/exec.h b/exec.h index 4fa4b10c6..97342f114 100644 --- a/exec.h +++ b/exec.h @@ -71,6 +71,12 @@ void exec_close( int fd ); int exec_pipe( int fd[2]); /* Close all fds in open_fds. This is called from postfork.cpp */ -void close_unused_internal_pipes( io_data_t *io ); +void close_unused_internal_pipes( const io_chain_t &io ); + +/* Gets all unused internal pipes into fds */ +void get_unused_internal_pipes(std::vector &fds, const io_chain_t &io); + +/** Gets the interpreter for a given command */ +char *get_interpreter( const char *command, char *interpreter, size_t buff_size ); #endif diff --git a/fish.cpp b/fish.cpp index 5e8c6b62d..baf8a7741 100644 --- a/fish.cpp +++ b/fish.cpp @@ -216,8 +216,10 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0) static int read_init(const struct config_paths_t &paths) { parser_t &parser = parser_t::principal_parser(); - parser.eval( L"builtin . " + paths.data + L"/config.fish 2>/dev/null", 0, TOP ); - parser.eval( L"builtin . " + paths.sysconf + L"/config.fish 2>/dev/null", 0, TOP ); + const io_chain_t empty_ios; + parser.eval( L"builtin . " + paths.data + L"/config.fish 2>/dev/null", empty_ios, TOP ); + parser.eval( L"builtin . " + paths.sysconf + L"/config.fish 2>/dev/null", empty_ios, TOP ); + /* We need to get the configuration directory before we can source the user configuration file @@ -232,7 +234,7 @@ static int read_init(const struct config_paths_t &paths) { wcstring config_dir_escaped = escape_string( config_dir, 1 ); wcstring eval_buff = format_string(L"builtin . %ls/config.fish 2>/dev/null", config_dir_escaped.c_str()); - parser.eval( eval_buff, 0, TOP ); + parser.eval( eval_buff, empty_ios, TOP ); } return 1; @@ -453,12 +455,13 @@ int main( int argc, char **argv ) if (g_log_forks) printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); + const io_chain_t empty_ios; if( read_init(paths) ) { if( cmd != 0 ) { wchar_t *cmd_wcs = str2wcs( cmd ); - res = parser.eval( cmd_wcs, 0, TOP ); + res = parser.eval( cmd_wcs, empty_ios, TOP ); free(cmd_wcs); reader_exit(0, 0); } @@ -466,7 +469,7 @@ int main( int argc, char **argv ) { if( my_optind == argc ) { - res = reader_read( STDIN_FILENO, 0 ); + res = reader_read( STDIN_FILENO, empty_ios ); } else { @@ -511,7 +514,7 @@ int main( int argc, char **argv ) free( rel_filename ); free( abs_filename ); - res = reader_read( fd, 0 ); + res = reader_read( fd, empty_ios ); if( res ) { diff --git a/fish_tests.cpp b/fish_tests.cpp index e682acdb7..a5bddfa23 100644 --- a/fish_tests.cpp +++ b/fish_tests.cpp @@ -451,7 +451,7 @@ static void test_parser() err( L"Null input when evaluating undetected" ); } #endif - if( !parser.eval( L"ls", 0, WHILE ) ) + if( !parser.eval( L"ls", io_chain_t(), WHILE ) ) { err( L"Invalid block mode when evaluating undetected" ); } diff --git a/input.cpp b/input.cpp index 0ae04c547..38164d21e 100644 --- a/input.cpp +++ b/input.cpp @@ -411,7 +411,7 @@ static wint_t input_exec_binding( const input_mapping_t &m, const wcstring &seq */ int last_status = proc_get_last_status(); - parser_t::principal_parser().eval( m.command.c_str(), 0, TOP ); + parser_t::principal_parser().eval( m.command.c_str(), io_chain_t(), TOP ); proc_set_last_status( last_status ); diff --git a/io.cpp b/io.cpp index 10648c5bc..176fb2a34 100644 --- a/io.cpp +++ b/io.cpp @@ -12,6 +12,8 @@ Utilities for io redirection. #include #include #include +#include +#include #ifdef HAVE_SYS_TERMIOS_H #include @@ -98,20 +100,20 @@ void io_buffer_read( io_data_t *d ) } -io_data_t *io_buffer_create( int is_input ) +io_data_t *io_buffer_create( bool is_input ) { - std::auto_ptr buffer_redirect(new io_data_t); + bool success = true; + io_data_t *buffer_redirect = new io_data_t; buffer_redirect->out_buffer_create(); - buffer_redirect->io_mode=IO_BUFFER; - buffer_redirect->next=0; - buffer_redirect->is_input = is_input; + buffer_redirect->io_mode = IO_BUFFER; + buffer_redirect->is_input = is_input ? true : false; buffer_redirect->fd=is_input?0:1; if( exec_pipe( buffer_redirect->param1.pipe_fd ) == -1 ) { debug( 1, PIPE_ERROR ); wperror (L"pipe"); - return NULL; + success = false; } else if( fcntl( buffer_redirect->param1.pipe_fd[0], F_SETFL, @@ -119,9 +121,16 @@ io_data_t *io_buffer_create( int is_input ) { debug( 1, PIPE_ERROR ); wperror( L"fcntl" ); - return NULL; + success = false; } - return buffer_redirect.release(); + + if (! success) + { + delete buffer_redirect; + buffer_redirect = NULL; + } + + return buffer_redirect; } void io_buffer_destroy( io_data_t *io_buffer ) @@ -145,98 +154,146 @@ void io_buffer_destroy( io_data_t *io_buffer ) delete io_buffer; } - - -io_data_t *io_add( io_data_t *list, io_data_t *element ) +void io_chain_t::remove(const io_data_t *element) { - io_data_t *curr = list; - if( curr == 0 ) - return element; - while( curr->next != 0 ) - curr = curr->next; - curr->next = element; - return list; + // See if you can guess why std::find doesn't work here + for (io_chain_t::iterator iter = this->begin(); iter != this->end(); ++iter) + { + if (*iter == element) + { + this->erase(iter); + break; + } + } } -io_data_t *io_remove( io_data_t *list, io_data_t *element ) +io_chain_t io_chain_t::duplicate() const { - io_data_t *curr, *prev=0; - for( curr=list; curr; curr = curr->next ) - { - if( element == curr ) - { - if( prev == 0 ) - { - io_data_t *tmp = element->next; - element->next = 0; - return tmp; - } - else - { - prev->next = element->next; - element->next = 0; - return list; - } - } - prev = curr; - } - return list; + io_chain_t result; + result.reserve(this->size()); + for (io_chain_t::const_iterator iter = this->begin(); iter != this->end(); iter++) + { + const io_data_t *io = *iter; + result.push_back(new io_data_t(*io)); + } + return result; } -io_data_t *io_duplicate( io_data_t *l ) +void io_chain_t::duplicate_append(const io_chain_t &src) { - io_data_t *res; - - if( l == 0 ) - return 0; - - res = new io_data_t(*l); - res->next=io_duplicate(l->next ); - return res; + this->reserve(this->size() + src.size()); + for (size_t idx = 0; idx < src.size(); idx++) + { + const io_data_t *src_data = src.at(idx); + this->push_back(new io_data_t(*src_data)); + } } -io_data_t *io_get( io_data_t *io, int fd ) +void io_chain_t::destroy() { - if( io == NULL ) - return 0; - - io_data_t *res = io_get( io->next, fd ); - if( res ) - return res; - - if( io->fd == fd ) - return io; - - return 0; + for (size_t idx = 0; idx < this->size(); idx++) + { + delete this->at(idx); + } + this->clear(); } - -void io_print( io_data_t *io ) +void io_remove(io_chain_t &list, const io_data_t *element) +{ + list.remove(element); +} + +io_chain_t io_duplicate(const io_chain_t &chain) +{ + return chain.duplicate(); +} + +void io_print(const io_chain_t &chain) +{ + if (chain.empty()) + { + fprintf(stderr, "Empty chain %p\n", &chain); + return; + } + + fprintf(stderr, "Chain %p (%ld items):\n", &chain, (long)chain.size()); + for (size_t i=0; i < chain.size(); i++) { + const io_data_t *io = chain.at(i); + fprintf(stderr, "\t%lu: fd:%d, input:%s, ", (unsigned long)i, io->fd, io->is_input ? "yes" : "no"); + switch (io->io_mode) + { + case IO_FILE: + fprintf(stderr, "file (%s)\n", io->filename_cstr); + break; + case IO_PIPE: + fprintf(stderr, "pipe {%d, %d}\n", io->param1.pipe_fd[0], io->param1.pipe_fd[1]); + break; + case IO_FD: + fprintf(stderr, "FD map %d -> %d\n", io->param1.old_fd, io->fd); + break; + case IO_BUFFER: + fprintf(stderr, "buffer %p (size %lu)\n", io->out_buffer_ptr(), io->out_buffer_size()); + break; + case IO_CLOSE: + fprintf(stderr, "close %d\n", io->fd); + break; + } + } +} + +void io_duplicate_append( const io_chain_t &src, io_chain_t &dst ) +{ + return dst.duplicate_append(src); +} + +void io_chain_destroy(io_chain_t &chain) +{ + chain.destroy(); +} + +/* Return the last IO for the given fd */ +const io_data_t *io_chain_t::get_io_for_fd(int fd) const +{ + size_t idx = this->size(); + while (idx--) + { + const io_data_t *data = this->at(idx); + if (data->fd == fd) { + return data; + } + } + return NULL; +} + +io_data_t *io_chain_t::get_io_for_fd(int fd) +{ + size_t idx = this->size(); + while (idx--) + { + io_data_t *data = this->at(idx); + if (data->fd == fd) { + return data; + } + } + return NULL; +} + +/* The old function returned the last match, so we mimic that. */ +const io_data_t *io_chain_get(const io_chain_t &src, int fd) +{ + return src.get_io_for_fd(fd); +} + +io_data_t *io_chain_get(io_chain_t &src, int fd) +{ + return src.get_io_for_fd(fd); +} + +io_chain_t::io_chain_t(io_data_t *data) : std::vector(1, data) +{ + +} + +io_chain_t::io_chain_t() : std::vector() { - if( !io ) - { - return; - } - - debug( 1, L"IO fd %d, type ", io->fd ); - switch( io->io_mode ) - { - case IO_PIPE: - debug( 1, L"PIPE, data %d", io->param1.pipe_fd[io->fd?1:0] ); - break; - - case IO_FD: - debug( 1, L"FD, copy %d", io->param1.old_fd ); - break; - - case IO_BUFFER: - debug( 1, L"BUFFER" ); - break; - - default: - debug( 1, L"OTHER" ); - } - - io_print( io->next ); - } diff --git a/io.h b/io.h index 49bbbafa8..109c1a5ab 100644 --- a/io.h +++ b/io.h @@ -73,7 +73,12 @@ public: /** Function to get a pointer to the buffer */ char *out_buffer_ptr(void) { assert(out_buffer.get() != NULL); - return (out_buffer->size() == 0) ? NULL : &out_buffer->at(0); + return out_buffer->empty() ? NULL : &out_buffer->at(0); + } + + const char *out_buffer_ptr(void) const { + assert(out_buffer.get() != NULL); + return out_buffer->empty() ? NULL : &out_buffer->at(0); } /** Function to get the size of the buffer */ @@ -83,11 +88,8 @@ public: } /** Set to true if this is an input io redirection */ - int is_input; + bool is_input; - /** Pointer to the next IO redirection */ - io_data_t *next; - io_data_t() : out_buffer(), io_mode(0), @@ -95,8 +97,7 @@ public: param1(), param2(), filename_cstr(NULL), - is_input(0), - next(NULL) + is_input(0) { } @@ -107,10 +108,8 @@ public: param1(rhs.param1), param2(rhs.param2), filename_cstr(rhs.filename_cstr ? strdup(rhs.filename_cstr) : NULL), - is_input(rhs.is_input), - next(rhs.next) + is_input(rhs.is_input) { - } ~io_data_t() { @@ -118,27 +117,44 @@ public: } }; - -/** - Join two chains of io redirections -*/ -io_data_t *io_add( io_data_t *first_chain, io_data_t *decond_chain ); +class io_chain_t : public std::vector { +public: + io_chain_t(); + io_chain_t(io_data_t *); + + void remove(const io_data_t *element); + io_chain_t duplicate() const; + void duplicate_append(const io_chain_t &src); + void destroy(); + + const io_data_t *get_io_for_fd(int fd) const; + io_data_t *get_io_for_fd(int fd); + + +}; /** Remove the specified io redirection from the chain */ -io_data_t *io_remove( io_data_t *list, io_data_t *element ); +void io_remove(io_chain_t &list, const io_data_t *element); -/** - Make a copy of the specified chain of redirections. Uses operator new. -*/ -io_data_t *io_duplicate( io_data_t *l ); +/** Make a copy of the specified chain of redirections. Uses operator new. */ +io_chain_t io_duplicate(const io_chain_t &chain); + +/** Return a shallow copy of the specified chain of redirections that contains only the applicable redirections. That is, if there's multiple redirections for the same fd, only the second one is included. */ +io_chain_t io_unique(const io_chain_t &chain); + +/** Appends a copy of the specified 'src' chain of redirections to 'dst.' Uses operator new. */ +void io_duplicate_append( const io_chain_t &src, io_chain_t &dst ); + +/** Destroys an io_chain */ +void io_chain_destroy(io_chain_t &chain); /** Return the last io redirection in the chain for the specified file descriptor. */ -io_data_t *io_get( io_data_t *io, int fd ); - +const io_data_t *io_chain_get(const io_chain_t &src, int fd); +io_data_t *io_chain_get(io_chain_t &src, int fd); /** @@ -155,16 +171,14 @@ void io_buffer_destroy( io_data_t *io_buffer ); used to buffer the output of a command, or non-zero to buffer the input to a command. */ -io_data_t *io_buffer_create( int is_input ); +io_data_t *io_buffer_create( bool is_input ); /** Close output pipe, and read from input pipe until eof. */ void io_buffer_read( io_data_t *d ); -/** - Print debug information about the specified IO redirection chain to stderr. -*/ -void io_print( io_data_t *io ); +/** Print debug information about the specified IO redirection chain to stderr. */ +void io_print( const io_chain_t &chain ); #endif diff --git a/osx/config.h b/osx/config.h index f606d92fe..a72a8825d 100644 --- a/osx/config.h +++ b/osx/config.h @@ -1,9 +1,6 @@ /* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ -#ifndef FISH_CONFIG_H -#define FISH_CONFIG_H - /* Define to 1 if you have the `backtrace' function. */ #define HAVE_BACKTRACE 1 @@ -76,6 +73,9 @@ /* Define to 1 if you have the header file. */ /* #undef HAVE_SIGINFO_H */ +/* Define to 1 if you have the header file. */ +#define HAVE_SPAWN_H 1 + /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 @@ -219,5 +219,3 @@ #define __warn_unused #define __sentinel #endif - -#endif diff --git a/parser.cpp b/parser.cpp index 3e95ee8e9..d701d56ad 100644 --- a/parser.cpp +++ b/parser.cpp @@ -1541,7 +1541,7 @@ void parser_t::parse_job_argument_list( process_t *p, tok_get_desc( tok_last_type(tok)) ); } - if( ! has_target || target.size() == 0 ) + if( ! has_target || target.empty() ) { if( error_code == 0 ) error( SYNTAX_ERROR, @@ -1611,7 +1611,7 @@ void parser_t::parse_job_argument_list( process_t *p, } } - j->io = io_add( j->io, new_io.release() ); + j->io.push_back(new_io.release()); } break; @@ -2488,14 +2488,18 @@ void parser_t::eval_job( tokenizer *tok ) } -int parser_t::eval( const wcstring &cmdStr, io_data_t *io, enum block_type_t block_type ) +int parser_t::eval( const wcstring &cmdStr, const io_chain_t &io, enum block_type_t block_type ) { const wchar_t * const cmd = cmdStr.c_str(); size_t forbid_count; int code; tokenizer *previous_tokenizer=current_tokenizer; block_t *start_current_block = current_block; - io_data_t *prev_io = block_io; + + /* Record the current chain so we can put it back later */ + const io_chain_t prev_io = block_io; + block_io = io; + std::vector prev_forbidden = forbidden_function; if( block_type == SUBST ) diff --git a/parser.h b/parser.h index b74a239ca..272008335 100644 --- a/parser.h +++ b/parser.h @@ -347,7 +347,7 @@ class parser_t { event_block_list_t global_event_blocks; /** Current block level io redirections */ - io_data_t *block_io; + io_chain_t block_io; /** Evaluate the expressions contained in cmd. @@ -358,7 +358,7 @@ class parser_t { \return 0 on success, 1 otherwise */ - int eval( const wcstring &cmd, io_data_t *io, enum block_type_t block_type ); + int eval( const wcstring &cmdStr, const io_chain_t &io, enum block_type_t block_type ); /** Evaluate line as a list of parameters, i.e. tokenize it and perform parameter expansion and cmdsubst execution on the tokens. diff --git a/postfork.cpp b/postfork.cpp index 7b210ff48..07842cb09 100644 --- a/postfork.cpp +++ b/postfork.cpp @@ -9,7 +9,6 @@ #include "iothread.h" #include "exec.h" - /** The number of times to try to call fork() before giving up */ #define FORK_LAPS 5 @@ -102,39 +101,49 @@ int set_child_group( job_t *j, process_t *p, int print_errors ) return res; } -/** Make sure the fd used by this redirection is not used by i.e. a pipe. */ -static void free_fd( io_data_t *io, int fd ) +/** Make sure the fd used by each redirection is not used by a pipe. */ +static void free_redirected_fds_from_pipes(io_chain_t &io_chain) { - if( !io ) - return; - - if( ( io->io_mode == IO_PIPE ) || ( io->io_mode == IO_BUFFER ) ) - { - int i; - for( i=0; i<2; i++ ) - { - if(io->param1.pipe_fd[i] == fd ) - { - while(1) - { - if( (io->param1.pipe_fd[i] = dup(fd)) == -1) - { - if( errno != EINTR ) - { - debug_safe_int( 1, FD_ERROR, fd ); - wperror( L"dup" ); - FATAL_EXIT(); - } - } - else - { - break; - } - } - } - } + size_t max = io_chain.size(); + for (size_t i = 0; i < max; i++) + { + int fd_to_free = io_chain.at(i)->fd; + + /* We only have to worry about fds beyond the three standard ones */ + if (fd_to_free <= 2) + continue; + + /* Make sure the fd is not used by a pipe */ + for (size_t j = 0; j < max; j++) + { + /* We're only interested in pipes */ + io_data_t *possible_conflict = io_chain.at(j); + if (possible_conflict->io_mode != IO_PIPE && possible_conflict->io_mode != IO_BUFFER) + continue; + + /* If the pipe is a conflict, dup it to some other value */ + for (int k=0; k<2; k++) + { + /* If it's not a conflict, we don't care */ + if (possible_conflict->param1.pipe_fd[k] != fd_to_free) + continue; + + /* Repeat until we have a replacement fd */ + int replacement_fd = -1; + while (replacement_fd < 0) + { + replacement_fd = dup(fd_to_free); + if (replacement_fd == -1 && errno != EINTR) + { + debug_safe_int( 1, FD_ERROR, fd_to_free ); + wperror( L"dup" ); + FATAL_EXIT(); + } + } + possible_conflict->param1.pipe_fd[k] = replacement_fd; + } + } } - free_fd( io->next, fd ); } @@ -150,25 +159,24 @@ static void free_fd( io_data_t *io, int fd ) \return 0 on sucess, -1 on failiure */ -static int handle_child_io( io_data_t *io ) +static int handle_child_io( io_chain_t &io_chain ) { - close_unused_internal_pipes( io ); - - for( ; io; io=io->next ) + close_unused_internal_pipes( io_chain ); + free_redirected_fds_from_pipes(io_chain); + for (size_t idx = 0; idx < io_chain.size(); idx++) { + io_data_t *io = io_chain.at(idx); int tmp; + + /* If this is not the last IO redirection for this fd, then skip it. This comes about because of the funky way in which we list IO redirections: every process in a job gets the input and output redirections, even internal ones. For example, in 'cat < foo | cat | cat > bar', the middle cat sees both < foo and > bar. It also gets pipes for its fd 0 and 1, which appear after in the list. */ + if (io != io_chain.get_io_for_fd(io->fd)) + continue; if( io->io_mode == IO_FD && io->fd == io->param1.old_fd ) { continue; } - - if( io->fd > 2 ) - { - /* Make sure the fd used by this redirection is not used by e.g. a pipe. */ - free_fd( io, io->fd ); - } switch( io->io_mode ) { @@ -184,7 +192,7 @@ static int handle_child_io( io_data_t *io ) case IO_FILE: { - // Here we definitely do not want to set CLO_EXEC because our child needs access + // Here we definitely do not want to set CLO_EXEC because our child needs access if( (tmp=open( io->filename_cstr, io->param2.flags, OPEN_MASK ) )==-1 ) { @@ -240,6 +248,7 @@ static int handle_child_io( io_data_t *io ) case IO_BUFFER: case IO_PIPE: { + /* If write_pipe_idx is 0, it means we're connecting to the read end (first pipe fd). If it's 1, we're connecting to the write end (second pipe fd). */ unsigned int write_pipe_idx = (io->is_input ? 0 : 1); /* debug( 0, @@ -256,16 +265,11 @@ static int handle_child_io( io_data_t *io ) perror( "dup2" ); return -1; } - - if( write_pipe_idx > 0 ) - { - exec_close( io->param1.pipe_fd[0]); - exec_close( io->param1.pipe_fd[1]); - } - else - { - exec_close( io->param1.pipe_fd[0] ); - } + + if (io->param1.pipe_fd[0] >= 0) + exec_close( io->param1.pipe_fd[0]); + if (io->param1.pipe_fd[1] >= 0) + exec_close( io->param1.pipe_fd[1]); break; } @@ -304,8 +308,7 @@ int setup_child_process( job_t *j, process_t *p ) /* Remove all signal blocks */ signal_unblock(); - return ok ? 0 : -1; - + return ok ? 0 : -1; } int g_fork_count = 0; @@ -362,3 +365,239 @@ pid_t execute_fork(bool wait_for_threads_to_die) FATAL_EXIT(); return 0; } + +#if FISH_USE_POSIX_SPAWN +bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p) +{ + /* Initialize the output */ + if (posix_spawnattr_init(attr) != 0) { + return false; + } + + if (posix_spawn_file_actions_init(actions) != 0) { + posix_spawnattr_destroy(attr); + return false; + } + + bool should_set_parent_group_id = false; + int desired_parent_group_id = 0; + if (job_get_flag(j, JOB_CONTROL)) + { + should_set_parent_group_id = true; + + // PCA: I'm quite fuzzy on process groups, + // but I believe that the default value of 0 + // means that the process becomes its own + // group leader, which is what set_child_group did + // in this case. So we want this to be 0 if j->pgid is 0. + desired_parent_group_id = j->pgid; + } + + /* Set the handling for job control signals back to the default. */ + bool reset_signal_handlers = true; + + /* Remove all signal blocks */ + bool reset_sigmask = true; + + /* Set our flags */ + short flags = 0; + if (reset_signal_handlers) + flags |= POSIX_SPAWN_SETSIGDEF; + if (reset_sigmask) + flags |= POSIX_SPAWN_SETSIGMASK; + if (should_set_parent_group_id) + flags |= POSIX_SPAWN_SETPGROUP; + + int err = 0; + if (! err) + err = posix_spawnattr_setflags(attr, flags); + + /* Everybody gets default handlers */ + if (! err && reset_signal_handlers) + { + sigset_t sigdefault; + get_signals_with_handlers(&sigdefault); + err = posix_spawnattr_setsigdefault(attr, &sigdefault); + } + + /* No signals blocked */ + sigset_t sigmask; + sigemptyset(&sigmask); + if (! err && reset_sigmask) + err = posix_spawnattr_setsigmask(attr, &sigmask); + + /* Make sure that our pipes don't use an fd that the redirection itself wants to use */ + free_redirected_fds_from_pipes(j->io); + + /* Close unused internal pipes */ + std::vector files_to_close; + get_unused_internal_pipes(files_to_close, j->io); + for (size_t i = 0; ! err && i < files_to_close.size(); i++) + { + err = posix_spawn_file_actions_addclose(actions, files_to_close.at(i)); + } + + for (size_t idx = 0; idx < j->io.size(); idx++) + { + const io_data_t *io = j->io.at(idx); + + /* If this is not the last IO redirection for this fd, then skip it. This comes about because of the funky way in which we list IO redirections: every process in a job gets the input and output redirections, even internal ones. For example, in 'cat < foo | cat | cat > bar', the middle cat sees both < foo and > bar. It also gets pipes for its fd 0 and 1, which appear after in the list. */ + if (io != j->io.get_io_for_fd(io->fd)) + continue; + + if( io->io_mode == IO_FD && io->fd == io->param1.old_fd ) + { + continue; + } + + if( io->fd > 2 ) + { + /* Make sure the fd used by this redirection is not used by e.g. a pipe. */ + // free_fd(io_chain, io->fd ); + // PCA I don't think we need to worry about this. fd redirection is pretty uncommon anyways. + } + switch (io->io_mode) + { + case IO_CLOSE: + { + if (! err) + err = posix_spawn_file_actions_addclose(actions, io->fd); + break; + } + + case IO_FILE: + { + if (! err) + err = posix_spawn_file_actions_addopen(actions, io->fd, io->filename_cstr, io->param2.flags /* mode */, OPEN_MASK); + break; + } + + case IO_FD: + { + if (! err) + err = posix_spawn_file_actions_adddup2(actions, io->param1.old_fd /* from */, io->fd /* to */); + break; + } + + case IO_BUFFER: + case IO_PIPE: + { + unsigned int write_pipe_idx = (io->is_input ? 0 : 1); + int from_fd = io->param1.pipe_fd[write_pipe_idx]; + int to_fd = io->fd; + if (! err) + err = posix_spawn_file_actions_adddup2(actions, from_fd, to_fd); + + + if( write_pipe_idx > 0 ) + { + if (! err) + err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]); + if (! err) + err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[1]); + } + else + { + if (! err) + err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]); + + } + break; + } + } + } + + /* Clean up on error */ + if (err) { + posix_spawnattr_destroy(attr); + posix_spawn_file_actions_destroy(actions); + } + + return ! err; +} +#endif //FISH_USE_POSIX_SPAWN + +void safe_report_exec_error(int err, const char *actual_cmd, char **argv, char **envv) +{ + debug_safe( 0, "Failed to execute process '%s'. Reason:", actual_cmd ); + + switch( err ) + { + + case E2BIG: + { + char sz1[128], sz2[128]; + + long arg_max = -1; + + size_t sz = 0; + char **p; + for(p=argv; *p; p++) + { + sz += strlen(*p)+1; + } + + for(p=envv; *p; p++) + { + sz += strlen(*p)+1; + } + + format_size_safe(sz1, sz); + arg_max = sysconf( _SC_ARG_MAX ); + + if( arg_max > 0 ) + { + format_size_safe(sz2, sz); + debug_safe(0, "The total size of the argument and environment lists %s exceeds the operating system limit of %s.", sz1, sz2); + } + else + { + debug_safe( 0, "The total size of the argument and environment lists (%s) exceeds the operating system limit.", sz1); + } + + debug_safe(0, "Try running the command again with fewer arguments."); + break; + } + + case ENOEXEC: + { + /* Hope strerror doesn't allocate... */ + const char *err = strerror(errno); + debug_safe(0, "exec: %s", err); + + debug_safe(0, "The file '%s' is marked as an executable but could not be run by the operating system.", actual_cmd); + break; + } + + case ENOENT: + { + char interpreter_buff[128] = {}, *interpreter; + interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff); + if( interpreter && 0 != access( interpreter, X_OK ) ) + { + debug_safe(0, "The file '%s' specified the interpreter '%s', which is not an executable command.", actual_cmd, interpreter ); + } + else + { + debug_safe(0, "The file '%s' or a script or ELF interpreter does not exist, or a shared library needed for file or interpreter cannot be found.", actual_cmd); + } + break; + } + + case ENOMEM: + { + debug_safe(0, "Out of memory"); + break; + } + + default: + { + /* Hope strerror doesn't allocate... */ + const char *err = strerror(errno); + debug_safe(0, "exec: %s", err); + + // debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd); + break; + } + } +} diff --git a/postfork.h b/postfork.h index 741e6369c..9acd4c86c 100644 --- a/postfork.h +++ b/postfork.h @@ -19,6 +19,15 @@ #include "wutil.h" #include "io.h" +#if HAVE_SPAWN_H +#include +#endif + +#ifndef FISH_USE_POSIX_SPAWN + #define FISH_USE_POSIX_SPAWN HAVE_SPAWN_H +#endif + + /** This function should be called by both the parent process and the child right after fork() has been called. If job control is @@ -44,6 +53,7 @@ int set_child_group( job_t *j, process_t *p, int print_errors ); \param j the job to set up the IO for \param p the child process to set up + \param io_chain the IO chain to use (ignores the job's iochain) \return 0 on sucess, -1 on failiure. When this function returns, signals are always unblocked. On failiure, signal handlers, io @@ -55,4 +65,12 @@ int setup_child_process( job_t *j, process_t *p ); */ pid_t execute_fork(bool wait_for_threads_to_die); +/** Report an error from failing to exec or posix_spawn a command */ +void safe_report_exec_error(int err, const char *actual_cmd, char **argv, char **envv); + +#if FISH_USE_POSIX_SPAWN +/* Initializes and fills in a posix_spawnattr_t; on success, the caller should destroy it via posix_spawnattr_destroy */ +bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p); +#endif + #endif diff --git a/proc.cpp b/proc.cpp index 7de06af53..fe4b9dda9 100644 --- a/proc.cpp +++ b/proc.cpp @@ -491,6 +491,51 @@ static void handle_child_status( pid_t pid, int status ) return; } +process_t::process_t() : + argv_array(), + argv0_narrow(), + type(0), + actual_cmd(), + pid(0), + pipe_write_fd(0), + pipe_read_fd(0), + completed(0), + stopped(0), + status(0), + count_help_magic(0), + next(NULL) +#ifdef HAVE__PROC_SELF_STAT + ,last_time(), + last_jiffies(0) +#endif +{ +} + +process_t::~process_t() +{ + if (this->next != NULL) + delete this->next; +} + +job_t::job_t(job_id_t jobid) : + command_str(), + command_narrow(), + first_process(NULL), + pgid(0), + tmodes(), + job_id(jobid), + io(), + flags(0) +{ +} + +job_t::~job_t() +{ + if (first_process != NULL) + delete first_process; + io_chain_destroy(this->io); + release_job_id(job_id); +} /* This is called from a signal handler */ void job_handle_signal ( int signal, siginfo_t *info, void *con ) @@ -808,12 +853,12 @@ static int select_try( job_t *j ) { fd_set fds; int maxfd=-1; - io_data_t *d; FD_ZERO(&fds); - for( d = j->io; d; d=d->next ) + for (size_t idx = 0; idx < j->io.size(); idx++) { + const io_data_t *d = j->io.at(idx); if( d->io_mode == IO_BUFFER ) { int fd = d->param1.pipe_fd[0]; @@ -846,14 +891,14 @@ static int select_try( job_t *j ) */ static void read_try( job_t *j ) { - io_data_t *d, *buff=0; + io_data_t *buff=NULL; /* Find the last buffer, which is the one we want to read from */ - for( d = j->io; d; d=d->next ) + for (size_t idx = 0; idx < j->io.size(); idx++) { - + io_data_t *d = j->io.at(idx); if( d->io_mode == IO_BUFFER ) { buff=d; diff --git a/proc.h b/proc.h index b313dac59..30c1defb2 100644 --- a/proc.h +++ b/proc.h @@ -140,31 +140,9 @@ class process_t public: - process_t() : - argv_array(), - argv0_narrow(), - type(0), - actual_cmd(), - pid(0), - pipe_write_fd(0), - pipe_read_fd(0), - completed(0), - stopped(0), - status(0), - count_help_magic(0), - next(NULL) -#ifdef HAVE__PROC_SELF_STAT - ,last_time(), - last_jiffies(0) -#endif - { - } + process_t(); - ~process_t() - { - if (this->next != NULL) - delete this->next; - } + ~process_t(); /** Type of process. Can be one of \c EXTERNAL, \c @@ -321,29 +299,8 @@ class job_t public: - job_t(job_id_t jobid) : - command_str(), - command_narrow(), - first_process(NULL), - pgid(0), - tmodes(), - job_id(jobid), - io(NULL), - flags(0) - { - } - - ~job_t() { - if (first_process != NULL) - delete first_process; - io_data_t *data = this->io; - while (data) { - io_data_t *tmp = data->next; - delete data; - data = tmp; - } - release_job_id(job_id); - } + job_t(job_id_t jobid); + ~job_t(); /** Returns whether the command is empty. */ bool command_is_empty() const { return command_str.empty(); } @@ -389,10 +346,8 @@ class job_t */ const job_id_t job_id; - /** - List of all IO redirections for this job. This linked list is allocated via new, and owned by the object, which should delete them. - */ - io_data_t *io; + /** List of all IO redirections for this job. */ + io_chain_t io; /** Bitset containing information about the job. A combination of the JOB_* constants. diff --git a/reader.cpp b/reader.cpp index ecf78d846..777502934 100644 --- a/reader.cpp +++ b/reader.cpp @@ -940,7 +940,7 @@ static void run_pager( const wcstring &prefix, int is_quoted, const std::vector< wcstring msg; wcstring prefix_esc; char *foo; - io_data_t *in; + wchar_t *escaped_separator; int has_case_sensitive=0; @@ -958,7 +958,7 @@ static void run_pager( const wcstring &prefix, int is_quoted, const std::vector< is_quoted?L"-q":L"", prefix_esc.c_str() ); - in= io_buffer_create( 1 ); + io_data_t *in = io_buffer_create(true); in->fd = 3; escaped_separator = escape( COMPLETE_SEP_STR, 1); @@ -1040,12 +1040,14 @@ static void run_pager( const wcstring &prefix, int is_quoted, const std::vector< term_donate(); - io_data_t *out = io_buffer_create( 0 ); - out->next = in; + io_data_t *out = io_buffer_create( false ); out->fd = 4; parser_t &parser = parser_t::principal_parser(); - parser.eval( cmd, out, TOP); + io_chain_t io_chain; + io_chain.push_back(out); + io_chain.push_back(in); + parser.eval( cmd, io_chain, TOP); term_steal(); io_buffer_read( out ); @@ -2062,7 +2064,7 @@ void reader_run_command( parser_t &parser, const wchar_t *cmd ) gettimeofday(&time_before, NULL); - parser.eval( cmd, 0, TOP ); + parser.eval( cmd, io_chain_t(), TOP ); job_reap( 1 ); gettimeofday(&time_after, NULL); @@ -3199,7 +3201,7 @@ int reader_search_mode() the prompt, using syntax highlighting. This is used for reading scripts and init files. */ -static int read_ni( int fd, io_data_t *io ) +static int read_ni( int fd, const io_chain_t &io ) { parser_t &parser = parser_t::principal_parser(); FILE *in_stream; @@ -3296,7 +3298,7 @@ static int read_ni( int fd, io_data_t *io ) return res; } -int reader_read( int fd, io_data_t *io ) +int reader_read( int fd, const io_chain_t &io ) { int res; diff --git a/reader.h b/reader.h index 732b06cfb..0c674fd07 100644 --- a/reader.h +++ b/reader.h @@ -24,7 +24,7 @@ class history_t; /** Read commands from \c fd until encountering EOF */ -int reader_read( int fd, io_data_t *io); +int reader_read( int fd, const io_chain_t &io); /** Tell the shell that it should exit after the currently running command finishes. diff --git a/signal.cpp b/signal.cpp index 3b6bc2dbd..2c51e9d2f 100644 --- a/signal.cpp +++ b/signal.cpp @@ -628,6 +628,18 @@ void signal_handle( int sig, int do_handle ) sigaction( sig, &act, 0); } +void get_signals_with_handlers(sigset_t *set) +{ + sigemptyset(set); + for( int i=0; lookup[i].desc ; i++ ) + { + struct sigaction act = {}; + sigaction(lookup[i].signal, NULL, &act); + if (act.sa_handler != SIG_DFL) + sigaddset(set, lookup[i].signal); + } +} + void signal_block() { ASSERT_IS_MAIN_THREAD(); diff --git a/signal.h b/signal.h index 91bddc920..3bc7527d2 100644 --- a/signal.h +++ b/signal.h @@ -6,6 +6,8 @@ The library for various signal related issues #ifndef FISH_SIGNALH #define FISH_SIGNALH +#include + /** Get the integer signal value representing the specified signal, or -1 of no signal was found @@ -55,4 +57,9 @@ void signal_unblock(); */ int signal_is_blocked(); +/** + Returns signals with non-default handlers +*/ +void get_signals_with_handlers(sigset_t *set); + #endif