diff --git a/libshpool/src/protocol.rs b/libshpool/src/protocol.rs index 754dca45..f96a444f 100644 --- a/libshpool/src/protocol.rs +++ b/libshpool/src/protocol.rs @@ -257,7 +257,7 @@ impl Client { loop { let nread = stdin.read(&mut buf).context("reading stdin from user")?; if nread == 0 { - continue; + return Ok(()); } debug!("read {} bytes", nread); @@ -371,12 +371,22 @@ impl Client { } match stdin_to_sock_h.join() { - Ok(v) => v?, + Ok(v) => { + if let Err(e) = v { + info!("stdin->sock finished with err: {}", e); + } + } Err(panic_err) => std::panic::resume_unwind(panic_err), } match sock_to_stdout_h.join() { - Ok(v) => v?, - Err(panic_err) => std::panic::resume_unwind(panic_err), + Ok(v) => { + if let Err(e) = v { + info!("sock->stdout finished with err: {}", e); + } + } + Err(panic_err) => { + std::panic::resume_unwind(panic_err); + } } Ok(exit_status.load(Ordering::Acquire)) diff --git a/shpool/tests/attach.rs b/shpool/tests/attach.rs index 79f69d90..69f9f8df 100644 --- a/shpool/tests/attach.rs +++ b/shpool/tests/attach.rs @@ -545,6 +545,7 @@ fn default_keybinding_detach() -> anyhow::Result<()> { a1.run_raw_cmd(vec![0, 17])?; // Ctrl-Space Ctrl-q let exit_status = a1.proc.wait()?; + dbg!(exit_status); assert!(exit_status.success()); waiter.wait_event("daemon-bidi-stream-done")?; @@ -1567,6 +1568,39 @@ fn version_mismatch_client_newer() -> anyhow::Result<()> { Ok(()) } +#[test] +#[timeout(30000)] +fn detaches_on_null_stdin() -> anyhow::Result<()> { + let mut daemon_proc = support::daemon::Proc::new("norc.toml", DaemonArgs::default()) + .context("starting daemon proc")?; + let _attach_proc = daemon_proc + .attach("sh1", AttachArgs { null_stdin: true, ..Default::default() }) + .context("starting attach proc")?; + + // not really needed, just here to test the events system + daemon_proc.await_event("daemon-about-to-listen")?; + + loop { + thread::sleep(time::Duration::from_millis(300)); + + let list_json_out = daemon_proc.list_json()?; + let list_str = String::from_utf8_lossy(&list_json_out.stdout); + eprintln!("LIST OUTPUT: '{}'", list_str); + + let list_blob: serde_json::Value = serde_json::from_str(&list_str)?; + + if list_blob["sessions"].as_array().unwrap().is_empty() { + continue; + } + assert_eq!(list_blob["sessions"][0]["name"].as_str().unwrap(), "sh1"); + if list_blob["sessions"][0]["status"].as_str().unwrap() != "Disconnected" { + continue; + } + + return Ok(()); + } +} + #[ignore] // TODO: re-enable, this test if flaky #[test] fn up_arrow_no_crash() -> anyhow::Result<()> { diff --git a/shpool/tests/support/daemon.rs b/shpool/tests/support/daemon.rs index 747d5a2b..834aa1d2 100644 --- a/shpool/tests/support/daemon.rs +++ b/shpool/tests/support/daemon.rs @@ -50,6 +50,7 @@ pub struct AttachArgs { pub ttl: Option, pub cmd: Option, pub dir: Option, + pub null_stdin: bool, } pub struct HooksRecorder { @@ -287,7 +288,7 @@ impl Proc { let mut cmd = Command::new(shpool_bin()?); cmd.stdout(Stdio::piped()) .stderr(Stdio::piped()) - .stdin(Stdio::piped()) + .stdin(if args.null_stdin { Stdio::null() } else { Stdio::piped() }) .arg("--config-file") .arg(if let Some(config_file) = args.config { testdata_file(config_file)