aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2012-08-15 00:57:56 -0700
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2012-08-15 00:57:56 -0700
commit61686aff34a9f8c9d4db1045bce987c85d009061 (patch)
tree3f352033e0bcde6aa7f89487c98b55db72b9499b
parentad6645c48da8ae733113ddf07a8675c750c23e1b (diff)
Adopt posix_spawn (!)
Rewrite IO chains to be a vector of pointers, instead of a linked list Removed io_transmogrify
-rw-r--r--FishsFish.xcodeproj/project.pbxproj30
-rw-r--r--builtin.cpp12
-rw-r--r--builtin.h2
-rw-r--r--builtin_set.cpp2
-rw-r--r--common.h4
-rw-r--r--configure.ac4
-rw-r--r--env.cpp44
-rw-r--r--env.h4
-rw-r--r--event.cpp2
-rw-r--r--exec.cpp408
-rw-r--r--exec.h8
-rw-r--r--fish.cpp15
-rw-r--r--fish_tests.cpp2
-rw-r--r--input.cpp2
-rw-r--r--io.cpp223
-rw-r--r--io.h68
-rw-r--r--osx/config.h8
-rw-r--r--parser.cpp12
-rw-r--r--parser.h4
-rw-r--r--postfork.cpp349
-rw-r--r--postfork.h18
-rw-r--r--proc.cpp55
-rw-r--r--proc.h57
-rw-r--r--reader.cpp18
-rw-r--r--reader.h2
-rw-r--r--signal.cpp12
-rw-r--r--signal.h7
27 files changed, 799 insertions, 573 deletions
diff --git a/FishsFish.xcodeproj/project.pbxproj b/FishsFish.xcodeproj/project.pbxproj
index 320528b5..e4987a9c 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 = "<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;
};
diff --git a/builtin.cpp b/builtin.cpp
index f910486c..f9a42f97 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 a34c83cb..eee03b90 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 35dea7a1..c9b0b4b6 100644
--- a/builtin_set.cpp
+++ b/builtin_set.cpp
@@ -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);
}
diff --git a/common.h b/common.h
index 05c795bb..2028a5d2 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 3da41794..ddb8a915 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 26658379..387d50c2 100644
--- a/env.cpp
+++ b/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,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<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;
}
diff --git a/env.h b/env.h
index bd597f1b..2c32b6a0 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<char> &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 619200ed..176bc0a0 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<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 );
diff --git a/exec.cpp b/exec.cpp
index e20c4826..49925092 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<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.");
- 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<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 )
@@ -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 4fa4b10c..97342f11 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<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
diff --git a/fish.cpp b/fish.cpp
index 5e8c6b62..baf8a774 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 e682acdb..a5bddfa2 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 0ae04c54..38164d21 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 10648c5b..176fb2a3 100644
--- a/io.cpp
+++ b/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;
}
+void io_chain_t::remove(const io_data_t *element)
+{
+ // 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_chain_t io_chain_t::duplicate() const
+{
+ 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_add( io_data_t *list, io_data_t *element )
+void io_chain_t::duplicate_append(const io_chain_t &src)
{
- io_data_t *curr = list;
- if( curr == 0 )
- return element;
- while( curr->next != 0 )
- curr = curr->next;
- curr->next = element;
- return list;
+ 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_remove( io_data_t *list, io_data_t *element )
+void io_chain_t::destroy()
{
- 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;
+ for (size_t idx = 0; idx < this->size(); idx++)
+ {
+ delete this->at(idx);
+ }
+ this->clear();
}
-io_data_t *io_duplicate( io_data_t *l )
+void io_remove(io_chain_t &list, const io_data_t *element)
{
- io_data_t *res;
-
- if( l == 0 )
- return 0;
+ list.remove(element);
+}
- res = new io_data_t(*l);
- res->next=io_duplicate(l->next );
- return res;
+io_chain_t io_duplicate(const io_chain_t &chain)
+{
+ return chain.duplicate();
}
-io_data_t *io_get( io_data_t *io, int fd )
+void io_print(const io_chain_t &chain)
{
- if( io == NULL )
- return 0;
-
- io_data_t *res = io_get( io->next, fd );
- if( res )
- return res;
-
- if( io->fd == fd )
- return io;
+ 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;
+ }
+ }
+}
- return 0;
+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();
+}
-void io_print( io_data_t *io )
+/* Return the last IO for the given fd */
+const io_data_t *io_chain_t::get_io_for_fd(int fd) const
{
- 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" );
- }
+ size_t idx = this->size();
+ while (idx--)
+ {
+ const io_data_t *data = this->at(idx);
+ if (data->fd == fd) {
+ return data;
+ }
+ }
+ return NULL;
+}
- io_print( io->next );
-
+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 *>()
+{
}
diff --git a/io.h b/io.h
index 49bbbafa..109c1a5a 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<io_data_t *> {
+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 f606d92f..a72a8825 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 <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
diff --git a/parser.cpp b/parser.cpp
index 3e95ee8e..d701d56a 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<wcstring> prev_forbidden = forbidden_function;
if( block_type == SUBST )
diff --git a/parser.h b/parser.h
index b74a239c..27200833 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 7b210ff4..07842cb0 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<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;
+ }
+ }
+}
diff --git a/postfork.h b/postfork.h
index 741e6369..9acd4c86 100644
--- a/postfork.h
+++ b/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
diff --git a/proc.cpp b/proc.cpp
index 7de06af5..fe4b9dda 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 b313dac5..30c1defb 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 ecf78d84..77750293 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 732b06cf..0c674fd0 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 3b6bc2db..2c51e9d2 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 91bddc92..3bc7527d 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 <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