diff --git a/Cargo.lock b/Cargo.lock index fc56112..c5845ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -902,7 +902,7 @@ dependencies = [ [[package]] name = "i3stat" -version = "0.10.0" +version = "0.10.1" dependencies = [ "async-trait", "automod", diff --git a/Cargo.toml b/Cargo.toml index fdf91ea..aa4bed0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "i3stat" -version = "0.10.0" +version = "0.10.1" edition = "2021" authors = ["acheronfail "] description = "A lightweight and batteries-included status_command for i3 and sway" diff --git a/justfile b/justfile index 4073ab9..0cf6b79 100644 --- a/justfile +++ b/justfile @@ -26,6 +26,10 @@ build *args: _lbuild: cargo lbuild --all +# runs rustfmt with nightly to enable all its features +fmt: + rustup run nightly cargo fmt + # run `i3stat` in the terminal and interact with it dev *args: _lbuild cd ./scripts/node && RUST_BACKTRACE=1 RUST_LOG=i3stat=trace yarn dev "$@" diff --git a/src/util/netlink/nl80211/enums.rs b/src/util/netlink/nl80211/enums.rs index 27b02c6..eb9e8ac 100644 --- a/src/util/netlink/nl80211/enums.rs +++ b/src/util/netlink/nl80211/enums.rs @@ -1264,6 +1264,20 @@ pub enum Nl80211Bss { } impl neli::consts::genl::NlAttrType for Nl80211Bss {} +/// The BSS status is a BSS attribute in scan dumps, which indicates the status +/// the interface has wrt. this BSS. +#[neli::neli_enum(serialized_type = "u32")] +pub enum Nl80211BssStatus { + /// Authenticated with this BSS. Note that this is no longer used since + /// cfg80211 no longer keeps track of whether or not authentication was done + /// with a given BSS. + Authenticated = 0, + /// Associated with this BSS. + Associated = 1, + /// Joined to this IBSS. + IbssJoined = 2, +} + #[neli::neli_enum(serialized_type = "u32")] pub enum Nl80211IfType { /// unspecified type, driver decides diff --git a/src/util/netlink/nl80211/mod.rs b/src/util/netlink/nl80211/mod.rs index d20eb34..f6ff9c8 100644 --- a/src/util/netlink/nl80211/mod.rs +++ b/src/util/netlink/nl80211/mod.rs @@ -31,13 +31,10 @@ use neli::utils::Groups; use tokio::sync::OnceCell; use self::enums::{ - Nl80211Attribute, - Nl80211Bss, - Nl80211Command, - Nl80211IfType, - Nl80211StationInfo, + Nl80211Attribute, Nl80211Bss, Nl80211Command, Nl80211IfType, Nl80211StationInfo, }; use super::NetlinkInterface; +use crate::util::nl80211::enums::Nl80211BssStatus; use crate::util::{MacAddr, Result}; // init ------------------------------------------------------------------------ @@ -52,7 +49,10 @@ async fn init_socket() -> Result { } async fn init_family(socket: &NlRouter) -> Result { - Ok(socket.resolve_genl_family(NL80211_FAMILY_NAME).await?) + let id = socket.resolve_genl_family(NL80211_FAMILY_NAME).await?; + log::debug!("nl80211 family id: {id}"); + + Ok(id) } // util ------------------------------------------------------------------------ @@ -95,6 +95,14 @@ async fn genl80211_send( .get_or_try_init(|| init_family(socket)) .await?; + log::trace!( + "genl80211_send: cmd={cmd:?}, flags={flags:?}, attrs={:02x?}", + attrs + .iter() + .map(|attr| (attr.nla_type().nla_type(), attr.nla_payload().as_ref())) + .collect::>() + ); + // create generic netlink message let genl_payload: Nl80211Payload = { let mut builder = GenlmsghdrBuilder::default().version(1).cmd(cmd); @@ -106,6 +114,7 @@ async fn genl80211_send( }; // send it to netlink + Ok(socket .send::<_, _, u16, Nl80211Payload>(family_id, flags, NlPayload::Payload(genl_payload)) .await?) @@ -334,45 +343,63 @@ async fn get_scan( ) .await?; - // look for our requested data inside netlink's results + // look for our requested data inside netlink's results - GetScan returns a list of scanned stations... while let Some(result) = recv.next().await as NextNl80211 { match result { Ok(msg) => { if let NlPayload::Payload(gen_msg) = msg.nl_payload() { let attr_handle = gen_msg.attrs().get_attr_handle(); + // extract the `Nl80211Bss` attributes so we can inspect them if let Ok(bss_attrs) = attr_handle.get_nested_attributes::(Nl80211Attribute::Bss) { - // set the ssid if it's not set - if ssid.is_none() { - if let Ok(bytes) = bss_attrs - .get_attr_payload_as_with_len_borrowed::<&[u8]>( - Nl80211Bss::InformationElements, - ) - { - log::debug!( - "index {} updating ssid from information elements", - index - ); - match ssid_from_ie(bytes) { - Ok(option) => *ssid = option, - Err(e) => log::error!( - "index {} failed to parse information elements: {}", - index, - e - ), - } - } - } - - if let Ok(bytes) = bss_attrs + // extract the bssid itself + let bssid = match bss_attrs .get_attr_payload_as_with_len_borrowed::<&[u8]>(Nl80211Bss::Bssid) + .map(|bytes| MacAddr::try_from(bytes)) { - if let Ok(bssid) = MacAddr::try_from(bytes) { - log::debug!("index {} found bssid: {}", index, bssid); - return Ok(Some(bssid)); + Ok(Ok(mac_addr)) => mac_addr, + // if we can't find a bssid, then keep looking for another one + _ => continue, + }; + + // NOTE: this is the important part - we find the bssid that the device is currently associated + // with! The GetScan can return multiple stations (even when we're connected to one) but we only + // want the information about the station we're currently associated with + if let Ok(Nl80211BssStatus::Associated) = + bss_attrs.get_attr_payload_as::(Nl80211Bss::Status) + { + log::debug!("index {} found associated bssid: {}", index, bssid); + + // set the ssid if it's not set already, this works around a quirk in netlink + // see: https://github.com/systemd/systemd/issues/24585 + // either way, this seems to be the more "reliable" way of getting the ssid + if ssid.is_none() { + // parse the information elements attributes + if let Ok(bytes) = bss_attrs + .get_attr_payload_as_with_len_borrowed::<&[u8]>( + Nl80211Bss::InformationElements, + ) + { + match ssid_from_ie(bytes) { + Ok(option) => { + *ssid = option; + log::debug!( + "index {} updated ssid {:?} from information elements", + index, + ssid + ); + } + Err(e) => log::error!( + "index {} failed to parse information elements: {}", + index, + e + ), + } + } } + return Ok(Some(bssid)); } } } diff --git a/tests/i3/mod.rs b/tests/i3/mod.rs index d70ed0b..e9345af 100644 --- a/tests/i3/mod.rs +++ b/tests/i3/mod.rs @@ -11,8 +11,15 @@ use serde_json::Value; use self::util::x_click; use crate::i3::util::MouseButton; use crate::util::{ - find_object_containing, get_current_exe, get_exe, get_fakeroot_lib, get_faketime_lib, - wait_for_file, LogOnDropChild, Test, FAKE_TIME, + find_object_containing, + get_current_exe, + get_exe, + get_fakeroot_lib, + get_faketime_lib, + wait_for_file, + LogOnDropChild, + Test, + FAKE_TIME, }; // start nested x server displays at 10 diff --git a/tests/spawn/mod.rs b/tests/spawn/mod.rs index a7f94a9..2fddaeb 100644 --- a/tests/spawn/mod.rs +++ b/tests/spawn/mod.rs @@ -14,7 +14,12 @@ use serde_json::Value; use timeout_readwrite::{TimeoutReadExt, TimeoutReader}; use crate::util::{ - get_current_exe, get_fakeroot_lib, get_faketime_lib, LogOnDropChild, Test, FAKE_TIME, + get_current_exe, + get_fakeroot_lib, + get_faketime_lib, + LogOnDropChild, + Test, + FAKE_TIME, }; /// Convenience struct for running assertions on and communicating with a running instance of the program