Skip to content

Commit

Permalink
make spawn also consume the pty
Browse files Browse the repository at this point in the history
both the pty and pts appear to need to be closed before the subprocess
gets an eof on macos
  • Loading branch information
doy committed Jan 30, 2025
1 parent 7f366bd commit e863a34
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 92 deletions.
59 changes: 29 additions & 30 deletions src/blocking/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ impl Command {
}

/// See [`std::process::Command::arg`]
pub fn arg<S: AsRef<std::ffi::OsStr>>(&mut self, arg: S) -> &mut Self {
#[must_use]
pub fn arg<S: AsRef<std::ffi::OsStr>>(mut self, arg: S) -> Self {
self.inner.arg(arg);
self
}

/// See [`std::process::Command::args`]
pub fn args<I, S>(&mut self, args: I) -> &mut Self
#[must_use]
pub fn args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<std::ffi::OsStr>,
Expand All @@ -42,7 +44,8 @@ impl Command {
}

/// See [`std::process::Command::env`]
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
#[must_use]
pub fn env<K, V>(mut self, key: K, val: V) -> Self
where
K: AsRef<std::ffi::OsStr>,
V: AsRef<std::ffi::OsStr>,
Expand All @@ -52,7 +55,8 @@ impl Command {
}

/// See [`std::process::Command::envs`]
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
#[must_use]
pub fn envs<I, K, V>(mut self, vars: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<std::ffi::OsStr>,
Expand All @@ -63,54 +67,45 @@ impl Command {
}

/// See [`std::process::Command::env_remove`]
pub fn env_remove<K: AsRef<std::ffi::OsStr>>(
&mut self,
key: K,
) -> &mut Self {
#[must_use]
pub fn env_remove<K: AsRef<std::ffi::OsStr>>(mut self, key: K) -> Self {
self.inner.env_remove(key);
self
}

/// See [`std::process::Command::env_clear`]
pub fn env_clear(&mut self) -> &mut Self {
#[must_use]
pub fn env_clear(mut self) -> Self {
self.inner.env_clear();
self
}

/// See [`std::process::Command::current_dir`]
pub fn current_dir<P: AsRef<std::path::Path>>(
&mut self,
dir: P,
) -> &mut Self {
#[must_use]
pub fn current_dir<P: AsRef<std::path::Path>>(mut self, dir: P) -> Self {
self.inner.current_dir(dir);
self
}

/// See [`std::process::Command::stdin`]
pub fn stdin<T: Into<std::process::Stdio>>(
&mut self,
cfg: T,
) -> &mut Self {
#[must_use]
pub fn stdin<T: Into<std::process::Stdio>>(mut self, cfg: T) -> Self {
self.stdin = true;
self.inner.stdin(cfg);
self
}

/// See [`std::process::Command::stdout`]
pub fn stdout<T: Into<std::process::Stdio>>(
&mut self,
cfg: T,
) -> &mut Self {
#[must_use]
pub fn stdout<T: Into<std::process::Stdio>>(mut self, cfg: T) -> Self {
self.stdout = true;
self.inner.stdout(cfg);
self
}

/// See [`std::process::Command::stderr`]
pub fn stderr<T: Into<std::process::Stdio>>(
&mut self,
cfg: T,
) -> &mut Self {
#[must_use]
pub fn stderr<T: Into<std::process::Stdio>>(mut self, cfg: T) -> Self {
self.stderr = true;
self.inner.stderr(cfg);
self
Expand All @@ -133,7 +128,7 @@ impl Command {
/// session leader or set its controlling terminal.
#[allow(clippy::needless_pass_by_value)]
pub fn spawn(
&mut self,
mut self,
pts: crate::blocking::Pts,
) -> crate::Result<std::process::Child> {
self.spawn_impl(&pts)
Expand Down Expand Up @@ -202,20 +197,23 @@ impl Command {
}

/// See [`std::os::unix::process::CommandExt::uid`]
pub fn uid(&mut self, id: u32) -> &mut Self {
#[must_use]
pub fn uid(mut self, id: u32) -> Self {
self.inner.uid(id);
self
}

/// See [`std::os::unix::process::CommandExt::gid`]
pub fn gid(&mut self, id: u32) -> &mut Self {
#[must_use]
pub fn gid(mut self, id: u32) -> Self {
self.inner.gid(id);
self
}

/// See [`std::os::unix::process::CommandExt::pre_exec`]
#[allow(clippy::missing_safety_doc)]
pub unsafe fn pre_exec<F>(&mut self, f: F) -> &mut Self
#[must_use]
pub unsafe fn pre_exec<F>(mut self, f: F) -> Self
where
F: FnMut() -> std::io::Result<()> + Send + Sync + 'static,
{
Expand All @@ -224,7 +222,8 @@ impl Command {
}

/// See [`std::os::unix::process::CommandExt::arg0`]
pub fn arg0<S>(&mut self, arg: S) -> &mut Self
#[must_use]
pub fn arg0<S>(mut self, arg: S) -> Self
where
S: AsRef<std::ffi::OsStr>,
{
Expand Down
62 changes: 31 additions & 31 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ impl Command {
}

/// See [`tokio::process::Command::arg`]
pub fn arg<S: AsRef<std::ffi::OsStr>>(&mut self, arg: S) -> &mut Self {
#[must_use]
pub fn arg<S: AsRef<std::ffi::OsStr>>(mut self, arg: S) -> Self {
self.inner.arg(arg);
self
}

/// See [`tokio::process::Command::args`]
pub fn args<I, S>(&mut self, args: I) -> &mut Self
#[must_use]
pub fn args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<std::ffi::OsStr>,
Expand All @@ -40,7 +42,8 @@ impl Command {
}

/// See [`tokio::process::Command::env`]
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
#[must_use]
pub fn env<K, V>(mut self, key: K, val: V) -> Self
where
K: AsRef<std::ffi::OsStr>,
V: AsRef<std::ffi::OsStr>,
Expand All @@ -50,7 +53,8 @@ impl Command {
}

/// See [`tokio::process::Command::envs`]
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
#[must_use]
pub fn envs<I, K, V>(mut self, vars: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<std::ffi::OsStr>,
Expand All @@ -61,60 +65,52 @@ impl Command {
}

/// See [`tokio::process::Command::env_remove`]
pub fn env_remove<K: AsRef<std::ffi::OsStr>>(
&mut self,
key: K,
) -> &mut Self {
#[must_use]
pub fn env_remove<K: AsRef<std::ffi::OsStr>>(mut self, key: K) -> Self {
self.inner.env_remove(key);
self
}

/// See [`tokio::process::Command::env_clear`]
pub fn env_clear(&mut self) -> &mut Self {
#[must_use]
pub fn env_clear(mut self) -> Self {
self.inner.env_clear();
self
}

/// See [`tokio::process::Command::current_dir`]
pub fn current_dir<P: AsRef<std::path::Path>>(
&mut self,
dir: P,
) -> &mut Self {
#[must_use]
pub fn current_dir<P: AsRef<std::path::Path>>(mut self, dir: P) -> Self {
self.inner.current_dir(dir);
self
}

/// See [`tokio::process::Command::kill_on_drop`]
pub fn kill_on_drop(&mut self, kill_on_drop: bool) -> &mut Self {
#[must_use]
pub fn kill_on_drop(mut self, kill_on_drop: bool) -> Self {
self.inner.kill_on_drop(kill_on_drop);
self
}

/// See [`tokio::process::Command::stdin`]
pub fn stdin<T: Into<std::process::Stdio>>(
&mut self,
cfg: T,
) -> &mut Self {
#[must_use]
pub fn stdin<T: Into<std::process::Stdio>>(mut self, cfg: T) -> Self {
self.stdin = true;
self.inner.stdin(cfg);
self
}

/// See [`tokio::process::Command::stdout`]
pub fn stdout<T: Into<std::process::Stdio>>(
&mut self,
cfg: T,
) -> &mut Self {
#[must_use]
pub fn stdout<T: Into<std::process::Stdio>>(mut self, cfg: T) -> Self {
self.stdout = true;
self.inner.stdout(cfg);
self
}

/// See [`tokio::process::Command::stderr`]
pub fn stderr<T: Into<std::process::Stdio>>(
&mut self,
cfg: T,
) -> &mut Self {
#[must_use]
pub fn stderr<T: Into<std::process::Stdio>>(mut self, cfg: T) -> Self {
self.stderr = true;
self.inner.stderr(cfg);
self
Expand All @@ -137,7 +133,7 @@ impl Command {
/// session leader or set its controlling terminal.
#[allow(clippy::needless_pass_by_value)]
pub fn spawn(
&mut self,
mut self,
pts: crate::Pts,
) -> crate::Result<tokio::process::Child> {
self.spawn_impl(&pts)
Expand Down Expand Up @@ -206,20 +202,23 @@ impl Command {
}

/// See [`tokio::process::Command::uid`]
pub fn uid(&mut self, id: u32) -> &mut Self {
#[must_use]
pub fn uid(mut self, id: u32) -> Self {
self.inner.uid(id);
self
}

/// See [`tokio::process::Command::gid`]
pub fn gid(&mut self, id: u32) -> &mut Self {
#[must_use]
pub fn gid(mut self, id: u32) -> Self {
self.inner.gid(id);
self
}

/// See [`tokio::process::Command::pre_exec`]
#[allow(clippy::missing_safety_doc)]
pub unsafe fn pre_exec<F>(&mut self, f: F) -> &mut Self
#[must_use]
pub unsafe fn pre_exec<F>(mut self, f: F) -> Self
where
F: FnMut() -> std::io::Result<()> + Send + Sync + 'static,
{
Expand All @@ -228,7 +227,8 @@ impl Command {
}

/// See [`tokio::process::Command::arg0`]
pub fn arg0<S>(&mut self, arg: S) -> &mut Self
#[must_use]
pub fn arg0<S>(mut self, arg: S) -> Self
where
S: AsRef<std::ffi::OsStr>,
{
Expand Down
26 changes: 13 additions & 13 deletions tests/behavior.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,19 @@ fn test_multiple_configured() {
let (pre_exec_pipe_r, pre_exec_pipe_w) = pipe();
let mut pre_exec_pipe_r =
std::io::BufReader::new(std::fs::File::from(pre_exec_pipe_r));
let mut cmd = pty_process::blocking::Command::new("perl");
cmd.arg("-Esay 'foo'; say STDERR 'foo-stderr'; open my $fh, '>&=3'; say $fh 'foo-3';")
let cmd = pty_process::blocking::Command::new("perl")
.arg("-Esay 'foo'; say STDERR 'foo-stderr'; open my $fh, '>&=3'; say $fh 'foo-3';")
.stderr(std::process::Stdio::from(stderr_pipe_w));
unsafe {
let mut cmd = unsafe {
cmd.pre_exec(move || {
nix::unistd::dup2(pre_exec_pipe_w.as_raw_fd(), 3)?;
nix::fcntl::fcntl(
3,
nix::fcntl::F_SETFD(nix::fcntl::FdFlag::empty()),
)?;
Ok(())
});
}
})
};
let mut child = cmd.spawn_borrowed(&pts).unwrap();

let mut output = helpers::output(&pty);
Expand Down Expand Up @@ -149,24 +149,24 @@ async fn test_multiple_configured_async() {
let mut pre_exec_pipe_r = tokio::io::BufReader::new(unsafe {
tokio::fs::File::from_raw_fd(pre_exec_pipe_r.into_raw_fd())
});
let mut cmd = pty_process::Command::new("perl");
cmd.arg(
"-Esay 'foo'; \
let cmd = pty_process::Command::new("perl")
.arg(
"-Esay 'foo'; \
say STDERR 'foo-stderr'; \
open my $fh, '>&=3'; \
say $fh 'foo-3';",
)
.stderr(std::process::Stdio::from(stderr_pipe_w));
unsafe {
)
.stderr(std::process::Stdio::from(stderr_pipe_w));
let mut cmd = unsafe {
cmd.pre_exec(move || {
nix::unistd::dup2(pre_exec_pipe_w.as_raw_fd(), 3)?;
nix::fcntl::fcntl(
3,
nix::fcntl::F_SETFD(nix::fcntl::FdFlag::empty()),
)?;
Ok(())
});
}
})
};
let mut child = cmd.spawn_borrowed(&pts).unwrap();

let mut output = helpers::output_async(pty_r);
Expand Down
Loading

0 comments on commit e863a34

Please sign in to comment.