aboutsummaryrefslogtreecommitdiffstats
path: root/src/blocking/command.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/blocking/command.rs')
-rw-r--r--src/blocking/command.rs156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/blocking/command.rs b/src/blocking/command.rs
new file mode 100644
index 0000000..3de0f3e
--- /dev/null
+++ b/src/blocking/command.rs
@@ -0,0 +1,156 @@
+use std::os::unix::process::CommandExt as _;
+
+pub struct Command {
+ inner: std::process::Command,
+ stdin: Option<std::process::Stdio>,
+ stdout: Option<std::process::Stdio>,
+ stderr: Option<std::process::Stdio>,
+}
+
+impl Command {
+ pub fn new<S: AsRef<std::ffi::OsStr>>(program: S) -> Self {
+ Self {
+ inner: std::process::Command::new(program),
+ stdin: None,
+ stdout: None,
+ stderr: None,
+ }
+ }
+
+ pub fn arg<S: AsRef<std::ffi::OsStr>>(&mut self, arg: S) -> &mut Self {
+ self.inner.arg(arg);
+ self
+ }
+
+ pub fn args<I, S>(&mut self, args: I) -> &mut Self
+ where
+ I: IntoIterator<Item = S>,
+ S: AsRef<std::ffi::OsStr>,
+ {
+ self.inner.args(args);
+ self
+ }
+
+ pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
+ where
+ K: AsRef<std::ffi::OsStr>,
+ V: AsRef<std::ffi::OsStr>,
+ {
+ self.inner.env(key, val);
+ self
+ }
+
+ pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
+ where
+ I: IntoIterator<Item = (K, V)>,
+ K: AsRef<std::ffi::OsStr>,
+ V: AsRef<std::ffi::OsStr>,
+ {
+ self.inner.envs(vars);
+ self
+ }
+
+ pub fn env_remove<K: AsRef<std::ffi::OsStr>>(
+ &mut self,
+ key: K,
+ ) -> &mut Self {
+ self.inner.env_remove(key);
+ self
+ }
+
+ pub fn env_clear(&mut self) -> &mut Self {
+ self.inner.env_clear();
+ self
+ }
+
+ pub fn current_dir<P: AsRef<std::path::Path>>(
+ &mut self,
+ dir: P,
+ ) -> &mut Self {
+ self.inner.current_dir(dir);
+ self
+ }
+
+ pub fn stdin<T: Into<std::process::Stdio>>(
+ &mut self,
+ cfg: Option<T>,
+ ) -> &mut Self {
+ self.stdin = cfg.map(Into::into);
+ self
+ }
+
+ pub fn stdout<T: Into<std::process::Stdio>>(
+ &mut self,
+ cfg: Option<T>,
+ ) -> &mut Self {
+ self.stdout = cfg.map(Into::into);
+ self
+ }
+
+ pub fn stderr<T: Into<std::process::Stdio>>(
+ &mut self,
+ cfg: Option<T>,
+ ) -> &mut Self {
+ self.stderr = cfg.map(Into::into);
+ self
+ }
+
+ pub fn spawn(
+ &mut self,
+ pty: crate::blocking::Pty,
+ ) -> crate::Result<Child> {
+ let (stdin, stdout, stderr, pre_exec) = crate::sys::setup_subprocess(
+ &pty,
+ pty.pts().map_err(crate::error::spawn)?,
+ )
+ .map_err(crate::error::spawn)?;
+
+ self.inner.stdin(self.stdin.take().unwrap_or(stdin));
+ self.inner.stdout(self.stdout.take().unwrap_or(stdout));
+ self.inner.stderr(self.stderr.take().unwrap_or(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.inner.pre_exec(pre_exec) };
+
+ let child = self.inner.spawn().map_err(crate::error::spawn)?;
+
+ Ok(Child::new(child, pty))
+ }
+}
+
+pub struct Child {
+ inner: std::process::Child,
+ pty: crate::blocking::Pty,
+}
+
+impl Child {
+ fn new(inner: std::process::Child, pty: crate::blocking::Pty) -> Self {
+ Self { inner, pty }
+ }
+
+ #[must_use]
+ pub fn pty(&self) -> &crate::blocking::Pty {
+ &self.pty
+ }
+
+ #[must_use]
+ pub fn pty_mut(&mut self) -> &mut crate::blocking::Pty {
+ &mut self.pty
+ }
+}
+
+impl std::ops::Deref for Child {
+ type Target = std::process::Child;
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+
+impl std::ops::DerefMut for Child {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.inner
+ }
+}