Increased main window size a bit to avoid the iconview shift down each time an

icon is selected.
Added some scsi-output properties to full parser to avoid console warnings.
Devices having only basic info can be displayed now in the info window.
This commit is contained in:
Alexander Shaduri
2014-04-30 10:38:48 +00:00
parent 85dbceb14d
commit 2293e357f4
12 changed files with 317 additions and 139 deletions
+10
View File
@@ -28,6 +28,12 @@ Bugs / patches:
TODO:
Don't rely on smartctl return code (2), parse the output instead.
This will allow us to show the Info page.
Update to latest drivedb.
Test Areca windows detection by installing CLI, then replacing it with a dummy
program that prints something (with enclosures and without).
@@ -38,6 +44,10 @@ Fix not being able to quit while executing commands.
https://developer.gnome.org/glib/2.34/glib-The-Main-Event-Loop.html#g-main-loop-quit
In main window add multi-selection support and right-click menu with various
tests, so that it's possible to run multiple tests in parallel from there.
Areca detection
Linux:
Somehow differentiate between the cards with expanders (-d areca,N/E) and without (-d areca,N).
@@ -111,7 +111,7 @@ std::string execute_smartctl(const std::string& device, const std::string& devic
+ " " + Glib::shell_quote(device));
if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Error while executing smartctl binary.\n");
debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n");
smartctl_output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str()));
+6 -2
View File
@@ -139,8 +139,12 @@ class SmartctlExecutorGeneric : public ExecutorSync {
if (error_type == "exit") {
int exit_code = 0;
e->get_code(exit_code);
// ignore everyone except these
if ( !((exit_code & exit_cant_parse) || (exit_code & exit_open_failed)) )
// 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.
+24 -1
View File
@@ -412,11 +412,26 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p)
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
} else if (app_pcre_match("/^Device Model$/mi", p.reported_name)) {
} else if (app_pcre_match("/^(?:Device Model|Device|Product)$/mi", p.reported_name)) { // "Device" and "Product" are from scsi/usb
p.set_name(p.reported_name, "device_model", "Device Model");
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
} else if (app_pcre_match("/^Vendor$/mi", p.reported_name)) { // From scsi/usb
p.set_name(p.reported_name, "vendor", "Vendor");
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
} else if (app_pcre_match("/^Revision$/mi", p.reported_name)) { // From scsi/usb
p.set_name(p.reported_name, "revision", "Revision");
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
} else if (app_pcre_match("/^Device type$/mi", p.reported_name)) { // From scsi/usb
p.set_name(p.reported_name, "device_type", "Device Type");
p.value_type = StorageProperty::value_type_string;
p.value_string = p.reported_value;
} else if (app_pcre_match("/^Serial Number$/mi", p.reported_name)) {
p.set_name(p.reported_name, "serial_number", "Serial Number");
p.value_type = StorageProperty::value_type_string;
@@ -452,6 +467,11 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p)
p.value_type = StorageProperty::value_type_string; // prints a single value (if it's not 512)
p.value_string = p.reported_value;
} else if (app_pcre_match("/^Logical block size$/mi", p.reported_name)) { // from scsi/usb
p.set_name(p.reported_name, "logical_block_size", "Logical Block Size");
p.value_type = StorageProperty::value_type_string; // "512 bytes"
p.value_string = p.reported_value;
} else if (app_pcre_match("/^Rotation Rate$/mi", p.reported_name)) {
p.set_name(p.reported_name, "rotation_rate", "Rotation Rate");
p.value_type = StorageProperty::value_type_string;
@@ -514,6 +534,9 @@ bool SmartctlParser::parse_section_info_property(StorageProperty& p)
p.value_bool = true; // let's be optimistic - just hope that it doesn't hurt.
}
} else if (app_pcre_match("/^scsiMode/mi", p.reported_name)) { // these are some debug warnings from smartctl on usb flash drives
p.show_in_ui = false;
} else {
debug_out_warn("app", DBG_FUNC_MSG << "Unknown property \"" << p.reported_name << "\"\n");
// this is not an error, just unknown attribute. treat it as string.
@@ -116,7 +116,7 @@ std::string get_scan_open_multiport_devices(std::vector<StorageDeviceRefPtr>& dr
smartctl_def_options + "--scan-open");
if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Error while executing smartctl binary.\n");
debug_out_warn("app", DBG_FUNC_MSG << "Smartctl binary did not execute cleanly.\n");
return smartctl_ex->get_error_msg();
}
+16 -14
View File
@@ -64,7 +64,7 @@ StorageDevice::StorageDevice(const string& dev_or_vfile, bool is_virtual)
// force_type_ = false;
is_virtual_ = is_virtual;
is_manually_added_ = false;
fully_parsed_ = false;
parse_status_ = parse_status_none;
test_is_active_ = false;
if (is_virtual) {
@@ -82,7 +82,7 @@ StorageDevice::StorageDevice(const string& dev, const string& type_arg)
// force_type_ = false;
is_virtual_ = false;
is_manually_added_ = false;
fully_parsed_ = false;
parse_status_ = parse_status_none;
test_is_active_ = false;
device_ = dev;
@@ -112,7 +112,7 @@ StorageDevice& StorageDevice::operator=(const StorageDevice& other)
virtual_file_ = other.virtual_file_;
is_manually_added_ = other.is_manually_added_;
fully_parsed_ = other.fully_parsed_;
parse_status_ = other.parse_status_;
test_is_active_ = other.test_is_active_;
detected_type_ = other.detected_type_;
@@ -137,7 +137,7 @@ void StorageDevice::clear_fetched(bool including_outputs) {
full_output_.clear();
}
fully_parsed_ = false;
parse_status_ = parse_status_none;
test_is_active_ = false; // not sure
smart_supported_.reset();
@@ -288,6 +288,8 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
}
}
set_parse_status(parse_status_info);
if (emit_signal)
signal_changed.emit(this); // notify listeners
@@ -350,13 +352,15 @@ std::string StorageDevice::parse_data()
// 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.
// Call this after parse_basic_data(), since it sets parse status to "info".
this->set_parse_status(StorageDevice::parse_status_full);
// set the full properties
this->set_fully_parsed(true);
this->set_properties(ps.get_properties()); // copy to our drive, overwriting old data
signal_changed.emit(this); // notify listeners
return "";
return std::string();
}
// Don't show any GUI warnings on parse failure - it may just be an unsupported
@@ -364,22 +368,20 @@ std::string StorageDevice::parse_data()
// parsed again in Info window, and we show the warnings there.
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse smartctl output.\n");
this->set_fully_parsed(false);
// 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.
return ps.get_error_msg(); // return full parser's error messages - they are more detailed.
}
return ""; // return ok if at least the info was ok.
return std::string(); // return ok if at least the info was ok.
}
bool StorageDevice::get_fully_parsed() const
StorageDevice::parse_status_t StorageDevice::get_parse_status() const
{
return fully_parsed_;
return parse_status_;
}
@@ -825,7 +827,7 @@ std::string StorageDevice::execute_device_smartctl(const std::string& command_op
command_options, smartctl_ex, smartctl_output);
if (!error_msg.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Error while executing smartctl binary.\n");
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,
@@ -844,9 +846,9 @@ std::string StorageDevice::execute_device_smartctl(const std::string& command_op
void StorageDevice::set_fully_parsed(bool b)
void StorageDevice::set_parse_status(parse_status_t value)
{
fully_parsed_ = b;
parse_status_ = value;
}
+11 -3
View File
@@ -67,6 +67,14 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
static bool order_less_than(const StorageDeviceRefPtr& a, const StorageDeviceRefPtr& b);
/// Statuses of various parse states
enum parse_status_t {
parse_status_full, ///< Fully parsed
parse_status_info, ///< Only info section available
parse_status_none, ///< No data
};
/// Constructor
StorageDevice(const std::string& dev_or_vfile, bool is_virtual = false);
@@ -100,7 +108,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
std::string parse_data();
/// Get the "fully parsed" flag
bool get_fully_parsed() const;
parse_status_t get_parse_status() const;
/// Try to enable SMART.
@@ -239,7 +247,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
protected:
/// Set the "fully parsed" flag
void set_fully_parsed(bool b);
void set_parse_status(parse_status_t value);
/// Set parsed properties
void set_properties(const SmartctlParser::prop_list_t& props);
@@ -258,7 +266,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced {
std::string virtual_file_; ///< A file (smartctl data) the virtual device was loaded from
bool is_manually_added_; ///< StorageDevice doesn't use it, but it's useful for its users.
bool fully_parsed_; ///< "Fully parsed" flag
parse_status_t parse_status_; ///< "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).
+3 -1
View File
@@ -354,7 +354,7 @@ class StorageProperty {
/// Constructor
StorageProperty()
: section(section_unknown), subsection(subsection_unknown),
value_type(value_type_unknown), warning(warning_none)
value_type(value_type_unknown), warning(warning_none), show_in_ui(true)
{
// value_from_db = false;
value_integer = 0; // this should nullify all union members
@@ -435,6 +435,8 @@ class StorageProperty {
warning_t warning; ///< Warning severity for this property
std::string warning_reason; // Warning reason (displayable)
bool show_in_ui; ///< Whether to show this property in UI or not
};
+10 -1
View File
@@ -408,6 +408,10 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
int row = 1; // row 0 is always empty. this way it's much easier.
for (prop_iterator iter = id_props.begin(); iter != id_props.end(); ++iter) {
if (!iter->show_in_ui) {
continue; // hide debug messages from smartctl
}
if (iter->generic_name == "overall_health") // a little distance for this one
++row;
@@ -1302,7 +1306,12 @@ void GscInfoWindow::on_view_output_button_clicked()
GscTextWindow<SmartctlOutputInstance>* win = GscTextWindow<SmartctlOutputInstance>::create();
// make save visible and enable monospace font
win->set_text("Smartctl Output", this->drive->get_full_output(), true, true);
std::string output = this->drive->get_full_output();
if (output.empty()) {
output = this->drive->get_info_output();
}
win->set_text("Smartctl Output", output, true, true);
std::string filename = drive->get_save_filename();
if (!filename.empty())
+3 -3
View File
@@ -1189,8 +1189,8 @@ GscInfoWindow* GscMainWindow::show_device_info_window(StorageDeviceRefPtr drive)
// Virtual drives are parsed at load time.
// Parse non-virtual drives here.
if (!drive->get_is_virtual()) {
// Parse non-virtual, smart-supporting drives here.
if (!drive->get_is_virtual() && drive->get_smart_status() != StorageDevice::status_unsupported) {
SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui());
ex->create_running_dialog(this, "Running %s on " + drive->get_device_with_type() + "...");
std::string error_msg = drive->fetch_data_and_parse(ex); // run it with GUI support
@@ -1205,7 +1205,7 @@ GscInfoWindow* GscMainWindow::show_device_info_window(StorageDeviceRefPtr drive)
// 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_fully_parsed()) {
if (drive->get_parse_status() == StorageDevice::parse_status_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());
return 0;
+230 -110
View File
@@ -20,128 +20,243 @@ Glib-based policy.
*/
/// Lock GStaticMutex
#define hz_glib_static_mutex_lock(mutex) \
g_mutex_lock(g_static_mutex_get_mutex(mutex))
/// Try locking GStaticMutex
#define hz_glib_static_mutex_trylock(mutex) \
g_mutex_trylock(g_static_mutex_get_mutex(mutex))
/// Unlock GStaticMutex
#define hz_glib_static_mutex_unlock(mutex) \
g_mutex_unlock(g_static_mutex_get_mutex(mutex))
namespace hz {
/// C++ Wrapper for Glib mutex
class MutexGlib : public hz::noncopyable {
public:
typedef GStaticMutex native_type;
static void native_lock(native_type& mutex)
{
hz_glib_static_mutex_lock(&mutex);
}
static bool native_trylock(native_type& mutex)
{
return hz_glib_static_mutex_trylock(&mutex);
}
static void native_unlock(native_type& mutex)
{
hz_glib_static_mutex_unlock(&mutex);
}
MutexGlib() { g_static_mutex_init(&mutex_); }
~MutexGlib() { g_static_mutex_free(&mutex_); }
void lock() { native_lock(mutex_); }
bool trylock() { return native_trylock(mutex_); }
void unlock() { native_unlock(mutex_); }
private:
GStaticMutex mutex_; // use StaticMutex, I think it uses less heap memory
};
#if GLIB_CHECK_VERSION(2, 32, 0)
/// C++ Wrapper for Glib mutex
class MutexGlib : public hz::noncopyable {
public:
typedef GMutex native_type;
/// C++ Wrapper for Glib recursive mutex
class RecMutexGlib : public hz::noncopyable {
public:
typedef GStaticRecMutex native_type;
static void native_lock(native_type& mutex)
{
g_static_rec_mutex_lock(&mutex);
}
static bool native_trylock(native_type& mutex)
{
return g_static_rec_mutex_trylock(&mutex);
}
static void native_unlock(native_type& mutex)
{
g_static_rec_mutex_unlock(&mutex);
}
RecMutexGlib() { g_static_rec_mutex_init(&mutex_); }
~RecMutexGlib() { g_static_rec_mutex_free(&mutex_); }
void lock() { native_lock(mutex_); }
bool trylock() { return native_trylock(mutex_); }
void unlock() { native_unlock(mutex_); }
private:
GStaticRecMutex mutex_;
};
/// C++ Wrapper for Glib RW lock
class RWMutexGlib : public hz::noncopyable {
public:
typedef GStaticRWLock native_type;
static void native_lock(native_type& mutex, bool for_write = false)
{
if (for_write) {
g_static_rw_lock_writer_lock(&mutex);
} else {
g_static_rw_lock_reader_lock(&mutex);
static void native_lock(native_type& mutex)
{
g_mutex_lock(&mutex);
}
}
static bool native_trylock(native_type& mutex, bool for_write = false)
{
return (for_write ? g_static_rw_lock_writer_trylock(&mutex) : g_static_rw_lock_reader_trylock(&mutex));
}
static void native_unlock(native_type& mutex, bool for_write = false)
{
if (for_write) {
g_static_rw_lock_writer_unlock(&mutex);
} else {
g_static_rw_lock_reader_unlock(&mutex);
static bool native_trylock(native_type& mutex)
{
return g_mutex_trylock(&mutex);
}
}
RWMutexGlib() { g_static_rw_lock_init(&mutex_); }
~RWMutexGlib() { g_static_rw_lock_free(&mutex_); }
static void native_unlock(native_type& mutex)
{
g_mutex_unlock(&mutex);
}
void lock(bool for_write = false) { native_lock(mutex_, for_write); }
bool trylock(bool for_write = false) { return native_trylock(mutex_, for_write); }
void unlock(bool for_write = false) { native_unlock(mutex_, for_write); }
MutexGlib() { g_mutex_init(&mutex_); }
~MutexGlib() { g_mutex_clear(&mutex_); }
private:
GStaticRWLock mutex_;
};
void lock() { native_lock(mutex_); }
bool trylock() { return native_trylock(mutex_); }
void unlock() { native_unlock(mutex_); }
private:
GMutex mutex_;
};
/// C++ Wrapper for Glib recursive mutex
class RecMutexGlib : public hz::noncopyable {
public:
typedef GRecMutex native_type;
static void native_lock(native_type& mutex)
{
g_rec_mutex_lock(&mutex);
}
static bool native_trylock(native_type& mutex)
{
return g_rec_mutex_trylock(&mutex);
}
static void native_unlock(native_type& mutex)
{
g_rec_mutex_unlock(&mutex);
}
RecMutexGlib() { g_rec_mutex_init(&mutex_); }
~RecMutexGlib() { g_rec_mutex_clear(&mutex_); }
void lock() { native_lock(mutex_); }
bool trylock() { return native_trylock(mutex_); }
void unlock() { native_unlock(mutex_); }
private:
GRecMutex mutex_;
};
/// C++ Wrapper for Glib RW lock
class RWMutexGlib : public hz::noncopyable {
public:
typedef GRWLock native_type;
static void native_lock(native_type& mutex, bool for_write = false)
{
if (for_write) {
g_rw_lock_writer_lock(&mutex);
} else {
g_rw_lock_reader_lock(&mutex);
}
}
static bool native_trylock(native_type& mutex, bool for_write = false)
{
return (for_write ? g_rw_lock_writer_trylock(&mutex) : g_rw_lock_reader_trylock(&mutex));
}
static void native_unlock(native_type& mutex, bool for_write = false)
{
if (for_write) {
g_rw_lock_writer_unlock(&mutex);
} else {
g_rw_lock_reader_unlock(&mutex);
}
}
RWMutexGlib() { g_rw_lock_init(&mutex_); }
~RWMutexGlib() { g_rw_lock_clear(&mutex_); }
void lock(bool for_write = false) { native_lock(mutex_, for_write); }
bool trylock(bool for_write = false) { return native_trylock(mutex_, for_write); }
void unlock(bool for_write = false) { native_unlock(mutex_, for_write); }
private:
GRWLock mutex_;
};
#else // older glib
/// Lock GStaticMutex
#define hz_glib_static_mutex_lock(mutex) \
g_mutex_lock(g_static_mutex_get_mutex(mutex))
/// Try locking GStaticMutex
#define hz_glib_static_mutex_trylock(mutex) \
g_mutex_trylock(g_static_mutex_get_mutex(mutex))
/// Unlock GStaticMutex
#define hz_glib_static_mutex_unlock(mutex) \
g_mutex_unlock(g_static_mutex_get_mutex(mutex))
/// C++ Wrapper for Glib mutex
class MutexGlib : public hz::noncopyable {
public:
typedef GStaticMutex native_type;
static void native_lock(native_type& mutex)
{
hz_glib_static_mutex_lock(&mutex);
}
static bool native_trylock(native_type& mutex)
{
return hz_glib_static_mutex_trylock(&mutex);
}
static void native_unlock(native_type& mutex)
{
hz_glib_static_mutex_unlock(&mutex);
}
MutexGlib() { g_static_mutex_init(&mutex_); }
~MutexGlib() { g_static_mutex_free(&mutex_); }
void lock() { native_lock(mutex_); }
bool trylock() { return native_trylock(mutex_); }
void unlock() { native_unlock(mutex_); }
private:
GStaticMutex mutex_; // use StaticMutex, I think it uses less heap memory
};
/// C++ Wrapper for Glib recursive mutex
class RecMutexGlib : public hz::noncopyable {
public:
typedef GStaticRecMutex native_type;
static void native_lock(native_type& mutex)
{
g_static_rec_mutex_lock(&mutex);
}
static bool native_trylock(native_type& mutex)
{
return g_static_rec_mutex_trylock(&mutex);
}
static void native_unlock(native_type& mutex)
{
g_static_rec_mutex_unlock(&mutex);
}
RecMutexGlib() { g_static_rec_mutex_init(&mutex_); }
~RecMutexGlib() { g_static_rec_mutex_free(&mutex_); }
void lock() { native_lock(mutex_); }
bool trylock() { return native_trylock(mutex_); }
void unlock() { native_unlock(mutex_); }
private:
GStaticRecMutex mutex_;
};
/// C++ Wrapper for Glib RW lock
class RWMutexGlib : public hz::noncopyable {
public:
typedef GStaticRWLock native_type;
static void native_lock(native_type& mutex, bool for_write = false)
{
if (for_write) {
g_static_rw_lock_writer_lock(&mutex);
} else {
g_static_rw_lock_reader_lock(&mutex);
}
}
static bool native_trylock(native_type& mutex, bool for_write = false)
{
return (for_write ? g_static_rw_lock_writer_trylock(&mutex) : g_static_rw_lock_reader_trylock(&mutex));
}
static void native_unlock(native_type& mutex, bool for_write = false)
{
if (for_write) {
g_static_rw_lock_writer_unlock(&mutex);
} else {
g_static_rw_lock_reader_unlock(&mutex);
}
}
RWMutexGlib() { g_static_rw_lock_init(&mutex_); }
~RWMutexGlib() { g_static_rw_lock_free(&mutex_); }
void lock(bool for_write = false) { native_lock(mutex_, for_write); }
bool trylock(bool for_write = false) { return native_trylock(mutex_, for_write); }
void unlock(bool for_write = false) { native_unlock(mutex_, for_write); }
private:
GStaticRWLock mutex_;
};
#endif
@@ -198,7 +313,12 @@ struct SyncPolicyGlib : public SyncScopedLockProvider<SyncPolicyGlib> {
// Static methods
/// If glib threads are unavailable, this will abort.
#if GLIB_CHECK_VERSION(2, 32, 0)
static bool init() { return true; } // g_thread_init() does nothing and is deprecated since 2.32.
#else
static bool init() { if (!g_thread_supported()) g_thread_init(NULL); return true; }
#endif
static void lock(Mutex& m) { m.lock(); }
static bool trylock(Mutex& m) { return m.trylock(); }
+2 -2
View File
@@ -3,8 +3,8 @@
<requires-version lib="gtk+" version="2.8"/>
<widget class="GtkWindow" id="gsc_main_window">
<property name="title" translatable="yes">GSmartControl</property>
<property name="default_width">460</property>
<property name="default_height">350</property>
<property name="default_width">580</property>
<property name="default_height">400</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>