wifi: Properly parse all NM config files for backend

Previously there was a half-baked parser that looked for a fixed value in a
fixed place, but this was not robust. This approach is properly robust, in case
various different files set or override it.
This commit is contained in:
Vicki Pfau 2024-08-29 20:12:43 -07:00
parent 6c485684b8
commit 4c81c92586
3 changed files with 110 additions and 24 deletions

73
Cargo.lock generated
View file

@ -306,10 +306,31 @@ dependencies = [
"lazy_static", "lazy_static",
"nom", "nom",
"pathdiff", "pathdiff",
"rust-ini",
"serde", "serde",
"toml", "toml",
] ]
[[package]]
name = "const-random"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
dependencies = [
"const-random-macro",
]
[[package]]
name = "const-random-macro"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
dependencies = [
"getrandom",
"once_cell",
"tiny-keccak",
]
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.12" version = "0.2.12"
@ -325,6 +346,12 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -345,6 +372,15 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "dlv-list"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
dependencies = [
"const-random",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.13.0" version = "1.13.0"
@ -499,6 +535,12 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.5" version = "0.14.5"
@ -536,7 +578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown 0.14.5",
] ]
[[package]] [[package]]
@ -698,6 +740,16 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "ordered-multimap"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
dependencies = [
"dlv-list",
"hashbrown 0.13.2",
]
[[package]] [[package]]
name = "ordered-stream" name = "ordered-stream"
version = "0.2.0" version = "0.2.0"
@ -885,6 +937,16 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rust-ini"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
dependencies = [
"cfg-if",
"ordered-multimap",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
@ -1088,6 +1150,15 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.39.2" version = "1.39.2"

View file

@ -13,7 +13,7 @@ strip="symbols"
anyhow = "1" anyhow = "1"
async-trait = "0.1" async-trait = "0.1"
clap = { version = "4.5", default-features = false, features = ["derive", "help", "std", "usage"] } clap = { version = "4.5", default-features = false, features = ["derive", "help", "std", "usage"] }
config = { version = "0.14", default-features = false, features = ["async", "toml"] } config = { version = "0.14", default-features = false, features = ["async", "ini", "toml"] }
inotify = { version = "0.10", default-features = false, features = ["stream"] } inotify = { version = "0.10", default-features = false, features = ["stream"] }
lazy_static = "1" lazy_static = "1"
libc = "0.2" libc = "0.2"

View file

@ -6,15 +6,17 @@
*/ */
use anyhow::{anyhow, bail, ensure, Error, Result}; use anyhow::{anyhow, bail, ensure, Error, Result};
use config::builder::AsyncState;
use config::{ConfigBuilder, FileFormat};
use std::str::FromStr; use std::str::FromStr;
use strum::{Display, EnumString}; use strum::{Display, EnumString};
use tokio::fs; use tokio::fs;
use tracing::error; use tracing::error;
use zbus::Connection; use zbus::Connection;
use crate::path;
use crate::process::{run_script, script_output}; use crate::process::{run_script, script_output};
use crate::systemd::{daemon_reload, SystemdUnit}; use crate::systemd::{daemon_reload, SystemdUnit};
use crate::{path, read_config_directory};
const OVERRIDE_CONTENTS: &str = "[Service] const OVERRIDE_CONTENTS: &str = "[Service]
ExecStart= ExecStart=
@ -30,7 +32,10 @@ const TRACE_CMD_PATH: &str = "/usr/bin/trace-cmd";
const MIN_BUFFER_SIZE: u32 = 100; const MIN_BUFFER_SIZE: u32 = 100;
const WIFI_BACKEND_PATH: &str = "/etc/NetworkManager/conf.d/99-valve-wifi-backend.conf"; const WIFI_BACKEND_PATHS: &[&str] = &[
"/usr/lib/etc/NetworkManager/conf.d",
"/etc/NetworkManager/conf.d",
];
#[derive(Display, EnumString, PartialEq, Debug, Copy, Clone)] #[derive(Display, EnumString, PartialEq, Debug, Copy, Clone)]
#[strum(serialize_all = "snake_case", ascii_case_insensitive)] #[strum(serialize_all = "snake_case", ascii_case_insensitive)]
@ -209,15 +214,19 @@ pub(crate) async fn set_wifi_debug_mode(
} }
pub(crate) async fn get_wifi_backend() -> Result<WifiBackend> { pub(crate) async fn get_wifi_backend() -> Result<WifiBackend> {
let wifi_backend_contents = fs::read_to_string(path(WIFI_BACKEND_PATH)) let mut builder = ConfigBuilder::<AsyncState>::default();
.await? for dir in WIFI_BACKEND_PATHS {
.trim() println!("{dir}");
.to_string(); builder = read_config_directory(builder, path(dir), &["conf"], FileFormat::Ini).await?;
for line in wifi_backend_contents.lines() { }
if line.starts_with("wifi.backend=") { println!("{builder:?}");
let backend = line.trim_start_matches("wifi.backend=").trim(); let config = builder.build().await?;
return Ok(WifiBackend::from_str(backend)?); println!("{config:?}");
}
if let Some(backend) = config.get_table("device")?.remove("wifi.backend") {
let backend = backend.into_string()?;
println!("{backend:?}");
return Ok(WifiBackend::from_str(backend.as_str())?);
} }
bail!("Wi-Fi backend not found in config"); bail!("Wi-Fi backend not found in config");
@ -330,29 +339,35 @@ mod test {
async fn test_get_wifi_backend() { async fn test_get_wifi_backend() {
let _h = testing::start(); let _h = testing::start();
create_dir_all(path(WIFI_BACKEND_PATH).parent().unwrap()) for dir in WIFI_BACKEND_PATHS {
.await create_dir_all(path(dir)).await.expect("create_dir_all");
.expect("create_dir_all"); }
assert!(get_wifi_backend().await.is_err()); assert!(get_wifi_backend().await.is_err());
write(path(WIFI_BACKEND_PATH), "[device]") write(path(WIFI_BACKEND_PATHS[0]).join("test.conf"), "[device]")
.await .await
.expect("write"); .expect("write");
assert!(get_wifi_backend().await.is_err()); assert!(get_wifi_backend().await.is_err());
write(path(WIFI_BACKEND_PATH), "[device]\nwifi.backend=fake\n") write(
.await path(WIFI_BACKEND_PATHS[0]).join("test.conf"),
.expect("write"); "[device]\nwifi.backend=fake\n",
)
.await
.expect("write");
assert!(get_wifi_backend().await.is_err()); assert!(get_wifi_backend().await.is_err());
write(path(WIFI_BACKEND_PATH), "[device]\nwifi.backend=iwd\n") write(
.await path(WIFI_BACKEND_PATHS[0]).join("test.conf"),
.expect("write"); "[device]\nwifi.backend=iwd\n",
)
.await
.expect("write");
assert_eq!(get_wifi_backend().await.unwrap(), WifiBackend::Iwd); assert_eq!(get_wifi_backend().await.unwrap(), WifiBackend::Iwd);
write( write(
path(WIFI_BACKEND_PATH), path(WIFI_BACKEND_PATHS[0]).join("test.conf"),
"[device]\nwifi.backend=wpa_supplicant\n", "[device]\nwifi.backend=wpa_supplicant\n",
) )
.await .await