aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-02-22 03:43:49 -0500
committerJesse Luehrs <doy@tozt.net>2021-02-22 03:43:49 -0500
commitedfffc8c490b9d179a62901dfb874a85ab0984e3 (patch)
tree59eb9120eb4f61d5d9d43cafb1a51eadaef6891f
parent2d3e62e97d1dc80594bdb302426cc226c6a9735c (diff)
downloadpty-process-edfffc8c490b9d179a62901dfb874a85ab0984e3.tar.gz
pty-process-edfffc8c490b9d179a62901dfb874a85ab0984e3.zip
refactor
-rw-r--r--examples/basic.rs2
-rw-r--r--src/command.rs156
-rw-r--r--src/command/std.rs37
3 files changed, 111 insertions, 84 deletions
diff --git a/examples/basic.rs b/examples/basic.rs
index c0c1236..d7f99d0 100644
--- a/examples/basic.rs
+++ b/examples/basic.rs
@@ -35,7 +35,7 @@ impl Drop for RawGuard {
}
}
-fn run(child: &pty_process::Child) {
+fn run(child: &pty_process::Child<std::process::Child>) {
let _raw = RawGuard::new();
let mut buf = [0_u8; 4096];
let pty = child.pty().as_raw_fd();
diff --git a/src/command.rs b/src/command.rs
index 4f8f808..ba7d764 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -1,87 +1,23 @@
use crate::error::*;
-use std::os::unix::io::{AsRawFd as _, FromRawFd as _};
-use std::os::unix::process::CommandExt as _;
+use ::std::os::unix::io::AsRawFd as _;
-pub trait Command {
- fn spawn_pty(&mut self, size: Option<&crate::pty::Size>)
- -> Result<Child>;
-}
+mod std;
-impl Command for std::process::Command {
+pub trait Command<T> {
fn spawn_pty(
&mut self,
size: Option<&crate::pty::Size>,
- ) -> Result<Child> {
- let pty = crate::pty::Pty::new()?;
- if let Some(size) = size {
- pty.resize(size)?;
- }
-
- let pts = pty.pts()?;
-
- 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 setsid() and close() are async-signal-safe functions
- // and ioctl() is a raw syscall (which is inherently
- // async-signal-safe).
- unsafe {
- self.pre_exec(move || {
- nix::unistd::setsid().map_err(|e| e.as_errno().unwrap())?;
- set_controlling_terminal(&pts)
- .map_err(|e| e.as_errno().unwrap())?;
-
- // 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 })
- }
+ ) -> Result<Child<T>>;
}
-pub struct Child {
- child: std::process::Child,
+pub struct Child<T> {
+ child: T,
pty: crate::pty::Pty,
}
-impl Child {
- pub fn pty(&self) -> &std::fs::File {
+impl<T> Child<T> {
+ pub fn pty(&self) -> &::std::fs::File {
self.pty.pt()
}
@@ -90,30 +26,84 @@ impl Child {
}
}
-impl std::ops::Deref for Child {
- type Target = std::process::Child;
+impl<T> ::std::ops::Deref for Child<T> {
+ type Target = T;
fn deref(&self) -> &Self::Target {
&self.child
}
}
-impl std::ops::DerefMut for Child {
+impl<T> ::std::ops::DerefMut for Child<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.child
}
}
-nix::ioctl_write_ptr_bad!(
- set_controlling_terminal_unsafe,
- libc::TIOCSCTTY,
- libc::c_int
-);
+fn setup_pty(
+ size: Option<&crate::pty::Size>,
+) -> Result<(
+ crate::pty::Pty,
+ ::std::fs::File,
+ ::std::os::unix::io::RawFd,
+ ::std::os::unix::io::RawFd,
+ ::std::os::unix::io::RawFd,
+)> {
+ let pty = crate::pty::Pty::new()?;
+ if let Some(size) = size {
+ pty.resize(size)?;
+ }
+
+ let pts = pty.pts()?;
+ 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)?;
-fn set_controlling_terminal(file: &std::fs::File) -> nix::Result<()> {
- let fd = file.as_raw_fd();
+ Ok((pty, pts, stdin, stdout, stderr))
+}
+
+fn pre_exec(
+ pt_fd: ::std::os::unix::io::RawFd,
+ pts_fd: ::std::os::unix::io::RawFd,
+ stdin: ::std::os::unix::io::RawFd,
+ stdout: ::std::os::unix::io::RawFd,
+ stderr: ::std::os::unix::io::RawFd,
+) -> ::std::io::Result<()> {
+ nix::unistd::setsid().map_err(|e| e.as_errno().unwrap())?;
+ set_controlling_terminal(pts_fd).map_err(|e| e.as_errno().unwrap())?;
+
+ // 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(())
+}
+
+fn set_controlling_terminal(
+ fd: ::std::os::unix::io::RawFd,
+) -> nix::Result<()> {
// safe because std::fs::File is required to contain a valid file
// descriptor
- unsafe { set_controlling_terminal_unsafe(fd, std::ptr::null()) }
+ unsafe { set_controlling_terminal_unsafe(fd, ::std::ptr::null()) }
.map(|_| ())
}
+
+nix::ioctl_write_ptr_bad!(
+ set_controlling_terminal_unsafe,
+ libc::TIOCSCTTY,
+ libc::c_int
+);
diff --git a/src/command/std.rs b/src/command/std.rs
new file mode 100644
index 0000000..d544b83
--- /dev/null
+++ b/src/command/std.rs
@@ -0,0 +1,37 @@
+use crate::error::*;
+
+use std::os::unix::io::{AsRawFd as _, FromRawFd as _};
+use std::os::unix::process::CommandExt as _;
+
+impl super::Command<std::process::Child> for std::process::Command {
+ fn spawn_pty(
+ &mut self,
+ size: Option<&crate::pty::Size>,
+ ) -> Result<super::Child<std::process::Child>> {
+ let (pty, pts, stdin, stdout, stderr) = super::setup_pty(size)?;
+
+ let pt_fd = pty.pt().as_raw_fd();
+ let pts_fd = pts.as_raw_fd();
+
+ // 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 setsid() and close() are async-signal-safe functions
+ // and ioctl() is a raw syscall (which is inherently
+ // async-signal-safe).
+ unsafe {
+ self.pre_exec(move || {
+ super::pre_exec(pt_fd, pts_fd, stdin, stdout, stderr)
+ });
+ }
+
+ let child = self.spawn().map_err(Error::Spawn)?;
+
+ Ok(super::Child { child, pty })
+ }
+}