Add Wi-Fi driver and control tools
Red Bear OS Team
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
use std::process;
|
||||
|
||||
use redox_driver_sys::dma::DmaBuffer;
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
println!("=== Red Bear OS DMA Runtime Check ===");
|
||||
|
||||
let mut one_page =
|
||||
DmaBuffer::allocate(4096, 4096).map_err(|err| format!("alloc 4K failed: {err}"))?;
|
||||
println!(
|
||||
"dma_4k cpu={:#x} phys={:#x} len={:#x}",
|
||||
one_page.as_ptr() as usize,
|
||||
one_page.physical_address(),
|
||||
one_page.len()
|
||||
);
|
||||
unsafe {
|
||||
(one_page.as_mut_ptr() as *mut u32).write_volatile(0x1122_3344);
|
||||
let value = (one_page.as_ptr() as *const u32).read_volatile();
|
||||
println!("dma_4k_value={:#x}", value);
|
||||
}
|
||||
|
||||
let mut two_page =
|
||||
DmaBuffer::allocate(8192, 4096).map_err(|err| format!("alloc 8K failed: {err}"))?;
|
||||
println!(
|
||||
"dma_8k cpu={:#x} phys={:#x} len={:#x}",
|
||||
two_page.as_ptr() as usize,
|
||||
two_page.physical_address(),
|
||||
two_page.len()
|
||||
);
|
||||
unsafe {
|
||||
let second_page = two_page.as_mut_ptr().add(4096) as *mut u32;
|
||||
second_page.write_volatile(0x5566_7788);
|
||||
let value = (two_page.as_ptr().add(4096) as *const u32).read_volatile();
|
||||
println!("dma_8k_second_page_value={:#x}", value);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = run() {
|
||||
eprintln!("redbear-phase-dma-check: {err}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
use std::fs;
|
||||
use std::process;
|
||||
|
||||
use redbear_hwutils::parse_args;
|
||||
use serde_json::Value;
|
||||
|
||||
const PROGRAM: &str = "redbear-phase5-wifi-analyze";
|
||||
const USAGE: &str =
|
||||
"Usage: redbear-phase5-wifi-analyze <capture.json>\n\nSummarize a Wi-Fi capture bundle into likely blocker categories.";
|
||||
|
||||
fn read_text<'a>(value: &'a Value, path: &[&str]) -> &'a str {
|
||||
let mut current = value;
|
||||
for segment in path {
|
||||
match current.get(*segment) {
|
||||
Some(next) => current = next,
|
||||
None => return "",
|
||||
}
|
||||
}
|
||||
current.as_str().unwrap_or("")
|
||||
}
|
||||
|
||||
fn classify(capture: &Value) -> Vec<&'static str> {
|
||||
let mut out = Vec::new();
|
||||
|
||||
let driver_probe = read_text(capture, &["commands", "driver_probe", "stdout"]);
|
||||
let connect = read_text(capture, &["commands", "phase5_wifi_check", "stdout"]);
|
||||
let connect_result = read_text(capture, &["scheme", "connect_result", "value"]);
|
||||
let disconnect_result = read_text(capture, &["scheme", "disconnect_result", "value"]);
|
||||
let last_error = read_text(capture, &["scheme", "last_error", "value"]);
|
||||
let netctl_status = read_text(capture, &["commands", "netctl_status", "stdout"]);
|
||||
let redbear_info = read_text(capture, &["commands", "redbear_info_json", "stdout"]);
|
||||
|
||||
if !driver_probe.contains("candidates=") || driver_probe.contains("candidates=0") {
|
||||
out.push("device-detection");
|
||||
}
|
||||
if connect.contains("missing firmware") || last_error.contains("firmware") {
|
||||
out.push("firmware");
|
||||
}
|
||||
if connect_result.is_empty() || connect_result.contains("not-run") {
|
||||
out.push("association-control-path");
|
||||
}
|
||||
if disconnect_result.is_empty() || disconnect_result.contains("not-run") {
|
||||
out.push("disconnect-lifecycle");
|
||||
}
|
||||
if !netctl_status.contains("address=") || netctl_status.contains("address=unknown") {
|
||||
out.push("dhcp-or-addressing");
|
||||
}
|
||||
if !redbear_info.contains("wifi_connect_result")
|
||||
|| !redbear_info.contains("wifi_disconnect_result")
|
||||
{
|
||||
out.push("reporting-surface");
|
||||
}
|
||||
if last_error.contains("timed out") || last_error.contains("failed") {
|
||||
out.push("runtime-failure");
|
||||
}
|
||||
|
||||
if out.is_empty() {
|
||||
out.push("bounded-lifecycle-pass-no-real-link-proof");
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
parse_args(PROGRAM, USAGE, args.clone().into_iter()).map_err(|err| {
|
||||
if err.is_empty() {
|
||||
process::exit(0);
|
||||
}
|
||||
err
|
||||
})?;
|
||||
|
||||
let path = args
|
||||
.get(1)
|
||||
.ok_or_else(|| "missing capture.json path".to_string())?;
|
||||
let text = fs::read_to_string(path).map_err(|err| format!("failed to read {}: {err}", path))?;
|
||||
let capture: Value = serde_json::from_str(&text)
|
||||
.map_err(|err| format!("failed to parse {} as JSON: {err}", path))?;
|
||||
|
||||
println!("=== Red Bear Wi-Fi Capture Analysis ===");
|
||||
println!("capture={path}");
|
||||
println!(
|
||||
"profile={}",
|
||||
capture
|
||||
.get("profile")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("unknown")
|
||||
);
|
||||
println!(
|
||||
"interface={}",
|
||||
capture
|
||||
.get("interface")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("unknown")
|
||||
);
|
||||
|
||||
let classes = classify(&capture);
|
||||
println!("classification={}", classes.join(","));
|
||||
for item in classes {
|
||||
println!("blocker={item}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = run() {
|
||||
eprintln!("{PROGRAM}: {err}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn classify_flags_missing_detection() {
|
||||
let capture = json!({
|
||||
"commands": {
|
||||
"driver_probe": {"stdout": "candidates=0"},
|
||||
"phase5_wifi_check": {"stdout": ""},
|
||||
"netctl_status": {"stdout": "address=unknown"},
|
||||
"redbear_info_json": {"stdout": "{}"}
|
||||
},
|
||||
"scheme": {
|
||||
"connect_result": {"value": ""},
|
||||
"disconnect_result": {"value": ""},
|
||||
"last_error": {"value": ""}
|
||||
}
|
||||
});
|
||||
let classes = classify(&capture);
|
||||
assert!(classes.contains(&"device-detection"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classify_pass_path_when_only_bounded_state_exists() {
|
||||
let capture = json!({
|
||||
"commands": {
|
||||
"driver_probe": {"stdout": "candidates=1"},
|
||||
"phase5_wifi_check": {"stdout": "PASS: bounded Intel Wi-Fi runtime path exercised on target"},
|
||||
"netctl_status": {"stdout": "address=10.0.0.44/24"},
|
||||
"redbear_info_json": {"stdout": "wifi_connect_result wifi_disconnect_result"}
|
||||
},
|
||||
"scheme": {
|
||||
"connect_result": {"value": "connect_result=bounded-associated"},
|
||||
"disconnect_result": {"value": "disconnect_result=bounded-disconnected"},
|
||||
"last_error": {"value": "none"}
|
||||
}
|
||||
});
|
||||
let classes = classify(&capture);
|
||||
assert_eq!(classes, vec!["bounded-lifecycle-pass-no-real-link-proof"]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::process::{self, Command};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use redbear_hwutils::parse_args;
|
||||
use serde_json::json;
|
||||
|
||||
const PROGRAM: &str = "redbear-phase5-wifi-capture";
|
||||
const USAGE: &str = "Usage: redbear-phase5-wifi-capture [PROFILE] [INTERFACE] [OUTPUT_PATH]\n\nCapture the current bounded Intel Wi-Fi runtime surfaces into a single JSON bundle.";
|
||||
|
||||
fn run_command(program: &str, args: &[&str]) -> serde_json::Value {
|
||||
match Command::new(program).args(args).output() {
|
||||
Ok(output) => json!({
|
||||
"ok": output.status.success(),
|
||||
"status": output.status.code(),
|
||||
"stdout": String::from_utf8_lossy(&output.stdout),
|
||||
"stderr": String::from_utf8_lossy(&output.stderr),
|
||||
}),
|
||||
Err(err) => json!({
|
||||
"ok": false,
|
||||
"status": null,
|
||||
"stdout": "",
|
||||
"stderr": format!("failed to run {} {:?}: {}", program, args, err),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_optional(path: &str) -> serde_json::Value {
|
||||
match fs::read_to_string(path) {
|
||||
Ok(content) => json!({"present": true, "value": content}),
|
||||
Err(err) => json!({"present": false, "error": err.to_string()}),
|
||||
}
|
||||
}
|
||||
|
||||
fn list_optional(path: &str) -> serde_json::Value {
|
||||
match fs::read_dir(path) {
|
||||
Ok(entries) => {
|
||||
let mut values = entries
|
||||
.flatten()
|
||||
.map(|entry| entry.file_name().to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
values.sort();
|
||||
json!({"present": true, "entries": values})
|
||||
}
|
||||
Err(err) => json!({"present": false, "error": err.to_string()}),
|
||||
}
|
||||
}
|
||||
|
||||
fn unix_timestamp_secs() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|duration| duration.as_secs())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
parse_args(PROGRAM, USAGE, args.clone().into_iter()).map_err(|err| {
|
||||
if err.is_empty() {
|
||||
process::exit(0);
|
||||
}
|
||||
err
|
||||
})?;
|
||||
|
||||
let profile = args
|
||||
.get(1)
|
||||
.filter(|arg| !arg.starts_with('-'))
|
||||
.map(String::as_str)
|
||||
.unwrap_or("wifi-open-bounded");
|
||||
let iface = args
|
||||
.get(2)
|
||||
.filter(|arg| !arg.starts_with('-'))
|
||||
.map(String::as_str)
|
||||
.unwrap_or("wlan0");
|
||||
let output_path = args
|
||||
.get(3)
|
||||
.filter(|arg| !arg.starts_with('-'))
|
||||
.map(String::as_str);
|
||||
|
||||
let scheme_root = format!("/scheme/wifictl/ifaces/{iface}");
|
||||
let payload = json!({
|
||||
"captured_at_unix": unix_timestamp_secs(),
|
||||
"profile": profile,
|
||||
"interface": iface,
|
||||
"installed": {
|
||||
"driver": Path::new("/usr/lib/drivers/redbear-iwlwifi").exists(),
|
||||
"wifictl": Path::new("/usr/bin/redbear-wifictl").exists(),
|
||||
"netctl": Path::new("/usr/bin/redbear-netctl").exists(),
|
||||
"redbear_info": Path::new("/usr/bin/redbear-info").exists(),
|
||||
},
|
||||
"host": {
|
||||
"uname": run_command("uname", &["-a"]),
|
||||
},
|
||||
"commands": {
|
||||
"driver_probe": run_command("redbear-iwlwifi", &["--probe"]),
|
||||
"driver_status": run_command("redbear-iwlwifi", &["--status", iface]),
|
||||
"wifictl_probe": run_command("redbear-wifictl", &["--probe"]),
|
||||
"wifictl_status": run_command("redbear-wifictl", &["--status", iface]),
|
||||
"netctl_list": run_command("redbear-netctl", &["list"]),
|
||||
"netctl_status_all": run_command("redbear-netctl", &["status"]),
|
||||
"netctl_status": run_command("redbear-netctl", &["status", profile]),
|
||||
"redbear_info_json": run_command("redbear-info", &["--json"]),
|
||||
"phase5_network_check": run_command("redbear-phase5-network-check", &[]),
|
||||
"phase5_wifi_check": run_command("redbear-phase5-wifi-check", &[profile, iface]),
|
||||
"lspci": run_command("lspci", &[]),
|
||||
},
|
||||
"filesystem": {
|
||||
"wifictl_ifaces": list_optional("/scheme/wifictl/ifaces"),
|
||||
"netcfg_ifaces": list_optional("/scheme/netcfg/ifaces"),
|
||||
"netctl_profiles": list_optional("/etc/netctl"),
|
||||
"active_profile": read_optional("/etc/netctl/active"),
|
||||
"profile_contents": read_optional(&format!("/etc/netctl/{profile}")),
|
||||
},
|
||||
"scheme": {
|
||||
"status": read_optional(&format!("{scheme_root}/status")),
|
||||
"link_state": read_optional(&format!("{scheme_root}/link-state")),
|
||||
"firmware_status": read_optional(&format!("{scheme_root}/firmware-status")),
|
||||
"transport_status": read_optional(&format!("{scheme_root}/transport-status")),
|
||||
"transport_init_status": read_optional(&format!("{scheme_root}/transport-init-status")),
|
||||
"activation_status": read_optional(&format!("{scheme_root}/activation-status")),
|
||||
"connect_result": read_optional(&format!("{scheme_root}/connect-result")),
|
||||
"disconnect_result": read_optional(&format!("{scheme_root}/disconnect-result")),
|
||||
"scan_results": read_optional(&format!("{scheme_root}/scan-results")),
|
||||
"last_error": read_optional(&format!("{scheme_root}/last-error")),
|
||||
}
|
||||
});
|
||||
|
||||
let rendered = serde_json::to_string_pretty(&payload)
|
||||
.map_err(|err| format!("failed to serialize capture payload: {err}"))?;
|
||||
if let Some(output_path) = output_path {
|
||||
fs::write(output_path, &rendered)
|
||||
.map_err(|err| format!("failed to write capture bundle to {}: {err}", output_path))?;
|
||||
}
|
||||
println!("{}", rendered);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = run() {
|
||||
eprintln!("{PROGRAM}: {err}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
use std::path::Path;
|
||||
use std::process::{self, Command};
|
||||
|
||||
use redbear_hwutils::parse_args;
|
||||
|
||||
const PROGRAM: &str = "redbear-phase5-wifi-check";
|
||||
const USAGE: &str = "Usage: redbear-phase5-wifi-check [PROFILE] [INTERFACE]\n\nExercise the bounded Intel Wi-Fi runtime path inside a Red Bear OS guest or target runtime. The packaged runtime path defaults to the bounded open-profile flow; WPA2-PSK remains implemented and host/unit-verified, but is not yet the default packaged runtime proof.";
|
||||
|
||||
fn require_path(path: &str) -> Result<(), String> {
|
||||
if Path::new(path).exists() {
|
||||
println!("{path}");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("missing {path}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn require_contains(label: &str, haystack: &str, needle: &str) -> Result<(), String> {
|
||||
if haystack.contains(needle) {
|
||||
println!("{label}={needle}");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("{label} missing {needle}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn run_command(program: &str, args: &[&str]) -> Result<String, String> {
|
||||
let output = Command::new(program)
|
||||
.args(args)
|
||||
.output()
|
||||
.map_err(|err| format!("failed to run {} {:?}: {err}", program, args))?;
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
if stderr.trim().is_empty() {
|
||||
return Err(format!(
|
||||
"{} {:?} exited with status {}",
|
||||
program, args, output.status
|
||||
));
|
||||
}
|
||||
return Err(format!(
|
||||
"{} {:?} exited with status {}: {}",
|
||||
program,
|
||||
args,
|
||||
output.status,
|
||||
stderr.trim()
|
||||
));
|
||||
}
|
||||
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
parse_args(PROGRAM, USAGE, args.clone().into_iter()).map_err(|err| {
|
||||
if err.is_empty() {
|
||||
process::exit(0);
|
||||
}
|
||||
err
|
||||
})?;
|
||||
|
||||
let profile = args
|
||||
.get(1)
|
||||
.filter(|arg| !arg.starts_with('-'))
|
||||
.map(String::as_str)
|
||||
.unwrap_or("wifi-open-bounded");
|
||||
let iface = args
|
||||
.get(2)
|
||||
.filter(|arg| !arg.starts_with('-'))
|
||||
.map(String::as_str)
|
||||
.unwrap_or("wlan0");
|
||||
|
||||
println!("=== Red Bear OS Phase 5 Wi-Fi Check ===");
|
||||
println!("profile={profile}");
|
||||
println!("interface={iface}");
|
||||
|
||||
require_path("/usr/lib/drivers/redbear-iwlwifi")?;
|
||||
require_path("/usr/bin/redbear-wifictl")?;
|
||||
require_path("/usr/bin/redbear-netctl")?;
|
||||
require_path("/usr/bin/redbear-info")?;
|
||||
|
||||
let driver_probe = run_command("redbear-iwlwifi", &["--probe"])?;
|
||||
print!("{driver_probe}");
|
||||
require_contains("driver_probe", &driver_probe, "candidates=")?;
|
||||
|
||||
let probe = run_command("redbear-wifictl", &["--probe"])?;
|
||||
print!("{probe}");
|
||||
require_contains("wifictl_probe", &probe, "interfaces=")?;
|
||||
require_contains("wifictl_probe", &probe, iface)?;
|
||||
|
||||
let connect = run_command("redbear-wifictl", &["--connect", iface, "demo", "open"])?;
|
||||
print!("{connect}");
|
||||
require_contains("connect", &connect, "status=connected")
|
||||
.or_else(|_| require_contains("connect", &connect, "status=associated"))
|
||||
.or_else(|_| require_contains("connect", &connect, "status=associating"))?;
|
||||
require_contains("connect", &connect, "connect_result=")?;
|
||||
|
||||
let disconnect = run_command("redbear-wifictl", &["--disconnect", iface])?;
|
||||
print!("{disconnect}");
|
||||
require_contains("disconnect", &disconnect, "disconnect_result=")?;
|
||||
|
||||
let start = run_command("redbear-netctl", &["start", profile])?;
|
||||
print!("{start}");
|
||||
let status = run_command("redbear-netctl", &["status", profile])?;
|
||||
print!("{status}");
|
||||
require_contains("netctl_status", &status, &format!("interface={iface}"))?;
|
||||
require_contains("netctl_status", &status, "connect_result=")?;
|
||||
|
||||
let stop = run_command("redbear-netctl", &["stop", profile])?;
|
||||
print!("{stop}");
|
||||
|
||||
let info = run_command("redbear-info", &["--json"])?;
|
||||
print!("{info}");
|
||||
require_contains("redbear_info", &info, "wifi_control_state")?;
|
||||
require_contains("redbear_info", &info, "wifi_connect_result")?;
|
||||
require_contains("redbear_info", &info, "wifi_disconnect_result")?;
|
||||
|
||||
println!("PASS: bounded Intel Wi-Fi runtime path exercised inside target runtime");
|
||||
println!("NOTE: the packaged runtime checker currently validates the bounded open-profile path by default; WPA2-PSK is implemented and host/unit-verified elsewhere in-repo but is not yet the default packaged runtime proof");
|
||||
println!("NOTE: this still does not prove real AP scan/auth/association, packet flow, DHCP success over Wi-Fi, or validated end-to-end connectivity");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = run() {
|
||||
eprintln!("{PROGRAM}: {err}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn require_contains_accepts_present_substring() {
|
||||
assert!(
|
||||
require_contains("test", "abc wifi_control_state xyz", "wifi_control_state").is_ok()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn require_contains_rejects_missing_substring() {
|
||||
assert!(require_contains("test", "abc", "wifi_connect_result").is_err());
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
use std::process::{self, Command};
|
||||
|
||||
use redbear_hwutils::parse_args;
|
||||
use serde_json::Value;
|
||||
|
||||
const PROGRAM: &str = "redbear-phase5-wifi-link-check";
|
||||
const USAGE: &str = "Usage: redbear-phase5-wifi-link-check\n\nCheck whether the current runtime exposes Wi-Fi interface/address/route signals beyond the bounded lifecycle layer.";
|
||||
|
||||
fn require_field<'a>(value: &'a Value, field: &str) -> Result<&'a Value, String> {
|
||||
value
|
||||
.get(field)
|
||||
.ok_or_else(|| format!("redbear-info --json did not report {field}"))
|
||||
}
|
||||
|
||||
fn present_nonempty(value: &Value) -> bool {
|
||||
value
|
||||
.as_str()
|
||||
.map(|s| !s.trim().is_empty() && s != "unknown")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
parse_args(PROGRAM, USAGE, std::env::args()).map_err(|err| {
|
||||
if err.is_empty() {
|
||||
process::exit(0);
|
||||
}
|
||||
err
|
||||
})?;
|
||||
|
||||
println!("=== Red Bear OS Phase 5 Wi-Fi Link Check ===");
|
||||
let output = Command::new("redbear-info")
|
||||
.arg("--json")
|
||||
.output()
|
||||
.map_err(|err| format!("failed to run redbear-info --json: {err}"))?;
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!("redbear-info --json failed: {}", stderr.trim()));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let json: Value = serde_json::from_str(&stdout)
|
||||
.map_err(|err| format!("failed to parse redbear-info --json output: {err}"))?;
|
||||
let network = json
|
||||
.get("network")
|
||||
.ok_or_else(|| "redbear-info --json did not include network section".to_string())?;
|
||||
|
||||
let interface = require_field(network, "interface")?;
|
||||
let address = require_field(network, "address")?;
|
||||
let default_route = require_field(network, "default_route")?;
|
||||
let wifi_connect_result = require_field(network, "wifi_connect_result")?;
|
||||
|
||||
if present_nonempty(interface) {
|
||||
println!("WIFI_INTERFACE=present");
|
||||
} else {
|
||||
return Err("Wi-Fi/network interface is not reported".to_string());
|
||||
}
|
||||
|
||||
if present_nonempty(wifi_connect_result) {
|
||||
println!("WIFI_CONNECT_RESULT=present");
|
||||
} else {
|
||||
return Err("Wi-Fi connect result is not reported".to_string());
|
||||
}
|
||||
|
||||
if present_nonempty(address) {
|
||||
println!("WIFI_ADDRESS=present");
|
||||
} else {
|
||||
println!("WIFI_ADDRESS=missing");
|
||||
}
|
||||
|
||||
if present_nonempty(default_route) {
|
||||
println!("WIFI_DEFAULT_ROUTE=present");
|
||||
} else {
|
||||
println!("WIFI_DEFAULT_ROUTE=missing");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = run() {
|
||||
eprintln!("{PROGRAM}: {err}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn present_nonempty_rejects_unknown() {
|
||||
assert!(!present_nonempty(&json!("unknown")));
|
||||
assert!(!present_nonempty(&json!("")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn present_nonempty_accepts_value() {
|
||||
assert!(present_nonempty(&json!("wlan0")));
|
||||
assert!(present_nonempty(&json!("10.0.0.44/24")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use std::process::{self, Command};
|
||||
|
||||
use redbear_hwutils::parse_args;
|
||||
|
||||
const PROGRAM: &str = "redbear-phase5-wifi-run";
|
||||
const USAGE: &str = "Usage: redbear-phase5-wifi-run [PROFILE] [INTERFACE] [OUTPUT_PATH]\n\nRun the packaged bounded Wi-Fi validator and then emit a JSON capture bundle.";
|
||||
|
||||
fn run_command(program: &str, args: &[&str]) -> Result<String, String> {
|
||||
let output = Command::new(program)
|
||||
.args(args)
|
||||
.output()
|
||||
.map_err(|err| format!("failed to run {} {:?}: {err}", program, args))?;
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
if stderr.trim().is_empty() {
|
||||
return Err(format!(
|
||||
"{} {:?} exited with status {}",
|
||||
program, args, output.status
|
||||
));
|
||||
}
|
||||
return Err(format!(
|
||||
"{} {:?} exited with status {}: {}",
|
||||
program,
|
||||
args,
|
||||
output.status,
|
||||
stderr.trim()
|
||||
));
|
||||
}
|
||||
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
parse_args(PROGRAM, USAGE, args.clone().into_iter()).map_err(|err| {
|
||||
if err.is_empty() {
|
||||
process::exit(0);
|
||||
}
|
||||
err
|
||||
})?;
|
||||
|
||||
let profile = args
|
||||
.get(1)
|
||||
.filter(|arg| !arg.starts_with('-'))
|
||||
.map(String::as_str)
|
||||
.unwrap_or("wifi-open-bounded");
|
||||
let iface = args
|
||||
.get(2)
|
||||
.filter(|arg| !arg.starts_with('-'))
|
||||
.map(String::as_str)
|
||||
.unwrap_or("wlan0");
|
||||
let output_path = args
|
||||
.get(3)
|
||||
.filter(|arg| !arg.starts_with('-'))
|
||||
.map(String::as_str)
|
||||
.unwrap_or("/tmp/redbear-phase5-wifi-capture.json");
|
||||
|
||||
let check = run_command("redbear-phase5-wifi-check", &[profile, iface])?;
|
||||
print!("{check}");
|
||||
|
||||
let link = run_command("redbear-phase5-wifi-link-check", &[])?;
|
||||
print!("{link}");
|
||||
|
||||
let capture = run_command(
|
||||
"redbear-phase5-wifi-capture",
|
||||
&[profile, iface, output_path],
|
||||
)?;
|
||||
print!("{capture}");
|
||||
println!("capture_output={output_path}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = run() {
|
||||
eprintln!("{PROGRAM}: {err}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn default_capture_path_is_stable() {
|
||||
assert_eq!(
|
||||
"/tmp/redbear-phase5-wifi-capture.json",
|
||||
"/tmp/redbear-phase5-wifi-capture.json"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user