feat: macos, update dmg (#13539)
* feat: macos, update dmg Signed-off-by: fufesou <linlong1266@gmail.com> * Update src/platform/macos.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: macos update, remove temp update dir Signed-off-by: fufesou <linlong1266@gmail.com> * refact: macos update, print Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com> Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -115,6 +115,10 @@ pub fn global_init() -> bool {
|
|||||||
crate::server::wayland::init();
|
crate::server::wayland::init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
crate::platform::macos::try_remove_temp_update_dir(None);
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -300,14 +300,35 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
{
|
{
|
||||||
use crate::platform;
|
use crate::platform;
|
||||||
if args[0] == "--update" {
|
if args[0] == "--update" {
|
||||||
let _text = match platform::update_me() {
|
if args.len() > 1 && args[1].ends_with(".dmg") {
|
||||||
Ok(_) => {
|
// Version check is unnecessary unless downgrading to an older version
|
||||||
log::info!("{}", translate("Update successfully!".to_string()));
|
// that lacks "update dmg" support. This is a special case since we cannot
|
||||||
|
// detect the version before extracting the DMG, so we skip the check.
|
||||||
|
let dmg_path = &args[1];
|
||||||
|
println!("Updating from DMG: {}", dmg_path);
|
||||||
|
match platform::update_from_dmg(dmg_path) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Update process from DMG started successfully.");
|
||||||
|
// The new process will handle the rest. We can exit.
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Failed to start update from DMG: {}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
} else {
|
||||||
log::error!("Update failed with error: {err}");
|
println!("Starting update process...");
|
||||||
}
|
log::info!("Starting update process...");
|
||||||
};
|
let _text = match platform::update_me() {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("{}", translate("Update successfully!".to_string()));
|
||||||
|
log::info!("Update successfully!");
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Update failed with error: {}", err);
|
||||||
|
log::error!("Update failed with error: {err}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ static PRIVILEGES_SCRIPTS_DIR: Dir =
|
|||||||
include_dir!("$CARGO_MANIFEST_DIR/src/platform/privileges_scripts");
|
include_dir!("$CARGO_MANIFEST_DIR/src/platform/privileges_scripts");
|
||||||
static mut LATEST_SEED: i32 = 0;
|
static mut LATEST_SEED: i32 = 0;
|
||||||
|
|
||||||
|
// Using a fixed temporary directory for updates is preferable to
|
||||||
|
// using one that includes the custom client name.
|
||||||
const UPDATE_TEMP_DIR: &str = "/tmp/.rustdeskupdate";
|
const UPDATE_TEMP_DIR: &str = "/tmp/.rustdeskupdate";
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -714,6 +716,14 @@ pub fn quit_gui() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_remove_temp_update_dir(dir: Option<&str>) {
|
||||||
|
let target_path = Path::new(dir.unwrap_or(UPDATE_TEMP_DIR));
|
||||||
|
if target_path.exists() {
|
||||||
|
std::fs::remove_dir_all(target_path).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_me() -> ResultType<()> {
|
pub fn update_me() -> ResultType<()> {
|
||||||
let is_installed_daemon = is_installed_daemon(false);
|
let is_installed_daemon = is_installed_daemon(false);
|
||||||
let option_stop_service = "stop-service";
|
let option_stop_service = "stop-service";
|
||||||
@@ -733,6 +743,7 @@ pub fn update_me() -> ResultType<()> {
|
|||||||
bail!("Unknown app directory of current exe file: {:?}", cmd);
|
bail!("Unknown app directory of current exe file: {:?}", cmd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let app_name = crate::get_app_name();
|
||||||
if is_installed_daemon && !is_service_stopped {
|
if is_installed_daemon && !is_service_stopped {
|
||||||
let agent = format!("{}_server.plist", crate::get_full_name());
|
let agent = format!("{}_server.plist", crate::get_full_name());
|
||||||
let agent_plist_file = format!("/Library/LaunchAgents/{}", agent);
|
let agent_plist_file = format!("/Library/LaunchAgents/{}", agent);
|
||||||
@@ -749,12 +760,13 @@ pub fn update_me() -> ResultType<()> {
|
|||||||
let update_body = format!(
|
let update_body = format!(
|
||||||
r#"
|
r#"
|
||||||
do shell script "
|
do shell script "
|
||||||
pgrep -x 'RustDesk' | grep -v {} | xargs kill -9 && rm -rf /Applications/RustDesk.app && ditto '{}' /Applications/RustDesk.app && chown -R {}:staff /Applications/RustDesk.app && xattr -r -d com.apple.quarantine /Applications/RustDesk.app
|
pgrep -x '{app_name}' | grep -v {pid} | xargs kill -9 && rm -rf '/Applications/{app_name}.app' && ditto '{app_dir}' '/Applications/{app_name}.app' && chown -R {user}:staff '/Applications/{app_name}.app' && xattr -r -d com.apple.quarantine '/Applications/{app_name}.app'
|
||||||
" with prompt "RustDesk wants to update itself" with administrator privileges
|
" with prompt "{app_name} wants to update itself" with administrator privileges
|
||||||
"#,
|
"#,
|
||||||
std::process::id(),
|
app_name = app_name,
|
||||||
app_dir,
|
pid = std::process::id(),
|
||||||
get_active_username()
|
app_dir = app_dir,
|
||||||
|
user = get_active_username()
|
||||||
);
|
);
|
||||||
match Command::new("osascript")
|
match Command::new("osascript")
|
||||||
.arg("-e")
|
.arg("-e")
|
||||||
@@ -772,7 +784,7 @@ pgrep -x 'RustDesk' | grep -v {} | xargs kill -9 && rm -rf /Applications/RustDes
|
|||||||
}
|
}
|
||||||
std::process::Command::new("open")
|
std::process::Command::new("open")
|
||||||
.arg("-n")
|
.arg("-n")
|
||||||
.arg(&format!("/Applications/{}.app", crate::get_app_name()))
|
.arg(&format!("/Applications/{}.app", app_name))
|
||||||
.spawn()
|
.spawn()
|
||||||
.ok();
|
.ok();
|
||||||
// leave open a little time
|
// leave open a little time
|
||||||
@@ -780,6 +792,15 @@ pgrep -x 'RustDesk' | grep -v {} | xargs kill -9 && rm -rf /Applications/RustDes
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_from_dmg(dmg_path: &str) -> ResultType<()> {
|
||||||
|
println!("Starting update from DMG: {}", dmg_path);
|
||||||
|
extract_dmg(dmg_path, UPDATE_TEMP_DIR)?;
|
||||||
|
println!("DMG extracted");
|
||||||
|
update_extracted(UPDATE_TEMP_DIR)?;
|
||||||
|
println!("Update process started");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_to(_file: &str) -> ResultType<()> {
|
pub fn update_to(_file: &str) -> ResultType<()> {
|
||||||
update_extracted(UPDATE_TEMP_DIR)?;
|
update_extracted(UPDATE_TEMP_DIR)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -811,10 +832,14 @@ fn extract_dmg(dmg_path: &str, target_dir: &str) -> ResultType<()> {
|
|||||||
}
|
}
|
||||||
std::fs::create_dir_all(target_path)?;
|
std::fs::create_dir_all(target_path)?;
|
||||||
|
|
||||||
Command::new("hdiutil")
|
let status = Command::new("hdiutil")
|
||||||
.args(&["attach", "-nobrowse", "-mountpoint", mount_point, dmg_path])
|
.args(&["attach", "-nobrowse", "-mountpoint", mount_point, dmg_path])
|
||||||
.status()?;
|
.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
bail!("Failed to attach DMG image at {}: {:?}", dmg_path, status);
|
||||||
|
}
|
||||||
|
|
||||||
struct DmgGuard(&'static str);
|
struct DmgGuard(&'static str);
|
||||||
impl Drop for DmgGuard {
|
impl Drop for DmgGuard {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@@ -825,7 +850,7 @@ fn extract_dmg(dmg_path: &str, target_dir: &str) -> ResultType<()> {
|
|||||||
}
|
}
|
||||||
let _guard = DmgGuard(mount_point);
|
let _guard = DmgGuard(mount_point);
|
||||||
|
|
||||||
let app_name = "RustDesk.app";
|
let app_name = format!("{}.app", crate::get_app_name());
|
||||||
let src_path = format!("{}/{}", mount_point, app_name);
|
let src_path = format!("{}/{}", mount_point, app_name);
|
||||||
let dest_path = format!("{}/{}", target_dir, app_name);
|
let dest_path = format!("{}/{}", target_dir, app_name);
|
||||||
|
|
||||||
@@ -834,7 +859,12 @@ fn extract_dmg(dmg_path: &str, target_dir: &str) -> ResultType<()> {
|
|||||||
.status()?;
|
.status()?;
|
||||||
|
|
||||||
if !copy_status.success() {
|
if !copy_status.success() {
|
||||||
bail!("Failed to copy application {:?}", copy_status);
|
bail!(
|
||||||
|
"Failed to copy application from {} to {}: {:?}",
|
||||||
|
src_path,
|
||||||
|
dest_path,
|
||||||
|
copy_status
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !Path::new(&dest_path).exists() {
|
if !Path::new(&dest_path).exists() {
|
||||||
@@ -848,9 +878,13 @@ fn extract_dmg(dmg_path: &str, target_dir: &str) -> ResultType<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_extracted(target_dir: &str) -> ResultType<()> {
|
fn update_extracted(target_dir: &str) -> ResultType<()> {
|
||||||
let exe_path = format!("{}/RustDesk.app/Contents/MacOS/RustDesk", target_dir);
|
let app_name = crate::get_app_name();
|
||||||
|
let exe_path = format!(
|
||||||
|
"{}/{}.app/Contents/MacOS/{}",
|
||||||
|
target_dir, app_name, app_name
|
||||||
|
);
|
||||||
let _child = unsafe {
|
let _child = unsafe {
|
||||||
Command::new(&exe_path)
|
if let Err(e) = Command::new(&exe_path)
|
||||||
.arg("--update")
|
.arg("--update")
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.stdout(Stdio::null())
|
.stdout(Stdio::null())
|
||||||
@@ -859,7 +893,11 @@ fn update_extracted(target_dir: &str) -> ResultType<()> {
|
|||||||
hbb_common::libc::setsid();
|
hbb_common::libc::setsid();
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.spawn()?
|
.spawn()
|
||||||
|
{
|
||||||
|
try_remove_temp_update_dir(Some(target_dir));
|
||||||
|
bail!(e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user