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.
|
This is fixed by using `winsplit` on Windows instead.
|
||||||
- On Windows, zombie `git-upload-pack` processes are now prevented by using the
|
- 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.
|
"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
|
## Deprecations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3151,6 +3151,7 @@ dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"libc",
|
"libc",
|
||||||
"signals_receipts",
|
"signals_receipts",
|
||||||
|
"windows 0.62.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ sqlite = "0.32.0"
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
uds_windows = "1.1.0"
|
uds_windows = "1.1.0"
|
||||||
|
windows = "0.62"
|
||||||
winpipe = "0.1.1"
|
winpipe = "0.1.1"
|
||||||
winsplit = "0.1.0"
|
winsplit = "0.1.0"
|
||||||
zeroize = "1.5.7"
|
zeroize = "1.5.7"
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ use radicle::profile;
|
||||||
use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
|
use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
|
||||||
use radicle_node::fingerprint::{Fingerprint, FingerprintVerification};
|
use radicle_node::fingerprint::{Fingerprint, FingerprintVerification};
|
||||||
use radicle_node::{Runtime, VERSION};
|
use radicle_node::{Runtime, VERSION};
|
||||||
#[cfg(unix)]
|
|
||||||
use radicle_signals as signals;
|
use radicle_signals as signals;
|
||||||
|
|
||||||
const HELP_MSG: &str = r#"
|
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}");
|
log::warn!(target: "node", "Unable to set process open file limit: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
let signals = {
|
let signals = {
|
||||||
let (notify, signals) = chan::bounded(1);
|
let (notify, signals) = chan::bounded(1);
|
||||||
signals::install(notify)?;
|
signals::install(notify)?;
|
||||||
signals
|
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 {
|
if options.force {
|
||||||
log::debug!(target: "node", "Removing existing control socket..");
|
log::debug!(target: "node", "Removing existing control socket..");
|
||||||
std::fs::remove_file(home.socket()).ok();
|
std::fs::remove_file(home.socket()).ok();
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,6 @@ impl Runtime {
|
||||||
|| control::listen(listener, handle)
|
|| control::listen(listener, handle)
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
let _signals = thread::spawn(&self.id, "signals", move || loop {
|
let _signals = thread::spawn(&self.id, "signals", move || loop {
|
||||||
use radicle::node::Handle as _;
|
use radicle::node::Handle as _;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,16 @@ edition.workspace = true
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[dependencies]
|
||||||
crossbeam-channel = { workspace = true }
|
crossbeam-channel = { workspace = true }
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
signals_receipts = { version = "0.2.0", features = ["channel_notify_facility"] }
|
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)]
|
#[cfg(unix)]
|
||||||
mod unix;
|
mod unix;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub use unix::*;
|
pub use unix::*;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod windows;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub use windows::*;
|
||||||
|
|
||||||
/// Operating system signal.
|
/// Operating system signal.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum Signal {
|
pub enum Signal {
|
||||||
|
|
@ -16,3 +24,12 @@ pub enum Signal {
|
||||||
/// `SIGWINCH`.
|
/// `SIGWINCH`.
|
||||||
WindowChanged,
|
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 {
|
SignalsChannel::install_with_outside_channel(ChanSender(notify)).map_err(|e| match e {
|
||||||
InstallError::AlreadyInstalled { unused_notify: _ } => io::Error::new(
|
InstallError::AlreadyInstalled { unused_notify: _ } => already_installed(),
|
||||||
io::ErrorKind::AlreadyExists,
|
|
||||||
"signal handling is already installed",
|
|
||||||
),
|
|
||||||
_ => io::Error::other(e), // The error type is non-exhaustive.
|
_ => 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 }
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
version = "0.62"
|
workspace = true
|
||||||
features = [
|
features = [
|
||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
"Win32_Security",
|
"Win32_Security",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue