2019-01-28 21:26:22 +00:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
|
|
|
|
|
|
|
#include "redirection.h"
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
2019-12-13 00:44:24 +00:00
|
|
|
#include "io.h"
|
2019-10-13 22:50:48 +00:00
|
|
|
#include "wutil.h"
|
|
|
|
|
2019-06-10 00:43:25 +00:00
|
|
|
#define NOCLOB_ERROR _(L"The file '%ls' already exists")
|
2019-01-28 21:26:22 +00:00
|
|
|
|
2019-06-10 00:43:25 +00:00
|
|
|
#define FILE_ERROR _(L"An error occurred while redirecting file '%ls'")
|
2019-01-28 21:26:22 +00:00
|
|
|
|
|
|
|
/// Base open mode to pass to calls to open.
|
|
|
|
#define OPEN_MASK 0666
|
|
|
|
|
|
|
|
dup2_list_t::~dup2_list_t() = default;
|
|
|
|
|
2019-12-13 00:44:24 +00:00
|
|
|
maybe_t<int> redirection_spec_t::get_target_as_fd() const {
|
|
|
|
errno = 0;
|
|
|
|
int result = fish_wcstoi(target.c_str());
|
|
|
|
if (errno || result < 0) return none();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int redirection_spec_t::oflags() const {
|
|
|
|
switch (mode) {
|
|
|
|
case redirection_mode_t::append:
|
|
|
|
return O_CREAT | O_APPEND | O_WRONLY;
|
|
|
|
case redirection_mode_t::overwrite:
|
|
|
|
return O_CREAT | O_WRONLY | O_TRUNC;
|
|
|
|
case redirection_mode_t::noclob:
|
|
|
|
return O_CREAT | O_EXCL | O_WRONLY;
|
|
|
|
case redirection_mode_t::input:
|
|
|
|
return O_RDONLY;
|
|
|
|
case redirection_mode_t::fd:
|
|
|
|
DIE("Not a file redirection");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-28 21:26:22 +00:00
|
|
|
maybe_t<dup2_list_t> dup2_list_t::resolve_chain(const io_chain_t &io_chain) {
|
|
|
|
ASSERT_IS_NOT_FORKED_CHILD();
|
|
|
|
dup2_list_t result;
|
|
|
|
for (const auto &io_ref : io_chain) {
|
|
|
|
switch (io_ref->io_mode) {
|
|
|
|
case io_mode_t::file: {
|
|
|
|
// Here we definitely do not want to set CLO_EXEC because our child needs access.
|
|
|
|
// Open the file.
|
|
|
|
const io_file_t *io_file = static_cast<const io_file_t *>(io_ref.get());
|
2019-12-12 22:42:03 +00:00
|
|
|
autoclose_fd_t file_fd{wopen(io_file->filename, io_file->flags, OPEN_MASK)};
|
|
|
|
if (!file_fd.valid()) {
|
2019-01-28 21:26:22 +00:00
|
|
|
if ((io_file->flags & O_EXCL) && (errno == EEXIST)) {
|
2019-06-10 00:43:25 +00:00
|
|
|
debug(1, NOCLOB_ERROR, io_file->filename.c_str());
|
2019-01-28 21:26:22 +00:00
|
|
|
} else {
|
2019-06-10 00:43:25 +00:00
|
|
|
debug(1, FILE_ERROR, io_file->filename.c_str());
|
2019-01-28 21:26:22 +00:00
|
|
|
if (should_debug(1)) wperror(L"open");
|
|
|
|
}
|
|
|
|
return none();
|
|
|
|
}
|
|
|
|
|
2019-02-10 06:14:42 +00:00
|
|
|
// If by chance we got the file we want, we're done. Otherwise move the fd to an
|
|
|
|
// unused place and dup2 it.
|
2019-01-28 21:26:22 +00:00
|
|
|
// Note move_fd_to_unused() will close the incoming file_fd.
|
2019-12-12 22:42:03 +00:00
|
|
|
if (file_fd.fd() != io_file->fd) {
|
|
|
|
file_fd = move_fd_to_unused(std::move(file_fd), io_chain.fd_set(),
|
|
|
|
false /* cloexec */);
|
|
|
|
if (!file_fd.valid()) {
|
2019-06-10 00:43:25 +00:00
|
|
|
debug(1, FILE_ERROR, io_file->filename.c_str());
|
2019-01-28 21:26:22 +00:00
|
|
|
if (should_debug(1)) wperror(L"dup");
|
|
|
|
return none();
|
|
|
|
}
|
|
|
|
}
|
2019-12-12 22:42:03 +00:00
|
|
|
assert(file_fd.valid() && "Should have a valid file_fd");
|
2019-01-28 21:26:22 +00:00
|
|
|
|
|
|
|
// Mark our dup2 and our close actions.
|
2019-12-12 22:42:03 +00:00
|
|
|
result.add_dup2(file_fd.fd(), io_file->fd);
|
|
|
|
result.add_close(file_fd.fd());
|
|
|
|
|
|
|
|
// Store our file.
|
|
|
|
result.opened_fds_.push_back(std::move(file_fd));
|
2019-01-28 21:26:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case io_mode_t::close: {
|
|
|
|
const io_close_t *io = static_cast<const io_close_t *>(io_ref.get());
|
|
|
|
result.add_close(io->fd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case io_mode_t::fd: {
|
|
|
|
const io_fd_t *io = static_cast<const io_fd_t *>(io_ref.get());
|
|
|
|
result.add_dup2(io->old_fd, io->fd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case io_mode_t::pipe: {
|
|
|
|
const io_pipe_t *io = static_cast<const io_pipe_t *>(io_ref.get());
|
2019-02-01 00:05:42 +00:00
|
|
|
result.add_dup2(io->pipe_fd(), io->fd);
|
|
|
|
result.add_close(io->pipe_fd());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case io_mode_t::bufferfill: {
|
|
|
|
const io_bufferfill_t *io = static_cast<const io_bufferfill_t *>(io_ref.get());
|
|
|
|
result.add_dup2(io->write_fd(), io->fd);
|
|
|
|
result.add_close(io->write_fd());
|
2019-01-28 21:26:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {std::move(result)};
|
|
|
|
}
|
2019-02-02 20:52:51 +00:00
|
|
|
|
|
|
|
int dup2_list_t::fd_for_target_fd(int target) const {
|
|
|
|
// Paranoia.
|
|
|
|
if (target < 0) {
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
// Note we can simply walk our action list backwards, looking for src -> target dups.
|
|
|
|
int cursor = target;
|
|
|
|
for (auto iter = actions_.rbegin(); iter != actions_.rend(); ++iter) {
|
|
|
|
if (iter->target == cursor) {
|
|
|
|
// cursor is replaced by iter->src
|
|
|
|
cursor = iter->src;
|
|
|
|
} else if (iter->src == cursor && iter->target < 0) {
|
|
|
|
// cursor is closed.
|
|
|
|
cursor = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cursor;
|
|
|
|
}
|