From 54e8bf4f2e91407be2054cd38fea81ba830303fd Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 6 Aug 2023 18:30:40 -0400 Subject: convert to rustix it seems more maintained, and hopefully this will fix compile issues on macos --- Cargo.toml | 3 +- deny.toml | 8 ++++ src/blocking/pty.rs | 12 +++--- src/error.rs | 12 +++--- src/pty.rs | 18 ++++---- src/sys.rs | 119 ++++++++++++++++++++++++++++++---------------------- src/types.rs | 2 +- 7 files changed, 102 insertions(+), 72 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 25e2693..f6c4e07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/deny.toml b/deny.toml index 2f0b764..1ac6f74 100644 --- a/deny.toml +++ b/deny.toml @@ -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"] diff --git a/src/blocking/pty.rs b/src/blocking/pty.rs index 445f102..fab88a2 100644 --- a/src/blocking/pty.rs +++ b/src/blocking/pty.rs @@ -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 { - self.0 .0.read(buf) + self.0.read(buf) } } impl std::io::Write for Pty { fn write(&mut self, buf: &[u8]) -> std::io::Result { - 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 { - (&self.0 .0).read(buf) + (&self.0).read(buf) } } impl std::io::Write for &Pty { fn write(&mut self, buf: &[u8]) -> std::io::Result { - (&self.0 .0).write(buf) + (&self.0).write(buf) } fn flush(&mut self) -> std::io::Result<()> { - (&self.0 .0).flush() + (&self.0).flush() } } diff --git a/src/error.rs b/src/error.rs index 8ed24e2..df515c3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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), @@ -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") @@ -29,9 +29,9 @@ impl std::convert::From for Error { } } -impl std::convert::From for Error { - fn from(e: nix::Error) -> Self { - Self::Nix(e) +impl std::convert::From for Error { + fn from(e: rustix::io::Errno) -> Self { + Self::Rustix(e) } } @@ -39,7 +39,7 @@ 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, } diff --git a/src/pty.rs b/src/pty.rs index 87ec874..5b6bd03 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -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(())); @@ -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, } @@ -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, } @@ -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(())); @@ -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, } @@ -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, } @@ -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(())); @@ -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, } @@ -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, } diff --git a/src/sys.rs b/src/sys.rs index 60c61e9..878a374 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -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 { - 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 { 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(()) } @@ -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 { + 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 { + 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 { + 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 { + 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 { @@ -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(()) } } @@ -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 -); diff --git a/src/types.rs b/src/types.rs index dd432eb..f9fd3db 100644 --- a/src/types.rs +++ b/src/types.rs @@ -38,7 +38,7 @@ impl Size { } } -impl From for nix::pty::Winsize { +impl From for libc::winsize { fn from(size: Size) -> Self { Self { ws_row: size.row, -- cgit v1.2.3-54-g00ecf