From 97f549cf5f3287ad0a4f37982d80641ed03cd6c6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 18 Feb 2016 12:42:01 +0100 Subject: [PATCH] Add daemonization. --- Cargo.lock | 9 ++++ Cargo.toml | 1 + parity/main.rs | 122 ++++++++++++++++++++++++++++++------------------- 3 files changed, 86 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e9f57900..87a599f3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,6 +4,7 @@ version = "0.9.99" dependencies = [ "clippy 0.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 1.0.1 (git+https://github.com/tomusdrw/rust-ctrlc.git)", + "daemonize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.78 (registry+https://github.com/rust-lang/crates.io-index)", "docopt_macros 0.6.81 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -108,6 +109,14 @@ dependencies = [ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "daemonize" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "docopt" version = "0.6.78" diff --git a/Cargo.toml b/Cargo.toml index c58cacf0d..828ed32e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ ethsync = { path = "sync" } ethcore-rpc = { path = "rpc", optional = true } fdlimit = { path = "util/fdlimit" } target_info = "0.1" +daemonize = "0.2" [features] default = ["rpc"] diff --git a/parity/main.rs b/parity/main.rs index 460922b64..d0bc05a08 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -25,11 +25,13 @@ extern crate rustc_serialize; extern crate ethcore_util as util; extern crate ethcore; extern crate ethsync; +#[macro_use] extern crate log as rlog; extern crate env_logger; extern crate ctrlc; extern crate fdlimit; extern crate target_info; +extern crate daemonize; #[cfg(feature = "rpc")] extern crate ethcore_rpc as rpc; @@ -48,6 +50,7 @@ use ethcore::ethereum; use ethcore::blockchain::CacheSize; use ethsync::EthSync; use target_info::Target; +use daemonize::{Daemonize}; docopt!(Args derive Debug, " Parity. Ethereum Client. @@ -55,12 +58,14 @@ Parity. Ethereum Client. Copyright 2015, 2016 Ethcore (UK) Limited Usage: + parity daemon [options] [ --no-bootstrap | ... ] parity [options] [ --no-bootstrap | ... ] Options: --chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file - or frontier, mainnet, morden, or testnet [default: frontier]. + or frontier, mainnet, morden, or testnet [default: frontier]. -d --db-path PATH Specify the database & configuration directory path [default: $HOME/.parity] + --keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys] --no-bootstrap Don't bother trying to connect to any nodes initially. --listen-address URL Specify the IP/port on which to listen for peers [default: 0.0.0.0:30304]. @@ -136,6 +141,10 @@ impl Configuration { self.args.flag_db_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap()) } + fn keys_path(&self) -> String { + self.args.flag_keys_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap()) + } + fn spec(&self) -> Spec { match self.args.flag_chain.as_ref() { "frontier" | "mainnet" => ethereum::new_frontier(), @@ -171,6 +180,71 @@ impl Configuration { (listen_address, public_address) } + + fn execute(&self) { + if self.args.flag_version { + print_version(); + return; + } + if self.args.cmd_daemon { + let daemonize = Daemonize::new() + .pid_file("/tmp/parity.pid") // Every method except `new` and `start` + .chown_pid_file(true) // is optional, see `Daemonize` documentation + .working_directory("/tmp") // for default behaviour. + .user("nobody") + .group("daemon") // Group name + .group(2) // Or group id + .privileged_action(|| "Executed before drop privileges"); + + match daemonize.start() { + Ok(_) => info!("Success, daemonized"), + Err(e) => { error!("{}", e); return; }, + } + } + self.execute_client(); + } + + fn execute_client(&self) { + // Setup logging + setup_log(&self.args.flag_logging); + // Raise fdlimit + unsafe { ::fdlimit::raise_fd_limit(); } + + let spec = self.spec(); + + // Configure network + let mut net_settings = NetworkConfiguration::new(); + net_settings.nat_enabled = self.args.flag_upnp; + net_settings.boot_nodes = self.init_nodes(&spec); + let (listen, public) = self.net_addresses(); + net_settings.listen_address = listen; + net_settings.public_address = public; + net_settings.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).expect("Invalid key string")); + + // Build client + let mut service = ClientService::start(spec, net_settings, &Path::new(&self.path())).unwrap(); + let client = service.client().clone(); + client.configure_cache(self.args.flag_cache_pref_size, self.args.flag_cache_max_size); + + // Sync + let sync = EthSync::register(service.network(), client); + + // Setup rpc + if self.args.flag_jsonrpc { + setup_rpc_server(service.client(), sync.clone(), &self.args.flag_jsonrpc_url); + } + + // Register IO handler + let io_handler = Arc::new(ClientIoHandler { + client: service.client(), + info: Default::default(), + sync: sync + }); + service.io().register_handler(io_handler).expect("Error registering IO handler"); + + // Handle exit + wait_for_exit(&service); + } } fn wait_for_exit(client_service: &ClientService) { @@ -186,51 +260,7 @@ fn wait_for_exit(client_service: &ClientService) { } fn main() { - let conf = Configuration::parse(); - if conf.args.flag_version { - print_version(); - return; - } - - let spec = conf.spec(); - - // Setup logging - setup_log(&conf.args.flag_logging); - // Raise fdlimit - unsafe { ::fdlimit::raise_fd_limit(); } - - // Configure network - let mut net_settings = NetworkConfiguration::new(); - net_settings.nat_enabled = conf.args.flag_upnp; - net_settings.boot_nodes = conf.init_nodes(&spec); - let (listen, public) = conf.net_addresses(); - net_settings.listen_address = listen; - net_settings.public_address = public; - net_settings.use_secret = conf.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).expect("Invalid key string")); - - // Build client - let mut service = ClientService::start(spec, net_settings, &Path::new(&conf.path())).unwrap(); - let client = service.client().clone(); - client.configure_cache(conf.args.flag_cache_pref_size, conf.args.flag_cache_max_size); - - // Sync - let sync = EthSync::register(service.network(), client); - - // Setup rpc - if conf.args.flag_jsonrpc { - setup_rpc_server(service.client(), sync.clone(), &conf.args.flag_jsonrpc_url); - } - - // Register IO handler - let io_handler = Arc::new(ClientIoHandler { - client: service.client(), - info: Default::default(), - sync: sync - }); - service.io().register_handler(io_handler).expect("Error registering IO handler"); - - // Handle exit - wait_for_exit(&service); + Configuration::parse().execute(); } struct Informant {