Allow smartctl < 7.3 again, falling back to Text parser; this configuration is for compatibility only and not fully supported.

This commit is contained in:
Alexander Shaduri
2025-02-24 13:52:04 +04:00
parent 589c280bdc
commit d7032eb351
5 changed files with 144 additions and 82 deletions
+27 -10
View File
@@ -69,17 +69,34 @@ bool SmartctlVersionParser::check_format_supported(SmartctlOutputFormat format,
SmartctlOutputFormat SmartctlVersionParser::get_default_format(SmartctlParserType parser_type)
namespace {
SmartctlOutputFormat s_smartctl_output_default_format = SmartctlOutputFormat::Json;
}
void SmartctlVersionParser::set_default_format(SmartctlOutputFormat format)
{
switch (parser_type) {
case SmartctlParserType::Basic:
return SmartctlOutputFormat::Json;
case SmartctlParserType::Ata:
return SmartctlOutputFormat::Json;
case SmartctlParserType::Nvme:
return SmartctlOutputFormat::Json;
}
return SmartctlOutputFormat::Json;
s_smartctl_output_default_format = format;
}
SmartctlOutputFormat SmartctlVersionParser::get_default_format([[maybe_unused]] SmartctlParserType parser_type)
{
// We no longer differentiate between parser types - they
// all either use Text, or Json.
// switch (parser_type) {
// case SmartctlParserType::Basic:
// return SmartctlOutputFormat::Json;
// case SmartctlParserType::Ata:
// return SmartctlOutputFormat::Json;
// case SmartctlParserType::Nvme:
// return SmartctlOutputFormat::Json;
// }
return s_smartctl_output_default_format;
}
+8
View File
@@ -48,13 +48,21 @@ class SmartctlVersionParser {
static bool check_format_supported(SmartctlOutputFormat format, const std::string& version_only);
/// Get default output format for a parser type.
static void set_default_format(SmartctlOutputFormat format);
/// Get default output format for a parser type.
static SmartctlOutputFormat get_default_format(SmartctlParserType parser_type);
/// Get default output format for a parser type.
static SmartctlParserType get_default_parser_type(StorageDeviceDetectedType detected_type);
// We require this version at runtime to support --get=all.
static constexpr double minimum_req_runtime_version = 5.43;
private:
// Text Parser:
+11 -2
View File
@@ -155,8 +155,17 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::parse_basic_data()
// Clear everything fetched before, except outputs and type
this->clear_parse_results();
// Detect the output format.
auto detect_status = SmartctlParser::detect_output_format(this->get_basic_output());
auto output_format = SmartctlOutputFormat::Text;
if (detect_status.has_value()) {
output_format = detect_status.value();
} else {
debug_out_warn("app", "Cannot detect smartctl output format. Assuming Text.\n");
}
// Parse using Basic parser. This supports all drive types.
auto basic_parser = SmartctlParser::create(SmartctlParserType::Basic, SmartctlOutputFormat::Json);
auto basic_parser = SmartctlParser::create(SmartctlParserType::Basic, output_format);
DBG_ASSERT_RETURN(basic_parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser")));
// This also fills the drive type properties.
@@ -189,7 +198,7 @@ hz::ExpectedVoid<StorageDeviceError> StorageDevice::parse_basic_data()
// 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) {
// auto parser = SmartctlParser::create(SmartctlParserType::Ata, SmartctlOutputFormat::Json);
// auto parser = SmartctlParser::create(SmartctlParserType::Ata, output_format);
// DBG_ASSERT_RETURN(parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser")));
//
// if (parser->parse(this->basic_output_)) { // try to parse it
+91 -68
View File
@@ -82,72 +82,10 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, Glib::RefPtr<Gtk::Builder>
// Check if smartctl is executable
std::string error_msg;
bool show_output_button = true;
do {
const std::string smartctl_binary = hz::fs_path_to_string(get_smartctl_binary());
// Don't use default options here - they are used when invoked
// with a device option.
// std::string smartctl_def_options = rconfig::get_data<std::string>("system/smartctl_options");
if (smartctl_binary.empty()) {
error_msg = _("Smartctl binary is not specified in configuration.");
show_output_button = false;
break;
}
// if (!smartctl_def_options.empty())
// smartctl_def_options += " ";
SmartctlExecutorGui ex;
ex.create_running_dialog(this);
ex.set_running_msg(_("Checking if smartctl is executable..."));
ex.set_command(smartctl_binary, {"-V"}); // --version
if (!ex.execute() || !ex.get_error_msg().empty()) {
error_msg = ex.get_error_msg();
break;
}
const std::string output = ex.get_stdout_str();
if (output.empty()) {
error_msg = _("Smartctl returned an empty output.");
break;
}
std::string version, version_full;
if (!SmartctlVersionParser::parse_version_text(output, version, version_full)) {
error_msg = _("Smartctl returned invalid output.");
break;
}
{
// We require this version at runtime to support --get=all.
const double minimum_req_version = 5.43;
double version_double = 0;
if (hz::string_is_numeric_nolocale<double>(version, version_double, false)) {
if (version_double < minimum_req_version) {
error_msg = Glib::ustring::compose(_("Smartctl version %1 found, %2 required."), version, hz::number_to_string_nolocale(minimum_req_version));
break;
}
}
}
} while (false);
const bool smartctl_valid = error_msg.empty();
if (!smartctl_valid) {
gsc_executor_error_dialog_show(_("There was an error while executing smartctl"),
error_msg + "\n\n<i>" + _("Please specify the correct smartctl binary in Preferences.") + "</i>",
this, true, show_output_button);
}
bool smartctl_valid = check_smartctl_version_and_set_format();
// Scan
populate_iconview(smartctl_valid);
populate_iconview_on_startup(smartctl_valid);
}
@@ -163,7 +101,7 @@ GscMainWindow::~GscMainWindow()
void GscMainWindow::populate_iconview(bool smartctl_valid)
void GscMainWindow::populate_iconview_on_startup(bool smartctl_valid)
{
if (!smartctl_valid) {
iconview_->set_empty_view_message(GscMainWindowIconView::Message::NoSmartctl);
@@ -173,7 +111,7 @@ void GscMainWindow::populate_iconview(bool smartctl_valid)
} else if (rconfig::get_data<bool>("gui/scan_on_startup") // config option
&& !get_startup_settings().no_scan) { // command-line option
rescan_devices(); // scan for devices and fill the iconview
rescan_devices(true); // scan for devices and fill the iconview
} else {
iconview_->set_empty_view_message(GscMainWindowIconView::Message::ScanDisabled);
@@ -568,7 +506,7 @@ void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type)
break;
case action_rescan_devices:
rescan_devices();
rescan_devices(false);
break;
case action_executor_log:
@@ -851,12 +789,21 @@ void GscMainWindow::update_status_widgets()
void GscMainWindow::rescan_devices()
void GscMainWindow::rescan_devices(bool startup)
{
// ignore double-scan (may happen because we use gtk loop iterations here).
if (this->scanning_)
return;
// If we're not in startup, smartctl version may have changed (by specifying a different binary in Preferences)
// so we need to re-validate the output format:
if (!startup) {
// This shows an error dialog on error
if (!check_smartctl_version_and_set_format()) {
return;
}
}
// don't manipulate window sensitiveness here - it breaks things
// (cursors, gtk errors pop out, etc.)
@@ -1166,6 +1113,82 @@ void GscMainWindow::show_add_device_chooser()
bool GscMainWindow::check_smartctl_version_and_set_format()
{
std::string error_msg;
bool show_output_button = true;
do {
const std::string smartctl_binary = hz::fs_path_to_string(get_smartctl_binary());
// Don't use default options here - they are used when invoked
// with a device option.
// std::string smartctl_def_options = rconfig::get_data<std::string>("system/smartctl_options");
if (smartctl_binary.empty()) {
error_msg = _("Smartctl binary is not specified in configuration.");
show_output_button = false;
break;
}
// if (!smartctl_def_options.empty())
// smartctl_def_options += " ";
SmartctlExecutorGui ex;
ex.create_running_dialog(this);
ex.set_running_msg(_("Checking if smartctl is executable..."));
ex.set_command(smartctl_binary, {"-V"}); // --version
if (!ex.execute() || !ex.get_error_msg().empty()) {
error_msg = ex.get_error_msg();
break;
}
const std::string output = ex.get_stdout_str();
if (output.empty()) {
error_msg = _("Smartctl returned an empty output.");
break;
}
std::string version, version_full;
if (!SmartctlVersionParser::parse_version_text(output, version, version_full)) {
error_msg = _("Smartctl returned invalid output.");
break;
}
// Check smartctl runtime version
if (double version_double = 0; hz::string_is_numeric_nolocale<double>(version, version_double, false)) {
if (version_double < SmartctlVersionParser::minimum_req_runtime_version) {
error_msg = Glib::ustring::compose(_("Smartctl version %1 found, %2 required."),
version,
hz::number_to_string_nolocale(SmartctlVersionParser::minimum_req_runtime_version));
break;
}
}
if (SmartctlVersionParser::check_format_supported(SmartctlOutputFormat::Json, version)) {
debug_out_info("app", "Smartctl JSON output format supported.\n");
SmartctlVersionParser::set_default_format(SmartctlOutputFormat::Json);
} else {
debug_out_warn("app", "Smartctl JSON output format not supported, falling back to Text output format.\n");
SmartctlVersionParser::set_default_format(SmartctlOutputFormat::Text);
}
} while (false);
const bool smartctl_valid = error_msg.empty();
if (!smartctl_valid) {
gsc_executor_error_dialog_show(_("There was an error while executing smartctl"),
error_msg + "\n\n<i>" + _("Please specify the correct smartctl binary in Preferences.") + "</i>",
this, true, show_output_button);
}
return smartctl_valid;
}
void GscMainWindow::show_load_virtual_file_chooser()
{
static std::string last_dir;
+7 -2
View File
@@ -45,7 +45,7 @@ class GscMainWindow : public AppBuilderWidget<GscMainWindow, false> {
/// Scan for devices and fill the iconview
void rescan_devices();
void rescan_devices(bool startup);
/// Execute update-smart-drivedb
@@ -111,7 +111,7 @@ class GscMainWindow : public AppBuilderWidget<GscMainWindow, false> {
bool create_widgets();
/// scan and populate iconview widget with drive icons
void populate_iconview(bool smartctl_valid);
void populate_iconview_on_startup(bool smartctl_valid);
/// Show "Add Device" window
void show_add_device_chooser();
@@ -120,6 +120,11 @@ class GscMainWindow : public AppBuilderWidget<GscMainWindow, false> {
void show_load_virtual_file_chooser();
/// Check smartctl version and set default parser format accordingly.
/// An error dialog is shown if there is an error with smartctl.
bool check_smartctl_version_and_set_format();
/// Called when quit has been requested (by delete event or Quit action)
void quit_requested();