mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-28 12:33:35 +01:00
cli: add streams to rpc, generic 'spawn' command (#179732)
* cli: apply improvements from integrated wsl branch * cli: add streams to rpc, generic 'spawn' command For the "exec server" concept, fyi @aeschli. * update clippy and apply fixes * fix unused imports :(
This commit is contained in:
@@ -2,29 +2,47 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
use super::errors::{wrap, AnyError, CommandFailed, WrappedError};
|
||||
use std::{borrow::Cow, ffi::OsStr, process::Stdio};
|
||||
use super::errors::CodeError;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::OsStr,
|
||||
process::{Output, Stdio},
|
||||
};
|
||||
use tokio::process::Command;
|
||||
|
||||
pub async fn capture_command_and_check_status(
|
||||
command_str: impl AsRef<OsStr>,
|
||||
args: &[impl AsRef<OsStr>],
|
||||
) -> Result<std::process::Output, AnyError> {
|
||||
) -> Result<std::process::Output, CodeError> {
|
||||
let output = capture_command(&command_str, args).await?;
|
||||
|
||||
check_output_status(output, || {
|
||||
format!(
|
||||
"{} {}",
|
||||
command_str.as_ref().to_string_lossy(),
|
||||
args.iter()
|
||||
.map(|a| a.as_ref().to_string_lossy())
|
||||
.collect::<Vec<Cow<'_, str>>>()
|
||||
.join(" ")
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check_output_status(
|
||||
output: Output,
|
||||
cmd_str: impl FnOnce() -> String,
|
||||
) -> Result<std::process::Output, CodeError> {
|
||||
if !output.status.success() {
|
||||
return Err(CommandFailed {
|
||||
command: format!(
|
||||
"{} {}",
|
||||
command_str.as_ref().to_string_lossy(),
|
||||
args.iter()
|
||||
.map(|a| a.as_ref().to_string_lossy())
|
||||
.collect::<Vec<Cow<'_, str>>>()
|
||||
.join(" ")
|
||||
),
|
||||
output,
|
||||
}
|
||||
.into());
|
||||
return Err(CodeError::CommandFailed {
|
||||
command: cmd_str(),
|
||||
code: output.status.code().unwrap_or(-1),
|
||||
output: String::from_utf8_lossy(if output.stderr.is_empty() {
|
||||
&output.stdout
|
||||
} else {
|
||||
&output.stderr
|
||||
})
|
||||
.into(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
@@ -33,7 +51,7 @@ pub async fn capture_command_and_check_status(
|
||||
pub async fn capture_command<A, I, S>(
|
||||
command_str: A,
|
||||
args: I,
|
||||
) -> Result<std::process::Output, WrappedError>
|
||||
) -> Result<std::process::Output, CodeError>
|
||||
where
|
||||
A: AsRef<OsStr>,
|
||||
I: IntoIterator<Item = S>,
|
||||
@@ -45,27 +63,23 @@ where
|
||||
.stdout(Stdio::piped())
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
wrap(
|
||||
e,
|
||||
format!(
|
||||
"failed to execute command '{}'",
|
||||
command_str.as_ref().to_string_lossy()
|
||||
),
|
||||
)
|
||||
.map_err(|e| CodeError::CommandFailed {
|
||||
command: command_str.as_ref().to_string_lossy().to_string(),
|
||||
code: -1,
|
||||
output: e.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Kills and processes and all of its children.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub async fn kill_tree(process_id: u32) -> Result<(), WrappedError> {
|
||||
pub async fn kill_tree(process_id: u32) -> Result<(), CodeError> {
|
||||
capture_command("taskkill", &["/t", "/pid", &process_id.to_string()]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Kills and processes and all of its children.
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub async fn kill_tree(process_id: u32) -> Result<(), WrappedError> {
|
||||
pub async fn kill_tree(process_id: u32) -> Result<(), CodeError> {
|
||||
use futures::future::join_all;
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
|
||||
@@ -82,7 +96,11 @@ pub async fn kill_tree(process_id: u32) -> Result<(), WrappedError> {
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|e| wrap(e, "error enumerating process tree"))?;
|
||||
.map_err(|e| CodeError::CommandFailed {
|
||||
command: format!("pgrep -P {}", parent_id),
|
||||
code: -1,
|
||||
output: e.to_string(),
|
||||
})?;
|
||||
|
||||
let mut kill_futures = vec![tokio::spawn(
|
||||
async move { kill_single_pid(parent_id).await },
|
||||
|
||||
@@ -258,18 +258,6 @@ impl std::fmt::Display for RefreshTokenNotAvailableError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnsupportedPlatformError();
|
||||
|
||||
impl std::fmt::Display for UnsupportedPlatformError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"This operation is not supported on your current platform"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NoInstallInUserProvidedPath(pub String);
|
||||
|
||||
@@ -419,28 +407,6 @@ impl std::fmt::Display for OAuthError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CommandFailed {
|
||||
pub output: std::process::Output,
|
||||
pub command: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CommandFailed {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Failed to run command \"{}\" (code {}): {}",
|
||||
self.command,
|
||||
self.output.status,
|
||||
String::from_utf8_lossy(if self.output.stderr.is_empty() {
|
||||
&self.output.stdout
|
||||
} else {
|
||||
&self.output.stderr
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Makes an "AnyError" enum that contains any of the given errors, in the form
|
||||
// `enum AnyError { FooError(FooError) }` (when given `makeAnyError!(FooError)`).
|
||||
// Useful to easily deal with application error types without making tons of "From"
|
||||
@@ -500,6 +466,20 @@ pub enum CodeError {
|
||||
#[cfg(windows)]
|
||||
#[error("could not get windows app lock: {0:?}")]
|
||||
AppLockFailed(std::io::Error),
|
||||
#[error("failed to run command \"{command}\" (code {code}): {output}")]
|
||||
CommandFailed {
|
||||
command: String,
|
||||
code: i32,
|
||||
output: String,
|
||||
},
|
||||
|
||||
#[error("platform not currently supported: {0}")]
|
||||
UnsupportedPlatform(String),
|
||||
#[error("This machine not meet {name}'s prerequisites, expected either...: {bullets}")]
|
||||
PrerequisitesFailed { name: &'static str, bullets: String },
|
||||
|
||||
#[error("failed to spawn process: {0:?}")]
|
||||
ProcessSpawnFailed(std::io::Error)
|
||||
}
|
||||
|
||||
makeAnyError!(
|
||||
@@ -518,7 +498,6 @@ makeAnyError!(
|
||||
ExtensionInstallFailed,
|
||||
MismatchedLaunchModeError,
|
||||
NoAttachedServerError,
|
||||
UnsupportedPlatformError,
|
||||
RefreshTokenNotAvailableError,
|
||||
NoInstallInUserProvidedPath,
|
||||
UserCancelledInstallation,
|
||||
@@ -530,7 +509,6 @@ makeAnyError!(
|
||||
UpdatesNotConfigured,
|
||||
CorruptDownload,
|
||||
MissingHomeDirectory,
|
||||
CommandFailed,
|
||||
OAuthError,
|
||||
InvalidRpcDataError,
|
||||
CodeError
|
||||
|
||||
@@ -16,7 +16,7 @@ use hyper::{
|
||||
HeaderMap, StatusCode,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::{io, pin::Pin, str::FromStr, task::Poll};
|
||||
use std::{io, pin::Pin, str::FromStr, sync::Arc, task::Poll};
|
||||
use tokio::{
|
||||
fs,
|
||||
io::{AsyncRead, AsyncReadExt},
|
||||
@@ -116,6 +116,8 @@ pub trait SimpleHttp {
|
||||
) -> Result<SimpleResponse, AnyError>;
|
||||
}
|
||||
|
||||
pub type BoxedHttp = Arc<dyn SimpleHttp + Send + Sync + 'static>;
|
||||
|
||||
// Implementation of SimpleHttp that uses a reqwest client.
|
||||
#[derive(Clone)]
|
||||
pub struct ReqwestSimpleHttp {
|
||||
@@ -324,7 +326,6 @@ impl AsyncRead for DelegatedReader {
|
||||
|
||||
/// Simple http implementation that falls back to delegated http if
|
||||
/// making a direct reqwest fails.
|
||||
#[derive(Clone)]
|
||||
pub struct FallbackSimpleHttp {
|
||||
native: ReqwestSimpleHttp,
|
||||
delegated: DelegatedSimpleHttp,
|
||||
|
||||
@@ -7,13 +7,12 @@ use std::cmp::Ordering;
|
||||
use super::command::capture_command;
|
||||
use crate::constants::QUALITYLESS_SERVER_NAME;
|
||||
use crate::update_service::Platform;
|
||||
use crate::util::errors::SetupError;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::bytes::Regex as BinRegex;
|
||||
use regex::Regex;
|
||||
use tokio::fs;
|
||||
|
||||
use super::errors::AnyError;
|
||||
use super::errors::CodeError;
|
||||
|
||||
lazy_static! {
|
||||
static ref LDCONFIG_STDC_RE: Regex = Regex::new(r"libstdc\+\+.* => (.+)").unwrap();
|
||||
@@ -41,19 +40,18 @@ impl PreReqChecker {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub async fn verify(&self) -> Result<Platform, AnyError> {
|
||||
use crate::constants::QUALITYLESS_PRODUCT_NAME;
|
||||
pub async fn verify(&self) -> Result<Platform, CodeError> {
|
||||
Platform::env_default().ok_or_else(|| {
|
||||
SetupError(format!(
|
||||
"{} is not supported on this platform",
|
||||
QUALITYLESS_PRODUCT_NAME
|
||||
CodeError::UnsupportedPlatform(format!(
|
||||
"{} {}",
|
||||
std::env::consts::OS,
|
||||
std::env::consts::ARCH
|
||||
))
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub async fn verify(&self) -> Result<Platform, AnyError> {
|
||||
pub async fn verify(&self) -> Result<Platform, CodeError> {
|
||||
let (is_nixos, gnu_a, gnu_b, or_musl) = tokio::join!(
|
||||
check_is_nixos(),
|
||||
check_glibc_version(),
|
||||
@@ -96,10 +94,10 @@ impl PreReqChecker {
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
Err(AnyError::from(SetupError(format!(
|
||||
"This machine not meet {}'s prerequisites, expected either...\n{}",
|
||||
QUALITYLESS_SERVER_NAME, bullets,
|
||||
))))
|
||||
Err(CodeError::PrerequisitesFailed {
|
||||
bullets,
|
||||
name: QUALITYLESS_SERVER_NAME,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
use async_trait::async_trait;
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
use tokio::sync::{
|
||||
broadcast, mpsc,
|
||||
watch::{self, error::RecvError},
|
||||
use tokio::{
|
||||
sync::{
|
||||
broadcast, mpsc,
|
||||
watch::{self, error::RecvError},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
Reference in New Issue
Block a user