Bugfix/wifi init (#619)

* initializes wifi system correctly

* add packages to initialization

* restart wpa_supplicant after conf file is sync'ed

* fix cli rendering for set_network

* debug statements

* more debug stuff, trim network id on return

* possibly solves all dhcp problems on startup, I wish I understood better why it starts by default for eth0 but not wlan0

* make consistent with new logging
This commit is contained in:
Keagan McClelland
2021-10-11 14:51:42 -06:00
committed by Aiden McClelland
parent debefdbc5f
commit 3f30905786
4 changed files with 102 additions and 66 deletions

View File

@@ -149,6 +149,7 @@ async fn init(cfg_path: Option<&str>) -> Result<(), Error> {
} }
tracing::info!("Enabled nginx public dir"); tracing::info!("Enabled nginx public dir");
embassy::net::wifi::synchronize_wpa_supplicant_conf(&cfg.datadir().join("main")).await?; embassy::net::wifi::synchronize_wpa_supplicant_conf(&cfg.datadir().join("main")).await?;
tracing::info!("Synchronized wpa_supplicant.conf");
let db = cfg.db(&secret_store).await?; let db = cfg.db(&secret_store).await?;
let mut handle = db.handle(); let mut handle = db.handle();

View File

@@ -1,5 +1,5 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::path::Path; use std::path::{Path, PathBuf};
use std::time::Duration; use std::time::Duration;
use clap::ArgMatches; use clap::ArgMatches;
@@ -7,6 +7,7 @@ use isocountry::CountryCode;
use rpc_toolkit::command; use rpc_toolkit::command;
use tokio::process::Command; use tokio::process::Command;
use crate::context::RpcContext;
use crate::util::{display_none, display_serializable, Invoke, IoFormat}; use crate::util::{display_none, display_serializable, Invoke, IoFormat};
use crate::{Error, ErrorKind}; use crate::{Error, ErrorKind};
@@ -17,12 +18,13 @@ pub async fn wifi() -> Result<(), Error> {
#[command(display(display_none))] #[command(display(display_none))]
pub async fn add( pub async fn add(
#[context] ctx: RpcContext,
#[arg] ssid: String, #[arg] ssid: String,
#[arg] password: String, #[arg] password: String,
#[arg] priority: isize, #[arg] priority: isize,
#[arg] connect: bool, #[arg] connect: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
let wpa_supplicant = WpaCli { interface: "wlan0" }; // TODO: pull from config let wpa_supplicant = WpaCli::init("wlan0".to_string(), ctx.datadir.join("main")); // TODO: pull from config
if !ssid.is_ascii() { if !ssid.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("SSID may not have special characters"), color_eyre::eyre::eyre!("SSID may not have special characters"),
@@ -35,8 +37,8 @@ pub async fn add(
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
async fn add_procedure<'a>( async fn add_procedure(
wpa_supplicant: WpaCli<'a>, wpa_supplicant: WpaCli,
ssid: &str, ssid: &str,
password: &str, password: &str,
priority: isize, priority: isize,
@@ -72,14 +74,14 @@ pub async fn add(
} }
#[command(display(display_none))] #[command(display(display_none))]
pub async fn connect(#[arg] ssid: String) -> Result<(), Error> { pub async fn connect(#[context] ctx: RpcContext, #[arg] ssid: String) -> Result<(), Error> {
if !ssid.is_ascii() { if !ssid.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("SSID may not have special characters"), color_eyre::eyre::eyre!("SSID may not have special characters"),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
async fn connect_procedure<'a>(wpa_supplicant: WpaCli<'a>, ssid: &String) -> Result<(), Error> { async fn connect_procedure(wpa_supplicant: WpaCli, ssid: &String) -> Result<(), Error> {
let current = wpa_supplicant.get_current_network().await?; let current = wpa_supplicant.get_current_network().await?;
let connected = wpa_supplicant.select_network(&ssid).await?; let connected = wpa_supplicant.select_network(&ssid).await?;
if connected { if connected {
@@ -97,7 +99,7 @@ pub async fn connect(#[arg] ssid: String) -> Result<(), Error> {
} }
Ok(()) Ok(())
} }
let wpa_supplicant = WpaCli { interface: "wlan0" }; let wpa_supplicant = WpaCli::init("wlan0".to_string(), ctx.datadir.join("main"));
tokio::spawn(async move { tokio::spawn(async move {
match connect_procedure(wpa_supplicant, &ssid).await { match connect_procedure(wpa_supplicant, &ssid).await {
Err(e) => { Err(e) => {
@@ -110,14 +112,14 @@ pub async fn connect(#[arg] ssid: String) -> Result<(), Error> {
} }
#[command(display(display_none))] #[command(display(display_none))]
pub async fn delete(#[arg] ssid: String) -> Result<(), Error> { pub async fn delete(#[context] ctx: RpcContext, #[arg] ssid: String) -> Result<(), Error> {
if !ssid.is_ascii() { if !ssid.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("SSID may not have special characters"), color_eyre::eyre::eyre!("SSID may not have special characters"),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
let wpa_supplicant = WpaCli { interface: "wlan0" }; let wpa_supplicant = WpaCli::init("wlan0".to_string(), ctx.datadir.join("main"));
let current = wpa_supplicant.get_current_network().await?; let current = wpa_supplicant.get_current_network().await?;
match current { match current {
None => { None => {
@@ -194,11 +196,12 @@ fn display_wifi_info(info: WiFiInfo, matches: &ArgMatches<'_>) {
#[command(display(display_wifi_info))] #[command(display(display_wifi_info))]
pub async fn get( pub async fn get(
#[context] ctx: RpcContext,
#[allow(unused_variables)] #[allow(unused_variables)]
#[arg(long = "format")] #[arg(long = "format")]
format: Option<IoFormat>, format: Option<IoFormat>,
) -> Result<WiFiInfo, Error> { ) -> Result<WiFiInfo, Error> {
let wpa_supplicant = WpaCli { interface: "wlan0" }; let wpa_supplicant = WpaCli::init("wlan0".to_string(), ctx.datadir.join("main"));
let ssids_task = async { let ssids_task = async {
Result::<Vec<String>, Error>::Ok( Result::<Vec<String>, Error>::Ok(
wpa_supplicant wpa_supplicant
@@ -237,14 +240,16 @@ pub async fn get(
#[command(display(display_none))] #[command(display(display_none))]
pub async fn set_country( pub async fn set_country(
#[context] ctx: RpcContext,
#[arg(parse(country_code_parse))] country: CountryCode, #[arg(parse(country_code_parse))] country: CountryCode,
) -> Result<(), Error> { ) -> Result<(), Error> {
let wpa_supplicant = WpaCli { interface: "wlan0" }; let wpa_supplicant = WpaCli::init("wlan0".to_string(), ctx.datadir.join("main"));
wpa_supplicant.set_country_low(country.alpha2()).await wpa_supplicant.set_country_low(country.alpha2()).await
} }
pub struct WpaCli<'a> { pub struct WpaCli {
interface: &'a str, datadir: PathBuf,
interface: String,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct NetworkId(String); pub struct NetworkId(String);
@@ -254,29 +259,42 @@ pub enum NetworkAttr {
Priority(isize), Priority(isize),
ScanSsid(bool), ScanSsid(bool),
} }
impl std::fmt::Display for NetworkAttr { impl NetworkAttr {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn name(&self) -> &'static str {
use NetworkAttr::*; use NetworkAttr::*;
match self { match self {
Ssid(s) => write!(f, "\"{}\"", s), Ssid(_) => "ssid",
Psk(s) => write!(f, "\"{}\"", s), Psk(_) => "psk",
Priority(n) => write!(f, "{}", n), 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) => { ScanSsid(b) => {
if *b { if *b {
write!(f, "1") String::from("1")
} else { } else {
write!(f, "0") String::from("0")
} }
} }
} }
} }
} }
impl<'a> WpaCli<'a> { impl WpaCli {
pub fn init(interface: String, datadir: PathBuf) -> Self {
WpaCli { interface, datadir }
}
// Low Level // Low Level
pub async fn add_network_low(&self) -> Result<NetworkId, Error> { pub async fn add_network_low(&self) -> Result<NetworkId, Error> {
let r = Command::new("wpa_cli") let r = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("add_network") .arg("add_network")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
@@ -286,10 +304,11 @@ impl<'a> WpaCli<'a> {
pub async fn set_network_low(&self, id: &NetworkId, attr: &NetworkAttr) -> Result<(), Error> { pub async fn set_network_low(&self, id: &NetworkId, attr: &NetworkAttr) -> Result<(), Error> {
let _ = Command::new("wpa_cli") let _ = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("set_network") .arg("set_network")
.arg(&id.0) .arg(&id.0)
.arg(format!("{}", attr)) .arg(attr.name())
.arg(attr.value())
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
Ok(()) Ok(())
@@ -297,7 +316,7 @@ impl<'a> WpaCli<'a> {
pub async fn set_country_low(&self, country_code: &str) -> Result<(), Error> { pub async fn set_country_low(&self, country_code: &str) -> Result<(), Error> {
let _ = Command::new("wpa_cli") let _ = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("set") .arg("set")
.arg("country") .arg("country")
.arg(country_code) .arg(country_code)
@@ -308,7 +327,7 @@ impl<'a> WpaCli<'a> {
pub async fn get_country_low(&self) -> Result<CountryCode, Error> { pub async fn get_country_low(&self) -> Result<CountryCode, Error> {
let r = Command::new("wpa_cli") let r = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("get") .arg("get")
.arg("country") .arg("country")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
@@ -318,7 +337,7 @@ impl<'a> WpaCli<'a> {
pub async fn enable_network_low(&self, id: &NetworkId) -> Result<(), Error> { pub async fn enable_network_low(&self, id: &NetworkId) -> Result<(), Error> {
let _ = Command::new("wpa_cli") let _ = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("enable_network") .arg("enable_network")
.arg(&id.0) .arg(&id.0)
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
@@ -328,7 +347,7 @@ impl<'a> WpaCli<'a> {
pub async fn save_config_low(&self) -> Result<(), Error> { pub async fn save_config_low(&self) -> Result<(), Error> {
let _ = Command::new("wpa_cli") let _ = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("save_config") .arg("save_config")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
@@ -337,7 +356,7 @@ impl<'a> WpaCli<'a> {
pub async fn remove_network_low(&self, id: NetworkId) -> Result<(), Error> { pub async fn remove_network_low(&self, id: NetworkId) -> Result<(), Error> {
let _ = Command::new("wpa_cli") let _ = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("remove_network") .arg("remove_network")
.arg(&id.0) .arg(&id.0)
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
@@ -347,7 +366,7 @@ impl<'a> WpaCli<'a> {
pub async fn reconfigure_low(&self) -> Result<(), Error> { pub async fn reconfigure_low(&self) -> Result<(), Error> {
let _ = Command::new("wpa_cli") let _ = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("reconfigure") .arg("reconfigure")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
@@ -356,7 +375,7 @@ impl<'a> WpaCli<'a> {
pub async fn list_networks_low(&self) -> Result<BTreeMap<String, NetworkId>, Error> { pub async fn list_networks_low(&self) -> Result<BTreeMap<String, NetworkId>, Error> {
let r = Command::new("wpa_cli") let r = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("list_networks") .arg("list_networks")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
@@ -374,7 +393,7 @@ impl<'a> WpaCli<'a> {
pub async fn select_network_low(&self, id: &NetworkId) -> Result<(), Error> { pub async fn select_network_low(&self, id: &NetworkId) -> Result<(), Error> {
let _ = Command::new("wpa_cli") let _ = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("select_network") .arg("select_network")
.arg(&id.0) .arg(&id.0)
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
@@ -384,7 +403,7 @@ impl<'a> WpaCli<'a> {
pub async fn new_password_low(&self, id: &NetworkId, pass: &str) -> Result<(), Error> { pub async fn new_password_low(&self, id: &NetworkId, pass: &str) -> Result<(), Error> {
let _ = Command::new("wpa_cli") let _ = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("new_password") .arg("new_password")
.arg(&id.0) .arg(&id.0)
.arg(pass) .arg(pass)
@@ -395,7 +414,7 @@ impl<'a> WpaCli<'a> {
pub async fn signal_poll_low(&self) -> Result<Option<isize>, Error> { pub async fn signal_poll_low(&self) -> Result<Option<isize>, Error> {
let r = Command::new("wpa_cli") let r = Command::new("wpa_cli")
.arg("-i") .arg("-i")
.arg(self.interface) .arg(&self.interface)
.arg("signal_poll") .arg("signal_poll")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
@@ -416,6 +435,15 @@ impl<'a> WpaCli<'a> {
} }
// High Level // High Level
pub async fn save_config(&self) -> Result<(), Error> {
self.save_config_low().await?;
tokio::fs::copy(
"/etc/wpa_supplicant.conf",
self.datadir.join("wpa_supplicant.conf"),
)
.await?;
Ok(())
}
pub async fn check_network(&self, ssid: &str) -> Result<Option<NetworkId>, Error> { pub async fn check_network(&self, ssid: &str) -> Result<Option<NetworkId>, Error> {
Ok(self.list_networks_low().await?.remove(ssid)) Ok(self.list_networks_low().await?.remove(ssid))
} }
@@ -428,17 +456,20 @@ impl<'a> WpaCli<'a> {
)), )),
Some(x) => { Some(x) => {
self.select_network_low(&x).await?; self.select_network_low(&x).await?;
self.save_config_low().await?; self.save_config().await?;
let connect = async { let connect = async {
let mut current; let mut current;
loop { loop {
current = self.get_current_network().await; current = self.get_current_network().await;
match &current { match &current {
Ok(Some(_)) => { Ok(Some(ssid)) => {
tracing::debug!("Connected to: {}", ssid);
break; break;
} }
_ => {} _ => {}
} }
tokio::time::sleep(Duration::from_millis(500)).await;
tracing::debug!("Retrying...");
} }
current current
}; };
@@ -446,6 +477,7 @@ impl<'a> WpaCli<'a> {
Err(_) => None, Err(_) => None,
Ok(net) => net?, Ok(net) => net?,
}; };
tracing::debug!("{:?}", res);
Ok(match res { Ok(match res {
None => false, None => false,
Some(net) => net == ssid, Some(net) => net == ssid,
@@ -455,15 +487,17 @@ impl<'a> WpaCli<'a> {
} }
pub async fn get_current_network(&self) -> Result<Option<String>, Error> { pub async fn get_current_network(&self) -> Result<Option<String>, Error> {
let r = Command::new("iwgetid") let r = Command::new("iwgetid")
.arg(self.interface) .arg(&self.interface)
.arg("--raw") .arg("--raw")
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
let output = String::from_utf8(r)?; let output = String::from_utf8(r)?;
if output.trim().is_empty() { let network = output.trim();
tracing::debug!("Current Network: \"{}\"", network);
if network.is_empty() {
Ok(None) Ok(None)
} else { } else {
Ok(Some(output)) Ok(Some(network.to_owned()))
} }
} }
pub async fn remove_network(&self, ssid: &str) -> Result<bool, Error> { pub async fn remove_network(&self, ssid: &str) -> Result<bool, Error> {
@@ -471,7 +505,7 @@ impl<'a> WpaCli<'a> {
None => Ok(false), None => Ok(false),
Some(x) => { Some(x) => {
self.remove_network_low(x).await?; self.remove_network_low(x).await?;
self.save_config_low().await?; self.save_config().await?;
self.reconfigure_low().await?; self.reconfigure_low().await?;
Ok(true) Ok(true)
} }
@@ -494,7 +528,7 @@ impl<'a> WpaCli<'a> {
} }
}?; }?;
self.enable_network_low(&nid).await?; self.enable_network_low(&nid).await?;
self.save_config_low().await?; self.save_config().await?;
Ok(()) Ok(())
} }
} }
@@ -519,30 +553,25 @@ pub fn country_code_parse(code: &str, _matches: &ArgMatches<'_>) -> Result<Count
} }
pub async fn synchronize_wpa_supplicant_conf<P: AsRef<Path>>(main_datadir: P) -> Result<(), Error> { pub async fn synchronize_wpa_supplicant_conf<P: AsRef<Path>>(main_datadir: P) -> Result<(), Error> {
let target = main_datadir.as_ref().join("wpa_supplicant.conf"); let persistent = main_datadir.as_ref().join("wpa_supplicant.conf");
if tokio::fs::metadata(&target).await.is_err() { tracing::debug!("persistent: {:?}", persistent);
tokio::fs::write(&target, include_str!("wpa_supplicant.conf.base")).await?; if tokio::fs::metadata(&persistent).await.is_err() {
tokio::fs::write(&persistent, include_str!("wpa_supplicant.conf.base")).await?;
} }
let link = Path::new("/etc/wpa_supplicant.conf"); let volatile = Path::new("/etc/wpa_supplicant.conf");
if let Ok(meta) = tokio::fs::symlink_metadata(&link).await { tracing::debug!("link: {:?}", volatile);
if meta.file_type().is_symlink() { if tokio::fs::metadata(&volatile).await.is_ok() {
tokio::fs::remove_file(&link).await? tokio::fs::remove_file(&volatile).await?
}
} }
tokio::fs::symlink(&target, link).await?; tokio::fs::copy(&persistent, volatile).await?;
Command::new("systemctl")
.arg("restart")
.arg("wpa_supplicant")
.invoke(ErrorKind::Wifi)
.await?;
Command::new("ifup")
.arg("wlan0")
.invoke(ErrorKind::Wifi)
.await?;
Ok(()) Ok(())
} }
#[tokio::test]
pub async fn test_interface_connected() {
println!("{}", interface_connected("wlp5s0").await.unwrap());
println!("{}", interface_connected("enp4s0f1").await.unwrap());
}
#[tokio::test]
pub async fn test_signal_strength() {
let wpa = WpaCli {
interface: "wlp5s0",
};
println!("{}", wpa.signal_poll_low().await.unwrap().unwrap())
}

View File

@@ -1,3 +1,3 @@
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev ctrl_interface=DIR=/run/wpa_supplicant GROUP=netdev
update_config=1 update_config=1
country=US country=US

View File

@@ -13,11 +13,17 @@ apt install -y \
bmon \ bmon \
zfsutils-linux \ zfsutils-linux \
exfat-utils \ exfat-utils \
sqlite3 sqlite3 \
wireless-tools \
net-tools \
ifupdown
sed -i 's/"1"/"0"/g' /etc/apt/apt.conf.d/20auto-upgrades sed -i 's/"1"/"0"/g' /etc/apt/apt.conf.d/20auto-upgrades
sed -i 's/Restart=on-failure/Restart=always/g' /lib/systemd/system/tor@default.service sed -i 's/Restart=on-failure/Restart=always/g' /lib/systemd/system/tor@default.service
sed -i '/}/i \ \ \ \ application\/wasm \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ wasm;' /etc/nginx/mime.types sed -i '/}/i \ \ \ \ application\/wasm \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ wasm;' /etc/nginx/mime.types
sed -i 's/# server_names_hash_bucket_size 64;/server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf sed -i 's/# server_names_hash_bucket_size 64;/server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf
sed -i 's/ExecStart=\/sbin\/wpa_supplicant -u -s -O \/run\/wpa_supplicant/ExecStart=\/sbin\/wpa_supplicant -u -s -O \/run\/wpa_supplicant -c \/etc\/wpa_supplicant.conf -i wlan0/g' /lib/systemd/system/wpa_supplicant.service
echo "auto wlan0" > /etc/network/interfaces
echo "iface wlan0 inet dhcp" >> /etc/network/interfaces
mkdir -p /etc/nginx/ssl mkdir -p /etc/nginx/ssl
docker run --privileged --rm tonistiigi/binfmt --install all docker run --privileged --rm tonistiigi/binfmt --install all
docker network create -d bridge --subnet 172.18.0.1/16 start9 || true docker network create -d bridge --subnet 172.18.0.1/16 start9 || true