mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-27 06:15:45 +00:00
Separate out basic text parser from StorageDevice.
Store all properties in StoragePropertyRepository.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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>());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user