mirror of
				https://github.com/velopack/velopack.git
				synced 2025-10-25 15:19:22 +00:00 
			
		
		
		
	Fix admin launching and locator
This commit is contained in:
		
							
								
								
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -2230,6 +2230,7 @@ dependencies = [ | |||||||
|  "ureq", |  "ureq", | ||||||
|  "url", |  "url", | ||||||
|  "uuid", |  "uuid", | ||||||
|  |  "waitpid-any", | ||||||
|  "windows", |  "windows", | ||||||
|  "xml", |  "xml", | ||||||
|  "zip", |  "zip", | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ edition = "2021" | |||||||
| rust-version = "1.75" | rust-version = "1.75" | ||||||
|  |  | ||||||
| [workspace.dependencies] | [workspace.dependencies] | ||||||
| velopack = { path = "src/lib-rust", features = ["file-logging"] } | velopack = { path = "src/lib-rust", features = ["file-logging", "public-utils"] } | ||||||
| log = "0.4" | log = "0.4" | ||||||
| log-derive = "0.4.1" | log-derive = "0.4.1" | ||||||
| ureq = "3.0" | ureq = "3.0" | ||||||
|   | |||||||
| @@ -3,3 +3,4 @@ use_small_heuristics = "Max" | |||||||
| indent_style = "Visual" | indent_style = "Visual" | ||||||
| unstable_features = true | unstable_features = true | ||||||
| format_strings = true | format_strings = true | ||||||
|  | single_line_if_else_max_width = 40 | ||||||
| @@ -1,13 +1,12 @@ | |||||||
| use crate::{ | use crate::{ | ||||||
|     dialogs, |     dialogs, | ||||||
|     shared::{self}, |     shared::{self}, | ||||||
|     // windows::locksmith, |     windows::{self, splash}, | ||||||
|     windows::splash, |  | ||||||
| }; | }; | ||||||
| use anyhow::{bail, Context, Result}; | use anyhow::{bail, Context, Result}; | ||||||
| use std::sync::mpsc; |  | ||||||
| use std::{fs, path::PathBuf}; | use std::{fs, path::PathBuf}; | ||||||
| use velopack::{bundle::load_bundle_from_file, constants, locator::VelopackLocator}; | use std::{sync::mpsc, time::Duration}; | ||||||
|  | use velopack::{bundle::load_bundle_from_file, constants, locator::VelopackLocator, process}; | ||||||
|  |  | ||||||
| // fn ropycopy<P1: AsRef<Path>, P2: AsRef<Path>>(source: &P1, dest: &P2) -> Result<()> { | // fn ropycopy<P1: AsRef<Path>, P2: AsRef<Path>>(source: &P1, dest: &P2) -> Result<()> { | ||||||
| //     let source = source.as_ref(); | //     let source = source.as_ref(); | ||||||
| @@ -40,11 +39,31 @@ use velopack::{bundle::load_bundle_from_file, constants, locator::VelopackLocato | |||||||
| // } | // } | ||||||
|  |  | ||||||
| pub fn apply_package_impl(old_locator: &VelopackLocator, package: &PathBuf, run_hooks: bool) -> Result<VelopackLocator> { | pub fn apply_package_impl(old_locator: &VelopackLocator, package: &PathBuf, run_hooks: bool) -> Result<VelopackLocator> { | ||||||
|  |     let root_path = old_locator.get_root_dir(); | ||||||
|  |  | ||||||
|     let mut bundle = load_bundle_from_file(package)?; |     let mut bundle = load_bundle_from_file(package)?; | ||||||
|     let new_app_manifest = bundle.read_manifest()?; |     let new_app_manifest = bundle.read_manifest()?; | ||||||
|     let new_locator = old_locator.clone_self_with_new_manifest(&new_app_manifest); |     let new_locator = old_locator.clone_self_with_new_manifest(&new_app_manifest); | ||||||
|  |  | ||||||
|     let root_path = old_locator.get_root_dir(); |     if !windows::is_directory_writable(&root_path) { | ||||||
|  |         if process::is_current_process_elevated() { | ||||||
|  |             bail!("The root directory is not writable & process is already admin. The update cannot continue."); | ||||||
|  |         } else { | ||||||
|  |             info!("Re-launching as administrator to update in {:?}", root_path); | ||||||
|  |  | ||||||
|  |             let package_string = package.to_string_lossy().to_string(); | ||||||
|  |             let args = vec!["--norestart".to_string(), "--package".to_string(), package_string]; | ||||||
|  |             let exe_path = std::env::current_exe()?; | ||||||
|  |             let work_dir: Option<String> = None; // same as this process | ||||||
|  |             let process_handle = process::run_process_as_admin(&exe_path, args, work_dir, false)?; | ||||||
|  |  | ||||||
|  |             info!("Waiting (up to 10 minutes) for elevated process to exit..."); | ||||||
|  |             let result = process::wait_for_process_to_exit_with_timeout(process_handle, Duration::from_secs(10 * 60))?; | ||||||
|  |             info!("Elevated process has exited ({:?}).", result); | ||||||
|  |             return Ok(new_locator); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     let old_version = old_locator.get_manifest_version(); |     let old_version = old_locator.get_manifest_version(); | ||||||
|     let new_version = new_locator.get_manifest_version(); |     let new_version = new_locator.get_manifest_version(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,9 +1,11 @@ | |||||||
| use crate::shared::{self, OperationWait}; | use crate::shared::{self, OperationWait}; | ||||||
| use anyhow::Result; | use anyhow::Result; | ||||||
|  | use velopack::locator::LocationContext; | ||||||
|  |  | ||||||
| #[allow(unused_variables, unused_imports)] | #[allow(unused_variables, unused_imports)] | ||||||
| pub fn start( | pub fn start( | ||||||
|     wait: OperationWait, |     wait: OperationWait, | ||||||
|  |     context: LocationContext, | ||||||
|     exe_name: Option<&String>, |     exe_name: Option<&String>, | ||||||
|     exe_args: Option<Vec<&str>>, |     exe_args: Option<Vec<&str>>, | ||||||
|     legacy_args: Option<&String>, |     legacy_args: Option<&String>, | ||||||
| @@ -18,12 +20,11 @@ pub fn start( | |||||||
|     shared::operation_wait(wait); |     shared::operation_wait(wait); | ||||||
|  |  | ||||||
|     #[cfg(target_os = "windows")] |     #[cfg(target_os = "windows")] | ||||||
|     super::start_windows_impl::start_impl(exe_name, exe_args, legacy_args)?; |     super::start_windows_impl::start_impl(context, exe_name, exe_args, legacy_args)?; | ||||||
|  |  | ||||||
|     #[cfg(not(target_os = "windows"))] |     #[cfg(not(target_os = "windows"))] | ||||||
|     { |     { | ||||||
|         use velopack::locator::{auto_locate_app_manifest, LocationContext}; |         let locator = velopack::locator::auto_locate_app_manifest(context)?; | ||||||
|         let locator = auto_locate_app_manifest(LocationContext::IAmUpdateExe)?; |  | ||||||
|         shared::start_package(&locator, exe_args, None)?; |         shared::start_package(&locator, exe_args, None)?; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -61,8 +61,8 @@ impl LocatorResult { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn legacy_locator() -> Result<LocatorResult> { | fn legacy_locator(context: LocationContext) -> Result<LocatorResult> { | ||||||
|     let locator = locator::auto_locate_app_manifest(LocationContext::IAmUpdateExe); |     let locator = locator::auto_locate_app_manifest(context); | ||||||
|     match locator { |     match locator { | ||||||
|         Ok(locator) => Ok(LocatorResult::Normal(locator)), |         Ok(locator) => Ok(LocatorResult::Normal(locator)), | ||||||
|         Err(e) => { |         Err(e) => { | ||||||
| @@ -81,11 +81,12 @@ fn legacy_locator() -> Result<LocatorResult> { | |||||||
| } | } | ||||||
|  |  | ||||||
| pub fn start_impl( | pub fn start_impl( | ||||||
|  |     context: LocationContext, | ||||||
|     exe_name: Option<&String>, |     exe_name: Option<&String>, | ||||||
|     exe_args: Option<Vec<&str>>, |     exe_args: Option<Vec<&str>>, | ||||||
|     legacy_args: Option<&String>, |     legacy_args: Option<&String>, | ||||||
| ) -> Result<()> { | ) -> Result<()> { | ||||||
|     let locator = legacy_locator()?; |     let locator = legacy_locator(context)?; | ||||||
|     let root_dir = locator.get_root_dir(); |     let root_dir = locator.get_root_dir(); | ||||||
|     let manifest = locator.get_manifest(); |     let manifest = locator.get_manifest(); | ||||||
|     if shared::has_app_prefixed_folder(&root_dir) { |     if shared::has_app_prefixed_folder(&root_dir) { | ||||||
|   | |||||||
| @@ -2,9 +2,8 @@ use crate::windows::strings; | |||||||
| use ::windows::{ | use ::windows::{ | ||||||
|     core::PWSTR, |     core::PWSTR, | ||||||
|     Win32::{ |     Win32::{ | ||||||
|         Foundation::CloseHandle, |  | ||||||
|         System::ProcessStatus::EnumProcesses, |         System::ProcessStatus::EnumProcesses, | ||||||
|         System::Threading::{OpenProcess, QueryFullProcessImageNameW, PROCESS_NAME_WIN32, PROCESS_QUERY_LIMITED_INFORMATION}, |         System::Threading::{QueryFullProcessImageNameW, PROCESS_NAME_WIN32, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_TERMINATE}, | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| use anyhow::{bail, Result}; | use anyhow::{bail, Result}; | ||||||
| @@ -33,27 +32,26 @@ fn get_pids() -> Result<Vec<u32>> { | |||||||
|     Ok(pids.iter().map(|x| *x as u32).collect()) |     Ok(pids.iter().map(|x| *x as u32).collect()) | ||||||
| } | } | ||||||
|  |  | ||||||
| unsafe fn get_processes_running_in_directory<P: AsRef<Path>>(dir: P) -> Result<HashMap<u32, PathBuf>> { | unsafe fn get_processes_running_in_directory<P: AsRef<Path>>(dir: P) -> Result<Vec<(u32, PathBuf, process::SafeProcessHandle)>> { | ||||||
|     let dir = dir.as_ref(); |     let dir = dir.as_ref(); | ||||||
|     let mut oup = HashMap::new(); |     let mut oup = Vec::new(); | ||||||
|  |  | ||||||
|     let mut full_path_vec = vec![0; i16::MAX as usize]; |     let mut full_path_vec = vec![0; i16::MAX as usize]; | ||||||
|     let full_path_ptr = PWSTR(full_path_vec.as_mut_ptr()); |     let full_path_ptr = PWSTR(full_path_vec.as_mut_ptr()); | ||||||
|  |  | ||||||
|     for pid in get_pids()? { |     for pid in get_pids()? { | ||||||
|         let process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid); |         let process = process::open_process(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE, false, pid); | ||||||
|         if process.is_err() { |         if process.is_err() { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let process = process.unwrap(); |         let process = process.unwrap(); | ||||||
|         if process.is_invalid() { |         if process.handle().is_invalid() { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let mut full_path_len = full_path_vec.len() as u32; |         let mut full_path_len = full_path_vec.len() as u32; | ||||||
|         if QueryFullProcessImageNameW(process, PROCESS_NAME_WIN32, full_path_ptr, &mut full_path_len).is_err() { |         if QueryFullProcessImageNameW(process.handle(), PROCESS_NAME_WIN32, full_path_ptr, &mut full_path_len).is_err() { | ||||||
|             let _ = CloseHandle(process); |  | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -65,7 +63,7 @@ unsafe fn get_processes_running_in_directory<P: AsRef<Path>>(dir: P) -> Result<H | |||||||
|         let full_path = PathBuf::from(full_path.unwrap()); |         let full_path = PathBuf::from(full_path.unwrap()); | ||||||
|         if let Ok(is_subpath) = crate::windows::is_sub_path(&full_path, dir) { |         if let Ok(is_subpath) = crate::windows::is_sub_path(&full_path, dir) { | ||||||
|             if is_subpath { |             if is_subpath { | ||||||
|                 oup.insert(pid, full_path); |                 oup.push((pid, full_path, process)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -84,13 +82,13 @@ fn _force_stop_package<P: AsRef<Path>>(root_dir: P) -> Result<()> { | |||||||
|     info!("Checking for running processes in: {}", dir.display()); |     info!("Checking for running processes in: {}", dir.display()); | ||||||
|     let processes = unsafe { get_processes_running_in_directory(dir)? }; |     let processes = unsafe { get_processes_running_in_directory(dir)? }; | ||||||
|     let my_pid = std::process::id(); |     let my_pid = std::process::id(); | ||||||
|     for (pid, exe) in processes.iter() { |     for (pid, path, handle) in processes.iter() { | ||||||
|         if *pid == my_pid { |         if *pid == my_pid { | ||||||
|             warn!("Skipping killing self: {} ({})", exe.display(), pid); |             warn!("Skipping killing self: {} ({})", path.display(), pid); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         warn!("Killing process: {} ({})", exe.display(), pid); |         warn!("Killing process: {} ({})", path.display(), pid); | ||||||
|         process::kill_pid(*pid)?; |         process::kill_process(handle)?; | ||||||
|     } |     } | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
| @@ -195,7 +193,7 @@ fn test_get_running_processes_finds_cargo() { | |||||||
|     assert!(processes.len() > 0); |     assert!(processes.len() > 0); | ||||||
|  |  | ||||||
|     let mut found = false; |     let mut found = false; | ||||||
|     for (_pid, exe) in processes.iter() { |     for (_pid, exe, _handle) in processes.iter() { | ||||||
|         if exe.ends_with("cargo.exe") { |         if exe.ends_with("cargo.exe") { | ||||||
|             found = true; |             found = true; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ fn root_command() -> Command { | |||||||
|     ) |     ) | ||||||
|     .arg(arg!(--verbose "Print debug messages to console / log").global(true)) |     .arg(arg!(--verbose "Print debug messages to console / log").global(true)) | ||||||
|     .arg(arg!(-s --silent "Don't show any prompts / dialogs").global(true)) |     .arg(arg!(-s --silent "Don't show any prompts / dialogs").global(true)) | ||||||
|  |     .arg(arg!(--root <PATH> "Override the default locator root directory").global(true).hide(true).value_parser(value_parser!(PathBuf))) | ||||||
|     .arg(arg!(-l --log <PATH> "Override the default log file location").global(true).value_parser(value_parser!(PathBuf))) |     .arg(arg!(-l --log <PATH> "Override the default log file location").global(true).value_parser(value_parser!(PathBuf))) | ||||||
|         // Legacy arguments should not be fully removed if it's possible to keep them |         // Legacy arguments should not be fully removed if it's possible to keep them | ||||||
|         // Reason being is clap.ignore_errors(true) is not 100%, and sometimes old args can trip things up. |         // Reason being is clap.ignore_errors(true) is not 100%, and sometimes old args can trip things up. | ||||||
| @@ -131,9 +132,16 @@ fn main() -> Result<()> { | |||||||
|     let silent = get_flag_or_false(&matches, "silent"); |     let silent = get_flag_or_false(&matches, "silent"); | ||||||
|     dialogs::set_silent(silent); |     dialogs::set_silent(silent); | ||||||
|  |  | ||||||
|  |     let root_dir = matches.get_one::<PathBuf>("root"); | ||||||
|  |     let location_context = if let Some(root) = root_dir { | ||||||
|  |         LocationContext::FromSpecifiedRootDir(root.clone()) | ||||||
|  |     } else { | ||||||
|  |         LocationContext::IAmUpdateExe | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     let verbose = get_flag_or_false(&matches, "verbose"); |     let verbose = get_flag_or_false(&matches, "verbose"); | ||||||
|     let log_file = matches.get_one("log"); |     let log_file = matches.get_one("log"); | ||||||
|     let desired_log_file = log_file.cloned().unwrap_or(default_logfile_path(LocationContext::IAmUpdateExe)); |     let desired_log_file = log_file.cloned().unwrap_or(default_logfile_path(&location_context)); | ||||||
|     init_logging("update", Some(&desired_log_file), true, verbose, None); |     init_logging("update", Some(&desired_log_file), true, verbose, None); | ||||||
|  |  | ||||||
|     // change working directory to the parent directory of the exe |     // change working directory to the parent directory of the exe | ||||||
| @@ -148,16 +156,17 @@ fn main() -> Result<()> { | |||||||
|     info!("    Verbose: {}", verbose); |     info!("    Verbose: {}", verbose); | ||||||
|     info!("    Silent: {}", silent); |     info!("    Silent: {}", silent); | ||||||
|     info!("    Log File: {:?}", log_file); |     info!("    Log File: {:?}", log_file); | ||||||
|  |     info!("    Context: {:?}", &location_context); | ||||||
|  |  | ||||||
|     let (subcommand, subcommand_matches) = |     let (subcommand, subcommand_matches) = | ||||||
|         matches.subcommand().ok_or_else(|| anyhow!("No known subcommand was used. Try `--help` for more information."))?; |         matches.subcommand().ok_or_else(|| anyhow!("No known subcommand was used. Try `--help` for more information."))?; | ||||||
|  |  | ||||||
|     let result = match subcommand { |     let result = match subcommand { | ||||||
|         #[cfg(target_os = "windows")] |         #[cfg(target_os = "windows")] | ||||||
|         "uninstall" => uninstall(subcommand_matches).map_err(|e| anyhow!("Uninstall error: {}", e)), |         "uninstall" => uninstall(location_context, subcommand_matches).map_err(|e| anyhow!("Uninstall error: {}", e)), | ||||||
|         "start" => start(subcommand_matches).map_err(|e| anyhow!("Start error: {}", e)), |         "start" => start(location_context, subcommand_matches).map_err(|e| anyhow!("Start error: {}", e)), | ||||||
|         "apply" => apply(subcommand_matches).map_err(|e| anyhow!("Apply error: {}", e)), |         "apply" => apply(location_context, subcommand_matches).map_err(|e| anyhow!("Apply error: {}", e)), | ||||||
|         "patch" => patch(subcommand_matches).map_err(|e| anyhow!("Patch error: {}", e)), |         "patch" => patch(location_context, subcommand_matches).map_err(|e| anyhow!("Patch error: {}", e)), | ||||||
|         _ => bail!("Unknown subcommand '{subcommand}'. Try `--help` for more information."), |         _ => bail!("Unknown subcommand '{subcommand}'. Try `--help` for more information."), | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| @@ -169,7 +178,7 @@ fn main() -> Result<()> { | |||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn patch(matches: &ArgMatches) -> Result<()> { | fn patch(_context: LocationContext, matches: &ArgMatches) -> Result<()> { | ||||||
|     let old_file = matches.get_one::<PathBuf>("old"); |     let old_file = matches.get_one::<PathBuf>("old"); | ||||||
|     let patch_file = matches.get_one::<PathBuf>("patch"); |     let patch_file = matches.get_one::<PathBuf>("patch"); | ||||||
|     let output_file = matches.get_one::<PathBuf>("output"); |     let output_file = matches.get_one::<PathBuf>("output"); | ||||||
| @@ -199,7 +208,7 @@ fn get_apply_args(matches: &ArgMatches) -> (OperationWait, bool, Option<&PathBuf | |||||||
|     (wait, restart, package, exe_args) |     (wait, restart, package, exe_args) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn apply(matches: &ArgMatches) -> Result<()> { | fn apply(context: LocationContext, matches: &ArgMatches) -> Result<()> { | ||||||
|     let (wait, restart, package, exe_args) = get_apply_args(matches); |     let (wait, restart, package, exe_args) = get_apply_args(matches); | ||||||
|     info!("Command: Apply"); |     info!("Command: Apply"); | ||||||
|     info!("    Restart: {:?}", restart); |     info!("    Restart: {:?}", restart); | ||||||
| @@ -207,7 +216,7 @@ fn apply(matches: &ArgMatches) -> Result<()> { | |||||||
|     info!("    Package: {:?}", package); |     info!("    Package: {:?}", package); | ||||||
|     info!("    Exe Args: {:?}", exe_args); |     info!("    Exe Args: {:?}", exe_args); | ||||||
|  |  | ||||||
|     let locator = auto_locate_app_manifest(LocationContext::IAmUpdateExe)?; |     let locator = auto_locate_app_manifest(context)?; | ||||||
|     let _mutex = locator.try_get_exclusive_lock()?; |     let _mutex = locator.try_get_exclusive_lock()?; | ||||||
|     let _ = commands::apply(&locator, restart, wait, package, exe_args, true)?; |     let _ = commands::apply(&locator, restart, wait, package, exe_args, true)?; | ||||||
|     Ok(()) |     Ok(()) | ||||||
| @@ -221,7 +230,7 @@ fn get_start_args(matches: &ArgMatches) -> (OperationWait, Option<&String>, Opti | |||||||
|     (wait, exe_name, legacy_args, exe_args) |     (wait, exe_name, legacy_args, exe_args) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn start(matches: &ArgMatches) -> Result<()> { | fn start(context: LocationContext, matches: &ArgMatches) -> Result<()> { | ||||||
|     let (wait, exe_name, legacy_args, exe_args) = get_start_args(matches); |     let (wait, exe_name, legacy_args, exe_args) = get_start_args(matches); | ||||||
|  |  | ||||||
|     info!("Command: Start"); |     info!("Command: Start"); | ||||||
| @@ -233,13 +242,13 @@ fn start(matches: &ArgMatches) -> Result<()> { | |||||||
|         warn!("Legacy args format is deprecated and will be removed in a future release. Please update your application to use the new format."); |         warn!("Legacy args format is deprecated and will be removed in a future release. Please update your application to use the new format."); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     commands::start(wait, exe_name, exe_args, legacy_args) |     commands::start(wait, context, exe_name, exe_args, legacy_args) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(target_os = "windows")] | #[cfg(target_os = "windows")] | ||||||
| fn uninstall(_matches: &ArgMatches) -> Result<()> { | fn uninstall(context: LocationContext, _matches: &ArgMatches) -> Result<()> { | ||||||
|     info!("Command: Uninstall"); |     info!("Command: Uninstall"); | ||||||
|     let locator = auto_locate_app_manifest(LocationContext::IAmUpdateExe)?; |     let locator = auto_locate_app_manifest(context)?; | ||||||
|     commands::uninstall(&locator, true) |     commands::uninstall(&locator, true) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -256,6 +265,15 @@ fn test_cli_parse_handles_equals_spaces() { | |||||||
|     assert_eq!(exe_args, None); |     assert_eq!(exe_args, None); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(target_os = "windows")] | ||||||
|  | #[test] | ||||||
|  | fn test_cli_handles_root_at_end() { | ||||||
|  |     let command = vec!["C:\\Some Path\\With = Spaces\\Update.exe", "apply", "--package", "C:\\Package.zip", "--root", "C:\\Some Path"]; | ||||||
|  |     let matches = try_parse_command_line_matches(command.iter().map(|s| s.to_string()).collect()).unwrap(); | ||||||
|  |     let root = matches.get_one::<PathBuf>("root"); | ||||||
|  |     assert_eq!(root, Some(&PathBuf::from("C:\\Some Path"))); | ||||||
|  | } | ||||||
|  |  | ||||||
| #[cfg(target_os = "windows")] | #[cfg(target_os = "windows")] | ||||||
| #[test] | #[test] | ||||||
| fn test_start_command_supports_legacy_commands() { | fn test_start_command_supports_legacy_commands() { | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ pub fn write_uninstall_entry(locator: &VelopackLocator) -> Result<()> { | |||||||
|     let main_exe_path = locator.get_main_exe_path_as_string(); |     let main_exe_path = locator.get_main_exe_path_as_string(); | ||||||
|     let updater_path = locator.get_update_path_as_string(); |     let updater_path = locator.get_update_path_as_string(); | ||||||
|  |  | ||||||
|     let folder_size = fs_extra::dir::get_size(locator.get_root_dir()).unwrap_or(0); |     let folder_size = fs_extra::dir::get_size(locator.get_current_bin_dir()).unwrap_or(0); | ||||||
|     let short_version = locator.get_manifest_version_short_string(); |     let short_version = locator.get_manifest_version_short_string(); | ||||||
|  |  | ||||||
|     let now = DateTime::now(); |     let now = DateTime::now(); | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ pub fn run_hook(locator: &VelopackLocator, hook_name: &str, timeout_secs: u64) - | |||||||
|     let sw = simple_stopwatch::Stopwatch::start_new(); |     let sw = simple_stopwatch::Stopwatch::start_new(); | ||||||
|     let root_dir = locator.get_root_dir(); |     let root_dir = locator.get_root_dir(); | ||||||
|     let current_path = locator.get_current_bin_dir(); |     let current_path = locator.get_current_bin_dir(); | ||||||
|     let main_exe_path = locator.get_main_exe_path_as_string(); |     let main_exe_path = locator.get_main_exe_path(); | ||||||
|     let ver_string = locator.get_manifest_version_full_string(); |     let ver_string = locator.get_manifest_version_full_string(); | ||||||
|     let args = vec![hook_name, &ver_string]; |     let args = vec![hook_name, &ver_string]; | ||||||
|     let mut success = false; |     let mut success = false; | ||||||
| @@ -40,7 +40,7 @@ pub fn run_hook(locator: &VelopackLocator, hook_name: &str, timeout_secs: u64) - | |||||||
|  |  | ||||||
|     let cmd = cmd.unwrap(); |     let cmd = cmd.unwrap(); | ||||||
|  |  | ||||||
|     match process::wait_for_process_to_exit_with_timeout(cmd.handle(), Duration::from_secs(timeout_secs)) { |     match process::wait_for_process_to_exit_with_timeout(&cmd, Duration::from_secs(timeout_secs)) { | ||||||
|         Ok(WaitResult::NoWaitRequired) => { |         Ok(WaitResult::NoWaitRequired) => { | ||||||
|             warn!("Was unable to wait for hook (it may have exited too quickly)."); |             warn!("Was unable to wait for hook (it may have exited too quickly)."); | ||||||
|         } |         } | ||||||
| @@ -53,7 +53,7 @@ pub fn run_hook(locator: &VelopackLocator, hook_name: &str, timeout_secs: u64) - | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         Ok(WaitResult::WaitTimeout) => { |         Ok(WaitResult::WaitTimeout) => { | ||||||
|             let _ = process::kill_process(cmd.handle()); |             let _ = process::kill_process(&cmd); | ||||||
|             error!("Process timed out after {}s and was killed.", timeout_secs); |             error!("Process timed out after {}s and was killed.", timeout_secs); | ||||||
|         } |         } | ||||||
|         Err(e) => { |         Err(e) => { | ||||||
|   | |||||||
| @@ -1,22 +1,11 @@ | |||||||
| use std::path::PathBuf; |  | ||||||
| use semver::Version; |  | ||||||
| use uuid::Uuid; |  | ||||||
|  |  | ||||||
| use crate::{ | use crate::{ | ||||||
|     bundle::{self, Manifest}, |     bundle::{self, Manifest}, | ||||||
|     util, Error, |     lockfile::LockFile, | ||||||
|     lockfile::LockFile |     misc, Error, | ||||||
| }; | }; | ||||||
|  | use semver::Version; | ||||||
| /// Returns the default channel name for the current OS. | use std::path::PathBuf; | ||||||
| pub fn default_channel_name() -> String { | use uuid::Uuid; | ||||||
|     #[cfg(target_os = "windows")] |  | ||||||
|     return "win".to_owned(); |  | ||||||
|     #[cfg(target_os = "linux")] |  | ||||||
|     return "linux".to_owned(); |  | ||||||
|     #[cfg(target_os = "macos")] |  | ||||||
|     return "osx".to_owned(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bitflags::bitflags! { | bitflags::bitflags! { | ||||||
|     #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |     #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||||||
| @@ -110,10 +99,16 @@ impl TryFrom<LocationContext> for VelopackLocator { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl TryFrom<&LocationContext> for VelopackLocator { | ||||||
|  |     type Error = Error; | ||||||
|  |     fn try_from(context: &LocationContext) -> Result<Self, Self::Error> { | ||||||
|  |         auto_locate_app_manifest(context.clone()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| impl VelopackLocator { | impl VelopackLocator { | ||||||
|     /// Creates a new VelopackLocator from the given paths, trying to auto-detect the manifest. |     /// Creates a new VelopackLocator from the given paths, trying to auto-detect the manifest. | ||||||
|     pub fn new(config: &VelopackLocatorConfig) -> Result<VelopackLocator, Error> |     pub fn new(config: &VelopackLocatorConfig) -> Result<VelopackLocator, Error> { | ||||||
|     { |  | ||||||
|         if !config.UpdateExePath.exists() { |         if !config.UpdateExePath.exists() { | ||||||
|             return Err(Error::MissingUpdateExe); |             return Err(Error::MissingUpdateExe); | ||||||
|         } |         } | ||||||
| @@ -122,10 +117,32 @@ impl VelopackLocator { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         let manifest = read_current_manifest(&config.ManifestPath)?; |         let manifest = read_current_manifest(&config.ManifestPath)?; | ||||||
|         Ok(Self { paths: config.clone(), manifest }) |         Ok(Self::new_with_manifest(config.clone(), manifest)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Creates a new VelopackLocator from the given paths and manifest. |     /// Creates a new VelopackLocator from the given paths and manifest. | ||||||
|  |     #[cfg(windows)] | ||||||
|  |     pub fn new_with_manifest(mut paths: VelopackLocatorConfig, manifest: Manifest) -> Self { | ||||||
|  |         let root = paths.RootAppDir.clone(); | ||||||
|  |         if root.starts_with("C:\\Program Files") || !misc::is_directory_writable(&root) { | ||||||
|  |             let temp_root = std::env::temp_dir().join(format!("velopack_{}", manifest.id)); | ||||||
|  |             let orig_update_path = paths.UpdateExePath.clone(); | ||||||
|  |             paths.PackagesDir = temp_root.join("packages"); | ||||||
|  |             if !paths.PackagesDir.exists() { | ||||||
|  |                 std::fs::create_dir_all(&paths.PackagesDir).unwrap(); | ||||||
|  |             } | ||||||
|  |             paths.UpdateExePath = temp_root.join("Update.exe"); | ||||||
|  |             if !paths.UpdateExePath.exists() && orig_update_path.exists() { | ||||||
|  |                 std::fs::copy(orig_update_path, &paths.UpdateExePath).unwrap(); | ||||||
|  |                 warn!("Application directory is not writable. Copying Update.exe to temp location: {:?}", paths.UpdateExePath); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Self { paths, manifest } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Creates a new VelopackLocator from the given paths and manifest. | ||||||
|  |     #[cfg(not(windows))] | ||||||
|     pub fn new_with_manifest(paths: VelopackLocatorConfig, manifest: Manifest) -> Self { |     pub fn new_with_manifest(paths: VelopackLocatorConfig, manifest: Manifest) -> Self { | ||||||
|         Self { paths, manifest } |         Self { paths, manifest } | ||||||
|     } |     } | ||||||
| @@ -159,7 +176,7 @@ impl VelopackLocator { | |||||||
|  |  | ||||||
|     /// Get the name of a new temporary directory inside get_temp_dir_root() with a random 16-character suffix. |     /// Get the name of a new temporary directory inside get_temp_dir_root() with a random 16-character suffix. | ||||||
|     pub fn get_temp_dir_rand16(&self) -> PathBuf { |     pub fn get_temp_dir_rand16(&self) -> PathBuf { | ||||||
|         self.get_temp_dir_root().join("tmp_".to_string() + &util::random_string(16)) |         self.get_temp_dir_root().join("tmp_".to_string() + &misc::random_string(16)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Returns the path to the current app temporary directory as a string. |     /// Returns the path to the current app temporary directory as a string. | ||||||
| @@ -274,12 +291,8 @@ impl VelopackLocator { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Returns a copy of the current VelopackLocator with the manifest field set to the given manifest. |     /// Returns a copy of the current VelopackLocator with the manifest field set to the given manifest. | ||||||
|     pub fn clone_self_with_new_manifest(&self, manifest: &Manifest) -> VelopackLocator |     pub fn clone_self_with_new_manifest(&self, manifest: &Manifest) -> VelopackLocator { | ||||||
|     { |         VelopackLocator { paths: self.paths.clone(), manifest: manifest.clone() } | ||||||
|         VelopackLocator { |  | ||||||
|             paths: self.paths.clone(), |  | ||||||
|             manifest: manifest.clone(), |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Returns whether the app is portable or installed. |     /// Returns whether the app is portable or installed. | ||||||
| @@ -324,8 +337,7 @@ impl VelopackLocator { | |||||||
| /// Create a paths object containing default / ideal paths for a given root directory | /// Create a paths object containing default / ideal paths for a given root directory | ||||||
| /// Generally, this should not be used except for installing the app for the first time. | /// Generally, this should not be used except for installing the app for the first time. | ||||||
| #[cfg(target_os = "windows")] | #[cfg(target_os = "windows")] | ||||||
| pub fn create_config_from_root_dir<P: AsRef<std::path::Path>>(root_dir: P) -> VelopackLocatorConfig | pub fn create_config_from_root_dir<P: AsRef<std::path::Path>>(root_dir: P) -> VelopackLocatorConfig { | ||||||
| { |  | ||||||
|     let root_dir = root_dir.as_ref(); |     let root_dir = root_dir.as_ref(); | ||||||
|     VelopackLocatorConfig { |     VelopackLocatorConfig { | ||||||
|         RootAppDir: root_dir.to_path_buf(), |         RootAppDir: root_dir.to_path_buf(), | ||||||
| @@ -338,8 +350,8 @@ pub fn create_config_from_root_dir<P: AsRef<std::path::Path>>(root_dir: P) -> Ve | |||||||
| } | } | ||||||
|  |  | ||||||
| /// LocationContext is an enumeration of possible contexts for locating the current app manifest. | /// LocationContext is an enumeration of possible contexts for locating the current app manifest. | ||||||
| pub enum LocationContext | #[derive(Debug, Clone)] | ||||||
| { | pub enum LocationContext { | ||||||
|     /// Should not really be used, will try a few other enumerations to locate the app manifest. |     /// Should not really be used, will try a few other enumerations to locate the app manifest. | ||||||
|     Unknown, |     Unknown, | ||||||
|     /// Locates the app manifest by assuming the current process is Update.exe. |     /// Locates the app manifest by assuming the current process is Update.exe. | ||||||
| @@ -352,8 +364,8 @@ pub enum LocationContext | |||||||
|     FromSpecifiedAppExecutable(PathBuf), |     FromSpecifiedAppExecutable(PathBuf), | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(target_os = "windows")] |  | ||||||
| /// Automatically locates the current app's important paths. If the app is not installed, it will return an error. | /// Automatically locates the current app's important paths. If the app is not installed, it will return an error. | ||||||
|  | #[cfg(target_os = "windows")] | ||||||
| pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLocator, Error> { | pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLocator, Error> { | ||||||
|     info!("Auto-locating app manifest..."); |     info!("Auto-locating app manifest..."); | ||||||
|     match context { |     match context { | ||||||
| @@ -441,11 +453,13 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca | |||||||
|     let appimage_path = match std::env::var("APPIMAGE") { |     let appimage_path = match std::env::var("APPIMAGE") { | ||||||
|         Ok(v) => { |         Ok(v) => { | ||||||
|             if v.is_empty() || !PathBuf::from(&v).exists() { |             if v.is_empty() || !PathBuf::from(&v).exists() { | ||||||
|                 return Err(Error::NotInstalled("The 'APPIMAGE' environment variable should point to the current AppImage path.".to_string())); |                 return Err(Error::NotInstalled( | ||||||
|  |                     "The 'APPIMAGE' environment variable should point to the current AppImage path.".to_string(), | ||||||
|  |                 )); | ||||||
|             } else { |             } else { | ||||||
|                 v |                 v | ||||||
|             } |             } | ||||||
|         }, |         } | ||||||
|         Err(_) => { |         Err(_) => { | ||||||
|             return Err(Error::NotInstalled("The 'APPIMAGE' environment variable should point to the current AppImage path.".to_string())); |             return Err(Error::NotInstalled("The 'APPIMAGE' environment variable should point to the current AppImage path.".to_string())); | ||||||
|         } |         } | ||||||
| @@ -517,7 +531,7 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca | |||||||
|  |  | ||||||
| fn read_current_manifest(nuspec_path: &PathBuf) -> Result<Manifest, Error> { | fn read_current_manifest(nuspec_path: &PathBuf) -> Result<Manifest, Error> { | ||||||
|     if nuspec_path.exists() { |     if nuspec_path.exists() { | ||||||
|         if let Ok(nuspec) = util::retry_io(|| std::fs::read_to_string(nuspec_path)) { |         if let Ok(nuspec) = misc::retry_io(|| std::fs::read_to_string(nuspec_path)) { | ||||||
|             return bundle::read_manifest_from_string(&nuspec); |             return bundle::read_manifest_from_string(&nuspec); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -553,7 +567,7 @@ fn test_locator_staged_id_for_new_user() { | |||||||
|     //Create new locator with paths to a test directory |     //Create new locator with paths to a test directory | ||||||
|     let tmp_dir = tempfile::TempDir::new().unwrap(); |     let tmp_dir = tempfile::TempDir::new().unwrap(); | ||||||
|     let tmp_buf = tmp_dir.path().to_path_buf(); |     let tmp_buf = tmp_dir.path().to_path_buf(); | ||||||
|     let test_dir = tmp_buf.join(format!("velopack_{}", util::random_string(8))); |     let test_dir = tmp_buf.join(format!("velopack_{}", misc::random_string(8))); | ||||||
|  |  | ||||||
|     let mut paths = VelopackLocatorConfig::default(); |     let mut paths = VelopackLocatorConfig::default(); | ||||||
|     paths.PackagesDir = test_dir; |     paths.PackagesDir = test_dir; | ||||||
| @@ -580,7 +594,7 @@ fn test_locator_staged_id_for_new_user() { | |||||||
| fn test_locator_staged_id_for_existing_user() { | fn test_locator_staged_id_for_existing_user() { | ||||||
|     let tmp_dir = tempfile::TempDir::new().unwrap(); |     let tmp_dir = tempfile::TempDir::new().unwrap(); | ||||||
|     let tmp_buf = tmp_dir.path().to_path_buf(); |     let tmp_buf = tmp_dir.path().to_path_buf(); | ||||||
|     let test_dir = tmp_buf.join(format!("velopack_{}", util::random_string(8))); |     let test_dir = tmp_buf.join(format!("velopack_{}", misc::random_string(8))); | ||||||
|  |  | ||||||
|     let mut paths = VelopackLocatorConfig::default(); |     let mut paths = VelopackLocatorConfig::default(); | ||||||
|     paths.PackagesDir = test_dir; |     paths.PackagesDir = test_dir; | ||||||
|   | |||||||
| @@ -506,12 +506,15 @@ impl UpdateManager { | |||||||
|         if silent { |         if silent { | ||||||
|             args.push("--silent".to_string()); |             args.push("--silent".to_string()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if !restart { |         if !restart { | ||||||
|             args.push("--norestart".to_string()); |             args.push("--norestart".to_string()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let restart_args: Vec<String> = restart_args.into_iter().map(|item| item.as_ref().to_string()).collect(); |         args.push("--root".to_string()); | ||||||
|  |         args.push(self.locator.get_root_dir_as_string()); | ||||||
|  |  | ||||||
|  |         let restart_args: Vec<String> = restart_args.into_iter().map(|item| item.as_ref().to_string()).collect(); | ||||||
|         if !restart_args.is_empty() { |         if !restart_args.is_empty() { | ||||||
|             args.push("--".to_string()); |             args.push("--".to_string()); | ||||||
|             for arg in restart_args { |             for arg in restart_args { | ||||||
|   | |||||||
| @@ -14,8 +14,9 @@ use windows::{ | |||||||
|         Security::{GetTokenInformation, TokenElevation, TOKEN_ELEVATION}, |         Security::{GetTokenInformation, TokenElevation, TOKEN_ELEVATION}, | ||||||
|         System::Threading::{ |         System::Threading::{ | ||||||
|             CreateProcessW, GetCurrentProcess, GetExitCodeProcess, GetProcessId, GetProcessTimes, OpenProcess, OpenProcessToken, |             CreateProcessW, GetCurrentProcess, GetExitCodeProcess, GetProcessId, GetProcessTimes, OpenProcess, OpenProcessToken, | ||||||
|             TerminateProcess, WaitForSingleObject, CREATE_NO_WINDOW, PROCESS_ACCESS_RIGHTS, PROCESS_BASIC_INFORMATION, |             TerminateProcess, WaitForSingleObject, CREATE_NO_WINDOW, CREATE_UNICODE_ENVIRONMENT, PROCESS_ACCESS_RIGHTS, | ||||||
|             PROCESS_CREATION_FLAGS, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SYNCHRONIZE, PROCESS_TERMINATE, |             PROCESS_BASIC_INFORMATION, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SYNCHRONIZE, PROCESS_TERMINATE, STARTUPINFOW, | ||||||
|  |             STARTUPINFOW_FLAGS, | ||||||
|         }, |         }, | ||||||
|         UI::{ |         UI::{ | ||||||
|             Shell::{ShellExecuteExW, SEE_MASK_NOCLOSEPROCESS, SHELLEXECUTEINFOW}, |             Shell::{ShellExecuteExW, SEE_MASK_NOCLOSEPROCESS, SHELLEXECUTEINFOW}, | ||||||
| @@ -51,7 +52,14 @@ fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> IoResult<T> { | |||||||
|  |  | ||||||
| fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> IoResult<()> { | fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> IoResult<()> { | ||||||
|     let (arg, quote) = match arg { |     let (arg, quote) = match arg { | ||||||
|         Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }), |         Arg::Regular(arg) => ( | ||||||
|  |             arg, | ||||||
|  |             if force_quotes { | ||||||
|  |                 Quote::Always | ||||||
|  |             } else { | ||||||
|  |                 Quote::Auto | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|         Arg::Raw(arg) => (arg, Quote::Never), |         Arg::Raw(arg) => (arg, Quote::Never), | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| @@ -206,26 +214,23 @@ impl SafeProcessHandle { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Into<u32> for SafeProcessHandle { | impl AsRef<HANDLE> for SafeProcessHandle { | ||||||
|     fn into(self) -> u32 { |     fn as_ref(&self) -> &HANDLE { | ||||||
|         self.pid() |         &self.handle | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Into<HANDLE> for SafeProcessHandle { | // impl Into<u32> for SafeProcessHandle { | ||||||
|     fn into(self) -> HANDLE { | //     fn into(self) -> u32 { | ||||||
|         self.handle() | //         self.pid() | ||||||
|     } | //     } | ||||||
| } | // } | ||||||
|  |  | ||||||
| fn open_pid_safe( | // impl Into<HANDLE> for SafeProcessHandle { | ||||||
|     dwdesiredaccess: PROCESS_ACCESS_RIGHTS, | //     fn into(self) -> HANDLE { | ||||||
|     binherithandle: bool, | //         self.handle() | ||||||
|     dwprocessid: u32, | //     } | ||||||
| ) -> windows::core::Result<SafeProcessHandle> { | // } | ||||||
|     let handle = unsafe { OpenProcess(dwdesiredaccess, binherithandle, dwprocessid)? }; |  | ||||||
|     return Ok(SafeProcessHandle { handle, pid: dwprocessid }); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn os_to_pcwstr<P: AsRef<OsStr>>(d: P) -> IoResult<(PCWSTR, Vec<u16>)> { | fn os_to_pcwstr<P: AsRef<OsStr>>(d: P) -> IoResult<(PCWSTR, Vec<u16>)> { | ||||||
|     let d = d.as_ref(); |     let d = d.as_ref(); | ||||||
| @@ -258,8 +263,11 @@ pub fn run_process_as_admin<P1: AsRef<Path>, P2: AsRef<Path>>( | |||||||
|     let params = PCWSTR(params.as_ptr()); |     let params = PCWSTR(params.as_ptr()); | ||||||
|     let work_dir = pathopt_to_pcwstr(work_dir.as_ref())?; |     let work_dir = pathopt_to_pcwstr(work_dir.as_ref())?; | ||||||
|  |  | ||||||
|     let n_show = |     let n_show = if show_window { | ||||||
|         if show_window { windows::Win32::UI::WindowsAndMessaging::SW_NORMAL.0 } else { windows::Win32::UI::WindowsAndMessaging::SW_HIDE.0 }; |         windows::Win32::UI::WindowsAndMessaging::SW_NORMAL.0 | ||||||
|  |     } else { | ||||||
|  |         windows::Win32::UI::WindowsAndMessaging::SW_HIDE.0 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     let mut exe_info: SHELLEXECUTEINFOW = SHELLEXECUTEINFOW { |     let mut exe_info: SHELLEXECUTEINFOW = SHELLEXECUTEINFOW { | ||||||
|         cbSize: std::mem::size_of::<SHELLEXECUTEINFOW>() as u32, |         cbSize: std::mem::size_of::<SHELLEXECUTEINFOW>() as u32, | ||||||
| @@ -290,7 +298,9 @@ pub fn run_process<P1: AsRef<Path>, P2: AsRef<Path>>( | |||||||
| ) -> IoResult<SafeProcessHandle> { | ) -> IoResult<SafeProcessHandle> { | ||||||
|     let exe_path = exe_path.as_ref(); |     let exe_path = exe_path.as_ref(); | ||||||
|     let exe_path = OsString::from(exe_path); |     let exe_path = OsString::from(exe_path); | ||||||
|     let exe_name = PCWSTR(exe_path.encode_wide().chain(Some(0)).collect::<Vec<_>>().as_mut_ptr()); |     let exe_name_ptr = os_to_pcwstr(&exe_path)?; | ||||||
|  |  | ||||||
|  |     let work_dir = work_dir.map(|d| d.as_ref().to_path_buf()); | ||||||
|  |  | ||||||
|     let wrapped_args: Vec<Arg> = args.iter().map(|a| Arg::Regular(a.into())).collect(); |     let wrapped_args: Vec<Arg> = args.iter().map(|a| Arg::Regular(a.into())).collect(); | ||||||
|     let mut params = make_command_line(Some(&exe_path), &wrapped_args, false)?; |     let mut params = make_command_line(Some(&exe_path), &wrapped_args, false)?; | ||||||
| @@ -298,35 +308,39 @@ pub fn run_process<P1: AsRef<Path>, P2: AsRef<Path>>( | |||||||
|  |  | ||||||
|     let mut pi = windows::Win32::System::Threading::PROCESS_INFORMATION::default(); |     let mut pi = windows::Win32::System::Threading::PROCESS_INFORMATION::default(); | ||||||
|  |  | ||||||
|     // let si = STARTUPINFOW { |     let si = STARTUPINFOW { | ||||||
|     //     cb: std::mem::size_of::<STARTUPINFOW>() as u32, |         cb: std::mem::size_of::<STARTUPINFOW>() as u32, | ||||||
|     //     lpReserved: PWSTR::null(), |         lpReserved: PWSTR::null(), | ||||||
|     //     lpDesktop: PWSTR::null(), |         lpDesktop: PWSTR::null(), | ||||||
|     //     lpTitle: PWSTR::null(), |         lpTitle: PWSTR::null(), | ||||||
|     //     dwX: 0, |         dwX: 0, | ||||||
|     //     dwY: 0, |         dwY: 0, | ||||||
|     //     dwXSize: 0, |         dwXSize: 0, | ||||||
|     //     dwYSize: 0, |         dwYSize: 0, | ||||||
|     //     dwXCountChars: 0, |         dwXCountChars: 0, | ||||||
|     //     dwYCountChars: 0, |         dwYCountChars: 0, | ||||||
|     //     dwFillAttribute: 0, |         dwFillAttribute: 0, | ||||||
|     //     dwFlags: STARTUPINFOW_FLAGS(0), |         dwFlags: STARTUPINFOW_FLAGS(0), | ||||||
|     //     wShowWindow: 0, |         wShowWindow: 0, | ||||||
|     //     cbReserved2: 0, |         cbReserved2: 0, | ||||||
|     //     lpReserved2: std::ptr::null_mut(), |         lpReserved2: std::ptr::null_mut(), | ||||||
|     //     hStdInput: HANDLE(std::ptr::null_mut()), |         hStdInput: HANDLE(std::ptr::null_mut()), | ||||||
|     //     hStdOutput: HANDLE(std::ptr::null_mut()), |         hStdOutput: HANDLE(std::ptr::null_mut()), | ||||||
|     //     hStdError: HANDLE(std::ptr::null_mut()), |         hStdError: HANDLE(std::ptr::null_mut()), | ||||||
|     // }; |     }; | ||||||
|  |  | ||||||
|     let envp = make_envp(set_env)?; |     let envp = make_envp(set_env)?; | ||||||
|     let dirp = pathopt_to_pcwstr(work_dir)?; |     let dirp = pathopt_to_pcwstr(work_dir.as_deref())?; | ||||||
|  |  | ||||||
|     let flags = if show_window { PROCESS_CREATION_FLAGS(0) } else { CREATE_NO_WINDOW }; |     let flags = if show_window { | ||||||
|  |         CREATE_UNICODE_ENVIRONMENT | ||||||
|  |     } else { | ||||||
|  |         CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     unsafe { |     unsafe { | ||||||
|         info!("About to launch: '{:?}' in dir '{:?}' with arguments: {:?}", exe_name, dirp, args); |         info!("About to launch: '{:?}' in dir '{:?}' with arguments: {:?}", exe_path, work_dir, args); | ||||||
|         CreateProcessW(exe_name, Option::Some(params), None, None, false, flags, envp.0, dirp.0, std::ptr::null(), &mut pi)?; |         CreateProcessW(exe_name_ptr.0, Option::Some(params), None, None, false, flags, envp.0, dirp.0, &si, &mut pi)?; | ||||||
|         let _ = AllowSetForegroundWindow(pi.dwProcessId); |         let _ = AllowSetForegroundWindow(pi.dwProcessId); | ||||||
|         let _ = CloseHandle(pi.hThread); |         let _ = CloseHandle(pi.hThread); | ||||||
|     } |     } | ||||||
| @@ -347,31 +361,41 @@ fn duration_to_ms(dur: Duration) -> u32 { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn kill_process<T: Into<HANDLE>>(process: T) -> IoResult<()> { | pub fn kill_process<T: AsRef<HANDLE>>(process: T) -> IoResult<()> { | ||||||
|     let process = process.into(); |     let process = process.as_ref(); | ||||||
|     unsafe { |     unsafe { | ||||||
|         if process.is_invalid() { |         if process.is_invalid() { | ||||||
|             return Ok(()); |             return Ok(()); | ||||||
|         } |         } | ||||||
|         TerminateProcess(process, 1)?; |         TerminateProcess(*process, 1)?; | ||||||
|     } |     } | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub fn open_process( | ||||||
|  |     dwdesiredaccess: PROCESS_ACCESS_RIGHTS, | ||||||
|  |     binherithandle: bool, | ||||||
|  |     dwprocessid: u32, | ||||||
|  | ) -> windows::core::Result<SafeProcessHandle> { | ||||||
|  |     let handle = unsafe { OpenProcess(dwdesiredaccess, binherithandle, dwprocessid)? }; | ||||||
|  |     return Ok(SafeProcessHandle { handle, pid: dwprocessid }); | ||||||
|  | } | ||||||
|  |  | ||||||
| pub fn kill_pid(pid: u32) -> IoResult<()> { | pub fn kill_pid(pid: u32) -> IoResult<()> { | ||||||
|     let handle = open_pid_safe(PROCESS_TERMINATE, false, pid)?; |     let handle = open_process(PROCESS_TERMINATE, false, pid)?; | ||||||
|     kill_process(handle)?; |     kill_process(handle)?; | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
| pub enum WaitResult { | pub enum WaitResult { | ||||||
|     WaitTimeout, |     WaitTimeout, | ||||||
|     ExitCode(u32), |     ExitCode(u32), | ||||||
|     NoWaitRequired, |     NoWaitRequired, | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn wait_for_process_to_exit_with_timeout<T: Into<HANDLE>>(process: T, dur: Duration) -> IoResult<WaitResult> { | pub fn wait_for_process_to_exit_with_timeout<T: AsRef<HANDLE>>(process: T, dur: Duration) -> IoResult<WaitResult> { | ||||||
|     let process = process.into(); |     let process = *process.as_ref(); | ||||||
|     if process.is_invalid() { |     if process.is_invalid() { | ||||||
|         return Ok(WaitResult::NoWaitRequired); |         return Ok(WaitResult::NoWaitRequired); | ||||||
|     } |     } | ||||||
| @@ -394,7 +418,7 @@ pub fn wait_for_process_to_exit_with_timeout<T: Into<HANDLE>>(process: T, dur: D | |||||||
|  |  | ||||||
| pub fn wait_for_pid_to_exit(pid: u32, dur: Duration) -> IoResult<WaitResult> { | pub fn wait_for_pid_to_exit(pid: u32, dur: Duration) -> IoResult<WaitResult> { | ||||||
|     info!("Waiting for process pid-{} to exit.", pid); |     info!("Waiting for process pid-{} to exit.", pid); | ||||||
|     let handle = open_pid_safe(PROCESS_SYNCHRONIZE, false, pid)?; |     let handle = open_process(PROCESS_SYNCHRONIZE, false, pid)?; | ||||||
|     wait_for_process_to_exit_with_timeout(handle, dur) |     wait_for_process_to_exit_with_timeout(handle, dur) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -439,7 +463,7 @@ pub fn wait_for_parent_to_exit(dur: Duration) -> IoResult<WaitResult> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     let permissions = PROCESS_SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION; |     let permissions = PROCESS_SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION; | ||||||
|     let parent_handle = open_pid_safe(permissions, false, info.InheritedFromUniqueProcessId as u32)?; |     let parent_handle = open_process(permissions, false, info.InheritedFromUniqueProcessId as u32)?; | ||||||
|     let parent_start_time = get_pid_start_time(parent_handle.handle())?; |     let parent_start_time = get_pid_start_time(parent_handle.handle())?; | ||||||
|     let myself_start_time = get_pid_start_time(my_handle)?; |     let myself_start_time = get_pid_start_time(my_handle)?; | ||||||
|  |  | ||||||
| @@ -455,3 +479,13 @@ pub fn wait_for_parent_to_exit(dur: Duration) -> IoResult<WaitResult> { | |||||||
|     info!("Waiting for parent process ({}) to exit.", info.InheritedFromUniqueProcessId); |     info!("Waiting for parent process ({}) to exit.", info.InheritedFromUniqueProcessId); | ||||||
|     wait_for_process_to_exit_with_timeout(parent_handle, dur) |     wait_for_process_to_exit_with_timeout(parent_handle, dur) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_kill_process() { | ||||||
|  |     let cmd = | ||||||
|  |         std::process::Command::new("cmd.exe").arg("/C").arg("ping").arg("8.8.8.8").arg("-t").spawn().expect("failed to start process"); | ||||||
|  |  | ||||||
|  |     let pid = cmd.id(); | ||||||
|  |  | ||||||
|  |     kill_pid(pid).expect("failed to kill process"); | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user