summaryrefslogtreecommitdiffstats
path: root/src/builtins/command.rs
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-03 05:33:21 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-03 05:59:31 -0500
commit4142936f657004f88e259583136f25f566b0b1c0 (patch)
treea742607891e39a03587e0b22be73e3bb8dd49b32 /src/builtins/command.rs
parenta3af022f75af415709481cd6dcd73f4d33b472e4 (diff)
downloadnbsh-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.rs235
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)
+ }
+ }
+}