diff --git a/src/applib/CMakeLists.txt b/src/applib/CMakeLists.txt index 1366bb7..26977e4 100644 --- a/src/applib/CMakeLists.txt +++ b/src/applib/CMakeLists.txt @@ -60,6 +60,8 @@ target_sources(applib PRIVATE storage_detector_win32.h storage_device.cpp storage_device.h + storage_property_repository.cpp + storage_property_repository.h storage_settings.h warning_colors.h warning_level.h diff --git a/src/applib/ata_storage_property_descr.cpp b/src/applib/ata_storage_property_descr.cpp index c2e39fc..fa596be 100644 --- a/src/applib/ata_storage_property_descr.cpp +++ b/src/applib/ata_storage_property_descr.cpp @@ -1978,9 +1978,10 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) -std::vector StoragePropertyProcessor::process_properties(std::vector properties, AtaStorageAttribute::DiskType disk_type) +StoragePropertyRepository StoragePropertyProcessor::process_properties( + StoragePropertyRepository properties, AtaStorageAttribute::DiskType disk_type) { - for (auto& p : properties) { + for (auto& p : properties.get_properties_ref()) { ata_storage_property_autoset_description(p, disk_type); ata_storage_property_autoset_warning(p); storage_property_autoset_warning_descr(p); // append warning to description diff --git a/src/applib/ata_storage_property_descr.h b/src/applib/ata_storage_property_descr.h index 800599e..3184270 100644 --- a/src/applib/ata_storage_property_descr.h +++ b/src/applib/ata_storage_property_descr.h @@ -12,15 +12,15 @@ Copyright: #ifndef ATA_STORAGE_PROPERTY_DESCR_H #define ATA_STORAGE_PROPERTY_DESCR_H -#include "ata_storage_property.h" - +#include "storage_property_repository.h" class StoragePropertyProcessor { public: /// Set descriptions, warnings, etc... on properties, and return them. - static std::vector process_properties(std::vector properties, AtaStorageAttribute::DiskType disk_type); + static StoragePropertyRepository process_properties(StoragePropertyRepository properties, + AtaStorageAttribute::DiskType disk_type); }; diff --git a/src/applib/examples/example_smartctl_parser.cpp b/src/applib/examples/example_smartctl_parser.cpp index a3ccf00..031a65b 100644 --- a/src/applib/examples/example_smartctl_parser.cpp +++ b/src/applib/examples/example_smartctl_parser.cpp @@ -48,7 +48,7 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } - const std::vector& props = parser.get_properties(); + const std::vector& props = parser.get_property_repository().get_properties(); for(const auto& prop : props) { debug_out_dump("app", prop << "\n"); } diff --git a/src/applib/selftest.cpp b/src/applib/selftest.cpp index e1b98a0..556d0b6 100644 --- a/src/applib/selftest.cpp +++ b/src/applib/selftest.cpp @@ -76,7 +76,7 @@ std::chrono::seconds SelfTest::get_min_duration_seconds() const case TestType::conveyance: prop_name = "ata_smart_data/self_test/polling_minutes/conveyance"; break; } - const AtaStorageProperty p = drive_->lookup_property(prop_name, + const AtaStorageProperty p = drive_->get_property_repository().lookup_property(prop_name, AtaStorageProperty::Section::data, AtaStorageProperty::SubSection::capabilities); // p stores it as uint64_t @@ -103,7 +103,7 @@ bool SelfTest::is_supported() const case TestType::conveyance: prop_name = "ata_smart_data/capabilities/conveyance_self_test_supported"; break; } - const AtaStorageProperty p = drive_->lookup_property(prop_name, AtaStorageProperty::Section::internal); + const AtaStorageProperty p = drive_->get_property_repository().lookup_property(prop_name, AtaStorageProperty::Section::internal); return (!p.empty() && p.get_value()); } @@ -185,7 +185,7 @@ std::string SelfTest::force_stop(const std::shared_ptr& smartct // any command (e.g. "--abort") will abort it. If it has "Suspend Offline...", // there's no way to abort such test. if (type_ == TestType::immediate_offline) { - const AtaStorageProperty p = drive_->lookup_property( + const AtaStorageProperty p = drive_->get_property_repository().lookup_property( "ata_smart_data/capabilities/offline_is_aborted_upon_new_cmd", AtaStorageProperty::Section::internal); if (!p.empty() && p.get_value()) { // if empty, give a chance to abort anyway. return _("Aborting this test is unsupported by the drive."); @@ -250,13 +250,13 @@ std::string SelfTest::update(const std::shared_ptr& smartctl_ex if (!parse_status) { return Glib::ustring::compose(_("Cannot parse smartctl output: %1"), parse_status.error().message()); } - auto properties = StoragePropertyProcessor::process_properties(parser->get_properties(), disk_type); + auto property_repo = StoragePropertyProcessor::process_properties(parser->get_property_repository(), disk_type); // Note: Since the self-test log is sometimes late // and in undetermined order (sorting by hours is too rough), // we use the "self-test status" capability. AtaStorageProperty p; - for (const auto& e : properties) { + for (const auto& e : property_repo.get_properties()) { // if (e.section != AtaStorageProperty::Section::data || e.subsection != AtaStorageProperty::SubSection::selftest_log if (e.section != AtaStorageProperty::Section::internal || !e.is_value_type() || e.get_value().test_num != 0 diff --git a/src/applib/smartctl_parser.cpp b/src/applib/smartctl_parser.cpp index 3eaa502..8f09437 100644 --- a/src/applib/smartctl_parser.cpp +++ b/src/applib/smartctl_parser.cpp @@ -62,7 +62,7 @@ hz::ExpectedValue SmartctlParser::det -const std::vector& SmartctlParser::get_properties() const +const StoragePropertyRepository& SmartctlParser::get_property_repository() const { return properties_; } @@ -73,7 +73,7 @@ const std::vector& SmartctlParser::get_properties() const // Yes, there's no place for this in the Parser, but whatever... void SmartctlParser::add_property(AtaStorageProperty p) { - properties_.push_back(std::move(p)); + properties_.add_property(std::move(p)); } diff --git a/src/applib/smartctl_parser.h b/src/applib/smartctl_parser.h index 1a16435..784551d 100644 --- a/src/applib/smartctl_parser.h +++ b/src/applib/smartctl_parser.h @@ -19,7 +19,7 @@ Copyright: #include "ata_storage_property.h" #include "smartctl_parser_types.h" #include "hz/error_container.h" - +#include "storage_property_repository.h" enum class SmartctlParserError { @@ -78,8 +78,8 @@ class SmartctlParser { [[nodiscard]] static hz::ExpectedValue detect_output_format(std::string_view smartctl_output); - /// Get parse result properties - [[nodiscard]] const std::vector& get_properties() const; + /// Get parsed properties. + [[nodiscard]] const StoragePropertyRepository& get_property_repository() const; protected: @@ -90,7 +90,7 @@ class SmartctlParser { private: - std::vector properties_; ///< Parsed data properties + StoragePropertyRepository properties_; ///< Parsed data properties }; diff --git a/src/applib/smartctl_text_ata_parser.cpp b/src/applib/smartctl_text_ata_parser.cpp index 6112ed6..8deaf81 100644 --- a/src/applib/smartctl_text_ata_parser.cpp +++ b/src/applib/smartctl_text_ata_parser.cpp @@ -495,7 +495,7 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse_section_info_ } else if (app_pcre_match("/^Rotation Rate$/mi", p.reported_name)) { p.set_name(p.reported_name, "rotation_rate", "Rotation Rate"); - p.value = p.reported_value; // string-type value + p.value = hz::string_to_number_nolocale(p.reported_value, false); } else if (app_pcre_match("/^Form Factor$/mi", p.reported_name)) { p.set_name(p.reported_name, "form_factor/name", "Form Factor"); diff --git a/src/applib/smartctl_text_basic_parser.cpp b/src/applib/smartctl_text_basic_parser.cpp index 84ea22d..ecf6183 100644 --- a/src/applib/smartctl_text_basic_parser.cpp +++ b/src/applib/smartctl_text_basic_parser.cpp @@ -15,7 +15,7 @@ Copyright: //#include // #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get(). -//#include "hz/string_algo.h" // string_* +#include "hz/string_algo.h" // string_* //#include "hz/string_num.h" // string_is_numeric, number_to_string //#include "hz/debug.h" // debug_* @@ -23,14 +23,185 @@ Copyright: //#include "smartctl_text_ata_parser.h" //#include "ata_storage_property_descr.h" // #include "warning_colors.h" -//#include "smartctl_version_parser.h" +#include "smartctl_version_parser.h" #include "smartctl_text_basic_parser.h" - - +#include "hz/string_num.h" +#include "smartctl_text_parser_helper.h" hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string_view smartctl_output) { + // perform any2unix + std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_output)); + + if (output.empty()) { + debug_out_warn("app", DBG_FUNC_MSG << "Empty string passed as an argument. Returning.\n"); + return hz::Unexpected(SmartctlParserError::EmptyInput, "Smartctl data is empty."); + } + + // Version + std::string version, version_full; + if (!SmartctlVersionParser::parse_version_text(output, version, version_full)) { // is this smartctl data at all? + debug_out_warn("app", DBG_FUNC_MSG << "Cannot extract version information. Returning.\n"); + return hz::Unexpected(SmartctlParserError::NoVersion, "Cannot extract smartctl version information."); + } + { + AtaStorageProperty p; + p.set_name("Smartctl version", "smartctl/version/_merged", "Smartctl Version"); + p.reported_value = version; + p.value = p.reported_value; // string-type value + p.section = AtaStorageProperty::Section::info; // add to info section + add_property(p); + } + { + AtaStorageProperty p; + p.set_name("Smartctl version", "smartctl/version/_merged_full", "Smartctl Version"); + p.reported_value = version_full; + p.value = p.reported_value; // string-type value + p.section = AtaStorageProperty::Section::info; // add to info section + add_property(p); + } + + bool is_raid = false; + + // Detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch). + // Sample output line 1 (encountered on a CDRW drive): + // SMART support is: Unavailable - Packet Interface Devices [this device: CD/DVD] don't support ATA SMART + // Sample output line 2 (encountered on a BDRW drive): + // Device type: CD/DVD + // NOTE: CD/DVD detection does not work in "-d scsi" mode. + 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.reported_value = "CD/DVD"; + p.value = p.reported_value; // TODO canonicalize + p.section = AtaStorageProperty::Section::info; // add to info section + add_property(p); + + // This was encountered on a csmi soft-raid under windows with pd0. + // The device reported that it had smart supported and enabled. + // 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.reported_value = "RAID"; + p.value = p.reported_value; // TODO canonicalize + p.section = AtaStorageProperty::Section::info; // add to info section + add_property(p); + + is_raid = true; + } + + bool smart_supported = true; + bool smart_enabled = true; + + // RAID volume may report that it has SMART, but it obviously doesn't. + if (is_raid) { + smart_supported = false; + smart_enabled = false; + + } else { + // Note: We don't use SmartctlTextAtaParser here, because this information + // may be in some other format. If this information is valid, only then it's + // passed to SmartctlTextAtaParser. + // Compared to SmartctlTextAtaParser, this one is much looser. + + // Don't put complete messages here - they change across smartctl versions. + if (app_pcre_match("/^SMART support is:[ \\t]*Unavailable/mi", output) // cdroms output this + || app_pcre_match("/Device does not support SMART/mi", output) // usb flash drives, non-smart hds + || app_pcre_match("/Device Read Identity Failed/mi", output)) { // solaris scsi, unsupported by smartctl (maybe others?) + smart_supported = false; + smart_enabled = false; + + } else if (app_pcre_match("/^SMART support is:[ \\t]*Available/mi", output) + || app_pcre_match("/^SMART support is:[ \\t]*Ambiguous/mi", output)) { + smart_supported = true; + + if (app_pcre_match("/^SMART support is:[ \\t]*Enabled/mi", output)) { + smart_enabled = true; + } else if (app_pcre_match("/^SMART support is:[ \\t]*Disabled/mi", output)) { + smart_enabled = false; + } + } + } + + { + AtaStorageProperty p; + p.set_name("SMART Supported", "_text_only/smart_supported", "SMART Supported"); + p.value = smart_supported; + p.section = AtaStorageProperty::Section::info; // add to info section + add_property(p); + } + { + AtaStorageProperty p; + p.set_name("SMART Enabled", "_text_only/smart_enabled", "SMART Enabled"); + p.value = smart_enabled; + p.section = AtaStorageProperty::Section::info; // add to info section + add_property(p); + } + + + std::string model; + if (app_pcre_match("/^Device Model:[ \\t]*(.*)$/mi", output, &model)) { // HDDs and CDROMs + model = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(model), ' '); + AtaStorageProperty p; + p.set_name("Device Model", "model_name", "Device Model"); + p.value = p.reported_value; // string-type value + add_property(p); + + } else if (app_pcre_match("/^(?:Device|Product):[ \\t]*(.*)$/mi", output, &model)) { // usb flash drives + model = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(model), ' '); + AtaStorageProperty p; + p.set_name("Device Model", "model_name", "Device Model"); + p.value = model; + add_property(p); + } + + + std::string family; // this is from smartctl's database + if (app_pcre_match("/^Model Family:[ \\t]*(.*)$/mi", output, &family)) { + family = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(family), ' '); + AtaStorageProperty p; + p.set_name("Model Family", "model_family", "Model Family"); + p.value = family; + add_property(p); + } + + std::string serial; + if (app_pcre_match("/^Serial Number:[ \\t]*(.*)$/mi", output, &serial)) { + serial = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(serial), ' '); + AtaStorageProperty p; + p.set_name("Serial Number", "serial_number", "Serial Number"); + p.value = serial; + add_property(p); + } + + std::string rpm_str; + if (app_pcre_match("/^Rotation Rate:[ \\t]*(.*)$/mi", output, &rpm_str)) { + AtaStorageProperty p; + p.set_name("Rotation Rate", "rotation_rate", "Rotation Rate"); + p.reported_value = rpm_str; + p.value = hz::string_to_number_nolocale(rpm_str, false); + p.section = AtaStorageProperty::Section::info; // add to info section + add_property(p); + } + + + // Note: this property is present since 5.33. + std::string size; + if (app_pcre_match("/^User Capacity:[ \\t]*(.*)$/mi", output, &size)) { + int64_t bytes = 0; + std::string readable_size = SmartctlTextParserHelper::parse_byte_size(size, bytes, false); + AtaStorageProperty p; + p.set_name("User Capacity", "user_capacity/bytes", "Capacity"); + p.reported_value = size; + p.value = bytes; + p.readable_value = readable_size; + p.section = AtaStorageProperty::Section::info; // add to info section + add_property(p); + } + return {}; } diff --git a/src/applib/storage_device.cpp b/src/applib/storage_device.cpp index 42442dc..af6e8e9 100644 --- a/src/applib/storage_device.cpp +++ b/src/applib/storage_device.cpp @@ -95,7 +95,7 @@ void StorageDevice::clear_fetched(bool including_outputs) { size_.reset(); health_property_.reset(); - properties_.clear(); + property_repository_.clear(); } @@ -140,95 +140,59 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si { this->clear_fetched(false); // clear everything fetched before, except outputs - if (this->info_output_.empty()) { - debug_out_error("app", DBG_FUNC_MSG << "String to parse is empty.\n"); - return _("Cannot read information from an empty string."); + AtaStorageAttribute::DiskType disk_type = AtaStorageAttribute::DiskType::Any; + + // Try the basic parser first. If it succeeds, use the specialized parser. + auto basic_parser = SmartctlParser::create(SmartctlParserType::TextBasic); + DBG_ASSERT_RETURN(basic_parser, "Cannot create parser"); + + auto parse_status = basic_parser->parse(this->get_info_output()); + if (!parse_status) { + return Glib::ustring::compose(_("Cannot parse smartctl output: %1"), parse_status.error().message()); } - std::string version, version_full; - if (!SmartctlVersionParser::parse_version_text(this->info_output_, version, version_full)) // is this smartctl data at all? - return _("Cannot get smartctl version information."); + auto basic_property_repo = basic_parser->get_property_repository(); - // Detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch). - // Sample output line 1 (encountered on a CDRW drive): - // SMART support is: Unavailable - Packet Interface Devices [this device: CD/DVD] don't support ATA SMART - // Sample output line 2 (encountered on a BDRW drive): - // Device type: CD/DVD - // NOTE: CD/DVD detection does not work in "-d scsi" mode. - if (app_pcre_match("/this device: CD\\/DVD/mi", info_output_) - || app_pcre_match("/^Device type:\\s+CD\\/DVD/mi", info_output_)) { - debug_out_dump("app", "Drive " << get_device_with_type() << " seems to be a CD/DVD device.\n"); - this->set_detected_type(DetectedType::cddvd); - - // This was encountered on a csmi soft-raid under windows with pd0. - // The device reported that it had smart supported and enabled. - // Product: Raid 5 Volume - } else if (app_pcre_match("/Product:[ \\t]*Raid/mi", info_output_)) { - debug_out_dump("app", "Drive " << get_device_with_type() << " seems to be a RAID volume/controller.\n"); - this->set_detected_type(DetectedType::raid); - } - - // RAID volume may report that it has SMART, but it obviously doesn't. - if (get_detected_type() == DetectedType::raid) { - smart_supported_ = false; - smart_enabled_ = false; - - } else { - // Note: We don't use SmartctlTextAtaParser here, because this information - // may be in some other format. If this information is valid, only then it's - // passed to SmartctlTextAtaParser. - // Compared to SmartctlTextAtaParser, this one is much looser. - - // Don't put complete messages here - they change across smartctl versions. - if (app_pcre_match("/^SMART support is:[ \\t]*Unavailable/mi", info_output_) // cdroms output this - || app_pcre_match("/Device does not support SMART/mi", info_output_) // usb flash drives, non-smart hds - || app_pcre_match("/Device Read Identity Failed/mi", info_output_)) { // solaris scsi, unsupported by smartctl (maybe others?) - smart_supported_ = false; - smart_enabled_ = false; - - } else if (app_pcre_match("/^SMART support is:[ \\t]*Available/mi", info_output_) - || app_pcre_match("/^SMART support is:[ \\t]*Ambiguous/mi", info_output_)) { - smart_supported_ = true; - - if (app_pcre_match("/^SMART support is:[ \\t]*Enabled/mi", info_output_)) { - smart_enabled_ = true; - } else if (app_pcre_match("/^SMART support is:[ \\t]*Disabled/mi", info_output_)) { - smart_enabled_ = false; - } + 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(); + 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); } } - std::string model; - if (app_pcre_match("/^Device Model:[ \\t]*(.*)$/mi", info_output_, &model)) { // HD's and cdroms - model_name_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(model), ' '); - - } else if (app_pcre_match("/^(?:Device|Product):[ \\t]*(.*)$/mi", info_output_, &model)) { // usb flash drives - model_name_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(model), ' '); + { + auto rpm_prop = basic_property_repo.lookup_property("rotation_rate"); + if (!rpm_prop.empty()) { + auto rpm = rpm_prop.get_value(); + this->hdd_ = rpm > 0; + } + if (hdd_.has_value()) { + disk_type = hdd_.value() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd; + } } - - std::string family; // this is from smartctl's database - if (app_pcre_match("/^Model Family:[ \\t]*(.*)$/mi", info_output_, &family)) { - family_name_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(family), ' '); + if (auto prop = basic_property_repo.lookup_property("_text_only/smart_supported"); !prop.empty()) { + smart_supported_ = prop.get_value(); } - - std::string serial; - if (app_pcre_match("/^Serial Number:[ \\t]*(.*)$/mi", info_output_, &serial)) { - serial_number_ = hz::string_remove_adjacent_duplicates_copy(hz::string_trim_copy(serial), ' '); + if (auto prop = basic_property_repo.lookup_property("_text_only/smart_enabled"); !prop.empty()) { + smart_enabled_ = prop.get_value(); } - - std::string rpm_str; - if (app_pcre_match("/^Rotation Rate:[ \\t]*(.*)$/mi", info_output_, &rpm_str)) { - const int rpm = hz::string_to_number_nolocale(rpm_str, false); - hdd_ = rpm > 0; + if (auto prop = basic_property_repo.lookup_property("model_name"); !prop.empty()) { + model_name_ = prop.get_value(); } - - - // Note: this property is present since 5.33. - std::string size; - if (app_pcre_match("/^User Capacity:[ \\t]*(.*)$/mi", info_output_, &size)) { - int64_t bytes = 0; - size_ = SmartctlTextParserHelper::parse_byte_size(size, bytes, false); + if (auto prop = basic_property_repo.lookup_property("model_family"); !prop.empty()) { + family_name_ = prop.get_value(); + } + if (auto prop = basic_property_repo.lookup_property("serial_number"); !prop.empty()) { + serial_number_ = prop.get_value(); + } + if (auto prop = basic_property_repo.lookup_property("user_capacity/bytes"); !prop.empty()) { + size_ = prop.readable_value; } @@ -236,16 +200,12 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si // Note that this may try to parse data the second time (it may already have // been parsed by parse_data() which failed at it). if (do_set_properties) { - AtaStorageAttribute::DiskType disk_type = AtaStorageAttribute::DiskType::Any; - if (hdd_.has_value()) { - disk_type = hdd_.value() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd; - } - auto parser = SmartctlParser::create(SmartctlParserType::TextAta); DBG_ASSERT_RETURN(parser, "Cannot create parser"); if (parser->parse(this->info_output_)) { // try to parse it - this->set_properties(StoragePropertyProcessor::process_properties(parser->get_properties(), disk_type)); // copy to our drive, overwriting old data + this->set_property_repository( + StoragePropertyProcessor::process_properties(parser->get_property_repository(), disk_type)); // copy to our drive, overwriting old data } } @@ -349,7 +309,7 @@ std::string StorageDevice::parse_data() // set the full properties. // copy to our drive, overwriting old data. - this->set_properties(StoragePropertyProcessor::process_properties(parser->get_properties(), disk_type)); + this->set_property_repository(StoragePropertyProcessor::process_properties(parser->get_property_repository(), disk_type)); signal_changed().emit(this); // notify listeners @@ -503,7 +463,7 @@ StorageDevice::Status StorageDevice::get_aodc_status() const bool aodc_supported = false; int found = 0; - for (const auto& p : properties_) { + for (const auto& p : property_repository_.get_properties()) { if (p.section == AtaStorageProperty::Section::internal) { if (p.generic_name == "ata_smart_data/offline_data_collection/status/value/_parsed") { // if this is not present at all, we set the unknown status. status = (p.get_value() ? Status::enabled : Status::disabled); @@ -545,7 +505,7 @@ AtaStorageProperty StorageDevice::get_health_property() const if (health_property_.has_value()) // cached return value return health_property_.value(); - AtaStorageProperty p = this->lookup_property("smart_status/passed", + AtaStorageProperty p = property_repository_.lookup_property("smart_status/passed", AtaStorageProperty::Section::data, AtaStorageProperty::SubSection::health); if (!p.empty()) health_property_ = p; // store to cache @@ -684,25 +644,9 @@ std::string StorageDevice::get_virtual_filename() const -const std::vector& StorageDevice::get_properties() const +const StoragePropertyRepository& StorageDevice::get_property_repository() const { - return properties_; -} - - - -AtaStorageProperty StorageDevice::lookup_property(const std::string& generic_name, AtaStorageProperty::Section section, AtaStorageProperty::SubSection subsection) const -{ - for (const auto& p : properties_) { - if (section != AtaStorageProperty::Section::unknown && p.section != section) - continue; - if (subsection != AtaStorageProperty::SubSection::unknown && p.subsection != subsection) - continue; - - if (p.generic_name == generic_name) - return p; - } - return {}; // check with .empty() + return property_repository_; } @@ -890,10 +834,9 @@ void StorageDevice::set_parse_status(ParseStatus value) } - -void StorageDevice::set_properties(std::vector props) +void StorageDevice::set_property_repository(StoragePropertyRepository repository) { - properties_ = std::move(props); + property_repository_ = std::move(repository); } diff --git a/src/applib/storage_device.h b/src/applib/storage_device.h index 8192117..910482e 100644 --- a/src/applib/storage_device.h +++ b/src/applib/storage_device.h @@ -22,7 +22,7 @@ Copyright: #include "ata_storage_property.h" #include "smartctl_text_ata_parser.h" // prop_list_t #include "smartctl_executor.h" - +#include "storage_property_repository.h" class StorageDevice; @@ -90,7 +90,7 @@ class StorageDevice { /// Note: this will clear the non-basic properties! std::string parse_basic_data(bool do_set_properties = true, bool emit_signal = true); - /// Execute smartctl --all (all sections), get output, parse it (basic data too), fill properties. + /// Execute smartctl --all / -x (all sections), get output, parse it (basic data too), fill properties. std::string fetch_data_and_parse(const std::shared_ptr& smartctl_ex); // returns error message on error. // Parses full info. If failed, try to parse it as basic info. @@ -111,122 +111,116 @@ class StorageDevice { /// Get SMART status - Status get_smart_status() const; + [[nodiscard]] Status get_smart_status() const; /// Get AODC status - Status get_aodc_status() const; + [[nodiscard]] Status get_aodc_status() const; /// Get format size string, or an empty string on error. - std::string get_device_size_str() const; + [[nodiscard]] std::string get_device_size_str() const; /// Get the overall health property - AtaStorageProperty get_health_property() const; + [[nodiscard]] AtaStorageProperty get_health_property() const; /// Get device name (e.g. /dev/sda) - std::string get_device() const; + [[nodiscard]] std::string get_device() const; /// Get device name without path. For example, "sda". - std::string get_device_base() const; + [[nodiscard]] std::string get_device_base() const; /// Get device name for display purposes (with a type argument in parentheses) - std::string get_device_with_type() const; + [[nodiscard]] std::string get_device_with_type() const; /// Set detected type void set_detected_type(DetectedType t); /// Get detected type - DetectedType get_detected_type() const; + [[nodiscard]] DetectedType get_detected_type() const; /// Set argument for "-d" smartctl parameter void set_type_argument(std::string arg); /// Get argument for "-d" smartctl parameter - std::string get_type_argument() const; + [[nodiscard]] std::string get_type_argument() const; /// Set extra arguments smartctl void set_extra_arguments(std::string args); /// Get extra arguments smartctl - std::string get_extra_arguments() const; + [[nodiscard]] std::string get_extra_arguments() const; /// Set windows drive letters for this drive void set_drive_letters(std::map letters_volnames); /// Get windows drive letters for this drive - const std::map& get_drive_letters() const; + [[nodiscard]] const std::map& get_drive_letters() const; /// Get comma-separated win32 drive letters (if present) - std::string format_drive_letters(bool with_volnames) const; + [[nodiscard]] std::string format_drive_letters(bool with_volnames) const; /// Get "virtual" status - bool get_is_virtual() const; + [[nodiscard]] bool get_is_virtual() const; /// If the device is virtual, return its file - hz::fs::path get_virtual_file() const; + [[nodiscard]] hz::fs::path get_virtual_file() const; /// Get only the filename portion of a virtual file - std::string get_virtual_filename() const; + [[nodiscard]] std::string get_virtual_filename() const; - /// Get all detected properties - const std::vector& get_properties() const; - - - /// Find a property - AtaStorageProperty lookup_property(const std::string& generic_name, - AtaStorageProperty::Section section = AtaStorageProperty::Section::unknown, // if unknown, search in all. - AtaStorageProperty::SubSection subsection = AtaStorageProperty::SubSection::unknown) const; + /// Get properties + [[nodiscard]] const StoragePropertyRepository& get_property_repository() const; /// Get model name. /// \return empty string if not found - std::string get_model_name() const; + [[nodiscard]] std::string get_model_name() const; /// Get family name. /// \return empty string if not found - std::string get_family_name() const; + [[nodiscard]] std::string get_family_name() const; /// Get serial number. /// \return empty string if not found - std::string get_serial_number() const; + [[nodiscard]] std::string get_serial_number() const; /// Check whether this drive is a rotational HDD. - bool get_is_hdd() const; + [[nodiscard]] bool get_is_hdd() const; /// Set "info" output to parse void set_info_output(std::string s); /// Get "info" output to parse - std::string get_info_output() const; + [[nodiscard]] std::string get_info_output() const; /// Set "full" output to parse void set_full_output(std::string s); /// Get "full" output to parse - std::string get_full_output() const; + [[nodiscard]] std::string get_full_output() const; /// Set "manually added" flag void set_is_manually_added(bool b); /// Get "manually added" flag - bool get_is_manually_added() const; + [[nodiscard]] bool get_is_manually_added() const; /// Set "test is active" flag, emit the "changed" signal if needed. void set_test_is_active(bool b); /// Get "test is active" flag - bool get_test_is_active() const; + [[nodiscard]] bool get_test_is_active() const; /// Get the recommended filename to save output to. Includes model and date. @@ -234,7 +228,7 @@ class StorageDevice { /// Get final smartctl options for this device from config and type info. - std::string get_device_options() const; + [[nodiscard]] std::string get_device_options() const; /// Execute smartctl on this device. Nothing is modified in this class. @@ -244,7 +238,7 @@ class StorageDevice { /// Emitted whenever new information is available - sigc::signal& signal_changed(); + [[nodiscard]] sigc::signal& signal_changed(); protected: @@ -252,8 +246,8 @@ class StorageDevice { /// Set the "fully parsed" flag void set_parse_status(ParseStatus value); - /// Set parsed properties - void set_properties(std::vector props); + /// Set properties + void set_property_repository(StoragePropertyRepository repository); private: @@ -289,7 +283,7 @@ class StorageDevice { std::optional hdd_; ///< Whether it's a rotational drive (HDD) or something else (SSD, flash, etc...) mutable std::optional health_property_; ///< Cached health property. - std::vector properties_; ///< Smart properties. Detected through full output. + StoragePropertyRepository property_repository_; ///< Parsed data properties /// Emitted whenever new information is available sigc::signal signal_changed_; diff --git a/src/applib/storage_property_repository.cpp b/src/applib/storage_property_repository.cpp new file mode 100644 index 0000000..b344a9e --- /dev/null +++ b/src/applib/storage_property_repository.cpp @@ -0,0 +1,61 @@ +/****************************************************************************** +License: GNU General Public License v3.0 only +Copyright: + (C) 2008 - 2024 Alexander Shaduri +******************************************************************************/ + +#include "storage_property_repository.h" + + + + +const std::vector& StoragePropertyRepository::get_properties() const +{ + return properties_; +} + + + +std::vector& StoragePropertyRepository::get_properties_ref() +{ + return properties_; +} + + + +AtaStorageProperty StoragePropertyRepository::lookup_property( + const std::string& generic_name, AtaStorageProperty::Section section, AtaStorageProperty::SubSection subsection) const +{ + for (const auto& p : properties_) { + if (section != AtaStorageProperty::Section::unknown && p.section != section) + continue; + if (subsection != AtaStorageProperty::SubSection::unknown && p.subsection != subsection) + continue; + + if (p.generic_name == generic_name) + return p; + } + return {}; // check with .empty() +} + + + +void StoragePropertyRepository::set_properties(std::vector properties) +{ + properties_ = std::move(properties); +} + + + +void StoragePropertyRepository::add_property(AtaStorageProperty property) +{ + properties_.push_back(std::move(property)); +} + + + +void StoragePropertyRepository::clear() +{ + properties_.clear(); +} + diff --git a/src/applib/storage_property_repository.h b/src/applib/storage_property_repository.h new file mode 100644 index 0000000..f68024e --- /dev/null +++ b/src/applib/storage_property_repository.h @@ -0,0 +1,47 @@ +/****************************************************************************** +License: GNU General Public License v3.0 only +Copyright: + (C) 2008 - 2024 Alexander Shaduri +******************************************************************************/ +#ifndef STORAGE_PROPERTY_REPOSITORY_H +#define STORAGE_PROPERTY_REPOSITORY_H + +#include +#include "ata_storage_property.h" + + +/// A repository of properties. Used to store and look up drive properties. +class StoragePropertyRepository { + public: + + /// Get all properties + [[nodiscard]] const std::vector& get_properties() const; + + /// Get all properties + [[nodiscard]] std::vector& get_properties_ref(); + + + /// Find a property + [[nodiscard]] AtaStorageProperty lookup_property(const std::string& generic_name, + AtaStorageProperty::Section section = AtaStorageProperty::Section::unknown, // if unknown, search in all. + AtaStorageProperty::SubSection subsection = AtaStorageProperty::SubSection::unknown) const; + + + /// Set properties + void set_properties(std::vector properties); + + /// Add a property + void add_property(AtaStorageProperty property); + + /// Clear all properties + void clear(); + + + private: + + std::vector properties_; ///< Parsed data properties + +}; + + +#endif // STORAGE_PROPERTY_REPOSITORY_H diff --git a/src/gsc_info_window.cpp b/src/gsc_info_window.cpp index 32d4ac8..535cee6 100644 --- a/src/gsc_info_window.cpp +++ b/src/gsc_info_window.cpp @@ -417,7 +417,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests // Fill the tabs with info // we need reference here - we take addresses of the elements - const auto& props = drive->get_properties(); // it's a vector + const auto& props = drive->get_property_repository().get_properties(); // it's a vector fill_ui_general(props); fill_ui_attributes(props); diff --git a/src/gsc_main_window_iconview.h b/src/gsc_main_window_iconview.h index b37b2da..e92fd5b 100644 --- a/src/gsc_main_window_iconview.h +++ b/src/gsc_main_window_iconview.h @@ -312,7 +312,7 @@ class GscMainWindowIconView : public Gtk::IconView { } AtaStorageProperty scan_time_prop; if (drive->get_is_virtual()) { - scan_time_prop = drive->lookup_property("local_time/asctime"); + scan_time_prop = drive->get_property_repository().lookup_property("local_time/asctime"); if (!scan_time_prop.empty() && !scan_time_prop.get_value().empty()) { name += "\n" + Glib::Markup::escape_text(scan_time_prop.get_value()); }