Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replacing RawFd with OwnedFd for compatibility with nix crate #26

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ edition = "2021"
exclude = ["tests", "src/bin", ".vscode"]

[target."cfg(unix)".dependencies]
libc = "0.2.90" # peer credentials for DragonFly BSD and NetBSD, SO_PEERSEC on all Linux architectures
libc = "0.2.161" # peer credentials for DragonFly BSD and NetBSD, SO_PEERSEC on all Linux architectures
# enabling this feature implements the extension traits for mio 0.8's unix socket types
# and Source for this crate's non-blocking seqpacket types.
mio_08 = { package = "mio", version = "0.8", features = ["os-ext", "net"], optional = true }
# .28 for AsyncFd.async_io() helper
tokio = { version = "1.28", features = ["net"], optional=true }
tokio = { version = "1.41", features = ["net"], optional=true }

[target."cfg(unix)".dev-dependencies]
# rt-multi-thread is required for #[tokio::main] in doctests
tokio = { version = "1.28", features = ["io-util", "macros", "rt", 'rt-multi-thread'] }
tokio = { version = "1.41", features = ["io-util", "macros", "rt", 'rt-multi-thread'] }

[package.metadata.docs.rs]
features = ["mio_08", "tokio"]
77 changes: 50 additions & 27 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
/// Several adapted from std.

use std::convert::TryInto;
use std::os::unix::io::{RawFd, AsRawFd, IntoRawFd};
use std::os::unix::io::{RawFd, AsFd, AsRawFd, IntoRawFd, BorrowedFd, FromRawFd, OwnedFd};
use std::io::{self, ErrorKind};
use std::mem;
use std::time::Duration;

use libc::{c_int, sockaddr, socklen_t, AF_UNIX};
use libc::{bind, connect, getsockname, getpeername};
use libc::{socket, accept, close, listen, socketpair};
use libc::{socket, accept, listen, socketpair};
use libc::{ioctl, FIONBIO};
#[cfg(not(target_os = "haiku"))]
use libc::{FIOCLEX,FIONCLEX};
Expand Down Expand Up @@ -211,26 +211,44 @@ pub fn get_timeout(socket: RawFd, direction: TimeoutDirection)

/// Used in setup of sockets to ensure the file descriptor is always closed
/// if later parts of the setup fails.
pub struct Socket(RawFd);
pub struct Socket(OwnedFd);

//OwnedFd closes the file descriptor on drop. It is guaranteed that nobody else will close the file descriptor.
/*
impl Drop for Socket {
fn drop(&mut self) {
// ignore errors - unlikely and there is nowhere to return them
unsafe { close(self.0) };
}
}
*/

impl IntoRawFd for Socket {
fn into_raw_fd(self) -> RawFd {
let fd = self.0;
mem::forget(self);
fd
self.0.into_raw_fd()
//let fd = self.0;
//mem::forget(self);
//fd
}
}

impl AsRawFd for Socket {
fn as_raw_fd(&self) -> RawFd {
self.0
self.0.as_raw_fd()
}
}

impl AsFd for Socket {
fn as_fd(&self) -> BorrowedFd<'_>
{
self.0.as_fd()
}
}

impl From<Socket> for OwnedFd
{
fn from(value: Socket) -> Self {
value.0
}
}

Expand All @@ -242,12 +260,17 @@ impl Socket {
unsafe {
let nosigpipe = &(nosigpipe as c_int) as *const c_int as *const c_void;
let int_size = mem::size_of::<c_int>() as socklen_t;
cvt!(setsockopt(self.0, SOL_SOCKET, SO_NOSIGPIPE, nosigpipe, int_size))?;
cvt!(setsockopt(self.0.as_raw_fd(), SOL_SOCKET, SO_NOSIGPIPE, nosigpipe, int_size))?;
}
}
Ok(())
}

fn new_int(fd: RawFd) -> Self
{
Self(unsafe { OwnedFd::from_raw_fd(fd) })
}

pub fn new(socket_type: c_int, nonblocking: bool) -> Result<Self, io::Error> {
// Set close-on-exec atomically with SOCK_CLOEXEC if possible.
// Falls through to the portable but race-prone way for compatibility
Expand All @@ -257,19 +280,19 @@ impl Socket {
#[cfg(not(any(target_vendor="apple", target_os = "haiku")))] {
let type_flags = socket_type | SOCK_CLOEXEC | if nonblocking {SOCK_NONBLOCK} else {0};
match cvt!(unsafe { socket(AF_UNIX, type_flags, 0) }) {
Ok(fd) => return Ok(Socket(fd)),
Ok(fd) => return Ok(Socket::new_int(fd)),
Err(ref e) if e.raw_os_error() == Some(EINVAL) => {/*try without*/}
Err(e) => return Err(e),
}
}

// portable but race-prone
let fd = cvt!(unsafe { socket(AF_UNIX, socket_type, 0) })?;
let socket = Socket(fd);
set_cloexec(socket.0, true)?;
let socket = Socket::new_int(fd);
set_cloexec(socket.0.as_raw_fd(), true)?;
socket.set_nosigpipe(true)?;
if nonblocking {
set_nonblocking(socket.0, true)?;
set_nonblocking(socket.0.as_raw_fd(), true)?;
}
Ok(socket)
}
Expand All @@ -290,26 +313,26 @@ impl Socket {
)))] {
let flags = SOCK_CLOEXEC | if nonblocking {SOCK_NONBLOCK} else {0};
match cvt_r!(accept4(fd, addr_ptr, len_ptr, flags)) {
Ok(fd) => return Ok(Socket(fd)),
Ok(fd) => return Ok(Socket::new_int(fd)),
Err(ref e) if e.raw_os_error() == Some(ENOSYS) => {/*try normal accept()*/},
Err(e) => return Err(e),
}
}

// Portable but not as efficient:
let fd = cvt_r!(accept(fd, addr_ptr, len_ptr))?;
let socket = Socket(fd);
set_cloexec(socket.0, true)?;
let socket = Socket::new_int(fd);
set_cloexec(socket.0.as_raw_fd(), true)?;
socket.set_nosigpipe(true)?;
if nonblocking {
set_nonblocking(socket.0, true)?;
set_nonblocking(socket.0.as_raw_fd(), true)?;
}
Ok(socket)
}) }
}

pub fn start_listening(&self) -> Result<(), io::Error> {
cvt!(unsafe { listen(self.0, LISTEN_BACKLOG) }).map(|_| () )
cvt!(unsafe { listen(self.0.as_raw_fd(), LISTEN_BACKLOG) }).map(|_| () )
}

pub fn try_clone_from(fd: RawFd) -> Result<Self, io::Error> {
Expand All @@ -323,7 +346,7 @@ impl Socket {
// for compatibility with Linux < 2.6.24
match cvt!(unsafe { fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
Ok(cloned) => {
let socket = Socket(cloned);
let socket = Socket::new_int(cloned);
socket.set_nosigpipe(true)?;
return Ok(socket);
},
Expand All @@ -332,8 +355,8 @@ impl Socket {
}

let cloned = cvt!(unsafe { dup(fd) })?;
let socket = Socket(cloned);
set_cloexec(socket.0, true)?;
let socket = Socket::new_int(cloned);
set_cloexec(socket.0.as_raw_fd(), true)?;
socket.set_nosigpipe(true)?;
Ok(socket)
}
Expand All @@ -345,22 +368,22 @@ impl Socket {
#[cfg(not(any(target_vendor="apple", target_os = "haiku")))] {
let type_flags = socket_type | SOCK_CLOEXEC | if nonblocking {SOCK_NONBLOCK} else {0};
match cvt!(unsafe { socketpair(AF_UNIX, type_flags, 0, fd_buf[..].as_mut_ptr()) }) {
Ok(_) => return Ok((Socket(fd_buf[0]), Socket(fd_buf[1]))),
Ok(_) => return Ok((Socket::new_int(fd_buf[0]), Socket::new_int(fd_buf[1]))),
Err(ref e) if e.raw_os_error() == Some(EINVAL) => {/*try without*/}
Err(e) => return Err(e),
}
}

cvt!(unsafe { socketpair(AF_UNIX, socket_type, 0, fd_buf[..].as_mut_ptr()) })?;
let a = Socket(fd_buf[0]);
let b = Socket(fd_buf[1]);
set_cloexec(a.0, true)?;
set_cloexec(b.0, true)?;
let a = Socket::new_int(fd_buf[0]);
let b = Socket::new_int(fd_buf[1]);
set_cloexec(a.0.as_raw_fd(), true)?;
set_cloexec(b.0.as_raw_fd(), true)?;
a.set_nosigpipe(true)?;
b.set_nosigpipe(true)?;
if nonblocking {
set_nonblocking(a.0, true)?;
set_nonblocking(b.0, true)?;
set_nonblocking(a.0.as_raw_fd(), true)?;
set_nonblocking(b.0.as_raw_fd(), true)?;
}
Ok((a, b))
}
Expand Down
Loading