conflux/
main.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5#[cfg(all(not(target_env = "msvc"), feature = "jemalloc-global"))]
6#[global_allocator]
7static ALLOC: cfx_mallocator_utils::allocator::Allocator =
8    cfx_mallocator_utils::allocator::new_allocator();
9// jemalloc profiling config
10#[allow(non_upper_case_globals)]
11#[export_name = "malloc_conf"]
12#[cfg(all(not(target_env = "msvc"), feature = "jemalloc-prof"))]
13pub static malloc_conf: &[u8] =
14    b"prof:true,prof_active:true,lg_prof_sample:19\0"; // 512kb
15
16#[cfg(test)]
17mod test;
18
19mod cli;
20mod command;
21
22use crate::command::rpc::RpcCommand;
23use cfxcore::NodeType;
24use clap::{crate_version, ArgMatches, CommandFactory};
25use cli::Cli;
26use client::{
27    archive::ArchiveClient,
28    common::{panic_handler, shutdown_handler, ClientTrait},
29    configuration::Configuration,
30    full::FullClient,
31    light::LightClient,
32};
33use command::{
34    account::{AccountCmd, ImportAccounts, ListAccounts, NewAccount},
35    dump::DumpCommand,
36};
37use log::{info, LevelFilter};
38use log4rs::{
39    append::{console::ConsoleAppender, file::FileAppender},
40    config::{Appender, Config as LogConfig, Logger, Root},
41    encode::pattern::PatternEncoder,
42};
43use network::throttling::THROTTLING_SERVICE;
44use parking_lot::{Condvar, Mutex};
45use std::sync::{Arc, OnceLock};
46
47static VERSION: OnceLock<String> = OnceLock::new();
48
49fn get_version() -> &'static str {
50    VERSION.get_or_init(|| parity_version::version(crate_version!()))
51}
52
53fn main() -> Result<(), String> {
54    #[cfg(feature = "deadlock-detection")]
55    {
56        // only for #[cfg]
57        use parking_lot::deadlock;
58        use std::{thread, time::Duration};
59
60        // Create a background thread which checks for deadlocks every 10s
61        thread::spawn(move || loop {
62            thread::sleep(Duration::from_secs(10));
63            let deadlocks = deadlock::check_deadlock();
64            if deadlocks.is_empty() {
65                continue;
66            }
67
68            eprintln!("{} deadlocks detected", deadlocks.len());
69            for (i, threads) in deadlocks.iter().enumerate() {
70                eprintln!("Deadlock #{}", i);
71                for t in threads {
72                    eprintln!("Thread Id {:#?}", t.thread_id());
73                    eprintln!("{:#?}", t.backtrace());
74                }
75            }
76        });
77    } // only for #[cfg]
78
79    let matches = Cli::command().version(get_version()).get_matches();
80
81    if let Some(output) = handle_sub_command(&matches)? {
82        println!("{}", output);
83        return Ok(());
84    }
85
86    let conf = Configuration::parse(&matches)?;
87
88    setup_logger(&conf)?;
89    panic_handler::setup();
90
91    THROTTLING_SERVICE.write().initialize(
92        conf.raw_conf.egress_queue_capacity,
93        conf.raw_conf.egress_min_throttle,
94        conf.raw_conf.egress_max_throttle,
95    );
96
97    let exit = Arc::new((Mutex::new(false), Condvar::new()));
98
99    info!(
100        "
101:'######:::'#######::'##::: ##:'########:'##:::::::'##::::'##:'##::::'##:
102'##... ##:'##.... ##: ###:: ##: ##.....:: ##::::::: ##:::: ##:. ##::'##::
103 ##:::..:: ##:::: ##: ####: ##: ##::::::: ##::::::: ##:::: ##::. ##'##:::
104 ##::::::: ##:::: ##: ## ## ##: ######::: ##::::::: ##:::: ##:::. ###::::
105 ##::::::: ##:::: ##: ##. ####: ##...:::: ##::::::: ##:::: ##::: ## ##:::
106 ##::: ##: ##:::: ##: ##:. ###: ##::::::: ##::::::: ##:::: ##:: ##:. ##::
107. ######::. #######:: ##::. ##: ##::::::: ########:. #######:: ##:::. ##:
108:......::::.......:::..::::..::..::::::::........:::.......:::..:::::..::
109Current Version: {}
110",
111        get_version()
112    );
113
114    let client_handle: Box<dyn ClientTrait> = match conf.node_type() {
115        NodeType::Archive => {
116            info!("Starting archive client...");
117            ArchiveClient::start(conf, exit.clone())
118                .map_err(|e| format!("failed to start archive client: {}", e))?
119        }
120        NodeType::Full => {
121            info!("Starting full client...");
122            FullClient::start(conf, exit.clone())
123                .map_err(|e| format!("failed to start full client: {}", e))?
124        }
125        NodeType::Light => {
126            info!("Starting light client...");
127            LightClient::start(conf, exit.clone())
128                .map_err(|e| format!("failed to start light client: {}", e))?
129        }
130        NodeType::Unknown => return Err("Unknown node type".into()),
131    };
132    info!("Conflux client started");
133    let graceful = shutdown_handler::run(client_handle, exit);
134
135    if !graceful {
136        eprintln!("Unclean shutdown, force exiting to avoid static destructor issues.");
137        // Use _exit() to skip C++ static destructors (e.g. RocksDB's
138        // PeriodicWorkScheduler) which may have already been invalidated
139        // by background threads during shutdown.
140        unsafe {
141            libc::_exit(1);
142        }
143    }
144
145    Ok(())
146}
147
148fn handle_sub_command(matches: &ArgMatches) -> Result<Option<String>, String> {
149    if matches.subcommand_name().is_none() {
150        return Ok(None);
151    }
152
153    // account sub-commands
154    if let Some(("account", account_matches)) = matches.subcommand() {
155        let account_cmd = match account_matches.subcommand() {
156            Some(("new", new_acc_matches)) => {
157                AccountCmd::New(NewAccount::new(new_acc_matches))
158            }
159            Some(("list", list_acc_matches)) => {
160                AccountCmd::List(ListAccounts::new(list_acc_matches))
161            }
162            Some(("import", import_acc_matches)) => {
163                AccountCmd::Import(ImportAccounts::new(import_acc_matches))
164            }
165            _ => unreachable!(),
166        };
167        let execute_output = command::account::execute(account_cmd)?;
168        return Ok(Some(execute_output));
169    }
170
171    // dump sub-commands
172    if let Some(("dump", dump_matches)) = matches.subcommand() {
173        let dump_cmd = DumpCommand::parse(dump_matches).map_err(|e| {
174            format!("Failed to parse dump command arguments: {}", e)
175        })?;
176        let mut conf = Configuration::parse(matches)?;
177        let execute_output = dump_cmd.execute(&mut conf)?;
178        return Ok(Some(execute_output));
179    }
180
181    // general RPC commands
182    let mut subcmd_matches = matches;
183    while let Some(m) = subcmd_matches.subcommand() {
184        subcmd_matches = m.1;
185    }
186
187    if let Some(cmd) = RpcCommand::parse(subcmd_matches)? {
188        let rt = tokio::runtime::Runtime::new().unwrap();
189        let result = rt.block_on(cmd.execute())?;
190        return Ok(Some(result));
191    }
192
193    Ok(None)
194}
195
196// If log_conf is provided, use it for log configuration and ignore
197// log_file and log_level. Otherwise, set stdout to INFO and set
198// all our crate log to log_level.
199fn setup_logger(conf: &Configuration) -> Result<(), String> {
200    match conf.raw_conf.log_conf {
201        Some(ref log_conf) => {
202            log4rs::init_file(log_conf, Default::default()).map_err(|e| {
203                format!(
204                    "failed to initialize log with log config file '{}': {:?}; maybe you want 'run/log.yaml'?",
205                    log_conf, e
206                )
207            })?;
208        }
209        None => {
210            let mut conf_builder =
211                LogConfig::builder().appender(Appender::builder().build(
212                    "stdout",
213                    Box::new(ConsoleAppender::builder().build()),
214                ));
215            let mut root_builder = Root::builder().appender("stdout");
216            if let Some(ref log_file) = conf.raw_conf.log_file {
217                conf_builder =
218                    conf_builder.appender(Appender::builder().build(
219                        "logfile",
220                        Box::new(
221                            FileAppender::builder().encoder(
222                                Box::new(
223                                    PatternEncoder::new(
224                                        "{d} {h({l}):5.5} {T:<20.20} {t:12.12} - {m}{n}")))
225                                .build(log_file)
226                                .map_err(
227                                    |e| format!("failed to build log pattern: {:?}", e))?,
228                        ),
229                    ));
230                root_builder = root_builder.appender("logfile");
231            };
232            // Should add new crate names here
233            for crate_name in [
234                "blockgen",
235                "cfxcore",
236                "cfx_statedb",
237                "cfx_storage",
238                "conflux",
239                "db",
240                "keymgr",
241                "network",
242                "txgen",
243                "client",
244                "primitives",
245                "io",
246            ]
247            .iter()
248            {
249                conf_builder = conf_builder.logger(
250                    Logger::builder()
251                        .build(*crate_name, conf.raw_conf.log_level),
252                );
253            }
254            let log_config = conf_builder
255                .build(root_builder.build(LevelFilter::Info))
256                .map_err(|e| format!("failed to build log config: {:?}", e))?;
257            log4rs::init_config(log_config).map_err(|e| {
258                format!("failed to initialize log with config: {:?}", e)
259            })?;
260        }
261    };
262
263    Ok(())
264}