Skip to content

Commit 50133d4

Browse files
neumieclaude
andcommitted
fix: clean up stale dtach sockets on startup
After a crash or ungraceful exit, dtach socket files in /tmp/okena-<uid>/ were left behind indefinitely. Add a startup cleanup that scans the socket directory, checks each .sock file with lsof, and removes any that have no active process — preventing unbounded accumulation of orphaned sockets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6c35fbe commit 50133d4

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

crates/okena-terminal/src/pty_manager.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ impl PtyManager {
107107
log::info!("Session persistence enabled with {:?}", session_backend);
108108
}
109109

110+
// Clean up stale dtach sockets from previous crashes
111+
#[cfg(unix)]
112+
if matches!(session_backend, ResolvedBackend::Dtach) {
113+
std::thread::Builder::new()
114+
.name("dtach-socket-gc".into())
115+
.spawn(|| {
116+
crate::terminal::session_backend::cleanup_stale_dtach_sockets();
117+
})
118+
.ok();
119+
}
120+
110121
(
111122
Self {
112123
terminals: Arc::new(Mutex::new(HashMap::new())),

crates/okena-terminal/src/session_backend.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,46 @@ impl ResolvedBackend {
314314
}
315315
}
316316

317+
/// Remove dtach socket files whose dtach process is no longer running.
318+
/// Called once at startup to clean up after crashes or ungraceful exits.
319+
#[cfg(unix)]
320+
pub fn cleanup_stale_dtach_sockets() {
321+
let dir = get_dtach_socket_dir();
322+
let entries = match std::fs::read_dir(&dir) {
323+
Ok(e) => e,
324+
Err(_) => return, // dir doesn't exist yet — nothing to clean
325+
};
326+
327+
let mut removed = 0;
328+
for entry in entries.flatten() {
329+
let path = entry.path();
330+
if path.extension().and_then(|e| e.to_str()) != Some("sock") {
331+
continue;
332+
}
333+
334+
// Check if any process still has this socket open
335+
let has_listener = Command::new("lsof")
336+
.arg("-t")
337+
.arg(&path)
338+
.output()
339+
.map(|o| !o.stdout.is_empty())
340+
.unwrap_or(false);
341+
342+
if !has_listener {
343+
let _ = std::fs::remove_file(&path);
344+
removed += 1;
345+
}
346+
}
347+
348+
if removed > 0 {
349+
log::info!(
350+
"Cleaned up {} stale dtach socket(s) from {:?}",
351+
removed,
352+
dir
353+
);
354+
}
355+
}
356+
317357
/// Resolve a session backend for a specific WSL distro.
318358
/// Runs `wsl.exe -d <distro> -- sh -c "command -v <tool>"` to check availability.
319359
/// Results are cached per (distro, preference) pair so detection runs at most once.

0 commit comments

Comments
 (0)