use fs2 advisory file lock on startup (#2600)

This commit is contained in:
Antioch Peverell 2019-02-19 13:23:12 +00:00 committed by GitHub
parent 5dc01b3de6
commit 48b7421d1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 1 deletions

11
Cargo.lock generated
View file

@ -669,6 +669,15 @@ name = "foreign-types-shared"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "fuchsia-cprng" name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
@ -947,6 +956,7 @@ dependencies = [
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_api 1.0.1", "grin_api 1.0.1",
"grin_chain 1.0.1", "grin_chain 1.0.1",
@ -3215,6 +3225,7 @@ dependencies = [
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
"checksum fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"

View file

@ -11,6 +11,7 @@ edition = "2018"
[dependencies] [dependencies]
hyper = "0.12" hyper = "0.12"
fs2 = "0.4"
futures = "0.1" futures = "0.1"
http = "0.1" http = "0.1"
hyper-staticfile = "0.3" hyper-staticfile = "0.3"

View file

@ -48,6 +48,8 @@ pub enum Error {
Pool(pool::PoolError), Pool(pool::PoolError),
/// Invalid Arguments. /// Invalid Arguments.
ArgumentError(String), ArgumentError(String),
/// Error originating from some I/O operation (likely a file on disk).
IOError(std::io::Error),
} }
impl From<core::block::Error> for Error { impl From<core::block::Error> for Error {
@ -60,7 +62,11 @@ impl From<chain::Error> for Error {
Error::Chain(e) Error::Chain(e)
} }
} }
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Error {
Error::IOError(e)
}
}
impl From<p2p::Error> for Error { impl From<p2p::Error> for Error {
fn from(e: p2p::Error) -> Error { fn from(e: p2p::Error) -> Error {
Error::P2P(e) Error::P2P(e)

View file

@ -16,9 +16,15 @@
//! the peer-to-peer server, the blockchain and the transaction pool) and acts //! the peer-to-peer server, the blockchain and the transaction pool) and acts
//! as a facade. //! as a facade.
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::{thread, time}; use std::{thread, time};
use fs2::FileExt;
use crate::api; use crate::api;
use crate::api::TLSConfig; use crate::api::TLSConfig;
use crate::chain; use crate::chain;
@ -59,6 +65,8 @@ pub struct Server {
state_info: ServerStateInfo, state_info: ServerStateInfo,
/// Stop flag /// Stop flag
pub stop_state: Arc<Mutex<StopState>>, pub stop_state: Arc<Mutex<StopState>>,
/// Maintain a lock_file so we do not run multiple Grin nodes from same dir.
lock_file: Arc<File>,
} }
impl Server { impl Server {
@ -102,8 +110,36 @@ impl Server {
} }
} }
// Exclusive (advisory) lock_file to ensure we do not run multiple
// instance of grin server from the same dir.
// This uses fs2 and should be safe cross-platform unless somebody abuses the file itself.
fn one_grin_at_a_time(config: &ServerConfig) -> Result<Arc<File>, Error> {
let path = Path::new(&config.db_root);
fs::create_dir_all(path.clone())?;
let path = path.join("grin.lock");
let lock_file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)?;
lock_file.try_lock_exclusive().map_err(|e| {
let mut stderr = std::io::stderr();
writeln!(
&mut stderr,
"Failed to lock {:?} (grin server already running?)",
path
)
.expect("Could not write to stderr");
e
})?;
Ok(Arc::new(lock_file))
}
/// Instantiates a new server associated with the provided future reactor. /// Instantiates a new server associated with the provided future reactor.
pub fn new(config: ServerConfig) -> Result<Server, Error> { pub fn new(config: ServerConfig) -> Result<Server, Error> {
// Obtain our lock_file or fail immediately with an error.
let lock_file = Server::one_grin_at_a_time(&config)?;
// Defaults to None (optional) in config file. // Defaults to None (optional) in config file.
// This translates to false here. // This translates to false here.
let archive_mode = match config.archive_mode { let archive_mode = match config.archive_mode {
@ -264,6 +300,7 @@ impl Server {
..Default::default() ..Default::default()
}, },
stop_state, stop_state,
lock_file,
}) })
} }
@ -451,6 +488,7 @@ impl Server {
pub fn stop(&self) { pub fn stop(&self) {
self.p2p.stop(); self.p2p.stop();
self.stop_state.lock().stop(); self.stop_state.lock().stop();
let _ = self.lock_file.unlock();
} }
/// Pause the p2p server. /// Pause the p2p server.