single_instance/
lib.rs

1//! A rust library for single instance application.
2//!
3//! single-instance provides a single API to check if there are any other running instance.
4//!
5//! ## Detail
6//! On windows, init `SingleInstance` will create a mutex named by given `&str` then check error code by calling `GetLastError`.
7//! On linux init will bind abstract unix domain socket with given name . On macos, init will create or open a file which path is given `&str`,
8//! then call `flock` to apply an advisory lock on the open file.
9//!
10//! ### Examples
11//! ```rust
12//! extern crate single_instance;
13//!
14//! use std::thread;
15//! use single_instance::SingleInstance;
16//!
17//! fn main() {
18//!     let instance = SingleInstance::new("whatever").unwrap();
19//!     assert!(instance.is_single());
20//! }
21//! ```
22
23pub mod error;
24
25#[cfg(target_os = "macos")]
26extern crate libc;
27#[cfg(target_os = "linux")]
28extern crate nix;
29extern crate thiserror;
30#[cfg(target_os = "windows")]
31extern crate widestring;
32#[cfg(target_os = "windows")]
33extern crate winapi;
34
35pub use self::inner::*;
36
37#[cfg(target_os = "windows")]
38mod inner {
39    use error::{Result, SingleInstanceError};
40    use std::ptr;
41    use widestring::WideCString;
42    use winapi::shared::winerror::{ERROR_ALREADY_EXISTS, ERROR_INVALID_HANDLE};
43    use winapi::um::errhandlingapi::GetLastError;
44    use winapi::um::handleapi::CloseHandle;
45    use winapi::um::synchapi::CreateMutexW;
46    use winapi::um::winnt::HANDLE;
47
48    /// A struct representing one running instance.
49    pub struct SingleInstance {
50        handle: Option<HANDLE>,
51    }
52
53    unsafe impl Send for SingleInstance {}
54    unsafe impl Sync for SingleInstance {}
55
56    impl SingleInstance {
57        /// Returns a new SingleInstance object.
58        pub fn new(name: &str) -> Result<Self> {
59            let name = WideCString::from_str(name)?;
60            unsafe {
61                let handle = CreateMutexW(ptr::null_mut(), 0, name.as_ptr());
62                let last_error = GetLastError();
63
64                // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexexw
65                if handle.is_null() || handle == ERROR_INVALID_HANDLE as _ {
66                    Err(SingleInstanceError::MutexError(last_error))
67                } else if last_error == ERROR_ALREADY_EXISTS {
68                    CloseHandle(handle);
69                    Ok(SingleInstance { handle: None })
70                } else {
71                    Ok(SingleInstance {
72                        handle: Some(handle),
73                    })
74                }
75            }
76        }
77
78        /// Returns whether this instance is single.
79        pub fn is_single(&self) -> bool {
80            self.handle.is_some()
81        }
82    }
83
84    impl Drop for SingleInstance {
85        fn drop(&mut self) {
86            if let Some(handle) = self.handle.take() {
87                unsafe {
88                    CloseHandle(handle);
89                }
90            }
91        }
92    }
93}
94
95#[cfg(target_os = "linux")]
96mod inner {
97    use error::Result;
98    use nix::sys::socket::{self, UnixAddr};
99    use nix::unistd;
100    use std::os::unix::prelude::RawFd;
101
102    /// A struct representing one running instance.
103    pub struct SingleInstance {
104        maybe_sock: Option<RawFd>,
105    }
106
107    impl SingleInstance {
108        /// Returns a new SingleInstance object.
109        pub fn new(name: &str) -> Result<Self> {
110            let addr = UnixAddr::new_abstract(name.as_bytes())?;
111            let sock = socket::socket(
112                socket::AddressFamily::Unix,
113                socket::SockType::Stream,
114                socket::SockFlag::empty(),
115                None,
116            )?;
117
118            let maybe_sock = match socket::bind(sock, &socket::SockAddr::Unix(addr)) {
119                Ok(()) => Some(sock),
120                Err(nix::errno::Errno::EADDRINUSE) => None,
121                Err(e) => return Err(e.into()),
122            };
123
124            Ok(Self { maybe_sock })
125        }
126
127        /// Returns whether this instance is single.
128        pub fn is_single(&self) -> bool {
129            self.maybe_sock.is_some()
130        }
131    }
132
133    impl Drop for SingleInstance {
134        fn drop(&mut self) {
135            if let Some(sock) = self.maybe_sock {
136                // Intentionally discard any close errors.
137                let _ = unistd::close(sock);
138            }
139        }
140    }
141}
142
143#[cfg(target_os = "macos")]
144mod inner {
145    use error::Result;
146    use libc::{__error, flock, EWOULDBLOCK, LOCK_EX, LOCK_NB};
147    use std::fs::File;
148    use std::os::unix::io::AsRawFd;
149    use std::path::Path;
150
151    /// A struct representing one running instance.
152    pub struct SingleInstance {
153        _file: File,
154        is_single: bool,
155    }
156
157    impl SingleInstance {
158        /// Returns a new SingleInstance object.
159        pub fn new(name: &str) -> Result<Self> {
160            let path = Path::new(name);
161            let file = if path.exists() {
162                File::open(path)?
163            } else {
164                File::create(path)?
165            };
166            unsafe {
167                let rc = flock(file.as_raw_fd(), LOCK_EX | LOCK_NB);
168                let is_single = rc == 0 || EWOULDBLOCK != *__error();
169                Ok(Self {
170                    _file: file,
171                    is_single,
172                })
173            }
174        }
175
176        /// Returns whether this instance is single.
177        pub fn is_single(&self) -> bool {
178            self.is_single
179        }
180    }
181}
182
183#[test]
184fn test_single_instance() {
185    {
186        let instance_a = SingleInstance::new("aa2d0258-ffe9-11e7-ba89-0ed5f89f718b").unwrap();
187        assert!(instance_a.is_single());
188        let instance_b = SingleInstance::new("aa2d0258-ffe9-11e7-ba89-0ed5f89f718b").unwrap();
189        assert!(!instance_b.is_single());
190    }
191    let instance_c = SingleInstance::new("aa2d0258-ffe9-11e7-ba89-0ed5f89f718b").unwrap();
192    assert!(instance_c.is_single());
193}