mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-11 05:31:29 +03:00
feat, update, win, macos (#11618)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -27,12 +27,18 @@ use include_dir::{include_dir, Dir};
|
||||
use objc::rc::autoreleasepool;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use scrap::{libc::c_void, quartz::ffi::*};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
os::unix::process::CommandExt,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
static PRIVILEGES_SCRIPTS_DIR: Dir =
|
||||
include_dir!("$CARGO_MANIFEST_DIR/src/platform/privileges_scripts");
|
||||
static mut LATEST_SEED: i32 = 0;
|
||||
|
||||
const UPDATE_TEMP_DIR: &str = "/tmp/.rustdeskupdate";
|
||||
|
||||
extern "C" {
|
||||
fn CGSCurrentCursorSeed() -> i32;
|
||||
fn CGEventCreate(r: *const c_void) -> *const c_void;
|
||||
@@ -155,6 +161,9 @@ pub fn install_service() -> bool {
|
||||
is_installed_daemon(false)
|
||||
}
|
||||
|
||||
// Remember to check if `update_daemon_agent()` need to be changed if changing `is_installed_daemon()`.
|
||||
// No need to merge the existing dup code, because the code in these two functions are too critical.
|
||||
// New code should be written in a common function.
|
||||
pub fn is_installed_daemon(prompt: bool) -> bool {
|
||||
let daemon = format!("{}_service.plist", crate::get_full_name());
|
||||
let agent = format!("{}_server.plist", crate::get_full_name());
|
||||
@@ -218,6 +227,70 @@ pub fn is_installed_daemon(prompt: bool) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn update_daemon_agent(agent_plist_file: String, update_source_dir: String, sync: bool) {
|
||||
let update_script_file = "update.scpt";
|
||||
let Some(update_script) = PRIVILEGES_SCRIPTS_DIR.get_file(update_script_file) else {
|
||||
return;
|
||||
};
|
||||
let Some(update_script_body) = update_script.contents_utf8().map(correct_app_name) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(daemon_plist) = PRIVILEGES_SCRIPTS_DIR.get_file("daemon.plist") else {
|
||||
return;
|
||||
};
|
||||
let Some(daemon_plist_body) = daemon_plist.contents_utf8().map(correct_app_name) else {
|
||||
return;
|
||||
};
|
||||
let Some(agent_plist) = PRIVILEGES_SCRIPTS_DIR.get_file("agent.plist") else {
|
||||
return;
|
||||
};
|
||||
let Some(agent_plist_body) = agent_plist.contents_utf8().map(correct_app_name) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let func = move || {
|
||||
let mut binding = std::process::Command::new("osascript");
|
||||
let mut cmd = binding
|
||||
.arg("-e")
|
||||
.arg(update_script_body)
|
||||
.arg(daemon_plist_body)
|
||||
.arg(agent_plist_body)
|
||||
.arg(&get_active_username())
|
||||
.arg(std::process::id().to_string())
|
||||
.arg(update_source_dir);
|
||||
match cmd.status() {
|
||||
Err(e) => {
|
||||
log::error!("run osascript failed: {}", e);
|
||||
}
|
||||
_ => {
|
||||
let installed = std::path::Path::new(&agent_plist_file).exists();
|
||||
log::info!("Agent file {} installed: {}", &agent_plist_file, installed);
|
||||
if installed {
|
||||
// Unload first, or load may not work if already loaded.
|
||||
// We hope that the load operation can immediately trigger a start.
|
||||
std::process::Command::new("launchctl")
|
||||
.args(&["unload", "-w", &agent_plist_file])
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
.ok();
|
||||
let status = std::process::Command::new("launchctl")
|
||||
.args(&["load", "-w", &agent_plist_file])
|
||||
.status();
|
||||
log::info!("launch server, status: {:?}", &status);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if sync {
|
||||
func();
|
||||
} else {
|
||||
std::thread::spawn(func);
|
||||
}
|
||||
}
|
||||
|
||||
fn correct_app_name(s: &str) -> String {
|
||||
let s = s.replace("rustdesk", &crate::get_app_name().to_lowercase());
|
||||
let s = s.replace("RustDesk", &crate::get_app_name());
|
||||
@@ -634,6 +707,140 @@ pub fn quit_gui() {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update_me() -> ResultType<()> {
|
||||
let is_installed_daemon = is_installed_daemon(false);
|
||||
let option_stop_service = "stop-service";
|
||||
let is_service_stopped = hbb_common::config::option2bool(
|
||||
option_stop_service,
|
||||
&crate::ui_interface::get_option(option_stop_service),
|
||||
);
|
||||
|
||||
let cmd = std::env::current_exe()?;
|
||||
// RustDesk.app/Contents/MacOS/RustDesk
|
||||
let app_dir = cmd
|
||||
.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.and_then(|p| p.parent())
|
||||
.map(|d| d.to_string_lossy().to_string());
|
||||
let Some(app_dir) = app_dir else {
|
||||
bail!("Unknown app directory of current exe file: {:?}", cmd);
|
||||
};
|
||||
|
||||
if is_installed_daemon && !is_service_stopped {
|
||||
let agent = format!("{}_server.plist", crate::get_full_name());
|
||||
let agent_plist_file = format!("/Library/LaunchAgents/{}", agent);
|
||||
std::process::Command::new("launchctl")
|
||||
.args(&["unload", "-w", &agent_plist_file])
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
.ok();
|
||||
update_daemon_agent(agent_plist_file, app_dir, true);
|
||||
} else {
|
||||
// `kill -9` may not work without "administrator privileges"
|
||||
let update_body = format!(
|
||||
r#"
|
||||
do shell script "
|
||||
pgrep -x 'RustDesk' | grep -v {} | xargs kill -9 && rm -rf /Applications/RustDesk.app && cp -R '{}' /Applications/ && chown -R {}:staff /Applications/RustDesk.app
|
||||
" with prompt "RustDesk wants to update itself" with administrator privileges
|
||||
"#,
|
||||
std::process::id(),
|
||||
app_dir,
|
||||
get_active_username()
|
||||
);
|
||||
match Command::new("osascript")
|
||||
.arg("-e")
|
||||
.arg(update_body)
|
||||
.status()
|
||||
{
|
||||
Ok(status) if !status.success() => {
|
||||
log::error!("osascript execution failed with status: {}", status);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("run osascript failed: {}", e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
std::process::Command::new("open")
|
||||
.arg("-n")
|
||||
.arg(&format!("/Applications/{}.app", crate::get_app_name()))
|
||||
.spawn()
|
||||
.ok();
|
||||
// leave open a little time
|
||||
std::thread::sleep(std::time::Duration::from_millis(300));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_to(file: &str) -> ResultType<()> {
|
||||
extract_dmg(file, UPDATE_TEMP_DIR)?;
|
||||
update_extracted(UPDATE_TEMP_DIR)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn extract_dmg(dmg_path: &str, target_dir: &str) -> ResultType<()> {
|
||||
let mount_point = "/Volumes/RustDeskUpdate";
|
||||
let target_path = Path::new(target_dir);
|
||||
|
||||
if target_path.exists() {
|
||||
std::fs::remove_dir_all(target_path)?;
|
||||
}
|
||||
std::fs::create_dir_all(target_path)?;
|
||||
|
||||
Command::new("hdiutil")
|
||||
.args(&["attach", "-nobrowse", "-mountpoint", mount_point, dmg_path])
|
||||
.status()?;
|
||||
|
||||
struct DmgGuard(&'static str);
|
||||
impl Drop for DmgGuard {
|
||||
fn drop(&mut self) {
|
||||
let _ = Command::new("hdiutil")
|
||||
.args(&["detach", self.0, "-force"])
|
||||
.status();
|
||||
}
|
||||
}
|
||||
let _guard = DmgGuard(mount_point);
|
||||
|
||||
let app_name = "RustDesk.app";
|
||||
let src_path = format!("{}/{}", mount_point, app_name);
|
||||
let dest_path = format!("{}/{}", target_dir, app_name);
|
||||
|
||||
let copy_status = Command::new("cp")
|
||||
.args(&["-R", &src_path, &dest_path])
|
||||
.status()?;
|
||||
|
||||
if !copy_status.success() {
|
||||
bail!("Failed to copy application {:?}", copy_status);
|
||||
}
|
||||
|
||||
if !Path::new(&dest_path).exists() {
|
||||
bail!(
|
||||
"Copy operation failed - destination not found at {}",
|
||||
dest_path
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_extracted(target_dir: &str) -> ResultType<()> {
|
||||
let exe_path = format!("{}/RustDesk.app/Contents/MacOS/RustDesk", target_dir);
|
||||
let _child = unsafe {
|
||||
Command::new(&exe_path)
|
||||
.arg("--update")
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.pre_exec(|| {
|
||||
hbb_common::libc::setsid();
|
||||
Ok(())
|
||||
})
|
||||
.spawn()?
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_double_click_time() -> u32 {
|
||||
// to-do: https://github.com/servo/core-foundation-rs/blob/786895643140fa0ee4f913d7b4aeb0c4626b2085/cocoa/src/appkit.rs#L2823
|
||||
500 as _
|
||||
|
||||
Reference in New Issue
Block a user