diff --git a/gsmartcontrol/TODO b/gsmartcontrol/TODO index da93e67..45a4824 100644 --- a/gsmartcontrol/TODO +++ b/gsmartcontrol/TODO @@ -120,22 +120,6 @@ RAID: is older than "V1.46 2009-01-06", notify the user (maybe its better to grep the smartctl output for that on port 0?). - 3ware Linux: - Call as: smartctl -i -d 3ware,[0-127] /dev/twa[0-15] (or twe[0-15]) - Use twe* for [678]xxx series, and twa* for 9xxx series. - Note: twe* devices are limited to [0-15] ports (not sure about this). - Note: /dev/tw* devices may not exist, they are created by smartctl on the first run. - Note: for twe*, /dev/sda may also exist (to be used with -d 3ware,N), we should - somehow detect and ignore them. - Note: when specifying non-existent port, either a "Device Read Identity Failed" - error, or a "blank" info may be returned. - Detection: - Grep /proc/devices for "twa" or "twe" (e.g. "251 twa"). Use this for /dev/tw* part. - Grep /proc/scsi/scsi for AMCC (3ware, LSI too?), use number of matched lines N - for /dev/tw*[0, N-1]. - For detecting the number of ports, use "tw_cli /c0 show all", 0 being the controller N. - If there's no tw_cli, we'll have to scan all 127 ports. - 3ware FreeBSD: Call as: smartctl -i -d 3ware,[0-127] /dev/twa0 (or twe0) Detection: unknown. diff --git a/gsmartcontrol/src/applib/Makefile.am b/gsmartcontrol/src/applib/Makefile.am index 9560f7a..b3544a4 100644 --- a/gsmartcontrol/src/applib/Makefile.am +++ b/gsmartcontrol/src/applib/Makefile.am @@ -2,16 +2,16 @@ AM_CPPFLAGS = $(all_includes) METASOURCES = AUTO noinst_LIBRARIES = libapplib.a libapplib_a_SOURCES = app_gtkmm_utils.cpp app_pango_utils.cpp cmdex.cpp \ - cmdex_sync.cpp cmdex_sync_gui.cpp gui_utils.cpp selftest.cpp smartctl_executor.cpp \ + cmdex_sync.cpp cmdex_sync_gui.cpp executor_factory.cpp gui_utils.cpp selftest.cpp smartctl_executor.cpp \ smartctl_parser.cpp storage_detector.cpp storage_detector_linux.cpp storage_detector_other.cpp \ storage_detector_win32.cpp storage_device.cpp storage_property.cpp \ storage_property_descr.cpp noinst_HEADERS = app_gtkmm_features.h app_gtkmm_utils.h app_pango_utils.h \ - app_pcrecpp.h app_ui_res_utils.h cmdex.h cmdex_sync.h cmdex_sync_gui.h gui_utils.h \ + app_pcrecpp.h app_ui_res_utils.h cmdex.h cmdex_sync.h cmdex_sync_gui.h executor_factory.h gui_utils.h \ selftest.h smartctl_executor.h smartctl_executor_gui.h smartctl_parser.h \ storage_detector.h storage_detector_linux.h storage_detector_other.h \ storage_detector_win32.h storage_device.h storage_property.h storage_property_colors.h \ - storage_property_descr.h storage_settings.h wrapping_label.h + storage_property_descr.h storage_settings.h tw_cli_executor.h wrapping_label.h # don't use absolute path for the current dir's .a, because the make # dependency resolver won't get it (needed for parallel builds) diff --git a/gsmartcontrol/src/applib/cmdex_sync.h b/gsmartcontrol/src/applib/cmdex_sync.h index a6fd425..ec7c0b0 100644 --- a/gsmartcontrol/src/applib/cmdex_sync.h +++ b/gsmartcontrol/src/applib/cmdex_sync.h @@ -202,6 +202,7 @@ class CmdexSync : public hz::intrusive_ptr_referenced, public sigc::trackable { return error_msg_; } + /// Set a message to display when running. %s in \c msg will be replaced by the command. void set_running_msg(const std::string& msg) { running_msg_ = msg; diff --git a/gsmartcontrol/src/applib/executor_factory.cpp b/gsmartcontrol/src/applib/executor_factory.cpp new file mode 100644 index 0000000..5acdab1 --- /dev/null +++ b/gsmartcontrol/src/applib/executor_factory.cpp @@ -0,0 +1,51 @@ +/************************************************************************** + Copyright: + (C) 2011 Alexander Shaduri + License: See LICENSE_gsmartcontrol.txt +***************************************************************************/ + +#include "hz/debug.h" +#include "executor_factory.h" +#include "smartctl_executor_gui.h" +#include "tw_cli_executor.h" + + + + +ExecutorFactory::ExecutorFactory(bool use_gui, Gtk::Window* parent) + : use_gui_(use_gui), parent_(parent) +{ } + + + +hz::intrusive_ptr ExecutorFactory::create_executor(ExecutorFactory::Type type) +{ + switch (type) { + case ExecutorSmartctl: + { + if (use_gui_) { + SmartctlExecutorGuiRefPtr ex = SmartctlExecutorGuiRefPtr(new SmartctlExecutorGui()); + ex->create_running_dialog(parent_); // dialog parent + return ex; + } + return SmartctlExecutorRefPtr(new SmartctlExecutor()); + } + case ExecutorTwCli: + { + if (use_gui_) { + TwCliExecutorGuiRefPtr ex = TwCliExecutorGuiRefPtr(new TwCliExecutorGui()); + ex->create_running_dialog(parent_); // dialog parent + return ex; + } + return TwCliExecutorRefPtr(new TwCliExecutor()); + } + } + + DBG_ASSERT(0); + return hz::intrusive_ptr(); +} + + + + + diff --git a/gsmartcontrol/src/applib/executor_factory.h b/gsmartcontrol/src/applib/executor_factory.h new file mode 100644 index 0000000..3ba3f6c --- /dev/null +++ b/gsmartcontrol/src/applib/executor_factory.h @@ -0,0 +1,55 @@ +/************************************************************************** + Copyright: + (C) 2008 - 2011 Alexander Shaduri + License: See LICENSE_gsmartcontrol.txt +***************************************************************************/ + +#ifndef EXECUTOR_FACTORY_H +#define EXECUTOR_FACTORY_H + +#include "cmdex_sync.h" +#include "hz/intrusive_ptr.h" + + +// Forward declaration +namespace Gtk { + class Window; +} + + + +/// This class allows you to create new executors for different commands, +/// without carrying the GUI/CLI stuff manually. +class ExecutorFactory : public hz::intrusive_ptr_referenced { + public: + + /// Executor type for create_executor() + enum Type { + ExecutorSmartctl, + ExecutorTwCli + }; + + + /// Constructor. If \c use_gui is true, specify \c parent for the GUI dialogs. + ExecutorFactory(bool use_gui, Gtk::Window* parent = 0); + + + /// Create a new executor instance according to \c type and the constructor parameters. + hz::intrusive_ptr create_executor(Type type); + + + private: + + bool use_gui_; ///< Whether to construct GUI executors or not. + Gtk::Window* parent_; ///< Parent window for dialogs + +}; + + + +typedef hz::intrusive_ptr ExecutorFactoryRefPtr; + + + + +#endif diff --git a/gsmartcontrol/src/applib/selftest.cpp b/gsmartcontrol/src/applib/selftest.cpp index 5f3274c..c0d7829 100644 --- a/gsmartcontrol/src/applib/selftest.cpp +++ b/gsmartcontrol/src/applib/selftest.cpp @@ -103,7 +103,7 @@ std::string SelfTest::start(hz::intrusive_ptr smartctl_ex) return "Invalid test specified"; std::string output; - std::string error_msg = drive_->execute_smartctl("-t " + test_param, smartctl_ex, output); // --test= + std::string error_msg = drive_->execute_device_smartctl("-t " + test_param, smartctl_ex, output); // --test= if (!error_msg.empty()) // checks for empty output too return error_msg; @@ -163,7 +163,7 @@ std::string SelfTest::force_stop(hz::intrusive_ptr smartctl_ex) // To abort non-captive short, long and conveyance tests, use "--abort". std::string output; - std::string error_msg = drive_->execute_smartctl("-X", smartctl_ex, output); // --abort + std::string error_msg = drive_->execute_device_smartctl("-X", smartctl_ex, output); // --abort if (!error_msg.empty()) // checks for empty output too return error_msg; @@ -202,8 +202,8 @@ std::string SelfTest::update(hz::intrusive_ptr smartctl_ex) return "Invalid drive given."; std::string output; -// std::string error_msg = drive_->execute_smartctl("-l selftest", smartctl_ex, output); // --log= - std::string error_msg = drive_->execute_smartctl("-c", smartctl_ex, output); // --capabilities +// std::string error_msg = drive_->execute_device_smartctl("-l selftest", smartctl_ex, output); // --log= + std::string error_msg = drive_->execute_device_smartctl("-c", smartctl_ex, output); // --capabilities if (!error_msg.empty()) // checks for empty output too return error_msg; diff --git a/gsmartcontrol/src/applib/smartctl_executor.cpp b/gsmartcontrol/src/applib/smartctl_executor.cpp index ac93264..f6f4e30 100644 --- a/gsmartcontrol/src/applib/smartctl_executor.cpp +++ b/gsmartcontrol/src/applib/smartctl_executor.cpp @@ -4,11 +4,15 @@ License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ +#include // Glib::shell_quote() + #include "smartctl_executor.h" #include "hz/fs_path.h" #include "hz/win32_tools.h" #include "rconfig/rconfig_mini.h" +#include "app_pcrecpp.h" + @@ -62,3 +66,70 @@ std::string get_smartctl_binary() +std::string execute_smartctl(const std::string& device, const std::string& device_opts, + const std::string& command_options, + hz::intrusive_ptr smartctl_ex, std::string& smartctl_output) +{ +#ifndef _WIN32 // win32 doesn't have slashes in devices names + { + 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."; + } + } +#endif + + if (!smartctl_ex) // if it doesn't exist, create a default one + smartctl_ex = new SmartctlExecutor(); // will be auto-deleted + + std::string smartctl_binary = get_smartctl_binary(); + + 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."; + } + + std::string smartctl_def_options; + rconfig::get_data("system/smartctl_options", smartctl_def_options); + + if (!smartctl_def_options.empty()) + smartctl_def_options += " "; + + + std::string device_specific_options = device_opts; + if (!device_specific_options.empty()) + device_specific_options += " "; + + + smartctl_ex->set_command(Glib::shell_quote(smartctl_binary), + smartctl_def_options + device_specific_options + command_options + + " " + 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"); + + std::string output = smartctl_ex->get_stdout_str(); + + // 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", output)) { + return "Permission denied while opening device."; + } + + // smartctl_output = smartctl_ex->get_stdout_str(); + return 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 std::string(); +} + + + diff --git a/gsmartcontrol/src/applib/smartctl_executor.h b/gsmartcontrol/src/applib/smartctl_executor.h index 8f0a3fe..263584b 100644 --- a/gsmartcontrol/src/applib/smartctl_executor.h +++ b/gsmartcontrol/src/applib/smartctl_executor.h @@ -132,7 +132,7 @@ class SmartctlExecutorGeneric : public ExecutorSync { if ( !((exit_code & exit_cant_parse) || (exit_code & exit_open_failed)) ) return; - // ignore giochannel errors - higher level errors will be triggered, and they more user-friendly. + // ignore giochannel errors - higher level errors will be triggered, and they more user-friendly. } else if (error_type == "giochannel" || error_type == "custom") { return; } @@ -140,9 +140,6 @@ class SmartctlExecutorGeneric : public ExecutorSync { this->set_error_msg(e->get_message()); } - - - }; @@ -150,12 +147,20 @@ class SmartctlExecutorGeneric : public ExecutorSync { typedef SmartctlExecutorGeneric SmartctlExecutor; +typedef hz::intrusive_ptr SmartctlExecutorRefPtr; + // returns an empty string if not found. std::string get_smartctl_binary(); +std::string execute_smartctl(const std::string& device, const std::string& device_opts, + const std::string& command_options, + hz::intrusive_ptr smartctl_ex, std::string& smartctl_output); + + + #endif diff --git a/gsmartcontrol/src/applib/storage_detector.cpp b/gsmartcontrol/src/applib/storage_detector.cpp index 812a25d..f25504b 100644 --- a/gsmartcontrol/src/applib/storage_detector.cpp +++ b/gsmartcontrol/src/applib/storage_detector.cpp @@ -19,68 +19,37 @@ -std::string StorageDetector::detect(std::vector& drives) +std::string StorageDetector::detect(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Starting drive detection.\n"); - std::vector devices; + std::vector all_detected; std::string error_msg; - bool found = false; // Try each one and move to next if it fails. #if defined CONFIG_KERNEL_LINUX - // Disable by-id detection - it's unreliable on broken systems. - // For example, on Ubuntu 8.04, /dev/disk/by-id contains two device - // links for two drives, but both point to the same sdb (instead of - // sda and sdb). Plus, there are no "*-partN" files (not that we need them). -/* - if (!found) { - error_msg = detect_drives_linux_udev_byid(devices); // linux udev - // we check for devices vector emptiness because it could be a dummy directory - // with no files, so treat it as an error. - if (error_msg.empty() && !devices.empty()) { - found = true; - } - } -*/ - if (!found) { - error_msg = detect_drives_linux_proc_partitions(devices); // linux /proc/partitions as fallback. - if (error_msg.empty() && !devices.empty()) { - found = true; - } - } + error_msg = detect_drives_linux(all_detected, ex_factory); // linux /proc/partitions as fallback. #elif defined CONFIG_KERNEL_FAMILY_WINDOWS - if (!found) { - error_msg = detect_drives_win32(devices); // win32 - if (error_msg.empty() && !devices.empty()) { - found = true; - } - } - + error_msg = detect_drives_win32(all_detected, ex_factory); // win32 #else // freebsd, etc... - if (!found) { - error_msg = detect_drives_other(devices); // bsd, etc... . scans /dev. - if (error_msg.empty() && !devices.empty()) { - found = true; - } - } + error_msg = detect_drives_other(all_detected, ex_factory); // bsd, etc... . scans /dev. #endif - if (!found) { + 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_msg; // last error message should be ok. } - for (std::vector::const_iterator iter = devices.begin(); iter != devices.end(); ++iter) { - std::string dev = *iter; + for (std::vector::iterator iter = all_detected.begin(); iter != all_detected.end(); ++iter) { + StorageDeviceRefPtr drive = *iter; // try to match against patterns // for (unsigned int i = 0; i < match_patterns_.size(); i++) { @@ -91,13 +60,12 @@ std::string StorageDetector::detect(std::vector& drives) // matched, check the blacklist bool blacked = false; for (unsigned int j = 0; j < blacklist_patterns_.size(); j++) { - if (app_pcre_match(blacklist_patterns_[j], dev)) { // matched the blacklist too + if (app_pcre_match(blacklist_patterns_[j], drive->get_device())) { // matched the blacklist too blacked = true; break; } } - StorageDeviceRefPtr drive(new StorageDevice(dev)); debug_out_info("app", "Found device: \"" << drive->get_device() << "\".\n"); if (!blacked) { @@ -119,16 +87,12 @@ std::string StorageDetector::detect(std::vector& drives) std::string StorageDetector::fetch_basic_data(std::vector& drives, - hz::intrusive_ptr smartctl_ex, bool return_first_error) + ExecutorFactoryRefPtr ex_factory, bool return_first_error) { fetch_data_errors_.clear(); fetch_data_error_outputs_.clear(); - - // If it doesn't exist, create a default one. Even though it will be auto-created later, - // we need it here to get its errors afterwards. - if (!smartctl_ex) - smartctl_ex = new SmartctlExecutor(); // will be auto-deleted + hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); for (unsigned int i = 0; i < drives.size(); ++i) { StorageDeviceRefPtr drive = drives[i]; @@ -168,12 +132,12 @@ std::string StorageDetector::fetch_basic_data(std::vector& std::string StorageDetector::detect_and_fetch_basic_data(std::vector& put_drives_here, - hz::intrusive_ptr smartctl_ex) + ExecutorFactoryRefPtr ex_factory) { - std::string error_msg = detect(put_drives_here); + std::string error_msg = detect(put_drives_here, ex_factory); if (error_msg.empty()) - fetch_basic_data(put_drives_here, smartctl_ex, false); // ignore its errors, there may be plenty of them. + fetch_basic_data(put_drives_here, ex_factory, false); // ignore its errors, there may be plenty of them. return error_msg; } diff --git a/gsmartcontrol/src/applib/storage_detector.h b/gsmartcontrol/src/applib/storage_detector.h index 971ef37..2359168 100644 --- a/gsmartcontrol/src/applib/storage_detector.h +++ b/gsmartcontrol/src/applib/storage_detector.h @@ -14,6 +14,7 @@ #include "storage_device.h" #include "cmdex_sync.h" +#include "executor_factory.h" @@ -28,17 +29,18 @@ class StorageDetector { // detects a list of drives. returns detection error if error occurs. - std::string detect(std::vector& put_drives_here); + std::string detect(std::vector& put_drives_here, + ExecutorFactoryRefPtr ex_factory); // fetch basic data of "drives" elements std::string fetch_basic_data(std::vector& drives, - hz::intrusive_ptr smartctl_ex = 0, bool return_first_error = false); + ExecutorFactoryRefPtr ex_factory, bool return_first_error = false); // do both of the above, return detection error. std::string detect_and_fetch_basic_data(std::vector& put_drives_here, - hz::intrusive_ptr smartctl_ex = 0); + ExecutorFactoryRefPtr ex_factory); // void add_match_patterns(std::vector& patterns) diff --git a/gsmartcontrol/src/applib/storage_detector_linux.cpp b/gsmartcontrol/src/applib/storage_detector_linux.cpp index c384355..2d8b940 100644 --- a/gsmartcontrol/src/applib/storage_detector_linux.cpp +++ b/gsmartcontrol/src/applib/storage_detector_linux.cpp @@ -12,6 +12,7 @@ #include // std::find #include // std::fgets(), std::FILE #include // ENXIO +#include #include "hz/debug.h" #include "hz/fs_path_utils.h" @@ -22,6 +23,8 @@ +namespace { + // Linux 2.6 with udev. Scan /dev/disk/by-id - the directory entries there // are symlinks to respective /dev devices. Some devices have multiple @@ -44,7 +47,7 @@ scsi-SATA_ST3500630AS_9QG0R38D-part1 /* // We don't use udev anymore - not all distros have it, and e.g. Ubuntu // has it all wrong (two symlinks (sda, sdb) pointing both to sdb). -std::string detect_drives_linux_udev_byid(std::vector& devices) +inline std::string detect_drives_linux_udev_byid(std::vector& devices) { debug_out_info("app", DBG_FUNC_MSG << "Detecting through device scan directory /dev/disk/by-id...\n"); @@ -113,7 +116,7 @@ std::string detect_drives_linux_udev_byid(std::vector& devices) // Procfs files don't support SEEK_END or ftello() (I think). Anyway, they can't // be read through hz::File::get_contents, so use this function instead. -inline bool read_proc_partitions_file(hz::File& file, std::vector& lines) +inline bool read_proc_file(hz::File& file, std::vector& lines) { if (!file.open("rb")) // closed automatically return false; // the error message is in File itself. @@ -133,9 +136,180 @@ inline bool read_proc_partitions_file(hz::File& file, std::vector& +// Read /proc/partitions file. Return error message on error. +inline std::string read_proc_partitions_file(std::vector& lines) +{ + std::string path; + if (!rconfig::get_data("system/linux_proc_partitions_path", path) || path.empty()) { + debug_out_warn("app", DBG_FUNC_MSG << "Partitions file path is not set.\n"); + return "Partitions file path is not set."; + } + + hz::File file(path); + if (!read_proc_file(file, lines)) { // this outputs to debug too + std::string error_msg = file.get_error_utf8(); // save before calling other file functions + if (!file.exists()) { + debug_out_warn("app", DBG_FUNC_MSG << "Partitions file doesn't exist.\n"); + } else { + debug_out_error("app", DBG_FUNC_MSG << "Partitions file exists but cannot be read.\n"); + } + return error_msg; + } + + return std::string(); +} + + + +// Read /proc/partitions file. Return error message on error. +inline std::string read_proc_devices_file(std::vector& lines) +{ + std::string path; + if (!rconfig::get_data("system/linux_proc_devices_path", path) || path.empty()) { + debug_out_warn("app", DBG_FUNC_MSG << "Devices file path is not set.\n"); + return "Devices file path is not set."; + } + + hz::File file(path); + if (!read_proc_file(file, lines)) { // this outputs to debug too + std::string error_msg = file.get_error_utf8(); // save before calling other file functions + if (!file.exists()) { + debug_out_warn("app", DBG_FUNC_MSG << "Devices file doesn't exist.\n"); + } else { + debug_out_error("app", DBG_FUNC_MSG << "Devices file exists but cannot be read.\n"); + } + return error_msg; + } + + return std::string(); +} + + + +// Read /proc/partitions file. Return error message on error. +inline std::string read_proc_scsi_scsi_file(std::vector& lines) +{ + std::string path; + if (!rconfig::get_data("system/linux_proc_scsi_scsi_path", path) || path.empty()) { + debug_out_warn("app", DBG_FUNC_MSG << "SCSI file path is not set.\n"); + return "SCSI file path is not set."; + } + + hz::File file(path); + if (!read_proc_file(file, lines)) { // this outputs to debug too + std::string error_msg = file.get_error_utf8(); // save before calling other file functions + if (!file.exists()) { + debug_out_warn("app", DBG_FUNC_MSG << "SCSI file doesn't exist.\n"); + } else { + debug_out_error("app", DBG_FUNC_MSG << "SCSI file exists but cannot be read.\n"); + } + return error_msg; + } + + return std::string(); +} + + + +/// Get number of ports using tw_cli. Return -1 on error. +inline std::string tw_cli_get_drives(const std::string& dev, int controller_no, + std::vector drives, ExecutorFactoryRefPtr ex_factory) +{ + hz::intrusive_ptr executor = ex_factory->create_executor(ExecutorFactory::ExecutorTwCli); + + std::string binary; + rconfig::get_data("system/tw_cli_binary", binary); + + if (binary.empty()) { + debug_out_error("app", DBG_FUNC_MSG << "tw_cli binary is not set in config.\n"); + return "tw_cli binary is not specified in configuration."; + } + + std::string command_options = hz::string_sprintf("/c%d show all", controller_no); + + std::vector binaries; // binaries to try + // Note: tw_cli is automatically added to PATH in windows, no need to look for it. + binaries.push_back(binary); +#ifdef CONFIG_KERNEL_LINUX + // tw_cli may be named tw_cli.x86 or tw_cli.x86_64 in linux + binaries.push_back(binary + ".x86"); + binaries.push_back(binary + ".x86_64"); +#endif + + for (std::size_t i = 0; i < binaries.size(); ++i) { + executor->set_command(Glib::shell_quote(binaries.at(i)), command_options); + + if (!executor->execute() || !executor->get_error_msg().empty()) { + debug_out_warn("app", DBG_FUNC_MSG << "Error while executing tw_cli binary.\n"); + } else { + break; // found it + } + } + + // any_to_unix is needed for windows + std::string 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."; + } + + // split to lines + std::vector lines; + hz::string_split(output, '\n', lines, true); + + pcrecpp::RE port_re = app_pcre_re("/^p([0-9])+[ \\t]+([^\\t\\n]+)/mi"); + for (std::size_t i = 0; i < lines.size(); ++i) { + std::string port_str, status; + if (port_re.PartialMatch(lines.at(i), &port_str, &status)) { + if (status != "NOT-PRESENT") { + int port = -1; + hz::string_is_numeric(port_str, port); + if (port != -1) { + drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, "3ware," + hz::number_to_string(port)))); + } + } + } + } + + return std::string(); +} + + + +/// Get number of ports by sequentially running smartctl on each port, until +/// one of the gives an error. Return -1 on error. +inline std::string smartctl_get_drives(const std::string& dev, const std::string& type, + int from, int to, std::vector drives, ExecutorFactoryRefPtr ex_factory) +{ + hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); + + for (int i = from; i <= to; ++i) { + std::string type_arg = hz::string_sprintf(type.c_str(), i); + + std::string output; + std::string error_msg = execute_smartctl(dev, "-d " + type_arg, "-i", smartctl_ex, output); + + // if we've reached smartctl port limit (older versions may have smaller limits), abort. + if (app_pcre_match("/VALID ARGUMENTS ARE/mi", output)) { + break; + } + + if (!error_msg.empty()) { + debug_out_info("app", "Sequential port scan aborted with error: " << error_msg); + } else { + drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, type_arg))); + } + } + + return std::string(); +} + + + + // Linux (tested with 2.4 and 2.6) /proc/partitions. Parses the file, appends /dev to each entry. // Note that file format changed from 2.4 to 2.6 (some statistics fields moved to another file). -// No /proc/partitions on at least freebsd, solaris and osx, afaik. +// No /proc/partitions on freebsd, solaris or osx, afaik. /* Sample 1 (2.4, devfs, with statistics): ------------------------------------------------------------ @@ -175,34 +349,16 @@ major minor #blocks name 254 8 1966080 mmcblk1 254 9 2007032 mmcblk1p1 */ -std::string detect_drives_linux_proc_partitions(std::vector& devices) +inline std::string detect_drives_linux_proc_partitions(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting through /proc/partitions...\n"); - std::string parts_file; - if (!rconfig::get_data("system/linux_proc_partitions_path", parts_file) || parts_file.empty()) { - debug_out_warn("app", DBG_FUNC_MSG << "Partitions file path is not set.\n"); - return "Partitions file path is not set."; - } - - hz::File file(parts_file); -// std::string contents; std::vector lines; -// if (!f.get_contents(contents)) { - if (!read_proc_partitions_file(file, lines)) { // this outputs to debug too - std::string error_msg = file.get_error_utf8(); // save before calling other file functions - if (!file.exists()) { - debug_out_warn("app", DBG_FUNC_MSG << "Partitions file doesn't exist.\n"); - } else { - debug_out_error("app", DBG_FUNC_MSG << "Partitions file exists but cannot be read.\n"); - } + std::string error_msg = read_proc_partitions_file(lines); + if (!error_msg.empty()) { return error_msg; } -// debug_out_dump("app", DBG_FUNC_MSG << "Dumping partitions file:\n" << contents << "\n"); - -// hz::string_split(contents, '\n', lines, true); - std::vector blacklist; // fixme: not sure about how partitions are visible with twa0. blacklist.push_back("/d[a-z][0-9]+$/"); // sda1, hdb2 - partitions. twa0 and twe1 are drives, not partitions. @@ -213,8 +369,9 @@ std::string detect_drives_linux_proc_partitions(std::vector& device blacklist.push_back("/md[0-9]*$/"); // linux software raid blacklist.push_back("/dm-[0-9]*$/"); // linux device mapper + std::vector devices; - for (unsigned int i = 0; i < lines.size(); ++i) { + for (std::size_t i = 0; i < lines.size(); ++i) { std::string line = hz::string_trim_copy(lines[i]); if (line.empty() || app_pcre_match("/^major/", line)) // file header continue; @@ -241,10 +398,136 @@ std::string detect_drives_linux_proc_partitions(std::vector& device devices.push_back(path); } + for (std::size_t i = 0; i < devices.size(); ++i) { + drives.push_back(StorageDeviceRefPtr(new StorageDevice(devices.at(i)))); + } + return std::string(); } +/** +Detect drives behind 3ware RAID controller. + +3ware Linux: +Call as: smartctl -i -d 3ware,[0-127] /dev/twa[0-15] (or twe[0-15]) +Use twe* for [678]xxx series, and twa* for 9xxx series. +Note: twe* devices are limited to [0-15] ports (not sure about this). +Note: /dev/tw* devices may not exist, they are created by smartctl on the first run. +Note: for twe*, /dev/sda may also exist (to be used with -d 3ware,N), we should + somehow detect and ignore them. +Note: when specifying non-existent port, either a "Device Read Identity Failed" + error, or a "blank" info may be returned. +Detection: +Grep /proc/devices for "twa" or "twe" (e.g. "251 twa"). Use this for /dev/tw* part. +Grep /proc/scsi/scsi for AMCC or 3ware (LSI too?), use number of matched lines N + for /dev/tw*[0, N-1]. +For detecting the number of ports, use "tw_cli /c0 show all", 0 being the controller N. + If there's no tw_cli, we'll have to scan all 128 ports. + +Implementation notes: it seems that twe uses "3ware" and twa uses "AMCC". +We can't handle a situation with both twa and twe present, since we don't know +how they will be ordered for tw_cli. +*/ +inline std::string detect_drives_linux_3ware(std::vector& drives, ExecutorFactoryRefPtr ex_factory) +{ + std::vector lines; + std::string error_msg = read_proc_devices_file(lines); + if (!error_msg.empty()) { + return error_msg; + } + + bool twa_found = false; + bool twe_found = false; + + // Check /proc/devices for twa or twe + for (std::size_t i = 0; i < lines.size(); ++i) { + std::string dev; + if (app_pcre_match("/^[ \\t]*[0-9]+[ \\t]+(tw[ae])/", hz::string_trim_copy(lines[i]), &dev)) { + debug_out_dump("app", DBG_FUNC_MSG << "Found " << dev << " entry in devices file.\n"); + if (dev == "twa") { + twa_found = true; + } else if (dev == "twe") { + twe_found = true; + } else { + DBG_ASSERT(0); // error in regexp? + } + } + } + + if (!twa_found && !twe_found) { + return std::string(); // no controllers + } + + // Count number of AMCC / 3ware entries in /proc/scsi/scsi + int num_controllers = 0; + + lines.clear(); + error_msg = read_proc_scsi_scsi_file(lines); + if (!error_msg.empty()) { + return error_msg; + } + + for (std::size_t i = 0; i < lines.size(); ++i) { + if (app_pcre_match("/ (AMCC)|(3ware) /i", hz::string_trim_copy(lines[i]))) { + ++num_controllers; + } + } + if (num_controllers == 0) { + debug_out_warn("app", DBG_FUNC_MSG << "3ware entry found in devices file, but SCSI file contains no known entries.\n"); + return std::string(); + } + + for (int i = 0; i < num_controllers; ++i) { + // we can't handle both twa and twe in one system, so assume twa by default + std::string dev = std::string("/dev/") + (twa_found ? "twa" : "twe") + hz::number_to_string(i); + + error_msg = tw_cli_get_drives(dev, i, drives, ex_factory); + if (!error_msg.empty()) { // no tw_cli + error_msg = smartctl_get_drives(dev, "3ware,%d", 0, (twa_found ? 127 : 15), drives, ex_factory); + } + + if (!error_msg.empty()) { + debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get number of ports on a 3ware controller.\n"); + } + } + + return error_msg; +} + + +} // anon ns + + + + +std::string detect_drives_linux(std::vector& drives, ExecutorFactoryRefPtr ex_factory) +{ + std::vector error_msgs; + std::string error_msg; + + // Disable by-id detection - it's unreliable on broken systems. + // For example, on Ubuntu 8.04, /dev/disk/by-id contains two device + // links for two drives, but both point to the same sdb (instead of + // sda and sdb). Plus, there are no "*-partN" files (not that we need them). +// error_msg = 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); + } + + error_msg = detect_drives_linux_3ware(drives, ex_factory); + if (!error_msg.empty()) { + error_msgs.push_back(error_msg); + } + + return hz::string_join(error_msgs, "\n"); +} + + + + #endif // CONFIG_KERNEL_LINUX diff --git a/gsmartcontrol/src/applib/storage_detector_linux.h b/gsmartcontrol/src/applib/storage_detector_linux.h index c834939..e2cc2fd 100644 --- a/gsmartcontrol/src/applib/storage_detector_linux.h +++ b/gsmartcontrol/src/applib/storage_detector_linux.h @@ -11,17 +11,17 @@ #include #include "hz/hz_config.h" // CONFIG_* +#include "executor_factory.h" +#include "storage_device.h" #if defined CONFIG_KERNEL_LINUX -/// Detect drives in linux using udev -// std::string detect_drives_linux_udev_byid(std::vector& devices); +/// Detect drives in linux +std::string detect_drives_linux(std::vector& drives, ExecutorFactoryRefPtr ex_factory); -/// Detect drives in linux using /proc/partitions -std::string detect_drives_linux_proc_partitions(std::vector& devices); #endif diff --git a/gsmartcontrol/src/applib/storage_detector_other.cpp b/gsmartcontrol/src/applib/storage_detector_other.cpp index 5564675..7ff367c 100644 --- a/gsmartcontrol/src/applib/storage_detector_other.cpp +++ b/gsmartcontrol/src/applib/storage_detector_other.cpp @@ -24,10 +24,12 @@ -std::string detect_drives_other(std::vector& devices) +std::string detect_drives_other(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { debug_out_info("app", DBG_FUNC_MSG << "Detecting through /dev...\n"); + std::vector devices; + std::string sdev_config_path; #if defined CONFIG_KERNEL_SOLARIS sdev_config_path = "system/solaris_dev_path"; @@ -253,6 +255,10 @@ std::string detect_drives_other(std::vector& devices) hz::shell_sort(devices.begin(), devices.end()); + for (int i = 0; i < devices.size(); ++i) { + drives.push_back(StorageDeviceRefPtr(new StorageDevice(devices.at(i)))); + } + return std::string(); } diff --git a/gsmartcontrol/src/applib/storage_detector_other.h b/gsmartcontrol/src/applib/storage_detector_other.h index 57c7956..6a6b8c6 100644 --- a/gsmartcontrol/src/applib/storage_detector_other.h +++ b/gsmartcontrol/src/applib/storage_detector_other.h @@ -11,13 +11,15 @@ #include #include "hz/hz_config.h" // CONFIG_* +#include "executor_factory.h" +#include "storage_device.h" #if !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS // FreeBSD, Solaris, etc... . -std::string detect_drives_other(std::vector& devices); +std::string detect_drives_other(std::vector& drives, ExecutorFactoryRefPtr ex_factory); #endif diff --git a/gsmartcontrol/src/applib/storage_detector_test.cpp b/gsmartcontrol/src/applib/storage_detector_test.cpp index 2f51d00..04fa542 100644 --- a/gsmartcontrol/src/applib/storage_detector_test.cpp +++ b/gsmartcontrol/src/applib/storage_detector_test.cpp @@ -24,7 +24,8 @@ int main() // sd.add_match_patterns(match_patterns); sd.add_blacklist_patterns(blacklist_patterns); - std::string error_msg = sd.detect_and_fetch_basic_data(drives); + ExecutorFactoryRefPtr ex_factory(new ExecutorFactory(false)); + std::string error_msg = sd.detect_and_fetch_basic_data(drives, ex_factory); if (!error_msg.empty()) { std::cerr << error_msg << "\n"; diff --git a/gsmartcontrol/src/applib/storage_detector_win32.cpp b/gsmartcontrol/src/applib/storage_detector_win32.cpp index 3469b5f..57811b3 100644 --- a/gsmartcontrol/src/applib/storage_detector_win32.cpp +++ b/gsmartcontrol/src/applib/storage_detector_win32.cpp @@ -19,7 +19,7 @@ // "\\.\PhysicalDriveN" (winnt only). // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx -std::string detect_drives_win32(std::vector& devices) +std::string detect_drives_win32(std::vector& drives, ExecutorFactoryRefPtr ex_factory) { for (int drive_num = 0; ; ++drive_num) { std::string name = hz::string_sprintf("\\\\.\\PhysicalDrive%d", drive_num); @@ -37,7 +37,7 @@ std::string detect_drives_win32(std::vector& devices) CloseHandle(h); - devices.push_back(hz::string_sprintf("pd%d", drive_num)); + drives.push_back(new StorageDevice(hz::string_sprintf("pd%d", drive_num))); } return std::string(); diff --git a/gsmartcontrol/src/applib/storage_detector_win32.h b/gsmartcontrol/src/applib/storage_detector_win32.h index 3592e74..957a7f3 100644 --- a/gsmartcontrol/src/applib/storage_detector_win32.h +++ b/gsmartcontrol/src/applib/storage_detector_win32.h @@ -11,12 +11,14 @@ #include #include "hz/hz_config.h" // CONFIG_* +#include "executor_factory.h" +#include "storage_device.h" #if defined CONFIG_KERNEL_FAMILY_WINDOWS -std::string detect_drives_win32(std::vector& devices); +std::string detect_drives_win32(std::vector& drives, ExecutorFactoryRefPtr ex_factory); #endif diff --git a/gsmartcontrol/src/applib/storage_device.cpp b/gsmartcontrol/src/applib/storage_device.cpp index bb35888..216686f 100644 --- a/gsmartcontrol/src/applib/storage_device.cpp +++ b/gsmartcontrol/src/applib/storage_device.cpp @@ -4,8 +4,6 @@ License: See LICENSE_gsmartcontrol.txt ***************************************************************************/ -#include // Glib::shell_quote() - #include "rconfig/rconfig_mini.h" #include "hz/string_algo.h" // string_trim_copy, string_any_to_unix_copy #include "hz/fs_path.h" // FsPath @@ -55,18 +53,33 @@ std::string StorageDevice::get_status_name(StorageDevice::status_t status, bool StorageDevice::StorageDevice(const string& dev_or_vfile, bool is_virtual) { - detected_type_ = detected_type_unknown; - // force_type_ = false; - is_virtual_ = is_virtual; - is_manually_added_ = false; - fully_parsed_ = false; - test_is_active_ = false; + detected_type_ = detected_type_unknown; + // force_type_ = false; + is_virtual_ = is_virtual; + is_manually_added_ = false; + fully_parsed_ = false; + test_is_active_ = false; - if (is_virtual) { - virtual_file_ = dev_or_vfile; - } else { - device_ = dev_or_vfile; - } + if (is_virtual) { + virtual_file_ = dev_or_vfile; + } else { + device_ = dev_or_vfile; + } +} + + + +StorageDevice::StorageDevice(const string& dev, const string& type_arg) +{ + detected_type_ = detected_type_unknown; + // force_type_ = false; + is_virtual_ = false; + is_manually_added_ = false; + fully_parsed_ = false; + test_is_active_ = false; + + device_ = dev; + type_arg_ = type_arg; } @@ -144,7 +157,7 @@ std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptr sma if (this->get_type_argument() == "scsi") { // not sure about correctness... FIXME probably fails with RAID/scsi // This doesn't do much yet, but just in case... // SCSI equivalent of -a: - error_msg = execute_smartctl("-H -i -A -l error -l selftest", smartctl_ex, output); + error_msg = execute_device_smartctl("-H -i -A -l error -l selftest", smartctl_ex, output); } else { // ATA equivalent of -a: - error_msg = execute_smartctl("-H -i -c -A -l error -l selftest -l selective", + error_msg = execute_device_smartctl("-H -i -c -A -l error -l selftest -l selective", smartctl_ex, output, true); // set type to invalid if needed } // See notes above (in fetch_basic_data_and_parse()). @@ -367,7 +380,7 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm std::string output; // --smart=on --saveauto=on, --smart=off - std::string error_msg = execute_smartctl((b ? "-s on -S on" : "-s off"), smartctl_ex, output); + std::string error_msg = execute_device_smartctl((b ? "-s on -S on" : "-s off"), smartctl_ex, output); if (!error_msg.empty()) return error_msg; @@ -402,7 +415,7 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm */ std::string output; // --offlineauto=on, --offlineauto=off - std::string error_msg = execute_smartctl((b ? "-o on" : "-o off"), smartctl_ex, output); + std::string error_msg = execute_device_smartctl((b ? "-o on" : "-o off"), smartctl_ex, output); if (!error_msg.empty()) return error_msg; @@ -771,7 +784,7 @@ std::string StorageDevice::get_device_options() const -std::string StorageDevice::execute_smartctl(const std::string& command_options, +std::string StorageDevice::execute_device_smartctl(const std::string& command_options, hz::intrusive_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. @@ -783,69 +796,22 @@ std::string StorageDevice::execute_smartctl(const std::string& command_options, std::string device = get_device(); -#ifndef _WIN32 // win32 doesn't have slashes in devices names - { - 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."; - } - } -#endif + std::string error_msg = execute_smartctl(device, this->get_device_options(), + command_options, smartctl_ex, smartctl_output); - if (!smartctl_ex) // if it doesn't exist, create a default one - smartctl_ex = new SmartctlExecutor(); // will be auto-deleted - - std::string smartctl_binary = get_smartctl_binary(); - - 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."; - } - - std::string smartctl_def_options; - rconfig::get_data("system/smartctl_options", smartctl_def_options); - - if (!smartctl_def_options.empty()) - smartctl_def_options += " "; - - - std::string device_specific_options = this->get_device_options(); - if (!device_specific_options.empty()) - device_specific_options += " "; - - - smartctl_ex->set_command(Glib::shell_quote(smartctl_binary), - smartctl_def_options + device_specific_options + command_options + " " + Glib::shell_quote(device)); - - if (!smartctl_ex->execute() || !smartctl_ex->get_error_msg().empty()) { + if (!error_msg.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Error while executing smartctl binary.\n"); - std::string output = smartctl_ex->get_stdout_str(); - - // 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", output)) { - return "Permission denied while opening device."; - } - // 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() == detected_type_unknown - && app_pcre_match("/specify device type with the -d option/mi", output)) { + && app_pcre_match("/specify device type with the -d option/mi", smartctl_output)) { this->set_detected_type(detected_type_invalid); } - return 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 error_msg; } return std::string(); diff --git a/gsmartcontrol/src/applib/storage_device.h b/gsmartcontrol/src/applib/storage_device.h index fd989c1..09b2be7 100644 --- a/gsmartcontrol/src/applib/storage_device.h +++ b/gsmartcontrol/src/applib/storage_device.h @@ -55,6 +55,9 @@ class StorageDevice : public hz::intrusive_ptr_referenced { /// Constructor StorageDevice(const std::string& dev_or_vfile, bool is_virtual = false); + /// Constructor + StorageDevice(const std::string& dev, const std::string& type_arg); + /// Copy constructor StorageDevice(const StorageDevice& other); @@ -210,7 +213,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced { /// Execute smartctl on this device. Nothing is modified in this class. /// \return error message on error, empty string on success - std::string execute_smartctl(const std::string& command_options, + std::string execute_device_smartctl(const std::string& command_options, hz::intrusive_ptr smartctl_ex, std::string& output, bool check_type = false); diff --git a/gsmartcontrol/src/applib/tw_cli_executor.h b/gsmartcontrol/src/applib/tw_cli_executor.h new file mode 100644 index 0000000..d250a03 --- /dev/null +++ b/gsmartcontrol/src/applib/tw_cli_executor.h @@ -0,0 +1,123 @@ +/************************************************************************** + Copyright: + (C) 2011 Alexander Shaduri + License: See LICENSE_gsmartcontrol.txt +***************************************************************************/ + +#ifndef TW_CLI_EXECUTOR_H +#define TW_CLI_EXECUTOR_H + +#include "cmdex.h" +#include "cmdex_sync.h" + + + +/// Executor for tw_cli (3ware utility) +template +class TwCliExecutorGeneric : public ExecutorSync { + + public: + + TwCliExecutorGeneric(const std::string& cmd, const std::string& cmdargs) + : ExecutorSync(cmd, cmdargs) + { + this->construct(); + } + + + TwCliExecutorGeneric() + { + this->construct(); + } + + + virtual ~TwCliExecutorGeneric() + { } + + + + protected: + + void construct() + { + ExecutorSync::cmdex_.set_exit_status_translator(&TwCliExecutorGeneric::translate_exit_status, NULL); + this->set_error_header("An error occurred while executing tw_cli:\n\n"); + } + + + + static std::string translate_exit_status(int status, void* user_data) + { + return std::string(); + } + + + + // import the last error from cmdex_ and clear all errors there + virtual void import_error() + { + Cmdex& cmdex = this->get_command_executor(); + cmdex.errors_lock(); + + Cmdex::error_list_t errors = cmdex.get_errors(false); // these are not clones + + hz::ErrorBase* e = 0; + // find the last relevant error. + // note: const_reverse_iterator doesn't work on gcc 3, so don't do it. + for (Cmdex::error_list_t::reverse_iterator iter = errors.rbegin(); iter != errors.rend(); ++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(false); // and clear them + + cmdex.errors_unlock(); + + if (e) { // if error is present, alert the user + on_error_warn(e); + } + } + + + // The warnings are already printed via debug_* in cmdex. + virtual void on_error_warn(hz::ErrorBase* e) + { + if (!e) + return; + + // import the error only if it's relevant. + std::string error_type = e->get_type(); + + // ignore giochannel errors - higher level errors will be triggered, and they more user-friendly. + if (error_type == "giochannel" || error_type == "custom") { + return; + } + + this->set_error_msg(e->get_message()); + } + +}; + + + + +typedef TwCliExecutorGeneric TwCliExecutor; + +typedef hz::intrusive_ptr TwCliExecutorRefPtr; + + + +typedef TwCliExecutorGeneric TwCliExecutorGui; + +typedef hz::intrusive_ptr TwCliExecutorGuiRefPtr; + + + + + + + +#endif diff --git a/gsmartcontrol/src/gsc_main_window.cpp b/gsmartcontrol/src/gsc_main_window.cpp index d205e82..89e8864 100644 --- a/gsmartcontrol/src/gsc_main_window.cpp +++ b/gsmartcontrol/src/gsc_main_window.cpp @@ -32,6 +32,7 @@ #include "gsc_main_window_iconview.h" #include "gsc_main_window.h" #include "gsc_add_device_window.h" +#include "applib/executor_factory.h" @@ -984,10 +985,9 @@ void GscMainWindow::rescan_devices() sd.add_blacklist_patterns(blacklist_patterns); - SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); - ex->create_running_dialog(this); // pass this as parent + ExecutorFactoryRefPtr ex_factory(new ExecutorFactory(true, this)); // run it with GUI support - std::string error_msg = sd.detect_and_fetch_basic_data(drives, ex); // run it with GUI support + std::string error_msg = sd.detect_and_fetch_basic_data(drives, ex_factory); bool error = false; @@ -1052,14 +1052,13 @@ bool GscMainWindow::add_device(const std::string& file, const std::string& type_ d->set_extra_arguments(extra_args); d->set_is_manually_added(true); - SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); - ex->create_running_dialog(this); // pass this as parent + ExecutorFactoryRefPtr ex_factory(new ExecutorFactory(true, this)); // pass this as dialog parent std::vector tmp_drives; tmp_drives.push_back(d); StorageDetector sd; - std::string error_msg = sd.fetch_basic_data(tmp_drives, ex, true); // return its first error + 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); diff --git a/gsmartcontrol/src/gsc_settings.h b/gsmartcontrol/src/gsc_settings.h index 3add462..b3330ab 100644 --- a/gsmartcontrol/src/gsc_settings.h +++ b/gsmartcontrol/src/gsc_settings.h @@ -32,8 +32,10 @@ inline void init_default_settings() #ifndef _WIN32 rconfig::set_default_data("system/smartctl_binary", "smartctl"); // must be in PATH or use absolute path. + rconfig::set_default_data("system/tw_cli_binary", "tw_cli"); // must be in PATH or use absolute path. #else rconfig::set_default_data("system/smartctl_binary", "smartctl-nc.exe"); // use no-console version by default. + rconfig::set_default_data("system/tw_cli_binary", "tw_cli.exe"); #endif // search for "smartctl-nc.exe" in smartmontools installation first. rconfig::set_default_data("system/win32_search_smartctl_in_smartmontools", true); @@ -46,6 +48,8 @@ inline void init_default_settings() rconfig::set_default_data("system/linux_udev_byid_path", "/dev/disk/by-id"); // linux hard disk device links here rconfig::set_default_data("system/linux_proc_partitions_path", "/proc/partitions"); // file in linux /proc/partitions format + rconfig::set_default_data("system/linux_proc_devices_path", "/proc/devices"); // file in linux /proc/devices format + rconfig::set_default_data("system/linux_proc_scsi_scsi_path", "/proc/scsi/scsi"); // file in linux /proc/scsi/scsi format rconfig::set_default_data("system/solaris_dev_path", "/dev/rdsk"); // path to /dev/rdsk for solaris. rconfig::set_default_data("system/unix_sdev_path", "/dev"); // path to /dev. used by other unices // rconfig::set_default_data("system/device_match_patterns", ""); // semicolon-separated PCRE patterns