From ca439a29ff394f51511eef095a1da234d06a6b62 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 1 Nov 2026 04:17:12 +0100 Subject: [PATCH] Pin the systemctl show property list and unquoted backslashes Co-Authored-By: Claude Sonnet 5.5 --- src/steward_orders.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/steward_orders.rs b/src/steward_orders.rs index fdcaafa..44f28db 100644 --- a/src/steward_orders.rs +++ b/src/steward_orders.rs @@ -1289,6 +1289,15 @@ fn parse_names(value: &str) -> Vec { names } +/// The properties one `systemctl show` is asked for: identity, state, edges. +fn show_properties() -> String { + ["Id", "Names", "LoadState", "ActiveState"] + .into_iter() + .chain(EDGES.iter().map(|(property, _)| *property)) + .collect::>() + .join(",") +} + fn parse_show(output: &str) -> Result { let mut facts = UnitFacts::default(); for line in output.lines() { @@ -1315,14 +1324,9 @@ fn parse_show(output: &str) -> Result { impl Systemd for HostSystemd { fn show(&self, unit: &str) -> Result { - let properties = ["Id", "Names", "LoadState", "ActiveState"] - .into_iter() - .chain(EDGES.iter().map(|(property, _)| *property)) - .collect::>() - .join(","); let output = Command::new("systemctl") .args(["show", "--no-pager"]) - .arg(format!("--property={properties}")) + .arg(format!("--property={}", show_properties())) .args(["--", unit]) .stdin(Stdio::null()) .output() @@ -3124,6 +3128,17 @@ mod tests { ["c.service", "ro.service", "d.target", "e.service", "p.service", "f.service"] ); assert!(parse_show("Names=a.service\n").is_err()); + // The request asks for exactly the properties the parse reads. + assert_eq!( + show_properties(), + "Id,Names,LoadState,ActiveState,Requires,Requisite,BindsTo,Wants,Upholds,\ + Conflicts,ConflictedBy,RequiredBy,BoundBy,RequisiteOf,ConsistsOf,\ + PropagatesStopTo,StopPropagatedFrom" + ); + // A backslash outside quotes is a backslash; inside, it escapes one character. + assert_eq!(parse_names("a\\b c\\\\d"), ["a\\b", "c\\\\d"]); + assert_eq!(parse_names("\"a\\\"b\" \"\" c"), ["a\"b", "", "c"]); + assert!(parse_names(" ").is_empty()); let gone = parse_show("Id=\\x22a.service\\x22.service\nLoadState=not-found\n").unwrap(); assert!(gone.not_found); for (state, running) in [