mirror of https://github.com/chipsenkbeil/distant
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
5.4 KiB
Rust
155 lines
5.4 KiB
Rust
use std::future::Future;
|
|
use std::pin::Pin;
|
|
|
|
use distant_core::protocol::{ProcessId, PtySize};
|
|
use tokio::io;
|
|
use tokio::sync::mpsc;
|
|
|
|
mod pty;
|
|
pub use pty::*;
|
|
|
|
mod simple;
|
|
pub use simple::*;
|
|
|
|
mod wait;
|
|
pub use wait::{ExitStatus, WaitRx, WaitTx};
|
|
|
|
/// Alias to the return type of an async function (for use with traits)
|
|
pub type FutureReturn<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
|
|
|
/// Represents a process on the remote server
|
|
pub trait Process: ProcessKiller + ProcessPty {
|
|
/// Represents the id of the process
|
|
fn id(&self) -> ProcessId;
|
|
|
|
/// Waits for the process to exit, returning the exit status
|
|
///
|
|
/// If the process has already exited, the status is returned immediately.
|
|
fn wait(&mut self) -> FutureReturn<'_, io::Result<ExitStatus>>;
|
|
|
|
/// Returns a reference to stdin channel if the process still has it associated
|
|
fn stdin(&self) -> Option<&dyn InputChannel>;
|
|
|
|
/// Returns a mutable reference to the stdin channel if the process still has it associated
|
|
fn mut_stdin(&mut self) -> Option<&mut (dyn InputChannel + 'static)>;
|
|
|
|
/// Takes the stdin channel from the process if it is still associated
|
|
fn take_stdin(&mut self) -> Option<Box<dyn InputChannel>>;
|
|
|
|
/// Returns a reference to stdout channel if the process still has it associated
|
|
fn stdout(&self) -> Option<&dyn OutputChannel>;
|
|
|
|
/// Returns a mutable reference to the stdout channel if the process still has it associated
|
|
fn mut_stdout(&mut self) -> Option<&mut (dyn OutputChannel + 'static)>;
|
|
|
|
/// Takes the stdout channel from the process if it is still associated
|
|
fn take_stdout(&mut self) -> Option<Box<dyn OutputChannel>>;
|
|
|
|
/// Returns a reference to stderr channel if the process still has it associated
|
|
fn stderr(&self) -> Option<&dyn OutputChannel>;
|
|
|
|
/// Returns a mutable reference to the stderr channel if the process still has it associated
|
|
fn mut_stderr(&mut self) -> Option<&mut (dyn OutputChannel + 'static)>;
|
|
|
|
/// Takes the stderr channel from the process if it is still associated
|
|
fn take_stderr(&mut self) -> Option<Box<dyn OutputChannel>>;
|
|
}
|
|
|
|
/// Represents interface that can be used to work with a pty associated with a process
|
|
pub trait ProcessPty: Send + Sync {
|
|
/// Returns the current size of the process' pty if it has one
|
|
fn pty_size(&self) -> Option<PtySize>;
|
|
|
|
/// Resize the pty associated with the process; returns an error if fails or if the
|
|
/// process does not leverage a pty
|
|
fn resize_pty(&self, size: PtySize) -> io::Result<()>;
|
|
|
|
/// Clone a process pty to support reading and updating pty independently
|
|
fn clone_pty(&self) -> Box<dyn ProcessPty>;
|
|
}
|
|
|
|
/// Trait that can be implemented to mark a process as not having a pty
|
|
pub trait NoProcessPty: Send + Sync {}
|
|
|
|
/// Internal type so we can create a dummy instance that implements trait
|
|
struct NoProcessPtyImpl {}
|
|
impl NoProcessPty for NoProcessPtyImpl {}
|
|
|
|
impl<T: NoProcessPty> ProcessPty for T {
|
|
fn pty_size(&self) -> Option<PtySize> {
|
|
None
|
|
}
|
|
|
|
fn resize_pty(&self, _size: PtySize) -> io::Result<()> {
|
|
Err(io::Error::new(
|
|
io::ErrorKind::Other,
|
|
"Process does not use pty",
|
|
))
|
|
}
|
|
|
|
fn clone_pty(&self) -> Box<dyn ProcessPty> {
|
|
Box::new(NoProcessPtyImpl {})
|
|
}
|
|
}
|
|
|
|
/// Represents interface that can be used to kill a remote process
|
|
pub trait ProcessKiller: Send + Sync {
|
|
/// Kill the process
|
|
///
|
|
/// If the process is dead or has already been killed, this will return
|
|
/// an error.
|
|
fn kill(&mut self) -> FutureReturn<'_, io::Result<()>>;
|
|
|
|
/// Clone a process killer to support sending signals independently
|
|
fn clone_killer(&self) -> Box<dyn ProcessKiller>;
|
|
}
|
|
|
|
impl ProcessKiller for mpsc::Sender<()> {
|
|
fn kill(&mut self) -> FutureReturn<'_, io::Result<()>> {
|
|
async fn inner(this: &mut mpsc::Sender<()>) -> io::Result<()> {
|
|
this.send(())
|
|
.await
|
|
.map_err(|x| io::Error::new(io::ErrorKind::BrokenPipe, x))
|
|
}
|
|
Box::pin(inner(self))
|
|
}
|
|
|
|
fn clone_killer(&self) -> Box<dyn ProcessKiller> {
|
|
Box::new(self.clone())
|
|
}
|
|
}
|
|
|
|
/// Represents an input channel of a process such as stdin
|
|
pub trait InputChannel: Send + Sync {
|
|
/// Sends input through channel, returning unit if succeeds or an error if fails
|
|
fn send<'a>(&'a mut self, data: &[u8]) -> FutureReturn<'a, io::Result<()>>;
|
|
}
|
|
|
|
impl InputChannel for mpsc::Sender<Vec<u8>> {
|
|
fn send<'a>(&'a mut self, data: &[u8]) -> FutureReturn<'a, io::Result<()>> {
|
|
let data = data.to_vec();
|
|
Box::pin(async move {
|
|
match mpsc::Sender::send(self, data).await {
|
|
Ok(_) => Ok(()),
|
|
Err(_) => Err(io::Error::new(
|
|
io::ErrorKind::BrokenPipe,
|
|
"Input channel closed",
|
|
)),
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Represents an output channel of a process such as stdout or stderr
|
|
pub trait OutputChannel: Send + Sync {
|
|
/// Waits for next output from channel, returning Some(data) if there is output, None if
|
|
/// the channel has been closed, or bubbles up an error if encountered
|
|
fn recv(&mut self) -> FutureReturn<'_, io::Result<Option<Vec<u8>>>>;
|
|
}
|
|
|
|
impl OutputChannel for mpsc::Receiver<Vec<u8>> {
|
|
fn recv(&mut self) -> FutureReturn<'_, io::Result<Option<Vec<u8>>>> {
|
|
Box::pin(async move { Ok(mpsc::Receiver::recv(self).await) })
|
|
}
|
|
}
|