Merge nucleic/olive-ember-seal-q7vk into dev

This commit is contained in:
2026-07-18 15:14:54 -07:00
parent 19218c7dae
commit 5ff6fd8631
5 changed files with 192 additions and 2 deletions
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "naros-init"
version = "0.1.0"
edition = "2021"
description = "narOS PID-1 supervisor for container surfaces (docs/NAROS.md §5)"
license = "MIT"
[dependencies]
libc = "0.2"
+154
View File
@@ -0,0 +1,154 @@
//! naros-init — narOS PID-1 supervisor for container surfaces (docs/NAROS.md §5).
//!
//! Supervision and plumbing only, no policy:
//! - reaps zombies (the historical job of PID 1);
//! - forwards SIGTERM/SIGINT to supervised children;
//! - `naros-init -- CMD ARGS…` supervises a primary command and exits with its status
//! (129+signum on signal death), replacing `sh -c` wrappers as the container entrypoint;
//! - with no primary command it is the keepalive that replaces ContainerEngine's
//! sleep loop, staying alive until signalled;
//! - NAROS_BRIDGE=1 additionally supervises the control bridge
//! (`node /opt/nucleic/control-bridge.js`), restarting it with backoff — the bridge is
//! best-effort transport, so its failures never affect the primary command or init.
//!
//! In the VM desktop flavor systemd stays PID 1 and this binary runs as a role unit.
use std::env;
use std::process::{exit, Command};
use std::sync::atomic::{AtomicI32, Ordering};
use std::time::{Duration, Instant};
static PENDING_SIGNAL: AtomicI32 = AtomicI32::new(0);
extern "C" fn on_signal(sig: libc::c_int) {
PENDING_SIGNAL.store(sig, Ordering::SeqCst);
}
fn install_handlers() {
unsafe {
let handler = on_signal as *const () as usize;
for sig in [libc::SIGTERM, libc::SIGINT] {
libc::signal(sig, handler);
}
// SIG_DFL for SIGCHLD keeps children reapable via waitpid below.
}
}
/// Reap every exited child; returns the primary's status when it is among them.
fn reap(primary: Option<libc::pid_t>, bridge: Option<libc::pid_t>) -> (Option<i32>, bool) {
let mut primary_status = None;
let mut bridge_died = false;
loop {
let mut status: libc::c_int = 0;
let pid = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG) };
if pid <= 0 {
break;
}
let code = if libc::WIFEXITED(status) {
libc::WEXITSTATUS(status)
} else if libc::WIFSIGNALED(status) {
128 + libc::WTERMSIG(status)
} else {
1
};
if Some(pid) == primary {
primary_status = Some(code);
} else if Some(pid) == bridge {
bridge_died = true;
}
}
(primary_status, bridge_died)
}
fn forward(sig: i32, pids: &[Option<libc::pid_t>]) {
for pid in pids.iter().flatten() {
unsafe {
libc::kill(*pid, sig);
}
}
}
fn spawn_bridge() -> Option<libc::pid_t> {
const BRIDGE: &str = "/opt/nucleic/control-bridge.js";
if !std::path::Path::new(BRIDGE).exists() {
return None;
}
match Command::new("node").arg(BRIDGE).spawn() {
Ok(child) => Some(child.id() as libc::pid_t),
Err(err) => {
eprintln!("naros-init: bridge spawn failed: {err}");
None
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.get(1).map(String::as_str) == Some("--version") {
println!("naros-init {}", env!("CARGO_PKG_VERSION"));
return;
}
install_handlers();
let cmd: Vec<&String> = match args.iter().position(|a| a == "--") {
Some(i) => args[i + 1..].iter().collect(),
None => Vec::new(),
};
let mut primary: Option<libc::pid_t> = None;
if let Some((prog, rest)) = cmd.split_first() {
match Command::new(prog).args(rest).spawn() {
Ok(child) => primary = Some(child.id() as libc::pid_t),
Err(err) => {
eprintln!("naros-init: exec {prog}: {err}");
exit(127);
}
}
}
let want_bridge = env::var("NAROS_BRIDGE").ok().as_deref() == Some("1");
let mut bridge = if want_bridge { spawn_bridge() } else { None };
let mut bridge_backoff = Duration::from_millis(500);
let mut bridge_retry_at: Option<Instant> = None;
loop {
let sig = PENDING_SIGNAL.swap(0, Ordering::SeqCst);
if sig != 0 {
forward(sig, &[primary, bridge]);
if primary.is_none() {
// Keepalive role: the signal is our own shutdown request.
exit(128 + sig);
}
}
let (primary_status, bridge_died) = reap(primary, bridge);
if let Some(code) = primary_status {
forward(libc::SIGTERM, &[bridge]);
reap(None, bridge);
exit(code);
}
if bridge_died {
bridge = None;
bridge_retry_at = Some(Instant::now() + bridge_backoff);
bridge_backoff = (bridge_backoff * 2).min(Duration::from_secs(30));
}
if want_bridge && bridge.is_none() {
if let Some(at) = bridge_retry_at {
if Instant::now() >= at {
bridge = spawn_bridge();
bridge_retry_at = None;
if bridge.is_some() {
bridge_backoff = Duration::from_millis(500);
} else {
bridge_retry_at = Some(Instant::now() + bridge_backoff);
}
}
} else {
bridge_retry_at = Some(Instant::now());
}
}
std::thread::sleep(Duration::from_millis(100));
}
}