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, info};
16use ckb_network::tokio;
17use clap::ArgMatches;
18use helper::raise_fd_limit;
19use setup::Setup;
20use setup_guard::SetupGuard;
21
22#[cfg(not(target_os = "windows"))]
23use colored::Colorize;
24#[cfg(not(target_os = "windows"))]
25use daemonize::Daemonize;
26#[cfg(not(target_os = "windows"))]
27use subcommand::check_process;
28#[cfg(feature = "with_sentry")]
29pub(crate) const LOG_TARGET_SENTRY: &str = "sentry";
30
31pub fn run_app(version: Version) -> Result<(), ExitCode> {
41 ::std::env::set_var("RUST_BACKTRACE", "full");
43
44 let (bin_name, app_matches) = cli::get_bin_name_and_matches(&version);
45 if let Some((cli, matches)) = app_matches.subcommand() {
46 match cli {
47 cli::CMD_INIT => {
48 return subcommand::init(Setup::init(matches)?);
49 }
50 cli::CMD_LIST_HASHES => {
51 return subcommand::list_hashes(Setup::root_dir_from_matches(matches)?, matches);
52 }
53 cli::CMD_PEERID => {
54 if let Some((cli, matches)) = matches.subcommand() {
55 match cli {
56 cli::CMD_GEN_SECRET => return Setup::gen(matches),
57 cli::CMD_FROM_SECRET => {
58 return subcommand::peer_id(Setup::peer_id(matches)?)
59 }
60 _ => {}
61 }
62 }
63 }
64 _ => {}
65 }
66 }
67
68 let (cmd, matches) = app_matches
69 .subcommand()
70 .expect("SubcommandRequiredElseHelp");
71
72 #[cfg(not(target_os = "windows"))]
73 if run_daemon(cmd, matches) {
74 return run_app_in_daemon(version, bin_name, cmd, matches);
75 }
76
77 debug!("ckb version: {}", version);
78 run_app_inner(version, bin_name, cmd, matches)
79}
80
81#[cfg(not(target_os = "windows"))]
82fn run_app_in_daemon(
83 version: Version,
84 bin_name: String,
85 cmd: &str,
86 matches: &ArgMatches,
87) -> Result<(), ExitCode> {
88 eprintln!("starting CKB in daemon mode ...");
89 eprintln!("check status : `{}`", "ckb daemon --check".green());
90 eprintln!("stop daemon : `{}`", "ckb daemon --stop".yellow());
91
92 assert!(matches!(cmd, cli::CMD_RUN));
93 let root_dir = Setup::root_dir_from_matches(matches)?;
94 let daemon_dir = root_dir.join("data/daemon");
95 std::fs::create_dir_all(daemon_dir)?;
97 let pid_file = Setup::daemon_pid_file_path(matches)?;
98
99 if check_process(&pid_file).is_ok() {
100 eprintln!("{}", "ckb is already running".red());
101 return Ok(());
102 }
103 eprintln!("no ckb process, starting ...");
104
105 let pwd = std::env::current_dir()?;
106 let daemon = Daemonize::new()
107 .pid_file(pid_file)
108 .chown_pid_file(true)
109 .working_directory(pwd);
110
111 match daemon.start() {
112 Ok(_) => {
113 info!("Success, daemonized ...");
114 run_app_inner(version, bin_name, cmd, matches)
115 }
116 Err(e) => {
117 info!("daemonize error: {}", e);
118 Err(ExitCode::Failure)
119 }
120 }
121}
122
123fn run_app_inner(
124 version: Version,
125 bin_name: String,
126 cmd: &str,
127 matches: &ArgMatches,
128) -> Result<(), ExitCode> {
129 let is_silent_logging = is_silent_logging(cmd);
130 let (mut handle, mut handle_stop_rx, _runtime) = new_global_runtime(None);
131 let setup = Setup::from_matches(bin_name, cmd, matches)?;
132 let _guard = SetupGuard::from_setup(&setup, &version, handle.clone(), is_silent_logging)?;
133
134 raise_fd_limit();
135
136 let ret = match cmd {
137 cli::CMD_RUN => subcommand::run(setup.run(matches)?, version, handle.clone()),
138 cli::CMD_MINER => subcommand::miner(setup.miner(matches)?, handle.clone()),
139 cli::CMD_REPLAY => subcommand::replay(setup.replay(matches)?, handle.clone()),
140 cli::CMD_EXPORT => subcommand::export(setup.export(matches)?, handle.clone()),
141 cli::CMD_IMPORT => subcommand::import(setup.import(matches)?, handle.clone()),
142 cli::CMD_STATS => subcommand::stats(setup.stats(matches)?, handle.clone()),
143 cli::CMD_RESET_DATA => subcommand::reset_data(setup.reset_data(matches)?),
144 cli::CMD_MIGRATE => subcommand::migrate(setup.migrate(matches)?),
145 #[cfg(not(target_os = "windows"))]
146 cli::CMD_DAEMON => subcommand::daemon(setup.daemon(matches)?),
147 _ => unreachable!(),
148 };
149
150 if matches!(cmd, cli::CMD_RUN) {
151 handle.drop_guard();
152
153 tokio::task::block_in_place(|| {
154 info!("Waiting for all tokio tasks to exit...");
155 handle_stop_rx.blocking_recv();
156 info!("All tokio tasks and threads have exited. CKB shutdown");
157 });
158 }
159
160 ret
161}
162
163#[cfg(not(target_os = "windows"))]
164fn run_daemon(cmd: &str, matches: &ArgMatches) -> bool {
165 match cmd {
166 cli::CMD_RUN => matches.get_flag(cli::ARG_DAEMON),
167 _ => false,
168 }
169}
170
171type Silent = bool;
172
173fn is_silent_logging(cmd: &str) -> Silent {
174 matches!(
175 cmd,
176 cli::CMD_EXPORT
177 | cli::CMD_IMPORT
178 | cli::CMD_STATS
179 | cli::CMD_MIGRATE
180 | cli::CMD_RESET_DATA
181 | cli::CMD_DAEMON
182 )
183}