diff --git a/gsmartcontrol/TODO b/gsmartcontrol/TODO index 531e59b..d52abf6 100644 --- a/gsmartcontrol/TODO +++ b/gsmartcontrol/TODO @@ -32,6 +32,8 @@ Add ability (through ctrl-C?) to copy selected rows from attributes and statisti Add menu item - "update drive database" Run it in terminal in Linux for verbosity. + xterm -hold -e 'update-smart-drivedb' + require xterm in rpm/deb deps. Testing: @@ -40,11 +42,9 @@ Testing: Detect running tests on launch (maybe ask the user too? some tests may be stuck due to bad firmware, e.g. 3ware/windows). +Parse "==> WARNING" -Per-line selftest error highlighting. - Tab highlighting according to error severity. - Parse all test data. diff --git a/gsmartcontrol/src/applib/storage_property.cpp b/gsmartcontrol/src/applib/storage_property.cpp index a9f2e16..4d3d79a 100644 --- a/gsmartcontrol/src/applib/storage_property.cpp +++ b/gsmartcontrol/src/applib/storage_property.cpp @@ -76,22 +76,23 @@ std::ostream& operator<<(std::ostream& os, const StorageStatistic& p) std::string StorageErrorBlock::get_readable_error_types(const std::vector& types) { - std::map m; - - m["ABRT"] = "Command aborted"; - m["AMNF"] = "Address mark not found"; - m["CCTO"] = "Command completion timed out"; - m["EOM"] = "End of media"; - m["ICRC"] = "Interface CRC error"; - m["IDNF"] = "Identity not found"; - m["ILI"] = "(Packet command-set specific)"; - m["MC"] = "Media changed"; - m["MCR"] = "Media change request"; - m["NM"] = "No media"; - m["obs"] = "Obsolete"; - m["TK0NF"] = "Track 0 not found"; - m["UNC"] = "Uncorrectable error in data"; - m["WP"] = "Media is write protected"; + static std::map m; + if (m.empty()) { + m["ABRT"] = "Command aborted"; + m["AMNF"] = "Address mark not found"; + m["CCTO"] = "Command completion timed out"; + m["EOM"] = "End of media"; + m["ICRC"] = "Interface CRC error"; + m["IDNF"] = "Identity not found"; + m["ILI"] = "(Packet command-set specific)"; + m["MC"] = "Media changed"; + m["MCR"] = "Media change request"; + m["NM"] = "No media"; + m["obs"] = "Obsolete"; + m["TK0NF"] = "Track 0 not found"; + m["UNC"] = "Uncorrectable error in data"; + m["WP"] = "Media is write protected"; + } std::vector sv; for (std::vector::const_iterator iter = types.begin(); iter != types.end(); ++iter) { @@ -107,6 +108,34 @@ std::string StorageErrorBlock::get_readable_error_types(const std::vector m; + if (m.empty()) { + m["ABRT"] = StorageProperty::warning_none; + m["AMNF"] = StorageProperty::warning_alert; + m["CCTO"] = StorageProperty::warning_warn; + m["EOM"] = StorageProperty::warning_warn; + m["ICRC"] = StorageProperty::warning_warn; + m["IDNF"] = StorageProperty::warning_alert; + m["ILI"] = StorageProperty::warning_notice; + m["MC"] = StorageProperty::warning_none; + m["MCR"] = StorageProperty::warning_none; + m["NM"] = StorageProperty::warning_none; + m["obs"] = StorageProperty::warning_none; + m["TK0NF"] = StorageProperty::warning_alert; + m["UNC"] = StorageProperty::warning_alert; + m["WP"] = StorageProperty::warning_none; + } + + if (m.find(type) != m.end()) { + return int(m[type]); + } + return StorageProperty::warning_none; // unknown error +} + + + std::string StorageErrorBlock::format_lifetime_hours() const { std::stringstream ss; diff --git a/gsmartcontrol/src/applib/storage_property.h b/gsmartcontrol/src/applib/storage_property.h index 2915295..06e0f88 100644 --- a/gsmartcontrol/src/applib/storage_property.h +++ b/gsmartcontrol/src/applib/storage_property.h @@ -188,6 +188,9 @@ class StorageErrorBlock { /// Get readable error types from reported types static std::string get_readable_error_types(const std::vector& types); + /// Get warning level (warning_t) for an error type + static int get_warning_level_for_error_type(std::string& type); + /// Format lifetime hours with comma std::string format_lifetime_hours() const; diff --git a/gsmartcontrol/src/applib/storage_property_descr.cpp b/gsmartcontrol/src/applib/storage_property_descr.cpp index 268eb53..03e55f4 100644 --- a/gsmartcontrol/src/applib/storage_property_descr.cpp +++ b/gsmartcontrol/src/applib/storage_property_descr.cpp @@ -1615,6 +1615,13 @@ bool storage_property_autoset_description(StorageProperty& p, StorageAttribute:: found = auto_set(p, "error_log_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 + if (p.value_type == StorageProperty::value_type_error_block) { + for (size_t i = 0; i < p.value_error_block.reported_types.size(); ++i) { + p.set_description(StorageErrorBlock::get_readable_error_types(p.value_error_block.reported_types)); + found = true; + } + } + break; case StorageProperty::subsection_selftest_log: @@ -1851,7 +1858,7 @@ StorageProperty::warning_t storage_property_autoset_warning(StorageProperty& p) // the tooltips. if (name_match(p, "error_log_error_count") && p.value_integer > 0) { - w = StorageProperty::warning_warn; + w = StorageProperty::warning_notice; reason = "The drive is reporting internal errors. Usually this means uncorrectable data loss and similar severe errors. " "Check the actual errors for details."; @@ -1860,7 +1867,20 @@ StorageProperty::warning_t storage_property_autoset_warning(StorageProperty& p) reason = "The drive does not support error logging. This means that SMART error history is unavailable."; } - // TODO Rate individual error log entries. + // Rate individual error log entries. + if (!p.value_error_block.reported_types.empty()) { + StorageProperty::warning_t error_block_warning = StorageProperty::warning_none; + for (size_t i = 0; i < p.value_error_block.reported_types.size(); ++i) { + int individual_warning = StorageErrorBlock::get_warning_level_for_error_type(p.value_error_block.reported_types[i]); + if (individual_warning > int(error_block_warning)) { + error_block_warning = StorageProperty::warning_t(individual_warning); + } + } + if (int(error_block_warning) > int(StorageProperty::warning_none)) { + w = error_block_warning; + reason = "The drive is reporting internal errors. Your data may be at risk depending on error severity."; + } + } break; diff --git a/gsmartcontrol/src/gsc_info_window.cpp b/gsmartcontrol/src/gsc_info_window.cpp index 36b0e0d..a75683a 100644 --- a/gsmartcontrol/src/gsc_info_window.cpp +++ b/gsmartcontrol/src/gsc_info_window.cpp @@ -108,32 +108,8 @@ namespace { - /// Cell renderer functions for attribute cells - inline void app_attr_cell_renderer_func(Gtk::CellRenderer* cr, const Gtk::TreeModel::iterator& iter, - Gtk::TreeModelColumn storage_column) - { - const StorageProperty* p = (*iter)[storage_column]; - Gtk::CellRendererText* crt = hz::down_cast(cr); - if (crt) { - std::string fg, bg; - if (app_property_get_row_highlight_colors(p->warning, fg, bg)) { - // Note: property_cell_background makes horizontal tree lines disappear around it, - // but property_background doesn't play nice with sorted column color. - crt->property_cell_background() = bg; - crt->property_foreground() = fg; - } else { - // this is needed because cellrenderer is shared in column, so the previous call - // may set the color for all subsequent cells. - crt->property_cell_background().reset_value(); - crt->property_foreground().reset_value(); - } - } - } - - - - /// Cell renderer functions for attribute cells - inline void app_statistic_cell_renderer_func(Gtk::CellRenderer* cr, const Gtk::TreeModel::iterator& iter, + /// Cell renderer functions for list cells + inline void app_list_cell_renderer_func(Gtk::CellRenderer* cr, const Gtk::TreeModel::iterator& iter, Gtk::TreeModelColumn storage_column) { const StorageProperty* p = (*iter)[storage_column]; @@ -667,7 +643,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), - sigc::bind(sigc::ptr_fun(app_attr_cell_renderer_func), col_storage)); + sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); } @@ -783,7 +759,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), - sigc::bind(sigc::ptr_fun(app_statistic_cell_renderer_func), col_storage)); + sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); } @@ -988,7 +964,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), - sigc::bind(sigc::ptr_fun(app_attr_cell_renderer_func), col_storage)); + sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); } @@ -1099,7 +1075,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), - sigc::bind(sigc::ptr_fun(app_attr_cell_renderer_func), col_storage)); + sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); } @@ -1157,7 +1133,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests row[col_details] = (type_details.empty() ? "-" : type_details); // e.g. OBS has no details // There are no descriptions in self-test log entries, so don't display // "No description available" for all of them. -// row[col_tooltip] = iter->get_description(); + row[col_tooltip] = iter->get_description(); row[col_storage] = &(*iter); row[col_mark_name] = "Error " + hz::number_to_string(iter->value_error_block.error_num); } @@ -1309,7 +1285,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests for (unsigned int i = 0; i < num_tree_cols; ++i) { Gtk::TreeViewColumn* tcol = treeview->get_column(i); tcol->set_cell_data_func(*(tcol->get_first_cell()), - sigc::bind(sigc::ptr_fun(app_attr_cell_renderer_func), col_storage)); + sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage)); }