Skip to content

Commit

Permalink
Merge pull request #4245 from chenyukang/yukang-daemon
Browse files Browse the repository at this point in the history
Support easy run daemon mode for Linux/MacOS
  • Loading branch information
chenyukang authored Dec 26, 2023
2 parents dbb055d + 6e975a5 commit ae1ab00
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 17 deletions.
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(
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");
// 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

0 comments on commit ae1ab00

Please sign in to comment.