From 5f434a7249e81556523509787c22d5897f6d91d1 Mon Sep 17 00:00:00 2001 From: Paul Hummer Date: Fri, 18 May 2018 19:37:59 -0600 Subject: Initial commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ♬ : Midtown / Like a Movie --- .gitignore | 4 ++ .travis.yml | 10 ++++ Cargo.toml | 23 +++++++++ LICENSE-MIT | 25 ++++++++++ README.md | 25 ++++++++++ src/lib.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 242 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Cargo.toml create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 src/lib.rs 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 "] +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"); + } + } + } +} -- cgit v1.2.3