rust-diskinfo 0.0.1

illumos diskinfo utility implemented in Rust.
Documentation
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(())
}