From c3a6f0b6249c23a1c5704e326d86c19022484976 Mon Sep 17 00:00:00 2001 From: Alexander Shaduri Date: Thu, 2 May 2024 14:38:31 +0400 Subject: [PATCH] Implemented detection of device type. --- src/applib/smartctl_json_basic_parser.cpp | 4 +- src/applib/smartctl_parser.cpp | 5 ++ src/applib/smartctl_version_parser.h | 1 + src/applib/storage_detector.cpp | 1 + src/applib/storage_detector_linux.cpp | 31 +++++++---- src/applib/storage_device.cpp | 68 +++++++++++++++-------- 6 files changed, 74 insertions(+), 36 deletions(-) diff --git a/src/applib/smartctl_json_basic_parser.cpp b/src/applib/smartctl_json_basic_parser.cpp index e33ebf7..3ccd8ec 100644 --- a/src/applib/smartctl_json_basic_parser.cpp +++ b/src/applib/smartctl_json_basic_parser.cpp @@ -72,8 +72,6 @@ hz::ExpectedVoid SmartctlJsonBasicParser::parse_section_bas { using namespace SmartctlJsonParserHelpers; - // TODO CD/DVD, RAID - bool smart_supported = true; // TODO bool smart_enabled = true; // TODO @@ -97,6 +95,8 @@ hz::ExpectedVoid SmartctlJsonBasicParser::parse_section_bas // 2. Present in devices for which we do not have specialized parsers (USB, etc.) static const std::vector> info_keys = { + {"device/type", _("Smartctl Device Type"), string_formatter()}, // nvme, sat, etc. + {"vendor", _("Vendor"), string_formatter()}, // Flash drive {"product", _("Product"), string_formatter()}, // Flash drive diff --git a/src/applib/smartctl_parser.cpp b/src/applib/smartctl_parser.cpp index e85fea0..eb623c9 100644 --- a/src/applib/smartctl_parser.cpp +++ b/src/applib/smartctl_parser.cpp @@ -11,12 +11,17 @@ Copyright: #include #include // isspace +#include +#include #include "smartctl_parser.h" +#include "ata_storage_property.h" +#include "hz/error_container.h" #include "smartctl_text_ata_parser.h" #include "smartctl_json_ata_parser.h" #include "smartctl_json_basic_parser.h" #include "smartctl_text_basic_parser.h" +#include "storage_property_repository.h" //#include "ata_storage_property_descr.h" diff --git a/src/applib/smartctl_version_parser.h b/src/applib/smartctl_version_parser.h index 644a431..3394e2a 100644 --- a/src/applib/smartctl_version_parser.h +++ b/src/applib/smartctl_version_parser.h @@ -19,6 +19,7 @@ Copyright: #include "smartctl_parser_types.h" #include "ata_storage_property_descr.h" +#include "storage_device_detected_type.h" diff --git a/src/applib/storage_detector.cpp b/src/applib/storage_detector.cpp index 728aed9..964dcb7 100644 --- a/src/applib/storage_detector.cpp +++ b/src/applib/storage_detector.cpp @@ -12,6 +12,7 @@ Copyright: #include "local_glibmm.h" #include // compose() #include +#include #include "build_config.h" diff --git a/src/applib/storage_detector_linux.cpp b/src/applib/storage_detector_linux.cpp index 6460b18..de6e165 100644 --- a/src/applib/storage_detector_linux.cpp +++ b/src/applib/storage_detector_linux.cpp @@ -9,25 +9,36 @@ Copyright: /// \weakgroup applib /// @{ -#include "build_config.h" +#include "storage_detector_linux.h" +#include "command_executor.h" #include "local_glibmm.h" + #include // std::find #include // std::fgets(), std::FILE // #include // ENXIO +#include +#include +#include #include #include +#include #include #include // std::pair #include +#include "build_config.h" +#include "command_executor_factory.h" +#include "hz/error_container.h" +#include "hz/string_algo.h" #include "hz/debug.h" #include "hz/fs.h" #include "hz/string_num.h" #include "rconfig/rconfig.h" #include "app_pcrecpp.h" -#include "storage_detector_linux.h" +#include "storage_detector.h" #include "storage_detector_helpers.h" +#include "storage_device.h" @@ -298,7 +309,7 @@ inline std::string read_proc_scsi_sg_devices_file(std::vector>& R"(^([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+))"); for (std::size_t i = 0; i < lines.size(); ++i) { - std::string trimmed = hz::string_trim_copy(lines[i]); + const std::string trimmed = hz::string_trim_copy(lines[i]); std::vector line(9); if (parse_re.PartialMatch(trimmed, &line[0], &line[1], &line[2], &line[3], &line[4], &line[5], &line[6], &line[7], &line[8])) { std::vector line_num(line.size(), -1); @@ -371,7 +382,7 @@ inline hz::ExpectedVoid detect_drives_linux_proc_partition debug_out_info("app", DBG_FUNC_MSG << "Detecting drives through partitions file (/proc/partitions by default; set \"system/linux_proc_partitions_path\" config key to override).\n"); std::vector lines; - std::string error_msg = read_proc_partitions_file(lines); + const std::string error_msg = read_proc_partitions_file(lines); if (!error_msg.empty()) { return hz::Unexpected(StorageDetectorError::ProcReadError, error_msg); } @@ -410,7 +421,7 @@ inline hz::ExpectedVoid detect_drives_linux_proc_partition if (blacked) continue; - std::string path = "/dev/" + dev; // let's just hope it's really /dev. + const std::string path = "/dev/" + dev; // let's just hope it's really /dev. if (std::find(devices.begin(), devices.end(), path) == devices.end()) { // there may be duplicates devices.push_back(path); } @@ -525,7 +536,7 @@ inline hz::ExpectedVoid detect_drives_linux_3ware( continue; // not a supported controller } - int host_num = vendor_model.first; + const int host_num = vendor_model.first; debug_out_dump("app", "Found LSI/AMCC/3ware controller in SCSI file, SCSI host " << host_num << ".\n"); @@ -557,7 +568,7 @@ inline hz::ExpectedVoid detect_drives_linux_3ware( // else we default to twl, twa, twe (in this order) } - // We can't map twaX to scsiY, so lets assume the relative order is the same. + // We can't map twaX to scsiY, so let's assume the relative order is the same. std::string dev = std::string("/dev/") + dev_base + hz::number_to_string_nolocale(device_numbers[dev_base]); ++device_numbers[dev_base]; @@ -651,7 +662,7 @@ inline hz::ExpectedVoid detect_drives_linux_adaptec( if (!app_pcre_match("/Vendor: Adaptec /i", vendors_model.second)) { continue; // not a supported controller } - int host_num = vendors_model.first; + const int host_num = vendors_model.first; debug_out_dump("app", "Found Adaptec controller in SCSI file, SCSI host " << host_num << ".\n"); // Skip additional adapters with the same host, since they are the same adapters @@ -673,11 +684,11 @@ inline hz::ExpectedVoid detect_drives_linux_adaptec( continue; } - std::string dev = std::string("/dev/sg") + hz::number_to_string_nolocale(sg_num); + const std::string dev = std::string("/dev/sg") + hz::number_to_string_nolocale(sg_num); auto drive = std::make_shared(dev, std::string("sat")); auto fetch_status = drive->fetch_basic_data_and_parse(smartctl_ex); - std::string output = drive->get_basic_output(); + const std::string output = drive->get_basic_output(); // Note: Not sure about this, have to check with real SAS drives if (app_pcre_match("/Device Read Identity Failed/mi", output)) { diff --git a/src/applib/storage_device.cpp b/src/applib/storage_device.cpp index 54dce32..54c9901 100644 --- a/src/applib/storage_device.cpp +++ b/src/applib/storage_device.cpp @@ -34,6 +34,7 @@ Copyright: #include "smartctl_executor.h" #include "smartctl_version_parser.h" #include "ata_storage_property_descr.h" +#include "build_config.h" //#include "smartctl_text_parser_helper.h" //#include "ata_storage_property_descr.h" @@ -160,7 +161,7 @@ hz::ExpectedVoid StorageDevice::parse_basic_data() auto basic_parser = SmartctlParser::create(SmartctlParserType::Basic, SmartctlOutputFormat::Json); DBG_ASSERT_RETURN(basic_parser, hz::Unexpected(StorageDeviceError::ParseError, _("Cannot create parser"))); - // This also detects the drive type and adds it to "_custom/parser_detected_drive_type" property. + // This also fills the drive type properties. auto parse_status = basic_parser->parse(this->get_basic_output()); if (!parse_status) { return hz::Unexpected(StorageDeviceError::ParseError, @@ -168,7 +169,7 @@ hz::ExpectedVoid StorageDevice::parse_basic_data() } // See if we can narrow down the drive type from what was detected - // by StorageDetector and "_custom/parser_detected_drive_type" property set by Basic parser. + // by StorageDetector and properties set by Basic parser. auto basic_property_repo = basic_parser->get_property_repository(); // Make detected type more exact. @@ -584,39 +585,58 @@ void StorageDevice::read_common_properties() void StorageDevice::detect_drive_type_from_properties(const StoragePropertyRepository& property_repo) { - auto drive_type_prop = property_repo.lookup_property("_custom/parser_detected_drive_type"); - if (!drive_type_prop.empty()) { + // This is set by Text parser + if (auto drive_type_prop = property_repo.lookup_property("_custom/parser_detected_drive_type"); !drive_type_prop.empty()) { const auto& drive_type_storable_str = drive_type_prop.get_value(); set_detected_type(StorageDeviceDetectedTypeExt::get_by_storable_name(drive_type_storable_str, StorageDeviceDetectedType::BasicScsi)); - } - switch (get_detected_type()) { - case StorageDeviceDetectedType::Unknown: - set_detected_type(StorageDeviceDetectedType::BasicScsi); // fall back to basic scsi parser - break; - case StorageDeviceDetectedType::AtaAny: - // Find out if it's SSD or HDD - { + // Find out if it's SSD or HDD + if (get_detected_type() == StorageDeviceDetectedType::AtaAny) { auto rpm_prop = property_repo.lookup_property("rotation_rate"); if (rpm_prop.empty() || rpm_prop.get_value() == 0) { set_detected_type(StorageDeviceDetectedType::AtaSsd); } else { set_detected_type(StorageDeviceDetectedType::AtaHdd); } - break; } - case StorageDeviceDetectedType::AtaHdd: - case StorageDeviceDetectedType::AtaSsd: - case StorageDeviceDetectedType::Nvme: - case StorageDeviceDetectedType::BasicScsi: - case StorageDeviceDetectedType::CdDvd: - case StorageDeviceDetectedType::UnsupportedRaid: - // leave as is - break; - case StorageDeviceDetectedType::NeedsExplicitType: - DBG_ASSERT(0); - break; } + + // This is set by JSON parser + if (auto device_type_prop = property_repo.lookup_property("device/type"); !device_type_prop.empty()) { + // Note: USB flash drives in non-scsi mode do not have this property. + const auto& smartctl_type = device_type_prop.get_value(); + + if (smartctl_type == "scsi") { // USB flash in scsi mode, optical, scsi, etc. + if (BuildEnv::is_kernel_linux() && get_device_base().starts_with("sr")) { + set_detected_type(StorageDeviceDetectedType::CdDvd); + } else { + set_detected_type(StorageDeviceDetectedType::BasicScsi); + } + + } else if (smartctl_type == "sat") { // (S)ATA, including behind supported RAID controllers + // Find out if it's SSD or HDD + auto rpm_prop = property_repo.lookup_property("rotation_rate"); + if (rpm_prop.empty() || rpm_prop.get_value() == 0) { + set_detected_type(StorageDeviceDetectedType::AtaSsd); + } else { + set_detected_type(StorageDeviceDetectedType::AtaHdd); + } + + } else if (smartctl_type == "nvme") { // NVMe SSD + set_detected_type(StorageDeviceDetectedType::Nvme); + + } else { + // TODO Detect unsupported RAID + debug_out_warn("app", "Unsupported type " << smartctl_type << " reported by smartctl for " << get_device_with_type() << "\n"); + } + } + + if (get_detected_type() == StorageDeviceDetectedType::Unknown) { + set_detected_type(StorageDeviceDetectedType::BasicScsi); // fall back to basic scsi parser + } + + debug_out_info("app", "Device " << get_device_with_type() << " detected after parser to be of type " + << StorageDeviceDetectedTypeExt::get_storable_name(get_detected_type()) << "\n"); }