hexz_cli/cmd/sys/doctor.rs
1//! Implementation of the `hexz doctor` command.
2//!
3//! Performs comprehensive system health checks to verify that all required
4//! dependencies, kernel modules, and system resources are available for Hexz
5//! operations. This command helps troubleshoot installation issues and validates
6//! the environment before running VMs or mounting snapshots.
7//!
8//! # Diagnostic Checks Performed
9//!
10//! ## Binary Dependencies
11//!
12//! **`fusermount`:**
13//! - Required for: FUSE mounting (`hexz mount`)
14//! - Checks: Command exists and returns version
15//! - Failure mode: Command not found or non-zero exit code
16//!
17//! **`qemu-system-x86_64`:**
18//! - Required for: VM booting (`hexz boot`)
19//! - Checks: Command exists and returns version
20//! - Failure mode: Command not found or non-zero exit code
21//!
22//! ## Kernel Support
23//!
24//! **FUSE Kernel Module (`/dev/fuse`):**
25//! - Required for: FUSE mounting
26//! - Checks: Device node exists
27//! - Failure mode: Device not found (module not loaded)
28//! - Fix: `sudo modprobe fuse`
29//!
30//! ## Network Connectivity
31//!
32//! **DNS Resolution:**
33//! - Required for: Remote backends (HTTP, S3), package updates
34//! - Checks: Can resolve `google.com` to socket address
35//! - Failure mode: DNS lookup fails (network down, DNS misconfigured)
36//!
37//! # Interpretation of Results
38//!
39//! **OK:**
40//! - Component is installed and working correctly
41//! - No action required
42//!
43//! **NOT FOUND:**
44//! - Binary is not in `$PATH`
45//! - Action: Install the required package
46//! - `fusermount`: Install `fuse` or `fuse3` package
47//! - `qemu-system-x86_64`: Install `qemu-system-x86` package
48//!
49//! **FAIL:**
50//! - Binary exists but returned error or unexpected behavior
51//! - Action: Check binary is correct version or reinstall
52//!
53//! **FAIL (Device node not found):**
54//! - Kernel module not loaded
55//! - Action: Load module with `sudo modprobe fuse`
56//!
57//! **FAIL (DNS error):**
58//! - Network connectivity issue
59//! - Action: Check network configuration, DNS settings
60//!
61//! # Use Cases
62//!
63//! - **Pre-Installation Verification**: Confirm all dependencies before first use
64//! - **Troubleshooting**: Diagnose why commands are failing
65//! - **CI/CD Validation**: Verify build environments have required tools
66//! - **Bug Reports**: Include `doctor` output in issue reports
67//!
68//! # Common Usage Patterns
69//!
70//! ```bash
71//! # Run comprehensive health check
72//! hexz doctor
73//!
74//! # Include in bug reports
75//! hexz doctor > hexz-diagnostics.txt
76//!
77//! # Verify installation
78//! sudo apt install qemu-system-x86 fuse3
79//! hexz doctor # Should show all OK
80//! ```
81
82use anyhow::Result;
83use std::process::Command;
84
85/// Executes the doctor command to perform system health checks.
86///
87/// Runs a series of diagnostic tests to verify that all required dependencies,
88/// kernel modules, and system resources are available. Prints results for each
89/// check and provides guidance on fixing issues.
90///
91/// # Output Format
92///
93/// ```text
94/// Hexz Doctor - System Health Check
95///
96/// Checking fusermount... OK
97/// Checking qemu-system-x86_64... OK
98/// Checking FUSE kernel support (/dev/fuse)... OK
99/// Checking Network Connectivity (DNS)... OK
100///
101/// Diagnosis complete.
102/// ```
103///
104/// # Errors
105///
106/// This command does not return errors. All diagnostic failures are reported
107/// as "FAIL" or "NOT FOUND" in the output, but the command itself succeeds.
108/// This allows the function to complete all checks even if some fail.
109///
110/// # Examples
111///
112/// ```no_run
113/// use hexz_cli::cmd::sys::doctor;
114///
115/// // Run system diagnostics
116/// doctor::run()?;
117/// # Ok::<(), anyhow::Error>(())
118/// ```
119pub fn run() -> Result<()> {
120 println!("Hexz Doctor - System Health Check\n");
121
122 check_binary("fusermount", &["--version"]);
123 check_binary("qemu-system-x86_64", &["--version"]);
124 check_fuse_support();
125 check_network();
126
127 println!("\nDiagnosis complete.");
128 Ok(())
129}
130
131/// Checks if a binary exists and runs successfully.
132///
133/// Executes the specified command with given arguments and reports whether
134/// it exists and returns successfully.
135///
136/// # Arguments
137///
138/// * `name` - Name of the binary to check (must be in `$PATH`)
139/// * `args` - Arguments to pass (typically `["--version"]`)
140///
141/// # Output
142///
143/// - "OK" if command exists and returns zero exit code
144/// - "FAIL (Exit code N)" if command exists but fails
145/// - "NOT FOUND (Please install ...)" if command not in `$PATH`
146fn check_binary(name: &str, args: &[&str]) {
147 print!("Checking {}... ", name);
148 match Command::new(name).args(args).output() {
149 Ok(output) => {
150 if output.status.success() {
151 println!("OK");
152 // Optionally print version
153 // let s = String::from_utf8_lossy(&output.stdout);
154 // println!(" Version: {}", s.lines().next().unwrap_or("unknown").trim());
155 } else {
156 println!("FAIL (Exit code {})", output.status);
157 }
158 }
159 Err(_) => println!("NOT FOUND (Please install {})", name),
160 }
161}
162
163/// Checks if the FUSE kernel module is loaded.
164///
165/// Verifies that `/dev/fuse` exists, which indicates the FUSE kernel module
166/// is loaded and ready for use.
167///
168/// # Output
169///
170/// - "OK" if `/dev/fuse` exists
171/// - "FAIL (Device node not found. Is fuse module loaded?)" otherwise
172fn check_fuse_support() {
173 print!("Checking FUSE kernel support (/dev/fuse)... ");
174 if std::path::Path::new("/dev/fuse").exists() {
175 println!("OK");
176 } else {
177 println!("FAIL (Device node not found. Is fuse module loaded?)");
178 }
179}
180
181/// Checks network connectivity via DNS resolution.
182///
183/// Attempts to resolve `google.com` to verify DNS and basic network connectivity.
184/// This is a simple connectivity test that doesn't require external network access
185/// beyond DNS.
186///
187/// # Output
188///
189/// - "OK" if DNS resolution succeeds
190/// - "FAIL (error message)" if resolution fails
191fn check_network() {
192 print!("Checking Network Connectivity (DNS)... ");
193 // Try to resolve google.com using std lib
194 match std::net::ToSocketAddrs::to_socket_addrs("google.com:80") {
195 Ok(_) => println!("OK"),
196 Err(e) => println!("FAIL ({})", e),
197 }
198}