Reintroduce IO transmorgrification (yuck) to fix problems with fish_config and complicated IO redirections

This commit is contained in:
ridiculousfish 2012-08-22 13:41:21 -07:00
parent 04ea680e9a
commit f5d4e3f94c
2 changed files with 133 additions and 10 deletions

135
exec.cpp
View file

@ -319,6 +319,123 @@ static int has_fd( const io_chain_t &d, int fd )
return io_chain_get( d, fd ) != NULL;
}
/**
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_cleanup_chains(io_chain_t &chains, const std::vector<int> &opened_fds) {
/* Close all the fds */
for (size_t idx = 0; idx < opened_fds.size(); idx++) {
close(opened_fds.at(idx));
}
/* Then delete all of the redirections we made */
chains.destroy();
}
/**
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 bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, std::vector<int> &out_opened_fds) {
ASSERT_IS_MAIN_THREAD();
assert(out_chain.empty());
/* Just to be clear what we do for an empty chain */
if (in_chain.empty()) {
return true;
}
bool success = true;
/* Make our chain of redirections */
io_chain_t result_chain;
/* In the event we can't finish transmorgrifying, we'll have to close all the files we opened. */
std::vector<int> opened_fds;
for (size_t idx = 0; idx < in_chain.size(); idx++)
{
io_data_t *in = in_chain.at(idx);
io_data_t *out = NULL; //gets allocated via new
switch( in->io_mode )
{
default:
/* Unknown type, should never happen */
fprintf(stderr, "Unknown io_mode %ld\n", (long)in->io_mode);
abort();
break;
/*
These redirections don't need transmogrification. They can be passed through.
*/
case IO_PIPE:
case IO_FD:
case IO_BUFFER:
case IO_CLOSE:
{
out = new io_data_t(*in);
break;
}
/*
Transmogrify file redirections
*/
case IO_FILE:
{
out = new io_data_t();
out->fd = in->fd;
out->io_mode = IO_FD;
out->param2.close_old = 1;
int fd;
if ((fd=open(in->filename_cstr, in->param2.flags, OPEN_MASK))==-1)
{
debug( 1,
FILE_ERROR,
in->filename_cstr );
wperror( L"open" );
success = false;
break;
}
opened_fds.push_back(fd);
out->param1.old_fd = fd;
break;
}
}
/* Record this IO redirection even if we failed (so we can free it) */
result_chain.push_back(out);
/* But don't go any further if we failed */
if (! success) {
break;
}
}
/* Now either return success, or clean up */
if (success) {
/* Yay */
out_chain.swap(result_chain);
out_opened_fds.swap(opened_fds);
} else {
/* No dice - clean up */
io_cleanup_chains(result_chain, opened_fds);
}
return success;
}
/**
Morph an io redirection chain into redirections suitable for
passing to eval, call eval, and clean up morphed redirections.
@ -333,15 +450,29 @@ static void internal_exec_helper( parser_t &parser,
enum block_type_t block_type,
io_chain_t &ios )
{
io_chain_t morphed_chain;
std::vector<int> opened_fds;
bool transmorgrified = io_transmogrify(ios, morphed_chain, opened_fds);
int is_block_old=is_block;
is_block=1;
/*
Did the transmogrification fail - if so, set error status and return
*/
if( ! transmorgrified )
{
proc_set_last_status( STATUS_EXEC_FAIL );
return;
}
signal_unblock();
parser.eval( def, ios, block_type );
parser.eval( def, morphed_chain, block_type );
signal_block();
io_cleanup_chains(morphed_chain, opened_fds);
job_reap( 0 );
is_block=is_block_old;
}

View file

@ -169,10 +169,6 @@ static int handle_child_io( io_chain_t &io_chain )
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;
@ -444,10 +440,6 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil
{
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;