fish-shell/src/postfork.h
ridiculousfish df2cbe321c Refactor tty transfer to be more deliberate
This is a big cleanup to how tty transfer works. Recall that when job
control is active, we transfer the tty to jobs via tcsetpgrp().

Previously, transferring was done "as needed" in continue_job. That is, if
we are running a job, and the job wants the terminal and does not have it,
we will transfer the tty at that point.

This got pretty weird when running mixed pipelines. For example:

    cmd1 | func1 | cmd2

Here we would run `func1` before calling continue_job. Thus the tty
would be transferred by the nested function invocation, and also restored
by that invocation, potentially racing with tty manipulation from cmd1 or
cmd2.

In the new model, migrate the tty transfer responsibility outside of
continue_job. The caller of continue_job is then responsible for setting up
the tty. There's two places where this gets done:

1. In `exec_job`, where we run a job for the first time.

2. In `builtin_fg` where we continue a stopped job in the foreground.

Fixes #8699
2022-03-19 14:48:36 -07:00

83 lines
3 KiB
C++

// Functions that we may safely call after fork(), of which there are very few. In particular we
// cannot allocate memory, since we're insane enough to call fork from a multithreaded process.
#ifndef FISH_POSTFORK_H
#define FISH_POSTFORK_H
#include "config.h"
#include <stddef.h>
#include <unistd.h>
#include "maybe.h"
#ifdef HAVE_SPAWN_H
#include <spawn.h>
#endif
#ifndef FISH_USE_POSIX_SPAWN
#define FISH_USE_POSIX_SPAWN HAVE_SPAWN_H
#endif
class dup2_list_t;
class job_t;
class process_t;
/// Tell the proc \p pid to join process group \p pgroup.
/// If \p is_child is true, we are the child process; otherwise we are fish.
/// Called by both parent and child; this is an unavoidable race inherent to Unix.
/// If is_parent is set, then we are the parent process and should swallow EACCESS.
/// \return 0 on success, an errno error code on failure.
int execute_setpgid(pid_t pid, pid_t pgroup, bool is_parent);
/// Report the error code \p err for a failed setpgid call.
/// Note not all errors should be reported; in particular EACCESS is expected and benign in the
/// parent only.
void report_setpgid_error(int err, bool is_parent, pid_t desired_pgid, const job_t *j,
const process_t *p);
/// Initialize a new child process. This should be called right away after forking in the child
/// process. This resets signal handlers and applies IO redirections.
///
/// If \p claim_tty_from is >= 0 and owns the tty, use tcsetpgrp() to claim it.
///
/// \return 0 on success, -1 on failure, in which case an error will be printed.
int child_setup_process(pid_t claim_tty_from, const job_t &job, bool is_forked,
const dup2_list_t &dup2s);
/// Call fork(), retrying on failure a few times.
pid_t execute_fork();
/// Report an error from failing to exec or posix_spawn a command.
void safe_report_exec_error(int err, const char *actual_cmd, const char *const *argv,
const char *const *envv);
#if FISH_USE_POSIX_SPAWN
/// A RAII type which wraps up posix_spawn's data structures.
class posix_spawner_t : noncopyable_t, nonmovable_t {
public:
/// Attempt to construct from a job and dup2 list.
/// The caller must check the error function, as this may fail.
posix_spawner_t(const job_t *j, const dup2_list_t &dup2s);
/// \return the last error code, or 0 if there is no error.
int get_error() const { return error_; }
/// If this spawner does not have an error, invoke posix_spawn. Parameters are the same as
/// posix_spawn.
/// \return the pid, or none() on failure, in which case our error will be set.
maybe_t<pid_t> spawn(const char *cmd, char *const argv[], char *const envp[]);
~posix_spawner_t();
private:
bool check_fail(int err);
posix_spawnattr_t *attr() { return &*attr_; }
posix_spawn_file_actions_t *actions() { return &*actions_; }
posix_spawner_t();
int error_{0};
maybe_t<posix_spawnattr_t> attr_{};
maybe_t<posix_spawn_file_actions_t> actions_{};
};
#endif
#endif