diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-08-05 19:34:24 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-05 19:34:24 +0300 |
| commit | 2863b715132fe29e53089e06f105943d1df0b3b4 (patch) | |
| tree | 6b4af2f6fdec6de2888fd2bff3c0829d6719b727 /src/remote/host_unix.rs | |
| parent | eb93ec8ce62b05de776f751f4d2f52ff60f93f16 (diff) | |
feat(windows): support remote attach to unix hosts (#2329)
* feat(windows): support remote attach to unix hosts
* fix(remote): preserve unix bridge behavior
Diffstat (limited to 'src/remote/host_unix.rs')
| -rw-r--r-- | src/remote/host_unix.rs | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/src/remote/host_unix.rs b/src/remote/host_unix.rs new file mode 100644 index 00000000..e04b0e00 --- /dev/null +++ b/src/remote/host_unix.rs @@ -0,0 +1,69 @@ +//! Unix remote-host side of the SSH stdio bridge. + +use std::io; +use std::os::unix::net::UnixStream; +use std::thread; +use std::time::Duration; + +pub(crate) fn run_remote_client_bridge() -> io::Result<()> { + ensure_remote_server_running()?; + + let socket_path = crate::server::socket_paths::client_socket_path(); + let stream = UnixStream::connect(&socket_path).map_err(|err| { + io::Error::new( + err.kind(), + format!( + "failed to connect to remote Herdr client socket {}: {err}", + socket_path.display() + ), + ) + })?; + + let mut stdout = io::stdout().lock(); + let mut socket_to_stdout = stream.try_clone()?; + let mut stdin_to_socket = stream; + + let _upload = thread::spawn(move || { + let mut stdin = io::stdin(); + let _ = copy_flush(&mut stdin, &mut stdin_to_socket); + let _ = stdin_to_socket.shutdown(std::net::Shutdown::Write); + }); + + copy_flush(&mut socket_to_stdout, &mut stdout).map(|_| ()) +} + +fn copy_flush<R: io::Read, W: io::Write>(reader: &mut R, writer: &mut W) -> io::Result<u64> { + let mut buffer = [0_u8; 16 * 1024]; + let mut total = 0; + loop { + let read = match reader.read(&mut buffer) { + Ok(0) => return Ok(total), + Ok(read) => read, + Err(err) if err.kind() == io::ErrorKind::Interrupted => continue, + Err(err) => return Err(err), + }; + writer.write_all(&buffer[..read])?; + writer.flush()?; + total += read as u64; + } +} + +fn ensure_remote_server_running() -> io::Result<()> { + let socket_path = crate::server::socket_paths::client_socket_path(); + if crate::server::autodetect::is_server_listening() { + let status = crate::api::read_runtime_status_at( + &crate::api::socket_path(), + Duration::from_millis(500), + )? + .ok_or_else(|| io::Error::other("remote server status API is unavailable"))?; + if status.protocol == Some(crate::protocol::PROTOCOL_VERSION) { + return Ok(()); + } + return Err(io::Error::other( + "remote herdr server must restart before this bridge can attach; rerun `herdr --remote` from an interactive terminal to approve stopping it", + )); + } + + crate::server::autodetect::spawn_server_daemon()?; + crate::server::autodetect::wait_for_server_socket(&socket_path, Duration::from_secs(5)) +} |
