2005-10-08 11:20:51 +00:00
|
|
|
#ifndef FISH_IO_H
|
|
|
|
#define FISH_IO_H
|
|
|
|
|
2019-02-01 09:58:06 +00:00
|
|
|
#include <pthread.h>
|
2016-05-02 03:32:40 +00:00
|
|
|
#include <stdarg.h>
|
2015-07-25 15:14:25 +00:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdlib.h>
|
2017-02-14 04:37:27 +00:00
|
|
|
|
2019-02-01 09:58:06 +00:00
|
|
|
#include <atomic>
|
2019-11-23 23:52:53 +00:00
|
|
|
#include <future>
|
2019-02-03 01:53:40 +00:00
|
|
|
#include <memory>
|
2019-02-01 09:58:06 +00:00
|
|
|
#include <mutex>
|
|
|
|
#include <vector>
|
2012-03-04 10:35:30 +00:00
|
|
|
|
2015-07-25 15:14:25 +00:00
|
|
|
#include "common.h"
|
2017-07-27 03:17:04 +00:00
|
|
|
#include "env.h"
|
2020-01-19 12:38:47 +00:00
|
|
|
#include "flog.h"
|
2019-11-23 22:11:07 +00:00
|
|
|
#include "global_safety.h"
|
2019-02-01 09:58:06 +00:00
|
|
|
#include "maybe.h"
|
2019-12-13 00:44:24 +00:00
|
|
|
#include "redirection.h"
|
2019-02-01 09:58:06 +00:00
|
|
|
|
|
|
|
using std::shared_ptr;
|
2015-07-25 15:14:25 +00:00
|
|
|
|
2019-12-12 22:42:03 +00:00
|
|
|
/// A simple set of FDs.
|
|
|
|
struct fd_set_t {
|
|
|
|
std::vector<bool> fds;
|
|
|
|
|
|
|
|
void add(int fd) {
|
|
|
|
assert(fd >= 0 && "Invalid fd");
|
|
|
|
if ((size_t)fd >= fds.size()) {
|
|
|
|
fds.resize(fd + 1);
|
|
|
|
}
|
|
|
|
fds[fd] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool contains(int fd) const {
|
|
|
|
assert(fd >= 0 && "Invalid fd");
|
|
|
|
return (size_t)fd < fds.size() && fds[fd];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-05-30 04:11:34 +00:00
|
|
|
/// separated_buffer_t is composed of a sequence of elements, some of which may be explicitly
|
|
|
|
/// separated (e.g. through string spit0) and some of which the separation is inferred. This enum
|
|
|
|
/// tracks the type.
|
|
|
|
enum class separation_type_t {
|
|
|
|
/// This element's separation should be inferred, e.g. through IFS.
|
|
|
|
inferred,
|
|
|
|
/// This element was explicitly separated and should not be separated further.
|
|
|
|
explicitly
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A separated_buffer_t contains a list of elements, some of which may be separated explicitly and
|
|
|
|
/// others which must be separated further by the user (e.g. via IFS).
|
|
|
|
template <typename StringType>
|
|
|
|
class separated_buffer_t {
|
2018-05-30 04:11:50 +00:00
|
|
|
public:
|
2018-05-30 04:11:34 +00:00
|
|
|
struct element_t {
|
|
|
|
StringType contents;
|
|
|
|
separation_type_t separation;
|
|
|
|
|
|
|
|
element_t(StringType contents, separation_type_t sep)
|
|
|
|
: contents(std::move(contents)), separation(sep) {}
|
|
|
|
|
|
|
|
bool is_explicitly_separated() const { return separation == separation_type_t::explicitly; }
|
|
|
|
};
|
|
|
|
|
2018-05-30 04:11:50 +00:00
|
|
|
private:
|
2018-05-30 04:11:34 +00:00
|
|
|
/// Limit on how much data we'll buffer. Zero means no limit.
|
|
|
|
size_t buffer_limit_;
|
|
|
|
|
|
|
|
/// Current size of all contents.
|
|
|
|
size_t contents_size_{0};
|
|
|
|
|
|
|
|
/// List of buffer elements.
|
|
|
|
std::vector<element_t> elements_;
|
|
|
|
|
|
|
|
/// True if we're discarding input because our buffer_limit has been exceeded.
|
|
|
|
bool discard = false;
|
|
|
|
|
|
|
|
/// Mark that we are about to add the given size \p delta to the buffer. \return true if we
|
|
|
|
/// succeed, false if we exceed buffer_limit.
|
|
|
|
bool try_add_size(size_t delta) {
|
|
|
|
if (discard) return false;
|
|
|
|
contents_size_ += delta;
|
|
|
|
if (contents_size_ < delta) {
|
|
|
|
// Overflow!
|
|
|
|
set_discard();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (buffer_limit_ > 0 && contents_size_ > buffer_limit_) {
|
|
|
|
set_discard();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// separated_buffer_t may not be copied.
|
|
|
|
separated_buffer_t(const separated_buffer_t &) = delete;
|
|
|
|
void operator=(const separated_buffer_t &) = delete;
|
|
|
|
|
2019-05-05 10:09:25 +00:00
|
|
|
public:
|
|
|
|
/// Construct a separated_buffer_t with the given buffer limit \p limit, or 0 for no limit.
|
|
|
|
separated_buffer_t(size_t limit) : buffer_limit_(limit) {}
|
|
|
|
|
|
|
|
/// \return the buffer limit size, or 0 for no limit.
|
|
|
|
size_t limit() const { return buffer_limit_; }
|
|
|
|
|
|
|
|
/// \return the contents size.
|
|
|
|
size_t size() const { return contents_size_; }
|
|
|
|
|
|
|
|
/// \return whether the output has been discarded.
|
|
|
|
bool discarded() const { return discard; }
|
|
|
|
|
|
|
|
/// Mark the contents as discarded.
|
|
|
|
void set_discard() {
|
|
|
|
elements_.clear();
|
|
|
|
contents_size_ = 0;
|
|
|
|
discard = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Serialize the contents to a single string, where explicitly separated elements have a
|
|
|
|
/// newline appended.
|
|
|
|
StringType newline_serialized() const {
|
|
|
|
StringType result;
|
|
|
|
result.reserve(size());
|
|
|
|
for (const auto &elem : elements_) {
|
|
|
|
result.append(elem.contents);
|
|
|
|
if (elem.is_explicitly_separated()) {
|
|
|
|
result.push_back('\n');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2018-05-30 04:11:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// \return the list of elements.
|
|
|
|
const std::vector<element_t> &elements() const { return elements_; }
|
|
|
|
|
|
|
|
/// Append an element with range [begin, end) and the given separation type \p sep.
|
|
|
|
template <typename Iterator>
|
|
|
|
void append(Iterator begin, Iterator end, separation_type_t sep = separation_type_t::inferred) {
|
|
|
|
if (!try_add_size(std::distance(begin, end))) return;
|
|
|
|
// Try merging with the last element.
|
2019-05-05 10:09:25 +00:00
|
|
|
if (sep == separation_type_t::inferred && !elements_.empty() &&
|
|
|
|
!elements_.back().is_explicitly_separated()) {
|
2018-05-30 04:11:34 +00:00
|
|
|
elements_.back().contents.append(begin, end);
|
|
|
|
} else {
|
|
|
|
elements_.emplace_back(StringType(begin, end), sep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Append a string \p str with the given separation type \p sep.
|
|
|
|
void append(const StringType &str, separation_type_t sep = separation_type_t::inferred) {
|
|
|
|
append(str.begin(), str.end(), sep);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Given that this is a narrow stream, convert a wide stream \p rhs to narrow and then append
|
|
|
|
// it.
|
|
|
|
template <typename RHSStringType>
|
|
|
|
void append_wide_buffer(const separated_buffer_t<RHSStringType> &rhs) {
|
|
|
|
for (const auto &rhs_elem : rhs.elements()) {
|
|
|
|
append(wcs2string(rhs_elem.contents), rhs_elem.separation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
/// Describes what type of IO operation an io_data_t represents.
|
2019-02-01 00:05:42 +00:00
|
|
|
enum class io_mode_t { file, pipe, fd, close, bufferfill };
|
2005-10-08 11:20:51 +00:00
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
/// Represents an FD redirection.
|
|
|
|
class io_data_t {
|
|
|
|
// No assignment or copying allowed.
|
2019-12-29 23:14:08 +00:00
|
|
|
io_data_t(const io_data_t &rhs) = delete;
|
|
|
|
void operator=(const io_data_t &rhs) = delete;
|
2013-01-04 10:03:41 +00:00
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
protected:
|
2019-12-29 23:14:08 +00:00
|
|
|
io_data_t(io_mode_t m, int fd, int source_fd) : io_mode(m), fd(fd), source_fd(source_fd) {}
|
2013-01-15 09:39:20 +00:00
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
public:
|
|
|
|
/// Type of redirect.
|
2013-01-17 06:46:02 +00:00
|
|
|
const io_mode_t io_mode;
|
2019-12-29 23:14:08 +00:00
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
/// FD to redirect.
|
2013-02-04 12:07:16 +00:00
|
|
|
const int fd;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2019-12-29 23:14:08 +00:00
|
|
|
/// Source fd. This is dup2'd to fd, or if it is -1, then fd is closed.
|
|
|
|
/// That is, we call dup2(source_fd, fd).
|
|
|
|
const int source_fd;
|
|
|
|
|
2013-01-15 09:39:20 +00:00
|
|
|
virtual void print() const = 0;
|
|
|
|
virtual ~io_data_t() = 0;
|
2012-02-10 02:43:36 +00:00
|
|
|
};
|
2013-01-09 08:02:04 +00:00
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
class io_close_t : public io_data_t {
|
|
|
|
public:
|
2019-12-29 23:14:08 +00:00
|
|
|
explicit io_close_t(int f) : io_data_t(io_mode_t::close, f, -1) {}
|
2013-01-09 08:02:04 +00:00
|
|
|
|
2018-02-19 02:50:35 +00:00
|
|
|
void print() const override;
|
2019-12-13 01:37:36 +00:00
|
|
|
~io_close_t() override;
|
2013-01-09 08:02:04 +00:00
|
|
|
};
|
2013-01-15 07:37:33 +00:00
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
class io_fd_t : public io_data_t {
|
|
|
|
public:
|
2018-02-19 02:50:35 +00:00
|
|
|
void print() const override;
|
2013-01-15 07:37:33 +00:00
|
|
|
|
2019-12-13 01:37:36 +00:00
|
|
|
~io_fd_t() override;
|
|
|
|
|
2019-12-29 23:14:08 +00:00
|
|
|
/// fd to redirect specified fd to. For example, in 2>&1, source_fd is 1, and io_data_t::fd
|
|
|
|
/// is 2.
|
|
|
|
io_fd_t(int f, int source_fd) : io_data_t(io_mode_t::fd, f, source_fd) {}
|
2013-01-15 07:37:33 +00:00
|
|
|
};
|
2013-01-15 08:18:03 +00:00
|
|
|
|
2019-12-13 01:27:48 +00:00
|
|
|
/// Represents a redirection to or from an opened file.
|
2016-05-02 03:32:40 +00:00
|
|
|
class io_file_t : public io_data_t {
|
|
|
|
public:
|
2018-02-19 02:50:35 +00:00
|
|
|
void print() const override;
|
2013-01-15 08:18:03 +00:00
|
|
|
|
2019-12-29 23:51:22 +00:00
|
|
|
io_file_t(int fd, autoclose_fd_t file)
|
|
|
|
: io_data_t(io_mode_t::file, fd, file.fd()), file_fd_(std::move(file)) {
|
|
|
|
assert(file_fd_.valid() && "File is not valid");
|
|
|
|
}
|
2019-12-13 01:27:48 +00:00
|
|
|
|
|
|
|
~io_file_t() override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
// The fd for the file which we are writing to or reading from.
|
|
|
|
autoclose_fd_t file_fd_;
|
2013-01-15 08:18:03 +00:00
|
|
|
};
|
2005-10-08 11:20:51 +00:00
|
|
|
|
2019-02-01 00:05:42 +00:00
|
|
|
/// Represents (one end) of a pipe.
|
2016-05-02 03:32:40 +00:00
|
|
|
class io_pipe_t : public io_data_t {
|
2019-02-01 00:05:42 +00:00
|
|
|
// The pipe's fd. Conceptually this is dup2'd to io_data_t::fd.
|
|
|
|
autoclose_fd_t pipe_fd_;
|
2013-01-15 09:31:36 +00:00
|
|
|
|
2019-02-01 00:05:42 +00:00
|
|
|
/// Whether this is an input pipe. This is used only for informational purposes.
|
|
|
|
const bool is_input_;
|
2013-01-15 09:31:36 +00:00
|
|
|
|
2019-02-01 00:05:42 +00:00
|
|
|
public:
|
2019-02-01 00:05:42 +00:00
|
|
|
void print() const override;
|
|
|
|
|
2019-02-01 00:05:42 +00:00
|
|
|
io_pipe_t(int fd, bool is_input, autoclose_fd_t pipe_fd)
|
2019-12-29 23:14:08 +00:00
|
|
|
: io_data_t(io_mode_t::pipe, fd, pipe_fd.fd()),
|
|
|
|
pipe_fd_(std::move(pipe_fd)),
|
2019-12-29 23:51:22 +00:00
|
|
|
is_input_(is_input) {
|
|
|
|
assert(pipe_fd_.valid() && "Pipe is not valid");
|
|
|
|
}
|
2019-02-01 00:05:42 +00:00
|
|
|
|
2019-11-20 06:17:30 +00:00
|
|
|
~io_pipe_t() override;
|
2019-02-01 00:05:42 +00:00
|
|
|
};
|
|
|
|
|
2019-02-01 00:05:42 +00:00
|
|
|
class io_buffer_t;
|
2019-02-03 01:53:40 +00:00
|
|
|
class io_chain_t;
|
2019-02-01 00:05:42 +00:00
|
|
|
|
|
|
|
/// Represents filling an io_buffer_t. Very similar to io_pipe_t.
|
|
|
|
/// Bufferfills always target stdout.
|
|
|
|
class io_bufferfill_t : public io_data_t {
|
|
|
|
/// Write end. The other end is connected to an io_buffer_t.
|
|
|
|
const autoclose_fd_t write_fd_;
|
|
|
|
|
|
|
|
/// The receiving buffer.
|
|
|
|
const std::shared_ptr<io_buffer_t> buffer_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
void print() const override;
|
|
|
|
|
2019-02-01 09:58:06 +00:00
|
|
|
// The ctor is public to support make_shared() in the static create function below.
|
|
|
|
// Do not invoke this directly.
|
2019-02-01 00:05:42 +00:00
|
|
|
io_bufferfill_t(autoclose_fd_t write_fd, std::shared_ptr<io_buffer_t> buffer)
|
2019-12-29 23:14:08 +00:00
|
|
|
: io_data_t(io_mode_t::bufferfill, STDOUT_FILENO, write_fd.fd()),
|
2019-02-01 00:05:42 +00:00
|
|
|
write_fd_(std::move(write_fd)),
|
2019-12-29 23:51:22 +00:00
|
|
|
buffer_(std::move(buffer)) {
|
|
|
|
assert(write_fd_.valid() && "fd is not valid");
|
|
|
|
}
|
2019-02-01 00:05:42 +00:00
|
|
|
|
2019-11-20 06:17:30 +00:00
|
|
|
~io_bufferfill_t() override;
|
2019-02-01 00:05:42 +00:00
|
|
|
|
|
|
|
std::shared_ptr<io_buffer_t> buffer() const { return buffer_; }
|
|
|
|
|
2019-02-01 09:58:06 +00:00
|
|
|
/// Create an io_bufferfill_t which, when written from, fills a buffer with the contents.
|
2019-02-01 00:05:42 +00:00
|
|
|
/// \returns nullptr on failure, e.g. too many open fds.
|
|
|
|
///
|
2019-12-12 22:42:03 +00:00
|
|
|
/// \param conflicts A set of fds. The function ensures that any pipe it makes does
|
2019-02-01 00:05:42 +00:00
|
|
|
/// not conflict with an fd redirection in this list.
|
2019-12-12 22:42:03 +00:00
|
|
|
static shared_ptr<io_bufferfill_t> create(const fd_set_t &conflicts, size_t buffer_limit = 0);
|
2019-02-01 09:58:06 +00:00
|
|
|
|
|
|
|
/// Reset the receiver (possibly closing the write end of the pipe), and complete the fillthread
|
|
|
|
/// of the buffer. \return the buffer.
|
|
|
|
static std::shared_ptr<io_buffer_t> finish(std::shared_ptr<io_bufferfill_t> &&filler);
|
2019-02-01 00:05:42 +00:00
|
|
|
};
|
|
|
|
|
2018-05-28 06:56:20 +00:00
|
|
|
class output_stream_t;
|
2019-02-01 00:05:42 +00:00
|
|
|
|
|
|
|
/// An io_buffer_t is a buffer which can populate itself by reading from an fd.
|
|
|
|
/// It is not an io_data_t.
|
|
|
|
class io_buffer_t {
|
2016-05-02 03:32:40 +00:00
|
|
|
private:
|
2019-02-01 09:58:06 +00:00
|
|
|
friend io_bufferfill_t;
|
2019-02-01 00:05:42 +00:00
|
|
|
|
|
|
|
/// Buffer storing what we have read.
|
2018-05-28 08:27:26 +00:00
|
|
|
separated_buffer_t<std::string> buffer_;
|
2013-01-15 08:44:31 +00:00
|
|
|
|
2019-02-01 09:58:06 +00:00
|
|
|
/// Atomic flag indicating our fillthread should shut down.
|
2019-11-23 22:11:07 +00:00
|
|
|
relaxed_atomic_bool_t shutdown_fillthread_{false};
|
2019-02-01 09:58:06 +00:00
|
|
|
|
2019-11-23 23:52:53 +00:00
|
|
|
/// The future allowing synchronization with the background fillthread, if the fillthread is
|
|
|
|
/// running. The fillthread fulfills the corresponding promise when it exits.
|
|
|
|
std::future<void> fillthread_waiter_{};
|
2019-02-01 09:58:06 +00:00
|
|
|
|
|
|
|
/// Lock for appending.
|
|
|
|
std::mutex append_lock_{};
|
|
|
|
|
2020-02-05 01:49:07 +00:00
|
|
|
/// Read a bit, filling the buffer. The append lock must be held.
|
|
|
|
/// \return positive on success, 0 if closed, -1 on error (in which case errno will be set).
|
|
|
|
ssize_t read_once(int fd);
|
2019-02-01 09:58:06 +00:00
|
|
|
|
2020-02-05 01:49:07 +00:00
|
|
|
/// Begin the fill operation, reading from the given fd in the background.
|
|
|
|
void begin_filling(autoclose_fd_t readfd);
|
2019-02-01 09:58:06 +00:00
|
|
|
|
|
|
|
/// End the background fillthread operation.
|
|
|
|
void complete_background_fillthread();
|
2019-02-01 00:05:42 +00:00
|
|
|
|
2019-11-23 23:52:53 +00:00
|
|
|
/// Helper to return whether the fillthread is running.
|
|
|
|
bool fillthread_running() const { return fillthread_waiter_.valid(); }
|
|
|
|
|
2019-02-01 00:05:42 +00:00
|
|
|
public:
|
2020-01-25 00:08:56 +00:00
|
|
|
explicit io_buffer_t(size_t limit) : buffer_(limit) {}
|
2013-01-15 08:44:31 +00:00
|
|
|
|
2019-02-01 00:05:42 +00:00
|
|
|
~io_buffer_t();
|
2013-01-15 09:02:46 +00:00
|
|
|
|
2018-05-28 08:27:26 +00:00
|
|
|
/// Access the underlying buffer.
|
2019-02-01 09:58:06 +00:00
|
|
|
/// This requires that the background fillthread be none.
|
|
|
|
const separated_buffer_t<std::string> &buffer() const {
|
2019-11-23 23:52:53 +00:00
|
|
|
assert(!fillthread_running() && "Cannot access buffer during background fill");
|
2019-02-01 09:58:06 +00:00
|
|
|
return buffer_;
|
|
|
|
}
|
2016-05-02 03:32:40 +00:00
|
|
|
|
2018-05-28 08:27:26 +00:00
|
|
|
/// Function to append to the buffer.
|
2019-02-01 09:58:06 +00:00
|
|
|
void append(const char *ptr, size_t count) {
|
|
|
|
scoped_lock locker(append_lock_);
|
|
|
|
buffer_.append(ptr, ptr + count);
|
|
|
|
}
|
2013-01-15 09:07:30 +00:00
|
|
|
|
2018-05-28 06:56:20 +00:00
|
|
|
/// Appends data from a given output_stream_t.
|
|
|
|
/// Marks the receiver as discarded if the stream was discarded.
|
|
|
|
void append_from_stream(const output_stream_t &stream);
|
2013-01-15 08:44:31 +00:00
|
|
|
};
|
|
|
|
|
2019-11-10 22:00:30 +00:00
|
|
|
using io_data_ref_t = std::shared_ptr<const io_data_t>;
|
|
|
|
|
|
|
|
class io_chain_t : public std::vector<io_data_ref_t> {
|
2016-05-02 03:32:40 +00:00
|
|
|
public:
|
2019-11-10 22:00:30 +00:00
|
|
|
using std::vector<io_data_ref_t>::vector;
|
2019-02-03 01:54:48 +00:00
|
|
|
// user-declared ctor to allow const init. Do not default this, it will break the build.
|
|
|
|
io_chain_t() {}
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2019-11-10 22:00:30 +00:00
|
|
|
void remove(const io_data_ref_t &element);
|
|
|
|
void push_back(io_data_ref_t element);
|
Big fat refactoring of how redirections work. In fish 1.x and 2.0.0, the redirections for a process were flattened into a big list associated with the job, so there was no way to tell which redirections applied to each process. Each process therefore got all the redirections associated with the job. See https://github.com/fish-shell/fish-shell/issues/877 for how this could manifest.
With this change, jobs only track their block-level redirections. Process level redirections are correctly associated with the process, and at exec time we stitch them together (block, pipe, and process redirects).
This fixes the weird issues where redirects bleed across pipelines (like #877), and also allows us to play with the order in which redirections are applied, since the final list is constructed right before it's needed. This lets us put pipes after block level redirections but before process level redirections, so that a 2>&1-type redirection gets picked up after the pipe, i.e. it should fix https://github.com/fish-shell/fish-shell/issues/110
This is a significant change. The tests all pass. Cross your fingers.
2013-08-19 23:16:41 +00:00
|
|
|
void append(const io_chain_t &chain);
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2019-11-10 22:00:30 +00:00
|
|
|
/// \return the last io redirection in the chain for the specified file descriptor, or nullptr
|
|
|
|
/// if none.
|
|
|
|
io_data_ref_t io_for_fd(int fd) const;
|
2019-12-11 01:05:17 +00:00
|
|
|
|
2019-12-13 00:44:24 +00:00
|
|
|
/// Attempt to resolve a list of redirection specs to IOs, appending to 'this'.
|
|
|
|
/// \return true on success, false on error, in which case an error will have been printed.
|
2019-12-13 01:27:48 +00:00
|
|
|
bool append_from_specs(const redirection_spec_list_t &specs, const wcstring &pwd);
|
2019-12-13 00:44:24 +00:00
|
|
|
|
2019-12-11 01:05:17 +00:00
|
|
|
/// Output debugging information to stderr.
|
|
|
|
void print() const;
|
2019-12-12 22:42:03 +00:00
|
|
|
|
|
|
|
/// \return the set of redirected FDs.
|
|
|
|
fd_set_t fd_set() const;
|
2012-08-15 07:57:56 +00:00
|
|
|
};
|
2005-10-08 11:20:51 +00:00
|
|
|
|
2019-02-01 00:05:42 +00:00
|
|
|
/// Helper type returned from making autoclose pipes.
|
|
|
|
struct autoclose_pipes_t {
|
|
|
|
/// Read end of the pipe.
|
|
|
|
autoclose_fd_t read;
|
|
|
|
|
|
|
|
/// Write end of the pipe.
|
|
|
|
autoclose_fd_t write;
|
2019-03-24 21:27:23 +00:00
|
|
|
|
|
|
|
autoclose_pipes_t() = default;
|
|
|
|
autoclose_pipes_t(autoclose_fd_t r, autoclose_fd_t w)
|
|
|
|
: read(std::move(r)), write(std::move(w)) {}
|
2019-02-01 00:05:42 +00:00
|
|
|
};
|
|
|
|
/// Call pipe(), populating autoclose fds, avoiding conflicts.
|
|
|
|
/// The pipes are marked CLO_EXEC.
|
|
|
|
/// \return pipes on success, none() on error.
|
2019-12-12 22:42:03 +00:00
|
|
|
maybe_t<autoclose_pipes_t> make_autoclose_pipes(const fd_set_t &fdset);
|
2019-01-28 21:26:22 +00:00
|
|
|
|
2019-12-12 22:42:03 +00:00
|
|
|
/// If the given fd is present in \p fdset, duplicates it repeatedly until an fd not used in the set
|
2019-12-29 22:57:16 +00:00
|
|
|
/// is found or we run out. If we return a new fd or an error, closes the old one. Marks the fd as
|
|
|
|
/// cloexec. \returns invalid fd on failure (in which case the given fd is still closed).
|
|
|
|
autoclose_fd_t move_fd_to_unused(autoclose_fd_t fd, const fd_set_t &fdset);
|
2019-01-28 21:26:22 +00:00
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
/// Class representing the output that a builtin can generate.
|
|
|
|
class output_stream_t {
|
|
|
|
private:
|
2018-05-30 04:11:34 +00:00
|
|
|
/// Storage for our data.
|
|
|
|
separated_buffer_t<wcstring> buffer_;
|
2016-05-02 03:32:40 +00:00
|
|
|
|
2018-05-30 04:11:34 +00:00
|
|
|
// No copying.
|
|
|
|
output_stream_t(const output_stream_t &s) = delete;
|
|
|
|
void operator=(const output_stream_t &s) = delete;
|
2017-07-27 03:17:04 +00:00
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
public:
|
2018-05-30 04:11:34 +00:00
|
|
|
output_stream_t(size_t buffer_limit) : buffer_(buffer_limit) {}
|
2016-05-02 03:32:40 +00:00
|
|
|
|
2018-05-30 04:11:34 +00:00
|
|
|
void append(const wcstring &s) { buffer_.append(s.begin(), s.end()); }
|
2016-05-02 03:32:40 +00:00
|
|
|
|
2018-05-30 04:11:50 +00:00
|
|
|
separated_buffer_t<wcstring> &buffer() { return buffer_; }
|
|
|
|
|
2018-05-30 04:11:34 +00:00
|
|
|
const separated_buffer_t<wcstring> &buffer() const { return buffer_; }
|
2016-05-02 03:32:40 +00:00
|
|
|
|
2019-03-12 21:06:01 +00:00
|
|
|
void append(const wchar_t *s) { append(s, std::wcslen(s)); }
|
2016-05-02 03:32:40 +00:00
|
|
|
|
2018-05-30 04:11:34 +00:00
|
|
|
void append(wchar_t s) { append(&s, 1); }
|
2017-07-27 03:17:04 +00:00
|
|
|
|
2018-05-30 04:11:34 +00:00
|
|
|
void append(const wchar_t *s, size_t amt) { buffer_.append(s, s + amt); }
|
|
|
|
|
|
|
|
void push_back(wchar_t c) { append(c); }
|
2016-05-02 03:32:40 +00:00
|
|
|
|
|
|
|
void append_format(const wchar_t *format, ...) {
|
2015-09-21 18:24:49 +00:00
|
|
|
va_list va;
|
|
|
|
va_start(va, format);
|
2018-05-30 04:11:34 +00:00
|
|
|
append_formatv(format, va);
|
2015-09-21 18:24:49 +00:00
|
|
|
va_end(va);
|
|
|
|
}
|
2016-05-02 03:32:40 +00:00
|
|
|
|
2018-05-30 04:11:34 +00:00
|
|
|
void append_formatv(const wchar_t *format, va_list va) { append(vformat_string(format, va)); }
|
2017-07-27 03:17:04 +00:00
|
|
|
|
2018-05-30 04:11:34 +00:00
|
|
|
wcstring contents() const { return buffer_.newline_serialized(); }
|
2015-09-21 18:24:49 +00:00
|
|
|
};
|
|
|
|
|
2016-05-02 03:32:40 +00:00
|
|
|
struct io_streams_t {
|
2015-09-21 18:24:49 +00:00
|
|
|
output_stream_t out;
|
|
|
|
output_stream_t err;
|
2016-05-02 03:32:40 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
// fd representing stdin. This is not closed by the destructor.
|
2019-02-17 22:16:47 +00:00
|
|
|
int stdin_fd{-1};
|
2016-05-02 03:32:40 +00:00
|
|
|
|
|
|
|
// Whether stdin is "directly redirected," meaning it is the recipient of a pipe (foo | cmd) or
|
|
|
|
// direct redirection (cmd < foo.txt). An "indirect redirection" would be e.g. begin ; cmd ; end
|
|
|
|
// < foo.txt
|
2019-02-17 22:16:47 +00:00
|
|
|
bool stdin_is_directly_redirected{false};
|
2016-05-02 03:32:40 +00:00
|
|
|
|
|
|
|
// Indicates whether stdout and stderr are redirected (e.g. to a file or piped).
|
2019-02-17 22:16:47 +00:00
|
|
|
bool out_is_redirected{false};
|
|
|
|
bool err_is_redirected{false};
|
2016-05-02 03:32:40 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
// Actual IO redirections. This is only used by the source builtin. Unowned.
|
2019-02-17 22:16:47 +00:00
|
|
|
const io_chain_t *io_chain{nullptr};
|
|
|
|
|
2020-04-26 00:11:36 +00:00
|
|
|
// The pgid of the job, if any. This enables builtins which run more code like eval() to share
|
|
|
|
// pgid.
|
|
|
|
// TODO: this is awkwardly placed, consider just embedding a lineage here.
|
|
|
|
maybe_t<pid_t> parent_pgid{};
|
|
|
|
|
2019-02-17 22:16:47 +00:00
|
|
|
// io_streams_t cannot be copied.
|
|
|
|
io_streams_t(const io_streams_t &) = delete;
|
|
|
|
void operator=(const io_streams_t &) = delete;
|
|
|
|
|
|
|
|
explicit io_streams_t(size_t read_limit) : out(read_limit), err(read_limit), stdin_fd(-1) {}
|
2015-09-21 18:24:49 +00:00
|
|
|
};
|
|
|
|
|
2005-10-08 11:20:51 +00:00
|
|
|
#endif
|