mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-30 07:35:37 +00:00
180 lines
5.0 KiB
C++
180 lines
5.0 KiB
C++
/******************************************************************************
|
|
License: GNU General Public License v3.0 only
|
|
Copyright:
|
|
(C) 2008 - 2021 Alexander Shaduri <ashaduri@gmail.com>
|
|
******************************************************************************/
|
|
/// \file
|
|
/// \author Alexander Shaduri
|
|
/// \ingroup applib
|
|
/// \weakgroup applib
|
|
/// @{
|
|
|
|
#ifndef SMARTCTL_EXECUTOR_H
|
|
#define SMARTCTL_EXECUTOR_H
|
|
|
|
#include <vector>
|
|
#include <glibmm/i18n.h>
|
|
|
|
#include "cmdex.h"
|
|
#include "cmdex_sync.h"
|
|
#include "hz/fs_ns.h"
|
|
|
|
|
|
|
|
/// Smartctl executor template.
|
|
template<class ExecutorSync>
|
|
class SmartctlExecutorGeneric : public ExecutorSync {
|
|
public:
|
|
|
|
/// Constructor
|
|
SmartctlExecutorGeneric(const std::string& cmd, const std::string& cmdargs)
|
|
: ExecutorSync(cmd, cmdargs)
|
|
{
|
|
this->construct();
|
|
}
|
|
|
|
|
|
/// Constructor
|
|
SmartctlExecutorGeneric()
|
|
{
|
|
this->construct();
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
/// Called by constructors
|
|
void construct()
|
|
{
|
|
ExecutorSync::get_command_executor().set_exit_status_translator(&SmartctlExecutorGeneric::translate_exit_status);
|
|
this->set_error_header(std::string(_("An error occurred while executing smartctl:")) + "\n\n");
|
|
}
|
|
|
|
|
|
enum {
|
|
exit_cant_parse = 1 << 0, ///< Smartctl error code bit
|
|
exit_open_failed = 1 << 1, ///< Smartctl error code bit
|
|
exit_smart_failed = 1 << 2, ///< Smartctl error code bit
|
|
exit_disk_failing = 1 << 3, ///< Smartctl error code bit
|
|
exit_prefail_threshold = 1 << 4, ///< Smartctl error code bit
|
|
exit_threshold_in_past = 1 << 5, ///< Smartctl error code bit
|
|
exit_error_log = 1 << 6, ///< Smartctl error code bit
|
|
exit_self_test_log = 1 << 7 ///< Smartctl error code bit
|
|
};
|
|
|
|
|
|
/// Translate smartctl error code to a readable message
|
|
static std::string translate_exit_status(int status)
|
|
{
|
|
static const std::vector<std::string> table = {
|
|
_("Command line did not parse."),
|
|
_("Device open failed, or device did not return an IDENTIFY DEVICE structure."),
|
|
_("Some SMART command to the disk failed, or there was a checksum error in a SMART data structure"),
|
|
_("SMART status check returned \"DISK FAILING\""),
|
|
_("SMART status check returned \"DISK OK\" but some prefail Attributes are less than threshold."),
|
|
_("SMART status check returned \"DISK OK\" but we found that some (usage or prefail) Attributes have been less than threshold at some time in the past."),
|
|
_("The device error log contains records of errors."),
|
|
_("The device self-test log contains records of errors.")
|
|
};
|
|
|
|
std::string str;
|
|
|
|
// check every bit
|
|
for (unsigned int i = 0; i <= 7; i++) {
|
|
if (status & (1 << i)) {
|
|
if (!str.empty())
|
|
str += "\n"; // new-line-separate each entry
|
|
str += table[i];
|
|
}
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
/// Import the last error from command executor and clear all errors there
|
|
void import_error() override
|
|
{
|
|
Cmdex& cmdex = this->get_command_executor();
|
|
|
|
Cmdex::error_list_t errors = cmdex.get_errors(); // these are not clones
|
|
|
|
hz::ErrorBase* e = nullptr;
|
|
// find the last relevant error.
|
|
for (auto iter = errors.crbegin(); iter != errors.crend(); ++iter) {
|
|
// ignore iochannel errors, they may mask the real errors
|
|
if ((*iter)->get_type() != "giochannel" && (*iter)->get_type() != "custom") {
|
|
e = (*iter)->clone();
|
|
break;
|
|
}
|
|
}
|
|
|
|
cmdex.clear_errors(); // and clear them
|
|
|
|
if (e) { // if error is present, alert the user
|
|
on_error_warn(e);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// This is called when an error occurs in command executor.
|
|
/// Note: The warnings are already printed via debug_* in cmdex.
|
|
void on_error_warn(hz::ErrorBase* e) override
|
|
{
|
|
if (!e)
|
|
return;
|
|
|
|
// import the error only if it's relevant.
|
|
std::string error_type = e->get_type();
|
|
// accept all errors by default, except:
|
|
|
|
// Treat most exit codes as non-errors.
|
|
if (error_type == "exit") {
|
|
int exit_code = 0;
|
|
e->get_code(exit_code);
|
|
// Ignore everyone except this.
|
|
// Note that we don't treat exit_open_failed as failure because:
|
|
// * It may be returned from a DVD that returns product info but has no disk inside;
|
|
// * It may be returned from a usb flash drive with -d scsi;
|
|
// * exit_cant_parse is returned when opening unsupported usb drives without -d scsi.
|
|
if ( !(exit_code & exit_cant_parse) )
|
|
return;
|
|
|
|
// ignore giochannel errors - higher level errors will be triggered, and they more user-friendly.
|
|
} else if (error_type == "giochannel" || error_type == "custom") {
|
|
return;
|
|
}
|
|
|
|
this->set_error_msg(e->get_message());
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Smartctl executor without GUI support
|
|
using SmartctlExecutor = SmartctlExecutorGeneric<CmdexSync>;
|
|
|
|
|
|
|
|
/// Get smartctl binary (from config, etc...). Returns an empty string if not found.
|
|
hz::fs::path get_smartctl_binary();
|
|
|
|
|
|
/// 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,
|
|
const std::string& command_options,
|
|
std::shared_ptr<CmdexSync> smartctl_ex, std::string& smartctl_output);
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
/// @}
|