use crate::error::*; use std::os::unix::io::{AsRawFd as _, FromRawFd as _}; use std::os::unix::process::CommandExt as _; pub trait Command { fn spawn_pty(&mut self, size: Option<&crate::pty::Size>) -> Result; } impl Command for std::process::Command { fn spawn_pty( &mut self, size: Option<&crate::pty::Size>, ) -> Result { let pty = crate::pty::Pty::new()?; let pts = pty.pts(size)?; let pt_fd = pty.pt().as_raw_fd(); let pts_fd = pts.as_raw_fd(); let stdin = nix::unistd::dup(pts_fd).map_err(Error::SpawnNix)?; let stdout = nix::unistd::dup(pts_fd).map_err(Error::SpawnNix)?; let stderr = nix::unistd::dup(pts_fd).map_err(Error::SpawnNix)?; // safe because the fds are valid (otherwise pty.pts() or dup() would // have returned an Err and we would have exited early) and are not // owned by any other structure (since dup() returns a fresh copy of // the file descriptor), allowing from_raw_fd to take ownership of it. self.stdin(unsafe { std::process::Stdio::from_raw_fd(stdin) }) .stdout(unsafe { std::process::Stdio::from_raw_fd(stdout) }) .stderr(unsafe { std::process::Stdio::from_raw_fd(stderr) }); // safe because the only thing this function does is call close(), // which is an async-signal-safe function unsafe { self.pre_exec(move || { // 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. // XXX unwrap nix::unistd::close(pt_fd) .map_err(|e| e.as_errno().unwrap())?; nix::unistd::close(pts_fd) .map_err(|e| e.as_errno().unwrap())?; // 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) .map_err(|e| e.as_errno().unwrap())?; nix::unistd::close(stdout) .map_err(|e| e.as_errno().unwrap())?; nix::unistd::close(stderr) .map_err(|e| e.as_errno().unwrap())?; Ok(()) }); } let child = self.spawn().map_err(Error::Spawn)?; Ok(Child { child, pty }) } } pub struct Child { child: std::process::Child, pty: crate::pty::Pty, } impl Child { pub fn pty(&self) -> &std::fs::File { self.pty.pt() } } impl std::ops::Deref for Child { type Target = std::process::Child; fn deref(&self) -> &Self::Target { &self.child } } impl std::ops::DerefMut for Child { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.child } }