From 3b3e74462c77b242098d163cc36cd70a040657e0 Mon Sep 17 00:00:00 2001 From: Chip Senkbeil Date: Thu, 18 Aug 2022 02:38:36 -0500 Subject: [PATCH] Fix Docker M1 Mac watcher failure and release v0.17.5 --- CHANGELOG.md | 7 ++ Cargo.lock | 8 +-- Cargo.toml | 6 +- distant-core/Cargo.toml | 4 +- distant-core/src/api/local/state/watcher.rs | 77 ++++++++++++++------- distant-net/Cargo.toml | 2 +- distant-ssh2/Cargo.toml | 4 +- 7 files changed, 70 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d2f6cd..7558ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.17.5] - 2022-08-18 +### Fixed + +- Handle `RecommendedWatcher` failing with an unsupported OS function on M1 Mac + architecture running a Linux container via Docker + ([notify #423](https://github.com/notify-rs/notify/issues/423)) + ## [0.17.4] - 2022-08-18 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index d8fcf76..d7e2433 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -709,7 +709,7 @@ dependencies = [ [[package]] name = "distant" -version = "0.17.3" +version = "0.17.5" dependencies = [ "anyhow", "assert_cmd", @@ -750,7 +750,7 @@ dependencies = [ [[package]] name = "distant-core" -version = "0.17.3" +version = "0.17.5" dependencies = [ "assert_fs", "async-trait", @@ -784,7 +784,7 @@ dependencies = [ [[package]] name = "distant-net" -version = "0.17.3" +version = "0.17.5" dependencies = [ "async-trait", "bytes", @@ -809,7 +809,7 @@ dependencies = [ [[package]] name = "distant-ssh2" -version = "0.17.3" +version = "0.17.5" dependencies = [ "anyhow", "assert_fs", diff --git a/Cargo.toml b/Cargo.toml index 08b8501..a72301a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "distant" description = "Operate on a remote computer through file and process manipulation" categories = ["command-line-utilities"] keywords = ["cli"] -version = "0.17.4" +version = "0.17.5" authors = ["Chip Senkbeil "] edition = "2021" homepage = "https://github.com/chipsenkbeil/distant" @@ -32,7 +32,7 @@ clap_complete = "3.2.3" config = { version = "0.13.2", default-features = false, features = ["toml"] } derive_more = { version = "0.99.17", default-features = false, features = ["display", "from", "error", "is_variant"] } dialoguer = { version = "0.10.2", default-features = false } -distant-core = { version = "=0.17.4", path = "distant-core", features = ["clap", "schemars"] } +distant-core = { version = "=0.17.5", path = "distant-core", features = ["clap", "schemars"] } directories = "4.0.1" flexi_logger = "0.23.0" indoc = "1.0.7" @@ -54,7 +54,7 @@ winsplit = "0.1.0" whoami = "1.2.1" # Optional native SSH functionality -distant-ssh2 = { version = "=0.17.4", path = "distant-ssh2", default-features = false, features = ["serde"], optional = true } +distant-ssh2 = { version = "=0.17.5", path = "distant-ssh2", default-features = false, features = ["serde"], optional = true } [target.'cfg(unix)'.dependencies] fork = "0.1.19" diff --git a/distant-core/Cargo.toml b/distant-core/Cargo.toml index 16d636c..88b1812 100644 --- a/distant-core/Cargo.toml +++ b/distant-core/Cargo.toml @@ -3,7 +3,7 @@ name = "distant-core" description = "Core library for distant, enabling operation on a remote computer through file and process manipulation" categories = ["network-programming"] keywords = ["api", "async"] -version = "0.17.4" +version = "0.17.5" authors = ["Chip Senkbeil "] edition = "2021" homepage = "https://github.com/chipsenkbeil/distant" @@ -19,7 +19,7 @@ async-trait = "0.1.57" bitflags = "1.3.2" bytes = "1.2.1" derive_more = { version = "0.99.17", default-features = false, features = ["as_mut", "as_ref", "deref", "deref_mut", "display", "from", "error", "into", "into_iterator", "is_variant", "try_into"] } -distant-net = { version = "=0.17.4", path = "../distant-net" } +distant-net = { version = "=0.17.5", path = "../distant-net" } futures = "0.3.21" hex = "0.4.3" log = "0.4.17" diff --git a/distant-core/src/api/local/state/watcher.rs b/distant-core/src/api/local/state/watcher.rs index 5f490c1..6bc6d37 100644 --- a/distant-core/src/api/local/state/watcher.rs +++ b/distant-core/src/api/local/state/watcher.rs @@ -1,8 +1,8 @@ use crate::{constants::SERVER_WATCHER_CAPACITY, data::ChangeKind, ConnectionId}; use log::*; use notify::{ - Config as WatcherConfig, Error as WatcherError, Event as WatcherEvent, RecommendedWatcher, - RecursiveMode, Watcher, + Config as WatcherConfig, Error as WatcherError, ErrorKind as WatcherErrorKind, + Event as WatcherEvent, PollWatcher, RecursiveMode, Watcher, }; use std::{ collections::HashMap, @@ -41,10 +41,32 @@ impl WatcherState { // with a large volume of watch requests let (tx, rx) = mpsc::channel(SERVER_WATCHER_CAPACITY); - let mut watcher = { - let tx = tx.clone(); - notify::recommended_watcher(move |res| { - match tx.try_send(match res { + macro_rules! configure_and_spawn { + ($watcher:ident) => {{ + // Attempt to configure watcher, but don't fail if these configurations fail + match $watcher.configure(WatcherConfig::PreciseEvents(true)) { + Ok(true) => debug!("Watcher configured for precise events"), + Ok(false) => debug!("Watcher not configured for precise events",), + Err(x) => error!("Watcher configuration for precise events failed: {}", x), + } + + // Attempt to configure watcher, but don't fail if these configurations fail + match $watcher.configure(WatcherConfig::NoticeEvents(true)) { + Ok(true) => debug!("Watcher configured for notice events"), + Ok(false) => debug!("Watcher not configured for notice events",), + Err(x) => error!("Watcher configuration for notice events failed: {}", x), + } + + Ok(Self { + channel: WatcherChannel { tx }, + task: tokio::spawn(watcher_task($watcher, rx)), + }) + }}; + } + + macro_rules! event_handler { + ($tx:ident) => { + move |res| match $tx.try_send(match res { Ok(x) => InnerWatcherMsg::Event { ev: x }, Err(x) => InnerWatcherMsg::Error { err: x }, }) { @@ -59,28 +81,31 @@ impl WatcherState { warn!("Skipping watch event because watcher channel closed"); } } - }) - .map_err(|x| io::Error::new(io::ErrorKind::Other, x))? - }; - - // Attempt to configure watcher, but don't fail if these configurations fail - match watcher.configure(WatcherConfig::PreciseEvents(true)) { - Ok(true) => debug!("Watcher configured for precise events"), - Ok(false) => debug!("Watcher not configured for precise events",), - Err(x) => error!("Watcher configuration for precise events failed: {}", x), + }; } - // Attempt to configure watcher, but don't fail if these configurations fail - match watcher.configure(WatcherConfig::NoticeEvents(true)) { - Ok(true) => debug!("Watcher configured for notice events"), - Ok(false) => debug!("Watcher not configured for notice events",), - Err(x) => error!("Watcher configuration for notice events failed: {}", x), - } + let tx = tx.clone(); + let result = { + let tx = tx.clone(); + notify::recommended_watcher(event_handler!(tx)) + }; - Ok(Self { - channel: WatcherChannel { tx }, - task: tokio::spawn(watcher_task(watcher, rx)), - }) + match result { + Ok(mut watcher) => configure_and_spawn!(watcher), + Err(x) => match x.kind { + // notify-rs has a bug on Mac M1 with Docker and Linux, so we detect that error + // and fall back to the poll watcher if this occurs + // + // https://github.com/notify-rs/notify/issues/423 + WatcherErrorKind::Io(x) if x.raw_os_error() == Some(38) => { + warn!("Recommended watcher is unsupported! Falling back to polling watcher!"); + let mut watcher = PollWatcher::new(event_handler!(tx)) + .map_err(|x| io::Error::new(io::ErrorKind::Other, x))?; + configure_and_spawn!(watcher) + } + _ => Err(io::Error::new(io::ErrorKind::Other, x)), + }, + } } pub fn clone_channel(&self) -> WatcherChannel { @@ -163,7 +188,7 @@ enum InnerWatcherMsg { }, } -async fn watcher_task(mut watcher: RecommendedWatcher, mut rx: mpsc::Receiver) { +async fn watcher_task(mut watcher: impl Watcher, mut rx: mpsc::Receiver) { // TODO: Optimize this in some way to be more performant than // checking every path whenever an event comes in let mut registered_paths: Vec = Vec::new(); diff --git a/distant-net/Cargo.toml b/distant-net/Cargo.toml index f2be1c5..dcf14b2 100644 --- a/distant-net/Cargo.toml +++ b/distant-net/Cargo.toml @@ -3,7 +3,7 @@ name = "distant-net" description = "Network library for distant, providing implementations to support client/server architecture" categories = ["network-programming"] keywords = ["api", "async"] -version = "0.17.4" +version = "0.17.5" authors = ["Chip Senkbeil "] edition = "2021" homepage = "https://github.com/chipsenkbeil/distant" diff --git a/distant-ssh2/Cargo.toml b/distant-ssh2/Cargo.toml index 3a7a7fc..d21482c 100644 --- a/distant-ssh2/Cargo.toml +++ b/distant-ssh2/Cargo.toml @@ -2,7 +2,7 @@ name = "distant-ssh2" description = "Library to enable native ssh-2 protocol for use with distant sessions" categories = ["network-programming"] -version = "0.17.4" +version = "0.17.5" authors = ["Chip Senkbeil "] edition = "2021" homepage = "https://github.com/chipsenkbeil/distant" @@ -20,7 +20,7 @@ async-compat = "0.2.1" async-once-cell = "0.4.2" async-trait = "0.1.57" derive_more = { version = "0.99.17", default-features = false, features = ["display", "error"] } -distant-core = { version = "=0.17.4", path = "../distant-core" } +distant-core = { version = "=0.17.5", path = "../distant-core" } futures = "0.3.21" hex = "0.4.3" log = "0.4.17"