diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/error.rs | 16 | ||||
| -rw-r--r-- | src/lib.rs | 66 | ||||
| -rw-r--r-- | tests/lib.rs | 16 | 
5 files changed, 92 insertions, 8 deletions
| diff --git a/CHANGELOG.md b/CHANGELOG.md index 046b609..e67fd81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ SPDX-License-Identifier: MIT    `nitrokey-test-state` dependency in version 0.1.0.  - Refactor connection management:    - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. +  - Add the `Manager` struct that manages connections to Nitrokey devices.  # v0.3.4 (2019-01-20)  - Fix authentication methods that assumed that `char` is signed. @@ -17,6 +17,7 @@ license = "MIT"  exclude = [".builds/*"]  [dependencies] +lazy_static = "1.2.0"  libc = "0.2"  nitrokey-sys = "3.5"  rand_core = {version = "0.3", default-features = false, features = ["std"] } diff --git a/src/error.rs b/src/error.rs index c6b19db..b84f5eb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,7 @@ pub enum Error {      /// A library usage error.      LibraryError(LibraryError),      /// An error that occurred due to a poisoned lock. -    PoisonError, +    PoisonError(sync::PoisonError<sync::MutexGuard<'static, crate::Manager>>),      /// An error that occurred during random number generation.      RandError(Box<dyn error::Error>),      /// An error that is caused by an unexpected value returned by libnitrokey. @@ -70,14 +70,14 @@ impl From<str::Utf8Error> for Error {      }  } -impl<T> From<sync::PoisonError<T>> for Error { -    fn from(_error: sync::PoisonError<T>) -> Self { -        Error::PoisonError +impl From<sync::PoisonError<sync::MutexGuard<'static, crate::Manager>>> for Error { +    fn from(error: sync::PoisonError<sync::MutexGuard<'static, crate::Manager>>) -> Self { +        Error::PoisonError(error)      }  } -impl<T> From<sync::TryLockError<T>> for Error { -    fn from(error: sync::TryLockError<T>) -> Self { +impl From<sync::TryLockError<sync::MutexGuard<'static, crate::Manager>>> for Error { +    fn from(error: sync::TryLockError<sync::MutexGuard<'static, crate::Manager>>) -> Self {          match error {              sync::TryLockError::Poisoned(err) => err.into(),              sync::TryLockError::WouldBlock => Error::ConcurrentAccessError, @@ -98,7 +98,7 @@ impl error::Error for Error {              Error::CommunicationError(ref err) => Some(err),              Error::ConcurrentAccessError => None,              Error::LibraryError(ref err) => Some(err), -            Error::PoisonError => None, +            Error::PoisonError(ref err) => Some(err),              Error::RandError(ref err) => Some(err.as_ref()),              Error::UnexpectedError => None,              Error::UnknownError(_) => None, @@ -114,7 +114,7 @@ impl fmt::Display for Error {              Error::CommunicationError(ref err) => write!(f, "Communication error: {}", err),              Error::ConcurrentAccessError => write!(f, "Internal error: concurrent access"),              Error::LibraryError(ref err) => write!(f, "Library error: {}", err), -            Error::PoisonError => write!(f, "Internal error: poisoned lock"), +            Error::PoisonError(_) => write!(f, "Internal error: poisoned lock"),              Error::RandError(ref err) => write!(f, "RNG error: {}", err),              Error::UnexpectedError => write!(f, "An unexpected error occurred"),              Error::UnknownError(ref err) => write!(f, "Unknown error: {}", err), @@ -89,6 +89,9 @@  #![warn(missing_docs, rust_2018_compatibility, rust_2018_idioms, unused)] +#[macro_use(lazy_static)] +extern crate lazy_static; +  mod auth;  mod config;  mod device; @@ -98,6 +101,8 @@ mod pws;  mod util;  use std::fmt; +use std::marker; +use std::sync;  use nitrokey_sys; @@ -117,6 +122,10 @@ pub const DEFAULT_ADMIN_PIN: &str = "12345678";  /// The default user PIN for all Nitrokey devices.  pub const DEFAULT_USER_PIN: &str = "123456"; +lazy_static! { +    static ref MANAGER: sync::Mutex<Manager> = sync::Mutex::new(Manager::new()); +} +  /// A version of the libnitrokey library.  ///  /// Use the [`get_library_version`](fn.get_library_version.html) function to query the library @@ -147,6 +156,63 @@ impl fmt::Display for Version {      }  } +/// A manager for connections to Nitrokey devices. +/// +/// Currently, libnitrokey only provides access to one Nitrokey device at the same time.  This +/// manager struct makes sure that `nitrokey-rs` does not try to connect to two devices at the same +/// time. +/// +/// To obtain an instance of this manager, use the [`take`][] function. +/// +/// [`take`]: fn.take.html +#[derive(Debug)] +pub struct Manager { +    marker: marker::PhantomData<()>, +} + +impl Manager { +    fn new() -> Self { +        Manager { +            marker: marker::PhantomData, +        } +    } +} + +/// Take an instance of the connection manager, blocking until an instance is available. +/// +/// There may only be one [`Manager`][] instance at the same time.  If there already is an +/// instance, this method blocks.  If you want a non-blocking version, use [`take`][]. +/// +/// # Errors +/// +/// - [`PoisonError`][] if the lock is poisoned +/// +/// [`take`]: fn.take.html +/// [`PoisonError`]: struct.Error.html#variant.PoisonError +/// [`Manager`]: struct.Manager.html +pub fn take_blocking() -> Result<sync::MutexGuard<'static, Manager>, Error> { +    MANAGER.lock().map_err(Into::into) +} + +/// Try to take an instance of the connection manager. +/// +/// There may only be one [`Manager`][] instance at the same time.  If there already is an +/// instance, a [`ConcurrentAccessError`][] is returned.  If you want a blocking version, use +/// [`take_blocking`][]. +/// +/// # Errors +/// +/// - [`ConcurrentAccessError`][] if the token for the `Manager` instance cannot be locked +/// - [`PoisonError`][] if the lock is poisoned +/// +/// [`take_blocking`]: fn.take_blocking.html +/// [`ConcurrentAccessError`]: struct.Error.html#variant.ConcurrentAccessError +/// [`PoisonError`]: struct.Error.html#variant.PoisonError +/// [`Manager`]: struct.Manager.html +pub fn take() -> Result<sync::MutexGuard<'static, Manager>, Error> { +    MANAGER.try_lock().map_err(Into::into) +} +  /// Enables or disables debug output.  Calling this method with `true` is equivalent to setting the  /// log level to `Debug`; calling it with `false` is equivalent to the log level `Error` (see  /// [`set_log_level`][]). diff --git a/tests/lib.rs b/tests/lib.rs index 8ab75f6..25aae0f 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -10,3 +10,19 @@ fn get_library_version() {      assert!(version.git.is_empty() || version.git.starts_with("v"));      assert!(version.major > 0);  } + +#[test] +fn take_manager() { +    assert!(nitrokey::take().is_ok()); + +    let result = nitrokey::take(); +    assert!(result.is_ok()); +    let result2 = nitrokey::take(); +    match result2 { +        Ok(_) => panic!("Expected error, got Ok(_)!"), +        Err(nitrokey::Error::ConcurrentAccessError) => {} +        Err(err) => panic!("Expected ConcurrentAccessError, got {}", err), +    } +    drop(result); +    assert!(nitrokey::take().is_ok()); +} | 
