pub(crate) const SYSTEMD_UNIT_PATTERN: &str = "opencrabs*.service";
pub(crate) fn build_systemd_restart_command(pid: u32, user: bool) -> std::process::Command {
let unit_name = format!("opencrabs-evolve-{pid}");
let mut cmd = std::process::Command::new("systemd-run");
let mut args = vec![];
if user {
args.push("--user".to_string());
}
args.push("--on-active=3".to_string());
args.push(format!("--unit={unit_name}"));
args.push("systemctl".to_string());
if user {
args.push("--user".to_string());
}
args.push("restart".to_string());
args.push(SYSTEMD_UNIT_PATTERN.to_string());
cmd.args(&args)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null());
cmd
}
pub(crate) const EVOLVE_UNIT_GLOB: &str = "opencrabs-evolve-*.service";
pub(crate) fn build_systemd_cleanup_command(user: bool) -> std::process::Command {
let mut cmd = std::process::Command::new("systemctl");
if user {
cmd.arg("--user");
}
cmd.arg("reset-failed")
.arg(EVOLVE_UNIT_GLOB)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null());
cmd
}
pub(crate) fn count_matching_systemd_units(pattern: &str, user: bool) -> Option<usize> {
let mut cmd = std::process::Command::new("systemctl");
cmd.args(["list-units", "--no-legend", "--no-pager"]);
if user {
cmd.arg("--user");
}
cmd.arg(pattern);
cmd.stderr(std::process::Stdio::null());
let output = cmd.output().ok()?;
if !output.status.success() {
return None;
}
let stdout = String::from_utf8_lossy(&output.stdout);
Some(stdout.lines().filter(|l| !l.trim().is_empty()).count())
}
pub(crate) fn select_unit_bus(sid: uuid::Uuid) -> Option<bool> {
match count_matching_systemd_units(SYSTEMD_UNIT_PATTERN, false) {
Some(0) => {}
Some(n) => {
tracing::info!(
target: "evolve",
pattern = SYSTEMD_UNIT_PATTERN,
matched_units = n,
use_user_units = false,
session_id = %sid,
"evolve: pre-flight found {n} system-level units, scheduling restart (+3s)"
);
return Some(false);
}
None => {
tracing::warn!(
target: "evolve",
pattern = SYSTEMD_UNIT_PATTERN,
session_id = %sid,
"evolve: could not count system-level units (systemctl spawn failed), \
scheduling restart anyway"
);
return Some(false);
}
}
match count_matching_systemd_units(SYSTEMD_UNIT_PATTERN, true) {
Some(n) if n > 0 => {
tracing::info!(
target: "evolve",
pattern = SYSTEMD_UNIT_PATTERN,
user_units = n,
session_id = %sid,
"evolve: no system-level units found, using {n} user-level units, \
scheduling restart with --user"
);
return Some(true);
}
_ => {}
}
tracing::warn!(
target: "evolve",
pattern = SYSTEMD_UNIT_PATTERN,
session_id = %sid,
"evolve: no systemd units matched the pattern (checked system and user level), \
skipping scheduled restart"
);
None
}
pub(crate) fn sweep_stale_evolve_units(use_user_units: bool, sid: uuid::Uuid) {
match build_systemd_cleanup_command(use_user_units).status() {
Ok(status) => tracing::debug!(
target: "evolve",
glob = EVOLVE_UNIT_GLOB,
success = status.success(),
session_id = %sid,
"evolve: reset-failed swept stale evolve units"
),
Err(e) => tracing::warn!(
target: "evolve",
glob = EVOLVE_UNIT_GLOB,
error = %e,
session_id = %sid,
"evolve: could not sweep stale evolve units, the restart still proceeds"
),
}
}