diff --git a/src/exec.cpp b/src/exec.cpp index e9f1818ad..83adbb60c 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -745,8 +745,8 @@ static bool exec_block_or_func_process(parser_t &parser, const std::shared_ptrredirection_specs(), parser.vars().get_pwd_slash())) { - // Error. - return false; + // If the redirection to a file failed, append_from_specs closes the corresponding handle + // allowing us to recover from failure mid-way through execution of a piped job to e.g. + // recover from loss of terminal control, etc. + + // It is unsafe to abort execution in the middle of a multi-command job as we might end up + // trying to resume execution without control of the terminal. + if (p->is_first_in_job) { + // It's OK to abort here + return false; + } + // Otherwise, just rely on the closed fd to take care of things. It's also probably more + // semantically correct! } // Read pipe goes last. diff --git a/src/io.cpp b/src/io.cpp index 081354b1b..dd71e6714 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -253,6 +253,10 @@ bool io_chain_t::append_from_specs(const redirection_spec_list_t &specs, const w FLOGF(warning, FILE_ERROR, spec.target.c_str()); if (should_flog(warning)) wperror(L"open"); } + // If opening a file fails, insert a closed FD instead of the file redirection + // and return false. This lets execution potentially recover and at least gives + // the shell a chance to gracefully regain control of the shell (see #7038). + this->push_back(make_unique(spec.fd)); return false; } this->push_back(std::make_shared(spec.fd, std::move(file))); diff --git a/src/io.h b/src/io.h index 4eab047cb..bb9f3e9e8 100644 --- a/src/io.h +++ b/src/io.h @@ -216,7 +216,9 @@ class io_file_t : public io_data_t { 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"); + // Invalid file redirections are replaced with a closed fd, so the following + // assertion isn't guaranteed to pass: + // assert(file_fd_.valid() && "File is not valid"); } ~io_file_t() override;