diff --git a/Cargo.lock b/Cargo.lock index 77f7bfd77..0b02c6df1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2533,6 +2533,7 @@ dependencies = [ "everscale-types", "futures-util", "hex", + "libc", "public-ip", "rand", "rustc_version", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 021185c2d..ca0dd06ef 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -22,6 +22,7 @@ everscale-crypto = { workspace = true } everscale-types = { workspace = true } futures-util = { workspace = true } hex = { workspace = true } +libc = { workspace = true } public-ip = { workspace = true } rand = { workspace = true } serde = { workspace = true } @@ -34,7 +35,7 @@ tikv-jemallocator = { workspace = true, features = [ "unprefixed_malloc_on_supported_platforms", "background_threads", ], optional = true } -tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } +tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal"] } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/cli/src/node/mod.rs b/cli/src/node/mod.rs index 1b6095b2a..c3c293fd2 100644 --- a/cli/src/node/mod.rs +++ b/cli/src/node/mod.rs @@ -34,6 +34,7 @@ use tycho_util::FastHashMap; use crate::util::error::ResultExt; use crate::util::logger::LoggerConfig; +use crate::util::signal; use self::config::{NodeConfig, NodeKeys}; @@ -82,7 +83,20 @@ impl CmdRun { tokio::runtime::Builder::new_multi_thread() .enable_all() .build()? - .block_on(self.run_impl()) + .block_on(async move { + let run_fut = tokio::spawn(self.run_impl()); + let stop_fut = signal::any_signal(signal::TERMINATION_SIGNALS); + tokio::select! { + res = run_fut => res.unwrap(), + signal = stop_fut => match signal { + Ok(signal) => { + tracing::info!(?signal, "received termination signal"); + Ok(()) + } + Err(e) => Err(e.into()), + } + } + }) } async fn run_impl(self) -> Result<()> { diff --git a/cli/src/util/mod.rs b/cli/src/util/mod.rs index a7b339b42..6c3a7591c 100644 --- a/cli/src/util/mod.rs +++ b/cli/src/util/mod.rs @@ -7,6 +7,7 @@ use everscale_types::prelude::*; pub mod error; pub mod logger; +pub mod signal; // TODO: move into types pub fn compute_storage_used(account: &Account) -> Result { diff --git a/cli/src/util/signal.rs b/cli/src/util/signal.rs new file mode 100644 index 000000000..d49d72f4d --- /dev/null +++ b/cli/src/util/signal.rs @@ -0,0 +1,35 @@ +use tokio::signal::unix; + +pub const TERMINATION_SIGNALS: [libc::c_int; 5] = [ + libc::SIGINT, + libc::SIGTERM, + libc::SIGQUIT, + libc::SIGABRT, + 20, // SIGTSTP +]; + +pub fn any_signal(signals: I) -> tokio::sync::oneshot::Receiver +where + I: IntoIterator, + T: Into + Send + 'static, +{ + let (tx, rx) = tokio::sync::oneshot::channel(); + + let any_signal = futures_util::future::select_all(signals.into_iter().map(|signal| { + Box::pin(async move { + let signal = signal.into(); + unix::signal(signal) + .expect("Failed subscribing on unix signals") + .recv() + .await; + signal + }) + })); + + tokio::spawn(async move { + let signal = any_signal.await.0; + tx.send(signal).ok(); + }); + + rx +}