1use crate::service;2use futures::{future, Future, sync::oneshot};3use std::cell::RefCell;4use tokio::runtime::Runtime;5pub use substrate_cli::{VersionInfo, IntoExit, error};6use substrate_cli::{informant, parse_and_prepare, ParseAndPrepare, NoCustom};7use substrate_service::{AbstractService, Roles as ServiceRoles, Configuration};8use crate::chain_spec;9use log::info;101112pub fn run<I, T, E>(args: I, exit: E, version: VersionInfo) -> error::Result<()> where13 I: IntoIterator<Item = T>,14 T: Into<std::ffi::OsString> + Clone,15 E: IntoExit,16{17 type Config<T> = Configuration<(), T>;18 match parse_and_prepare::<NoCustom, NoCustom, _>(&version, "substrate-node", args) {19 ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit,20 |exit, _cli_args, _custom_args, config: Config<_>| {21 info!("{}", version.name);22 info!(" version {}", config.full_version());23 info!(" by {}, 2017, 2018", version.author);24 info!("Chain specification: {}", config.chain_spec.name());25 info!("Node name: {}", config.name);26 info!("Roles: {:?}", config.roles);27 let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;28 match config.roles {29 ServiceRoles::LIGHT => run_until_exit(30 runtime,31 service::new_light(config).map_err(|e| format!("{:?}", e))?,32 exit33 ),34 _ => run_until_exit(35 runtime,36 service::new_full(config).map_err(|e| format!("{:?}", e))?,37 exit38 ),39 }.map_err(|e| format!("{:?}", e))40 }),41 ParseAndPrepare::BuildSpec(cmd) => cmd.run(load_spec),42 ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|43 Ok(new_full_start!(config).0), load_spec, exit),44 ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|45 Ok(new_full_start!(config).0), load_spec, exit),46 ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),47 ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder(|config: Config<_>|48 Ok(new_full_start!(config).0), load_spec),49 ParseAndPrepare::CustomCommand(_) => Ok(())50 }?;5152 Ok(())53}5455fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {56 Ok(match chain_spec::Alternative::from(id) {57 Some(spec) => Some(spec.load()?),58 None => None,59 })60}6162fn run_until_exit<T, E>(63 mut runtime: Runtime,64 service: T,65 e: E,66) -> error::Result<()>67where68 T: AbstractService,69 E: IntoExit,70{71 let (exit_send, exit) = exit_future::signal();7273 let informant = informant::build(&service);74 runtime.executor().spawn(exit.until(informant).map(|_| ()));7576 77 78 let _telemetry = service.telemetry();7980 let service_res = {81 let exit = e.into_exit().map_err(|_| error::Error::Other("Exit future failed.".into()));82 let service = service.map_err(|err| error::Error::Service(err));83 let select = service.select(exit).map(|_| ()).map_err(|(err, _)| err);84 runtime.block_on(select)85 };8687 exit_send.fire();8889 90 let _ = runtime.shutdown_on_idle().wait();9192 service_res93}949596pub struct Exit;97impl IntoExit for Exit {98 type Exit = future::MapErr<oneshot::Receiver<()>, fn(oneshot::Canceled) -> ()>;99 fn into_exit(self) -> Self::Exit {100 101 let (exit_send, exit) = oneshot::channel();102103 let exit_send_cell = RefCell::new(Some(exit_send));104 ctrlc::set_handler(move || {105 let exit_send = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take();106 if let Some(exit_send) = exit_send {107 exit_send.send(()).expect("Error sending exit notification");108 }109 }).expect("Error setting Ctrl-C handler");110111 exit.map_err(drop)112 }113}