Added support for devices detected by "smartctl --scan-open" under win32 (this

includes 3ware).
This commit is contained in:
Alexander Shaduri
2011-04-17 20:36:54 +00:00
parent c197ba76fa
commit 5e46ed7cb6
3 changed files with 133 additions and 27 deletions
+3 -25
View File
@@ -42,6 +42,8 @@ Add debian/ubuntu's su-to-root support to gsmartcontrol-root.
Set tooltips wherever we have "No description available".
!!! Allow quitting when test is running (ask first).
Testing:
If ETA time has elapsed, but it's still only at 10% completion,
@@ -50,7 +52,7 @@ Testing:
Can't reproduce it?
Detect running tests on launch (maybe ask the user too? some tests
may be stuck due to bad firmware).
Allow quitting when test is running (ask first).
Add support for launching with consolehelper (from usermode package).
Fedora uses this by default.
@@ -124,30 +126,6 @@ RAID:
Call as: smartctl -i -d 3ware,[0-127] /dev/twa0 (or twe0)
Detection: unknown.
3ware Windows (XP so far, maybe the same under the others):
For 3ware 9xxx only.
Call as: smartctl -i sd[a-z],N
N is port, a-z is logical drive (unit) provided by controller.
N is limited to [0, 31] in the code.
The sd[a-z] device actually exists as \\.\PhysicalDrive[0-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.
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.
3DM2 install can be detected by checking:
HKEY_USERS\.DEFAULT\Software\3ware\3DM2, InstallPath
Another option for detection (whether it's 3ware) would be getting
\\.\PhysicalDrive0 properties, like smartctl does.
Newer (> 5.39.1) smartctl supports --scan-open, which will give us:
/dev/sda,0 -d ata (opened)
/dev/sda,1 -d ata (opened)
-d 3ware is not needed under Windows. We should treat sda as pd0
and remove pd0 from PhysicalDrive-detected list.
Running smartctl on sda gives almost the same result as on sda,0.
LSI MegaRAID (SCSI/SAS), Linux:
Call as: smartctl -i -d megaraid,[0-?] /dev/sda
@@ -260,7 +260,7 @@ inline std::string tw_cli_get_drives(const std::string& dev, int controller_no,
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 (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);
@@ -9,8 +9,130 @@
#if defined CONFIG_KERNEL_FAMILY_WINDOWS
#include <windows.h> // CreateFileA(), CloseHandle(), etc...
#include <glibmm.h>
#include "hz/string_sprintf.h"
#include "rconfig/rconfig_mini.h"
#include "app_pcrecpp.h"
/*
3ware Windows (XP so far, maybe the same under the others):
For 3ware 9xxx only.
Call as: smartctl -i sd[a-z],N
N is port, a-z is logical drive (unit) provided by controller.
N is limited to [0, 31] in the code.
The sd[a-z] device actually exists as \\.\PhysicalDrive[0-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.
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.
3DM2 install can be detected by checking:
HKEY_USERS\.DEFAULT\Software\3ware\3DM2, InstallPath
Another option for detection (whether it's 3ware) would be getting
\\.\PhysicalDrive0 properties, like smartctl does.
Newer (> 5.39.1) smartctl supports --scan-open, which will give us:
/dev/sda,0 -d ata (opened)
/dev/sda,1 -d ata (opened)
-d 3ware is not needed under Windows. We should treat sda as pd0
and remove pd0 from PhysicalDrive-detected list.
Running smartctl on sda gives almost the same result as on sda,0.
*/
/**
smartctl --scan-open output for win32 (3ware):
/dev/sda,0 -d ata (opened)
/dev/sda,1 -d ata (opened)
smartctl --scan-open output for linux:
/dev/sda -d sat # /dev/sda [SAT], ATA device
/dev/sdb -d sat # /dev/sdb [SAT], ATA device
/dev/sdc -d sat # /dev/sdc [SAT], ATA device
*/
namespace {
/// Run "smartctl --scan-open" and pick the devices which have
/// a port parameter. We don't pick the others because the may
/// conflict with pd* devices, and we like pd* better than sd*.
std::string get_scan_open_multiport_devices(std::vector<StorageDeviceRefPtr>& drives,
ExecutorFactoryRefPtr ex_factory, std::vector<int>& equivalent_pds)
{
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
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 += " ";
smartctl_ex->set_command(Glib::shell_quote(smartctl_binary),
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");
return smartctl_ex->get_error_msg();
}
// any_to_unix is needed for windows
std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str()));
if (output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n");
return "Smartctl returned an empty output.";
}
// if we've reached smartctl port limit (older versions may have smaller limits), abort.
if (app_pcre_match("/UNRECOGNIZED OPTION/mi", output)) {
return "Smartctl doesn't support --scan-open switch.";
}
std::vector<std::string> lines;
hz::string_split(output, '\n', lines, true);
// /dev/sda,0 -d ata (opened)
// /dev/sda,1 -d ata (opened)
// /dev/sda -d sat # /dev/sda [SAT], ATA device
// we only pick the ones with ports
pcrecpp::RE port_re = app_pcre_re("/^(\\/dev/\\[a-z0-9]),([0-9])+[ \\t]+-d[ \\t]+([^\\t\\n]+)/i");
pcrecpp::RE dev_re = app_pcre_re("/^\\/dev\\/sd([a-z])$/");
for (std::size_t i = 0; i < lines.size(); ++i) {
std::string dev, port_str, type;
if (port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &dev, &port_str, &type)) {
std::string letter;
if (dev_re.PartialMatch(dev, &letter)) {
// don't use pd* devices equivalent to these sd* devices.
equivalent_pds.push_back(letter.at(0) - 'a');
}
std::string full_dev = dev + "," + port_str;
drives.push_back(StorageDeviceRefPtr(new StorageDevice(full_dev, type)));
}
}
return std::string();
}
}
@@ -21,6 +143,9 @@
std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
std::vector<int> used_pds;
std::string error_msg = get_scan_open_multiport_devices(drives, ex_factory, used_pds);
for (int drive_num = 0; ; ++drive_num) {
std::string name = hz::string_sprintf("\\\\.\\PhysicalDrive%d", drive_num);
@@ -37,7 +162,10 @@ std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, Execut
CloseHandle(h);
drives.push_back(new StorageDevice(hz::string_sprintf("pd%d", drive_num)));
if (std::find(used_pds.begin(), used_pds.end(), drive_num) == used_pds.end()) {
std::string dev = hz::string_sprintf("pd%d", drive_num);
drives.push_back(new StorageDevice(dev));
}
}
return std::string();