diff --git a/gsmartcontrol/README.txt b/gsmartcontrol/README.txt index 20e9288..bde5b68 100644 --- a/gsmartcontrol/README.txt +++ b/gsmartcontrol/README.txt @@ -148,9 +148,7 @@ Windows port uses pd0, pd1, etc... for physical drives 0, 1, etc... . * Solaris - Tested with Solaris 10 / x86 / gcc-3.4.3 / blastwave, Solaris 10 / x86 / sunstudio12 / sunfreeware. OpenSolaris should work but has -not been tested yet. Note: smartctl doesn't seem to support ATA under Solaris, -while GSmartControl doesn't support SCSI yet. This only leaves various USB to -ATA bridges. See smartctl (5), -d option for details. +not been tested yet. * Mac OS X - 3rd-party testing by Fink project. diff --git a/gsmartcontrol/TODO b/gsmartcontrol/TODO index 450ed16..0d78199 100644 --- a/gsmartcontrol/TODO +++ b/gsmartcontrol/TODO @@ -126,23 +126,8 @@ If a drive is not recognized, suggest using -d (sat, sat,12, ...) RAID: http://sourceforge.net/apps/trac/smartmontools/wiki/Supported_RAID-Controllers - Areca Linux: - Call as: smartctl -i -d areca,[1-24] /dev/sg0 - Detection: - Check if /proc/scsi/sg/device_strs contains "Areca RAID controller". - If not, there's no Areca controller present. - Check /proc/scsi/sg/devices, the line # with type 3 is the sg* number. - The "host" field value is the N value in /sys, see below. - 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. - 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). - 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?). - - Areca FreeBSD: unresearched. since 5.42. + Areca FreeBSD: since 5.42. + Call as: smartctl -a -d areca,[1-24] /dev/arcmsr1 3ware FreeBSD: Call as: smartctl -i -d 3ware,[0-127] /dev/twa0 (or twe0) @@ -182,7 +167,8 @@ RAID: It may not return the "SMART Health Status: OK" line in smartctl -i for some reason? (It returns it in -a though, and in some freebsd output I found). If no /dev/cciss exists, you may need to run "cd /dev; ./MAKEDEV cciss". - Smartctl SVN has some /dev/sg* stuff, check it out. + Smartctl SVN has some /dev/sg* stuff, check it out. (?) + May need "-d sat+cciss,N" for SATA. CCISS (HP (Compaq) Smart Array Controller) FreeBSD: Seems to be the same as Linux (uses the same driver?). diff --git a/gsmartcontrol/src/applib/storage_detector_linux.cpp b/gsmartcontrol/src/applib/storage_detector_linux.cpp index d1ff93c..5b46725 100644 --- a/gsmartcontrol/src/applib/storage_detector_linux.cpp +++ b/gsmartcontrol/src/applib/storage_detector_linux.cpp @@ -144,6 +144,18 @@ inline bool read_proc_file(hz::File& file, std::vector& lines) +/// Read a file completely from /proc +inline bool read_proc_complete_file(hz::File& file, std::string& contents) +{ + // The crude way + std::vector lines; + bool status = read_proc_file(file, lines); + contents = hz::string_join(lines, '\n'); + return status; +} + + + /// Read /proc/partitions file. Return error message on error. inline std::string read_proc_partitions_file(std::vector& lines) { @@ -438,17 +450,18 @@ inline std::string detect_drives_linux_proc_partitions(std::vector& drives, ExecutorFactoryRefPtr ex_factory) @@ -470,23 +484,26 @@ inline std::string detect_drives_linux_3ware(std::vector& d bool twa_found = false; bool twe_found = false; + bool twl_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])(?:[ \\t]*|$)/", hz::string_trim_copy(lines[i]), &dev)) { + if (app_pcre_match("/^[ \\t]*[0-9]+[ \\t]+(tw[ael])(?:[ \\t]*|$)/", 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 if (dev == "twl") { + twl_found = true; } else { DBG_ASSERT(0); // error in regexp? } } } - if (!twa_found && !twe_found) { + if (!twa_found && !twe_found && !twl_found) { return std::string(); // no controllers } @@ -502,13 +519,14 @@ inline std::string detect_drives_linux_3ware(std::vector& d std::set controller_hosts; for (std::size_t i = 0; i < vendors_models.size(); ++i) { + // TODO Not sure which vendor twl uses. if (!app_pcre_match("/Vendor: (AMCC)|(3ware) /i", vendors_models[i].second)) { continue; // not a supported controller } int host_num = vendors_models[i].first; - debug_out_dump("app", "Found AMCC/3ware controller in SCSI file, SCSI host " << host_num << ".\n"); + debug_out_dump("app", "Found AMCC/LSI/3ware controller in SCSI file, SCSI host " << host_num << ".\n"); // Skip additional adapters with the same host, since they are the same adapters // with different LUNs. @@ -520,7 +538,13 @@ inline std::string detect_drives_linux_3ware(std::vector& d // We can't handle both twa and twe in one system, so assume twa by default. // We can't map twaX to scsiY, so lets assume the relative order is the same. - std::string dev = std::string("/dev/") + (twa_found ? "twa" : "twe") + hz::number_to_string(controller_hosts.size() - 1); + std::string dev_base = "twa"; + if (twe_found) { + dev_base = "twe"; + } else if (twl_found) { + dev_base = "twl"; + } + std::string dev = std::string("/dev/") + dev_base + hz::number_to_string(controller_hosts.size() - 1); error_msg = tw_cli_get_drives(dev, vendors_models[i].first, drives, ex_factory, false); if (!error_msg.empty()) { // no tw_cli @@ -530,12 +554,12 @@ inline std::string detect_drives_linux_3ware(std::vector& d } if (!error_msg.empty()) { - debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get number of ports on a 3ware controller.\n"); + debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get the drives on ports of AMCC/LSI/3ware controller: " << error_msg << "\n"); } } if (controller_hosts.empty()) { - debug_out_warn("app", DBG_FUNC_MSG << "3ware entry found in devices file, but SCSI file contains no known entries.\n"); + debug_out_warn("app", DBG_FUNC_MSG << "AMCC/LSI/3ware entry found in devices file, but SCSI file contains no known entries.\n"); } return error_msg; @@ -544,8 +568,9 @@ inline std::string detect_drives_linux_3ware(std::vector& d /** -Detect drives behind Adaptec RAID controller. +Detect drives behind Adaptec RAID controller (aacraid driver). Tested using Adaptec RAID 5805 (SAS / SATA controller). +Uses /dev/sgN devices (with -d sat for SATA and -d scsi (default) for SCSI). Adaptec Linux detection strategy: Check /proc/devices for "aac"; if it's there, continue. @@ -655,6 +680,109 @@ inline std::string detect_drives_linux_adaptec(std::vector& +/** +Areca Linux (arcmsr driver): +Call as: smartctl -i -d areca,[1-24] /dev/sg0 + +Detection: + There don't seem to be any entries in /proc/devices. + Check /proc/scsi/scsi for "Vendor: Areca". + (Alternatively, /proc/scsi/sg/device_strs should contain "Areca RAID controller"). +Detect SCSI host (N in hostN below): + It's N of scsiN in /proc/scsi/sg/device_strs of the entry with "Vendor: Areca", + which has type 3 in /proc/scsi/sg/devices (to filter out logical volumes) and id 16. + 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). +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. +*/ +inline std::string detect_drives_linux_areca(std::vector& drives, ExecutorFactoryRefPtr ex_factory) +{ + std::vector< std::pair > vendors_models; + std::string error_msg = read_proc_scsi_scsi_file(vendors_models); + if (!error_msg.empty()) { + return error_msg; + } + + std::set controller_hosts; + + for (std::size_t i = 0; i < vendors_models.size(); ++i) { + if (!app_pcre_match("/Vendor: Areca /i", vendors_models[i].second)) { + continue; // not a supported controller + } + int host_num = vendors_models[i].first; + debug_out_dump("app", "Found Areca controller in SCSI file, SCSI host " << host_num << ".\n"); + + // Skip additional adapters with the same host (they are volumes) + if (controller_hosts.find(host_num) != controller_hosts.end()) { + debug_out_dump("app", "Skipping adapter with SCSI host " << host_num << ", host already found.\n"); + continue; + } + controller_hosts.insert(host_num); + } + + if (controller_hosts.empty()) { + return error_msg; + } + + std::vector< std::vector > sg_entries; + error_msg = read_proc_scsi_sg_devices_file(sg_entries); + if (!error_msg.empty()) { + return error_msg; + } + + hz::intrusive_ptr smartctl_ex = ex_factory->create_executor(ExecutorFactory::ExecutorSmartctl); + + for (std::set::iterator iter = controller_hosts.begin(); iter != controller_hosts.end(); ++iter) { + const int host_num = *iter; + + for (std::size_t sg_num = 0; sg_num < sg_entries.size(); ++sg_num) { + if (sg_entries[sg_num].size() < 5) { + // We need at least 5 columns in that file + continue; + } + const int type = sg_entries[sg_num][4]; // type 3 is Areca controller + const int id = sg_entries[sg_num][2]; // id should be 16 (as per smartmontools) + if (sg_entries[sg_num][0] != host_num || id != 16 || type != 3) { + continue; + } + + int max_ports = 0; + + 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_complete_file(ports_file, ports_file_contents)) { + debug_out_warn("app", DBG_FUNC_MSG << "Couldn't read number of ports on Areca controller: " + << ports_file.get_error_utf8() << ", trying manually.\n"); + } else { + hz::string_is_numeric(hz::string_trim_copy(ports_file_contents), max_ports); + } + + 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); + + error_msg = smartctl_get_drives(dev, "areca,%d", 1, max_ports, drives, ex_factory); + + if (!error_msg.empty()) { + debug_out_warn("app", DBG_FUNC_MSG << "Couldn't get the drives on ports of Areca controller: " << error_msg << "\n"); + } + } + } + + return error_msg; +} + + + } // anon ns @@ -681,6 +809,11 @@ std::string detect_drives_linux(std::vector& drives, Execut error_msgs.push_back(error_msg); } + error_msg = detect_drives_linux_areca(drives, ex_factory); + if (!error_msg.empty()) { + error_msgs.push_back(error_msg); + } + error_msg = detect_drives_linux_adaptec(drives, ex_factory); if (!error_msg.empty()) { error_msgs.push_back(error_msg);