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