gunnar-server 2.0.0

The gunnar git server: SSH and smart-HTTP git transports and the gRPC control plane. The appliance half, extracted from gunnar-cli so the server code lives in its own crate.
Documentation
//! **Which writer arm this process appends through, decided once, said once.**
//!
//! `znippy-plugin-git` has two durable arms with ONE durability contract (the
//! blob's bytes are durable before the journal row that references them):
//! `safe` = pwrite → fsync → write → fsync as four blocking syscalls, `uring` =
//! the same four as ONE `io_uring_enter` with `IOSQE_IO_LINK`. znippy ships
//! `safe` and never falls back on its own — `UringWriter::create` refuses on a
//! kernel without a ring, by name, because a pwrite number reported as an
//! io_uring number would be a lie. **A server is the one process that has to
//! choose before it opens its first store**, and on the appliance the kernel is
//! whatever the medium's donor built (tunnr shipped `# CONFIG_IO_URING is not
//! set` until `[build.installer.data] io = "uring"` asked for it). So:
//!
//! 1. the operator's `ZNIPPY_GIT_WRITER=` wins, spelled exactly (a dev box, a
//!    bench column);
//! 2. else the medium's `korp.data.io=uring|safe` token off `/proc/cmdline`
//!    (baked by nornir-build-thing from the recipe's `data.io`, carried onto
//!    the installed disk as a site token — the same path `korp.data.fs` takes);
//! 3. else znippy's shipping default (`safe`).
//!
//! Then, and only when the answer is `uring`, ONE `io_uring_setup` probe
//! ([`znippy_plugin_git::uring_write::probe`]): a refusal (`ENOSYS` = no
//! ring in this kernel, `EPERM` = `kernel.io_uring_disabled`/seccomp, …) picks
//! `safe` **by name** — the row says `writer  safe (io_uring unavailable:
//! ENOSYS)`, never a silent downgrade. The dashboard's disk cell carries the
//! arm that was built (`… xfs uring`), so a screen photo says which column a
//! number belongs to.
//!
//! [`resolve`] is pure (env value, cmdline text, a probe closure) so the four
//! doors are asserted without a kernel; [`select`] is the one call
//! `znippy_engine::arms` makes, and [`selected`] is what the dashboard reads.

use std::sync::OnceLock;

use znippy_plugin_git::arms::{StoreConfig, WriterArm};

/// The cmdline key — `korp-installer::cmdline::DATA_IO_KEY` and
/// `nornir-build-thing::installer::DATA_IO_BOOT_PARAM` are the same spelling.
pub const DATA_IO_KEY: &str = "korp.data.io";

/// Where the arm came from, for the row.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Source {
    /// `ZNIPPY_GIT_WRITER=` in the environment.
    Env,
    /// `korp.data.io=` on the kernel command line.
    Cmdline,
    /// Nobody asked: znippy's shipping default.
    Default,
}

impl Source {
    pub fn as_str(self) -> &'static str {
        match self {
            Source::Env => "env",
            Source::Cmdline => "cmdline",
            Source::Default => "default",
        }
    }
}

/// The decision: the arm that was built, where the wish came from, and — when
/// the wish was `uring` and the kernel said no — the refusal, named.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Selection {
    pub arm: WriterArm,
    pub source: Source,
    /// `Some("io_uring unavailable: ENOSYS")` when `uring` was asked for and
    /// `safe` was built instead.
    pub fallback: Option<String>,
}

impl Selection {
    /// The row text: `uring`, `safe`, or `safe (io_uring unavailable: ENOSYS)`.
    pub fn describe(&self) -> String {
        match &self.fallback {
            Some(why) => format!("{} ({why})", self.arm.as_str()),
            None => self.arm.as_str().to_owned(),
        }
    }
}

/// The LAST `korp.data.io=` token on `cmdline` (an operator-appended token
/// overrides a baked one, the `korp.env.*` rule), `None` when absent.
pub fn data_io_token(cmdline: &str) -> Option<&str> {
    cmdline
        .split_whitespace()
        .filter_map(|tok| {
            tok.strip_prefix(DATA_IO_KEY)
                .and_then(|rest| rest.strip_prefix('='))
        })
        .map(|v| v.trim_matches('"'))
        .rfind(|v| !v.is_empty())
}

/// The errno's NAME for the row — `ENOSYS` says "this kernel has no ring" in
/// one word where `Function not implemented (os error 38)` does not.
fn errno_name(e: &std::io::Error) -> String {
    match e.raw_os_error() {
        Some(1) => "EPERM".into(),
        Some(12) => "ENOMEM".into(),
        Some(13) => "EACCES".into(),
        Some(22) => "EINVAL".into(),
        Some(23) => "ENFILE".into(),
        Some(24) => "EMFILE".into(),
        Some(38) => "ENOSYS".into(),
        Some(n) => format!("errno {n}"),
        None => e.to_string(),
    }
}

/// **The pure decision.** `env` is `ZNIPPY_GIT_WRITER`'s value as znippy
/// already parsed it into `cfg.writer` (`env_set` says whether it was set at
/// all — an unset variable is not a wish for `safe`); `cmdline` is the text of
/// `/proc/cmdline`; `probe` is asked at most once, and only for `uring`.
pub fn resolve(
    cfg: StoreConfig,
    env_set: bool,
    cmdline: &str,
    probe: impl FnOnce() -> std::io::Result<()>,
) -> (StoreConfig, Selection) {
    let (wanted, source) = if env_set {
        (cfg.writer, Source::Env)
    } else {
        match data_io_token(cmdline).and_then(|v| WriterArm::parse(v).ok()) {
            Some(arm) => (arm, Source::Cmdline),
            None => (cfg.writer, Source::Default),
        }
    };
    let (arm, fallback) = match wanted {
        WriterArm::Uring => match probe() {
            Ok(()) => (WriterArm::Uring, None),
            Err(e) => (
                WriterArm::Safe,
                Some(format!("io_uring unavailable: {}", errno_name(&e))),
            ),
        },
        other => (other, None),
    };
    (
        cfg.with_writer(arm),
        Selection {
            arm,
            source,
            fallback,
        },
    )
}

static SELECTED: OnceLock<Selection> = OnceLock::new();

/// **The one call the engine makes**, with the real environment, the real
/// `/proc/cmdline` and the real probe. Logs the row once, remembers the
/// answer for [`selected`]. Idempotent: a second call returns the first
/// decision without probing again.
pub fn select(cfg: StoreConfig) -> StoreConfig {
    let env_set =
        std::env::var_os(znippy_plugin_git::arms::ENV_WRITER).is_some_and(|v| !v.is_empty());
    let cmdline = std::fs::read_to_string("/proc/cmdline").unwrap_or_default();
    let (cfg, sel) = resolve(
        cfg,
        env_set,
        &cmdline,
        znippy_plugin_git::uring_write::probe,
    );
    let sel = SELECTED.get_or_init(|| sel).clone();
    tracing::info!(
        target: "gunnar::store",
        event = "store.writer_selected",
        writer = sel.arm.as_str(),
        source = sel.source.as_str(),
        fallback = sel.fallback.as_deref().unwrap_or("-"),
        "the writer arm every store in this process appends through"
    );
    cfg.with_writer(sel.arm)
}

/// The arm that was built, once [`select`] ran; `None` before the first store.
pub fn selected() -> Option<&'static Selection> {
    SELECTED.get()
}

#[cfg(test)]
mod tests {
    use super::*;

    fn nosys() -> std::io::Result<()> {
        Err(std::io::Error::from_raw_os_error(38))
    }
    fn yes() -> std::io::Result<()> {
        Ok(())
    }
    fn never() -> std::io::Result<()> {
        panic!("the probe is asked only for uring")
    }

    #[test]
    fn the_token_is_the_last_one_and_absent_is_none() {
        assert_eq!(
            data_io_token("console=tty0 korp.data.fs=xfs korp.data.io=uring korp.entry=gunnar"),
            Some("uring")
        );
        assert_eq!(
            data_io_token("korp.data.io=uring korp.data.io=safe"),
            Some("safe"),
            "an appended token overrides a baked one"
        );
        assert_eq!(data_io_token("korp.data.io=\"uring\""), Some("uring"));
        assert_eq!(data_io_token("korp.data.iox=uring korp.data.io="), None);
        assert_eq!(data_io_token(""), None);
    }

    /// The four doors: env wins over the token; the token picks; nothing asked
    /// = the shipping default and the probe is never bothered; `uring` refused
    /// = `safe` BY NAME with the errno on the row.
    #[test]
    fn env_then_cmdline_then_default_and_a_refused_ring_falls_back_by_name() {
        let d = StoreConfig::DEFAULT;
        let (cfg, s) = resolve(d, false, "korp.entry=gunnar", never);
        assert_eq!(
            (cfg.writer, s.source, s.fallback.clone()),
            (WriterArm::Safe, Source::Default, None)
        );
        assert_eq!(s.describe(), "safe");

        let (cfg, s) = resolve(d, false, "korp.data.io=uring", yes);
        assert_eq!(
            (cfg.writer, s.source, s.fallback.clone()),
            (WriterArm::Uring, Source::Cmdline, None)
        );
        assert_eq!(s.describe(), "uring");
        assert_eq!(cfg.index, d.index, "only the writer moves");

        let (cfg, s) = resolve(d, false, "korp.data.io=uring", nosys);
        assert_eq!((cfg.writer, s.source), (WriterArm::Safe, Source::Cmdline));
        assert_eq!(s.describe(), "safe (io_uring unavailable: ENOSYS)");

        let (cfg, s) = resolve(d, false, "korp.data.io=uring", || {
            Err(std::io::Error::from_raw_os_error(1))
        });
        assert_eq!(
            (cfg.writer, s.describe()),
            (
                WriterArm::Safe,
                "safe (io_uring unavailable: EPERM)".to_owned()
            )
        );

        // env says safe on a medium that says uring: the operator wins, no probe.
        let (cfg, s) = resolve(
            d.with_writer(WriterArm::Safe),
            true,
            "korp.data.io=uring",
            never,
        );
        assert_eq!((cfg.writer, s.source), (WriterArm::Safe, Source::Env));
        // env says uring: probed like any other wish.
        let (cfg, s) = resolve(d.with_writer(WriterArm::Uring), true, "", nosys);
        assert_eq!((cfg.writer, s.source), (WriterArm::Safe, Source::Env));
        assert!(s.fallback.is_some());
        // a token that is not an arm is ignored, not a crash.
        let (cfg, s) = resolve(d, false, "korp.data.io=aio", never);
        assert_eq!((cfg.writer, s.source), (WriterArm::Safe, Source::Default));
    }

    /// The real `select` is decided ONCE per process — whatever test or store
    /// asked first — and a later call with another wish neither re-probes nor
    /// changes its mind; and a built `uring` implies this kernel's probe says yes.
    #[test]
    fn select_is_decided_once_and_never_builds_uring_the_kernel_refuses() {
        let cfg = select(StoreConfig::DEFAULT.with_writer(WriterArm::Uring));
        let first = selected().expect("selected after select").clone();
        assert_eq!(cfg.writer, first.arm);
        let again = select(StoreConfig::DEFAULT);
        assert_eq!(again.writer, first.arm, "remembered, not re-decided");
        assert_eq!(selected().unwrap(), &first);
        if first.arm == WriterArm::Uring {
            znippy_plugin_git::uring_write::probe().expect("uring was built, so the ring exists");
            assert!(first.fallback.is_none());
        }
        if first.fallback.is_some() {
            assert_eq!(first.arm, WriterArm::Safe, "a fallback is always to safe");
        }
    }
}