use std::{
io, mem,
os::{
fd::{AsRawFd, FromRawFd},
unix::{ffi::OsStrExt, net::UnixStream},
},
path::Path,
};
pub(super) fn connect(path: &Path) -> io::Result<UnixStream> {
let mut address: libc::sockaddr_un = unsafe { mem::zeroed() };
let bytes = path.as_os_str().as_bytes();
if bytes.len() >= address.sun_path.len() || bytes.contains(&0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid daemon socket path",
));
}
address.sun_family = libc::AF_UNIX as libc::sa_family_t;
for (target, source) in address.sun_path.iter_mut().zip(bytes) {
*target = *source as libc::c_char;
}
let fd = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_STREAM, 0) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
let stream = unsafe { UnixStream::from_raw_fd(fd) };
stream.set_nonblocking(true)?;
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } < 0 {
return Err(io::Error::last_os_error());
}
let result = unsafe {
libc::connect(
stream.as_raw_fd(),
(&address as *const libc::sockaddr_un).cast(),
mem::size_of_val(&address) as libc::socklen_t,
)
};
if result == 0 {
return Ok(stream);
}
let error = io::Error::last_os_error();
if error.raw_os_error() != Some(libc::EINPROGRESS) {
return Err(error);
}
let mut descriptor = libc::pollfd {
fd,
events: libc::POLLOUT,
revents: 0,
};
let result = unsafe { libc::poll(&mut descriptor, 1, 5000) };
if result < 0 {
return Err(io::Error::last_os_error());
}
if result == 0 {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
"daemon connect timeout",
));
}
if let Some(error) = stream.take_error()? {
return Err(error);
}
Ok(stream)
}