signals: Add support for Windows
This commit is contained in:
parent
865abd35e2
commit
03bbe52417
|
|
@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
This is fixed by using `winsplit` on Windows instead.
|
||||
- On Windows, zombie `git-upload-pack` processes are now prevented by using the
|
||||
"Job" API of the operating system to group child processes and their children.
|
||||
- On Windows, signal handling was not supported. The `radicle-node` executable
|
||||
will now respect signal handling on Windows.
|
||||
|
||||
## Deprecations
|
||||
|
||||
|
|
|
|||
|
|
@ -3151,6 +3151,7 @@ dependencies = [
|
|||
"crossbeam-channel",
|
||||
"libc",
|
||||
"signals_receipts",
|
||||
"windows 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ sqlite = "0.32.0"
|
|||
tempfile = "3.3.0"
|
||||
thiserror = { version = "2", default-features = false }
|
||||
uds_windows = "1.1.0"
|
||||
windows = "0.62"
|
||||
winpipe = "0.1.1"
|
||||
winsplit = "0.1.0"
|
||||
zeroize = "1.5.7"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use radicle::profile;
|
|||
use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
|
||||
use radicle_node::fingerprint::{Fingerprint, FingerprintVerification};
|
||||
use radicle_node::{Runtime, VERSION};
|
||||
#[cfg(unix)]
|
||||
use radicle_signals as signals;
|
||||
|
||||
const HELP_MSG: &str = r#"
|
||||
|
|
@ -320,20 +319,12 @@ fn execute(options: Options) -> Result<(), ExecutionError> {
|
|||
log::warn!(target: "node", "Unable to set process open file limit: {e}");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
let signals = {
|
||||
let (notify, signals) = chan::bounded(1);
|
||||
signals::install(notify)?;
|
||||
signals
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
let signals = {
|
||||
let (_, signals) = chan::bounded(1);
|
||||
log::info!(target: "node", "No signal handlers were installed, because this is not available on Windows.");
|
||||
signals
|
||||
};
|
||||
|
||||
if options.force {
|
||||
log::debug!(target: "node", "Removing existing control socket..");
|
||||
std::fs::remove_file(home.socket()).ok();
|
||||
|
|
|
|||
|
|
@ -287,7 +287,6 @@ impl Runtime {
|
|||
|| control::listen(listener, handle)
|
||||
});
|
||||
|
||||
#[cfg(unix)]
|
||||
let _signals = thread::spawn(&self.id, "signals", move || loop {
|
||||
use radicle::node::Handle as _;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,16 @@ edition.workspace = true
|
|||
version = "0.11.0"
|
||||
rust-version.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
[dependencies]
|
||||
crossbeam-channel = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = { workspace = true }
|
||||
signals_receipts = { version = "0.2.0", features = ["channel_notify_facility"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
workspace = true
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_Console",
|
||||
]
|
||||
|
|
@ -1,9 +1,17 @@
|
|||
use std::io;
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub use unix::*;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub use windows::*;
|
||||
|
||||
/// Operating system signal.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Signal {
|
||||
|
|
@ -16,3 +24,12 @@ pub enum Signal {
|
|||
/// `SIGWINCH`.
|
||||
WindowChanged,
|
||||
}
|
||||
|
||||
/// Return an error indicating that signal handling is already installed.
|
||||
#[inline(always)]
|
||||
fn already_installed() -> io::Error {
|
||||
io::Error::new(
|
||||
io::ErrorKind::AlreadyExists,
|
||||
"signal handling is already installed",
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,10 +57,7 @@ pub fn install(notify: chan::Sender<Signal>) -> io::Result<()> {
|
|||
}
|
||||
|
||||
SignalsChannel::install_with_outside_channel(ChanSender(notify)).map_err(|e| match e {
|
||||
InstallError::AlreadyInstalled { unused_notify: _ } => io::Error::new(
|
||||
io::ErrorKind::AlreadyExists,
|
||||
"signal handling is already installed",
|
||||
),
|
||||
InstallError::AlreadyInstalled { unused_notify: _ } => already_installed(),
|
||||
_ => io::Error::other(e), // The error type is non-exhaustive.
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
use std::io;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crossbeam_channel as chan;
|
||||
|
||||
use ::windows::core::BOOL;
|
||||
use ::windows::Win32::System::Console::{
|
||||
SetConsoleCtrlHandler, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_C_EVENT, CTRL_LOGOFF_EVENT,
|
||||
CTRL_SHUTDOWN_EVENT,
|
||||
};
|
||||
|
||||
use crate::{already_installed, Signal};
|
||||
|
||||
static NOTIFY: OnceLock<chan::Sender<Signal>> = OnceLock::new();
|
||||
|
||||
/// Callback function, called by the system when a control signal is to be received.
|
||||
/// See <https://learn.microsoft.com/en-us/windows/console/handlerroutine>.
|
||||
unsafe extern "system" fn handler(ctrltype: u32) -> BOOL {
|
||||
match ctrltype {
|
||||
CTRL_C_EVENT | CTRL_BREAK_EVENT | CTRL_CLOSE_EVENT | CTRL_SHUTDOWN_EVENT => {
|
||||
if let Some(notify) = NOTIFY.get() {
|
||||
if notify.send(Signal::Terminate).is_ok() {
|
||||
return true.into();
|
||||
}
|
||||
} else {
|
||||
// Do nothing, since we do not have a channel to send notifications to.
|
||||
}
|
||||
}
|
||||
CTRL_LOGOFF_EVENT => {
|
||||
// Do nothing, since we do not know which user is logging off.
|
||||
}
|
||||
_ => {
|
||||
// Do nothing, since we received an unknown control signal.
|
||||
}
|
||||
}
|
||||
|
||||
false.into()
|
||||
}
|
||||
|
||||
/// Install global signal handlers, with notifications sent to the given
|
||||
/// `notify` channel.
|
||||
pub fn install(notify: chan::Sender<Signal>) -> io::Result<()> {
|
||||
if let Err(_) = NOTIFY.set(notify) {
|
||||
return Err(already_installed());
|
||||
}
|
||||
|
||||
// SAFETY: Our handler function is sane.
|
||||
let result = unsafe { SetConsoleCtrlHandler(Some(handler), true) };
|
||||
result.map_err(|_| io::Error::last_os_error())
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ rust-version.workspace = true
|
|||
thiserror = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.62"
|
||||
workspace = true
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
|
|
|
|||
Loading…
Reference in New Issue