diff --git a/Cargo.lock b/Cargo.lock index bbbbb0e41..b3a1b70cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2529,6 +2529,7 @@ dependencies = [ "indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/install/Cargo.toml b/install/Cargo.toml index 571a08112..40405b670 100644 --- a/install/Cargo.toml +++ b/install/Cargo.toml @@ -24,6 +24,7 @@ dirs = "2.0.1" indicatif = "0.11.0" lazy_static = "1.3.0" log = "0.4.2" +nix = "0.14.1" reqwest = "0.9.18" ring = "0.13.2" serde = "1.0.94" diff --git a/install/src/command.rs b/install/src/command.rs index a02b34a06..d5d40141b 100644 --- a/install/src/command.rs +++ b/install/src/command.rs @@ -1,4 +1,5 @@ use crate::config::Config; +use crate::stop_process::stop_process; use crate::update_manifest::{SignedUpdateManifest, UpdateManifest}; use chrono::{Local, TimeZone}; use console::{style, Emoji}; @@ -793,8 +794,9 @@ pub fn run( Ok(true) => { // Update successful, kill current process so it will be restart if let Some(ref mut child) = child_option { - let id = child.id(); - println!("Killing pid {}: {:?}", id, child.kill()); + stop_process(child).unwrap_or_else(|err| { + eprintln!("Failed to stop child: {:?}", err); + }); } } Ok(false) => {} // No update available diff --git a/install/src/lib.rs b/install/src/lib.rs index 0fc688248..540470823 100644 --- a/install/src/lib.rs +++ b/install/src/lib.rs @@ -8,6 +8,7 @@ mod build_env; mod command; mod config; mod defaults; +mod stop_process; mod update_manifest; // Return an error if a url cannot be parsed. diff --git a/install/src/stop_process.rs b/install/src/stop_process.rs new file mode 100644 index 000000000..2a888f99d --- /dev/null +++ b/install/src/stop_process.rs @@ -0,0 +1,67 @@ +use std::io; +use std::process::Child; + +fn kill_process(process: &mut Child) -> Result<(), io::Error> { + if let Ok(()) = process.kill() { + process.wait()?; + } else { + println!("Process {} has already exited", process.id()); + } + Ok(()) +} + +#[cfg(windows)] +pub fn stop_process(process: &mut Child) -> Result<(), io::Error> { + kill_process(process) +} + +#[cfg(not(windows))] +pub fn stop_process(process: &mut Child) -> Result<(), io::Error> { + use nix::errno::Errno::{EINVAL, EPERM, ESRCH}; + use nix::sys::signal::{kill, Signal}; + use nix::unistd::Pid; + use nix::Error::Sys; + use std::io::ErrorKind; + use std::thread; + use std::time::{Duration, Instant}; + + let nice_wait = Duration::from_secs(5); + let pid = Pid::from_raw(process.id() as i32); + match kill(pid, Signal::SIGINT) { + Ok(()) => { + let expire = Instant::now() + nice_wait; + while let Ok(None) = process.try_wait() { + if Instant::now() > expire { + break; + } + thread::sleep(nice_wait / 10); + } + if let Ok(None) = process.try_wait() { + kill_process(process)?; + } + } + Err(Sys(EINVAL)) => { + println!("Invalid signal. Killing process {}", pid); + kill_process(process)?; + } + Err(Sys(EPERM)) => { + return Err(io::Error::new( + ErrorKind::InvalidInput, + format!("Insufficient permissions to signal process {}", pid), + )); + } + Err(Sys(ESRCH)) => { + return Err(io::Error::new( + ErrorKind::InvalidInput, + format!("Process {} does not exist", pid), + )); + } + Err(e) => { + return Err(io::Error::new( + ErrorKind::InvalidInput, + format!("Unexpected error {}", e), + )); + } + }; + Ok(()) +}