mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-27 06:15:45 +00:00
Refactored drive data fetch and parsing, and drive types.
This commit is contained in:
@@ -48,11 +48,11 @@ class AtaStorageAttribute {
|
||||
public:
|
||||
|
||||
/// Disk type the attribute may match
|
||||
enum class DiskType {
|
||||
Any, ///< Any disk type
|
||||
Hdd, ///< HDD (rotational) only
|
||||
Ssd ///< SSD only
|
||||
};
|
||||
// enum class DiskType {
|
||||
// Any, ///< Any disk type
|
||||
// Hdd, ///< HDD (rotational) only
|
||||
// Ssd ///< SSD only
|
||||
// };
|
||||
|
||||
/// Attribute pre-failure / old-age type
|
||||
enum class AttributeType {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,10 @@ Copyright:
|
||||
#define ATA_STORAGE_PROPERTY_DESCR_H
|
||||
|
||||
#include "storage_property_repository.h"
|
||||
#include "storage_device_detected_type.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class StoragePropertyProcessor {
|
||||
@@ -20,7 +24,7 @@ class StoragePropertyProcessor {
|
||||
|
||||
/// Set descriptions, warnings, etc. on properties, and return them.
|
||||
static StoragePropertyRepository process_properties(StoragePropertyRepository properties,
|
||||
AtaStorageAttribute::DiskType disk_type);
|
||||
StorageDeviceDetectedType device_type);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ int main()
|
||||
} else {
|
||||
for (const auto& drive : drives) {
|
||||
std::cerr << drive->get_device_with_type() <<
|
||||
" (" << StorageDevice::get_type_storable_name(drive->get_detected_type()) << ")\n";
|
||||
" (" << StorageDeviceDetectedTypeExt::get_storable_name(drive->get_detected_type()) << ")\n";
|
||||
}
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -257,7 +257,6 @@ hz::ExpectedVoid<SelfTestError> SelfTest::update(const std::shared_ptr<CommandEx
|
||||
std::vformat(_("Sending command to drive failed: {}"), std::make_format_args(execute_status.error().message())));
|
||||
}
|
||||
|
||||
const AtaStorageAttribute::DiskType disk_type = drive_->get_is_hdd() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd;
|
||||
auto parser = SmartctlParser::create(SmartctlParserType::Ata, SmartctlVersionParser::get_default_format(SmartctlParserType::Ata));
|
||||
DBG_ASSERT_RETURN(parser, hz::Unexpected(SelfTestError::ParseError, _("Cannot create parser.")));
|
||||
|
||||
@@ -266,7 +265,8 @@ hz::ExpectedVoid<SelfTestError> SelfTest::update(const std::shared_ptr<CommandEx
|
||||
return hz::Unexpected(SelfTestError::ParseError,
|
||||
std::vformat(_("Cannot parse smartctl output: {}"), std::make_format_args(parse_status.error().message())));
|
||||
}
|
||||
auto property_repo = StoragePropertyProcessor::process_properties(parser->get_property_repository(), disk_type);
|
||||
auto property_repo = StoragePropertyProcessor::process_properties(
|
||||
parser->get_property_repository(), drive_->get_detected_type());
|
||||
|
||||
// Note: Since the self-test log is sometimes late
|
||||
// and in undetermined order (sorting by hours is too rough),
|
||||
|
||||
@@ -82,9 +82,6 @@ smartctl/version/_merged
|
||||
Looks like "7.2"
|
||||
smartctl/version/_merged_full
|
||||
Looks like "smartctl 7.2 2020-12-30 r5155", formed from "/smartctl" subkeys.
|
||||
|
||||
_custom/smart_enabled
|
||||
Not present in json?
|
||||
*/
|
||||
|
||||
|
||||
@@ -285,11 +282,19 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_info(
|
||||
}
|
||||
},
|
||||
|
||||
{"rotation_rate", _("Rotation Rate"),
|
||||
custom_string_formatter<int64_t>([](int64_t value)
|
||||
{"rotation_rate", _("Rotation Rate"), // (S)ATA, used to detect HDD vs SSD
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
return std::format("{} RPM", value);
|
||||
})
|
||||
if (auto jval = get_node_data<int64_t>(root_node, key); jval) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.readable_value = std::format("{} RPM", jval.value());
|
||||
p.value = jval.value();
|
||||
return p;
|
||||
}
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
|
||||
{"form_factor/name", _("Form Factor"), string_formatter()},
|
||||
|
||||
@@ -186,10 +186,18 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonBasicParser::parse_section_bas
|
||||
{"local_time/asctime", _("Scanned on"), string_formatter()},
|
||||
|
||||
{"rotation_rate", _("Rotation Rate"), // (S)ATA, used to detect HDD vs SSD
|
||||
custom_string_formatter<int64_t>([](int64_t value)
|
||||
[](const nlohmann::json& root_node, const std::string& key, const std::string& displayable_name)
|
||||
-> hz::ExpectedValue<AtaStorageProperty, SmartctlParserError>
|
||||
{
|
||||
return std::format("{} RPM", value);
|
||||
})
|
||||
if (auto jval = get_node_data<int64_t>(root_node, key); jval) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name(key, key, displayable_name);
|
||||
p.readable_value = std::format("{} RPM", jval.value());
|
||||
p.value = jval.value();
|
||||
return p;
|
||||
}
|
||||
return hz::Unexpected(SmartctlParserError::KeyNotFound, std::format("Error getting key {} from JSON data.", key));
|
||||
}
|
||||
},
|
||||
|
||||
{"form_factor/name", _("Form Factor"), string_formatter()},
|
||||
|
||||
@@ -45,7 +45,14 @@ std::unique_ptr<SmartctlParser> SmartctlParser::create(SmartctlParserType type,
|
||||
}
|
||||
break;
|
||||
case SmartctlParserType::Nvme:
|
||||
// TODO
|
||||
switch(format) {
|
||||
case SmartctlOutputFormat::Json:
|
||||
// return std::make_unique<SmartctlJsonNvmeParser>();
|
||||
break;
|
||||
case SmartctlOutputFormat::Text:
|
||||
// nothing
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -62,7 +62,7 @@ struct SmartctlParserPreferenceTypeExt
|
||||
: public hz::EnumHelper<
|
||||
SmartctlParserPreferenceType,
|
||||
SmartctlParserPreferenceTypeExt,
|
||||
Glib::ustring>
|
||||
Glib::ustring>
|
||||
{
|
||||
static constexpr inline SmartctlParserPreferenceType default_value = SmartctlParserPreferenceType::Auto;
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ Copyright:
|
||||
#include "smartctl_parser_types.h"
|
||||
#include "smartctl_version_parser.h"
|
||||
#include "smartctl_text_parser_helper.h"
|
||||
#include "storage_device.h"
|
||||
|
||||
|
||||
|
||||
@@ -77,9 +78,9 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlTextBasicParser::parse(std::string
|
||||
if (app_pcre_match("/this device: CD\\/DVD/mi", output)
|
||||
|| app_pcre_match("/^Device type:\\s+CD\\/DVD/mi", output)) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name("Drive type", "_custom/disk_type", "Drive Type");
|
||||
p.set_name("Drive type", "_custom/parser_detected_drive_type", "Parser-Detected Drive Type");
|
||||
p.reported_value = "CD/DVD";
|
||||
p.value = p.reported_value; // TODO canonicalize
|
||||
p.value = StorageDeviceDetectedTypeExt::get_storable_name(StorageDeviceDetectedType::CdDvd);
|
||||
p.section = AtaStorageProperty::Section::Info; // add to info section
|
||||
add_property(p);
|
||||
|
||||
@@ -88,9 +89,9 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlTextBasicParser::parse(std::string
|
||||
// Product: Raid 5 Volume
|
||||
} else if (app_pcre_match("/Product:[ \\t]*Raid/mi", output)) {
|
||||
AtaStorageProperty p;
|
||||
p.set_name("Drive type", "_custom/disk_type", "Drive Type");
|
||||
p.set_name("Drive type", "_custom/parser_detected_drive_type", "Parser-Detected Drive Type");
|
||||
p.reported_value = "RAID";
|
||||
p.value = p.reported_value; // TODO canonicalize
|
||||
p.value = StorageDeviceDetectedTypeExt::get_storable_name(StorageDeviceDetectedType::UnsupportedRaid);
|
||||
p.section = AtaStorageProperty::Section::Info; // add to info section
|
||||
add_property(p);
|
||||
|
||||
|
||||
@@ -83,6 +83,26 @@ SmartctlOutputFormat SmartctlVersionParser::get_default_format(SmartctlParserTyp
|
||||
|
||||
|
||||
|
||||
SmartctlParserType SmartctlVersionParser::get_default_parser_type(StorageDeviceDetectedType detected_type)
|
||||
{
|
||||
switch (detected_type) {
|
||||
case StorageDeviceDetectedType::Unknown:
|
||||
case StorageDeviceDetectedType::NeedsExplicitType:
|
||||
case StorageDeviceDetectedType::BasicScsi:
|
||||
case StorageDeviceDetectedType::CdDvd:
|
||||
case StorageDeviceDetectedType::UnsupportedRaid:
|
||||
break;
|
||||
case StorageDeviceDetectedType::AtaAny:
|
||||
case StorageDeviceDetectedType::AtaHdd:
|
||||
case StorageDeviceDetectedType::AtaSsd:
|
||||
return SmartctlParserType::Ata;
|
||||
case StorageDeviceDetectedType::Nvme:
|
||||
return SmartctlParserType::Nvme;
|
||||
}
|
||||
return SmartctlParserType::Basic;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -18,6 +18,9 @@ Copyright:
|
||||
#include <optional>
|
||||
|
||||
#include "smartctl_parser_types.h"
|
||||
#include "ata_storage_property_descr.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +50,9 @@ class SmartctlVersionParser {
|
||||
/// Get default output format for a parser type.
|
||||
static SmartctlOutputFormat get_default_format(SmartctlParserType parser_type);
|
||||
|
||||
/// Get default output format for a parser type.
|
||||
static SmartctlParserType get_default_parser_type(StorageDeviceDetectedType detected_type);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ hz::ExpectedVoid<StorageDetectorError> StorageDetector::fetch_basic_data(std::ve
|
||||
debug_out_dump("app", "Device information for " << drive->get_device()
|
||||
<< " (type: \"" << drive->get_type_argument() << "\"):\n"
|
||||
<< "\tModel: " << drive->get_model_name() << "\n"
|
||||
<< "\tDetected type: " << StorageDevice::get_type_storable_name(drive->get_detected_type()) << "\n"
|
||||
<< "\tDetected type: " << StorageDeviceDetectedTypeExt::get_displayable_name(drive->get_detected_type()) << "\n"
|
||||
<< "\tSMART status: " << StorageDevice::get_status_displayable_name(drive->get_smart_status()) << "\n"
|
||||
);
|
||||
|
||||
|
||||
@@ -682,7 +682,8 @@ inline hz::ExpectedVoid<StorageDetectorError> detect_drives_linux_adaptec(
|
||||
// Note: Not sure about this, have to check with real SAS drives
|
||||
if (app_pcre_match("/Device Read Identity Failed/mi", output)) {
|
||||
// "-d sat" didn't work, default back to smartctl's "-d scsi"
|
||||
drive->clear_fetched();
|
||||
drive->clear_parse_results();
|
||||
drive->clear_outputs();
|
||||
drive->set_type_argument("");
|
||||
fetch_status = drive->fetch_basic_data_and_parse(smartctl_ex);
|
||||
}
|
||||
|
||||
+316
-143
@@ -9,48 +9,43 @@ Copyright:
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#include "storage_device.h"
|
||||
|
||||
#include "local_glibmm.h"
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rconfig/rconfig.h"
|
||||
#include "hz/string_algo.h" // string_trim_copy, string_any_to_unix_copy
|
||||
#include "hz/fs.h"
|
||||
#include "hz/format_unit.h" // hz::format_date
|
||||
#include "hz/error_container.h"
|
||||
|
||||
#include "app_pcrecpp.h"
|
||||
#include "storage_device.h"
|
||||
#include "smartctl_text_ata_parser.h"
|
||||
#include "smartctl_parser_types.h"
|
||||
#include "storage_device_detected_type.h"
|
||||
#include "storage_settings.h"
|
||||
#include "smartctl_executor.h"
|
||||
#include "smartctl_version_parser.h"
|
||||
//#include "smartctl_text_parser_helper.h"
|
||||
#include "ata_storage_property_descr.h"
|
||||
|
||||
|
||||
|
||||
std::string StorageDevice::get_type_storable_name(DetectedType type)
|
||||
{
|
||||
static const std::unordered_map<DetectedType, std::string> m {
|
||||
{DetectedType::Unknown, "unknown"},
|
||||
{DetectedType::NeedsExplicitType, "invalid"},
|
||||
{DetectedType::CdDvd, "cd/dvd"},
|
||||
{DetectedType::Raid, "raid"},
|
||||
};
|
||||
if (auto iter = m.find(type); iter != m.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
return "[internal_error]";
|
||||
}
|
||||
//#include "smartctl_text_parser_helper.h"
|
||||
//#include "ata_storage_property_descr.h"
|
||||
|
||||
|
||||
|
||||
std::string StorageDevice::get_status_displayable_name(Status status)
|
||||
{
|
||||
static const std::unordered_map<Status, std::string> m {
|
||||
{Status::Enabled, C_("status", "Enabled")},
|
||||
{Status::Disabled, C_("status", "Disabled")},
|
||||
{Status::Enabled, C_("status", "Enabled")},
|
||||
{Status::Disabled, C_("status", "Disabled")},
|
||||
{Status::Unsupported, C_("status", "Unsupported")},
|
||||
{Status::Unknown, C_("status", "Unknown")},
|
||||
{Status::Unknown, C_("status", "Unknown")},
|
||||
};
|
||||
if (auto iter = m.find(status); iter != m.end()) {
|
||||
return iter->second;
|
||||
@@ -78,15 +73,20 @@ StorageDevice::StorageDevice(std::string dev, std::string type_arg)
|
||||
|
||||
|
||||
|
||||
void StorageDevice::clear_fetched(bool including_outputs)
|
||||
void StorageDevice::clear_outputs()
|
||||
{
|
||||
if (including_outputs) {
|
||||
basic_output_.clear();
|
||||
full_output_.clear();
|
||||
}
|
||||
basic_output_.clear();
|
||||
full_output_.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StorageDevice::clear_parse_results()
|
||||
{
|
||||
parse_status_ = ParseStatus::None;
|
||||
test_is_active_ = false; // not sure
|
||||
// test_is_active_ = false; // not sure
|
||||
|
||||
property_repository_.clear();
|
||||
|
||||
smart_supported_.reset();
|
||||
smart_enabled_.reset();
|
||||
@@ -95,8 +95,6 @@ void StorageDevice::clear_fetched(bool including_outputs)
|
||||
family_name_.reset();
|
||||
size_.reset();
|
||||
health_property_.reset();
|
||||
|
||||
property_repository_.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +106,9 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::fetch_basic_data_and_parse(
|
||||
return hz::Unexpected(StorageDeviceError::TestRunning, _("A test is currently being performed on this drive."));
|
||||
}
|
||||
|
||||
this->clear_fetched(); // clear everything fetched before, including outputs
|
||||
// Clear everything fetched before, including outputs
|
||||
this->clear_parse_results();
|
||||
this->clear_outputs();
|
||||
|
||||
// We don't use "--all" - it may cause really screwed up the output (tests, etc.).
|
||||
// This looks just like "--info" only on non-smart devices.
|
||||
@@ -125,10 +125,14 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::fetch_basic_data_and_parse(
|
||||
// This means that the old SCSI identify command isn't executed by default,
|
||||
// and there is no information about the device manufacturer/etc. in the output.
|
||||
// We detect this and set the device type to scsi to at least have _some_ info.
|
||||
|
||||
// Note: This match works even with JSON (the text output is included in --json=o).
|
||||
if ((execute_status || execute_status.error().data() == StorageDeviceError::ExecutionError)
|
||||
&& get_detected_type() == DetectedType::NeedsExplicitType && get_type_argument().empty()) {
|
||||
&& get_detected_type() == StorageDeviceDetectedType::NeedsExplicitType
|
||||
&& get_type_argument().empty() ) {
|
||||
debug_out_info("app", "The device seems to be of different type than auto-detected, trying again with scsi.\n");
|
||||
this->set_type_argument("scsi");
|
||||
this->set_detected_type(StorageDeviceDetectedType::BasicScsi);
|
||||
return this->fetch_basic_data_and_parse(smartctl_ex); // try again with scsi
|
||||
}
|
||||
|
||||
@@ -142,73 +146,44 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::fetch_basic_data_and_parse(
|
||||
// }
|
||||
|
||||
// Set some properties too - they are needed for e.g. AODC status, etc.
|
||||
return this->parse_basic_data(true);
|
||||
return this->parse_basic_data();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<StorageDeviceError> StorageDevice::parse_basic_data(bool do_set_properties, bool emit_signal)
|
||||
hz::ExpectedVoid<StorageDeviceError> StorageDevice::parse_basic_data()
|
||||
{
|
||||
this->clear_fetched(false); // clear everything fetched before, except outputs
|
||||
// Clear everything fetched before, except outputs and type
|
||||
this->clear_parse_results();
|
||||
|
||||
AtaStorageAttribute::DiskType disk_type = AtaStorageAttribute::DiskType::Any;
|
||||
|
||||
// Try the basic parser first. If it succeeds, use the specialized parser.
|
||||
// Parse using Basic parser. This supports all drive types.
|
||||
auto basic_parser = SmartctlParser::create(SmartctlParserType::Basic, SmartctlOutputFormat::Json);
|
||||
DBG_ASSERT_RETURN(basic_parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser")));
|
||||
|
||||
// This also detects the drive type and adds it to "_custom/parser_detected_drive_type" property.
|
||||
auto parse_status = basic_parser->parse(this->get_basic_output());
|
||||
if (!parse_status) {
|
||||
return hz::Unexpected(StorageDeviceError::ParseError,
|
||||
std::vformat(_("Cannot parse smartctl output: {}"), std::make_format_args(parse_status.error().message())));
|
||||
}
|
||||
|
||||
// See if we can narrow down the drive type from what was detected
|
||||
// by StorageDetector and "_custom/parser_detected_drive_type" property set by Basic parser.
|
||||
auto basic_property_repo = basic_parser->get_property_repository();
|
||||
|
||||
auto drive_type_prop = basic_property_repo.lookup_property("_custom/drive_type");
|
||||
if (!drive_type_prop.empty()) {
|
||||
const auto& drive_type = drive_type_prop.get_value<std::string>();
|
||||
if (drive_type == "CD/DVD") {
|
||||
debug_out_dump("app", "Drive " << get_device_with_type() << " seems to be a CD/DVD device.\n");
|
||||
this->set_detected_type(DetectedType::CdDvd);
|
||||
} else if (drive_type == "RAID") {
|
||||
debug_out_dump("app", "Drive " << get_device_with_type() << " seems to be a RAID volume/controller.\n");
|
||||
this->set_detected_type(DetectedType::Raid);
|
||||
}
|
||||
}
|
||||
// Make detected type more exact.
|
||||
detect_drive_type_from_properties(basic_property_repo);
|
||||
|
||||
{
|
||||
auto rpm_prop = basic_property_repo.lookup_property("rotation_rate");
|
||||
if (!rpm_prop.empty()) {
|
||||
auto rpm = rpm_prop.get_value<int64_t>();
|
||||
this->hdd_ = rpm > 0;
|
||||
}
|
||||
if (hdd_.has_value()) {
|
||||
disk_type = hdd_.value() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd;
|
||||
}
|
||||
}
|
||||
// A model field (and its aliases) is a good indication whether there was any data or not
|
||||
set_parse_status(model_name_.has_value() ? ParseStatus::Basic : ParseStatus::None);
|
||||
|
||||
if (auto prop = basic_property_repo.lookup_property("smart_support/available"); !prop.empty()) {
|
||||
smart_supported_ = prop.get_value<bool>();
|
||||
}
|
||||
if (auto prop = basic_property_repo.lookup_property("smart_support/enabled"); !prop.empty()) {
|
||||
smart_enabled_ = prop.get_value<bool>();
|
||||
}
|
||||
if (auto prop = basic_property_repo.lookup_property("model_name"); !prop.empty()) {
|
||||
model_name_ = prop.get_value<std::string>();
|
||||
}
|
||||
if (auto prop = basic_property_repo.lookup_property("model_family"); !prop.empty()) {
|
||||
family_name_ = prop.get_value<std::string>();
|
||||
}
|
||||
if (auto prop = basic_property_repo.lookup_property("serial_number"); !prop.empty()) {
|
||||
serial_number_ = prop.get_value<std::string>();
|
||||
}
|
||||
if (auto prop = basic_property_repo.lookup_property("user_capacity/bytes/_short"); !prop.empty()) {
|
||||
size_ = prop.readable_value;
|
||||
} else if (auto prop2 = basic_property_repo.lookup_property("user_capacity/bytes"); !prop.empty()) {
|
||||
size_ = prop2.readable_value;
|
||||
}
|
||||
// Add property descriptions and set to the drive.
|
||||
this->set_property_repository(StoragePropertyProcessor::process_properties(basic_property_repo, get_detected_type()));
|
||||
|
||||
debug_out_dump("app", "Drive " << get_device_with_type() << " set to be "
|
||||
<< StorageDeviceDetectedTypeExt::get_displayable_name(get_detected_type()) << " device.\n");
|
||||
|
||||
read_common_properties();
|
||||
|
||||
// Try to parse the properties. ignore its errors - we already got what we came for.
|
||||
// Note that this may try to parse data the second time (it may already have
|
||||
@@ -222,15 +197,8 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::parse_basic_data(bool do_set
|
||||
// StoragePropertyProcessor::process_properties(parser->get_property_repository(), disk_type)); // copy to our drive, overwriting old data
|
||||
// }
|
||||
// }
|
||||
if (do_set_properties) {
|
||||
this->set_property_repository(StoragePropertyProcessor::process_properties(basic_property_repo, disk_type));
|
||||
}
|
||||
|
||||
// A model field (and its aliases) is a good indication whether there was any data or not
|
||||
set_parse_status(model_name_.has_value() ? ParseStatus::Basic : ParseStatus::None);
|
||||
|
||||
if (emit_signal)
|
||||
signal_changed().emit(this); // notify listeners
|
||||
signal_changed().emit(this); // notify listeners
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -244,71 +212,109 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::fetch_full_data_and_parse(
|
||||
return hz::Unexpected(StorageDeviceError::TestRunning, _("A test is currently being performed on this drive."));
|
||||
}
|
||||
|
||||
this->clear_fetched(); // clear everything fetched before, including outputs
|
||||
// Drive type must be already set at this point, using fetch_basic_data_and_parse().
|
||||
DBG_ASSERT(this->get_detected_type() != StorageDeviceDetectedType::Unknown);
|
||||
|
||||
// Clear everything fetched before, including outputs
|
||||
this->clear_parse_results();
|
||||
this->clear_outputs();
|
||||
|
||||
// Execute smartctl.
|
||||
|
||||
// Instead of -x, we use all the individual options -x encompasses, so that
|
||||
// an addition to default -x output won't affect us.
|
||||
std::string command_options;
|
||||
|
||||
// Type was detected by Basic parser
|
||||
switch (this->get_detected_type()) {
|
||||
case StorageDeviceDetectedType::Unknown:
|
||||
case StorageDeviceDetectedType::NeedsExplicitType:
|
||||
DBG_ASSERT(0);
|
||||
break;
|
||||
case StorageDeviceDetectedType::AtaAny:
|
||||
case StorageDeviceDetectedType::AtaHdd:
|
||||
case StorageDeviceDetectedType::AtaSsd:
|
||||
command_options = "--health --info --get=all --capabilities --attributes --format=brief --log=xerror,50,error --log=xselftest,50,selftest --log=selective --log=directory --log=scttemp --log=scterc --log=devstat --log=sataphy";
|
||||
break;
|
||||
case StorageDeviceDetectedType::Nvme:
|
||||
command_options = "--health --info --get=all --capabilities --attributes --format=brief --log=xerror,50,error --log=xselftest,50,selftest --log=selective --log=directory --log=scttemp --log=scterc --log=devstat --log=sataphy";
|
||||
break;
|
||||
case StorageDeviceDetectedType::BasicScsi:
|
||||
case StorageDeviceDetectedType::CdDvd:
|
||||
case StorageDeviceDetectedType::UnsupportedRaid:
|
||||
// SCSI equivalent of -x:
|
||||
command_options = "--health --info --attributes --log=error --log=selftest --log=background --log=sasphy";
|
||||
break;
|
||||
}
|
||||
|
||||
auto parser_type = SmartctlVersionParser::get_default_parser_type(this->get_detected_type());
|
||||
auto parser_format = SmartctlVersionParser::get_default_format(parser_type);
|
||||
if (parser_format == SmartctlOutputFormat::Json) {
|
||||
// --json flags: o means include original output (just in case).
|
||||
command_options += " --json=o";
|
||||
}
|
||||
|
||||
std::string output;
|
||||
hz::ExpectedVoid<StorageDeviceError> execute_status;
|
||||
|
||||
// instead of -x, we use all the individual options -x encompasses, so that
|
||||
// an addition to default -x output won't affect us.
|
||||
if (this->get_type_argument() == "scsi") { // not sure about correctness... FIXME probably fails with RAID/scsi
|
||||
const auto default_parser_type = SmartctlVersionParser::get_default_format(SmartctlParserType::Basic);
|
||||
// This doesn't do much yet, but just in case...
|
||||
// SCSI equivalent of -x:
|
||||
std::string command_options = "--health --info --attributes --log=error --log=selftest --log=background --log=sasphy";
|
||||
if (default_parser_type == SmartctlOutputFormat::Json) {
|
||||
// --json flags: o means include original output (just in case).
|
||||
command_options += " --json=o";
|
||||
}
|
||||
|
||||
execute_status = execute_device_smartctl(command_options, smartctl_ex, output);
|
||||
|
||||
} else {
|
||||
const auto default_parser_type = SmartctlVersionParser::get_default_format(SmartctlParserType::Ata);
|
||||
// ATA equivalent of -x.
|
||||
std::string command_options = "--health --info --get=all --capabilities --attributes --format=brief --log=xerror,50,error --log=xselftest,50,selftest --log=selective --log=directory --log=scttemp --log=scterc --log=devstat --log=sataphy";
|
||||
if (default_parser_type == SmartctlOutputFormat::Json) {
|
||||
// --json flags: o means include original output (just in case).
|
||||
command_options += " --json=o";
|
||||
}
|
||||
|
||||
execute_status = execute_device_smartctl(command_options, smartctl_ex, output, true); // set type to invalid if needed
|
||||
}
|
||||
auto execute_status = execute_device_smartctl(command_options, smartctl_ex, output);
|
||||
|
||||
// if (this->get_type_argument() == "scsi") { // not sure about correctness... FIXME probably fails with RAID/scsi
|
||||
// const auto default_parser_type = SmartctlVersionParser::get_default_format(SmartctlParserType::Basic);
|
||||
// // This doesn't do much yet, but just in case...
|
||||
// // SCSI equivalent of -x:
|
||||
// std::string command_options = "--health --info --attributes --log=error --log=selftest --log=background --log=sasphy";
|
||||
// if (default_parser_type == SmartctlOutputFormat::Json) {
|
||||
// // --json flags: o means include original output (just in case).
|
||||
// command_options += " --json=o";
|
||||
// }
|
||||
//
|
||||
// execute_status = execute_device_smartctl(command_options, smartctl_ex, output);
|
||||
//
|
||||
// } else {
|
||||
// const auto default_parser_type = SmartctlVersionParser::get_default_format(SmartctlParserType::Ata);
|
||||
// // ATA equivalent of -x.
|
||||
// std::string command_options = "--health --info --get=all --capabilities --attributes --format=brief --log=xerror,50,error --log=xselftest,50,selftest --log=selective --log=directory --log=scttemp --log=scterc --log=devstat --log=sataphy";
|
||||
// if (default_parser_type == SmartctlOutputFormat::Json) {
|
||||
// // --json flags: o means include original output (just in case).
|
||||
// command_options += " --json=o";
|
||||
// }
|
||||
//
|
||||
// execute_status = execute_device_smartctl(command_options, smartctl_ex, output, true); // set type to invalid if needed
|
||||
// }
|
||||
// See notes above (in fetch_basic_data_and_parse()).
|
||||
if ((execute_status || execute_status.error().data() == StorageDeviceError::ExecutionError)
|
||||
&& get_detected_type() == DetectedType::NeedsExplicitType && get_type_argument().empty()) {
|
||||
debug_out_info("app", "The device seems to be of different type than auto-detected, trying again with scsi.\n");
|
||||
this->set_type_argument("scsi");
|
||||
return this->fetch_full_data_and_parse(smartctl_ex); // try again with scsi
|
||||
}
|
||||
|
||||
// No need to do this: if the basic data was fetched, the type is already set.
|
||||
// if ((execute_status || execute_status.error().data() == StorageDeviceError::ExecutionError)
|
||||
// && get_detected_type() == DetectedType::NeedsExplicitType && get_type_argument().empty()) {
|
||||
// debug_out_info("app", "The device seems to be of different type than auto-detected, trying again with scsi.\n");
|
||||
// this->set_type_argument("scsi");
|
||||
// return this->fetch_full_data_and_parse(smartctl_ex); // try again with scsi
|
||||
// }
|
||||
// Since the type error leads to "command line didn't parse" error here,
|
||||
// we do this after the scsi stuff.
|
||||
|
||||
|
||||
if (!execute_status)
|
||||
return execute_status;
|
||||
|
||||
this->full_output_ = output;
|
||||
return this->try_parse_data();
|
||||
return this->parse_full_data(parser_type, parser_format);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
hz::ExpectedVoid<StorageDeviceError> StorageDevice::try_parse_data()
|
||||
{
|
||||
this->clear_fetched(false); // clear everything fetched before, except outputs
|
||||
|
||||
AtaStorageAttribute::DiskType disk_type = AtaStorageAttribute::DiskType::Any;
|
||||
if (hdd_.has_value()) {
|
||||
disk_type = hdd_.value() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd;
|
||||
}
|
||||
this->clear_fetched(false); // clear everything fetched before, except outputs and disk type
|
||||
|
||||
auto parser_format = SmartctlParser::detect_output_format(this->full_output_);
|
||||
|
||||
if (!parser_format.has_value()) {
|
||||
return hz::Unexpected(StorageDeviceError::ParseError, parser_format.error().message());
|
||||
}
|
||||
|
||||
// auto basic_parser = SmartctlParser::create(SmartctlParserType::Basic, parser_format.value());
|
||||
// if (!basic_parser) {
|
||||
// return hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser"));
|
||||
// }
|
||||
|
||||
// TODO Choose format according to device type
|
||||
SmartctlParserType parser_type = SmartctlParserType::Ata;
|
||||
|
||||
@@ -355,6 +361,113 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::try_parse_data()
|
||||
|
||||
return {}; // return ok if at least the info was ok.
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<StorageDeviceError> StorageDevice::parse_full_data(SmartctlParserType parser_type, SmartctlOutputFormat format)
|
||||
{
|
||||
// Clear everything fetched before, except outputs and disk type
|
||||
clear_parse_results();
|
||||
|
||||
auto parser = SmartctlParser::create(parser_type, format);
|
||||
DBG_ASSERT_RETURN(parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser")));
|
||||
|
||||
const auto parse_status = parser->parse(this->full_output_);
|
||||
if (parse_status.has_value()) {
|
||||
set_parse_status(parser_type == SmartctlParserType::Basic ? ParseStatus::Basic : ParseStatus::Full);
|
||||
|
||||
// Detect drive type based on parsed properties
|
||||
detect_drive_type_from_properties(parser->get_property_repository());
|
||||
|
||||
// Set the full properties, overwriting old data.
|
||||
set_property_repository(StoragePropertyProcessor::process_properties(parser->get_property_repository(), get_detected_type()));
|
||||
|
||||
// Read common properties from the repository.
|
||||
read_common_properties();
|
||||
|
||||
signal_changed().emit(this); // notify listeners
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
return hz::Unexpected(StorageDeviceError::ParseError,
|
||||
std::vformat(_("Cannot parse smartctl output: {}"), std::make_format_args(parse_status.error().message())));
|
||||
}
|
||||
|
||||
|
||||
|
||||
hz::ExpectedVoid<StorageDeviceError> StorageDevice::parse_any_data_for_virtual()
|
||||
{
|
||||
// Clear everything fetched before, except outputs and disk type
|
||||
this->clear_parse_results();
|
||||
|
||||
auto parser_format = SmartctlParser::detect_output_format(this->full_output_);
|
||||
if (!parser_format.has_value()) {
|
||||
return hz::Unexpected(StorageDeviceError::ParseError, parser_format.error().message());
|
||||
}
|
||||
|
||||
auto basic_parser = SmartctlParser::create(SmartctlParserType::Basic, parser_format.value());
|
||||
if (!basic_parser) {
|
||||
return hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser"));
|
||||
}
|
||||
|
||||
// This will add some properties and emit signal_changed().
|
||||
auto basic_parse_status = this->parse_basic_data();
|
||||
if (!basic_parse_status) {
|
||||
return hz::Unexpected(StorageDeviceError::ParseError,
|
||||
std::vformat(_("Cannot parse smartctl output: {}"), std::make_format_args(basic_parse_status.error().message())));
|
||||
}
|
||||
|
||||
auto basic_property_repo = basic_parser->get_property_repository();
|
||||
|
||||
// Make detected type more exact.
|
||||
detect_drive_type_from_properties(basic_property_repo);
|
||||
|
||||
// Set properties from the basic parser.
|
||||
set_property_repository(StoragePropertyProcessor::process_properties(basic_property_repo, get_detected_type()));
|
||||
|
||||
// Read common properties from the repository.
|
||||
read_common_properties();
|
||||
|
||||
|
||||
// Try to parse with a specialized parser based on drive type
|
||||
auto parser_type = SmartctlVersionParser::get_default_parser_type(this->get_detected_type());
|
||||
|
||||
if (parser_type != SmartctlParserType::Basic) {
|
||||
// Try specialized parser
|
||||
auto parser = SmartctlParser::create(parser_type, parser_format.value());
|
||||
DBG_ASSERT_RETURN(parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser.")));
|
||||
|
||||
const auto parse_status = parser->parse(this->full_output_);
|
||||
if (parse_status.has_value()) {
|
||||
// Call this after parse_basic_data(), since it sets parse status to "info".
|
||||
set_parse_status(StorageDevice::ParseStatus::Full);
|
||||
|
||||
// set the full properties.
|
||||
// copy to our drive, overwriting old data.
|
||||
set_property_repository(StoragePropertyProcessor::process_properties(parser->get_property_repository(), get_detected_type()));
|
||||
}
|
||||
}
|
||||
|
||||
if (get_parse_status() != ParseStatus::Full) {
|
||||
// Only basic data available
|
||||
set_parse_status(ParseStatus::Basic);
|
||||
set_property_repository(StoragePropertyProcessor::process_properties(basic_parser->get_property_repository(), get_detected_type()));
|
||||
}
|
||||
|
||||
signal_changed().emit(this); // notify listeners
|
||||
|
||||
// Don't show any GUI warnings on parse failure - it may just be an unsupported
|
||||
// drive (e.g. usb flash disk). Plus, it may flood the string. The data will be
|
||||
// parsed again in Info window, and we show the warnings there.
|
||||
// debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse smartctl output.\n");
|
||||
|
||||
// proper parsing failed. try to at least extract info section
|
||||
// this->basic_output_ = this->full_output_; // complete output here. sometimes it's only the info section
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -443,6 +556,71 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
|
||||
|
||||
|
||||
|
||||
void StorageDevice::read_common_properties()
|
||||
{
|
||||
if (auto prop = property_repository_.lookup_property("smart_support/available"); !prop.empty()) {
|
||||
smart_supported_ = prop.get_value<bool>();
|
||||
}
|
||||
if (auto prop = property_repository_.lookup_property("smart_support/enabled"); !prop.empty()) {
|
||||
smart_enabled_ = prop.get_value<bool>();
|
||||
}
|
||||
if (auto prop = property_repository_.lookup_property("model_name"); !prop.empty()) {
|
||||
model_name_ = prop.get_value<std::string>();
|
||||
}
|
||||
if (auto prop = property_repository_.lookup_property("model_family"); !prop.empty()) {
|
||||
family_name_ = prop.get_value<std::string>();
|
||||
}
|
||||
if (auto prop = property_repository_.lookup_property("serial_number"); !prop.empty()) {
|
||||
serial_number_ = prop.get_value<std::string>();
|
||||
}
|
||||
if (auto prop = property_repository_.lookup_property("user_capacity/bytes/_short"); !prop.empty()) {
|
||||
size_ = prop.readable_value;
|
||||
} else if (auto prop2 = property_repository_.lookup_property("user_capacity/bytes"); !prop.empty()) {
|
||||
size_ = prop2.readable_value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StorageDevice::detect_drive_type_from_properties(const StoragePropertyRepository& property_repo)
|
||||
{
|
||||
auto drive_type_prop = property_repo.lookup_property("_custom/parser_detected_drive_type");
|
||||
if (!drive_type_prop.empty()) {
|
||||
const auto& drive_type_storable_str = drive_type_prop.get_value<std::string>();
|
||||
set_detected_type(StorageDeviceDetectedTypeExt::get_by_storable_name(drive_type_storable_str, StorageDeviceDetectedType::BasicScsi));
|
||||
}
|
||||
|
||||
switch (get_detected_type()) {
|
||||
case StorageDeviceDetectedType::Unknown:
|
||||
set_detected_type(StorageDeviceDetectedType::BasicScsi); // fall back to basic scsi parser
|
||||
break;
|
||||
case StorageDeviceDetectedType::AtaAny:
|
||||
// Find out if it's SSD or HDD
|
||||
{
|
||||
auto rpm_prop = property_repo.lookup_property("rotation_rate");
|
||||
if (rpm_prop.empty() || rpm_prop.get_value<std::int64_t>() == 0) {
|
||||
set_detected_type(StorageDeviceDetectedType::AtaSsd);
|
||||
} else {
|
||||
set_detected_type(StorageDeviceDetectedType::AtaHdd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case StorageDeviceDetectedType::AtaHdd:
|
||||
case StorageDeviceDetectedType::AtaSsd:
|
||||
case StorageDeviceDetectedType::Nvme:
|
||||
case StorageDeviceDetectedType::BasicScsi:
|
||||
case StorageDeviceDetectedType::CdDvd:
|
||||
case StorageDeviceDetectedType::UnsupportedRaid:
|
||||
// leave as is
|
||||
break;
|
||||
case StorageDeviceDetectedType::NeedsExplicitType:
|
||||
DBG_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
StorageDevice::Status StorageDevice::get_smart_status() const
|
||||
{
|
||||
Status status = Status::Unsupported;
|
||||
@@ -580,14 +758,14 @@ std::string StorageDevice::get_device_with_type() const
|
||||
|
||||
|
||||
|
||||
void StorageDevice::set_detected_type(DetectedType t)
|
||||
void StorageDevice::set_detected_type(StorageDeviceDetectedType t)
|
||||
{
|
||||
detected_type_ = t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
StorageDevice::DetectedType StorageDevice::get_detected_type() const
|
||||
StorageDeviceDetectedType StorageDevice::get_detected_type() const
|
||||
{
|
||||
return detected_type_;
|
||||
}
|
||||
@@ -700,13 +878,6 @@ std::string StorageDevice::get_serial_number() const
|
||||
|
||||
|
||||
|
||||
bool StorageDevice::get_is_hdd() const
|
||||
{
|
||||
return hdd_.has_value() && hdd_.value();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StorageDevice::set_info_output(std::string s)
|
||||
{
|
||||
basic_output_ = std::move(s);
|
||||
@@ -824,7 +995,7 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::execute_device_smartctl(cons
|
||||
return hz::Unexpected(StorageDeviceError::CannotExecuteOnVirtual, _("Cannot execute smartctl on a virtual device."));
|
||||
}
|
||||
|
||||
std::string device = get_device();
|
||||
const std::string device = get_device();
|
||||
|
||||
auto smartctl_status = execute_smartctl(device, this->get_device_options(),
|
||||
command_options, smartctl_ex, smartctl_output);
|
||||
@@ -836,9 +1007,11 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::execute_device_smartctl(cons
|
||||
// This means that the old SCSI identify command isn't executed by default,
|
||||
// and there is no information about the device manufacturer/etc. in the output.
|
||||
// We detect this and set the device type to scsi to at least have _some_ info.
|
||||
if (check_type && this->get_detected_type() == DetectedType::Unknown
|
||||
|
||||
// Note: This match works even with JSON (the text output is included in --json=o).
|
||||
if (check_type && this->get_detected_type() == StorageDeviceDetectedType::Unknown
|
||||
&& app_pcre_match("/specify device type with the -d option/mi", smartctl_output)) {
|
||||
this->set_detected_type(DetectedType::NeedsExplicitType);
|
||||
this->set_detected_type(StorageDeviceDetectedType::NeedsExplicitType);
|
||||
}
|
||||
|
||||
return hz::Unexpected(StorageDeviceError::ExecutionError, smartctl_status.error().message());
|
||||
|
||||
+40
-34
@@ -23,6 +23,10 @@ Copyright:
|
||||
#include "smartctl_text_ata_parser.h" // prop_list_t
|
||||
#include "smartctl_executor.h"
|
||||
#include "storage_property_repository.h"
|
||||
#include "storage_device_detected_type.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class StorageDevice;
|
||||
@@ -47,20 +51,6 @@ enum class StorageDeviceError {
|
||||
class StorageDevice {
|
||||
public:
|
||||
|
||||
/// These may be used to force smartctl to a special type, as well as
|
||||
/// to display the correct icon
|
||||
enum class DetectedType {
|
||||
Unknown, // Unknown. Will be autodetected by smartctl.
|
||||
Invalid, // This is set by smartctl executor if it detects invalid type (but not if it's scsi).
|
||||
CdDvd, // CD/DVD/Blu-Ray. Unsupported by smartctl, only basic info is given.
|
||||
Raid, // RAID controller or volume. Unsupported by smartctl, only basic info is given.
|
||||
};
|
||||
|
||||
|
||||
/// This gives a string which can be displayed in outputs
|
||||
[[nodiscard]] static std::string get_type_storable_name(DetectedType type);
|
||||
|
||||
|
||||
/// Statuses of various states
|
||||
enum class Status {
|
||||
Enabled, ///< SMART, AODC
|
||||
@@ -75,8 +65,8 @@ class StorageDevice {
|
||||
|
||||
/// Statuses of various parse states
|
||||
enum class ParseStatus {
|
||||
Full, ///< Fully parsed
|
||||
Basic, ///< Only info section available
|
||||
Full, ///< Parsed with specialized parser
|
||||
Basic, ///< Only info section available, parsed with Basic parser
|
||||
None, ///< No data
|
||||
};
|
||||
|
||||
@@ -88,8 +78,12 @@ class StorageDevice {
|
||||
StorageDevice(std::string dev, std::string type_arg);
|
||||
|
||||
|
||||
// clear everything fetched before.
|
||||
void clear_fetched(bool including_outputs = true);
|
||||
/// Clear fetched smartctl outputs
|
||||
void clear_outputs();
|
||||
|
||||
/// Clear common properties previously imported from repository.
|
||||
void clear_parse_results();
|
||||
|
||||
|
||||
/// Calls "smartctl -i -H -c" (info section, health, capabilities), then parse_basic_data().
|
||||
/// Called during drive detection.
|
||||
@@ -99,14 +93,20 @@ class StorageDevice {
|
||||
|
||||
/// Detects type, smart support, smart status (on / off).
|
||||
/// Note: this will clear all previous properties!
|
||||
[[nodiscard]] hz::ExpectedVoid<StorageDeviceError> parse_basic_data(bool do_set_properties = true, bool emit_signal = true);
|
||||
[[nodiscard]] hz::ExpectedVoid<StorageDeviceError> parse_basic_data();
|
||||
|
||||
|
||||
/// Execute smartctl --all / -x (all sections), get output, parse it (basic data too), fill properties.
|
||||
[[nodiscard]] hz::ExpectedVoid<StorageDeviceError> fetch_full_data_and_parse(const std::shared_ptr<CommandExecutor>& smartctl_ex);
|
||||
|
||||
/// Parse full info. If failed, try to parse it as basic info.
|
||||
[[nodiscard]] hz::ExpectedVoid<StorageDeviceError> try_parse_data();
|
||||
// [[nodiscard]] hz::ExpectedVoid<StorageDeviceError> try_parse_data();
|
||||
|
||||
/// Parse full info.
|
||||
[[nodiscard]] hz::ExpectedVoid<StorageDeviceError> parse_full_data(SmartctlParserType parser_type, SmartctlOutputFormat format);
|
||||
|
||||
/// Try to detect the data type and parse it. This is used when loading virtual drives.
|
||||
[[nodiscard]] hz::ExpectedVoid<StorageDeviceError> parse_any_data_for_virtual();
|
||||
|
||||
/// Get the "fully parsed" flag
|
||||
[[nodiscard]] ParseStatus get_parse_status() const;
|
||||
@@ -119,6 +119,13 @@ class StorageDevice {
|
||||
[[nodiscard]] hz::ExpectedVoid<StorageDeviceError> set_aodc_enabled(bool b, const std::shared_ptr<CommandExecutor>& smartctl_ex);
|
||||
|
||||
|
||||
/// Read common properties (smart supported, smart enabled, etc.) from the repository.
|
||||
void read_common_properties();
|
||||
|
||||
/// Detect drive type based on parsed properties.
|
||||
void detect_drive_type_from_properties(const StoragePropertyRepository& property_repo);
|
||||
|
||||
|
||||
/// Get SMART status
|
||||
[[nodiscard]] Status get_smart_status() const;
|
||||
|
||||
@@ -144,10 +151,10 @@ class StorageDevice {
|
||||
|
||||
|
||||
/// Set detected type
|
||||
void set_detected_type(DetectedType t);
|
||||
void set_detected_type(StorageDeviceDetectedType t);
|
||||
|
||||
/// Get detected type
|
||||
[[nodiscard]] DetectedType get_detected_type() const;
|
||||
[[nodiscard]] StorageDeviceDetectedType get_detected_type() const;
|
||||
|
||||
|
||||
/// Set argument for "-d" smartctl parameter
|
||||
@@ -200,9 +207,6 @@ class StorageDevice {
|
||||
/// \return empty string if not found
|
||||
[[nodiscard]] std::string get_serial_number() const;
|
||||
|
||||
/// Check whether this drive is a rotational HDD.
|
||||
[[nodiscard]] bool get_is_hdd() const;
|
||||
|
||||
|
||||
/// Set "info" output to parse
|
||||
void set_info_output(std::string s);
|
||||
@@ -260,9 +264,6 @@ class StorageDevice {
|
||||
|
||||
private:
|
||||
|
||||
std::string basic_output_; ///< "smartctl --info" output
|
||||
std::string full_output_; ///< "smartctl --all" or "-x" output
|
||||
|
||||
std::string device_; ///< e.g. /dev/sda or pd0. empty if virtual.
|
||||
std::string type_arg_; ///< Device type (for -d smartctl parameter), as specified when adding the device.
|
||||
std::string extra_args_; ///< Extra parameters for smartctl, as specified when adding the device.
|
||||
@@ -273,14 +274,21 @@ class StorageDevice {
|
||||
hz::fs::path virtual_file_; ///< A file (smartctl data) the virtual device was loaded from
|
||||
bool is_manually_added_ = false; ///< StorageDevice doesn't use it, but it's useful for its users.
|
||||
|
||||
ParseStatus parse_status_ = ParseStatus::None; ///< "Fully parsed" flag
|
||||
|
||||
/// Sort of a "lock". If true, the device is not allowed to perform any commands
|
||||
/// except "-l selftest" and maybe "--capabilities" and "--info" (not sure).
|
||||
bool test_is_active_ = false;
|
||||
|
||||
// Note: These are detected through info output
|
||||
DetectedType detected_type_ = DetectedType::Unknown; ///< e.g. type_unknown
|
||||
// Outputs
|
||||
std::string basic_output_; ///< "smartctl --info" output
|
||||
std::string full_output_; ///< "smartctl --all" or "-x" output
|
||||
|
||||
StorageDeviceDetectedType detected_type_ = StorageDeviceDetectedType::Unknown; ///< Detected by basic parser
|
||||
|
||||
ParseStatus parse_status_ = ParseStatus::None; ///< "Fully parsed" flag
|
||||
|
||||
StoragePropertyRepository property_repository_; ///< Parsed data properties
|
||||
|
||||
// Common properties
|
||||
std::optional<bool> smart_supported_; ///< SMART support status
|
||||
std::optional<bool> smart_enabled_; ///< SMART enabled status
|
||||
mutable std::optional<Status> aodc_status_; ///< Cached aodc status.
|
||||
@@ -288,10 +296,8 @@ class StorageDevice {
|
||||
std::optional<std::string> family_name_; ///< Family name
|
||||
std::optional<std::string> serial_number_; ///< Serial number
|
||||
std::optional<std::string> size_; ///< Formatted size
|
||||
std::optional<bool> hdd_; ///< Whether it's a rotational drive (HDD) or something else (SSD, flash, etc.)
|
||||
mutable std::optional<AtaStorageProperty> health_property_; ///< Cached health property.
|
||||
|
||||
StoragePropertyRepository property_repository_; ///< Parsed data properties
|
||||
|
||||
/// Emitted whenever new information is available
|
||||
sigc::signal<void, StorageDevice*> signal_changed_;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*****************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2024 Alexander Shaduri <ashaduri@gmail.com>
|
||||
*****************************************************************************
|
||||
*/
|
||||
#include "storage_device_detected_type.h"
|
||||
@@ -0,0 +1,69 @@
|
||||
/******************************************************************************
|
||||
License: GNU General Public License v3.0 only
|
||||
Copyright:
|
||||
(C) 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com>
|
||||
******************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup applib
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
#ifndef STORAGE_DEVICE_DETECTED_TYPE_H
|
||||
#define STORAGE_DEVICE_DETECTED_TYPE_H
|
||||
|
||||
#include "local_glibmm.h"
|
||||
|
||||
#include "hz/enum_helper.h"
|
||||
|
||||
|
||||
/// These may be used to force smartctl to a special type, as well as
|
||||
/// to display the correct icon
|
||||
enum class StorageDeviceDetectedType {
|
||||
Unknown, ///< Unknown, default state.
|
||||
NeedsExplicitType, ///< This is set by smartctl executor if it detects the need for -d option.
|
||||
AtaAny, ///< Any ATA device (HDD or SSD), before it is detected whether it's HDD or SSD.
|
||||
AtaHdd, ///< ATA HDD
|
||||
AtaSsd, ///< ATA SSD
|
||||
Nvme, ///< NVMe device (SSD)
|
||||
BasicScsi, ///< Basic SCSI device (no smart data). Usually flash drives, etc.
|
||||
CdDvd, ///< CD/DVD/Blu-Ray. Blu-ray is not always detected.
|
||||
UnsupportedRaid, ///< RAID controller or volume. Unsupported by smartctl, only basic info is given.
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Helper structure for enum-related functions
|
||||
struct StorageDeviceDetectedTypeExt
|
||||
: public hz::EnumHelper<
|
||||
StorageDeviceDetectedType,
|
||||
StorageDeviceDetectedTypeExt,
|
||||
Glib::ustring>
|
||||
{
|
||||
static constexpr inline StorageDeviceDetectedType default_value = StorageDeviceDetectedType::Unknown;
|
||||
|
||||
static std::unordered_map<EnumType, std::pair<std::string, Glib::ustring>> build_enum_map()
|
||||
{
|
||||
return {
|
||||
{StorageDeviceDetectedType::Unknown, {"unknown", _("Unknown")}},
|
||||
{StorageDeviceDetectedType::NeedsExplicitType, {"needs_explicit_type", _("Needs Explicit Type")}},
|
||||
{StorageDeviceDetectedType::AtaAny, {"ata_any", _("ATA Device (HDD or SSD)")}},
|
||||
{StorageDeviceDetectedType::AtaHdd, {"ata_hdd", _("ATA HDD")}},
|
||||
{StorageDeviceDetectedType::AtaSsd, {"ata_ssd", _("ATA SSD")}},
|
||||
{StorageDeviceDetectedType::Nvme, {"nvme", _("NVMe Device")}},
|
||||
{StorageDeviceDetectedType::BasicScsi, {"basic_scsi", _("Basic SCSI Device")}},
|
||||
{StorageDeviceDetectedType::CdDvd, {"cd_dvd", _("CD/DVD/Blu-Ray")}},
|
||||
{StorageDeviceDetectedType::UnsupportedRaid, {"unsupported_raid", _("Unsupported RAID Controller or Volume")}},
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
@@ -13,6 +13,7 @@ Copyright:
|
||||
#include <gtkmm.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#include "hz/string_algo.h" // string_split
|
||||
#include "hz/string_num.h"
|
||||
@@ -1162,7 +1163,8 @@ bool GscMainWindow::add_virtual_drive(const std::string& file)
|
||||
drive->set_full_output(output);
|
||||
drive->set_info_output(output); // info can be parsed from full output string too.
|
||||
|
||||
auto parse_error = drive->try_parse_data(); // this will set the type and add the properties
|
||||
// this will set the type and add the properties
|
||||
auto parse_error = drive->parse_any_data_for_virtual();
|
||||
if (!parse_error) {
|
||||
gui_show_error_dialog(_("Cannot interpret SMART data"), parse_error.error().message(), this);
|
||||
return false;
|
||||
|
||||
@@ -347,12 +347,17 @@ class GscMainWindowIconView : public Gtk::IconView {
|
||||
|
||||
Glib::RefPtr<Gdk::Pixbuf> icon;
|
||||
switch(drive->get_detected_type()) {
|
||||
case StorageDevice::DetectedType::CdDvd:
|
||||
case StorageDeviceDetectedType::CdDvd:
|
||||
icon = cddvd_icon;
|
||||
break;
|
||||
case StorageDevice::DetectedType::Unknown: // standard HD icon
|
||||
case StorageDevice::DetectedType::NeedsExplicitType:
|
||||
case StorageDevice::DetectedType::Raid: // TODO a separate icon for this
|
||||
case StorageDeviceDetectedType::Unknown: // standard HD icon
|
||||
case StorageDeviceDetectedType::NeedsExplicitType:
|
||||
case StorageDeviceDetectedType::AtaAny:
|
||||
case StorageDeviceDetectedType::AtaHdd:
|
||||
case StorageDeviceDetectedType::AtaSsd:
|
||||
case StorageDeviceDetectedType::Nvme:
|
||||
case StorageDeviceDetectedType::BasicScsi:
|
||||
case StorageDeviceDetectedType::UnsupportedRaid: // TODO a separate icon
|
||||
icon = hd_icon;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user