From 98842f238e693f42d62a7c71e0fb2d8404b24f7b Mon Sep 17 00:00:00 2001 From: Alexander Shaduri Date: Sun, 1 May 2011 14:41:58 +0000 Subject: [PATCH] Windows: SUpport tw_cli/cx/px variant of smartctl device if --scan-open fails and tw_cli is installed. --- gsmartcontrol/TODO | 4 +- .../src/applib/storage_detector_helpers.h | 159 ++++++++++++++++++ .../src/applib/storage_detector_linux.cpp | 71 +------- .../src/applib/storage_detector_win32.cpp | 26 ++- 4 files changed, 190 insertions(+), 70 deletions(-) create mode 100644 gsmartcontrol/src/applib/storage_detector_helpers.h diff --git a/gsmartcontrol/TODO b/gsmartcontrol/TODO index 74b11a3..906d0e8 100644 --- a/gsmartcontrol/TODO +++ b/gsmartcontrol/TODO @@ -36,11 +36,13 @@ Bugs / patches: TODO: -Windows: If smartctl --scan-open returns no "sd*,port"-style devices, ++Windows: If smartctl --scan-open returns no "sd*,port"-style devices, check if 3dm2 is installed and execute "tw_cli show" to get the controllers, then use the tw_cli variant of smartctl. +Support brief format of attributes (-f brief). + At least as a parse-only thing (no sorting through columns) Testing: diff --git a/gsmartcontrol/src/applib/storage_detector_helpers.h b/gsmartcontrol/src/applib/storage_detector_helpers.h new file mode 100644 index 0000000..a235b94 --- /dev/null +++ b/gsmartcontrol/src/applib/storage_detector_helpers.h @@ -0,0 +1,159 @@ +/************************************************************************** + Copyright: + (C) 2011 Alexander Shaduri + License: See LICENSE_gsmartcontrol.txt +***************************************************************************/ + +#ifndef STORAGE_DETECTOR_HELPERS_H +#define STORAGE_DETECTOR_HELPERS_H + +#include +#include +#include + +#include "executor_factory.h" +#include "storage_device.h" +#include "rconfig/rconfig_mini.h" +#include "app_pcrecpp.h" + + + + + +/// Get number of ports using tw_cli. Return -1 on error. +inline std::string tw_cli_get_drives(const std::string& dev, int scsi_host_no, + std::vector& drives, ExecutorFactoryRefPtr ex_factory, bool use_tw_cli_dev) +{ + 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", scsi_host_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_64"); // try this first + binaries.push_back(binary + ".x86"); +#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(hz::string_trim_copy(lines.at(i)), &port_str, &status)) { + if (status != "NOT-PRESENT") { + int port = -1; + hz::string_is_numeric(port_str, port); + if (port != -1) { + if (use_tw_cli_dev) { // use "tw_cli/cx/py" device + drives.push_back(StorageDeviceRefPtr(new StorageDevice("tw_cli/c" + + hz::number_to_string(scsi_host_no) + "/p" + hz::number_to_string(port)))); + } else { + drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, "3ware," + hz::number_to_string(port)))); + } + } + } + } + } + + return std::string(); +} + + + +/// Return 3ware SCSI host numbers (same as /c switch to tw_cli) +/// \return error string on error +inline std::string tw_cli_get_controllers(ExecutorFactoryRefPtr ex_factory, std::vector& controllers) +{ + 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::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_64"); // try this first + binaries.push_back(binary + ".x86"); +#endif + + for (std::size_t i = 0; i < binaries.size(); ++i) { + executor->set_command(Glib::shell_quote(binaries.at(i)), "show"); + + 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 controller_re = app_pcre_re("/^c([0-9])+[ \\t]+/mi"); + for (std::size_t i = 0; i < lines.size(); ++i) { + std::string controller_str; + if (controller_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &controller_str)) { + int controller = -1; + hz::string_is_numeric(controller_str, controller); + if (controller != -1) { + controllers.push_back(controller); + } + } + } + + return std::string(); +} + + + + + + + + +#endif diff --git a/gsmartcontrol/src/applib/storage_detector_linux.cpp b/gsmartcontrol/src/applib/storage_detector_linux.cpp index 327524f..4e8bf8d 100644 --- a/gsmartcontrol/src/applib/storage_detector_linux.cpp +++ b/gsmartcontrol/src/applib/storage_detector_linux.cpp @@ -12,14 +12,14 @@ #include // std::find #include // std::fgets(), std::FILE #include // ENXIO -#include #include "hz/debug.h" #include "hz/fs_path_utils.h" #include "hz/fs_file.h" #include "rconfig/rconfig_mini.h" - #include "app_pcrecpp.h" +#include "storage_detector_helpers.h" + @@ -211,71 +211,6 @@ inline std::string read_proc_scsi_scsi_file(std::vector& lines) -/// Get number of ports using tw_cli. Return -1 on error. -inline std::string tw_cli_get_drives(const std::string& dev, int scsi_host_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", scsi_host_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_64"); // try this first - binaries.push_back(binary + ".x86"); -#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(hz::string_trim_copy(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, @@ -503,7 +438,7 @@ inline std::string detect_drives_linux_3ware(std::vector& d // We can't map twaX to scsiY, so lets assume the relative order is the same. std::string dev = std::string("/dev/") + (twa_found ? "twa" : "twe") + hz::number_to_string(num_controllers); - error_msg = tw_cli_get_drives(dev, last_scsi_host, drives, ex_factory); + error_msg = tw_cli_get_drives(dev, last_scsi_host, drives, ex_factory, false); if (!error_msg.empty()) { // no tw_cli int max_ports = rconfig::get_data("system/linux_max_scan_ports"); max_ports = std::max(max_ports, 23); // 128 smartctl calls are too much (it's too slow). Settle for 24. diff --git a/gsmartcontrol/src/applib/storage_detector_win32.cpp b/gsmartcontrol/src/applib/storage_detector_win32.cpp index de1c12c..10db305 100644 --- a/gsmartcontrol/src/applib/storage_detector_win32.cpp +++ b/gsmartcontrol/src/applib/storage_detector_win32.cpp @@ -11,9 +11,11 @@ #include // CreateFileA(), CloseHandle(), etc... #include +#include "hz/win32_tools.h" #include "hz/string_sprintf.h" #include "rconfig/rconfig_mini.h" #include "app_pcrecpp.h" +#include "storage_detector_helpers.h" /* @@ -26,7 +28,7 @@ Call as: smartctl -i sd[a-z],N No idea how to check if it's 3ware. Call as: smarctl -i tw_cli/cx/py This runs tw_cli tool and parses the output; controller x, port y. - tw_cli is needed for 64-bit systems, as well as older controllers. + tw_cli may be needed for older controllers / drivers. In tw_cli mode only limited information-gathering is supported. tw_cli (part of 3DM2) is automatically added to system PATH, no need to look for it. @@ -153,6 +155,7 @@ std::string detect_drives_win32(std::vector& drives, Execut { std::vector used_pds; std::string error_msg = get_scan_open_multiport_devices(drives, ex_factory, used_pds); + bool multiport_found = !drives.empty(); for (int drive_num = 0; ; ++drive_num) { std::string name = hz::string_sprintf("\\\\.\\PhysicalDrive%d", drive_num); @@ -176,6 +179,27 @@ std::string detect_drives_win32(std::vector& drives, Execut } } + + // If smartctl --scan-open returns no "sd*,port"-style devices, + // check if 3dm2 is installed and execute "tw_cli show" to get + // the controllers, then use the tw_cli variant of smartctl. + + if (!multiport_found) { + std::string inst_path; + hz::win32_get_registry_value_string(HKEY_USERS, ".DEFAULT\\Software\\3ware\\3DM2", "InstallPath", inst_path); + + if (!inst_path.empty()) { + std::vector controllers; + error_msg = tw_cli_get_controllers(ex_factory, controllers); + // ignore the error message above, it's of no use. + for (std::size_t i = 0; i < controllers.size(); ++i) { + // don't specify device, it's ignored in tw_cli mode + tw_cli_get_drives("", controllers.at(i), drives, ex_factory, true); + } + } + } + + return std::string(); }