wei_single/
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(any(target_os = "linux", target_os ="android"))]
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(any(target_os = "linux", target_os="android"))]
96mod inner {
97    use std::io::ErrorKind;
98    use std::os::unix::prelude::RawFd;
99    use nix::unistd;
100    use std::os::unix::net::{UnixListener, UnixStream};
101    use std::os::fd::IntoRawFd;
102    use std::io::Result;
103
104    /// A struct representing one running instance.
105    pub struct SingleInstance {
106        socket: Option<RawFd>,
107        single: bool,
108    }
109
110    impl SingleInstance {
111        /// Returns a new SingleInstance object.
112        pub fn new(name: &str) -> Result<Self> {
113            let socket_path = "/tmp/".to_string() + name + ".sock";
114            let single;
115
116            let socket = match UnixListener::bind(&socket_path) {
117                Ok(socket) => {
118                    single = true;
119                    Some(socket.into_raw_fd())
120                },
121                Err(ref e) if e.kind() == ErrorKind::AddrInUse => {
122                    // Try to connect to the socket.
123                    match UnixStream::connect(&socket_path) {
124                        Ok(_) => {
125                            // If the connection is successful, there is another instance running.
126                            single = false;
127                            None
128                        },
129                        Err(_) => {
130                            // If the connection fails, the socket file is orphaned. Delete it and try again.
131                            std::fs::remove_file(&socket_path)?;
132                            match UnixListener::bind(&socket_path) {
133                                Ok(socket) => {
134                                    single = true;
135                                    Some(socket.into_raw_fd())
136                                },
137                                Err(_) => {
138                                    single = false;
139                                    None
140                                },
141                            }
142                        },
143                    }
144                },
145                Err(_) => {
146                    single = false;
147                    None
148                },
149            };
150
151            Ok(Self {
152                socket,
153                single,
154            })
155        }
156
157        /// Returns whether this instance is single.
158        pub fn is_single(&self) -> bool {
159            self.single
160        }
161    }
162
163    impl Drop for SingleInstance {
164        fn drop(&mut self) {
165            if let Some(sock) = self.socket {
166                let _ = unistd::close(sock);
167            }
168        }
169    }
170}
171
172#[cfg(target_os = "macos")]
173mod inner {
174    use error::Result;
175    use libc::{__error, flock, EWOULDBLOCK, LOCK_EX, LOCK_NB};
176    use std::fs::File;
177    use std::os::unix::io::AsRawFd;
178    use std::path::Path;
179
180    /// A struct representing one running instance.
181    pub struct SingleInstance {
182        _file: File,
183        is_single: bool,
184    }
185
186    impl SingleInstance {
187        /// Returns a new SingleInstance object.
188        pub fn new(name: &str) -> Result<Self> {
189            let path = Path::new(name);
190            let file = if path.exists() {
191                File::open(path)?
192            } else {
193                File::create(path)?
194            };
195            unsafe {
196                let rc = flock(file.as_raw_fd(), LOCK_EX | LOCK_NB);
197                let is_single = rc == 0 || EWOULDBLOCK != *__error();
198                Ok(Self {
199                    _file: file,
200                    is_single,
201                })
202            }
203        }
204
205        /// Returns whether this instance is single.
206        pub fn is_single(&self) -> bool {
207            self.is_single
208        }
209    }
210}
211
212#[test]
213fn test_single_instance() {
214    {
215        let instance_a = SingleInstance::new("aa2d0258-ffe9-11e7-ba89-0ed5f89f718b").unwrap();
216        assert!(instance_a.is_single());
217        let instance_b = SingleInstance::new("aa2d0258-ffe9-11e7-ba89-0ed5f89f718b").unwrap();
218        assert!(!instance_b.is_single());
219    }
220    let instance_c = SingleInstance::new("aa2d0258-ffe9-11e7-ba89-0ed5f89f718b").unwrap();
221    assert!(instance_c.is_single());
222}