feat(core): improve troubleshooter behavior on critical errors

This commit is contained in:
Federico Terzi 2021-07-27 22:23:48 +02:00
parent 6dc3f1093d
commit 925a411460
2 changed files with 75 additions and 22 deletions

View File

@ -90,20 +90,26 @@ fn daemon_main(args: CliModuleArgs) -> i32 {
// at a given time.
let mut _current_troubleshoot_guard = None;
let config_store = match troubleshoot::load_config_or_troubleshoot(&paths, &paths_overrides) {
troubleshoot::LoadResult::Correct(load_result) => load_result.config_store,
troubleshoot::LoadResult::Warning(load_result, guard) => {
_current_troubleshoot_guard = guard;
load_result.config_store
}
troubleshoot::LoadResult::Fatal(mut guard) => {
guard
.wait()
.expect("unable to wait for troubleshooting guard");
error!("critical error while loading config");
return DAEMON_FATAL_CONFIG_ERROR;
}
};
let (watcher_notify, watcher_signal) = unbounded::<()>();
watcher::initialize_and_spawn(&paths.config, watcher_notify)
.expect("unable to initialize config watcher thread");
let config_store =
match troubleshoot::load_config_or_troubleshoot_until_config_is_correct_or_abort(
&paths,
&paths_overrides,
watcher_signal.clone(),
) {
Ok((result, guard)) => {
_current_troubleshoot_guard = guard;
result.config_store
}
Err(err) => {
error!("critical error while loading config: {}", err);
return DAEMON_FATAL_CONFIG_ERROR;
}
};
info!("espanso version: {}", VERSION);
// TODO: print os system and version? (with os_info crate)
@ -126,13 +132,6 @@ fn daemon_main(args: CliModuleArgs) -> i32 {
ipc::initialize_and_spawn(&paths.runtime, exit_notify.clone())
.expect("unable to initialize ipc server for daemon");
let (watcher_notify, watcher_signal) = unbounded::<()>();
if config_store.default().auto_restart() {
watcher::initialize_and_spawn(&paths.config, watcher_notify)
.expect("unable to initialize config watcher thread");
}
if cli_args.is_present("show-welcome") {
ui::show_welcome_screen(&preferences);
}
@ -140,6 +139,10 @@ fn daemon_main(args: CliModuleArgs) -> i32 {
loop {
select! {
recv(watcher_signal) -> _ => {
if !config_store.default().auto_restart() {
continue;
}
info!("configuration change detected, restarting worker process...");
// Before killing the previous worker, we make sure there is no fatal error

View File

@ -18,9 +18,13 @@
*/
use std::process::{Child, Command};
use std::time::Duration;
use anyhow::{Result};
use anyhow::{bail, Result};
use crossbeam::channel::Receiver;
use crossbeam::select;
use espanso_path::Paths;
use log::info;
use crate::cli::util::CommandExt;
use crate::cli::PathsOverrides;
@ -47,10 +51,15 @@ impl TroubleshootGuard {
pub fn new(child: Child) -> Self {
Self { child }
}
#[allow(dead_code)]
pub fn wait(&mut self) -> Result<()> {
self.child.wait()?;
Ok(())
}
pub fn try_wait(&mut self) -> Result<bool> {
let result = self.child.try_wait()?;
Ok(result.is_some())
}
}
impl Drop for TroubleshootGuard {
@ -97,3 +106,44 @@ pub fn load_config_or_troubleshoot(paths: &Paths, paths_overrides: &PathsOverrid
}
}
}
pub fn load_config_or_troubleshoot_until_config_is_correct_or_abort(
paths: &Paths,
paths_overrides: &PathsOverrides,
watcher_receiver: Receiver<()>,
) -> Result<(ConfigLoadResult, Option<TroubleshootGuard>)> {
let mut _troubleshoot_guard = None;
loop {
// If the loading process is fatal, we keep showing the troubleshooter until
// either the config is correct or the user aborts by closing the troubleshooter
_troubleshoot_guard = match load_config_or_troubleshoot(paths, paths_overrides) {
LoadResult::Correct(result) => return Ok((result, None)),
LoadResult::Warning(result, guard) => return Ok((result, guard)),
LoadResult::Fatal(guard) => Some(guard),
};
loop {
select! {
recv(watcher_receiver) -> _ => {
info!("config change detected, reloading configs...");
break
},
default(Duration::from_millis(500)) => {
if let Some(guard) = &mut _troubleshoot_guard {
if let Ok(ended) = guard.try_wait() {
if ended {
bail!("user aborted troubleshooter");
}
} else {
bail!("unable to wait for troubleshooter");
}
} else {
bail!("no troubleshoot guard found");
}
}
}
}
}
}