1 use std
::path
::{Path, PathBuf}
;
2 use std
::collections
::HashMap
;
4 use anyhow
::{bail, Error}
;
9 OptionalDeviceIdentification
,
12 tools
::fs
::scan_subdir
,
15 lazy_static
::lazy_static
!{
16 static ref SCSI_GENERIC_NAME_REGEX
: regex
::Regex
=
17 regex
::Regex
::new(r
"^sg\d+$").unwrap();
20 /// List linux tape changer devices
21 pub fn linux_tape_changer_list() -> Vec
<TapeDeviceInfo
> {
23 let mut list
= Vec
::new();
25 let dir_iter
= match scan_subdir(
27 "/sys/class/scsi_generic",
28 &SCSI_GENERIC_NAME_REGEX
)
30 Err(_
) => return list
,
34 for item
in dir_iter
{
35 let item
= match item
{
40 let name
= item
.file_name().to_str().unwrap().to_string();
42 let mut sys_path
= PathBuf
::from("/sys/class/scsi_generic");
45 let device
= match udev
::Device
::from_syspath(&sys_path
) {
50 let devnum
= match device
.devnum() {
52 Some(devnum
) => devnum
,
55 let parent
= match device
.parent() {
57 Some(parent
) => parent
,
60 match parent
.attribute_value("type") {
62 if type_osstr
!= "8" {
69 // let mut test_path = sys_path.clone();
70 // test_path.push("device/scsi_changer");
71 // if !test_path.exists() { continue; }
73 let _dev_path
= match device
.devnode().map(Path
::to_owned
) {
75 Some(dev_path
) => dev_path
,
78 let serial
= match device
.property_value("ID_SCSI_SERIAL")
79 .map(std
::ffi
::OsString
::from
)
80 .and_then(|s
| if let Ok(s
) = s
.into_string() { Some(s) }
else { None }
)
83 Some(serial
) => serial
,
86 let vendor
= device
.property_value("ID_VENDOR")
87 .map(std
::ffi
::OsString
::from
)
88 .and_then(|s
| if let Ok(s
) = s
.into_string() { Some(s) }
else { None }
)
89 .unwrap_or_else(|| String
::from("unknown"));
91 let model
= device
.property_value("ID_MODEL")
92 .map(std
::ffi
::OsString
::from
)
93 .and_then(|s
| if let Ok(s
) = s
.into_string() { Some(s) }
else { None }
)
94 .unwrap_or_else(|| String
::from("unknown"));
96 let dev_path
= format
!("/dev/tape/by-id/scsi-{}", serial
);
98 if PathBuf
::from(&dev_path
).exists() {
99 list
.push(TapeDeviceInfo
{
100 kind
: DeviceKind
::Changer
,
105 major
: unsafe { libc::major(devnum) }
,
106 minor
: unsafe { libc::minor(devnum) }
,
115 pub fn lto_tape_device_list() -> Vec
<TapeDeviceInfo
> {
117 let mut list
= Vec
::new();
119 let dir_iter
= match scan_subdir(
121 "/sys/class/scsi_generic",
122 &SCSI_GENERIC_NAME_REGEX
)
124 Err(_
) => return list
,
128 for item
in dir_iter
{
129 let item
= match item
{
134 let name
= item
.file_name().to_str().unwrap().to_string();
136 let mut sys_path
= PathBuf
::from("/sys/class/scsi_generic");
137 sys_path
.push(&name
);
139 let device
= match udev
::Device
::from_syspath(&sys_path
) {
141 Ok(device
) => device
,
144 let devnum
= match device
.devnum() {
146 Some(devnum
) => devnum
,
149 let parent
= match device
.parent() {
151 Some(parent
) => parent
,
154 match parent
.attribute_value("type") {
155 Some(type_osstr
) => {
156 if type_osstr
!= "1" {
163 // let mut test_path = sys_path.clone();
164 // test_path.push("device/scsi_tape");
165 // if !test_path.exists() { continue; }
167 let _dev_path
= match device
.devnode().map(Path
::to_owned
) {
169 Some(dev_path
) => dev_path
,
172 let serial
= match device
.property_value("ID_SCSI_SERIAL")
173 .map(std
::ffi
::OsString
::from
)
174 .and_then(|s
| if let Ok(s
) = s
.into_string() { Some(s) }
else { None }
)
177 Some(serial
) => serial
,
180 let vendor
= device
.property_value("ID_VENDOR")
181 .map(std
::ffi
::OsString
::from
)
182 .and_then(|s
| if let Ok(s
) = s
.into_string() { Some(s) }
else { None }
)
183 .unwrap_or_else(|| String
::from("unknown"));
185 let model
= device
.property_value("ID_MODEL")
186 .map(std
::ffi
::OsString
::from
)
187 .and_then(|s
| if let Ok(s
) = s
.into_string() { Some(s) }
else { None }
)
188 .unwrap_or_else(|| String
::from("unknown"));
190 let dev_path
= format
!("/dev/tape/by-id/scsi-{}-sg", serial
);
192 if PathBuf
::from(&dev_path
).exists() {
193 list
.push(TapeDeviceInfo
{
194 kind
: DeviceKind
::Tape
,
199 major
: unsafe { libc::major(devnum) }
,
200 minor
: unsafe { libc::minor(devnum) }
,
208 /// Test if a device exists, and returns associated `TapeDeviceInfo`
209 pub fn lookup_device
<'a
>(
210 devices
: &'a
[TapeDeviceInfo
],
212 ) -> Option
<&'a TapeDeviceInfo
> {
214 if let Ok(stat
) = nix
::sys
::stat
::stat(path
) {
216 let major
= unsafe { libc::major(stat.st_rdev) }
;
217 let minor
= unsafe { libc::minor(stat.st_rdev) }
;
219 devices
.iter().find(|d
| d
.major
== major
&& d
.minor
== minor
)
225 /// Lookup optional drive identification attributes
226 pub fn lookup_device_identification
<'a
>(
227 devices
: &'a
[TapeDeviceInfo
],
229 ) -> OptionalDeviceIdentification
{
231 if let Some(info
) = lookup_device(devices
, path
) {
232 OptionalDeviceIdentification
{
233 vendor
: Some(info
.vendor
.clone()),
234 model
: Some(info
.model
.clone()),
235 serial
: Some(info
.serial
.clone()),
238 OptionalDeviceIdentification
{
246 /// Make sure path is a lto tape device
247 pub fn check_drive_path(
248 drives
: &[TapeDeviceInfo
],
250 ) -> Result
<(), Error
> {
251 if lookup_device(drives
, path
).is_none() {
252 bail
!("path '{}' is not a lto SCSI-generic tape device", path
);
257 // shell completion helper
259 /// List changer device paths
260 pub fn complete_changer_path(_arg
: &str, _param
: &HashMap
<String
, String
>) -> Vec
<String
> {
261 linux_tape_changer_list().iter().map(|v
| v
.path
.clone()).collect()
264 /// List tape device paths
265 pub fn complete_drive_path(_arg
: &str, _param
: &HashMap
<String
, String
>) -> Vec
<String
> {
266 lto_tape_device_list().iter().map(|v
| v
.path
.clone()).collect()