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_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
|
"sysinfo",
|
||||||
"tempdir",
|
"tempdir",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"widestring",
|
"widestring",
|
||||||
|
@ -1395,9 +1396,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.101"
|
version = "0.2.126"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
|
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libdbus-sys"
|
name = "libdbus-sys"
|
||||||
|
@ -2191,6 +2192,30 @@ dependencies = [
|
||||||
"rand_core 0.5.1",
|
"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]]
|
[[package]]
|
||||||
name = "rdrand"
|
name = "rdrand"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -2607,6 +2632,21 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"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]]
|
[[package]]
|
||||||
name = "tempdir"
|
name = "tempdir"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
|
|
|
@ -56,6 +56,7 @@ colored = "2.0.0"
|
||||||
tempdir = "0.3.7"
|
tempdir = "0.3.7"
|
||||||
notify = "4.0.17"
|
notify = "4.0.17"
|
||||||
opener = "0.5.0"
|
opener = "0.5.0"
|
||||||
|
sysinfo = "0.24.5"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
named_pipe = "0.4.1"
|
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!("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
|
SERVICE_TIMED_OUT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,18 @@
|
||||||
|
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use log::error;
|
use log::error;
|
||||||
|
use std::process::Command;
|
||||||
use std::{path::Path, time::Instant};
|
use std::{path::Path, time::Instant};
|
||||||
|
use sysinfo::{PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use espanso_ipc::IPCClient;
|
use espanso_ipc::IPCClient;
|
||||||
|
|
||||||
|
use crate::info_println;
|
||||||
use crate::{
|
use crate::{
|
||||||
ipc::{create_ipc_client_to_worker, IPCEvent},
|
ipc::{create_ipc_client_to_worker, IPCEvent},
|
||||||
lock::acquire_worker_lock,
|
lock::acquire_worker_lock,
|
||||||
|
warn_eprintln,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn terminate_worker(runtime_dir: &Path) -> Result<()> {
|
pub fn terminate_worker(runtime_dir: &Path) -> Result<()> {
|
||||||
|
@ -46,14 +50,18 @@ pub fn terminate_worker(runtime_dir: &Path) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let now = Instant::now();
|
if wait_for_worker_to_be_stopped(runtime_dir) {
|
||||||
while now.elapsed() < std::time::Duration::from_secs(3) {
|
|
||||||
let lock_file = acquire_worker_lock(runtime_dir);
|
|
||||||
if lock_file.is_some() {
|
|
||||||
return Ok(());
|
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())
|
Err(StopError::WorkerTimedOut.into())
|
||||||
|
@ -67,3 +75,47 @@ pub enum StopError {
|
||||||
#[error("ipc error: `{0}`")]
|
#[error("ipc error: `{0}`")]
|
||||||
IPCError(Error),
|
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::{
|
use crate::{
|
||||||
cli::{LogMode, PathsOverrides},
|
cli::{LogMode, PathsOverrides},
|
||||||
config::load_config,
|
config::load_config,
|
||||||
|
util::log_system_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod capabilities;
|
mod capabilities;
|
||||||
|
@ -586,6 +587,7 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#))
|
||||||
info!("reading configs from: {:?}", paths.config);
|
info!("reading configs from: {:?}", paths.config);
|
||||||
info!("reading packages from: {:?}", paths.packages);
|
info!("reading packages from: {:?}", paths.packages);
|
||||||
info!("using runtime dir: {:?}", paths.runtime);
|
info!("using runtime dir: {:?}", paths.runtime);
|
||||||
|
log_system_info();
|
||||||
|
|
||||||
if handler.requires_config {
|
if handler.requires_config {
|
||||||
let config_result =
|
let config_result =
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use log::info;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use sysinfo::{System, SystemExt};
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub fn set_command_flags(command: &mut Command) {
|
pub fn set_command_flags(command: &mut Command) {
|
||||||
|
@ -43,3 +45,13 @@ pub fn attach_console() {
|
||||||
pub fn attach_console() {
|
pub fn attach_console() {
|
||||||
// Not necessary on Linux and macOS
|
// 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