tests/exec_start_durability.rs
//! **Z2 — the scratch-`ExecStart` trap.** A unit that points into a build tree.
//!
//! # The measurement, oden 2026-08-27
//!
//! `dwarves-server --install` builds its unit around [`std::env::current_exe`], so an
//! install run from a build directory wrote
//!
//! ```text
//! ExecStart=~/git/dwarves/target/release/dwarves-server
//! ```
//!
//! A machine-wide service executing out of a cargo target tree. Every gate that existed
//! passed: the file was there, it was a file, it had the exec bit. And then a routine
//! `cargo clean --target-dir` (a lane reclaimed 139 GB that way the same day) deletes the
//! binary of a live service. The unit stays, the manager still reports it enabled, and
//! the next start fails 203/EXEC with nothing in the unit having changed.
//!
//! **Existence is not durability**, and that is the whole gap.
//!
//! # The red, asserted rather than described
//!
//! [`the_pre_existing_gate_is_green_on_exactly_this`] asserts that
//! `validate_exec_on_disk()` — the gate that was already wired into the install path —
//! returns **Ok** for an `ExecStart` inside a real cargo target tree. That is not a
//! hypothetical: it is the hole, pinned, so the new refusal can never be mistaken for a
//! duplicate of the old one.
//!
//! # Applied output, not a state round-trip
//!
//! [`a_scratch_execstart_is_refused_and_no_unit_is_written`] drives the real
//! `install_systemd_scoped` and then asserts on the FILESYSTEM: no unit file exists. The
//! accepting half writes a real `systemctl --user` unit and reads its `ExecStart=` line
//! back off disk.
#![cfg(all(unix, target_os = "linux"))]
use std::path::{Path, PathBuf};
use skidbladnir::systemd::{
Scope, SystemdServiceSpec, enclosing_cargo_target_dir, install_systemd_scoped,
};
/// A unit name no product uses, so a stray run cannot collide with a real service — and
/// specifically NOT `dwarves-server`, whose live unit another lane owns today.
const UNIT_NAME: &str = "skid-execdurable-selftest";
fn whoami() -> String {
std::env::var("USER")
.or_else(|_| std::env::var("LOGNAME"))
.unwrap_or_default()
}
fn user_unit_dir() -> PathBuf {
PathBuf::from(std::env::var("HOME").expect("HOME")).join(".config/systemd/user")
}
/// A scratch root on **md1** — never `/tmp`, which is RAM on this box (the disk law).
fn scratch(name: &str) -> PathBuf {
let home = std::env::var("HOME").expect("HOME");
let dir = PathBuf::from(home)
.join("scratch")
.join("skid-execdurable")
.join(format!("{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).expect("create the scratch root");
dir
}
/// **A REAL cargo target tree** — the two artefacts cargo itself writes, verbatim.
///
/// Not a directory called `target`: the detector reads the markers, so the fixture must
/// carry the markers. `CACHEDIR.TAG` gets the Cache Directory Tagging Standard signature
/// cargo emits, byte for byte, and `.rustc_info.json` sits beside it. This pair is exactly
/// what `cargo clean --target-dir <dir>` removes — verified against a live lane target dir
/// on oden 2026-08-27 (`~/scratch/cargo/doors-agent` held both).
///
/// Returns `(target_dir, exe_inside_it)`.
fn fake_cargo_target(root: &Path) -> (PathBuf, PathBuf) {
let target = root.join("z-lane");
std::fs::create_dir_all(target.join("release")).expect("create the target tree");
std::fs::write(
target.join("CACHEDIR.TAG"),
"Signature: 8a477f597d28d172789f06886806bc55\n\
# This file is a cache directory tag created by cargo.\n",
)
.expect("write CACHEDIR.TAG");
std::fs::write(target.join(".rustc_info.json"), "{\"rustc_fingerprint\":0}")
.expect("write .rustc_info.json");
let exe = target.join("release").join("sleep");
copy_real_binary(&exe);
(target, exe)
}
/// Copy a real ELF, keeping the basename `sleep`.
///
/// A shell script would not do: `#!` makes the kernel exec the INTERPRETER, and the
/// coreutils binary on this box is a multicall that dispatches on `argv[0]` and exits at
/// once under any other name (that lesson is already paid for in
/// `tests/exec_verify_live.rs`).
fn copy_real_binary(dest: &Path) {
let src = std::fs::canonicalize("/usr/bin/sleep")
.or_else(|_| std::fs::canonicalize("/bin/sleep"))
.expect("need a sleep binary");
std::fs::create_dir_all(dest.parent().unwrap()).expect("create the dest dir");
std::fs::copy(&src, dest).expect("copy the probe binary");
}
fn spec_for(exec: &Path, home: &Path) -> SystemdServiceSpec {
SystemdServiceSpec::new(
UNIT_NAME,
"skidbladnir ExecStart-durability self test",
exec.display().to_string(),
whoami(),
home.display().to_string(),
)
.with_arg("3000")
.with_restart(skidbladnir::systemd::RestartPolicy::No)
}
/// ★ **THE RED.** The gate that was already in the install path is GREEN on exactly the
/// path that killed the service.
///
/// `validate_exec_on_disk` asks: absolute? stat-able? not a directory? executable? A
/// binary in a cargo target tree answers yes to all four. Pinning that here is what
/// stops the new refusal from being read as a second copy of the old one — and what
/// proves the new one is looking at something the old one structurally could not see.
#[test]
fn the_pre_existing_gate_is_green_on_exactly_this() {
let root = scratch("red");
let (target, exe) = fake_cargo_target(&root);
let spec = spec_for(&exe, &root);
assert_eq!(
spec.validate_exec_on_disk(),
Ok(()),
"the EXISTING gate must accept `{}` — if it ever refuses this on its own, the \
durability check below is redundant and should be deleted rather than kept as \
decoration",
exe.display()
);
// And the new one refuses it, naming the tree and the command that deletes it.
let err = spec
.validate_exec_is_durable()
.expect_err("an ExecStart inside a cargo target tree must be REFUSED");
assert!(
err.contains(&target.display().to_string()),
"the refusal must NAME the target dir: {err}"
);
assert!(
err.contains("cargo clean --target-dir"),
"…and the command that will delete it, so the operator can see why: {err}"
);
assert!(
err.contains("203/EXEC"),
"…and the failure it becomes: {err}"
);
}
/// ★ The detector reads cargo's ARTEFACTS, not the path text — in both directions.
#[test]
fn the_target_detector_reads_the_markers_and_not_the_name() {
let root = scratch("detect");
let (target, exe) = fake_cargo_target(&root);
assert_eq!(
enclosing_cargo_target_dir(&exe).as_deref(),
Some(target.as_path()),
"the marker pair must be found from one level down"
);
// A durable location under the SAME root is not flagged: the markers are what matter,
// not the neighbourhood.
let durable = root.join("bin").join("sleep");
copy_real_binary(&durable);
assert_eq!(
enclosing_cargo_target_dir(&durable),
None,
"a binary outside the marked tree must NOT be flagged"
);
// A directory literally NAMED `target`, with no markers, is innocent — and a
// name-based check would libel it. This is the assertion that keeps the detector
// honest.
let innocent = root.join("target").join("release").join("sleep");
copy_real_binary(&innocent);
assert_eq!(
enclosing_cargo_target_dir(&innocent),
None,
"a directory called `target` with no CACHEDIR.TAG/.rustc_info.json is not a cargo \
target dir; flagging it would be a name check wearing a marker check's clothes"
);
// …and conversely, a target dir NOT called `target` IS flagged. This is the case a
// name check misses, and it is where every build on this box actually goes
// (`CARGO_TARGET_DIR=~/scratch/cargo/<lane>`).
assert!(
!target.ends_with("target"),
"the fixture is deliberately named `z-lane`, like the real scratch lanes"
);
// Each marker ALONE must not be enough: plenty of caches carry a CACHEDIR.TAG.
let half = root.join("cache-only");
std::fs::create_dir_all(half.join("release")).unwrap();
std::fs::write(
half.join("CACHEDIR.TAG"),
"Signature: 8a477f597d28d172789f06886806bc55\n",
)
.unwrap();
let half_exe = half.join("release").join("sleep");
copy_real_binary(&half_exe);
assert_eq!(
enclosing_cargo_target_dir(&half_exe),
None,
"CACHEDIR.TAG alone is any cache, not a cargo target dir"
);
}
/// ★ **The declared program path is a refusal, not a warning** — and it discriminates.
#[test]
fn an_execstart_outside_the_declared_program_path_is_refused() {
let root = scratch("progdir");
let bin = root.join("bin");
let elsewhere = root.join("elsewhere");
let installed = bin.join("sleep");
let stray = elsewhere.join("sleep");
copy_real_binary(&installed);
copy_real_binary(&stray);
// Declared: <root>/bin. The stray copy is refused…
let refused = spec_for(&stray, &root)
.with_program_dirs([bin.display().to_string()])
.validate_exec_is_durable()
.expect_err("an ExecStart outside every declared program path must be refused");
assert!(
refused.contains(&elsewhere.display().to_string()),
"the refusal must name where the binary actually is: {refused}"
);
assert!(
refused.contains(&bin.display().to_string()),
"…and what was declared: {refused}"
);
// …and the installed copy is accepted. Without this the guard could be a blanket
// refusal that passes the test above for the wrong reason.
assert_eq!(
spec_for(&installed, &root)
.with_program_dirs([bin.display().to_string()])
.validate_exec_is_durable(),
Ok(()),
"a binary INSIDE the declared program path must install"
);
// A symlinked or `..`-laden spelling of the same directory is the same directory:
// the comparison is on canonicalised paths, so an operator's `--prefix
// <root>/bin/../bin` is not a different answer.
let noisy = format!("{}/../bin", bin.display());
assert_eq!(
spec_for(&installed, &root)
.with_program_dirs([noisy.clone()])
.validate_exec_is_durable(),
Ok(()),
"`{noisy}` names the same directory and must be accepted"
);
// UNDECLARED (empty) leaves the program-path rule off — the build-tree refusal is
// unconditional, this one is opt-in, and that difference has to be visible.
assert_eq!(
spec_for(&stray, &root).validate_exec_is_durable(),
Ok(()),
"with no declared program path there is nothing to be outside of"
);
}
/// **The refusal writes NOTHING** — no systemd needed.
///
/// The applied-output assertion is the FILESYSTEM, not a `Result`: after the refusal there
/// must be no unit file at all. An installer that refused after writing the unit would
/// return the same `Err` and leave the trap armed. The refusal comes before any bus is
/// dialled, so this half runs everywhere, a container with no `systemd --user` included.
///
/// Until 2026-10-25 this and the accepting install below were ONE test, so a box with no
/// user bus (the CI container, ODEN-CI round 1) saw the whole test red at the install
/// half, for a reason that had nothing to do with the refusal.
#[test]
fn a_scratch_execstart_is_refused_and_no_unit_is_written() {
let root = scratch("refuse");
let (target, scratch_exe) = fake_cargo_target(&root);
let unit_file = user_unit_dir().join(format!("{UNIT_NAME}.service"));
// Start from a known state; a leftover from an interrupted run would fake either
// answer.
let _ = std::fs::remove_file(&unit_file);
assert!(!unit_file.exists(), "the fixture starts with no unit file");
let err = install_systemd_scoped(&spec_for(&scratch_exe, &root), Scope::User)
.expect_err("installing a unit around a build-tree binary must be refused");
let msg = format!("{err:#}");
assert!(
msg.contains(&target.display().to_string()),
"the refusal must name the target tree: {msg}"
);
assert!(
!unit_file.exists(),
"REFUSED BUT WROTE ANYWAY: {} exists. A refusal after the write leaves the trap \
armed and returns the same Err.",
unit_file.display()
);
}
/// ★ **LIVE, against real systemd: the accepting install writes the durable path.**
///
/// It installs a scratch `systemctl --user` unit (never a live one), so it needs the
/// caller's user bus and is `#[ignore]` by this repository's rule
/// (`tests/live_tests_are_ignored.rs`). Without a bus it FAILS, it never passes green.
/// Run it with `cargo test --test exec_start_durability -- --ignored` on a box with
/// `systemd --user`.
#[test]
#[ignore = "live: installs a scratch systemctl --user unit; needs the caller's user bus"]
fn a_durable_execstart_installs_and_the_unit_execs_the_durable_path() {
let root = scratch("live");
let installed = root.join("bin").join("sleep");
copy_real_binary(&installed);
let home = PathBuf::from(std::env::var("HOME").expect("HOME"));
let unit_file = home
.join(".config/systemd/user")
.join(format!("{UNIT_NAME}.service"));
let _ = std::fs::remove_file(&unit_file);
// ── the accepting install, with the program path DECLARED ─────────────────────
let spec = spec_for(&installed, &root)
.with_program_dirs([root.join("bin").display().to_string()]);
install_systemd_scoped(&spec, Scope::User).expect("a durable ExecStart must install");
let unit = std::fs::read_to_string(&unit_file).expect("the unit file must now exist");
let exec_line = unit
.lines()
.find(|l| l.starts_with("ExecStart="))
.expect("the unit must carry an ExecStart");
assert!(
exec_line.contains(&installed.display().to_string()),
"the APPLIED ExecStart must be the durable path, got `{exec_line}`"
);
assert!(
enclosing_cargo_target_dir(Path::new(
exec_line
.trim_start_matches("ExecStart=")
.split_whitespace()
.next()
.unwrap()
))
.is_none(),
"and the path systemd will actually exec must not be in a build tree: {exec_line}"
);
// Clean up: stop + disable + remove the unit through the ship's own verb, never by
// hand.
let lc = skidbladnir::service::Lifecycle::new(
skidbladnir::service::ServiceSpec::new(
UNIT_NAME,
"skidbladnir ExecStart-durability self test",
installed.display().to_string(),
)
.scope(Scope::User),
);
let _ = lc.uninstall();
let _ = std::fs::remove_file(&unit_file);
}