cli: use win32 app mutex to detect running tunnels and tunnel sevices (#179622)

* cli: fix distro mixin

* cli: use win32 app mutex to detect running tunnels and tunnel sevices

Fixes #179265

* cli: fix distro mixin more

* fix

* fix build
This commit is contained in:
Connor Peet
2023-04-10 12:25:14 -07:00
committed by GitHub
parent 116293e65e
commit b547b58db6
11 changed files with 104 additions and 8 deletions

View File

@@ -20,7 +20,7 @@ use super::{
use crate::{
async_pipe::socket_stream_split,
auth::Auth,
constants::APPLICATION_NAME,
constants::{APPLICATION_NAME, TUNNEL_NO_SERVICE_LOCK_NAME, TUNNEL_SERVICE_LOCK_NAME},
json_rpc::{new_json_rpc, start_json_rpc},
log,
singleton::connect_as_client,
@@ -37,6 +37,7 @@ use crate::{
Next, ServiceContainer, ServiceManager,
},
util::{
app_lock::AppMutex,
errors::{wrap, AnyError, CodeError},
prereqs::PreReqChecker,
},
@@ -148,6 +149,7 @@ pub async fn service(
manager.show_logs().await?;
}
TunnelServiceSubCommands::InternalRun => {
let _lock = AppMutex::new(TUNNEL_SERVICE_LOCK_NAME);
manager
.run(ctx.paths.clone(), TunnelServiceContainer::new(ctx.args))
.await?;
@@ -384,6 +386,7 @@ async fn serve_with_csa(
let mut server =
make_singleton_server(log_broadcast.clone(), log.clone(), server, shutdown.clone());
let platform = spanf!(log, log.span("prereq"), PreReqChecker::new().verify())?;
let _lock = AppMutex::new(TUNNEL_NO_SERVICE_LOCK_NAME).unwrap();
let auth = Auth::new(&paths, log.clone());
let mut dt = dev_tunnels::DevTunnels::new(&log, auth, &paths);

View File

@@ -34,6 +34,26 @@ pub const VSCODE_CLI_COMMIT: Option<&'static str> = option_env!("VSCODE_CLI_COMM
pub const VSCODE_CLI_UPDATE_ENDPOINT: Option<&'static str> =
option_env!("VSCODE_CLI_UPDATE_ENDPOINT");
/// Windows lock name for the running tunnel service. Used by the setup script
/// to detect a tunnel process. See #179265.
pub const TUNNEL_SERVICE_LOCK_NAME: &str = concatcp!(
"code_tunnel_service_",
match VSCODE_CLI_QUALITY {
Some(n) => n,
None => "oss",
}
);
/// Windows lock name for the running tunnel without a service. Used by the setup
/// script to detect a tunnel process. See #179265.
pub const TUNNEL_NO_SERVICE_LOCK_NAME: &str = concatcp!(
"code_tunnel_",
match VSCODE_CLI_QUALITY {
Some(n) => n,
None => "oss",
}
);
pub const TUNNEL_SERVICE_USER_AGENT_ENV_VAR: &str = "TUNNEL_SERVICE_USER_AGENT";
/// Application name as it appears on the CLI.

View File

@@ -15,6 +15,7 @@ pub mod prereqs;
pub mod ring_buffer;
pub mod sync;
pub use is_integrated::*;
pub mod app_lock;
pub mod file_lock;
#[cfg(target_os = "linux")]

58
cli/src/util/app_lock.rs Normal file
View File

@@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#[cfg(windows)]
use std::{io, ptr};
#[cfg(windows)]
use winapi::{
shared::winerror::ERROR_ALREADY_EXISTS,
um::{handleapi::CloseHandle, synchapi::CreateMutexA, winnt::HANDLE},
};
use super::errors::CodeError;
pub struct AppMutex {
#[cfg(windows)]
handle: HANDLE,
}
#[cfg(windows)] // handle is thread-safe, mark it so with this
unsafe impl Send for AppMutex {}
impl AppMutex {
#[cfg(unix)]
pub fn new(_name: &str) -> Result<Self, CodeError> {
Ok(Self {})
}
#[cfg(windows)]
pub fn new(name: &str) -> Result<Self, CodeError> {
let handle = unsafe { CreateMutexA(ptr::null_mut(), 0, name.as_ptr() as _) };
if !handle.is_null() {
return Ok(Self { handle });
}
let err = io::Error::last_os_error();
let raw = err.raw_os_error();
// docs report it should return ERROR_IO_PENDING, but in my testing it actually
// returns ERROR_LOCK_VIOLATION. Or maybe winapi is wrong?
if raw == Some(ERROR_ALREADY_EXISTS as i32) {
return Err(CodeError::AppAlreadyLocked(name.to_string()));
}
Err(CodeError::AppLockFailed(err))
}
}
impl Drop for AppMutex {
fn drop(&mut self) {
#[cfg(windows)]
unsafe {
CloseHandle(self.handle)
};
}
}

View File

@@ -494,6 +494,12 @@ pub enum CodeError {
NoRunningTunnel,
#[error("rpc call failed: {0:?}")]
TunnelRpcCallFailed(ResponseError),
#[cfg(windows)]
#[error("the windows app lock {0} already exists")]
AppAlreadyLocked(String),
#[cfg(windows)]
#[error("could not get windows app lock: {0:?}")]
AppLockFailed(std::io::Error),
}
makeAnyError!(