From cc238ef5521071f649affa2e029462a14e5af76a Mon Sep 17 00:00:00 2001 From: jcg Date: Wed, 13 Sep 2023 19:42:22 +0800 Subject: [PATCH 002/103] feature: add scsi_id --- exts/devmaster/Cargo.toml | 4 + exts/devmaster/src/bin/tools/scsi_id/main.rs | 1577 ++++++++++++++++++ 2 files changed, 1581 insertions(+) create mode 100644 exts/devmaster/src/bin/tools/scsi_id/main.rs diff --git a/exts/devmaster/Cargo.toml b/exts/devmaster/Cargo.toml index edd73ca2..c52e9880 100644 --- a/exts/devmaster/Cargo.toml +++ b/exts/devmaster/Cargo.toml @@ -13,6 +13,10 @@ path = "src/bin/devctl/main.rs" name = "ata_id" path = "src/bin/tools/ata_id/main.rs" +[[bin]] +name = "scsi_id" +path = "src/bin/tools/scsi_id/main.rs" + [lib] name = "libdevmaster" path = "src/lib/lib.rs" diff --git a/exts/devmaster/src/bin/tools/scsi_id/main.rs b/exts/devmaster/src/bin/tools/scsi_id/main.rs new file mode 100644 index 00000000..8098c8bf --- /dev/null +++ b/exts/devmaster/src/bin/tools/scsi_id/main.rs @@ -0,0 +1,1577 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! scsi_id - retrieve and generate a unique identifier +//! + +use basic::IN_SET; +use libc::{c_char, c_void, getopt_long, option, EBADF, EINVAL, ENOSYS}; +use libdevmaster::utils::commons::{encode_devnode_name, replace_chars, replace_whitespace}; +use nix::{ + errno::{self, Errno}, + fcntl::{open, OFlag}, + sys::stat::{self, fstat, major, minor, Mode}, +}; +use scsi_generic_rs::{sg_io_hdr, sg_io_v4, SG_IO}; +use std::{ + ffi::{CStr, CString}, + fs::File, + io::{BufRead, BufReader}, + os::{ + raw::{c_int, c_uchar, c_uint, c_ulonglong}, + unix::prelude::FromRawFd, + }, + process::exit, + ptr::NonNull, + str::from_utf8, + thread, + time::Duration, +}; + +extern "C" { + ///option arg for getopt_long + pub static mut optarg: *mut c_char; + ///option index for getopt_long + pub static mut optind: c_int; +} + +///device in whitelist or blacklist +pub static mut ALL_GOOD: bool = false; +///device specified +pub static mut DEV_SPECIFIED: bool = false; +///config file for scsi_id +pub static mut SCSI_ID_CONFIG: &str = "/etc/scsi_id.config"; +///devnode name +pub static mut MAJ_MIN_DEV: String = String::new(); +///default pagecode +pub static mut DEFAULT_PAGE_CODE: PageCode = PageCode::PageUnspecified; +///default SCSI Generic version +pub static mut DEFAULT_SG_VERSION: u8 = 4; +///replace whitespace +pub static mut REFORMAT_SERIAL: bool = false; +///print values +pub static mut EXPORT: bool = false; +///vendor name +pub static mut VENDOR: String = String::new(); +///encoded vendor name +pub static mut VENDOR_ENC_STR: String = String::new(); +///model name +pub static mut MODEL: String = String::new(); +///encoded model name +pub static mut MODEL_ENC_STR: String = String::new(); +///revision get from ioctl +pub static mut REVISION: String = String::new(); +///device type get from ioctl +pub static mut TYPE_STR: String = String::new(); +///argc get from scsi_id.conf +pub static mut ARGC: i32 = 0; +///argv get from scsi_id.conf +pub static mut ARGV: Vec = Vec::new(); +///default timeout for ioctl +pub const DEF_TIMEOUT: u32 = 5000; +///length of SENSE_BUFF +pub const SENSE_BUFF_LEN: u32 = 32; +///length of SCSI_INQ_BUFF +pub const SCSI_INQ_BUFF_LEN: u32 = 254; +///max length for vendor +pub const VENDOR_LENGTH: usize = 8; +///max length for model +pub const MODEL_LENGTH: usize = 16; +///inquiry cmd for ioctl +pub const INQUIRY_CMD: c_uchar = 0x12; +///inquiry cmd length for ioctl +pub const INQUIRY_CMDLEN: u32 = 6; +///id type values of id descriptors +pub const SCSI_ID_VENDOR_SPECIFIC: c_uchar = 0; +///id type values of id descriptors +pub const SCSI_ID_T10_VENDOR: c_uchar = 1; +///id type values of id descriptors +pub const SCSI_ID_EUI_64: c_uchar = 2; +///id type values of id descriptors +pub const SCSI_ID_NAA: c_uchar = 3; +///id type values of id descriptors +pub const SCSI_ID_RELPORT: c_uchar = 4; +///id type values of id descriptors +pub const SCSI_ID_TGTGROUP: c_uchar = 5; +///naa type values of id descriptors +pub const SCSI_ID_NAA_DONT_CARE: c_uchar = 0xff; +///naa type values of id descriptors +pub const SCSI_ID_NAA_IEEE_REG: c_uchar = 0x05; +///naa type values of id descriptors +pub const SCSI_ID_NAA_IEEE_REG_EXTENDED: c_uchar = 0x06; +///code set values of id descriptors +pub const SCSI_ID_BINARY: c_uchar = 1; +///code set values of id descriptors +pub const SCSI_ID_ASCII: c_uchar = 2; +///SCSI status codes +pub const SCSI_CHECK_CONDITION: c_uchar = 0x02; +///SCSI status codes +pub const SCSI_COMMAND_TERMINATED: c_uchar = 0x22; +///unable to connect before timeout +pub const DID_NO_CONNECT: u32 = 0x01; +///bus remain busy until timeout +pub const DID_BUS_BUSY: u32 = 0x02; +///timed out for some other reason +pub const DID_TIME_OUT: u32 = 0x03; +///transport disrupted and should retry +pub const DID_TRANSPORT_DISPUPTED: u32 = 0x0e; +///driver status +pub const DRIVER_TIMEOUT: u32 = 0x06; +///sense_buffer has been set +pub const DRIVER_SENSE: u32 = 0x08; +///no errors or other information +pub const SG_ERR_CAT_CLEAN: i32 = 0; +///interpreted from sense buffer +pub const SG_ERR_CAT_MEDIA_CHANGED: i32 = 1; +///interpreted from sense buffer +pub const SG_ERR_CAT_RESET: i32 = 2; +///timeout to get sense buffer +pub const SG_ERR_CAT_TIMEOUT: i32 = 3; +///successful command after recovered err +pub const SG_ERR_CAT_RECOVERED: i32 = 4; +///illegal / unsupported command +pub const SG_ERR_CAT_NOTSUPPORTED: i32 = 5; +///illegal / unsupported command +pub const SG_ERR_CAT_RETRY: i32 = 6; +///something else in the sense buffer +pub const SG_ERR_CAT_SENSE: i32 = 98; +///some other error/warning +pub const SG_ERR_CAT_OTHER: i32 = 99; +///subprotocol of io_v4 +pub const BSG_PROTOCOL_SCSI: u32 = 0; +///subprotocol of io_v4 +pub const BSG_SUB_PROTOCOL_SCSI_CMD: u32 = 0; +///dxfer direction of io_hdr +pub const SG_DXFER_FROM_DEV: i32 = -3; +///sense key +pub const RECOVERED_ERROR: u64 = 0x01; +///sense key +pub const ILLEGAL_REQUEST: u64 = 0x05; +///sense key +pub const UNIT_ATTENTION: u64 = 0x06; +///max length for serial +pub const MAX_SERIAL_LEN: i32 = 256; + +#[allow(missing_docs)] +#[derive(Debug)] +pub enum PageCode { + Page83PreSpc3 = -0x83, + PageUnspecified = 0x00, + Page80 = 0x80, + Page83 = 0x83, +} + +impl std::fmt::Display for PageCode { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + PageCode::Page83PreSpc3 => write!(f, "-{:#04x}", 0x83), + PageCode::PageUnspecified => write!(f, "{:#04x}", 0x00), + PageCode::Page80 => write!(f, "{:#04x}", 0x80), + PageCode::Page83 => write!(f, "{:#04x}", 0x83), + } + } +} + +#[allow(missing_docs)] +#[derive(Default, Debug)] +pub struct ScsiIdDevice { + vendor: String, + model: String, + revision: String, + kernel: String, + serial: String, + serial_short: String, + r#type: u8, + use_sg: u8, + unit_serial_number: String, + wwn: String, + wwn_vendor_extension: String, + tgpt_group: String, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[derive(Debug, Copy, Clone)] +pub struct scsi_id_search_values { + id_type: c_uchar, + naa_type: c_uchar, + code_set: c_uchar, +} +#[allow(missing_docs)] +pub static ID_SEARCH_LIST: [scsi_id_search_values; 13] = [ + scsi_id_search_values { + id_type: SCSI_ID_TGTGROUP, + naa_type: SCSI_ID_NAA_DONT_CARE, + code_set: SCSI_ID_BINARY, + }, + scsi_id_search_values { + id_type: SCSI_ID_NAA, + naa_type: SCSI_ID_NAA_IEEE_REG_EXTENDED, + code_set: SCSI_ID_BINARY, + }, + scsi_id_search_values { + id_type: SCSI_ID_NAA, + naa_type: SCSI_ID_NAA_IEEE_REG_EXTENDED, + code_set: SCSI_ID_ASCII, + }, + scsi_id_search_values { + id_type: SCSI_ID_NAA, + naa_type: SCSI_ID_NAA_IEEE_REG, + code_set: SCSI_ID_BINARY, + }, + scsi_id_search_values { + id_type: SCSI_ID_NAA, + naa_type: SCSI_ID_NAA_IEEE_REG, + code_set: SCSI_ID_ASCII, + }, + scsi_id_search_values { + id_type: SCSI_ID_NAA, + naa_type: SCSI_ID_NAA_DONT_CARE, + code_set: SCSI_ID_BINARY, + }, + scsi_id_search_values { + id_type: SCSI_ID_NAA, + naa_type: SCSI_ID_NAA_DONT_CARE, + code_set: SCSI_ID_ASCII, + }, + scsi_id_search_values { + id_type: SCSI_ID_EUI_64, + naa_type: SCSI_ID_NAA_DONT_CARE, + code_set: SCSI_ID_BINARY, + }, + scsi_id_search_values { + id_type: SCSI_ID_EUI_64, + naa_type: SCSI_ID_NAA_DONT_CARE, + code_set: SCSI_ID_ASCII, + }, + scsi_id_search_values { + id_type: SCSI_ID_T10_VENDOR, + naa_type: SCSI_ID_NAA_DONT_CARE, + code_set: SCSI_ID_BINARY, + }, + scsi_id_search_values { + id_type: SCSI_ID_T10_VENDOR, + naa_type: SCSI_ID_NAA_DONT_CARE, + code_set: SCSI_ID_ASCII, + }, + scsi_id_search_values { + id_type: SCSI_ID_VENDOR_SPECIFIC, + naa_type: SCSI_ID_NAA_DONT_CARE, + code_set: SCSI_ID_BINARY, + }, + scsi_id_search_values { + id_type: SCSI_ID_VENDOR_SPECIFIC, + naa_type: SCSI_ID_NAA_DONT_CARE, + code_set: SCSI_ID_ASCII, + }, +]; + +fn set_options(argc: i32, argv: Vec) -> i32 { + let mut args_c: Vec<*mut c_char> = Vec::new(); + for option in &argv { + args_c.push(CString::new(option.clone()).unwrap().into_raw()); + } + let device = CString::new("device").unwrap(); + let config = CString::new("config").unwrap(); + let page = CString::new("page").unwrap(); + let denylisted = CString::new("denylisted").unwrap(); + let allowlisted = CString::new("allowlisted").unwrap(); + let blacklisted = CString::new("blacklisted").unwrap(); + let whitelisted = CString::new("whitelisted").unwrap(); + let replace_whitespace = CString::new("replace-whitespace").unwrap(); + let sg_version = CString::new("sg-version").unwrap(); + let verbose = CString::new("verbose").unwrap(); + let version = CString::new("version").unwrap(); + let export = CString::new("export").unwrap(); + let help = CString::new("help").unwrap(); + + let longopts = [ + option { + name: device.as_ptr(), + has_arg: 1, + flag: std::ptr::null_mut(), + val: b'd' as c_int, + }, + option { + name: config.as_ptr(), + has_arg: 1, + flag: std::ptr::null_mut(), + val: b'f' as c_int, + }, + option { + name: page.as_ptr(), + has_arg: 1, + flag: std::ptr::null_mut(), + val: b'p' as c_int, + }, + option { + name: denylisted.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'b' as c_int, + }, + option { + name: allowlisted.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'g' as c_int, + }, + option { + name: blacklisted.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'b' as c_int, + }, + option { + name: whitelisted.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'g' as c_int, + }, + option { + name: replace_whitespace.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'u' as c_int, + }, + option { + name: sg_version.as_ptr(), + has_arg: 1, + flag: std::ptr::null_mut(), + val: b's' as c_int, + }, + option { + name: verbose.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'v' as c_int, + }, + option { + name: version.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'V' as c_int, + }, + option { + name: export.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'x' as c_int, + }, + option { + name: help.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'h' as c_int, + }, + option { + name: std::ptr::null_mut(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: 0, + }, + ]; + unsafe { + optind = 1; + let optstring = CString::new("d:f:gp:uvVxhbs:").unwrap(); + + loop { + let c = getopt_long( + argc as c_int, + args_c.as_ptr() as *const *mut c_char, + optstring.as_ptr(), + longopts.as_ptr(), + &mut 0, + ); + + if c == -1 { + break; + } + + match c as u8 as char { + 'b' => ALL_GOOD = false, + 'd' => { + DEV_SPECIFIED = true; + let optarg_tmp = CStr::from_ptr(optarg as *const c_char); + MAJ_MIN_DEV = from_utf8(optarg_tmp.to_bytes()).unwrap().to_string(); + } + 'f' => { + let optarg_tmp = CStr::from_ptr(optarg as *const c_char); + SCSI_ID_CONFIG = optarg_tmp.to_str().unwrap(); + } + 'g' => ALL_GOOD = true, + 'h' => { + println!("Usage: scsi_id [OPTION...] DEVICE"); + println!("SCSI device identification."); + println!(); + println!(" -h --help Print this message"); + println!(" -V --version Print version of the program"); + println!(" -d --device= Device node for SG_IO commands"); + println!(" -f --config= Location of config file"); + println!( + " -p --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)" + ); + println!(" -s --sg-version=3|4 Use SGv3 or SGv4"); + println!(" -b --blacklisted Treat device as blacklist"); + println!(" -g --whitelisted Treat device as whitelisted"); + println!( + " -u --replace-whitespace Replace all whitespace by underscores" + ); + println!(" -x --export Print values as environment keys"); + exit(0); + } + 'p' => { + let optarg_tmp = CStr::from_ptr(optarg as *const c_char); + let pagecode = optarg_tmp.to_str().unwrap(); + match pagecode { + "0x80" => DEFAULT_PAGE_CODE = PageCode::Page80, + "0x83" => DEFAULT_PAGE_CODE = PageCode::Page83, + "pre-spc3-83" => DEFAULT_PAGE_CODE = PageCode::Page83PreSpc3, + _ => { + log::error!("unknown page code."); + return -1; + } + } + } + 's' => { + let optarg_tmp = CStr::from_ptr(optarg as *const c_char); + let sg_version_str = optarg_tmp.to_str().unwrap(); + DEFAULT_SG_VERSION = sg_version_str.parse().unwrap(); + if !(3..=4).contains(&DEFAULT_SG_VERSION) { + return -1; + } + } + 'u' => REFORMAT_SERIAL = true, + 'V' => { + println!("scsi_id {}", env!("CARGO_PKG_VERSION")); + return 0; + } + 'x' => EXPORT = true, + _ => { + println!("invalid arguments"); + return -1; + } + } + } + + if optind < argc && !DEV_SPECIFIED { + DEV_SPECIFIED = true; + let tmp = CStr::from_ptr(args_c[optind as usize] as *const c_char); + MAJ_MIN_DEV = from_utf8(tmp.to_bytes()).unwrap().to_string(); + } + } + + 0 +} + +fn sg_err_category_new( + mut scsi_status: u32, + host_status: u32, + driver_status: u32, + sense: u64, + sb_len: u32, +) -> i32 { + scsi_status &= 0x7e; + if scsi_status == 0 && host_status == 0 && driver_status == 0 { + return SG_ERR_CAT_CLEAN; + } + + if IN_SET!( + scsi_status, + SCSI_CHECK_CONDITION as u32, + SCSI_COMMAND_TERMINATED as u32 + ) || (driver_status & 0xf) == DRIVER_SENSE + { + let sense_buffer; + let non_null_ptr = NonNull::new(sense as *mut u64); + match non_null_ptr { + Some(sense) => { + sense_buffer = unsafe { + std::slice::from_raw_parts(sense.as_ptr() as *mut u64, SENSE_BUFF_LEN as usize) + }; + } + None => { + return SG_ERR_CAT_SENSE; + } + } + if sb_len > 2 { + let sense_key; + let asc; + + if (sense_buffer[0] & 0x2) != 0 { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + } else { + sense_key = sense_buffer[2] & 0xf; + if sb_len > 12 { + asc = sense_buffer[12]; + } else { + asc = 0; + } + } + + if sense_key == RECOVERED_ERROR { + return SG_ERR_CAT_RECOVERED; + } else if sense_key == UNIT_ATTENTION { + if asc == 0x28 { + return SG_ERR_CAT_MEDIA_CHANGED; + } + + if asc == 0x29 { + return SG_ERR_CAT_RESET; + } + } else if sense_key == ILLEGAL_REQUEST { + return SG_ERR_CAT_NOTSUPPORTED; + } + return SG_ERR_CAT_SENSE; + } + if host_status != 0 { + if IN_SET!(host_status, DID_NO_CONNECT, DID_BUS_BUSY, DID_TIME_OUT) { + return SG_ERR_CAT_TIMEOUT; + } + + if host_status == DID_TRANSPORT_DISPUPTED { + return SG_ERR_CAT_RETRY; + } + } + + if driver_status != 0 && driver_status == DRIVER_TIMEOUT { + return SG_ERR_CAT_TIMEOUT; + } + } + SG_ERR_CAT_OTHER +} + +fn sg_err_catagory4(io_buf_v4: &mut sg_io_v4) -> i32 { + sg_err_category_new( + io_buf_v4.device_status, + io_buf_v4.transport_status, + io_buf_v4.device_status, + io_buf_v4.response, + io_buf_v4.request_len, + ) +} + +fn sg_err_catagory3(io_buf_hdr: &mut sg_io_hdr) -> i32 { + sg_err_category_new( + io_buf_hdr.status.into(), + io_buf_hdr.host_status.into(), + io_buf_hdr.driver_status.into(), + io_buf_hdr.sbp as u64, + io_buf_hdr.sb_len_wr.into(), + ) +} + +fn scsi_dump_sense(dev_scsi: &mut ScsiIdDevice, sense: u64, sb_len: u32) -> i32 { + if sb_len < 1 { + log::debug!("{}: sense buffer empty.", dev_scsi.kernel); + return -EINVAL; + } + let sense_buffer; + let non_null_ptr = NonNull::new(sense as *mut u64); + match non_null_ptr { + Some(sense) => { + sense_buffer = + unsafe { std::slice::from_raw_parts(sense.as_ptr() as *mut u64, sb_len as usize) }; + } + None => { + return -EINVAL; + } + } + let s; + let sense_key; + let asc; + let ascq; + + let sense_class = sense_buffer[0] >> 4 & 0x07; + let code = sense_buffer[0] & 0xf; + + if sense_class == 7 { + s = sense_buffer[7] as u32 + 8; + if sb_len < s { + log::debug!( + "{}: sense buffer too small {} bytes, {} bytes too short.", + dev_scsi.kernel, + sb_len, + s - sb_len + ); + return -EINVAL; + } + + if IN_SET!(code, 0x0, 0x1) { + sense_key = sense_buffer[2] & 0xf; + if s < 14 { + log::debug!("{}: sense result too small {} bytes", dev_scsi.kernel, s); + return -EINVAL; + } + asc = sense_buffer[12]; + ascq = sense_buffer[13]; + } else if IN_SET!(code, 0x2, 0x3) { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + ascq = sense_buffer[3]; + } else { + log::debug!("{}: invalid sense code {:x}.", dev_scsi.kernel, code); + return -EINVAL; + } + log::debug!( + "{}: sense key {:x} ASC {:x} ASCQ {:x}.", + dev_scsi.kernel, + sense_key, + asc, + ascq + ); + } else { + if sb_len < 4 { + log::debug!( + "{}: sense buffer too small {} bytes, {} bytes too short.", + dev_scsi.kernel, + sb_len, + 4 - sb_len + ); + return -EINVAL; + } + + if sense_buffer[0] < 15 { + log::debug!( + "{}: old sense key: {:x}", + dev_scsi.kernel, + sense_buffer[0] & 0x0f + ); + } else { + log::debug!( + "{}: sense = {} {}.", + dev_scsi.kernel, + sense_buffer[0], + sense_buffer[2] + ); + } + log::debug!( + "{}: non-extended sense class {} code {:x}.", + dev_scsi.kernel, + sense_class, + code + ); + } + + -1 +} + +fn scsi_dump_v4(dev_scsi: &mut ScsiIdDevice, io_buf_v4: sg_io_v4) -> i32 { + if io_buf_v4.device_status != 0 + && io_buf_v4.transport_status != 0 + && io_buf_v4.driver_status != 0 + { + log::debug!("scsi_dump_v4: {}: sense buffer empty.", dev_scsi.kernel); + return -EINVAL; + } + + log::debug!( + "scsi_dump_v4: {}: sg_io failed status {:x} {:x} {:x}", + dev_scsi.kernel, + io_buf_v4.driver_status, + io_buf_v4.transport_status, + io_buf_v4.device_status + ); + if io_buf_v4.device_status == (SCSI_CHECK_CONDITION as u32) { + return scsi_dump_sense(dev_scsi, io_buf_v4.response, io_buf_v4.request_len); + } + + -1 +} + +fn scsi_dump(dev_scsi: &mut ScsiIdDevice, io_buf_hdr: sg_io_hdr) -> i32 { + if io_buf_hdr.status != 0 + && io_buf_hdr.host_status != 0 + && io_buf_hdr.msg_status != 0 + && io_buf_hdr.driver_status != 0 + { + log::debug!("scsi_dump: {}: sense buffer empty.", dev_scsi.kernel); + return -EINVAL; + } + + log::debug!( + "scsi_dump: {}: sg_io failed status {:x} {:x} {:x} {:x}", + dev_scsi.kernel, + io_buf_hdr.driver_status, + io_buf_hdr.host_status, + io_buf_hdr.msg_status, + io_buf_hdr.status + ); + if io_buf_hdr.status == (SCSI_CHECK_CONDITION as u8) { + return scsi_dump_sense(dev_scsi, io_buf_hdr.sbp as u64, io_buf_hdr.sb_len_wr as u32); + } + + -1 +} + +fn scsi_inquiry( + dev_scsi: &mut ScsiIdDevice, + fd: i32, + evpd: c_uchar, + page: c_uchar, + buffer: &mut [c_uchar], + buflen: u32, +) -> i32 { + let inq_cmd: [c_uchar; INQUIRY_CMDLEN as usize] = + [INQUIRY_CMD, evpd, page, 0, buflen as c_uchar, 0]; + let sense = [0_u8; SENSE_BUFF_LEN as usize]; + let mut io_buf_v4 = sg_io_v4::default(); + let mut io_buf_hdr = sg_io_hdr::default(); + let mut r = 0; + let mut retry = 3; + + while retry > 0 { + if dev_scsi.use_sg == 4 { + io_buf_v4.guard = 'Q' as c_int; + io_buf_v4.protocol = BSG_PROTOCOL_SCSI; + io_buf_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + io_buf_v4.request_len = INQUIRY_CMDLEN; + io_buf_v4.request = inq_cmd.as_ptr() as c_ulonglong; + io_buf_v4.max_response_len = SENSE_BUFF_LEN; + io_buf_v4.response = sense.as_ptr() as c_ulonglong; + io_buf_v4.din_xfer_len = buflen; + io_buf_v4.din_xferp = buffer.as_mut_ptr() as c_ulonglong; + + r = unsafe { libc::ioctl(fd, (SG_IO as i32).try_into().unwrap(), &io_buf_v4) }; + if r < 0 { + if IN_SET!(errno::errno(), EINVAL, ENOSYS) { + dev_scsi.use_sg = 3; + } else { + log::debug!("{}: ioctl failed for io_buf_v4!", dev_scsi.kernel); + return r; + } + } + } + + if dev_scsi.use_sg != 4 { + io_buf_hdr.interface_id = 'S' as c_int; + io_buf_hdr.cmd_len = INQUIRY_CMDLEN as c_uchar; + io_buf_hdr.mx_sb_len = SENSE_BUFF_LEN as c_uchar; + io_buf_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_buf_hdr.dxfer_len = buflen; + io_buf_hdr.dxferp = buffer.as_mut_ptr() as *mut c_void; + io_buf_hdr.cmdp = inq_cmd.as_ptr() as *mut c_uchar; + io_buf_hdr.sbp = sense.as_ptr() as *mut c_uchar; + io_buf_hdr.timeout = DEF_TIMEOUT; + + r = unsafe { libc::ioctl(fd, (SG_IO as i32).try_into().unwrap(), &io_buf_hdr) }; + if r < 0 { + log::debug!("{}: ioctl failed for io_buf_hdr!", dev_scsi.kernel); + return r; + } + } + + if dev_scsi.use_sg == 4 { + r = sg_err_catagory4(&mut io_buf_v4); + } else { + r = sg_err_catagory3(&mut io_buf_hdr); + } + + match r { + SG_ERR_CAT_NOTSUPPORTED => { + buffer[1] = 0_u8; + r = 0; + } + SG_ERR_CAT_CLEAN | SG_ERR_CAT_RECOVERED => { + r = 0; + } + SG_ERR_CAT_RETRY => {} + _ => { + if dev_scsi.use_sg == 4 { + r = scsi_dump_v4(dev_scsi, io_buf_v4); + } else { + r = scsi_dump(dev_scsi, io_buf_hdr); + } + } + } + + if r == 0 { + r = buflen as i32; + break; + } + + if r > 0 && retry == 1 { + r = -1 + } + + retry -= 1; + } + r +} + +fn scsi_std_inquiry(dev_scsi: &mut ScsiIdDevice, devname: &str) -> i32 { + let mut buffer = [0_u8; SCSI_INQ_BUFF_LEN as usize]; + let fd = match open( + devname, + OFlag::O_RDONLY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC | OFlag::O_NOCTTY, + Mode::from_bits(0o666).unwrap(), + ) { + Ok(fd) => fd, + Err(e) => { + log::debug!("scsi_std_inquiry: Cannot open {}: {}", devname, e); + return e as i32; + } + }; + + let statbuf = match fstat(fd) { + Err(_) => { + log::debug!("scsi_std_inquiry: can't fstat {}", devname); + return 2; + } + Ok(stat) => stat, + }; + + dev_scsi.kernel = format!("{}:{}", major(statbuf.st_rdev), minor(statbuf.st_rdev)); + + let r = scsi_inquiry(dev_scsi, fd, 0, 0, &mut buffer, SCSI_INQ_BUFF_LEN); + if r < 0 { + return r; + } + + let tmp = + unsafe { std::slice::from_raw_parts(buffer.as_mut_ptr() as *mut c_uchar, buffer.len()) }; + dev_scsi.vendor = String::from_utf8(tmp[8..16].to_vec()).unwrap(); + dev_scsi.model = String::from_utf8(tmp[16..32].to_vec()).unwrap(); + dev_scsi.revision = String::from_utf8(tmp[32..36].to_vec()).unwrap(); + dev_scsi.r#type = tmp[0] as u8 & 0x1f; + + 0 +} + +fn set_inq_values(dev_scsi: &mut ScsiIdDevice, maj_min_dev: &str) -> i32 { + dev_scsi.use_sg = unsafe { DEFAULT_SG_VERSION }; + + let r = scsi_std_inquiry(dev_scsi, maj_min_dev); + if r != 0 { + return r; + } + + encode_devnode_name(&dev_scsi.vendor, unsafe { &mut VENDOR_ENC_STR }); + encode_devnode_name(&dev_scsi.model, unsafe { &mut MODEL_ENC_STR }); + + unsafe { + VENDOR = replace_whitespace(&dev_scsi.vendor); + VENDOR = replace_chars(&VENDOR, ""); + MODEL = replace_whitespace(&dev_scsi.model); + MODEL = replace_chars(&MODEL, ""); + REVISION = replace_whitespace(&dev_scsi.revision); + REVISION = replace_chars(&REVISION, ""); + }; + + unsafe { + TYPE_STR = match dev_scsi.r#type { + 0 | 0xe => "disk".to_string(), + 1 => "tape".to_string(), + 4 | 7 | 0xf => "optical".to_string(), + 5 => "cd".to_string(), + _ => "generic".to_string(), + } + } + + 0 +} + +fn get_file_options(vendor: String, model: String) -> i32 { + let r = 0; + let mut vendor_in = String::new(); + let mut model_in = String::new(); + let mut options_in = String::new(); + let file = match open( + unsafe { SCSI_ID_CONFIG }, + OFlag::O_RDONLY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC | OFlag::O_NOCTTY, + stat::Mode::empty(), + ) { + Ok(fd) => unsafe { File::from_raw_fd(fd) }, + Err(e) => { + return e as i32; + } + }; + + let reader: BufReader = BufReader::new(file); + for (lineno, lines) in reader.lines().enumerate() { + let line = match lines { + Ok(line) => line, + Err(e) => { + log::debug!("read line err: {}", e.to_string()); + return -1; + } + }; + + if line.clone().starts_with('#') { + continue; + } + + if line.clone().trim().is_empty() { + continue; + } + + let vec: Vec = line.split(',').map(|s| s.to_string()).collect(); + for v in vec { + let pair: Vec = v.split('=').map(|s| s.to_string()).collect(); + if pair.len() > 2 { + log::error!( + "failed to parse config file {} line {}", + unsafe { SCSI_ID_CONFIG }, + lineno + ); + return -1; + } + + if pair[0].to_lowercase().starts_with("vendor") { + vendor_in = pair[1].clone(); + } + + if pair[0].to_lowercase().starts_with("model") { + model_in = pair[1].clone(); + } + + if pair[0].to_lowercase().starts_with("options") { + options_in = pair[1].clone(); + } + } + + if options_in.is_empty() || (vendor_in.is_empty() && !model_in.is_empty()) { + log::error!( + "failed to parse config file {} line {}", + unsafe { SCSI_ID_CONFIG }, + lineno + ); + return -1; + } + + if vendor.is_empty() { + if vendor_in.is_empty() { + break; + } + } else if !vendor_in.is_empty() + && vendor.starts_with(&vendor_in) + && (model_in.is_empty() || model.starts_with(&model_in)) + { + break; + } + } + + if vendor_in.is_empty() && model_in.is_empty() && options_in.is_empty() { + log::error!("can't get any values from {}", unsafe { SCSI_ID_CONFIG }); + return -1; + } + unsafe { + ARGV = options_in.split(' ').map(|s| s.to_string()).collect(); + ARGV.insert(0, "".to_owned()); + ARGC = ARGV.len() as i32 + }; + r +} + +fn per_dev_options(dev_scsi: &mut ScsiIdDevice) -> i32 { + let newargc: i32 = 0; + let newargv: Vec = Vec::new(); + + let page = CString::new("page").unwrap(); + let denylisted = CString::new("denylisted").unwrap(); + let allowlisted = CString::new("allowlisted").unwrap(); + let blacklisted = CString::new("blacklisted").unwrap(); + let whitelisted = CString::new("whitelisted").unwrap(); + + let longopts = [ + option { + name: page.as_ptr(), + has_arg: 1, + flag: std::ptr::null_mut(), + val: b'p' as c_int, + }, + option { + name: denylisted.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'b' as c_int, + }, + option { + name: allowlisted.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'g' as c_int, + }, + option { + name: blacklisted.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'b' as c_int, + }, + option { + name: whitelisted.as_ptr(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: b'g' as c_int, + }, + option { + name: std::ptr::null_mut(), + has_arg: 0, + flag: std::ptr::null_mut(), + val: 0, + }, + ]; + + let mut r = get_file_options(dev_scsi.vendor.clone(), dev_scsi.model.clone()); + unsafe { optind = 1 }; + let optstring = CString::new("bgp:").unwrap(); + while r == 0 { + unsafe { + let o = getopt_long( + newargc, + newargv.as_ptr() as *const *mut c_char, + optstring.as_ptr(), + longopts.as_ptr(), + &mut 0, + ); + if o == -1 { + break; + } + match o as u8 as char { + 'b' => ALL_GOOD = false, + 'g' => ALL_GOOD = true, + 'p' => { + let optarg_tmp = CStr::from_ptr(optarg as *const c_char); + let pagecode = optarg_tmp.to_str().unwrap(); + match pagecode { + "0x80" => DEFAULT_PAGE_CODE = PageCode::Page80, + "0x83" => DEFAULT_PAGE_CODE = PageCode::Page83, + "pre-spc3-83" => DEFAULT_PAGE_CODE = PageCode::Page83PreSpc3, + _ => { + log::error!("unknown page code."); + return -1; + } + } + } + _ => { + println!("invalid arguments"); + r = -1; + break; + } + } + } + } + r +} + +fn do_scsi_page0_inquiry( + dev_scsi: &mut ScsiIdDevice, + fd: i32, + buffer: &mut [c_uchar], + len: u32, +) -> bool { + let r = scsi_inquiry(dev_scsi, fd, 1, 0x0, buffer, len); + if r < 0 { + return false; + } + + if buffer[1] != 0_u8 { + log::debug!("{}: page 0 not available.", dev_scsi.kernel); + return false; + } + + if (buffer[3] as u32) > len { + log::debug!("{}: page 0 buffer too long {}", dev_scsi.kernel, buffer[3]); + return false; + } + + if (buffer[3] as i32) > (MODEL_LENGTH as i32) + && String::from_utf8(buffer[VENDOR_LENGTH..2 * VENDOR_LENGTH].to_vec()).unwrap() + != dev_scsi.vendor[VENDOR_LENGTH..] + { + log::debug!("{}: invalid page0 data", dev_scsi.kernel); + return false; + } + + true +} + +fn append_vendor_model(dev_scsi: &mut ScsiIdDevice) -> i32 { + if dev_scsi.vendor.len() >= VENDOR_LENGTH { + dev_scsi.serial += &dev_scsi.vendor[..VENDOR_LENGTH]; + } else { + log::debug!( + "append_vendor_model: {} bad vendor string {}", + dev_scsi.kernel, + dev_scsi.vendor + ); + return -EINVAL; + } + if dev_scsi.model.len() >= MODEL_LENGTH { + dev_scsi.serial += &dev_scsi.model[..MODEL_LENGTH]; + } else { + log::debug!( + "append_vendor_model: {} bad model string {}", + dev_scsi.kernel, + dev_scsi.model + ); + return -EINVAL; + } + (VENDOR_LENGTH + MODEL_LENGTH).try_into().unwrap() +} + +fn do_scsi_page80_inquiry( + dev_scsi: &mut ScsiIdDevice, + fd: i32, + get_serial: bool, + get_unit_serial_number: bool, + max_len: i32, +) -> i32 { + let mut buffer = [0_u8; SCSI_INQ_BUFF_LEN as usize]; + let r = scsi_inquiry( + dev_scsi, + fd, + 1, + PageCode::Page80 as c_uchar, + &mut buffer, + SCSI_INQ_BUFF_LEN, + ); + if r < 0 { + return r; + } + + if buffer[1] != (PageCode::Page80 as c_uchar) { + log::debug!("{}: Invalid page 0x80.", dev_scsi.kernel); + return 1; + } + + let len = 1 + (VENDOR_LENGTH + MODEL_LENGTH) as i32 + buffer[3] as i32; + + if max_len < len { + log::debug!( + "{}: length {} too short - need {}.", + dev_scsi.kernel, + max_len, + len + ); + return 1; + } + + let len: usize = (buffer[3] + 4) as usize; + + if get_serial { + dev_scsi.serial = "S".to_string(); + if append_vendor_model(dev_scsi) < 0 { + return 1; + } + dev_scsi.serial += &String::from_utf8(buffer[4..len].to_vec()).unwrap(); + } + + if get_unit_serial_number { + dev_scsi.unit_serial_number = String::from_utf8(buffer[4..len].to_vec()).unwrap(); + } else { + dev_scsi.serial_short = String::from_utf8(buffer[4..len].to_vec()).unwrap(); + } + + 0 +} + +fn check_fill_0x83_prespc3(dev_scsi: &mut ScsiIdDevice, page_83: Vec, len: i32) -> i32 { + if dev_scsi.serial.is_empty() { + dev_scsi.serial = format!("{:x}", SCSI_ID_NAA); + } else { + dev_scsi + .serial + .replace_range(0..=0, &format!("{:x}", SCSI_ID_NAA)); + } + + for i in 0..page_83[3] as i32 { + dev_scsi.serial += &format!("{:x}", (page_83[4 + i as usize] as i32 & 0xf0) >> 4); + dev_scsi.serial += &format!("{:x}", page_83[4 + i as usize] as i32 & 0x0f); + if dev_scsi.serial.len() >= ((len - 3) as usize) { + break; + } + } + dev_scsi.serial_short = dev_scsi.serial.clone(); + 0 +} + +fn check_fill_0x83_id( + dev_scsi: &mut ScsiIdDevice, + page_83: Vec, + id_search: scsi_id_search_values, + max_len: i32, +) -> i32 { + if (page_83[1] & 0x30) == 0x10 { + if id_search.id_type != SCSI_ID_TGTGROUP { + return 1; + } + } else if (page_83[1] & 0x30) != 0 { + return 1; + } + + if (page_83[1] & 0x0f) != id_search.id_type { + return 1; + } + + if id_search.naa_type != SCSI_ID_NAA_DONT_CARE && id_search.naa_type != (page_83[4] & 0xf0 >> 4) + { + return 1; + } + + if (page_83[0] & 0x0f) != id_search.code_set { + return 1; + } + + let mut len: i32 = page_83[3] as i32; + if (page_83[0] & 0x0f) != SCSI_ID_ASCII { + len *= 2; + } + + len += 2; + if id_search.id_type == SCSI_ID_VENDOR_SPECIFIC { + len += (VENDOR_LENGTH + MODEL_LENGTH) as i32; + } + + if max_len < len { + log::debug!( + "{}: length {} too short - need {}", + dev_scsi.kernel, + max_len, + len + ); + return 1; + } + + if id_search.id_type == SCSI_ID_TGTGROUP && !dev_scsi.tgpt_group.is_empty() { + let group = ((page_83[6] as c_uint) << 8) | page_83[7] as c_uint; + dev_scsi.tgpt_group = format!("{:x}", group); + return 1; + } + + if !dev_scsi.serial.is_empty() { + dev_scsi + .serial + .replace_range(0..=0, &format!("{:x}", id_search.id_type)); + } else { + dev_scsi.serial = format!("{:x}", id_search.id_type); + } + + if id_search.id_type == SCSI_ID_VENDOR_SPECIFIC && append_vendor_model(dev_scsi) < 0 { + return -1; + } + + let mut i = 4; /* offset to the start of the identifier */ + let s = dev_scsi.serial.len(); + if (page_83[0] as c_uchar & 0x0f) == SCSI_ID_ASCII { + let range = 4..(4 + page_83[3]); + dev_scsi.serial += + &String::from_utf8(page_83[range.start as usize..range.end as usize].to_vec()).unwrap(); + } else { + while i < (4 + page_83[3] as i32) { + dev_scsi.serial += &format!("{:x}", (page_83[i as usize] as usize & 0xf0) >> 4); + dev_scsi.serial += &format!("{:x}", page_83[i as usize] as usize & 0x0f); + i += 1; + } + } + + dev_scsi.serial_short = dev_scsi.serial[s..].to_string(); + + if id_search.id_type == SCSI_ID_NAA && dev_scsi.wwn.is_empty() { + let len = dev_scsi.serial.len(); + if len >= (s + 16) { + dev_scsi.wwn = dev_scsi.serial[s..s + 16].to_string(); + } else { + dev_scsi.wwn = dev_scsi.serial[s..].to_string(); + } + + if dev_scsi.wwn_vendor_extension.is_empty() { + if len >= (s + 32) { + dev_scsi.wwn_vendor_extension = dev_scsi.serial[s + 16..s + 32].to_string(); + } else if len >= (s + 16) { + dev_scsi.wwn_vendor_extension = dev_scsi.serial[s + 16..].to_string(); + } + } + } + + 0 +} + +fn do_scsi_page83_inquiry(dev_scsi: &mut ScsiIdDevice, fd: i32, len: i32) -> i32 { + let mut page_83 = vec![0_u8; SCSI_INQ_BUFF_LEN as usize]; + do_scsi_page80_inquiry(dev_scsi, fd, false, true, MAX_SERIAL_LEN); + let mut r = scsi_inquiry( + dev_scsi, + fd, + 1, + PageCode::Page83 as u8, + &mut page_83, + SCSI_INQ_BUFF_LEN, + ); + if r < 0 { + return 1; + } + + if page_83[1] != (PageCode::Page83 as c_uchar) { + log::debug!("{}: Invalid page 0x83.", dev_scsi.kernel); + return 1; + } + + if page_83[6] != 0_u8 { + return check_fill_0x83_prespc3(dev_scsi, page_83, len); + } + + for id in &ID_SEARCH_LIST { + let mut i = 4; + while i <= (((page_83[2] as c_uint) << 8) + page_83[3] as c_uint + 3) { + r = check_fill_0x83_id( + dev_scsi, + page_83.clone().split_off(i.try_into().unwrap()), + *id, + len, + ); + if r <= 0 { + return r; + } + i += page_83[i as usize + 3] as c_uint + 4; + } + } + + 1 +} + +fn do_scsi_page83_prespc3_inquiry(dev_scsi: &mut ScsiIdDevice, fd: i32) -> i32 { + let mut page_83 = vec![0_u8; SCSI_INQ_BUFF_LEN as usize]; + let r = scsi_inquiry( + dev_scsi, + fd, + 1, + PageCode::Page83 as u8, + &mut page_83, + SCSI_INQ_BUFF_LEN, + ); + if r < 0 { + return 1; + } + + if page_83[1] != (PageCode::Page83 as c_uchar) { + log::debug!("{}: Invalid page 0x83.", dev_scsi.kernel); + return 1; + } + + if page_83[6] == 0_u8 { + return 2; + } + + if dev_scsi.serial.is_empty() { + dev_scsi.serial += &format!("{:x}", SCSI_ID_NAA); + } else { + dev_scsi + .serial + .replace_range(0..1, &format!("{:x}", SCSI_ID_NAA)); + } + + let mut i = 4; + + while i < (page_83[3] as i32 + 4) { + dev_scsi.serial += &format!("{:x}", (page_83[i as usize] as usize & 0xf0) >> 4); + dev_scsi.serial += &format!("{:x}", page_83[i as usize] as usize & 0x0f); + i += 1; + } + + 0 +} + +fn scsi_get_serial( + dev_scsi: &mut ScsiIdDevice, + devname: &str, + page_code: &PageCode, + len: i32, +) -> i32 { + dev_scsi.serial = String::new(); + let mut fd = -EBADF; + let mut page0 = vec![0_u8; SCSI_INQ_BUFF_LEN as usize]; + for _i in 0..=20 { + fd = match open( + devname, + OFlag::O_RDONLY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC | OFlag::O_NOCTTY, + Mode::from_bits(0o666).unwrap(), + ) { + Ok(fd) => fd, + Err(e) => { + log::error!("Cannot open {}: {}", devname, e); + if e != Errno::EBUSY { + break; + } + -1 + } + }; + + if fd >= 0 { + break; + } + thread::sleep(Duration::from_millis(200)); + } + + if fd < 0 { + return 1; + } + + match page_code { + PageCode::Page80 => { + if do_scsi_page80_inquiry(dev_scsi, fd, true, false, len) != 0 { + return 1; + } else { + return 0; + } + } + PageCode::Page83 => { + if do_scsi_page83_inquiry(dev_scsi, fd, len) != 0 { + return 1; + } else { + return 0; + } + } + PageCode::Page83PreSpc3 => { + let r = do_scsi_page83_prespc3_inquiry(dev_scsi, fd); + if r != 0 { + if r == 2 { + if do_scsi_page83_inquiry(dev_scsi, fd, len) != 0 { + return 1; + } else { + return 0; + } + } else { + return 1; + } + } else { + return r; + } + } + PageCode::PageUnspecified => {} + } + + if !do_scsi_page0_inquiry(dev_scsi, fd, &mut page0, SCSI_INQ_BUFF_LEN) { + return 1; + } + + for ind in 4..=(page0[3] as i32 + 3) { + if page0[ind as usize] == (PageCode::Page83 as c_uchar) + && do_scsi_page83_inquiry(dev_scsi, fd, len) == 0 + { + return 0; + } + } + + for ind in 4..=(page0[3] as i32 + 3) { + if page0[ind as usize] == (PageCode::Page80 as c_uchar) + && do_scsi_page80_inquiry(dev_scsi, fd, true, false, len) == 0 + { + return 0; + } + } + 1 +} + +fn scsi_id(maj_min_dev: &str) -> i32 { + let mut dev_scsi = ScsiIdDevice::default(); + let page_code = unsafe { &DEFAULT_PAGE_CODE }; + + if set_inq_values(&mut dev_scsi, maj_min_dev) < 0 { + return 1; + } + + per_dev_options(&mut dev_scsi); + if !unsafe { ALL_GOOD } { + return 1; + } + + scsi_get_serial(&mut dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN); + unsafe { + if EXPORT { + println!("ID_SCSI=1"); + println!("ID_VENDOR={}", &VENDOR); + println!("ID_VENDOR_ENC={}", &VENDOR_ENC_STR); + println!("ID_MODEL={}", &MODEL); + println!("ID_MODEL_ENC={}", &MODEL_ENC_STR); + println!("ID_REVISION={}", &REVISION); + println!("ID_TYPE={}", &TYPE_STR); + + if !dev_scsi.serial.is_empty() { + dev_scsi.serial = replace_whitespace(&dev_scsi.serial); + dev_scsi.serial = replace_chars(&dev_scsi.serial, ""); + println!("ID_SERIAL={}", dev_scsi.serial); + dev_scsi.serial_short = replace_whitespace(&dev_scsi.serial_short); + dev_scsi.serial_short = replace_chars(&dev_scsi.serial_short, ""); + println!("ID_SERIAL_SHORT={}", dev_scsi.serial_short); + } + + if !dev_scsi.wwn.is_empty() { + println!("ID_WWN=0x{}", dev_scsi.wwn); + if !dev_scsi.wwn_vendor_extension.is_empty() { + println!( + "ID_WWN_VENDOR_EXTENSION=0x{}", + dev_scsi.wwn_vendor_extension + ); + println!( + "ID_WWN_WITH_EXTENSION=0x{}{}", + dev_scsi.wwn, dev_scsi.wwn_vendor_extension + ); + } else { + println!("ID_WWN_WITH_EXTENSION=0x{}", dev_scsi.wwn); + } + } + + if !dev_scsi.tgpt_group.is_empty() { + println!("ID_TARGET_PORT={}", dev_scsi.tgpt_group); + } + + if !dev_scsi.unit_serial_number.is_empty() { + println!("ID_SCSI_SERIAL={}", dev_scsi.unit_serial_number); + } + + return 0; + } + } + + if dev_scsi.serial.is_empty() { + return 1; + } + + if unsafe { REFORMAT_SERIAL } { + dev_scsi.serial = replace_whitespace(&dev_scsi.serial); + dev_scsi.serial = replace_chars(&dev_scsi.serial, ""); + println!("{}", dev_scsi.serial); + return 0; + } + + println!("{}", dev_scsi.serial); + 0 +} + +fn main() { + let args: Vec = std::env::args().collect(); + let argc = args.len() as i32; + + let r = get_file_options(String::from(""), String::from("")); + if r < 0 { + exit(1); + } + + if r == 0 && set_options(unsafe { ARGC }, unsafe { ARGV.clone() }) < 0 { + exit(2); + } + + if set_options(argc, args) < 0 { + exit(1); + } + + if !unsafe { DEV_SPECIFIED } { + log::error!("No device specified!"); + exit(1); + } + + let r = scsi_id(unsafe { &MAJ_MIN_DEV }); + exit(r); +} -- 2.33.0