1use crate::utils::*;
4use num_enum::TryFromPrimitive;
5use std::convert::TryFrom;
6use std::fmt;
7use std::str::FromStr;
8use std::time::Duration;
9
10pub(crate) const DEST: &str = "org.freedesktop.UDisks2.Drive.Ata";
11pub(crate) const UPDATE: &str = "SmartUpdate";
12pub(crate) const GET_ATTRS: &str = "SmartGetAttributes";
13pub(crate) const ENABLED: &str = "SmartEnabled";
14pub(crate) const SUPPORTED: &str = "SmartSupported";
15pub(crate) const UPDATED: &str = "SmartUpdated";
16pub(crate) const FAILING: &str = "SmartFailing";
17pub(crate) const TIME_POWER_ON: &str = "SmartPowerOnSeconds";
18pub(crate) const TEMPERATURE: &str = "SmartTemperature";
19pub(crate) const FAILING_ATTRS_COUNT: &str = "SmartNumAttributesFailing";
20pub(crate) const PAST_FAILING_ATTRS_COUNT: &str = "SmartNumAttributesFailedInThePast";
21pub(crate) const BAD_SECTORS: &str = "SmartNumBadSectors";
22pub(crate) const STATUS: &str = "SmartSelftestStatus";
23pub(crate) type RawSmartAttribute = (u8, String, u16, i32, i32, i32, i64, i32, KeyVariant);
24
25#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
26#[non_exhaustive]
27pub enum SmartStatus {
29 Success,
31 Aborted,
33 Interrupted,
35 Fatal,
37 UnknownError,
39 ElectricalError,
41 ServoError,
43 ReadError,
45 HandlingError,
47 InProgress,
49 Unknown,
51}
52
53impl FromStr for SmartStatus {
54 type Err = ();
55
56 fn from_str(s: &str) -> Result<Self, Self::Err> {
57 match s {
58 "success" => Ok(SmartStatus::Success),
59 "aborted" => Ok(SmartStatus::Aborted),
60 "interrupted" => Ok(SmartStatus::Interrupted),
61 "fatal" => Ok(SmartStatus::Fatal),
62 "error_unknown" => Ok(SmartStatus::UnknownError),
63 "error_electrical" => Ok(SmartStatus::ElectricalError),
64 "error_servo" => Ok(SmartStatus::ServoError),
65 "error_read" => Ok(SmartStatus::ReadError),
66 "error_handling" => Ok(SmartStatus::HandlingError),
67 "inprogress" => Ok(SmartStatus::InProgress),
68 _ => Err(()),
69 }
70 }
71}
72
73#[derive(Clone, Debug)]
74pub enum SmartValue {
76 NotSupported,
78 NotEnabled,
80 NotUpdated,
84 Enabled(SmartData),
85}
86
87#[derive(Clone, Debug)]
88pub struct SmartData {
90 pub attributes: Vec<SmartAttribute>,
91 pub updated: u64,
93 pub failing: bool,
97 pub time_powered_on: u64,
99 pub temperature: f64,
101 pub failing_attrs_count: i32,
103 pub past_failing_attrs_count: i32,
105 pub bad_sectors: i64,
107 pub status: SmartStatus,
109}
110
111#[derive(Debug, Eq, PartialEq, TryFromPrimitive, Copy, Clone, Hash)]
112#[repr(u8)]
113#[non_exhaustive]
114pub enum PrettyUnit {
115 Dimensionless = 1,
116 Milliseconds,
117 Sectors,
118 Millikelvin,
119}
120
121#[derive(Eq, PartialEq, Copy, Clone, Hash)]
122pub struct PrettyValue {
123 pub value: i64,
124 pub unit: PrettyUnit,
125}
126
127impl fmt::Debug for PrettyValue {
128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129 write!(f, "{} / ", self)?;
130 f.debug_struct("PrettyValue")
131 .field("value", &self.value)
132 .field("unit", &self.unit)
133 .finish()
134 }
135}
136
137impl fmt::Display for PrettyValue {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 match self.unit {
140 PrettyUnit::Dimensionless => write!(f, "{}", self.value),
141 PrettyUnit::Milliseconds => write!(f, "{:?}", Duration::from_millis(self.value as u64)),
142 PrettyUnit::Sectors => write!(f, "{} sectors", self.value),
143 PrettyUnit::Millikelvin => {
144 write!(f, "{:.1} degrees C", self.value as f32 / 1000. - 273.15)
145 }
146 }
147 }
148}
149
150#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
151pub enum SmartAssessment {
152 Failing,
153 FailedInPast,
154 Ok,
155}
156
157#[derive(Clone, Debug)]
158pub struct SmartAttribute {
160 pub id: u8,
162 pub name: String,
164 pub flags: u16,
166 pub normalized: i32,
168 pub worst: i32,
170 pub threshold: i32,
172 pub pretty: Option<PrettyValue>,
174}
175
176impl SmartAttribute {
177 pub fn pre_fail(&self) -> bool {
197 self.flags & 0x01 > 0
198 }
199 pub fn online(&self) -> bool {
206 self.flags & 0x02 > 0
207 }
208 pub fn performance(&self) -> bool {
210 self.flags & 0x04 > 0
211 }
212 pub fn error_rate(&self) -> bool {
213 self.flags & 0x08 > 0
214 }
215 pub fn event_count(&self) -> bool {
216 self.flags & 0x10 > 0
217 }
218 pub fn self_preserving(&self) -> bool {
219 self.flags & 0x20 > 0
220 }
221 pub fn assessment(&self) -> SmartAssessment {
225 if self.normalized > 0 && self.threshold > 0 && self.normalized <= self.threshold {
226 SmartAssessment::Failing
227 } else if self.worst > 0 && self.threshold > 0 && self.worst <= self.threshold {
228 SmartAssessment::FailedInPast
229 } else {
230 SmartAssessment::Ok
231 }
232 }
233}
234
235impl From<RawSmartAttribute> for SmartAttribute {
236 fn from(
237 (id, name, flags, value, worst, threshold, pretty_value, pretty_unit, _expansion): RawSmartAttribute,
238 ) -> Self {
239 let pretty = PrettyUnit::try_from(pretty_unit as u8)
240 .map(|unit| PrettyValue {
241 value: pretty_value,
242 unit,
243 })
244 .ok();
245 SmartAttribute {
246 id,
247 name,
248 flags,
249 normalized: value,
250 worst,
251 threshold,
252 pretty,
253 }
254 }
255}