use std::io;
use std::time::{Duration, Instant};
#[cfg(unix)]
pub type Endpoint = std::path::Path;
#[cfg(windows)]
pub type Endpoint = str;
pub const CONNECT_BUDGET: Duration = Duration::from_secs(5);
pub const RETRY_INTERVAL: Duration = Duration::from_millis(50);
pub trait Duplex: Sized {
type Reader: io::Read + Send + 'static;
type Writer: crate::transport::WriteHalf;
fn connect_once_until(endpoint: &Endpoint, deadline: Instant) -> Result<Self, ConnectError>;
fn connect_until(endpoint: &Endpoint, deadline: Instant) -> Result<Self, ConnectError> {
loop {
match Self::connect_once_until(endpoint, deadline) {
Ok(connected) => return Ok(connected),
Err(ConnectError::PermissionDenied) => return Err(ConnectError::PermissionDenied),
Err(error) => {
let remaining = deadline.saturating_duration_since(Instant::now());
if remaining.is_zero() {
return Err(error);
}
std::thread::sleep(RETRY_INTERVAL.min(remaining));
}
}
}
}
fn connect_once(endpoint: &Endpoint) -> Result<Self, ConnectError> {
Self::connect_once_until(endpoint, Instant::now() + CONNECT_BUDGET)
}
fn connect(endpoint: &Endpoint) -> Result<Self, ConnectError> {
Self::connect_until(endpoint, Instant::now() + CONNECT_BUDGET)
}
fn verify_peer_user(&self) -> io::Result<()>;
fn peer_pid(&self) -> io::Result<u32>;
fn set_read_deadline(&self, timeout: Option<Duration>) -> io::Result<()>;
fn split(self) -> io::Result<(Self::Reader, Self::Writer)>;
}
#[derive(Debug)]
pub enum Bound<L> {
Won(L),
AlreadyRunning,
}
#[cfg(unix)]
pub use unix::UnixDuplex as PlatformDuplex;
#[cfg(windows)]
pub use windows::WindowsDuplex as PlatformDuplex;
#[cfg(unix)]
pub use unix::{process_has_exited, terminate_legacy_peer};
#[cfg(windows)]
pub use windows::{process_has_exited, terminate_legacy_peer};
#[cfg(unix)]
pub use unix_socket as socket;
#[cfg(windows)]
pub use windows_socket as socket;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConnectError {
ConnectTimeout,
PermissionDenied,
}
impl std::fmt::Display for ConnectError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::ConnectTimeout => "daemon connection timed out",
Self::PermissionDenied => "daemon endpoint permission denied",
})
}
}
impl std::error::Error for ConnectError {}
#[cfg(unix)]
pub mod unix;
#[cfg(unix)]
pub mod unix_socket;
#[cfg(windows)]
pub mod windows;
#[cfg(test)]
fn test_handshake(write: &mut impl io::Write, read: &mut impl io::Read, _: &str) -> io::Result<()> {
write.write_all(b"hello\n")?;
write.flush()?;
let mut reply = [0; 6];
read.read_exact(&mut reply)?;
if reply != *b"ready\n" {
return Err(io::Error::other("invalid test reply"));
}
Ok(())
}
#[cfg(windows)]
pub mod windows_socket;