the freenet layer: the app supervises its own peer, so a new release no longer quietly ends it
This commit is contained in:
1 parent
733e3b57a8
commit
07227a90a7
1 file changed
+58
-2
@@ -138,6 +138,11 @@ pub struct NodeInfo {
|
|||||||
const TELEMETRY_ON: &str = "telemetry-enabled = true";
|
const TELEMETRY_ON: &str = "telemetry-enabled = true";
|
||||||
const TELEMETRY_OFF: &str = "telemetry-enabled = false";
|
const TELEMETRY_OFF: &str = "telemetry-enabled = false";
|
||||||
const FIRST_BOOT_WAIT: Duration = Duration::from_secs(20);
|
const FIRST_BOOT_WAIT: Duration = Duration::from_secs(20);
|
||||||
|
/// What the node returns when it has spotted a newer release and wants one.
|
||||||
|
const EXIT_UPDATE_NEEDED: i32 = 42;
|
||||||
|
/// How long to watch a fresh child for that exit. The check runs at startup,
|
||||||
|
/// before any network work, so this only has to outlast process spawn.
|
||||||
|
const UPDATE_EXIT_WAIT: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
struct NodeDirs {
|
struct NodeDirs {
|
||||||
base: PathBuf,
|
base: PathBuf,
|
||||||
@@ -226,6 +231,57 @@ fn spawn_node(bin: &Path, dirs: &NodeDirs, log: &Path) -> Result<Child, String>
|
|||||||
.map_err(|e| format!("start the peer: {e}"))
|
.map_err(|e| format!("start the peer: {e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spawn the peer and be its supervisor.
|
||||||
|
///
|
||||||
|
/// A node that spots a newer release exits with code 42 and waits for someone
|
||||||
|
/// to apply it; Freenet ships that someone as a system service. We cannot use
|
||||||
|
/// one: `freenet service install` takes no `--config-dir`/`--data-dir`, so its
|
||||||
|
/// node would run on the default folders — a different peer with none of this
|
||||||
|
/// app's identity — and fight ours for port 7509. The app owns the node, so
|
||||||
|
/// the app is the supervisor.
|
||||||
|
///
|
||||||
|
/// Without this, an update simply ends the layer: 0.2.133 landed on
|
||||||
|
/// 2026-09-03 and the pinned 0.2.132 exited at every launch, leaving the node
|
||||||
|
/// permanently absent with only a line in node.log to say why. One update, one
|
||||||
|
/// restart, no recursion — if the fresh binary asks again we let it be, rather
|
||||||
|
/// than loop.
|
||||||
|
fn spawn_supervised(bin: &Path, dirs: &NodeDirs, log: &Path) -> Result<Child, String> {
|
||||||
|
let mut child = spawn_node(bin, dirs, log)?;
|
||||||
|
let deadline = Instant::now() + UPDATE_EXIT_WAIT;
|
||||||
|
while Instant::now() < deadline {
|
||||||
|
match child.try_wait() {
|
||||||
|
Ok(Some(status)) if status.code() == Some(EXIT_UPDATE_NEEDED) => {
|
||||||
|
log::warn!("freenet node: exit 42 — a newer release is out; applying it");
|
||||||
|
apply_update(bin)?;
|
||||||
|
let child = spawn_node(bin, dirs, log)?;
|
||||||
|
log::info!("freenet node: updated and restarted (pid {})", child.id());
|
||||||
|
return Ok(child);
|
||||||
|
}
|
||||||
|
// Any other early exit is the caller's problem to report, and a
|
||||||
|
// live child is the normal case.
|
||||||
|
Ok(Some(_)) | Err(_) => break,
|
||||||
|
Ok(None) => std::thread::sleep(Duration::from_millis(100)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(child)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the node's own updater. Blocking — this whole path is already on a
|
||||||
|
/// blocking task, and the download takes as long as it takes.
|
||||||
|
fn apply_update(bin: &Path) -> Result<(), String> {
|
||||||
|
let out = quiet(Command::new(bin))
|
||||||
|
.arg("update")
|
||||||
|
.output()
|
||||||
|
.map_err(|e| format!("could not run freenet update: {e}"))?;
|
||||||
|
if !out.status.success() {
|
||||||
|
return Err(format!(
|
||||||
|
"freenet update failed: {}",
|
||||||
|
String::from_utf8_lossy(&out.stderr).trim()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn node_info(app: &tauri::AppHandle, state: &AppState) -> Result<NodeInfo, String> {
|
fn node_info(app: &tauri::AppHandle, state: &AppState) -> Result<NodeInfo, String> {
|
||||||
let dirs = node_dirs(app)?;
|
let dirs = node_dirs(app)?;
|
||||||
let bin = find_binary(app);
|
let bin = find_binary(app);
|
||||||
@@ -272,7 +328,7 @@ async fn start_node_inner(app: &tauri::AppHandle) -> Result<Child, String> {
|
|||||||
let config = dirs.config.join("config.toml");
|
let config = dirs.config.join("config.toml");
|
||||||
if !config.is_file() {
|
if !config.is_file() {
|
||||||
let first_log = dirs.base.join("first-boot.log");
|
let first_log = dirs.base.join("first-boot.log");
|
||||||
let mut first = spawn_node(&bin, &dirs, &first_log)?;
|
let mut first = spawn_supervised(&bin, &dirs, &first_log)?;
|
||||||
let deadline = Instant::now() + FIRST_BOOT_WAIT;
|
let deadline = Instant::now() + FIRST_BOOT_WAIT;
|
||||||
while !config.is_file() && Instant::now() < deadline {
|
while !config.is_file() && Instant::now() < deadline {
|
||||||
if let Ok(Some(status)) = first.try_wait() {
|
if let Ok(Some(status)) = first.try_wait() {
|
||||||
@@ -291,7 +347,7 @@ async fn start_node_inner(app: &tauri::AppHandle) -> Result<Child, String> {
|
|||||||
log::info!("freenet node: first boot wrote {}", config.display());
|
log::info!("freenet node: first boot wrote {}", config.display());
|
||||||
}
|
}
|
||||||
pin_telemetry_off(&config)?;
|
pin_telemetry_off(&config)?;
|
||||||
let child = spawn_node(&bin, &dirs, &dirs.base.join("node.log"))?;
|
let child = spawn_supervised(&bin, &dirs, &dirs.base.join("node.log"))?;
|
||||||
log::info!("freenet node started (pid {}) from {}", child.id(), bin.display());
|
log::info!("freenet node started (pid {}) from {}", child.id(), bin.display());
|
||||||
Ok(child)
|
Ok(child)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in new issue
Block a user