openssh/native_mux_impl/
command.rs

1use super::Error;
2use super::RemoteChild;
3use super::{ChildStderr, ChildStdin, ChildStdout, Stdio};
4
5use std::borrow::Cow;
6use std::ffi::OsStr;
7use std::os::unix::ffi::OsStrExt;
8use std::path::Path;
9
10use openssh_mux_client::{Connection, NonZeroByteSlice, Session};
11
12#[derive(Debug)]
13pub(crate) struct Command {
14    cmd: Vec<u8>,
15    ctl: Box<Path>,
16    subsystem: bool,
17
18    stdin_v: Stdio,
19    stdout_v: Stdio,
20    stderr_v: Stdio,
21}
22
23impl Command {
24    pub(crate) fn new(ctl: Box<Path>, cmd: Vec<u8>, subsystem: bool) -> Self {
25        Self {
26            cmd,
27            ctl,
28            subsystem,
29
30            stdin_v: Stdio::inherit(),
31            stdout_v: Stdio::inherit(),
32            stderr_v: Stdio::inherit(),
33        }
34    }
35
36    pub(crate) fn raw_arg<S: AsRef<OsStr>>(&mut self, arg: S) {
37        self.cmd.push(b' ');
38        self.cmd.extend_from_slice(arg.as_ref().as_bytes());
39    }
40
41    pub(crate) fn stdin<T: Into<Stdio>>(&mut self, cfg: T) {
42        self.stdin_v = cfg.into();
43    }
44
45    pub(crate) fn stdout<T: Into<Stdio>>(&mut self, cfg: T) {
46        self.stdout_v = cfg.into();
47    }
48
49    pub(crate) fn stderr<T: Into<Stdio>>(&mut self, cfg: T) {
50        self.stderr_v = cfg.into();
51    }
52
53    pub(crate) async fn spawn(
54        &mut self,
55    ) -> Result<
56        (
57            RemoteChild,
58            Option<ChildStdin>,
59            Option<ChildStdout>,
60            Option<ChildStderr>,
61        ),
62        Error,
63    > {
64        let (stdin, child_stdin) = self.stdin_v.to_stdin()?;
65        let (stdout, child_stdout) = self.stdout_v.to_stdout()?;
66        let (stderr, child_stderr) = self.stderr_v.to_stderr()?;
67
68        let stdios = [
69            stdin.as_raw_fd_or_null_fd()?,
70            stdout.as_raw_fd_or_null_fd()?,
71            stderr.as_raw_fd_or_null_fd()?,
72        ];
73
74        let cmd = NonZeroByteSlice::new(&self.cmd).ok_or(Error::InvalidCommand)?;
75
76        #[cfg(feature = "tracing")]
77        tracing::debug!(cmd = String::from_utf8_lossy(cmd.into_inner()).as_ref());
78
79        let session = Session::builder()
80            .cmd(Cow::Borrowed(cmd))
81            .subsystem(self.subsystem)
82            .build();
83
84        let established_session = Connection::connect(&self.ctl)
85            .await?
86            .open_new_session(&session, &stdios)
87            .await?;
88
89        Ok((
90            RemoteChild::new(established_session),
91            child_stdin,
92            child_stdout,
93            child_stderr,
94        ))
95    }
96}