// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of OpenEthereum. // OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with OpenEthereum. If not, see . //! Ethcore client application. #![warn(missing_docs)] extern crate ctrlc; extern crate dir; extern crate fdlimit; #[macro_use] extern crate log; extern crate ansi_term; extern crate openethereum; extern crate panic_hook; extern crate parity_daemonize; extern crate parking_lot; extern crate ethcore_logger; #[cfg(windows)] extern crate winapi; use std::{ io::Write, process, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, }; use ansi_term::Colour; use ctrlc::CtrlC; use ethcore_logger::setup_log; use fdlimit::raise_fd_limit; use openethereum::{start, ExecutionAction}; use parity_daemonize::AsHandle; use parking_lot::{Condvar, Mutex}; #[derive(Debug)] /// Status used to exit or restart the program. struct ExitStatus { /// Whether the program panicked. panicking: bool, /// Whether the program should exit. should_exit: bool, } fn main() -> Result<(), i32> { let conf = { let args = std::env::args().collect::>(); openethereum::Configuration::parse_cli(&args).unwrap_or_else(|e| e.exit()) }; let logger = setup_log(&conf.logger_config()).unwrap_or_else(|e| { eprintln!("{}", e); process::exit(2) }); // FIXME: `pid_file` shouldn't need to cloned here // see: `https://github.com/paritytech/parity-daemonize/pull/13` for more info let handle = if let Some(pid) = conf.args.arg_daemon_pid_file.clone() { info!( "{}", Colour::Blue.paint("starting in daemon mode").to_string() ); let _ = std::io::stdout().flush(); match parity_daemonize::daemonize(pid) { Ok(h) => Some(h), Err(e) => { error!("{}", Colour::Red.paint(format!("{}", e))); return Err(1); } } } else { None }; // increase max number of open files raise_fd_limit(); let exit = Arc::new(( Mutex::new(ExitStatus { panicking: false, should_exit: false, }), Condvar::new(), )); // Double panic can happen. So when we lock `ExitStatus` after the main thread is notified, it cannot be locked // again. let exiting = Arc::new(AtomicBool::new(false)); trace!(target: "mode", "Not hypervised: not setting exit handlers."); let exec = start(conf, logger); match exec { Ok(result) => match result { ExecutionAction::Instant(output) => { if let Some(s) = output { println!("{}", s); } } ExecutionAction::Running(client) => { panic_hook::set_with({ let e = exit.clone(); let exiting = exiting.clone(); move |panic_msg| { warn!("Panic occured, see stderr for details"); eprintln!("{}", panic_msg); if !exiting.swap(true, Ordering::SeqCst) { *e.0.lock() = ExitStatus { panicking: true, should_exit: true, }; e.1.notify_all(); } } }); CtrlC::set_handler({ let e = exit.clone(); let exiting = exiting.clone(); move || { if !exiting.swap(true, Ordering::SeqCst) { *e.0.lock() = ExitStatus { panicking: false, should_exit: true, }; e.1.notify_all(); } } }); // so the client has started successfully // if this is a daemon, detach from the parent process if let Some(mut handle) = handle { handle.detach() } // Wait for signal let mut lock = exit.0.lock(); if !lock.should_exit { let _ = exit.1.wait(&mut lock); } client.shutdown(); if lock.panicking { return Err(1); } } }, Err(err) => { // error occured during start up // if this is a daemon, detach from the parent process if let Some(mut handle) = handle { handle.detach_with_msg(format!("{}", Colour::Red.paint(&err))) } eprintln!("{}", err); return Err(1); } }; Ok(()) }