From c596e0e986833df330392927d839352574e1dc21 Mon Sep 17 00:00:00 2001 From: Caelan Sayler Date: Sun, 2 Feb 2025 19:44:38 +0000 Subject: [PATCH] Add ability to wait for a specific process (instead of the current one) --- samples/CPlusPlusWidgets/main.cpp | 2 +- samples/CPlusPlusWin32/CppWin32Sample.cpp | 2 +- src/lib-cpp/include/Velopack.h | 26 ++++-- src/lib-cpp/include/Velopack.hpp | 31 +++++-- src/lib-cpp/src/lib.rs | 34 ++++++- src/lib-csharp/UpdateExe.cs | 29 +++--- src/lib-csharp/UpdateManager.Helpers.cs | 4 +- src/lib-csharp/VelopackApp.cs | 2 +- src/lib-csharp/VelopackRuntimeInfo.cs | 7 +- src/lib-rust/src/manager.rs | 106 +++++++++++++++------- 10 files changed, 177 insertions(+), 66 deletions(-) diff --git a/samples/CPlusPlusWidgets/main.cpp b/samples/CPlusPlusWidgets/main.cpp index babee61e..efdc0867 100644 --- a/samples/CPlusPlusWidgets/main.cpp +++ b/samples/CPlusPlusWidgets/main.cpp @@ -179,7 +179,7 @@ private: try { - updateManager->WaitExitThenApplyUpdate(updateInfo.value()); + updateManager->WaitExitThenApplyUpdates(updateInfo.value()); wxTheApp->ExitMainLoop(); } catch (...) { /* exception will print in log */ } diff --git a/samples/CPlusPlusWin32/CppWin32Sample.cpp b/samples/CPlusPlusWin32/CppWin32Sample.cpp index 0fecdf74..7066927d 100644 --- a/samples/CPlusPlusWin32/CppWin32Sample.cpp +++ b/samples/CPlusPlusWin32/CppWin32Sample.cpp @@ -188,7 +188,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) MessageBoxCentered(hWnd, L"Download an update first.", szTitle, MB_OK); } else { - manager->WaitExitThenApplyUpdate(updInfo.value()); + manager->WaitExitThenApplyUpdates(updInfo.value()); exit(0); } } diff --git a/src/lib-cpp/include/Velopack.h b/src/lib-cpp/include/Velopack.h index a7c3faac..94910a05 100644 --- a/src/lib-cpp/include/Velopack.h +++ b/src/lib-cpp/include/Velopack.h @@ -294,12 +294,26 @@ bool vpkc_download_updates(vpkc_update_manager_t *p_manager, * You should then clean up any state and exit your app. The updater will apply updates and then * optionally restart your app. The updater will only wait for 60 seconds before giving up. */ -bool vpkc_wait_exit_then_apply_update(vpkc_update_manager_t *p_manager, - struct vpkc_asset_t *p_asset, - bool b_silent, - bool b_restart, - char **p_restart_args, - size_t c_restart_args); +bool vpkc_wait_exit_then_apply_updates(vpkc_update_manager_t *p_manager, + struct vpkc_asset_t *p_asset, + bool b_silent, + bool b_restart, + char **p_restart_args, + size_t c_restart_args); + +/** + * This will launch the Velopack updater and optionally wait for a program to exit gracefully. + * This method is unsafe because it does not necessarily wait for any / the correct process to exit + * before applying updates. The `vpkc_wait_exit_then_apply_updates` method is recommended for most use cases. + * If dw_wait_pid is 0, the updater will not wait for any process to exit before applying updates (Not Recommended). + */ +bool vpkc_unsafe_apply_updates(vpkc_update_manager_t *p_manager, + struct vpkc_asset_t *p_asset, + bool b_silent, + uint32_t dw_wait_pid, + bool b_restart, + char **p_restart_args, + size_t c_restart_args); /** * Frees a vpkc_update_manager_t instance. diff --git a/src/lib-cpp/include/Velopack.hpp b/src/lib-cpp/include/Velopack.hpp index 55d24fd8..7aa5ff8b 100644 --- a/src/lib-cpp/include/Velopack.hpp +++ b/src/lib-cpp/include/Velopack.hpp @@ -583,10 +583,19 @@ public: * You should then clean up any state and exit your app. The updater will apply updates and then * optionally restart your app. The updater will only wait for 60 seconds before giving up. */ - void WaitExitThenApplyUpdate(const VelopackAsset& asset, bool silent = false, bool restart = true, std::vector restartArgs = {}) { + void WaitExitThenApplyUpdates(const UpdateInfo& asset, bool silent = false, bool restart = true, std::vector restartArgs = {}) { + this->WaitExitThenApplyUpdates(asset.TargetFullRelease, silent, restart, restartArgs); + }; + + /** + * This will launch the Velopack updater and tell it to wait for this program to exit gracefully. + * You should then clean up any state and exit your app. The updater will apply updates and then + * optionally restart your app. The updater will only wait for 60 seconds before giving up. + */ + void WaitExitThenApplyUpdates(const VelopackAsset& asset, bool silent = false, bool restart = true, std::vector restartArgs = {}) { char** pRestartArgs = allocate_cstring_array(restartArgs); vpkc_asset_t vpkc_asset = to_c(asset); - bool result = vpkc_wait_exit_then_apply_update(m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size()); + bool result = vpkc_wait_exit_then_apply_updates(m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size()); free_cstring_array(pRestartArgs, restartArgs.size()); if (!result) { @@ -595,12 +604,20 @@ public: }; /** - * This will launch the Velopack updater and tell it to wait for this program to exit gracefully. - * You should then clean up any state and exit your app. The updater will apply updates and then - * optionally restart your app. The updater will only wait for 60 seconds before giving up. + * This will launch the Velopack updater and optionally wait for a program to exit gracefully. + * This method is unsafe because it does not necessarily wait for any / the correct process to exit + * before applying updates. The `WaitExitThenApplyUpdates` method is recommended for most use cases. + * If waitPid is 0, the updater will not wait for any process to exit before applying updates (Not Recommended). */ - void WaitExitThenApplyUpdate(const UpdateInfo& asset, bool silent = false, bool restart = true, std::vector restartArgs = {}) { - this->WaitExitThenApplyUpdate(asset.TargetFullRelease, silent, restart, restartArgs); + void UnsafeApplyUpdates(const VelopackAsset& asset, bool silent, uint32_t waitPid, bool restart, std::vector restartArgs) { + char** pRestartArgs = allocate_cstring_array(restartArgs); + vpkc_asset_t vpkc_asset = to_c(asset); + bool result = vpkc_unsafe_apply_updates(m_pManager, &vpkc_asset, silent, waitPid, restart, pRestartArgs, restartArgs.size()); + free_cstring_array(pRestartArgs, restartArgs.size()); + + if (!result) { + throw_last_error(); + } }; }; diff --git a/src/lib-cpp/src/lib.rs b/src/lib-cpp/src/lib.rs index b011d020..be3c756c 100644 --- a/src/lib-cpp/src/lib.rs +++ b/src/lib-cpp/src/lib.rs @@ -15,7 +15,7 @@ use anyhow::{anyhow, bail}; use libc::{c_char, c_void, size_t}; use log_derive::{logfn, logfn_inputs}; use std::{ffi::CString, ptr}; -use velopack::{sources, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp}; +use velopack::{sources, ApplyWaitMode, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp}; /// Create a new FileSource update source for a given file path. #[no_mangle] @@ -295,7 +295,7 @@ pub extern "C" fn vpkc_download_updates( #[no_mangle] #[logfn(Trace)] #[logfn_inputs(Trace)] -pub extern "C" fn vpkc_wait_exit_then_apply_update( +pub extern "C" fn vpkc_wait_exit_then_apply_updates( p_manager: *mut vpkc_update_manager_t, p_asset: *mut vpkc_asset_t, b_silent: bool, @@ -316,6 +316,36 @@ pub extern "C" fn vpkc_wait_exit_then_apply_update( }) } +/// This will launch the Velopack updater and optionally wait for a program to exit gracefully. +/// This method is unsafe because it does not necessarily wait for any / the correct process to exit +/// before applying updates. The `vpkc_wait_exit_then_apply_updates` method is recommended for most use cases. +/// If dw_wait_pid is 0, the updater will not wait for any process to exit before applying updates (Not Recommended). +#[no_mangle] +#[logfn(Trace)] +#[logfn_inputs(Trace)] +pub extern "C" fn vpkc_unsafe_apply_updates( + p_manager: *mut vpkc_update_manager_t, + p_asset: *mut vpkc_asset_t, + b_silent: bool, + dw_wait_pid: u32, + b_restart: bool, + p_restart_args: *mut *mut c_char, + c_restart_args: size_t, +) -> bool { + wrap_error(|| { + let manager = match p_manager.to_opaque_ref() { + Some(manager) => manager, + None => bail!("pManager must not be null"), + }; + + let asset = c_to_velopackasset_opt(p_asset).ok_or(anyhow!("pAsset must not be null"))?; + let restart_args = c_to_string_array_opt(p_restart_args, c_restart_args).unwrap_or_default(); + let wait_mode = if dw_wait_pid > 0 { ApplyWaitMode::WaitPid(dw_wait_pid) } else { ApplyWaitMode::NoWait }; + manager.unsafe_apply_updates(&asset, b_silent, wait_mode, b_restart, &restart_args)?; + Ok(()) + }) +} + /// Frees a vpkc_update_manager_t instance. #[no_mangle] #[logfn(Trace)] diff --git a/src/lib-csharp/UpdateExe.cs b/src/lib-csharp/UpdateExe.cs index b690d0fc..10891470 100644 --- a/src/lib-csharp/UpdateExe.cs +++ b/src/lib-csharp/UpdateExe.cs @@ -58,11 +58,11 @@ namespace Velopack /// Runs Update.exe in the current working directory with the 'start' command which will simply start the application. /// Combined with the `waitForExit` parameter, this can be used to gracefully restart the application. /// - /// If true, Update.exe will wait for the current process to exit before re-starting the application. + /// Optionally wait for the specified process to exit before continuing. /// The locator to use to find the path to Update.exe and the packages directory. /// The arguments to pass to the application when it is restarted. /// The logger to use for diagnostic messages - public static void Start(IVelopackLocator? locator = null, bool waitForExit = true, string[]? startArgs = null, ILogger? logger = null) + public static void Start(IVelopackLocator? locator = null, uint waitPid = 0, string[]? startArgs = null, ILogger? logger = null) { logger ??= NullLogger.Instance; locator ??= VelopackLocator.GetDefault(logger); @@ -70,9 +70,9 @@ namespace Velopack var args = new List(); args.Add("start"); - if (waitForExit) { + if (waitPid > 0) { args.Add("--waitPid"); - args.Add(Process.GetCurrentProcess().Id.ToString()); + args.Add(waitPid.ToString()); } if (startArgs != null && startArgs.Length > 0) { @@ -85,8 +85,8 @@ namespace Velopack StartUpdateExe(logger, locator, args); } - private static Process ApplyImpl(IVelopackLocator? locator, VelopackAsset? toApply, bool silent, bool restart, string[]? restartArgs = null, - ILogger? logger = null) + private static Process ApplyImpl(IVelopackLocator? locator, VelopackAsset? toApply, bool silent, uint waitPid, bool restart, + string[]? restartArgs = null, ILogger? logger = null) { logger ??= NullLogger.Instance; locator ??= VelopackLocator.GetDefault(logger); @@ -104,8 +104,10 @@ namespace Velopack } } - args.Add("--waitPid"); - args.Add(Process.GetCurrentProcess().Id.ToString()); + if (waitPid > 0) { + args.Add("--waitPid"); + args.Add(waitPid.ToString()); + } if (!restart) args.Add("--norestart"); // restarting is now the default Update.exe behavior @@ -128,13 +130,14 @@ namespace Velopack /// If true, restarts the application after updates are applied (or if they failed) /// The locator to use to find the path to Update.exe and the packages directory. /// The update package you wish to apply, can be left null. + /// Optionally wait for the specified process to exit before continuing. /// The arguments to pass to the application when it is restarted. /// The logger to use for diagnostic messages /// Thrown if Update.exe does not initialize properly. - public static void Apply(IVelopackLocator? locator, VelopackAsset? toApply, bool silent, bool restart, string[]? restartArgs = null, - ILogger? logger = null) + public static void Apply(IVelopackLocator? locator, VelopackAsset? toApply, bool silent, uint waitPid, bool restart, + string[]? restartArgs = null, ILogger? logger = null) { - var process = ApplyImpl(locator, toApply, silent, restart, restartArgs, logger); + var process = ApplyImpl(locator, toApply, silent, waitPid, restart, restartArgs, logger); Thread.Sleep(500); if (process.HasExited) { @@ -143,10 +146,10 @@ namespace Velopack } /// - public static async Task ApplyAsync(IVelopackLocator? locator, VelopackAsset? toApply, bool silent, bool restart, string[]? restartArgs = null, + public static async Task ApplyAsync(IVelopackLocator? locator, VelopackAsset? toApply, bool silent, uint waitPid, bool restart, string[]? restartArgs = null, ILogger? logger = null) { - var process = ApplyImpl(locator, toApply, silent, restart, restartArgs, logger); + var process = ApplyImpl(locator, toApply, silent, waitPid, restart, restartArgs, logger); await Task.Delay(500).ConfigureAwait(false); if (process.HasExited) { diff --git a/src/lib-csharp/UpdateManager.Helpers.cs b/src/lib-csharp/UpdateManager.Helpers.cs index 0110abc5..03f9f4be 100644 --- a/src/lib-csharp/UpdateManager.Helpers.cs +++ b/src/lib-csharp/UpdateManager.Helpers.cs @@ -45,13 +45,13 @@ namespace Velopack /// The arguments to pass to the application when it is restarted. public void WaitExitThenApplyUpdates(VelopackAsset? toApply, bool silent = false, bool restart = true, string[]? restartArgs = null) { - UpdateExe.Apply(Locator, toApply, silent, restart, restartArgs, Log); + UpdateExe.Apply(Locator, toApply, silent, VelopackRuntimeInfo.ProcessId, restart, restartArgs, Log); } /// public async Task WaitExitThenApplyUpdatesAsync(VelopackAsset? toApply, bool silent = false, bool restart = true, string[]? restartArgs = null) { - await UpdateExe.ApplyAsync(Locator, toApply, silent, restart, restartArgs, Log).ConfigureAwait(false); + await UpdateExe.ApplyAsync(Locator, toApply, silent, VelopackRuntimeInfo.ProcessId, restart, restartArgs, Log).ConfigureAwait(false); } } } diff --git a/src/lib-csharp/VelopackApp.cs b/src/lib-csharp/VelopackApp.cs index 228c45bf..1450c6fa 100644 --- a/src/lib-csharp/VelopackApp.cs +++ b/src/lib-csharp/VelopackApp.cs @@ -224,7 +224,7 @@ namespace Velopack log.Info($"Launching app is out-dated. Current: {myVersion}, Newest Local Available: {latestLocal.Version}"); if (!restarted && _autoApply) { log.Info("Auto apply is true, so restarting to apply update..."); - UpdateExe.Apply(locator, latestLocal, false, true, args, log); + UpdateExe.Apply(locator, latestLocal, false, VelopackRuntimeInfo.ProcessId, true, args, log); Exit(0); } else { log.Info("Pre-condition failed, we will not restart to apply updates. (restarted: " + restarted + ", autoApply: " + _autoApply + ")"); diff --git a/src/lib-csharp/VelopackRuntimeInfo.cs b/src/lib-csharp/VelopackRuntimeInfo.cs index 1054ce13..d17c8b91 100644 --- a/src/lib-csharp/VelopackRuntimeInfo.cs +++ b/src/lib-csharp/VelopackRuntimeInfo.cs @@ -90,6 +90,9 @@ namespace Velopack /// The path on disk of the entry assembly. public static string EntryExePath { get; } + + /// The current executing process ID. + public static uint ProcessId { get; } /// The current machine architecture, ignoring the current process / pe architecture. public static RuntimeCpu SystemArch { get; private set; } @@ -122,7 +125,9 @@ namespace Velopack static VelopackRuntimeInfo() { - EntryExePath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; + var currentProcess = System.Diagnostics.Process.GetCurrentProcess(); + EntryExePath = currentProcess.MainModule.FileName; + ProcessId = (uint)currentProcess.Id; #if DEBUG InUnitTestRunner = CheckForUnitTestRunner(); diff --git a/src/lib-rust/src/manager.rs b/src/lib-rust/src/manager.rs index 4172518b..6d9795fa 100644 --- a/src/lib-rust/src/manager.rs +++ b/src/lib-rust/src/manager.rs @@ -6,24 +6,36 @@ use std::{ sync::mpsc::Sender, }; +use semver::Version; +use serde::{Deserialize, Serialize}; + #[cfg(feature = "async")] use async_std::channel::Sender as AsyncSender; #[cfg(feature = "async")] use async_std::task::JoinHandle; -use semver::Version; -use serde::{Deserialize, Serialize}; use crate::{ - locator::{self, VelopackLocatorConfig, LocationContext, VelopackLocator}, + locator::{self, LocationContext, VelopackLocator, VelopackLocatorConfig}, sources::UpdateSource, - Error, - util, + util, Error, }; +/// Configure how the update process should wait before applying updates. +pub enum ApplyWaitMode { + /// NOT RECOMMENDED: Will not wait for any process before continuing. This could result in the update process being + /// killed, or the update process itself failing. + NoWait, + /// Will wait for the current process to exit before continuing. This is the default and recommended mode. + WaitCurrentProcess, + /// Wait for the specified process ID to exit before continuing. This is useful if you are updating a program + /// different from the one that is currently running. + WaitPid(u32), +} + +/// A feed of Velopack assets, usually retrieved from a remote location. #[allow(non_snake_case)] #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[serde(default)] -/// A feed of Velopack assets, usually retrieved from a remote location. pub struct VelopackAssetFeed { /// The list of assets in the (probably remote) update feed. pub Assets: Vec, @@ -36,11 +48,11 @@ impl VelopackAssetFeed { } } +/// An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. #[allow(non_snake_case)] #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(default)] -/// An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. pub struct VelopackAsset { /// The name or Id of the package containing this release. pub PackageId: String, @@ -62,11 +74,11 @@ pub struct VelopackAsset { pub NotesHtml: String, } +/// Holds information about the current version and pending updates, such as how many there are, and access to release notes. #[allow(non_snake_case)] #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(default)] -/// Holds information about the current version and pending updates, such as how many there are, and access to release notes. pub struct UpdateInfo { /// The available version that we are updating to. pub TargetFullRelease: VelopackAsset, @@ -88,11 +100,11 @@ impl AsRef for VelopackAsset { } } +/// Options to customise the behaviour of UpdateManager. #[allow(non_snake_case)] #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(default)] -/// Options to customise the behaviour of UpdateManager. pub struct UpdateOptions { /// Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading). /// This could happen if a release has bugs and was retracted from the release feed, or if you're using @@ -166,11 +178,7 @@ impl UpdateManager { } else { locator::auto_locate_app_manifest(LocationContext::FromCurrentExe)? }; - Ok(UpdateManager { - options: options.unwrap_or_default(), - source, - locator, - }) + Ok(UpdateManager { options: options.unwrap_or_default(), source, locator }) } fn get_practical_channel(&self) -> String { @@ -189,7 +197,7 @@ impl UpdateManager { pub fn get_current_version_as_string(&self) -> String { self.locator.get_manifest_version_full_string() } - + /// The currently installed app version as a semver Version. pub fn get_current_version(&self) -> Version { self.locator.get_manifest_version() @@ -206,7 +214,7 @@ impl UpdateManager { self.locator.get_is_portable() } - /// Returns None if there is no local package waiting to be applied. Returns a VelopackAsset + /// Returns None if there is no local package waiting to be applied. Returns a VelopackAsset /// if there is an update downloaded which has not yet been applied. In that case, the /// VelopackAsset can be applied by calling apply_updates_and_restart or wait_exit_then_apply_updates. pub fn get_update_pending_restart(&self) -> Option { @@ -235,10 +243,9 @@ impl UpdateManager { self.source.get_release_feed(&channel, &self.locator.get_manifest()) } - #[cfg(feature = "async")] /// Get a list of available remote releases from the package source. - pub fn get_release_feed_async(&self) -> JoinHandle> - { + #[cfg(feature = "async")] + pub fn get_release_feed_async(&self) -> JoinHandle> { let self_clone = self.clone(); async_std::task::spawn_blocking(move || self_clone.get_release_feed()) } @@ -299,11 +306,10 @@ impl UpdateManager { } } - #[cfg(feature = "async")] /// Checks for updates, returning None if there are none available. If there are updates available, this method will return an /// UpdateInfo object containing the latest available release, and any delta updates that can be applied if they are available. - pub fn check_for_updates_async(&self) -> JoinHandle> - { + #[cfg(feature = "async")] + pub fn check_for_updates_async(&self) -> JoinHandle> { let self_clone = self.clone(); async_std::task::spawn_blocking(move || self_clone.check_for_updates()) } @@ -331,7 +337,7 @@ impl UpdateManager { let old_nupkg_pattern = format!("{}/*.nupkg", packages_dir.to_string_lossy()); let old_partial_pattern = format!("{}/*.partial", packages_dir.to_string_lossy()); let mut to_delete = Vec::new(); - + fn find_files_to_delete(pattern: &str, to_delete: &mut Vec) { info!("Searching for packages to clean: '{}'", pattern); match glob::glob(pattern) { @@ -345,13 +351,13 @@ impl UpdateManager { } } } - + find_files_to_delete(&old_nupkg_pattern, &mut to_delete); find_files_to_delete(&old_partial_pattern, &mut to_delete); self.source.download_release_entry(&update.TargetFullRelease, &partial_file.to_string_lossy(), progress)?; info!("Successfully placed file: '{}'", partial_file.to_string_lossy()); - + info!("Renaming partial file to final target: '{}'", final_target_file.to_string_lossy()); fs::rename(&partial_file, &final_target_file)?; @@ -378,13 +384,13 @@ impl UpdateManager { Ok(()) } - #[cfg(feature = "async")] /// Downloads the specified updates to the local app packages directory. Progress is reported back to the caller via an optional Sender. /// This function will acquire a global update lock so may fail if there is already another update operation in progress. /// - If the update contains delta packages and the delta feature is enabled /// this method will attempt to unpack and prepare them. /// - If there is no delta update available, or there is an error preparing delta /// packages, this method will fall back to downloading the full version of the update. + #[cfg(feature = "async")] pub fn download_updates_async(&self, update: &UpdateInfo, progress: Option>) -> JoinHandle> { let mut sync_progress: Option> = None; @@ -424,7 +430,7 @@ impl UpdateManager { where A: AsRef, S: AsRef, - C: IntoIterator, + C: IntoIterator, { self.wait_exit_then_apply_updates(to_apply, false, true, restart_args)?; exit(0); @@ -449,7 +455,27 @@ impl UpdateManager { where A: AsRef, S: AsRef, - C: IntoIterator, + C: IntoIterator, + { + self.unsafe_apply_updates(to_apply, silent, ApplyWaitMode::WaitCurrentProcess, restart, restart_args)?; + Ok(()) + } + + /// This will launch the Velopack updater and optionally wait for a program to exit gracefully. + /// This method is unsafe because it does not necessarily wait for any / the correct process to exit + /// before applying updates. The `wait_exit_then_apply_updates` method is recommended for most use cases. + pub fn unsafe_apply_updates( + &self, + to_apply: A, + silent: bool, + wait_mode: ApplyWaitMode, + restart: bool, + restart_args: C, + ) -> Result<(), Error> + where + A: AsRef, + S: AsRef, + C: IntoIterator, { let to_apply = to_apply.as_ref(); let pkg_path = self.locator.get_packages_dir().join(&to_apply.FileName); @@ -457,10 +483,26 @@ impl UpdateManager { let mut args = Vec::new(); args.push("apply".to_string()); - args.push("--waitPid".to_string()); - args.push(format!("{}", std::process::id())); + args.push("--package".to_string()); - args.push(pkg_path_str.into_owned()); + args.push(pkg_path_str.to_string()); + + if !pkg_path.exists() { + error!("Package does not exist on disk: '{}'", &pkg_path_str); + return Err(Error::FileNotFound(pkg_path_str.to_string())); + } + + match wait_mode { + ApplyWaitMode::NoWait => {} + ApplyWaitMode::WaitCurrentProcess => { + args.push("--waitPid".to_string()); + args.push(format!("{}", std::process::id())); + } + ApplyWaitMode::WaitPid(pid) => { + args.push("--waitPid".to_string()); + args.push(format!("{}", pid)); + } + } if silent { args.push("--silent".to_string()); @@ -495,4 +537,4 @@ impl UpdateManager { p.spawn()?; Ok(()) } -} \ No newline at end of file +}