[ENG-1222] udevadm doesn't exist in Docker container (#1471)

* Changing how we watch for volumes

* Small nitpick
This commit is contained in:
Ericson "Fogo" Soares 2023-10-11 02:59:15 -03:00 committed by GitHub
parent 69ade6b3f5
commit bc0f4787bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 78 deletions

View file

@ -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,
};

View file

@ -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!()),
})

View file

@ -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<System> {
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<H: Hasher>(&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}")]

View file

@ -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<Library>) {
// 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<Library>) {
#[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::<HashSet<_>>();
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::<HashSet<_>>();
if existing_volumes != current_volumes {
existing_volumes = current_volumes;
invalidate_query!(
&library,
"volumes.list":
Vec<Volume>,
existing_volumes.iter().cloned().collect::<Vec<_>>()
);
}
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());
});
}