diff options
author | Jesse Luehrs <doy@tozt.net> | 2022-01-03 05:33:21 -0500 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2022-01-03 05:59:31 -0500 |
commit | 4142936f657004f88e259583136f25f566b0b1c0 (patch) | |
tree | a742607891e39a03587e0b22be73e3bb8dd49b32 /src/builtins/command.rs | |
parent | a3af022f75af415709481cd6dcd73f4d33b472e4 (diff) | |
download | nbsh-4142936f657004f88e259583136f25f566b0b1c0.tar.gz nbsh-4142936f657004f88e259583136f25f566b0b1c0.zip |
split up code into more files
Diffstat (limited to 'src/builtins/command.rs')
-rw-r--r-- | src/builtins/command.rs | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/builtins/command.rs b/src/builtins/command.rs new file mode 100644 index 0000000..6dfa56c --- /dev/null +++ b/src/builtins/command.rs @@ -0,0 +1,235 @@ +use async_std::io::{ReadExt as _, WriteExt as _}; +use std::os::unix::io::{AsRawFd as _, FromRawFd as _, IntoRawFd as _}; + +pub struct Command { + exe: crate::parse::Exe, + f: super::Builtin, + io: Io, +} + +impl Command { + pub fn new(exe: &crate::parse::Exe) -> Option<Self> { + super::BUILTINS.get(exe.exe()).map(|f| Self { + exe: exe.clone(), + f, + io: Io::new(), + }) + } + + pub fn stdin(&mut self, fh: std::fs::File) { + self.io.set_stdin(fh); + } + + pub fn stdout(&mut self, fh: std::fs::File) { + self.io.set_stdout(fh); + } + + pub fn stderr(&mut self, fh: std::fs::File) { + self.io.set_stderr(fh); + } + + pub unsafe fn pre_exec<F>(&mut self, f: F) + where + F: 'static + FnMut() -> std::io::Result<()> + Send + Sync, + { + self.io.pre_exec(f); + } + + pub fn spawn(self, env: &crate::env::Env) -> anyhow::Result<Child> { + let Self { f, exe, io } = self; + (f)(&exe, env, io) + } +} + +pub struct Io { + fds: std::collections::HashMap< + std::os::unix::io::RawFd, + std::os::unix::io::RawFd, + >, + pre_exec: Option< + Box<dyn 'static + FnMut() -> std::io::Result<()> + Send + Sync>, + >, +} + +impl Io { + fn new() -> Self { + let mut fds = std::collections::HashMap::new(); + fds.insert(0.as_raw_fd(), 0.as_raw_fd()); + fds.insert(1.as_raw_fd(), 1.as_raw_fd()); + fds.insert(2.as_raw_fd(), 2.as_raw_fd()); + Self { + fds, + pre_exec: None, + } + } + + fn stdin(&self) -> Option<async_std::fs::File> { + self.fds + .get(&0.as_raw_fd()) + .copied() + .map(|fd| unsafe { async_std::fs::File::from_raw_fd(fd) }) + } + + fn set_stdin<T: std::os::unix::io::IntoRawFd>(&mut self, stdin: T) { + if let Some(fd) = self.fds.get(&0.as_raw_fd()) { + if *fd > 2 { + drop(unsafe { async_std::fs::File::from_raw_fd(*fd) }); + } + } + self.fds.insert(0.as_raw_fd(), stdin.into_raw_fd()); + } + + fn stdout(&self) -> Option<async_std::fs::File> { + self.fds + .get(&1.as_raw_fd()) + .copied() + .map(|fd| unsafe { async_std::fs::File::from_raw_fd(fd) }) + } + + fn set_stdout<T: std::os::unix::io::IntoRawFd>(&mut self, stdout: T) { + if let Some(fd) = self.fds.get(&1.as_raw_fd()) { + if *fd > 2 { + drop(unsafe { async_std::fs::File::from_raw_fd(*fd) }); + } + } + self.fds.insert(1.as_raw_fd(), stdout.into_raw_fd()); + } + + fn stderr(&self) -> Option<async_std::fs::File> { + self.fds + .get(&2.as_raw_fd()) + .copied() + .map(|fd| unsafe { async_std::fs::File::from_raw_fd(fd) }) + } + + fn set_stderr<T: std::os::unix::io::IntoRawFd>(&mut self, stderr: T) { + if let Some(fd) = self.fds.get(&2.as_raw_fd()) { + if *fd > 2 { + drop(unsafe { async_std::fs::File::from_raw_fd(*fd) }); + } + } + self.fds.insert(2.as_raw_fd(), stderr.into_raw_fd()); + } + + pub unsafe fn pre_exec<F>(&mut self, f: F) + where + F: 'static + FnMut() -> std::io::Result<()> + Send + Sync, + { + self.pre_exec = Some(Box::new(f)); + } + + pub async fn read_stdin(&self, buf: &mut [u8]) -> anyhow::Result<usize> { + if let Some(mut fh) = self.stdin() { + let res = fh.read(buf).await; + let _ = fh.into_raw_fd(); + Ok(res?) + } else { + Ok(0) + } + } + + pub async fn write_stdout(&self, buf: &[u8]) -> anyhow::Result<()> { + if let Some(mut fh) = self.stdout() { + let res = fh.write_all(buf).await; + let _ = fh.into_raw_fd(); + Ok(res.map(|_| ())?) + } else { + Ok(()) + } + } + + pub async fn write_stderr(&self, buf: &[u8]) -> anyhow::Result<()> { + if let Some(mut fh) = self.stderr() { + let res = fh.write_all(buf).await; + let _ = fh.into_raw_fd(); + Ok(res.map(|_| ())?) + } else { + Ok(()) + } + } + + pub fn setup_command(mut self, cmd: &mut crate::pipeline::Command) { + if let Some(stdin) = self.stdin() { + let stdin = stdin.into_raw_fd(); + if stdin != 0 { + cmd.stdin(unsafe { std::fs::File::from_raw_fd(stdin) }); + self.fds.remove(&0.as_raw_fd()); + } + } + if let Some(stdout) = self.stdout() { + let stdout = stdout.into_raw_fd(); + if stdout != 1 { + cmd.stdout(unsafe { std::fs::File::from_raw_fd(stdout) }); + self.fds.remove(&1.as_raw_fd()); + } + } + if let Some(stderr) = self.stderr() { + let stderr = stderr.into_raw_fd(); + if stderr != 2 { + cmd.stderr(unsafe { std::fs::File::from_raw_fd(stderr) }); + self.fds.remove(&2.as_raw_fd()); + } + } + if let Some(pre_exec) = self.pre_exec.take() { + unsafe { cmd.pre_exec(pre_exec) }; + } + } +} + +impl Drop for Io { + fn drop(&mut self) { + for fd in self.fds.values() { + if *fd > 2 { + drop(unsafe { std::fs::File::from_raw_fd(*fd) }); + } + } + } +} + +pub struct Child { + fut: std::pin::Pin< + Box< + dyn std::future::Future<Output = std::process::ExitStatus> + + Sync + + Send, + >, + >, + wrapped_child: Option<Box<crate::pipeline::Child>>, +} + +impl Child { + pub fn new_fut<F>(fut: F) -> Self + where + F: std::future::Future<Output = std::process::ExitStatus> + + Sync + + Send + + 'static, + { + Self { + fut: Box::pin(fut), + wrapped_child: None, + } + } + + pub fn new_wrapped(child: crate::pipeline::Child) -> Self { + Self { + fut: Box::pin(async move { unreachable!() }), + wrapped_child: Some(Box::new(child)), + } + } + + pub fn id(&self) -> Option<u32> { + self.wrapped_child.as_ref().and_then(|cmd| cmd.id()) + } + + #[async_recursion::async_recursion] + pub async fn status( + self, + ) -> anyhow::Result<async_std::process::ExitStatus> { + if let Some(child) = self.wrapped_child { + child.status().await + } else { + Ok(self.fut.await) + } + } +} |