Couple of changes:

1. Add support to detach processes from distant server
2. Refactor distant_bin and distant_args to distant.bin and distant.args
3. Add use_login_shell option for launch distant opts
pull/96/head
Chip Senkbeil 3 years ago
parent bd526c9c82
commit 043ae6ca4b
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

10
Cargo.lock generated

@ -944,8 +944,9 @@ checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "libssh2-sys"
version = "0.2.21"
source = "git+https://github.com/wez/ssh2-rs.git?branch=win32ssl#c65067040c97a0cf7f96c69d6fc87764a32c34ae"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca"
dependencies = [
"cc",
"libc",
@ -1677,8 +1678,9 @@ dependencies = [
[[package]]
name = "ssh2"
version = "0.9.1"
source = "git+https://github.com/wez/ssh2-rs.git?branch=win32ssl#c65067040c97a0cf7f96c69d6fc87764a32c34ae"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "269343e64430067a14937ae0e3c4ec604c178fb896dde0964b1acd22b3e2eeb1"
dependencies = [
"bitflags",
"libc",

@ -19,9 +19,6 @@ opt-level = 'z'
lto = true
codegen-units = 1
[patch.crates-io]
ssh2 = { git = "https://github.com/wez/ssh2-rs.git", branch="win32ssl" }
[features]
default = ["ssh2"]
ssh2 = ["distant-ssh2"]

@ -31,8 +31,9 @@ impl RemoteLspProcess {
channel: SessionChannel,
cmd: impl Into<String>,
args: Vec<String>,
detached: bool,
) -> Result<Self, RemoteProcessError> {
let mut inner = RemoteProcess::spawn(tenant, channel, cmd, args).await?;
let mut inner = RemoteProcess::spawn(tenant, channel, cmd, args, detached).await?;
let stdin = inner.stdin.take().map(RemoteLspStdin::new);
let stdout = inner.stdout.take().map(RemoteLspStdout::new);
let stderr = inner.stderr.take().map(RemoteLspStderr::new);
@ -324,6 +325,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});

@ -67,6 +67,7 @@ impl RemoteProcess {
mut channel: SessionChannel,
cmd: impl Into<String>,
args: Vec<String>,
detached: bool,
) -> Result<Self, RemoteProcessError> {
let tenant = tenant.into();
let cmd = cmd.into();
@ -75,7 +76,11 @@ impl RemoteProcess {
let mut mailbox = channel
.mail(Request::new(
tenant.as_str(),
vec![RequestData::ProcRun { cmd, args }],
vec![RequestData::ProcRun {
cmd,
args,
detached,
}],
))
.await?;
@ -365,6 +370,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});
@ -403,6 +409,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});
@ -448,6 +455,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});
@ -493,6 +501,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});
@ -549,6 +558,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});
@ -604,6 +614,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});
@ -653,6 +664,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});
@ -702,6 +714,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});
@ -744,6 +757,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});
@ -793,6 +807,7 @@ mod tests {
session.clone_channel(),
String::from("cmd"),
vec![String::from("arg")],
false,
)
.await
});

@ -121,6 +121,7 @@ pub trait SessionChannelExt {
tenant: impl Into<String>,
cmd: impl Into<String>,
args: Vec<impl Into<String>>,
detached: bool,
) -> AsyncReturn<'_, RemoteProcess, RemoteProcessError>;
/// Spawns an LSP process on the remote machine
@ -129,6 +130,7 @@ pub trait SessionChannelExt {
tenant: impl Into<String>,
cmd: impl Into<String>,
args: Vec<impl Into<String>>,
detached: bool,
) -> AsyncReturn<'_, RemoteLspProcess, RemoteProcessError>;
/// Retrieves information about the remote system
@ -367,11 +369,14 @@ impl SessionChannelExt for SessionChannel {
tenant: impl Into<String>,
cmd: impl Into<String>,
args: Vec<impl Into<String>>,
detached: bool,
) -> AsyncReturn<'_, RemoteProcess, RemoteProcessError> {
let tenant = tenant.into();
let cmd = cmd.into();
let args = args.into_iter().map(Into::into).collect();
Box::pin(async move { RemoteProcess::spawn(tenant, self.clone(), cmd, args).await })
Box::pin(
async move { RemoteProcess::spawn(tenant, self.clone(), cmd, args, detached).await },
)
}
fn spawn_lsp(
@ -379,11 +384,14 @@ impl SessionChannelExt for SessionChannel {
tenant: impl Into<String>,
cmd: impl Into<String>,
args: Vec<impl Into<String>>,
detached: bool,
) -> AsyncReturn<'_, RemoteLspProcess, RemoteProcessError> {
let tenant = tenant.into();
let cmd = cmd.into();
let args = args.into_iter().map(Into::into).collect();
Box::pin(async move { RemoteLspProcess::spawn(tenant, self.clone(), cmd, args).await })
Box::pin(
async move { RemoteLspProcess::spawn(tenant, self.clone(), cmd, args, detached).await },
)
}
fn system_info(&mut self, tenant: impl Into<String>) -> AsyncReturn<'_, SystemInfo> {

@ -222,6 +222,11 @@ pub enum RequestData {
/// Arguments for the command
args: Vec<String>,
/// Whether or not the process should be detached, meaning that the process will not be
/// killed when the associated client disconnects
#[cfg_attr(feature = "structopt", structopt(long))]
detached: bool,
},
/// Kills a process running on the remote machine
@ -465,6 +470,9 @@ pub struct RunningProcess {
/// Arguments for the command
pub args: Vec<String>,
/// Whether or not the process was run in detached mode
pub detached: bool,
/// Arbitrary id associated with running process
///
/// Not the same as the process' pid!

@ -98,7 +98,11 @@ pub(super) async fn process(
canonicalize,
resolve_file_type,
} => metadata(path, canonicalize, resolve_file_type).await,
RequestData::ProcRun { cmd, args } => proc_run(conn_id, state, reply, cmd, args).await,
RequestData::ProcRun {
cmd,
args,
detached,
} => proc_run(conn_id, state, reply, cmd, args, detached).await,
RequestData::ProcKill { id } => proc_kill(state, id).await,
RequestData::ProcStdin { id, data } => proc_stdin(state, id, data).await,
RequestData::ProcList {} => proc_list(state).await,
@ -424,6 +428,7 @@ async fn proc_run<F>(
reply: F,
cmd: String,
args: Vec<String>,
detached: bool,
) -> Result<Outgoing, ServerError>
where
F: FnMut(Vec<ResponseData>) -> ReplyRet + Clone + Send + 'static,
@ -439,7 +444,7 @@ where
state
.lock()
.await
.push_process(conn_id, Process::new(id, cmd, args));
.push_process(conn_id, Process::new(id, cmd, args, detached));
let post_hook = Box::new(move |mut state_lock: MutexGuard<'_, State>| {
// Spawn a task that sends stdout as a response
@ -656,6 +661,7 @@ async fn proc_list(state: HState) -> Result<Outgoing, ServerError> {
.map(|p| RunningProcess {
cmd: p.cmd.to_string(),
args: p.args.clone(),
detached: p.detached,
id: p.id,
})
.collect(),
@ -2096,6 +2102,7 @@ mod tests {
vec![RequestData::ProcRun {
cmd: DOES_NOT_EXIST_BIN.to_str().unwrap().to_string(),
args: Vec::new(),
detached: false,
}],
);
@ -2121,6 +2128,7 @@ mod tests {
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![ECHO_ARGS_TO_STDOUT_SH.to_str().unwrap().to_string()],
detached: false,
}],
);
@ -2153,6 +2161,7 @@ mod tests {
ECHO_ARGS_TO_STDOUT_SH.to_str().unwrap().to_string(),
String::from("some stdout"),
],
detached: false,
}],
);
@ -2218,6 +2227,7 @@ mod tests {
ECHO_ARGS_TO_STDERR_SH.to_str().unwrap().to_string(),
String::from("some stderr"),
],
detached: false,
}],
);
@ -2280,6 +2290,7 @@ mod tests {
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("0.1")],
detached: false,
}],
);
@ -2322,6 +2333,7 @@ mod tests {
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("1")],
detached: false,
}],
);
@ -2396,6 +2408,7 @@ mod tests {
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("1")],
detached: false,
}],
);
@ -2491,6 +2504,7 @@ mod tests {
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![ECHO_STDIN_TO_STDOUT_SH.to_str().unwrap().to_string()],
detached: false,
}],
);
@ -2563,6 +2577,7 @@ mod tests {
RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("1")],
detached: false,
},
RequestData::ProcList {},
],
@ -2586,6 +2601,7 @@ mod tests {
entries: vec![RunningProcess {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("1")],
detached: false,
id,
}],
},

@ -68,14 +68,22 @@ impl State {
if let Some(ids) = self.client_processes.remove(&conn_id) {
for id in ids {
if let Some(process) = self.processes.remove(&id) {
trace!(
"<Conn @ {:?}> Requesting proc {} be killed",
conn_id,
process.id
);
let pid = process.id;
if !process.kill() {
error!("Conn {} failed to send process {} kill signal", id, pid);
if !process.detached {
trace!(
"<Conn @ {:?}> Requesting proc {} be killed",
conn_id,
process.id
);
let pid = process.id;
if !process.kill() {
error!("Conn {} failed to send process {} kill signal", id, pid);
}
} else {
trace!(
"<Conn @ {:?}> Proc {} is detached and will not be killed",
conn_id,
process.id
);
}
}
}
@ -94,6 +102,9 @@ pub struct Process {
/// Arguments associated with the process
pub args: Vec<String>,
/// Whether or not this process was run detached
pub detached: bool,
/// Transport channel to send new input to the stdin of the process,
/// one line at a time
stdin_tx: Option<mpsc::Sender<String>>,
@ -106,11 +117,12 @@ pub struct Process {
}
impl Process {
pub fn new(id: usize, cmd: String, args: Vec<String>) -> Self {
pub fn new(id: usize, cmd: String, args: Vec<String>, detached: bool) -> Self {
Self {
id,
cmd,
args,
detached,
stdin_tx: None,
kill_tx: None,
wait_task: None,

@ -34,9 +34,7 @@ pub fn make_session_tbl(lua: &Lua) -> LuaResult<LuaTable> {
"launch",
lua.create_function(|lua, opts: LuaValue| {
let opts = LaunchOpts::from_lua(opts, lua)?;
let x = runtime::block_on(Session::launch(opts))?;
trace!("launch: {:?}", x);
Ok(x)
runtime::block_on(Session::launch(opts))
})?,
)?;
@ -159,9 +157,8 @@ impl Session {
mode,
handler,
ssh,
distant,
timeout,
distant_bin,
distant_args,
} = opts;
// First, establish a connection to an SSH server
@ -177,8 +174,9 @@ impl Session {
let session = match mode {
Mode::Distant => ssh_session
.into_distant_session(IntoDistantSessionOpts {
binary: distant_bin,
args: distant_args,
binary: distant.bin,
args: distant.args,
use_login_shell: distant.use_login_shell,
timeout,
})
.await

@ -164,16 +164,23 @@ make_api!(
make_api!(
spawn,
RemoteProcess,
{ cmd: String, #[serde(default)] args: Vec<String> },
|channel, tenant, params| { channel.spawn(tenant, params.cmd, params.args).await }
{ cmd: String, #[serde(default)] args: Vec<String>, #[serde(default)] detached: bool },
|channel, tenant, params| {
channel.spawn(tenant, params.cmd, params.args, params.detached).await
}
);
make_api!(
spawn_wait,
Output,
{ cmd: String, #[serde(default)] args: Vec<String> },
{ cmd: String, #[serde(default)] args: Vec<String>, #[serde(default)] detached: bool },
|channel, tenant, params| {
let proc = channel.spawn(tenant, params.cmd, params.args).await.to_lua_err()?;
let proc = channel.spawn(
tenant,
params.cmd,
params.args,
params.detached,
).await.to_lua_err()?;
let id = LuaRemoteProcess::from_distant_async(proc).await?.id;
LuaRemoteProcess::output_async(id).await
}
@ -182,8 +189,10 @@ make_api!(
make_api!(
spawn_lsp,
RemoteLspProcess,
{ cmd: String, #[serde(default)] args: Vec<String> },
|channel, tenant, params| { channel.spawn_lsp(tenant, params.cmd, params.args).await }
{ cmd: String, #[serde(default)] args: Vec<String>, #[serde(default)] detached: bool },
|channel, tenant, params| {
channel.spawn_lsp(tenant, params.cmd, params.args, params.detached).await
}
);
make_api!(system_info, SystemInfo, |channel, tenant, _params| {

@ -92,14 +92,11 @@ pub struct LaunchOpts<'a> {
/// Miscellaneous ssh configuration options
pub ssh: Ssh2SessionOpts,
/// Options specific to launching the distant binary on the remote machine
pub distant: LaunchDistantOpts,
/// Maximum time to wait for launch to complete
pub timeout: Duration,
/// Binary representing the distant server on the remote machine
pub distant_bin: String,
/// Additional CLI options to pass to the distant server when starting
pub distant_args: String,
}
impl fmt::Debug for LaunchOpts<'_> {
@ -109,6 +106,7 @@ impl fmt::Debug for LaunchOpts<'_> {
.field("mode", &self.mode)
.field("handler", &"...")
.field("ssh", &self.ssh)
.field("distant", &self.distant)
.field("timeout", &self.timeout)
.finish()
}
@ -182,25 +180,17 @@ impl<'lua> FromLua<'lua> for LaunchOpts<'lua> {
None => Default::default(),
}
},
distant: {
let distant_tbl: Option<LuaValue> = tbl.get("distant")?;
match distant_tbl {
Some(value) => LaunchDistantOpts::from_lua(value, lua)?,
None => Default::default(),
}
},
timeout: {
let milliseconds: Option<u64> = tbl.get("timeout")?;
Duration::from_millis(milliseconds.unwrap_or(TIMEOUT_MILLIS))
},
distant_bin: {
let distant_bin: Option<String> = tbl.get("distant_bin")?;
distant_bin.unwrap_or_else(|| String::from("distant"))
},
distant_args: {
let value: LuaValue = tbl.get("distant_args")?;
match value {
LuaValue::Nil => String::new(),
LuaValue::String(args) => args.to_str()?.to_string(),
x => {
let args: Vec<String> = lua.from_value(x)?;
args.join(" ")
}
}
},
}),
LuaValue::Nil => Err(LuaError::FromLuaConversionError {
from: "Nil",
@ -256,6 +246,120 @@ impl<'lua> FromLua<'lua> for LaunchOpts<'lua> {
}
}
#[derive(Debug)]
pub struct LaunchDistantOpts {
/// Binary representing the distant server on the remote machine
pub bin: String,
/// Additional CLI options to pass to the distant server when starting
pub args: String,
/// If true, will run distant via `echo <CMD> | $SHELL -l`, which will spawn a login shell to
/// execute distant
pub use_login_shell: bool,
}
impl Default for LaunchDistantOpts {
/// Create default options
///
/// * bin = "distant"
/// * args = ""
/// * use_login_shell = false
fn default() -> Self {
Self {
bin: String::from("distant"),
args: String::new(),
use_login_shell: false,
}
}
}
impl<'lua> FromLua<'lua> for LaunchDistantOpts {
fn from_lua(lua_value: LuaValue<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
let LaunchDistantOpts {
bin: default_bin,
args: default_args,
use_login_shell: default_use_login_shell,
} = Default::default();
match lua_value {
LuaValue::Table(tbl) => Ok(Self {
bin: {
let bin: Option<String> = tbl.get("bin")?;
bin.unwrap_or(default_bin)
},
// Allows "--some --args" or {"--some", "--args"}
args: {
let value: LuaValue = tbl.get("args")?;
match value {
LuaValue::Nil => default_args,
LuaValue::String(args) => args.to_str()?.to_string(),
x => {
let args: Vec<String> = lua.from_value(x)?;
args.join(" ")
}
}
},
use_login_shell: tbl
.get::<_, Option<bool>>("use_login_shell")?
.unwrap_or(default_use_login_shell),
}),
LuaValue::Nil => Err(LuaError::FromLuaConversionError {
from: "Nil",
to: "LaunchDistantOpts",
message: None,
}),
LuaValue::Boolean(_) => Err(LuaError::FromLuaConversionError {
from: "Boolean",
to: "LaunchDistantOpts",
message: None,
}),
LuaValue::LightUserData(_) => Err(LuaError::FromLuaConversionError {
from: "LightUserData",
to: "LaunchDistantOpts",
message: None,
}),
LuaValue::Integer(_) => Err(LuaError::FromLuaConversionError {
from: "Integer",
to: "LaunchDistantOpts",
message: None,
}),
LuaValue::Number(_) => Err(LuaError::FromLuaConversionError {
from: "Number",
to: "LaunchDistantOpts",
message: None,
}),
LuaValue::String(_) => Err(LuaError::FromLuaConversionError {
from: "String",
to: "LaunchDistantOpts",
message: None,
}),
LuaValue::Function(_) => Err(LuaError::FromLuaConversionError {
from: "Function",
to: "LaunchDistantOpts",
message: None,
}),
LuaValue::Thread(_) => Err(LuaError::FromLuaConversionError {
from: "Thread",
to: "LaunchDistantOpts",
message: None,
}),
LuaValue::UserData(_) => Err(LuaError::FromLuaConversionError {
from: "UserData",
to: "LaunchDistantOpts",
message: None,
}),
LuaValue::Error(_) => Err(LuaError::FromLuaConversionError {
from: "Error",
to: "LaunchDistantOpts",
message: None,
}),
}
}
}
#[derive(Copy, Clone, Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Mode {

@ -35,6 +35,7 @@ struct Process {
id: usize,
cmd: String,
args: Vec<String>,
detached: bool,
stdin_tx: mpsc::Sender<String>,
kill_tx: mpsc::Sender<()>,
}
@ -96,7 +97,11 @@ pub(super) async fn process(
canonicalize,
resolve_file_type,
} => metadata(session, path, canonicalize, resolve_file_type).await,
RequestData::ProcRun { cmd, args } => proc_run(session, state, reply, cmd, args).await,
RequestData::ProcRun {
cmd,
args,
detached,
} => proc_run(session, state, reply, cmd, args, detached).await,
RequestData::ProcKill { id } => proc_kill(session, state, id).await,
RequestData::ProcStdin { id, data } => proc_stdin(session, state, id, data).await,
RequestData::ProcList {} => proc_list(session, state).await,
@ -598,6 +603,7 @@ async fn proc_run<F>(
reply: F,
cmd: String,
args: Vec<String>,
detached: bool,
) -> io::Result<Outgoing>
where
F: FnMut(Vec<ResponseData>) -> ReplyRet + Clone + Send + 'static,
@ -644,6 +650,7 @@ where
id,
cmd,
args,
detached,
stdin_tx,
kill_tx,
},
@ -865,6 +872,7 @@ async fn proc_list(_session: WezSession, state: Arc<Mutex<State>>) -> io::Result
.map(|p| RunningProcess {
cmd: p.cmd.to_string(),
args: p.args.clone(),
detached: p.detached,
id: p.id,
})
.collect(),

@ -91,6 +91,10 @@ pub struct IntoDistantSessionOpts {
/// Arguments to supply to the distant server when starting it
pub args: String,
/// If true, launches via `echo distant listen ... | $SHELL -l`, otherwise attempts to launch
/// by directly invoking distant
pub use_login_shell: bool,
/// Timeout to use when connecting to the distant server
pub timeout: Duration,
}
@ -100,6 +104,7 @@ impl Default for IntoDistantSessionOpts {
Self {
binary: String::from("distant"),
args: String::new(),
use_login_shell: false,
timeout: Duration::from_secs(15),
}
}
@ -332,12 +337,33 @@ impl Ssh2Session {
.map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?,
);
// Spawn distant server
// If we are using a login shell, we need to make the binary be sh
// so we can appropriately pipe into the login shell
let (bin, args) = if opts.use_login_shell {
(
String::from("sh"),
vec![
String::from("-c"),
shell_words::quote(&format!(
"echo {} {} | $SHELL -l",
opts.binary,
args.join(" ")
))
.to_string(),
],
)
} else {
(opts.binary, args)
};
// Spawn distant server and detach it so that we don't kill it when the
// ssh session is closed
let mut proc = session
.spawn("<ssh-launch>", opts.binary, args)
.spawn("<ssh-launch>", bin, args, true)
.await
.map_err(|x| io::Error::new(io::ErrorKind::Other, x))?;
let mut stdout = proc.stdout.take().unwrap();
let mut stderr = proc.stderr.take().unwrap();
let (success, code) = proc
.wait()
.await
@ -370,12 +396,18 @@ impl Ssh2Session {
)),
}
} else {
let mut err = String::new();
while let Ok(data) = stderr.read().await {
err.push_str(&data);
}
Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Spawning distant failed: {}",
"Spawning distant failed [{}]: {}",
code.map(|x| x.to_string())
.unwrap_or_else(|| String::from("???"))
.unwrap_or_else(|| String::from("???")),
err
),
))
}

@ -1355,6 +1355,7 @@ async fn proc_run_should_send_error_over_stderr_on_failure(#[future] session: Se
vec![RequestData::ProcRun {
cmd: DOES_NOT_EXIST_BIN.to_str().unwrap().to_string(),
args: Vec::new(),
detached: false,
}],
);
@ -1400,6 +1401,7 @@ async fn proc_run_should_send_back_proc_start_on_success(#[future] session: Sess
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![ECHO_ARGS_TO_STDOUT_SH.to_str().unwrap().to_string()],
detached: false,
}],
);
@ -1428,6 +1430,7 @@ async fn proc_run_should_send_back_stdout_periodically_when_available(#[future]
ECHO_ARGS_TO_STDOUT_SH.to_str().unwrap().to_string(),
String::from("'some stdout'"),
],
detached: false,
}],
);
@ -1491,6 +1494,7 @@ async fn proc_run_should_send_back_stderr_periodically_when_available(#[future]
ECHO_ARGS_TO_STDERR_SH.to_str().unwrap().to_string(),
String::from("'some stderr'"),
],
detached: false,
}],
);
@ -1551,6 +1555,7 @@ async fn proc_run_should_clear_process_from_state_when_done(#[future] session: S
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("0.1")],
detached: false,
}],
);
let mut mailbox = session.mail(req).await.unwrap();
@ -1598,6 +1603,7 @@ async fn proc_run_should_clear_process_from_state_when_killed(#[future] session:
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("1")],
detached: false,
}],
);
@ -1671,6 +1677,7 @@ async fn proc_kill_should_send_ok_and_done_responses_on_success(#[future] sessio
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("1")],
detached: false,
}],
);
@ -1745,6 +1752,7 @@ async fn proc_stdin_should_send_ok_on_success_and_properly_send_stdin_to_process
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![ECHO_STDIN_TO_STDOUT_SH.to_str().unwrap().to_string()],
detached: false,
}],
);
let mut mailbox = session.mail(req).await.unwrap();
@ -1793,6 +1801,7 @@ async fn proc_list_should_send_proc_entry_list(#[future] session: Session) {
vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("10")],
detached: false,
}],
);
@ -1817,6 +1826,7 @@ async fn proc_list_should_send_proc_entry_list(#[future] session: Session) {
entries: vec![RunningProcess {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![SLEEP_SH.to_str().unwrap().to_string(), String::from("10")],
detached: false,
id,
}],
},

@ -659,6 +659,11 @@ pub struct LspSubcommand {
#[structopt(flatten)]
pub ssh_connection: SshConnectionOpts,
/// If provided, will run in detached mode, meaning that the process will not be killed if the
/// client disconnects from the server
#[structopt(long)]
pub detached: bool,
/// Command to run on the remote machine that represents an LSP server
pub cmd: String,

@ -86,10 +86,22 @@ async fn start(
match (cmd.interactive, cmd.operation) {
// ProcRun request w/ shell format is specially handled and we ignore interactive as
// the stdin will be used for sending ProcStdin to remote process
(_, Some(RequestData::ProcRun { cmd, args })) if is_shell_format => {
let mut proc =
RemoteProcess::spawn(utils::new_tenant(), session.clone_channel(), cmd, args)
.await?;
(
_,
Some(RequestData::ProcRun {
cmd,
args,
detached,
}),
) if is_shell_format => {
let mut proc = RemoteProcess::spawn(
utils::new_tenant(),
session.clone_channel(),
cmd,
args,
detached,
)
.await?;
// If we also parsed an LSP's initialize request for its session, we want to forward
// it along in the case of a process call

@ -73,6 +73,7 @@ async fn start(
session.clone_channel(),
cmd.cmd,
cmd.args,
cmd.detached,
)
.await?;

@ -171,6 +171,7 @@ fn should_support_json_to_execute_program_and_return_exit_status(mut action_cmd:
payload: vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![ECHO_ARGS_TO_STDOUT_SH.to_str().unwrap().to_string()],
detached: false,
}],
};
@ -203,6 +204,7 @@ fn should_support_json_to_capture_and_print_stdout(ctx: &'_ DistantServerCtx) {
ECHO_ARGS_TO_STDOUT_SH.to_str().unwrap().to_string(),
output.to_string(),
],
detached: false,
}],
};
@ -271,6 +273,7 @@ fn should_support_json_to_capture_and_print_stderr(ctx: &'_ DistantServerCtx) {
ECHO_ARGS_TO_STDERR_SH.to_str().unwrap().to_string(),
output.to_string(),
],
detached: false,
}],
};
@ -335,6 +338,7 @@ fn should_support_json_to_forward_stdin_to_remote_process(ctx: &'_ DistantServer
payload: vec![RequestData::ProcRun {
cmd: SCRIPT_RUNNER.to_string(),
args: vec![ECHO_STDIN_TO_STDOUT_SH.to_str().unwrap().to_string()],
detached: false,
}],
};
@ -428,6 +432,7 @@ fn should_support_json_output_for_error(mut action_cmd: Command) {
payload: vec![RequestData::ProcRun {
cmd: DOES_NOT_EXIST_BIN.to_str().unwrap().to_string(),
args: Vec::new(),
detached: false,
}],
};

Loading…
Cancel
Save