From 02428a26c05c3e454dc22754f221bc32ebe29cbd Mon Sep 17 00:00:00 2001 From: Alexander Shaduri Date: Sun, 6 Mar 2011 20:31:39 +0000 Subject: [PATCH] Show Error Log and Self-Test Log support. Support attributes with undefined values like "---". --- gsmartcontrol/TODO | 6 +- gsmartcontrol/src/applib/smartctl_parser.cpp | 63 +++++++++++++++---- gsmartcontrol/src/applib/storage_property.cpp | 10 ++- gsmartcontrol/src/applib/storage_property.h | 18 +++--- .../src/applib/storage_property_descr.cpp | 12 +++- gsmartcontrol/src/gsc_info_window.cpp | 6 +- gsmartcontrol/src/hz/optional_value.h | 2 +- 7 files changed, 86 insertions(+), 31 deletions(-) diff --git a/gsmartcontrol/TODO b/gsmartcontrol/TODO index df1f726..780eb62 100644 --- a/gsmartcontrol/TODO +++ b/gsmartcontrol/TODO @@ -38,7 +38,9 @@ TODO: Add debian/ubuntu's su-to-root support to gsmartcontrol-root. -Add support for parsing SSD outputs. +Add support for SSD smartctl attribute names (they're totally different). + Check smartctl sources, they print those names. + Testing: If ETA time has elapsed, but it's still only at 10% completion, @@ -105,8 +107,6 @@ Monitor: Periodically see if drives were added / removed (possibly use OS callbacks instead of polling?) -Fix inability to detect types in some error logs. - Options->Prefs->Drives Add a hint about "pd0" for Windows version. diff --git a/gsmartcontrol/src/applib/smartctl_parser.cpp b/gsmartcontrol/src/applib/smartctl_parser.cpp index d6aa866..d89b0cb 100644 --- a/gsmartcontrol/src/applib/smartctl_parser.cpp +++ b/gsmartcontrol/src/applib/smartctl_parser.cpp @@ -997,16 +997,23 @@ bool SmartctlParser::parse_section_data_subsection_attributes(const std::string& // named "Head flying hours". So, parse until we encounter the next column. // * One WD drive had non-integer flags, something like "PO--C-", with several // lines of their descriptions after the attributes block (each line started with spaces and |). + // * SSD drives may show "---" in value/worst/threshold fields. + // " 1 Raw_Read_Error_Rate 0x000f 115 099 006 Pre-fail Always - 90981479" + // " 12 Power_Cycle_Count 0x0000 --- --- --- Old_age Offline - 167" bool attr_found = false; // at least one attribute was found bool attr_format_with_updated = false; // UPDATED column present - pcrecpp::RE re_up = app_pcre_re("/[ \\t]*([0-9]+) ([^\\t\\n]+)[ \\t]+((?:0x[a-fA-F0-9]+)|(?:[A-Z-]{2,}))[ \\t]+([0-9]+)[ \\t]+([0-9]+)[ \\t]+([0-9]+)[ \\t]+" - "([^ \\t\\n]+)[ \\t]+([^ \\t\\n]+)[ \\t]+([^ \\t\\n]+)[ \\t]+(.+)[ \\t]*/mi"); + std::string base_re = "[ \\t]*([0-9]+) ([^\\t\\n]+)[ \\t]+((?:0x[a-fA-F0-9]+)|(?:[A-Z-]{2,}))[ \\t]+" // name / flag + "([0-9-]+)[ \\t]+([0-9-]+)[ \\t]+([0-9-]+)[ \\t]+" // value / worst / threshold + "([^ \\t\\n]+)[ \\t]+"; // type - pcrecpp::RE re_noup = app_pcre_re("/[ \\t]*([0-9]+) ([^\\t\\n]+)[ \\t]+((?:0x[a-fA-F0-9]+)|(?:[A-Z-]{2,}))[ \\t]+([0-9]+)[ \\t]+([0-9]+)[ \\t]+([0-9]+)[ \\t]+" - "([^ \\t\\n]+)[ \\t]+([^ \\t\\n]+)[ \\t]+(.+)[ \\t]*/mi"); + pcrecpp::RE re_up = app_pcre_re("/" + base_re + + "([^ \\t\\n]+)[ \\t]+([^ \\t\\n]+)[ \\t]+(.+)[ \\t]*/mi"); // updated / when_failed / raw + + pcrecpp::RE re_noup = app_pcre_re("/" + base_re + + "([^ \\t\\n]+)[ \\t]+(.+)[ \\t]*/mi"); // when_failed / raw pcrecpp::RE re_flag_descr = app_pcre_re("/^[\\t ]+\\|/mi"); @@ -1080,9 +1087,17 @@ bool SmartctlParser::parse_section_data_subsection_attributes(const std::string& StorageAttribute a; hz::string_is_numeric(hz::string_trim_copy(id), a.id, true, 10); a.flag = hz::string_trim_copy(flag); - hz::string_is_numeric(hz::string_trim_copy(value), a.value, true, 10); - hz::string_is_numeric(hz::string_trim_copy(worst), a.worst, true, 10); - hz::string_is_numeric(hz::string_trim_copy(threshold), a.threshold, true, 10); + uint8_t norm_value = 0, worst_value = 0, threshold_value = 0; + + if (hz::string_is_numeric(hz::string_trim_copy(value), norm_value, true, 10)) { + a.value = norm_value; + } + if (hz::string_is_numeric(hz::string_trim_copy(worst), worst_value, true, 10)) { + a.worst = worst_value; + } + if (hz::string_is_numeric(hz::string_trim_copy(threshold), threshold_value, true, 10)) { + a.threshold = threshold_value; + } a.attr_type = (attr_type == "Pre-fail" ? StorageAttribute::attr_type_prefail : (attr_type == "Old_age" ? StorageAttribute::attr_type_oldage : StorageAttribute::attr_type_unknown)); @@ -1139,7 +1154,7 @@ bool SmartctlParser::parse_section_data_subsection_error_log(const std::string& bool data_found = false; - // error log version + // Error log version { pcrecpp::RE re = app_pcre_re("/^(SMART Error Log Version):[ \\t]*(.*)$/mi"); @@ -1162,7 +1177,20 @@ bool SmartctlParser::parse_section_data_subsection_error_log(const std::string& } } - // error log count + // Error log support + { + pcrecpp::RE re = app_pcre_re("/^(Warning: device does not support Error Logging)$/mi"); + + if (re.PartialMatch(sub)) { + StorageProperty p(pt); + p.set_name("error_log_unsupported"); + p.readable_name = "Warning"; + p.readable_value = "Device does not support error logging"; + add_property(p); + } + } + + // Error log enty count { // note: these represent the same information pcrecpp::RE re1 = app_pcre_re("/^ATA Error Count:[ \\t]*([0-9]+)/mi"); @@ -1279,7 +1307,7 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin bool data_found = false; // true if something was found. - // the whole subsection + // The whole subsection { StorageProperty p(pt); p.set_name("SMART Self-test log", "selftest_log"); @@ -1292,7 +1320,20 @@ bool SmartctlParser::parse_section_data_subsection_selftest_log(const std::strin } - // self-test log version + // Self-test log support + { + pcrecpp::RE re = app_pcre_re("/^(Warning: device does not support Self Test Logging)$/mi"); + + if (re.PartialMatch(sub)) { + StorageProperty p(pt); + p.set_name("selftest_log_unsupported"); + p.readable_name = "Warning"; + p.readable_value = "Device does not support self-test logging"; + add_property(p); + } + } + + // Self-test log version { // newer smartctl (since smartctl 5.1-16) pcrecpp::RE re1 = app_pcre_re("/(SMART Self-test log structure[^\\n0-9]*)([^ \\n]+)[ \\t]*$/mi"); diff --git a/gsmartcontrol/src/applib/storage_property.cpp b/gsmartcontrol/src/applib/storage_property.cpp index 3ae7639..26d23ce 100644 --- a/gsmartcontrol/src/applib/storage_property.cpp +++ b/gsmartcontrol/src/applib/storage_property.cpp @@ -31,9 +31,13 @@ std::ostream& operator<< (std::ostream& os, const StorageCapability& p) std::ostream& operator<< (std::ostream& os, const StorageAttribute& p) { - os -// << p.name << ": " - << static_cast(p.value) << " (" << p.raw_value_int << ")"; +// os << p.name << ": " + if (p.value.defined()) { + os << static_cast(p.value.value()); + } else { + os << "-"; + } + os << " (" << p.raw_value_int << ")"; return os; } diff --git a/gsmartcontrol/src/applib/storage_property.h b/gsmartcontrol/src/applib/storage_property.h index 56476ef..29f6e62 100644 --- a/gsmartcontrol/src/applib/storage_property.h +++ b/gsmartcontrol/src/applib/storage_property.h @@ -11,6 +11,7 @@ #include #include +#include "hz/optional_value.h" #include "hz/cstdint.h" @@ -96,22 +97,21 @@ class StorageAttribute { } - StorageAttribute() : id(-1), value(0), worst(0), threshold(0), - attr_type(attr_type_unknown), update_type(update_type_unknown), + StorageAttribute() : id(-1), attr_type(attr_type_unknown), update_type(update_type_unknown), when_failed(fail_time_unknown), raw_value_int(0) { } - int32_t id; - std::string flag; // some have it in 0xXXXX format, others in "PO--C-" format (some WD drives?). - uint8_t value; - uint8_t worst; - uint8_t threshold; + int32_t id; ///< Attribute ID (most vendors agree on this) + std::string flag; ///< Some have it in 0xXXXX format, others in "PO--C-" format (some WD drives?). + hz::OptionalValue value; ///< Normalized value. May be unset ("---"). + hz::OptionalValue worst; ///< Worst ever value. May be unset ("---"). + hz::OptionalValue threshold; ///< Threshold for normalized value. May be unset ("---"). attr_t attr_type; update_t update_type; fail_time_t when_failed; - std::string raw_value; // as presented by smartctl (formatted). - int64_t raw_value_int; // same as raw_value, but parsed as int. original value is 6 bytes I think. + std::string raw_value; ///< as presented by smartctl (formatted). + int64_t raw_value_int; ///< same as raw_value, but parsed as int. original value is 6 bytes I think. }; diff --git a/gsmartcontrol/src/applib/storage_property_descr.cpp b/gsmartcontrol/src/applib/storage_property_descr.cpp index 520904c..10ce696 100644 --- a/gsmartcontrol/src/applib/storage_property_descr.cpp +++ b/gsmartcontrol/src/applib/storage_property_descr.cpp @@ -106,7 +106,6 @@ bool storage_property_autoset_description(StorageProperty& p) p.set_description("Checksum errors indicate that SMART data is invalid. This shouldn't happen in normal circumstances."); found = true; - // Section Info } else if (p.section == StorageProperty::section_info) { found = auto_set(p, "Serial Number", "Serial number, unique to each physical drive.") @@ -265,10 +264,12 @@ bool storage_property_autoset_description(StorageProperty& p) } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_error_log) { found = auto_set(p, "error_count", "Number of errors in error log. Note: Some manufacturers may list completely harmless errors in this log " "(e.g., command invalid, not implemented, etc...)."); +// || auto_set(p, "error_log_unsupported", "This device does not support error logging."); // the property text already says that } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_selftest_log) { found = auto_set(p, "selftest_num_entries", "Number of tests in selftest log. Note: This log usually contains only the last 20 or so manual tests. "); +// || auto_set(p, "selftest_log_unsupported", "This device does not support self-test logging."); // the property text already says that } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_selective_selftest_log) { // nothing here @@ -388,6 +389,10 @@ StorageProperty::warning_t storage_property_autoset_warning(StorageProperty& p) w = StorageProperty::warning_warn; reason = "The drive is reporting internal errors. Usually this means uncorrectable data loss and similar severe errors. " "Check the actual errors for details."; + + } else if (name_match(p, "error_log_unsupported")) { + w = StorageProperty::warning_notice; + reason = "The drive does not support error logging. This means that SMART error history is unavailable."; } @@ -395,6 +400,11 @@ StorageProperty::warning_t storage_property_autoset_warning(StorageProperty& p) // don't include selftest warnings - they may be old or something. // self-tests are carried manually anyway, so the user is expected to check their status anyway. + if (name_match(p, "selftest_log_unsupported")) { + w = StorageProperty::warning_notice; + reason = "The drive does not support self-test logging. This means that SMART test results won't be logged."; + } + } else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_selective_selftest_log) { // nothing here diff --git a/gsmartcontrol/src/gsc_info_window.cpp b/gsmartcontrol/src/gsc_info_window.cpp index 4ac0f3a..56d4324 100644 --- a/gsmartcontrol/src/gsc_info_window.cpp +++ b/gsmartcontrol/src/gsc_info_window.cpp @@ -659,9 +659,9 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests row[col_id] = iter->value_attribute.id; row[col_name] = iter->readable_name; row[col_flag_value] = iter->value_attribute.flag; // it's a string, not int. - row[col_value] = hz::number_to_string(iter->value_attribute.value); - row[col_worst] = hz::number_to_string(iter->value_attribute.worst); - row[col_threshold] = hz::number_to_string(iter->value_attribute.threshold); + row[col_value] = (iter->value_attribute.value.defined() ? hz::number_to_string(iter->value_attribute.value.value()) : "-"); + row[col_worst] = (iter->value_attribute.worst.defined() ? hz::number_to_string(iter->value_attribute.worst.value()) : "-"); + row[col_threshold] = (iter->value_attribute.threshold.defined() ? hz::number_to_string(iter->value_attribute.threshold.value()) : "-"); row[col_raw] = iter->value_attribute.raw_value; row[col_type] = attr_type; row[col_updated] = StorageAttribute::get_update_type_name(iter->value_attribute.update_type); diff --git a/gsmartcontrol/src/hz/optional_value.h b/gsmartcontrol/src/hz/optional_value.h index 4594e86..a5e254e 100644 --- a/gsmartcontrol/src/hz/optional_value.h +++ b/gsmartcontrol/src/hz/optional_value.h @@ -27,7 +27,7 @@ class OptionalValue { typedef T value_type; - OptionalValue() : value_(), defined_(true) + OptionalValue() : value_(), defined_(false) { }