fix: derive wifi interface dynamically from gateway info instead of detecting at startup

Remove static wifi_interface/ethernet_interface fields from RpcContextSeed. Instead, look up
the wifi interface from the DB (populated by gateway sync) and check ethernet connectivity
by querying gateway entries. This ensures the wifi manager always uses the correct interface
even if network devices change after boot.
This commit is contained in:
Aiden McClelland
2026-03-18 15:59:58 -06:00
parent 5cf70dc8f5
commit 9ed6c1263c
5 changed files with 164 additions and 139 deletions

View File

@@ -35,7 +35,6 @@ use crate::lxc::LxcManager;
use crate::net::gateway::WildcardListener; use crate::net::gateway::WildcardListener;
use crate::net::net_controller::{NetController, NetService}; use crate::net::net_controller::{NetController, NetService};
use crate::net::socks::DEFAULT_SOCKS_LISTEN; use crate::net::socks::DEFAULT_SOCKS_LISTEN;
use crate::net::utils::{find_eth_iface, find_wifi_iface};
use crate::net::web_server::WebServerAcceptorSetter; use crate::net::web_server::WebServerAcceptorSetter;
use crate::net::wifi::WpaCli; use crate::net::wifi::WpaCli;
use crate::prelude::*; use crate::prelude::*;
@@ -55,8 +54,6 @@ use crate::{DATA_DIR, PLATFORM, PackageId};
pub struct RpcContextSeed { pub struct RpcContextSeed {
is_closed: AtomicBool, is_closed: AtomicBool,
pub os_partitions: OsPartitionInfo, pub os_partitions: OsPartitionInfo,
pub wifi_interface: Option<String>,
pub ethernet_interface: String,
pub disk_guid: InternedString, pub disk_guid: InternedString,
pub ephemeral_sessions: SyncMutex<Sessions>, pub ephemeral_sessions: SyncMutex<Sessions>,
pub db: TypedPatchDb<Database>, pub db: TypedPatchDb<Database>,
@@ -73,7 +70,7 @@ pub struct RpcContextSeed {
pub open_authed_continuations: OpenAuthedContinuations<Option<InternedString>>, pub open_authed_continuations: OpenAuthedContinuations<Option<InternedString>>,
pub rpc_continuations: RpcContinuations, pub rpc_continuations: RpcContinuations,
pub callbacks: Arc<ServiceCallbacks>, pub callbacks: Arc<ServiceCallbacks>,
pub wifi_manager: Arc<RwLock<Option<WpaCli>>>, pub wifi_manager: RwLock<Option<WpaCli>>,
pub current_secret: Arc<Jwk>, pub current_secret: Arc<Jwk>,
pub client: Client, pub client: Client,
pub start_time: Instant, pub start_time: Instant,
@@ -323,13 +320,9 @@ impl RpcContext {
}); });
} }
let wifi_interface = find_wifi_iface().await?;
let seed = Arc::new(RpcContextSeed { let seed = Arc::new(RpcContextSeed {
is_closed: AtomicBool::new(false), is_closed: AtomicBool::new(false),
os_partitions: OsPartitionInfo::from_fstab().await?, os_partitions: OsPartitionInfo::from_fstab().await?,
wifi_interface: wifi_interface.clone(),
ethernet_interface: find_eth_iface().await?,
disk_guid, disk_guid,
ephemeral_sessions: SyncMutex::new(Sessions::new()), ephemeral_sessions: SyncMutex::new(Sessions::new()),
sync_db: watch::Sender::new(db.sequence().await), sync_db: watch::Sender::new(db.sequence().await),
@@ -350,7 +343,7 @@ impl RpcContext {
shutdown, shutdown,
lxc_manager: Arc::new(LxcManager::new()), lxc_manager: Arc::new(LxcManager::new()),
open_authed_continuations: OpenAuthedContinuations::new(), open_authed_continuations: OpenAuthedContinuations::new(),
wifi_manager: Arc::new(RwLock::new(wifi_interface.clone().map(|i| WpaCli::init(i)))), wifi_manager: RwLock::new(None),
current_secret: Arc::new( current_secret: Arc::new(
Jwk::generate_ec_key(josekit::jwk::alg::ec::EcCurve::P256).map_err(|e| { Jwk::generate_ec_key(josekit::jwk::alg::ec::EcCurve::P256).map_err(|e| {
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);

View File

@@ -98,7 +98,7 @@ impl Public {
port_forwards: BTreeSet::new(), port_forwards: BTreeSet::new(),
}, },
wifi: WifiInfo { wifi: WifiInfo {
enabled: true, enabled: false,
..Default::default() ..Default::default()
}, },
gateways: OrdMap::new(), gateways: OrdMap::new(),
@@ -378,7 +378,7 @@ pub struct ServerStatus {
#[ts(export)] #[ts(export)]
pub struct WifiInfo { pub struct WifiInfo {
pub enabled: bool, pub enabled: bool,
pub interface: Option<String>, pub interface: Option<GatewayId>,
pub ssids: BTreeSet<String>, pub ssids: BTreeSet<String>,
pub selected: Option<String>, pub selected: Option<String>,
#[ts(type = "string | null")] #[ts(type = "string | null")]

View File

@@ -23,7 +23,6 @@ use crate::middleware::auth::local::LocalAuthContext;
use crate::net::gateway::WildcardListener; use crate::net::gateway::WildcardListener;
use crate::net::net_controller::{NetController, NetService}; use crate::net::net_controller::{NetController, NetService};
use crate::net::socks::DEFAULT_SOCKS_LISTEN; use crate::net::socks::DEFAULT_SOCKS_LISTEN;
use crate::net::utils::find_wifi_iface;
use crate::net::web_server::WebServerAcceptorSetter; use crate::net::web_server::WebServerAcceptorSetter;
use crate::prelude::*; use crate::prelude::*;
use crate::progress::{ use crate::progress::{
@@ -280,20 +279,17 @@ pub async fn init(
load_ca_cert.complete(); load_ca_cert.complete();
load_wifi.start(); load_wifi.start();
let wifi_interface = find_wifi_iface().await?; crate::net::wifi::synchronize_network_manager(
let wifi = db MAIN_DATA,
.mutate(|db| { &peek
let wifi = db .as_public()
.as_public_mut() .as_server_info()
.as_server_info_mut() .as_network()
.as_network_mut() .as_wifi()
.as_wifi_mut(); .de()
wifi.as_interface_mut().ser(&wifi_interface)?; .unwrap_or_default(),
wifi.de() )
}) .await?;
.await
.result?;
crate::net::wifi::synchronize_network_manager(MAIN_DATA, &wifi).await?;
load_wifi.complete(); load_wifi.complete();
init_tmp.start(); init_tmp.start();

View File

@@ -34,6 +34,7 @@ use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceTyp
use crate::net::forward::START9_BRIDGE_IFACE; use crate::net::forward::START9_BRIDGE_IFACE;
use crate::net::gateway::device::DeviceProxy; use crate::net::gateway::device::DeviceProxy;
use crate::net::host::all_hosts; use crate::net::host::all_hosts;
use crate::net::utils::find_wifi_iface;
use crate::net::web_server::{Accept, AcceptStream, MetadataVisitor, TcpMetadata}; use crate::net::web_server::{Accept, AcceptStream, MetadataVisitor, TcpMetadata};
use crate::prelude::*; use crate::prelude::*;
use crate::util::Invoke; use crate::util::Invoke;
@@ -775,7 +776,9 @@ async fn watcher(
async fn get_wan_ipv4(iface: &str, base_url: &Url) -> Result<Option<Ipv4Addr>, Error> { async fn get_wan_ipv4(iface: &str, base_url: &Url) -> Result<Option<Ipv4Addr>, Error> {
let client = reqwest::Client::builder(); let client = reqwest::Client::builder();
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
let client = client.interface(iface); let client = client
.interface(iface)
.local_address(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
let url = base_url.join("/ip").with_kind(ErrorKind::ParseUrl)?; let url = base_url.join("/ip").with_kind(ErrorKind::ParseUrl)?;
let text = client let text = client
.build()? .build()?
@@ -1289,14 +1292,15 @@ async fn poll_ip_info(
}; };
let mut wan_ip = None; let mut wan_ip = None;
for echoip_url in echoip_urls { for echoip_url in echoip_urls {
let wan_ip = if echoip_ratelimit_state if echoip_ratelimit_state
.get(&echoip_url) .get(&echoip_url)
.map_or(true, |i| i.elapsed() > Duration::from_secs(300)) .map_or(true, |i| i.elapsed() > Duration::from_secs(300))
&& !subnets.is_empty() && !subnets.is_empty()
&& !matches!( && !matches!(
device_type, device_type,
Some(NetworkInterfaceType::Bridge | NetworkInterfaceType::Loopback) Some(NetworkInterfaceType::Bridge | NetworkInterfaceType::Loopback)
) { )
{
match get_wan_ipv4(iface.as_str(), &echoip_url).await { match get_wan_ipv4(iface.as_str(), &echoip_url).await {
Ok(a) => { Ok(a) => {
wan_ip = a; wan_ip = a;
@@ -1458,12 +1462,27 @@ impl NetworkInterfaceController {
) -> Result<(), Error> { ) -> Result<(), Error> {
tracing::debug!("syncronizing {info:?} to db"); tracing::debug!("syncronizing {info:?} to db");
let mut wifi_iface = info
.iter()
.find(|(_, info)| {
info.ip_info.as_ref().map_or(false, |i| {
i.device_type == Some(NetworkInterfaceType::Wireless)
})
})
.map(|(id, _)| id.clone());
if wifi_iface.is_none() {
wifi_iface = find_wifi_iface()
.await
.ok()
.and_then(|a| a)
.map(InternedString::from)
.map(GatewayId::from);
}
db.mutate(|db| { db.mutate(|db| {
db.as_public_mut() let network = db.as_public_mut().as_server_info_mut().as_network_mut();
.as_server_info_mut() network.as_gateways_mut().ser(info)?;
.as_network_mut() network.as_wifi_mut().as_interface_mut().ser(&wifi_iface)?;
.as_gateways_mut()
.ser(info)?;
let hostname = crate::hostname::ServerHostname::load(db.as_public().as_server_info())?; let hostname = crate::hostname::ServerHostname::load(db.as_public().as_server_info())?;
let ports = db.as_private().as_available_ports().de()?; let ports = db.as_private().as_available_ports().de()?;
for host in all_hosts(db) { for host in all_hosts(db) {

View File

@@ -1,6 +1,5 @@
use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::path::Path; use std::path::Path;
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use clap::Parser; use clap::Parser;
@@ -11,30 +10,100 @@ use regex::Regex;
use rpc_toolkit::{Context, Empty, HandlerExt, ParentHandler, from_fn_async}; use rpc_toolkit::{Context, Empty, HandlerExt, ParentHandler, from_fn_async};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::process::Command; use tokio::process::Command;
use tokio::sync::RwLock; use tokio::sync::{RwLockMappedWriteGuard, RwLockReadGuard, RwLockWriteGuard};
use tracing::instrument; use tracing::instrument;
use ts_rs::TS; use ts_rs::TS;
use crate::context::{CliContext, RpcContext}; use crate::context::{CliContext, RpcContext};
use crate::db::model::Database; use crate::db::model::Database;
use crate::db::model::public::WifiInfo; use crate::db::model::public::{NetworkInterfaceType, WifiInfo};
use crate::prelude::*; use crate::prelude::*;
use crate::util::Invoke; use crate::util::Invoke;
use crate::util::serde::{HandlerExtSerde, WithIoFormat, display_serializable}; use crate::util::serde::{HandlerExtSerde, WithIoFormat, display_serializable};
use crate::{Error, ErrorKind}; use crate::{Error, ErrorKind, GatewayId};
type WifiManager = Arc<RwLock<Option<WpaCli>>>; impl RpcContext {
async fn read_wifi_manager(&self) -> Result<RwLockReadGuard<'_, WpaCli>, Error> {
let err = || {
Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
};
let Some(interface) = self
.db
.peek()
.await
.as_public()
.as_server_info()
.as_network()
.as_wifi()
.as_interface()
.de()?
else {
return Err(err());
};
let mut cli = RwLockReadGuard::try_map(self.wifi_manager.read().await, |c| c.as_ref()).ok();
while cli.as_ref().map_or(true, |c| c.interface != interface) {
drop(cli.take());
let mut guard = self.wifi_manager.write().await;
*guard = Some(WpaCli::new(interface.clone()));
drop(guard);
cli = RwLockReadGuard::try_map(self.wifi_manager.read().await, |c| c.as_ref()).ok();
}
cli.ok_or_else(err)
}
// pub fn wifi_manager(ctx: &RpcContext) -> Result<&WifiManager, Error> { async fn write_wifi_manager(&self) -> Result<RwLockMappedWriteGuard<'_, WpaCli>, Error> {
// if let Some(wifi_manager) = ctx.wifi_manager.as_ref() { let err = || {
// Ok(wifi_manager) Error::new(
// } else { color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
// Err(Error::new( ErrorKind::Wifi,
// color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")), )
// ErrorKind::Wifi, };
// )) let Some(interface) = self
// } .db
// } .peek()
.await
.as_public()
.as_server_info()
.as_network()
.as_wifi()
.as_interface()
.de()?
else {
return Err(err());
};
let mut cli = self.wifi_manager.write().await;
if cli.as_ref().map_or(true, |c| c.interface != interface) {
*cli = Some(WpaCli::new(interface));
}
RwLockWriteGuard::try_map(cli, |c| c.as_mut()).map_err(|_| err())
}
async fn ethernet_interface_connected(&self) -> Result<bool, Error> {
for (iface, info) in self
.db
.peek()
.await
.as_public()
.as_server_info()
.as_network()
.as_gateways()
.as_entries()?
{
let Some(info) = info.as_ip_info().transpose_ref() else {
continue;
};
if info.as_deref().as_device_type().de()? == Some(NetworkInterfaceType::Ethernet) {
if interface_connected(iface.as_str()).await? {
return Ok(true);
}
}
}
Ok(false)
}
}
pub fn wifi<C: Context>() -> ParentHandler<C> { pub fn wifi<C: Context>() -> ParentHandler<C> {
ParentHandler::new() ParentHandler::new()
@@ -165,55 +234,35 @@ pub async fn add(
ctx: RpcContext, ctx: RpcContext,
WifiAddParams { ssid, password }: WifiAddParams, WifiAddParams { ssid, password }: WifiAddParams,
) -> Result<(), Error> { ) -> Result<(), Error> {
let wifi_manager = ctx.wifi_manager.clone(); let mut wpa_supplicant = ctx.write_wifi_manager().await?;
if !ssid.is_ascii() { if !ssid.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")), color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
let ssid = Ssid(ssid);
if !password.is_ascii() { if !password.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.password-no-special-characters")), color_eyre::eyre::eyre!("{}", t!("net.wifi.password-no-special-characters")),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
async fn add_procedure( if let Err(err) = wpa_supplicant
db: TypedPatchDb<Database>, .add_network(ctx.db.clone(), &ssid, &Psk(password))
wifi_manager: WifiManager,
ssid: &Ssid,
password: &Psk,
) -> Result<(), Error> {
tracing::info!("{}", t!("net.wifi.adding-network", ssid = &ssid.0));
let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
wpa_supplicant.add_network(db, ssid, password).await?;
Ok(())
}
if let Err(err) = add_procedure(
ctx.db.clone(),
wifi_manager.clone(),
&Ssid(ssid.clone()),
&Psk(password.clone()),
)
.await .await
{ {
tracing::error!( tracing::error!(
"{}", "{}",
t!( t!(
"net.wifi.failed-to-add-network", "net.wifi.failed-to-add-network",
ssid = &ssid, ssid = &ssid.0,
error = err.to_string() error = err.to_string()
) )
); );
tracing::debug!("{:?}", err); tracing::debug!("{:?}", err);
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.failed-adding", ssid = &ssid)), color_eyre::eyre::eyre!("{}", t!("net.wifi.failed-adding", ssid = &ssid.0)),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
@@ -225,7 +274,7 @@ pub async fn add(
.as_wifi_mut() .as_wifi_mut()
.as_ssids_mut() .as_ssids_mut()
.mutate(|s| { .mutate(|s| {
s.insert(ssid); s.insert(ssid.0);
Ok(()) Ok(())
}) })
}) })
@@ -247,45 +296,36 @@ pub async fn connect(
ctx: RpcContext, ctx: RpcContext,
WifiSsidParams { ssid }: WifiSsidParams, WifiSsidParams { ssid }: WifiSsidParams,
) -> Result<(), Error> { ) -> Result<(), Error> {
let wifi_manager = ctx.wifi_manager.clone(); let mut wpa_supplicant = ctx.write_wifi_manager().await?;
if !ssid.is_ascii() { if !ssid.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")), color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
async fn connect_procedure( if let Err(err) = async {
db: TypedPatchDb<Database>,
wifi_manager: WifiManager,
ssid: &Ssid,
) -> Result<(), Error> {
let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
let current = wpa_supplicant.get_current_network().await?; let current = wpa_supplicant.get_current_network().await?;
let connected = wpa_supplicant.select_network(db.clone(), ssid).await?; let connected = wpa_supplicant
.select_network(ctx.db.clone(), &Ssid(ssid.clone()))
.await?;
if connected { if connected {
tracing::info!("{}", t!("net.wifi.connected-successfully", ssid = &ssid.0)); tracing::info!("{}", t!("net.wifi.connected-successfully", ssid = &ssid));
} else { } else {
tracing::info!("{}", t!("net.wifi.connection-failed", ssid = &ssid.0)); tracing::info!("{}", t!("net.wifi.connection-failed", ssid = &ssid));
match current { match current {
None => { None => {
tracing::info!("{}", t!("net.wifi.no-wifi-to-revert")); tracing::info!("{}", t!("net.wifi.no-wifi-to-revert"));
} }
Some(current) => { Some(current) => {
wpa_supplicant.select_network(db, &current).await?; wpa_supplicant
.select_network(ctx.db.clone(), &current)
.await?;
} }
} }
} }
Ok(()) Ok::<_, Error>(())
} }
.await
if let Err(err) =
connect_procedure(ctx.db.clone(), wifi_manager.clone(), &Ssid(ssid.clone())).await
{ {
tracing::error!( tracing::error!(
"{}", "{}",
@@ -321,26 +361,18 @@ pub async fn connect(
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn remove(ctx: RpcContext, WifiSsidParams { ssid }: WifiSsidParams) -> Result<(), Error> { pub async fn remove(ctx: RpcContext, WifiSsidParams { ssid }: WifiSsidParams) -> Result<(), Error> {
let wifi_manager = ctx.wifi_manager.clone(); let mut wpa_supplicant = ctx.write_wifi_manager().await?;
if !ssid.is_ascii() { if !ssid.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")), color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
let current = wpa_supplicant.get_current_network().await?; let current = wpa_supplicant.get_current_network().await?;
let ssid = Ssid(ssid); let ssid = Ssid(ssid);
let is_current_being_removed = matches!(current, Some(current) if current == ssid); let is_current_being_removed = matches!(current, Some(current) if current == ssid);
let is_current_removed_and_no_hardwire = let is_current_removed_and_no_hardwire =
is_current_being_removed && !interface_connected(&ctx.ethernet_interface).await?; is_current_being_removed && !ctx.ethernet_interface_connected().await?;
if is_current_removed_and_no_hardwire { if is_current_removed_and_no_hardwire {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.forbidden-delete-would-disconnect")), color_eyre::eyre::eyre!("{}", t!("net.wifi.forbidden-delete-would-disconnect")),
@@ -487,19 +519,12 @@ fn display_wifi_list(params: WithIoFormat<Empty>, info: Vec<WifiListOut>) -> Res
// #[command(display(display_wifi_info))] // #[command(display(display_wifi_info))]
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn get(ctx: RpcContext, _: Empty) -> Result<WifiListInfo, Error> { pub async fn get(ctx: RpcContext, _: Empty) -> Result<WifiListInfo, Error> {
let wifi_manager = ctx.wifi_manager.clone(); let wpa_supplicant = ctx.read_wifi_manager().await?;
let wpa_supplicant = wifi_manager.read_owned().await;
let wpa_supplicant = wpa_supplicant.as_ref().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
let (list_networks, current_res, country_res, ethernet_res, signal_strengths) = tokio::join!( let (list_networks, current_res, country_res, ethernet_res, signal_strengths) = tokio::join!(
wpa_supplicant.list_networks_low(), wpa_supplicant.list_networks_low(),
wpa_supplicant.get_current_network(), wpa_supplicant.get_current_network(),
wpa_supplicant.get_country_low(), wpa_supplicant.get_country_low(),
interface_connected(&ctx.ethernet_interface), ctx.ethernet_interface_connected(),
wpa_supplicant.list_wifi_low() wpa_supplicant.list_wifi_low()
); );
let signal_strengths = signal_strengths?; let signal_strengths = signal_strengths?;
@@ -541,14 +566,7 @@ pub async fn get(ctx: RpcContext, _: Empty) -> Result<WifiListInfo, Error> {
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn get_available(ctx: RpcContext, _: Empty) -> Result<Vec<WifiListOut>, Error> { pub async fn get_available(ctx: RpcContext, _: Empty) -> Result<Vec<WifiListOut>, Error> {
let wifi_manager = ctx.wifi_manager.clone(); let wpa_supplicant = ctx.read_wifi_manager().await?;
let wpa_supplicant = wifi_manager.read_owned().await;
let wpa_supplicant = wpa_supplicant.as_ref().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
let (wifi_list, network_list) = tokio::join!( let (wifi_list, network_list) = tokio::join!(
wpa_supplicant.list_wifi_low(), wpa_supplicant.list_wifi_low(),
wpa_supplicant.list_networks_low() wpa_supplicant.list_networks_low()
@@ -584,20 +602,13 @@ pub async fn set_country(
ctx: RpcContext, ctx: RpcContext,
SetCountryParams { country }: SetCountryParams, SetCountryParams { country }: SetCountryParams,
) -> Result<(), Error> { ) -> Result<(), Error> {
let wifi_manager = ctx.wifi_manager.clone(); if !ctx.ethernet_interface_connected().await? {
if !interface_connected(&ctx.ethernet_interface).await? {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.wont-change-country-without-ethernet")), color_eyre::eyre::eyre!("{}", t!("net.wifi.wont-change-country-without-ethernet")),
crate::ErrorKind::Wifi, crate::ErrorKind::Wifi,
)); ));
} }
let mut wpa_supplicant = wifi_manager.write_owned().await; let mut wpa_supplicant = ctx.write_wifi_manager().await?;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
wpa_supplicant.set_country_low(country.alpha2()).await?; wpa_supplicant.set_country_low(country.alpha2()).await?;
for (network_id, _wifi_info) in wpa_supplicant.list_networks_low().await? { for (network_id, _wifi_info) in wpa_supplicant.list_networks_low().await? {
wpa_supplicant.remove_network_low(network_id).await?; wpa_supplicant.remove_network_low(network_id).await?;
@@ -611,7 +622,7 @@ pub async fn set_country(
#[derive(Debug)] #[derive(Debug)]
pub struct WpaCli { pub struct WpaCli {
interface: String, interface: GatewayId,
} }
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct NetworkId(String); pub struct NetworkId(String);
@@ -661,7 +672,7 @@ pub struct WifiInfoLow {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Psk(String); pub struct Psk(String);
impl WpaCli { impl WpaCli {
pub fn init(interface: String) -> Self { pub fn new(interface: GatewayId) -> Self {
WpaCli { interface } WpaCli { interface }
} }
@@ -709,7 +720,7 @@ impl WpaCli {
.arg("modify") .arg("modify")
.arg(&ssid.0) .arg(&ssid.0)
.arg("ifname") .arg("ifname")
.arg(&self.interface) .arg(self.interface.as_str())
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await .await
.map(|_| ()) .map(|_| ())
@@ -951,7 +962,7 @@ impl WpaCli {
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn get_current_network(&self) -> Result<Option<Ssid>, Error> { pub async fn get_current_network(&self) -> Result<Option<Ssid>, Error> {
let r = Command::new("iwgetid") let r = Command::new("iwgetid")
.arg(&self.interface) .arg(self.interface.as_str())
.arg("--raw") .arg("--raw")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
@@ -1055,6 +1066,12 @@ pub async fn synchronize_network_manager<P: AsRef<Path>>(
.arg("all") .arg("all")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
} else {
Command::new("rfkill")
.arg("unblock")
.arg("all")
.invoke(ErrorKind::Wifi)
.await?;
} }
Command::new("ip") Command::new("ip")
@@ -1093,7 +1110,7 @@ pub async fn synchronize_network_manager<P: AsRef<Path>>(
}; };
Command::new("ifconfig") Command::new("ifconfig")
.arg(wifi_iface) .arg(wifi_iface.as_str())
.arg("up") .arg("up")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;