use super::{
HandoffAttemptDecision, HandoffAttemptFailure, HandoffFallbackDecision, HandoffFallbackReason,
HandoffToken,
};
pub const DUPLICATE_HANDLE_TRANSPORT_SUPPORTED: bool =
running_process_platform_internal::LEGACY_DUPLICATE_HANDLE_TRANSPORT_SUPPORTED;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct WindowsHandleValue(usize);
impl WindowsHandleValue {
pub fn new(value: usize) -> Self {
Self(value)
}
pub fn get(self) -> usize {
self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DuplicateHandleAttempt {
pub pipe_handle: WindowsHandleValue,
pub backend_pid: u32,
pub handoff_token: HandoffToken,
}
impl DuplicateHandleAttempt {
pub fn new(
pipe_handle: WindowsHandleValue,
backend_pid: u32,
handoff_token: HandoffToken,
) -> Self {
Self {
pipe_handle,
backend_pid,
handoff_token,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DuplicateHandleSuccess {
pub duplicated_handle: WindowsHandleValue,
pub backend_pid: u32,
pub handoff_token: HandoffToken,
}
impl DuplicateHandleSuccess {
pub fn new(
duplicated_handle: WindowsHandleValue,
backend_pid: u32,
handoff_token: HandoffToken,
) -> Self {
Self {
duplicated_handle,
backend_pid,
handoff_token,
}
}
}
pub type DuplicateHandleResult = Result<DuplicateHandleSuccess, DuplicateHandleError>;
pub fn try_duplicate_handle(attempt: &DuplicateHandleAttempt) -> DuplicateHandleResult {
let duplicated = running_process_platform_internal::legacy_duplicate_handle(
attempt.pipe_handle.get(),
attempt.backend_pid,
)
.map_err(|error| legacy_error(attempt.backend_pid, error))?;
Ok(DuplicateHandleSuccess::new(
WindowsHandleValue::new(duplicated),
attempt.backend_pid,
attempt.handoff_token,
))
}
fn legacy_error(
backend_pid: u32,
error: running_process_platform_internal::LegacyHandoffError,
) -> DuplicateHandleError {
use running_process_platform_internal::platform::ipc::HandoffTransferErrorKind;
match error.kind() {
HandoffTransferErrorKind::Unsupported => DuplicateHandleError::UnsupportedPlatform,
HandoffTransferErrorKind::PermissionDenied => {
DuplicateHandleError::PermissionDenied { backend_pid }
}
HandoffTransferErrorKind::BackendUnavailable => {
DuplicateHandleError::CannotOpenBackend { backend_pid }
}
HandoffTransferErrorKind::WouldBlock | HandoffTransferErrorKind::Failed => {
DuplicateHandleError::DuplicateFailed {
backend_pid,
raw_os_error: error.raw_os_error(),
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum DuplicateHandleError {
#[error("DuplicateHandle handoff transport is unsupported on this platform")]
UnsupportedPlatform,
#[error("cannot open backend process {backend_pid} for DuplicateHandle")]
CannotOpenBackend {
backend_pid: u32,
},
#[error("permission denied duplicating handle into backend process {backend_pid}")]
PermissionDenied {
backend_pid: u32,
},
#[error("DuplicateHandle failed for backend process {backend_pid}")]
DuplicateFailed {
backend_pid: u32,
raw_os_error: Option<i32>,
},
#[error("integrity mismatch duplicating handle into backend process {backend_pid}")]
IntegrityMismatch {
backend_pid: u32,
},
#[error("backend process {backend_pid} did not acknowledge duplicated handle")]
BackendAckTimeout {
backend_pid: u32,
},
}
impl DuplicateHandleError {
pub fn attempt_failure(&self) -> Option<HandoffAttemptFailure> {
match self {
Self::UnsupportedPlatform => None,
Self::CannotOpenBackend { .. }
| Self::PermissionDenied { .. }
| Self::DuplicateFailed { .. } => Some(HandoffAttemptFailure::PermissionDenied),
Self::IntegrityMismatch { .. } => Some(HandoffAttemptFailure::IntegrityMismatch),
Self::BackendAckTimeout { .. } => Some(HandoffAttemptFailure::BackendAckTimeout),
}
}
pub fn fallback_reason(&self) -> HandoffFallbackReason {
match self.attempt_failure() {
Some(failure) => failure.into(),
None => HandoffFallbackReason::ServicePolicyDisabled,
}
}
pub fn fallback_decision(&self) -> HandoffFallbackDecision {
HandoffFallbackDecision::new(self.fallback_reason())
}
pub fn fallback_attempt_decision(&self) -> HandoffAttemptDecision {
HandoffAttemptDecision::FallbackToReconnect(self.fallback_decision())
}
pub fn is_fallback_safe(&self) -> bool {
let fallback = self.fallback_decision();
fallback.uses_backend_reconnect() && !fallback.sends_client_error()
}
}