ckb_bin/
lib.rs

1//! CKB executable.
2//!
3//! This crate is created to reduce the link time to build CKB.
4#[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
31/// The executable main entry.
32///
33/// It returns `Ok` when the process exist normally, otherwise the `ExitCode` is converted to the
34/// process exit status code.
35///
36/// ## Parameters
37///
38/// * `version` - The version is passed in so the bin crate can collect the version without trigger
39///   re-linking.
40pub fn run_app(version: Version) -> Result<(), ExitCode> {
41    // Always print backtrace on panic.
42    ::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    // make sure daemon dir exists
96    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}