From 3b550f5d3dad77a56455352579fae3071b42e86d Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 29 Dec 2021 02:55:24 -0500 Subject: wip another complete refactor --- src/sys.rs | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/sys.rs (limited to 'src/sys.rs') diff --git a/src/sys.rs b/src/sys.rs new file mode 100644 index 0000000..9cf6a44 --- /dev/null +++ b/src/sys.rs @@ -0,0 +1,103 @@ +use std::os::unix::io::{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::pty::grantpt(&pt)?; + nix::pty::unlockpt(&pt)?; + + let ptsname = nix::pty::ptsname_r(&pt)?.into(); + + let pt_fd = pt.into_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) }; + + Ok((pt, ptsname)) +} + +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()) + } + .map(|_| ()) +} + +pub fn setup_subprocess( + pt: &impl std::os::unix::io::AsRawFd, + pts: impl std::os::unix::io::IntoRawFd, +) -> nix::Result<( + std::process::Stdio, + std::process::Stdio, + std::process::Stdio, + impl FnMut() -> std::io::Result<()>, +)> { + let pt_fd = pt.as_raw_fd(); + let pts_fd = pts.into_raw_fd(); + + let stdin = nix::unistd::dup(pts_fd)?; + let stdout = nix::unistd::dup(pts_fd)?; + let stderr = nix::unistd::dup(pts_fd)?; + + // 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) }, + move || { + nix::unistd::setsid()?; + set_controlling_terminal(pts_fd)?; + + // in the parent, destructors will handle closing these file + // descriptors (other than pt, used by the parent to + // communicate with the child) when the function ends, but in + // the child, we end by calling exec(), which doesn't call + // destructors. + + nix::unistd::close(pt_fd)?; + nix::unistd::close(pts_fd)?; + // at this point, stdin/stdout/stderr have already been + // reopened as fds 0/1/2 in the child, so we can (and should) + // close the originals + nix::unistd::close(stdin)?; + nix::unistd::close(stdout)?; + nix::unistd::close(stderr)?; + + Ok(()) + }, + )) +} + +fn set_controlling_terminal(fd: std::os::unix::io::RawFd) -> nix::Result<()> { + // Safety: std::fs::File is required to contain a valid file descriptor + unsafe { set_controlling_terminal_unsafe(fd, std::ptr::null()) } + .map(|_| ()) +} + +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 +); -- cgit v1.2.3-54-g00ecf