From a2379996e6d03c458bc2a14798a15424d8dde0bd Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Mon, 25 Jan 2021 10:14:56 +0100 Subject: [PATCH] sgutils2: add scsi_inquiry command --- src/tools/sgutils2.rs | 123 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/src/tools/sgutils2.rs b/src/tools/sgutils2.rs index 6cb4ccf0..62e5e3dc 100644 --- a/src/tools/sgutils2.rs +++ b/src/tools/sgutils2.rs @@ -6,8 +6,11 @@ use std::os::unix::io::AsRawFd; -use anyhow::{bail, Error}; +use anyhow::{bail, format_err, Error}; use libc::{c_char, c_int}; +use endian_trait::Endian; + +use proxmox::tools::io::ReadExt; // Opaque wrapper for sg_pt_base #[repr(C)] @@ -19,6 +22,76 @@ impl Drop for SgPtBase { } } +/// Peripheral device type text (see `inquiry` command) +/// +/// see [https://en.wikipedia.org/wiki/SCSI_Peripheral_Device_Type] +pub const PERIPHERAL_DEVICE_TYPE_TEXT: [&'static str; 32] = [ + "Disk Drive", + "Tape Drive", + "Printer", + "Processor", + "Write-once", + "CD-ROM", // 05h + "Scanner", + "Optical", + "Medium Changer", // 08h + "Communications", + "ASC IT8", + "ASC IT8", + "RAID Array", + "Enclosure Services", + "Simplified direct-access", + "Optical card reader/writer", + "Bridging Expander", + "Object-based Storage", + "Automation/Drive Interface", + "Security manager", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Unknown", +]; + +#[repr(C, packed)] +#[derive(Endian)] +struct InquiryPage { + peripheral_type: u8, + rmb: u8, + version: u8, + flags3: u8, + additional_length: u8, + flags5: u8, + flags6: u8, + flags7: u8, + vendor: [u8; 8], + product: [u8; 16], + revision: [u8; 4], + // additional data follows, but we do not need that +} + +/// Inquiry result +#[derive(Debug)] +pub struct InquiryInfo { + /// Peripheral device type (0-31) + pub peripheral_type: u8, + /// Peripheral device type as string + pub peripheral_type_text: String, + /// Vendor + pub vendor: String, + /// Product + pub product: String, + /// Revision + pub revision: String, +} + pub const SCSI_PT_RESULT_GOOD:c_int = 0; pub const SCSI_PT_RESULT_STATUS:c_int = 1; pub const SCSI_PT_RESULT_SENSE:c_int = 2; @@ -27,7 +100,7 @@ pub const SCSI_PT_RESULT_OS_ERR:c_int = 4; #[link(name = "sgutils2")] extern { - + #[allow(dead_code)] fn scsi_pt_open_device( device_name: * const c_char, @@ -276,3 +349,49 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { Ok(()) } } + +// Useful helpers + +/// Converts SCSI ASCII text into String, trim zero and spaces +pub fn scsi_ascii_to_string(data: &[u8]) -> String { + String::from_utf8_lossy(data) + .trim_matches(char::from(0)) + .trim() + .to_string() +} + +/// Read SCSI Inquiry page +/// +/// Returns Product/Vendor/Revision and device type. +pub fn scsi_inquiry( + file: &mut F, +) -> Result { + + let allocation_len: u8 = u8::MAX; + let mut sg_raw = SgRaw::new(file, allocation_len as usize)?; + sg_raw.set_timeout(30); // use short timeout + + let mut cmd = Vec::new(); + cmd.extend(&[0x12, 0, 0, 0, allocation_len, 0]); // INQUIRY + + let data = sg_raw.do_command(&cmd) + .map_err(|err| format_err!("SCSI inquiry failed - {}", err))?; + + proxmox::try_block!({ + let mut reader = &data[..]; + + let page: InquiryPage = unsafe { reader.read_be_value()? }; + + let peripheral_type = page.peripheral_type & 31; + + let info = InquiryInfo { + peripheral_type, + peripheral_type_text: PERIPHERAL_DEVICE_TYPE_TEXT[peripheral_type as usize].to_string(), + vendor: scsi_ascii_to_string(&page.vendor), + product: scsi_ascii_to_string(&page.product), + revision: scsi_ascii_to_string(&page.revision), + }; + + Ok(info) + }).map_err(|err: Error| format_err!("decode inquiry page failed - {}", err)) +}