1#[allow(dead_code)]
5mod cli;
6mod helper;
7mod setup;
8mod setup_guard;
9mod subcommand;
10#[cfg(test)]
11mod tests;
12use ckb_app_config::ExitCode;
13use ckb_async_runtime::new_global_runtime;
14use ckb_build_info::Version;
15use ckb_logger::debug;
16use ckb_network::tokio;
17use clap::ArgMatches;
18use helper::raise_fd_limit;
19use setup::Setup;
20use setup_guard::SetupGuard;
21use time::OffsetDateTime;
22
23#[cfg(not(target_os = "windows"))]
24use colored::Colorize;
25#[cfg(not(target_os = "windows"))]
26use daemonize_me::Daemon;
27#[cfg(not(target_os = "windows"))]
28use subcommand::check_process;
29#[cfg(feature = "with_sentry")]
30pub(crate) const LOG_TARGET_SENTRY: &str = "sentry";
31
32fn log_println(message: &str) {
35 let now = OffsetDateTime::now_utc();
36 let thread = std::thread::current();
37 let thread_name = thread.name().unwrap_or("main");
38 eprintln!(
39 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03} +00:00 {} INFO ckb_bin {}",
40 now.year(),
41 now.month() as u8,
42 now.day(),
43 now.hour(),
44 now.minute(),
45 now.second(),
46 now.millisecond(),
47 thread_name,
48 message
49 );
50}
51
52pub fn run_app(version: Version) -> Result<(), ExitCode> {
62 unsafe {
64 ::std::env::set_var("RUST_BACKTRACE", "full");
65 }
66
67 let (bin_name, app_matches) = cli::get_bin_name_and_matches(&version);
68 if let Some((cli, matches)) = app_matches.subcommand() {
69 match cli {
70 cli::CMD_INIT => {
71 return subcommand::init(Setup::init(matches)?);
72 }
73 cli::CMD_LIST_HASHES => {
74 return subcommand::list_hashes(Setup::root_dir_from_matches(matches)?, matches);
75 }
76 cli::CMD_PEERID => {
77 if let Some((cli, matches)) = matches.subcommand() {
78 match cli {
79 cli::CMD_GEN_SECRET => return Setup::generate(matches),
80 cli::CMD_FROM_SECRET => {
81 return subcommand::peer_id(Setup::peer_id(matches)?);
82 }
83 _ => {}
84 }
85 }
86 }
87 _ => {}
88 }
89 }
90
91 let (cmd, matches) = app_matches
92 .subcommand()
93 .expect("SubcommandRequiredElseHelp");
94
95 #[cfg(not(target_os = "windows"))]
96 if run_daemon(cmd, matches) {
97 return run_app_in_daemon(version, bin_name, cmd, matches);
98 }
99
100 debug!("ckb version: {}", version);
101 run_app_inner(version, bin_name, cmd, matches)
102}
103
104#[cfg(not(target_os = "windows"))]
105fn run_app_in_daemon(
106 version: Version,
107 bin_name: String,
108 cmd: &str,
109 matches: &ArgMatches,
110) -> Result<(), ExitCode> {
111 eprintln!("starting CKB in daemon mode ...");
112 eprintln!("check status : `{}`", "ckb daemon --check".green());
113 eprintln!("stop daemon : `{}`", "ckb daemon --stop".yellow());
114
115 assert!(matches!(cmd, cli::CMD_RUN));
116 let root_dir = Setup::root_dir_from_matches(matches)?;
117 let daemon_dir = root_dir.join("data/daemon");
118 std::fs::create_dir_all(daemon_dir)?;
120 let pid_file = Setup::daemon_pid_file_path(matches)?;
121
122 if check_process(&pid_file).is_ok() {
123 eprintln!("{}", "ckb is already running".red());
124 return Ok(());
125 }
126 eprintln!("no ckb process, starting ...");
127
128 let pwd = std::env::current_dir()?;
129 let daemon = Daemon::new().pid_file(pid_file, Some(false)).work_dir(pwd);
130
131 match daemon.start() {
132 Ok(_) => {
133 ckb_logger::info!("Success, daemonized ...");
134 run_app_inner(version, bin_name, cmd, matches)
135 }
136 Err(e) => {
137 ckb_logger::info!("daemonize error: {}", e);
138 Err(ExitCode::Failure)
139 }
140 }
141}
142
143fn run_app_inner(
144 version: Version,
145 bin_name: String,
146 cmd: &str,
147 matches: &ArgMatches,
148) -> Result<(), ExitCode> {
149 let is_silent_logging = is_silent_logging(cmd);
150 let (mut handle, mut handle_stop_rx, _runtime) = new_global_runtime(None);
151 let setup = Setup::from_matches(bin_name, cmd, matches)?;
152 let (_guard, log_config) = SetupGuard::from_setup(
156 &setup,
157 &version,
158 handle.clone(),
159 is_silent_logging,
160 cmd != cli::CMD_RUN,
161 )?;
162
163 raise_fd_limit();
164
165 let ret = match cmd {
166 cli::CMD_RUN => subcommand::run(setup.run(matches)?, version, handle.clone(), log_config),
167 cli::CMD_MINER => subcommand::miner(setup.miner(matches)?, handle.clone()),
168 cli::CMD_REPLAY => subcommand::replay(setup.replay(matches)?, handle.clone()),
169 cli::CMD_EXPORT => subcommand::export(setup.export(matches)?, handle.clone()),
170 cli::CMD_IMPORT => subcommand::import(setup.import(matches)?, handle.clone()),
171 cli::CMD_STATS => subcommand::stats(setup.stats(matches)?, handle.clone()),
172 cli::CMD_RESET_DATA => subcommand::reset_data(setup.reset_data(matches)?),
173 cli::CMD_MIGRATE => subcommand::migrate(setup.migrate(matches)?),
174 #[cfg(not(target_os = "windows"))]
175 cli::CMD_DAEMON => subcommand::daemon(setup.daemon(matches)?),
176 _ => unreachable!(),
177 };
178
179 if matches!(cmd, cli::CMD_RUN) {
180 handle.drop_guard();
181
182 tokio::task::block_in_place(|| {
183 log_println("Waiting for all tokio tasks to exit...");
193 handle_stop_rx.blocking_recv();
194 log_println("All tokio tasks and threads have exited. CKB shutdown");
195 });
196 }
197
198 ret
199}
200
201#[cfg(not(target_os = "windows"))]
202fn run_daemon(cmd: &str, matches: &ArgMatches) -> bool {
203 match cmd {
204 cli::CMD_RUN => matches.get_flag(cli::ARG_DAEMON),
205 _ => false,
206 }
207}
208
209type Silent = bool;
210
211fn is_silent_logging(cmd: &str) -> Silent {
212 matches!(
213 cmd,
214 cli::CMD_EXPORT
215 | cli::CMD_IMPORT
216 | cli::CMD_STATS
217 | cli::CMD_MIGRATE
218 | cli::CMD_RESET_DATA
219 | cli::CMD_DAEMON
220 )
221}