From 39287b07f87aba15c4cb0f64d7008ba67289151d Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Tue, 22 Feb 2022 17:02:12 -0500 Subject: another rewrite --- src/sys.rs | 172 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 108 insertions(+), 64 deletions(-) (limited to 'src/sys.rs') diff --git a/src/sys.rs b/src/sys.rs index 0d897bc..244a43d 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,83 +1,127 @@ -use std::os::unix::io::{FromRawFd as _, IntoRawFd as _}; +use std::os::unix::io::{AsRawFd as _, FromRawFd as _, IntoRawFd as _}; -pub fn create_pt() -> nix::Result<(std::fs::File, std::path::PathBuf)> { - let pt = nix::pty::posix_openpt( - nix::fcntl::OFlag::O_RDWR - | nix::fcntl::OFlag::O_NOCTTY - | nix::fcntl::OFlag::O_CLOEXEC, - )?; - nix::pty::grantpt(&pt)?; - nix::pty::unlockpt(&pt)?; +#[derive(Debug)] +pub struct Pty(pub nix::pty::PtyMaster); - let ptsname = nix::pty::ptsname_r(&pt)?.into(); +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, + )?; + nix::pty::grantpt(&pt)?; + nix::pty::unlockpt(&pt)?; - let pt_fd = pt.into_raw_fd(); + Ok(Self(pt)) + } + + pub fn set_term_size(&self, size: crate::Size) -> crate::Result<()> { + let size = size.into(); + let fd = self.0.as_raw_fd(); - // Safety: posix_openpt (or the previous functions operating on the - // result) would have returned an Err (causing us to return early) if the - // file descriptor was invalid. additionally, into_raw_fd gives up - // ownership over the file descriptor, allowing the newly created File - // object to take full ownership. - let pt = unsafe { std::fs::File::from_raw_fd(pt_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()) + } + .map(|_| ())?) + } + + pub fn pts(&self) -> crate::Result { + Ok(Pts(std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(nix::pty::ptsname_r(&self.0)?)? + .into_raw_fd())) + } + + #[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), + )?; + + Ok(()) + } +} - Ok((pt, ptsname)) +impl std::os::unix::io::AsRawFd for Pty { + fn as_raw_fd(&self) -> std::os::unix::io::RawFd { + self.0.as_raw_fd() + } } -pub fn set_term_size( - fh: &impl std::os::unix::io::AsRawFd, - size: crate::Size, -) -> nix::Result<()> { - let size = size.into(); - let fd = fh.as_raw_fd(); - - // Safety: std::fs::File 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. - unsafe { - set_term_size_unsafe(fd, std::ptr::NonNull::from(&size).as_ptr()) +pub struct Pts(std::os::unix::io::RawFd); + +impl Pts { + pub fn setup_subprocess( + &self, + ) -> nix::Result<( + std::process::Stdio, + std::process::Stdio, + std::process::Stdio, + )> { + let pts_fd = self.0.as_raw_fd(); + + let stdin = nix::fcntl::fcntl( + pts_fd, + nix::fcntl::FcntlArg::F_DUPFD_CLOEXEC(0), + )?; + let stdout = nix::fcntl::fcntl( + pts_fd, + nix::fcntl::FcntlArg::F_DUPFD_CLOEXEC(0), + )?; + let stderr = nix::fcntl::fcntl( + pts_fd, + nix::fcntl::FcntlArg::F_DUPFD_CLOEXEC(0), + )?; + + // Safety: these file descriptors were all just returned from dup, so + // they must be valid + Ok(( + unsafe { std::process::Stdio::from_raw_fd(stdin) }, + unsafe { std::process::Stdio::from_raw_fd(stdout) }, + unsafe { std::process::Stdio::from_raw_fd(stderr) }, + )) + } + + pub fn session_leader(&self) -> impl FnMut() -> std::io::Result<()> { + let pts_fd = self.0.as_raw_fd(); + move || { + nix::unistd::setsid()?; + set_controlling_terminal(pts_fd)?; + Ok(()) + } } - .map(|_| ()) } -pub fn setup_subprocess( - pts: &impl std::os::unix::io::AsRawFd, -) -> nix::Result<( - std::process::Stdio, - std::process::Stdio, - std::process::Stdio, -)> { - let pts_fd = pts.as_raw_fd(); - - let stdin = - nix::fcntl::fcntl(pts_fd, nix::fcntl::FcntlArg::F_DUPFD_CLOEXEC(0))?; - let stdout = - nix::fcntl::fcntl(pts_fd, nix::fcntl::FcntlArg::F_DUPFD_CLOEXEC(0))?; - let stderr = - nix::fcntl::fcntl(pts_fd, nix::fcntl::FcntlArg::F_DUPFD_CLOEXEC(0))?; - - // Safety: these file descriptors were all just returned from dup, so they - // must be valid - Ok(( - unsafe { std::process::Stdio::from_raw_fd(stdin) }, - unsafe { std::process::Stdio::from_raw_fd(stdout) }, - unsafe { std::process::Stdio::from_raw_fd(stderr) }, - )) +impl Drop for Pts { + fn drop(&mut self) { + let _ = nix::unistd::close(self.0); + } } -pub fn session_leader( - pts: &impl std::os::unix::io::AsRawFd, -) -> impl FnMut() -> std::io::Result<()> { - let pts_fd = pts.as_raw_fd(); - move || { - nix::unistd::setsid()?; - set_controlling_terminal(pts_fd)?; - Ok(()) +impl std::os::unix::io::AsRawFd for Pts { + fn as_raw_fd(&self) -> std::os::unix::io::RawFd { + self.0 } } fn set_controlling_terminal(fd: std::os::unix::io::RawFd) -> nix::Result<()> { - // Safety: std::fs::File is required to contain a valid file descriptor + // Safety: Pts is required to contain a valid file descriptor unsafe { set_controlling_terminal_unsafe(fd, std::ptr::null()) } .map(|_| ()) } -- cgit v1.2.3-54-g00ecf