Files
RedBear-OS/local/recipes/system/redbear-wifictl/source/src/main.rs
T
vasilito 7c7399e0a6 feat: recipe durability guard — prevents build system from deleting local recipes
Add guard-recipes.sh with four modes:
- --verify: check all local/recipes have correct symlinks into recipes/
- --fix: repair broken symlinks (run before builds)
- --save-all: snapshot all recipe.toml into local/recipes/
- --restore: recreate all symlinks from local/recipes/ (run after sync-upstream)

Wired into apply-patches.sh (post-patch) and sync-upstream.sh (post-sync).
This prevents the build system from deleting recipe files during
cargo cook, make distclean, or upstream source refresh.
2026-04-30 18:47:03 +01:00

488 lines
17 KiB
Rust

mod backend;
#[cfg(target_os = "redox")]
mod dbus_nm;
mod scheme;
use std::env;
#[cfg(target_os = "redox")]
use std::os::fd::RawFd;
use std::path::Path;
use std::process;
use backend::{Backend, IntelBackend, NoDeviceBackend, StubBackend};
#[cfg(target_os = "redox")]
use dbus_nm::register_nm_interface;
use log::LevelFilter;
#[cfg(target_os = "redox")]
use log::{error, info};
#[cfg(target_os = "redox")]
use redox_scheme::{scheme::SchemeSync, SignalBehavior, Socket};
#[cfg(target_os = "redox")]
use scheme::WifiCtlScheme;
fn init_logging(level: LevelFilter) {
log::set_max_level(level);
}
#[cfg(target_os = "redox")]
unsafe fn get_init_notify_fd() -> Option<RawFd> {
let Ok(value) = env::var("INIT_NOTIFY") else {
return None;
};
let Ok(fd) = value.parse::<RawFd>() else {
return None;
};
unsafe {
libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC);
}
Some(fd)
}
#[cfg(target_os = "redox")]
fn notify_scheme_ready(notify_fd: Option<RawFd>, socket: &Socket, scheme: &mut WifiCtlScheme) {
let Some(notify_fd) = notify_fd else {
return;
};
let Ok(cap_id) = scheme.scheme_root() else {
log::warn!("redbear-wifictl: scheme_root failed; continuing without scheme notification");
return;
};
let Ok(cap_fd) = socket.create_this_scheme_fd(0, cap_id, 0, 0) else {
log::warn!("redbear-wifictl: create_this_scheme_fd failed; continuing without scheme notification");
return;
};
if let Err(err) = syscall::call_wo(
notify_fd as usize,
&libredox::Fd::new(cap_fd).into_raw().to_ne_bytes(),
syscall::CallFlags::FD,
&[],
) {
log::warn!(
"redbear-wifictl: failed to notify init that scheme is ready ({err}); continuing with manual startup"
);
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum BackendMode {
Intel,
NoDevice,
Stub,
}
fn iwlwifi_command_path() -> std::path::PathBuf {
env::var_os("REDBEAR_IWLWIFI_CMD")
.map(std::path::PathBuf::from)
.unwrap_or_else(|| std::path::PathBuf::from("/usr/lib/drivers/redbear-iwlwifi"))
}
fn select_backend_mode(
explicit: Option<&str>,
intel_driver_present: bool,
intel_interfaces_present: bool,
redox_runtime: bool,
) -> BackendMode {
match explicit {
Some("intel") => BackendMode::Intel,
Some("stub") => BackendMode::Stub,
_ if redox_runtime && intel_driver_present && intel_interfaces_present => {
BackendMode::Intel
}
_ if redox_runtime && intel_driver_present => BackendMode::NoDevice,
_ => BackendMode::Stub,
}
}
fn build_backend() -> Box<dyn Backend> {
let explicit = env::var("REDBEAR_WIFICTL_BACKEND").ok();
let intel_driver_present = Path::new(&iwlwifi_command_path()).exists();
let intel_interfaces_present = if cfg!(target_os = "redox") && intel_driver_present {
!IntelBackend::from_env().interfaces().is_empty()
} else {
false
};
let mode = select_backend_mode(
explicit.as_deref(),
intel_driver_present,
intel_interfaces_present,
cfg!(target_os = "redox"),
);
match mode {
BackendMode::Intel => Box::new(IntelBackend::from_env()),
BackendMode::NoDevice => Box::new(NoDeviceBackend::new()),
BackendMode::Stub => Box::new(StubBackend::from_env()),
}
}
fn split_dbus_args(args: Vec<String>, dbus_env_present: bool) -> (bool, Vec<String>) {
let dbus_flag_present = args.iter().any(|arg| arg == "--dbus");
let filtered_args = args
.into_iter()
.filter(|arg| arg != "--dbus")
.collect::<Vec<_>>();
(dbus_env_present || dbus_flag_present, filtered_args)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn explicit_backend_selection_wins() {
assert_eq!(
select_backend_mode(Some("intel"), false, false, false),
BackendMode::Intel
);
assert_eq!(
select_backend_mode(Some("stub"), true, true, true),
BackendMode::Stub
);
}
#[test]
fn redox_runtime_prefers_intel_when_driver_present() {
assert_eq!(
select_backend_mode(None, true, true, true),
BackendMode::Intel
);
assert_eq!(
select_backend_mode(None, false, false, true),
BackendMode::Stub
);
}
#[test]
fn redox_runtime_uses_no_device_backend_without_detected_intel_interfaces() {
assert_eq!(
select_backend_mode(None, true, false, true),
BackendMode::NoDevice
);
}
#[test]
fn host_runtime_stays_stub_without_explicit_override() {
assert_eq!(
select_backend_mode(None, true, true, false),
BackendMode::Stub
);
assert_eq!(
select_backend_mode(None, false, false, false),
BackendMode::Stub
);
}
#[test]
fn dbus_flag_is_detected_and_removed_from_args() {
let (dbus_enabled, args) = split_dbus_args(
vec!["--dbus".to_string(), "--probe".to_string(), "wlan0".to_string()],
false,
);
assert!(dbus_enabled);
assert_eq!(args, vec!["--probe".to_string(), "wlan0".to_string()]);
}
#[test]
fn dbus_env_enables_registration_without_flag() {
let (dbus_enabled, args) = split_dbus_args(vec!["--status".to_string()], true);
assert!(dbus_enabled);
assert_eq!(args, vec!["--status".to_string()]);
}
}
fn main() {
let log_level = match env::var("REDBEAR_WIFICTL_LOG").as_deref() {
Ok("debug") => LevelFilter::Debug,
Ok("trace") => LevelFilter::Trace,
Ok("warn") => LevelFilter::Warn,
Ok("error") => LevelFilter::Error,
_ => LevelFilter::Info,
};
init_logging(log_level);
let raw_args = env::args().skip(1).collect::<Vec<_>>();
#[cfg(target_os = "redox")]
let (dbus_enabled, filtered_args) =
split_dbus_args(raw_args, env::var_os("DBUS_SYSTEM_BUS").is_some());
#[cfg(not(target_os = "redox"))]
let (_, filtered_args) = split_dbus_args(raw_args, env::var_os("DBUS_SYSTEM_BUS").is_some());
let mut args = filtered_args.into_iter();
match args.next().as_deref() {
Some("--probe") => {
let backend = build_backend();
println!("interfaces={}", backend.interfaces().join(","));
println!("capabilities={}", backend.capabilities().join(","));
return;
}
Some("--prepare") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.prepare(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("status={}", status.as_str());
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("transport_init_status=transport_init=not-run");
return;
}
Err(err) => {
eprintln!("redbear-wifictl: prepare failed for {}: {}", iface, err);
process::exit(1);
}
}
}
Some("--status") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let backend = build_backend();
println!("interface={}", iface);
println!("status={}", backend.initial_status(&iface).as_str());
println!("link_state={}", backend.initial_link_state(&iface));
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("transport_init_status=transport_init=unknown");
println!("connect_result={}", backend.connect_result(&iface));
return;
}
Some("--scan") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.scan(&iface) {
Ok(results) => {
println!("interface={}", iface);
println!("status=scanning");
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("scan_results={}", results.join(","));
return;
}
Err(err) => {
eprintln!("redbear-wifictl: scan failed for {}: {}", iface, err);
process::exit(1);
}
}
}
Some("--transport-probe") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.transport_probe(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("transport_status={}", status);
return;
}
Err(err) => {
eprintln!(
"redbear-wifictl: transport probe failed for {}: {}",
iface, err
);
process::exit(1);
}
}
}
Some("--init-transport") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.init_transport(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("transport_init_status={}", status);
println!("transport_status={}", backend.transport_status(&iface));
return;
}
Err(err) => {
eprintln!(
"redbear-wifictl: transport init failed for {}: {}",
iface, err
);
process::exit(1);
}
}
}
Some("--activate-nic") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.activate(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("activation_status={}", status);
println!("transport_status={}", backend.transport_status(&iface));
return;
}
Err(err) => {
eprintln!(
"redbear-wifictl: activate-nic failed for {}: {}",
iface, err
);
process::exit(1);
}
}
}
Some("--retry") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
match backend.retry(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("status={}", status.as_str());
println!("link_state=link=retrying");
return;
}
Err(err) => {
eprintln!("redbear-wifictl: retry failed for {}: {}", iface, err);
process::exit(1);
}
}
}
Some("--connect") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let ssid = args.next().unwrap_or_default();
let security = args.next().unwrap_or_else(|| "open".to_string());
let key = args.next().unwrap_or_default();
let mut backend = build_backend();
if let Err(err) = backend.prepare(&iface) {
eprintln!("redbear-wifictl: prepare failed for {}: {}", iface, err);
process::exit(1);
}
if let Err(err) = backend.init_transport(&iface) {
eprintln!(
"redbear-wifictl: transport init failed for {}: {}",
iface, err
);
process::exit(1);
}
if let Err(err) = backend.activate(&iface) {
eprintln!(
"redbear-wifictl: activate-nic failed for {}: {}",
iface, err
);
process::exit(1);
}
let state = backend::InterfaceState {
ssid,
security,
key,
..Default::default()
};
match backend.connect(&iface, &state) {
Ok(status) => {
println!("interface={}", iface);
println!("status={}", status.as_str());
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("connect_result={}", backend.connect_result(&iface));
return;
}
Err(err) => {
eprintln!("redbear-wifictl: connect failed for {}: {}", iface, err);
process::exit(1);
}
}
}
Some("--disconnect") => {
let iface = args.next().unwrap_or_else(|| "wlan0".to_string());
let mut backend = build_backend();
if let Err(err) = backend.prepare(&iface) {
eprintln!("redbear-wifictl: prepare failed for {}: {}", iface, err);
process::exit(1);
}
if let Err(err) = backend.init_transport(&iface) {
eprintln!(
"redbear-wifictl: transport init failed for {}: {}",
iface, err
);
process::exit(1);
}
if let Err(err) = backend.activate(&iface) {
eprintln!(
"redbear-wifictl: activate-nic failed for {}: {}",
iface, err
);
process::exit(1);
}
match backend.disconnect(&iface) {
Ok(status) => {
println!("interface={}", iface);
println!("status={}", status.as_str());
println!("firmware_status={}", backend.firmware_status(&iface));
println!("transport_status={}", backend.transport_status(&iface));
println!("disconnect_result={}", backend.disconnect_result(&iface));
return;
}
Err(err) => {
eprintln!("redbear-wifictl: disconnect failed for {}: {}", iface, err);
process::exit(1);
}
}
}
_ => {}
}
#[cfg(not(target_os = "redox"))]
{
eprintln!("redbear-wifictl: daemon mode is only supported on Redox; use --probe on host");
process::exit(1);
}
#[cfg(target_os = "redox")]
{
if dbus_enabled {
register_nm_interface();
}
let notify_fd = unsafe { get_init_notify_fd() };
let socket = match Socket::create() {
Ok(s) => s,
Err(err) => {
error!("redbear-wifictl: failed to create scheme socket: {err}");
process::exit(1);
}
};
let mut scheme = WifiCtlScheme::new(build_backend());
let mut state = redox_scheme::scheme::SchemeState::new();
notify_scheme_ready(notify_fd, &socket, &mut scheme);
match libredox::call::setrens(0, 0) {
Ok(_) => info!("redbear-wifictl: registered scheme:wifictl"),
Err(err) => {
error!("redbear-wifictl: failed to enter null namespace: {err}");
process::exit(1);
}
}
let mut exit_code = 0;
loop {
let request = match socket.next_request(SignalBehavior::Restart) {
Ok(Some(req)) => req,
Ok(None) => {
info!("redbear-wifictl: scheme socket closed, shutting down");
break;
}
Err(err) => {
error!("redbear-wifictl: failed to read scheme request: {err}");
exit_code = 1;
break;
}
};
match request.kind() {
redox_scheme::RequestKind::Call(request) => {
let response = request.handle_sync(&mut scheme, &mut state);
if let Err(err) = socket.write_response(response, SignalBehavior::Restart) {
error!("redbear-wifictl: failed to write response: {err}");
exit_code = 1;
break;
}
}
_ => {}
}
}
process::exit(exit_code);
}
}