Skip to content

Commit 5cd57cd

Browse files
BiomeOS Developercursoragent
andcommitted
S264: stale socket cleanup — daemon shutdown + display Drop + SIGTERM + clippy sweep
primalSpring stale socket audit response: all 6 UDS bind sites already clean (unlink before bind). Fixed two production gaps: - CLI daemon shutdown was a no-op stub — now removes socket file - CLI daemon only handled ctrl_c — now handles SIGTERM via tokio signal - DisplayServer had no Drop impl — now removes display.sock on drop - Fixed 20+ Rust 1.92 clippy issues across upstream code (new_without_default, unnecessary_literal_bound, type_complexity, items_after_statements, collection_is_never_read, single_match_else, io_other_error, etc.) - 85 JSON-RPC methods, 9,028 lib tests, 0 clippy warnings, deny clean Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 577bae9 commit 5cd57cd

24 files changed

Lines changed: 289 additions & 123 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# ToadStool Environment Configuration
2-
# Updated: S263 (May 17, 2026)
2+
# Updated: S264 (May 18, 2026)
33
#
44
# All values have sensible defaults. Only override what you need.
55
# Copy this file to .env and customize for your environment.

CHANGELOG.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,20 @@ All notable changes to ToadStool will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased] - May 17, 2026 (Sessions 43-263)
8+
## [Unreleased] - May 18, 2026 (Sessions 43-264)
9+
10+
### Session S264 (May 18, 2026) — Stale Socket Cleanup: primalSpring Audit Response
11+
12+
primalSpring stale socket cleanup audit response: server-side socket hygiene hardened.
13+
14+
- FIXED: CLI daemon shutdown now removes socket file (was no-op stub)
15+
- FIXED: CLI daemon now handles SIGTERM in addition to SIGINT (was ctrl_c only)
16+
- ADDED: `DisplayServer` Drop impl — removes display socket on drop
17+
- AUDITED: All 6 UDS bind sites already do `unlink()` before `bind()` (no changes needed)
18+
- AUDITED: UniBin server already cleans up both sockets + legacy symlink on SIGINT/SIGTERM
19+
- AUDITED: `IpcServer::Drop` already removes Unix socket files
20+
- FIXED: 20+ pre-existing Rust 1.92 clippy issues across upstream code (`if_not_else`, `ignored_unit_patterns`, `collapsible_if`, `map_unwrap_or`, `default_trait_access`, `new_without_default`, `unnecessary_literal_bound`, `type_complexity`, `needless_late_init`, `too_many_arguments`, `items_after_statements`, `used_underscore_binding`, `collection_is_never_read`, `single_match_else`, `io_other_error`, `match_wildcard_for_single_variants`, `unfulfilled_lint_expectations`)
21+
- METRICS: 85 JSON-RPC methods (direct), 9,028 lib tests, 0 clippy warnings, deny clean
922

1023
### Session S263 (May 17, 2026) — Stadial Gate: primalSpring Audit Response
1124

DEBT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Active Technical Debt Register
22

3-
**Date**: May 2026 — S263
3+
**Date**: May 2026 — S264
44
**Philosophy**: Math is universal, precision is silicon. Workarounds are
55
short-term solutions that increase debt. We aim to solve deep debt over
66
iterations, evolving toward vendor-agnostic, capability-based solutions—

DOCUMENTATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ToadStool Documentation Hub
22

3-
**Last Updated**: May 2026 — S263
3+
**Last Updated**: May 2026 — S264
44

55
---
66

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ToadStool
22

3-
**Sovereign Compute Hardware** | Pure Rust | ecoBin | May 2026 | S263 | v0.2.0
3+
**Sovereign Compute Hardware** | Pure Rust | ecoBin | May 2026 | S264 | v0.2.0
44

55
---
66

@@ -42,7 +42,7 @@ Nest = Tower + Storage <- storage
4242
| `cargo fmt --all -- --check` | 0 diffs |
4343
| `cargo clippy --workspace --all-targets -- -D warnings` | 0 warnings |
4444
| `cargo doc --workspace --no-deps` (RUSTDOCFLAGS="-D warnings") | 0 warnings |
45-
| `cargo test --workspace` | **22,900+ tests, 0 failures** (8,945+ lib-only), **~222** ignored (hardware-gated); full workspace ~7m |
45+
| `cargo test --workspace` | **23,000+ tests, 0 failures** (9,028+ lib-only), **~222** ignored (hardware-gated); full workspace ~7m |
4646
| Doctests | All passing (common, core, server, cli, testing, display) |
4747
| Standalone clone test | Pull to any machine, `cargo test` works (GPU-optional, CPU fallback, device-lost resilient) |
4848
| `unsafe` blocks | **46 actual** (all in hw-safe/GPU/VFIO/display/plugin containment crates); all SAFETY-documented (S204, reconciled S221); workspace `unsafe_code = "deny"`, **41 crates `forbid`** + 5 hw crates with narrow `#[allow(unsafe_code, reason)]`; **all lint attrs have `reason =`** (S211+S213) |

crates/cli/src/daemon/server.rs

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
//! - Workload lifecycle management
99
1010
use crate::Result;
11+
use std::path::PathBuf;
1112
use std::sync::Arc;
1213
use toadstool_common::platform_paths::{PathEnv, PlatformPaths};
13-
use tokio::signal;
1414
use tracing::{info, warn};
1515

1616
use super::config::DaemonConfig;
@@ -30,6 +30,9 @@ pub struct DaemonServer {
3030

3131
/// Workload manager
3232
workload_manager: Arc<WorkloadManager>,
33+
34+
/// Socket path used for the JSON-RPC listener (for cleanup on shutdown).
35+
socket_path: PathBuf,
3336
}
3437

3538
impl DaemonServer {
@@ -75,28 +78,29 @@ impl DaemonServer {
7578
}
7679
}
7780

81+
let socket_path = config.socket_path.clone().unwrap_or_else(|| {
82+
let env = PathEnv::from_env();
83+
PlatformPaths::new(&env).toadstool_socket()
84+
});
85+
7886
info!("✅ ToadStool daemon server initialized");
7987

8088
Ok(Self {
8189
config,
8290
workload_manager: Arc::new(workload_manager),
91+
socket_path,
8392
})
8493
}
8594

8695
/// Run the daemon server until shutdown signal
8796
pub async fn run(self) -> Result<()> {
88-
let socket_path = self.config.socket_path.clone().unwrap_or_else(|| {
89-
let env = PathEnv::from_env();
90-
PlatformPaths::new(&env).toadstool_socket()
91-
});
92-
9397
info!("🚀 ToadStool daemon running");
94-
info!("🍄 JSON-RPC socket: {}", socket_path.display());
98+
info!("🍄 JSON-RPC socket: {}", self.socket_path.display());
9599
info!("📊 Methods: daemon.health, daemon.metrics, daemon.submit_workload, etc.");
96100

97101
// Start JSON-RPC Unix socket server
98102
{
99-
let socket = socket_path.clone();
103+
let socket = self.socket_path.clone();
100104
let manager = Arc::clone(&self.workload_manager);
101105

102106
tokio::spawn(async move {
@@ -120,37 +124,57 @@ impl DaemonServer {
120124

121125
info!("✨ JSON-RPC over UDS per wateringHole standard");
122126

123-
// Wait for shutdown signal
124-
signal::ctrl_c().await?;
127+
// Wait for SIGINT or SIGTERM
128+
wait_for_shutdown().await;
125129

126130
info!("🛑 Shutdown signal received");
127131

128-
// Graceful shutdown
132+
// Graceful shutdown — clean up socket files
129133
self.shutdown().await?;
130134

131135
info!("👋 ToadStool daemon stopped");
132136
Ok(())
133137
}
134138

135-
/// Graceful shutdown
136-
#[expect(
137-
clippy::unused_async,
138-
reason = "async signature required by trait/interface"
139-
)] // Shutdown hook; async for future extension
139+
/// Graceful shutdown — remove socket files to prevent stale socket accumulation.
140140
async fn shutdown(&self) -> Result<()> {
141141
info!("🧹 Performing graceful shutdown...");
142142

143-
// Shutdown sequence:
144-
// 1. Stop accepting new workloads
145-
// 2. Stop JSON-RPC servers (Unix + TCP)
146-
// 3. Gracefully terminate running workloads
147-
// 4. Unregister from coordination service (if registered)
143+
if self.socket_path.exists() {
144+
match tokio::fs::remove_file(&self.socket_path).await {
145+
Ok(()) => info!("Removed socket: {}", self.socket_path.display()),
146+
Err(e) => warn!("Failed to remove socket {}: {e}", self.socket_path.display()),
147+
}
148+
}
148149

149150
info!("✅ Shutdown complete");
150151
Ok(())
151152
}
152153
}
153154

155+
/// Wait for SIGINT (Ctrl+C) or SIGTERM.
156+
async fn wait_for_shutdown() {
157+
#[cfg(unix)]
158+
{
159+
use tokio::signal::unix::{SignalKind, signal};
160+
161+
let mut sigint =
162+
signal(SignalKind::interrupt()).expect("failed to register SIGINT handler");
163+
let mut sigterm =
164+
signal(SignalKind::terminate()).expect("failed to register SIGTERM handler");
165+
166+
tokio::select! {
167+
_ = sigint.recv() => info!("Received SIGINT"),
168+
_ = sigterm.recv() => info!("Received SIGTERM"),
169+
}
170+
}
171+
172+
#[cfg(not(unix))]
173+
{
174+
let _ = tokio::signal::ctrl_c().await;
175+
}
176+
}
177+
154178
#[cfg(test)]
155179
mod tests {
156180
use super::*;

crates/core/cylinder/src/hardware.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ pub trait BootPipeline: Send + Sync + std::fmt::Debug {
198198
type InitResult: std::fmt::Debug + Clone;
199199

200200
/// Device family name (e.g. "Kepler", "Volta", "Vega 20").
201-
fn device_family(&self) -> &str;
201+
fn device_family(&self) -> &'static str;
202202

203203
/// Phase 1: Probe the device — read identity registers, detect warm/cold.
204204
fn probe(

crates/core/cylinder/src/vfio/amd_metal.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,12 @@ pub struct VegaInit {
376376
bdf: Option<String>,
377377
}
378378

379+
impl Default for VegaInit {
380+
fn default() -> Self {
381+
Self::new()
382+
}
383+
}
384+
379385
impl VegaInit {
380386
/// Create a Vega pipeline with no BDF.
381387
pub fn new() -> Self {
@@ -394,7 +400,7 @@ impl BootPipeline for VegaInit {
394400
type ProbeResult = VegaProbeResult;
395401
type InitResult = VegaInitResult;
396402

397-
fn device_family(&self) -> &str {
403+
fn device_family(&self) -> &'static str {
398404
"Vega 20"
399405
}
400406

crates/core/cylinder/src/vfio/boot_state.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ impl SovereignBootState {
177177
}
178178
}
179179

180+
/// Strategy-provided closure that reads falcon registers and classifies state.
181+
pub type FalconDetector<'a> = &'a dyn Fn(&MappedBar, bool) -> FalconWarmState;
182+
180183
/// Probe the GPU's boot state from BAR0 registers.
181184
///
182185
/// This is the authoritative warm/cold classifier. It reads PMC_ENABLE,
@@ -188,7 +191,7 @@ impl SovereignBootState {
188191
/// probing (the state will default to `FalconWarmState::Cold`).
189192
pub fn probe_boot_state(
190193
bar0: &MappedBar,
191-
detect_falcon: Option<&dyn Fn(&MappedBar, bool) -> FalconWarmState>,
194+
detect_falcon: Option<FalconDetector<'_>>,
192195
) -> SovereignBootState {
193196
let pmc_enable = bar0.read_u32(PMC_ENABLE).unwrap_or(0);
194197
let pmc_popcount = pmc_enable.count_ones();

crates/core/cylinder/src/vfio/clutch.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ impl Clutch {
8787
/// # Errors
8888
///
8989
/// Returns error if BAR0 region info ioctl fails or mmap fails.
90-
#[expect(clippy::cast_possible_truncation, reason = "struct argsz always fits u32")]
9190
pub fn engage(
9291
bdf: &str,
9392
device_fd: BorrowedFd<'_>,

0 commit comments

Comments
 (0)