From bc0f4787bd6aafe5d3a68eb4a01ca60164fc9bc9 Mon Sep 17 00:00:00 2001 From: "Ericson \"Fogo\" Soares" Date: Wed, 11 Oct 2023 02:59:15 -0300 Subject: [PATCH] [ENG-1222] `udevadm` doesn't exist in Docker container (#1471) * Changing how we watch for volumes * Small nitpick --- core/src/api/files.rs | 5 +- core/src/api/utils/invalidate.rs | 12 +++-- core/src/volume/mod.rs | 37 ++++++++++++- core/src/volume/watcher.rs | 92 ++++++++------------------------ 4 files changed, 68 insertions(+), 78 deletions(-) diff --git a/core/src/api/files.rs b/core/src/api/files.rs index df3f274b1..3adc7a350 100644 --- a/core/src/api/files.rs +++ b/core/src/api/files.rs @@ -5,8 +5,7 @@ use crate::{ library::Library, location::{ file_path_helper::{ - file_path_to_full_path, file_path_to_isolate, file_path_to_isolate_with_id, - FilePathError, IsolatedFilePathData, + file_path_to_isolate, file_path_to_isolate_with_id, FilePathError, IsolatedFilePathData, }, get_location_path_from_location_id, LocationError, }, @@ -33,7 +32,7 @@ use sd_media_metadata::MediaMetadata; use std::{ ffi::OsString, - path::{Path, PathBuf, MAIN_SEPARATOR, MAIN_SEPARATOR_STR}, + path::{Path, PathBuf}, str::FromStr, sync::Arc, }; diff --git a/core/src/api/utils/invalidate.rs b/core/src/api/utils/invalidate.rs index 12868e5d0..404510677 100644 --- a/core/src/api/utils/invalidate.rs +++ b/core/src/api/utils/invalidate.rs @@ -197,10 +197,16 @@ macro_rules! invalidate_query { .queries .push($crate::api::utils::InvalidationRequest { key: $key, - arg_ty: Some(<$arg_ty as rspc::internal::specta::Type>::reference(rspc::internal::specta::DefOpts { + arg_ty: <$arg_ty as specta::Type>::reference(specta::DefOpts { parent_inline: false, - type_map: &mut rspc::internal::specta::TypeDefs::new(), - }, &[])), + type_map: &mut specta::TypeDefs::new(), + }, &[]).map_err(|e| { + ::tracing::error!( + "Failed to get type reference for invalidate query '{}': {:?}", + $key, + e + ) + }).ok(), result_ty: None, macro_src: concat!(file!(), ":", line!()), }) diff --git a/core/src/volume/mod.rs b/core/src/volume/mod.rs index 9ee617fa8..3622c1ee3 100644 --- a/core/src/volume/mod.rs +++ b/core/src/volume/mod.rs @@ -1,9 +1,15 @@ // Adapted from: https://github.com/kimlimjustin/xplorer/blob/f4f3590d06783d64949766cc2975205a3b689a56/src-tauri/src/drives.rs +use std::{ + fmt::Display, + hash::{Hash, Hasher}, + path::PathBuf, + sync::OnceLock, +}; + use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use specta::Type; -use std::{fmt::Display, path::PathBuf, sync::OnceLock}; use sysinfo::{DiskExt, System, SystemExt}; use thiserror::Error; use tokio::sync::Mutex; @@ -16,7 +22,7 @@ fn sys_guard() -> &'static Mutex { SYS.get_or_init(|| Mutex::new(System::new_all())) } -#[derive(Serialize, Deserialize, Debug, Clone, Type)] +#[derive(Serialize, Deserialize, Debug, Clone, Type, Hash, PartialEq, Eq)] #[allow(clippy::upper_case_acronyms)] pub enum DiskType { SSD, @@ -50,6 +56,33 @@ pub struct Volume { pub is_root_filesystem: bool, } +impl Hash for Volume { + fn hash(&self, state: &mut H) { + self.name.hash(state); + self.mount_points.iter().for_each(|mount_point| { + // Hashing like this to ignore ordering between mount points + mount_point.hash(state); + }); + self.disk_type.hash(state); + self.file_system.hash(state); + } +} + +impl PartialEq for Volume { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + && self.disk_type == other.disk_type + && self.file_system == other.file_system + // Leaving mount points for last because O(n * m) + && self + .mount_points + .iter() + .all(|mount_point| other.mount_points.contains(mount_point)) + } +} + +impl Eq for Volume {} + #[derive(Error, Debug)] pub enum VolumeError { #[error("Database error: {0}")] diff --git a/core/src/volume/watcher.rs b/core/src/volume/watcher.rs index f7f909cf6..eb16d58fb 100644 --- a/core/src/volume/watcher.rs +++ b/core/src/volume/watcher.rs @@ -1,81 +1,33 @@ -use std::io::BufRead; -use std::process::Command; -use std::sync::Arc; -use std::thread; -use std::time::Duration; +use crate::{invalidate_query, library::Library}; -use crate::invalidate_query; -use crate::library::Library; +use std::{collections::HashSet, sync::Arc}; -/// Currently the only thing we do is invalidate the volumes.list query. -/// Later, we will want to extract specific data into a struct. -/// That way we can determine if we want to trigger the import files flow. -/// -fn handle_disk_change(library: Arc) { - // Clone the Arc to be moved into the closure - let library_cloned = library.clone(); +use tokio::{ + spawn, + time::{interval, Duration}, +}; - // Spawn a new thread to perform a delayed operation - thread::spawn(move || { - thread::sleep(Duration::from_millis(500)); // Delay for 500 milliseconds - invalidate_query!(library_cloned, "volumes.list"); - }); -} +use super::{get_volumes, Volume}; pub fn spawn_volume_watcher(library: Arc) { - #[cfg(target_os = "macos")] - thread::spawn(move || { - let mut child = Command::new("diskutil") - .arg("activity") - .stdout(std::process::Stdio::piped()) - .spawn() - .expect("Failed to start diskutil"); + spawn(async move { + let mut interval = interval(Duration::from_secs(1)); + let mut existing_volumes = get_volumes().await.into_iter().collect::>(); - let stdout = child.stdout.as_mut().expect("Failed to capture stdout"); - let mut reader = std::io::BufReader::new(stdout); + loop { + interval.tick().await; - let mut buffer = String::new(); - while reader.read_line(&mut buffer).expect("Failed to read line") > 0 { - if buffer.contains("DiskAppeared") || buffer.contains("DiskDisappeared") { - // println!("Disk change detected: {:?}", &buffer); - handle_disk_change(library.clone()); + let current_volumes = get_volumes().await.into_iter().collect::>(); + + if existing_volumes != current_volumes { + existing_volumes = current_volumes; + invalidate_query!( + &library, + "volumes.list": + Vec, + existing_volumes.iter().cloned().collect::>() + ); } - buffer.clear(); } }); - - #[cfg(target_os = "linux")] - thread::spawn(move || { - let mut child = Command::new("udevadm") - .arg("monitor") - .stdout(std::process::Stdio::piped()) - .spawn() - .expect("Failed to start udevadm"); - - let stdout = child.stdout.as_mut().expect("Failed to capture stdout"); - let mut reader = std::io::BufReader::new(stdout); - - let mut buffer = String::new(); - while reader.read_line(&mut buffer).expect("Failed to read line") > 0 { - if buffer.contains("add") || buffer.contains("remove") { - println!("Disk change detected: {:?}", &buffer); - handle_disk_change(library.clone()); - } - - buffer.clear(); - } - }); - - #[cfg(target_os = "windows")] - thread::spawn(move || { - let mut child = Command::new("wmic") - .arg("diskdrive") - .stdout(std::process::Stdio::piped()) - .spawn() - .expect("Failed to start wmic"); - - // Shared handling code - // ... - // handle_disk_change(library.clone()); - }); }