mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-20 02:08:47 +00:00
@@ -6,7 +6,7 @@
|
||||
use std::{fmt, path::PathBuf};
|
||||
|
||||
use crate::{constants, log, options, tunnels::code_server::CodeServerArgs};
|
||||
use clap::{ValueEnum, Args, Parser, Subcommand};
|
||||
use clap::{Args, Parser, Subcommand, ValueEnum};
|
||||
use const_format::concatcp;
|
||||
|
||||
const CLI_NAME: &str = concatcp!(constants::PRODUCT_NAME_LONG, " CLI");
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
use async_trait::async_trait;
|
||||
use base64::{Engine as _, engine::general_purpose as b64};
|
||||
use base64::{engine::general_purpose as b64, Engine as _};
|
||||
use serde::Serialize;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{str::FromStr, time::Duration};
|
||||
use sysinfo::Pid;
|
||||
@@ -247,8 +248,14 @@ pub async fn kill(ctx: CommandContext) -> Result<i32, AnyError> {
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct StatusOutput {
|
||||
pub tunnel: Option<protocol::singleton::TunnelState>,
|
||||
pub service_installed: bool,
|
||||
}
|
||||
|
||||
pub async fn status(ctx: CommandContext) -> Result<i32, AnyError> {
|
||||
let status = do_single_rpc_call::<_, protocol::singleton::Status>(
|
||||
let tunnel_status = do_single_rpc_call::<_, protocol::singleton::Status>(
|
||||
&ctx.paths.tunnel_lockfile(),
|
||||
ctx.log.clone(),
|
||||
protocol::singleton::METHOD_STATUS,
|
||||
@@ -256,17 +263,24 @@ pub async fn status(ctx: CommandContext) -> Result<i32, AnyError> {
|
||||
)
|
||||
.await;
|
||||
|
||||
match status {
|
||||
Err(CodeError::NoRunningTunnel) => {
|
||||
ctx.log.result(CodeError::NoRunningTunnel.to_string());
|
||||
Ok(1)
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
Ok(s) => {
|
||||
ctx.log.result(serde_json::to_string(&s).unwrap());
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
let service_installed = create_service_manager(ctx.log.clone(), &ctx.paths)
|
||||
.is_installed()
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
|
||||
ctx.log.result(
|
||||
serde_json::to_string(&StatusOutput {
|
||||
service_installed,
|
||||
tunnel: match tunnel_status {
|
||||
Ok(s) => Some(s.tunnel),
|
||||
Err(CodeError::NoRunningTunnel) => None,
|
||||
Err(e) => return Err(e.into()),
|
||||
},
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Removes unused servers.
|
||||
|
||||
@@ -18,8 +18,8 @@ pub mod tunnels;
|
||||
pub mod update_service;
|
||||
pub mod util;
|
||||
|
||||
mod download_cache;
|
||||
mod async_pipe;
|
||||
mod download_cache;
|
||||
mod json_rpc;
|
||||
mod msgpack_rpc;
|
||||
mod rpc;
|
||||
|
||||
@@ -41,6 +41,9 @@ pub trait ServiceManager {
|
||||
/// Show logs from the running service to standard out.
|
||||
async fn show_logs(&self) -> Result<(), AnyError>;
|
||||
|
||||
/// Gets whether the tunnel service is installed.
|
||||
async fn is_installed(&self) -> Result<bool, AnyError>;
|
||||
|
||||
/// Unregisters the current executable as a service.
|
||||
async fn unregister(&self) -> Result<(), AnyError>;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ impl SystemdService {
|
||||
async fn connect() -> Result<Connection, AnyError> {
|
||||
let connection = Connection::session()
|
||||
.await
|
||||
.map_err(|e| wrap(e, "error creating dbus session"))?;
|
||||
.map_err(|e| wrap(e, "Error creating dbus session. This command uses systemd for managing services, you should check that systemd is installed and running as a user. If it's already installed, you may need to:\n\n- Install the `dbus-user-session` package and reboot\n- Start the user dbus session with `systemctl --user enable dbus --now`. \n\nThe error encountered was"))?;
|
||||
Ok(connection)
|
||||
}
|
||||
|
||||
@@ -113,6 +113,20 @@ impl ServiceManager for SystemdService {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn is_installed(&self) -> Result<bool, AnyError> {
|
||||
let connection = SystemdService::connect().await?;
|
||||
let proxy = SystemdService::proxy(&connection).await?;
|
||||
let state = proxy
|
||||
.get_unit_file_state(SystemdService::service_name_string())
|
||||
.await;
|
||||
|
||||
if let Ok(s) = state {
|
||||
Ok(s == "enabled")
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(
|
||||
self,
|
||||
launcher_paths: crate::state::LauncherPaths,
|
||||
@@ -219,6 +233,8 @@ trait SystemdManagerDbus {
|
||||
force: bool,
|
||||
) -> zbus::Result<(bool, Vec<(String, String, String)>)>;
|
||||
|
||||
fn get_unit_file_state(&self, file: String) -> zbus::Result<String>;
|
||||
|
||||
fn link_unit_files(
|
||||
&self,
|
||||
files: Vec<String>,
|
||||
|
||||
@@ -75,6 +75,11 @@ impl ServiceManager for LaunchdService {
|
||||
handle.run_service(self.log, launcher_paths).await
|
||||
}
|
||||
|
||||
async fn is_installed(&self) -> Result<bool, AnyError> {
|
||||
let cmd = capture_command_and_check_status("launchctl", &["list"]).await?;
|
||||
Ok(String::from_utf8_lossy(&cmd.stdout).contains(&get_service_label()))
|
||||
}
|
||||
|
||||
async fn unregister(&self) -> Result<(), crate::util::errors::AnyError> {
|
||||
let service_file = get_service_file_path()?;
|
||||
|
||||
|
||||
@@ -114,6 +114,11 @@ impl CliServiceManager for WindowsService {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn is_installed(&self) -> Result<bool, AnyError> {
|
||||
let key = WindowsService::open_key()?;
|
||||
Ok(key.get_raw_value(TUNNEL_ACTIVITY_NAME).is_ok())
|
||||
}
|
||||
|
||||
async fn unregister(&self) -> Result<(), AnyError> {
|
||||
let key = WindowsService::open_key()?;
|
||||
key.delete_value(TUNNEL_ACTIVITY_NAME)
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::util::errors::{wrap, WrappedError};
|
||||
|
||||
use flate2::read::GzDecoder;
|
||||
use std::fs;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::io::Seek;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tar::Archive;
|
||||
|
||||
@@ -65,7 +65,7 @@ where
|
||||
|
||||
// reset since skip logic read the tar already:
|
||||
tar_gz
|
||||
.seek(SeekFrom::Start(0))
|
||||
.rewind()
|
||||
.map_err(|e| wrap(e, "error resetting seek position"))?;
|
||||
|
||||
let tar = GzDecoder::new(tar_gz);
|
||||
|
||||
@@ -88,8 +88,7 @@ where
|
||||
use std::io::Read;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
|
||||
if matches!(file.unix_mode(), Some(mode) if mode & (S_IFLNK as u32) == (S_IFLNK as u32))
|
||||
{
|
||||
if matches!(file.unix_mode(), Some(mode) if mode & S_IFLNK == S_IFLNK) {
|
||||
let mut link_to = Vec::new();
|
||||
file.read_to_end(&mut link_to).map_err(|e| {
|
||||
wrap(
|
||||
|
||||
@@ -165,7 +165,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
}
|
||||
|
||||
async startTunnel(session: IRemoteTunnelSession): Promise<TunnelStatus> {
|
||||
if (isSameSession(session, this._session)) {
|
||||
if (isSameSession(session, this._session) && this._tunnelStatus.type !== 'disconnected') {
|
||||
return this._tunnelStatus;
|
||||
}
|
||||
this.setSession(session);
|
||||
@@ -195,7 +195,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
}
|
||||
};
|
||||
try {
|
||||
await this.runCodeTunneCommand('stop', ['kill'], onOutput);
|
||||
await this.runCodeTunnelCommand('stop', ['kill'], onOutput);
|
||||
} catch (e) {
|
||||
this._logger.error(e);
|
||||
}
|
||||
@@ -213,26 +213,35 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
}
|
||||
|
||||
let isAttached = false;
|
||||
let output = '';
|
||||
|
||||
const onOutput = (a: string, isErr: boolean) => {
|
||||
if (isErr) {
|
||||
this._logger.error(a);
|
||||
} else {
|
||||
this._logger.info(a);
|
||||
output += a;
|
||||
}
|
||||
if (!this.environmentService.isBuilt && a.startsWith(' Compiling')) {
|
||||
this.setTunnelStatus(TunnelStates.connecting(localize('remoteTunnelService.building', 'Building CLI from sources')));
|
||||
}
|
||||
};
|
||||
|
||||
const statusProcess = this.runCodeTunneCommand('status', ['status'], onOutput);
|
||||
const statusProcess = this.runCodeTunnelCommand('status', ['status'], onOutput);
|
||||
this._tunnelProcess = statusProcess;
|
||||
try {
|
||||
const status = await statusProcess;
|
||||
await statusProcess;
|
||||
if (this._tunnelProcess !== statusProcess) {
|
||||
return;
|
||||
}
|
||||
isAttached = status === 0;
|
||||
|
||||
// split and find the line, since in dev builds additional noise is
|
||||
// added by cargo to the output.
|
||||
const status: {
|
||||
service_installed: boolean;
|
||||
tunnel: object | null;
|
||||
} = JSON.parse(output.trim().split('\n').find(l => l.startsWith('{'))!);
|
||||
|
||||
isAttached = !!status.tunnel;
|
||||
this._logger.info(isAttached ? 'Other tunnel running, attaching...' : 'No other tunnel running');
|
||||
if (!isAttached && !this._session) {
|
||||
this._tunnelProcess = undefined;
|
||||
@@ -255,7 +264,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
a = a.replaceAll(token, '*'.repeat(4));
|
||||
onOutput(a, isErr);
|
||||
};
|
||||
const loginProcess = this.runCodeTunneCommand('login', ['user', 'login', '--provider', session.providerId, '--access-token', token, '--log', LogLevelToString(this._logger.getLevel())], onLoginOutput);
|
||||
const loginProcess = this.runCodeTunnelCommand('login', ['user', 'login', '--provider', session.providerId, '--access-token', token, '--log', LogLevelToString(this._logger.getLevel())], onLoginOutput);
|
||||
this._tunnelProcess = loginProcess;
|
||||
try {
|
||||
await loginProcess;
|
||||
@@ -286,7 +295,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
if (this._preventSleep()) {
|
||||
args.push('--no-sleep');
|
||||
}
|
||||
const serveCommand = this.runCodeTunneCommand('tunnel', args, (message: string, isErr: boolean) => {
|
||||
const serveCommand = this.runCodeTunnelCommand('tunnel', args, (message: string, isErr: boolean) => {
|
||||
if (isErr) {
|
||||
this._logger.error(message);
|
||||
} else {
|
||||
@@ -315,7 +324,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
});
|
||||
}
|
||||
|
||||
private runCodeTunneCommand(logLabel: string, commandArgs: string[], onOutput: (message: string, isError: boolean) => void = () => { }): CancelablePromise<number> {
|
||||
private runCodeTunnelCommand(logLabel: string, commandArgs: string[], onOutput: (message: string, isError: boolean) => void = () => { }): CancelablePromise<number> {
|
||||
return createCancelablePromise<number>(token => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (token.isCancellationRequested) {
|
||||
|
||||
Reference in New Issue
Block a user