mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-26 05:45:35 +00:00
Implemented Linux support for Areca controllers with enclosures (untested).
Implemented Windows support for Areca controllers (thanks to Richard Kagerer); untested so far.
This commit is contained in:
@@ -7,11 +7,11 @@ libapplib_a_SOURCES = app_gtkmm_utils.cpp app_pango_utils.cpp cmdex.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 executor_factory.h gui_utils.h \
|
||||
app_pcrecpp.h app_ui_res_utils.h cli_executors.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_helpers.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 tw_cli_executor.h wrapping_label.h
|
||||
storage_property_descr.h storage_settings.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)
|
||||
|
||||
+114
-2
@@ -9,8 +9,8 @@
|
||||
/// \weakgroup applib
|
||||
/// @{
|
||||
|
||||
#ifndef TW_CLI_EXECUTOR_H
|
||||
#define TW_CLI_EXECUTOR_H
|
||||
#ifndef CLI_EXECUTORS_H
|
||||
#define CLI_EXECUTORS_H
|
||||
|
||||
#include "cmdex.h"
|
||||
#include "cmdex_sync.h"
|
||||
@@ -129,6 +129,118 @@ typedef hz::intrusive_ptr<TwCliExecutorGui> TwCliExecutorGuiRefPtr;
|
||||
|
||||
|
||||
|
||||
/// Executor for cli (Areca utility)
|
||||
template<class ExecutorSync>
|
||||
class ArecaCliExecutorGeneric : public ExecutorSync {
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
ArecaCliExecutorGeneric(const std::string& cmd, const std::string& cmdargs)
|
||||
: ExecutorSync(cmd, cmdargs)
|
||||
{
|
||||
this->construct();
|
||||
}
|
||||
|
||||
|
||||
/// Constructor
|
||||
ArecaCliExecutorGeneric()
|
||||
{
|
||||
this->construct();
|
||||
}
|
||||
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~ArecaCliExecutorGeneric()
|
||||
{ }
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/// Called from constructors
|
||||
void construct()
|
||||
{
|
||||
ExecutorSync::get_command_executor().set_exit_status_translator(&ArecaCliExecutorGeneric::translate_exit_status, NULL);
|
||||
this->set_error_header("An error occurred while executing Areca cli:\n\n");
|
||||
}
|
||||
|
||||
|
||||
/// Exit status translate handler
|
||||
static std::string translate_exit_status(int status, void* user_data)
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
||||
/// Import the last error from command executor 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// This is called when an error occurs in command executor.
|
||||
/// Note: 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());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/// tw_cli executor without GUI support
|
||||
typedef ArecaCliExecutorGeneric<CmdexSync> ArecaCliExecutor;
|
||||
|
||||
/// A reference-counting pointer to ArecaCliExecutor
|
||||
typedef hz::intrusive_ptr<ArecaCliExecutor> ArecaCliExecutorRefPtr;
|
||||
|
||||
|
||||
|
||||
/// tw_cli executor with GUI support
|
||||
typedef ArecaCliExecutorGeneric<CmdexSyncGui> ArecaCliExecutorGui;
|
||||
|
||||
/// A reference-counting pointer to ArecaCliExecutorGui
|
||||
typedef hz::intrusive_ptr<ArecaCliExecutorGui> ArecaCliExecutorGuiRefPtr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "hz/debug.h"
|
||||
#include "executor_factory.h"
|
||||
#include "smartctl_executor_gui.h"
|
||||
#include "tw_cli_executor.h"
|
||||
#include "cli_executors.h"
|
||||
|
||||
|
||||
|
||||
@@ -44,6 +44,15 @@ hz::intrusive_ptr<CmdexSync> ExecutorFactory::create_executor(ExecutorFactory::T
|
||||
}
|
||||
return TwCliExecutorRefPtr(new TwCliExecutor());
|
||||
}
|
||||
case ExecutorArecaCli:
|
||||
{
|
||||
if (use_gui_) {
|
||||
ArecaCliExecutorGuiRefPtr ex = ArecaCliExecutorGuiRefPtr(new ArecaCliExecutorGui());
|
||||
ex->create_running_dialog(parent_); // dialog parent
|
||||
return ex;
|
||||
}
|
||||
return ArecaCliExecutorRefPtr(new ArecaCliExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
DBG_ASSERT(0);
|
||||
|
||||
@@ -31,7 +31,8 @@ class ExecutorFactory : public hz::intrusive_ptr_referenced {
|
||||
/// Executor type for create_executor()
|
||||
enum Type {
|
||||
ExecutorSmartctl,
|
||||
ExecutorTwCli
|
||||
ExecutorTwCli,
|
||||
ExecutorArecaCli
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ inline std::string execute_tw_cli(ExecutorFactoryRefPtr ex_factory, const std::s
|
||||
|
||||
|
||||
|
||||
/// Get number of ports using tw_cli. Return -1 on error.
|
||||
/// Get the drives on a 3ware controller using tw_cli.
|
||||
/// Note that the drives are inserted in the order they are detected.
|
||||
inline std::string tw_cli_get_drives(const std::string& dev, int controller,
|
||||
std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory, bool use_tw_cli_dev)
|
||||
@@ -149,6 +149,53 @@ inline std::string tw_cli_get_controllers(ExecutorFactoryRefPtr ex_factory, std:
|
||||
|
||||
|
||||
|
||||
/// Get number of ports by sequentially running smartctl on each port, until
|
||||
/// one of the gives an error. \c type contains a printf-formatted string with %d.
|
||||
/// \return an error message on error.
|
||||
inline std::string smartctl_scan_drives_sequentially(const std::string& dev, const std::string& type,
|
||||
int from, int to, std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory, std::string& last_output)
|
||||
{
|
||||
hz::intrusive_ptr<CmdexSync> 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);
|
||||
StorageDeviceRefPtr drive(new StorageDevice(dev, type_arg));
|
||||
|
||||
// This will generate an error if smartctl doesn't return 0, which is what happens
|
||||
// with non-populated ports.
|
||||
// Sometimes the output contains:
|
||||
// "Read Device Identity failed: Input/output error"
|
||||
// or
|
||||
// "Read Device Identity failed: empty IDENTIFY data"
|
||||
std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
|
||||
last_output = drive->get_info_output();
|
||||
|
||||
// If we've reached smartctl port limit (older versions may have smaller limits), abort.
|
||||
if (app_pcre_match("/VALID ARGUMENTS ARE/mi", last_output)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If we couldn't open the device, it means there is no such controller at specified device
|
||||
// and scanning the ports is useless.
|
||||
if (app_pcre_match("/No .* controller found/mi", last_output)
|
||||
|| app_pcre_match("/Smartctl open device: .* failed: No such device/mi", last_output) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!error_msg.empty()) {
|
||||
debug_out_info("app", "Smartctl returned with an error: " << error_msg << "\n");
|
||||
debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << " due to smartctl error.\n");
|
||||
} else {
|
||||
drives.push_back(drive);
|
||||
debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n");
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <cstdio> // std::fgets(), std::FILE
|
||||
#include <cerrno> // ENXIO
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <utility> // std::pair
|
||||
|
||||
@@ -328,40 +329,6 @@ inline std::string read_proc_scsi_sg_devices_file(std::vector< std::vector<int>
|
||||
|
||||
|
||||
|
||||
/// 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<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
|
||||
{
|
||||
hz::intrusive_ptr<CmdexSync> 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);
|
||||
StorageDeviceRefPtr drive(new StorageDevice(dev, type_arg));
|
||||
|
||||
// std::string error_msg = execute_smartctl(dev, "-d " + type_arg, "-i", smartctl_ex, output);
|
||||
std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
|
||||
std::string output = drive->get_info_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", "Smartctl returned with an error: " << error_msg << "\n");
|
||||
debug_out_dump("app", "Skipping drive " << drive->get_device_with_type() << " due to smartctl error.\n");
|
||||
} else {
|
||||
drives.push_back(drive);
|
||||
debug_out_info("app", "Added drive " << drive->get_device_with_type() << ".\n");
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
<pre>
|
||||
@@ -601,10 +568,12 @@ inline std::string detect_drives_linux_3ware(std::vector<StorageDeviceRefPtr>& d
|
||||
|
||||
error_msg = tw_cli_get_drives(dev, vendors_models[i].first, drives, ex_factory, false);
|
||||
if (!error_msg.empty()) { // no tw_cli
|
||||
int max_ports = rconfig::get_data<int32_t>("system/linux_max_scan_ports");
|
||||
debug_out_dump("app", "Starting brute-force port scan on 0-" << max_ports << " ports, device \"" << dev << "\". Change the maximum by setting \"system/linux_max_scan_ports\" config key.\n");
|
||||
max_ports = std::max(0, std::min(max_ports, 128)); // Reasonable sanity check.
|
||||
error_msg = smartctl_get_drives(dev, "3ware,%d", 0, max_ports, drives, ex_factory);
|
||||
int max_ports = rconfig::get_data<int32_t>("system/linux_3ware_max_scan_port");
|
||||
max_ports = std::max(0, std::min(max_ports, 127)); // Sanity check
|
||||
debug_out_dump("app", "Starting brute-force port scan on 0-" << max_ports << " ports, device \"" << dev
|
||||
<< "\". Change the maximum by setting \"system/linux_3ware_max_scan_port\" config key.\n");
|
||||
std::string last_output;
|
||||
error_msg = smartctl_scan_drives_sequentially(dev, "3ware,%d", 0, max_ports, drives, ex_factory, last_output);
|
||||
debug_out_dump("app", "Brute-force port scan finished.\n");
|
||||
}
|
||||
|
||||
@@ -741,9 +710,15 @@ inline std::string detect_drives_linux_adaptec(std::vector<StorageDeviceRefPtr>&
|
||||
|
||||
|
||||
|
||||
/** </tt>
|
||||
/** <pre>
|
||||
Areca Linux (arcmsr driver):
|
||||
Call as: smartctl -i -d areca,[1-24] /dev/sg0
|
||||
Call as:
|
||||
- smartctl -i -d areca,N /dev/sg0 (N is [1,24]).
|
||||
- smartctl -i -d areca,N/E /dev/sg0 (N is [1,128], E is [1,8]).
|
||||
The models that have "ix" (case-insensitive) in their model names
|
||||
have the enclosures and require the N,E syntax.
|
||||
Note: Using N/E syntax with non-enclosure cards seems to work
|
||||
regardless of the value of E.
|
||||
|
||||
Detection:
|
||||
There don't seem to be any entries in /proc/devices.
|
||||
@@ -755,13 +730,14 @@ Detect SCSI host (N in hostN below):
|
||||
The line number in /proc/scsi/sg/devices is X in /dev/sgX.
|
||||
Check /sys/bus/scsi/devices/hostN/scsi_host/hostN/host_fw_hd_channels, set
|
||||
its contents as the number of ports. If not present, use 24 (maximum).
|
||||
Probe each port for a valid drive: If the output contains "Device Read Identity Failed",
|
||||
then there is no drive on that port (or Areca has an old firmware, nothing we can
|
||||
do there).
|
||||
No-enclosure models only, since we have no info yet for the enclosure ones.
|
||||
Probe each port for a valid drive: If the output contains "Device Read Identity Failed"
|
||||
or "empty IDENTIFY data", then there is no drive on that port (or Areca has an
|
||||
old firmware, nothing we can do there).
|
||||
Notification: If /sys/bus/scsi/devices/hostN/scsi_host/hostN/host_fw_version
|
||||
is older than "V1.46 2009-01-06", notify the user (maybe its better to grep
|
||||
the smartctl output for that on port 0?). NOT IMPLEMENTED YET.
|
||||
</tt> */
|
||||
is older than "V1.46 2009-01-06", (1.51 for enclosure-having cards) notify the user
|
||||
(maybe its better to grep the smartctl output for that on port 0?). NOT IMPLEMENTED YET.
|
||||
</pre> */
|
||||
inline std::string detect_drives_linux_areca(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
|
||||
{
|
||||
debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind Areca controller(s)...\n");
|
||||
@@ -772,10 +748,11 @@ inline std::string detect_drives_linux_areca(std::vector<StorageDeviceRefPtr>& d
|
||||
return error_msg;
|
||||
}
|
||||
|
||||
std::set<int> controller_hosts;
|
||||
std::map<int, bool> controller_hosts; // controller # -> has_enclosure
|
||||
|
||||
for (std::size_t i = 0; i < vendors_models.size(); ++i) {
|
||||
if (!app_pcre_match("/Vendor: Areca /i", vendors_models[i].second)) {
|
||||
std::string vendor_line = vendors_models[i].second;
|
||||
if (!app_pcre_match("/Vendor: Areca /i", vendor_line)) {
|
||||
continue; // not a supported controller
|
||||
}
|
||||
int host_num = vendors_models[i].first;
|
||||
@@ -786,7 +763,14 @@ inline std::string detect_drives_linux_areca(std::vector<StorageDeviceRefPtr>& d
|
||||
debug_out_dump("app", "Skipping adapter with SCSI host " << host_num << ", host already found.\n");
|
||||
continue;
|
||||
}
|
||||
controller_hosts.insert(host_num);
|
||||
|
||||
// Check if it contains the "ix" string in its model name. It may not be exactly
|
||||
// a suffix (there may be "-VOL#00" or something like that appended as well).
|
||||
bool has_enclosure = app_pcre_match("/Model:.+ix.+Rev:/i", vendor_line);
|
||||
debug_out_dump("app", DBG_FUNC_MSG << "Areca controller (SCSI host " << host_num
|
||||
<< ") seems to " << (has_enclosure ? "have enclosure(s)" : "have no enclosures") << ".\n");
|
||||
|
||||
controller_hosts[host_num] = has_enclosure;
|
||||
}
|
||||
|
||||
if (controller_hosts.empty()) {
|
||||
@@ -802,8 +786,9 @@ inline std::string detect_drives_linux_areca(std::vector<StorageDeviceRefPtr>& d
|
||||
|
||||
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
|
||||
|
||||
for (std::set<int>::iterator iter = controller_hosts.begin(); iter != controller_hosts.end(); ++iter) {
|
||||
const int host_num = *iter;
|
||||
for (std::map<int, bool>::iterator iter = controller_hosts.begin(); iter != controller_hosts.end(); ++iter) {
|
||||
const int host_num = iter->first;
|
||||
const bool has_enclosure = iter->second;
|
||||
|
||||
for (std::size_t sg_num = 0; sg_num < sg_entries.size(); ++sg_num) {
|
||||
if (sg_entries[sg_num].size() < 5) {
|
||||
@@ -816,29 +801,53 @@ inline std::string detect_drives_linux_areca(std::vector<StorageDeviceRefPtr>& d
|
||||
continue;
|
||||
}
|
||||
|
||||
int max_ports = 0;
|
||||
if (has_enclosure) {
|
||||
// TODO We have no information on what "/sys/bus/scsi/devices/host%d/scsi_host/host%d/host_fw_hd_channels"
|
||||
// contains in case of enclosure-having cards.
|
||||
|
||||
int max_enclosures = rconfig::get_data<int32_t>("system/linux_areca_enc_max_enclosure");
|
||||
max_enclosures = std::max(1, std::min(8, max_enclosures)); // 1-8
|
||||
|
||||
int max_ports = rconfig::get_data<int32_t>("system/linux_areca_enc_max_scan_port");
|
||||
max_ports = std::max(1, std::min(128, max_ports)); // 1-128 sanity check
|
||||
|
||||
std::string dev = std::string("/dev/sg") + hz::number_to_string(sg_num);
|
||||
|
||||
debug_out_dump("app", "Starting brute-force port/enclosure scan on 1-" << max_ports << " ports, 1-" << max_enclosures << " enclosures, device \"" << dev
|
||||
<< "\". Change the maximums by setting \"system/linux_areca_enc_max_scan_port\" and \"system/linux_areca_enc_max_enclosure\" config keys.\n");
|
||||
std::string last_output;
|
||||
for (int enclosure_no = 1; enclosure_no < max_enclosures; ++enclosure_no) {
|
||||
error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d/" + hz::number_to_string(enclosure_no), 1, max_ports, drives, ex_factory, last_output);
|
||||
}
|
||||
debug_out_dump("app", "Brute-force port/enclosure scan finished.\n");
|
||||
|
||||
std::string ports_path = hz::string_sprintf("/sys/bus/scsi/devices/host%d/scsi_host/host%d/host_fw_hd_channels", host_num, host_num);
|
||||
hz::File ports_file(ports_path);
|
||||
std::string ports_file_contents;
|
||||
if (!read_proc_file(ports_file, ports_file_contents)) {
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Couldn't read the number of ports on Areca controller (\"" << ports_path << "\"): "
|
||||
<< ports_file.get_error_utf8() << ", trying manually.\n");
|
||||
} else {
|
||||
hz::string_is_numeric(hz::string_trim_copy(ports_file_contents), max_ports);
|
||||
debug_out_dump("app", DBG_FUNC_MSG << "Detected " << max_ports << " ports, through \"" << ports_path << "\".\n");
|
||||
int max_ports = 0;
|
||||
|
||||
// Read the number of ports.
|
||||
std::string ports_path = hz::string_sprintf("/sys/bus/scsi/devices/host%d/scsi_host/host%d/host_fw_hd_channels", host_num, host_num);
|
||||
hz::File ports_file(ports_path);
|
||||
std::string ports_file_contents;
|
||||
if (!read_proc_file(ports_file, ports_file_contents)) {
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Couldn't read the number of ports on Areca controller (\"" << ports_path << "\"): "
|
||||
<< ports_file.get_error_utf8() << ", trying manually.\n");
|
||||
} else {
|
||||
hz::string_is_numeric(hz::string_trim_copy(ports_file_contents), max_ports);
|
||||
debug_out_dump("app", DBG_FUNC_MSG << "Detected " << max_ports << " ports, through \"" << ports_path << "\".\n");
|
||||
}
|
||||
if (max_ports == 0) {
|
||||
max_ports = rconfig::get_data<int32_t>("system/linux_areca_noenc_max_scan_port");
|
||||
}
|
||||
max_ports = std::max(1, std::min(24, max_ports)); // 1-24 sanity check
|
||||
|
||||
std::string dev = std::string("/dev/sg") + hz::number_to_string(sg_num);
|
||||
debug_out_dump("app", "Starting brute-force port scan on 1-" << max_ports << " ports, device \"" << dev
|
||||
<< "\". Change the maximum by setting \"system/linux_areca_noenc_max_scan_port\" config key.\n");
|
||||
std::string last_output;
|
||||
error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d", 1, max_ports, drives, ex_factory, last_output);
|
||||
debug_out_dump("app", "Brute-force port scan finished.\n");
|
||||
}
|
||||
|
||||
if (max_ports < 1 || max_ports > 24) {
|
||||
max_ports = 24; // default to 24 ports (smartctl maximum for Areca)
|
||||
}
|
||||
|
||||
std::string dev = std::string("/dev/sg") + hz::number_to_string(sg_num);
|
||||
|
||||
debug_out_dump("app", "Starting brute-force port scan on 1-" << max_ports << " ports, device \"" << dev << "\".\n");
|
||||
error_msg = smartctl_get_drives(dev, "areca,%d", 1, max_ports, drives, ex_factory);
|
||||
debug_out_dump("app", "Brute-force port scan finished.\n");
|
||||
|
||||
if (!error_msg.empty()) {
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get the drives on ports of Areca controller: " << error_msg << "\n");
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "hz/win32_tools.h"
|
||||
#include "hz/string_sprintf.h"
|
||||
#include "hz/fs_path.h"
|
||||
#include "rconfig/rconfig_mini.h"
|
||||
#include "app_pcrecpp.h"
|
||||
#include "storage_detector_win32.h"
|
||||
@@ -163,6 +164,390 @@ std::string get_scan_open_multiport_devices(std::vector<StorageDeviceRefPtr>& dr
|
||||
|
||||
|
||||
|
||||
|
||||
/// Find and execute areca cli with specified options, return its output through \c output.
|
||||
/// \return error message
|
||||
inline std::string execute_areca_cli(ExecutorFactoryRefPtr ex_factory, const std::string& cli_binary,
|
||||
const std::string& command_options, std::string& output)
|
||||
{
|
||||
hz::intrusive_ptr<CmdexSync> executor = ex_factory->create_executor(ExecutorFactory::ExecutorArecaCli);
|
||||
|
||||
executor->set_command(Glib::shell_quote(cli_binary), command_options);
|
||||
|
||||
if (!executor->execute() || !executor->get_error_msg().empty()) {
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Error while executing Areca cli binary.\n");
|
||||
}
|
||||
|
||||
// any_to_unix is needed for windows
|
||||
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 << "Areca cli returned an empty output.\n");
|
||||
return "Areca CLI returned an empty output.";
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** <pre>
|
||||
Get the drives on Areca controller using Areca cli tool.
|
||||
Note that the drives are inserted in the order they are detected.
|
||||
|
||||
There are 3 formats of "disk info" output:
|
||||
|
||||
1. No expanders:
|
||||
------------------------------------------------------------
|
||||
# Ch# ModelName Capacity Usage
|
||||
===============================================================================
|
||||
1 1 INTEL SSDSA2M160G2GC 160.0GB System
|
||||
2 2 INTEL SSDSA2M160G2GC 160.0GB System
|
||||
3 3 INTEL SSDSA2M160G2GC 160.0GB System
|
||||
4 4 INTEL SSDSA2M160G2GC 160.0GB System
|
||||
5 5 Hitachi HDS724040ALE640 4000.8GB Storage
|
||||
6 6 Hitachi HDS724040ALE640 4000.8GB Storage
|
||||
7 7 Hitachi HDS724040ALE640 4000.8GB Storage
|
||||
8 8 Hitachi HDS724040ALE640 4000.8GB Storage
|
||||
9 9 Hitachi HDS724040ALE640 4000.8GB Storage
|
||||
10 10 Hitachi HDS724040ALE640 4000.8GB Storage
|
||||
11 11 Hitachi HDS724040ALE640 4000.8GB Backup
|
||||
12 12 Hitachi HDS724040ALE640 4000.8GB Backup
|
||||
===============================================================================
|
||||
GuiErrMsg<0x00>: Success.
|
||||
------------------------------------------------------------
|
||||
|
||||
2. No expanders (this output comes from CLI documentation, possibly an old format):
|
||||
------------------------------------------------------------
|
||||
# ModelName Serial# FirmRev Capacity State
|
||||
===============================================================================
|
||||
1 ST3250620NS 5QE1CP8S 3.AEE 250.1GB RaidSet Member(1)
|
||||
2 ST3250620NS 5QE1CP8S 3.AEE 250.1GB RaidSet Member(1)
|
||||
....(snip)....
|
||||
12 ST3250620NS 5QE1CP8S 3.AEE 250.1GB RaidSet Member(1)
|
||||
===============================================================================
|
||||
GuiErrMsg<0x00>: Success.
|
||||
------------------------------------------------------------
|
||||
|
||||
3. Expanders:
|
||||
------------------------------------------------------------
|
||||
# Enc# Slot# ModelName Capacity Usage
|
||||
===============================================================================
|
||||
1 01 Slot#1 N.A. 0.0GB N.A.
|
||||
2 01 Slot#2 N.A. 0.0GB N.A.
|
||||
3 01 Slot#3 N.A. 0.0GB N.A.
|
||||
4 01 Slot#4 N.A. 0.0GB N.A.
|
||||
5 01 Slot#5 N.A. 0.0GB N.A.
|
||||
6 01 Slot#6 N.A. 0.0GB N.A.
|
||||
7 01 Slot#7 N.A. 0.0GB N.A.
|
||||
8 01 Slot#8 N.A. 0.0GB N.A.
|
||||
9 02 SLOT 01 N.A. 0.0GB N.A.
|
||||
10 02 SLOT 02 N.A. 0.0GB N.A.
|
||||
11 02 SLOT 03 N.A. 0.0GB N.A.
|
||||
12 02 SLOT 04 N.A. 0.0GB N.A.
|
||||
13 02 SLOT 05 N.A. 0.0GB N.A.
|
||||
14 02 SLOT 06 N.A. 0.0GB N.A.
|
||||
15 02 SLOT 07 N.A. 0.0GB N.A.
|
||||
16 02 SLOT 08 N.A. 0.0GB N.A.
|
||||
17 02 SLOT 09 N.A. 0.0GB N.A.
|
||||
18 02 SLOT 10 N.A. 0.0GB N.A.
|
||||
19 02 SLOT 11 N.A. 0.0GB N.A.
|
||||
20 02 SLOT 12 N.A. 0.0GB N.A.
|
||||
21 02 SLOT 13 N.A. 0.0GB N.A.
|
||||
22 02 SLOT 14 ST910021AS 100.0GB Free
|
||||
23 02 SLOT 15 WDC WD3200BEVT-75A23T0 320.1GB HotSpare[Global]
|
||||
24 02 SLOT 16 N.A. 0.0GB N.A.
|
||||
25 02 SLOT 17 Hitachi HDS724040ALE640 4000.8GB Raid Set # 000
|
||||
26 02 SLOT 18 ST31500341AS 1500.3GB Raid Set # 000
|
||||
27 02 SLOT 19 ST3320620AS 320.1GB Raid Set # 000
|
||||
28 02 SLOT 20 ST31500341AS 1500.3GB Raid Set # 000
|
||||
29 02 SLOT 21 ST3500320AS 500.1GB Raid Set # 000
|
||||
30 02 SLOT 22 Hitachi HDS724040ALE640 4000.8GB Raid Set # 000
|
||||
31 02 SLOT 23 Hitachi HDS724040ALE640 4000.8GB Raid Set # 000
|
||||
32 02 SLOT 24 Hitachi HDS724040ALE640 4000.8GB Raid Set # 000
|
||||
33 02 EXTP 01 N.A. 0.0GB N.A.
|
||||
34 02 EXTP 02 N.A. 0.0GB N.A.
|
||||
35 02 EXTP 03 N.A. 0.0GB N.A.
|
||||
36 02 EXTP 04 N.A. 0.0GB N.A.
|
||||
===============================================================================
|
||||
GuiErrMsg<0x00>: Success.
|
||||
------------------------------------------------------------
|
||||
</pre> */
|
||||
inline std::string areca_cli_get_drives(const std::string& cli_binary, const std::string& dev, int controller,
|
||||
std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
|
||||
{
|
||||
debug_out_info("app", "Getting available Areca drives (ports) for controller " << controller << " through Areca CLI...\n");
|
||||
|
||||
// TODO Support controller number.
|
||||
// So far it seems the only way to pass the controller number to CLI is to use
|
||||
// the interactive mode.
|
||||
|
||||
std::string output;
|
||||
std::string error = execute_areca_cli(ex_factory, cli_binary, "disk info", output);
|
||||
if (!error.empty()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
// split to lines
|
||||
std::vector<std::string> lines;
|
||||
hz::string_split(output, '\n', lines, true);
|
||||
|
||||
enum FormatType {
|
||||
FormatUnknown,
|
||||
FormatNoEnc1,
|
||||
FormatNoEnc2,
|
||||
FormatEnc
|
||||
};
|
||||
|
||||
pcrecpp::RE noenc1_header_re = app_pcre_re("/^\\s*#\\s+Ch#/mi");
|
||||
pcrecpp::RE noenc2_header_re = app_pcre_re("/^\\s*#\\s+ModelName/mi");
|
||||
pcrecpp::RE exp_header_re = app_pcre_re("/^\\s*#\\s+Enc#/mi");
|
||||
|
||||
FormatType format_type = FormatUnknown;
|
||||
for (std::size_t i = 0; i < lines.size(); ++i) {
|
||||
if (noenc1_header_re.PartialMatch(lines.at(i))) {
|
||||
format_type = FormatNoEnc1;
|
||||
break;
|
||||
} else if (noenc2_header_re.PartialMatch(lines.at(i))) {
|
||||
format_type = FormatNoEnc2;
|
||||
break;
|
||||
} else if (exp_header_re.PartialMatch(lines.at(i))) {
|
||||
format_type = FormatEnc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (format_type == FormatUnknown) {
|
||||
debug_out_warn("app", "Could not read Areca CLI output: No valid header found.\n");
|
||||
return "Could not read Areca CLI output: No valid header found.";
|
||||
}
|
||||
|
||||
// Note: These may not match the full model, but just the first part is sufficient for comparison with "N.A.".
|
||||
pcrecpp::RE noexp1_port_re = app_pcre_re("/^\\s*[0-9]+\\s+([0-9]+)\\s+([^\\s]+)/mi"); // matches port, model.
|
||||
pcrecpp::RE noexp2_port_re = app_pcre_re("/^\\s*([0-9]+)\\s+([^\\s]+)/mi"); // matches port, model.
|
||||
pcrecpp::RE exp_port_re = app_pcre_re("/^\\s*[0-9]+\\s+([0-9]+)\\s+(?:Slot#|SLOT\\s+)([0-9]+)\\s+([^\\s]+)/mi"); // matches enclosure, port, model.
|
||||
|
||||
bool has_enclosure = (format_type == FormatEnc);
|
||||
if (has_enclosure) {
|
||||
debug_out_dump("app", "Areca controller seems to have enclosures.\n");
|
||||
} else {
|
||||
debug_out_dump("app", "Areca controller doesn't have any enclosures.\n");
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < lines.size(); ++i) {
|
||||
std::string port_str, model_str;
|
||||
if (has_enclosure) {
|
||||
std::string enclosure_str;
|
||||
if (exp_port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &enclosure_str, &port_str, &model_str)) {
|
||||
if (model_str != "N.A.") {
|
||||
int port = -1, enclosure = -1;
|
||||
hz::string_is_numeric(port_str, port);
|
||||
hz::string_is_numeric(enclosure_str, enclosure);
|
||||
drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev,
|
||||
"areca," + hz::number_to_string(port) + "/" + hz::number_to_string(enclosure))));
|
||||
debug_out_info("app", "Added Areca drive " << drives.back()->get_device_with_type() << ".\n");
|
||||
}
|
||||
}
|
||||
} else { // no enclosures
|
||||
pcrecpp::RE port_re = (format_type == FormatNoEnc1 ? noexp1_port_re : noexp2_port_re);
|
||||
if (port_re.PartialMatch(hz::string_trim_copy(lines.at(i)), &port_str, &model_str)) {
|
||||
if (model_str != "N.A.") {
|
||||
int port = -1;
|
||||
hz::string_is_numeric(port_str, port);
|
||||
drives.push_back(StorageDeviceRefPtr(new StorageDevice(dev, "areca," + hz::number_to_string(port))));
|
||||
debug_out_info("app", "Added Areca drive " << drives.back()->get_device_with_type() << ".\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** <pre>
|
||||
Areca Windows:
|
||||
Call as:
|
||||
- smartctl -i -d areca,N /dev/arcmsr0 (N is [1,24]).
|
||||
- smartctl -i -d areca,N/E /dev/arcmsr0 (N is [1,128], E is [1,8]).
|
||||
The models that have "ix" (case-insensitive) in their model names
|
||||
have the enclosures and require the N,E syntax.
|
||||
Note: Using N/E syntax with non-enclosure cards seems to work
|
||||
regardless of the value of E.
|
||||
|
||||
Detection:
|
||||
Check the registry keys for Areca management software and
|
||||
the CLI utility. One of them has to be installed.
|
||||
Check if CLI is installed. If it is, run it and parse the output. This should
|
||||
give us the populated port list (non-enclosure), and the populated port
|
||||
and expander list (enclosure models).
|
||||
There are two problems with CLI:
|
||||
If no controller is present, it crashes.
|
||||
We try the following first:
|
||||
smartctl -i -d areca,1 /dev/arcmsr0
|
||||
If it contains "arcmsr0: No Areca controller found" (Windows), or
|
||||
"Smartctl open device: /dev/arcmsr0 [areca_disk#01_enc#01] failed: No such device" (Linux),
|
||||
then there's no controller there.
|
||||
I don't see how it reports different controllers (there's only one list).
|
||||
We ignore this for now.
|
||||
If CLI is not installed, do the brute-force way:
|
||||
For arcmsr0, 1, ..., 8 (until we see "No Areca controller found"):
|
||||
Scan -d areca,[1-24] /dev/arcmsrN
|
||||
If at least one drive was found, it's a no-enclosure controller.
|
||||
If no drives were found, it's probably an enclosure controller, try
|
||||
-d areca,[1-128]/[1-8] /dev/arcmsrN
|
||||
It's 2-3 drives a second on an empty port, so some limits are set in config.
|
||||
</pre> */
|
||||
inline std::string detect_drives_win32_areca(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
|
||||
{
|
||||
debug_out_info("app", DBG_FUNC_MSG << "Detecting drives behind Areca controller(s)...\n");
|
||||
|
||||
int scan_controllers = rconfig::get_data<int32_t>("system/win32_areca_scan_controllers");
|
||||
if (scan_controllers == 0) { // disabled
|
||||
debug_out_info("app", "Areca controller scanning is disabled through config.\n");
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// Check if Areca tools are installed
|
||||
|
||||
std::string cli_inst_path;
|
||||
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CLI", "InstallPath", cli_inst_path);
|
||||
if (cli_inst_path.empty()) {
|
||||
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CLI", "InstallPath", cli_inst_path);
|
||||
}
|
||||
|
||||
if (scan_controllers == 2) { // auto
|
||||
std::string http_inst_path;
|
||||
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\archttp", "InstallPath", http_inst_path);
|
||||
if (http_inst_path.empty()) {
|
||||
hz::win32_get_registry_value_string(HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\archttp", "InstallPath", http_inst_path);
|
||||
}
|
||||
if (cli_inst_path.empty() && http_inst_path.empty()) {
|
||||
debug_out_info("app", "No Areca software found. Install Areca CLI or set \"system/win32_areca_scan_controllers\" config key to 1 to force scanning.\n");
|
||||
return std::string();
|
||||
}
|
||||
if (http_inst_path.empty()) {
|
||||
debug_out_dump("app", "Areca HTTP installation not found.\n");
|
||||
} else {
|
||||
debug_out_dump("app", "Areca HTTP installation found at: \"" << http_inst_path << "\".\n");
|
||||
}
|
||||
}
|
||||
if (cli_inst_path.empty()) {
|
||||
debug_out_dump("app", "Areca CLI installation not found.\n");
|
||||
} else {
|
||||
debug_out_dump("app", "Areca CLI installation found at: \"" << cli_inst_path << "\".\n");
|
||||
}
|
||||
|
||||
bool cli_found = !cli_inst_path.empty();
|
||||
|
||||
int use_cli = rconfig::get_data<int32_t>("system/win32_areca_use_cli");
|
||||
bool scan_detect = (use_cli != 1); // Whether to detect using sequential port scanning. Only do that if CLI is not forced.
|
||||
if (use_cli == 2) { // auto
|
||||
use_cli = cli_found;
|
||||
}
|
||||
|
||||
std::string cli_binary;
|
||||
if (use_cli == 1) {
|
||||
cli_binary = rconfig::get_data<std::string>("system/areca_cli_binary");
|
||||
if (!hz::FsPath(cli_binary).is_absolute() && !cli_inst_path.empty()) {
|
||||
cli_binary = hz::FsPath(cli_inst_path).append(cli_binary).str();
|
||||
}
|
||||
if (hz::FsPath(cli_binary).is_absolute() && !hz::FsPath(cli_binary).exists()) {
|
||||
use_cli = 0;
|
||||
debug_out_dump("app", "Areca CLI binary \"" << cli_binary << "\" not found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
|
||||
|
||||
// --- CLI mode
|
||||
|
||||
// Since CLI may segfault if there are no drives, test the controller presence first.
|
||||
// It doesn't matter if we use areca,N or areca,N/E - we will still get a different
|
||||
// error if there's no controller.
|
||||
if (use_cli) {
|
||||
debug_out_dump("app", "Testing Areca controller presence using smartctl...\n");
|
||||
|
||||
StorageDeviceRefPtr drive(new StorageDevice("/dev/arcmsr0", "areca,1"));
|
||||
std::string error_msg = drive->fetch_basic_data_and_parse(smartctl_ex);
|
||||
std::string output = drive->get_info_output();
|
||||
if (app_pcre_match("/No Areca controller found/mi", output)
|
||||
|| app_pcre_match("/Smartctl open device: .* failed: No such device/mi", output) ) {
|
||||
use_cli = 0;
|
||||
scan_detect = false;
|
||||
debug_out_dump("app", "Areca controller not found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (use_cli) {
|
||||
debug_out_info("app", "Scanning Areca drives using CLI...\n");
|
||||
int cli_max_controllers = 1; // TODO controller # with CLI.
|
||||
for (int controller_no = 0; controller_no < cli_max_controllers; ++controller_no) {
|
||||
std::string error_msg = areca_cli_get_drives(cli_binary, "/dev/arcmsr" + hz::number_to_string(controller_no), controller_no, drives, ex_factory);
|
||||
// If we get an error on controller 0, fall back to no-cli detection.
|
||||
if (!error_msg.empty() && controller_no == 0) {
|
||||
use_cli = 0;
|
||||
debug_out_warn("app", "Areca scan using CLI failed.\n");
|
||||
if (scan_detect) {
|
||||
debug_out_dump("app", "Trying manual Areca scan because of a CLI error.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If CLI scanning failed (and it was not forced), or if there was no CLI, scan manually.
|
||||
if (use_cli == 0 && scan_detect) {
|
||||
debug_out_info("app", "Manually scanning Areca controllers and ports...\n");
|
||||
|
||||
int max_controllers = rconfig::get_data<int32_t>("system/win32_areca_max_controllers");
|
||||
int max_noenc_ports = rconfig::get_data<int32_t>("system/win32_areca_neonc_max_scan_port");
|
||||
max_noenc_ports = std::max(1, std::min(24, max_noenc_ports)); // 1-24 sanity check
|
||||
int max_enc_ports = rconfig::get_data<int32_t>("system/win32_areca_enc_max_scan_port");
|
||||
max_enc_ports = std::max(1, std::min(128, max_enc_ports)); // 1-128 sanity check
|
||||
int max_enclosures = rconfig::get_data<int32_t>("system/win32_areca_enc_max_enclosure");
|
||||
max_enclosures = std::max(1, std::min(8, max_enclosures)); // 1-8 sanity check
|
||||
|
||||
for (int controller_no = 0; controller_no < max_controllers; ++controller_no) {
|
||||
std::string dev = std::string("/dev/arcmsr") + hz::number_to_string(controller_no);
|
||||
|
||||
// First, scan using the areca,N format.
|
||||
debug_out_dump("app", "Starting brute-force port scan (no-enclosure) on 1-" << max_noenc_ports << " ports, device \"" << dev
|
||||
<< "\". Change the maximum by setting \"system/win32_areca_neonc_max_scan_port\" config key.\n");
|
||||
|
||||
std::size_t old_drive_count = drives.size();
|
||||
std::string last_output;
|
||||
std::string error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d", 1, max_noenc_ports, drives, ex_factory, last_output);
|
||||
// If the scan stopped because of no controller, stop it all.
|
||||
if (!error_msg.empty() && (app_pcre_match("/No Areca controller found/mi", last_output)
|
||||
|| app_pcre_match("/Smartctl open device: .* failed: No such device/mi", last_output)) ) {
|
||||
debug_out_dump("app", "Areca controller " << controller_no << " not present, stopping sequential scan.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// If no drives were added (but the controller is present), it may be due to
|
||||
// expander syntax requirement. Try that.
|
||||
if (drives.size() == old_drive_count) {
|
||||
for (int enclosure_no = 1; enclosure_no < max_enclosures; ++enclosure_no) {
|
||||
debug_out_dump("app", "Starting brute-force port scan (enclosure #" << enclosure_no << ") on 1-" << max_enc_ports << " ports, device \"" << dev
|
||||
<< "\". Change the maximums by setting \"system/win32_areca_onc_max_scan_port\" and \"system/win32_areca_enc_max_enclosure\" config keys.\n");
|
||||
error_msg = smartctl_scan_drives_sequentially(dev, "areca,%d/" + hz::number_to_string(enclosure_no), 1, max_enc_ports, drives, ex_factory, last_output);
|
||||
}
|
||||
}
|
||||
|
||||
debug_out_dump("app", "Brute-force port scan finished.\n");
|
||||
}
|
||||
}
|
||||
|
||||
debug_out_info("app", DBG_FUNC_MSG << "Finished scanning Areca controllers.\n");
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -174,31 +559,48 @@ std::string get_scan_open_multiport_devices(std::vector<StorageDeviceRefPtr>& dr
|
||||
// http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
|
||||
std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
|
||||
{
|
||||
std::vector<std::string> error_msgs;
|
||||
std::string error_msg;
|
||||
|
||||
hz::intrusive_ptr<CmdexSync> smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl);
|
||||
|
||||
// Fetch multiport devices using --scan-open.
|
||||
std::set<int> used_pds;
|
||||
std::string error_msg = get_scan_open_multiport_devices(drives, ex_factory, used_pds);
|
||||
bool multiport_found = !drives.empty();
|
||||
|
||||
// Find out their serial numbers
|
||||
std::set<int> used_pds;
|
||||
error_msg = get_scan_open_multiport_devices(drives, ex_factory, used_pds);
|
||||
if (!error_msg.empty()) {
|
||||
error_msgs.push_back(error_msg);
|
||||
}
|
||||
|
||||
bool multiport_found = !drives.empty();
|
||||
bool areca_open_found = false; // whether areca devices were found at --scan-open time.
|
||||
|
||||
// Find out their serial numbers and whether there are Arecas there.
|
||||
std::set<std::string> serials;
|
||||
for (std::size_t i = 0; i < drives.size(); ++i) {
|
||||
std::string local_error = drives.at(i)->fetch_basic_data_and_parse(smartctl_ex);
|
||||
if (!local_error.empty()) {
|
||||
debug_out_info("app", "Smartctl returned with an error: " << error_msg << "\n");
|
||||
debug_out_info("app", "Smartctl returned with an error: " << local_error << "\n");
|
||||
// Don't exit, just report it.
|
||||
}
|
||||
if (!drives.at(i)->get_serial_number().empty()) {
|
||||
// add model as well, who knows, there may be duplicates across vendors
|
||||
serials.insert(drives.at(i)->get_model_name() + "_" + drives.at(i)->get_serial_number());
|
||||
}
|
||||
|
||||
// See if there are any areca devices. This is not implemented yet by smartctl (as of 6.0),
|
||||
// but if it ever is, we can skip our own detection below.
|
||||
std::string type_arg = drives.at(i)->get_type_argument();
|
||||
if (type_arg.find("areca") != std::string::npos) {
|
||||
areca_open_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Scan PhysicalDrive entries
|
||||
|
||||
debug_out_info("app", "Starting sequential scan of \\\\.\\PhysicalDriveN devices...\n");
|
||||
|
||||
// Scan PhysicalDrive entries
|
||||
for (int drive_num = 0; ; ++drive_num) {
|
||||
|
||||
// If the drive was already encountered in --scan-open (with a port number), skip it.
|
||||
@@ -233,7 +635,7 @@ std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, Execut
|
||||
if (!serials.empty()) {
|
||||
std::string local_error = drive->fetch_basic_data_and_parse(smartctl_ex);
|
||||
if (!local_error.empty()) {
|
||||
debug_out_info("app", "Smartctl returned with an error: " << error_msg << "\n");
|
||||
debug_out_info("app", "Smartctl returned with an error: " << local_error << "\n");
|
||||
// Don't exit, just report it.
|
||||
}
|
||||
// A serial may be empty if "-q noserial" was given to smartctl.
|
||||
@@ -275,7 +677,14 @@ std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, Execut
|
||||
}
|
||||
|
||||
|
||||
return std::string();
|
||||
if (!areca_open_found) {
|
||||
detect_drives_win32_areca(drives, ex_factory);
|
||||
if (!error_msg.empty()) {
|
||||
error_msgs.push_back(error_msg);
|
||||
}
|
||||
}
|
||||
|
||||
return hz::string_join(error_msgs, "\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@ GscPreferencesWindow::GscPreferencesWindow(BaseObjectType* gtkcobj, const app_ui
|
||||
|
||||
Glib::ustring smartctl_binary_tooltip = "A path to smartctl binary. If the path is not absolute, the binary will be looked for in user's PATH.";
|
||||
#if defined CONFIG_KERNEL_FAMILY_WINDOWS
|
||||
smartctl_binary_tooltip += "\n" + "Note: smartctl.exe shows a console during execution, while smartctl-nc.exe (default) doesn't (nc means no-console).";
|
||||
smartctl_binary_tooltip += Glib::ustring("\n") + "Note: smartctl.exe shows a console during execution, while smartctl-nc.exe (default) doesn't (nc means no-console).";
|
||||
#endif
|
||||
if (Gtk::Label* smartctl_binary_label = lookup_widget<Gtk::Label*>("smartctl_binary_label")) {
|
||||
app_gtkmm_set_widget_tooltip(*smartctl_binary_label, smartctl_binary_tooltip);
|
||||
|
||||
@@ -37,12 +37,19 @@ inline void init_default_settings()
|
||||
#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");
|
||||
rconfig::set_default_data("system/areca_cli_binary", "cli.exe"); // if relative, an installation path is prepended (if found).
|
||||
#endif
|
||||
// search for "smartctl-nc.exe" in smartmontools installation first.
|
||||
rconfig::set_default_data("system/win32_search_smartctl_in_smartmontools", true);
|
||||
rconfig::set_default_data("system/win32_smartmontools_regpath", "SOFTWARE\\smartmontools"); // in HKLM
|
||||
rconfig::set_default_data("system/win32_smartmontools_regkey", "Install_Dir");
|
||||
rconfig::set_default_data("system/win32_smartmontools_smartctl_binary", "bin\\smartctl-nc.exe"); // relative to smt install path
|
||||
rconfig::set_default_data("system/win32_areca_scan_controllers", int32_t(2)); // 0 - no, 1 - yes, 2 - auto (if areca tools are found)
|
||||
rconfig::set_default_data("system/win32_areca_use_cli", int32_t(2)); // 0 - no, 1 - yes, 2 - auto (if it's found)
|
||||
rconfig::set_default_data("system/win32_areca_max_controllers", int32_t(4)); // Maximum number of areca controllers (a safety measure). CLI supports 4.
|
||||
rconfig::set_default_data("system/win32_areca_enc_max_scan_port", int32_t(36)); // 1-128 (areca with enclosures). The last RAID port to scan if no other method is available
|
||||
rconfig::set_default_data("system/win32_areca_enc_max_enclosure", int32_t(3)); // 1-8 (areca with enclosures). The last RAID enclosure to scan if no other method is available
|
||||
rconfig::set_default_data("system/win32_areca_neonc_max_scan_port", int32_t(24)); // 1-24 (areca without enclosures). The last RAID port to scan if no other method is available
|
||||
|
||||
rconfig::set_default_data("system/smartctl_options", ""); // default options on ALL commands
|
||||
rconfig::set_default_data("system/smartctl_device_options", ""); // dev1:val1;dev2:val2;... format, each bin2ascii-encoded.
|
||||
@@ -52,7 +59,10 @@ inline void init_default_settings()
|
||||
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/linux_proc_scsi_sg_devices_path", "/proc/scsi/sg/devices"); // file in linux /proc/scsi/sg/devices format
|
||||
rconfig::set_default_data("system/linux_max_scan_ports", int32_t(23)); // maximum number of RAID ports to scan if no other method is available
|
||||
rconfig::set_default_data("system/linux_3ware_max_scan_port", int32_t(23)); // 0-127 (3ware). The last RAID port to scan if no other method is available
|
||||
rconfig::set_default_data("system/linux_areca_enc_max_scan_port", int32_t(36)); // 1-128 (areca with enclosures). The last RAID port to scan if no other method is available
|
||||
rconfig::set_default_data("system/linux_areca_enc_max_enclosure", int32_t(4)); // 1-8 (areca with enclosures). The last RAID enclosure to scan if no other method is available
|
||||
rconfig::set_default_data("system/linux_areca_neonc_max_scan_port", int32_t(24)); // 1-24 (areca without enclosures). The last RAID port to scan if no other method is available
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user