diff --git a/src/applib/app_builder_widget.h b/src/applib/app_builder_widget.h index 433684f..4c121d8 100644 --- a/src/applib/app_builder_widget.h +++ b/src/applib/app_builder_widget.h @@ -31,7 +31,7 @@ Copyright: #define APP_BUILDER_CONNECT(ui_element, signal_name, callback) \ if (true) { \ if (!(ui_element)) \ - this->lookup_widget(#ui_element, ui_element); \ + [[maybe_unused]] bool found = this->lookup_widget(#ui_element, ui_element); \ if (ui_element) { \ (ui_element)->signal_ ## signal_name ().connect(sigc::mem_fun(*this, &std::remove_reference_t::callback)); \ } \ @@ -85,24 +85,24 @@ class AppBuilderWidget : public WidgetType, public WindowInstanceManager get_ui(); + [[nodiscard]] Glib::RefPtr get_ui(); /// Find a widget in UI and return it. /// \return nullptr if widget was not found. - Gtk::Widget* lookup_widget(const Glib::ustring& name); + [[nodiscard]] Gtk::Widget* lookup_widget(const Glib::ustring& name); /// Find a widget in UI and return it. /// \return nullptr if widget was not found. template - WidgetPtr lookup_widget(const Glib::ustring& name); + [[nodiscard]] WidgetPtr lookup_widget(const Glib::ustring& name); /// Find a widget in UI and return it in \ref w. /// \return false if widget was not found. template - bool lookup_widget(const Glib::ustring& name, Widget*& w); + [[nodiscard]] bool lookup_widget(const Glib::ustring& name, Widget*& w); protected: diff --git a/src/applib/async_command_executor.cpp b/src/applib/async_command_executor.cpp index 702d139..b639320 100644 --- a/src/applib/async_command_executor.cpp +++ b/src/applib/async_command_executor.cpp @@ -53,14 +53,14 @@ extern "C" { /// Child process stdout handler callback inline gboolean cmdex_on_channel_io_stdout(GIOChannel* source, GIOCondition cond, gpointer data) { - return AsyncCommandExecutor::on_channel_io(source, cond, static_cast(data), AsyncCommandExecutor::Channel::standard_output); + return AsyncCommandExecutor::on_channel_io(source, cond, static_cast(data), AsyncCommandExecutor::Channel::StandardOutput); } /// Child process stderr handler callback inline gboolean cmdex_on_channel_io_stderr(GIOChannel* source, GIOCondition cond, gpointer data) { - return AsyncCommandExecutor::on_channel_io(source, cond, static_cast(data), AsyncCommandExecutor::Channel::standard_error); + return AsyncCommandExecutor::on_channel_io(source, cond, static_cast(data), AsyncCommandExecutor::Channel::StandardError); } @@ -142,7 +142,7 @@ bool AsyncCommandExecutor::execute() } catch(Glib::ShellError& e) { - push_error(Error("gshell", ErrorLevel::error, e.what())); + push_error(Error("gshell", ErrorLevel::Error, e.what())); return false; } @@ -184,7 +184,7 @@ bool AsyncCommandExecutor::execute() } catch(Glib::SpawnError& e) { // no data is returned to &-parameters on error. - push_error(Error("gspawn", ErrorLevel::error, e.what())); + push_error(Error("gspawn", ErrorLevel::Error, e.what())); // Restore CWD if (path_changed) { std::error_code dummy_ec; @@ -280,7 +280,7 @@ bool AsyncCommandExecutor::try_stop(hz::Signal sig) } // Possible: EPERM (no permissions), ESRCH (no such process, or zombie) - push_error(Error("errno", ErrorLevel::error, errno)); + push_error(Error("errno", ErrorLevel::Error, errno)); DBG_FUNCTION_EXIT_MSG; return false; @@ -355,7 +355,7 @@ void AsyncCommandExecutor::stopped_cleanup() // translate the exit_code into a message const std::string msg = (translator_func_ ? translator_func_(exit_status) : "[no translator function, exit code: " + std::to_string(exit_status)); - push_error(Error("exit", ErrorLevel::warn, exit_status, msg)); + push_error(Error("exit", ErrorLevel::Warn, exit_status, msg)); } } else { @@ -365,9 +365,9 @@ void AsyncCommandExecutor::stopped_cleanup() // If it's not our signal, treat as error. // Note: they will never match under win32 if (sig_num != this->kill_signal_sent_) { - push_error(Error("signal", ErrorLevel::error, sig_num)); + push_error(Error("signal", ErrorLevel::Error, sig_num)); } else { // it's our signal, treat as warning - push_error(Error("signal", ErrorLevel::warn, sig_num)); + push_error(Error("signal", ErrorLevel::Warn, sig_num)); } } } @@ -395,10 +395,10 @@ void AsyncCommandExecutor::on_child_watch_handler([[maybe_unused]] GPid arg_pid, // These are needed because Windows doesn't read the remaining data otherwise. g_io_channel_flush(self->channel_stdout_, nullptr); - on_channel_io(self->channel_stdout_, GIOCondition(0), self, Channel::standard_output); + on_channel_io(self->channel_stdout_, GIOCondition(0), self, Channel::StandardOutput); g_io_channel_flush(self->channel_stderr_, nullptr); - on_channel_io(self->channel_stderr_, GIOCondition(0), self, Channel::standard_error); + on_channel_io(self->channel_stderr_, GIOCondition(0), self, Channel::StandardError); if (self->channel_stdout_) { g_io_channel_shutdown(self->channel_stdout_, FALSE, nullptr); @@ -452,7 +452,7 @@ gboolean AsyncCommandExecutor::on_channel_io(GIOChannel* channel, continue_events = false; // there'll be no more data } - DBG_ASSERT_RETURN(channel_type == Channel::standard_output || channel_type == Channel::standard_error, false); + DBG_ASSERT_RETURN(channel_type == Channel::StandardOutput || channel_type == Channel::StandardError, false); // const gsize count = 4 * 1024; // read the bytes one by one. without this, a buffered iochannel hangs while waiting for data. @@ -461,9 +461,9 @@ gboolean AsyncCommandExecutor::on_channel_io(GIOChannel* channel, std::array buf = {0}; std::string* output_str = nullptr; - if (channel_type == Channel::standard_output) { + if (channel_type == Channel::StandardOutput) { output_str = &self->str_stdout_; - } else if (channel_type == Channel::standard_error) { + } else if (channel_type == Channel::StandardError) { output_str = &self->str_stderr_; } DBG_ASSERT_RETURN(output_str, false); @@ -478,7 +478,7 @@ gboolean AsyncCommandExecutor::on_channel_io(GIOChannel* channel, output_str->append(buf.data(), bytes_read); if (channel_error) { - self->push_error(Error("giochannel", ErrorLevel::error, channel_error->message)); + self->push_error(Error("giochannel", ErrorLevel::Error, channel_error->message)); g_error_free(channel_error); break; // stop on next invocation (is this correct?) } diff --git a/src/applib/async_command_executor.h b/src/applib/async_command_executor.h index 13de49b..b4410f1 100644 --- a/src/applib/async_command_executor.h +++ b/src/applib/async_command_executor.h @@ -123,11 +123,11 @@ class AsyncCommandExecutor : public hz::ErrorHolder { /// If stdout_make_str_as_available_ is false, call this after stopped_cleanup(), /// before next execute(). If it's true, you may call this before the command has /// stopped, but it will decrease performance significantly. - std::string get_stdout_str(bool clear_existing = false); + [[nodiscard]] std::string get_stdout_str(bool clear_existing = false); /// See notes for \ref get_stdout_str(). - std::string get_stderr_str(bool clear_existing = false); + [[nodiscard]] std::string get_stderr_str(bool clear_existing = false); /// Return execution time, in seconds. Call this after execute(). @@ -149,8 +149,8 @@ class AsyncCommandExecutor : public hz::ErrorHolder { /// Channel type, for passing to callbacks enum class Channel { - standard_output, - standard_error + StandardOutput, + StandardError }; diff --git a/src/applib/ata_storage_property.cpp b/src/applib/ata_storage_property.cpp index 936c902..de0f1cb 100644 --- a/src/applib/ata_storage_property.cpp +++ b/src/applib/ata_storage_property.cpp @@ -42,9 +42,9 @@ std::ostream& operator<< (std::ostream& os, const AtaStorageCapability& p) std::string AtaStorageAttribute::get_attr_type_name(AtaStorageAttribute::AttributeType type) { static const std::unordered_map m { - {AttributeType::unknown, "[unknown]"}, - {AttributeType::prefail, "pre-failure"}, - {AttributeType::old_age, "old age"}, + {AttributeType::Unknown, "[unknown]"}, + {AttributeType::Prefail, "pre-failure"}, + {AttributeType::OldAge, "old age"}, }; if (auto iter = m.find(type); iter != m.end()) { return iter->second; @@ -57,9 +57,9 @@ std::string AtaStorageAttribute::get_attr_type_name(AtaStorageAttribute::Attribu std::string AtaStorageAttribute::get_update_type_name(AtaStorageAttribute::UpdateType type) { static const std::unordered_map m { - {UpdateType::unknown, "[unknown]"}, - {UpdateType::always, "continuously"}, - {UpdateType::offline, "on offline data collect."}, + {UpdateType::Unknown, "[unknown]"}, + {UpdateType::Always, "continuously"}, + {UpdateType::Offline, "on offline data collect."}, }; if (auto iter = m.find(type); iter != m.end()) { return iter->second; @@ -72,10 +72,10 @@ std::string AtaStorageAttribute::get_update_type_name(AtaStorageAttribute::Updat std::string AtaStorageAttribute::get_fail_time_name(AtaStorageAttribute::FailTime type) { static const std::unordered_map m { - {FailTime::unknown, "[unknown]"}, - {FailTime::none, "never"}, - {FailTime::past, "in the past"}, - {FailTime::now, "now"}, + {FailTime::Unknown, "[unknown]"}, + {FailTime::None, "never"}, + {FailTime::Past, "in the past"}, + {FailTime::Now, "now"}, }; if (auto iter = m.find(type); iter != m.end()) { return iter->second; @@ -192,26 +192,26 @@ std::string AtaStorageErrorBlock::get_displayable_error_types(const std::vector< WarningLevel AtaStorageErrorBlock::get_warning_level_for_error_type(const std::string& type) { static const std::map m = { - {"ABRT", WarningLevel::none}, - {"AMNF", WarningLevel::alert}, - {"CCTO", WarningLevel::warning}, - {"EOM", WarningLevel::warning}, - {"ICRC", WarningLevel::warning}, - {"IDNF", WarningLevel::alert}, - {"ILI", WarningLevel::notice}, - {"MC", WarningLevel::none}, - {"MCR", WarningLevel::none}, - {"NM", WarningLevel::none}, - {"obs", WarningLevel::none}, - {"TK0NF", WarningLevel::alert}, - {"UNC", WarningLevel::alert}, - {"WP", WarningLevel::none}, + {"ABRT", WarningLevel::None}, + {"AMNF", WarningLevel::Alert}, + {"CCTO", WarningLevel::Warning}, + {"EOM", WarningLevel::Warning}, + {"ICRC", WarningLevel::Warning}, + {"IDNF", WarningLevel::Alert}, + {"ILI", WarningLevel::Notice}, + {"MC", WarningLevel::None}, + {"MCR", WarningLevel::None}, + {"NM", WarningLevel::None}, + {"obs", WarningLevel::None}, + {"TK0NF", WarningLevel::Alert}, + {"UNC", WarningLevel::Alert}, + {"WP", WarningLevel::None}, }; if (m.find(type) != m.end()) { return m.at(type); } - return WarningLevel::none; // unknown error + return WarningLevel::None; // unknown error } @@ -244,18 +244,18 @@ std::ostream& operator<< (std::ostream& os, const AtaStorageErrorBlock& b) std::string AtaStorageSelftestEntry::get_status_displayable_name(AtaStorageSelftestEntry::Status s) { static const std::unordered_map m { - {Status::unknown, "[unknown]"}, - {Status::completed_no_error, "Completed without error"}, - {Status::aborted_by_host, "Manually aborted"}, - {Status::interrupted, "Interrupted (host reset)"}, - {Status::fatal_or_unknown, "Fatal or unknown error"}, - {Status::compl_unknown_failure, "Completed with unknown failure"}, - {Status::compl_electrical_failure, "Completed with electrical failure"}, - {Status::compl_servo_failure, "Completed with servo/seek failure"}, - {Status::compl_read_failure, "Completed with read failure"}, - {Status::compl_handling_damage, "Completed: handling damage"}, - {Status::in_progress, "In progress"}, - {Status::reserved, "Unknown / reserved state"}, + {Status::Unknown, "[unknown]"}, + {Status::CompletedNoError, "Completed without error"}, + {Status::AbortedByHost, "Manually aborted"}, + {Status::Interrupted, "Interrupted (host reset)"}, + {Status::FatalOrUnknown, "Fatal or unknown error"}, + {Status::ComplUnknownFailure, "Completed with unknown failure"}, + {Status::ComplElectricalFailure, "Completed with electrical failure"}, + {Status::ComplServoFailure, "Completed with servo/seek failure"}, + {Status::ComplReadFailure, "Completed with read failure"}, + {Status::ComplHandlingDamage, "Completed: handling damage"}, + {Status::InProgress, "In progress"}, + {Status::Reserved, "Unknown / reserved state"}, }; if (auto iter = m.find(s); iter != m.end()) { return iter->second; @@ -268,30 +268,30 @@ std::string AtaStorageSelftestEntry::get_status_displayable_name(AtaStorageSelft AtaStorageSelftestEntry::StatusSeverity AtaStorageSelftestEntry::get_status_severity(AtaStorageSelftestEntry::Status s) { static const std::unordered_map m { - {Status::unknown, StatusSeverity::none}, - {Status::completed_no_error, StatusSeverity::none}, - {Status::aborted_by_host, StatusSeverity::warning}, - {Status::interrupted, StatusSeverity::warning}, - {Status::fatal_or_unknown, StatusSeverity::error}, - {Status::compl_unknown_failure, StatusSeverity::error}, - {Status::compl_electrical_failure, StatusSeverity::error}, - {Status::compl_servo_failure, StatusSeverity::error}, - {Status::compl_read_failure, StatusSeverity::error}, - {Status::compl_handling_damage, StatusSeverity::error}, - {Status::in_progress, StatusSeverity::none}, - {Status::reserved, StatusSeverity::none}, + {Status::Unknown, StatusSeverity::None}, + {Status::CompletedNoError, StatusSeverity::None}, + {Status::AbortedByHost, StatusSeverity::Warning}, + {Status::Interrupted, StatusSeverity::Warning}, + {Status::FatalOrUnknown, StatusSeverity::Error}, + {Status::ComplUnknownFailure, StatusSeverity::Error}, + {Status::ComplElectricalFailure, StatusSeverity::Error}, + {Status::ComplServoFailure, StatusSeverity::Error}, + {Status::ComplReadFailure, StatusSeverity::Error}, + {Status::ComplHandlingDamage, StatusSeverity::Error}, + {Status::InProgress, StatusSeverity::None}, + {Status::Reserved, StatusSeverity::None}, }; if (auto iter = m.find(s); iter != m.end()) { return iter->second; } - return StatusSeverity::none; + return StatusSeverity::None; } std::string AtaStorageSelftestEntry::get_status_str() const { - return (status == Status::unknown ? status_str : get_status_displayable_name(status)); + return (status == Status::Unknown ? status_str : get_status_displayable_name(status)); } @@ -323,10 +323,10 @@ std::ostream& operator<< (std::ostream& os, const AtaStorageSelftestEntry& b) std::string AtaStorageProperty::get_section_name(AtaStorageProperty::Section s) { static const std::unordered_map m { - {Section::unknown, "unknown"}, - {Section::info, "info"}, - {Section::data, "data"}, - {Section::internal, "internal"}, + {Section::Unknown, "unknown"}, + {Section::Info, "info"}, + {Section::Data, "data"}, + {Section::Internal, "internal"}, }; if (auto iter = m.find(s); iter != m.end()) { return iter->second; @@ -339,18 +339,18 @@ std::string AtaStorageProperty::get_section_name(AtaStorageProperty::Section s) std::string AtaStorageProperty::get_subsection_name(AtaStorageProperty::SubSection s) { static const std::unordered_map m { - {SubSection::unknown, "unknown"}, - {SubSection::health, "health"}, - {SubSection::capabilities, "capabilities"}, - {SubSection::attributes, "attributes"}, - {SubSection::devstat, "devstat"}, - {SubSection::error_log, "error_log"}, - {SubSection::selftest_log, "selftest_log"}, - {SubSection::selective_selftest_log, "selective_selftest_log"}, - {SubSection::temperature_log, "temperature_log"}, - {SubSection::erc_log, "erc_log"}, - {SubSection::phy_log, "phy_log"}, - {SubSection::directory_log, "directory_log"}, + {SubSection::Unknown, "unknown"}, + {SubSection::Health, "health"}, + {SubSection::Capabilities, "capabilities"}, + {SubSection::Attributes, "attributes"}, + {SubSection::Devstat, "devstat"}, + {SubSection::ErrorLog, "error_log"}, + {SubSection::SelftestLog, "selftest_log"}, + {SubSection::SelectiveSelftestLog, "selective_selftest_log"}, + {SubSection::TemperatureLog, "temperature_log"}, + {SubSection::ErcLog, "erc_log"}, + {SubSection::PhyLog, "phy_log"}, + {SubSection::DirectoryLog, "directory_log"}, }; if (auto iter = m.find(s); iter != m.end()) { return iter->second; @@ -399,7 +399,7 @@ void AtaStorageProperty::dump(std::ostream& os, std::size_t internal_offset) con const std::string offset(internal_offset, ' '); os << offset << "[" << get_section_name(section) - << (section == Section::data ? (", " + get_subsection_name(subsection)) : "") << "]" + << (section == Section::Data ? (", " + get_subsection_name(subsection)) : "") << "]" << " " << generic_name // << (generic_name == reported_name ? "" : (" (" + reported_name + ")")) << ": [" << get_value_type_name() << "] "; diff --git a/src/applib/ata_storage_property.h b/src/applib/ata_storage_property.h index b8620fc..1d3e678 100644 --- a/src/applib/ata_storage_property.h +++ b/src/applib/ata_storage_property.h @@ -56,9 +56,9 @@ class AtaStorageAttribute { /// Attribute pre-failure / old-age type enum class AttributeType { - unknown, ///< Unknown - prefail, ///< Pre-failure (reported: Pre-fail) - old_age ///< Old age (reported: Old_age) + Unknown, ///< Unknown + Prefail, ///< Pre-failure (reported: Pre-fail) + OldAge ///< Old age (reported: Old_age) }; /// Get readable attribute type name @@ -67,9 +67,9 @@ class AtaStorageAttribute { /// Attribute when-updated type enum class UpdateType { - unknown, ///< Unknown - always, ///< Continuously (reported: Always) - offline ///< Only during offline data collection (reported: Offline) + Unknown, ///< Unknown + Always, ///< Continuously (reported: Always) + Offline ///< Only during offline data collection (reported: Offline) }; /// Get readable when-updated type name @@ -78,10 +78,10 @@ class AtaStorageAttribute { /// Attribute when-failed type enum class FailTime { - unknown, ///< Unknown - none, ///< Never (reported: -) - past, ///< In the past (reported: In_the_past) - now ///< Now (reported: FAILING_NOW) + Unknown, ///< Unknown + None, ///< Never (reported: -) + Past, ///< In the past (reported: In_the_past) + Now ///< Now (reported: FAILING_NOW) }; /// Get a readable when-failed type name @@ -97,9 +97,9 @@ class AtaStorageAttribute { std::optional value; ///< Normalized value. May be unset ("---"). std::optional worst; ///< Worst ever value. May be unset ("---"). std::optional threshold; ///< Threshold for normalized value. May be unset ("---"). - AttributeType attr_type = AttributeType::unknown; ///< Attribute pre-fail / old-age type - UpdateType update_type = UpdateType::unknown; ///< When-updated type - FailTime when_failed = FailTime::unknown; ///< When-failed type + AttributeType attr_type = AttributeType::Unknown; ///< Attribute pre-fail / old-age type + UpdateType update_type = UpdateType::Unknown; ///< When-updated type + FailTime when_failed = FailTime::Unknown; ///< When-failed type std::string raw_value; ///< Raw value as a string, as presented by smartctl (formatted). int64_t raw_value_int = 0; ///< Same as raw_value, but parsed as int64. original value is 6 bytes I think. @@ -141,10 +141,10 @@ class AtaStorageErrorBlock { public: /// Get readable error types from reported types - static std::string get_displayable_error_types(const std::vector& types); + [[nodiscard]] static std::string get_displayable_error_types(const std::vector& types); /// Get warning level (Warning) for an error type - static WarningLevel get_warning_level_for_error_type(const std::string& type); + [[nodiscard]] static WarningLevel get_warning_level_for_error_type(const std::string& type); /// Format lifetime hours with comma [[nodiscard]] std::string format_lifetime_hours() const; @@ -170,25 +170,25 @@ class AtaStorageSelftestEntry { /// Self-test log entry status enum class Status { - unknown, ///< Initial state - completed_no_error, ///< Completed with no error, or no test was run - aborted_by_host, ///< Aborted by host - interrupted, ///< Interrupted by user - fatal_or_unknown, ///< Fatal or unknown error. Treated as test failure. - compl_unknown_failure, ///< Completed with unknown error. Treated as test failure. - compl_electrical_failure, ///< Completed with electrical error. Treated as test failure. - compl_servo_failure, ///< Completed with servo error. Treated as test failure. - compl_read_failure, ///< Completed with read error. Treated as test failure. - compl_handling_damage, ///< Completed with handling damage error. Treated as test failure. - in_progress, ///< Test in progress - reserved ///< Reserved + Unknown, ///< Initial state + CompletedNoError, ///< Completed with no error, or no test was run + AbortedByHost, ///< Aborted by host + Interrupted, ///< Interrupted by user + FatalOrUnknown, ///< Fatal or unknown error. Treated as test failure. + ComplUnknownFailure, ///< Completed with unknown error. Treated as test failure. + ComplElectricalFailure, ///< Completed with electrical error. Treated as test failure. + ComplServoFailure, ///< Completed with servo error. Treated as test failure. + ComplReadFailure, ///< Completed with read error. Treated as test failure. + ComplHandlingDamage, ///< Completed with handling damage error. Treated as test failure. + InProgress, ///< Test in progress + Reserved ///< Reserved }; /// Self-test error severity enum class StatusSeverity { - none, - warning, - error + None, + Warning, + Error }; /// Get log entry status displayable name @@ -209,7 +209,7 @@ class AtaStorageSelftestEntry { uint32_t test_num = 0; ///< Test number. always starts from 1. larger means older or newer, depending on model. 0 for capability. std::string type; ///< Extended offline, Short offline, Conveyance offline, etc... . capability: unused. std::string status_str; ///< Self-test routine in progress, Completed without error, etc... (as reported by log or capability) - Status status = Status::unknown; ///< same as status_str, but from enum + Status status = Status::Unknown; ///< same as status_str, but from enum int8_t remaining_percent = -1; ///< Remaining %. 0% for completed, 90% for started. -1 if n/a. uint32_t lifetime_hours = 0; ///< When the test happened (in lifetime hours). capability: unused. std::string lba_of_first_error; ///< LBA of the first error. "-" or value (format? usually hex). capability: unused. @@ -228,10 +228,10 @@ class AtaStorageProperty { /// Sections in output enum class Section { - unknown, ///< Used when searching in all sections - info, ///< Short info (--info) - data, ///< SMART DATA - internal ///< Internal application-specific data + Unknown, ///< Used when searching in all sections + Info, ///< Short info (--info) + Data, ///< SMART DATA + Internal ///< Internal application-specific data }; /// Get displayable section type name @@ -240,18 +240,18 @@ class AtaStorageProperty { /// Subsections in smart data section enum class SubSection { - unknown, ///< Used when searching in all subsections - health, ///< Overall-health (-H, --health) - capabilities, ///< General SMART Values, aka Capabilities (-c, --capabilities) - attributes, ///< Attributes (-A, --attributes). These need decoding. - devstat, ///< Device statistics (--log=devstat). These need decoding. - error_log, ///< Error Log (--log=error) - selftest_log, ///< Self-test log (--log=selftest) - selective_selftest_log, ///< Selective self-test log (--log=selective) - temperature_log, ///< SCT temperature (current and history) (--log=scttemp) - erc_log, ///< SCT Error Recovery Control settings (--log=scterc) - phy_log, ///< Phy log (--log=sataphy) - directory_log, ///< Directory log (--log=directory) + Unknown, ///< Used when searching in all subsections + Health, ///< Overall-health (-H, --health) + Capabilities, ///< General SMART Values, aka Capabilities (-c, --capabilities) + Attributes, ///< Attributes (-A, --attributes). These need decoding. + Devstat, ///< Device statistics (--log=devstat). These need decoding. + ErrorLog, ///< Error Log (--log=error) + SelftestLog, ///< Self-test log (--log=selftest) + SelectiveSelftestLog, ///< Selective self-test log (--log=selective) + TemperatureLog, ///< SCT temperature (current and history) (--log=scttemp) + ErcLog, ///< SCT Error Recovery Control settings (--log=scterc) + PhyLog, ///< Phy log (--log=sataphy) + DirectoryLog, ///< Directory log (--log=directory) }; /// Get displayable subsection type name @@ -276,7 +276,7 @@ class AtaStorageProperty { /// Get value of type T template - const T& get_value() const; + [[nodiscard]] const T& get_value() const; /// Check if value is of type T @@ -302,8 +302,8 @@ class AtaStorageProperty { std::string description; ///< Property description (for tooltips, etc...). May contain markup. - Section section = Section::unknown; ///< Section this property belongs to - SubSection subsection = SubSection::unknown; ///< Subsection this property belongs to + Section section = Section::Unknown; ///< Section this property belongs to + SubSection subsection = SubSection::Unknown; ///< Subsection this property belongs to std::string reported_value; ///< String representation of the value as reported std::string readable_value; ///< User-friendly readable representation of value. if empty, use the other members. @@ -321,7 +321,7 @@ class AtaStorageProperty { AtaStorageSelftestEntry ///< Value (if it's a self-test entry) > value; - WarningLevel warning_level = WarningLevel::none; ///< Warning severity for this property + WarningLevel warning_level = WarningLevel::None; ///< Warning severity for this property std::string warning_reason; // Warning reason (displayable) bool show_in_ui = true; ///< Whether to show this property in UI or not diff --git a/src/applib/ata_storage_property_descr.cpp b/src/applib/ata_storage_property_descr.cpp index fa596be..5507027 100644 --- a/src/applib/ata_storage_property_descr.cpp +++ b/src/applib/ata_storage_property_descr.cpp @@ -1151,7 +1151,7 @@ namespace { /// Get program-wide attribute description database - inline const AttributeDatabase& get_attribute_db() + [[nodiscard]] inline const AttributeDatabase& get_attribute_db() { static const AttributeDatabase attribute_db; return attribute_db; @@ -1387,7 +1387,7 @@ namespace { /// Get program-wide devstat description database - inline const StatisticsDatabase& get_devstat_db() + [[nodiscard]] inline const StatisticsDatabase& get_devstat_db() { static const StatisticsDatabase devstat_db; return devstat_db; @@ -1571,7 +1571,7 @@ bool ata_storage_property_autoset_description(AtaStorageProperty& p, AtaStorageA found = true; // Section Info - } else if (p.section == AtaStorageProperty::Section::info) { + } else if (p.section == AtaStorageProperty::Section::Info) { found = auto_set(p, "model_family", "Model family (from smartctl database)") || auto_set(p, "model_name", "Device model") || auto_set(p, "serial_number", "Serial number, unique to each physical drive") @@ -1593,15 +1593,15 @@ bool ata_storage_property_autoset_description(AtaStorageProperty& p, AtaStorageA found = true; } - } else if (p.section == AtaStorageProperty::Section::data) { + } else if (p.section == AtaStorageProperty::Section::Data) { switch (p.subsection) { - case AtaStorageProperty::SubSection::health: + case AtaStorageProperty::SubSection::Health: found = auto_set(p, "smart_status/passed", "Overall health self-assessment test result. Note: If the drive passes this test, it doesn't mean it's OK. " "However, if the drive doesn't pass it, then it's either already dead, or it's predicting its own failure within the next 24 hours. In this case do a backup immediately!"); break; - case AtaStorageProperty::SubSection::capabilities: + case AtaStorageProperty::SubSection::Capabilities: found = auto_set(p, "ata_smart_data/offline_data_collection/status/_group", "Offline Data Collection (a.k.a. Offline test) is usually automatically performed when the device is idle or every fixed amount of time. " "This should show if Automatic Offline Data Collection is enabled.") || auto_set(p, "ata_smart_data/offline_data_collection/completion_seconds", "Offline Data Collection (a.k.a. Offline test) is usually automatically performed when the device is idle or every fixed amount of time. " @@ -1617,7 +1617,7 @@ bool ata_storage_property_autoset_description(AtaStorageProperty& p, AtaStorageA || auto_set(p, "ata_sct_capabilities/_group", "Drive properties related to temperature information."); break; - case AtaStorageProperty::SubSection::attributes: + case AtaStorageProperty::SubSection::Attributes: found = auto_set(p, "ata_smart_attributes/revision", p.displayable_name.c_str()); if (!found) { auto_set_attr(p, disk_type); @@ -1625,11 +1625,11 @@ bool ata_storage_property_autoset_description(AtaStorageProperty& p, AtaStorageA } break; - case AtaStorageProperty::SubSection::devstat: + case AtaStorageProperty::SubSection::Devstat: found = auto_set_statistic(p); break; - case AtaStorageProperty::SubSection::error_log: + case AtaStorageProperty::SubSection::ErrorLog: found = auto_set(p, "ata_smart_error_log/extended/revision", p.displayable_name.c_str()) || auto_set(p, "ata_smart_error_log/extended/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...)."); @@ -1642,24 +1642,24 @@ bool ata_storage_property_autoset_description(AtaStorageProperty& p, AtaStorageA } break; - case AtaStorageProperty::SubSection::selftest_log: + case AtaStorageProperty::SubSection::SelftestLog: found = auto_set(p, "ata_smart_self_test_log/extended/revision", p.displayable_name.c_str()) || auto_set(p, "ata_smart_self_test_log/extended/table/count", "Number of tests in selftest log. Note: The number of entries may be limited to the newest manual tests."); // || auto_set(p, "ata_smart_self_test_log/_present", "This device does not support self-test logging."); // the property text already says that break; - case AtaStorageProperty::SubSection::selective_selftest_log: + case AtaStorageProperty::SubSection::SelectiveSelftestLog: // nothing here break; - case AtaStorageProperty::SubSection::temperature_log: + case AtaStorageProperty::SubSection::TemperatureLog: found = auto_set(p, "ata_sct_status/_not_present", "SCT support is needed for SCT temperature logging."); break; - case AtaStorageProperty::SubSection::erc_log: - case AtaStorageProperty::SubSection::phy_log: - case AtaStorageProperty::SubSection::directory_log: - case AtaStorageProperty::SubSection::unknown: + case AtaStorageProperty::SubSection::ErcLog: + case AtaStorageProperty::SubSection::PhyLog: + case AtaStorageProperty::SubSection::DirectoryLog: + case AtaStorageProperty::SubSection::Unknown: // nothing break; } @@ -1673,46 +1673,46 @@ bool ata_storage_property_autoset_description(AtaStorageProperty& p, AtaStorageA WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) { - WarningLevel w = WarningLevel::none; + WarningLevel w = WarningLevel::None; std::string reason; // checksum errors first if (p.generic_name.find("_checksum_error") != std::string::npos) { - w = WarningLevel::warning; + w = WarningLevel::Warning; reason = "The drive may have a broken implementation of SMART, or it's failing."; // Section Info - } else if (p.section == AtaStorageProperty::Section::info) { + } else if (p.section == AtaStorageProperty::Section::Info) { if (name_match(p, "_custom/smart_supported") && !p.get_value()) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "SMART is not supported. You won't be able to read any SMART information from this drive."; } else if (name_match(p, "_custom/smart_enabled") && !p.get_value()) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "SMART is disabled. You should enable it to read any SMART information from this drive. " "Additionally, some drives do not log useful data with SMART disabled, so it's advisable to keep it always enabled."; } else if (name_match(p, "_text_only/info_warning")) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "Your drive may be affected by the warning, please see the details."; } - } else if (p.section == AtaStorageProperty::Section::data) { + } else if (p.section == AtaStorageProperty::Section::Data) { switch(p.subsection) { - case AtaStorageProperty::SubSection::health: + case AtaStorageProperty::SubSection::Health: if (name_match(p, "smart_status/passed") && !p.get_value()) { - w = WarningLevel::alert; + w = WarningLevel::Alert; reason = "The drive is reporting that it will FAIL very soon. Please back up as soon as possible!"; } break; - case AtaStorageProperty::SubSection::capabilities: + case AtaStorageProperty::SubSection::Capabilities: // nothing break; - case AtaStorageProperty::SubSection::attributes: + case AtaStorageProperty::SubSection::Attributes: { if (p.is_value_type()) { @@ -1723,86 +1723,86 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) // Reallocated Sector Count if (attr_match(p, "attr_reallocated_sector_count") && attr.raw_value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. " "This could be an indication of future failures and/or potential data loss in bad sectors."; // Spin-up Retry Count } else if (attr_match(p, "attr_spin_up_retry_count") && attr.raw_value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. " "Your drive may have problems spinning up, which could lead to a complete mechanical failure. Please back up."; // Soft Read Error Rate } else if (attr_match(p, "attr_soft_read_error_rate") && attr.raw_value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. " "This could be an indication of future failures and/or potential data loss in bad sectors."; // Temperature (for some it may be 10xTemp, so limit the upper bound.) } else if (attr_match(p, "attr_temperature_celsius") && attr.raw_value_int > 50 && attr.raw_value_int <= 120) { // 50C - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The temperature of the drive is higher than 50 degrees Celsius. " "This may shorten its lifespan and cause damage under severe load. Please install a cooling solution."; // Temperature (for some it may be 10xTemp, so limit the upper bound.) } else if (attr_match(p, "attr_temperature_celsius_x10") && attr.raw_value_int > 500) { // 50C - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The temperature of the drive is higher than 50 degrees Celsius. " "This may shorten its lifespan and cause damage under severe load. Please install a cooling solution."; // Reallocation Event Count } else if (attr_match(p, "attr_reallocation_event_count") && attr.raw_value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. " "This could be an indication of future failures and/or potential data loss in bad sectors."; // Current Pending Sector Count } else if ((attr_match(p, "attr_current_pending_sector_count") || attr_match(p, "attr_total_pending_sectors")) && attr.raw_value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. " "This could be an indication of future failures and/or potential data loss in bad sectors."; // Uncorrectable Sector Count } else if ((attr_match(p, "attr_offline_uncorrectable") || attr_match(p, "attr_total_attr_offline_uncorrectable")) && attr.raw_value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. " "This could be an indication of future failures and/or potential data loss in bad sectors."; // SSD Life Left (%) } else if ((attr_match(p, "attr_ssd_life_left")) && attr.value.value() < 50) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has less than half of its estimated life left."; // SSD Life Used (%) } else if ((attr_match(p, "attr_ssd_life_used")) && attr.raw_value_int >= 50) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has less than half of its estimated life left."; } // Now override this with reported SMART attribute failure warnings / errors - if (attr.when_failed == AtaStorageAttribute::FailTime::now) { // NOW + if (attr.when_failed == AtaStorageAttribute::FailTime::Now) { // NOW - if (attr.attr_type == AtaStorageAttribute::AttributeType::old_age) { // old-age - w = WarningLevel::warning; + if (attr.attr_type == AtaStorageAttribute::AttributeType::OldAge) { // old-age + w = WarningLevel::Warning; reason = "The drive has a failing old-age attribute. Usually this indicates a wear-out. You should consider replacing the drive."; } else { // pre-fail - w = WarningLevel::alert; + w = WarningLevel::Alert; reason = "The drive has a failing pre-fail attribute. Usually this indicates a that the drive will FAIL soon. Please back up immediately!"; } - } else if (attr.when_failed == AtaStorageAttribute::FailTime::past) { // PAST + } else if (attr.when_failed == AtaStorageAttribute::FailTime::Past) { // PAST - if (attr.attr_type == AtaStorageAttribute::AttributeType::old_age) { // old-age + if (attr.attr_type == AtaStorageAttribute::AttributeType::OldAge) { // old-age // nothing. we don't warn about e.g. temperature increase in the past } else { // pre-fail - w = WarningLevel::warning; // there was a problem, it got corrected (hopefully) + w = WarningLevel::Warning; // there was a problem, it got corrected (hopefully) reason = "The drive had a failing pre-fail attribute, but it has been restored to a normal value. " "This may be a serious problem, you should consider replacing the drive."; } @@ -1811,13 +1811,13 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) break; } - case AtaStorageProperty::SubSection::devstat: + case AtaStorageProperty::SubSection::Devstat: { if (p.is_value_type()) { const auto& statistic = p.get_value(); if (name_match(p, "Pending Error Count") && statistic.value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; // "Workload Utilization" is either normalized, or encodes several values, so we can't use it. @@ -1832,54 +1832,54 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) */ } else if (name_match(p, "Utilization Usage Rate") && statistic.value_int >= 50) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has less than half of its estimated life left."; } else if (name_match(p, "Utilization Usage Rate") && statistic.value_int >= 100) { - w = WarningLevel::warning; + w = WarningLevel::Warning; reason = "The drive is past its estimated lifespan."; } else if (name_match(p, "Number of Reallocated Logical Sectors") && !statistic.is_normalized() && statistic.value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; } else if (name_match(p, "Number of Reallocated Logical Sectors") && statistic.is_normalized() && statistic.value_int <= 0) { - w = WarningLevel::warning; + w = WarningLevel::Warning; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; } else if (name_match(p, "Number of Mechanical Start Failures") && statistic.value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive is reporting mechanical errors."; } else if (name_match(p, "Number of Realloc. Candidate Logical Sectors") && statistic.value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; } else if (name_match(p, "Number of Reported Uncorrectable Errors") && statistic.value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive is reporting surface errors. This could be an indication of future failures and/or potential data loss in bad sectors."; } else if (name_match(p, "Current Temperature") && statistic.value_int > 50) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The temperature of the drive is higher than 50 degrees Celsius. " "This may shorten its lifespan and cause damage under severe load. Please install a cooling solution."; } else if (name_match(p, "Time in Over-Temperature") && statistic.value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The temperature of the drive is or was over the manufacturer-specified maximum. " "This may have shortened its lifespan and caused damage. Please install a cooling solution."; } else if (name_match(p, "Time in Under-Temperature") && statistic.value_int > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The temperature of the drive is or was under the manufacturer-specified minimum. " "This may have shortened its lifespan and caused damage. Please operate the drive within manufacturer-specified temperature range."; } else if (name_match(p, "Percentage Used Endurance Indicator") && statistic.value_int >= 50) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive has less than half of its estimated life left."; } else if (name_match(p, "Percentage Used Endurance Indicator") && statistic.value_int >= 100) { - w = WarningLevel::warning; + w = WarningLevel::Warning; reason = "The drive is past its estimated lifespan."; } } @@ -1887,19 +1887,19 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) break; } - case AtaStorageProperty::SubSection::error_log: + case AtaStorageProperty::SubSection::ErrorLog: { // Note: The error list table doesn't display any descriptions, so if any // error-entry related descriptions are added here, don't forget to enable // the tooltips. if (name_match(p, "ata_smart_error_log/extended/count") && p.get_value() > 0) { - w = WarningLevel::notice; + w = WarningLevel::Notice; 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, "ata_smart_error_log/_not_present")) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive does not support error logging. This means that SMART error history is unavailable."; } @@ -1907,14 +1907,14 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) if (p.is_value_type()) { const auto& eb = p.get_value(); if (!eb.reported_types.empty()) { - WarningLevel error_block_warning = WarningLevel::none; + WarningLevel error_block_warning = WarningLevel::None; for (const auto& reported_type : eb.reported_types) { const WarningLevel individual_warning = AtaStorageErrorBlock::get_warning_level_for_error_type(reported_type); if (individual_warning > error_block_warning) { error_block_warning = WarningLevel(individual_warning); } } - if (error_block_warning > WarningLevel::none) { + if (error_block_warning > WarningLevel::None) { w = error_block_warning; reason = "The drive is reporting internal errors. Your data may be at risk depending on error severity."; } @@ -1924,7 +1924,7 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) break; } - case AtaStorageProperty::SubSection::selftest_log: + case AtaStorageProperty::SubSection::SelftestLog: { // Note: The error list table doesn't display any descriptions, so if any // error-entry related descriptions are added here, don't forget to enable @@ -1934,17 +1934,17 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) // Self-tests are carried manually anyway, so the user is expected to check their status anyway. if (name_match(p, "ata_smart_self_test_log/_present")) { - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The drive does not support self-test logging. This means that SMART test results won't be logged."; } break; } - case AtaStorageProperty::SubSection::selective_selftest_log: + case AtaStorageProperty::SubSection::SelectiveSelftestLog: // nothing here break; - case AtaStorageProperty::SubSection::temperature_log: + case AtaStorageProperty::SubSection::TemperatureLog: // Don't highlight SCT Unsupported as warning, it's harmless. // if (name_match(p, "ata_sct_status/_not_present") && p.value_bool) { // w = WarningLevel::notice; @@ -1952,16 +1952,16 @@ WarningLevel ata_storage_property_autoset_warning(AtaStorageProperty& p) // } // Current temperature if (name_match(p, "ata_sct_status/temperature/current") && p.get_value() > 50) { // 50C - w = WarningLevel::notice; + w = WarningLevel::Notice; reason = "The temperature of the drive is higher than 50 degrees Celsius. " "This may shorten its lifespan and cause damage under severe load. Please install a cooling solution."; } break; - case AtaStorageProperty::SubSection::erc_log: - case AtaStorageProperty::SubSection::phy_log: - case AtaStorageProperty::SubSection::directory_log: - case AtaStorageProperty::SubSection::unknown: + case AtaStorageProperty::SubSection::ErcLog: + case AtaStorageProperty::SubSection::PhyLog: + case AtaStorageProperty::SubSection::DirectoryLog: + case AtaStorageProperty::SubSection::Unknown: // nothing here break; } diff --git a/src/applib/command_executor.cpp b/src/applib/command_executor.cpp index c1c2e94..18756ca 100644 --- a/src/applib/command_executor.cpp +++ b/src/applib/command_executor.cpp @@ -72,7 +72,7 @@ bool CommandExecutor::execute() const bool slot_connected = !(signal_execute_tick().slots().begin() == signal_execute_tick().slots().end()); - if (slot_connected && !signal_execute_tick().emit(TickStatus::starting)) + if (slot_connected && !signal_execute_tick().emit(TickStatus::Starting)) return false; if (!cmdex_.execute()) { // try to execute @@ -84,7 +84,7 @@ bool CommandExecutor::execute() get_command_args(), get_stdout_str(), get_stderr_str(), get_error_msg())); if (slot_connected) - signal_execute_tick().emit(TickStatus::failed); + signal_execute_tick().emit(TickStatus::Failed); return false; } @@ -96,7 +96,7 @@ bool CommandExecutor::execute() if (!stop_requested) { // running and no stop requested yet // call the tick function with "running" periodically. // if it returns false, try to stop. - if (slot_connected && !signal_execute_tick().emit(TickStatus::running)) { + if (slot_connected && !signal_execute_tick().emit(TickStatus::Running)) { debug_out_info("app", DBG_FUNC_MSG << "execute_tick slot returned false, trying to stop the program.\n"); stop_requested = true; } @@ -119,7 +119,7 @@ bool CommandExecutor::execute() // alert the tick function if (stop_requested && slot_connected) { - signal_execute_tick().emit(TickStatus::stopping); // ignore returned value here + signal_execute_tick().emit(TickStatus::Stopping); // ignore returned value here } @@ -144,7 +144,7 @@ bool CommandExecutor::execute() get_command_args(), get_stdout_str(), get_stderr_str(), get_error_msg())); if (slot_connected) - signal_execute_tick().emit(TickStatus::stopped); // last call + signal_execute_tick().emit(TickStatus::Stopped); // last call return true; } diff --git a/src/applib/command_executor.h b/src/applib/command_executor.h index a1abdf3..c5fd72b 100644 --- a/src/applib/command_executor.h +++ b/src/applib/command_executor.h @@ -88,11 +88,11 @@ class CommandExecutor : public sigc::trackable { /// Get command to execute - std::string get_command_name() const; + [[nodiscard]] std::string get_command_name() const; /// Get command arguments - std::string get_command_args() const; + [[nodiscard]] std::string get_command_args() const; /// Execute the command. The function will return only after the command exits. @@ -136,10 +136,10 @@ class CommandExecutor : public sigc::trackable { void set_buffer_sizes(gsize stdout_buffer_size = 0, gsize stderr_buffer_size = 0); /// See AsyncCommandExecutor::get_stdout_str() for details. - std::string get_stdout_str(bool clear_existing = false); + [[nodiscard]] std::string get_stdout_str(bool clear_existing = false); /// See AsyncCommandExecutor::get_stderr_str() for details. - std::string get_stderr_str(bool clear_existing = false); + [[nodiscard]] std::string get_stderr_str(bool clear_existing = false); /// See AsyncCommandExecutor::set_exit_status_translator() for details. void set_exit_status_translator(AsyncCommandExecutor::exit_status_translator_func_t func); @@ -147,7 +147,7 @@ class CommandExecutor : public sigc::trackable { /// Get command execution error message. If \c with_header /// is true, a header set using set_error_header() will be displayed first. - std::string get_error_msg(bool with_header = false) const; + [[nodiscard]] std::string get_error_msg(bool with_header = false) const; /// Set a message to display when running. "{command}" in \c msg will be replaced by the command. @@ -159,7 +159,7 @@ class CommandExecutor : public sigc::trackable { /// Get error header string. See get_error_msg() - std::string get_error_header(); + [[nodiscard]] std::string get_error_header(); // ----------------- Signals @@ -167,11 +167,11 @@ class CommandExecutor : public sigc::trackable { /// Status flags for signal_execute_tick slots, along with possible return values. enum class TickStatus { - starting, ///< Return status will indicate whether to proceed with the execution - failed, ///< The execution failed - running, ///< Return status will indicate whether to abort the execution - stopping, ///< The child has been sent a signal - stopped ///< The child exited + Starting, ///< Return status will indicate whether to proceed with the execution + Failed, ///< The execution failed + Running, ///< Return status will indicate whether to abort the execution + Stopping, ///< The child has been sent a signal + Stopped ///< The child exited }; @@ -195,11 +195,11 @@ class CommandExecutor : public sigc::trackable { /// Get "running" message - std::string get_running_msg() const; + [[nodiscard]] std::string get_running_msg() const; /// Get command executor object - AsyncCommandExecutor& get_async_executor(); + [[nodiscard]] AsyncCommandExecutor& get_async_executor(); private: diff --git a/src/applib/command_executor_gui.cpp b/src/applib/command_executor_gui.cpp index 796787f..b35a036 100644 --- a/src/applib/command_executor_gui.cpp +++ b/src/applib/command_executor_gui.cpp @@ -146,7 +146,7 @@ void CommandExecutorGui::set_running_dialog_abort_mode(bool aborting) bool CommandExecutorGui::execute_tick_func(TickStatus status) { - if (status == TickStatus::starting) { + if (status == TickStatus::Starting) { if (execution_running_) return false; // already running, abort the new one (?) @@ -166,7 +166,7 @@ bool CommandExecutorGui::execute_tick_func(TickStatus status) } - if (status == TickStatus::failed) { + if (status == TickStatus::Failed) { // close the dialog this->show_hide_dialog(false); @@ -179,7 +179,7 @@ bool CommandExecutorGui::execute_tick_func(TickStatus status) } - if (status == TickStatus::running) { + if (status == TickStatus::Running) { while (Gtk::Main::events_pending()) { // Gtk::Main::iteration() returns true if Gtk::Main::quit() has been called, or if there's no Main yet. @@ -204,7 +204,7 @@ bool CommandExecutorGui::execute_tick_func(TickStatus status) } - if (status == TickStatus::stopping) { + if (status == TickStatus::Stopping) { if (Gtk::Main::iteration(false) && Gtk::Main::level() > 0) { return false; // we're exiting from the main loop, so return early } @@ -215,7 +215,7 @@ bool CommandExecutorGui::execute_tick_func(TickStatus status) } - if (status == TickStatus::stopped) { + if (status == TickStatus::Stopped) { // close the dialog. this->show_hide_dialog(false); diff --git a/src/applib/examples/example_storage_detector.cpp b/src/applib/examples/example_storage_detector.cpp index bfb5769..7258cf6 100644 --- a/src/applib/examples/example_storage_detector.cpp +++ b/src/applib/examples/example_storage_detector.cpp @@ -36,9 +36,9 @@ int main() sd.add_blacklist_patterns(blacklist_patterns); auto ex_factory = std::make_shared(false); - std::string error_msg = sd.detect_and_fetch_basic_data(drives, ex_factory); - if (!error_msg.empty()) { - std::cerr << error_msg << "\n"; + auto fetch_error = sd.detect_and_fetch_basic_data(drives, ex_factory); + if (!fetch_error) { + std::cerr << fetch_error.error().message() << "\n"; } else { for (const auto& drive : drives) { diff --git a/src/applib/selftest.cpp b/src/applib/selftest.cpp index 2952581..afcf3b9 100644 --- a/src/applib/selftest.cpp +++ b/src/applib/selftest.cpp @@ -26,10 +26,10 @@ Copyright: std::string SelfTest::get_test_displayable_name(SelfTest::TestType type) { static const std::unordered_map m { - {TestType::immediate_offline, _("Immediate Offline Test")}, - {TestType::short_test, _("Short Self-Test")}, - {TestType::long_test, _("Extended Self-Test")}, - {TestType::conveyance, _("Conveyance Self-Test")}, + {TestType::ImmediateOffline, _("Immediate Offline Test")}, + {TestType::ShortTest, _("Short Self-Test")}, + {TestType::LongTest, _("Extended Self-Test")}, + {TestType::Conveyance, _("Conveyance Self-Test")}, }; if (auto iter = m.find(type); iter != m.end()) { return iter->second; @@ -70,14 +70,14 @@ std::chrono::seconds SelfTest::get_min_duration_seconds() const std::string prop_name; switch(type_) { - case TestType::immediate_offline: prop_name = "ata_smart_data/offline_data_collection/completion_seconds"; break; - case TestType::short_test: prop_name = "ata_smart_data/self_test/polling_minutes/short"; break; - case TestType::long_test: prop_name = "ata_smart_data/self_test/polling_minutes/extended"; break; - case TestType::conveyance: prop_name = "ata_smart_data/self_test/polling_minutes/conveyance"; break; + case TestType::ImmediateOffline: prop_name = "ata_smart_data/offline_data_collection/completion_seconds"; break; + case TestType::ShortTest: prop_name = "ata_smart_data/self_test/polling_minutes/short"; break; + case TestType::LongTest: prop_name = "ata_smart_data/self_test/polling_minutes/extended"; break; + case TestType::Conveyance: prop_name = "ata_smart_data/self_test/polling_minutes/conveyance"; break; } const AtaStorageProperty p = drive_->get_property_repository().lookup_property(prop_name, - AtaStorageProperty::Section::data, AtaStorageProperty::SubSection::capabilities); + AtaStorageProperty::Section::Data, AtaStorageProperty::SubSection::Capabilities); // p stores it as uint64_t return (total_duration_ = (p.empty() ? 0s : p.get_value())); @@ -92,18 +92,18 @@ bool SelfTest::is_supported() const std::string prop_name; switch(type_) { - case TestType::immediate_offline: + case TestType::ImmediateOffline: // prop_name = "ata_smart_data/capabilities/exec_offline_immediate_supported"; // break; return false; // disable this for now - it's unsupported. - case TestType::short_test: - case TestType::long_test: // same for short and long + case TestType::ShortTest: + case TestType::LongTest: // same for short and long prop_name = "ata_smart_data/capabilities/self_tests_supported"; break; - case TestType::conveyance: prop_name = "ata_smart_data/capabilities/conveyance_self_test_supported"; break; + case TestType::Conveyance: prop_name = "ata_smart_data/capabilities/conveyance_self_test_supported"; break; } - const AtaStorageProperty p = drive_->get_property_repository().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()); } @@ -111,38 +111,43 @@ bool SelfTest::is_supported() const // start the test -std::string SelfTest::start(const std::shared_ptr& smartctl_ex) +hz::ExpectedVoid SelfTest::start(const std::shared_ptr& smartctl_ex) { - if (!drive_) - return "[internal error: drive must not be NULL]"; - if (drive_->get_test_is_active()) - return _("A test is already running on this drive."); + if (!drive_) { + return hz::Unexpected(SelfTestError::InternalError, _("Internal Error: Drive must not be NULL.")); + } + if (drive_->get_test_is_active()) { + return hz::Unexpected(SelfTestError::AlreadyRunning, _("A test is already running on this drive.")); + } if (!this->is_supported()) { - /// Translators: %1 is test name - Short test, etc... - return Glib::ustring::compose(_("%1 is unsupported by this drive."), get_test_displayable_name(type_)); + // Translators: {} is a test name - Short test, etc... + return hz::Unexpected(SelfTestError::UnsupportedTest, + std::vformat(_("{} is unsupported by this drive."), std::make_format_args(get_test_displayable_name(type_)))); } std::string test_param; switch(type_) { - case TestType::immediate_offline: test_param = "offline"; break; - case TestType::short_test: test_param = "short"; break; - case TestType::long_test: test_param = "long"; break; - case TestType::conveyance: test_param = "conveyance"; break; + case TestType::ImmediateOffline: test_param = "offline"; break; + case TestType::ShortTest: test_param = "short"; break; + case TestType::LongTest: test_param = "long"; break; + case TestType::Conveyance: test_param = "conveyance"; break; // no default - this way we get warned by compiler if we're not listing all of them. } - if (test_param.empty()) - return _("Invalid test specified"); - - std::string output; - std::string error_msg = drive_->execute_device_smartctl("--test=" + test_param, smartctl_ex, output); - - if (!error_msg.empty()) // checks for empty output too - return error_msg; - - if (!app_pcre_match(R"(/^Drive command .* successful\.\nTesting has begun\.$/mi)", output)) { - return _("Sending command to drive failed."); + if (test_param.empty()) { + return hz::Unexpected(SelfTestError::InvalidTestType, _("Invalid test specified.")); } + std::string output; + auto execute_status = drive_->execute_device_smartctl("--test=" + test_param, smartctl_ex, output); + + if (!execute_status) { + return hz::Unexpected(SelfTestError::CommandFailed, + std::vformat(_("Sending command to drive failed: {}"), std::make_format_args(execute_status.error().message()))); + } + + if (!app_pcre_match(R"(/^Drive command .* successful\.\nTesting has begun\.$/mi)", output)) { + return hz::Unexpected(SelfTestError::CommandUnknownError, _("Sending command to drive failed.")); + } // update our members // error_message = this->update(smartctl_ex); @@ -154,7 +159,7 @@ std::string SelfTest::start(const std::shared_ptr& smartctl_ex) // Set up everything so that the caller won't have to. - status_ = AtaStorageSelftestEntry::Status::in_progress; + status_ = AtaStorageSelftestEntry::Status::InProgress; remaining_percent_ = 100; // set to 90 to avoid the 100->90 timer reset. this way we won't be looking at @@ -166,52 +171,55 @@ std::string SelfTest::start(const std::shared_ptr& smartctl_ex) drive_->set_test_is_active(true); - return {}; // everything ok } // abort test. -std::string SelfTest::force_stop(const std::shared_ptr& smartctl_ex) +hz::ExpectedVoid SelfTest::force_stop(const std::shared_ptr& smartctl_ex) { - if (!drive_) - return "[internal error: drive must not be NULL]"; - if (!drive_->get_test_is_active()) - return _("No test is currently running on this drive."); + if (!drive_) { + return hz::Unexpected(SelfTestError::InternalError, _("Internal Error: Drive must not be NULL.")); + } + if (!drive_->get_test_is_active()) { + return hz::Unexpected(SelfTestError::NotRunning, _("No test is currently running on this drive.")); + } // To abort immediate offline test, the device MUST have // "Abort Offline collection upon new command" capability, // 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) { + if (type_ == TestType::ImmediateOffline) { const AtaStorageProperty p = drive_->get_property_repository().lookup_property( - "ata_smart_data/capabilities/offline_is_aborted_upon_new_cmd", AtaStorageProperty::Section::internal); + "ata_smart_data/capabilities/offline_is_aborted_upon_new_cmd", AtaStorageProperty::Section::Internal); if (!p.empty() && p.get_value()) { // if empty, give a chance to abort anyway. - return _("Aborting this test is unsupported by the drive."); + return hz::Unexpected(SelfTestError::StopUnsupported, _("Aborting this test is unsupported by the drive.")); } // else, proceed as any other test } // To abort non-captive short, long and conveyance tests, use "--abort". std::string output; - std::string error_msg = drive_->execute_device_smartctl("--abort", smartctl_ex, output); + auto execute_status = drive_->execute_device_smartctl("--abort", smartctl_ex, output); - if (!error_msg.empty()) // checks for empty output too - return error_msg; + if (!execute_status) { + return hz::Unexpected(SelfTestError::CommandFailed, + std::vformat(_("Sending command to drive failed: {}"), std::make_format_args(execute_status.error().message()))); + } // this command prints success even if no test was running. if (!app_pcre_match("/^Self-testing aborted!$/mi", output)) { - return _("Sending command to drive failed."); + return hz::Unexpected(SelfTestError::CommandUnknownError, _("Sending command to drive failed.")); } // update our members - error_msg = this->update(smartctl_ex); + auto update_status = this->update(smartctl_ex); // the thing is, update() may fail to actually update the statuses, so // do it manually. - if (status_ == AtaStorageSelftestEntry::Status::in_progress) { // update() couldn't do its job - status_ = AtaStorageSelftestEntry::Status::aborted_by_host; + if (status_ == AtaStorageSelftestEntry::Status::InProgress) { // update() couldn't do its job + status_ = AtaStorageSelftestEntry::Status::AbortedByHost; remaining_percent_ = -1; last_seen_percent_ = -1; poll_in_seconds_ = std::chrono::seconds(-1); @@ -219,8 +227,11 @@ std::string SelfTest::force_stop(const std::shared_ptr& smartct drive_->set_test_is_active(false); } - if (!error_msg.empty()) // update can error out too. - return error_msg; + if (!update_status) { // update can error out too. + return hz::Unexpected(SelfTestError::UpdateError, + std::vformat(_("Error fetching test progress information: {}"), std::make_format_args(update_status.error().message()))); + } + return {}; // everything ok } @@ -228,27 +239,31 @@ std::string SelfTest::force_stop(const std::shared_ptr& smartct // update status variables. note: the returned error is an error in logic, // not a hw defect error. -std::string SelfTest::update(const std::shared_ptr& smartctl_ex) +hz::ExpectedVoid SelfTest::update(const std::shared_ptr& smartctl_ex) { using namespace std::literals; - if (!drive_) - return "[internal error: drive must not be NULL]"; + if (!drive_) { + return hz::Unexpected(SelfTestError::InternalError, _("Internal Error: Drive must not be NULL.")); + } std::string output; // std::string error_message = drive_->execute_device_smartctl("--log=selftest", smartctl_ex, output); - std::string error_msg = drive_->execute_device_smartctl("--capabilities", smartctl_ex, output); + auto execute_status = drive_->execute_device_smartctl("--capabilities", smartctl_ex, output); - if (!error_msg.empty()) // checks for empty output too - return error_msg; + if (!execute_status) { + return hz::Unexpected(SelfTestError::CommandFailed, + std::vformat(_("Sending command to drive failed: {}"), std::make_format_args(execute_status.error().message()))); + } const AtaStorageAttribute::DiskType disk_type = drive_->get_is_hdd() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd; auto parser = SmartctlParser::create(SmartctlParserType::Ata, SmartctlVersionParser::get_default_format(SmartctlParserType::Ata)); - DBG_ASSERT_RETURN(parser, "Cannot create parser"); + DBG_ASSERT_RETURN(parser, hz::Unexpected(SelfTestError::ParseError, _("Cannot create parser."))); auto parse_status = parser->parse(output); if (!parse_status) { - return Glib::ustring::compose(_("Cannot parse smartctl output: %1"), parse_status.error().message()); + return hz::Unexpected(SelfTestError::ParseError, + std::vformat(_("Cannot parse smartctl output: {}"), std::make_format_args(parse_status.error().message()))); } auto property_repo = StoragePropertyProcessor::process_properties(parser->get_property_repository(), disk_type); @@ -258,18 +273,19 @@ std::string SelfTest::update(const std::shared_ptr& smartctl_ex AtaStorageProperty p; 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 + if (e.section != AtaStorageProperty::Section::Internal || !e.is_value_type() || e.get_value().test_num != 0 || e.generic_name != "ata_smart_data/self_test/status/passed") continue; p = e; } - if (p.empty()) - return _("The drive doesn't report the test status."); + if (p.empty()) { + return hz::Unexpected(SelfTestError::ReportUnsupported, _("The drive doesn't report the test status.")); + } status_ = p.get_value().status; - const bool active = (status_ == AtaStorageSelftestEntry::Status::in_progress); + const bool active = (status_ == AtaStorageSelftestEntry::Status::InProgress); // Note that the test needs 90% to complete, not 100. It starts at 90% @@ -296,7 +312,7 @@ std::string SelfTest::update(const std::shared_ptr& smartctl_ex // for long tests we don't want to make the user wait too much, so // we need to poll more frequently by the end, in case it's completed. - if (type_ == TestType::long_test && remaining_percent_ == 10) + if (type_ == TestType::LongTest && remaining_percent_ == 10) poll_in_seconds_ = std::chrono::seconds(std::max(int64_t(1*60), int64_t(gran / 10.))); // that's 2 min for 180min extended test debug_out_dump("app", DBG_FUNC_MSG << "total: " << total.count() << ", gran: " << gran diff --git a/src/applib/selftest.h b/src/applib/selftest.h index f0024cf..518f64c 100644 --- a/src/applib/selftest.h +++ b/src/applib/selftest.h @@ -22,6 +22,21 @@ Copyright: #include "command_executor.h" +enum class SelfTestError { + InternalError, + AlreadyRunning, + UnsupportedTest, + InvalidTestType, + CommandFailed, + CommandUnknownError, + NotRunning, + StopUnsupported, + UpdateError, + ParseError, + ReportUnsupported, +}; + + /// SMART self-test runner. class SelfTest { @@ -29,15 +44,15 @@ class SelfTest { /// Test type enum class TestType { - immediate_offline, ///< Immediate offline, not supported - short_test, ///< Short self-test - long_test, ///< Extended (a.k.a. long) self-test - conveyance ///< Conveyance self-test + ImmediateOffline, ///< Immediate offline, not supported + ShortTest, ///< Short self-test + LongTest, ///< Extended (a.k.a. long) self-test + Conveyance ///< Conveyance self-test }; /// Get displayable name for a test type - static std::string get_test_displayable_name(TestType type); + [[nodiscard]] static std::string get_test_displayable_name(TestType type); /// Constructor. \c drive must have the capabilities present in its properties. @@ -47,15 +62,15 @@ class SelfTest { /// Check if the test is currently active - bool is_active() const + [[nodiscard]] bool is_active() const { - return (status_ == AtaStorageSelftestEntry::Status::in_progress); + return (status_ == AtaStorageSelftestEntry::Status::InProgress); } /// Get remaining time percent until the test completion. /// \return -1 if N/A or unknown. - int8_t get_remaining_percent() const + [[nodiscard]] int8_t get_remaining_percent() const { return remaining_percent_; } @@ -63,61 +78,61 @@ class SelfTest { /// Get estimated time of completion for the test. /// \return -1 if N/A or unknown. Note that 0 is a valid value. - std::chrono::seconds get_remaining_seconds() const; + [[nodiscard]] std::chrono::seconds get_remaining_seconds() const; /// Get test type - TestType get_test_type() const + [[nodiscard]] TestType get_test_type() const { return type_; } /// Get test status - AtaStorageSelftestEntry::Status get_status() const + [[nodiscard]] AtaStorageSelftestEntry::Status get_status() const { return status_; } /// Get the number of seconds after which the caller should call update(). - std::chrono::seconds get_poll_in_seconds() const + [[nodiscard]] std::chrono::seconds get_poll_in_seconds() const { return poll_in_seconds_; } /// Get a constant "test duration during idle" capability drive's stored capabilities. -1 if N/A. - std::chrono::seconds get_min_duration_seconds() const; + [[nodiscard]] std::chrono::seconds get_min_duration_seconds() const; /// Gets the current test type support status from drive's stored capabilities. - bool is_supported() const; + [[nodiscard]] bool is_supported() const; /// Start the test. Note that this object is not reusable, start() must be called /// only on newly constructed objects. /// \return error message on error, empty string on success. - std::string start(const std::shared_ptr& smartctl_ex = nullptr); + hz::ExpectedVoid start(const std::shared_ptr& smartctl_ex = nullptr); /// Abort the running test. /// \return error message on error, empty string on success. - std::string force_stop(const std::shared_ptr& smartctl_ex = nullptr); + hz::ExpectedVoid force_stop(const std::shared_ptr& smartctl_ex = nullptr); /// Update status variables. The user should call this every get_poll_in_seconds() seconds. /// \return error message on error, empty string on success. - std::string update(const std::shared_ptr& smartctl_ex = nullptr); + hz::ExpectedVoid update(const std::shared_ptr& smartctl_ex = nullptr); private: StorageDevicePtr drive_; ///< Drive to run the tests on - TestType type_ = TestType::short_test; ///< Test type + TestType type_ = TestType::ShortTest; ///< Test type // status variables: - AtaStorageSelftestEntry::Status status_ = AtaStorageSelftestEntry::Status::unknown; ///< Current status of the test as reported by the drive + AtaStorageSelftestEntry::Status status_ = AtaStorageSelftestEntry::Status::Unknown; ///< Current status of the test as reported by the drive int8_t remaining_percent_ = -1; ///< Remaining %. 0 means unknown, -1 means N/A. This is set to 100 on start. int8_t last_seen_percent_ = -1; ///< Last reported %, to detect changes in percentage (needed for timer update). mutable std::chrono::seconds total_duration_ = std::chrono::seconds(-1); ///< Total duration needed for the test, as reported by the drive. Constant. This variable acts as a cache. diff --git a/src/applib/smartctl_executor.cpp b/src/applib/smartctl_executor.cpp index a278848..55d37e4 100644 --- a/src/applib/smartctl_executor.cpp +++ b/src/applib/smartctl_executor.cpp @@ -80,7 +80,7 @@ hz::fs::path get_smartctl_binary() -std::string execute_smartctl(const std::string& device, const std::string& device_opts, +hz::ExpectedVoid execute_smartctl(const std::string& device, const std::string& device_opts, const std::string& command_options, std::shared_ptr smartctl_ex, std::string& smartctl_output) { @@ -89,7 +89,7 @@ std::string execute_smartctl(const std::string& device, const std::string& devic const std::string::size_type pos = device.rfind('/'); // find basename if (pos == std::string::npos) { debug_out_error("app", DBG_FUNC_MSG << "Invalid device name \"" << device << "\".\n"); - return _("Invalid device name specified."); + return hz::Unexpected(SmartctlExecutorError::InvalidDevice, _("Invalid device name specified.")); } } @@ -101,7 +101,7 @@ std::string execute_smartctl(const std::string& device, const std::string& devic if (smartctl_binary.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Smartctl binary is not set in config.\n"); - return _("Smartctl binary is not specified in configuration."); + return hz::Unexpected(SmartctlExecutorError::NoBinary, _("Smartctl binary is not specified in configuration.")); } auto smartctl_def_options = rconfig::get_data("system/smartctl_options"); @@ -127,18 +127,18 @@ std::string execute_smartctl(const std::string& device, const std::string& devic // check if it's a device permission error. // Smartctl open device: /dev/sdb failed: Permission denied if (app_pcre_match("/Smartctl open device.+Permission denied/mi", smartctl_output)) { - return _("Permission denied while opening device."); + return hz::Unexpected(SmartctlExecutorError::PermissionDenied, _("Permission denied while opening device.")); } // smartctl_output = smartctl_ex->get_stdout_str(); - return smartctl_ex->get_error_msg(); + return hz::Unexpected(SmartctlExecutorError::ExecutionError, smartctl_ex->get_error_msg()); } // any_to_unix is needed for windows smartctl_output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str())); if (smartctl_output.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n"); - return _("Smartctl returned an empty output."); + return hz::Unexpected(SmartctlExecutorError::EmptyOutput, _("Smartctl returned an empty output.")); } return {}; diff --git a/src/applib/smartctl_executor.h b/src/applib/smartctl_executor.h index f199cd3..e2efd1e 100644 --- a/src/applib/smartctl_executor.h +++ b/src/applib/smartctl_executor.h @@ -19,6 +19,9 @@ Copyright: #include "async_command_executor.h" #include "command_executor.h" #include "hz/fs_ns.h" +#include "hz/error_container.h" + + @@ -162,12 +165,22 @@ using SmartctlExecutor = SmartctlExecutorGeneric; /// Get smartctl binary (from config, etc...). Returns an empty string if not found. -hz::fs::path get_smartctl_binary(); +[[nodiscard]] hz::fs::path get_smartctl_binary(); + + + +enum class SmartctlExecutorError { + InvalidDevice, ///< Device name is invalid + NoBinary, ///< Smartctl binary is not specified in configuration + PermissionDenied, ///< Permission denied while opening device + ExecutionError, ///< Error executing smartctl + EmptyOutput, ///< Smartctl returned an empty output +}; /// Execute smartctl on device \c device. /// \return error message on error, empty string on success. -std::string execute_smartctl(const std::string& device, const std::string& device_opts, +[[nodiscard]] hz::ExpectedVoid execute_smartctl(const std::string& device, const std::string& device_opts, const std::string& command_options, std::shared_ptr smartctl_ex, std::string& smartctl_output); diff --git a/src/applib/smartctl_json_ata_parser.cpp b/src/applib/smartctl_json_ata_parser.cpp index cb26c59..0057551 100644 --- a/src/applib/smartctl_json_ata_parser.cpp +++ b/src/applib/smartctl_json_ata_parser.cpp @@ -221,7 +221,7 @@ hz::ExpectedVoid SmartctlJsonAtaParser::parse_section_info( auto p = retrieval_func(json_root_node, key, displayable_name); if (p.has_value()) { // ignore if not found - p->section = AtaStorageProperty::Section::info; + p->section = AtaStorageProperty::Section::Info; add_property(p.value()); } } @@ -244,8 +244,8 @@ hz::ExpectedVoid SmartctlJsonAtaParser::parse_section_healt auto p = retrieval_func(json_root_node, key, displayable_name); if (p.has_value()) { // ignore if not found - p->section = AtaStorageProperty::Section::data; - p->subsection = AtaStorageProperty::SubSection::health; + p->section = AtaStorageProperty::Section::Data; + p->subsection = AtaStorageProperty::SubSection::Health; add_property(p.value()); } } diff --git a/src/applib/smartctl_json_ata_parser.h b/src/applib/smartctl_json_ata_parser.h index a908b3a..3ba8832 100644 --- a/src/applib/smartctl_json_ata_parser.h +++ b/src/applib/smartctl_json_ata_parser.h @@ -27,7 +27,7 @@ class SmartctlJsonAtaParser : public SmartctlParser { SmartctlJsonAtaParser() = default; // Overridden - hz::ExpectedVoid parse(std::string_view smartctl_output) override; + [[nodiscard]] hz::ExpectedVoid parse(std::string_view smartctl_output) override; private: diff --git a/src/applib/smartctl_json_basic_parser.cpp b/src/applib/smartctl_json_basic_parser.cpp index 9df4eb4..dadec34 100644 --- a/src/applib/smartctl_json_basic_parser.cpp +++ b/src/applib/smartctl_json_basic_parser.cpp @@ -73,14 +73,14 @@ hz::ExpectedVoid SmartctlJsonBasicParser::parse_section_bas 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 + 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 + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } @@ -177,7 +177,7 @@ hz::ExpectedVoid SmartctlJsonBasicParser::parse_section_bas auto p = retrieval_func(json_root_node, key, displayable_name); if (p.has_value()) { // ignore if not found - p->section = AtaStorageProperty::Section::info; + p->section = AtaStorageProperty::Section::Info; add_property(p.value()); } } diff --git a/src/applib/smartctl_json_basic_parser.h b/src/applib/smartctl_json_basic_parser.h index e2d3e88..0d38c99 100644 --- a/src/applib/smartctl_json_basic_parser.h +++ b/src/applib/smartctl_json_basic_parser.h @@ -27,7 +27,7 @@ class SmartctlJsonBasicParser : public SmartctlParser { SmartctlJsonBasicParser() = default; // Overridden - hz::ExpectedVoid parse(std::string_view smartctl_output) override; + [[nodiscard]] hz::ExpectedVoid parse(std::string_view smartctl_output) override; private: diff --git a/src/applib/smartctl_json_parser_helpers.h b/src/applib/smartctl_json_parser_helpers.h index e7213dd..31bfa5d 100644 --- a/src/applib/smartctl_json_parser_helpers.h +++ b/src/applib/smartctl_json_parser_helpers.h @@ -179,7 +179,7 @@ auto custom_string_formatter(std::function formatter) /// Parse version from json output, returning 2 properties. -inline hz::ExpectedVoid parse_version(const nlohmann::json& json_root_node, +[[nodiscard]] inline hz::ExpectedVoid parse_version(const nlohmann::json& json_root_node, AtaStorageProperty& merged_property, AtaStorageProperty& full_property) { using namespace SmartctlJsonParserHelpers; @@ -207,7 +207,7 @@ inline hz::ExpectedVoid parse_version(const nlohmann::json& // p.reported_value = smartctl_version; merged_property.readable_value = smartctl_version; merged_property.value = smartctl_version; // string-type value - merged_property.section = AtaStorageProperty::Section::info; // add to info section + merged_property.section = AtaStorageProperty::Section::Info; // add to info section } { full_property.set_name("Smartctl version", "smartctl/version/_merged_full", "Smartctl Version"); @@ -217,7 +217,7 @@ inline hz::ExpectedVoid parse_version(const nlohmann::json& get_node_data(json_root_node, "smartctl/build_info", {}).value_or(std::string()) ); full_property.value = full_property.readable_value; // string-type value - full_property.section = AtaStorageProperty::Section::info; // add to info section + full_property.section = AtaStorageProperty::Section::Info; // add to info section } if (!SmartctlVersionParser::check_format_supported(SmartctlOutputFormat::Json, smartctl_version)) { debug_out_warn("app", DBG_FUNC_MSG << "Incompatible smartctl version. Returning.\n"); diff --git a/src/applib/smartctl_parser.h b/src/applib/smartctl_parser.h index fb4c541..3d8520e 100644 --- a/src/applib/smartctl_parser.h +++ b/src/applib/smartctl_parser.h @@ -52,12 +52,12 @@ class SmartctlParser { /// Create an instance of this class. /// \return nullptr if no such class exists - static std::unique_ptr create(SmartctlParserType type, SmartctlOutputFormat format); + [[nodiscard]] static std::unique_ptr create(SmartctlParserType type, SmartctlOutputFormat format); /// Parse full "smartctl -x" output. /// Note: Once parsed, this function cannot be called again. - virtual hz::ExpectedVoid parse(std::string_view smartctl_output) = 0; + [[nodiscard]] virtual hz::ExpectedVoid parse(std::string_view smartctl_output) = 0; /// Detect smartctl output type (text, json). diff --git a/src/applib/smartctl_text_ata_parser.cpp b/src/applib/smartctl_text_ata_parser.cpp index 9bb5096..317e616 100644 --- a/src/applib/smartctl_text_ata_parser.cpp +++ b/src/applib/smartctl_text_ata_parser.cpp @@ -36,22 +36,22 @@ namespace { inline AtaStorageProperty app_get_checksum_error_property(const std::string& name) { AtaStorageProperty p; - p.section = AtaStorageProperty::Section::data; + p.section = AtaStorageProperty::Section::Data; if (name == "Attribute Data") { - p.subsection = AtaStorageProperty::SubSection::attributes; + p.subsection = AtaStorageProperty::SubSection::Attributes; p.set_name(name, "_text_only/attribute_data_checksum_error"); } else if (name == "Attribute Thresholds") { - p.subsection = AtaStorageProperty::SubSection::attributes; + p.subsection = AtaStorageProperty::SubSection::Attributes; p.set_name(name, "_text_only/attribute_thresholds_checksum_error"); } else if (name == "ATA Error Log") { - p.subsection = AtaStorageProperty::SubSection::error_log; + p.subsection = AtaStorageProperty::SubSection::ErrorLog; p.set_name(name, "_text_only/ata_error_log_checksum_error"); } else if (name == "Self-Test Log") { - p.subsection = AtaStorageProperty::SubSection::selftest_log; + p.subsection = AtaStorageProperty::SubSection::SelftestLog; p.set_name(name, "_text_only/selftest_log_checksum_error"); } @@ -211,7 +211,7 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse(std::string_v 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 + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } { @@ -219,7 +219,7 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse(std::string_v 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 + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } @@ -312,7 +312,7 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse_section_info( { this->set_data_section_info(body); - const AtaStorageProperty::Section section = AtaStorageProperty::Section::info; + const AtaStorageProperty::Section section = AtaStorageProperty::Section::Info; // split by lines. // e.g. Device Model: ST3500630AS @@ -423,7 +423,7 @@ http://knowledge.seagate.com/articles/en_US/FAQ/213891en hz::ExpectedVoid SmartctlTextAtaParser::parse_section_info_property(AtaStorageProperty& p) { // ---- Info - if (p.section != AtaStorageProperty::Section::info) { + if (p.section != AtaStorageProperty::Section::Info) { debug_out_error("app", DBG_FUNC_MSG << "Called with non-info section!\n"); return hz::Unexpected(SmartctlParserError::InternalError, "Internal parser error."); } @@ -776,8 +776,8 @@ Device is: In smartctl database [for details use: -P show] */ AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::health; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::Health; std::string name, value; if (app_pcre_match("/^([^:\\n]+):[ \\t]*(.*)$/mi", sub, &name, &value)) { @@ -842,8 +842,8 @@ SCT capabilities: (0x003d) SCT Status supported. */ AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::capabilities; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::Capabilities; std::string sub = sub_initial; @@ -1027,7 +1027,7 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_ const pcrecpp::RE re_selftest_long_time = app_pcre_re("/^(Extended self-test routine recommended polling time)/mi"); const pcrecpp::RE re_conv_selftest_time = app_pcre_re("/^(Conveyance self-test routine recommended polling time)/mi"); - if (cap_prop.section != AtaStorageProperty::Section::data || cap_prop.subsection != AtaStorageProperty::SubSection::capabilities) { + if (cap_prop.section != AtaStorageProperty::Section::Data || cap_prop.subsection != AtaStorageProperty::SubSection::Capabilities) { debug_out_error("app", DBG_FUNC_MSG << "Non-capability property passed.\n"); return hz::Unexpected(SmartctlParserError::DataError, "Non-capability property passed."); } @@ -1061,7 +1061,7 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_ // The last self-test status. break up into pieces. AtaStorageProperty p; - p.section = AtaStorageProperty::Section::internal; + p.section = AtaStorageProperty::Section::Internal; p.set_name("ata_smart_data/self_test/status/passed"); AtaStorageSelftestEntry sse; @@ -1079,52 +1079,52 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_ } else if (app_pcre_match("/^(The previous self-test routine completed without error or no .*)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::completed_no_error; + sse.status = AtaStorageSelftestEntry::Status::CompletedNoError; } else if (app_pcre_match("/^(The self-test routine was aborted by the host)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::aborted_by_host; + sse.status = AtaStorageSelftestEntry::Status::AbortedByHost; } else if (app_pcre_match("/^(The self-test routine was interrupted by the host with a hard.*)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::interrupted; + sse.status = AtaStorageSelftestEntry::Status::Interrupted; } else if (app_pcre_match("/^(A fatal error or unknown test error occurred while the device was executing its .*)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::fatal_or_unknown; + sse.status = AtaStorageSelftestEntry::Status::FatalOrUnknown; } else if (app_pcre_match("/^(The previous self-test completed having a test element that failed and the test element that failed is not known)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::compl_unknown_failure; + sse.status = AtaStorageSelftestEntry::Status::ComplUnknownFailure; } else if (app_pcre_match("/^(The previous self-test completed having the electrical element of the test failed)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::compl_electrical_failure; + sse.status = AtaStorageSelftestEntry::Status::ComplElectricalFailure; } else if (app_pcre_match("/^(The previous self-test completed having the servo .*)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::compl_servo_failure; + sse.status = AtaStorageSelftestEntry::Status::ComplServoFailure; } else if (app_pcre_match("/^(The previous self-test completed having the read element of the test failed)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::compl_read_failure; + sse.status = AtaStorageSelftestEntry::Status::ComplReadFailure; } else if (app_pcre_match("/^(The previous self-test completed having a test element that failed and the device is suspected of having handling damage)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::compl_handling_damage; + sse.status = AtaStorageSelftestEntry::Status::ComplHandlingDamage; // samsung bug (?), as per smartctl sources. } else if (app_pcre_match("/^(The previous self-test routine completed with unknown result or self-test .*)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::compl_unknown_failure; // we'll use this again (correct?) + sse.status = AtaStorageSelftestEntry::Status::ComplUnknownFailure; // we'll use this again (correct?) } else if (app_pcre_match("/^(Self-test routine in progress)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::in_progress; + sse.status = AtaStorageSelftestEntry::Status::InProgress; } else if (app_pcre_match("/^(Reserved)/mi", sv, &value)) { sse.status_str = value; - sse.status = AtaStorageSelftestEntry::Status::reserved; + sse.status = AtaStorageSelftestEntry::Status::Reserved; } } @@ -1167,7 +1167,7 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_ // debug_out_dump("app", "Looking for internal capability in: \"" << sv << "\"\n"); AtaStorageProperty p; - p.section = AtaStorageProperty::Section::internal; + p.section = AtaStorageProperty::Section::Internal; // Note: We don't set reported_value on internal properties. std::string name, value; @@ -1244,8 +1244,8 @@ hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_ hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_attributes(const std::string& sub) { AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::attributes; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::Attributes; // split to lines std::vector lines; @@ -1406,37 +1406,37 @@ ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE } if (attr_format_style == FormatStyleBrief) { - attr.attr_type = app_pcre_match("/P/", attr.flag) ? AtaStorageAttribute::AttributeType::prefail : AtaStorageAttribute::AttributeType::old_age; + attr.attr_type = app_pcre_match("/P/", attr.flag) ? AtaStorageAttribute::AttributeType::Prefail : AtaStorageAttribute::AttributeType::OldAge; } else { if (attr_type == "Pre-fail") { - attr.attr_type = AtaStorageAttribute::AttributeType::prefail; + attr.attr_type = AtaStorageAttribute::AttributeType::Prefail; } else if (attr_type == "Old_age") { - attr.attr_type = AtaStorageAttribute::AttributeType::old_age; + attr.attr_type = AtaStorageAttribute::AttributeType::OldAge; } else { - attr.attr_type = AtaStorageAttribute::AttributeType::unknown; + attr.attr_type = AtaStorageAttribute::AttributeType::Unknown; } } if (attr_format_style == FormatStyleBrief) { - attr.update_type = app_pcre_match("/O/", attr.flag) ? AtaStorageAttribute::UpdateType::always : AtaStorageAttribute::UpdateType::offline; + attr.update_type = app_pcre_match("/O/", attr.flag) ? AtaStorageAttribute::UpdateType::Always : AtaStorageAttribute::UpdateType::Offline; } else { if (update_type == "Always") { - attr.update_type = AtaStorageAttribute::UpdateType::always; + attr.update_type = AtaStorageAttribute::UpdateType::Always; } else if (update_type == "Offline") { - attr.update_type = AtaStorageAttribute::UpdateType::offline; + attr.update_type = AtaStorageAttribute::UpdateType::Offline; } else { - attr.update_type = AtaStorageAttribute::UpdateType::unknown; + attr.update_type = AtaStorageAttribute::UpdateType::Unknown; } } - attr.when_failed = AtaStorageAttribute::FailTime::unknown; + attr.when_failed = AtaStorageAttribute::FailTime::Unknown; hz::string_trim(when_failed); if (when_failed == "-") { - attr.when_failed = AtaStorageAttribute::FailTime::none; + attr.when_failed = AtaStorageAttribute::FailTime::None; } else if (when_failed == "In_the_past" || when_failed == "Past") { // the second one if from brief format - attr.when_failed = AtaStorageAttribute::FailTime::past; + attr.when_failed = AtaStorageAttribute::FailTime::Past; } else if (when_failed == "FAILING_NOW" || when_failed == "NOW") { // the second one if from brief format - attr.when_failed = AtaStorageAttribute::FailTime::now; + attr.when_failed = AtaStorageAttribute::FailTime::Now; } attr.raw_value = hz::string_trim_copy(raw_value); @@ -1465,8 +1465,8 @@ ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_directory_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::directory_log; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::DirectoryLog; // Directory log contains: /* @@ -1517,8 +1517,8 @@ Address Access R/W Size Description hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_error_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::error_log; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::ErrorLog; // Note: The format of this section was changed somewhere between 5.0-x and 5.30. // The old format is doesn't really give any useful info, and whatever's left is somewhat @@ -1714,8 +1714,8 @@ Error 1 [0] occurred at disk power-on lifetime: 1 hours (0 days + 1 hours) hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_selftest_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::selftest_log; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::SelftestLog; // Self-test log contains: // * structure revision number @@ -1823,31 +1823,31 @@ Num Test_Description Status Remaining LifeTime(hours) LBA sse.lba_of_first_error = "-"; hz::string_trim(status_str); - AtaStorageSelftestEntry::Status status = AtaStorageSelftestEntry::Status::unknown; + AtaStorageSelftestEntry::Status status = AtaStorageSelftestEntry::Status::Unknown; // don't match end - some of them are not complete here if (app_pcre_match("/^Completed without error/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::completed_no_error; + status = AtaStorageSelftestEntry::Status::CompletedNoError; } else if (app_pcre_match("/^Aborted by host/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::aborted_by_host; + status = AtaStorageSelftestEntry::Status::AbortedByHost; } else if (app_pcre_match("/^Interrupted \\(host reset\\)/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::interrupted; + status = AtaStorageSelftestEntry::Status::Interrupted; } else if (app_pcre_match("/^Fatal or unknown error/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::fatal_or_unknown; + status = AtaStorageSelftestEntry::Status::FatalOrUnknown; } else if (app_pcre_match("/^Completed: unknown failure/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::compl_unknown_failure; + status = AtaStorageSelftestEntry::Status::ComplUnknownFailure; } else if (app_pcre_match("/^Completed: electrical failure/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::compl_electrical_failure; + status = AtaStorageSelftestEntry::Status::ComplElectricalFailure; } else if (app_pcre_match("/^Completed: servo\\/seek failure/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::compl_servo_failure; + status = AtaStorageSelftestEntry::Status::ComplServoFailure; } else if (app_pcre_match("/^Completed: read failure/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::compl_read_failure; + status = AtaStorageSelftestEntry::Status::ComplReadFailure; } else if (app_pcre_match("/^Completed: handling damage/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::compl_handling_damage; + status = AtaStorageSelftestEntry::Status::ComplHandlingDamage; } else if (app_pcre_match("/^Self-test routine in progress/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::in_progress; + status = AtaStorageSelftestEntry::Status::InProgress; } else if (app_pcre_match("/^Unknown\\/reserved test status/mi", status_str)) { - status = AtaStorageSelftestEntry::Status::reserved; + status = AtaStorageSelftestEntry::Status::Reserved; } sse.status_str = status_str; @@ -1893,8 +1893,8 @@ Num Test_Description Status Remaining LifeTime(hours) LBA hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_selective_selftest_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::selective_selftest_log; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::SelectiveSelftestLog; // Selective self-test log contains: /* @@ -1950,8 +1950,8 @@ If Selective self-test is pending on power-up, resume after 0 minute delay. hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_scttemp_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::temperature_log; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::TemperatureLog; // scttemp log contains: /* @@ -2017,8 +2017,8 @@ Index Estimated Time Temperature Celsius std::string name, value; if (app_pcre_match("/^(Current Temperature):[ \\t]+(.*) Celsius$/mi", sub, &name, &value)) { AtaStorageProperty p; - p.section = AtaStorageProperty::Section::data; - p.subsection = AtaStorageProperty::SubSection::temperature_log; + p.section = AtaStorageProperty::Section::Data; + p.subsection = AtaStorageProperty::SubSection::TemperatureLog; p.set_name("Current Temperature", "ata_sct_status/temperature/current"); p.reported_value = value; p.value = hz::string_to_number_nolocale(value); // integer @@ -2040,8 +2040,8 @@ Index Estimated Time Temperature Celsius hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_scterc_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::erc_log; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::ErcLog; // scterc log contains: /* @@ -2089,8 +2089,8 @@ SCT Error Recovery Control: hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_devstat(const std::string& sub) { AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::devstat; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::Devstat; // devstat log contains: /* @@ -2260,8 +2260,8 @@ Page Offset Size Value Description hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_sataphy(const std::string& sub) { AtaStorageProperty pt; // template for easy copying - pt.section = AtaStorageProperty::Section::data; - pt.subsection = AtaStorageProperty::SubSection::phy_log; + pt.section = AtaStorageProperty::Section::Data; + pt.subsection = AtaStorageProperty::SubSection::PhyLog; // sataphy log contains: /* diff --git a/src/applib/smartctl_text_ata_parser.h b/src/applib/smartctl_text_ata_parser.h index a9c33ce..237620e 100644 --- a/src/applib/smartctl_text_ata_parser.h +++ b/src/applib/smartctl_text_ata_parser.h @@ -29,7 +29,7 @@ class SmartctlTextAtaParser : public SmartctlParser { SmartctlTextAtaParser() = default; // Overridden - hz::ExpectedVoid parse(std::string_view smartctl_output) override; + [[nodiscard]] hz::ExpectedVoid parse(std::string_view smartctl_output) override; protected: diff --git a/src/applib/smartctl_text_basic_parser.cpp b/src/applib/smartctl_text_basic_parser.cpp index ecf6183..20224af 100644 --- a/src/applib/smartctl_text_basic_parser.cpp +++ b/src/applib/smartctl_text_basic_parser.cpp @@ -50,7 +50,7 @@ hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string 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 + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } { @@ -58,7 +58,7 @@ hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string 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 + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } @@ -76,7 +76,7 @@ hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string 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 + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); // This was encountered on a csmi soft-raid under windows with pd0. @@ -87,7 +87,7 @@ hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string 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 + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); is_raid = true; @@ -130,14 +130,14 @@ hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string 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 + 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 + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } @@ -183,7 +183,7 @@ hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string p.set_name("Rotation Rate", "rotation_rate", "Rotation Rate"); p.reported_value = rpm_str; p.value = hz::string_to_number_nolocale(rpm_str, false); - p.section = AtaStorageProperty::Section::info; // add to info section + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } @@ -198,7 +198,7 @@ hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string p.reported_value = size; p.value = bytes; p.readable_value = readable_size; - p.section = AtaStorageProperty::Section::info; // add to info section + p.section = AtaStorageProperty::Section::Info; // add to info section add_property(p); } diff --git a/src/applib/smartctl_text_basic_parser.h b/src/applib/smartctl_text_basic_parser.h index 4a5b428..3be7e01 100644 --- a/src/applib/smartctl_text_basic_parser.h +++ b/src/applib/smartctl_text_basic_parser.h @@ -27,7 +27,7 @@ class SmartctlTextBasicParser : public SmartctlParser { SmartctlTextBasicParser() = default; // Overridden - hz::ExpectedVoid parse(std::string_view smartctl_output) override; + [[nodiscard]] hz::ExpectedVoid parse(std::string_view smartctl_output) override; }; diff --git a/src/applib/storage_detector.cpp b/src/applib/storage_detector.cpp index 083fe31..97bfdd9 100644 --- a/src/applib/storage_detector.cpp +++ b/src/applib/storage_detector.cpp @@ -28,28 +28,28 @@ Copyright: -std::string StorageDetector::detect(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +hz::ExpectedVoid StorageDetector::detect(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Starting drive detection.\n"); std::vector all_detected; - std::string error_message; + hz::ExpectedVoid detect_status; // Try each one and move to next if it fails. if constexpr(BuildEnv::is_kernel_linux()) { - error_message = detect_drives_linux(all_detected, ex_factory); // linux /proc/partitions as fallback. + detect_status = detect_drives_linux(all_detected, ex_factory); // linux /proc/partitions as fallback. } else if constexpr(BuildEnv::is_kernel_family_windows()) { - error_message = detect_drives_win32(all_detected, ex_factory); // win32 + detect_status = detect_drives_win32(all_detected, ex_factory); // win32 } else { // freebsd, etc... - error_message = detect_drives_other(all_detected, ex_factory); // bsd, etc... . scans /dev. + detect_status = detect_drives_other(all_detected, ex_factory); // bsd, etc... . scans /dev. } if (all_detected.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Cannot detect drives: None of the drive detection methods returned any drives.\n"); - return error_message; // last error message should be ok. + return detect_status; } for (auto& drive : all_detected) { @@ -86,12 +86,12 @@ std::string StorageDetector::detect(std::vector& drives, const std::sort(drives.begin(), drives.end()); debug_out_info("app", DBG_FUNC_MSG << "Drive detection finished.\n"); - return std::string(); + return {}; } -std::string StorageDetector::fetch_basic_data(std::vector& drives, +hz::ExpectedVoid StorageDetector::fetch_basic_data(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory, bool return_first_error) { fetch_data_errors_.clear(); @@ -107,21 +107,22 @@ std::string StorageDetector::fetch_basic_data(std::vector& dri // don't show any errors here - we don't want a screen flood. // no need for gui-based executors here, we already show the message in // iconview background (if called from main window) - std::string error_msg; - if (drive->get_info_output().empty()) { // if not fetched during detection - error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); + hz::ExpectedVoid fetch_status; + if (drive->get_basic_output().empty()) { // if not fetched during detection + fetch_status = drive->fetch_basic_data_and_parse(smartctl_ex); } // normally we skip drives with errors - possibly scsi, etc... - if (return_first_error && !error_msg.empty()) - return error_msg; + if (return_first_error && !fetch_status) { + return hz::Unexpected(StorageDetectorError::StorageDeviceError, fetch_status.error().message()); + } - if (!error_msg.empty()) { + if (!fetch_status) { // use original executor error if present (permits matches by our users). // if (!smartctl_ex->get_error_msg().empty()) // error_message = smartctl_ex->get_error_msg(); - fetch_data_errors_.push_back(error_msg); + fetch_data_errors_.push_back(fetch_status.error().message()); fetch_data_error_outputs_.push_back(smartctl_ex->get_stdout_str()); } @@ -134,20 +135,22 @@ std::string StorageDetector::fetch_basic_data(std::vector& dri } - return std::string(); + return {}; } -std::string StorageDetector::detect_and_fetch_basic_data(std::vector& put_drives_here, +hz::ExpectedVoid StorageDetector::detect_and_fetch_basic_data(std::vector& put_drives_here, const CommandExecutorFactoryPtr& ex_factory) { - std::string error_msg = detect(put_drives_here, ex_factory); + auto detect_status = detect(put_drives_here, ex_factory); - if (error_msg.empty()) - fetch_basic_data(put_drives_here, ex_factory, false); // ignore its errors, there may be plenty of them. + if (!detect_status) { + // ignore its errors, there may be plenty of them. + [[maybe_unused]] auto fetch_status = fetch_basic_data(put_drives_here, ex_factory, false); + } - return error_msg; + return detect_status; } diff --git a/src/applib/storage_detector.h b/src/applib/storage_detector.h index 4823993..84ac6bd 100644 --- a/src/applib/storage_detector.h +++ b/src/applib/storage_detector.h @@ -20,26 +20,41 @@ Copyright: #include "command_executor_factory.h" +enum class StorageDetectorError { + NoSmartctlBinary, + NoHelperBinary, + SmartctlExecutionError, + EmptyCommandOutput, + UnsupportedCommandVersion, + ParseError, + StorageDeviceError, + ProcReadError, + GeneralDetectionErrors, + ConfigError, + DevOpenError, +}; + + /// Storage detector - detects available drives in the system. class StorageDetector { public: /// Detects a list of drives. Returns detection error message if error occurs. - std::string detect(std::vector& drives, + [[nodiscard]] hz::ExpectedVoid detect(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory); /// For each drive, fetch basic data and parse it. /// If \c return_first_error is true, the function returns on the first error. /// \return An empty string. Or, if return_first_error is true, the first error that occurs. - std::string fetch_basic_data(std::vector& drives, + [[nodiscard]] hz::ExpectedVoid fetch_basic_data(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory, bool return_first_error = false); /// Run detect() and fetch_basic_data(). /// \return An error if such occurs. - std::string detect_and_fetch_basic_data(std::vector& put_drives_here, + [[nodiscard]] hz::ExpectedVoid detect_and_fetch_basic_data(std::vector& put_drives_here, const CommandExecutorFactoryPtr& ex_factory); diff --git a/src/applib/storage_detector_helpers.h b/src/applib/storage_detector_helpers.h index ac40d2d..25a1740 100644 --- a/src/applib/storage_detector_helpers.h +++ b/src/applib/storage_detector_helpers.h @@ -23,12 +23,14 @@ Copyright: #include "rconfig/rconfig.h" #include "app_pcrecpp.h" #include "hz/string_num.h" +#include "storage_detector.h" /// Find and execute tw_cli with specified options, return its output through \c output. /// \return error message -inline std::string execute_tw_cli(const CommandExecutorFactoryPtr& ex_factory, const std::string& command_options, std::string& output) +inline hz::ExpectedVoid execute_tw_cli(const CommandExecutorFactoryPtr& ex_factory, + const std::string& command_options, std::string& output) { std::shared_ptr executor = ex_factory->create_executor(CommandExecutorFactory::ExecutorType::TwCli); @@ -36,7 +38,8 @@ inline std::string execute_tw_cli(const CommandExecutorFactoryPtr& ex_factory, c if (binary.empty()) { debug_out_error("app", DBG_FUNC_MSG << "tw_cli binary is not set in config.\n"); - return Glib::ustring::compose(_("%1 binary is not specified in configuration."), "tw_cli"); + return hz::Unexpected(StorageDetectorError::NoHelperBinary, + _("tw_cli binary is not specified in configuration.")); } std::vector binaries; // binaries to try @@ -62,7 +65,8 @@ inline std::string execute_tw_cli(const CommandExecutorFactoryPtr& ex_factory, c output = hz::string_trim_copy(hz::string_any_to_unix_copy(executor->get_stdout_str())); if (output.empty()) { debug_out_error("app", DBG_FUNC_MSG << "tw_cli returned an empty output.\n"); - return _("tw_cli returned an empty output."); + return hz::Unexpected(StorageDetectorError::EmptyCommandOutput, + _("tw_cli returned an empty output.")); } return {}; @@ -72,15 +76,15 @@ inline std::string execute_tw_cli(const CommandExecutorFactoryPtr& ex_factory, c /// Get the drives on a 3ware controller using tw_cli. /// Note that the drives are inserted in the order they are detected. -inline std::string tw_cli_get_drives(const std::string& dev, int controller, +inline hz::ExpectedVoid tw_cli_get_drives(const std::string& dev, int controller, std::vector& drives, const CommandExecutorFactoryPtr& ex_factory, bool use_tw_cli_dev) { debug_out_info("app", "Getting available 3ware drives (ports) for controller " << controller << " through tw_cli...\n"); std::string output; - std::string error = execute_tw_cli(ex_factory, hz::string_sprintf("/c%d show all", controller), output); - if (!error.empty()) { - return error; + auto exec_status = execute_tw_cli(ex_factory, hz::string_sprintf("/c%d show all", controller), output); + if (!exec_status) { + return exec_status; } // split to lines @@ -114,14 +118,15 @@ inline std::string tw_cli_get_drives(const std::string& dev, int controller, /// Return 3ware SCSI host numbers (same as /c switch to tw_cli). /// \return error string on error -inline std::string tw_cli_get_controllers(const CommandExecutorFactoryPtr& ex_factory, std::vector& controllers) +inline hz::ExpectedVoid tw_cli_get_controllers( + const CommandExecutorFactoryPtr& ex_factory, std::vector& controllers) { debug_out_info("app", "Getting available 3ware controllers through tw_cli...\n"); std::string output; - std::string error = execute_tw_cli(ex_factory, "show", output); - if (!error.empty()) { - return error; + auto exec_status = execute_tw_cli(ex_factory, "show", output); + if (!exec_status) { + return exec_status; } // split to lines @@ -152,7 +157,7 @@ inline std::string tw_cli_get_controllers(const CommandExecutorFactoryPtr& ex_fa /// Get number of ports by sequentially running smartctl on each port, until /// one of the gives an error. \c type contains a printf-formatted string with %d. /// \return an error message on error. -inline std::string smartctl_scan_drives_sequentially(const std::string& dev, const std::string& type, +inline hz::ExpectedVoid smartctl_scan_drives_sequentially(const std::string& dev, const std::string& type, int from, int to, std::vector& drives, const CommandExecutorFactoryPtr& ex_factory, std::string& last_output) { std::shared_ptr smartctl_ex = ex_factory->create_executor(CommandExecutorFactory::ExecutorType::Smartctl); @@ -167,8 +172,8 @@ inline std::string smartctl_scan_drives_sequentially(const std::string& dev, con // "Read Device Identity failed: Input/output error" // or // "Read Device Identity failed: empty IDENTIFY data" - std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); - last_output = drive->get_info_output(); + auto fetch_status = drive->fetch_basic_data_and_parse(smartctl_ex); + last_output = drive->get_basic_output(); // If we've reached smartctl port limit (older versions may have smaller limits), abort. if (app_pcre_match("/VALID ARGUMENTS ARE/mi", last_output)) { @@ -182,8 +187,8 @@ inline std::string smartctl_scan_drives_sequentially(const std::string& dev, con break; } - if (!error_msg.empty()) { - debug_out_info("app", "Smartctl returned with an error: " << error_msg << "\n"); + if (!fetch_status) { + debug_out_info("app", "Smartctl returned with an error: " << fetch_status.error().message() << "\n"); debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << " due to smartctl error.\n"); } else { drives.push_back(drive); diff --git a/src/applib/storage_detector_linux.cpp b/src/applib/storage_detector_linux.cpp index 83f1f0a..6f81cf9 100644 --- a/src/applib/storage_detector_linux.cpp +++ b/src/applib/storage_detector_linux.cpp @@ -365,14 +365,15 @@ major minor #blocks name 254 8 1966080 mmcblk1 254 9 2007032 mmcblk1p1 */ -inline std::string detect_drives_linux_proc_partitions(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +inline hz::ExpectedVoid detect_drives_linux_proc_partitions( + std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives through partitions file (/proc/partitions by default; set \"system/linux_proc_partitions_path\" config key to override).\n"); std::vector lines; std::string error_msg = read_proc_partitions_file(lines); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } static const std::vector blacklist = { @@ -409,7 +410,7 @@ inline std::string detect_drives_linux_proc_partitions(std::vector(device); - drive->fetch_basic_data_and_parse(smartctl_ex); + auto fetch_status = drive->fetch_basic_data_and_parse(smartctl_ex); + if (!fetch_status) { + continue; + } // 3ware controllers also export themselves as sd*. Smartctl detects that, // so we can avoid adding them. Older smartctl (5.38) prints "AMCC", newer one // prints "AMCC/3ware controller". It's better to search it this way. - if (app_pcre_match("/try adding '-d 3ware,N'/im", drive->get_info_output())) { + if (app_pcre_match("/try adding '-d 3ware,N'/im", drive->get_basic_output())) { debug_out_dump("app", "Drive " << drive->get_device_with_type() << " seems to be a 3ware controller, ignoring.\n"); } else { drives.push_back(drive); @@ -464,14 +468,15 @@ Implementation notes: it seems that twe uses "3ware" and twa uses "AMCC" We can't handle a situation with mixed twa/twe/twl systems, since we don't know how they will be ordered for tw_cli. */ -inline std::string detect_drives_linux_3ware(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +inline hz::ExpectedVoid detect_drives_linux_3ware( + std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind 3ware controller(s)...\n"); std::vector lines; std::string error_msg = read_proc_devices_file(lines); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } bool twa_found = false; @@ -506,7 +511,7 @@ inline std::string detect_drives_linux_3ware(std::vector& driv std::vector< std::pair > vendors_models; error_msg = read_proc_scsi_scsi_file(vendors_models); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } @@ -556,19 +561,19 @@ inline std::string detect_drives_linux_3ware(std::vector& driv std::string dev = std::string("/dev/") + dev_base + hz::number_to_string_nolocale(device_numbers[dev_base]); ++device_numbers[dev_base]; - error_msg = tw_cli_get_drives(dev, vendor_model.first, drives, ex_factory, false); - if (!error_msg.empty()) { // no tw_cli + auto exec_status = tw_cli_get_drives(dev, vendor_model.first, drives, ex_factory, false); + if (!exec_status) { // no tw_cli int max_ports = rconfig::get_data("system/linux_3ware_max_scan_port"); max_ports = std::max(0, std::min(max_ports, 127)); // Sanity check debug_out_dump("app", "Starting brute-force port scan on 0-" << max_ports << " ports, device \"" << dev << "\". Change the maximum by setting \"system/linux_3ware_max_scan_port\" config key.\n"); std::string last_output; - error_msg = smartctl_scan_drives_sequentially(dev, "3ware,%d", 0, max_ports, drives, ex_factory, last_output); + exec_status = smartctl_scan_drives_sequentially(dev, "3ware,%d", 0, max_ports, drives, ex_factory, last_output); debug_out_dump("app", "Brute-force port scan finished.\n"); } - if (!error_msg.empty()) { - debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get the drives on ports of LSI/AMCC/3ware controller: " << error_msg << "\n"); + if (!exec_status) { + debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get the drives on ports of LSI/AMCC/3ware controller: " << exec_status.error().message() << "\n"); } } @@ -576,7 +581,7 @@ inline std::string detect_drives_linux_3ware(std::vector& driv debug_out_warn("app", DBG_FUNC_MSG << "AMCC/LSI/3ware entry found in devices file, but SCSI file contains no known entries.\n"); } - return error_msg; + return {}; } @@ -598,14 +603,15 @@ using "id > 0" requirement (the third column of /proc/scsi/sg/devices. Try "-d sat" by default. If it fails ("Device Read Identity Failed:" ? not sure how to detect the failure), fall back to "-d scsi". */ -inline std::string detect_drives_linux_adaptec(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +inline hz::ExpectedVoid detect_drives_linux_adaptec( + std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind Adaptec controller(s)...\n"); std::vector lines; std::string error_msg = read_proc_devices_file(lines); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } bool aac_found = false; @@ -628,13 +634,13 @@ inline std::string detect_drives_linux_adaptec(std::vector& dr std::vector< std::pair > vendors_models; error_msg = read_proc_scsi_scsi_file(vendors_models); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } std::vector< std::vector > sg_entries; error_msg = read_proc_scsi_sg_devices_file(sg_entries); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } std::shared_ptr smartctl_ex = ex_factory->create_executor(CommandExecutorFactory::ExecutorType::Smartctl); @@ -670,19 +676,19 @@ inline std::string detect_drives_linux_adaptec(std::vector& dr std::string dev = std::string("/dev/sg") + hz::number_to_string_nolocale(sg_num); auto drive = std::make_shared(dev, std::string("sat")); - std::string local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); - std::string output = drive->get_info_output(); + auto fetch_status = drive->fetch_basic_data_and_parse(smartctl_ex); + std::string output = drive->get_basic_output(); // Note: Not sure about this, have to check with real SAS drives if (app_pcre_match("/Device Read Identity Failed/mi", output)) { // "-d sat" didn't work, default back to smartctl's "-d scsi" drive->clear_fetched(); drive->set_type_argument(""); - local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); + fetch_status = drive->fetch_basic_data_and_parse(smartctl_ex); } - if (!local_error_msg.empty()) { - debug_out_info("app", "Smartctl returned with an error: " << local_error_msg << "\n"); + if (!fetch_status) { + debug_out_info("app", "Smartctl returned with an error: " << fetch_status.error().message() << "\n"); debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << ".\n"); } else { drives.push_back(drive); @@ -695,7 +701,7 @@ inline std::string detect_drives_linux_adaptec(std::vector& dr debug_out_warn("app", DBG_FUNC_MSG << "Adaptec entry found in devices file, but SCSI file contains no known entries.\n"); } - return error_msg; + return {}; } @@ -728,14 +734,15 @@ Notification: If /sys/bus/scsi/devices/hostN/scsi_host/hostN/host_fw_version is older than "V1.46 2009-01-06", (1.51 for enclosure-having cards) notify the user (maybe its better to grep the smartctl output for that on port 0?). NOT IMPLEMENTED YET. */ -inline std::string detect_drives_linux_areca(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +inline hz::ExpectedVoid detect_drives_linux_areca( + std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind Areca controller(s)...\n"); std::vector< std::pair > vendors_models; std::string error_msg = read_proc_scsi_scsi_file(vendors_models); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } std::map controller_hosts; // controller # -> has_enclosure @@ -765,13 +772,13 @@ inline std::string detect_drives_linux_areca(std::vector& driv if (controller_hosts.empty()) { debug_out_info("app", DBG_FUNC_MSG << "No Areca-specific entries found in SCSI file.\n"); - return error_msg; + return {}; } std::vector< std::vector > sg_entries; error_msg = read_proc_scsi_sg_devices_file(sg_entries); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } std::shared_ptr smartctl_ex = ex_factory->create_executor(CommandExecutorFactory::ExecutorType::Smartctl); @@ -791,6 +798,8 @@ inline std::string detect_drives_linux_areca(std::vector& driv continue; } + hz::ExpectedVoid exec_status; + if (has_enclosure) { // TODO We have no information on what "/sys/bus/scsi/devices/host%d/scsi_host/host%d/host_fw_hd_channels" // contains in case of enclosure-having cards. @@ -807,7 +816,7 @@ inline std::string detect_drives_linux_areca(std::vector& driv << "\". Change the maximums by setting \"system/linux_areca_enc_max_scan_port\" and \"system/linux_areca_enc_max_enclosure\" config keys.\n"); std::string last_output; for (int enclosure_no = 1; enclosure_no < max_enclosures; ++enclosure_no) { - error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d/" + hz::number_to_string_nolocale(enclosure_no), 1, max_ports, drives, ex_factory, last_output); + exec_status = smartctl_scan_drives_sequentially(dev, "areca,%d/" + hz::number_to_string_nolocale(enclosure_no), 1, max_ports, drives, ex_factory, last_output); } debug_out_dump("app", "Brute-force port/enclosure scan finished.\n"); @@ -834,17 +843,17 @@ inline std::string detect_drives_linux_areca(std::vector& driv debug_out_dump("app", "Starting brute-force port scan on 1-" << max_ports << " ports, device \"" << dev << "\". Change the maximum by setting \"system/linux_areca_noenc_max_scan_port\" config key.\n"); std::string last_output; - error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d", 1, max_ports, drives, ex_factory, last_output); + exec_status = smartctl_scan_drives_sequentially(dev, "areca,%d", 1, max_ports, drives, ex_factory, last_output); debug_out_dump("app", "Brute-force port scan finished.\n"); } - if (!error_msg.empty()) { - debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get the drives on ports of Areca controller: " << error_msg << "\n"); + if (!exec_status) { + debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get the drives on ports of Areca controller: " << exec_status.error().message() << "\n"); } } } - return error_msg; + return {}; } @@ -879,14 +888,15 @@ Detection: Note: We're not sure how to differentiate the outputs of free / non-existent ports, so scan them until 15, just in case. */ -inline std::string detect_drives_linux_cciss(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +inline hz::ExpectedVoid detect_drives_linux_cciss( + std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind HP RAID (CCISS) controller(s)...\n"); std::vector lines; std::string error_msg = read_proc_devices_file(lines); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } std::vector controllers; @@ -915,11 +925,11 @@ inline std::string detect_drives_linux_cciss(std::vector& driv for (int port = 0; port <= max_port; ++port) { auto drive = std::make_shared(dev, std::string("cciss,") + hz::number_to_string_nolocale(port)); - std::string local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); - std::string output = drive->get_info_output(); + auto fetch_status = drive->fetch_basic_data_and_parse(smartctl_ex); + std::string output = drive->get_basic_output(); - if (!local_error_msg.empty()) { - debug_out_info("app", "Smartctl returned with an error: " << local_error_msg << "\n"); + if (!fetch_status) { + debug_out_info("app", "Smartctl returned with an error: " << fetch_status.error().message() << "\n"); } if (app_pcre_match("/VALID ARGUMENTS ARE/mi", output)) { @@ -933,7 +943,7 @@ inline std::string detect_drives_linux_cciss(std::vector& driv break; } - if (local_error_msg.empty()) { + if (fetch_status) { drives.push_back(drive); debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n"); } else { @@ -944,7 +954,7 @@ inline std::string detect_drives_linux_cciss(std::vector& driv debug_out_dump("app", "Brute-force port scan finished.\n"); } - return error_msg; + return {}; } @@ -967,20 +977,21 @@ Detection: Run smartctl -i -d cciss,[0-127] /dev/cciss/cNd0 until "No such device or address" or "VALID ARGUMENTS ARE" is encountered in output. */ -inline std::string detect_drives_linux_hpsa(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +inline hz::ExpectedVoid detect_drives_linux_hpsa( + std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind HP RAID (hpsa/hpahcisr) controller(s)...\n"); std::vector< std::pair > vendors_models; std::string error_msg = read_proc_scsi_scsi_file(vendors_models); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } std::vector< std::vector > sg_entries; error_msg = read_proc_scsi_sg_devices_file(sg_entries); if (!error_msg.empty()) { - return error_msg; + return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } std::shared_ptr smartctl_ex = ex_factory->create_executor(CommandExecutorFactory::ExecutorType::Smartctl); @@ -1021,16 +1032,16 @@ inline std::string detect_drives_linux_hpsa(std::vector& drive for (int port = 0; port <= max_port; ++port) { auto drive = std::make_shared(dev, std::string("cciss,") + hz::number_to_string_nolocale(port)); - std::string local_error_msg = drive->fetch_basic_data_and_parse(smartctl_ex); - std::string output = drive->get_info_output(); + auto fetch_status = drive->fetch_basic_data_and_parse(smartctl_ex); + std::string output = drive->get_basic_output(); if (app_pcre_match("/No such device or address/mi", output) || app_pcre_match("/VALID ARGUMENTS ARE/mi", output)) { // We reached the controller port limit, or smartctl-supported port limit. debug_out_dump("app", "Reached controller or smartctl port limit with port " << port << ", stopping port scan.\n"); break; } - if (!local_error_msg.empty()) { - debug_out_info("app", "Smartctl returned with an error: " << local_error_msg << "\n"); + if (!fetch_status) { + debug_out_info("app", "Smartctl returned with an error: " << fetch_status.error().message() << "\n"); debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << " due to smartctl error.\n"); } else { drives.push_back(drive); @@ -1046,7 +1057,7 @@ inline std::string detect_drives_linux_hpsa(std::vector& drive debug_out_info("app", DBG_FUNC_MSG << "No hpsa/hpahcisr-specific entries found in Sg devices file.\n"); } - return error_msg; + return {}; } @@ -1056,12 +1067,13 @@ inline std::string detect_drives_linux_hpsa(std::vector& drive -std::string detect_drives_linux(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +hz::ExpectedVoid detect_drives_linux( + std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { clear_read_file_cache(); std::vector error_msgs; - std::string error_msg; + hz::ExpectedVoid status; // Disable by-id detection - it's unreliable on broken systems. // For example, on Ubuntu 8.04, /dev/disk/by-id contains two device @@ -1069,37 +1081,41 @@ std::string detect_drives_linux(std::vector& drives, const Com // sda and sdb). Plus, there are no "*-partN" files (not that we need them). // error_message = detect_drives_linux_udev_byid(devices); // linux udev - error_msg = detect_drives_linux_proc_partitions(drives, ex_factory); - if (!error_msg.empty()) { - error_msgs.push_back(error_msg); + status = detect_drives_linux_proc_partitions(drives, ex_factory); + if (!status) { + error_msgs.push_back(status.error().message()); } - error_msg = detect_drives_linux_3ware(drives, ex_factory); - if (!error_msg.empty()) { - error_msgs.push_back(error_msg); + status = detect_drives_linux_3ware(drives, ex_factory); + if (!status) { + error_msgs.push_back(status.error().message()); } - error_msg = detect_drives_linux_areca(drives, ex_factory); - if (!error_msg.empty()) { - error_msgs.push_back(error_msg); + status = detect_drives_linux_areca(drives, ex_factory); + if (!status) { + error_msgs.push_back(status.error().message()); } - error_msg = detect_drives_linux_adaptec(drives, ex_factory); - if (!error_msg.empty()) { - error_msgs.push_back(error_msg); + status = detect_drives_linux_adaptec(drives, ex_factory); + if (!status) { + error_msgs.push_back(status.error().message()); } - error_msg = detect_drives_linux_cciss(drives, ex_factory); - if (!error_msg.empty()) { - error_msgs.push_back(error_msg); + status = detect_drives_linux_cciss(drives, ex_factory); + if (!status) { + error_msgs.push_back(status.error().message()); } - error_msg = detect_drives_linux_hpsa(drives, ex_factory); - if (!error_msg.empty()) { - error_msgs.push_back(error_msg); + status = detect_drives_linux_hpsa(drives, ex_factory); + if (!status) { + error_msgs.push_back(status.error().message()); } - return hz::string_join(error_msgs, "\n"); + if (!error_msgs.empty()) { + return hz::Unexpected(StorageDetectorError::GeneralDetectionErrors, hz::string_join(error_msgs, "\n")); + } + + return {}; } diff --git a/src/applib/storage_detector_linux.h b/src/applib/storage_detector_linux.h index 0f148a9..82ddf57 100644 --- a/src/applib/storage_detector_linux.h +++ b/src/applib/storage_detector_linux.h @@ -20,11 +20,13 @@ Copyright: #include "command_executor_factory.h" #include "storage_device.h" +#include "storage_detector.h" /// Detect drives in Linux -std::string detect_drives_linux(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory); +[[nodiscard]] hz::ExpectedVoid detect_drives_linux(std::vector& drives, + const CommandExecutorFactoryPtr& ex_factory); diff --git a/src/applib/storage_detector_other.cpp b/src/applib/storage_detector_other.cpp index 397a911..22e4461 100644 --- a/src/applib/storage_detector_other.cpp +++ b/src/applib/storage_detector_other.cpp @@ -28,7 +28,7 @@ Copyright: -std::string detect_drives_other(std::vector& drives, +hz::ExpectedVoid detect_drives_other(std::vector& drives, [[maybe_unused]] const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives through /dev...\n"); @@ -46,14 +46,14 @@ std::string detect_drives_other(std::vector& drives, auto dev_dir = rconfig::get_data(sdev_config_path); if (dev_dir.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Device directory path is not set.\n"); - return _("Device directory path is not set."); + return hz::Unexpected(StorageDetectorError::ConfigError, _("Device directory path is not set.")); } auto dir = hz::fs_path_from_string(dev_dir); std::error_code dummy_ec; if (!hz::fs::exists(dir, dummy_ec)) { debug_out_warn("app", DBG_FUNC_MSG << "Device directory doesn't exist.\n"); - return _("Device directory does not exist."); + return hz::Unexpected(StorageDetectorError::ConfigError, _("Device directory does not exist.")); } std::vector whitelist; @@ -201,7 +201,8 @@ std::string detect_drives_other(std::vector& drives, } if (ec) { debug_out_error("app", DBG_FUNC_MSG << "Cannot list device directory entries.\n"); - return Glib::ustring::compose(_("Cannot list device directory entries: %1"), ec.message()); + return hz::Unexpected(StorageDetectorError::DevOpenError, + std::vformat(_("Cannot list device directory entries: {}"), std::make_format_args(ec.message()))); } @@ -235,7 +236,7 @@ std::string detect_drives_other(std::vector& drives, continue; } if (fp) { - std::fclose(fp); + [[maybe_unused]] auto close_status = std::fclose(fp); } debug_out_info("app", DBG_FUNC_MSG << "Device \"" << dev.string() << "\" opened successfully, adding to device list.\n"); } diff --git a/src/applib/storage_detector_other.h b/src/applib/storage_detector_other.h index fc41ff2..1d07d9a 100644 --- a/src/applib/storage_detector_other.h +++ b/src/applib/storage_detector_other.h @@ -19,11 +19,12 @@ Copyright: #include "command_executor_factory.h" #include "storage_device.h" - +#include "storage_detector.h" /// Detect drives in FreeBSD, Solaris, etc... (all except Linux and Windows). -std::string detect_drives_other(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory); +[[nodiscard]] hz::ExpectedVoid detect_drives_other(std::vector& drives, + const CommandExecutorFactoryPtr& ex_factory); diff --git a/src/applib/storage_detector_win32.cpp b/src/applib/storage_detector_win32.cpp index 85b66a1..284e71f 100644 --- a/src/applib/storage_detector_win32.cpp +++ b/src/applib/storage_detector_win32.cpp @@ -185,7 +185,7 @@ std::map win32_get_drive_letter_map() /// Run "smartctl --scan-open" and pick the devices which have /// a port parameter. We don't pick the others because the may /// conflict with pd* devices, and we like pd* better than sd*. -std::string get_scan_open_multiport_devices(std::vector& drives, +hz::ExpectedVoid get_scan_open_multiport_devices(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory, const std::map& drive_letter_map, std::set& equivalent_pds) @@ -198,7 +198,7 @@ std::string get_scan_open_multiport_devices(std::vector& drive if (smartctl_binary.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Smartctl binary is not set in config.\n"); - return _("Smartctl binary is not specified in configuration."); + return hz::Unexpected(StorageDetectorError::NoSmartctlBinary, _("Smartctl binary is not specified in configuration.")); } auto smartctl_def_options = rconfig::get_data("system/smartctl_options"); @@ -209,22 +209,23 @@ std::string get_scan_open_multiport_devices(std::vector& drive smartctl_ex->set_command(Glib::shell_quote(hz::fs_path_to_string(smartctl_binary)), smartctl_def_options + "--scan-open"); - if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) { + if (bool execute_status = smartctl_ex->execute(); !execute_status) { debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n"); - return smartctl_ex->get_error_msg(); + return hz::Unexpected(StorageDetectorError::SmartctlExecutionError, smartctl_ex->get_error_msg()); } // any_to_unix is needed for windows const std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str())); if (output.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n"); - return _("Smartctl returned an empty output."); + return hz::Unexpected(StorageDetectorError::EmptyCommandOutput, _("Smartctl returned an empty output.")); } if (app_pcre_match("/UNRECOGNIZED OPTION/mi", output)) { // Our requirements list smartctl with --scan-open support, so this should never happen. // Therefore, we don't translate it. - return "Unsupported smartctl version: Smartctl doesn't support --scan-open switch."; + return hz::Unexpected(StorageDetectorError::UnsupportedCommandVersion, + "Unsupported smartctl version: Smartctl doesn't support --scan-open switch."); } @@ -256,7 +257,7 @@ std::string get_scan_open_multiport_devices(std::vector& drive std::map letters_volnames; for (const auto& iter : drive_letter_map) { - if (iter.second.physical_drives.count(drive_num) > 0) { + if (iter.second.physical_drives.contains(drive_num)) { letters_volnames[iter.first] = iter.second.volume_name; } } @@ -274,7 +275,7 @@ std::string get_scan_open_multiport_devices(std::vector& drive /// Find and execute areca cli with specified options, return its output through \c output. /// \return error message -inline std::string execute_areca_cli(const CommandExecutorFactoryPtr& ex_factory, const std::string& cli_binary, +inline hz::ExpectedVoid execute_areca_cli(const CommandExecutorFactoryPtr& ex_factory, const std::string& cli_binary, const std::string& command_options, std::string& output) { std::shared_ptr executor = ex_factory->create_executor(CommandExecutorFactory::ExecutorType::ArecaCli); @@ -289,7 +290,8 @@ inline std::string execute_areca_cli(const CommandExecutorFactoryPtr& ex_factory output = hz::string_trim_copy(hz::string_any_to_unix_copy(executor->get_stdout_str())); if (output.empty()) { debug_out_error("app", DBG_FUNC_MSG << "Areca cli returned an empty output.\n"); - return _("Areca CLI returned an empty output."); + return hz::Unexpected(StorageDetectorError::EmptyCommandOutput, + "Areca CLI returned an empty output."); } return {}; @@ -379,7 +381,8 @@ GuiErrMsg<0x00>: Success. GuiErrMsg<0x00>: Success. ------------------------------------------------------------ */ -inline std::string areca_cli_get_drives(const std::string& cli_binary, const std::string& dev, int controller, +[[nodiscard]] inline hz::ExpectedVoid areca_cli_get_drives( + const std::string& cli_binary, const std::string& dev, int controller, std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", "Getting available Areca drives (ports) for controller " << controller << " through Areca CLI...\n"); @@ -389,9 +392,9 @@ inline std::string areca_cli_get_drives(const std::string& cli_binary, const std // the interactive mode. std::string output; - std::string error = execute_areca_cli(ex_factory, cli_binary, "disk info", output); - if (!error.empty()) { - return error; + auto execute_status = execute_areca_cli(ex_factory, cli_binary, "disk info", output); + if (!execute_status) { + return execute_status; } // split to lines @@ -426,7 +429,8 @@ inline std::string areca_cli_get_drives(const std::string& cli_binary, const std } if (format_type == FormatType::Unknown) { debug_out_warn("app", "Could not read Areca CLI output: No valid header found.\n"); - return _("Could not read Areca CLI output: No valid header found."); + return hz::Unexpected(StorageDetectorError::ParseError, + _("Could not read Areca CLI output: No valid header found.")); } // Note: These may not match the full model, but just the first part is sufficient for comparison with "N.A.". @@ -504,7 +508,7 @@ If CLI is not installed, do the brute-force way: -d areca,[1-128]/[1-8] /dev/arcmsrN It's 2-3 drives a second on an empty port, so some limits are set in config. */ -inline std::string detect_drives_win32_areca(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +inline hz::ExpectedVoid detect_drives_win32_areca(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind Areca controller(s)...\n"); @@ -582,8 +586,8 @@ inline std::string detect_drives_win32_areca(std::vector& driv debug_out_dump("app", "Testing Areca controller presence using smartctl...\n"); auto drive = std::make_shared("/dev/arcmsr0", "areca,1"); - const std::string error_message = drive->fetch_basic_data_and_parse(smartctl_ex); - const std::string output = drive->get_info_output(); + [[maybe_unused]] auto drive_status = drive->fetch_basic_data_and_parse(smartctl_ex); + const std::string output = drive->get_basic_output(); if (app_pcre_match("/No Areca controller found/mi", output) || app_pcre_match("/Smartctl open device: .* failed: No such device/mi", output) ) { use_cli = 0; @@ -596,10 +600,10 @@ inline std::string detect_drives_win32_areca(std::vector& driv debug_out_info("app", "Scanning Areca drives using CLI...\n"); const int cli_max_controllers = 1; // TODO controller # with CLI. for (int controller_no = 0; controller_no < cli_max_controllers; ++controller_no) { - const std::string error_message = areca_cli_get_drives(cli_binary.string(), + auto execute_status = areca_cli_get_drives(cli_binary.string(), "/dev/arcmsr" + hz::number_to_string_nolocale(controller_no), controller_no, drives, ex_factory); // If we get an error on controller 0, fall back to no-cli detection. - if (!error_message.empty() && controller_no == 0) { + if (!execute_status && controller_no == 0) { use_cli = 0; debug_out_warn("app", "Areca scan using CLI failed.\n"); if (scan_detect) { @@ -630,9 +634,9 @@ inline std::string detect_drives_win32_areca(std::vector& driv const std::size_t old_drive_count = drives.size(); std::string last_output; - std::string error_message = smartctl_scan_drives_sequentially(dev, "areca,%d", 1, max_noenc_ports, drives, ex_factory, last_output); + auto scan_status = smartctl_scan_drives_sequentially(dev, "areca,%d", 1, max_noenc_ports, drives, ex_factory, last_output); // If the scan stopped because of no controller, stop it all. - if (!error_message.empty() && (app_pcre_match("/No Areca controller found/mi", last_output) + if (!scan_status && (app_pcre_match("/No Areca controller found/mi", last_output) || app_pcre_match("/Smartctl open device: .* failed: No such device/mi", last_output)) ) { debug_out_dump("app", "Areca controller " << controller_no << " not present, stopping sequential scan.\n"); break; @@ -645,7 +649,7 @@ inline std::string detect_drives_win32_areca(std::vector& driv debug_out_dump("app", "Starting brute-force port scan (enclosure #" << enclosure_no << ") on 1-" << max_enc_ports << " ports, device \"" << dev << "\". Change the maximums by setting \"system/win32_areca_onc_max_scan_port\" and \"system/win32_areca_enc_max_enclosure\" config keys.\n"); // FIXME Not sure whether we should ignore this error message - error_message = smartctl_scan_drives_sequentially(dev, "areca,%d/" + hz::number_to_string_nolocale(enclosure_no), 1, max_enc_ports, drives, ex_factory, last_output); + [[maybe_unused]] auto encl_status = smartctl_scan_drives_sequentially(dev, "areca,%d/" + hz::number_to_string_nolocale(enclosure_no), 1, max_enc_ports, drives, ex_factory, last_output); } } @@ -669,10 +673,10 @@ inline std::string detect_drives_win32_areca(std::vector& driv // (or /dev/pdN, /dev/ being optional) where N comes from // "\\.\PhysicalDriveN" (winnt only). // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx -std::string detect_drives_win32(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory) +hz::ExpectedVoid detect_drives_win32(std::vector& drives, + const CommandExecutorFactoryPtr& ex_factory) { std::vector error_msgs; - std::string error_message; // Construct drive letter map debug_out_info("app", "Checking which drive corresponds to which \\\\.\\PhysicalDriveN device...\n"); @@ -685,9 +689,9 @@ std::string detect_drives_win32(std::vector& drives, const Com // Note that this may return duplicates (e.g. /dev/sda and /dev/csmi0,0) std::set used_pds; - error_message = get_scan_open_multiport_devices(drives, ex_factory, drive_letter_map, used_pds); - if (!error_message.empty()) { - error_msgs.push_back(error_message); + auto multiport_status = get_scan_open_multiport_devices(drives, ex_factory, drive_letter_map, used_pds); + if (!multiport_status) { + error_msgs.push_back(multiport_status.error().message()); } const bool multiport_found = !drives.empty(); @@ -696,9 +700,9 @@ std::string detect_drives_win32(std::vector& drives, const Com // Find out their serial numbers and whether there are Arecas there. std::map serials; for (auto& drive : drives) { - const std::string local_error = drive->fetch_basic_data_and_parse(smartctl_ex); - if (!local_error.empty()) { - debug_out_info("app", "Smartctl returned with an error: " << local_error << "\n"); + const auto local_status = drive->fetch_basic_data_and_parse(smartctl_ex); + if (!local_status) { + debug_out_info("app", "Smartctl returned with an error: " << local_status.error().message() << "\n"); // Don't exit, just report it. } if (!drive->get_serial_number().empty()) { @@ -725,7 +729,7 @@ std::string detect_drives_win32(std::vector& drives, const Com for (int drive_num = 0; drive_num < max_drives; ++drive_num) { // If the drive was already encountered in --scan-open (with a port number), skip it. - if (used_pds.count(drive_num) > 0) { + if (used_pds.contains(drive_num)) { debug_out_dump("app", "pd" << drive_num << " already encountered in --scan-open output (as sd*), skipping.\n"); continue; } @@ -759,7 +763,7 @@ std::string detect_drives_win32(std::vector& drives, const Com std::map letters_volnames; for (const auto& iter : drive_letter_map) { - if (iter.second.physical_drives.count(drive_num) > 0) { + if (iter.second.physical_drives.contains(drive_num)) { letters_volnames[iter.first] = iter.second.volume_name; } } @@ -771,15 +775,15 @@ std::string detect_drives_win32(std::vector& drives, const Com // they contain more information. // The only way to detect these duplicates is to compare them using serial numbers. if (!serials.empty()) { - const std::string local_error = drive->fetch_basic_data_and_parse(smartctl_ex); - if (!local_error.empty()) { - debug_out_info("app", "Smartctl returned with an error: " << local_error << "\n"); + auto local_status = drive->fetch_basic_data_and_parse(smartctl_ex); + if (!local_status) { + debug_out_info("app", "Smartctl returned with an error: " << local_status.error().message() << "\n"); // Don't exit, just report it. } const std::string drive_serial_id = drive->get_model_name() + "_" + drive->get_serial_number(); // A serial may be empty if "-q noserial" was given to smartctl. - if (!drive->get_serial_number().empty() && serials.count(drive_serial_id) > 0) { + if (!drive->get_serial_number().empty() && serials.contains(drive_serial_id)) { debug_out_info("app", "Skipping drive due to duplicate S/N: model: \"" << drive->get_model_name() << "\", S/N: \"" << drive->get_serial_number() << "\".\n"); // Copy the drive letters over to previously detected one (since we can't detect drive letters there). @@ -807,11 +811,11 @@ std::string detect_drives_win32(std::vector& drives, const Com if (!inst_path.empty()) { debug_out_dump("app", "3ware 3DM2 found at\"" << inst_path << "\".\n"); std::vector controllers; - error_message = tw_cli_get_controllers(ex_factory, controllers); + auto tw_status = tw_cli_get_controllers(ex_factory, controllers); // ignore the error message above, it's of no use. for (const int controller : controllers) { // don't specify device, it's ignored in tw_cli mode - tw_cli_get_drives("", controller, drives, ex_factory, true); + [[maybe_unused]] auto tw_drive_status = tw_cli_get_drives("", controller, drives, ex_factory, true); } } else { debug_out_info("app", "3ware 3DM2 not installed.\n"); @@ -820,13 +824,17 @@ std::string detect_drives_win32(std::vector& drives, const Com if (!areca_open_found) { - detect_drives_win32_areca(drives, ex_factory); - if (!error_message.empty()) { - error_msgs.push_back(error_message); + auto areca_status = detect_drives_win32_areca(drives, ex_factory); + if (!areca_status) { + error_msgs.push_back(areca_status.error().message()); } } - return hz::string_join(error_msgs, "\n"); + if (!error_msgs.empty()) { + return hz::Unexpected(StorageDetectorError::GeneralDetectionErrors, hz::string_join(error_msgs, "\n")); + } + + return {}; } diff --git a/src/applib/storage_detector_win32.h b/src/applib/storage_detector_win32.h index f5ded86..9eb312a 100644 --- a/src/applib/storage_detector_win32.h +++ b/src/applib/storage_detector_win32.h @@ -19,11 +19,12 @@ Copyright: #include "command_executor_factory.h" #include "storage_device.h" - +#include "storage_detector.h" /// Detect drives in Windows -std::string detect_drives_win32(std::vector& drives, const CommandExecutorFactoryPtr& ex_factory); +[[nodiscard]] hz::ExpectedVoid detect_drives_win32(std::vector& drives, + const CommandExecutorFactoryPtr& ex_factory); diff --git a/src/applib/storage_device.cpp b/src/applib/storage_device.cpp index b7e45a9..d2b8738 100644 --- a/src/applib/storage_device.cpp +++ b/src/applib/storage_device.cpp @@ -31,10 +31,10 @@ Copyright: std::string StorageDevice::get_type_storable_name(DetectedType type) { static const std::unordered_map m { - {DetectedType::unknown, "unknown"}, - {DetectedType::invalid, "invalid"}, - {DetectedType::cddvd, "cd/dvd"}, - {DetectedType::raid, "raid"}, + {DetectedType::Unknown, "unknown"}, + {DetectedType::Invalid, "invalid"}, + {DetectedType::CdDvd, "cd/dvd"}, + {DetectedType::Raid, "raid"}, }; if (auto iter = m.find(type); iter != m.end()) { return iter->second; @@ -47,10 +47,10 @@ std::string StorageDevice::get_type_storable_name(DetectedType type) std::string StorageDevice::get_status_displayable_name(Status status) { static const std::unordered_map m { - {Status::enabled, C_("status", "Enabled")}, - {Status::disabled, C_("status", "Disabled")}, - {Status::unsupported, C_("status", "Unsupported")}, - {Status::unknown, C_("status", "Unknown")}, + {Status::Enabled, C_("status", "Enabled")}, + {Status::Disabled, C_("status", "Disabled")}, + {Status::Unsupported, C_("status", "Unsupported")}, + {Status::Unknown, C_("status", "Unknown")}, }; if (auto iter = m.find(status); iter != m.end()) { return iter->second; @@ -81,11 +81,11 @@ StorageDevice::StorageDevice(std::string dev, std::string type_arg) void StorageDevice::clear_fetched(bool including_outputs) { if (including_outputs) { - info_output_.clear(); + basic_output_.clear(); full_output_.clear(); } - parse_status_ = ParseStatus::none; + parse_status_ = ParseStatus::None; test_is_active_ = false; // not sure smart_supported_.reset(); @@ -101,10 +101,12 @@ void StorageDevice::clear_fetched(bool including_outputs) -std::string StorageDevice::fetch_basic_data_and_parse(const std::shared_ptr& smartctl_ex) +hz::ExpectedVoid StorageDevice::fetch_basic_data_and_parse( + const std::shared_ptr& smartctl_ex) { - if (this->test_is_active_) - return _("A test is currently being performed on this drive."); + if (this->test_is_active_) { + return hz::Unexpected(StorageDeviceError::TestRunning, _("A test is currently being performed on this drive.")); + } this->clear_fetched(); // clear everything fetched before, including outputs @@ -117,13 +119,14 @@ std::string StorageDevice::fetch_basic_data_and_parse(const std::shared_ptrinfo_output_, true); // set type to invalid if needed + auto execute_status = execute_device_smartctl(command_options, smartctl_ex, this->basic_output_, true); // set type to invalid if needed // Smartctl 5.39 cvs/svn version defaults to usb type on at least linux and windows. // This means that the old SCSI identify command isn't executed by default, // and there is no information about the device manufacturer/etc... in the output. // We detect this and set the device type to scsi to at least have _some_ info. - if (get_detected_type() == DetectedType::invalid && get_type_argument().empty()) { + if ((execute_status || execute_status.error().data() == StorageDeviceError::ExecutionError) + && get_detected_type() == DetectedType::Invalid && get_type_argument().empty()) { debug_out_info("app", "The device seems to be of different type than auto-detected, trying again with scsi.\n"); this->set_type_argument("scsi"); return this->fetch_basic_data_and_parse(smartctl_ex); // try again with scsi @@ -131,12 +134,12 @@ std::string StorageDevice::fetch_basic_data_and_parse(const std::shared_ptrparse_basic_data(false, true); - return error_msg; - } +// this->parse_basic_data(false, true); +// return execute_status; +// } // Set some properties too - they are needed for e.g. AODC status, etc... return this->parse_basic_data(true); @@ -144,7 +147,7 @@ std::string StorageDevice::fetch_basic_data_and_parse(const std::shared_ptr StorageDevice::parse_basic_data(bool do_set_properties, bool emit_signal) { this->clear_fetched(false); // clear everything fetched before, except outputs @@ -152,11 +155,12 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si // Try the basic parser first. If it succeeds, use the specialized parser. auto basic_parser = SmartctlParser::create(SmartctlParserType::Basic, SmartctlOutputFormat::Json); - DBG_ASSERT_RETURN(basic_parser, "Cannot create parser"); + DBG_ASSERT_RETURN(basic_parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser"))); - auto parse_status = basic_parser->parse(this->get_info_output()); + auto parse_status = basic_parser->parse(this->get_basic_output()); if (!parse_status) { - return Glib::ustring::compose(_("Cannot parse smartctl output: %1"), parse_status.error().message()); + return hz::Unexpected(StorageDeviceError::ParseError, + std::vformat(_("Cannot parse smartctl output: {}"), std::make_format_args(parse_status.error().message()))); } auto basic_property_repo = basic_parser->get_property_repository(); @@ -166,10 +170,10 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si const auto& drive_type = drive_type_prop.get_value(); 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); + 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); + this->set_detected_type(DetectedType::Raid); } } @@ -207,18 +211,21 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si // Try to parse the properties. ignore its errors - we already got what we came for. // 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); +// DBG_ASSERT_RETURN(parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser"))); +// +// if (parser->parse(this->basic_output_)) { // try to parse it +// this->set_property_repository( +// StoragePropertyProcessor::process_properties(parser->get_property_repository(), disk_type)); // copy to our drive, overwriting old data +// } +// } if (do_set_properties) { - auto parser = SmartctlParser::create(SmartctlParserType::Ata, SmartctlOutputFormat::Json); - DBG_ASSERT_RETURN(parser, "Cannot create parser"); - - if (parser->parse(this->info_output_)) { // try to parse it - this->set_property_repository( - StoragePropertyProcessor::process_properties(parser->get_property_repository(), disk_type)); // copy to our drive, overwriting old data - } + this->set_property_repository(StoragePropertyProcessor::process_properties(basic_property_repo, disk_type)); } // A model field (and its aliases) is a good indication whether there was any data or not - set_parse_status(model_name_.has_value() ? ParseStatus::info : ParseStatus::none); + set_parse_status(model_name_.has_value() ? ParseStatus::Basic : ParseStatus::None); if (emit_signal) signal_changed().emit(this); // notify listeners @@ -228,15 +235,17 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si -std::string StorageDevice::fetch_data_and_parse(const std::shared_ptr& smartctl_ex) +hz::ExpectedVoid StorageDevice::fetch_full_data_and_parse( + const std::shared_ptr& smartctl_ex) { - if (this->test_is_active_) - return _("A test is currently being performed on this drive."); + if (this->test_is_active_) { + return hz::Unexpected(StorageDeviceError::TestRunning, _("A test is currently being performed on this drive.")); + } this->clear_fetched(); // clear everything fetched before, including outputs std::string output; - std::string error_msg; + hz::ExpectedVoid execute_status; // instead of -x, we use all the individual options -x encompasses, so that // an addition to default -x output won't affect us. @@ -250,7 +259,7 @@ std::string StorageDevice::fetch_data_and_parse(const std::shared_ptrset_type_argument("scsi"); - return this->fetch_data_and_parse(smartctl_ex); // try again with scsi + return this->fetch_full_data_and_parse(smartctl_ex); // try again with scsi } // Since the type error leads to "command line didn't parse" error here, // we do this after the scsi stuff. - if (!error_msg.empty()) - return error_msg; + if (!execute_status) + return execute_status; this->full_output_ = output; - return this->parse_data(); + return this->try_parse_data(); } -std::string StorageDevice::parse_data() +hz::ExpectedVoid StorageDevice::try_parse_data() { this->clear_fetched(false); // clear everything fetched before, except outputs @@ -294,29 +304,29 @@ std::string StorageDevice::parse_data() auto parser_format = SmartctlParser::detect_output_format(this->full_output_); if (!parser_format.has_value()) { - return parser_format.error().message(); + return hz::Unexpected(StorageDeviceError::ParseError, parser_format.error().message()); } // TODO Choose format according to device type SmartctlParserType parser_type = SmartctlParserType::Ata; auto parser = SmartctlParser::create(parser_type, parser_format.value()); - DBG_ASSERT_RETURN(parser, "Cannot create parser"); + DBG_ASSERT_RETURN(parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser"))); // Try to parse it (parse only, set the properties after basic parsing). const auto parse_status = parser->parse(this->full_output_); if (parse_status.has_value()) { // refresh basic info too - this->info_output_ = this->full_output_; // put data including version information + this->basic_output_ = this->full_output_; // put data including version information // note: this will clear the non-basic properties! // this will parse some info that is already parsed by SmartctlAtaTextParser::parse(), // but this one sets the StorageDevice class members, not properties. - this->parse_basic_data(false, false); // don't emit signal, we're not complete yet. + static_cast(this->parse_basic_data(false, false)); // don't emit signal, we're not complete yet. // Call this after parse_basic_data(), since it sets parse status to "info". - this->set_parse_status(StorageDevice::ParseStatus::full); + this->set_parse_status(StorageDevice::ParseStatus::Full); // set the full properties. // copy to our drive, overwriting old data. @@ -333,10 +343,12 @@ std::string StorageDevice::parse_data() debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse smartctl output.\n"); // proper parsing failed. try to at least extract info section - this->info_output_ = this->full_output_; // complete output here. sometimes it's only the info section - if (!this->parse_basic_data(true).empty()) { // will add some properties too. this will emit signal_changed(). + this->basic_output_ = this->full_output_; // complete output here. sometimes it's only the info section + auto basic_parse_status = this->parse_basic_data(true); // will add some properties too. this will emit signal_changed(). + if (!basic_parse_status) { // return full parser's error messages - they are more detailed. - return Glib::ustring::compose(_("Cannot parse smartctl output: %1"), parse_status.error().message()); + return hz::Unexpected(StorageDeviceError::ParseError, + std::vformat(_("Cannot parse smartctl output: {}"), std::make_format_args(parse_status.error().message()))); } return {}; // return ok if at least the info was ok. @@ -351,10 +363,12 @@ StorageDevice::ParseStatus StorageDevice::get_parse_status() const -std::string StorageDevice::set_smart_enabled(bool b, const std::shared_ptr& smartctl_ex) +hz::ExpectedVoid StorageDevice::set_smart_enabled(bool b, + const std::shared_ptr& smartctl_ex) { - if (this->test_is_active_) - return _("A test is currently being performed on this drive."); + if (this->test_is_active_) { + return hz::Unexpected(StorageDeviceError::TestRunning, _("A test is currently being performed on this drive.")); + } // execute smartctl --smart=on|off /dev/... // --saveauto=on is also executed when enabling smart. @@ -372,9 +386,9 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm */ std::string output; - std::string error_msg = execute_device_smartctl((b ? "--smart=on --saveauto=on" : "--smart=off"), smartctl_ex, output); - if (!error_msg.empty()) { - return error_msg; + auto status = execute_device_smartctl((b ? "--smart=on --saveauto=on" : "--smart=off"), smartctl_ex, output); + if (!status) { + return status; } // search at line start, because they are sometimes present in other sentences too. @@ -383,18 +397,18 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm } if (app_pcre_match("/^A mandatory SMART command failed/mi", output)) { - return _("Mandatory SMART command failed."); + return hz::Unexpected(StorageDeviceError::CommandFailed, _("Mandatory SMART command failed.")); } - return _("Unknown error occurred."); + return hz::Unexpected(StorageDeviceError::CommandUnknownError, _("Unknown error occurred.")); } -std::string StorageDevice::set_aodc_enabled(bool b, const std::shared_ptr& smartctl_ex) +hz::ExpectedVoid StorageDevice::set_aodc_enabled(bool b, const std::shared_ptr& smartctl_ex) { if (this->test_is_active_) { - return _("A test is currently being performed on this drive."); + return hz::Unexpected(StorageDeviceError::TestRunning, _("A test is currently being performed on this drive.")); } // execute smartctl --offlineauto=on|off /dev/... @@ -409,49 +423,50 @@ SMART Automatic Offline Testing Disabled. A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options. */ std::string output; - std::string error_msg = execute_device_smartctl((b ? "--offlineauto=on" : "--offlineauto=off"), smartctl_ex, output); - if (!error_msg.empty()) - return error_msg; + auto status = execute_device_smartctl((b ? "--offlineauto=on" : "--offlineauto=off"), smartctl_ex, output); + if (!status) { + return status; + } if (app_pcre_match("/Testing Enabled/mi", output) || app_pcre_match("/Testing Disabled/mi", output)) { return {}; // success } if (app_pcre_match("/^A mandatory SMART command failed/mi", output)) { - return _("Mandatory SMART command failed."); + return hz::Unexpected(StorageDeviceError::CommandFailed, _("Mandatory SMART command failed.")); } - return _("Unknown error occurred."); + return hz::Unexpected(StorageDeviceError::CommandUnknownError, _("Unknown error occurred.")); } StorageDevice::Status StorageDevice::get_smart_status() const { - Status status = Status::unsupported; + Status status = Status::Unsupported; if (smart_enabled_.has_value()) { if (smart_enabled_.value()) { // enabled, supported - status = Status::enabled; + status = Status::Enabled; } else { // if it's disabled, maybe it's unsupported, check that: if (smart_supported_.has_value()) { if (smart_supported_.value()) { // disabled, supported - status = Status::disabled; + status = Status::Disabled; } else { // disabled, unsupported - status = Status::unsupported; + status = Status::Unsupported; } } else { // disabled, support unknown - status = Status::disabled; + status = Status::Disabled; } } } else { // status unknown if (smart_supported_.has_value()) { if (smart_supported_.value()) { // status unknown, supported - status = Status::disabled; // at least give the user a chance to try enabling it + status = Status::Disabled; // at least give the user a chance to try enabling it } else { // status unknown, unsupported - status = Status::unsupported; // most likely + status = Status::Unsupported; // most likely } } else { // status unknown, support unknown - status = Status::unsupported; + status = Status::Unsupported; } } return status; @@ -463,21 +478,21 @@ StorageDevice::Status StorageDevice::get_aodc_status() const { // smart-disabled drives are known to print some garbage, so // let's protect us from it. - if (get_smart_status() != Status::enabled) - return Status::unsupported; + if (get_smart_status() != Status::Enabled) + return Status::Unsupported; if (aodc_status_.has_value()) // cached return value return aodc_status_.value(); - Status status = Status::unknown; // for now + Status status = Status::Unknown; // for now bool aodc_supported = false; int found = 0; for (const auto& p : property_repository_.get_properties()) { - if (p.section == AtaStorageProperty::Section::internal) { + 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() ? Status::enabled : Status::disabled); + status = (p.get_value() ? Status::Enabled : Status::Disabled); //++found; continue; } @@ -492,7 +507,7 @@ StorageDevice::Status StorageDevice::get_aodc_status() const } if (!aodc_supported) - status = Status::unsupported; + status = Status::Unsupported; // if it's supported, then status may be enabled, disabled or unknown. aodc_status_ = status; // store to cache @@ -517,7 +532,7 @@ AtaStorageProperty StorageDevice::get_health_property() const return health_property_.value(); AtaStorageProperty p = property_repository_.lookup_property("smart_status/passed", - AtaStorageProperty::Section::data, AtaStorageProperty::SubSection::health); + AtaStorageProperty::Section::Data, AtaStorageProperty::SubSection::Health); if (!p.empty()) health_property_ = p; // store to cache @@ -692,14 +707,14 @@ bool StorageDevice::get_is_hdd() const void StorageDevice::set_info_output(std::string s) { - info_output_ = std::move(s); + basic_output_ = std::move(s); } -std::string StorageDevice::get_info_output() const +std::string StorageDevice::get_basic_output() const { - return info_output_; + return basic_output_; } @@ -797,34 +812,34 @@ std::string StorageDevice::get_device_options() const -std::string StorageDevice::execute_device_smartctl(const std::string& command_options, +hz::ExpectedVoid StorageDevice::execute_device_smartctl(const std::string& command_options, const std::shared_ptr& smartctl_ex, std::string& smartctl_output, bool check_type) { // don't forbid running on currently tested drive - we need to call this from the test code. if (is_virtual_) { debug_out_warn("app", DBG_FUNC_MSG << "Cannot execute smartctl on a virtual device.\n"); - return _("Cannot execute smartctl on a virtual device."); + return hz::Unexpected(StorageDeviceError::CannotExecuteOnVirtual, _("Cannot execute smartctl on a virtual device.")); } std::string device = get_device(); - std::string error_msg = execute_smartctl(device, this->get_device_options(), + auto smartctl_status = execute_smartctl(device, this->get_device_options(), command_options, smartctl_ex, smartctl_output); - if (!error_msg.empty()) { + if (!smartctl_status) { debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n"); // Smartctl 5.39 cvs/svn version defaults to usb type on at least linux and windows. // This means that the old SCSI identify command isn't executed by default, // and there is no information about the device manufacturer/etc... in the output. // We detect this and set the device type to scsi to at least have _some_ info. - if (check_type && this->get_detected_type() == DetectedType::unknown + if (check_type && this->get_detected_type() == DetectedType::Unknown && app_pcre_match("/specify device type with the -d option/mi", smartctl_output)) { - this->set_detected_type(DetectedType::invalid); + this->set_detected_type(DetectedType::Invalid); } - return error_msg; + return hz::Unexpected(StorageDeviceError::ExecutionError, smartctl_status.error().message()); } return {}; diff --git a/src/applib/storage_device.h b/src/applib/storage_device.h index 910482e..9a5b467 100644 --- a/src/applib/storage_device.h +++ b/src/applib/storage_device.h @@ -33,6 +33,16 @@ using StorageDevicePtr = std::shared_ptr; +enum class StorageDeviceError { + TestRunning, ///< A test is running, so the device cannot perform this operation. + CannotExecuteOnVirtual, ///< Cannot execute this operation on a virtual device. + ExecutionError, ///< Error executing the command. + CommandFailed, ///< SMART command (e.g. enable/disable SMART) failed. + CommandUnknownError, ///< Unknown error from the command. + ParseError, ///< Error parsing the output. +}; + + /// This class represents a single drive class StorageDevice { public: @@ -40,34 +50,34 @@ class StorageDevice { /// These may be used to force smartctl to a special type, as well as /// to display the correct icon enum class DetectedType { - unknown, // Unknown. Will be autodetected by smartctl. - invalid, // This is set by smartctl executor if it detects invalid type (but not if it's scsi). - cddvd, // CD/DVD/Blu-Ray. Unsupported by smartctl, only basic info is given. - raid, // RAID controller or volume. Unsupported by smartctl, only basic info is given. + Unknown, // Unknown. Will be autodetected by smartctl. + Invalid, // This is set by smartctl executor if it detects invalid type (but not if it's scsi). + CdDvd, // CD/DVD/Blu-Ray. Unsupported by smartctl, only basic info is given. + Raid, // RAID controller or volume. Unsupported by smartctl, only basic info is given. }; /// This gives a string which can be displayed in outputs - static std::string get_type_storable_name(DetectedType type); + [[nodiscard]] static std::string get_type_storable_name(DetectedType type); /// Statuses of various states enum class Status { - enabled, ///< SMART, AODC - disabled, ///< SMART, AODC - unsupported, ///< SMART, AODC - unknown ///< AODC - supported but unknown if it's enabled or not. + Enabled, ///< SMART, AODC + Disabled, ///< SMART, AODC + Unsupported, ///< SMART, AODC + Unknown ///< AODC - supported but unknown if it's enabled or not. }; /// Get displayable name for Status. - static std::string get_status_displayable_name(Status status); + [[nodiscard]] static std::string get_status_displayable_name(Status status); /// Statuses of various parse states enum class ParseStatus { - full, ///< Fully parsed - info, ///< Only info section available - none, ///< No data + Full, ///< Fully parsed + Basic, ///< Only info section available + None, ///< No data }; @@ -83,31 +93,30 @@ class StorageDevice { /// Calls "smartctl -i -H -c" (info section, health, capabilities), then parse_basic_data(). /// Called during drive detection. - /// Note: this will clear the non-basic properties! - std::string fetch_basic_data_and_parse(const std::shared_ptr& smartctl_ex = nullptr); + /// Note: this will clear all previous properties! + [[nodiscard]] hz::ExpectedVoid fetch_basic_data_and_parse( + const std::shared_ptr& smartctl_ex = nullptr); /// Detects type, smart support, smart status (on / off). - /// Note: this will clear the non-basic properties! - std::string parse_basic_data(bool do_set_properties = true, bool emit_signal = true); + /// Note: this will clear all previous properties! + [[nodiscard]] hz::ExpectedVoid parse_basic_data(bool do_set_properties = true, bool emit_signal = true); + /// 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& smartctl_ex); // returns error message on error. + [[nodiscard]] hz::ExpectedVoid fetch_full_data_and_parse(const std::shared_ptr& smartctl_ex); - // Parses full info. If failed, try to parse it as basic info. - /// \return error message on error. - std::string parse_data(); + /// Parse full info. If failed, try to parse it as basic info. + [[nodiscard]] hz::ExpectedVoid try_parse_data(); /// Get the "fully parsed" flag - ParseStatus get_parse_status() const; + [[nodiscard]] ParseStatus get_parse_status() const; /// Try to enable SMART. - /// \return error message on error, empty string on success - std::string set_smart_enabled(bool b, const std::shared_ptr&); + [[nodiscard]] hz::ExpectedVoid set_smart_enabled(bool b, const std::shared_ptr& smartctl_ex); /// Try to enable Automatic Offline Data Collection. - /// \return error message on error, empty string on success - std::string set_aodc_enabled(bool b, const std::shared_ptr&); + [[nodiscard]] hz::ExpectedVoid set_aodc_enabled(bool b, const std::shared_ptr& smartctl_ex); /// Get SMART status @@ -199,7 +208,7 @@ class StorageDevice { void set_info_output(std::string s); /// Get "info" output to parse - [[nodiscard]] std::string get_info_output() const; + [[nodiscard]] std::string get_basic_output() const; /// Set "full" output to parse @@ -224,7 +233,7 @@ class StorageDevice { /// Get the recommended filename to save output to. Includes model and date. - std::string get_save_filename() const; + [[nodiscard]] std::string get_save_filename() const; /// Get final smartctl options for this device from config and type info. @@ -232,8 +241,7 @@ class StorageDevice { /// Execute smartctl on this device. Nothing is modified in this class. - /// \return error message on error, empty string on success - std::string execute_device_smartctl(const std::string& command_options, + [[nodiscard]] hz::ExpectedVoid execute_device_smartctl(const std::string& command_options, const std::shared_ptr& smartctl_ex, std::string& output, bool check_type = false); @@ -252,8 +260,8 @@ class StorageDevice { private: - std::string info_output_; ///< "smartctl --info" output - std::string full_output_; ///< "smartctl --all" output + std::string basic_output_; ///< "smartctl --info" output + std::string full_output_; ///< "smartctl --all" or "-x" output std::string device_; ///< e.g. /dev/sda or pd0. empty if virtual. std::string type_arg_; ///< Device type (for -d smartctl parameter), as specified when adding the device. @@ -265,14 +273,14 @@ class StorageDevice { hz::fs::path virtual_file_; ///< A file (smartctl data) the virtual device was loaded from bool is_manually_added_ = false; ///< StorageDevice doesn't use it, but it's useful for its users. - ParseStatus parse_status_ = ParseStatus::none; ///< "Fully parsed" flag + ParseStatus parse_status_ = ParseStatus::None; ///< "Fully parsed" flag /// Sort of a "lock". If true, the device is not allowed to perform any commands /// except "-l selftest" and maybe "--capabilities" and "--info" (not sure). bool test_is_active_ = false; // Note: These are detected through info output - DetectedType detected_type_ = DetectedType::unknown; ///< e.g. type_unknown + DetectedType detected_type_ = DetectedType::Unknown; ///< e.g. type_unknown std::optional smart_supported_; ///< SMART support status std::optional smart_enabled_; ///< SMART enabled status mutable std::optional aodc_status_; ///< Cached aodc status. diff --git a/src/applib/storage_property_repository.cpp b/src/applib/storage_property_repository.cpp index b344a9e..cea0fa5 100644 --- a/src/applib/storage_property_repository.cpp +++ b/src/applib/storage_property_repository.cpp @@ -27,9 +27,9 @@ 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) + if (section != AtaStorageProperty::Section::Unknown && p.section != section) continue; - if (subsection != AtaStorageProperty::SubSection::unknown && p.subsection != subsection) + if (subsection != AtaStorageProperty::SubSection::Unknown && p.subsection != subsection) continue; if (p.generic_name == generic_name) diff --git a/src/applib/storage_property_repository.h b/src/applib/storage_property_repository.h index f68024e..f9a51c7 100644 --- a/src/applib/storage_property_repository.h +++ b/src/applib/storage_property_repository.h @@ -23,8 +23,8 @@ class StoragePropertyRepository { /// 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; + AtaStorageProperty::Section section = AtaStorageProperty::Section::Unknown, // if unknown, search in all. + AtaStorageProperty::SubSection subsection = AtaStorageProperty::SubSection::Unknown) const; /// Set properties diff --git a/src/applib/warning_colors.h b/src/applib/warning_colors.h index cfaac91..3b9254c 100644 --- a/src/applib/warning_colors.h +++ b/src/applib/warning_colors.h @@ -23,15 +23,15 @@ Copyright: inline bool app_property_get_row_highlight_colors(WarningLevel warning, std::string& fg, std::string& bg) { // Note: we're setting both fg and bg, to avoid theme conflicts. - if (warning == WarningLevel::notice) { + if (warning == WarningLevel::Notice) { fg = "#000000"; // black bg = "#FFD5EE"; // pinkish - } else if (warning == WarningLevel::warning) { + } else if (warning == WarningLevel::Warning) { fg = "#000000"; // black bg = "#FFA0A0"; // even more pinkish - } else if (warning == WarningLevel::alert) { + } else if (warning == WarningLevel::Alert) { fg = "#000000"; // black bg = "#FF0000"; // red } @@ -45,13 +45,13 @@ inline bool app_property_get_row_highlight_colors(WarningLevel warning, std::str /// \return true if the color was changed. inline bool app_property_get_label_highlight_color(WarningLevel warning, std::string& fg) { - if (warning == WarningLevel::notice) { + if (warning == WarningLevel::Notice) { fg = "#770000"; // very dark red - } else if (warning == WarningLevel::warning) { + } else if (warning == WarningLevel::Warning) { fg = "#C00000"; // dark red - } else if (warning == WarningLevel::alert) { + } else if (warning == WarningLevel::Alert) { fg = "#FF0000"; // red } @@ -71,16 +71,16 @@ inline std::string storage_property_get_warning_reason(const AtaStorageProperty& } switch (p.warning_level) { - case WarningLevel::none: + case WarningLevel::None: // nothing break; - case WarningLevel::notice: + case WarningLevel::Notice: /// Translators: %1 and %2 are HTML tags, %3 is a message. return Glib::ustring::compose(_("%1Notice:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason)); - case WarningLevel::warning: + case WarningLevel::Warning: /// Translators: %1 and %2 are HTML tags, %3 is a message. return Glib::ustring::compose(_("%1Warning:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason)); - case WarningLevel::alert: + case WarningLevel::Alert: /// Translators: %1 and %2 are HTML tags, %3 is a message. return Glib::ustring::compose(_("%1ALERT:%2 %3"), start, stop, Glib::Markup::escape_text(p.warning_reason)); } diff --git a/src/applib/warning_level.h b/src/applib/warning_level.h index b002858..51450ff 100644 --- a/src/applib/warning_level.h +++ b/src/applib/warning_level.h @@ -16,10 +16,10 @@ Copyright: /// Warning type enum class WarningLevel { - none, ///< No warning - notice, ///< A known attribute is somewhat disturbing, but no smart error - warning, ///< SMART warning is raised by old-age attribute - alert ///< SMART warning is raised by pre-fail attribute, and similar errors + None, ///< No warning + Notice, ///< A known attribute is somewhat disturbing, but no smart error + Warning, ///< SMART warning is raised by old-age attribute + Alert ///< SMART warning is raised by pre-fail attribute, and similar errors }; diff --git a/src/gsc_info_window.cpp b/src/gsc_info_window.cpp index 535cee6..c56e407 100644 --- a/src/gsc_info_window.cpp +++ b/src/gsc_info_window.cpp @@ -120,7 +120,7 @@ namespace { if (!label) return; - if (warning == WarningLevel::none) { + if (warning == WarningLevel::None) { label->set_markup_with_mnemonic(original_label); return; } @@ -348,10 +348,10 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests if (scan) { std::shared_ptr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this, Glib::ustring::compose(_("Running {command} on %1..."), drive->get_device_with_type())); - const std::string error_msg = drive->fetch_data_and_parse(ex); // run it with GUI support + auto fetch_status = drive->fetch_full_data_and_parse(ex); // run it with GUI support - if (!error_msg.empty()) { - gsc_executor_error_dialog_show(_("Cannot retrieve SMART data"), error_msg, this); + if (!fetch_status) { + gsc_executor_error_dialog_show(_("Cannot retrieve SMART data"), fetch_status.error().message(), this); return; } } @@ -370,7 +370,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests // hide all tabs except the first if smart is disabled, because they may contain // completely random data (smartctl does that sometimes). if (get_startup_settings().hide_tabs_on_smart_disabled) { - const bool smart_enabled = (drive->get_smart_status() == StorageDevice::Status::enabled); + const bool smart_enabled = (drive->get_smart_status() == StorageDevice::Status::Enabled); Gtk::Widget* note_page_box = nullptr; if (note_page_box = lookup_widget("attributes_tab_vbox"); note_page_box != nullptr) { @@ -474,7 +474,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("general_tab_label"), WarningLevel::none, tab_identity_name); + app_highlight_tab_label(lookup_widget("general_tab_label"), WarningLevel::None, tab_identity_name); } { @@ -490,7 +490,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("attributes_tab_label"), WarningLevel::none, tab_attributes_name); + app_highlight_tab_label(lookup_widget("attributes_tab_label"), WarningLevel::None, tab_attributes_name); } { @@ -503,7 +503,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("statistics_tab_label"), WarningLevel::none, tab_statistics_name); + app_highlight_tab_label(lookup_widget("statistics_tab_label"), WarningLevel::None, tab_statistics_name); } { @@ -554,7 +554,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("test_tab_label"), WarningLevel::none, tab_test_name); + app_highlight_tab_label(lookup_widget("test_tab_label"), WarningLevel::None, tab_test_name); } { @@ -578,7 +578,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("error_log_tab_label"), WarningLevel::none, tab_error_log_name); + app_highlight_tab_label(lookup_widget("error_log_tab_label"), WarningLevel::None, tab_error_log_name); } { @@ -589,11 +589,11 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("temperature_log_tab_label"), WarningLevel::none, tab_temperature_name); + app_highlight_tab_label(lookup_widget("temperature_log_tab_label"), WarningLevel::None, tab_temperature_name); } // tab label - app_highlight_tab_label(lookup_widget("advanced_tab_label"), WarningLevel::none, tab_advanced_name); + app_highlight_tab_label(lookup_widget("advanced_tab_label"), WarningLevel::None, tab_advanced_name); { if (auto* treeview = lookup_widget("capabilities_treeview")) { @@ -607,7 +607,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("capabilities_tab_label"), WarningLevel::none, tab_capabilities_name); + app_highlight_tab_label(lookup_widget("capabilities_tab_label"), WarningLevel::None, tab_capabilities_name); } { @@ -616,7 +616,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("erc_tab_label"), WarningLevel::none, tab_erc_name); + app_highlight_tab_label(lookup_widget("erc_tab_label"), WarningLevel::None, tab_erc_name); } { @@ -625,7 +625,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("selective_selftest_tab_label"), WarningLevel::none, tab_selective_selftest_name); + app_highlight_tab_label(lookup_widget("selective_selftest_tab_label"), WarningLevel::None, tab_selective_selftest_name); } { @@ -634,7 +634,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("phy_tab_label"), WarningLevel::none, tab_phy_name); + app_highlight_tab_label(lookup_widget("phy_tab_label"), WarningLevel::None, tab_phy_name); } { @@ -643,7 +643,7 @@ void GscInfoWindow::clear_ui_info(bool clear_tests_too) } // tab label - app_highlight_tab_label(lookup_widget("directory_tab_label"), WarningLevel::none, tab_directory_name); + app_highlight_tab_label(lookup_widget("directory_tab_label"), WarningLevel::None, tab_directory_name); } } @@ -691,7 +691,7 @@ void GscInfoWindow::on_view_output_button_clicked() std::string output = this->drive->get_full_output(); if (output.empty()) { - output = this->drive->get_info_output(); + output = this->drive->get_basic_output(); } win->set_text_from_command(_("Smartctl Output"), output); @@ -784,7 +784,7 @@ void GscInfoWindow::on_save_info_button_clicked() std::string data = this->drive->get_full_output(); if (data.empty()) { - data = this->drive->get_info_output(); + data = this->drive->get_basic_output(); } const std::error_code ec = hz::fs_file_put_contents(file, data); if (ec) { @@ -845,7 +845,7 @@ void GscInfoWindow::fill_ui_general(const std::vector& props std::vector id_props, version_props, health_props; for (auto&& p : props) { - if (p.section == AtaStorageProperty::Section::info) { + if (p.section == AtaStorageProperty::Section::Info) { if (p.generic_name == "smartctl/version/_merged_full") { version_props.push_back(p); } else if (p.generic_name == "smartctl/version/_merged") { @@ -853,7 +853,7 @@ void GscInfoWindow::fill_ui_general(const std::vector& props } else { id_props.push_back(p); } - } else if (p.section == AtaStorageProperty::Section::data && p.subsection == AtaStorageProperty::SubSection::health) { + } else if (p.section == AtaStorageProperty::Section::Data && p.subsection == AtaStorageProperty::SubSection::Health) { health_props.push_back(p); } } @@ -871,7 +871,7 @@ void GscInfoWindow::fill_ui_general(const std::vector& props identity_table->hide(); - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; int row = 0; for (auto&& p : id_props) { @@ -1007,11 +1007,11 @@ void GscInfoWindow::fill_ui_attributes(const std::vector& pr } - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; std::vector label_strings; // outside-of-tree properties for (const auto& p : props) { - if (p.section != AtaStorageProperty::Section::data || p.subsection != AtaStorageProperty::SubSection::attributes) + if (p.section != AtaStorageProperty::Section::Data || p.subsection != AtaStorageProperty::SubSection::Attributes) continue; // add non-attribute-type properties to label above @@ -1098,11 +1098,11 @@ void GscInfoWindow::fill_ui_statistics(const std::vector& pr sigc::bind(sigc::mem_fun(*this, &GscInfoWindow::cell_renderer_for_statistics), i)); } - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; std::vector label_strings; // outside-of-tree properties for (const auto& p : props) { - if (p.section != AtaStorageProperty::Section::data || p.subsection != AtaStorageProperty::SubSection::devstat) + if (p.section != AtaStorageProperty::Section::Data || p.subsection != AtaStorageProperty::SubSection::Devstat) continue; // add non-entry-type properties to label above @@ -1164,10 +1164,10 @@ void GscInfoWindow::fill_ui_self_test_info() Gtk::TreeModel::Row row; - auto test_ioffline = std::make_shared(drive, SelfTest::TestType::immediate_offline); + auto test_ioffline = std::make_shared(drive, SelfTest::TestType::ImmediateOffline); if (test_ioffline->is_supported()) { row = *(test_combo_model->append()); - row[test_combo_columns.name] = SelfTest::get_test_displayable_name(SelfTest::TestType::immediate_offline); + row[test_combo_columns.name] = SelfTest::get_test_displayable_name(SelfTest::TestType::ImmediateOffline); row[test_combo_columns.description] = _("Immediate Offline Test (also known as Immediate Offline Data Collection)" " is the manual version of Automatic Offline Data Collection, which, if enabled, is automatically run" @@ -1176,10 +1176,10 @@ void GscInfoWindow::fill_ui_self_test_info() row[test_combo_columns.self_test] = test_ioffline; } - auto test_short = std::make_shared(drive, SelfTest::TestType::short_test); + auto test_short = std::make_shared(drive, SelfTest::TestType::ShortTest); if (test_short->is_supported()) { row = *(test_combo_model->append()); - row[test_combo_columns.name] = SelfTest::get_test_displayable_name(SelfTest::TestType::short_test); + row[test_combo_columns.name] = SelfTest::get_test_displayable_name(SelfTest::TestType::ShortTest); row[test_combo_columns.description] = _("Short self-test consists of a collection of test routines that have the highest chance" " of detecting drive problems. Its result is reported in the Self-Test Log." @@ -1190,20 +1190,20 @@ void GscInfoWindow::fill_ui_self_test_info() row[test_combo_columns.self_test] = test_short; } - auto test_long = std::make_shared(drive, SelfTest::TestType::long_test); + auto test_long = std::make_shared(drive, SelfTest::TestType::LongTest); if (test_long->is_supported()) { row = *(test_combo_model->append()); - row[test_combo_columns.name] = SelfTest::get_test_displayable_name(SelfTest::TestType::long_test); + row[test_combo_columns.name] = SelfTest::get_test_displayable_name(SelfTest::TestType::LongTest); row[test_combo_columns.description] = _("Extended self-test examines complete disk surface and performs various test routines" " built into the drive. Its result is reported in the Self-Test Log."); row[test_combo_columns.self_test] = test_long; } - auto test_conveyance = std::make_shared(drive, SelfTest::TestType::conveyance); + auto test_conveyance = std::make_shared(drive, SelfTest::TestType::Conveyance); if (test_conveyance->is_supported()) { row = *(test_combo_model->append()); - row[test_combo_columns.name] = SelfTest::get_test_displayable_name(SelfTest::TestType::conveyance); + row[test_combo_columns.name] = SelfTest::get_test_displayable_name(SelfTest::TestType::Conveyance); row[test_combo_columns.description] = _("Conveyance self-test is intended to identify damage incurred during transporting of the drive."); row[test_combo_columns.self_test] = test_conveyance; @@ -1276,11 +1276,11 @@ void GscInfoWindow::fill_ui_self_test_log(const std::vector& } - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; std::vector label_strings; // outside-of-tree properties for (auto&& p : props) { - if (p.section != AtaStorageProperty::Section::data || p.subsection != AtaStorageProperty::SubSection::selftest_log) + if (p.section != AtaStorageProperty::Section::Data || p.subsection != AtaStorageProperty::SubSection::SelftestLog) continue; if (p.generic_name == "ata_smart_self_test_log/_merged") // the whole section, we don't need it @@ -1373,11 +1373,11 @@ void GscInfoWindow::fill_ui_error_log(const std::vector& pro } - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; std::vector label_strings; // outside-of-tree properties for (auto&& p : props) { - if (p.section != AtaStorageProperty::Section::data || p.subsection != AtaStorageProperty::SubSection::error_log) + if (p.section != AtaStorageProperty::Section::Data || p.subsection != AtaStorageProperty::SubSection::ErrorLog) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. @@ -1453,7 +1453,7 @@ void GscInfoWindow::fill_ui_temperature_log(const std::vector("temperature_log_textview"); - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; std::vector label_strings; // outside-of-tree properties std::string temperature; @@ -1484,7 +1484,7 @@ void GscInfoWindow::fill_ui_temperature_log(const std::vector()) { // only show if unsupported @@ -1567,11 +1567,11 @@ WarningLevel GscInfoWindow::fill_ui_capabilities(const std::vector("erc_log_textview"); - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; for (auto&& p : props) { - if (p.section != AtaStorageProperty::Section::data || p.subsection != AtaStorageProperty::SubSection::erc_log) + if (p.section != AtaStorageProperty::Section::Data || p.subsection != AtaStorageProperty::SubSection::ErcLog) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. @@ -1641,10 +1641,10 @@ WarningLevel GscInfoWindow::fill_ui_selective_self_test_log(const std::vector("selective_selftest_log_textview"); - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; for (auto&& p : props) { - if (p.section != AtaStorageProperty::Section::data || p.subsection != AtaStorageProperty::SubSection::selective_selftest_log) + if (p.section != AtaStorageProperty::Section::Data || p.subsection != AtaStorageProperty::SubSection::SelectiveSelftestLog) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. @@ -1671,10 +1671,10 @@ WarningLevel GscInfoWindow::fill_ui_physical(const std::vector("phy_log_textview"); - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; for (auto&& p : props) { - if (p.section != AtaStorageProperty::Section::data || p.subsection != AtaStorageProperty::SubSection::phy_log) + if (p.section != AtaStorageProperty::Section::Data || p.subsection != AtaStorageProperty::SubSection::PhyLog) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. @@ -1701,10 +1701,10 @@ WarningLevel GscInfoWindow::fill_ui_directory(const std::vector("directory_log_textview"); - WarningLevel max_tab_warning = WarningLevel::none; + WarningLevel max_tab_warning = WarningLevel::None; for (auto&& p : props) { - if (p.section != AtaStorageProperty::Section::data || p.subsection != AtaStorageProperty::SubSection::directory_log) + if (p.section != AtaStorageProperty::Section::Data || p.subsection != AtaStorageProperty::SubSection::DirectoryLog) continue; // Note: Don't use property description as a tooltip here. It won't be available if there's no property. @@ -1759,14 +1759,14 @@ void GscInfoWindow::cell_renderer_for_attributes(Gtk::CellRenderer* cr, crt->property_weight() = Pango::WEIGHT_BOLD; } if (column_index == attribute_table_columns.type.index()) { - if (attribute.attr_type == AtaStorageAttribute::AttributeType::prefail) { + if (attribute.attr_type == AtaStorageAttribute::AttributeType::Prefail) { crt->property_weight() = Pango::WEIGHT_BOLD; } else { // reset to default value if reloading crt->property_weight().reset_value(); } } if (column_index == attribute_table_columns.when_failed.index()) { - if (attribute.when_failed != AtaStorageAttribute::FailTime::none) { + if (attribute.when_failed != AtaStorageAttribute::FailTime::None) { crt->property_weight() = Pango::WEIGHT_BOLD; } else { // reset to default value if reloading // Do not use WEIGHT_NORMAL here, it interferes with cell markup @@ -1978,7 +1978,8 @@ gboolean GscInfoWindow::test_idle_callback(void* data) std::shared_ptr ex(new SmartctlExecutorGui()); ex->create_running_dialog(self); - self->test_error_msg = self->current_test->update(ex); + auto test_status = self->current_test->update(ex); + self->test_error_msg = (test_status ? test_status.error().message() : ""); if (!self->test_error_msg.empty()) { // gui_show_error_dialog("Cannot monitor test progress", self->test_error_msg, this); // better show in progressbar. self->current_test->force_stop(ex); // what else can we do? @@ -2007,17 +2008,17 @@ gboolean GscInfoWindow::test_idle_callback(void* data) auto status = self->current_test->get_status(); bool aborted = false; - AtaStorageSelftestEntry::StatusSeverity severity = AtaStorageSelftestEntry::StatusSeverity::none; + AtaStorageSelftestEntry::StatusSeverity severity = AtaStorageSelftestEntry::StatusSeverity::None; std::string result_msg; if (!self->test_error_msg.empty()) { aborted = true; - severity = AtaStorageSelftestEntry::StatusSeverity::error; + severity = AtaStorageSelftestEntry::StatusSeverity::Error; result_msg = Glib::ustring::compose(_("Test aborted: %1"), Glib::Markup::escape_text(self->test_error_msg)); } else { severity = AtaStorageSelftestEntry::get_status_severity(status); - if (status == AtaStorageSelftestEntry::Status::aborted_by_host) { + if (status == AtaStorageSelftestEntry::Status::AbortedByHost) { aborted = true; result_msg = ""s + _("Test was manually aborted.") + ""; // it's a StatusSeverity::none message @@ -2031,7 +2032,7 @@ gboolean GscInfoWindow::test_idle_callback(void* data) } } - if (severity != AtaStorageSelftestEntry::StatusSeverity::none) { + if (severity != AtaStorageSelftestEntry::StatusSeverity::None) { result_msg += "\n"s + _("Check the Self-Test Log for more information."); } @@ -2049,9 +2050,9 @@ gboolean GscInfoWindow::test_idle_callback(void* data) test_stop_button->set_sensitive(false); Gtk::StockID stock_id = Gtk::Stock::DIALOG_ERROR; - if (severity == AtaStorageSelftestEntry::StatusSeverity::none) { + if (severity == AtaStorageSelftestEntry::StatusSeverity::None) { stock_id = Gtk::Stock::DIALOG_INFO; - } else if (severity == AtaStorageSelftestEntry::StatusSeverity::warning) { + } else if (severity == AtaStorageSelftestEntry::StatusSeverity::Warning) { stock_id = Gtk::Stock::DIALOG_WARNING; } @@ -2097,10 +2098,11 @@ void GscInfoWindow::on_test_execute_button_clicked() std::shared_ptr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); - const std::string error_msg = test->start(ex); // this runs update() too. - if (!error_msg.empty()) { + auto test_status = test->start(ex); // this runs update() too. + if (!test_status) { /// Translators: %1 is test name - gui_show_error_dialog(Glib::ustring::compose(_("Cannot run %1"), SelfTest::get_test_displayable_name(test->get_test_type())), error_msg, this); + gui_show_error_dialog(Glib::ustring::compose(_("Cannot run %1"), + SelfTest::get_test_displayable_name(test->get_test_type())), test_status.error().message(), this); return; } @@ -2159,10 +2161,11 @@ void GscInfoWindow::on_test_stop_button_clicked() std::shared_ptr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); - const std::string error_msg = current_test->force_stop(ex); - if (!error_msg.empty()) { + auto test_status = current_test->force_stop(ex); + if (!test_status) { /// Translators: %1 is test name - gui_show_error_dialog(Glib::ustring::compose(_("Cannot stop %1"), SelfTest::get_test_displayable_name(current_test->get_test_type())), error_msg, this); + gui_show_error_dialog(Glib::ustring::compose(_("Cannot stop %1"), + SelfTest::get_test_displayable_name(current_test->get_test_type())), test_status.error().message(), this); return; } diff --git a/src/gsc_init.cpp b/src/gsc_init.cpp index 274e65d..b5087c2 100644 --- a/src/gsc_init.cpp +++ b/src/gsc_init.cpp @@ -55,7 +55,7 @@ Copyright: namespace { /// Config file in user's HOME - inline const hz::fs::path& get_home_config_file() + [[nodiscard]] inline const hz::fs::path& get_home_config_file() { static hz::fs::path home_config_file = hz::fs_get_user_config_dir() / "gsmartcontrol" / "gsmartcontrol2.conf"; return home_config_file; @@ -64,7 +64,7 @@ namespace { /// Libdebug channel buffer stream - inline std::ostringstream& get_debug_buf_channel_stream() + [[nodiscard]] inline std::ostringstream& get_debug_buf_channel_stream() { static std::ostringstream stream; return stream; @@ -72,7 +72,7 @@ namespace { /// Get libdebug buffer channel (create new one if unavailable). - inline DebugChannelBasePtr get_debug_buf_channel() + [[nodiscard]] inline DebugChannelBasePtr get_debug_buf_channel() { static DebugChannelBasePtr channel = std::make_shared(get_debug_buf_channel_stream()); return channel; diff --git a/src/gsc_main_window.cpp b/src/gsc_main_window.cpp index 579a771..9406db8 100644 --- a/src/gsc_main_window.cpp +++ b/src/gsc_main_window.cpp @@ -164,7 +164,7 @@ GscMainWindow::~GscMainWindow() void GscMainWindow::populate_iconview(bool smartctl_valid) { if (!smartctl_valid) { - iconview_->set_empty_view_message(GscMainWindowIconView::Message::no_smartctl); + iconview_->set_empty_view_message(GscMainWindowIconView::Message::NoSmartctl); iconview_->clear_all(); // the message won't be shown without invalidating the region. while (Gtk::Main::events_pending()) // give expose event the time it needs Gtk::Main::iteration(); @@ -174,7 +174,7 @@ void GscMainWindow::populate_iconview(bool smartctl_valid) rescan_devices(); // scan for devices and fill the iconview } else { - iconview_->set_empty_view_message(GscMainWindowIconView::Message::scan_disabled); + iconview_->set_empty_view_message(GscMainWindowIconView::Message::ScanDisabled); iconview_->clear_all(); // the message won't be shown without invalidating the region. while (Gtk::Main::events_pending()) // give expose event the time it needs Gtk::Main::iteration(); @@ -638,22 +638,22 @@ void GscMainWindow::on_action_enable_smart_toggled(Gtk::ToggleAction* action) return; StorageDevice::Status status = drive->get_smart_status(); - if (status == StorageDevice::Status::unsupported) // this shouldn't happen + if (status == StorageDevice::Status::Unsupported) // this shouldn't happen return; bool toggle_active = action->get_active(); - if ( (toggle_active && status == StorageDevice::Status::disabled) - || (!toggle_active && status == StorageDevice::Status::enabled) ) { + if ( (toggle_active && status == StorageDevice::Status::Disabled) + || (!toggle_active && status == StorageDevice::Status::Enabled) ) { std::shared_ptr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); - std::string error_msg = drive->set_smart_enabled(toggle_active, ex); // run it with GUI support + auto command_status = drive->set_smart_enabled(toggle_active, ex); // run it with GUI support - if (!error_msg.empty()) { + if (!command_status) { std::string error_header = (toggle_active ? _("Cannot enable SMART") : _("Cannot disable SMART")); - gsc_executor_error_dialog_show(error_header, error_msg, this); + gsc_executor_error_dialog_show(error_header, command_status.error().message(), this); } on_action_reread_device_data(); // reread if changed @@ -676,10 +676,10 @@ void GscMainWindow::on_action_enable_aodc_toggled(Gtk::ToggleAction* action) return; StorageDevice::Status status = drive->get_aodc_status(); - if (status == StorageDevice::Status::unsupported) // this shouldn't happen + if (status == StorageDevice::Status::Unsupported) // this shouldn't happen return; - if (status == StorageDevice::Status::unknown) { + if (status == StorageDevice::Status::Unknown) { // it's supported, but we don't know if it's enabled or not. ask the user. int response = 0; @@ -730,12 +730,12 @@ void GscMainWindow::on_action_enable_aodc_toggled(Gtk::ToggleAction* action) std::shared_ptr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); - std::string error_msg = drive->set_aodc_enabled(enable_aodc, ex); // run it with GUI support + auto command_status = drive->set_aodc_enabled(enable_aodc, ex); // run it with GUI support - if (!error_msg.empty()) { + if (!command_status) { std::string error_header = (enable_aodc ? _("Cannot enable Automatic Offline Data Collection") : _("Cannot disable Automatic Offline Data Collection")); - gsc_executor_error_dialog_show(error_header, error_msg, this); + gsc_executor_error_dialog_show(error_header, command_status.error().message(), this); } else { // tell the user, because there's no other feedback gui_show_info_dialog((enable_aodc ? _("Automatic Offline Data Collection enabled.") @@ -748,18 +748,18 @@ void GscMainWindow::on_action_enable_aodc_toggled(Gtk::ToggleAction* action) bool toggle_active = action->get_active(); - if ( (toggle_active && status == StorageDevice::Status::disabled) - || (!toggle_active && status == StorageDevice::Status::enabled) ) { + if ( (toggle_active && status == StorageDevice::Status::Disabled) + || (!toggle_active && status == StorageDevice::Status::Enabled) ) { std::shared_ptr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this); - std::string error_msg = drive->set_aodc_enabled(toggle_active, ex); // run it with GUI support + auto command_status = drive->set_aodc_enabled(toggle_active, ex); // run it with GUI support - if (!error_msg.empty()) { + if (!command_status) { std::string error_header = (toggle_active ? _("Cannot enable Automatic Offline Data Collection") : _("Cannot disable Automatic Offline Data Collection")); - gsc_executor_error_dialog_show(error_header, error_msg, this); + gsc_executor_error_dialog_show(error_header, command_status.error().message(), this); } on_action_reread_device_data(); // reread if changed @@ -780,11 +780,11 @@ void GscMainWindow::on_action_reread_device_data() ex->create_running_dialog(this); // note: this will clear the non-basic properties! - std::string error_msg = drive->fetch_basic_data_and_parse(ex); // run it with GUI support + auto fetch_status = drive->fetch_basic_data_and_parse(ex); // run it with GUI support // the icon will be updated through drive's signal_changed callback. - if (!error_msg.empty()) { - gsc_executor_error_dialog_show(_("Cannot retrieve SMART data"), error_msg, this); + if (!fetch_status) { + gsc_executor_error_dialog_show(_("Cannot retrieve SMART data"), fetch_status.error().message(), this); } } } @@ -822,8 +822,8 @@ void GscMainWindow::set_drive_menu_status(const StorageDevicePtr& drive) bool is_virtual = (drive && drive->get_is_virtual()); - StorageDevice::Status smart_status = StorageDevice::Status::unsupported; - StorageDevice::Status aodc_status = StorageDevice::Status::unsupported; + StorageDevice::Status smart_status = StorageDevice::Status::Unsupported; + StorageDevice::Status aodc_status = StorageDevice::Status::Unsupported; if (drive && !is_virtual) { smart_status = drive->get_smart_status(); @@ -837,7 +837,7 @@ void GscMainWindow::set_drive_menu_status(const StorageDevicePtr& drive) Glib::RefPtr action; if ((action = actiongroup_device_->get_action(APP_ACTION_NAME(action_perform_tests)))) - action->set_sensitive(smart_status == StorageDevice::Status::enabled); + action->set_sensitive(smart_status == StorageDevice::Status::Enabled); if ((action = actiongroup_device_->get_action(APP_ACTION_NAME(action_reread_device_data)))) action->set_visible(drive && !is_virtual); if ((action = actiongroup_device_->get_action(APP_ACTION_NAME(action_remove_device)))) { @@ -847,10 +847,10 @@ void GscMainWindow::set_drive_menu_status(const StorageDevicePtr& drive) if ((action = actiongroup_device_->get_action(APP_ACTION_NAME(action_remove_virtual_device)))) action->set_visible(drive && is_virtual); if ((action = actiongroup_device_->get_action(APP_ACTION_NAME(action_enable_smart)))) { - action->set_sensitive(smart_status != StorageDevice::Status::unsupported); + action->set_sensitive(smart_status != StorageDevice::Status::Unsupported); } if ((action = actiongroup_device_->get_action(APP_ACTION_NAME(action_enable_aodc)))) - action->set_sensitive(aodc_status != StorageDevice::Status::unsupported); + action->set_sensitive(aodc_status != StorageDevice::Status::Unsupported); } @@ -859,7 +859,7 @@ void GscMainWindow::set_drive_menu_status(const StorageDevicePtr& drive) Gtk::ToggleAction* action = dynamic_cast( actiongroup_device_->get_action(APP_ACTION_NAME(action_enable_smart)).operator->()); if (action) { - action->set_active(smart_status == StorageDevice::Status::enabled); + action->set_active(smart_status == StorageDevice::Status::Enabled); } } @@ -878,14 +878,14 @@ void GscMainWindow::set_drive_menu_status(const StorageDevicePtr& drive) // true if supported, but unknown whether it's enabled or not. if (dev_odc_item) - dev_odc_item->set_inconsistent(aodc_status == StorageDevice::Status::unknown); + dev_odc_item->set_inconsistent(aodc_status == StorageDevice::Status::Unknown); if (popup_odc_item) - popup_odc_item->set_inconsistent(aodc_status == StorageDevice::Status::unknown); + popup_odc_item->set_inconsistent(aodc_status == StorageDevice::Status::Unknown); if (status_aodc_check) - status_aodc_check->set_inconsistent(aodc_status == StorageDevice::Status::unknown); + status_aodc_check->set_inconsistent(aodc_status == StorageDevice::Status::Unknown); // for unknown it doesn't really matter what state it's in. - action->set_active(aodc_status == StorageDevice::Status::enabled); + action->set_active(aodc_status == StorageDevice::Status::Enabled); } } @@ -955,7 +955,7 @@ void GscMainWindow::update_status_widgets() // unless it's failing. // app_gtkmm_set_widget_tooltip(*health_label, health_prop.get_description(), true); - if (health_prop.warning_level != WarningLevel::none) { + if (health_prop.warning_level != WarningLevel::None) { std::string tooltip_str = storage_property_get_warning_reason(health_prop) + "\n\n" + _("View details for more information."); app_gtkmm_set_widget_tooltip(*health_label_, tooltip_str, true); @@ -1013,7 +1013,7 @@ void GscMainWindow::rescan_devices() // hz::string_split(match_str, ';', match_patterns, true); hz::string_split(blacklist_str, ';', blacklist_patterns, true); - iconview_->set_empty_view_message(GscMainWindowIconView::Message::scanning); + iconview_->set_empty_view_message(GscMainWindowIconView::Message::Scanning); iconview_->clear_all(); // clear previous icons, invalidate region to update the message. while (Gtk::Main::events_pending()) // give expose event the time it needs @@ -1029,7 +1029,7 @@ void GscMainWindow::rescan_devices() auto ex_factory = std::make_shared(true, this); // run it with GUI support - std::string error_msg = sd.detect_and_fetch_basic_data(drives_, ex_factory); + auto fetch_status = sd.detect_and_fetch_basic_data(drives_, ex_factory); bool error = false; @@ -1047,10 +1047,10 @@ void GscMainWindow::rescan_devices() } } - if (!error && !error_msg.empty()) { // generic scan error. smartctl errors are not reported during scan at all. + if (!error && !fetch_status) { // generic scan error. smartctl errors are not reported during scan at all. // we don't show output button here gsc_executor_error_dialog_show(_("An error occurred while scanning the system"), - error_msg, this, false, false); + fetch_status.error().message(), this, false, false); // error = true; // add them anyway, in case the error was only on one drive. @@ -1058,7 +1058,7 @@ void GscMainWindow::rescan_devices() // add them to iconview for (auto& drive : drives_) { if (rconfig::get_data("gui/show_smart_capable_only")) { - if (drive->get_smart_status() != StorageDevice::Status::unsupported) + if (drive->get_smart_status() != StorageDevice::Status::Unsupported) iconview_->add_entry(drive); } else { iconview_->add_entry(drive); @@ -1068,7 +1068,7 @@ void GscMainWindow::rescan_devices() // in case there are no drives in the system. if (iconview_->get_num_icons() == 0) - iconview_->set_empty_view_message(GscMainWindowIconView::Message::no_drives_found); + iconview_->set_empty_view_message(GscMainWindowIconView::Message::NoDrivesFound); this->scanning_ = false; } @@ -1127,9 +1127,9 @@ bool GscMainWindow::add_device(const std::string& file, const std::string& type_ tmp_drives.push_back(drive); StorageDetector sd; - std::string error_msg = sd.fetch_basic_data(tmp_drives, ex_factory, true); // return its first error - if (!error_msg.empty()) { - gsc_executor_error_dialog_show(_("An error occurred while adding the device"), error_msg, this); + auto fetch_error = sd.fetch_basic_data(tmp_drives, ex_factory, true); // return its first error + if (!fetch_error) { + gsc_executor_error_dialog_show(_("An error occurred while adding the device"), fetch_error.error().message(), this); } else { this->drives_.push_back(drive); @@ -1159,9 +1159,9 @@ bool GscMainWindow::add_virtual_drive(const std::string& file) drive->set_full_output(output); drive->set_info_output(output); // info can be parsed from full output string too. - std::string error_msg = drive->parse_data(); // this will set the type and add the properties - if (!error_msg.empty()) { - gui_show_error_dialog(_("Cannot interpret SMART data"), error_msg, this); + auto parse_error = drive->try_parse_data(); // this will set the type and add the properties + if (!parse_error) { + gui_show_error_dialog(_("Cannot interpret SMART data"), parse_error.error().message(), this); return false; } @@ -1199,7 +1199,7 @@ std::shared_ptr GscMainWindow::show_device_info_window(const Stor } // ask to enable SMART if it's supported but disabled - if (!drive->get_is_virtual() && (drive->get_smart_status() == StorageDevice::Status::disabled)) { + if (!drive->get_is_virtual() && (drive->get_smart_status() == StorageDevice::Status::Disabled)) { int status = 0; @@ -1218,10 +1218,10 @@ std::shared_ptr GscMainWindow::show_device_info_window(const Stor if (status == Gtk::RESPONSE_YES) { std::shared_ptr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this, Glib::ustring::compose(_("Running {command} on %1..."), drive->get_device_with_type())); - std::string error_msg = drive->set_smart_enabled(true, ex); // run it with GUI support + auto command_status = drive->set_smart_enabled(true, ex); // run it with GUI support - if (!error_msg.empty()) { - gsc_executor_error_dialog_show(_("Cannot enable SMART"), error_msg, this); + if (!command_status) { + gsc_executor_error_dialog_show(_("Cannot enable SMART"), command_status.error().message(), this); } } } @@ -1229,13 +1229,13 @@ std::shared_ptr GscMainWindow::show_device_info_window(const Stor // Virtual drives are parsed at load time. // Parse non-virtual, smart-supporting drives here. - if (!drive->get_is_virtual() && drive->get_smart_status() != StorageDevice::Status::unsupported) { + if (!drive->get_is_virtual() && drive->get_smart_status() != StorageDevice::Status::Unsupported) { std::shared_ptr ex(new SmartctlExecutorGui()); ex->create_running_dialog(this, Glib::ustring::compose(_("Running {command} on %1..."), drive->get_device_with_type())); - std::string error_msg = drive->fetch_data_and_parse(ex); // run it with GUI support + auto command_status = drive->fetch_full_data_and_parse(ex); // run it with GUI support - if (!error_msg.empty()) { - gsc_executor_error_dialog_show(_("Cannot retrieve SMART data"), error_msg, this); + if (!command_status) { + gsc_executor_error_dialog_show(_("Cannot retrieve SMART data"), command_status.error().message(), this); return nullptr; } } @@ -1244,9 +1244,9 @@ std::shared_ptr GscMainWindow::show_device_info_window(const Stor // If the drive output wasn't fully parsed (happens with e.g. scsi and // usb devices), only very basic info is available and there's no point // in showing this window. - for both virtual and non-virtual. - if (drive->get_parse_status() == StorageDevice::ParseStatus::none) { + if (drive->get_parse_status() == StorageDevice::ParseStatus::None) { gsc_no_info_dialog_show(_("No additional information is available for this drive."), - "", this, false, drive->get_info_output(), _("Smartctl Output"), drive->get_save_filename()); + "", this, false, drive->get_basic_output(), _("Smartctl Output"), drive->get_save_filename()); return nullptr; } @@ -1267,7 +1267,7 @@ std::shared_ptr GscMainWindow::show_device_info_window(const Stor void GscMainWindow::show_prefs_updated_message() { - iconview_->set_empty_view_message(GscMainWindowIconView::Message::please_rescan); + iconview_->set_empty_view_message(GscMainWindowIconView::Message::PleaseRescan); iconview_->clear_all(); // the message won't be shown without invalidating the region. while (Gtk::Main::events_pending()) // give expose event the time it needs Gtk::Main::iteration(); diff --git a/src/gsc_main_window.h b/src/gsc_main_window.h index f0373ca..a531958 100644 --- a/src/gsc_main_window.h +++ b/src/gsc_main_window.h @@ -102,7 +102,7 @@ class GscMainWindow : public AppBuilderWidget { void set_drive_menu_status(const StorageDevicePtr& drive); /// Get popup menu for a drive - Gtk::Menu* get_popup_menu(const StorageDevicePtr& drive); + [[nodiscard]] Gtk::Menu* get_popup_menu(const StorageDevicePtr& drive); /// Update status widgets (status area, etc...) void update_status_widgets(); diff --git a/src/gsc_main_window_iconview.h b/src/gsc_main_window_iconview.h index e92fd5b..3f8edf0 100644 --- a/src/gsc_main_window_iconview.h +++ b/src/gsc_main_window_iconview.h @@ -39,12 +39,12 @@ class GscMainWindowIconView : public Gtk::IconView { /// Message type to show enum class Message { - none, ///< No message - scan_disabled, ///< Scanning is disabled - scanning, ///< Scanning drives... - no_drives_found, ///< No drives found - no_smartctl, ///< No smartctl installed - please_rescan, ///< Re-scan to see the drives + None, ///< No message + ScanDisabled, ///< Scanning is disabled + Scanning, ///< Scanning drives... + NoDrivesFound, ///< No drives found + NoSmartctl, ///< No smartctl installed + PleaseRescan, ///< Re-scan to see the drives }; @@ -52,12 +52,12 @@ class GscMainWindowIconView : public Gtk::IconView { static std::string get_message_string(Message type) { static const std::unordered_map m { - {Message::none, _("[error - invalid message]")}, - {Message::scan_disabled, _("Automatic scanning is disabled.\nPress Ctrl+R to scan manually.")}, - {Message::scanning, _("Scanning system, please wait...")}, - {Message::no_drives_found, _("No drives found.")}, - {Message::no_smartctl, _("Please specify the correct smartctl binary in\nPreferences and press Ctrl-R to re-scan.")}, - {Message::please_rescan, _("Preferences changed.\nPress Ctrl-R to re-scan.")}, + {Message::None, _("[error - invalid message]")}, + {Message::ScanDisabled, _("Automatic scanning is disabled.\nPress Ctrl+R to scan manually.")}, + {Message::Scanning, _("Scanning system, please wait...")}, + {Message::NoDrivesFound, _("No drives found.")}, + {Message::NoSmartctl, _("Please specify the correct smartctl binary in\nPreferences and press Ctrl-R to re-scan.")}, + {Message::PleaseRescan, _("Preferences changed.\nPress Ctrl-R to re-scan.")}, }; if (auto iter = m.find(type); iter != m.end()) { return iter->second; @@ -175,7 +175,7 @@ class GscMainWindowIconView : public Gtk::IconView { if (in_destruction()) { return true; } - if (empty_view_message != Message::none && this->num_icons == 0) { // no icons + if (empty_view_message != Message::None && this->num_icons == 0) { // no icons Glib::RefPtr layout = this->create_pango_layout(""); layout->set_alignment(Pango::ALIGN_CENTER); layout->set_markup(get_message_string(empty_view_message)); @@ -347,18 +347,18 @@ class GscMainWindowIconView : public Gtk::IconView { Glib::RefPtr icon; switch(drive->get_detected_type()) { - case StorageDevice::DetectedType::cddvd: + case StorageDevice::DetectedType::CdDvd: icon = cddvd_icon; break; - case StorageDevice::DetectedType::unknown: // standard HD icon - case StorageDevice::DetectedType::invalid: - case StorageDevice::DetectedType::raid: // TODO a separate icon for this + case StorageDevice::DetectedType::Unknown: // standard HD icon + case StorageDevice::DetectedType::Invalid: + case StorageDevice::DetectedType::Raid: // TODO a separate icon for this icon = hd_icon; break; } const AtaStorageProperty health_prop = drive->get_health_property(); - if (health_prop.warning_level != WarningLevel::none && health_prop.generic_name == "smart_status/passed") { + if (health_prop.warning_level != WarningLevel::None && health_prop.generic_name == "smart_status/passed") { if (icon) { icon = icon->copy(); // work on a copy if (icon->get_colorspace() == Gdk::COLORSPACE_RGB && icon->get_bits_per_sample() == 8) { @@ -589,7 +589,7 @@ class GscMainWindowIconView : public Gtk::IconView { GscMainWindow* main_window = nullptr; ///< The main window, our parent - Message empty_view_message = Message::none; ///< Message type to display when not showing any icons + Message empty_view_message = Message::None; ///< Message type to display when not showing any icons }; diff --git a/src/gsc_startup_settings.h b/src/gsc_startup_settings.h index 919d57b..69de140 100644 --- a/src/gsc_startup_settings.h +++ b/src/gsc_startup_settings.h @@ -27,7 +27,7 @@ struct GscStartupSettings { /// Get startup settings -inline GscStartupSettings& get_startup_settings() +[[nodiscard]] inline GscStartupSettings& get_startup_settings() { static GscStartupSettings startup_settings; return startup_settings; diff --git a/src/hz/enum_helper.h b/src/hz/enum_helper.h index 30d5bc5..e561acf 100644 --- a/src/hz/enum_helper.h +++ b/src/hz/enum_helper.h @@ -36,7 +36,7 @@ class EnumHelper { /// Return storable name of an enum member - static std::string get_storable_name(EnumType enum_value) + [[nodiscard]] static std::string get_storable_name(EnumType enum_value) { const auto& m = get_enum_static_map(); // Iterator: enum -> pair{storable, displayable} @@ -46,7 +46,7 @@ class EnumHelper { /// Return an enum member by its storable name - static EnumType get_by_storable_name(const std::string& storable_name, + [[nodiscard]] static EnumType get_by_storable_name(const std::string& storable_name, EnumType default_value = EnumExtClass::default_value) { const auto& m = get_storable_enum_static_map(); @@ -57,7 +57,7 @@ class EnumHelper { /// Return displayable name of an enum member - static DisplayableStringType get_displayable_name(EnumType enum_value) + [[nodiscard]] static DisplayableStringType get_displayable_name(EnumType enum_value) { const auto& m = get_enum_static_map(); // Iterator: enum -> pair{storable, displayable} @@ -67,7 +67,7 @@ class EnumHelper { /// Return all possible members of an enum - static std::vector getAllValues() + [[nodiscard]] static std::vector getAllValues() { static const auto v = build_enum_value_list(); return v; @@ -77,7 +77,7 @@ class EnumHelper { private: /// Get a static map of storable names to enum values. - static const EnumMapType& get_enum_static_map() + [[nodiscard]] static const EnumMapType& get_enum_static_map() { static const auto m = EnumExtClass::build_enum_map(); return m; @@ -85,7 +85,7 @@ class EnumHelper { /// Get a static map of storable names to enum values. - static const std::unordered_map& get_storable_enum_static_map() + [[nodiscard]] static const std::unordered_map& get_storable_enum_static_map() { static const auto m = build_storable_enum_map(); return m; diff --git a/src/hz/error_holder.h b/src/hz/error_holder.h index 7f7cc9c..6cb89b1 100644 --- a/src/hz/error_holder.h +++ b/src/hz/error_holder.h @@ -38,12 +38,12 @@ Predefined error types are: "errno", "signal" (child exited with signal). /// Error level (severity) enum class ErrorLevel { - none = 0, ///< No error - dump = 1 << 0, ///< Dump - info = 1 << 1, ///< Informational (default) - warn = 1 << 2, ///< Warning - error = 1 << 3, ///< Error - fatal = 1 << 4 ///< Fatal + None = 0, ///< No error + Dump = 1 << 0, ///< Dump + Info = 1 << 1, ///< Informational (default) + Warn = 1 << 2, ///< Warning + Error = 1 << 3, ///< Error + Fatal = 1 << 4 ///< Fatal }; @@ -104,7 +104,7 @@ class ErrorBase { /// Get error code of type \c CodeMemberType template - CodeMemberType get_code() const // this may throw on bad cast! + [[nodiscard]] CodeMemberType get_code() const // this may throw on bad cast! { if (get_code_type_info() != typeid(CodeMemberType)) throw type_mismatch(get_code_type_info(), typeid(CodeMemberType)); @@ -113,7 +113,7 @@ class ErrorBase { /// Get error code of type \c CodeMemberType template - bool get_code(CodeMemberType& put_it_here) const // this doesn't throw + [[nodiscard]] bool get_code(CodeMemberType& put_it_here) const // this doesn't throw { if (get_code_type_info() != typeid(CodeMemberType)) return false; @@ -125,7 +125,7 @@ class ErrorBase { /// Increase the level (severity) of the error ErrorLevel level_inc() { - if (level_ == ErrorLevel::fatal) + if (level_ == ErrorLevel::Fatal) return level_; return (level_ = static_cast(static_cast(level_) << 1)); } @@ -133,7 +133,7 @@ class ErrorBase { /// Decrease the level (severity) of the error ErrorLevel level_dec() { - if (level_ == ErrorLevel::none) + if (level_ == ErrorLevel::None) return level_; return (level_ = static_cast(static_cast(level_) >> 1)); } @@ -184,7 +184,7 @@ class ErrorBase { private: std::string type_; ///< Error type - ErrorLevel level_ = ErrorLevel::none; ///< Error severity + ErrorLevel level_ = ErrorLevel::None; ///< Error severity std::string message_; ///< Error message }; @@ -422,12 +422,12 @@ class ErrorHolder { // use debug macros, not functions (to allow complete removal through preprocessor). switch (level) { - case ErrorLevel::none: break; - case ErrorLevel::dump: debug_out_dump("hz", msg); break; - case ErrorLevel::info: debug_out_info("hz", msg); break; - case ErrorLevel::warn: debug_out_warn("hz", "Warning: " << msg); break; - case ErrorLevel::error: debug_out_error("hz", "Error: " << msg); break; - case ErrorLevel::fatal: debug_out_fatal("hz", "Fatal: " << msg); break; + case ErrorLevel::None: break; + case ErrorLevel::Dump: debug_out_dump("hz", msg); break; + case ErrorLevel::Info: debug_out_info("hz", msg); break; + case ErrorLevel::Warn: debug_out_warn("hz", "Warning: " << msg); break; + case ErrorLevel::Error: debug_out_error("hz", "Error: " << msg); break; + case ErrorLevel::Fatal: debug_out_fatal("hz", "Fatal: " << msg); break; } } diff --git a/src/hz/system_specific.h b/src/hz/system_specific.h index 60c5e83..0e0a248 100644 --- a/src/hz/system_specific.h +++ b/src/hz/system_specific.h @@ -110,7 +110,7 @@ namespace hz { } - inline std::type_info* get_current_exception_type() + [[nodiscard]] inline std::type_info* get_current_exception_type() { return ::abi::__cxa_current_exception_type(); } @@ -129,7 +129,7 @@ namespace hz { } - inline std::type_info* get_current_exception_type() + [[nodiscard]] inline std::type_info* get_current_exception_type() { return nullptr; } diff --git a/src/libdebug/dcmdarg.cpp b/src/libdebug/dcmdarg.cpp index f55dceb..eec4f2f 100644 --- a/src/libdebug/dcmdarg.cpp +++ b/src/libdebug/dcmdarg.cpp @@ -58,7 +58,7 @@ namespace debug_internal { /// Get libdebug command-line arguments - inline DebugCmdArgs* get_debug_get_args_holder() + [[nodiscard]] inline DebugCmdArgs* get_debug_get_args_holder() { static DebugCmdArgs args; return &args; diff --git a/src/libdebug/dflags.h b/src/libdebug/dflags.h index 06861cd..952cd89 100644 --- a/src/libdebug/dflags.h +++ b/src/libdebug/dflags.h @@ -29,20 +29,20 @@ namespace debug_level { using flags = std::bitset; ///< Combination of debug level flags /// Get bitset with all flags enabled - const flags& get_all_flags(); + [[nodiscard]] const flags& get_all_flags(); /// Get debug level name - const char* get_name(flag level); + [[nodiscard]] const char* get_name(flag level); /// Get color start sequence for debug level (for colorizing the output) - const char* get_color_start(flag level); + [[nodiscard]] const char* get_color_start(flag level); /// Get color stop sequence for debug level (for colorizing the output) - const char* get_color_stop(flag level); + [[nodiscard]] const char* get_color_stop(flag level); /// Convert ORed flags into a vector of flags - template inline + template void get_matched_levels_array(const flags& levels, Container& put_here) { for (auto level : { diff --git a/src/libdebug/dstate.h b/src/libdebug/dstate.h index fb84235..5119e97 100644 --- a/src/libdebug/dstate.h +++ b/src/libdebug/dstate.h @@ -121,7 +121,7 @@ namespace debug_internal { /// Get global libdebug state - DebugState& get_debug_state_ref(); + [[nodiscard]] DebugState& get_debug_state_ref(); diff --git a/src/libdebug/dstream.h b/src/libdebug/dstream.h index b3d4ed2..3d3b638 100644 --- a/src/libdebug/dstream.h +++ b/src/libdebug/dstream.h @@ -29,15 +29,15 @@ namespace debug_internal { /// Get null streambuf - a streambuf which does nothing. - std::streambuf& get_null_streambuf(); + [[nodiscard]] std::streambuf& get_null_streambuf(); /// Get null ostream - an ostream which does nothing. - std::ostream& get_null_stream(); + [[nodiscard]] std::ostream& get_null_stream(); // state.h includes us, so we need these forward declarations // class DebugState; -// DebugState& get_debug_state(); +// [[nodiscard]] DebugState& get_debug_state(); @@ -234,7 +234,7 @@ namespace debug_internal { } /// Get channel list - std::vector& get_channels() + [[nodiscard]] std::vector& get_channels() { return channels_; } diff --git a/src/rconfig/rconfig.h b/src/rconfig/rconfig.h index b74a4b2..9949dd6 100644 --- a/src/rconfig/rconfig.h +++ b/src/rconfig/rconfig.h @@ -165,7 +165,7 @@ inline bool init_root() /// Get the config branch node -inline json& get_config_branch() +[[nodiscard]] inline json& get_config_branch() { init_root(); return *impl::config_node; @@ -174,7 +174,7 @@ inline json& get_config_branch() /// Get the default branch node -inline json& get_default_branch() +[[nodiscard]] inline json& get_default_branch() { init_root(); return *impl::default_node; @@ -212,7 +212,7 @@ void set_default_data(const std::string& path, T data) /// Get the data from config. If no such node exists, look it up in defaults. template -T get_data(const std::string& path) +[[nodiscard]] T get_data(const std::string& path) { T data = {}; bool found = false; @@ -235,7 +235,7 @@ T get_data(const std::string& path) /// Get the data from defaults. template -T get_default_data(const std::string& path) +[[nodiscard]] T get_default_data(const std::string& path) { T data = {}; // This can throw only for errors within the program.