|
|
|
|
@@ -1,10 +1,13 @@
|
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
use std::collections::{BTreeMap, HashMap};
|
|
|
|
|
use std::path::Path;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
|
|
use clap::ArgMatches;
|
|
|
|
|
use isocountry::CountryCode;
|
|
|
|
|
use lazy_static::lazy_static;
|
|
|
|
|
use patch_db::DbHandle;
|
|
|
|
|
use regex::Regex;
|
|
|
|
|
use rpc_toolkit::command;
|
|
|
|
|
use tokio::process::Command;
|
|
|
|
|
use tokio::sync::RwLock;
|
|
|
|
|
@@ -15,13 +18,23 @@ use crate::util::serde::{display_serializable, IoFormat};
|
|
|
|
|
use crate::util::{display_none, Invoke};
|
|
|
|
|
use crate::{Error, ErrorKind};
|
|
|
|
|
|
|
|
|
|
#[command(subcommands(add, connect, delete, get, set_country))]
|
|
|
|
|
#[command(subcommands(add, connect, delete, get, country, available))]
|
|
|
|
|
pub async fn wifi() -> Result<(), Error> {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[command(subcommands(get_available))]
|
|
|
|
|
pub async fn available() -> Result<(), Error> {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[command(subcommands(set_country))]
|
|
|
|
|
pub async fn country() -> Result<(), Error> {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[command(display(display_none))]
|
|
|
|
|
#[instrument(skip(ctx))]
|
|
|
|
|
#[instrument(skip(ctx, password))]
|
|
|
|
|
pub async fn add(
|
|
|
|
|
#[context] ctx: RpcContext,
|
|
|
|
|
#[arg] ssid: String,
|
|
|
|
|
@@ -42,48 +55,32 @@ pub async fn add(
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
async fn add_procedure(
|
|
|
|
|
db: impl DbHandle,
|
|
|
|
|
wifi_manager: Arc<RwLock<WpaCli>>,
|
|
|
|
|
ssid: &str,
|
|
|
|
|
password: &str,
|
|
|
|
|
ssid: &Ssid,
|
|
|
|
|
password: &Psk,
|
|
|
|
|
priority: isize,
|
|
|
|
|
connect: bool,
|
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
tracing::info!("Adding new WiFi network: '{}'", ssid);
|
|
|
|
|
tracing::info!("Adding new WiFi network: '{}'", ssid.0);
|
|
|
|
|
let mut wpa_supplicant = wifi_manager.write().await;
|
|
|
|
|
wpa_supplicant.add_network(ssid, password, priority).await?;
|
|
|
|
|
wpa_supplicant
|
|
|
|
|
.add_network(db, ssid, password, priority)
|
|
|
|
|
.await?;
|
|
|
|
|
drop(wpa_supplicant);
|
|
|
|
|
if connect {
|
|
|
|
|
let wpa_supplicant = wifi_manager.read().await;
|
|
|
|
|
let current = wpa_supplicant.get_current_network().await?;
|
|
|
|
|
drop(wpa_supplicant);
|
|
|
|
|
let mut wpa_supplicant = wifi_manager.write().await;
|
|
|
|
|
let connected = wpa_supplicant.select_network(ssid).await?;
|
|
|
|
|
if !connected {
|
|
|
|
|
tracing::info!("Failed to add new WiFi network: '{}'", ssid);
|
|
|
|
|
wpa_supplicant.remove_network(ssid).await?;
|
|
|
|
|
match current {
|
|
|
|
|
None => {}
|
|
|
|
|
Some(current) => {
|
|
|
|
|
wpa_supplicant.select_network(¤t).await?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
match add_procedure(
|
|
|
|
|
&mut ctx.db.handle(),
|
|
|
|
|
ctx.wifi_manager.clone(),
|
|
|
|
|
&ssid,
|
|
|
|
|
&password,
|
|
|
|
|
&Ssid(ssid.clone()),
|
|
|
|
|
&Psk(password.clone()),
|
|
|
|
|
priority,
|
|
|
|
|
connect,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
Err(e) => {
|
|
|
|
|
tracing::info!("Failed to add new WiFi network '{}': {}", ssid, e);
|
|
|
|
|
tracing::debug!("{:?}", e);
|
|
|
|
|
}
|
|
|
|
|
Ok(_) => {}
|
|
|
|
|
}
|
|
|
|
|
@@ -101,31 +98,38 @@ pub async fn connect(#[context] ctx: RpcContext, #[arg] ssid: String) -> Result<
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
async fn connect_procedure(
|
|
|
|
|
mut db: impl DbHandle,
|
|
|
|
|
wifi_manager: Arc<RwLock<WpaCli>>,
|
|
|
|
|
ssid: &String,
|
|
|
|
|
ssid: &Ssid,
|
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
let wpa_supplicant = wifi_manager.read().await;
|
|
|
|
|
let current = wpa_supplicant.get_current_network().await?;
|
|
|
|
|
drop(wpa_supplicant);
|
|
|
|
|
let mut wpa_supplicant = wifi_manager.write().await;
|
|
|
|
|
let connected = wpa_supplicant.select_network(&ssid).await?;
|
|
|
|
|
let connected = wpa_supplicant.select_network(&mut db, &ssid).await?;
|
|
|
|
|
if connected {
|
|
|
|
|
tracing::info!("Successfully connected to WiFi: '{}'", ssid);
|
|
|
|
|
tracing::info!("Successfully connected to WiFi: '{}'", ssid.0);
|
|
|
|
|
} else {
|
|
|
|
|
tracing::info!("Failed to connect to WiFi: '{}'", ssid);
|
|
|
|
|
tracing::info!("Failed to connect to WiFi: '{}'", ssid.0);
|
|
|
|
|
match current {
|
|
|
|
|
None => {
|
|
|
|
|
tracing::info!("No WiFi to revert to!");
|
|
|
|
|
}
|
|
|
|
|
Some(current) => {
|
|
|
|
|
wpa_supplicant.select_network(¤t).await?;
|
|
|
|
|
wpa_supplicant.select_network(&mut db, ¤t).await?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
match connect_procedure(ctx.wifi_manager.clone(), &ssid).await {
|
|
|
|
|
match connect_procedure(
|
|
|
|
|
&mut ctx.db.handle(),
|
|
|
|
|
ctx.wifi_manager.clone(),
|
|
|
|
|
&Ssid(ssid.clone()),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
Err(e) => {
|
|
|
|
|
tracing::info!("Failed to connect to WiFi network '{}': {}", &ssid, e);
|
|
|
|
|
}
|
|
|
|
|
@@ -148,31 +152,42 @@ pub async fn delete(#[context] ctx: RpcContext, #[arg] ssid: String) -> Result<(
|
|
|
|
|
let current = wpa_supplicant.get_current_network().await?;
|
|
|
|
|
drop(wpa_supplicant);
|
|
|
|
|
let mut wpa_supplicant = ctx.wifi_manager.write().await;
|
|
|
|
|
match current {
|
|
|
|
|
None => {
|
|
|
|
|
wpa_supplicant.remove_network(&ssid).await?;
|
|
|
|
|
}
|
|
|
|
|
Some(current) => {
|
|
|
|
|
if current == ssid {
|
|
|
|
|
if interface_connected("eth0").await? {
|
|
|
|
|
wpa_supplicant.remove_network(&ssid).await?;
|
|
|
|
|
} else {
|
|
|
|
|
return Err(Error::new(color_eyre::eyre::eyre!("Forbidden: Deleting this Network would make your Embassy Unreachable. Either connect to ethernet or connect to a different WiFi network to remedy this."), ErrorKind::Wifi));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let ssid = Ssid(ssid);
|
|
|
|
|
let is_current_being_removed = matches!(current, Some(current) if current == ssid);
|
|
|
|
|
let is_current_removed_and_no_hardwire =
|
|
|
|
|
is_current_being_removed && !interface_connected("eth0").await?;
|
|
|
|
|
if is_current_removed_and_no_hardwire {
|
|
|
|
|
return Err(Error::new(color_eyre::eyre::eyre!("Forbidden: Deleting this Network would make your Embassy Unreachable. Either connect to ethernet or connect to a different WiFi network to remedy this."), ErrorKind::Wifi));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wpa_supplicant
|
|
|
|
|
.remove_network(&mut ctx.db.handle(), &ssid)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
|
pub struct WiFiInfo {
|
|
|
|
|
ssids: Vec<String>,
|
|
|
|
|
connected: Option<String>,
|
|
|
|
|
ssids: HashMap<Ssid, SignalStrength>,
|
|
|
|
|
connected: Option<Ssid>,
|
|
|
|
|
country: CountryCode,
|
|
|
|
|
ethernet: bool,
|
|
|
|
|
signal_strength: Option<usize>,
|
|
|
|
|
available_wifi: Vec<WifiListOut>,
|
|
|
|
|
}
|
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize, Clone)]
|
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
|
pub struct WifiListInfo {
|
|
|
|
|
strength: SignalStrength,
|
|
|
|
|
security: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
|
pub struct WifiListOut {
|
|
|
|
|
ssid: Ssid,
|
|
|
|
|
strength: SignalStrength,
|
|
|
|
|
security: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
pub type WifiList = HashMap<Ssid, WifiListInfo>;
|
|
|
|
|
fn display_wifi_info(info: WiFiInfo, matches: &ArgMatches<'_>) {
|
|
|
|
|
use prettytable::*;
|
|
|
|
|
|
|
|
|
|
@@ -191,34 +206,74 @@ fn display_wifi_info(info: WiFiInfo, matches: &ArgMatches<'_>) {
|
|
|
|
|
&info
|
|
|
|
|
.connected
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map_or("[N/A]".to_owned(), |c| format!("{}", c)),
|
|
|
|
|
.map_or("[N/A]".to_owned(), |c| format!("{}", c.0)),
|
|
|
|
|
&info
|
|
|
|
|
.signal_strength
|
|
|
|
|
.connected
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map_or("[N/A]".to_owned(), |ss| format!("{}", ss)),
|
|
|
|
|
.and_then(|x| info.ssids.get(x))
|
|
|
|
|
.map_or("[N/A]".to_owned(), |ss| format!("{}", ss.0)),
|
|
|
|
|
&format!("{}", info.country.alpha2()),
|
|
|
|
|
&format!("{}", info.ethernet)
|
|
|
|
|
]);
|
|
|
|
|
table_global.print_tty(false);
|
|
|
|
|
|
|
|
|
|
let mut table_ssids = Table::new();
|
|
|
|
|
table_ssids.add_row(row![bc => "SSID",]);
|
|
|
|
|
for ssid in &info.ssids {
|
|
|
|
|
let mut row = row![ssid];
|
|
|
|
|
if Some(ssid) == info.connected.as_ref() {
|
|
|
|
|
row.iter_mut()
|
|
|
|
|
.map(|c| {
|
|
|
|
|
c.style(Attr::ForegroundColor(match &info.signal_strength {
|
|
|
|
|
Some(100) => color::GREEN,
|
|
|
|
|
Some(0) => color::RED,
|
|
|
|
|
_ => color::YELLOW,
|
|
|
|
|
}))
|
|
|
|
|
})
|
|
|
|
|
.collect::<()>()
|
|
|
|
|
}
|
|
|
|
|
table_ssids.add_row(row![bc => "SSID", "STRENGTH"]);
|
|
|
|
|
for (ssid, signal_strength) in &info.ssids {
|
|
|
|
|
let mut row = row![&ssid.0, format!("{}", signal_strength.0)];
|
|
|
|
|
row.iter_mut()
|
|
|
|
|
.map(|c| {
|
|
|
|
|
c.style(Attr::ForegroundColor(match &signal_strength.0 {
|
|
|
|
|
x if x >= &90 => color::GREEN,
|
|
|
|
|
x if x == &50 => color::MAGENTA,
|
|
|
|
|
x if x == &0 => color::RED,
|
|
|
|
|
_ => color::YELLOW,
|
|
|
|
|
}))
|
|
|
|
|
})
|
|
|
|
|
.for_each(drop);
|
|
|
|
|
table_ssids.add_row(row);
|
|
|
|
|
}
|
|
|
|
|
table_ssids.print_tty(false);
|
|
|
|
|
|
|
|
|
|
let mut table_global = Table::new();
|
|
|
|
|
table_global.add_row(row![bc =>
|
|
|
|
|
"SSID",
|
|
|
|
|
"STRENGTH",
|
|
|
|
|
"SECURITY",
|
|
|
|
|
]);
|
|
|
|
|
for table_info in info.available_wifi {
|
|
|
|
|
table_global.add_row(row![
|
|
|
|
|
&table_info.ssid.0,
|
|
|
|
|
&format!("{}", table_info.strength.0),
|
|
|
|
|
&format!("{}", table_info.security.join(" "))
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table_global.print_tty(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn display_wifi_list(info: Vec<WifiListOut>, matches: &ArgMatches<'_>) {
|
|
|
|
|
use prettytable::*;
|
|
|
|
|
|
|
|
|
|
if matches.is_present("format") {
|
|
|
|
|
return display_serializable(info, matches);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut table_global = Table::new();
|
|
|
|
|
table_global.add_row(row![bc =>
|
|
|
|
|
"SSID",
|
|
|
|
|
"STRENGTH",
|
|
|
|
|
"SECURITY",
|
|
|
|
|
]);
|
|
|
|
|
for table_info in info {
|
|
|
|
|
table_global.add_row(row![
|
|
|
|
|
&table_info.ssid.0,
|
|
|
|
|
&format!("{}", table_info.strength.0),
|
|
|
|
|
&format!("{}", table_info.security.join(" "))
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table_global.print_tty(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[command(display(display_wifi_info))]
|
|
|
|
|
@@ -230,261 +285,327 @@ pub async fn get(
|
|
|
|
|
format: Option<IoFormat>,
|
|
|
|
|
) -> Result<WiFiInfo, Error> {
|
|
|
|
|
let wpa_supplicant = ctx.wifi_manager.read().await;
|
|
|
|
|
let ssids_task = async {
|
|
|
|
|
Result::<Vec<String>, Error>::Ok(
|
|
|
|
|
wpa_supplicant
|
|
|
|
|
.list_networks_low()
|
|
|
|
|
.await?
|
|
|
|
|
.into_keys()
|
|
|
|
|
.collect::<Vec<String>>(),
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let current_task = wpa_supplicant.get_current_network();
|
|
|
|
|
let country_task = wpa_supplicant.get_country_low();
|
|
|
|
|
let ethernet_task = interface_connected("eth0"); // TODO: pull from config
|
|
|
|
|
let rssi_task = wpa_supplicant.signal_poll_low();
|
|
|
|
|
let (ssids_res, current_res, country_res, ethernet_res, rssi_res) = tokio::join!(
|
|
|
|
|
ssids_task,
|
|
|
|
|
current_task,
|
|
|
|
|
country_task,
|
|
|
|
|
ethernet_task,
|
|
|
|
|
rssi_task
|
|
|
|
|
let (list_networks, current_res, country_res, ethernet_res, signal_strengths) = tokio::join!(
|
|
|
|
|
wpa_supplicant.list_networks_low(),
|
|
|
|
|
wpa_supplicant.get_current_network(),
|
|
|
|
|
wpa_supplicant.get_country_low(),
|
|
|
|
|
interface_connected("eth0"), // TODO: pull from config
|
|
|
|
|
wpa_supplicant.list_wifi_low()
|
|
|
|
|
);
|
|
|
|
|
let current = current_res?;
|
|
|
|
|
let signal_strength = match rssi_res? {
|
|
|
|
|
None => None,
|
|
|
|
|
Some(x) if x <= -100 => Some(0 as usize),
|
|
|
|
|
Some(x) if x >= -50 => Some(100 as usize),
|
|
|
|
|
Some(x) => Some(2 * (x + 100) as usize),
|
|
|
|
|
let signal_strengths = signal_strengths?;
|
|
|
|
|
let list_networks = list_networks?;
|
|
|
|
|
let available_wifi = {
|
|
|
|
|
let mut wifi_list: Vec<WifiListOut> = signal_strengths
|
|
|
|
|
.clone()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter(|(ssid, _)| !list_networks.contains_key(ssid))
|
|
|
|
|
.map(|(ssid, info)| WifiListOut {
|
|
|
|
|
ssid,
|
|
|
|
|
strength: info.strength,
|
|
|
|
|
security: info.security,
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
wifi_list.sort_by_key(|x| x.strength);
|
|
|
|
|
wifi_list.reverse();
|
|
|
|
|
wifi_list
|
|
|
|
|
};
|
|
|
|
|
let ssids: HashMap<Ssid, SignalStrength> = list_networks
|
|
|
|
|
.into_keys()
|
|
|
|
|
.map(|x| {
|
|
|
|
|
let signal_strength = signal_strengths
|
|
|
|
|
.get(&x)
|
|
|
|
|
.map(|x| x.strength)
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
(x, signal_strength)
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
let current = current_res?;
|
|
|
|
|
Ok(WiFiInfo {
|
|
|
|
|
ssids: ssids_res?,
|
|
|
|
|
connected: current,
|
|
|
|
|
ssids,
|
|
|
|
|
connected: current.map(|x| x),
|
|
|
|
|
country: country_res?,
|
|
|
|
|
ethernet: ethernet_res?,
|
|
|
|
|
signal_strength,
|
|
|
|
|
available_wifi,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[command(display(display_none))]
|
|
|
|
|
#[command(rename = "get", display(display_wifi_list))]
|
|
|
|
|
#[instrument(skip(ctx))]
|
|
|
|
|
pub async fn get_available(
|
|
|
|
|
#[context] ctx: RpcContext,
|
|
|
|
|
#[allow(unused_variables)]
|
|
|
|
|
#[arg(long = "format")]
|
|
|
|
|
format: Option<IoFormat>,
|
|
|
|
|
) -> Result<Vec<WifiListOut>, Error> {
|
|
|
|
|
let wpa_supplicant = ctx.wifi_manager.read().await;
|
|
|
|
|
let (wifi_list, network_list) = tokio::join!(
|
|
|
|
|
wpa_supplicant.list_wifi_low(),
|
|
|
|
|
wpa_supplicant.list_networks_low()
|
|
|
|
|
);
|
|
|
|
|
let network_list = network_list?;
|
|
|
|
|
let mut wifi_list: Vec<WifiListOut> = wifi_list?
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter(|(ssid, _)| !network_list.contains_key(ssid))
|
|
|
|
|
.map(|(ssid, info)| WifiListOut {
|
|
|
|
|
ssid,
|
|
|
|
|
strength: info.strength,
|
|
|
|
|
security: info.security,
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
wifi_list.sort_by_key(|x| x.strength);
|
|
|
|
|
wifi_list.reverse();
|
|
|
|
|
Ok(wifi_list)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[command(rename = "set", display(display_none))]
|
|
|
|
|
pub async fn set_country(
|
|
|
|
|
#[context] ctx: RpcContext,
|
|
|
|
|
#[arg(parse(country_code_parse))] country: CountryCode,
|
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
if !interface_connected("eth0").await? {
|
|
|
|
|
return Err(Error::new(
|
|
|
|
|
color_eyre::eyre::eyre!("Won't change country without hardwire connection"),
|
|
|
|
|
crate::ErrorKind::Wifi,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
let mut wpa_supplicant = ctx.wifi_manager.write().await;
|
|
|
|
|
wpa_supplicant.set_country_low(country.alpha2()).await
|
|
|
|
|
wpa_supplicant.set_country_low(country.alpha2()).await?;
|
|
|
|
|
for (_ssid, network_id) in wpa_supplicant.list_networks_low().await? {
|
|
|
|
|
wpa_supplicant.remove_network_low(network_id).await?;
|
|
|
|
|
}
|
|
|
|
|
wpa_supplicant.remove_all_connections().await?;
|
|
|
|
|
|
|
|
|
|
wpa_supplicant.save_config(&mut ctx.db.handle()).await?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct WpaCli {
|
|
|
|
|
datadir: PathBuf,
|
|
|
|
|
interface: String,
|
|
|
|
|
}
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
|
pub struct NetworkId(String);
|
|
|
|
|
pub enum NetworkAttr {
|
|
|
|
|
Ssid(String),
|
|
|
|
|
Psk(String),
|
|
|
|
|
Priority(isize),
|
|
|
|
|
ScanSsid(bool),
|
|
|
|
|
}
|
|
|
|
|
impl NetworkAttr {
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
use NetworkAttr::*;
|
|
|
|
|
match self {
|
|
|
|
|
Ssid(_) => "ssid",
|
|
|
|
|
Psk(_) => "psk",
|
|
|
|
|
Priority(_) => "priority",
|
|
|
|
|
ScanSsid(_) => "scan_ssid",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fn value(&self) -> String {
|
|
|
|
|
use NetworkAttr::*;
|
|
|
|
|
match self {
|
|
|
|
|
Ssid(s) => format!("\"{}\"", s),
|
|
|
|
|
Psk(s) => format!("\"{}\"", s),
|
|
|
|
|
Priority(n) => format!("{}", n),
|
|
|
|
|
ScanSsid(b) => {
|
|
|
|
|
if *b {
|
|
|
|
|
String::from("1")
|
|
|
|
|
} else {
|
|
|
|
|
String::from("0")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Ssid are the names of the wifis, usually human readable.
|
|
|
|
|
#[derive(
|
|
|
|
|
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
|
|
|
|
)]
|
|
|
|
|
pub struct Ssid(String);
|
|
|
|
|
|
|
|
|
|
/// So a signal strength is a number between 0-100, I want the null option to be 0 since there is no signal
|
|
|
|
|
#[derive(
|
|
|
|
|
Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
|
|
|
|
|
)]
|
|
|
|
|
pub struct SignalStrength(u8);
|
|
|
|
|
|
|
|
|
|
impl SignalStrength {
|
|
|
|
|
fn new(size: Option<u8>) -> Self {
|
|
|
|
|
let size = match size {
|
|
|
|
|
None => return Self(0),
|
|
|
|
|
Some(x) => x,
|
|
|
|
|
};
|
|
|
|
|
if size >= 100 {
|
|
|
|
|
return Self(100);
|
|
|
|
|
}
|
|
|
|
|
Self(size)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for SignalStrength {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Psk(String);
|
|
|
|
|
impl WpaCli {
|
|
|
|
|
pub fn init(interface: String, datadir: PathBuf) -> Self {
|
|
|
|
|
WpaCli { interface, datadir }
|
|
|
|
|
pub fn init(interface: String) -> Self {
|
|
|
|
|
WpaCli { interface }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Low Level
|
|
|
|
|
pub async fn add_network_low(&mut self) -> Result<NetworkId, Error> {
|
|
|
|
|
let r = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("add_network")
|
|
|
|
|
#[instrument(skip(self, psk))]
|
|
|
|
|
pub async fn set_add_network_low(&mut self, ssid: &Ssid, psk: &Psk) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("nmcli")
|
|
|
|
|
.arg("-a")
|
|
|
|
|
.arg("-w")
|
|
|
|
|
.arg("30")
|
|
|
|
|
.arg("d")
|
|
|
|
|
.arg("wifi")
|
|
|
|
|
.arg("con")
|
|
|
|
|
.arg(&ssid.0)
|
|
|
|
|
.arg("password")
|
|
|
|
|
.arg(&psk.0)
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
let s = std::str::from_utf8(&r)?;
|
|
|
|
|
Ok(NetworkId(s.trim().to_owned()))
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
pub async fn set_network_low(
|
|
|
|
|
&mut self,
|
|
|
|
|
id: &NetworkId,
|
|
|
|
|
attr: &NetworkAttr,
|
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
#[instrument(skip(self, psk))]
|
|
|
|
|
pub async fn add_network_low(&mut self, ssid: &Ssid, psk: &Psk) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("nmcli")
|
|
|
|
|
.arg("con")
|
|
|
|
|
.arg("add")
|
|
|
|
|
.arg("con-name")
|
|
|
|
|
.arg(&ssid.0)
|
|
|
|
|
.arg("ifname")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("set_network")
|
|
|
|
|
.arg(&id.0)
|
|
|
|
|
.arg(attr.name())
|
|
|
|
|
.arg(attr.value())
|
|
|
|
|
.arg("type")
|
|
|
|
|
.arg("wifi")
|
|
|
|
|
.arg("ssid")
|
|
|
|
|
.arg(&ssid.0)
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
let _ = Command::new("nmcli")
|
|
|
|
|
.arg("con")
|
|
|
|
|
.arg("modify")
|
|
|
|
|
.arg(&ssid.0)
|
|
|
|
|
.arg("wifi-sec.key-mgmt")
|
|
|
|
|
.arg("wpa-psk")
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
let _ = Command::new("nmcli")
|
|
|
|
|
.arg("con")
|
|
|
|
|
.arg("modify")
|
|
|
|
|
.arg(&ssid.0)
|
|
|
|
|
.arg("wifi-sec.psk")
|
|
|
|
|
.arg(&psk.0)
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
pub async fn set_country_low(&mut self, country_code: &str) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
let _ = Command::new("iw")
|
|
|
|
|
.arg("reg")
|
|
|
|
|
.arg("set")
|
|
|
|
|
.arg("country")
|
|
|
|
|
.arg(country_code)
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
pub async fn get_country_low(&self) -> Result<CountryCode, Error> {
|
|
|
|
|
let r = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
let r = Command::new("iw")
|
|
|
|
|
.arg("reg")
|
|
|
|
|
.arg("get")
|
|
|
|
|
.arg("country")
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(CountryCode::for_alpha2(&String::from_utf8(r)?).unwrap())
|
|
|
|
|
}
|
|
|
|
|
pub async fn enable_network_low(&mut self, id: &NetworkId) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("enable_network")
|
|
|
|
|
.arg(&id.0)
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
pub async fn save_config_low(&mut self) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("save_config")
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
let r = String::from_utf8(r)?;
|
|
|
|
|
lazy_static! {
|
|
|
|
|
static ref RE: Regex = Regex::new("country (\\w+):").unwrap();
|
|
|
|
|
}
|
|
|
|
|
let first_country = r
|
|
|
|
|
.lines()
|
|
|
|
|
.filter(|s| s.contains("country"))
|
|
|
|
|
.next()
|
|
|
|
|
.ok_or_else(|| {
|
|
|
|
|
Error::new(
|
|
|
|
|
color_eyre::eyre::eyre!("Could not find a country config lines"),
|
|
|
|
|
ErrorKind::Wifi,
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
let country = &RE.captures(first_country).ok_or_else(|| {
|
|
|
|
|
Error::new(
|
|
|
|
|
color_eyre::eyre::eyre!("Could not find a country config with regex"),
|
|
|
|
|
ErrorKind::Wifi,
|
|
|
|
|
)
|
|
|
|
|
})?[1];
|
|
|
|
|
Ok(CountryCode::for_alpha2(country).or(Err(Error::new(
|
|
|
|
|
color_eyre::eyre::eyre!("Invalid Country Code: {}", country),
|
|
|
|
|
ErrorKind::Wifi,
|
|
|
|
|
)))?)
|
|
|
|
|
}
|
|
|
|
|
pub async fn remove_network_low(&mut self, id: NetworkId) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("remove_network")
|
|
|
|
|
let _ = Command::new("nmcli")
|
|
|
|
|
.arg("c")
|
|
|
|
|
.arg("del")
|
|
|
|
|
.arg(&id.0)
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
pub async fn reconfigure_low(&mut self) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("reconfigure")
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
#[instrument]
|
|
|
|
|
pub async fn list_networks_low(&self) -> Result<BTreeMap<String, NetworkId>, Error> {
|
|
|
|
|
let r = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("list_networks")
|
|
|
|
|
pub async fn list_networks_low(&self) -> Result<BTreeMap<Ssid, NetworkId>, Error> {
|
|
|
|
|
let r = Command::new("nmcli")
|
|
|
|
|
.arg("-t")
|
|
|
|
|
.arg("c")
|
|
|
|
|
.arg("show")
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(String::from_utf8(r)?
|
|
|
|
|
.lines()
|
|
|
|
|
.skip(1)
|
|
|
|
|
.filter_map(|l| {
|
|
|
|
|
let mut cs = l.split("\t");
|
|
|
|
|
let nid = NetworkId(cs.next()?.to_owned());
|
|
|
|
|
let ssid = cs.next()?.to_owned();
|
|
|
|
|
Some((ssid, nid))
|
|
|
|
|
let mut cs = l.split(":");
|
|
|
|
|
let name = Ssid(cs.next()?.to_owned());
|
|
|
|
|
let uuid = NetworkId(cs.next()?.to_owned());
|
|
|
|
|
let _connection_type = cs.next()?;
|
|
|
|
|
let _device = cs.next()?;
|
|
|
|
|
Some((name, uuid))
|
|
|
|
|
})
|
|
|
|
|
.collect::<BTreeMap<String, NetworkId>>())
|
|
|
|
|
}
|
|
|
|
|
pub async fn select_network_low(&mut self, id: &NetworkId) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("select_network")
|
|
|
|
|
.arg(&id.0)
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
pub async fn new_password_low(&mut self, id: &NetworkId, pass: &str) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("new_password")
|
|
|
|
|
.arg(&id.0)
|
|
|
|
|
.arg(pass)
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
#[instrument]
|
|
|
|
|
pub async fn signal_poll_low(&self) -> Result<Option<isize>, Error> {
|
|
|
|
|
let r = Command::new("wpa_cli")
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("signal_poll")
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
let e = || {
|
|
|
|
|
Error::new(
|
|
|
|
|
color_eyre::eyre::eyre!("Invalid output from wpa_cli signal_poll"),
|
|
|
|
|
ErrorKind::Wifi,
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let output = String::from_utf8(r)?;
|
|
|
|
|
Ok(if output.trim() == "FAIL" {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
let l = output.lines().next().ok_or_else(e)?;
|
|
|
|
|
let rssi = l.split("=").nth(1).ok_or_else(e)?.parse()?;
|
|
|
|
|
Some(rssi)
|
|
|
|
|
})
|
|
|
|
|
.collect::<BTreeMap<Ssid, NetworkId>>())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// High Level
|
|
|
|
|
pub async fn save_config(&mut self) -> Result<(), Error> {
|
|
|
|
|
self.save_config_low().await?;
|
|
|
|
|
tokio::fs::copy(
|
|
|
|
|
"/etc/wpa_supplicant.conf",
|
|
|
|
|
self.datadir.join("wpa_supplicant.conf"),
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
|
|
|
|
#[instrument]
|
|
|
|
|
pub async fn list_wifi_low(&self) -> Result<WifiList, Error> {
|
|
|
|
|
let r = Command::new("nmcli")
|
|
|
|
|
.arg("-g")
|
|
|
|
|
.arg("SSID,SIGNAL,security")
|
|
|
|
|
.arg("d")
|
|
|
|
|
.arg("wifi")
|
|
|
|
|
.arg("list")
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(String::from_utf8(r)?
|
|
|
|
|
.lines()
|
|
|
|
|
.filter_map(|l| {
|
|
|
|
|
let mut values = l.split(":");
|
|
|
|
|
let ssid = Ssid(values.next()?.to_owned());
|
|
|
|
|
let signal = SignalStrength::new(std::str::FromStr::from_str(values.next()?).ok());
|
|
|
|
|
let security: Vec<String> =
|
|
|
|
|
values.next()?.split(" ").map(|x| x.to_owned()).collect();
|
|
|
|
|
Some((
|
|
|
|
|
ssid,
|
|
|
|
|
WifiListInfo {
|
|
|
|
|
strength: signal,
|
|
|
|
|
security,
|
|
|
|
|
},
|
|
|
|
|
))
|
|
|
|
|
})
|
|
|
|
|
.collect::<WifiList>())
|
|
|
|
|
}
|
|
|
|
|
pub async fn select_network_low(&mut self, id: &NetworkId) -> Result<(), Error> {
|
|
|
|
|
let _ = Command::new("nmcli")
|
|
|
|
|
.arg("c")
|
|
|
|
|
.arg("up")
|
|
|
|
|
.arg(&id.0)
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
pub async fn check_network(&self, ssid: &str) -> Result<Option<NetworkId>, Error> {
|
|
|
|
|
pub async fn remove_all_connections(&mut self) -> Result<(), Error> {
|
|
|
|
|
let location_connections = Path::new("/etc/NetworkManager/system-connections");
|
|
|
|
|
let mut connections = tokio::fs::read_dir(&location_connections).await?;
|
|
|
|
|
while let Some(connection) = connections.next_entry().await? {
|
|
|
|
|
let path = connection.path();
|
|
|
|
|
if path.is_file() {
|
|
|
|
|
let _ = tokio::fs::remove_file(&path).await?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
pub async fn save_config(&mut self, mut db: impl DbHandle) -> Result<(), Error> {
|
|
|
|
|
crate::db::DatabaseModel::new()
|
|
|
|
|
.server_info()
|
|
|
|
|
.last_wifi_region()
|
|
|
|
|
.put(&mut db, &Some(self.get_country_low().await?))
|
|
|
|
|
.await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
pub async fn check_network(&self, ssid: &Ssid) -> Result<Option<NetworkId>, Error> {
|
|
|
|
|
Ok(self.list_networks_low().await?.remove(ssid))
|
|
|
|
|
}
|
|
|
|
|
#[instrument]
|
|
|
|
|
pub async fn select_network(&mut self, ssid: &str) -> Result<bool, Error> {
|
|
|
|
|
#[instrument(skip(db))]
|
|
|
|
|
pub async fn select_network(&mut self, db: impl DbHandle, ssid: &Ssid) -> Result<bool, Error> {
|
|
|
|
|
let m_id = self.check_network(ssid).await?;
|
|
|
|
|
match m_id {
|
|
|
|
|
None => Err(Error::new(
|
|
|
|
|
@@ -493,14 +614,14 @@ impl WpaCli {
|
|
|
|
|
)),
|
|
|
|
|
Some(x) => {
|
|
|
|
|
self.select_network_low(&x).await?;
|
|
|
|
|
self.save_config().await?;
|
|
|
|
|
self.save_config(db).await?;
|
|
|
|
|
let connect = async {
|
|
|
|
|
let mut current;
|
|
|
|
|
loop {
|
|
|
|
|
current = self.get_current_network().await;
|
|
|
|
|
match ¤t {
|
|
|
|
|
Ok(Some(ssid)) => {
|
|
|
|
|
tracing::debug!("Connected to: {}", ssid);
|
|
|
|
|
tracing::debug!("Connected to: {}", ssid.0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
@@ -517,13 +638,13 @@ impl WpaCli {
|
|
|
|
|
tracing::debug!("{:?}", res);
|
|
|
|
|
Ok(match res {
|
|
|
|
|
None => false,
|
|
|
|
|
Some(net) => net == ssid,
|
|
|
|
|
Some(net) => &net == ssid,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[instrument]
|
|
|
|
|
pub async fn get_current_network(&self) -> Result<Option<String>, Error> {
|
|
|
|
|
pub async fn get_current_network(&self) -> Result<Option<Ssid>, Error> {
|
|
|
|
|
let r = Command::new("iwgetid")
|
|
|
|
|
.arg(&self.interface)
|
|
|
|
|
.arg("--raw")
|
|
|
|
|
@@ -535,45 +656,42 @@ impl WpaCli {
|
|
|
|
|
if network.is_empty() {
|
|
|
|
|
Ok(None)
|
|
|
|
|
} else {
|
|
|
|
|
Ok(Some(network.to_owned()))
|
|
|
|
|
Ok(Some(Ssid(network.to_owned())))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[instrument]
|
|
|
|
|
pub async fn remove_network(&mut self, ssid: &str) -> Result<bool, Error> {
|
|
|
|
|
#[instrument(skip(db))]
|
|
|
|
|
pub async fn remove_network(&mut self, db: impl DbHandle, ssid: &Ssid) -> Result<bool, Error> {
|
|
|
|
|
match self.check_network(ssid).await? {
|
|
|
|
|
None => Ok(false),
|
|
|
|
|
Some(x) => {
|
|
|
|
|
self.remove_network_low(x).await?;
|
|
|
|
|
self.save_config().await?;
|
|
|
|
|
self.reconfigure_low().await?;
|
|
|
|
|
self.save_config(db).await?;
|
|
|
|
|
Ok(true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[instrument]
|
|
|
|
|
pub async fn add_network(
|
|
|
|
|
#[instrument(skip(psk, db))]
|
|
|
|
|
pub async fn set_add_network(
|
|
|
|
|
&mut self,
|
|
|
|
|
ssid: &str,
|
|
|
|
|
psk: &str,
|
|
|
|
|
db: impl DbHandle,
|
|
|
|
|
ssid: &Ssid,
|
|
|
|
|
psk: &Psk,
|
|
|
|
|
priority: isize,
|
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
use NetworkAttr::*;
|
|
|
|
|
let nid = match self.check_network(ssid).await? {
|
|
|
|
|
None => {
|
|
|
|
|
let nid = self.add_network_low().await?;
|
|
|
|
|
self.set_network_low(&nid, &Ssid(ssid.to_owned())).await?;
|
|
|
|
|
self.set_network_low(&nid, &Psk(psk.to_owned())).await?;
|
|
|
|
|
self.set_network_low(&nid, &Priority(priority)).await?;
|
|
|
|
|
self.set_network_low(&nid, &ScanSsid(true)).await?;
|
|
|
|
|
Result::<NetworkId, Error>::Ok(nid)
|
|
|
|
|
}
|
|
|
|
|
Some(nid) => {
|
|
|
|
|
self.new_password_low(&nid, psk).await?;
|
|
|
|
|
Ok(nid)
|
|
|
|
|
}
|
|
|
|
|
}?;
|
|
|
|
|
self.enable_network_low(&nid).await?;
|
|
|
|
|
self.save_config().await?;
|
|
|
|
|
self.set_add_network_low(&ssid, &psk).await?;
|
|
|
|
|
self.save_config(db).await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
#[instrument(skip(psk, db))]
|
|
|
|
|
pub async fn add_network(
|
|
|
|
|
&mut self,
|
|
|
|
|
db: impl DbHandle,
|
|
|
|
|
ssid: &Ssid,
|
|
|
|
|
psk: &Psk,
|
|
|
|
|
priority: isize,
|
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
self.add_network_low(&ssid, &psk).await?;
|
|
|
|
|
self.save_config(db).await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -599,21 +717,26 @@ pub fn country_code_parse(code: &str, _matches: &ArgMatches<'_>) -> Result<Count
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[instrument(skip(main_datadir))]
|
|
|
|
|
pub async fn synchronize_wpa_supplicant_conf<P: AsRef<Path>>(main_datadir: P) -> Result<(), Error> {
|
|
|
|
|
let persistent = main_datadir.as_ref().join("wpa_supplicant.conf");
|
|
|
|
|
pub async fn synchronize_wpa_supplicant_conf<P: AsRef<Path>>(
|
|
|
|
|
main_datadir: P,
|
|
|
|
|
last_country_code: &Option<CountryCode>,
|
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
let persistent = main_datadir.as_ref().join("system-connections");
|
|
|
|
|
tracing::debug!("persistent: {:?}", persistent);
|
|
|
|
|
let supplicant = Path::new("/etc/wpa_supplicant.conf");
|
|
|
|
|
|
|
|
|
|
if tokio::fs::metadata(&persistent).await.is_err() {
|
|
|
|
|
tokio::fs::write(&persistent, include_str!("wpa_supplicant.conf.base")).await?;
|
|
|
|
|
tokio::fs::create_dir_all(&persistent).await?;
|
|
|
|
|
}
|
|
|
|
|
let volatile = Path::new("/etc/wpa_supplicant.conf");
|
|
|
|
|
tracing::debug!("link: {:?}", volatile);
|
|
|
|
|
if tokio::fs::metadata(&volatile).await.is_ok() {
|
|
|
|
|
tokio::fs::remove_file(&volatile).await?
|
|
|
|
|
crate::disk::mount::util::bind(&persistent, "/etc/NetworkManager/system-connections", false)
|
|
|
|
|
.await?;
|
|
|
|
|
if tokio::fs::metadata(&supplicant).await.is_err() {
|
|
|
|
|
tokio::fs::write(&supplicant, include_str!("wpa_supplicant.conf.base")).await?;
|
|
|
|
|
}
|
|
|
|
|
tokio::fs::copy(&persistent, volatile).await?;
|
|
|
|
|
|
|
|
|
|
Command::new("systemctl")
|
|
|
|
|
.arg("restart")
|
|
|
|
|
.arg("wpa_supplicant")
|
|
|
|
|
.arg("NetworkManager")
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Command::new("ifconfig")
|
|
|
|
|
@@ -622,5 +745,14 @@ pub async fn synchronize_wpa_supplicant_conf<P: AsRef<Path>>(main_datadir: P) ->
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
Command::new("dhclient").invoke(ErrorKind::Wifi).await?;
|
|
|
|
|
if let Some(last_country_code) = last_country_code {
|
|
|
|
|
tracing::info!("Setting the region");
|
|
|
|
|
let _ = Command::new("iw")
|
|
|
|
|
.arg("reg")
|
|
|
|
|
.arg("set")
|
|
|
|
|
.arg(last_country_code.alpha2())
|
|
|
|
|
.invoke(ErrorKind::Wifi)
|
|
|
|
|
.await?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|