1use std::collections::HashMap;
4use std::ops::Deref;
5use std::time::Duration;
6
7use dbus::arg::Variant;
8use dbus::blocking;
9use dbus::blocking::stdintf::org_freedesktop_dbus::{ObjectManager, Properties};
10
11use crate::smart::{RawSmartAttribute, SmartData, SmartStatus, SmartValue};
12pub use block::*;
13pub use disks::*;
14pub use drive::*;
15#[cfg(feature = "futures")]
16pub use nonblock::*;
17use utils::*;
18
19mod block;
20mod disks;
21mod drive;
22#[cfg(feature = "futures")]
23mod nonblock;
24pub mod smart;
25mod utils;
26
27const DEST: &str = "org.freedesktop.UDisks2";
28const PATH: &str = "/org/freedesktop/UDisks2";
29const NO_WAKEUP: &str = "nowakeup";
30
31#[derive(Default)]
32struct DiskCache(HashMap<dbus::Path<'static>, DbusObjects>);
33
34impl DiskCache {
35 fn get_object<T: ParseFrom>(&self, path: &str) -> Option<T> {
36 self.0
37 .iter()
38 .flat_map(|object| {
39 if object.0.deref() == path {
40 T::parse_from(&object.0, &object.1)
41 } else {
42 None
43 }
44 })
45 .next()
46 }
47
48 fn get_drive(&self, path: &str) -> Option<Drive> {
50 self.get_object::<Drive>(path)
51 }
52
53 fn get_drives<'a>(&'a self) -> impl Iterator<Item = Drive> + 'a {
55 self.0
56 .iter()
57 .flat_map(|object| Drive::parse_from(&object.0, &object.1))
58 }
59
60 fn get_block(&self, path: &str) -> Option<Block> {
62 self.get_object::<Block>(path)
63 }
64
65 fn get_blocks<'a>(&'a self) -> impl Iterator<Item = Block> + 'a {
67 self.0
68 .iter()
69 .flat_map(|object| Block::parse_from(&object.0, &object.1))
70 }
71}
72
73pub struct UDisks2 {
74 conn: blocking::Connection,
75 cache: DiskCache,
76}
77
78impl UDisks2 {
79 pub fn new() -> Result<Self, dbus::Error> {
80 let mut udisks2 = Self {
81 conn: blocking::Connection::new_system()?,
82 cache: Default::default(),
83 };
84
85 udisks2.update()?;
86 Ok(udisks2)
87 }
88
89 fn proxy<'a>(
90 &'a self,
91 path: impl Into<dbus::Path<'a>>,
92 ) -> blocking::Proxy<&blocking::Connection> {
93 blocking::Proxy::new(DEST, path, Duration::from_millis(3000), &self.conn)
94 }
95
96 pub fn update(&mut self) -> Result<(), dbus::Error> {
98 self.cache.0 = self.proxy(PATH).get_managed_objects()?;
99 Ok(())
100 }
101
102 pub fn get_drive(&self, path: &str) -> Option<Drive> {
104 self.cache.get_drive(path)
105 }
106
107 pub fn get_drives<'a>(&'a self) -> impl Iterator<Item = Drive> + 'a {
109 self.cache.get_drives()
110 }
111
112 pub fn get_block(&self, path: &str) -> Option<Block> {
114 self.cache.get_block(path)
115 }
116
117 pub fn get_blocks<'a>(&'a self) -> impl Iterator<Item = Block> + 'a {
119 self.cache.get_blocks()
120 }
121
122 pub fn smart_update<'a>(
125 &'a self,
126 drive: impl Into<dbus::Path<'a>>,
127 allow_wakeup: bool,
128 ) -> Result<(), dbus::Error> {
129 let proxy = self.proxy(drive);
130 let mut options = KeyVariant::<&str>::new();
131 if !allow_wakeup {
132 options.insert(NO_WAKEUP, Variant(Box::new(true)));
133 }
134 proxy.method_call(smart::DEST, smart::UPDATE, (options,))
135 }
136
137 pub fn smart_attributes<'a>(
140 &'a self,
141 drive: impl Into<dbus::Path<'a>>,
142 allow_wakeup: bool,
143 ) -> Result<SmartValue, dbus::Error> {
144 let proxy = self.proxy(drive);
145 if !proxy.get::<bool>(smart::DEST, smart::SUPPORTED)? {
146 return Ok(SmartValue::NotSupported);
147 }
148 if !proxy.get::<bool>(smart::DEST, smart::ENABLED)? {
149 return Ok(SmartValue::NotEnabled);
150 }
151 let updated: u64 = proxy.get(smart::DEST, smart::UPDATED)?;
152 if updated == 0 {
153 return Ok(SmartValue::NotUpdated);
154 }
155 let mut options = KeyVariant::<&str>::new();
156 if !allow_wakeup {
157 options.insert(NO_WAKEUP, Variant(Box::new(true)));
158 }
159 let (attrs,): (Vec<RawSmartAttribute>,) =
160 proxy.method_call(smart::DEST, smart::GET_ATTRS, (options,))?;
161 Ok(SmartValue::Enabled(SmartData {
162 updated,
163 failing: proxy.get(smart::DEST, smart::FAILING)?,
164 time_powered_on: proxy.get(smart::DEST, smart::TIME_POWER_ON)?,
165 temperature: proxy.get(smart::DEST, smart::TEMPERATURE)?,
166 failing_attrs_count: proxy.get(smart::DEST, smart::FAILING_ATTRS_COUNT)?,
167 past_failing_attrs_count: proxy.get(smart::DEST, smart::PAST_FAILING_ATTRS_COUNT)?,
168 bad_sectors: proxy.get(smart::DEST, smart::BAD_SECTORS)?,
169 status: proxy
170 .get::<String>(smart::DEST, smart::STATUS)?
171 .parse()
172 .unwrap_or(SmartStatus::Unknown),
173 attributes: attrs.into_iter().map(Into::into).collect(),
174 }))
175 }
176}