diff options
| author | divens <divens@users.noreply.github.com> | 2026-03-19 08:58:13 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-19 08:58:13 +0100 |
| commit | 62633f930d9970e3f2ef4b0afb924ff75dbba870 (patch) | |
| tree | 3537a8637a1b482e91f0bcc0c347fb8e163c8f4a | |
| parent | Fix Windows config/data paths and consolidate path helpers (#4852) (diff) | |
| download | sl0ppij-62633f930d9970e3f2ef4b0afb924ff75dbba870.tar.gz sl0ppij-62633f930d9970e3f2ef4b0afb924ff75dbba870.tar.xz sl0ppij-62633f930d9970e3f2ef4b0afb924ff75dbba870.zip | |
fix(windows): avoid hang when share plugin starts web server (#4863)
Use cmd.status() instead of cmd.output() on Windows to prevent pipe
handle inheritance from blocking indefinitely. cmd.output() creates
pipe handles that the grandchild web server process inherits via
CreateProcess bInheritHandles, keeping them open and preventing EOF.
Co-authored-by: divens <divens.dev@gmail.com>
| -rw-r--r-- | zellij-client/src/lib.rs | 44 |
1 files changed, 43 insertions, 1 deletions
diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index 95a0810f..bef96e25 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -221,7 +221,7 @@ impl ErrorInstruction for ClientInstruction { } } -#[cfg(feature = "web_server_capability")] +#[cfg(all(feature = "web_server_capability", not(windows)))] fn spawn_web_server(cli_args: &CliArgs) -> Result<String, String> { let mut cmd = Command::new(current_exe().map_err(|e| e.to_string())?); if let Some(config_file_path) = Config::config_file_path(cli_args) { @@ -252,6 +252,48 @@ fn spawn_web_server(cli_args: &CliArgs) -> Result<String, String> { } } +/// On Windows, cmd.output() creates pipe handles for stdout/stderr. The child +/// (zellij web -d) spawns a grandchild (the web server) which inherits these +/// pipe handles. cmd.output() waits for EOF on the pipes, but the long-lived +/// grandchild keeps them open — hanging forever. +/// +/// Redirecting the grandchild's stdio to null is not sufficient: on Windows, +/// CreateProcess with bInheritHandles=TRUE inherits ALL inheritable handles, +/// not just the stdio handles specified in STARTUPINFO. The pipe handles leak +/// through regardless of the grandchild's stdio configuration. +/// +/// Use cmd.status() instead: no pipes are created, so nothing to hang on. +#[cfg(all(feature = "web_server_capability", windows))] +fn spawn_web_server(cli_args: &CliArgs) -> Result<String, String> { + let mut cmd = Command::new(current_exe().map_err(|e| e.to_string())?); + if let Some(config_file_path) = Config::config_file_path(cli_args) { + let config_file_path_exists = Path::new(&config_file_path).exists(); + if !config_file_path_exists { + return Err(format!( + "Config file: {} does not exist", + config_file_path.display() + )); + } + cmd.arg("--config"); + cmd.arg(format!("{}", config_file_path.display())); + } + cmd.arg("web"); + cmd.arg("-d"); + match cmd.status() { + Ok(status) => { + if status.success() { + Ok(String::new()) + } else { + Err(format!( + "Web server process exited with code: {}", + status.code().unwrap_or(-1) + )) + } + }, + Err(e) => Err(e.to_string()), + } +} + #[cfg(not(feature = "web_server_capability"))] fn spawn_web_server(_cli_args: &CliArgs) -> Result<String, String> { log::error!( |
