1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//! Daemon lock file: written on bind, deleted on shutdown.
//!
//! Why: Clients use the lock file to discover the daemon's actual address
//! (which may differ from the configured port if auto-selection kicked in).
//! The file lives at `~/.trusty-mpm/daemon.lock` — the same framework root
//! every other artifact uses — so the daemon and its clients always agree on
//! its location.
//! What: `write_lock` creates the TOML file; `remove_lock` deletes it.
//! Both are best-effort — errors are logged but not fatal.
//! Test: `write_lock` / `remove_lock` round-trip covered below.
use crate::core::lock_file_path;
/// Write the daemon lock file with the actual bound address and current PID.
///
/// Why: Must be called after `TcpListener::local_addr()` is known. As of
/// ADR-0011's loopback-only doctrine (issue #3330) the daemon never binds a
/// second (Tailscale) listener, so the lock file no longer carries an
/// optional `tailscale_addr` field.
/// What: Creates parent dirs if needed, writes TOML, logs on error.
pub fn write_lock(addr: &str) {
let path = lock_file_path();
if let Some(parent) = path.parent() {
let _ = std::fs::create_dir_all(parent);
}
let pid = std::process::id();
let started_at = chrono::Utc::now().to_rfc3339();
let content = format!("pid = {pid}\naddr = \"{addr}\"\nstarted_at = \"{started_at}\"\n");
if let Err(e) = std::fs::write(&path, content) {
tracing::warn!("failed to write daemon lock file {}: {e}", path.display());
} else {
tracing::debug!("lock file written: {}", path.display());
}
}
/// Remove the daemon lock file on clean shutdown.
///
/// Why: A deleted lock file signals that no daemon is running, avoiding stale
/// reads by clients that check the file before falling back to the default URL.
/// What: Best-effort `fs::remove_file`; silently ignores "not found".
pub fn remove_lock() {
let path = lock_file_path();
match std::fs::remove_file(&path) {
Ok(()) => tracing::debug!("lock file removed"),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
Err(e) => tracing::warn!("failed to remove lock file: {e}"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_and_remove_round_trip() {
// Just verify the functions don't panic on a writable path.
write_lock("http://127.0.0.1:7880");
remove_lock();
}
}