1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! PMAT-560: exec parity for a `service` — what the loaded unit EXECUTES.
//!
//! `service.rs` asked the host `is-active` and `is-enabled`. On yoga,
//! 2026-09-15, both were true of `github-runner-ephemeral.service` while the
//! unit systemd had loaded ran `run-ephemeral-docker-v3.sh`, a file the repo
//! never declared. This module adds the two questions that were missing:
//!
//! * which program does the LOADED unit start — `systemctl show`, so a
//! drop-in override or an edited unit file is seen for what it is, not
//! the unit file forjar wrote;
//! * what are the bytes of THAT program — `sha256sum` over the live path,
//! never the declared one, because hashing the declared path would
//! confirm the file forjar just wrote and prove nothing about the unit.
//!
//! Every fragment here is gated on the declaration: a `service` that declares
//! neither `exec_start` nor `exec_sha256` emits none of it, so its check asks
//! exactly what it always asked and its observed digest does not move.
use cratesh_squote;
use crateResource;
use crateverdict;
/// Shell variable holding the loaded unit's `ExecStart` program path.
///
/// Named without the word `exec`: bashrs reads `exec` inside an identifier as
/// the builtin and reports SEC016 on every line that mentions it. The fallback
/// groups are `{ …; }`, not `( … )` — a parenthesised group is a subshell to
/// bashrs' SC2031 whether or not the assignment is inside it.
const LIVE: &str = "__fj_unit_prog";
/// Shell variable holding the sha256 of the file at [`LIVE`].
const SHA: &str = "__fj_unit_sha";
/// Read the loaded unit's `ExecStart` program and hash it.
///
/// `systemctl show -p ExecStart --value` prints one
/// `{ path=/x ; argv[]=/x … ; status=0/0 }` line per `ExecStart=`, and an
/// EMPTY line, exit 0, for a unit it has not loaded — so an unloaded unit
/// yields an empty path and `sha256sum ""` yields `missing`, and both diverge
/// from any declaration. A `systemctl show` that FAILS (no dbus, no manager)
/// is folded into the same empty-path shape — under the apply script's
/// `set -eo pipefail` a failing substitution would abort with no marker, and
/// an unmeasured program must read as divergent, never as an opaque death and
/// never as a pass. The awk reads every line and prints the first path: no
/// `head`, so nothing upstream is left writing into a closed pipe under
/// `pipefail` (the SIGPIPE class, PMAT-240). Fields are split on systemd's
/// own ` ; ` separator, not on a bare space — `/opt/my app/run.sh` is a
/// legal program path and a space-split read it as `/opt/my` (found by the
/// PMAT-560 review quorum, 2 of 3 lanes). A path containing the
/// three-character separator itself is the accepted limit: it is misread and
/// reports divergent, which is the failing direction, and the book says so.
/// The divergent branch: the marker, to stdout AND stderr.
///
/// stdout is the `verdict` convention and what the state query is hashed
/// from. stderr is what the operator sees: `cli::check` prints only a failing
/// script's stderr under `exit 1`, and the executor reports a failed apply
/// from stderr too — so a marker on stdout alone names what the unit runs to
/// nobody (found by the PMAT-560 review quorum).
/// The parity assertions for a check or apply script, in `verdict` form.
///
/// Empty when nothing is declared. Otherwise the probe first, then one
/// assertion per declared field. The divergent marker names the LIVE value,
/// because the operator's next question is "so what IS it running?".
/// The state-query lines: the live program and its digest, for the tripwire.
///
/// Empty when nothing is declared, so an undeclared service's observation is
/// byte-identical to what it was before PMAT-560 and no digest on the fleet
/// moves. Once declared, a program swapped after apply moves the digest, and
/// `forjar drift` sees the swap without waiting for a check.
/// The apply-path tail: assert parity and fail the apply when it does not hold.
///
/// Starting and enabling a unit that runs the wrong program is not
/// convergence, and the unit file is another resource's to fix — so the apply
/// stops here, with the markers on stdout and one line on stderr, rather than
/// reporting a resource converged over code nobody reviewed.