aboutsummaryrefslogtreecommitdiffhomepage
path: root/io.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'io.cpp')
-rw-r--r--io.cpp114
1 files changed, 86 insertions, 28 deletions
diff --git a/io.cpp b/io.cpp
index 1ee36e72..264f0b9f 100644
--- a/io.cpp
+++ b/io.cpp
@@ -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> >()
{
}
+