use crate::pty::Pty as _;
use ::std::os::unix::io::AsRawFd as _;
#[cfg(any(feature = "backend-async-std", feature = "backend-smol"))]
mod async_process;
#[cfg(feature = "backend-std")]
mod std;
#[cfg(feature = "backend-tokio")]
mod tokio;
pub trait Command {
type Child;
type Pty;
fn spawn_pty(
&mut self,
size: Option<&crate::pty::Size>,
) -> crate::error::Result<Child<Self::Child, Self::Pty>>;
}
impl<T> Command for T
where
T: Impl,
T::Pty: crate::pty::Pty,
<<T as Impl>::Pty as crate::pty::Pty>::Pt: ::std::os::unix::io::AsRawFd,
{
type Child = T::Child;
type Pty = T::Pty;
fn spawn_pty(
&mut self,
size: Option<&crate::pty::Size>,
) -> crate::error::Result<Child<Self::Child, Self::Pty>> {
let (pty, pts, stdin, stdout, stderr) = setup_pty::<Self::Pty>(size)?;
let pt_fd = pty.pt().as_raw_fd();
let pts_fd = pts.as_raw_fd();
self.std_fds(stdin, stdout, stderr);
let pre_exec = move || {
nix::unistd::setsid()?;
set_controlling_terminal(pts_fd)?;
nix::unistd::close(pt_fd)?;
nix::unistd::close(pts_fd)?;
nix::unistd::close(stdin)?;
nix::unistd::close(stdout)?;
nix::unistd::close(stderr)?;
Ok(())
};
unsafe { self.pre_exec_impl(pre_exec) };
let child = self.spawn_impl().map_err(crate::error::spawn)?;
Ok(Child { child, pty })
}
}
pub struct Child<C, P> {
child: C,
pty: P,
}
impl<C, P> Child<C, P>
where
P: crate::pty::Pty,
{
pub fn pty(&self) -> &P::Pt {
self.pty.pt()
}
pub fn pty_mut(&mut self) -> &mut P::Pt {
self.pty.pt_mut()
}
pub fn resize_pty(
&self,
size: &crate::pty::Size,
) -> crate::error::Result<()> {
self.pty.resize(size)
}
}
impl<C, P> ::std::ops::Deref for Child<C, P> {
type Target = C;
fn deref(&self) -> &Self::Target {
&self.child
}
}
impl<C, P> ::std::ops::DerefMut for Child<C, P> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.child
}
}
pub trait Impl {
type Child;
type Pty;
fn std_fds(
&mut self,
stdin: ::std::os::unix::io::RawFd,
stdout: ::std::os::unix::io::RawFd,
stderr: ::std::os::unix::io::RawFd,
);
unsafe fn pre_exec_impl<F>(&mut self, f: F)
where
F: FnMut() -> ::std::io::Result<()> + Send + Sync + 'static;
fn spawn_impl(&mut self) -> ::std::io::Result<Self::Child>;
}
fn setup_pty<P>(
size: Option<&crate::pty::Size>,
) -> crate::error::Result<(
P,
::std::fs::File,
::std::os::unix::io::RawFd,
::std::os::unix::io::RawFd,
::std::os::unix::io::RawFd,
)>
where
P: crate::pty::Pty,
{
let pty = P::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(crate::error::create_pty)?;
let stdout =
nix::unistd::dup(pts_fd).map_err(crate::error::create_pty)?;
let stderr =
nix::unistd::dup(pts_fd).map_err(crate::error::create_pty)?;
Ok((pty, pts, stdin, stdout, stderr))
}
fn set_controlling_terminal(
fd: ::std::os::unix::io::RawFd,
) -> nix::Result<()> {
unsafe { set_controlling_terminal_unsafe(fd, ::std::ptr::null()) }
.map(|_| ())
}
nix::ioctl_write_ptr_bad!(
set_controlling_terminal_unsafe,
libc::TIOCSCTTY,
libc::c_int
);