diff --git a/src/exec.cpp b/src/exec.cpp index d70c726cd..a69d7b904 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -52,17 +52,14 @@ /// Number of calls to fork() or posix_spawn(). static relaxed_atomic_t s_fork_count{0}; -pgroup_provenance_t get_pgroup_provenance(const shared_ptr &j, - const job_lineage_t &lineage) { +pgroup_provenance_t get_pgroup_provenance(const shared_ptr &j) { bool first_proc_is_internal = j->processes.front()->is_internal(); bool has_internal = j->has_internal_proc(); bool has_external = j->has_external_proc(); assert(first_proc_is_internal ? has_internal : has_external); - if (lineage.job_tree && lineage.job_tree->get_pgid().has_value() && j->is_foreground()) { - // Our lineage indicates a pgid. This means the job is "nested" as a function or block - // inside another job, which has a real pgroup. We're going to use that, unless it's - // backgrounded, in which case it should not inherit a pgroup. + if (j->job_tree->get_pgid().has_value()) { + // Our job tree already has a pgid. return pgroup_provenance_t::lineage; } else if (!j->wants_job_control()) { // This job doesn't need job control, it can just live in the fish pgroup. @@ -928,7 +925,7 @@ static bool allow_exec_with_background_jobs(parser_t &parser) { } } -bool exec_job(parser_t &parser, const shared_ptr &j, const job_lineage_t &lineage) { +bool exec_job(parser_t &parser, const shared_ptr &j, const io_chain_t &block_io) { assert(j && "null job_t passed to exec_job!"); // Set to true if something goes wrong while executing the job, in which case the cleanup @@ -948,7 +945,7 @@ bool exec_job(parser_t &parser, const shared_ptr &j, const job_lineage_t assert(j->pgid == INVALID_PID && "Should not yet have a pid."); switch (j->pgroup_provenance) { case pgroup_provenance_t::lineage: { - auto pgid = lineage.job_tree->get_pgid(); + auto pgid = j->job_tree->get_pgid(); assert(pgid && *pgid != INVALID_PID && "Should have valid pgid"); j->pgid = *pgid; break; @@ -971,7 +968,7 @@ bool exec_job(parser_t &parser, const shared_ptr &j, const job_lineage_t const size_t stdout_read_limit = parser.libdata().read_limit; // Get the list of all FDs so we can ensure our pipes do not conflict. - fd_set_t conflicts = lineage.block_io.fd_set(); + fd_set_t conflicts = block_io.fd_set(); for (const auto &p : j->processes) { for (const auto &spec : p->redirection_specs()) { conflicts.add(spec.fd); @@ -985,7 +982,7 @@ bool exec_job(parser_t &parser, const shared_ptr &j, const job_lineage_t return false; } - internal_exec(parser.vars(), j.get(), lineage.block_io); + internal_exec(parser.vars(), j.get(), block_io); // internal_exec only returns if it failed to set up redirections. // In case of an successful exec, this code is not reached. int status = j->flags().negate ? 0 : 1; @@ -1034,8 +1031,8 @@ bool exec_job(parser_t &parser, const shared_ptr &j, const job_lineage_t if (p.get() == deferred_process) { deferred_pipes = std::move(proc_pipes); } else { - if (!exec_process_in_job(parser, p.get(), j, lineage.block_io, std::move(proc_pipes), - conflicts, deferred_pipes, stdout_read_limit)) { + if (!exec_process_in_job(parser, p.get(), j, block_io, std::move(proc_pipes), conflicts, + deferred_pipes, stdout_read_limit)) { exec_error = true; break; } @@ -1046,9 +1043,8 @@ bool exec_job(parser_t &parser, const shared_ptr &j, const job_lineage_t // Now execute any deferred process. if (!exec_error && deferred_process) { assert(deferred_pipes.write.valid() && "Deferred process should always have a write pipe"); - if (!exec_process_in_job(parser, deferred_process, j, lineage.block_io, - std::move(deferred_pipes), conflicts, {}, stdout_read_limit, - true)) { + if (!exec_process_in_job(parser, deferred_process, j, block_io, std::move(deferred_pipes), + conflicts, {}, stdout_read_limit, true)) { exec_error = true; } } diff --git a/src/exec.h b/src/exec.h index 1ac767b63..02aa63488 100644 --- a/src/exec.h +++ b/src/exec.h @@ -13,7 +13,7 @@ #define PIPE_ERROR _(L"An error occurred while setting up pipe") /// Execute the processes specified by \p j in the parser \p. -bool exec_job(parser_t &parser, const std::shared_ptr &j, const job_lineage_t &lineage); +bool exec_job(parser_t &parser, const std::shared_ptr &j, const io_chain_t &block_io); /// Evaluate a command. /// @@ -39,8 +39,7 @@ void exec_close(int fd); /// Compute the "pgroup provenance" for a job. This is a description of how the pgroup is /// assigned. It's factored out because the logic has subtleties, and this centralizes it. -pgroup_provenance_t get_pgroup_provenance(const std::shared_ptr &j, - const job_lineage_t &lineage); +pgroup_provenance_t get_pgroup_provenance(const std::shared_ptr &j); /// Add signals that should be masked for external processes in this job. bool blocked_signals_for_job(const job_t &job, sigset_t *sigmask); diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 18e27954b..52ec7b07b 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -88,10 +88,13 @@ static redirection_spec_t get_stderr_merge() { return redirection_spec_t{STDERR_FILENO, redirection_mode_t::fd, stdout_fileno_str}; } -parse_execution_context_t::parse_execution_context_t(parsed_source_ref_t pstree, parser_t *p, +parse_execution_context_t::parse_execution_context_t(parsed_source_ref_t pstree, const operation_context_t &ctx, - job_lineage_t lineage) - : pstree(std::move(pstree)), parser(p), ctx(ctx), lineage(std::move(lineage)) {} + io_chain_t block_io) + : pstree(std::move(pstree)), + parser(ctx.parser.get()), + ctx(ctx), + block_io(std::move(block_io)) {} // Utilities @@ -1251,7 +1254,7 @@ end_execution_reason_t parse_execution_context_t::run_1_job(tnode_t job_ bool wants_job_control = (job_control_mode == job_control_t::all) || ((job_control_mode == job_control_t::interactive) && parser->is_interactive()) || - (lineage.job_tree && lineage.job_tree->wants_job_control()); + (ctx.job_tree && ctx.job_tree->wants_job_control()); job_t::properties_t props{}; props.wants_terminal = wants_job_control && !ld.is_event; @@ -1285,8 +1288,8 @@ end_execution_reason_t parse_execution_context_t::run_1_job(tnode_t job_ // Clean up the job on failure or cancellation. if (pop_result == end_execution_reason_t::ok) { // Set the pgroup assignment mode and job tree, now that the job is populated. - job->pgroup_provenance = get_pgroup_provenance(job, lineage); - job_tree_t::populate_tree_for_job(job.get(), lineage.job_tree); + job_tree_t::populate_tree_for_job(job.get(), ctx.job_tree); + job->pgroup_provenance = get_pgroup_provenance(job); assert(job->job_tree && "Should have a job tree"); // Success. Give the job to the parser - it will clean it up. @@ -1302,7 +1305,7 @@ end_execution_reason_t parse_execution_context_t::run_1_job(tnode_t job_ } // Actually execute the job. - if (!exec_job(*this->parser, job, lineage)) { + if (!exec_job(*this->parser, job, block_io)) { remove_job(*this->parser, job.get()); } diff --git a/src/parse_execution.h b/src/parse_execution.h index 35b6fd4b6..fa50712f8 100644 --- a/src/parse_execution.h +++ b/src/parse_execution.h @@ -36,13 +36,18 @@ class parse_execution_context_t { parsed_source_ref_t pstree; parser_t *const parser; const operation_context_t &ctx; + // The currently executing job node, used to indicate the line number. tnode_t executing_job_node{}; + // Cached line number information. size_t cached_lineno_offset = 0; int cached_lineno_count = 0; - // The lineage for any jobs created by this context. - const job_lineage_t lineage; + + /// The block IO chain. + /// For example, in `begin; foo ; end < file.txt` this would have the 'file.txt' IO. + io_chain_t block_io{}; + // No copying allowed. parse_execution_context_t(const parse_execution_context_t &) = delete; parse_execution_context_t &operator=(const parse_execution_context_t &) = delete; @@ -139,8 +144,10 @@ class parse_execution_context_t { int line_offset_of_character_at_offset(size_t offset); public: - parse_execution_context_t(parsed_source_ref_t pstree, parser_t *p, - const operation_context_t &ctx, job_lineage_t lineage); + /// Construct a context in preparation for evaluating a node in a tree, with the given block_io. + /// The execution context may access the parser and job_tree through ctx. + parse_execution_context_t(parsed_source_ref_t pstree, const operation_context_t &ctx, + io_chain_t block_io); /// Returns the current line number, indexed from 1. Not const since it touches /// cached_lineno_offset. diff --git a/src/parser.cpp b/src/parser.cpp index a0e3658fe..d2dec0b1e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -710,8 +710,8 @@ eval_res_t parser_t::eval_node(const parsed_source_ref_t &ps, tnode_t node, // Create and set a new execution context. using exc_ctx_ref_t = std::unique_ptr; - scoped_push exc(&execution_context, make_unique( - ps, this, op_ctx, std::move(lineage))); + scoped_push exc( + &execution_context, make_unique(ps, op_ctx, lineage.block_io)); // Check the exec count so we know if anything got executed. const size_t prev_exec_count = libdata().exec_count;