nx_printer_health.nx source
↩ module page · 166 lines · 7973 B
1// nx_printer_health.nx -- honest-status / anti-silent-failure model layer (R1) over nx_ipp_codec.
2//
3// THE operator complaint this targets: printers "silently fail". A real IPP printer always tells you
4// WHY it is stuck via printer-state + printer-state-reasons; Brother's UI swallows it. This layer reads
5// a parsed Get-Printer-Attributes response and answers, honestly and mechanically, the only question a
6// user cares about: "can this thing actually print right now, and if not, exactly why?"
7//
8// Grounded in RFC 8011 sec 5.4.11/5.4.12 (fetched by the sovereign researcher -> ipp_rfc8011.raw):
9// - printer-state: idle=3 processing=4 stopped=5
10// - printer-state-reasons (1setOf keyword): each value MAY carry a severity suffix
11// '-report' (least severe; MUST NOT affect output)
12// '-warning' (won't stop the Job; output may be lower quality)
13// '-error' (most severe; if ANY error reason is present the printer MUST be 'stopped')
14// If a value has NO suffix and is not 'none': it is an ERROR when the printer is 'stopped',
15// otherwise a WARNING. 'none' means there are no reasons.
16// - printer-is-accepting-jobs (boolean, sec 5.4.23)
17//
18// NEVER-BRICK (#26): pure model logic; no syscalls, no hardware writes. Safe by construction.
19// no_silent_failure: a malformed response yields NX_PR_UNKNOWN, never a false "ready".
20// Sovereign: imports only nx_ipp_codec.nx; nx_cc -> nxasm, no gcc.
21//
22// genealogy_id: project-printer-management-ipp-sclass-2026-06-20
23// license_tier: ORIGINAL
24//
25// nx_capability_claims:
26// provides: [printer_reason_severity, printer_worst_severity, printer_accepting, printer_can_print,
27// printer_diagnose]
28// safety: [no_unchecked_deref, no_floating_point, no_syscall, bounded_iteration,
29// never_brick_pure_model, defensive_bounds_checked]
30// verdict: [sealed_enum_severity, sealed_enum_can_print, no_silent_failure]
31// sss: [S6, S7]
32
33import "nx_ipp_codec.nx"
34
35// ---- sealed severity enum (RFC 8011 sec 5.4.12 levels) -------------
36const NX_SEV_NONE: i64 = 0
37const NX_SEV_REPORT: i64 = 1
38const NX_SEV_WARNING: i64 = 2
39const NX_SEV_ERROR: i64 = 3
40
41// ---- sealed can-print verdict -------------------------------------
42const NX_PR_CAN_PRINT: i64 = 1 // ready
43const NX_PR_DEGRADED: i64 = 2 // will print, but needs attention (a warning is active)
44const NX_PR_BLOCKED: i64 = 3 // will NOT print until a human acts (stopped / not accepting / error)
45const NX_PR_UNKNOWN: i64 = 4 // could not be determined (malformed/absent) -- never a false "ready"
46const NX_PR_VERDICT_N: i64 = 5
47
48func nx_pr_verdict_is_valid(v: i64) -> i64 {
49 if v <= 0 { return 0 }
50 if v >= NX_PR_VERDICT_N { return 0 }
51 return 1
52}
53
54// 1 if buf[off .. off+len) ends with the null-terminated suffix `suf`.
55func nx_ph_ends_with(buf: *u8, off: i64, len: i64, suf: *u8) -> i64 {
56 let sl: i64 = nx_ipp_strlen(suf)
57 if sl > len { return 0 }
58 let start: i64 = off + len - sl
59 return nx_ipp_name_eq(buf, start, suf, sl)
60}
61
62// Severity of one printer-state-reasons keyword value buf[off .. off+len), given the printer-state.
63func nx_ph_reason_severity(buf: *u8, off: i64, len: i64, pstate: i64) -> i64 {
64 if len == 4 {
65 if nx_ipp_name_eq(buf, off, "none", 4) == 1 { return NX_SEV_NONE }
66 }
67 if nx_ph_ends_with(buf, off, len, "-error") == 1 { return NX_SEV_ERROR }
68 if nx_ph_ends_with(buf, off, len, "-warning") == 1 { return NX_SEV_WARNING }
69 if nx_ph_ends_with(buf, off, len, "-report") == 1 { return NX_SEV_REPORT }
70 // no suffix and not 'none' (RFC 8011 sec 5.4.12)
71 if pstate == NX_IPP_PSTATE_STOPPED { return NX_SEV_ERROR }
72 return NX_SEV_WARNING
73}
74
75// Walk EVERY value of printer-state-reasons (the attribute value + its 1setOf additional-values) and
76// return the worst severity; writes the value count via out_count. Returns -1 if the message is
77// malformed (length overrun) -- the no-silent-failure signal. Bounded: off strictly increases.
78func nx_ph_worst_reason_severity(buf: *u8, n: i64, pstate: i64, out_count: *i64) -> i64 {
79 out_count[0] = 0
80 if n < 8 { return 0 - 1 }
81 var off: i64 = 8
82 var worst: i64 = NX_SEV_NONE
83 var cnt: i64 = 0
84 var in_reasons: i64 = 0
85 var bad: i64 = 0
86 var keep: i64 = 1
87 while keep == 1 {
88 if off >= n { keep = 0 }
89 else {
90 let tag: i64 = buf[off] as i64
91 if tag == NX_IPP_TAG_END { keep = 0 }
92 else {
93 if tag <= 0x0f { off = off + 1; in_reasons = 0 }
94 else {
95 if (off + 3) > n { bad = 1; keep = 0 }
96 else {
97 let nlen: i64 = nx_ipp_get_u16(buf, off + 1)
98 let name_off: i64 = off + 3
99 if (name_off + nlen + 2) > n { bad = 1; keep = 0 }
100 else {
101 let vlen_off: i64 = name_off + nlen
102 let vlen: i64 = nx_ipp_get_u16(buf, vlen_off)
103 let val_off: i64 = vlen_off + 2
104 if (val_off + vlen) > n { bad = 1; keep = 0 }
105 else {
106 if nlen > 0 {
107 if nx_ipp_name_eq(buf, name_off, "printer-state-reasons", 21) == 1 { in_reasons = 1 }
108 else { in_reasons = 0 }
109 }
110 if in_reasons == 1 {
111 let sev: i64 = nx_ph_reason_severity(buf, val_off, vlen, pstate)
112 if sev > worst { worst = sev }
113 cnt = cnt + 1
114 }
115 off = val_off + vlen
116 }
117 }
118 }
119 }
120 }
121 }
122 }
123 if bad == 1 { return 0 - 1 }
124 out_count[0] = cnt
125 return worst
126}
127
128// printer-is-accepting-jobs: returns 1 (accepting), 0 (refusing), or -1 (not present / malformed).
129// `scratch3` is a caller-provided i64[3] work area.
130func nx_ph_accepting(buf: *u8, n: i64, scratch3: *i64) -> i64 {
131 let p_tag: *i64 = scratch3
132 let p_voff: *i64 = ((scratch3 as i64) + 8) as *i64
133 let p_vlen: *i64 = ((scratch3 as i64) + 16) as *i64
134 let v: i64 = nx_ipp_find(buf, n, "printer-is-accepting-jobs", p_tag, p_voff, p_vlen)
135 if v != NX_IPP_OK { return 0 - 1 }
136 if p_tag[0] != NX_IPP_VT_BOOLEAN { return 0 - 1 }
137 if p_vlen[0] != 1 { return 0 - 1 }
138 return buf[p_voff[0]] as i64
139}
140
141// The honest verdict: can this printer print right now?
142func nx_printer_can_print(pstate: i64, accepting: i64, worst_sev: i64) -> i64 {
143 if pstate < 0 { return NX_PR_UNKNOWN }
144 if worst_sev < 0 { return NX_PR_UNKNOWN }
145 if pstate == NX_IPP_PSTATE_STOPPED { return NX_PR_BLOCKED }
146 if accepting == 0 { return NX_PR_BLOCKED }
147 if worst_sev == NX_SEV_ERROR { return NX_PR_BLOCKED }
148 if worst_sev == NX_SEV_WARNING { return NX_PR_DEGRADED }
149 return NX_PR_CAN_PRINT
150}
151
152// One-shot diagnosis of a Get-Printer-Attributes response. Fills out_state (printer-state enum or -1),
153// out_sev (worst reason severity or -1), out_count (#reasons), out_accepting (1/0/-1). Returns the
154// sealed can-print verdict. `scratch3` is a caller-provided i64[3] work area.
155func nx_printer_diagnose(buf: *u8, n: i64, scratch3: *i64,
156 out_state: *i64, out_sev: *i64, out_count: *i64, out_accepting: *i64) -> i64 {
157 let ev: i64 = nx_ipp_get_enum(buf, n, "printer-state", scratch3, out_state)
158 var pstate: i64 = 0 - 1
159 if ev == NX_IPP_OK { pstate = out_state[0] }
160 else { out_state[0] = 0 - 1 }
161 let worst: i64 = nx_ph_worst_reason_severity(buf, n, pstate, out_count)
162 out_sev[0] = worst
163 let acc: i64 = nx_ph_accepting(buf, n, scratch3)
164 out_accepting[0] = acc
165 return nx_printer_can_print(pstate, acc, worst)
166}