summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Hummer <paul@eventuallyanyway.com>2018-05-18 19:37:59 -0600
committerPaul Hummer <paul@eventuallyanyway.com>2018-05-18 19:37:59 -0600
commit5f434a7249e81556523509787c22d5897f6d91d1 (patch)
treea301808d81df39627c53efa236966c3a0d92f5d3
Initial commit
♬ : Midtown / Like a Movie
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml10
-rw-r--r--Cargo.toml23
-rw-r--r--LICENSE-MIT25
-rw-r--r--README.md25
-rw-r--r--src/lib.rs155
6 files changed, 242 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a821aa9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+
+/target
+**/*.rs.bk
+Cargo.lock
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..5c5f42a
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: rust
+sudo: false
+rust:
+ - stable
+ - beta
+ - nightly
+matrix:
+ allow_failures:
+ - rust: beta
+ - rust: nightly
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..5472a16
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "pidlock"
+version = "0.1.0"
+authors = ["Paul Hummer <paul@eventuallyanyway.com>"]
+license = "MIT"
+description = "A library for using pidfiles as resource locks"
+repository = "https://github.com/rockstar/pidlock"
+keywords = ["pidfile", "file", "filelock", "server", "lock"]
+categories = ["Filesystem"]
+readme = "README.md"
+include = [
+ "src/*.rs",
+ "Cargo.toml",
+ "README.md",
+ "LICENSE-MIT",
+ "LICENSE-APACHE",
+]
+
+
+[dependencies]
+libc = "0.2.40"
+
+travis-ci = { repository = "rockstar/pidlock" }
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..39d4bdb
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2014 The Rust Project Developers
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c5ca11d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,25 @@
+pidlock
+==
+
+A library for working with pidfiles, with a lock-like API.
+
+Usage
+--
+
+```
+extern crate pidlock;
+
+fn main() {
+ let mut lock = pidlock::Pidlock("/path/to/pidfile.pid");
+ lock.acquire().unwrap();
+
+ ...
+
+ lock.release().unwrap();
+}
+```
+
+License
+--
+
+pidlock is licensed under the MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..68bf435
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,155 @@
+extern crate libc;
+
+use std::fs;
+use std::io::Write;
+
+#[derive(Debug, PartialEq)]
+pub enum PidlockError {
+ LockExists,
+ InvalidState,
+}
+
+type PidlockResult = Result<(), PidlockError>;
+
+#[derive(Debug, PartialEq)]
+enum PidlockState {
+ New,
+ Acquired,
+ Released,
+}
+
+fn getpid() -> u32 {
+ unsafe { libc::getpid() as u32 }
+}
+
+pub struct Pidlock {
+ pid: u32,
+ path: String,
+ state: PidlockState,
+}
+
+impl Pidlock {
+ pub fn new(path: &str) -> Self {
+ Pidlock {
+ pid: getpid(),
+ path: path.to_string(),
+ state: PidlockState::New,
+ }
+ }
+
+ pub fn acquire(&mut self) -> PidlockResult {
+ match self.state {
+ PidlockState::New => {}
+ _ => {
+ return Err(PidlockError::InvalidState);
+ }
+ }
+
+ let mut file = match fs::OpenOptions::new()
+ .create_new(true)
+ .write(true)
+ .read(true)
+ .open(self.path.clone())
+ {
+ Ok(file) => file,
+ Err(_) => {
+ return Err(PidlockError::LockExists);
+ }
+ };
+ file.write(&format!("{}", self.pid).into_bytes()[..])
+ .unwrap();
+
+ self.state = PidlockState::Acquired;
+ Ok(())
+ }
+
+ pub fn release(&mut self) -> PidlockResult {
+ match self.state {
+ PidlockState::Acquired => {}
+ _ => {
+ return Err(PidlockError::InvalidState);
+ }
+ }
+
+ fs::remove_file(self.path.clone()).unwrap();
+
+ self.state = PidlockState::Released;
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{Pidlock, PidlockError};
+ use super::{getpid, PidlockState};
+
+ const TEST_PID: &str = "/tmp/test.pid";
+
+ #[test]
+ fn test_new() {
+ let pidfile = Pidlock::new(TEST_PID);
+
+ assert_eq!(pidfile.pid, getpid());
+ assert_eq!(pidfile.path, "/tmp/test.pid".to_string());
+ assert_eq!(pidfile.state, PidlockState::New);
+ }
+
+ #[test]
+ fn test_acquire_and_release() {
+ let mut pidfile = Pidlock::new(TEST_PID);
+ pidfile.acquire().unwrap();
+
+ assert_eq!(pidfile.state, PidlockState::Acquired);
+
+ pidfile.release().unwrap();
+
+ assert_eq!(pidfile.state, PidlockState::Released);
+ }
+
+ #[test]
+ fn test_acquire_lock_exists() {
+ let mut orig_pidfile = Pidlock::new(TEST_PID);
+ orig_pidfile.acquire().unwrap();
+
+ let mut pidfile = Pidlock::new(TEST_PID);
+ match pidfile.acquire() {
+ Err(err) => {
+ orig_pidfile.release().unwrap();
+ assert_eq!(err, PidlockError::LockExists);
+ }
+ _ => {
+ orig_pidfile.release().unwrap();
+ panic!("Test failed");
+ }
+ }
+ }
+
+ #[test]
+ fn test_acquire_already_acquired() {
+ let mut pidfile = Pidlock::new(TEST_PID);
+ pidfile.acquire().unwrap();
+ match pidfile.acquire() {
+ Err(err) => {
+ pidfile.release().unwrap();
+ assert_eq!(err, PidlockError::InvalidState);
+ }
+ _ => {
+ pidfile.release().unwrap();
+ panic!("Test failed");
+ }
+ }
+ }
+
+ #[test]
+ fn test_release_bad_state() {
+ let mut pidfile = Pidlock::new(TEST_PID);
+ match pidfile.release() {
+ Err(err) => {
+ assert_eq!(err, PidlockError::InvalidState);
+ }
+ _ => {
+ panic!("Test failed");
+ }
+ }
+ }
+}