Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/cli/src/tunnels/service_windows.rs
3314 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
use async_trait::async_trait;
7
use shell_escape::windows::escape as shell_escape;
8
use std::os::windows::process::CommandExt;
9
use std::{path::PathBuf, process::Stdio};
10
use winapi::um::winbase::{CREATE_NEW_PROCESS_GROUP, DETACHED_PROCESS};
11
use winreg::{enums::HKEY_CURRENT_USER, RegKey};
12
13
use crate::util::command::new_std_command;
14
use crate::{
15
constants::TUNNEL_ACTIVITY_NAME,
16
log,
17
state::LauncherPaths,
18
tunnels::{protocol, singleton_client::do_single_rpc_call},
19
util::errors::{wrap, wrapdbg, AnyError},
20
};
21
22
use super::service::{tail_log_file, ServiceContainer, ServiceManager as CliServiceManager};
23
24
const DID_LAUNCH_AS_HIDDEN_PROCESS: &str = "VSCODE_CLI_DID_LAUNCH_AS_HIDDEN_PROCESS";
25
26
pub struct WindowsService {
27
log: log::Logger,
28
tunnel_lock: PathBuf,
29
log_file: PathBuf,
30
}
31
32
impl WindowsService {
33
pub fn new(log: log::Logger, paths: &LauncherPaths) -> Self {
34
Self {
35
log,
36
tunnel_lock: paths.tunnel_lockfile(),
37
log_file: paths.service_log_file(),
38
}
39
}
40
41
fn open_key() -> Result<RegKey, AnyError> {
42
RegKey::predef(HKEY_CURRENT_USER)
43
.create_subkey(r"Software\Microsoft\Windows\CurrentVersion\Run")
44
.map_err(|e| wrap(e, "error opening run registry key").into())
45
.map(|(key, _)| key)
46
}
47
}
48
49
#[async_trait]
50
impl CliServiceManager for WindowsService {
51
async fn register(&self, exe: std::path::PathBuf, args: &[&str]) -> Result<(), AnyError> {
52
let key = WindowsService::open_key()?;
53
54
let mut reg_str = String::new();
55
let mut cmd = new_std_command(&exe);
56
reg_str.push_str(shell_escape(exe.to_string_lossy()).as_ref());
57
58
let mut add_arg = |arg: &str| {
59
reg_str.push(' ');
60
reg_str.push_str(shell_escape((*arg).into()).as_ref());
61
cmd.arg(arg);
62
};
63
64
for arg in args {
65
add_arg(arg);
66
}
67
68
add_arg("--log-to-file");
69
add_arg(self.log_file.to_string_lossy().as_ref());
70
71
key.set_value(TUNNEL_ACTIVITY_NAME, &reg_str)
72
.map_err(|e| AnyError::from(wrapdbg(e, "error setting registry key")))?;
73
74
info!(self.log, "Successfully registered service...");
75
76
cmd.stderr(Stdio::null());
77
cmd.stdout(Stdio::null());
78
cmd.stdin(Stdio::null());
79
cmd.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS);
80
cmd.spawn()
81
.map_err(|e| wrapdbg(e, "error starting service"))?;
82
83
info!(self.log, "Tunnel service successfully started");
84
Ok(())
85
}
86
87
async fn show_logs(&self) -> Result<(), AnyError> {
88
tail_log_file(&self.log_file).await
89
}
90
91
async fn run(
92
self,
93
launcher_paths: LauncherPaths,
94
mut handle: impl 'static + ServiceContainer,
95
) -> Result<(), AnyError> {
96
if std::env::var(DID_LAUNCH_AS_HIDDEN_PROCESS).is_ok() {
97
return handle.run_service(self.log, launcher_paths).await;
98
}
99
100
// Start as a hidden subprocess to avoid showing cmd.exe on startup.
101
// Fixes https://github.com/microsoft/vscode/issues/184058
102
// I also tried the winapi ShowWindow, but that didn't yield fruit.
103
new_std_command(std::env::current_exe().unwrap())
104
.args(std::env::args().skip(1))
105
.env(DID_LAUNCH_AS_HIDDEN_PROCESS, "1")
106
.stderr(Stdio::null())
107
.stdout(Stdio::null())
108
.stdin(Stdio::null())
109
.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS)
110
.spawn()
111
.map_err(|e| wrap(e, "error starting nested process"))?;
112
113
Ok(())
114
}
115
116
async fn is_installed(&self) -> Result<bool, AnyError> {
117
let key = WindowsService::open_key()?;
118
Ok(key.get_raw_value(TUNNEL_ACTIVITY_NAME).is_ok())
119
}
120
121
async fn unregister(&self) -> Result<(), AnyError> {
122
let key = WindowsService::open_key()?;
123
match key.delete_value(TUNNEL_ACTIVITY_NAME) {
124
Ok(_) => {}
125
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
126
Err(e) => return Err(wrap(e, "error deleting registry key").into()),
127
}
128
129
info!(self.log, "Tunnel service uninstalled");
130
131
let r = do_single_rpc_call::<_, ()>(
132
&self.tunnel_lock,
133
self.log.clone(),
134
protocol::singleton::METHOD_SHUTDOWN,
135
protocol::EmptyObject {},
136
)
137
.await;
138
139
if r.is_err() {
140
warning!(self.log, "The tunnel service has been unregistered, but we couldn't find a running tunnel process. You may need to restart or log out and back in to fully stop the tunnel.");
141
} else {
142
info!(self.log, "Successfully shut down running tunnel.");
143
}
144
145
Ok(())
146
}
147
}
148
149