Watch non-canonical path for config symlinks

To make it possible to detect the replacement of the configuration file
when it is a symlink, the symlinks path has to be observed in addition
to the canonicalized path. That way changes to either file will trigger
a live config reload.

Multiple layers of symlinks would still not get detected when any
symlink other than the configuration file itself is replaced, but this
patch should cover most realistic usage scenarios.
This commit is contained in:
Naïm Favier 2021-08-27 23:30:39 +02:00 committed by GitHub
parent e6565f1b76
commit 70d3b4ef6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 17 additions and 21 deletions

View File

@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `ExpandSelection` is now a configurable mouse binding action
- Config option `background_opacity`, you should use `window.opacity` instead
- Reload configuration files when their symbolic link is replaced
## 0.9.0

View File

@ -1,4 +1,3 @@
use std::fs;
use std::path::PathBuf;
use std::sync::mpsc;
use std::time::Duration;
@ -16,23 +15,21 @@ const DEBOUNCE_DELAY: Duration = Duration::from_millis(10);
const DEBOUNCE_DELAY: Duration = Duration::from_millis(1000);
pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventProxy) {
// Canonicalize all paths, filtering out the ones that do not exist.
paths = paths
.drain(..)
.filter_map(|path| match fs::canonicalize(&path) {
Ok(path) => Some(path),
Err(err) => {
error!("Unable to canonicalize config path {:?}: {}", path, err);
None
},
})
.collect();
// Don't monitor config if there is no path to watch.
if paths.is_empty() {
return;
}
// Canonicalize paths, keeping the base paths for symlinks.
for i in 0..paths.len() {
if let Ok(canonical_path) = paths[i].canonicalize() {
match paths[i].symlink_metadata() {
Ok(metadata) if metadata.file_type().is_symlink() => paths.push(canonical_path),
_ => paths[i] = canonical_path,
}
}
}
// The Duration argument is a debouncing period.
let (tx, rx) = mpsc::channel();
let mut watcher = match watcher(tx, DEBOUNCE_DELAY) {
@ -73,17 +70,15 @@ pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventProxy) {
};
match event {
DebouncedEvent::Rename(..) => continue,
DebouncedEvent::Write(path)
DebouncedEvent::Rename(_, path)
| DebouncedEvent::Write(path)
| DebouncedEvent::Create(path)
| DebouncedEvent::Chmod(path) => {
if !paths.contains(&path) {
continue;
}
| DebouncedEvent::Chmod(path)
if paths.contains(&path) =>
{
// Always reload the primary configuration file.
event_proxy.send_event(Event::ConfigReload(paths[0].clone()));
},
}
_ => {},
}
}