working on windows service install/uninstall

This commit is contained in:
rustdesk
2023-06-05 20:27:48 +08:00
parent f1cc42769c
commit b07ac438f5
17 changed files with 280 additions and 688 deletions

View File

@@ -722,7 +722,7 @@ pub fn is_share_rdp() -> bool {
}
pub fn set_share_rdp(enable: bool) {
let (subkey, _, _, _, _) = get_install_info();
let (subkey, _, _, _) = get_install_info();
let cmd = format!(
"reg add {} /f /v share_rdp /t REG_SZ /d \"{}\"",
subkey,
@@ -815,11 +815,11 @@ fn get_valid_subkey() -> String {
return get_subkey(&app_name, false);
}
pub fn get_install_info() -> (String, String, String, String, String) {
pub fn get_install_info() -> (String, String, String, String) {
get_install_info_with_subkey(get_valid_subkey())
}
fn get_default_install_info() -> (String, String, String, String, String) {
fn get_default_install_info() -> (String, String, String, String) {
get_install_info_with_subkey(get_subkey(&crate::get_app_name(), false))
}
@@ -841,7 +841,6 @@ fn get_default_install_path() -> String {
}
pub fn check_update_broker_process() -> ResultType<()> {
// let (_, path, _, _) = get_install_info();
let process_exe = privacy_win_mag::INJECTED_PROCESS_EXE;
let origin_process_exe = privacy_win_mag::ORIGIN_PROCESS_EXE;
@@ -876,19 +875,17 @@ pub fn check_update_broker_process() -> ResultType<()> {
let cmds = format!(
"
chcp 65001
taskkill /F /IM {broker_exe}
taskkill /F /IM {process_exe}
copy /Y \"{origin_process_exe}\" \"{cur_exe}\"
",
broker_exe = process_exe,
origin_process_exe = origin_process_exe,
cur_exe = cur_exe.to_string_lossy().to_string(),
cur_exe = cur_exe.to_string_lossy(),
);
run_cmds(cmds, false, "update_broker")?;
Ok(())
}
fn get_install_info_with_subkey(subkey: String) -> (String, String, String, String, String) {
fn get_install_info_with_subkey(subkey: String) -> (String, String, String, String) {
let mut path = get_reg_of(&subkey, "InstallLocation");
if path.is_empty() {
path = get_default_install_path();
@@ -899,8 +896,7 @@ fn get_install_info_with_subkey(subkey: String) -> (String, String, String, Stri
crate::get_app_name()
);
let exe = format!("{}\\{}.exe", path, crate::get_app_name());
let dll = format!("{}\\sciter.dll", path);
(subkey, path, start_menu, exe, dll)
(subkey, path, start_menu, exe)
}
pub fn copy_raw_cmd(src_raw: &str, _raw: &str, _path: &str) -> String {
@@ -923,15 +919,14 @@ pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String {
{main_exe}
copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\"
",
main_exe = main_exe,
path = path,
ORIGIN_PROCESS_EXE = privacy_win_mag::ORIGIN_PROCESS_EXE,
broker_exe = privacy_win_mag::INJECTED_PROCESS_EXE,
)
}
/* // update_me has bad compatibility, so disable it.
pub fn update_me() -> ResultType<()> {
let (_, path, _, exe, _dll) = get_install_info();
let (_, path, _, exe) = get_install_info();
let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned();
let cmds = format!(
"
@@ -949,16 +944,14 @@ pub fn update_me() -> ResultType<()> {
lic = register_licence(),
cur_pid = get_current_pid(),
);
std::thread::sleep(std::time::Duration::from_millis(1000));
run_cmds(cmds, false, "update")?;
std::thread::sleep(std::time::Duration::from_millis(2000));
std::process::Command::new(&exe).arg("--tray").spawn().ok();
std::process::Command::new(&exe).spawn().ok();
run_after_run_cmds(false)?;
std::process::Command::new(&exe)
.args(&["--remove", &src_exe])
.spawn()?;
Ok(())
}
*/
fn get_after_install(exe: &str) -> String {
let app_name = crate::get_app_name();
@@ -986,24 +979,21 @@ fn get_after_install(exe: &str) -> String {
reg add HKEY_CLASSES_ROOT\\{ext}\\shell\\open /f
reg add HKEY_CLASSES_ROOT\\{ext}\\shell\\open\\command /f
reg add HKEY_CLASSES_ROOT\\{ext}\\shell\\open\\command /f /ve /t REG_SZ /d \"\\\"{exe}\\\" \\\"%%1\\\"\"
sc create {app_name} binpath= \"\\\"{exe}\\\" --service\" start= auto DisplayName= \"{app_name} Service\"
netsh advfirewall firewall add rule name=\"{app_name} Service\" dir=in action=allow program=\"{exe}\" enable=yes
sc start {app_name}
{create_service}
reg add HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System /f /v SoftwareSASGeneration /t REG_DWORD /d 1
", ext=ext, exe=exe, app_name=app_name)
", create_service=get_create_service(&exe))
}
pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> ResultType<()> {
let uninstall_str = get_uninstall(false);
let mut path = path.trim_end_matches('\\').to_owned();
let (subkey, _path, start_menu, exe, dll) = get_default_install_info();
let (subkey, _path, start_menu, exe) = get_default_install_info();
let mut exe = exe;
let mut _dll = dll;
if path.is_empty() {
path = _path;
} else {
exe = exe.replace(&_path, &path);
_dll = _dll.replace(&_path, &path);
}
let mut version_major = "0";
let mut version_minor = "0";
@@ -1018,6 +1008,7 @@ pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> Res
if versions.len() > 2 {
version_build = versions[2];
}
let app_name = crate::get_app_name();
let tmp_path = std::env::temp_dir().to_string_lossy().to_string();
let mk_shortcut = write_cmds(
@@ -1029,10 +1020,7 @@ sLinkFile = \"{tmp_path}\\{app_name}.lnk\"
Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = \"{exe}\"
oLink.Save
",
tmp_path = tmp_path,
app_name = crate::get_app_name(),
exe = exe,
"
),
"vbs",
"mk_shortcut",
@@ -1051,10 +1039,7 @@ Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.Arguments = \"--uninstall\"
oLink.IconLocation = \"msiexec.exe\"
oLink.Save
",
tmp_path = tmp_path,
app_name = crate::get_app_name(),
exe = exe,
"
),
"vbs",
"uninstall_shortcut",
@@ -1062,27 +1047,7 @@ oLink.Save
.to_str()
.unwrap_or("")
.to_owned();
let tray_shortcut = write_cmds(
format!(
"
Set oWS = WScript.CreateObject(\"WScript.Shell\")
sLinkFile = \"{tmp_path}\\{app_name} Tray.lnk\"
Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = \"{exe}\"
oLink.Arguments = \"--tray\"
oLink.Save
",
tmp_path = tmp_path,
app_name = crate::get_app_name(),
exe = exe,
),
"vbs",
"tray_shortcut",
)?
.to_str()
.unwrap_or("")
.to_owned();
let tray_shortcut = get_tray_shortcut(&exe, &tmp_path)?;
let mut shortcuts = Default::default();
if options.contains("desktopicon") {
shortcuts = format!(
@@ -1093,15 +1058,11 @@ oLink.Save
}
if options.contains("startmenu") {
shortcuts = format!(
"{}
"{shortcuts}
md \"{start_menu}\"
copy /Y \"{tmp_path}\\{app_name}.lnk\" \"{start_menu}\\\"
copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{start_menu}\\\"
",
shortcuts,
start_menu = start_menu,
tmp_path = tmp_path,
app_name = crate::get_app_name(),
"
);
}
@@ -1119,12 +1080,7 @@ if exist \"{tray_shortcut}\" del /f /q \"{tray_shortcut}\"
if exist \"{tmp_path}\\{app_name}.lnk\" del /f /q \"{tmp_path}\\{app_name}.lnk\"
if exist \"{tmp_path}\\Uninstall {app_name}.lnk\" del /f /q \"{tmp_path}\\Uninstall {app_name}.lnk\"
if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} Tray.lnk\"
",
mk_shortcut = mk_shortcut,
uninstall_shortcut = uninstall_shortcut,
tray_shortcut = tray_shortcut,
tmp_path = tmp_path,
app_name = crate::get_app_name(),
"
);
let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_string();
@@ -1145,11 +1101,12 @@ reg add {subkey} /f /v DisplayIcon /t REG_SZ /d \"{exe}\"
reg add {subkey} /f /v DisplayName /t REG_SZ /d \"{app_name}\"
reg add {subkey} /f /v DisplayVersion /t REG_SZ /d \"{version}\"
reg add {subkey} /f /v Version /t REG_SZ /d \"{version}\"
reg add {subkey} /f /v BuildDate /t REG_SZ /d \"{build_date}\"
reg add {subkey} /f /v InstallLocation /t REG_SZ /d \"{path}\"
reg add {subkey} /f /v Publisher /t REG_SZ /d \"{app_name}\"
reg add {subkey} /f /v VersionMajor /t REG_DWORD /d {major}
reg add {subkey} /f /v VersionMinor /t REG_DWORD /d {minor}
reg add {subkey} /f /v VersionBuild /t REG_DWORD /d {build}
reg add {subkey} /f /v VersionMajor /t REG_DWORD /d {version_major}
reg add {subkey} /f /v VersionMinor /t REG_DWORD /d {version_minor}
reg add {subkey} /f /v VersionBuild /t REG_DWORD /d {version_build}
reg add {subkey} /f /v UninstallString /t REG_SZ /d \"\\\"{exe}\\\" --uninstall\"
reg add {subkey} /f /v EstimatedSize /t REG_DWORD /d {size}
reg add {subkey} /f /v WindowsInstaller /t REG_DWORD /d 0
@@ -1161,32 +1118,14 @@ copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"%PROGRAMDATA%\\Microsoft\\Windows\
{shortcuts}
copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\"
{dels}
sc create {app_name} binpath= \"\\\"{exe}\\\" --import-config \\\"{config_path}\\\"\" start= auto DisplayName= \"{app_name} Service\"
sc start {app_name}
sc stop {app_name}
sc delete {app_name}
{import_config}
{install_cert}
{after_install}
{sleep}
",
uninstall_str=uninstall_str,
path=path,
exe=exe,
subkey=subkey,
app_name=crate::get_app_name(),
version=crate::VERSION,
major=version_major,
minor=version_minor,
build=version_build,
size=size,
mk_shortcut=mk_shortcut,
uninstall_shortcut=uninstall_shortcut,
tray_shortcut=tray_shortcut,
tmp_path=tmp_path,
shortcuts=shortcuts,
config_path=Config::file().to_str().unwrap_or(""),
build_date=crate::BUILD_DATE,
lic=register_licence(),
install_cert=install_cert,
after_install=get_after_install(&exe),
sleep=if debug {
"timeout 300"
@@ -1199,19 +1138,15 @@ sc delete {app_name}
&dels
},
copy_exe = copy_exe_cmd(&src_exe, &exe, &path),
import_config = get_import_config(&exe),
);
run_cmds(cmds, debug, "install")?;
std::thread::sleep(std::time::Duration::from_millis(2000));
if !silent {
std::process::Command::new(&exe).spawn()?;
std::process::Command::new(&exe).arg("--tray").spawn()?;
std::thread::sleep(std::time::Duration::from_millis(1000));
}
run_after_run_cmds(silent)?;
Ok(())
}
pub fn run_after_install() -> ResultType<()> {
let (_, _, _, exe, _) = get_install_info();
let (_, _, _, exe) = get_install_info();
run_cmds(get_after_install(&exe), true, "after_install")
}
@@ -1235,12 +1170,10 @@ fn get_before_uninstall(kill_self: bool) -> String {
taskkill /F /IM {broker_exe}
taskkill /F /IM {app_name}.exe{filter}
reg delete HKEY_CLASSES_ROOT\\.{ext} /f
reg delete HKEY_CLASSES_ROOT\\{ext} /f
netsh advfirewall firewall delete rule name=\"{app_name} Service\"
",
app_name = app_name,
broker_exe = WIN_MAG_INJECTED_PROCESS_EXE,
ext = ext,
filter = filter,
)
}
@@ -1251,8 +1184,7 @@ fn get_uninstall(kill_self: bool) -> String {
uninstall_cert_cmd = format!("\"{}\" --uninstall-cert", exe_path);
}
}
let (subkey, path, start_menu, _, _) = get_install_info();
let (subkey, path, start_menu, _) = get_install_info();
format!(
"
{before_uninstall}
@@ -1264,11 +1196,7 @@ fn get_uninstall(kill_self: bool) -> String {
if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
",
before_uninstall=get_before_uninstall(kill_self),
uninstall_cert_cmd = uninstall_cert_cmd,
subkey=subkey,
app_name = crate::get_app_name(),
path = path,
start_menu = start_menu,
)
}
@@ -1277,6 +1205,7 @@ pub fn uninstall_me(kill_self: bool) -> ResultType<()> {
}
fn write_cmds(cmds: String, ext: &str, tip: &str) -> ResultType<std::path::PathBuf> {
let mut cmds = cmds;
let mut tmp = std::env::temp_dir();
// When dir contains these characters, the bat file will not execute in elevated mode.
if vec!["&", "@", "^"]
@@ -1289,9 +1218,20 @@ fn write_cmds(cmds: String, ext: &str, tip: &str) -> ResultType<std::path::PathB
}
tmp.push(format!("{}_{}.{}", crate::get_app_name(), tip, ext));
let mut file = std::fs::File::create(&tmp)?;
if ext == "bat" {
let tmp2 = get_undone_file(&tmp);
std::fs::File::create(&tmp2).ok();
cmds = format!(
"
{cmds}
if exist \"{path}\" del /f /q \"{path}\"
",
path = tmp2.to_string_lossy()
);
}
// in case cmds mixed with \r\n and \n, make sure all ending with \r\n
// in some windows, \r\n required for cmd file to run
let cmds = cmds.replace("\r\n", "\n").replace("\n", "\r\n");
cmds = cmds.replace("\r\n", "\n").replace("\n", "\r\n");
if ext == "vbs" {
let mut v: Vec<u16> = cmds.encode_utf16().collect();
// utf8 -> utf16le which vbs support it only
@@ -1310,8 +1250,18 @@ fn to_le(v: &mut [u16]) -> &[u8] {
unsafe { v.align_to().1 }
}
fn get_undone_file(tmp: &PathBuf) -> PathBuf {
let mut tmp1 = tmp.clone();
tmp1.set_file_name(format!(
"{}.undone",
tmp.file_name().unwrap().to_string_lossy()
));
tmp1
}
fn run_cmds(cmds: String, show: bool, tip: &str) -> ResultType<()> {
let tmp = write_cmds(cmds, "bat", tip)?;
let tmp2 = get_undone_file(&tmp);
let tmp_fn = tmp.to_str().unwrap_or("");
let res = runas::Command::new("cmd")
.args(&["/C", &tmp_fn])
@@ -1322,6 +1272,10 @@ fn run_cmds(cmds: String, show: bool, tip: &str) -> ResultType<()> {
allow_err!(std::fs::remove_file(tmp));
}
let _ = res?;
if tmp2.exists() {
allow_err!(std::fs::remove_file(tmp2));
bail!("{} failed", tip);
}
Ok(())
}
@@ -1357,7 +1311,7 @@ pub fn is_installed() -> bool {
service::ServiceAccess,
service_manager::{ServiceManager, ServiceManagerAccess},
};
let (_, _, _, exe, _) = get_install_info();
let (_, _, _, exe) = get_install_info();
if !std::fs::metadata(exe).is_ok() {
return false;
}
@@ -1372,18 +1326,8 @@ pub fn is_installed() -> bool {
return false;
}
pub fn get_installed_version() -> String {
let (_, _, _, exe, _) = get_install_info();
if let Ok(output) = std::process::Command::new(exe).arg("--version").output() {
for line in String::from_utf8_lossy(&output.stdout).lines() {
return line.to_owned();
}
}
"".to_owned()
}
fn get_reg(name: &str) -> String {
let (subkey, _, _, _, _) = get_install_info();
pub fn get_reg(name: &str) -> String {
let (subkey, _, _, _) = get_install_info();
get_reg_of(&subkey, name)
}
@@ -1434,7 +1378,7 @@ pub fn bootstrap() {
}
fn register_licence() -> String {
let (subkey, _, _, _, _) = get_install_info();
let (subkey, _, _, _) = get_install_info();
if let Ok(lic) = get_license_from_exe_name() {
format!(
"
@@ -1442,7 +1386,6 @@ fn register_licence() -> String {
reg add {subkey} /f /v Host /t REG_SZ /d \"{host}\"
reg add {subkey} /f /v Api /t REG_SZ /d \"{api}\"
",
subkey = subkey,
key = &lic.key,
host = &lic.host,
api = &lic.api,
@@ -1469,9 +1412,7 @@ Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = \"{exe}\"
oLink.Arguments = \"--connect {id}\"
oLink.Save
",
exe = exe,
id = id,
"
),
"vbs",
"connect_shortcut",
@@ -2181,6 +2122,117 @@ pub fn is_process_consent_running() -> ResultType<bool> {
Ok(output.status.success() && !output.stdout.is_empty())
}
pub fn uninstall_service(show_new_window: bool) -> ResultType<()> {
let filter = format!(" /FI \"PID ne {}\"", get_current_pid());
Config::set_option("stop-service".into(), "Y".into());
let cmds = format!(
"
chcp 65001
sc stop {app_name}
sc delete {app_name}
if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
taskkill /F /IM {app_name}.exe{filter}
",
app_name = crate::get_app_name(),
);
if let Err(err) = run_cmds(cmds, false, "uninstall") {
Config::set_option("stop-service".into(), "".into());
bail!(err);
}
run_after_run_cmds(!show_new_window)?;
std::process::exit(0);
}
pub fn install_service() -> ResultType<()> {
let (_, _, _, exe) = get_install_info();
let tmp_path = std::env::temp_dir().to_string_lossy().to_string();
let tray_shortcut = get_tray_shortcut(&exe, &tmp_path)?;
let filter = format!(" /FI \"PID ne {}\"", get_current_pid());
Config::set_option("stop-service".into(), "".into());
let cmds = format!(
"
chcp 65001
cscript \"{tray_shortcut}\"
copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\"
{import_config}
{create_service}
if exist \"{tray_shortcut}\" del /f /q \"{tray_shortcut}\"
taskkill /F /IM {app_name}.exe{filter}
",
app_name = crate::get_app_name(),
import_config = get_import_config(&exe),
create_service = get_create_service(&exe),
);
if let Err(err) = run_cmds(cmds, false, "install") {
Config::set_option("stop-service".into(), "Y".into());
bail!(err);
}
run_after_run_cmds(false)?;
std::process::exit(0);
}
pub fn get_tray_shortcut(exe: &str, tmp_path: &str) -> ResultType<String> {
Ok(write_cmds(
format!(
"
Set oWS = WScript.CreateObject(\"WScript.Shell\")
sLinkFile = \"{tmp_path}\\{app_name} Tray.lnk\"
Set oLink = oWS.CreateShortcut(sLinkFile)
oLink.TargetPath = \"{exe}\"
oLink.Arguments = \"--tray\"
oLink.Save
",
app_name = crate::get_app_name(),
),
"vbs",
"tray_shortcut",
)?
.to_str()
.unwrap_or("")
.to_owned())
}
fn get_import_config(exe: &str) -> String {
format!("
sc create {app_name} binpath= \"\\\"{exe}\\\" --import-config \\\"{config_path}\\\"\" start= auto DisplayName= \"{app_name} Service\"
sc start {app_name}
sc stop {app_name}
sc delete {app_name}
",
app_name = crate::get_app_name(),
config_path=Config::file().to_str().unwrap_or(""),
)
}
fn get_create_service(exe: &str) -> String {
let stop = Config::get_option("stop-service") == "Y";
if stop {
format!("
if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
", app_name = crate::get_app_name())
} else {
format!("
sc create {app_name} binpath= \"\\\"{exe}\\\" --service\" start= auto DisplayName= \"{app_name} Service\"
sc start {app_name}
",
app_name = crate::get_app_name())
}
}
fn run_after_run_cmds(silent: bool) -> ResultType<()> {
let (_, _, _, exe) = get_install_info();
std::thread::sleep(std::time::Duration::from_millis(2000));
if !silent {
std::process::Command::new(&exe).spawn()?;
}
if Config::get_option("stop-service") != "Y" {
std::process::Command::new(&exe).arg("--tray").spawn()?;
}
std::thread::sleep(std::time::Duration::from_millis(1000));
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;