Separate out basic text parser from StorageDevice.

Store all properties in StoragePropertyRepository.
This commit is contained in:
Alexander Shaduri
2024-03-20 20:29:19 +04:00
parent 9889f8792f
commit 21267acb8c
15 changed files with 390 additions and 171 deletions
+2
View File
@@ -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
+3 -2
View File
@@ -1978,9 +1978,10 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p)
std::vector<AtaStorageProperty> StoragePropertyProcessor::process_properties(std::vector<AtaStorageProperty> 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
+3 -3
View File
@@ -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<AtaStorageProperty> process_properties(std::vector<AtaStorageProperty> properties, AtaStorageAttribute::DiskType disk_type);
static StoragePropertyRepository process_properties(StoragePropertyRepository properties,
AtaStorageAttribute::DiskType disk_type);
};
@@ -48,7 +48,7 @@ int main(int argc, char* argv[])
return EXIT_FAILURE;
}
const std::vector<AtaStorageProperty>& props = parser.get_properties();
const std::vector<AtaStorageProperty>& props = parser.get_property_repository().get_properties();
for(const auto& prop : props) {
debug_out_dump("app", prop << "\n");
}
+5 -5
View File
@@ -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<bool>());
}
@@ -185,7 +185,7 @@ std::string SelfTest::force_stop(const std::shared_ptr<CommandExecutor>& 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<bool>()) { // 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<CommandExecutor>& 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<AtaStorageSelftestEntry>() || e.get_value<AtaStorageSelftestEntry>().test_num != 0
+2 -2
View File
@@ -62,7 +62,7 @@ hz::ExpectedValue<SmartctlParserFormat, SmartctlParserError> SmartctlParser::det
const std::vector<AtaStorageProperty>& SmartctlParser::get_properties() const
const StoragePropertyRepository& SmartctlParser::get_property_repository() const
{
return properties_;
}
@@ -73,7 +73,7 @@ const std::vector<AtaStorageProperty>& 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));
}
+4 -4
View File
@@ -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<SmartctlParserFormat, SmartctlParserError> detect_output_format(std::string_view smartctl_output);
/// Get parse result properties
[[nodiscard]] const std::vector<AtaStorageProperty>& get_properties() const;
/// Get parsed properties.
[[nodiscard]] const StoragePropertyRepository& get_property_repository() const;
protected:
@@ -90,7 +90,7 @@ class SmartctlParser {
private:
std::vector<AtaStorageProperty> properties_; ///< Parsed data properties
StoragePropertyRepository properties_; ///< Parsed data properties
};
+1 -1
View File
@@ -495,7 +495,7 @@ hz::ExpectedVoid<SmartctlParserError> 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<int64_t>(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");
+175 -4
View File
@@ -15,7 +15,7 @@ Copyright:
//#include <utility>
// #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<SmartctlParserError> 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<int>(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 {};
}
+52 -109
View File
@@ -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<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);
}
}
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<int64_t>();
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<bool>();
}
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<bool>();
}
std::string rpm_str;
if (app_pcre_match("/^Rotation Rate:[ \\t]*(.*)$/mi", info_output_, &rpm_str)) {
const int rpm = hz::string_to_number_nolocale<int>(rpm_str, false);
hdd_ = rpm > 0;
if (auto prop = basic_property_repo.lookup_property("model_name"); !prop.empty()) {
model_name_ = prop.get_value<std::string>();
}
// 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<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"); !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<bool>() ? 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<AtaStorageProperty>& 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<AtaStorageProperty> props)
void StorageDevice::set_property_repository(StoragePropertyRepository repository)
{
properties_ = std::move(props);
property_repository_ = std::move(repository);
}
+32 -38
View File
@@ -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<CommandExecutor>& 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<char, std::string> letters_volnames);
/// Get windows drive letters for this drive
const std::map<char, std::string>& get_drive_letters() const;
[[nodiscard]] const std::map<char, std::string>& 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<AtaStorageProperty>& 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<void, StorageDevice*>& signal_changed();
[[nodiscard]] sigc::signal<void, StorageDevice*>& 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<AtaStorageProperty> props);
/// Set properties
void set_property_repository(StoragePropertyRepository repository);
private:
@@ -289,7 +283,7 @@ class StorageDevice {
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.
std::vector<AtaStorageProperty> properties_; ///< Smart properties. Detected through full output.
StoragePropertyRepository property_repository_; ///< Parsed data properties
/// Emitted whenever new information is available
sigc::signal<void, StorageDevice*> signal_changed_;
@@ -0,0 +1,61 @@
/******************************************************************************
License: GNU General Public License v3.0 only
Copyright:
(C) 2008 - 2024 Alexander Shaduri <ashaduri@gmail.com>
******************************************************************************/
#include "storage_property_repository.h"
const std::vector<AtaStorageProperty>& StoragePropertyRepository::get_properties() const
{
return properties_;
}
std::vector<AtaStorageProperty>& 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<AtaStorageProperty> properties)
{
properties_ = std::move(properties);
}
void StoragePropertyRepository::add_property(AtaStorageProperty property)
{
properties_.push_back(std::move(property));
}
void StoragePropertyRepository::clear()
{
properties_.clear();
}
+47
View File
@@ -0,0 +1,47 @@
/******************************************************************************
License: GNU General Public License v3.0 only
Copyright:
(C) 2008 - 2024 Alexander Shaduri <ashaduri@gmail.com>
******************************************************************************/
#ifndef STORAGE_PROPERTY_REPOSITORY_H
#define STORAGE_PROPERTY_REPOSITORY_H
#include <vector>
#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<AtaStorageProperty>& get_properties() const;
/// Get all properties
[[nodiscard]] std::vector<AtaStorageProperty>& 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<AtaStorageProperty> properties);
/// Add a property
void add_property(AtaStorageProperty property);
/// Clear all properties
void clear();
private:
std::vector<AtaStorageProperty> properties_; ///< Parsed data properties
};
#endif // STORAGE_PROPERTY_REPOSITORY_H
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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<std::string>().empty()) {
name += "\n" + Glib::Markup::escape_text(scan_time_prop.get_value<std::string>());
}