diff --git a/Cargo.lock b/Cargo.lock index a70811b27..4cd867166 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,6 +366,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "futures" version = "0.3.21" @@ -661,6 +671,7 @@ dependencies = [ "chrono", "clap", "env_logger", + "fs2", "futures", "ipnet", "iptables", diff --git a/Cargo.toml b/Cargo.toml index 611b54479..f489e8720 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ tokio = { version = "1.20.1", features = ["full"] } zvariant = "3.4.1" sha2 = "0.10.1" netlink-packet-route = "0.13" +fs2 = "0.4.3" [build-dependencies] chrono = "0.4.20" diff --git a/src/dns/aardvark.rs b/src/dns/aardvark.rs index fb16c0ef2..ce6a565d6 100644 --- a/src/dns/aardvark.rs +++ b/src/dns/aardvark.rs @@ -1,4 +1,5 @@ use crate::network::types; +use fs2::FileExt; use nix::sys::signal::{self, Signal}; use nix::unistd::Pid; use std::collections::HashMap; @@ -14,6 +15,7 @@ use std::process::{Command, Stdio}; const SYSTEMD_CHECK_PATH: &str = "/run/systemd/system"; const SYSTEMD_RUN: &str = "systemd-run"; +const AARDVARK_COMMIT_LOCK: &str = "aardvark.lock"; #[derive(Clone, Debug)] pub struct AardvarkEntry { @@ -144,6 +146,36 @@ impl Aardvark { Ok(()) } pub fn commit_entries(&self, entries: Vec) -> Result<()> { + // Acquire fs lock to ensure other instance of aardvark cannot commit + // or start aardvark instance till already running instance has not + // completed its `commit` phase. + let lockfile_path = Path::new(&self.config) + .join("..") + .join(AARDVARK_COMMIT_LOCK); + let lockfile = match OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(lockfile_path.clone()) + { + Ok(file) => file, + Err(e) => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to open/create lockfile {:?}: {}", lockfile_path, e), + )); + } + }; + if let Err(er) = lockfile.lock_exclusive() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "Failed to acquire exclusive lock on {:?}: {}", + lockfile_path, er + ), + )); + } + for entry in &entries { let path = Path::new(&self.config).join(&entry.network_name); if !path.exists() { @@ -162,6 +194,16 @@ impl Aardvark { } match self.commit_entry(entry) { Err(er) => { + // drop lockfile when commit is completed + if let Err(er) = lockfile.unlock() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "Failed to unlock exclusive lock on {:?}: {}", + lockfile_path, er + ), + )); + } return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("Failed to commit entry {:?}: {}", entry, er), @@ -171,6 +213,16 @@ impl Aardvark { } } + // drop lockfile when commit is completed + if let Err(er) = lockfile.unlock() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "Failed to unlock exclusive lock on {:?}: {}", + lockfile_path, er + ), + )); + } Ok(()) }