first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -19,18 +19,69 @@ use std::time::Duration;
|
||||
|
||||
use super::super::setup;
|
||||
|
||||
/// Path to the daemon's Unix domain socket.
|
||||
/// Path to the daemon's Unix domain socket, versioned on release channels.
|
||||
pub(super) fn socket_path(identity_key: &str) -> PathBuf {
|
||||
let dir = setup::remote_server_daemon_dir(identity_key);
|
||||
let expanded = shellexpand::tilde(&dir).into_owned();
|
||||
PathBuf::from(expanded).join("server.sock")
|
||||
PathBuf::from(expanded).join(setup::daemon_socket_name())
|
||||
}
|
||||
|
||||
/// Path to the daemon's PID file (also used as the flock target).
|
||||
/// Path to the daemon's PID file (also used as the flock target),
|
||||
/// versioned on release channels.
|
||||
pub(super) fn pid_path(identity_key: &str) -> PathBuf {
|
||||
let dir = setup::remote_server_daemon_dir(identity_key);
|
||||
let expanded = shellexpand::tilde(&dir).into_owned();
|
||||
PathBuf::from(expanded).join("server.pid")
|
||||
PathBuf::from(expanded).join(setup::daemon_pid_name())
|
||||
}
|
||||
|
||||
/// Daemon directory for the given identity key (expanded, no tilde).
|
||||
fn daemon_dir(identity_key: &str) -> PathBuf {
|
||||
let dir = setup::remote_server_daemon_dir(identity_key);
|
||||
let expanded = shellexpand::tilde(&dir).into_owned();
|
||||
PathBuf::from(expanded)
|
||||
}
|
||||
|
||||
/// Scans the identity-key daemon directory and removes socket/PID files
|
||||
/// from previous daemon versions.
|
||||
///
|
||||
/// Old daemons are **not** killed — they may still be serving active
|
||||
/// connections from an older Warp client. Removing their socket file
|
||||
/// prevents new proxies from accidentally connecting to them, and the
|
||||
/// daemon's built-in grace timer (10 min with no connections) will shut
|
||||
/// it down naturally after the last client disconnects.
|
||||
///
|
||||
/// Errors are logged but do not prevent the proxy from proceeding.
|
||||
fn cleanup_old_versions(identity_key: &str) {
|
||||
let dir = daemon_dir(identity_key);
|
||||
let current_socket = setup::daemon_socket_name();
|
||||
let current_pid = setup::daemon_pid_name();
|
||||
|
||||
let entries = match std::fs::read_dir(&dir) {
|
||||
Ok(entries) => entries,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
for entry in entries.flatten() {
|
||||
let name = entry.file_name();
|
||||
let Some(name_str) = name.to_str() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Remove old PID files: server*.pid that aren't the current version.
|
||||
if name_str.ends_with(".pid") && name_str.starts_with("server") && name_str != current_pid {
|
||||
log::info!("Proxy: removing old PID file {name_str}");
|
||||
let _ = std::fs::remove_file(entry.path());
|
||||
}
|
||||
|
||||
// Remove old socket files: server*.sock that aren't the current version.
|
||||
if name_str.ends_with(".sock")
|
||||
&& name_str.starts_with("server")
|
||||
&& name_str != current_socket
|
||||
{
|
||||
log::info!("Proxy: removing old socket file {name_str}");
|
||||
let _ = std::fs::remove_file(entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures the daemon directory exists with owner-only permissions.
|
||||
@@ -40,6 +91,13 @@ pub(super) fn ensure_private_daemon_dir(path: &std::path::Path) -> anyhow::Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Maximum usable `sun_path` length for Unix domain sockets.
|
||||
///
|
||||
/// macOS has the strictest limit (104 bytes including null terminator,
|
||||
/// 103 usable). We use 103 on all platforms so a single binary works
|
||||
/// everywhere without per-target branching.
|
||||
const SUN_PATH_MAX: usize = 103;
|
||||
|
||||
/// Entry point for `remote-server-proxy`.
|
||||
///
|
||||
/// Ensures the daemon is running, then bridges stdin/stdout to the daemon's
|
||||
@@ -48,11 +106,31 @@ pub fn run(identity_key: &str) -> anyhow::Result<()> {
|
||||
let socket_path = socket_path(identity_key);
|
||||
let pid_path = pid_path(identity_key);
|
||||
|
||||
// Guard against socket paths that exceed the sun_path limit.
|
||||
// Without this check, UnixListener::bind fails silently in the
|
||||
// daemon and the proxy times out after 10s with no actionable
|
||||
// error. With hashed identity + version names the path should
|
||||
// always fit, so hitting this guard indicates a new path component
|
||||
// was added without budgeting for sun_path. The error surfaces in
|
||||
// client telemetry (RemoteServerInitialization) and daemon logs.
|
||||
let path_len = socket_path.as_os_str().len();
|
||||
if path_len > SUN_PATH_MAX {
|
||||
anyhow::bail!(
|
||||
"daemon socket path is {path_len} bytes, which exceeds the \
|
||||
sun_path limit of {SUN_PATH_MAX} bytes: {}",
|
||||
socket_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure the parent directory exists.
|
||||
if let Some(parent) = socket_path.parent() {
|
||||
ensure_private_daemon_dir(parent)?;
|
||||
}
|
||||
|
||||
// Clean up socket/PID files from previous daemon versions so stale
|
||||
// daemons left over after autoupdate don't linger.
|
||||
cleanup_old_versions(identity_key);
|
||||
|
||||
// ---- Acquire exclusive flock on the PID file --------------------------------
|
||||
//
|
||||
// This serialises concurrent proxy starts. If two tabs SSH in at the
|
||||
|
||||
Reference in New Issue
Block a user