diff --git a/CHANGELOG.md b/CHANGELOG.md index 433560e..bf18cdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY! This project adheres to [Semantic Versioning](http://semver.org/). +## v0.9.0 - 2018-02-15 + +* Error handling with error-chain #110 [majorz] + ## v0.8.6 - 2018-01-10 * Incomplete NM80211ApFlags enumeration definitions #108 [majorz] diff --git a/Cargo.toml b/Cargo.toml index edf9ea5..6efd5d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "network-manager" -version = "0.8.6" +version = "0.9.0" authors = ["Zahari Petkov ", "Aaron Brodersen "] description = "Rust NetworkManager bindings" homepage = "https://github.com/resin-io-modules/network-manager" @@ -17,3 +17,7 @@ tokio-timer = "0.1" bitflags = "0.9" ascii = "0.8" log = "0.3" + +[dependencies.error-chain] +version = "0.11" +default-features = false diff --git a/examples/create.rs b/examples/create.rs index 691aff0..d39e62e 100644 --- a/examples/create.rs +++ b/examples/create.rs @@ -1,35 +1,97 @@ +#[macro_use] +extern crate error_chain; + extern crate network_manager; use std::env; use std::process; +use std::io::Write; + +use network_manager::{AccessPoint, Device, DeviceType, NetworkManager}; + +mod errors { + use network_manager; + + error_chain! { + links { + NetworkManager(network_manager::errors::Error, network_manager::errors::ErrorKind); + } + + errors { + Runtime(info: String) { + description("Runtime error") + display("{}", info) + } + } + } +} -use network_manager::{DeviceType, NetworkManager}; +use errors::*; fn main() { + if let Err(ref e) = run() { + let stderr = &mut ::std::io::stderr(); + let errmsg = "Error writing to stderr"; + + writeln!(stderr, "{}", e).expect(errmsg); + + for e in e.iter().skip(1) { + writeln!(stderr, " caused by: {}", e).expect(errmsg); + } + + ::std::process::exit(1); + } +} + +fn run() -> Result<()> { let args: Vec = env::args().collect(); - if args.len() < 3 { + if args.len() != 3 { println!("USAGE: create SSID PASSWORD"); process::exit(1); } let manager = NetworkManager::new(); - let devices = manager.get_devices().unwrap(); - let device_index = devices - .iter() - .position(|d| *d.device_type() == DeviceType::WiFi) - .unwrap(); - let wifi_device = devices[device_index].as_wifi_device().unwrap(); + let device = find_device(&manager)?; - let access_points = wifi_device.get_access_points().unwrap(); + let wifi_device = device.as_wifi_device().unwrap(); - let ap_index = access_points + let access_points = wifi_device.get_access_points()?; + + let ap_index = find_access_point(&access_points, &args[1] as &str)?; + + wifi_device.connect(&access_points[ap_index], &args[2] as &str)?; + + Ok(()) +} + +fn find_device(manager: &NetworkManager) -> Result { + let devices = manager.get_devices()?; + + let index = devices .iter() - .position(|ap| ap.ssid().as_str().unwrap() == args[1]) - .unwrap(); + .position(|d| *d.device_type() == DeviceType::WiFi); - wifi_device - .connect(&access_points[ap_index], &args[2] as &str) - .unwrap(); + if let Some(index) = index { + Ok(devices[index].clone()) + } else { + bail!(ErrorKind::Runtime("Cannot find a WiFi device".into())) + } +} + +fn find_access_point(access_points: &[AccessPoint], ssid: &str) -> Result { + if let Some(index) = access_points.iter().position(|ap| same_ssid(ap, ssid)) { + Ok(index) + } else { + bail!(ErrorKind::Runtime("Access point not found".into())) + } +} + +fn same_ssid(ap: &AccessPoint, ssid: &str) -> bool { + if let Ok(ap_ssid) = ap.ssid().as_str() { + ap_ssid == ssid + } else { + false + } } diff --git a/examples/hotspot.rs b/examples/hotspot.rs index 558a985..c94af1b 100644 --- a/examples/hotspot.rs +++ b/examples/hotspot.rs @@ -1,19 +1,74 @@ +#[macro_use] +extern crate error_chain; + extern crate network_manager; use std::env; use std::process; +use std::io::Write; use network_manager::{Device, DeviceType, NetworkManager}; +mod errors { + use network_manager; + + error_chain! { + links { + NetworkManager(network_manager::errors::Error, network_manager::errors::ErrorKind); + } + + errors { + Runtime(info: String) { + description("Runtime error") + display("{}", info) + } + } + } +} + +use errors::*; + struct Options { interface: Option, ssid: String, password: Option, } -fn print_usage_and_exit() { - println!("USAGE: hotspot [-i INTERFACE] SSID [PASSWORD]"); - process::exit(1); +fn main() { + if let Err(ref e) = run() { + let stderr = &mut ::std::io::stderr(); + let errmsg = "Error writing to stderr"; + + writeln!(stderr, "{}", e).expect(errmsg); + + for e in e.iter().skip(1) { + writeln!(stderr, " caused by: {}", e).expect(errmsg); + } + + ::std::process::exit(1); + } +} + +fn run() -> Result<()> { + let Options { + interface, + ssid, + password, + } = parse_options(); + + let pass_str = match password { + Some(ref s) => Some(s as &str), + None => None, + }; + + let manager = NetworkManager::new(); + + let device = find_device(&manager, interface)?; + let wifi_device = device.as_wifi_device().unwrap(); + + wifi_device.create_hotspot(&ssid as &str, pass_str, None)?; + + Ok(()) } fn parse_options() -> Options { @@ -48,48 +103,34 @@ fn parse_options() -> Options { } } -fn find_device(manager: &NetworkManager, interface: Option) -> Option { +fn print_usage_and_exit() { + println!("USAGE: hotspot [-i INTERFACE] SSID [PASSWORD]"); + process::exit(1); +} + +fn find_device(manager: &NetworkManager, interface: Option) -> Result { if let Some(interface) = interface { - let device = manager.get_device_by_interface(&interface).unwrap(); + let device = manager.get_device_by_interface(&interface)?; if *device.device_type() == DeviceType::WiFi { - Some(device) + Ok(device) } else { - None + bail!(ErrorKind::Runtime(format!( + "{} is not a WiFi device", + interface + ))) } } else { - let devices = manager.get_devices().unwrap(); + let devices = manager.get_devices()?; let index = devices .iter() .position(|d| *d.device_type() == DeviceType::WiFi); if let Some(index) = index { - Some(devices[index].clone()) + Ok(devices[index].clone()) } else { - None + bail!(ErrorKind::Runtime("Cannot find a WiFi device".into())) } } } - -fn main() { - let Options { - interface, - ssid, - password, - } = parse_options(); - - let pass_str = match password { - Some(ref s) => Some(s as &str), - None => None, - }; - - let manager = NetworkManager::new(); - - let device = find_device(&manager, interface).unwrap(); - let wifi_device = device.as_wifi_device().unwrap(); - - wifi_device - .create_hotspot(&ssid as &str, pass_str, None) - .unwrap(); -} diff --git a/src/connection.rs b/src/connection.rs index 7af9ddd..42899e4 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -4,6 +4,7 @@ use std::net::Ipv4Addr; use ascii::AsAsciiStr; +use errors::*; use dbus_nm::DBusNetworkManager; use wifi::Security; @@ -18,7 +19,7 @@ pub struct Connection { } impl Connection { - fn init(dbus_manager: &Rc, path: &str) -> Result { + fn init(dbus_manager: &Rc, path: &str) -> Result { let settings = dbus_manager.get_connection_settings(path)?; Ok(Connection { @@ -32,7 +33,7 @@ impl Connection { &self.settings } - pub fn get_state(&self) -> Result { + pub fn get_state(&self) -> Result { let active_path_option = get_connection_active_path(&self.dbus_manager, &self.path)?; if let Some(active_path) = active_path_option { @@ -44,7 +45,7 @@ impl Connection { } } - pub fn delete(&self) -> Result<(), String> { + pub fn delete(&self) -> Result<()> { self.dbus_manager.delete_connection(&self.path) } @@ -58,7 +59,7 @@ impl Connection { /// let connections = manager.get_connections().unwrap(); /// connections[0].activate().unwrap(); /// ``` - pub fn activate(&self) -> Result { + pub fn activate(&self) -> Result { let state = self.get_state()?; match state { @@ -68,7 +69,9 @@ impl Connection { &ConnectionState::Activated, self.dbus_manager.method_timeout(), ), - ConnectionState::Unknown => Err("Unable to get connection state".to_string()), + ConnectionState::Unknown => bail!(ErrorKind::NetworkManager( + "Unable to get connection state".into() + )), _ => { self.dbus_manager.activate_connection(&self.path)?; @@ -91,7 +94,7 @@ impl Connection { /// let connections = manager.get_connections().unwrap(); /// connections[0].deactivate().unwrap(); /// ``` - pub fn deactivate(&self) -> Result { + pub fn deactivate(&self) -> Result { let state = self.get_state()?; match state { @@ -101,7 +104,9 @@ impl Connection { &ConnectionState::Deactivated, self.dbus_manager.method_timeout(), ), - ConnectionState::Unknown => Err("Unable to get connection state".to_string()), + ConnectionState::Unknown => bail!(ErrorKind::NetworkManager( + "Unable to get connection state".into() + )), _ => { let active_path_option = get_connection_active_path(&self.dbus_manager, &self.path)?; @@ -121,7 +126,7 @@ impl Connection { } } - pub fn get_devices(&self) -> Result, String> { + pub fn get_devices(&self) -> Result> { let active_path_option = get_connection_active_path(&self.dbus_manager, &self.path)?; if let Some(active_path) = active_path_option { @@ -208,7 +213,7 @@ impl From for ConnectionState { } } -pub fn get_connections(dbus_manager: &Rc) -> Result, String> { +pub fn get_connections(dbus_manager: &Rc) -> Result> { let paths = dbus_manager.list_connections()?; let mut connections = Vec::with_capacity(paths.len()); @@ -222,9 +227,7 @@ pub fn get_connections(dbus_manager: &Rc) -> Result, -) -> Result, String> { +pub fn get_active_connections(dbus_manager: &Rc) -> Result> { let active_paths = dbus_manager.get_active_connections()?; let mut connections = Vec::with_capacity(active_paths.len()); @@ -247,7 +250,7 @@ pub fn connect_to_access_point

( ssid: &SsidSlice, security: &Security, password: &P, -) -> Result<(Connection, ConnectionState), String> +) -> Result<(Connection, ConnectionState)> where P: AsAsciiStr + ?Sized, { @@ -277,7 +280,7 @@ pub fn create_hotspot( ssid: &S, password: Option<&P>, address: Option, -) -> Result<(Connection, ConnectionState), String> +) -> Result<(Connection, ConnectionState)> where S: AsSsidSlice + ?Sized, P: AsAsciiStr + ?Sized, @@ -298,7 +301,7 @@ where fn get_connection_active_path( dbus_manager: &DBusNetworkManager, connection_path: &str, -) -> Result, String> { +) -> Result> { let active_paths = dbus_manager.get_active_connections()?; for active_path in active_paths { @@ -316,7 +319,7 @@ fn wait( connection: &Connection, target_state: &ConnectionState, timeout: u64, -) -> Result { +) -> Result { if timeout == 0 { return connection.get_state(); } diff --git a/src/dbus_api.rs b/src/dbus_api.rs index 206b02f..73017e1 100644 --- a/src/dbus_api.rs +++ b/src/dbus_api.rs @@ -2,7 +2,8 @@ use dbus::Connection as DBusConnection; use dbus::{BusType, ConnPath, Message, Path}; use dbus::arg::{Array, Get, Iter, RefArg, Variant}; use dbus::stdintf::OrgFreedesktopDBusProperties; -use dbus::Error; + +use errors::*; const DEFAULT_TIMEOUT: u64 = 15; const RETRIES_ALLOWED: usize = 10; @@ -36,7 +37,7 @@ impl DBusApi { self.method_timeout } - pub fn call(&self, path: &str, interface: &str, method: &str) -> Result { + pub fn call(&self, path: &str, interface: &str, method: &str) -> Result { self.call_with_args(path, interface, method, &[]) } @@ -46,15 +47,12 @@ impl DBusApi { interface: &str, method: &str, args: &[&RefArg], - ) -> Result { + ) -> Result { self.call_with_args_retry(path, interface, method, args) - .map_err(|error| { - let message = format!( - "D-Bus '{}'::'{}' method call failed on '{}': {}", - interface, method, path, error - ); + .map_err(|e| { + let message = format!("{}::{} method call failed on {}", interface, method, path); error!("{}", message); - message + e.chain_err(|| ErrorKind::DBusAPI(message)) }) } @@ -64,7 +62,7 @@ impl DBusApi { interface: &str, method: &str, args: &[&RefArg], - ) -> Result { + ) -> Result { let mut retries = 0; loop { @@ -75,14 +73,14 @@ impl DBusApi { retries += 1; if retries == RETRIES_ALLOWED { - return Err(format!( - "method call failed after {} retries", + bail!(ErrorKind::DBusAPI(format!( + "Method call failed after {} retries", RETRIES_ALLOWED - )); + ))); } debug!( - "Retrying '{}'::'{}' method call: retry #{}", + "Retrying {}::{} method call: retry #{}", interface, method, retries, ); @@ -96,7 +94,7 @@ impl DBusApi { interface: &str, method: &str, args: &[&RefArg], - ) -> Option> { + ) -> Option> { match Message::new_method_call(self.base, path, interface, method) { Ok(mut message) => { if !args.is_empty() { @@ -105,39 +103,39 @@ impl DBusApi { self.send_message_checked(message) }, - Err(details) => Some(Err(details)), + Err(details) => Some(Err(ErrorKind::DBusAPI(details).into())), } } - fn send_message_checked(&self, message: Message) -> Option> { + fn send_message_checked(&self, message: Message) -> Option> { match self.connection .send_with_reply_and_block(message, self.method_timeout as i32 * 1000) { Ok(response) => Some(Ok(response)), - Err(err) => { - let message = get_error_message(&err).to_string(); - - let name = err.name(); - for error_name in self.method_retry_error_names { - if name == Some(error_name) { - debug!("Should retry D-Bus method call: {}", error_name); - - return None; + Err(e) => { + { + let name = e.name(); + for error_name in self.method_retry_error_names { + if name == Some(error_name) { + debug!("Should retry D-Bus method call: {}", error_name); + + return None; + } } } - Some(Err(message)) + Some(Err(Error::from(e))) }, } } - pub fn property(&self, path: &str, interface: &str, name: &str) -> Result + pub fn property(&self, path: &str, interface: &str, name: &str) -> Result where DBusApi: VariantTo, { let property_error = |details: &str, err: bool| { let message = format!( - "D-Bus get '{}'::'{}' property failed on '{}': {}", + "Get {}::{} property failed on {}: {}", interface, name, path, details ); if err { @@ -145,7 +143,7 @@ impl DBusApi { } else { debug!("{}", message); } - Err(message) + ErrorKind::DBusAPI(message) }; let path = self.with_path(path); @@ -153,25 +151,28 @@ impl DBusApi { match path.get(interface, name) { Ok(variant) => match DBusApi::variant_to(&variant) { Some(data) => Ok(data), - None => property_error("wrong property type", true), + None => bail!(property_error("wrong property type", true)), }, - Err(err) => match err.message() { - Some(details) => property_error(details, false), - None => property_error("no details", false), + Err(e) => { + let dbus_err = match e.message() { + Some(details) => property_error(details, false), + None => property_error("no details", false), + }; + Err(e).chain_err(|| dbus_err) }, } } - pub fn extract<'a, T>(&self, response: &'a Message) -> Result + pub fn extract<'a, T>(&self, response: &'a Message) -> Result where T: Get<'a>, { response .get1() - .ok_or_else(|| "D-Bus wrong response type".to_string()) + .ok_or_else(|| ErrorKind::DBusAPI("Wrong response type".into()).into()) } - pub fn extract_two<'a, T1, T2>(&self, response: &'a Message) -> Result<(T1, T2), String> + pub fn extract_two<'a, T1, T2>(&self, response: &'a Message) -> Result<(T1, T2)> where T1: Get<'a>, T2: Get<'a>, @@ -184,7 +185,7 @@ impl DBusApi { } } - Err("D-Bus wrong response type".to_string()) + bail!(ErrorKind::DBusAPI("Wrong response type".into())) } fn with_path<'a, P: Into>>(&'a self, path: P) -> ConnPath<&'a DBusConnection> { @@ -261,36 +262,35 @@ impl VariantTo> for DBusApi { } } -pub fn extract<'a, T>(var: &mut Variant>) -> Result +pub fn extract<'a, T>(var: &mut Variant>) -> Result where T: Get<'a>, { var.0 .get::() - .ok_or_else(|| format!("D-Bus variant type does not match: {:?}", var)) + .ok_or_else(|| ErrorKind::DBusAPI(format!("Variant type does not match: {:?}", var)).into()) } -pub fn variant_iter_to_vec_u8(var: &mut Variant) -> Result, String> { +pub fn variant_iter_to_vec_u8(var: &mut Variant) -> Result> { let array_option = &var.0.get::>(); if let Some(array) = *array_option { Ok(array.collect()) } else { - Err(format!("D-Bus variant not an array: {:?}", var)) + bail!(ErrorKind::DBusAPI(format!( + "Variant not an array: {:?}", + var + ))) } } -pub fn path_to_string(path: &Path) -> Result { +pub fn path_to_string(path: &Path) -> Result { if let Ok(slice) = path.as_cstr().to_str() { Ok(slice.to_string()) } else { - Err(format!("Path not a UTF-8 string: {:?}", path)) - } -} - -fn get_error_message(err: &Error) -> &str { - match err.message() { - Some(details) => details, - None => "Undefined error message", + bail!(ErrorKind::DBusAPI(format!( + "Path not a UTF-8 string: {:?}", + path + ))) } } diff --git a/src/dbus_nm.rs b/src/dbus_nm.rs index 9e2d265..62ac023 100644 --- a/src/dbus_nm.rs +++ b/src/dbus_nm.rs @@ -6,6 +6,7 @@ use ascii::AsAsciiStr; use dbus::Path; use dbus::arg::{Array, Dict, Iter, RefArg, Variant}; +use errors::*; use dbus_api::{extract, path_to_string, DBusApi, VariantTo, variant_iter_to_vec_u8}; use manager::{Connectivity, NetworkManagerState}; use connection::{ConnectionSettings, ConnectionState}; @@ -49,7 +50,7 @@ impl DBusNetworkManager { self.dbus.method_timeout() } - pub fn get_state(&self) -> Result { + pub fn get_state(&self) -> Result { let response = self.dbus .call(NM_SERVICE_PATH, NM_SERVICE_INTERFACE, "state")?; @@ -58,7 +59,7 @@ impl DBusNetworkManager { Ok(NetworkManagerState::from(state)) } - pub fn check_connectivity(&self) -> Result { + pub fn check_connectivity(&self) -> Result { let response = self.dbus .call(NM_SERVICE_PATH, NM_SERVICE_INTERFACE, "CheckConnectivity")?; @@ -67,17 +68,17 @@ impl DBusNetworkManager { Ok(Connectivity::from(connectivity)) } - pub fn is_wireless_enabled(&self) -> Result { + pub fn is_wireless_enabled(&self) -> Result { self.dbus .property(NM_SERVICE_PATH, NM_SERVICE_INTERFACE, "WirelessEnabled") } - pub fn is_networking_enabled(&self) -> Result { + pub fn is_networking_enabled(&self) -> Result { self.dbus .property(NM_SERVICE_PATH, NM_SERVICE_INTERFACE, "NetworkingEnabled") } - pub fn list_connections(&self) -> Result, String> { + pub fn list_connections(&self) -> Result> { let response = self.dbus .call(NM_SETTINGS_PATH, NM_SETTINGS_INTERFACE, "ListConnections")?; @@ -86,7 +87,7 @@ impl DBusNetworkManager { Ok(array.map(|e| e.to_string()).collect()) } - pub fn get_active_connections(&self) -> Result, String> { + pub fn get_active_connections(&self) -> Result> { self.dbus .property(NM_SERVICE_PATH, NM_SERVICE_INTERFACE, "ActiveConnections") } @@ -97,7 +98,7 @@ impl DBusNetworkManager { .ok() } - pub fn get_connection_state(&self, path: &str) -> Result { + pub fn get_connection_state(&self, path: &str) -> Result { let state: i64 = match self.dbus.property(path, NM_ACTIVE_INTERFACE, "State") { Ok(state) => state, Err(_) => return Ok(ConnectionState::Unknown), @@ -106,7 +107,7 @@ impl DBusNetworkManager { Ok(ConnectionState::from(state)) } - pub fn get_connection_settings(&self, path: &str) -> Result { + pub fn get_connection_settings(&self, path: &str) -> Result { let response = self.dbus .call(path, NM_CONNECTION_INTERFACE, "GetSettings")?; @@ -150,17 +151,17 @@ impl DBusNetworkManager { }) } - pub fn get_active_connection_devices(&self, path: &str) -> Result, String> { + pub fn get_active_connection_devices(&self, path: &str) -> Result> { self.dbus.property(path, NM_ACTIVE_INTERFACE, "Devices") } - pub fn delete_connection(&self, path: &str) -> Result<(), String> { + pub fn delete_connection(&self, path: &str) -> Result<()> { self.dbus.call(path, NM_CONNECTION_INTERFACE, "Delete")?; Ok(()) } - pub fn activate_connection(&self, path: &str) -> Result<(), String> { + pub fn activate_connection(&self, path: &str) -> Result<()> { self.dbus.call_with_args( NM_SERVICE_PATH, NM_SERVICE_INTERFACE, @@ -175,7 +176,7 @@ impl DBusNetworkManager { Ok(()) } - pub fn deactivate_connection(&self, path: &str) -> Result<(), String> { + pub fn deactivate_connection(&self, path: &str) -> Result<()> { self.dbus.call_with_args( NM_SERVICE_PATH, NM_SERVICE_INTERFACE, @@ -193,7 +194,7 @@ impl DBusNetworkManager { ssid: &SsidSlice, security: &Security, password: &P, - ) -> Result<(String, String), String> + ) -> Result<(String, String)> where P: AsAsciiStr + ?Sized, { @@ -251,7 +252,7 @@ impl DBusNetworkManager { ssid: &T, password: Option<&U>, address: Option, - ) -> Result<(String, String), String> + ) -> Result<(String, String)> where T: AsSsidSlice + ?Sized, U: AsAsciiStr + ?Sized, @@ -321,12 +322,12 @@ impl DBusNetworkManager { )) } - pub fn get_devices(&self) -> Result, String> { + pub fn get_devices(&self) -> Result> { self.dbus .property(NM_SERVICE_PATH, NM_SERVICE_INTERFACE, "Devices") } - pub fn get_device_by_interface(&self, interface: &str) -> Result { + pub fn get_device_by_interface(&self, interface: &str) -> Result { let response = self.dbus.call_with_args( NM_SERVICE_PATH, NM_SERVICE_INTERFACE, @@ -339,19 +340,19 @@ impl DBusNetworkManager { path_to_string(&path) } - pub fn get_device_interface(&self, path: &str) -> Result { + pub fn get_device_interface(&self, path: &str) -> Result { self.dbus.property(path, NM_DEVICE_INTERFACE, "Interface") } - pub fn get_device_type(&self, path: &str) -> Result { + pub fn get_device_type(&self, path: &str) -> Result { self.dbus.property(path, NM_DEVICE_INTERFACE, "DeviceType") } - pub fn get_device_state(&self, path: &str) -> Result { + pub fn get_device_state(&self, path: &str) -> Result { self.dbus.property(path, NM_DEVICE_INTERFACE, "State") } - pub fn connect_device(&self, path: &str) -> Result<(), String> { + pub fn connect_device(&self, path: &str) -> Result<()> { self.dbus.call_with_args( NM_SERVICE_PATH, NM_SERVICE_INTERFACE, @@ -366,13 +367,13 @@ impl DBusNetworkManager { Ok(()) } - pub fn disconnect_device(&self, path: &str) -> Result<(), String> { + pub fn disconnect_device(&self, path: &str) -> Result<()> { self.dbus.call(path, NM_DEVICE_INTERFACE, "Disconnect")?; Ok(()) } - pub fn get_device_access_points(&self, path: &str) -> Result, String> { + pub fn get_device_access_points(&self, path: &str) -> Result> { self.dbus .property(path, NM_WIRELESS_INTERFACE, "AccessPoints") } @@ -387,21 +388,21 @@ impl DBusNetworkManager { } } - pub fn get_access_point_strength(&self, path: &str) -> Result { + pub fn get_access_point_strength(&self, path: &str) -> Result { self.dbus .property(path, NM_ACCESS_POINT_INTERFACE, "Strength") } - pub fn get_access_point_flags(&self, path: &str) -> Result { + pub fn get_access_point_flags(&self, path: &str) -> Result { self.dbus.property(path, NM_ACCESS_POINT_INTERFACE, "Flags") } - pub fn get_access_point_wpa_flags(&self, path: &str) -> Result { + pub fn get_access_point_wpa_flags(&self, path: &str) -> Result { self.dbus .property(path, NM_ACCESS_POINT_INTERFACE, "WpaFlags") } - pub fn get_access_point_rsn_flags(&self, path: &str) -> Result { + pub fn get_access_point_rsn_flags(&self, path: &str) -> Result { self.dbus .property(path, NM_ACCESS_POINT_INTERFACE, "RsnFlags") } @@ -453,15 +454,15 @@ where map.insert(key.into(), Variant(Box::new(value.into()))); } -fn verify_password(password: &T) -> Result<&str, String> { +fn verify_password(password: &T) -> Result<&str> { match password.as_ascii_str() { - Err(_) => Err("Not an ASCII password".to_string()), + Err(e) => Err(e).chain_err(|| ErrorKind::PreSharedKey("Not an ASCII password".into())), Ok(p) => { if p.len() > 64 { - Err(format!( + bail!(ErrorKind::PreSharedKey(format!( "Password length should not exceed 64: {} len", p.len() - )) + ))) } else { Ok(p.as_str()) } diff --git a/src/device.rs b/src/device.rs index 48e6ffb..bd7f22b 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use std::fmt; +use errors::*; use dbus_nm::DBusNetworkManager; use wifi::{new_wifi_device, WiFiDevice}; @@ -14,7 +15,7 @@ pub struct Device { } impl Device { - fn init(dbus_manager: &Rc, path: &str) -> Result { + fn init(dbus_manager: &Rc, path: &str) -> Result { let interface = dbus_manager.get_device_interface(path)?; let device_type = dbus_manager.get_device_type(path)?; @@ -35,7 +36,7 @@ impl Device { &self.interface } - pub fn get_state(&self) -> Result { + pub fn get_state(&self) -> Result { self.dbus_manager.get_device_state(&self.path) } @@ -58,7 +59,7 @@ impl Device { /// let i = devices.iter().position(|ref d| *d.device_type() == DeviceType::WiFi).unwrap(); /// devices[i].connect().unwrap(); /// ``` - pub fn connect(&self) -> Result { + pub fn connect(&self) -> Result { let state = self.get_state()?; match state { @@ -86,7 +87,7 @@ impl Device { /// let i = devices.iter().position(|ref d| *d.device_type() == DeviceType::WiFi).unwrap(); /// devices[i].disconnect().unwrap(); /// ``` - pub fn disconnect(&self) -> Result { + pub fn disconnect(&self) -> Result { let state = self.get_state()?; match state { @@ -226,7 +227,7 @@ impl From for DeviceState { } } -pub fn get_devices(dbus_manager: &Rc) -> Result, String> { +pub fn get_devices(dbus_manager: &Rc) -> Result> { let device_paths = dbus_manager.get_devices()?; let mut result = Vec::with_capacity(device_paths.len()); @@ -243,7 +244,7 @@ pub fn get_devices(dbus_manager: &Rc) -> Result, pub fn get_device_by_interface( dbus_manager: &Rc, interface: &str, -) -> Result { +) -> Result { let path = dbus_manager.get_device_by_interface(interface)?; Device::init(dbus_manager, &path) @@ -252,7 +253,7 @@ pub fn get_device_by_interface( pub fn get_active_connection_devices( dbus_manager: &Rc, active_path: &str, -) -> Result, String> { +) -> Result> { let device_paths = dbus_manager.get_active_connection_devices(active_path)?; let mut result = Vec::with_capacity(device_paths.len()); @@ -266,7 +267,7 @@ pub fn get_active_connection_devices( Ok(result) } -fn wait(device: &Device, target_state: &DeviceState, timeout: u64) -> Result { +fn wait(device: &Device, target_state: &DeviceState, timeout: u64) -> Result { if timeout == 0 { return device.get_state(); } diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..f878e0d --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,31 @@ +error_chain!{ + foreign_links { + Ascii(::ascii::AsAsciiStrError); + Utf8(::std::str::Utf8Error); + DBus(::dbus::Error); + } + + errors { + NetworkManager(info: String) { + description("NetworkManager error") + display("NetworkManager failure: {}", info) + } + + SSID(info: String) { + description("Invalid SSID") + display("{}", info) + } + + PreSharedKey(info: String) { + description("Invalid Pre-Shared-Key") + display("{}", info) + } + + DBusAPI(info: String) { + description("D-Bus API error") + display("D-Bus failure: {}", info) + } + + Service + } +} diff --git a/src/lib.rs b/src/lib.rs index cb467d1..768bb31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,9 @@ //! The Network Manager Library provides the essential //! functionality for configuring Network Manager from Rust. +#[macro_use] +extern crate error_chain; + #[macro_use] extern crate log; @@ -13,6 +16,8 @@ extern crate dbus; extern crate ascii; +pub mod errors; + mod dbus_nm; mod dbus_api; mod manager; diff --git a/src/manager.rs b/src/manager.rs index 51e2fc8..22eee2b 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -1,10 +1,11 @@ use std::rc::Rc; +use errors::*; use dbus_nm::DBusNetworkManager; use connection::{get_active_connections, get_connections, Connection}; use device::{get_device_by_interface, get_devices, Device}; -use service::{get_service_state, start_service, stop_service, Error, ServiceState}; +use service::{get_service_state, start_service, stop_service, ServiceState}; pub struct NetworkManager { dbus_manager: Rc, @@ -32,7 +33,7 @@ impl NetworkManager { /// let state = NetworkManager::start_service(10).unwrap(); /// println!("{:?}", state); /// ``` - pub fn start_service(timeout: u64) -> Result { + pub fn start_service(timeout: u64) -> Result { start_service(timeout) } @@ -45,7 +46,7 @@ impl NetworkManager { /// let state = NetworkManager::stop_service(10).unwrap(); /// println!("{:?}", state); /// ``` - pub fn stop_service(timeout: u64) -> Result { + pub fn stop_service(timeout: u64) -> Result { stop_service(timeout) } @@ -58,7 +59,7 @@ impl NetworkManager { /// let state = NetworkManager::get_service_state().unwrap(); /// println!("{:?}", state); /// ``` - pub fn get_service_state() -> Result { + pub fn get_service_state() -> Result { get_service_state() } @@ -72,11 +73,11 @@ impl NetworkManager { /// let connections = manager.get_connections().unwrap(); /// println!("{:?}", connections); /// ``` - pub fn get_connections(&self) -> Result, String> { + pub fn get_connections(&self) -> Result> { get_connections(&self.dbus_manager) } - pub fn get_active_connections(&self) -> Result, String> { + pub fn get_active_connections(&self) -> Result> { get_active_connections(&self.dbus_manager) } @@ -90,27 +91,27 @@ impl NetworkManager { /// let devices = manager.get_devices().unwrap(); /// println!("{:?}", devices); /// ``` - pub fn get_devices(&self) -> Result, String> { + pub fn get_devices(&self) -> Result> { get_devices(&self.dbus_manager) } - pub fn get_device_by_interface(&self, interface: &str) -> Result { + pub fn get_device_by_interface(&self, interface: &str) -> Result { get_device_by_interface(&self.dbus_manager, interface) } - pub fn get_state(&self) -> Result { + pub fn get_state(&self) -> Result { self.dbus_manager.get_state() } - pub fn get_connectivity(&self) -> Result { + pub fn get_connectivity(&self) -> Result { self.dbus_manager.check_connectivity() } - pub fn is_networking_enabled(&self) -> Result { + pub fn is_networking_enabled(&self) -> Result { self.dbus_manager.is_networking_enabled() } - pub fn is_wireless_enabled(&self) -> Result { + pub fn is_wireless_enabled(&self) -> Result { self.dbus_manager.is_wireless_enabled() } } diff --git a/src/service.rs b/src/service.rs index 8d15283..4f7f094 100644 --- a/src/service.rs +++ b/src/service.rs @@ -11,79 +11,83 @@ use self::futures::Future; use self::futures_cpupool::CpuPool; use self::tokio_timer::Timer; +use errors::*; + pub const SD_SERVICE_MANAGER: &str = "org.freedesktop.systemd1"; pub const SD_SERVICE_PATH: &str = "/org/freedesktop/systemd1"; pub const SD_MANAGER_INTERFACE: &str = "org.freedesktop.systemd1.Manager"; pub const SD_UNIT_INTERFACE: &str = "org.freedesktop.systemd1.Unit"; -pub fn start_service(timeout: u64) -> Result { +pub fn start_service(timeout: u64) -> Result { let state = get_service_state()?; match state { ServiceState::Active => Ok(state), ServiceState::Activating => handler(timeout, ServiceState::Active), - ServiceState::Failed => Err(Error::Failed), + ServiceState::Failed => bail!(ErrorKind::Service), _ => { let message = Message::new_method_call( SD_SERVICE_MANAGER, SD_SERVICE_PATH, SD_MANAGER_INTERFACE, "StartUnit", - ).map_err(Error::Message)? + ).map_err(|_| ErrorKind::Service)? .append2("NetworkManager.service", "fail"); - let connection = Connection::get_private(BusType::System).map_err(Error::Connection)?; + let connection = + Connection::get_private(BusType::System).map_err(|_| ErrorKind::Service)?; connection .send_with_reply_and_block(message, 2000) - .map_err(Error::Connection)?; + .map_err(|_| ErrorKind::Service)?; handler(timeout, ServiceState::Active) }, } } -pub fn stop_service(timeout: u64) -> Result { +pub fn stop_service(timeout: u64) -> Result { let state = get_service_state()?; match state { ServiceState::Inactive => Ok(state), ServiceState::Deactivating => handler(timeout, ServiceState::Inactive), - ServiceState::Failed => Err(Error::Failed), + ServiceState::Failed => bail!(ErrorKind::Service), _ => { let message = Message::new_method_call( SD_SERVICE_MANAGER, SD_SERVICE_PATH, SD_MANAGER_INTERFACE, "StopUnit", - ).map_err(Error::Message)? + ).map_err(|_| ErrorKind::Service)? .append2("NetworkManager.service", "fail"); - let connection = Connection::get_private(BusType::System).map_err(Error::Connection)?; + let connection = + Connection::get_private(BusType::System).map_err(|_| ErrorKind::Service)?; connection .send_with_reply_and_block(message, 2000) - .map_err(Error::Connection)?; + .map_err(|_| ErrorKind::Service)?; handler(timeout, ServiceState::Inactive) }, } } -pub fn get_service_state() -> Result { +pub fn get_service_state() -> Result { let message = Message::new_method_call( SD_SERVICE_MANAGER, SD_SERVICE_PATH, SD_MANAGER_INTERFACE, "GetUnit", - ).map_err(Error::Message)? + ).map_err(|_| ErrorKind::Service)? .append1("NetworkManager.service"); - let connection = Connection::get_private(BusType::System).map_err(Error::Connection)?; + let connection = Connection::get_private(BusType::System).map_err(|_| ErrorKind::Service)?; let response = connection .send_with_reply_and_block(message, 2000) - .map_err(Error::Connection)?; + .map_err(|_| ErrorKind::Service)?; - let path = response.get1::().ok_or(Error::NotFound)?; + let path = response.get1::().ok_or(ErrorKind::Service)?; let response = Props::new( &connection, @@ -92,26 +96,26 @@ pub fn get_service_state() -> Result { SD_UNIT_INTERFACE, 2000, ).get("ActiveState") - .map_err(Error::Props)?; + .map_err(|_| ErrorKind::Service)?; response .inner::<&str>() .ok() - .ok_or(Error::NotFound)? + .ok_or(ErrorKind::Service)? .parse() } -fn handler(timeout: u64, target_state: ServiceState) -> Result { +fn handler(timeout: u64, target_state: ServiceState) -> Result { if timeout == 0 { return get_service_state(); } let timer = Timer::default() .sleep(Duration::from_secs(timeout)) - .then(|_| Err(Error::TimedOut)); + .then(|_| bail!(ErrorKind::Service)); let process = CpuPool::new_num_cpus().spawn_fn(|| { - let connection = Connection::get_private(BusType::System).map_err(Error::Connection)?; + let connection = Connection::get_private(BusType::System).map_err(|_| ErrorKind::Service)?; connection .add_match( "type='signal', sender='org.freedesktop.systemd1', \ @@ -119,7 +123,7 @@ fn handler(timeout: u64, target_state: ServiceState) -> Result Result Result, _>>(); - if interface.ok_or(Error::NotFound)? != "org.freedesktop.systemd1.Unit" { + if interface.ok_or(ErrorKind::Service)? != "org.freedesktop.systemd1.Unit" { continue; } - for (k, mut v) in dictionary.ok_or(Error::NotFound)? { + for (k, mut v) in dictionary.ok_or(ErrorKind::Service)? { if k == "ActiveState" { - let response = v.0.get::<&str>().ok_or(Error::NotFound)?; + let response = v.0.get::<&str>().ok_or(ErrorKind::Service)?; let state: ServiceState = response.parse()?; if state == target_state { return Ok(target_state); @@ -157,7 +161,7 @@ fn handler(timeout: u64, target_state: ServiceState) -> Result Result { + fn from_str(s: &str) -> Result { match s { "active" => Ok(ServiceState::Active), "reloading" => Ok(ServiceState::Reloading), @@ -196,7 +190,7 @@ impl FromStr for ServiceState { "failed" => Ok(ServiceState::Failed), "activating" => Ok(ServiceState::Activating), "deactivating" => Ok(ServiceState::Deactivating), - _ => Err(Error::NotFound), + _ => bail!(ErrorKind::Service), } } } diff --git a/src/ssid.rs b/src/ssid.rs index 2e6ecea..ed7f09b 100644 --- a/src/ssid.rs +++ b/src/ssid.rs @@ -5,6 +5,8 @@ use std::fmt; use std::fmt::Write; use std::ascii; +use errors::*; + #[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Ssid { vec: Vec, @@ -15,7 +17,7 @@ impl Ssid { Ssid { vec: Vec::new() } } - pub fn from_bytes(bytes: B) -> Result + pub fn from_bytes(bytes: B) -> Result where B: Into> + AsRef<[u8]>, { @@ -34,7 +36,7 @@ impl Ssid { } pub trait IntoSsid: Sized { - fn into_ssid(self) -> Result; + fn into_ssid(self) -> Result; } impl Deref for Ssid { @@ -58,12 +60,12 @@ pub struct SsidSlice { } pub trait AsSsidSlice { - fn as_ssid_slice(&self) -> Result<&SsidSlice, String>; + fn as_ssid_slice(&self) -> Result<&SsidSlice>; } impl SsidSlice { - pub fn as_str(&self) -> Result<&str, str::Utf8Error> { - str::from_utf8(&self.slice) + pub fn as_str(&self) -> Result<&str> { + Ok(str::from_utf8(&self.slice)?) } pub fn as_bytes(&self) -> &[u8] { @@ -84,12 +86,12 @@ impl fmt::Debug for SsidSlice { } impl AsSsidSlice for [u8] { - fn as_ssid_slice(&self) -> Result<&SsidSlice, String> { + fn as_ssid_slice(&self) -> Result<&SsidSlice> { if self.len() > 32 { - Err(format!( + bail!(ErrorKind::SSID(format!( "SSID length should not exceed 32: {} len", self.len() - )) + ))) } else { Ok(unsafe { mem::transmute(self) }) } @@ -97,7 +99,7 @@ impl AsSsidSlice for [u8] { } impl AsSsidSlice for str { - fn as_ssid_slice(&self) -> Result<&SsidSlice, String> { + fn as_ssid_slice(&self) -> Result<&SsidSlice> { self.as_bytes().as_ssid_slice() } } diff --git a/src/wifi.rs b/src/wifi.rs index 87f103a..3ed878a 100644 --- a/src/wifi.rs +++ b/src/wifi.rs @@ -3,6 +3,7 @@ use std::net::Ipv4Addr; use ascii::AsAsciiStr; +use errors::*; use dbus_nm::DBusNetworkManager; use connection::{connect_to_access_point, create_hotspot, Connection, ConnectionState}; @@ -28,7 +29,7 @@ impl<'a> WiFiDevice<'a> { /// let access_points = device.get_access_points().unwrap(); /// println!("{:?}", access_points); /// ``` - pub fn get_access_points(&self) -> Result, String> { + pub fn get_access_points(&self) -> Result> { let mut access_points = Vec::new(); let paths = self.dbus_manager @@ -50,7 +51,7 @@ impl<'a> WiFiDevice<'a> { &self, access_point: &AccessPoint, password: &P, - ) -> Result<(Connection, ConnectionState), String> + ) -> Result<(Connection, ConnectionState)> where P: AsAsciiStr + ?Sized, { @@ -69,7 +70,7 @@ impl<'a> WiFiDevice<'a> { ssid: &T, password: Option<&U>, address: Option, - ) -> Result<(Connection, ConnectionState), String> + ) -> Result<(Connection, ConnectionState)> where T: AsSsidSlice + ?Sized, U: AsAsciiStr + ?Sized, @@ -161,10 +162,7 @@ pub fn new_wifi_device<'a>( } } -fn get_access_point( - manager: &DBusNetworkManager, - path: &str, -) -> Result, String> { +fn get_access_point(manager: &DBusNetworkManager, path: &str) -> Result> { if let Some(ssid) = manager.get_access_point_ssid(path) { let strength = manager.get_access_point_strength(path)?; @@ -183,7 +181,7 @@ fn get_access_point( } } -fn get_access_point_security(manager: &DBusNetworkManager, path: &str) -> Result { +fn get_access_point_security(manager: &DBusNetworkManager, path: &str) -> Result { let flags = manager.get_access_point_flags(path)?; let wpa_flags = manager.get_access_point_wpa_flags(path)?;