diff --git a/examples/wifi-ap.rs b/examples/wifi-ap.rs new file mode 100644 index 0000000..1f7c5c9 --- /dev/null +++ b/examples/wifi-ap.rs @@ -0,0 +1,65 @@ +#![allow(clippy::result_large_err)] +use env_logger::Env; +use log::{error, info}; +use network_interface::{NetworkInterface, NetworkInterfaceConfig}; +use tokio::io; +use wifi_ctrl::{ap, Result}; + +#[tokio::main] +async fn main() -> Result { + env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); + info!("Starting wifi-ap example"); + + let mut network_interfaces = NetworkInterface::show().unwrap(); + network_interfaces.sort_by(|a, b| a.index.cmp(&b.index)); + for (i, itf) in network_interfaces.iter().enumerate() { + info!("[{:?}] {:?}", i, itf.name); + } + let user_input = read_until_break().await; + let index = user_input.trim().parse::()?; + let mut setup = ap::WifiSetup::new()?; + + let proposed_path = format!("/var/run/hostapd/{}", network_interfaces[index].name); + info!("Connect to \"{proposed_path}\"? Type full new path or just press enter to accept."); + + let user_input = read_until_break().await; + if user_input.trim().is_empty() { + setup.set_socket_path(proposed_path); + } else { + setup.set_socket_path(user_input.trim().to_string()); + } + + let broadcast = setup.get_broadcast_receiver(); + let requester = setup.get_request_client(); + let runtime = setup.complete(); + + let (_runtime, _app, _broadcast) = tokio::join!( + async move { + if let Err(e) = runtime.run().await { + error!("Error: {e}"); + } + }, + app(requester), + broadcast_listener(broadcast), + ); + Ok(()) +} + +async fn app(_: ap::RequestClient) -> Result { + Ok(()) +} + +async fn broadcast_listener(mut broadcast_receiver: ap::BroadcastReceiver) -> Result { + while let Ok(broadcast) = broadcast_receiver.recv().await { + info!("Broadcast: {:?}", broadcast); + } + Ok(()) +} + +async fn read_until_break() -> String { + use futures::stream::StreamExt; + use tokio_util::codec::{FramedRead, LinesCodec}; + let stdin = io::stdin(); + let mut reader = FramedRead::new(stdin, LinesCodec::new()); + reader.next().await.unwrap().unwrap() +} diff --git a/examples/wifi-sta.rs b/examples/wifi-sta.rs index 8a37ddd..9519ed3 100644 --- a/examples/wifi-sta.rs +++ b/examples/wifi-sta.rs @@ -1,3 +1,4 @@ +#![allow(clippy::result_large_err)] use env_logger::Env; use log::{error, info}; use network_interface::{NetworkInterface, NetworkInterfaceConfig}; @@ -22,7 +23,7 @@ async fn main() -> Result { info!("Connect to \"{proposed_path}\"? Type full new path or just press enter to accept."); let user_input = read_until_break().await; - if user_input.trim().len() == 0 { + if user_input.trim().is_empty() { setup.set_socket_path(proposed_path); } else { setup.set_socket_path(user_input.trim().to_string()); diff --git a/src/ap/client.rs b/src/ap/client.rs index 5676f40..15f3244 100644 --- a/src/ap/client.rs +++ b/src/ap/client.rs @@ -94,6 +94,7 @@ pub enum Broadcast { Ready, Connected(String), Disconnected(String), + UnknownEvent(String), } /// Channel for broadcasting events. diff --git a/src/ap/event_socket.rs b/src/ap/event_socket.rs index 9041ba9..79617ec 100644 --- a/src/ap/event_socket.rs +++ b/src/ap/event_socket.rs @@ -2,6 +2,7 @@ use super::*; pub(crate) struct EventSocket { socket_handle: SocketHandle<256>, + attach_options: Vec, /// Sends messages to client sender: mpsc::Sender, } @@ -10,6 +11,7 @@ pub(crate) struct EventSocket { pub(crate) enum Event { ApStaConnected(String), ApStaDisconnected(String), + Unknown(String), } pub(crate) type EventReceiver = mpsc::Receiver; @@ -18,12 +20,13 @@ impl EventSocket { pub(crate) async fn new

( socket: P, request_receiver: &mut mpsc::Receiver, + attach_options: &[String], ) -> Result<(EventReceiver, Vec, Self)> where P: AsRef + std::fmt::Debug, { let (socket_handle, deferred_requests) = - SocketHandle::open(socket, "mapper_hostapd_async.sock", request_receiver).await?; + SocketHandle::open(socket, "hostapd_async.sock", request_receiver).await?; // setup the channel for client requests let (sender, receiver) = mpsc::channel(32); @@ -33,6 +36,7 @@ impl EventSocket { Self { socket_handle, sender, + attach_options: attach_options.to_vec(), }, )) } @@ -46,10 +50,15 @@ impl EventSocket { } pub(crate) async fn run(mut self) -> Result { - let mut attach = self.socket_handle.command(b"ATTACH").await; + let mut command = "ATTACH".to_string(); + for o in &self.attach_options { + command.push(' '); + command.push_str(o); + } + let mut attach = self.socket_handle.command(command.as_bytes()).await; while attach.is_err() { tokio::time::sleep(tokio::time::Duration::from_millis(250)).await; - attach = self.socket_handle.command(b"ATTACH").await; + attach = self.socket_handle.command(command.as_bytes()).await; } let mut log_level = self.socket_handle.command(b"LOG_LEVEL DEBUG").await; @@ -68,7 +77,6 @@ impl EventSocket { { Ok(n) => { let data_str = std::str::from_utf8(&self.socket_handle.buffer[..n])?.trim_end(); - debug!("hostapd event: {data_str}"); if let Some(n) = data_str.find("AP-STA-DISCONNECTED") { let index = n + "AP-STA-DISCONNECTED".len(); let mac = &data_str[index..]; @@ -79,6 +87,9 @@ impl EventSocket { let mac = &data_str[index..]; self.send_event(Event::ApStaConnected(mac.to_string())) .await?; + } else { + self.send_event(Event::Unknown(data_str.to_string())) + .await?; } } Err(e) => { diff --git a/src/ap/mod.rs b/src/ap/mod.rs index 5b7c595..2b6594b 100644 --- a/src/ap/mod.rs +++ b/src/ap/mod.rs @@ -18,6 +18,8 @@ const PATH_DEFAULT_SERVER: &str = "/var/run/hostapd/wlan1"; pub struct WifiAp { /// Path to the socket socket_path: std::path::PathBuf, + /// Options to pass to the hostapd attach command + attach_options: Vec, /// Channel for receiving requests request_receiver: mpsc::Receiver, #[allow(unused)] @@ -30,8 +32,12 @@ pub struct WifiAp { impl WifiAp { pub async fn run(mut self) -> Result { info!("Starting Wifi AP process"); - let (event_receiver, mut deferred_requests, event_socket) = - EventSocket::new(&self.socket_path, &mut self.request_receiver).await?; + let (event_receiver, mut deferred_requests, event_socket) = EventSocket::new( + &self.socket_path, + &mut self.request_receiver, + &self.attach_options, + ) + .await?; // We start up a separate socket for receiving the "unexpected" events that // gets forwarded to us via the event_receiver let (socket_handle, next_deferred_requests) = SocketHandle::open( @@ -90,12 +96,17 @@ impl WifiAp { ) -> Result { match event_msg { Event::ApStaConnected(mac) => { - if let Err(e) = broadcast_sender.send(client::Broadcast::Connected(mac)) { + if let Err(e) = broadcast_sender.send(Broadcast::Connected(mac)) { warn!("error broadcasting: {e}"); } } Event::ApStaDisconnected(mac) => { - if let Err(e) = broadcast_sender.send(client::Broadcast::Disconnected(mac)) { + if let Err(e) = broadcast_sender.send(Broadcast::Disconnected(mac)) { + warn!("error broadcasting: {e}"); + } + } + Event::Unknown(msg) => { + if let Err(e) = broadcast_sender.send(Broadcast::UnknownEvent(msg)) { warn!("error broadcasting: {e}"); } } diff --git a/src/ap/setup.rs b/src/ap/setup.rs index 1aa3277..f3a3790 100644 --- a/src/ap/setup.rs +++ b/src/ap/setup.rs @@ -26,6 +26,7 @@ impl WifiSetupGeneric { Ok(Self { wifi: WifiAp { socket_path: PATH_DEFAULT_SERVER.into(), + attach_options: vec![], request_receiver, broadcast_sender, self_sender, @@ -39,6 +40,12 @@ impl WifiSetupGeneric { self.wifi.socket_path = path.into(); } + pub fn add_attach_options(&mut self, options: &[&str]) { + for o in options { + self.wifi.attach_options.push(o.to_string()); + } + } + pub fn get_broadcast_receiver(&self) -> BroadcastReceiver { self.wifi.broadcast_sender.subscribe() } diff --git a/src/sta/event_socket.rs b/src/sta/event_socket.rs index 674fb75..1edda52 100644 --- a/src/sta/event_socket.rs +++ b/src/sta/event_socket.rs @@ -26,7 +26,7 @@ impl EventSocket { P: AsRef + std::fmt::Debug, { let (socket_handle, deferred_requests) = - SocketHandle::open(socket, "mapper_wpa_ctrl_async.sock", request_receiver).await?; + SocketHandle::open(socket, "wpa_ctrl_async.sock", request_receiver).await?; // setup the channel for client requests let (sender, receiver) = mpsc::channel(32); Ok((