Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/Skidbladnir

crates/skidbladnir-systemd/src/lib.rs

//! **skidbladnir-systemd** — the systemd lifecycle of Skíðblaðnir, on its own
//! (lane B4, A31 §7.5).
//!
//! The backend-neutral [`service::ServiceSpec`] and its renderers (systemd, OpenRC,
//! SysV, runit, s6-rc, dinit, OCI, Quadlet), the shared systemd installer
//! ([`systemd`]: unit render, scope, the service user and dirs, start/stop/status
//! over D-Bus), the drop-in check, the running-binary check, THE UNINSTALL LAW with
//! its guarded purge, and the install identity. A product that installs itself on
//! a systemd host (Holger's `systemd` flavour) links this crate alone.
//!
//! `skidbladnir` re-exports every module here at its old path
//! (`skidbladnir::systemd`, `skidbladnir::service`, …) and adds what is not a
//! systemd host's concern: the Kubernetes and Windows renderers, the signed-bundle
//! refresh and the git watch.

/// Record one functional-status row (`testmatrix`) or one `tracing` event (RED =
/// WARN). The same shim as `skidbladnir::functional_status`.
#[inline]
pub fn functional_status(component: &str, check: &str, ok: bool, detail: &str) {
    #[cfg(feature = "testmatrix")]
    nornir_testmatrix::functional_status(component, check, ok, detail);
    #[cfg(not(feature = "testmatrix"))]
    trace_status(component, check, ok, format_args!("{detail}"));
}

/// The `testmatrix`-off sink: one `tracing` event on target `skidbladnir::surface`.
#[cfg(not(feature = "testmatrix"))]
#[inline]
pub(crate) fn trace_status(component: &str, check: &str, ok: bool, detail: std::fmt::Arguments<'_>) {
    if ok {
        tracing::debug!(target: "skidbladnir::surface", component, check, ok, "{detail}");
    } else {
        tracing::warn!(target: "skidbladnir::surface", component, check, ok, "{detail}");
    }
}

/// One lifecycle-surface row, formatted only when it is wanted (see
/// `skidbladnir::surface_status!`).
macro_rules! surface_status {
    ($component:expr, $check:expr, $ok:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
        #[cfg(feature = "testmatrix")]
        {
            let _ok: bool = $ok;
            $crate::functional_status($component, $check, _ok, &::std::format!($fmt $(, $arg)*));
        }
        #[cfg(not(feature = "testmatrix"))]
        $crate::trace_status($component, $check, $ok, ::std::format_args!($fmt $(, $arg)*));
    }};
}
pub(crate) use surface_status;

/// Reusable systemd-service installer — the extracted, parameterized form of
/// `nornir install systemd` (`SystemdServiceSpec` + pure `systemd_unit`/`env_file`
/// generators + privileged `install_systemd`). Holger and other nordisk products
/// reuse this to install themselves; nornir/holger are NOT yet swapped over.
/// Design + guide: `.nornir/airgap-installer.md`.
pub mod systemd;

/// **An install that is silently overridden must not report success.** systemd
/// resolves a unit as the fragment PLUS `<unit>.service.d/*.conf`, and the drop-in
/// wins. Measured on oden 2026-08-03: the fragment said `User=korp`, the install
/// printed `user korp`, and `systemctl show -p User` answered `alice` — a
/// months-old `rootless-podman.conf` had decided it, and the service therefore
/// resolved its `$HOME`-derived data root into `/home/alice/.korp`. [`dropin`]
/// reads that directory before the install claims anything, names every directive
/// that contradicts the spec, and never deletes one — a drop-in is systemd's own
/// extension seam and skidbladnir does not own it. See [`dropin`].
pub mod dropin;

/// **Is the RUNNING process the binary on disk?** `--version` reads the FILE, not
/// the process, so a service that was rebuilt-but-not-restarted reports the new
/// hash while the old code serves (observed live on oden: `korp-ui --version` said
/// `fda8c87` while `/proc/<pid>/exe` said `(deleted)`). This answers it honestly
/// from `/proc` — deleted-inode check plus start-time-vs-mtime — and treats
/// "undetermined" as NOT a pass. See [`running::Freshness`].
pub mod running;

/// **THE UNINSTALL LAW** — `uninstall` ALWAYS kills the DATA and ALWAYS keeps the
/// SSH KEYS. Not behind a flag, not opt-in, not `--purge`: plain
/// [`service::Lifecycle::uninstall`] does both halves, every time. Holds the ONE
/// declared [`uninstall::DataRoot`] (one absolute path per product, the same whoever
/// owns the process — never `$HOME`, never scope-branched, refused at declaration
/// time if it is or contains a real user's home), the single resolver+eraser pair
/// that reads it ([`uninstall::plan_state_erase`] → [`uninstall::execute_state_erase`]),
/// the identity-sparing walk, the explicit `--wipe-identity` opt-in, and the honest
/// [`uninstall::UninstallReport`]. Design: `.nornir/uninstall-design.md`.
pub mod uninstall;

/// INSTALL IDENTITY — a record minted at install time (per `install_systemd_scoped`)
/// that distinguishes the same machine, a reinstall on the same machine, and a
/// different machine. A reinstall-stable host-derived `machine_fp` plus a fresh
/// UUIDv7 `install_id`. Identity references credentials (ssh key / node cert) by
/// fingerprint; it is not itself a credential. See [`identity::ensure`].
pub mod identity;

/// INSTALL-TREE — the backend-neutral service-install abstraction: ONE
/// declarative [`service::ServiceSpec`] rendered to many init/service systems
/// through the [`service::ServiceRenderer`] trait, and the [`service::Lifecycle`]
/// that drives one service (install, start, stop, status, health, uninstall).
pub mod service;