1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use std::fs::{ File, read_dir, read_link };
use std::mem::zeroed;
use std::os::unix::io::AsRawFd;
use std::io::{ BufReader, BufRead, Error };

extern crate libc;
use libc::{ c_int, ioctl };

extern crate kstat;
use kstat::KstatReader;
use kstat::kstat_named::KstatNamedData;


const DKIOCREMOVABLE: i32 = 1040;
const DKIOCGMEDIAINFO: i32 = 1066;
const DKIOCINFO: i32 = 1027;

pub type ULonglongT = ::std::os::raw::c_ulonglong;
pub type DiskaddrT = ULonglongT;
pub type UChar = ::std::os::raw::c_uchar;
pub type UShortT = ::std::os::raw::c_ushort;
pub type UIntT = ::std::os::raw::c_uint;


#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct DkMinfo {
    pub dki_media_type: UIntT,
    pub dki_lbsize: UIntT,
    pub dki_capacity: DiskaddrT,
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct DkCinfo {
    pub dki_cname: [::std::os::raw::c_char; 16usize],
    pub dki_ctype: UShortT,
    pub dki_flags: UShortT,
    pub dki_cnum: UShortT,
    pub dki_addr: UIntT,
    pub dki_space: UIntT,
    pub dki_prio: UIntT,
    pub dki_vec: UIntT,
    pub dki_dname: [::std::os::raw::c_char; 16usize],
    pub dki_unit: UIntT,
    pub dki_slave: UIntT,
    pub dki_partition: UShortT,
    pub dki_maxtransfer: UShortT,
}

pub fn get_removable(fd: c_int) -> i64 {
    let mut removable = 0;
    let res = unsafe { ioctl(fd, DKIOCREMOVABLE, &mut removable) };
    if res == -1 {
        panic!("DKIOCREMOVABLE err")
    }
    removable
}

pub fn get_ctype(fd: c_int) -> String {
    let mut dkinfo:DkCinfo = unsafe { zeroed() };
    let mut ctype:String = String::new(); 
    let res = unsafe { ioctl(fd, DKIOCINFO, &mut dkinfo) };
    if res == -1 {
        panic!("DKIOCINFO err");
    }
    match dkinfo.dki_ctype {
      20 => ctype.push_str("ATA"),
      13 => ctype.push_str("SCSI"),
      _ => ctype.push_str("UNKNOWN"),
    }
    ctype
}

pub fn get_media(fd: c_int) -> f64 {
    let mut media: DkMinfo = unsafe { zeroed() };
    let res = unsafe { ioctl(fd, DKIOCGMEDIAINFO, &mut media) };
    if res == -1 {
        panic!("DKIOMEDIA err")
    }
    let size = ((media.dki_lbsize as u64 * media.dki_capacity) as f64) / 1024.0 / 1024.0 / 1024.0;
    size
}

fn format_ks(m: &KstatNamedData) -> String {
    let s = format!("{:?}!", m);
    let tokens: Vec<_> = s.split('"').collect();
    tokens[1].to_string()
}

fn get_kstat(sd: &str, data: &str) -> String {
    let mut kstat_val:String = String::new();
    let dkerr = format!("{},err", sd);
    let reader = KstatReader::new(None, None, Some(dkerr), None)
        .expect("failed to create kstat reader");
    let stats = reader.read().expect("failed to read kstats");
    for stat in stats {
       kstat_val = format_ks(&stat.data[data]);
      }
    kstat_val
}

fn print_disk(path: &str, fname: File, disk: &str) { 
    let ctype = get_ctype(fname.as_raw_fd());
    let res = get_removable(fname.as_raw_fd());
    if res == 0 {
        let dksize = get_media(fname.as_raw_fd());
        let sympath = read_link(&path).unwrap();
        let devpath = sympath.into_os_string().into_string().unwrap(); 
        let tokens: Vec<&str> = devpath.split("/").collect();
        let sdpart = tokens[tokens.len()-1];
        let sdnum: Vec<&str> = sdpart.split(":").collect();
        let pathinst = File::open("/etc/path_to_inst").unwrap();
        let file = BufReader::new(&pathinst);
        for line in file.lines() {
            let l = line.unwrap();
            if l.contains(sdnum[0]) && l.contains(tokens[tokens.len()-2]) {
	        let sdtok: Vec<&str> = l.split(" ").collect();
	        let sd = format!("sd{}", sdtok[sdtok.len()-2]); 
	        let serial = get_kstat(&sd, "Serial No");
	        let product = get_kstat(&sd, "Product");
	        let vendor = get_kstat(&sd, "Vendor");
	        println!("{: <7} {: <23} {: <8} {: <16} {: <20} {:.2?} GiB",
		    ctype, disk, vendor, product, serial, dksize);
            }
	}
    }
}

pub fn get_disks() -> Result<(), Box<Error>> {
    let dir = "/dev/rdsk";
    println!("TYPE    DISK                    VID      PID              SERIAL               SIZE");
    for entry in read_dir(dir).unwrap() { 
        let post_path = entry.unwrap().path();
        let fname = post_path.file_name().unwrap().to_str().unwrap();
        if fname.ends_with("p0") { 
           let mut disk = String::from(fname);
           let len = disk.len();
           disk.truncate(len - 2); 
           let path = ["/dev/rdsk", fname].join("/"); 
           let _rawfd = match File::open(&path) {
               Ok(rawfd) => print_disk(&path, rawfd, &disk),
               Err(_error) => (),
           };
        }
    }
    Ok(())
}