1#[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#[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"; #[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 use parking_lot::deadlock;
58 use std::{thread, time::Duration};
59
60 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 } 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 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 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 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 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
196fn 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 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}