Skip to content

Commit

Permalink
convert to rustix
Browse files Browse the repository at this point in the history
it seems more maintained, and hopefully this will fix compile issues on
macos
  • Loading branch information
doy committed Aug 6, 2023
1 parent db96755 commit 54e8bf4
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 72 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ include = ["src/**/*", "LICENSE", "README.md", "CHANGELOG.md"]

[dependencies]
libc = "0.2.147"
nix = "0.26.2"
rustix = { version = "0.38.7", features = ["pty", "process", "fs"] }

tokio = { version = "1.29.1", features = ["fs", "process", "net"], optional = true }

[dev-dependencies]
futures = "0.3.28"
nix = { version = "0.26.2", default-features = false, features = ["signal", "fs", "term", "poll"] }
regex = "1.9.3"
tokio = { version = "1.29.1", features = ["full"] }

Expand Down
8 changes: 8 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ yanked = "deny"
unsound = "deny"

[bans]
multiple-versions = "deny"
wildcards = "deny"

skip = [
# this is only a dev dependency (between nix and rustix)
{ name = "bitflags", version = "1.3.2" },
{ name = "bitflags", version = "2.3.3" },
]

[licenses]
allow = ["MIT", "Apache-2.0", "Unicode-DFS-2016"]
Expand Down
12 changes: 6 additions & 6 deletions src/blocking/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,33 +44,33 @@ impl std::os::fd::AsFd for Pty {

impl std::io::Read for Pty {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.0 .0.read(buf)
self.0.read(buf)
}
}

impl std::io::Write for Pty {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0 .0.write(buf)
self.0.write(buf)
}

fn flush(&mut self) -> std::io::Result<()> {
self.0 .0.flush()
self.0.flush()
}
}

impl std::io::Read for &Pty {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
(&self.0 .0).read(buf)
(&self.0).read(buf)
}
}

impl std::io::Write for &Pty {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
(&self.0 .0).write(buf)
(&self.0).write(buf)
}

fn flush(&mut self) -> std::io::Result<()> {
(&self.0 .0).flush()
(&self.0).flush()
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub enum Error {
/// error came from std::io::Error
Io(std::io::Error),
/// error came from nix::Error
Nix(nix::Error),
Rustix(rustix::io::Errno),
/// unsplit was called on halves of two different ptys
#[cfg(feature = "async")]
Unsplit(crate::OwnedReadPty, crate::OwnedWritePty),
Expand All @@ -14,7 +14,7 @@ impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(e) => write!(f, "{e}"),
Self::Nix(e) => write!(f, "{e}"),
Self::Rustix(e) => write!(f, "{e}"),
#[cfg(feature = "async")]
Self::Unsplit(..) => {
write!(f, "unsplit called on halves of two different ptys")
Expand All @@ -29,17 +29,17 @@ impl std::convert::From<std::io::Error> for Error {
}
}

impl std::convert::From<nix::Error> for Error {
fn from(e: nix::Error) -> Self {
Self::Nix(e)
impl std::convert::From<rustix::io::Errno> for Error {
fn from(e: rustix::io::Errno) -> Self {
Self::Rustix(e)
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Nix(e) => Some(e),
Self::Rustix(e) => Some(e),
#[cfg(feature = "async")]
Self::Unsplit(..) => None,
}
Expand Down
18 changes: 9 additions & 9 deletions src/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl tokio::io::AsyncRead for Pty {
// XXX should be able to optimize this once read_buf is stabilized
// in std
let b = buf.initialize_unfilled();
match guard.try_io(|inner| (&inner.get_ref().0).read(b)) {
match guard.try_io(|inner| inner.get_ref().read(b)) {
Ok(Ok(bytes)) => {
buf.advance(bytes);
return std::task::Poll::Ready(Ok(()));
Expand All @@ -108,7 +108,7 @@ impl tokio::io::AsyncWrite for Pty {
std::task::Poll::Ready(guard) => guard,
std::task::Poll::Pending => return std::task::Poll::Pending,
}?;
match guard.try_io(|inner| (&inner.get_ref().0).write(buf)) {
match guard.try_io(|inner| inner.get_ref().write(buf)) {
Ok(result) => return std::task::Poll::Ready(result),
Err(_would_block) => continue,
}
Expand All @@ -124,7 +124,7 @@ impl tokio::io::AsyncWrite for Pty {
std::task::Poll::Ready(guard) => guard,
std::task::Poll::Pending => return std::task::Poll::Pending,
}?;
match guard.try_io(|inner| (&inner.get_ref().0).flush()) {
match guard.try_io(|inner| inner.get_ref().flush()) {
Ok(_) => return std::task::Poll::Ready(Ok(())),
Err(_would_block) => continue,
}
Expand Down Expand Up @@ -161,7 +161,7 @@ impl<'a> tokio::io::AsyncRead for ReadPty<'a> {
// XXX should be able to optimize this once read_buf is stabilized
// in std
let b = buf.initialize_unfilled();
match guard.try_io(|inner| (&inner.get_ref().0).read(b)) {
match guard.try_io(|inner| inner.get_ref().read(b)) {
Ok(Ok(bytes)) => {
buf.advance(bytes);
return std::task::Poll::Ready(Ok(()));
Expand Down Expand Up @@ -197,7 +197,7 @@ impl<'a> tokio::io::AsyncWrite for WritePty<'a> {
std::task::Poll::Ready(guard) => guard,
std::task::Poll::Pending => return std::task::Poll::Pending,
}?;
match guard.try_io(|inner| (&inner.get_ref().0).write(buf)) {
match guard.try_io(|inner| inner.get_ref().write(buf)) {
Ok(result) => return std::task::Poll::Ready(result),
Err(_would_block) => continue,
}
Expand All @@ -213,7 +213,7 @@ impl<'a> tokio::io::AsyncWrite for WritePty<'a> {
std::task::Poll::Ready(guard) => guard,
std::task::Poll::Pending => return std::task::Poll::Pending,
}?;
match guard.try_io(|inner| (&inner.get_ref().0).flush()) {
match guard.try_io(|inner| inner.get_ref().flush()) {
Ok(_) => return std::task::Poll::Ready(Ok(())),
Err(_would_block) => continue,
}
Expand Down Expand Up @@ -272,7 +272,7 @@ impl tokio::io::AsyncRead for OwnedReadPty {
// XXX should be able to optimize this once read_buf is stabilized
// in std
let b = buf.initialize_unfilled();
match guard.try_io(|inner| (&inner.get_ref().0).read(b)) {
match guard.try_io(|inner| inner.get_ref().read(b)) {
Ok(Ok(bytes)) => {
buf.advance(bytes);
return std::task::Poll::Ready(Ok(()));
Expand Down Expand Up @@ -309,7 +309,7 @@ impl tokio::io::AsyncWrite for OwnedWritePty {
std::task::Poll::Ready(guard) => guard,
std::task::Poll::Pending => return std::task::Poll::Pending,
}?;
match guard.try_io(|inner| (&inner.get_ref().0).write(buf)) {
match guard.try_io(|inner| inner.get_ref().write(buf)) {
Ok(result) => return std::task::Poll::Ready(result),
Err(_would_block) => continue,
}
Expand All @@ -325,7 +325,7 @@ impl tokio::io::AsyncWrite for OwnedWritePty {
std::task::Poll::Ready(guard) => guard,
std::task::Poll::Pending => return std::task::Poll::Pending,
}?;
match guard.try_io(|inner| (&inner.get_ref().0).flush()) {
match guard.try_io(|inner| inner.get_ref().flush()) {
Ok(_) => return std::task::Poll::Ready(Ok(())),
Err(_would_block) => continue,
}
Expand Down
119 changes: 70 additions & 49 deletions src/sys.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,60 @@
use std::os::fd::{AsRawFd as _, FromRawFd as _};
use std::os::{
fd::{AsRawFd as _, FromRawFd as _},
unix::prelude::OsStrExt as _,
};

#[derive(Debug)]
pub struct Pty(pub nix::pty::PtyMaster);
pub struct Pty(std::os::fd::OwnedFd);

impl Pty {
pub fn open() -> crate::Result<Self> {
let pt = nix::pty::posix_openpt(
nix::fcntl::OFlag::O_RDWR
| nix::fcntl::OFlag::O_NOCTTY
| nix::fcntl::OFlag::O_CLOEXEC,
let pt = rustix::pty::openpt(
rustix::pty::OpenptFlags::RDWR
| rustix::pty::OpenptFlags::NOCTTY
| rustix::pty::OpenptFlags::CLOEXEC,
)?;
nix::pty::grantpt(&pt)?;
nix::pty::unlockpt(&pt)?;
rustix::pty::grantpt(&pt)?;
rustix::pty::unlockpt(&pt)?;

Ok(Self(pt))
}

pub fn set_term_size(&self, size: crate::Size) -> crate::Result<()> {
let size = size.into();
let size: libc::winsize = size.into();
let fd = self.0.as_raw_fd();

// Safety: nix::pty::PtyMaster is required to contain a valid file
// descriptor and size is guaranteed to be initialized because it's a
// normal rust value, and nix::pty::Winsize is a repr(C) struct with
// the same layout as `struct winsize` from sys/ioctl.h.
Ok(unsafe {
set_term_size_unsafe(fd, std::ptr::NonNull::from(&size).as_ptr())
// TODO: upstream this to rustix
unsafe {
let ret = libc::ioctl(
fd,
libc::TIOCSWINSZ,
std::ptr::NonNull::from(&size).as_ptr(),
);
if ret == -1 {
Err(rustix::io::Errno::from_raw_os_error(
*libc::__errno_location(),
)
.into())
} else {
Ok(())
}
}
.map(|_| ())?)
}

pub fn pts(&self) -> crate::Result<Pts> {
Ok(Pts(std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(nix::pty::ptsname_r(&self.0)?)?
.open(std::ffi::OsStr::from_bytes(
rustix::pty::ptsname(&self.0, vec![])?.as_bytes(),
))?
.into()))
}

#[cfg(feature = "async")]
pub fn set_nonblocking(&self) -> nix::Result<()> {
let bits = nix::fcntl::fcntl(
self.0.as_raw_fd(),
nix::fcntl::FcntlArg::F_GETFL,
)?;
// Safety: bits was just returned from a F_GETFL call. ideally i would
// just be able to use from_bits here, but it fails for some reason?
let mut opts =
unsafe { nix::fcntl::OFlag::from_bits_unchecked(bits) };
opts |= nix::fcntl::OFlag::O_NONBLOCK;
nix::fcntl::fcntl(
self.0.as_raw_fd(),
nix::fcntl::FcntlArg::F_SETFL(opts),
)?;
pub fn set_nonblocking(&self) -> rustix::io::Result<()> {
let mut opts = rustix::fs::fcntl_getfl(&self.0)?;
opts |= rustix::fs::OFlags::NONBLOCK;
rustix::fs::fcntl_setfl(&self.0, opts)?;

Ok(())
}
Expand Down Expand Up @@ -87,6 +89,38 @@ impl std::os::fd::AsRawFd for Pty {
}
}

impl std::io::Read for Pty {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
rustix::io::read(&self.0, buf).map_err(std::io::Error::from)
}
}

impl std::io::Write for Pty {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
rustix::io::write(&self.0, buf).map_err(std::io::Error::from)
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

impl std::io::Read for &Pty {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
rustix::io::read(&self.0, buf).map_err(std::io::Error::from)
}
}

impl std::io::Write for &Pty {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
rustix::io::write(&self.0, buf).map_err(std::io::Error::from)
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

pub struct Pts(std::os::fd::OwnedFd);

impl Pts {
Expand All @@ -107,11 +141,10 @@ impl Pts {
pub fn session_leader(&self) -> impl FnMut() -> std::io::Result<()> {
let pts_fd = self.0.as_raw_fd();
move || {
nix::unistd::setsid()?;
// Safety: OwnedFds are required to contain a valid file descriptor
unsafe {
set_controlling_terminal_unsafe(pts_fd, std::ptr::null())
}?;
rustix::process::setsid()?;
rustix::process::ioctl_tiocsctty(unsafe {
std::os::fd::BorrowedFd::borrow_raw(pts_fd)
})?;
Ok(())
}
}
Expand All @@ -134,15 +167,3 @@ impl std::os::fd::AsRawFd for Pts {
self.0.as_raw_fd()
}
}

nix::ioctl_write_ptr_bad!(
set_term_size_unsafe,
libc::TIOCSWINSZ,
nix::pty::Winsize
);

nix::ioctl_write_ptr_bad!(
set_controlling_terminal_unsafe,
libc::TIOCSCTTY,
libc::c_int
);
2 changes: 1 addition & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl Size {
}
}

impl From<Size> for nix::pty::Winsize {
impl From<Size> for libc::winsize {
fn from(size: Size) -> Self {
Self {
ws_row: size.row,
Expand Down

0 comments on commit 54e8bf4

Please sign in to comment.