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"
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ max_width = 140
|
|||||||
use_small_heuristics = "Max"
|
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,19 +291,15 @@ 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.
|
||||||
pub fn get_is_portable(&self) -> bool {
|
pub fn get_is_portable(&self) -> bool {
|
||||||
self.paths.IsPortable
|
self.paths.IsPortable
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attemps to open / lock a file in the app's package directory for exclusive write access.
|
/// Attemps to open / lock a file in the app's package directory for exclusive write access.
|
||||||
/// Fails immediately if the lock cannot be acquired.
|
/// Fails immediately if the lock cannot be acquired.
|
||||||
pub fn try_get_exclusive_lock(&self) -> Result<LockFile, Error> {
|
pub fn try_get_exclusive_lock(&self) -> Result<LockFile, Error> {
|
||||||
@@ -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 {
|
||||||
@@ -409,7 +421,7 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Err(Error::NotInstalled("Could not auto-locate app manifest".to_owned()))
|
Err(Error::NotInstalled("Could not auto-locate app manifest".to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,16 +453,18 @@ 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()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let app = read_current_manifest(&metadata_path)?;
|
let app = read_current_manifest(&metadata_path)?;
|
||||||
let packages_dir = PathBuf::from("/var/tmp/velopack").join(&app.id).join("packages");
|
let packages_dir = PathBuf::from("/var/tmp/velopack").join(&app.id).join("packages");
|
||||||
|
|
||||||
@@ -511,13 +525,13 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca
|
|||||||
CurrentBinaryDir: contents_dir,
|
CurrentBinaryDir: contents_dir,
|
||||||
IsPortable: true,
|
IsPortable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(VelopackLocator::new_with_manifest(config, app))
|
Ok(VelopackLocator::new_with_manifest(config, app))
|
||||||
}
|
}
|
||||||
|
|
||||||
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