mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Adopt posix_spawn (!)
Rewrite IO chains to be a vector of pointers, instead of a linked list Removed io_transmogrify
This commit is contained in:
parent
ad6645c48d
commit
61686aff34
27 changed files with 805 additions and 579 deletions
|
@ -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 = "<group>"; };
|
||||
D025C02815D1FEA100B9DB63 /* functions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = functions; path = share/functions; sourceTree = "<group>"; };
|
||||
D025C02915D1FEA100B9DB63 /* tools */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tools; path = share/tools; sourceTree = "<group>"; };
|
||||
D03EE83814DF88B200FC7150 /* lru.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lru.h; sourceTree = "<group>"; };
|
||||
D07B247215BCC15700D4ADB4 /* add-shell */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "add-shell"; path = "build_tools/osx_package_scripts/add-shell"; sourceTree = "<group>"; };
|
||||
D07B247515BCC4BE00D4ADB4 /* install.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = install.sh; path = osx/install.sh; sourceTree = "<group>"; };
|
||||
|
@ -387,9 +381,6 @@
|
|||
D0C4FD9415A7D7EE00212EF1 /* config.fish */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.fish; path = etc/config.fish; sourceTree = "<group>"; };
|
||||
D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = autoload.cpp; sourceTree = "<group>"; };
|
||||
D0C6FCCB14CFA4B7004CE8AD /* autoload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autoload.h; sourceTree = "<group>"; };
|
||||
D0CBD578159EE4600024809C /* completions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = completions; path = share/completions; sourceTree = "<group>"; };
|
||||
D0CBD579159EE4600024809C /* functions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = functions; path = share/functions; sourceTree = "<group>"; };
|
||||
D0CBD57A159EE4600024809C /* tools */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tools; path = share/tools; sourceTree = "<group>"; };
|
||||
D0CBD580159EE48F0024809C /* config.fish */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.fish; path = share/config.fish; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
|
@ -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 = "<group>";
|
||||
|
@ -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;
|
||||
};
|
||||
|
|
12
builtin.cpp
12
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 );
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -313,7 +313,7 @@ static void erase_values(wcstring_list_t &list, const std::vector<long> &indexes
|
|||
std::set<long>::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);
|
||||
}
|
||||
|
|
4
common.h
4
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 ); \
|
||||
} \
|
||||
|
||||
|
|
|
@ -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],
|
||||
|
|
42
env.cpp
42
env.cpp
|
@ -101,6 +101,7 @@ struct var_entry_t
|
|||
typedef std::map<wcstring, var_entry_t*> 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<char> 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<bool>(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<bool>(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,7 +750,7 @@ 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;
|
||||
|
@ -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<wcstring, wcstring> vals;
|
||||
size_t i;
|
||||
|
@ -1515,22 +1527,22 @@ static void update_export_array_if_necessary(bool recalc) {
|
|||
std::vector<std::string> 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<char> &output)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
update_export_array_if_necessary(recalc != 0);
|
||||
update_export_array_if_necessary(recalc);
|
||||
output = export_array;
|
||||
}
|
||||
|
||||
|
|
4
env.h
4
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<char> &result);
|
||||
|
||||
/**
|
||||
|
@ -201,5 +201,7 @@ public:
|
|||
extern bool g_log_forks;
|
||||
extern int g_fork_count;
|
||||
|
||||
extern bool g_use_posix_spawn;
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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<const event_t *>() = 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 );
|
||||
|
|
342
exec.cpp
342
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 )
|
||||
static bool use_fd_in_pipe(int fd, const io_chain_t &io_chain )
|
||||
{
|
||||
if( !io )
|
||||
return 0;
|
||||
|
||||
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 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return use_fd_in_pipe( fd, io->next );
|
||||
}
|
||||
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<int> &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.");
|
||||
safe_report_exec_error(errno, actual_cmd, argv, envv);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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<io_data_t> 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 )
|
||||
|
@ -826,7 +647,7 @@ 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 );
|
||||
j->io.push_back(io_buffer);
|
||||
}
|
||||
|
||||
internal_exec_helper( parser, def, TOP, j->io );
|
||||
|
@ -843,7 +664,7 @@ 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 );
|
||||
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,9 +1096,41 @@ 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 )
|
||||
if (pid == 0)
|
||||
{
|
||||
/*
|
||||
This is the child process.
|
||||
|
@ -1289,8 +1143,9 @@ void exec( parser_t &parser, job_t *j )
|
|||
safe_launch_process _never_ returns...
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This is the parent process. Store away
|
||||
information on the child, and possibly fice
|
||||
|
@ -1300,7 +1155,6 @@ void exec( parser_t &parser, job_t *j )
|
|||
|
||||
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 );
|
||||
io_remove( j->io, &pipe_read );
|
||||
|
||||
for( tmp = parser.block_io; tmp; tmp=tmp->next )
|
||||
j->io = io_remove( j->io, tmp );
|
||||
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;
|
||||
}
|
||||
|
|
8
exec.h
8
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<int> &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
|
||||
|
|
15
fish.cpp
15
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 )
|
||||
{
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
217
io.cpp
217
io.cpp
|
@ -12,6 +12,8 @@ Utilities for io redirection.
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef HAVE_SYS_TERMIOS_H
|
||||
#include <sys/termios.h>
|
||||
|
@ -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<io_data_t> 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;
|
||||
}
|
||||
|
||||
io_data_t *io_remove( io_data_t *list, io_data_t *element )
|
||||
{
|
||||
io_data_t *curr, *prev=0;
|
||||
for( curr=list; curr; curr = curr->next )
|
||||
// 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( element == curr )
|
||||
if (*iter == element)
|
||||
{
|
||||
if( prev == 0 )
|
||||
{
|
||||
io_data_t *tmp = element->next;
|
||||
element->next = 0;
|
||||
return tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev->next = element->next;
|
||||
element->next = 0;
|
||||
return list;
|
||||
this->erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
prev = curr;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
io_data_t *io_duplicate( io_data_t *l )
|
||||
io_chain_t io_chain_t::duplicate() const
|
||||
{
|
||||
io_data_t *res;
|
||||
|
||||
if( l == 0 )
|
||||
return 0;
|
||||
|
||||
res = new io_data_t(*l);
|
||||
res->next=io_duplicate(l->next );
|
||||
return res;
|
||||
}
|
||||
|
||||
io_data_t *io_get( io_data_t *io, int fd )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void io_print( io_data_t *io )
|
||||
{
|
||||
if( !io )
|
||||
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;
|
||||
}
|
||||
|
||||
void io_chain_t::duplicate_append(const io_chain_t &src)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
void io_chain_t::destroy()
|
||||
{
|
||||
for (size_t idx = 0; idx < this->size(); idx++)
|
||||
{
|
||||
delete this->at(idx);
|
||||
}
|
||||
this->clear();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
debug( 1, L"IO fd %d, type ", io->fd );
|
||||
switch( io->io_mode )
|
||||
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:
|
||||
debug( 1, L"PIPE, data %d", io->param1.pipe_fd[io->fd?1:0] );
|
||||
fprintf(stderr, "pipe {%d, %d}\n", io->param1.pipe_fd[0], io->param1.pipe_fd[1]);
|
||||
break;
|
||||
|
||||
case IO_FD:
|
||||
debug( 1, L"FD, copy %d", io->param1.old_fd );
|
||||
fprintf(stderr, "FD map %d -> %d\n", io->param1.old_fd, io->fd);
|
||||
break;
|
||||
|
||||
case IO_BUFFER:
|
||||
debug( 1, L"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;
|
||||
|
||||
default:
|
||||
debug( 1, L"OTHER" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
io_print( io->next );
|
||||
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<io_data_t *>(1, data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
io_chain_t::io_chain_t() : std::vector<io_data_t *>()
|
||||
{
|
||||
}
|
||||
|
|
66
io.h
66
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,10 +88,7 @@ public:
|
|||
}
|
||||
|
||||
/** Set to true if this is an input io redirection */
|
||||
int is_input;
|
||||
|
||||
/** Pointer to the next IO redirection */
|
||||
io_data_t *next;
|
||||
bool is_input;
|
||||
|
||||
io_data_t() :
|
||||
out_buffer(),
|
||||
|
@ -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:
|
|||
}
|
||||
};
|
||||
|
||||
class io_chain_t : public std::vector<io_data_t *> {
|
||||
public:
|
||||
io_chain_t();
|
||||
io_chain_t(io_data_t *);
|
||||
|
||||
/**
|
||||
Join two chains of io redirections
|
||||
*/
|
||||
io_data_t *io_add( io_data_t *first_chain, io_data_t *decond_chain );
|
||||
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
|
||||
|
|
|
@ -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 <siginfo.h> header file. */
|
||||
/* #undef HAVE_SIGINFO_H */
|
||||
|
||||
/* Define to 1 if you have the <spawn.h> header file. */
|
||||
#define HAVE_SPAWN_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
|
@ -219,5 +219,3 @@
|
|||
#define __warn_unused
|
||||
#define __sentinel
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
12
parser.cpp
12
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<wcstring> prev_forbidden = forbidden_function;
|
||||
|
||||
if( block_type == SUBST )
|
||||
|
|
4
parser.h
4
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.
|
||||
|
|
317
postfork.cpp
317
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;
|
||||
size_t max = io_chain.size();
|
||||
for (size_t i = 0; i < max; i++)
|
||||
{
|
||||
int fd_to_free = io_chain.at(i)->fd;
|
||||
|
||||
if( ( io->io_mode == IO_PIPE ) || ( io->io_mode == IO_BUFFER ) )
|
||||
/* 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++)
|
||||
{
|
||||
int i;
|
||||
for( i=0; i<2; i++ )
|
||||
/* 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(io->param1.pipe_fd[i] == fd )
|
||||
/* 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)
|
||||
{
|
||||
while(1)
|
||||
replacement_fd = dup(fd_to_free);
|
||||
if (replacement_fd == -1 && errno != EINTR)
|
||||
{
|
||||
if( (io->param1.pipe_fd[i] = dup(fd)) == -1)
|
||||
{
|
||||
if( errno != EINTR )
|
||||
{
|
||||
debug_safe_int( 1, FD_ERROR, fd );
|
||||
debug_safe_int( 1, FD_ERROR, fd_to_free );
|
||||
wperror( L"dup" );
|
||||
FATAL_EXIT();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
possible_conflict->param1.pipe_fd[k] = replacement_fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free_fd( io->next, fd );
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,26 +159,25 @@ 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 )
|
||||
{
|
||||
case IO_CLOSE:
|
||||
|
@ -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,
|
||||
|
@ -257,15 +266,10 @@ static int handle_child_io( io_data_t *io )
|
|||
return -1;
|
||||
}
|
||||
|
||||
if( write_pipe_idx > 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]);
|
||||
}
|
||||
else
|
||||
{
|
||||
exec_close( io->param1.pipe_fd[0] );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -305,7 +309,6 @@ int setup_child_process( job_t *j, process_t *p )
|
|||
signal_unblock();
|
||||
|
||||
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<int> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
18
postfork.h
18
postfork.h
|
@ -19,6 +19,15 @@
|
|||
#include "wutil.h"
|
||||
#include "io.h"
|
||||
|
||||
#if HAVE_SPAWN_H
|
||||
#include <spawn.h>
|
||||
#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
|
||||
|
|
55
proc.cpp
55
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;
|
||||
|
|
57
proc.h
57
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.
|
||||
|
|
18
reader.cpp
18
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;
|
||||
|
||||
|
|
2
reader.h
2
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.
|
||||
|
|
12
signal.cpp
12
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();
|
||||
|
|
7
signal.h
7
signal.h
|
@ -6,6 +6,8 @@ The library for various signal related issues
|
|||
#ifndef FISH_SIGNALH
|
||||
#define FISH_SIGNALH
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
/**
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue