Support detecting drives behind 3ware controllers.

Add ExecutorFactory - now detectors can create their own gui or non-gui
executors, depending on who called them.
This commit is contained in:
Alexander Shaduri
2011-04-17 18:47:43 +00:00
parent fa2b10f3ce
commit c197ba76fa
22 changed files with 715 additions and 193 deletions
-16
View File
@@ -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.
+3 -3
View File
@@ -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)
+1
View File
@@ -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;
@@ -0,0 +1,51 @@
/**************************************************************************
Copyright:
(C) 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
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<CmdexSync> 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<CmdexSync>();
}
@@ -0,0 +1,55 @@
/**************************************************************************
Copyright:
(C) 2008 - 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
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<CmdexSync> 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<ExecutorFactory> ExecutorFactoryRefPtr;
#endif
+4 -4
View File
@@ -103,7 +103,7 @@ std::string SelfTest::start(hz::intrusive_ptr<CmdexSync> 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<CmdexSync> 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<CmdexSync> 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;
@@ -4,11 +4,15 @@
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include <glibmm.h> // 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<CmdexSync> 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();
}
+9 -4
View File
@@ -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<CmdexSync> SmartctlExecutor;
typedef hz::intrusive_ptr<SmartctlExecutor> 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<CmdexSync> smartctl_ex, std::string& smartctl_output);
#endif
+14 -50
View File
@@ -19,68 +19,37 @@
std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives)
std::string StorageDetector::detect(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
debug_out_info("app", DBG_FUNC_MSG << "Starting drive detection.\n");
std::vector<std::string> devices;
std::vector<StorageDeviceRefPtr> 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<std::string>::const_iterator iter = devices.begin(); iter != devices.end(); ++iter) {
std::string dev = *iter;
for (std::vector<StorageDeviceRefPtr>::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<StorageDeviceRefPtr>& 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<StorageDeviceRefPtr>& drives)
std::string StorageDetector::fetch_basic_data(std::vector<StorageDeviceRefPtr>& drives,
hz::intrusive_ptr<CmdexSync> 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<CmdexSync> 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<StorageDeviceRefPtr>&
std::string StorageDetector::detect_and_fetch_basic_data(std::vector<StorageDeviceRefPtr>& put_drives_here,
hz::intrusive_ptr<CmdexSync> 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;
}
+5 -3
View File
@@ -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<StorageDeviceRefPtr>& put_drives_here);
std::string detect(std::vector<StorageDeviceRefPtr>& put_drives_here,
ExecutorFactoryRefPtr ex_factory);
// fetch basic data of "drives" elements
std::string fetch_basic_data(std::vector<StorageDeviceRefPtr>& drives,
hz::intrusive_ptr<CmdexSync> 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<StorageDeviceRefPtr>& put_drives_here,
hz::intrusive_ptr<CmdexSync> smartctl_ex = 0);
ExecutorFactoryRefPtr ex_factory);
// void add_match_patterns(std::vector<std::string>& patterns)
@@ -12,6 +12,7 @@
#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"
@@ -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<std::string>& devices)
inline std::string detect_drives_linux_udev_byid(std::vector<std::string>& 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<std::string>& 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<std::string>& lines)
inline bool read_proc_file(hz::File& file, std::vector<std::string>& 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<std::string>&
// Read /proc/partitions file. Return error message on error.
inline std::string read_proc_partitions_file(std::vector<std::string>& 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<std::string>& 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<std::string>& 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<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", controller_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");
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<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(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<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);
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<std::string>& devices)
inline std::string detect_drives_linux_proc_partitions(std::vector<StorageDeviceRefPtr>& 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<std::string> 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<std::string> 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<std::string>& device
blacklist.push_back("/md[0-9]*$/"); // linux software raid
blacklist.push_back("/dm-[0-9]*$/"); // linux device mapper
std::vector<std::string> 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<std::string>& 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<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
std::vector<std::string> 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<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
std::vector<std::string> 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
@@ -11,17 +11,17 @@
#include <vector>
#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<std::string>& devices);
/// Detect drives in linux
std::string detect_drives_linux(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory);
/// Detect drives in linux using /proc/partitions
std::string detect_drives_linux_proc_partitions(std::vector<std::string>& devices);
#endif
@@ -24,10 +24,12 @@
std::string detect_drives_other(std::vector<std::string>& devices)
std::string detect_drives_other(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory)
{
debug_out_info("app", DBG_FUNC_MSG << "Detecting through /dev...\n");
std::vector<std::string> 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<std::string>& 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();
}
@@ -11,13 +11,15 @@
#include <vector>
#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<std::string>& devices);
std::string detect_drives_other(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory);
#endif
@@ -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";
@@ -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<std::string>& devices)
std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& 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<std::string>& 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();
@@ -11,12 +11,14 @@
#include <vector>
#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<std::string>& devices);
std::string detect_drives_win32(std::vector<StorageDeviceRefPtr>& drives, ExecutorFactoryRefPtr ex_factory);
#endif
+37 -71
View File
@@ -4,8 +4,6 @@
License: See LICENSE_gsmartcontrol.txt
***************************************************************************/
#include <glibmm.h> // 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<CmdexSyn
// We don't use "--all" - it may cause really screwed up the output (tests, etc...).
// This looks just like "--info" only on non-smart devices.
// --info --health --capabilities
std::string error_msg = execute_smartctl("-i -H -c", smartctl_ex, output, true); // set type to invalid if needed
std::string error_msg = execute_device_smartctl("-i -H -c", smartctl_ex, output, true); // set type to invalid if needed
// 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,
@@ -272,10 +285,10 @@ std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr<CmdexSync> 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<CmdexSync> 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();
+4 -1
View File
@@ -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<CmdexSync> smartctl_ex, std::string& output, bool check_type = false);
+123
View File
@@ -0,0 +1,123 @@
/**************************************************************************
Copyright:
(C) 2011 Alexander Shaduri <ashaduri 'at' gmail.com>
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 ExecutorSync>
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<CmdexSync> TwCliExecutor;
typedef hz::intrusive_ptr<TwCliExecutor> TwCliExecutorRefPtr;
typedef TwCliExecutorGeneric<CmdexSyncGui> TwCliExecutorGui;
typedef hz::intrusive_ptr<TwCliExecutorGui> TwCliExecutorGuiRefPtr;
#endif
+5 -6
View File
@@ -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<StorageDeviceRefPtr> 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);
+4
View File
@@ -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