diff --git a/Cargo.lock b/Cargo.lock index ce8b517a0..7859da472 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 516609615..4d31ae142 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -18,18 +18,19 @@ #![warn(missing_docs)] #![feature(plugin)] -#![plugin(docopt_macros)] #![plugin(clippy)] extern crate docopt; 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,20 +49,24 @@ use ethcore::service::{ClientService, NetSyncMessage}; use ethcore::ethereum; use ethcore::blockchain::CacheSize; use ethsync::EthSync; +use docopt::Docopt; use target_info::Target; +use daemonize::Daemonize; -docopt!(Args derive Debug, " +const USAGE: &'static str = " Parity. Ethereum Client. By Wood/Paronyan/Kotewicz/Drwięga/Volf. 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]. -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]. @@ -81,14 +86,33 @@ Options: -l --logging LOGGING Specify the logging level. -v --version Show information about version. -h --help Show this screen. -", flag_cache_pref_size: usize, - flag_cache_max_size: usize, - flag_peers: u32, - flag_address: Option, - flag_public_address: Option, - flag_node_key: Option); +"; -fn setup_log(init: &str) { +#[derive(Debug, RustcDecodable)] +struct Args { + cmd_daemon: bool, + arg_pid_file: String, + arg_enode: Vec, + flag_chain: String, + flag_db_path: String, + flag_keys_path: String, + flag_no_bootstrap: bool, + flag_listen_address: String, + flag_public_address: Option, + flag_address: Option, + flag_peers: u32, + flag_no_discovery: bool, + flag_upnp: bool, + flag_node_key: Option, + flag_cache_pref_size: usize, + flag_cache_max_size: usize, + flag_jsonrpc: bool, + flag_jsonrpc_url: String, + flag_logging: Option, + flag_version: bool, +} + +fn setup_log(init: &Option) { let mut builder = LogBuilder::new(); builder.filter(None, LogLevelFilter::Info); @@ -96,7 +120,9 @@ fn setup_log(init: &str) { builder.parse(&env::var("RUST_LOG").unwrap()); } - builder.parse(init); + if let Some(ref s) = *init { + builder.parse(s); + } builder.init().unwrap(); } @@ -136,7 +162,7 @@ struct Configuration { impl Configuration { fn parse() -> Self { Configuration { - args: Args::docopt().decode().unwrap_or_else(|e| e.exit()) + args: Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()), } } @@ -144,6 +170,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(), @@ -157,7 +187,7 @@ impl Configuration { if self.args.flag_no_bootstrap { Vec::new() } else { match self.args.arg_enode.len() { 0 => spec.nodes().clone(), - _ => self.args.arg_enode.clone(), + _ => self.args.arg_enode.clone(), // TODO check format first. } } } @@ -181,6 +211,68 @@ 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(self.args.arg_pid_file.clone()).chown_pid_file(true); + match daemonize.start() { + Ok(_) => info!("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")); + net_settings.discovery_enabled = !self.args.flag_no_discovery; + net_settings.ideal_peers = self.args.flag_peers; + let mut net_path = PathBuf::from(&self.path()); + net_path.push("network"); + net_settings.config_path = Some(net_path.to_str().unwrap().to_owned()); + + // 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) { @@ -196,56 +288,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")); - net_settings.discovery_enabled = !conf.args.flag_no_discovery; - net_settings.ideal_peers = conf.args.flag_peers; - let mut net_path = PathBuf::from(&conf.path()); - net_path.push("network"); - net_settings.config_path = Some(net_path.to_str().unwrap().to_owned()); - - // 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 {