summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--Cargo.toml1
-rw-r--r--src/error.rs16
-rw-r--r--src/lib.rs66
-rw-r--r--tests/lib.rs16
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.
diff --git a/Cargo.toml b/Cargo.toml
index d26bd88..7daadd1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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),
diff --git a/src/lib.rs b/src/lib.rs
index c35829c..573f45f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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());
+}