1
// This file is part of Substrate.
2

            
3
// Copyright (C) Parity Technologies (UK) Ltd.
4
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5

            
6
// This program is free software: you can redistribute it and/or modify
7
// it under the terms of the GNU General Public License as published by
8
// the Free Software Foundation, either version 3 of the License, or
9
// (at your option) any later version.
10

            
11
// This program is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
// GNU General Public License for more details.
15

            
16
// You should have received a copy of the GNU General Public License
17
// along with this program. If not, see <https://www.gnu.org/licenses/>.
18

            
19
use futures::{
20
	future::{self, BoxFuture, FutureExt},
21
	pin_mut, select, Future,
22
};
23

            
24
use sc_service::Error as ServiceError;
25

            
26
/// Abstraction over OS signals to handle the shutdown of the node smoothly.
27
///
28
/// On `unix` this represents `SigInt` and `SigTerm`.
29
pub struct Signals(BoxFuture<'static, ()>);
30

            
31
impl Signals {
32
	/// Return the signals future.
33
	pub fn future(self) -> BoxFuture<'static, ()> {
34
		self.0
35
	}
36

            
37
	/// Capture the relevant signals to handle shutdown of the node smoothly.
38
	///
39
	/// Needs to be called in a Tokio context to have access to the tokio reactor.
40
	#[cfg(target_family = "unix")]
41
	pub fn capture() -> std::result::Result<Self, ServiceError> {
42
		use tokio::signal::unix::{signal, SignalKind};
43

            
44
		let mut stream_int = signal(SignalKind::interrupt()).map_err(ServiceError::Io)?;
45
		let mut stream_term = signal(SignalKind::terminate()).map_err(ServiceError::Io)?;
46

            
47
		Ok(Signals(
48
			async move {
49
				future::select(stream_int.recv().boxed(), stream_term.recv().boxed()).await;
50
			}
51
			.boxed(),
52
		))
53
	}
54

            
55
	/// Capture the relevant signals to handle shutdown of the node smoothly.
56
	///
57
	/// Needs to be called in a Tokio context to have access to the tokio reactor.
58
	#[cfg(not(unix))]
59
	pub fn capture() -> Result<Self, ServiceError> {
60
		use tokio::signal::ctrl_c;
61

            
62
		Ok(Signals(
63
			async move {
64
				let _ = ctrl_c().await;
65
			}
66
			.boxed(),
67
		))
68
	}
69

            
70
	/// A dummy signal that never returns.
71
	pub fn dummy() -> Self {
72
		Self(future::pending().boxed())
73
	}
74

            
75
	/// Run a future task until receive a signal.
76
	pub async fn run_until_signal<F, E>(self, func: F) -> Result<(), E>
77
	where
78
		F: Future<Output = Result<(), E>> + future::FusedFuture,
79
		E: std::error::Error + Send + Sync + 'static,
80
	{
81
		let signals = self.future().fuse();
82

            
83
		pin_mut!(func, signals);
84

            
85
		select! {
86
			_ = signals => {},
87
			res = func => res?,
88
		}
89

            
90
		Ok(())
91
	}
92
}