|
2 | 2 |
|
3 | 3 | //! Hardware Crash Team — Low-Level Hardware Diagnostics & Remediation (CLI). |
4 | 4 | //! |
5 | | -//! This binary implements the "Emergency Room" logic for physical systems |
6 | | -//! within the AmbientOps ecosystem. It is designed to identify and mitigate |
7 | | -//! hardware-induced crashes by analyzing PCI buses, driver conflicts, |
| 5 | +//! This binary implements the "Emergency Room" logic for physical systems |
| 6 | +//! within the AmbientOps ecosystem. It is designed to identify and mitigate |
| 7 | +//! hardware-induced crashes by analyzing PCI buses, driver conflicts, |
8 | 8 | //! and kernel-level trace logs. |
9 | 9 | //! |
10 | 10 | //! CORE CAPABILITIES: |
11 | 11 | //! 1. **Scanner**: Deep inspection of PCI devices, IOMMU groups, and ACPI tables. |
12 | 12 | //! 2. **Diagnose**: Temporal correlation between hardware events and system crashes. |
13 | | -//! 3. **Remediation**: Generates declarative plans to isolate "Zombie Hardware" |
| 13 | +//! 3. **Remediation**: Generates declarative plans to isolate "Zombie Hardware" |
14 | 14 | //! (e.g., using `pci-stub` or `vfio-pci`). |
15 | | -//! 4. **Safety**: All destructive actions (Apply) require human oversight |
| 15 | +//! 4. **Safety**: All destructive actions (Apply) require human oversight |
16 | 16 | //! and produce reversible receipts. |
17 | 17 | //! |
18 | 18 | //! ARCHITECTURE: |
19 | 19 | //! - **Clap**: CLI argument parsing with domain-specific subcommands. |
20 | | -//! - **Contracts**: Full integration with AmbientOps Evidence Envelopes |
| 20 | +//! - **Contracts**: Full integration with AmbientOps Evidence Envelopes |
21 | 21 | //! for verifiable reporting. |
22 | 22 |
|
23 | 23 | #![forbid(unsafe_code)] |
24 | 24 | use clap::{Parser, Subcommand}; |
25 | 25 | use anyhow::Result; |
26 | | -use serde_json; |
27 | 26 |
|
28 | 27 | mod scanner; |
29 | 28 | mod analyzer; |
30 | 29 | mod remediation; |
31 | 30 | mod types; |
32 | 31 | mod tui; |
33 | | -mod sarif; // SARIF serialization for high-assurance audit trails. |
| 32 | +mod sarif; |
34 | 33 |
|
| 34 | +/// Hardware Crash Team — hardware diagnostic and remediation CLI. |
| 35 | +/// |
| 36 | +/// Identifies zombie PCI devices, driver conflicts, and hardware-induced |
| 37 | +/// crashes. Generates reversible remediation plans. |
35 | 38 | #[derive(Parser)] |
36 | | -#[command(name = "hardware-crash-team")] |
| 39 | +#[command(name = "hardware-crash-team", version, about)] |
37 | 40 | struct Cli { |
| 41 | + /// Subcommand to execute. |
38 | 42 | #[command(subcommand)] |
39 | 43 | command: Commands, |
| 44 | + |
| 45 | + /// Enable verbose output for debugging. |
| 46 | + #[arg(short, long, global = true)] |
| 47 | + verbose: bool, |
40 | 48 | } |
41 | 49 |
|
| 50 | +/// Available subcommands for the Hardware Crash Team CLI. |
42 | 51 | #[derive(Subcommand)] |
43 | 52 | enum Commands { |
44 | | - /// SCAN: Audits the host hardware state. |
45 | | - /// Supports exporting to `sarif` or `EvidenceEnvelope` formats. |
| 53 | + /// Audit the host hardware state (PCI devices, BARs, drivers). |
| 54 | + /// |
| 55 | + /// Reads sysfs, enriches with lspci, and detects zombie devices, |
| 56 | + /// partial bindings, unmanaged memory, and other anomalies. |
46 | 57 | Scan { |
47 | | - #[arg(short, long, default_value = "text")] format: String, |
48 | | - #[arg(long)] envelope: bool, // Wrap in contract-conformant envelope. |
49 | | - // ... [other flags] |
| 58 | + /// Output format: text, json, or sarif. |
| 59 | + #[arg(short, long, default_value = "text")] |
| 60 | + format: String, |
| 61 | + |
| 62 | + /// Wrap output in a contract-conformant EvidenceEnvelope. |
| 63 | + #[arg(long)] |
| 64 | + envelope: bool, |
| 65 | + |
| 66 | + /// Write output to a file instead of stdout. |
| 67 | + #[arg(short, long)] |
| 68 | + output: Option<std::path::PathBuf>, |
50 | 69 | }, |
51 | 70 |
|
52 | | - /// DIAGNOSE: Analyzes historical boot logs (`journalctl`) to isolate |
53 | | - /// the specific PCI device responsible for a kernel panic. |
| 71 | + /// Analyse historical boot logs to isolate crash-causing hardware. |
| 72 | + /// |
| 73 | + /// Parses journalctl across multiple boots to find temporal |
| 74 | + /// correlations between hardware events and kernel panics. |
54 | 75 | Diagnose { |
55 | | - #[arg(short, long, default_value = "10")] boots: usize, |
56 | | - #[arg(short, long)] device: Option<String>, // BDF address (e.g. 01:00.0) |
| 76 | + /// Number of previous boots to analyse. |
| 77 | + #[arg(short, long, default_value = "10")] |
| 78 | + boots: usize, |
| 79 | + |
| 80 | + /// Filter to a specific PCI device by BDF address (e.g. 01:00.0). |
| 81 | + #[arg(short, long)] |
| 82 | + device: Option<String>, |
57 | 83 | }, |
58 | 84 |
|
59 | | - /// PLAN: Generates a declarative procedure to disable or isolate |
60 | | - /// faulty hardware without physical removal. |
| 85 | + /// Generate a declarative remediation plan to isolate faulty hardware. |
| 86 | + /// |
| 87 | + /// Supports strategies: pci-stub, vfio-pci, dual, power-off, |
| 88 | + /// disable, unbind. Multi-device plans combine kernel args. |
61 | 89 | Plan { |
62 | | - #[arg(required = true)] devices: Vec<String>, |
63 | | - #[arg(short, long)] strategy: Option<String>, // e.g. "pci-stub", "power-off" |
| 90 | + /// PCI BDF addresses of devices to remediate (e.g. 01:00.0). |
| 91 | + #[arg(required = true)] |
| 92 | + devices: Vec<String>, |
| 93 | + |
| 94 | + /// Remediation strategy (pci-stub, vfio-pci, dual, power-off, disable, unbind). |
| 95 | + #[arg(short, long)] |
| 96 | + strategy: Option<String>, |
64 | 97 | }, |
65 | 98 |
|
66 | | - /// APPLY: Physically executes a remediation plan (e.g. modifying kernel cmdline). |
67 | | - Apply { plan: std::path::PathBuf, #[arg(long)] yes: bool }, |
| 99 | + /// Execute a remediation plan (dry-run by default). |
| 100 | + /// |
| 101 | + /// Reads a plan JSON file and prints the commands that would be |
| 102 | + /// executed. Pass --yes to actually apply changes. Generates a |
| 103 | + /// receipt for rollback via `undo`. |
| 104 | + Apply { |
| 105 | + /// Path to the plan JSON file. |
| 106 | + plan: std::path::PathBuf, |
68 | 107 |
|
69 | | - /// UNDO: Uses a receipt to restore the system to its pre-remediation state. |
70 | | - Undo { receipt: std::path::PathBuf }, |
| 108 | + /// Skip confirmation and execute for real. |
| 109 | + #[arg(long)] |
| 110 | + yes: bool, |
| 111 | + }, |
71 | 112 |
|
72 | | - /// STATUS: Quick health overview of the physical PCI topology. |
| 113 | + /// Reverse a previously applied plan using its receipt. |
| 114 | + /// |
| 115 | + /// Reads a receipt JSON generated by `apply` and restores the |
| 116 | + /// system to its pre-remediation state. |
| 117 | + Undo { |
| 118 | + /// Path to the receipt JSON file. |
| 119 | + receipt: std::path::PathBuf, |
| 120 | + }, |
| 121 | + |
| 122 | + /// Quick health overview of the physical PCI topology. |
| 123 | + /// |
| 124 | + /// Shows device counts, issue summary, and class breakdown |
| 125 | + /// without the full scan detail. |
73 | 126 | Status, |
| 127 | + |
| 128 | + /// Interactive TUI for hardware diagnostics (requires --features tui). |
| 129 | + Tui, |
74 | 130 | } |
75 | 131 |
|
76 | | -/// MAIN ENTRY: Initializes the async runtime and dispatches to |
77 | | -/// specialized module runners. |
| 132 | +/// Main entry point: parses CLI arguments and dispatches to the |
| 133 | +/// appropriate module handler. |
78 | 134 | fn main() -> Result<()> { |
79 | | - // ... [Tracing and CLI parsing logic] |
| 135 | + // Initialise tracing subscriber for structured logging. |
| 136 | + tracing_subscriber::fmt() |
| 137 | + .with_env_filter( |
| 138 | + tracing_subscriber::EnvFilter::from_default_env() |
| 139 | + ) |
| 140 | + .init(); |
| 141 | + |
| 142 | + let cli = Cli::parse(); |
| 143 | + |
80 | 144 | match cli.command { |
81 | | - Commands::Scan { .. } => { |
82 | | - // EXECUTION: Triggers the physical bus probe. |
83 | | - let report = scanner::scan_system(verbose)?; |
84 | | - // ... [Reporting and conversion logic] |
| 145 | + Commands::Scan { format, envelope: _, output } => { |
| 146 | + let report = scanner::scan_system(cli.verbose)?; |
| 147 | + let rendered = match format.as_str() { |
| 148 | + "json" => serde_json::to_string_pretty(&report)?, |
| 149 | + "sarif" => sarif::format_sarif(&report)?, |
| 150 | + _ => scanner::format_report(&report, "text")?, |
| 151 | + }; |
| 152 | + |
| 153 | + if let Some(path) = output { |
| 154 | + std::fs::write(&path, &rendered)?; |
| 155 | + println!("Report written to {}", path.display()); |
| 156 | + } else { |
| 157 | + println!("{rendered}"); |
| 158 | + } |
| 159 | + } |
| 160 | + |
| 161 | + Commands::Diagnose { boots, device } => { |
| 162 | + let diagnosis = analyzer::diagnose(boots, device.as_deref())?; |
| 163 | + analyzer::print_diagnosis(&diagnosis); |
| 164 | + } |
| 165 | + |
| 166 | + Commands::Plan { devices, strategy } => { |
| 167 | + if devices.len() == 1 { |
| 168 | + let plan = remediation::create_plan( |
| 169 | + &devices[0], |
| 170 | + strategy.as_deref(), |
| 171 | + )?; |
| 172 | + remediation::print_plan(&plan); |
| 173 | + } else { |
| 174 | + let multi = remediation::create_multi_plan( |
| 175 | + &devices, |
| 176 | + strategy.as_deref(), |
| 177 | + )?; |
| 178 | + remediation::print_multi_plan(&multi); |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + Commands::Apply { plan, yes: _ } => { |
| 183 | + remediation::apply_plan(&plan)?; |
| 184 | + } |
| 185 | + |
| 186 | + Commands::Undo { receipt } => { |
| 187 | + remediation::undo(&receipt)?; |
| 188 | + } |
| 189 | + |
| 190 | + Commands::Status => { |
| 191 | + let report = scanner::scan_system(false)?; |
| 192 | + scanner::print_status(&report); |
| 193 | + } |
| 194 | + |
| 195 | + Commands::Tui => { |
| 196 | + tui::run()?; |
85 | 197 | } |
86 | | - // ... [Remaining handlers] |
87 | 198 | } |
| 199 | + |
88 | 200 | Ok(()) |
89 | 201 | } |
0 commit comments