feat(core): prevent multiple daemon and worker instances
This commit is contained in:
parent
e08bf2f69a
commit
22ba3a5e03
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -311,6 +311,7 @@ dependencies = [
|
||||||
"espanso-path",
|
"espanso-path",
|
||||||
"espanso-render",
|
"espanso-render",
|
||||||
"espanso-ui",
|
"espanso-ui",
|
||||||
|
"fs2",
|
||||||
"html2text",
|
"html2text",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
@ -484,6 +485,16 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2"
|
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]]
|
[[package]]
|
||||||
name = "fuchsia-cprng"
|
name = "fuchsia-cprng"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|
|
@ -38,3 +38,4 @@ serde_json = "1.0.62"
|
||||||
markdown = "0.3.0"
|
markdown = "0.3.0"
|
||||||
html2text = "0.2.1"
|
html2text = "0.2.1"
|
||||||
log-panics = "2.0.0"
|
log-panics = "2.0.0"
|
||||||
|
fs2 = "0.4.3"
|
|
@ -19,7 +19,9 @@
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use log::info;
|
use log::{error, info};
|
||||||
|
|
||||||
|
use crate::lock::acquire_daemon_lock;
|
||||||
|
|
||||||
use super::{CliModule, CliModuleArgs};
|
use super::{CliModule, CliModuleArgs};
|
||||||
|
|
||||||
|
@ -41,16 +43,20 @@ const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
fn daemon_main(args: CliModuleArgs) {
|
fn daemon_main(args: CliModuleArgs) {
|
||||||
let paths = args.paths.expect("missing paths in daemon main");
|
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);
|
info!("espanso version: {}", VERSION);
|
||||||
// TODO: print os system and version? (with os_info crate)
|
// 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: check worker lock file, if taken stop the worker process through IPC
|
||||||
|
|
||||||
// TODO: start IPC server
|
// TODO: register signals to terminate the worker if the daemon terminates
|
||||||
|
|
||||||
// TODO: start file watcher thread
|
|
||||||
|
|
||||||
let espanso_exe_path =
|
let espanso_exe_path =
|
||||||
std::env::current_exe().expect("unable to obtain espanso executable location");
|
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");
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use crate::lock::acquire_worker_lock;
|
||||||
|
|
||||||
use self::ui::util::convert_icon_paths_to_tray_vec;
|
use self::ui::util::convert_icon_paths_to_tray_vec;
|
||||||
|
|
||||||
use super::{CliModule, CliModuleArgs};
|
use super::{CliModule, CliModuleArgs};
|
||||||
|
@ -39,6 +43,15 @@ pub fn new() -> CliModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn worker_main(args: CliModuleArgs) {
|
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
|
let config_store = args
|
||||||
.config_store
|
.config_store
|
||||||
.expect("missing config store in worker main");
|
.expect("missing config store in worker main");
|
||||||
|
@ -46,8 +59,6 @@ fn worker_main(args: CliModuleArgs) {
|
||||||
.match_store
|
.match_store
|
||||||
.expect("missing match store in worker main");
|
.expect("missing match store in worker main");
|
||||||
|
|
||||||
let paths = args.paths.expect("missing paths in worker main");
|
|
||||||
|
|
||||||
let icon_paths =
|
let icon_paths =
|
||||||
self::ui::icon::load_icon_paths(&paths.runtime).expect("unable to initialize icons");
|
self::ui::icon::load_icon_paths(&paths.runtime).expect("unable to initialize icons");
|
||||||
|
|
||||||
|
|
72
espanso/src/lock.rs
Normal file
72
espanso/src/lock.rs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<Lock> {
|
||||||
|
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> {
|
||||||
|
Lock::acquire(runtime_dir, "espanso-daemon.lock")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acquire_worker_lock(runtime_dir: &Path) -> Option<Lock> {
|
||||||
|
Lock::acquire(runtime_dir, "espanso-worker.lock")
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ use crate::cli::LogMode;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod engine;
|
mod engine;
|
||||||
mod gui;
|
mod gui;
|
||||||
|
mod lock;
|
||||||
mod logging;
|
mod logging;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
@ -253,7 +254,7 @@ fn main() {
|
||||||
if handler.enable_logs {
|
if handler.enable_logs {
|
||||||
let config = ConfigBuilder::new()
|
let config = ConfigBuilder::new()
|
||||||
.set_time_to_local(true)
|
.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)
|
.set_location_level(LevelFilter::Off)
|
||||||
.add_filter_ignore_str("html5ever")
|
.add_filter_ignore_str("html5ever")
|
||||||
.build();
|
.build();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user