diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-06 02:27:06 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-06 02:27:06 +0300 |
| commit | 9e9bc8a144667a6b7debfd4f0c7a31ff0e11493a (patch) | |
| tree | 03f1259f91fd9edeacfd4b904f2e84d4acf25232 /src/server/autodetect.rs | |
| parent | 35b0dff96579f099e0436290085024424ffb289c (diff) | |
feat: manage multiple ssh machines from one client (#3670)
Diffstat (limited to 'src/server/autodetect.rs')
| -rw-r--r-- | src/server/autodetect.rs | 46 |
1 files changed, 31 insertions, 15 deletions
diff --git a/src/server/autodetect.rs b/src/server/autodetect.rs index bd364a70..e6fdaaa4 100644 --- a/src/server/autodetect.rs +++ b/src/server/autodetect.rs @@ -142,7 +142,7 @@ fn client_protocol_accepts_hello(socket_path: &Path) -> io::Result<bool> { } } -fn validate_running_server_compatibility() -> io::Result<()> { +fn validate_running_server_compatibility(saved_federation: bool) -> io::Result<()> { let Some(status) = read_server_status()? else { return Err(io::Error::other(format!( "a herdr server is listening, but its status API is unavailable.\n\n{}\nIf that fails, stop the old server process manually.", @@ -150,16 +150,23 @@ fn validate_running_server_compatibility() -> io::Result<()> { ))); }; - let endpoint_generation = status - .capabilities - .as_ref() - .and_then(|capabilities| capabilities.endpoint_protocol_generation); - if endpoint_generation == Some(crate::protocol::endpoint::ENDPOINT_PROTOCOL_GENERATION) { + let capabilities = status.capabilities.as_ref(); + let endpoint_generation = + capabilities.and_then(|capabilities| capabilities.endpoint_protocol_generation); + let surface_interest = capabilities.is_some_and(|capabilities| capabilities.surface_interest); + if endpoint_generation == Some(crate::protocol::endpoint::ENDPOINT_PROTOCOL_GENERATION) + && (!saved_federation || surface_interest) + { return Ok(()); } + let requirement = if saved_federation && !surface_interest { + "saved SSH machines require surface lifecycle support" + } else { + "the stable endpoint generation is incompatible" + }; Err(io::Error::other(format!( - "This session predates Herdr's stable endpoint protocol and needs one final server update.\n\nserver: v{} endpoint generation {}\nclient: v{} endpoint generation {}\n\n{}", + "This session needs one final server update before Herdr can attach ({requirement}).\n\nserver: v{} endpoint generation {}\nclient: v{} endpoint generation {}\n\n{}", status.version.as_deref().unwrap_or("unknown"), endpoint_generation .map(|value| value.to_string()) @@ -285,18 +292,27 @@ pub fn wait_for_server_socket(socket_path: &Path, timeout: Duration) -> io::Resu /// 1. Check if a server is listening on the client socket /// 2. If no server → spawn server daemon → wait for socket readiness /// 3. Run the thin client (which connects to the server) -pub fn auto_detect_launch() -> io::Result<()> { +pub fn auto_detect_launch(saved_federation: bool) -> io::Result<()> { let socket_path = client_socket_path(); info!(path = %socket_path.display(), "auto-detect launch starting"); - if is_server_listening_at(&socket_path) { - validate_running_server_compatibility()?; + let startup = if is_server_listening_at(&socket_path) { info!("server already running, attaching as client"); + if saved_federation { + Ok(()) + } else { + validate_running_server_compatibility(false) + } } else { info!("no server running, spawning server daemon"); - spawn_server_daemon()?; - wait_for_server_socket(&socket_path, SERVER_READY_TIMEOUT)?; - info!("server ready, attaching as client"); + spawn_server_daemon() + .and_then(|_| wait_for_server_socket(&socket_path, SERVER_READY_TIMEOUT)) + }; + if let Err(error) = startup { + if !saved_federation { + return Err(error); + } + tracing::warn!(%error, "Local startup failed; keeping saved machines available"); } // Now attach as a thin client. @@ -522,7 +538,7 @@ test "$sid" = "$$" let path = dir.join("api.sock"); std::env::set_var(crate::api::SOCKET_PATH_ENV_VAR, &path); - let err = validate_running_server_compatibility().unwrap_err(); + let err = validate_running_server_compatibility(false).unwrap_err(); assert!( err.to_string().contains("status API is unavailable"), @@ -558,7 +574,7 @@ test "$sid" = "$$" stream.flush().unwrap(); }); - let err = validate_running_server_compatibility().unwrap_err(); + let err = validate_running_server_compatibility(false).unwrap_err(); let message = err.to_string(); let _ = handle.join(); |
