Make output_stream_t::append() fallible

Allow errors encountered by certain implementations of `output_stream_t` when
writing to the output sink to be bubbled back to the caller.
This commit is contained in:
Mahmoud Al-Qudsi 2022-10-08 12:24:27 -05:00
parent dd0fd88736
commit 8e97fcb22c
2 changed files with 60 additions and 39 deletions

View File

@ -206,9 +206,10 @@ void io_chain_t::push_back(io_data_ref_t element) {
std::vector<io_data_ref_t>::push_back(std::move(element)); std::vector<io_data_ref_t>::push_back(std::move(element));
} }
void io_chain_t::append(const io_chain_t &chain) { bool io_chain_t::append(const io_chain_t &chain) {
assert(&chain != this && "Cannot append self to self"); assert(&chain != this && "Cannot append self to self");
this->insert(this->end(), chain.begin(), chain.end()); this->insert(this->end(), chain.begin(), chain.end());
return true;
} }
bool io_chain_t::append_from_specs(const redirection_spec_list_t &specs, const wcstring &pwd) { bool io_chain_t::append_from_specs(const redirection_spec_list_t &specs, const wcstring &pwd) {
@ -308,21 +309,24 @@ shared_ptr<const io_data_t> io_chain_t::io_for_fd(int fd) const {
return nullptr; return nullptr;
} }
void output_stream_t::append_narrow_buffer(const separated_buffer_t &buffer) { bool output_stream_t::append_narrow_buffer(const separated_buffer_t &buffer) {
for (const auto &rhs_elem : buffer.elements()) { for (const auto &rhs_elem : buffer.elements()) {
append_with_separation(str2wcstring(rhs_elem.contents), rhs_elem.separation, false); if (!append_with_separation(str2wcstring(rhs_elem.contents), rhs_elem.separation, false)) {
return false;
} }
} }
return true;
}
void output_stream_t::append_with_separation(const wchar_t *s, size_t len, separation_type_t type, bool output_stream_t::append_with_separation(const wchar_t *s, size_t len, separation_type_t type,
bool want_newline) { bool want_newline) {
if (type == separation_type_t::explicitly && want_newline) { if (type == separation_type_t::explicitly && want_newline) {
// Try calling "append" less - it might write() to an fd // Try calling "append" less - it might write() to an fd
wcstring buf{s, len}; wcstring buf{s, len};
buf.push_back(L'\n'); buf.push_back(L'\n');
append(buf); return append(buf);
} else { } else {
append(s, len); return append(s, len);
} }
} }
@ -330,8 +334,8 @@ const wcstring &output_stream_t::contents() const { return g_empty_string; }
int output_stream_t::flush_and_check_error() { return STATUS_CMD_OK; } int output_stream_t::flush_and_check_error() { return STATUS_CMD_OK; }
void fd_output_stream_t::append(const wchar_t *s, size_t amt) { bool fd_output_stream_t::append(const wchar_t *s, size_t amt) {
if (errored_) return; if (errored_) return false;
int res = wwrite_to_fd(s, amt, this->fd_); int res = wwrite_to_fd(s, amt, this->fd_);
if (res < 0) { if (res < 0) {
// TODO: this error is too aggressive, e.g. if we got SIGINT we should not complain. // TODO: this error is too aggressive, e.g. if we got SIGINT we should not complain.
@ -340,6 +344,7 @@ void fd_output_stream_t::append(const wchar_t *s, size_t amt) {
} }
errored_ = true; errored_ = true;
} }
return !errored_;
} }
int fd_output_stream_t::flush_and_check_error() { int fd_output_stream_t::flush_and_check_error() {
@ -347,20 +352,23 @@ int fd_output_stream_t::flush_and_check_error() {
return errored_ ? STATUS_CMD_ERROR : STATUS_CMD_OK; return errored_ ? STATUS_CMD_ERROR : STATUS_CMD_OK;
} }
void null_output_stream_t::append(const wchar_t *, size_t) {} bool null_output_stream_t::append(const wchar_t *, size_t) { return true; }
void string_output_stream_t::append(const wchar_t *s, size_t amt) { contents_.append(s, amt); } bool string_output_stream_t::append(const wchar_t *s, size_t amt) {
contents_.append(s, amt);
return true;
}
const wcstring &string_output_stream_t::contents() const { return contents_; } const wcstring &string_output_stream_t::contents() const { return contents_; }
void buffered_output_stream_t::append(const wchar_t *s, size_t amt) { bool buffered_output_stream_t::append(const wchar_t *s, size_t amt) {
buffer_->append(wcs2string(s, amt)); return buffer_->append(wcs2string(s, amt));
} }
void buffered_output_stream_t::append_with_separation(const wchar_t *s, size_t len, bool buffered_output_stream_t::append_with_separation(const wchar_t *s, size_t len,
separation_type_t type, bool want_newline) { separation_type_t type, bool want_newline) {
UNUSED(want_newline); UNUSED(want_newline);
buffer_->append(wcs2string(s, len), type); return buffer_->append(wcs2string(s, len), type);
} }
int buffered_output_stream_t::flush_and_check_error() { int buffered_output_stream_t::flush_and_check_error() {

View File

@ -16,6 +16,8 @@
#include "fds.h" #include "fds.h"
#include "global_safety.h" #include "global_safety.h"
#include "redirection.h" #include "redirection.h"
#include "signal.h"
#include "topic_monitor.h"
using std::shared_ptr; using std::shared_ptr;
@ -82,25 +84,27 @@ class separated_buffer_t : noncopyable_t {
const std::vector<element_t> &elements() const { return elements_; } const std::vector<element_t> &elements() const { return elements_; }
/// Append a string \p str of a given length \p len, with separation type \p sep. /// Append a string \p str of a given length \p len, with separation type \p sep.
void append(const char *str, size_t len, separation_type_t sep = separation_type_t::inferred) { bool append(const char *str, size_t len, separation_type_t sep = separation_type_t::inferred) {
if (!try_add_size(len)) return; if (!try_add_size(len)) return false;
// Try merging with the last element. // Try merging with the last element.
if (sep == separation_type_t::inferred && last_inferred()) { if (sep == separation_type_t::inferred && last_inferred()) {
elements_.back().contents.append(str, len); elements_.back().contents.append(str, len);
} else { } else {
elements_.emplace_back(std::string(str, len), sep); elements_.emplace_back(std::string(str, len), sep);
} }
return true;
} }
/// Append a string \p str with separation type \p sep. /// Append a string \p str with separation type \p sep.
void append(std::string &&str, separation_type_t sep = separation_type_t::inferred) { bool append(std::string &&str, separation_type_t sep = separation_type_t::inferred) {
if (!try_add_size(str.size())) return; if (!try_add_size(str.size())) return false;
// Try merging with the last element. // Try merging with the last element.
if (sep == separation_type_t::inferred && last_inferred()) { if (sep == separation_type_t::inferred && last_inferred()) {
elements_.back().contents.append(str); elements_.back().contents.append(str);
} else { } else {
elements_.emplace_back(std::move(str), sep); elements_.emplace_back(std::move(str), sep);
} }
return true;
} }
/// Remove all elements and unset the discard flag. /// Remove all elements and unset the discard flag.
@ -280,8 +284,8 @@ class io_buffer_t {
~io_buffer_t(); ~io_buffer_t();
/// Append a string to the buffer. /// Append a string to the buffer.
void append(std::string &&str, separation_type_t type = separation_type_t::inferred) { bool append(std::string &&str, separation_type_t type = separation_type_t::inferred) {
buffer_.acquire()->append(std::move(str), type); return buffer_.acquire()->append(std::move(str), type);
} }
/// \return true if output was discarded due to exceeding the read limit. /// \return true if output was discarded due to exceeding the read limit.
@ -328,7 +332,7 @@ class io_chain_t : public std::vector<io_data_ref_t> {
void remove(const io_data_ref_t &element); void remove(const io_data_ref_t &element);
void push_back(io_data_ref_t element); void push_back(io_data_ref_t element);
void append(const io_chain_t &chain); bool append(const io_chain_t &chain);
/// \return the last io redirection in the chain for the specified file descriptor, or nullptr /// \return the last io redirection in the chain for the specified file descriptor, or nullptr
/// if none. /// if none.
@ -347,7 +351,7 @@ class io_chain_t : public std::vector<io_data_ref_t> {
class output_stream_t : noncopyable_t, nonmovable_t { class output_stream_t : noncopyable_t, nonmovable_t {
public: public:
/// Required override point. The output stream receives a string \p s with \p amt chars. /// Required override point. The output stream receives a string \p s with \p amt chars.
virtual void append(const wchar_t *s, size_t amt) = 0; virtual bool append(const wchar_t *s, size_t amt) = 0;
/// \return any internally buffered contents. /// \return any internally buffered contents.
/// This is only implemented for a string_output_stream; others flush data to their underlying /// This is only implemented for a string_output_stream; others flush data to their underlying
@ -361,35 +365,39 @@ class output_stream_t : noncopyable_t, nonmovable_t {
/// An optional override point. This is for explicit separation. /// An optional override point. This is for explicit separation.
/// \param want_newline this is true if the output item should be ended with a newline. This /// \param want_newline this is true if the output item should be ended with a newline. This
/// is only relevant if we are printing the output to a stream, /// is only relevant if we are printing the output to a stream,
virtual void append_with_separation(const wchar_t *s, size_t len, separation_type_t type, virtual bool append_with_separation(const wchar_t *s, size_t len, separation_type_t type,
bool want_newline = true); bool want_newline = true);
/// The following are all convenience overrides. /// The following are all convenience overrides.
void append_with_separation(const wcstring &s, separation_type_t type, bool append_with_separation(const wcstring &s, separation_type_t type,
bool want_newline = true) { bool want_newline = true) {
append_with_separation(s.data(), s.size(), type, want_newline); return append_with_separation(s.data(), s.size(), type, want_newline);
} }
/// Append a string. /// Append a string.
void append(const wcstring &s) { append(s.data(), s.size()); } bool append(const wcstring &s) { return append(s.data(), s.size()); }
void append(const wchar_t *s) { append(s, std::wcslen(s)); } bool append(const wchar_t *s) { return append(s, std::wcslen(s)); }
/// Append a char. /// Append a char.
void append(wchar_t s) { append(&s, 1); } bool append(wchar_t s) { return append(&s, 1); }
void push_back(wchar_t c) { append(c); } bool push_back(wchar_t c) { return append(c); }
// Append data from a narrow buffer, widening it. // Append data from a narrow buffer, widening it.
void append_narrow_buffer(const separated_buffer_t &buffer); bool append_narrow_buffer(const separated_buffer_t &buffer);
/// Append a format string. /// Append a format string.
void append_format(const wchar_t *format, ...) { bool append_format(const wchar_t *format, ...) {
va_list va; va_list va;
va_start(va, format); va_start(va, format);
append_formatv(format, va); bool r = append_formatv(format, va);
va_end(va); va_end(va);
return r;
} }
void append_formatv(const wchar_t *format, va_list va) { append(vformat_string(format, va)); } bool append_formatv(const wchar_t *format, va_list va) {
return append(vformat_string(format, va));
}
output_stream_t() = default; output_stream_t() = default;
virtual ~output_stream_t() = default; virtual ~output_stream_t() = default;
@ -397,7 +405,7 @@ class output_stream_t : noncopyable_t, nonmovable_t {
/// A null output stream which ignores all writes. /// A null output stream which ignores all writes.
class null_output_stream_t final : public output_stream_t { class null_output_stream_t final : public output_stream_t {
virtual void append(const wchar_t *s, size_t amt) override; virtual bool append(const wchar_t *s, size_t amt) override;
}; };
/// An output stream for builtins which outputs to an fd. /// An output stream for builtins which outputs to an fd.
@ -405,16 +413,21 @@ class null_output_stream_t final : public output_stream_t {
class fd_output_stream_t final : public output_stream_t { class fd_output_stream_t final : public output_stream_t {
public: public:
/// Construct from a file descriptor, which must be nonegative. /// Construct from a file descriptor, which must be nonegative.
explicit fd_output_stream_t(int fd) : fd_(fd) { assert(fd_ >= 0 && "Invalid fd"); } explicit fd_output_stream_t(int fd) : fd_(fd), sigcheck_(topic_t::sighupint) {
assert(fd_ >= 0 && "Invalid fd");
}
int flush_and_check_error() override; int flush_and_check_error() override;
void append(const wchar_t *s, size_t amt) override; bool append(const wchar_t *s, size_t amt) override;
private: private:
/// The file descriptor to write to. /// The file descriptor to write to.
const int fd_; const int fd_;
/// Used to check if a SIGINT has been received when EINTR is encountered
sigchecker_t sigcheck_;
/// Whether we have received an error. /// Whether we have received an error.
bool errored_{false}; bool errored_{false};
}; };
@ -423,7 +436,7 @@ class fd_output_stream_t final : public output_stream_t {
class string_output_stream_t final : public output_stream_t { class string_output_stream_t final : public output_stream_t {
public: public:
string_output_stream_t() = default; string_output_stream_t() = default;
void append(const wchar_t *s, size_t amt) override; bool append(const wchar_t *s, size_t amt) override;
/// \return the wcstring containing the output. /// \return the wcstring containing the output.
const wcstring &contents() const override; const wcstring &contents() const override;
@ -440,8 +453,8 @@ class buffered_output_stream_t final : public output_stream_t {
assert(buffer_ && "Buffer must not be null"); assert(buffer_ && "Buffer must not be null");
} }
void append(const wchar_t *s, size_t amt) override; bool append(const wchar_t *s, size_t amt) override;
void append_with_separation(const wchar_t *s, size_t len, separation_type_t type, bool append_with_separation(const wchar_t *s, size_t len, separation_type_t type,
bool want_newline) override; bool want_newline) override;
int flush_and_check_error() override; int flush_and_check_error() override;