From a2d735fee5a5bb30108c4a43681d57195c8fb86b Mon Sep 17 00:00:00 2001 From: Alexander Shaduri Date: Mon, 4 Apr 2011 20:02:41 +0000 Subject: [PATCH] Added support for specifying -d option and extra parameters via command line and preferences dialog. StorageDevice will use the specified type and arguments when executing smartctl. This change effectively adds support for multiple drives behind a single device name. The preferences dialog can distinguish between various device types now (when supplying smartctl arguments). --- gsmartcontrol/src/applib/storage_device.cpp | 61 ++++++++------------ gsmartcontrol/src/applib/storage_device.h | 7 +-- gsmartcontrol/src/applib/storage_settings.h | 26 +++++++-- gsmartcontrol/src/gsc_init.cpp | 22 ++++--- gsmartcontrol/src/gsc_main_window.cpp | 22 +++++-- gsmartcontrol/src/gsc_main_window.h | 2 +- gsmartcontrol/src/gsc_preferences_window.cpp | 4 +- 7 files changed, 83 insertions(+), 61 deletions(-) diff --git a/gsmartcontrol/src/applib/storage_device.cpp b/gsmartcontrol/src/applib/storage_device.cpp index 0a1d177..bb35888 100644 --- a/gsmartcontrol/src/applib/storage_device.cpp +++ b/gsmartcontrol/src/applib/storage_device.cpp @@ -30,31 +30,12 @@ std::string StorageDevice::get_type_readable_name(StorageDevice::detected_type_t return "invalid"; case detected_type_cddvd: return "cd/dvd"; - case detected_type_scsi: - return "scsi"; } return "[internal_error]"; } -std::string StorageDevice::get_type_arg_name(StorageDevice::detected_type_t type) -{ - switch (type) { - case detected_type_unknown: - return ""; - case detected_type_invalid: - return ""; - case detected_type_cddvd: - return ""; - case detected_type_scsi: - return "scsi"; - } - return ""; -} - - - std::string StorageDevice::get_status_name(StorageDevice::status_t status, bool use_yesno) { switch (status) { @@ -169,9 +150,9 @@ std::string StorageDevice::fetch_basic_data_and_parse(hz::intrusive_ptrset_detected_type(detected_type_scsi); + this->set_type_argument("scsi"); return this->fetch_basic_data_and_parse(smartctl_ex); // try again with scsi } @@ -288,7 +269,7 @@ std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr sma // instead of -a, we use all the individual options -a encompasses, so that // an addition to default -a output won't affect us. - if (this->get_detected_type() == detected_type_scsi) { + 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); @@ -298,9 +279,9 @@ std::string StorageDevice::fetch_data_and_parse(hz::intrusive_ptr sma smartctl_ex, output, true); // set type to invalid if needed } // See notes above (in fetch_basic_data_and_parse()). - if (get_detected_type() == detected_type_invalid) { + if (get_detected_type() == detected_type_invalid && get_type_argument().empty()) { debug_out_info("app", "The device seems to be of different type than auto-detected, trying again with scsi.\n"); - this->set_detected_type(detected_type_scsi); + this->set_type_argument("scsi"); return this->fetch_data_and_parse(smartctl_ex); // try again with scsi } @@ -603,7 +584,7 @@ std::string StorageDevice::get_type_argument() const -void StorageDevice::set_extra_argument(const string& args) +void StorageDevice::set_extra_arguments(const string& args) { extra_args_ = args; } @@ -766,20 +747,26 @@ std::string StorageDevice::get_device_options() const return std::string(); } - std::string config_options = app_get_device_option(get_device()); + // If we have some special type or option, specify it on the command line (like "-d scsi"). + // Note that the latter "-d" option overrides the former. - // If we have some special type, specify it on the command line (like "-d scsi"). - // Note that the latter "-d" option overrides the former, so we're ok with multiple ones. - std::string type_arg = get_type_arg_name(this->get_detected_type()); - - if (!type_arg.empty()) { - if (!config_options.empty()) { - config_options += " "; - } - config_options += "-d " + type_arg; + // lowest priority - the detected type + std::vector args; + if (!get_type_argument().empty()) { + args.push_back("-d " + get_type_argument()); + } + // extra args, as specified manually in CLI or when adding the drive + if (!get_extra_arguments().empty()) { + args.push_back(get_extra_arguments()); } - return config_options; + // config options, as specified in preferences. + std::string config_options = app_get_device_option(get_device(), get_type_argument()); + if (!config_options.empty()) { + args.push_back(config_options); + } + + return hz::string_join(args, " "); } @@ -846,7 +833,7 @@ std::string StorageDevice::execute_smartctl(const std::string& command_options, // 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 // && this->get_type() == "" + if (check_type && this->get_detected_type() == detected_type_unknown && app_pcre_match("/specify device type with the -d option/mi", output)) { this->set_detected_type(detected_type_invalid); } diff --git a/gsmartcontrol/src/applib/storage_device.h b/gsmartcontrol/src/applib/storage_device.h index 8ddb066..fd989c1 100644 --- a/gsmartcontrol/src/applib/storage_device.h +++ b/gsmartcontrol/src/applib/storage_device.h @@ -33,7 +33,6 @@ class StorageDevice : public hz::intrusive_ptr_referenced { detected_type_unknown, // unknown. will be autodetected by smartctl detected_type_invalid, // this is set by smartctl executor if it detects invalid type (but not if it's scsi). detected_type_cddvd, // unsupported by smartctl, only basic info is given. - detected_type_scsi // this is used to force "-d scsi" to execute IDENTIFY command. }; @@ -41,10 +40,6 @@ class StorageDevice : public hz::intrusive_ptr_referenced { static std::string get_type_readable_name(detected_type_t type); - /// This gives a string which, if not empty, can be given as a parameter of "-d". - static std::string get_type_arg_name(detected_type_t type); - - /// Statuses of various states enum status_t { status_enabled, ///< SMART, AODC @@ -138,7 +133,7 @@ class StorageDevice : public hz::intrusive_ptr_referenced { /// Set extra arguments smartctl - void set_extra_argument(const std::string& args); + void set_extra_arguments(const std::string& args); /// Get extra arguments smartctl std::string get_extra_arguments() const; diff --git a/gsmartcontrol/src/applib/storage_settings.h b/gsmartcontrol/src/applib/storage_settings.h index 4a6c2cb..0859911 100644 --- a/gsmartcontrol/src/applib/storage_settings.h +++ b/gsmartcontrol/src/applib/storage_settings.h @@ -68,7 +68,7 @@ inline std::string app_serialize_device_option_map(const device_option_map_t& op -inline std::string app_get_device_option(const std::string& dev) +inline std::string app_get_device_option(const std::string& dev, const std::string& type_arg) { if (dev.empty()) return std::string(); @@ -79,11 +79,27 @@ inline std::string app_get_device_option(const std::string& dev) device_option_map_t devmap = app_unserialize_device_option_map(devmap_str); - device_option_map_t::const_iterator iter = devmap.find(dev); - if (iter == devmap.end()) - return std::string(); + // try the concrete type first + if (!type_arg.empty()) { + device_option_map_t::const_iterator iter = devmap.find(dev + "::" + type_arg); + if (iter != devmap.end()) { + return iter->second; + } + } - return iter->second; + // in case there's a trailing delimiter + device_option_map_t::const_iterator iter = devmap.find(dev + "::" + type_arg); + if (iter != devmap.end()) { + return iter->second; + } + + // just the device name + iter = devmap.find(dev); + if (iter != devmap.end()) { + return iter->second; + } + + return std::string(); } diff --git a/gsmartcontrol/src/gsc_init.cpp b/gsmartcontrol/src/gsc_init.cpp index 8a578f8..2e611f5 100644 --- a/gsmartcontrol/src/gsc_init.cpp +++ b/gsmartcontrol/src/gsc_init.cpp @@ -207,12 +207,20 @@ inline bool parse_cmdline_args(CmdArgs& args, int& argc, char**& argv) { static const GOptionEntry arg_entries[] = { - { "no-locale", 'l', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args.arg_locale), "Don't use system locale", NULL }, - { "version", 'V', 0, G_OPTION_ARG_NONE, &(args.arg_version), "Display version information", NULL }, - { "no-scan", '\0', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args.arg_scan), "Don't scan devices on startup", NULL }, - { "no-hide-tabs", '\0', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args.arg_hide_tabs), "Don't hide non-identity tabs when SMART is disabled. Useful for debugging.", NULL }, - { "add-virtual", '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &(args.arg_add_virtual), "Load smartctl data from file, creating a virtual drive", NULL }, - { "add-device", '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &(args.arg_add_device), "Add this device to device list. Useful with --no-scan to list certain drives only.", NULL }, + { "no-locale", 'l', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args.arg_locale), + "Don't use system locale", NULL }, + { "version", 'V', 0, G_OPTION_ARG_NONE, &(args.arg_version), + "Display version information", NULL }, + { "no-scan", '\0', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args.arg_scan), + "Don't scan devices on startup", NULL }, + { "no-hide-tabs", '\0', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(args.arg_hide_tabs), + "Don't hide non-identity tabs when SMART is disabled. Useful for debugging.", NULL }, + { "add-virtual", '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &(args.arg_add_virtual), + "Load smartctl data from file, creating a virtual drive. You can specify this option multiple times.", NULL }, + { "add-device", '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &(args.arg_add_device), + "Add this device to device list. The format of the device is \"::::\", where type and extra_args are optional." + " This option is useful with --no-scan to list certain drives only. You can specify this option multiple times." + "Example: --add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device '/dev/sdb::::-T permissive'", NULL }, { NULL } }; @@ -319,7 +327,7 @@ bool app_init_and_loop(int& argc, char**& argv) load_devices.push_back(entry); } } - std::string load_devices_str = hz::string_join(load_devices, ", "); // for display purposes only + std::string load_devices_str = hz::string_join(load_devices, "; "); // for display purposes only // it's here because earlier there are no domains diff --git a/gsmartcontrol/src/gsc_main_window.cpp b/gsmartcontrol/src/gsc_main_window.cpp index 2794a38..64af132 100644 --- a/gsmartcontrol/src/gsc_main_window.cpp +++ b/gsmartcontrol/src/gsc_main_window.cpp @@ -145,7 +145,14 @@ void GscMainWindow::populate_iconview(bool smartctl_valid) if (rconfig::get_data("/runtime/gui/add_devices_on_startup", load_devices)) { for (unsigned int i = 0; i < load_devices.size(); ++i) { if (!load_devices[i].empty()) { - add_device(load_devices[i]); + std::vector parts; + hz::string_split(load_devices[i], "::", parts, false); + std::string file = (parts.size() > 0 ? parts.at(0) : ""); + std::string type_arg = (parts.size() > 1 ? parts.at(1) : ""); + std::string extra_args = (parts.size() > 2 ? parts.at(2) : ""); + if (!file.empty()) { + add_device(file, type_arg, extra_args); + } } } } @@ -1027,7 +1034,7 @@ void GscMainWindow::rescan_devices() -bool GscMainWindow::add_device(const std::string& file) +bool GscMainWindow::add_device(const std::string& file, const std::string& type_arg, const std::string& extra_args) { #ifndef _WIN32 // win32 doesn't have device files, so skip the check hz::File f(file); @@ -1040,6 +1047,8 @@ bool GscMainWindow::add_device(const std::string& file) #endif StorageDeviceRefPtr d(new StorageDevice(file)); + d->set_type_argument(type_arg); + d->set_extra_arguments(extra_args); d->set_is_manually_added(true); SmartctlExecutorGuiRefPtr ex(new SmartctlExecutorGui()); @@ -1199,7 +1208,10 @@ void GscMainWindow::show_add_device_chooser() "For example, pd0 means the first physical drive", dev, last_str, this, false)) { last_str = dev; // safe for the future - this->add_device(dev); // both the GUI and the API is in utf-8, no conversion is necessary. + std::string type_arg = ""; /// TODO + std::string extra_args = ""; /// TODO + // both the GUI and the API is in utf-8, no conversion is necessary. + this->add_device(dev, type_arg, extra_args); } @@ -1231,7 +1243,9 @@ void GscMainWindow::show_add_device_chooser() last_dir = dialog.get_current_folder(); // safe for the future std::string file = dialog.get_filename(); // in fs encoding - this->add_device(file); + std::string type_arg = ""; /// TODO + std::string extra_args = ""; /// TODO + this->add_device(file, type_arg, extra_args); break; } diff --git a/gsmartcontrol/src/gsc_main_window.h b/gsmartcontrol/src/gsc_main_window.h index 72ac21d..ff37a6f 100644 --- a/gsmartcontrol/src/gsc_main_window.h +++ b/gsmartcontrol/src/gsc_main_window.h @@ -52,7 +52,7 @@ class GscMainWindow : public AppUIResWidget { // manually add device file to icon list - bool add_device(const std::string& file); + bool add_device(const std::string& file, const std::string& type_arg, const std::string& extra_args); // read smartctl data from file, add it as a virtual drive to icon list diff --git a/gsmartcontrol/src/gsc_preferences_window.cpp b/gsmartcontrol/src/gsc_preferences_window.cpp index 78d40c1..7e77133 100644 --- a/gsmartcontrol/src/gsc_preferences_window.cpp +++ b/gsmartcontrol/src/gsc_preferences_window.cpp @@ -210,7 +210,9 @@ GscPreferencesWindow::GscPreferencesWindow(BaseObjectType* gtkcobj, const app_ui Gtk::Entry* device_options_device_entry = 0; APP_UI_RES_AUTO_CONNECT(device_options_device_entry, changed); - Glib::ustring device_options_tooltip = "Device name (for example, %s)"; + Glib::ustring device_options_tooltip = "Format: ::, where \"::\" part is optional. " + " An example of device name would be %s. Type is an argument of smartctl -d option and can be " + " used to specify a drive behind a RAID device, e.g. /dev/twa0::3ware,2"; #ifdef _WIN32 device_options_tooltip = hz::string_sprintf(device_options_tooltip.c_str(), "\"pd0\" for the first physical drive"); #else