diff options
Diffstat (limited to 'io.cpp')
-rw-r--r-- | io.cpp | 114 |
1 files changed, 86 insertions, 28 deletions
@@ -24,6 +24,8 @@ Utilities for io redirection. #if HAVE_NCURSES_H #include <ncurses.h> +#elif HAVE_NCURSES_CURSES_H +#include <ncurses/curses.h> #else #include <curses.h> #endif @@ -71,7 +73,7 @@ void io_pipe_t::print() const void io_buffer_t::print() const { fprintf(stderr, "buffer %p (input: %s, size %lu)\n", out_buffer_ptr(), - is_input ? "yes" : "no", out_buffer_size()); + is_input ? "yes" : "no", (unsigned long) out_buffer_size()); } void io_buffer_t::read() @@ -122,15 +124,21 @@ void io_buffer_t::read() } } - -io_buffer_t *io_buffer_t::create(bool is_input, int fd) +bool io_buffer_t::avoid_conflicts_with_io_chain(const io_chain_t &ios) { - bool success = true; - if (fd == -1) + bool result = pipe_avoid_conflicts_with_io_chain(this->pipe_fd, ios); + if (! result) { - fd = is_input ? STDIN_FILENO : STDOUT_FILENO; + wperror(L"dup"); } - io_buffer_t *buffer_redirect = new io_buffer_t(fd, is_input); + return result; +} + +io_buffer_t *io_buffer_t::create(int fd, const io_chain_t &conflicts) +{ + bool success = true; + assert(fd >= 0); + io_buffer_t *buffer_redirect = new io_buffer_t(fd); if (exec_pipe(buffer_redirect->pipe_fd) == -1) { @@ -138,6 +146,11 @@ io_buffer_t *io_buffer_t::create(bool is_input, int fd) wperror(L"pipe"); success = false; } + else if (! buffer_redirect->avoid_conflicts_with_io_chain(conflicts)) + { + // The above call closes the fds on error + success = false; + } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) { debug(1, PIPE_ERROR); @@ -150,27 +163,11 @@ io_buffer_t *io_buffer_t::create(bool is_input, int fd) delete buffer_redirect; buffer_redirect = NULL; } - else - { - //fprintf(stderr, "Created pipes {%d, %d} for %p\n", buffer_redirect->pipe_fd[0], buffer_redirect->pipe_fd[1], buffer_redirect); - } - return buffer_redirect; } io_buffer_t::~io_buffer_t() { - - //fprintf(stderr, "Deallocating pipes {%d, %d} for %p\n", this->pipe_fd[0], this->pipe_fd[1], this); - /** - If this is an input buffer, then io_read_buffer will not have - been called, and we need to close the output fd as well. - */ - if (is_input && pipe_fd[1] >= 0) - { - exec_close(pipe_fd[1]); - } - if (pipe_fd[0] >= 0) { exec_close(pipe_fd[0]); @@ -213,11 +210,6 @@ void io_chain_t::append(const io_chain_t &chain) this->insert(this->end(), chain.begin(), chain.end()); } -void io_remove(io_chain_t &list, const shared_ptr<const io_data_t> &element) -{ - list.remove(element); -} - void io_print(const io_chain_t &chain) { if (chain.empty()) @@ -242,6 +234,71 @@ void io_print(const io_chain_t &chain) } } +/* If the given fd is used by the io chain, duplicates it repeatedly until an fd not used in the io chain is found, or we run out. If we return a new fd or an error, closes the old one. Any fd created is marked close-on-exec. Returns -1 on failure (in which case the given fd is still closed). */ +static int move_fd_to_unused(int fd, const io_chain_t &io_chain) +{ + int new_fd = fd; + if (fd >= 0 && io_chain.get_io_for_fd(fd).get() != NULL) + { + /* We have fd >= 0, and it's a conflict. dup it and recurse. Note that we recurse before anything is closed; this forces the kernel to give us a new one (or report fd exhaustion). */ + int tmp_fd; + do + { + tmp_fd = dup(fd); + } while (tmp_fd < 0 && errno == EINTR); + + assert(tmp_fd != fd); + if (tmp_fd < 0) + { + /* Likely fd exhaustion. */ + new_fd = -1; + } + else + { + /* Ok, we have a new candidate fd. Recurse. If we get a valid fd, either it's the same as what we gave it, or it's a new fd and what we gave it has been closed. If we get a negative value, the fd also has been closed. */ + set_cloexec(tmp_fd); + new_fd = move_fd_to_unused(tmp_fd, io_chain); + } + + /* We're either returning a new fd or an error. In both cases, we promise to close the old one. */ + assert(new_fd != fd); + int saved_errno = errno; + exec_close(fd); + errno = saved_errno; + } + return new_fd; +} + +bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios) +{ + bool success = true; + for (int i=0; i < 2; i++) + { + fds[i] = move_fd_to_unused(fds[i], ios); + if (fds[i] < 0) + { + success = false; + break; + } + } + + /* If any fd failed, close all valid fds */ + if (! success) + { + int saved_errno = errno; + for (int i=0; i < 2; i++) + { + if (fds[i] >= 0) + { + exec_close(fds[i]); + fds[i] = -1; + } + } + errno = saved_errno; + } + return success; +} + /* Return the last IO for the given fd */ shared_ptr<const io_data_t> io_chain_t::get_io_for_fd(int fd) const { @@ -291,3 +348,4 @@ io_chain_t::io_chain_t(const shared_ptr<io_data_t> &data) : io_chain_t::io_chain_t() : std::vector<shared_ptr<io_data_t> >() { } + |