aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-25 20:56:17 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-25 20:56:17 +0300
commit2ea4878bae8fb2bdd04f07d93aa919be6cfa0142 (patch)
tree3556610da0efab26d9918284299c61abc670b8de /src/server
parent58bcd76bfdb76ecbb8cfee868dabe60b43f16cc6 (diff)
feat: gate live handoff behind explicit flag
Diffstat (limited to 'src/server')
-rw-r--r--src/server/handoff.rs36
-rw-r--r--src/server/headless.rs10
2 files changed, 39 insertions, 7 deletions
diff --git a/src/server/handoff.rs b/src/server/handoff.rs
index d38bdc29..6dadfdf4 100644
--- a/src/server/handoff.rs
+++ b/src/server/handoff.rs
@@ -9,7 +9,7 @@ use std::os::unix::process::CommandExt;
#[cfg(unix)]
use std::path::{Path, PathBuf};
#[cfg(unix)]
-use std::process::Command;
+use std::process::{Child, Command};
#[cfg(unix)]
use std::time::Duration;
@@ -71,7 +71,7 @@ pub(crate) fn spawn_handoff_import(
import_exe: Option<&Path>,
socket_path: &Path,
token: &str,
-) -> io::Result<u32> {
+) -> io::Result<Child> {
let fallback_exe;
let exe = if let Some(import_exe) = import_exe {
import_exe
@@ -94,7 +94,7 @@ pub(crate) fn spawn_handoff_import(
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null());
- let child = command.spawn().map_err(|err| {
+ command.spawn().map_err(|err| {
io::Error::new(
err.kind(),
format!(
@@ -102,8 +102,34 @@ pub(crate) fn spawn_handoff_import(
exe.display()
),
)
- })?;
- Ok(child.id())
+ })
+}
+
+#[cfg(unix)]
+pub(crate) fn cleanup_failed_import_child(child: &mut Child) {
+ let pid = child.id();
+ match child.try_wait() {
+ Ok(Some(status)) => {
+ info!(pid, status = %status, "handoff import server exited during rollback");
+ return;
+ }
+ Ok(None) => {}
+ Err(err) => {
+ warn!(pid, err = %err, "failed to inspect handoff import server before rollback");
+ }
+ }
+
+ if let Err(err) = child.kill() {
+ warn!(pid, err = %err, "failed to kill handoff import server during rollback");
+ }
+ match child.wait() {
+ Ok(status) => {
+ info!(pid, status = %status, "handoff import server reaped during rollback");
+ }
+ Err(err) => {
+ warn!(pid, err = %err, "failed to reap handoff import server during rollback");
+ }
+ }
}
#[cfg(unix)]
diff --git a/src/server/headless.rs b/src/server/headless.rs
index bde4d362..2b8550b4 100644
--- a/src/server/headless.rs
+++ b/src/server/headless.rs
@@ -655,17 +655,18 @@ impl HeadlessServer {
params.expected_protocol,
params.expected_version,
);
- let child_pid = match crate::server::handoff::spawn_handoff_import(
+ let mut import_child = match crate::server::handoff::spawn_handoff_import(
import_exe.as_deref(),
&socket_path,
&token,
) {
- Ok(child_pid) => child_pid,
+ Ok(child) => child,
Err(err) => {
self.rollback_handoff_before_commit(&socket_path, &paused_terminal_ids);
return Err(err);
}
};
+ let child_pid = import_child.id();
info!(pid = child_pid, socket = %socket_path.display(), "spawned handoff import server");
let mut fds = Vec::new();
@@ -682,6 +683,7 @@ impl HeadlessServer {
for fd in fds {
let _ = unsafe { libc::close(fd) };
}
+ crate::server::handoff::cleanup_failed_import_child(&mut import_child);
self.rollback_handoff_before_commit(&socket_path, &paused_terminal_ids);
return Err(err);
}
@@ -697,6 +699,7 @@ impl HeadlessServer {
for fd in fds {
let _ = unsafe { libc::close(fd) };
}
+ crate::server::handoff::cleanup_failed_import_child(&mut import_child);
self.rollback_handoff_before_commit(&socket_path, &paused_terminal_ids);
return Err(err);
}
@@ -707,6 +710,7 @@ impl HeadlessServer {
let _ = unsafe { libc::close(fd) };
}
if let Err(err) = send_result {
+ crate::server::handoff::cleanup_failed_import_child(&mut import_child);
self.rollback_handoff_before_commit(&socket_path, &paused_terminal_ids);
return Err(err);
}
@@ -718,6 +722,7 @@ impl HeadlessServer {
}
let _ = remove_socket_file_if_owned(&self.client_socket_path, self.client_socket_identity);
if let Err(err) = crate::server::handoff::wait_ready(&mut stream) {
+ crate::server::handoff::cleanup_failed_import_child(&mut import_child);
match self.wait_then_restore_public_sockets_after_failed_handoff() {
Ok(()) => {
self.rollback_handoff_before_commit(&socket_path, &paused_terminal_ids);
@@ -734,6 +739,7 @@ impl HeadlessServer {
)));
}
if let Err(err) = crate::server::handoff::report_committed(&mut stream) {
+ crate::server::handoff::cleanup_failed_import_child(&mut import_child);
match self.wait_then_restore_public_sockets_after_failed_handoff() {
Ok(()) => {
self.rollback_handoff_before_commit(&socket_path, &paused_terminal_ids);