Fix zed sometimes stopping by using setsid on interactive shells (#29070)
For some reason `SIGTTIN` sometimes gets sent to the process group, causing it to stop when run from a terminal. This solves that issue by putting the shell in a new session + progress group. This allows removal of a workaround of using `exit 0;` to restore handling of ctrl-c after exit. In testing this appears to no longer be necessary. Closes #27716 Release Notes: - Fixed Zed sometimes becoming a stopped background process when run from a terminal.
This commit is contained in:
parent
7abe2c9c31
commit
8c55063417
5 changed files with 42 additions and 44 deletions
|
@ -273,23 +273,14 @@ async fn load_shell_environment(
|
|||
//
|
||||
// In certain shells we need to execute additional_command in order to
|
||||
// trigger the behavior of direnv, etc.
|
||||
//
|
||||
//
|
||||
// The `exit 0` is the result of hours of debugging, trying to find out
|
||||
// why running this command here, without `exit 0`, would mess
|
||||
// up signal process for our process so that `ctrl-c` doesn't work
|
||||
// anymore.
|
||||
//
|
||||
// We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would
|
||||
// do that, but it does, and `exit 0` helps.
|
||||
|
||||
let command = match shell_name {
|
||||
Some("fish") => format!(
|
||||
"cd '{}'; emit fish_prompt; printf '%s' {MARKER}; /usr/bin/env; exit 0;",
|
||||
"cd '{}'; emit fish_prompt; printf '%s' {MARKER}; /usr/bin/env;",
|
||||
dir.display()
|
||||
),
|
||||
_ => format!(
|
||||
"cd '{}'; printf '%s' {MARKER}; /usr/bin/env; exit 0;",
|
||||
"cd '{}'; printf '%s' {MARKER}; /usr/bin/env;",
|
||||
dir.display()
|
||||
),
|
||||
};
|
||||
|
@ -297,16 +288,21 @@ async fn load_shell_environment(
|
|||
// csh/tcsh only supports `-l` if it's the only flag. So this won't be a login shell.
|
||||
// Users must rely on vars from `~/.tcshrc` or `~/.cshrc` and not `.login` as a result.
|
||||
let args = match shell_name {
|
||||
Some("tcsh") | Some("csh") => vec!["-i", "-c", &command],
|
||||
_ => vec!["-l", "-i", "-c", &command],
|
||||
Some("tcsh") | Some("csh") => vec!["-i".to_string(), "-c".to_string(), command],
|
||||
_ => vec![
|
||||
"-l".to_string(),
|
||||
"-i".to_string(),
|
||||
"-c".to_string(),
|
||||
command,
|
||||
],
|
||||
};
|
||||
|
||||
let Some(output) = smol::process::Command::new(&shell)
|
||||
.args(&args)
|
||||
.output()
|
||||
.await
|
||||
.log_err()
|
||||
else {
|
||||
let Some(output) = smol::unblock(move || {
|
||||
util::set_pre_exec_to_start_new_session(std::process::Command::new(&shell).args(&args))
|
||||
.output()
|
||||
})
|
||||
.await
|
||||
.log_err() else {
|
||||
return message(
|
||||
"Failed to spawn login shell to source login environment variables. See logs for details",
|
||||
);
|
||||
|
|
|
@ -321,21 +321,16 @@ pub fn load_login_shell_environment() -> Result<()> {
|
|||
.and_then(|home| home.into_string().ok())
|
||||
.map(|home| format!("cd '{home}';"));
|
||||
|
||||
// The `exit 0` is the result of hours of debugging, trying to find out
|
||||
// why running this command here, without `exit 0`, would mess
|
||||
// up signal process for our process so that `ctrl-c` doesn't work
|
||||
// anymore.
|
||||
// We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would
|
||||
// do that, but it does, and `exit 0` helps.
|
||||
let shell_cmd = format!(
|
||||
"{}printf '%s' {marker}; /usr/bin/env; exit 0;",
|
||||
"{}printf '%s' {marker}; /usr/bin/env;",
|
||||
shell_cmd_prefix.as_deref().unwrap_or("")
|
||||
);
|
||||
|
||||
let output = std::process::Command::new(&shell)
|
||||
.args(["-l", "-i", "-c", &shell_cmd])
|
||||
.output()
|
||||
.context("failed to spawn login shell to source login environment variables")?;
|
||||
let output = set_pre_exec_to_start_new_session(
|
||||
std::process::Command::new(&shell).args(["-l", "-i", "-c", &shell_cmd]),
|
||||
)
|
||||
.output()
|
||||
.context("failed to spawn login shell to source login environment variables")?;
|
||||
if !output.status.success() {
|
||||
Err(anyhow!("login shell exited with error"))?;
|
||||
}
|
||||
|
@ -357,6 +352,26 @@ pub fn load_login_shell_environment() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Configures the process to start a new session, to prevent interactive shells from taking control
|
||||
/// of the terminal.
|
||||
///
|
||||
/// For more details: https://registerspill.thorstenball.com/p/how-to-lose-control-of-your-shell
|
||||
pub fn set_pre_exec_to_start_new_session(
|
||||
command: &mut std::process::Command,
|
||||
) -> &mut std::process::Command {
|
||||
// safety: code in pre_exec should be signal safe.
|
||||
// https://man7.org/linux/man-pages/man7/signal-safety.7.html
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
unsafe {
|
||||
use std::os::unix::process::CommandExt;
|
||||
command.pre_exec(|| {
|
||||
libc::setsid();
|
||||
Ok(())
|
||||
});
|
||||
};
|
||||
command
|
||||
}
|
||||
|
||||
/// Parse the result of calling `usr/bin/env` with no arguments
|
||||
pub fn parse_env_output(env: &str, mut f: impl FnMut(String, String)) {
|
||||
let mut current_key: Option<String> = None;
|
||||
|
|
|
@ -28,7 +28,6 @@ futures.workspace = true
|
|||
gpui.workspace = true
|
||||
itertools.workspace = true
|
||||
language.workspace = true
|
||||
libc.workspace = true
|
||||
log.workspace = true
|
||||
multi_buffer.workspace = true
|
||||
nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", features = ["use_tokio"], optional = true }
|
||||
|
|
|
@ -1519,18 +1519,7 @@ impl ShellExec {
|
|||
process.stdin(Stdio::null());
|
||||
};
|
||||
|
||||
// https://registerspill.thorstenball.com/p/how-to-lose-control-of-your-shell
|
||||
//
|
||||
// safety: code in pre_exec should be signal safe.
|
||||
// https://man7.org/linux/man-pages/man7/signal-safety.7.html
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
unsafe {
|
||||
use std::os::unix::process::CommandExt;
|
||||
process.pre_exec(|| {
|
||||
libc::setsid();
|
||||
Ok(())
|
||||
});
|
||||
};
|
||||
util::set_pre_exec_to_start_new_session(&mut process);
|
||||
let is_read = self.is_read;
|
||||
|
||||
let task = cx.spawn_in(window, async move |vim, cx| {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue