mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 14:33:34 +00:00
[ENG-1222] udevadm
doesn't exist in Docker container (#1471)
* Changing how we watch for volumes * Small nitpick
This commit is contained in:
parent
69ade6b3f5
commit
bc0f4787bd
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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!()),
|
||||
})
|
||||
|
|
|
@ -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}")]
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue