windows: Introduce new crate
node: Use new windows crate to terminate job
This commit is contained in:
parent
50fb228ae4
commit
990edbf055
|
|
@ -3207,6 +3207,14 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radicle-windows"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
"windows 0.62.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
|
@ -4798,6 +4806,27 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.62.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||||
|
dependencies = [
|
||||||
|
"windows-collections",
|
||||||
|
"windows-core 0.62.2",
|
||||||
|
"windows-future",
|
||||||
|
"windows-numerics",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-collections"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core 0.62.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
|
|
@ -4813,13 +4842,37 @@ version = "0.58.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-implement",
|
"windows-implement 0.58.0",
|
||||||
"windows-interface",
|
"windows-interface 0.58.0",
|
||||||
"windows-result",
|
"windows-result 0.2.0",
|
||||||
"windows-strings",
|
"windows-strings 0.1.0",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.62.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement 0.60.2",
|
||||||
|
"windows-interface 0.59.3",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result 0.4.1",
|
||||||
|
"windows-strings 0.5.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-future"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core 0.62.2",
|
||||||
|
"windows-link",
|
||||||
|
"windows-threading",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-implement"
|
name = "windows-implement"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
|
|
@ -4831,6 +4884,17 @@ dependencies = [
|
||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-interface"
|
name = "windows-interface"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
|
|
@ -4842,12 +4906,33 @@ dependencies = [
|
||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-numerics"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core 0.62.2",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
@ -4857,16 +4942,34 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-strings"
|
name = "windows-strings"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-result",
|
"windows-result 0.2.0",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
|
|
@ -4950,6 +5053,15 @@ dependencies = [
|
||||||
"windows_x86_64_msvc 0.53.0",
|
"windows_x86_64_msvc 0.53.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-threading"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
|
|
@ -5107,7 +5219,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"sync-ptr",
|
"sync-ptr",
|
||||||
"windows",
|
"windows 0.58.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ radicle-signals = { version = "0.11", path = "crates/radicle-signals" }
|
||||||
radicle-ssh = { version = "0.10", path = "crates/radicle-ssh", default-features = false }
|
radicle-ssh = { version = "0.10", path = "crates/radicle-ssh", default-features = false }
|
||||||
radicle-systemd = { version = "0.11", path = "crates/radicle-systemd" }
|
radicle-systemd = { version = "0.11", path = "crates/radicle-systemd" }
|
||||||
radicle-term = { version = "0.16", path = "crates/radicle-term" }
|
radicle-term = { version = "0.16", path = "crates/radicle-term" }
|
||||||
|
radicle-windows = { version = "0.1", path = "crates/radicle-windows" }
|
||||||
schemars = { version = "1.0.4", default-features = false }
|
schemars = { version = "1.0.4", default-features = false }
|
||||||
serde = { version = "1.0", default-features = false }
|
serde = { version = "1.0", default-features = false }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "radicle-windows"
|
||||||
|
description = "Radicle integration with Windows"
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
version = "0.1.0"
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
|
version = "0.62"
|
||||||
|
features = [
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_Security",
|
||||||
|
"Win32_System_JobObjects",
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
default-target = "x86_64-pc-windows-msvc"
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
use std::io;
|
||||||
|
use std::os::windows::io::AsRawHandle as _;
|
||||||
|
|
||||||
|
use windows::{
|
||||||
|
core::PCWSTR,
|
||||||
|
Win32::{
|
||||||
|
Foundation::{CloseHandle, HANDLE},
|
||||||
|
System::JobObjects::{AssignProcessToJobObject, CreateJobObjectW, TerminateJobObject},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Failed to create job: {0}")]
|
||||||
|
Create(io::Error),
|
||||||
|
#[error("Failed to assign job: {0}")]
|
||||||
|
Assign(io::Error),
|
||||||
|
#[error("Failed to terminate job: {0}")]
|
||||||
|
Terminate(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for io::Error {
|
||||||
|
fn from(value: Error) -> Self {
|
||||||
|
use Error::*;
|
||||||
|
match value {
|
||||||
|
Create(error) | Assign(error) | Terminate(error) => error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps a handle to a job object.
|
||||||
|
/// See also <https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects>.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Job {
|
||||||
|
handle: HANDLE,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Job {
|
||||||
|
/// Create a new, empty, job object.
|
||||||
|
/// See also <https://learn.microsoft.com/windows/win32/api/jobapi2/nf-jobapi2-createjobobjectw>.
|
||||||
|
pub fn new() -> Result<Self, Error> {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
// To match the naming in the documentation of `CreateJobObjectW` for clarity.
|
||||||
|
let lpJobAttributes = None;
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
// To match the naming in the documentation of `CreateJobObjectW` for clarity.
|
||||||
|
let lpName = PCWSTR::null();
|
||||||
|
|
||||||
|
// SAFETY: We argue that calling `CrateJobObjectW` is safe based on
|
||||||
|
// its documentation (see documentation of this function):
|
||||||
|
// Both parameters take their `null`/empty value.
|
||||||
|
// For `lpJobAttributes`, this is immediate as we use `None`, and the
|
||||||
|
// wrapper in the `windows` crate ensures safety.
|
||||||
|
// For `lpName`, we pass `name`, which was initialized to
|
||||||
|
// `PCWSTR::null()` (see above) which we rely on to obtain a sane
|
||||||
|
// `null` value.
|
||||||
|
// For the case of `null`/empty arguments, the documentation of
|
||||||
|
// `CreateJobObjectW` does not specify any further preconditions.
|
||||||
|
let result = unsafe { CreateJobObjectW(lpJobAttributes, lpName) };
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(handle) => Ok(Self { handle }),
|
||||||
|
Err(e) => Err(Error::Create(e.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assign a process to the job object.
|
||||||
|
/// See also <https://docs.microsoft.com/windows/win32/api/jobapi2/nf-jobapi2-assignprocesstojobobject>.
|
||||||
|
pub fn assign(&self, child: &std::process::Child) -> Result<(), Error> {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
// To match the naming in the documentation of `AssignProcessToJobObject` for clarity.
|
||||||
|
let hJob = self.handle;
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
// To match the naming in the documentation of `AssignProcessToJobObject` for clarity.
|
||||||
|
let hProcess = HANDLE(child.as_raw_handle());
|
||||||
|
|
||||||
|
// SAFETY: We argue that calling `AssignProcessToJobObject` is safe based on
|
||||||
|
// its documentation (see documentation of this function):
|
||||||
|
// First, we argue that the argument
|
||||||
|
// For `hJob`, consider that its value is the same as `self.handle` (see above),
|
||||||
|
// and that `self.handle` is only assigned in `Self::new` via a call to `CreateJobObjectW`.
|
||||||
|
// For `hProcess`, we pass the raw handle of the child process we were given.
|
||||||
|
// Thus, we rely on `impl std::os::windows::io::AsRawHandle for std::process::Child`
|
||||||
|
// for its value to be a valid handle to a process.
|
||||||
|
// Note that the documentation of `AssignProcessToJobObject` specifies further preconditions
|
||||||
|
// on the arguments, especially concerning:
|
||||||
|
// - Job Object Security (see <https://learn.microsoft.com/windows/win32/procthread/job-object-security-and-access-rights>)
|
||||||
|
// - Process Security and Access Rights (see <https://learn.microsoft.com/windows/win32/procthread/process-security-and-access-rights>)
|
||||||
|
// We assume that violations of these preconditions will be reflected in
|
||||||
|
// the return value of `AssignProcessToJobObject`, and will not result in safety violations.
|
||||||
|
let result = unsafe { AssignProcessToJobObject(hJob, hProcess) };
|
||||||
|
|
||||||
|
result.map_err(|e| Error::Assign(e.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Terminate a job object.
|
||||||
|
/// See also <https://learn.microsoft.com/windows/win32/api/jobapi2/nf-jobapi2-terminatejobobject>.
|
||||||
|
pub fn terminate(self, exit_code: u32) -> Result<(), Error> {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
// To match the naming in the documentation of `TerminateJobObject` for clarity.
|
||||||
|
let hJob = self.handle;
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
// To match the naming in the documentation of `TerminateJobObject` for clarity.
|
||||||
|
let uExitCode = exit_code;
|
||||||
|
|
||||||
|
// SAFETY: We argue that calling `TerminateJobObject` is safe based on
|
||||||
|
// its documentation (see documentation of this function):
|
||||||
|
// For `hJob`, consider that its value is the same as `self.handle`,
|
||||||
|
// and that `self.handle` is only assigned in `Self::new` via a call to `CreateJobObjectW`.
|
||||||
|
// For `uExitCode`, there are no preconditions to satisfy.
|
||||||
|
// Note that the documentation of `TerminateJobObject` specifies further preconditions
|
||||||
|
// on the arguments, especially concerning:
|
||||||
|
// - Job Object Security (see <https://learn.microsoft.com/windows/win32/procthread/job-object-security-and-access-rights>)
|
||||||
|
// - Process Security and Access Rights (see <https://learn.microsoft.com/windows/win32/procthread/process-security-and-access-rights>)
|
||||||
|
// We assume that violations of these preconditions will be reflected in
|
||||||
|
// the return value of `AssignProcessToJobObject`, and will not result in safety violations.
|
||||||
|
let result = unsafe { TerminateJobObject(hJob, uExitCode) };
|
||||||
|
|
||||||
|
result.map_err(|e| Error::Terminate(e.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a new job and assign a child process to it.
|
||||||
|
/// See also [`Job::new`] and [`Job::assign`].
|
||||||
|
pub fn for_child(child: &std::process::Child) -> Result<Self, Error> {
|
||||||
|
let job = Self::new()?;
|
||||||
|
job.assign(child)?;
|
||||||
|
Ok(job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Job {
|
||||||
|
/// Close the handle to the job object.
|
||||||
|
/// See also <https://learn.microsoft.com/windows/win32/api/handleapi/nf-handleapi-closehandle>.
|
||||||
|
fn drop(&mut self) {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
// To match the naming in the documentation of `CloseHandle` for clarity.
|
||||||
|
let hObject = self.handle;
|
||||||
|
|
||||||
|
// SAFETY: We argue that calling `CloseHandle` is safe based on
|
||||||
|
// its documentation (see documentation of this function):
|
||||||
|
// For `hObject`, consider that its value is the same as `self.handle`,
|
||||||
|
// and that `self.handle` is only assigned in `Self::new` via a call to `CreateJobObjectW`,
|
||||||
|
// thus is a valid handle to a job object.
|
||||||
|
let _ = unsafe { CloseHandle(hObject) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
//! Library for interaction with Windows, specialized for Radicle.
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub mod jobs;
|
||||||
Loading…
Reference in New Issue