Windows: SUpport tw_cli/cx/px variant of smartctl device if --scan-open fails

and tw_cli is installed.
This commit is contained in:
Alexander Shaduri
2011-05-01 14:41:58 +00:00
parent 5ffc87ad9b
commit 98842f238e
4 changed files with 190 additions and 70 deletions
+3 -1
View File
@@ -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:
@@ -0,0 +1,159 @@
/**************************************************************************
Copyright:
(C) 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#ifndef STORAGE_DETECTOR_HELPERS_H
#define STORAGE_DETECTOR_HELPERS_H
#include <string>
#include <vector>
#include <glibmm.h>
#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<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory, bool use_tw_cli_dev)
{
hz::intrusive_ptr<CmdexSync> 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<std::string> 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<std::string> 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<int>& controllers)
{
hz::intrusive_ptr<CmdexSync> 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<std::string> 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<std::string> 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
@@ -12,14 +12,14 @@
#include <algorithm> // std::find
#include <cstdio> // std::fgets(), std::FILE
#include <cerrno> // ENXIO
#include <glibmm.h>
#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<std::string>& 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<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
hz::intrusive_ptr<CmdexSync> 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<std::string> 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<std::string> 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<StorageDeviceRefPtr>& 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<int32_t>("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.
@@ -11,9 +11,11 @@
#include <windows.h> // CreateFileA(), CloseHandle(), etc...
#include <glibmm.h>
#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<StorageDeviceRefPtr>& drives, Execut
{
std::vector<int> 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<StorageDeviceRefPtr>& 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<int> 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();
}