diff --git a/src/builtin_bg.cpp b/src/builtin_bg.cpp index 4858e0c8e..0b3b146b1 100644 --- a/src/builtin_bg.cpp +++ b/src/builtin_bg.cpp @@ -22,12 +22,12 @@ static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j) { if (!j->wants_job_control()) { wcstring error_message = format_string( _(L"%ls: Can't put job %d, '%ls' to background because it is not under job control\n"), - L"bg", j->job_id, j->command_wcstr()); + L"bg", j->job_id(), j->command_wcstr()); builtin_print_help(parser, streams, L"bg", &error_message); return STATUS_CMD_ERROR; } - streams.err.append_format(_(L"Send job %d '%ls' to background\n"), j->job_id, + streams.err.append_format(_(L"Send job %d '%ls' to background\n"), j->job_id(), j->command_wcstr()); parser.job_promote(j); j->mut_flags().foreground = false; diff --git a/src/builtin_disown.cpp b/src/builtin_disown.cpp index bdf2f4373..70a91baa0 100644 --- a/src/builtin_disown.cpp +++ b/src/builtin_disown.cpp @@ -28,7 +28,7 @@ static int disown_job(const wchar_t *cmd, parser_t &parser, io_streams_t &stream killpg(j->pgid, SIGCONT); const wchar_t *fmt = _(L"%ls: job %d ('%ls') was stopped and has been signalled to continue.\n"); - streams.err.append_format(fmt, cmd, j->job_id, j->command_wcstr()); + streams.err.append_format(fmt, cmd, j->job_id(), j->command_wcstr()); } // We cannot directly remove the job from the jobs() list as `disown` might be called diff --git a/src/builtin_fg.cpp b/src/builtin_fg.cpp index 231d435fd..628e76470 100644 --- a/src/builtin_fg.cpp +++ b/src/builtin_fg.cpp @@ -91,11 +91,11 @@ int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (streams.err_is_redirected) { - streams.err.append_format(FG_MSG, job->job_id, job->command_wcstr()); + streams.err.append_format(FG_MSG, job->job_id(), job->command_wcstr()); } else { // If we aren't redirecting, send output to real stderr, since stuff in sb_err won't get // printed until the command finishes. - std::fwprintf(stderr, FG_MSG, job->job_id, job->command_wcstr()); + std::fwprintf(stderr, FG_MSG, job->job_id(), job->command_wcstr()); } const wcstring ft = tok_first(job->command()); diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index 3b2530ae7..fe82b7704 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -61,7 +61,7 @@ static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_ streams.out.append(_(L"State\tCommand\n")); } - streams.out.append_format(L"%d\t%d\t", j->job_id, j->pgid); + streams.out.append_format(L"%d\t%d\t", j->job_id(), j->pgid); if (have_proc_stat()) { streams.out.append_format(L"%d%%\t", cpu_use(j)); diff --git a/src/builtin_wait.cpp b/src/builtin_wait.cpp index 678caf32e..3d21d04f8 100644 --- a/src/builtin_wait.cpp +++ b/src/builtin_wait.cpp @@ -20,12 +20,12 @@ static job_id_t get_job_id_from_pid(pid_t pid, const parser_t &parser) { for (const auto &j : parser.jobs()) { if (j->pgid == pid) { - return j->job_id; + return j->job_id(); } // Check if the specified pid is a child process of the job. for (const process_ptr_t &p : j->processes) { if (p->pid == pid) { - return j->job_id; + return j->job_id(); } } } @@ -146,9 +146,9 @@ static bool find_job_by_name(const wchar_t *proc, std::vector &ids, if (j->command().empty()) continue; if (match_pid(j->command(), proc)) { - if (!contains(ids, j->job_id)) { + if (!contains(ids, j->job_id())) { // If pids doesn't already have the pgid, add it. - ids.push_back(j->job_id); + ids.push_back(j->job_id()); } found = true; } @@ -158,9 +158,9 @@ static bool find_job_by_name(const wchar_t *proc, std::vector &ids, if (p->actual_cmd.empty()) continue; if (match_pid(p->actual_cmd, proc)) { - if (!contains(ids, j->job_id)) { + if (!contains(ids, j->job_id())) { // If pids doesn't already have the pgid, add it. - ids.push_back(j->job_id); + ids.push_back(j->job_id()); } found = true; } diff --git a/src/event.cpp b/src/event.cpp index 3382fa2a9..f3bae6d13 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -157,7 +157,7 @@ wcstring event_get_desc(const event_t &evt) { // In events, PGIDs are stored as negative PIDs job_t *j = job_t::from_pid(-ed.param1.pid); if (j) { - return format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, + return format_string(_(L"exit handler for job %d, '%ls'"), j->job_id(), j->command_wcstr()); } else { return format_string(_(L"exit handler for job with process group %d"), @@ -170,7 +170,7 @@ wcstring event_get_desc(const event_t &evt) { case event_type_t::job_exit: { job_t *j = job_t::from_job_id(ed.param1.job_id); if (j) { - return format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, + return format_string(_(L"exit handler for job %d, '%ls'"), j->job_id(), j->command_wcstr()); } else { return format_string(_(L"exit handler for job with job id %d"), ed.param1.job_id); diff --git a/src/exec.cpp b/src/exec.cpp index d486517fb..3ef1288ab 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -511,7 +511,7 @@ static bool handle_builtin_output(parser_t &parser, const std::shared_ptr p->completed = true; if (p->is_last_in_job) { FLOGF(exec_job_status, L"Set status of job %d (%ls) to %d using short circuit", - j->job_id, j->preview().c_str(), p->status); + j->job_id(), j->preview().c_str(), p->status); parser.set_last_statuses(j->get_statuses()); } return true; @@ -686,7 +686,7 @@ using proc_performer_t = std::function; // exists. This is just a dumb artifact of the fact that we only capture the functions name, not its // properties, when creating the job; thus a race could delete the function before we fetch its // properties. -static proc_performer_t get_performer_for_process(process_t *p, const job_t *job, +static proc_performer_t get_performer_for_process(process_t *p, job_t *job, const io_chain_t &io_chain) { assert((p->type == process_type_t::function || p->type == process_type_t::block_node) && "Unexpected process type"); @@ -714,6 +714,13 @@ static proc_performer_t get_performer_for_process(process_t *p, const job_t *job }; } else { assert(p->type == process_type_t::function); + // If this function is the only process in the current job + // and that job is in the foreground then mark this job as internal + // so it doesn't increment the job id for any jobs created within this + // function. + if (p->is_first_in_job && p->is_last_in_job && job->flags().foreground) { + job->mark_internal(); + } auto props = function_get_properties(p->argv0()); if (!props) { FLOGF(error, _(L"Unknown function '%ls'"), p->argv0()); @@ -1099,7 +1106,7 @@ bool exec_job(parser_t &parser, const shared_ptr &j, const job_lineage_t } } - FLOGF(exec_job_exec, L"Executed job %d from command '%ls' with pgrp %d", j->job_id, + FLOGF(exec_job_exec, L"Executed job %d from command '%ls' with pgrp %d", j->job_id(), j->command_wcstr(), j->pgid); j->mark_constructed(); diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index d2e4b294c..b07dc3b01 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -1287,14 +1287,14 @@ eval_result_t parse_execution_context_t::run_1_job(tnode_t job_node, // the job ID here. auto &libdata = parser->libdata(); const auto saved_caller_jid = libdata.caller_job_id; - libdata.caller_job_id = job->job_id; + libdata.caller_job_id = job->job_id(); // Populate the job. This may fail for reasons like command_not_found. If this fails, an error // will have been printed. eval_result_t pop_result = this->populate_job_from_job_node(job.get(), job_node, associated_block); - assert(libdata.caller_job_id == job->job_id && "Caller job ID unexpectedly changed"); + assert(libdata.caller_job_id == job->job_id() && "Caller job ID unexpectedly changed"); parser->libdata().caller_job_id = saved_caller_jid; // Store time it took to 'parse' the command. diff --git a/src/parser.cpp b/src/parser.cpp index e0ee0bb1b..24544e41b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -576,7 +576,7 @@ void parser_t::job_promote(job_t *job) { job_t *parser_t::job_get(job_id_t id) { for (const auto &job : job_list) { - if (id <= 0 || job->job_id == id) return job.get(); + if (id <= 0 || job->job_id() == id) return job.get(); } return nullptr; } diff --git a/src/postfork.cpp b/src/postfork.cpp index 9d939a83d..dcdb78a08 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -75,7 +75,7 @@ bool child_set_group(job_t *j, process_t *p) { char command[64]; format_long_safe(pid_buff, p->pid); - format_long_safe(job_id_buff, j->job_id); + format_long_safe(job_id_buff, j->job_id()); format_long_safe(getpgid_buff, getpgid(p->pid)); format_long_safe(job_pgid_buff, j->pgid); narrow_string_safe(argv0, p->argv0()); diff --git a/src/proc.cpp b/src/proc.cpp index 60d779afb..b3ceabf6c 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -271,10 +271,12 @@ void process_t::check_generations_before_launch() { job_t::job_t(job_id_t job_id, const properties_t &props, const job_lineage_t &lineage) : properties(props), - job_id(job_id), + job_id_(job_id), root_constructed(lineage.root_constructed ? lineage.root_constructed : this->constructed) {} -job_t::~job_t() { release_job_id(job_id); } +job_t::~job_t() { + if (job_id_ != -1) release_job_id(job_id_); +} void job_t::mark_constructed() { assert(!is_constructed() && "Job was already constructed"); @@ -415,7 +417,7 @@ static void print_job_status(const job_t *j, job_status_t status) { if (status == JOB_STOPPED) msg = L"Job %d, '%ls' has stopped"; outputter_t outp; outp.writestr("\r"); - outp.writestr(format_string(_(msg), j->job_id, truncate_command(j->command()).c_str())); + outp.writestr(format_string(_(msg), j->job_id(), truncate_command(j->command()).c_str())); if (clr_eol) outp.term_puts(clr_eol, 1); outp.writestr(L"\n"); fflush(stdout); @@ -492,14 +494,14 @@ static bool try_clean_process_in_job(process_t *p, job_t *j, std::vectorjob_id); + only_one_job ? wcstring() : format_string(_(L"Job %d, "), j->job_id()); std::fwprintf(stdout, _(L"%ls: %ls\'%ls\' terminated by signal %ls (%ls)"), program_name, job_number_desc.c_str(), truncate_command(j->command()).c_str(), sig2wcs(s.signal_code()), signal_get_desc(s.signal_code())); } else { const wcstring job_number_desc = - only_one_job ? wcstring() : format_string(L"from job %d, ", j->job_id); + only_one_job ? wcstring() : format_string(L"from job %d, ", j->job_id()); const wchar_t *fmt = _(L"%ls: Process %d, \'%ls\' %ls\'%ls\' terminated by signal %ls (%ls)"); std::fwprintf(stdout, fmt, program_name, p->pid, p->argv0(), job_number_desc.c_str(), @@ -595,7 +597,7 @@ static bool process_clean_after_marking(parser_t &parser, bool allow_interactive proc_create_event(L"JOB_EXIT", event_type_t::exit, -j->pgid, 0)); } exit_events.push_back( - proc_create_event(L"JOB_EXIT", event_type_t::job_exit, j->job_id, 0)); + proc_create_event(L"JOB_EXIT", event_type_t::job_exit, j->job_id(), 0)); } } @@ -757,8 +759,8 @@ int terminal_maybe_give_to_job(const job_t *j, bool continuing_from_stopped) { if (errno == ENOTTY) { redirect_tty_output(); } - debug(1, _(L"Could not send job %d ('%ls') with pgid %d to foreground"), j->job_id, - j->command_wcstr(), j->pgid); + debug(1, _(L"Could not send job %d ('%ls') with pgid %d to foreground"), + j->job_id(), j->command_wcstr(), j->pgid); wperror(L"tcsetpgrp"); return error; } @@ -785,7 +787,7 @@ int terminal_maybe_give_to_job(const job_t *j, bool continuing_from_stopped) { redirect_tty_output(); } - debug(1, _(L"Could not send job %d ('%ls') to foreground"), j->job_id, + debug(1, _(L"Could not send job %d ('%ls') to foreground"), j->job_id(), j->preview().c_str()); wperror(L"tcsetattr"); return error; @@ -852,7 +854,7 @@ void job_t::continue_job(parser_t &parser, bool reclaim_foreground_pgrp, bool se mut_flags().notified = false; FLOGF(proc_job_run, L"%ls job %d, gid %d (%ls), %ls, %ls", - send_sigcont ? L"Continue" : L"Start", job_id, pgid, command_wcstr(), + send_sigcont ? L"Continue" : L"Start", job_id_, pgid, command_wcstr(), is_completed() ? L"COMPLETED" : L"UNCOMPLETED", parser.libdata().is_interactive ? L"INTERACTIVE" : L"NON-INTERACTIVE"); diff --git a/src/proc.h b/src/proc.h index 63772b434..341dc028e 100644 --- a/src/proc.h +++ b/src/proc.h @@ -315,6 +315,9 @@ class job_t { /// messages about job status on the terminal. wcstring command_str; + /// The job_id for this job. + job_id_t job_id_; + // No copying. job_t(const job_t &rhs) = delete; void operator=(const job_t &) = delete; @@ -378,7 +381,15 @@ class job_t { pid_t pgid{INVALID_PID}; /// The id of this job. - const job_id_t job_id; + job_id_t job_id() const { return job_id_; } + + /// Mark this job as internal. Internal jobs' job_ids are removed from the + /// list of jobs so that, among other things, they don't take a job_id + /// entry. + void mark_internal() { + release_job_id(job_id_); + job_id_ = -1; + } /// The saved terminal modes of this job. This needs to be saved so that we can restore the /// terminal to the same state after temporarily taking control over the terminal when a job