dbus_udisks2/
lib.rs

1//! You probably want to look at [`UDisks2`] or [`AsyncUDisks2`].
2
3use 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    /// Find the drive that corresponds to the given dbus object path.
49    fn get_drive(&self, path: &str) -> Option<Drive> {
50        self.get_object::<Drive>(path)
51    }
52
53    /// An iterator of `Drive` objects fetched from the inner cached managed objects.
54    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    /// Find the block that corresponds to the given dbus object path.
61    fn get_block(&self, path: &str) -> Option<Block> {
62        self.get_object::<Block>(path)
63    }
64
65    /// An iterator of `Block` objects fetched from the inner cached managed objects.
66    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    /// Refresh the managed objects fetched from the DBus server.
97    pub fn update(&mut self) -> Result<(), dbus::Error> {
98        self.cache.0 = self.proxy(PATH).get_managed_objects()?;
99        Ok(())
100    }
101
102    /// Find the drive that corresponds to the given dbus object path.
103    pub fn get_drive(&self, path: &str) -> Option<Drive> {
104        self.cache.get_drive(path)
105    }
106
107    /// An iterator of `Drive` objects fetched from the inner cached managed objects.
108    pub fn get_drives<'a>(&'a self) -> impl Iterator<Item = Drive> + 'a {
109        self.cache.get_drives()
110    }
111
112    /// Find the block that corresponds to the given dbus object path.
113    pub fn get_block(&self, path: &str) -> Option<Block> {
114        self.cache.get_block(path)
115    }
116
117    /// An iterator of `Block` objects fetched from the inner cached managed objects.
118    pub fn get_blocks<'a>(&'a self) -> impl Iterator<Item = Block> + 'a {
119        self.cache.get_blocks()
120    }
121
122    /// Update the S.M.A.R.T. attributes of a drive. You may pass either a `&`[`Drive`] or `&str`
123    /// which is a path to a drive, starting with `/org/freedesktop/UDisks2/drives/`.
124    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    /// Get the S.M.A.R.T. attributes of a drive. You may pass either a `&`[`Drive`] or `&str` which
138    /// is a path to a drive, starting with `/org/freedesktop/UDisks2/drives/`.
139    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}