use std::cmp::Ordering;
use std::io;
use std::mem;
use std::os::unix::prelude::*;
use std::path::Path;
use libc::{self, c_int, c_ulong};
use cvt;
#[cfg(target_os = "linux")]
use libc::{SOCK_CLOEXEC, SOCK_NONBLOCK};
#[cfg(not(target_os = "linux"))]
const SOCK_CLOEXEC: c_int = 0;
#[cfg(not(target_os = "linux"))]
const SOCK_NONBLOCK: c_int = 0;
pub struct Socket {
fd: c_int,
}
impl Socket {
pub fn new(ty: c_int) -> io::Result<Socket> {
unsafe {
if cfg!(target_os = "linux") {
let flags = ty | SOCK_CLOEXEC | SOCK_NONBLOCK;
match cvt(libc::socket(libc::AF_UNIX, flags, 0)) {
Ok(fd) => return Ok(Socket { fd: fd }),
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
Err(e) => return Err(e),
}
}
let fd = Socket { fd: try!(cvt(libc::socket(libc::AF_UNIX, ty, 0))) };
try!(cvt(libc::ioctl(fd.fd, libc::FIOCLEX)));
let mut nonblocking = 1 as c_ulong;
try!(cvt(libc::ioctl(fd.fd, libc::FIONBIO, &mut nonblocking)));
Ok(fd)
}
}
pub fn pair(ty: c_int) -> io::Result<(Socket, Socket)> {
unsafe {
let mut fds = [0, 0];
if cfg!(target_os = "linux") {
let flags = ty | SOCK_CLOEXEC | SOCK_NONBLOCK;
match cvt(libc::socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr())) {
Ok(_) => {
return Ok((Socket { fd: fds[0] }, Socket { fd: fds[1] }))
}
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {},
Err(e) => return Err(e),
}
}
try!(cvt(libc::socketpair(libc::AF_UNIX, ty, 0, fds.as_mut_ptr())));
let a = Socket { fd: fds[0] };
let b = Socket { fd: fds[1] };
try!(cvt(libc::ioctl(a.fd, libc::FIOCLEX)));
try!(cvt(libc::ioctl(b.fd, libc::FIOCLEX)));
let mut nonblocking = 1 as c_ulong;
try!(cvt(libc::ioctl(a.fd, libc::FIONBIO, &mut nonblocking)));
try!(cvt(libc::ioctl(b.fd, libc::FIONBIO, &mut nonblocking)));
Ok((a, b))
}
}
pub fn fd(&self) -> c_int {
self.fd
}
pub fn into_fd(self) -> c_int {
let ret = self.fd;
mem::forget(self);
ret
}
}
impl Drop for Socket {
fn drop(&mut self) {
unsafe {
let _ = libc::close(self.fd);
}
}
}
pub unsafe fn sockaddr_un(path: &Path)
-> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
let mut addr: libc::sockaddr_un = mem::zeroed();
addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
let bytes = path.as_os_str().as_bytes();
match (bytes.get(0), bytes.len().cmp(&addr.sun_path.len())) {
(Some(&0), Ordering::Greater) => {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
"path must be no longer than SUN_LEN"));
}
(_, Ordering::Greater) | (_, Ordering::Equal) => {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
"path must be shorter than SUN_LEN"));
}
_ => {}
}
for (dst, src) in addr.sun_path.iter_mut().zip(bytes.iter()) {
*dst = *src as libc::c_char;
}
let mut len = sun_path_offset() + bytes.len();
match bytes.get(0) {
Some(&0) | None => {}
Some(_) => len += 1,
}
Ok((addr, len as libc::socklen_t))
}
fn sun_path_offset() -> usize {
unsafe {
let addr: libc::sockaddr_un = mem::uninitialized();
let base = &addr as *const _ as usize;
let path = &addr.sun_path as *const _ as usize;
path - base
}
}