feat(core): improve 'espanso stop' by handling non-graceful termination (#1281)
* feat(core): add workaround to forcefully stop Espanso if needed. #929 * feat(core): log system info to make debugging easier
This commit is contained in:
		
							parent
							
								
									abf31616c3
								
							
						
					
					
						commit
						4d0cc7a6f1
					
				
							
								
								
									
										44
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										44
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -624,6 +624,7 @@ dependencies = [
 | 
			
		|||
 "serde_json",
 | 
			
		||||
 "serde_yaml",
 | 
			
		||||
 "simplelog",
 | 
			
		||||
 "sysinfo",
 | 
			
		||||
 "tempdir",
 | 
			
		||||
 "thiserror",
 | 
			
		||||
 "widestring",
 | 
			
		||||
| 
						 | 
				
			
			@ -1395,9 +1396,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 | 
			
		|||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libc"
 | 
			
		||||
version = "0.2.101"
 | 
			
		||||
version = "0.2.126"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
 | 
			
		||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libdbus-sys"
 | 
			
		||||
| 
						 | 
				
			
			@ -2191,6 +2192,30 @@ dependencies = [
 | 
			
		|||
 "rand_core 0.5.1",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rayon"
 | 
			
		||||
version = "1.5.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "autocfg",
 | 
			
		||||
 "crossbeam-deque",
 | 
			
		||||
 "either",
 | 
			
		||||
 "rayon-core",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rayon-core"
 | 
			
		||||
version = "1.9.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crossbeam-channel",
 | 
			
		||||
 "crossbeam-deque",
 | 
			
		||||
 "crossbeam-utils",
 | 
			
		||||
 "num_cpus",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rdrand"
 | 
			
		||||
version = "0.4.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -2607,6 +2632,21 @@ dependencies = [
 | 
			
		|||
 "winapi 0.3.9",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "sysinfo"
 | 
			
		||||
version = "0.24.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7d80929a3b477bce3a64360ca82bfb361eacce1dcb7b1fb31e8e5e181e37c212"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if 1.0.0",
 | 
			
		||||
 "core-foundation-sys",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "ntapi",
 | 
			
		||||
 "once_cell",
 | 
			
		||||
 "rayon",
 | 
			
		||||
 "winapi 0.3.9",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "tempdir"
 | 
			
		||||
version = "0.3.7"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,6 +56,7 @@ colored = "2.0.0"
 | 
			
		|||
tempdir = "0.3.7"
 | 
			
		||||
notify = "4.0.17"
 | 
			
		||||
opener = "0.5.0"
 | 
			
		||||
sysinfo = "0.24.5"
 | 
			
		||||
 | 
			
		||||
[target.'cfg(windows)'.dependencies]
 | 
			
		||||
named_pipe = "0.4.1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -152,6 +152,14 @@ fn start_main(paths: &Paths, _paths_overrides: &PathsOverrides, args: &ArgMatche
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  error_eprintln!("unable to start service: timed out");
 | 
			
		||||
 | 
			
		||||
  error_eprintln!(
 | 
			
		||||
    "Hint: sometimes this happens because another Espanso process is left running for some reason."
 | 
			
		||||
  );
 | 
			
		||||
  error_eprintln!(
 | 
			
		||||
    "      Please try running 'espanso restart' or manually killing all Espanso processes, then try again."
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  SERVICE_TIMED_OUT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,14 +19,18 @@
 | 
			
		|||
 | 
			
		||||
use anyhow::{Error, Result};
 | 
			
		||||
use log::error;
 | 
			
		||||
use std::process::Command;
 | 
			
		||||
use std::{path::Path, time::Instant};
 | 
			
		||||
use sysinfo::{PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt};
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
 | 
			
		||||
use espanso_ipc::IPCClient;
 | 
			
		||||
 | 
			
		||||
use crate::info_println;
 | 
			
		||||
use crate::{
 | 
			
		||||
  ipc::{create_ipc_client_to_worker, IPCEvent},
 | 
			
		||||
  lock::acquire_worker_lock,
 | 
			
		||||
  warn_eprintln,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub fn terminate_worker(runtime_dir: &Path) -> Result<()> {
 | 
			
		||||
| 
						 | 
				
			
			@ -46,14 +50,18 @@ pub fn terminate_worker(runtime_dir: &Path) -> Result<()> {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let now = Instant::now();
 | 
			
		||||
  while now.elapsed() < std::time::Duration::from_secs(3) {
 | 
			
		||||
    let lock_file = acquire_worker_lock(runtime_dir);
 | 
			
		||||
    if lock_file.is_some() {
 | 
			
		||||
      return Ok(());
 | 
			
		||||
    }
 | 
			
		||||
  if wait_for_worker_to_be_stopped(runtime_dir) {
 | 
			
		||||
    return Ok(());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    std::thread::sleep(std::time::Duration::from_millis(200));
 | 
			
		||||
  warn_eprintln!(
 | 
			
		||||
    "unable to gracefully terminate espanso (timed-out), trying to force the termination..."
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  forcefully_terminate_espanso();
 | 
			
		||||
 | 
			
		||||
  if wait_for_worker_to_be_stopped(runtime_dir) {
 | 
			
		||||
    return Ok(());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Err(StopError::WorkerTimedOut.into())
 | 
			
		||||
| 
						 | 
				
			
			@ -67,3 +75,47 @@ pub enum StopError {
 | 
			
		|||
  #[error("ipc error: `{0}`")]
 | 
			
		||||
  IPCError(Error),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn wait_for_worker_to_be_stopped(runtime_dir: &Path) -> bool {
 | 
			
		||||
  let now = Instant::now();
 | 
			
		||||
  while now.elapsed() < std::time::Duration::from_secs(3) {
 | 
			
		||||
    let lock_file = acquire_worker_lock(runtime_dir);
 | 
			
		||||
    if lock_file.is_some() {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::thread::sleep(std::time::Duration::from_millis(200));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn forcefully_terminate_espanso() {
 | 
			
		||||
  let mut sys =
 | 
			
		||||
    System::new_with_specifics(RefreshKind::new().with_processes(ProcessRefreshKind::new()));
 | 
			
		||||
  sys.refresh_processes_specifics(ProcessRefreshKind::new());
 | 
			
		||||
 | 
			
		||||
  let target_process_names = if cfg!(target_os = "windows") {
 | 
			
		||||
    vec!["espanso.exe", "espansod.exe"]
 | 
			
		||||
  } else {
 | 
			
		||||
    vec!["espanso"]
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let current_pid = std::process::id();
 | 
			
		||||
 | 
			
		||||
  // We want to terminate all Espanso processes except this one
 | 
			
		||||
  for (pid, process) in sys.processes() {
 | 
			
		||||
    if target_process_names.contains(&process.name()) && pid.as_u32() != current_pid {
 | 
			
		||||
      let str_pid = pid.as_u32().to_string();
 | 
			
		||||
      info_println!("killing espanso process with PID: {}", str_pid);
 | 
			
		||||
 | 
			
		||||
      if cfg!(target_os = "windows") {
 | 
			
		||||
        let _ = Command::new("taskkill")
 | 
			
		||||
          .args(&["/pid", &str_pid, "/f"])
 | 
			
		||||
          .output();
 | 
			
		||||
      } else {
 | 
			
		||||
        let _ = Command::new("kill").args(&["-9", &str_pid]).output();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,7 @@ use simplelog::{
 | 
			
		|||
use crate::{
 | 
			
		||||
  cli::{LogMode, PathsOverrides},
 | 
			
		||||
  config::load_config,
 | 
			
		||||
  util::log_system_info,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod capabilities;
 | 
			
		||||
| 
						 | 
				
			
			@ -586,6 +587,7 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#))
 | 
			
		|||
      info!("reading configs from: {:?}", paths.config);
 | 
			
		||||
      info!("reading packages from: {:?}", paths.packages);
 | 
			
		||||
      info!("using runtime dir: {:?}", paths.runtime);
 | 
			
		||||
      log_system_info();
 | 
			
		||||
 | 
			
		||||
      if handler.requires_config {
 | 
			
		||||
        let config_result =
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,9 @@
 | 
			
		|||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use log::info;
 | 
			
		||||
use std::process::Command;
 | 
			
		||||
use sysinfo::{System, SystemExt};
 | 
			
		||||
 | 
			
		||||
#[cfg(target_os = "windows")]
 | 
			
		||||
pub fn set_command_flags(command: &mut Command) {
 | 
			
		||||
| 
						 | 
				
			
			@ -43,3 +45,13 @@ pub fn attach_console() {
 | 
			
		|||
pub fn attach_console() {
 | 
			
		||||
  // Not necessary on Linux and macOS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn log_system_info() {
 | 
			
		||||
  let sys = System::new();
 | 
			
		||||
  info!(
 | 
			
		||||
    "system info: {} v{} - kernel: {}",
 | 
			
		||||
    sys.name().unwrap_or_default(),
 | 
			
		||||
    sys.os_version().unwrap_or_default(),
 | 
			
		||||
    sys.kernel_version().unwrap_or_default()
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user