diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs
index e73321ecce5b3..5d451bbed6562 100644
--- a/client/cli/src/lib.rs
+++ b/client/cli/src/lib.rs
@@ -31,6 +31,7 @@ mod config;
mod error;
mod params;
mod runner;
+mod signals;
pub use arg_enums::*;
pub use clap;
@@ -41,6 +42,7 @@ pub use params::*;
pub use runner::*;
pub use sc_service::{ChainSpec, Role};
pub use sc_tracing::logging::LoggerBuilder;
+pub use signals::Signals;
pub use sp_version::RuntimeVersion;
/// Substrate client CLI
diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs
index 3d216aef4a75c..a8b75f2665aea 100644
--- a/client/cli/src/runner.rs
+++ b/client/cli/src/runner.rs
@@ -16,80 +16,15 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use crate::{error::Error as CliError, Result, SubstrateCli};
+use crate::{error::Error as CliError, Result, Signals, SubstrateCli};
use chrono::prelude::*;
-use futures::{
- future::{self, BoxFuture, FutureExt},
- pin_mut, select, Future,
-};
+use futures::{future::FutureExt, Future};
use log::info;
use sc_service::{Configuration, Error as ServiceError, TaskManager};
use sc_utils::metrics::{TOKIO_THREADS_ALIVE, TOKIO_THREADS_TOTAL};
use std::{marker::PhantomData, time::Duration};
-/// Abstraction over OS signals to handle the shutdown of the node smoothly.
-///
-/// On `unix` this represents `SigInt` and `SigTerm`.
-pub struct Signals(BoxFuture<'static, ()>);
-
-impl Signals {
- /// Capture the relevant signals to handle shutdown of the node smoothly.
- ///
- /// Needs to be called in a Tokio context to have access to the tokio reactor.
- #[cfg(target_family = "unix")]
- pub fn capture() -> std::result::Result {
- use tokio::signal::unix::{signal, SignalKind};
-
- let mut stream_int = signal(SignalKind::interrupt()).map_err(ServiceError::Io)?;
- let mut stream_term = signal(SignalKind::terminate()).map_err(ServiceError::Io)?;
-
- Ok(Signals(
- async move {
- future::select(stream_int.recv().boxed(), stream_term.recv().boxed()).await;
- }
- .boxed(),
- ))
- }
-
- /// Capture the relevant signals to handle shutdown of the node smoothly.
- ///
- /// Needs to be called in a Tokio context to have access to the tokio reactor.
- #[cfg(not(unix))]
- pub fn capture() -> std::result::Result {
- use tokio::signal::ctrl_c;
-
- Ok(Signals(
- async move {
- let _ = ctrl_c().await;
- }
- .boxed(),
- ))
- }
-
- /// A dummy signal that never returns.
- pub fn dummy() -> Self {
- Self(future::pending().boxed())
- }
-}
-
-async fn main(func: F, signals: impl Future