From 22ba3a5e036bda3b6505ea1a35a27682522e7e67 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sun, 16 May 2021 16:55:40 +0200 Subject: [PATCH] feat(core): prevent multiple daemon and worker instances --- Cargo.lock | 11 ++++++ espanso/Cargo.toml | 3 +- espanso/src/cli/daemon/mod.rs | 24 +++++++++--- espanso/src/cli/worker/mod.rs | 15 +++++++- espanso/src/lock.rs | 72 +++++++++++++++++++++++++++++++++++ espanso/src/main.rs | 3 +- 6 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 espanso/src/lock.rs diff --git a/Cargo.lock b/Cargo.lock index eda3076..9ab99c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -311,6 +311,7 @@ dependencies = [ "espanso-path", "espanso-render", "espanso-ui", + "fs2", "html2text", "lazy_static", "log", @@ -484,6 +485,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2" +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" diff --git a/espanso/Cargo.toml b/espanso/Cargo.toml index 9c0762c..3850c00 100644 --- a/espanso/Cargo.toml +++ b/espanso/Cargo.toml @@ -37,4 +37,5 @@ serde = { version = "1.0.123", features = ["derive"] } serde_json = "1.0.62" markdown = "0.3.0" html2text = "0.2.1" -log-panics = "2.0.0" \ No newline at end of file +log-panics = "2.0.0" +fs2 = "0.4.3" \ No newline at end of file diff --git a/espanso/src/cli/daemon/mod.rs b/espanso/src/cli/daemon/mod.rs index bb024a4..cc83226 100644 --- a/espanso/src/cli/daemon/mod.rs +++ b/espanso/src/cli/daemon/mod.rs @@ -19,7 +19,9 @@ use std::process::Command; -use log::info; +use log::{error, info}; + +use crate::lock::acquire_daemon_lock; use super::{CliModule, CliModuleArgs}; @@ -41,16 +43,20 @@ const VERSION: &str = env!("CARGO_PKG_VERSION"); fn daemon_main(args: CliModuleArgs) { let paths = args.paths.expect("missing paths in daemon main"); + // Make sure only one instance of the daemon is running + let lock_file = acquire_daemon_lock(&paths.runtime); + if lock_file.is_none() { + error!("daemon is already running!"); + std::process::exit(1); + } + info!("espanso version: {}", VERSION); // TODO: print os system and version? (with os_info crate) - // TODO: check daemon lock file to avoid duplicates // TODO: check worker lock file, if taken stop the worker process through IPC - // TODO: start IPC server - - // TODO: start file watcher thread + // TODO: register signals to terminate the worker if the daemon terminates let espanso_exe_path = std::env::current_exe().expect("unable to obtain espanso executable location"); @@ -71,4 +77,12 @@ fn daemon_main(args: CliModuleArgs) { } command.spawn().expect("unable to spawn worker process"); + + // TODO: start IPC server + + // TODO: start file watcher thread + + loop { + std::thread::sleep(std::time::Duration::from_millis(1000)); + } } diff --git a/espanso/src/cli/worker/mod.rs b/espanso/src/cli/worker/mod.rs index 8ad11d0..ce39055 100644 --- a/espanso/src/cli/worker/mod.rs +++ b/espanso/src/cli/worker/mod.rs @@ -17,6 +17,10 @@ * along with espanso. If not, see . */ +use log::error; + +use crate::lock::acquire_worker_lock; + use self::ui::util::convert_icon_paths_to_tray_vec; use super::{CliModule, CliModuleArgs}; @@ -39,6 +43,15 @@ pub fn new() -> CliModule { } fn worker_main(args: CliModuleArgs) { + let paths = args.paths.expect("missing paths in worker main"); + + // Avoid running multiple worker instances + let lock_file = acquire_worker_lock(&paths.runtime); + if lock_file.is_none() { + error!("worker is already running!"); + std::process::exit(1); + } + let config_store = args .config_store .expect("missing config store in worker main"); @@ -46,8 +59,6 @@ fn worker_main(args: CliModuleArgs) { .match_store .expect("missing match store in worker main"); - let paths = args.paths.expect("missing paths in worker main"); - let icon_paths = self::ui::icon::load_icon_paths(&paths.runtime).expect("unable to initialize icons"); diff --git a/espanso/src/lock.rs b/espanso/src/lock.rs new file mode 100644 index 0000000..ea85613 --- /dev/null +++ b/espanso/src/lock.rs @@ -0,0 +1,72 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use anyhow::Result; +use fs2::FileExt; +use std::{ + fs::{File, OpenOptions}, + path::Path, +}; + +pub struct Lock { + lock_file: File, +} + +impl Lock { + pub fn release(self) -> Result<()> { + self.lock_file.unlock()?; + Ok(()) + } + + fn acquire(runtime_dir: &Path, name: &str) -> Option { + let lock_file_path = runtime_dir.join(format!("{}.lock", name)); + let lock_file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&lock_file_path) + .expect(&format!( + "unable to create reference to lock file: {:?}", + lock_file_path + )); + + if lock_file.try_lock_exclusive().is_ok() { + Some(Lock { lock_file }) + } else { + None + } + } +} + +impl Drop for Lock { + fn drop(&mut self) { + self + .lock_file + .unlock() + .expect(&format!("unable to unlock lock_file: {:?}", self.lock_file)); + } +} + +pub fn acquire_daemon_lock(runtime_dir: &Path) -> Option { + Lock::acquire(runtime_dir, "espanso-daemon.lock") +} + +pub fn acquire_worker_lock(runtime_dir: &Path) -> Option { + Lock::acquire(runtime_dir, "espanso-worker.lock") +} diff --git a/espanso/src/main.rs b/espanso/src/main.rs index d09c168..2e9a25d 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -35,6 +35,7 @@ use crate::cli::LogMode; mod cli; mod engine; mod gui; +mod lock; mod logging; mod util; @@ -253,7 +254,7 @@ fn main() { if handler.enable_logs { let config = ConfigBuilder::new() .set_time_to_local(true) - .set_time_format(format!("%H:%M:%S [{}]", handler.subcommand)) + .set_time_format(format!("%H:%M:%S [{}({})]", handler.subcommand, std::process::id())) .set_location_level(LevelFilter::Off) .add_filter_ignore_str("html5ever") .build();