1use std::fs::{ File, read_dir, read_link };
2use std::mem::zeroed;
3use std::os::unix::io::AsRawFd;
4use std::io::{ BufReader, BufRead, Error };
5
6extern crate libc;
7use libc::{ c_int, ioctl };
8
9extern crate kstat;
10use kstat::KstatReader;
11use kstat::kstat_named::KstatNamedData;
12
13
14const DKIOCREMOVABLE: i32 = 1040;
15const DKIOCGMEDIAINFO: i32 = 1066;
16const DKIOCINFO: i32 = 1027;
17
18pub type ULonglongT = ::std::os::raw::c_ulonglong;
19pub type DiskaddrT = ULonglongT;
20pub type UChar = ::std::os::raw::c_uchar;
21pub type UShortT = ::std::os::raw::c_ushort;
22pub type UIntT = ::std::os::raw::c_uint;
23
24
25#[repr(C)]
26#[derive(Debug, Copy, Clone)]
27pub struct DkMinfo {
28    pub dki_media_type: UIntT,
29    pub dki_lbsize: UIntT,
30    pub dki_capacity: DiskaddrT,
31}
32
33#[repr(C)]
34#[derive(Debug, Copy, Clone)]
35pub struct DkCinfo {
36    pub dki_cname: [::std::os::raw::c_char; 16usize],
37    pub dki_ctype: UShortT,
38    pub dki_flags: UShortT,
39    pub dki_cnum: UShortT,
40    pub dki_addr: UIntT,
41    pub dki_space: UIntT,
42    pub dki_prio: UIntT,
43    pub dki_vec: UIntT,
44    pub dki_dname: [::std::os::raw::c_char; 16usize],
45    pub dki_unit: UIntT,
46    pub dki_slave: UIntT,
47    pub dki_partition: UShortT,
48    pub dki_maxtransfer: UShortT,
49}
50
51pub fn get_removable(fd: c_int) -> i64 {
52    let mut removable = 0;
53    let res = unsafe { ioctl(fd, DKIOCREMOVABLE, &mut removable) };
54    if res == -1 {
55        panic!("DKIOCREMOVABLE err")
56    }
57    removable
58}
59
60pub fn get_ctype(fd: c_int) -> String {
61    let mut dkinfo:DkCinfo = unsafe { zeroed() };
62    let mut ctype:String = String::new(); 
63    let res = unsafe { ioctl(fd, DKIOCINFO, &mut dkinfo) };
64    if res == -1 {
65        panic!("DKIOCINFO err");
66    }
67    match dkinfo.dki_ctype {
68      20 => ctype.push_str("ATA"),
69      13 => ctype.push_str("SCSI"),
70      _ => ctype.push_str("UNKNOWN"),
71    }
72    ctype
73}
74
75pub fn get_media(fd: c_int) -> f64 {
76    let mut media: DkMinfo = unsafe { zeroed() };
77    let res = unsafe { ioctl(fd, DKIOCGMEDIAINFO, &mut media) };
78    if res == -1 {
79        panic!("DKIOMEDIA err")
80    }
81    let size = ((media.dki_lbsize as u64 * media.dki_capacity) as f64) / 1024.0 / 1024.0 / 1024.0;
82    size
83}
84
85fn format_ks(m: &KstatNamedData) -> String {
86    let s = format!("{:?}!", m);
87    let tokens: Vec<_> = s.split('"').collect();
88    tokens[1].to_string()
89}
90
91fn get_kstat(sd: &str, data: &str) -> String {
92    let mut kstat_val:String = String::new();
93    let dkerr = format!("{},err", sd);
94    let reader = KstatReader::new(None, None, Some(dkerr), None)
95        .expect("failed to create kstat reader");
96    let stats = reader.read().expect("failed to read kstats");
97    for stat in stats {
98       kstat_val = format_ks(&stat.data[data]);
99      }
100    kstat_val
101}
102
103fn print_disk(path: &str, fname: File, disk: &str) { 
104    let ctype = get_ctype(fname.as_raw_fd());
105    let res = get_removable(fname.as_raw_fd());
106    if res == 0 {
107        let dksize = get_media(fname.as_raw_fd());
108        let sympath = read_link(&path).unwrap();
109        let devpath = sympath.into_os_string().into_string().unwrap(); 
110        let tokens: Vec<&str> = devpath.split("/").collect();
111        let sdpart = tokens[tokens.len()-1];
112        let sdnum: Vec<&str> = sdpart.split(":").collect();
113        let pathinst = File::open("/etc/path_to_inst").unwrap();
114        let file = BufReader::new(&pathinst);
115        for line in file.lines() {
116            let l = line.unwrap();
117            if l.contains(sdnum[0]) && l.contains(tokens[tokens.len()-2]) {
118	        let sdtok: Vec<&str> = l.split(" ").collect();
119	        let sd = format!("sd{}", sdtok[sdtok.len()-2]); 
120	        let serial = get_kstat(&sd, "Serial No");
121	        let product = get_kstat(&sd, "Product");
122	        let vendor = get_kstat(&sd, "Vendor");
123	        println!("{: <7} {: <23} {: <8} {: <16} {: <20} {:.2?} GiB",
124		    ctype, disk, vendor, product, serial, dksize);
125            }
126	}
127    }
128}
129
130pub fn get_disks() -> Result<(), Box<Error>> {
131    let dir = "/dev/rdsk";
132    println!("TYPE    DISK                    VID      PID              SERIAL               SIZE");
133    for entry in read_dir(dir).unwrap() { 
134        let post_path = entry.unwrap().path();
135        let fname = post_path.file_name().unwrap().to_str().unwrap();
136        if fname.ends_with("p0") { 
137           let mut disk = String::from(fname);
138           let len = disk.len();
139           disk.truncate(len - 2); 
140           let path = ["/dev/rdsk", fname].join("/"); 
141           let _rawfd = match File::open(&path) {
142               Ok(rawfd) => print_disk(&path, rawfd, &disk),
143               Err(_error) => (),
144           };
145        }
146    }
147    Ok(())
148}
149