use std::os::unix::net::{UnixListener, UnixStream};
pub const INHERITED_DESCRIPTOR: i32 = 3;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Bind {
Abstract(String),
Path(std::path::PathBuf),
Inherited,
}
pub fn bind(address: &Bind) -> std::io::Result<UnixListener> {
return match address {
#[cfg(target_os = "linux")]
Bind::Abstract(name) => {
use std::os::linux::net::SocketAddrExt;
let socket = std::os::unix::net::SocketAddr::from_abstract_name(name.as_bytes())?;
UnixListener::bind_addr(&socket)
}
#[cfg(not(target_os = "linux"))]
Bind::Abstract(_) => Err(std::io::Error::other("abstract sockets need Linux")),
Bind::Path(path) => bind_path(path),
Bind::Inherited => {
use std::os::fd::FromRawFd;
ensure_socket_passed_in(std::env::var("LISTEN_PID").ok().as_deref(), std::env::var("LISTEN_FDS").ok().as_deref(), std::process::id())?;
Ok(unsafe { UnixListener::from_raw_fd(INHERITED_DESCRIPTOR) })
}
};
}
pub fn socket_passed_in() -> bool {
return ensure_socket_passed_in(std::env::var("LISTEN_PID").ok().as_deref(), std::env::var("LISTEN_FDS").ok().as_deref(), std::process::id()).is_ok();
}
pub(crate) fn ensure_socket_passed_in(listen_pid: Option<&str>, listen_fds: Option<&str>, own_pid: u32) -> std::io::Result<()> {
let announced_for: bool = matches!(listen_pid, Some(pid) if pid == own_pid.to_string());
let count: i32 = match listen_fds {
Some(text) => text.parse::<i32>().unwrap_or(0),
None => 0,
};
if !announced_for || count < 1 {
return Err(std::io::Error::other("no socket was passed in"));
}
return Ok(());
}
pub(crate) fn bind_path(path: &std::path::Path) -> std::io::Result<UnixListener> {
use std::os::unix::fs::FileTypeExt;
let first: std::io::Error = match UnixListener::bind(path) {
Ok(listener) => return Ok(listener),
Err(error) if error.kind() == std::io::ErrorKind::AddrInUse => error,
Err(error) => return Err(error),
};
let is_socket: bool = match std::fs::symlink_metadata(path) {
Ok(meta) => meta.file_type().is_socket(),
Err(_missing) => false,
};
let refused: bool = matches!(UnixStream::connect(path), Err(error) if error.kind() == std::io::ErrorKind::ConnectionRefused);
if !is_socket || !refused {
return Err(first);
}
std::fs::remove_file(path)?;
return UnixListener::bind(path);
}