Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support easy run daemon mode for Linux/MacOS #4245

Merged
merged 15 commits into from
Dec 26, 2023
Merged
36 changes: 35 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions ckb-bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ is-terminal = "0.4.7"
fdlimit = "0.2.1"
ckb-stop-handler = { path = "../util/stop-handler", version = "= 0.114.0-pre" }

[target.'cfg(not(target_os="windows"))'.dependencies]
daemonize = { version = "0.5.0" }
nix = { version = "0.24.0", default-features = false, features = ["signal"] }
colored = "2.0"

[features]
deadlock_detection = ["ckb-util/deadlock_detection"]
profiling = ["ckb-memory-tracker/profiling"]
Expand Down
6 changes: 3 additions & 3 deletions ckb-bin/src/helper.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ckb_logger::info;
use ckb_logger::debug;

use std::io::{stdin, stdout, Write};

Expand All @@ -8,7 +8,7 @@ pub fn deadlock_detection() {}
#[cfg(feature = "deadlock_detection")]
pub fn deadlock_detection() {
use ckb_channel::select;
use ckb_logger::warn;
use ckb_logger::{info, warn};
use ckb_stop_handler::{new_crossbeam_exit_rx, register_thread};
use ckb_util::parking_lot::deadlock;
use std::{thread, time::Duration};
Expand Down Expand Up @@ -73,6 +73,6 @@ pub fn prompt(msg: &str) -> String {
/// on the number of cores available.
pub fn raise_fd_limit() {
if let Some(limit) = fdlimit::raise_fd_limit() {
info!("raise_fd_limit newly-increased limit: {}", limit);
debug!("raise_fd_limit newly-increased limit: {}", limit);
}
}
86 changes: 82 additions & 4 deletions ckb-bin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@
mod helper;
mod setup_guard;
mod subcommand;

use ckb_app_config::{cli, ExitCode, Setup};
use ckb_async_runtime::new_global_runtime;
use ckb_build_info::Version;
use ckb_logger::info;
use ckb_logger::{debug, info};
use ckb_network::tokio;
use clap::ArgMatches;
use helper::raise_fd_limit;
use setup_guard::SetupGuard;

#[cfg(not(target_os = "windows"))]
use colored::Colorize;
#[cfg(not(target_os = "windows"))]
use daemonize::Daemonize;
#[cfg(not(target_os = "windows"))]
use subcommand::check_process;
#[cfg(feature = "with_sentry")]
pub(crate) const LOG_TARGET_SENTRY: &str = "sentry";

Expand Down Expand Up @@ -56,8 +62,65 @@ pub fn run_app(version: Version) -> Result<(), ExitCode> {
let (cmd, matches) = app_matches
.subcommand()
.expect("SubcommandRequiredElseHelp");
let is_silent_logging = is_silent_logging(cmd);

#[cfg(not(target_os = "windows"))]
if run_deamon(cmd, matches) {
return run_app_in_daemon(version, bin_name, cmd, matches);
}

debug!("ckb version: {}", version);
run_app_inner(version, bin_name, cmd, matches)
}

#[cfg(not(target_os = "windows"))]
fn run_app_in_daemon(
chenyukang marked this conversation as resolved.
Show resolved Hide resolved
version: Version,
bin_name: String,
cmd: &str,
matches: &ArgMatches,
) -> Result<(), ExitCode> {
eprintln!("starting CKB in daemon mode ...");
eprintln!("check status : `{}`", "ckb daemon --check".green());
eprintln!("stop daemon : `{}`", "ckb daemon --stop".yellow());

assert!(matches!(cmd, cli::CMD_RUN));
let root_dir = Setup::root_dir_from_matches(matches)?;
let daemon_dir = root_dir.join("data/daemon");
zhangsoledad marked this conversation as resolved.
Show resolved Hide resolved
// make sure daemon dir exists
std::fs::create_dir_all(daemon_dir)?;
let pid_file = Setup::daemon_pid_file_path(matches)?;

if check_process(&pid_file).is_ok() {
eprintln!("{}", "ckb is already running".red());
return Ok(());
}
eprintln!("no ckb process, starting ...");

let pwd = std::env::current_dir()?;
let daemon = Daemonize::new()
.pid_file(pid_file)
.chown_pid_file(true)
.working_directory(pwd);

match daemon.start() {
Ok(_) => {
info!("Success, daemonized ...");
run_app_inner(version, bin_name, cmd, matches)
}
Err(e) => {
info!("daemonize error: {}", e);
Err(ExitCode::Failure)
}
}
}

fn run_app_inner(
version: Version,
bin_name: String,
cmd: &str,
matches: &ArgMatches,
) -> Result<(), ExitCode> {
let is_silent_logging = is_silent_logging(cmd);
let (mut handle, mut handle_stop_rx, _runtime) = new_global_runtime();
let setup = Setup::from_matches(bin_name, cmd, matches)?;
let _guard = SetupGuard::from_setup(&setup, &version, handle.clone(), is_silent_logging)?;
Expand All @@ -73,6 +136,8 @@ pub fn run_app(version: Version) -> Result<(), ExitCode> {
cli::CMD_STATS => subcommand::stats(setup.stats(matches)?, handle.clone()),
cli::CMD_RESET_DATA => subcommand::reset_data(setup.reset_data(matches)?),
cli::CMD_MIGRATE => subcommand::migrate(setup.migrate(matches)?),
#[cfg(not(target_os = "windows"))]
cli::CMD_DAEMON => subcommand::daemon(setup.daemon(matches)?),
_ => unreachable!(),
};

Expand All @@ -89,11 +154,24 @@ pub fn run_app(version: Version) -> Result<(), ExitCode> {
ret
}

#[cfg(not(target_os = "windows"))]
fn run_deamon(cmd: &str, matches: &ArgMatches) -> bool {
match cmd {
cli::CMD_RUN => matches.get_flag(cli::ARG_DAEMON),
_ => false,
}
}

type Silent = bool;

fn is_silent_logging(cmd: &str) -> Silent {
matches!(
cmd,
cli::CMD_EXPORT | cli::CMD_IMPORT | cli::CMD_STATS | cli::CMD_MIGRATE | cli::CMD_RESET_DATA
cli::CMD_EXPORT
| cli::CMD_IMPORT
| cli::CMD_STATS
| cli::CMD_MIGRATE
| cli::CMD_RESET_DATA
| cli::CMD_DAEMON
)
}
89 changes: 89 additions & 0 deletions ckb-bin/src/subcommand/daemon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use ckb_app_config::{DaemonArgs, ExitCode};
use colored::*;
use nix::sys::signal::{kill, Signal};
use nix::unistd::Pid;
use std::io::Write;
use std::path::PathBuf;
use std::{fs, io};

pub fn daemon(args: DaemonArgs) -> Result<(), ExitCode> {
let pid_file = &args.pid_file;
if args.check {
// find the pid file and check if the process is running
match check_process(pid_file) {
Ok(pid) => {
eprintln!("{}, pid - {}", "ckb daemon service is running".green(), pid);
}
_ => {
eprintln!("{}", "ckb daemon service is not running".red());
}
}
} else if args.stop {
kill_process(pid_file, "ckb")?;
fs::remove_file(pid_file).map_err(|_| ExitCode::Failure)?;
}
Ok(())
}

pub fn check_process(pid_file: &PathBuf) -> Result<i32, ExitCode> {
let pid_str = fs::read_to_string(pid_file).map_err(|_| ExitCode::Failure)?;
let pid = pid_str
.trim()
.parse::<i32>()
.map_err(|_| ExitCode::Failure)?;

// Check if the process is running
match kill(Pid::from_raw(pid), None) {
Ok(_) => Ok(pid),
Err(_) => Err(ExitCode::Failure),
}
}

fn kill_process(pid_file: &PathBuf, name: &str) -> Result<(), ExitCode> {
if check_process(pid_file).is_err() {
eprintln!("{} is not running", name);
return Ok(());
}
let pid_str = fs::read_to_string(pid_file).map_err(|_| ExitCode::Failure)?;
let pid = pid_str
.trim()
.parse::<i32>()
.map_err(|_| ExitCode::Failure)?;
eprintln!(
"stopping {} deamon service with pid {} ...",
name,
pid.to_string().red()
);
// Send a SIGTERM signal to the process
let _ = kill(Pid::from_raw(pid), Some(Signal::SIGTERM)).map_err(|_| ExitCode::Failure);
let mut wait_time = 60;
eprintln!("{}", "waiting ckb service to stop ...".yellow());
loop {
let res = check_process(pid_file);
match res {
Ok(_) => {
wait_time -= 1;
eprint!("{}", ".".yellow());
let _ = io::stderr().flush();
std::thread::sleep(std::time::Duration::from_secs(1));
}
_ if wait_time <= 0 => {
eprintln!(
"{}",
format!(
"ckb daemon service is is still running with pid {}..., stop it now forcefully ...",
pid
)
.red()
);
kill(Pid::from_raw(pid), Some(Signal::SIGKILL)).map_err(|_| ExitCode::Failure)?;
break;
}
_ => {
break;
}
}
}
eprintln!("\n{}", "cbk daemon service stopped successfully".green());
Ok(())
}
4 changes: 4 additions & 0 deletions ckb-bin/src/subcommand/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(not(target_os = "windows"))]
mod daemon;
mod export;
mod import;
mod init;
Expand All @@ -10,6 +12,8 @@ mod reset_data;
mod run;
mod stats;

#[cfg(not(target_os = "windows"))]
pub use self::daemon::{check_process, daemon};
pub use self::export::export;
pub use self::import::import;
pub use self::init::init;
Expand Down
1 change: 1 addition & 0 deletions ckb-bin/src/subcommand/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub fn run(args: RunArgs, version: Version, async_handle: Handle) -> Result<(),
let tx_pool_builder = pack.take_tx_pool_builder();
tx_pool_builder.start(network_controller.clone());

info!("CKB service started ...");
ctrlc::set_handler(|| {
info!("Trapped exit signal, exiting...");
broadcast_exit_signals();
Expand Down
28 changes: 26 additions & 2 deletions devtools/init/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
# Init/Service Scripts
# CKB Init Scripts

## Run CKB in deamon mode

CKB has a builtin deamon mode, command to run CKB in deamon mode(only for Linux/MacOS):

```bash
ckb run --deamon
```

Check deamon satus:

```bash
ckb deamon --check
```

Stop deamon process:

```bash
ckb deamon --stop
```

The deamon mode is only for Linux/MacOS, and the CKB service will not be started automatically after reboot.

## Init/Service Scripts

This folder provides the init/service scripts to start CKB node and miner as
daemons on various Unix like distributions.

See the README in each folder for the detailed instructions.

## Disclaimer
### Disclaimer

Users are expected to know how to administer their system, and these files
should be considered as only a guide or suggestion to setup CKB.
Loading
Loading