get_sys_info 0.1.21

Get system information
Documentation
use super::bsd;
use super::common::*;
use super::unix;
use crate::data::*;
use libc::{c_int, c_uchar, c_uint, c_ulong, c_void, ioctl, sysctl, timeval};
use std::mem::size_of;
use std::os::unix::io::AsRawFd;
use std::{fs, io, mem, path, ptr, time};

pub struct PlatformImpl;

macro_rules! sysctl {
    ($mib:expr, $dataptr:expr, $size:expr, $shouldcheck:expr) => {{
        let mib = &$mib;
        let mut size = $size;
        if unsafe {
            sysctl(
                &mib[0],
                mib.len() as u32,
                $dataptr as *mut _ as *mut c_void,
                &mut size,
                ptr::null_mut(),
                0,
            )
        } != 0
            && $shouldcheck
        {
            return Err(io::Error::new(io::ErrorKind::Other, "sysctl() failed"));
        }
        size
    }};
    ($mib:expr, $dataptr:expr, $size:expr) => {
        sysctl!($mib, $dataptr, $size, true)
    };
}

lazy_static! {
    static ref APM_IOC_GETPOWER: c_ulong = (0x40000000u64 | ((size_of::<apm_power_info>() & 0x1fff) << 16) as u64 | (0x41 << 8) | 3);
    // OpenBSD does not have sysctlnametomib, so more copy-pasting of magic numbers from C headers :(
    static ref HW_NCPU: [c_int; 2] = [6, 3];
    static ref KERN_CPTIME2: [c_int; 3] = [1, 71, 0];
    static ref KERN_BOOTTIME: [c_int; 2] = [1, 21];
    static ref VM_UVMEXP: [c_int; 2] = [2, 4];
    static ref VFS_BCACHESTAT: [c_int; 3] = [10, 0, 3];
}

/// An implementation of `Platform` for OpenBSD.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
    #[inline(always)]
    fn new() -> Self {
        PlatformImpl
    }

    fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
        let loads = try!(measure_cpu());
        Ok(DelayedMeasurement::new(Box::new(move || {
            Ok(loads
                .iter()
                .zip(try!(measure_cpu()).iter())
                .map(|(prev, now)| (*now - prev).to_cpuload())
                .collect::<Vec<_>>())
        })))
    }

    fn load_average(&self) -> io::Result<LoadAverage> {
        unix::load_average()
    }

    fn memory(&self) -> io::Result<Memory> {
        let mut uvm_info = uvmexp::default();
        sysctl!(VM_UVMEXP, &mut uvm_info, mem::size_of::<uvmexp>());
        let mut bcache_info = bcachestats::default();
        sysctl!(
            VFS_BCACHESTAT,
            &mut bcache_info,
            mem::size_of::<bcachestats>()
        );
        let total = readable_byte::kib((uvm_info.npages << *bsd::PAGESHIFT) as usize);
        let pmem = PlatformMemory {
            active: readable_byte::kib((uvm_info.active << *bsd::PAGESHIFT) as usize),
            inactive: readable_byte::kib((uvm_info.inactive << *bsd::PAGESHIFT) as usize),
            wired: readable_byte::kib((uvm_info.wired << *bsd::PAGESHIFT) as usize),
            cache: readable_byte::kib((bcache_info.numbufpages << *bsd::PAGESHIFT) as usize),
            free: readable_byte::kib((uvm_info.free << *bsd::PAGESHIFT) as usize),
            paging: readable_byte::kib((uvm_info.paging << *bsd::PAGESHIFT) as usize),
        };
        Ok(Memory {
            total: total,
            free: pmem.inactive + pmem.cache + pmem.free + pmem.paging,
            platform_memory: pmem,
        })
    }

    fn boot_time(&self) -> io::Result<DateTime<Utc>> {
        let mut data: timeval = unsafe { mem::zeroed() };
        sysctl!(KERN_BOOTTIME, &mut data, mem::size_of::<timeval>());
        Ok(DateTime::<Utc>::from_utc(
            NaiveDateTime::from_timestamp(data.tv_sec, data.tv_usec as u32),
            Utc,
        ))
    }

    // /dev/apm is probably the nicest interface I've seen :)
    fn battery_life(&self) -> io::Result<BatteryLife> {
        let f = try!(fs::File::open("/dev/apm"));
        let mut info = apm_power_info::default();
        if unsafe { ioctl(f.as_raw_fd(), *APM_IOC_GETPOWER, &mut info) } == -1 {
            return Err(io::Error::new(io::ErrorKind::Other, "ioctl() failed"));
        }
        if info.battery_state == 0xff {
            // APM_BATT_UNKNOWN
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "Battery state unknown",
            ));
        }
        if info.battery_state == 4 {
            // APM_BATTERY_ABSENT
            return Err(io::Error::new(io::ErrorKind::Other, "Battery absent"));
        }
        Ok(BatteryLife {
            remaining_capacity: info.battery_life as f32,
            remaining_time: time::Duration::from_secs(info.minutes_left as u64),
        })
    }

    fn on_ac_power(&self) -> io::Result<bool> {
        let f = try!(fs::File::open("/dev/apm"));
        let mut info = apm_power_info::default();
        if unsafe { ioctl(f.as_raw_fd(), *APM_IOC_GETPOWER, &mut info) } == -1 {
            return Err(io::Error::new(io::ErrorKind::Other, "ioctl() failed"));
        }
        Ok(info.ac_state == 0x01) // APM_AC_ON
    }

    fn mounts(&self) -> io::Result<Vec<Filesystem>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn mount_at<P: AsRef<path::Path>>(&self, _: P) -> io::Result<Filesystem> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn block_device_statistics(&self) -> io::Result<BTreeMap<String, BlockDeviceStats>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
        unix::networks()
    }

    fn network_stats(&self, interface: &str) -> io::Result<NetworkStats> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn cpu_temp(&self) -> io::Result<f32> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn socket_stats(&self) -> io::Result<SocketStats> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }
}

fn measure_cpu() -> io::Result<Vec<CpuTime>> {
    let mut cpus: usize = 0;
    sysctl!(HW_NCPU, &mut cpus, mem::size_of::<usize>());
    let mut data: Vec<bsd::sysctl_cpu> = Vec::with_capacity(cpus);
    unsafe { data.set_len(cpus) };
    for i in 0..cpus {
        let mut mib = KERN_CPTIME2.clone();
        mib[2] = i as i32;
        sysctl!(mib, &mut data[i], mem::size_of::<bsd::sysctl_cpu>());
    }
    Ok(data.into_iter().map(|cpu| cpu.into()).collect())
}

#[derive(Default, Debug)]
#[repr(C)]
struct apm_power_info {
    battery_state: c_uchar,
    ac_state: c_uchar,
    battery_life: c_uchar,
    spare1: c_uchar,
    minutes_left: c_uint,
    spare2: [c_uint; 6],
}

#[derive(Default, Debug)]
#[repr(C)]
struct bcachestats {
    numbufs: i64,        /* number of buffers allocated */
    numbufpages: i64,    /* number of pages in buffer cache */
    numdirtypages: i64,  /* number of dirty free pages */
    numcleanpages: i64,  /* number of clean free pages */
    pendingwrites: i64,  /* number of pending writes */
    pendingreads: i64,   /* number of pending reads */
    numwrites: i64,      /* total writes started */
    numreads: i64,       /* total reads started */
    cachehits: i64,      /* total reads found in cache */
    busymapped: i64,     /* number of busy and mapped buffers */
    dmapages: i64,       /* dma reachable pages in buffer cache */
    highpages: i64,      /* pages above dma region */
    delwribufs: i64,     /* delayed write buffers */
    kvaslots: i64,       /* kva slots total */
    kvaslots_avail: i64, /* available kva slots */
    highflips: i64,      /* total flips to above DMA */
    highflops: i64,      /* total failed flips to above DMA */
    dmaflips: i64,       /* total flips from high to DMA */
}

#[derive(Default, Debug)]
#[repr(C)]
struct uvmexp {
    /* vm_page constants */
    pagesize: c_int,  /* size of a page (PAGE_SIZE): must be power of 2 */
    pagemask: c_int,  /* page mask */
    pageshift: c_int, /* page shift */

    /* vm_page counters */
    npages: c_int,   /* number of pages we manage */
    free: c_int,     /* number of free pages */
    active: c_int,   /* number of active pages */
    inactive: c_int, /* number of pages that we free'd but may want back */
    paging: c_int,   /* number of pages in the process of being paged out */
    wired: c_int,    /* number of wired pages */

    zeropages: c_int,          /* number of zero'd pages */
    reserve_pagedaemon: c_int, /* number of pages reserved for pagedaemon */
    reserve_kernel: c_int,     /* number of pages reserved for kernel */
    anonpages: c_int,          /* number of pages used by anon pagers */
    vnodepages: c_int,         /* number of pages used by vnode page cache */
    vtextpages: c_int,         /* number of pages used by vtext vnodes */

    /* pageout params */
    freemin: c_int,     /* min number of free pages */
    freetarg: c_int,    /* target number of free pages */
    inactarg: c_int,    /* target number of inactive pages */
    wiredmax: c_int,    /* max number of wired pages */
    anonmin: c_int,     /* min threshold for anon pages */
    vtextmin: c_int,    /* min threshold for vtext pages */
    vnodemin: c_int,    /* min threshold for vnode pages */
    anonminpct: c_int,  /* min percent anon pages */
    vtextminpct: c_int, /* min percent vtext pages */
    vnodeminpct: c_int, /* min percent vnode pages */

    /* swap */
    nswapdev: c_int,    /* number of configured swap devices in system */
    swpages: c_int,     /* number of PAGE_SIZE'ed swap pages */
    swpginuse: c_int,   /* number of swap pages in use */
    swpgonly: c_int,    /* number of swap pages in use, not also in RAM */
    nswget: c_int,      /* number of times fault calls uvm_swap_get() */
    nanon: c_int,       /* number total of anon's in system */
    nanonneeded: c_int, /* number of anons currently needed */
    nfreeanon: c_int,   /* number of free anon's */

    /* stat counters */
    faults: c_int,   /* page fault count */
    traps: c_int,    /* trap count */
    intrs: c_int,    /* interrupt count */
    swtch: c_int,    /* context switch count */
    softs: c_int,    /* software interrupt count */
    syscalls: c_int, /* system calls */
    pageins: c_int,  /* pagein operation count */
    /* pageouts are in pdpageouts below */
    obsolete_swapins: c_int,  /* swapins */
    obsolete_swapouts: c_int, /* swapouts */
    pgswapin: c_int,          /* pages swapped in */
    pgswapout: c_int,         /* pages swapped out */
    forks: c_int,             /* forks */
    forks_ppwait: c_int,      /* forks where parent waits */
    forks_sharevm: c_int,     /* forks where vmspace is shared */
    pga_zerohit: c_int,       /* pagealloc where zero wanted and zero
                              was available */
    pga_zeromiss: c_int, /* pagealloc where zero wanted and zero
                         not available */
    zeroaborts: c_int, /* number of times page zeroing was
                       aborted */

    /* fault subcounters */
    fltnoram: c_int,   /* number of times fault was out of ram */
    fltnoanon: c_int,  /* number of times fault was out of anons */
    fltnoamap: c_int,  /* number of times fault was out of amap chunks */
    fltpgwait: c_int,  /* number of times fault had to wait on a page */
    fltpgrele: c_int,  /* number of times fault found a released page */
    fltrelck: c_int,   /* number of times fault relock called */
    fltrelckok: c_int, /* number of times fault relock is a success */
    fltanget: c_int,   /* number of times fault gets anon page */
    fltanretry: c_int, /* number of times fault retrys an anon get */
    fltamcopy: c_int,  /* number of times fault clears "needs copy" */
    fltnamap: c_int,   /* number of times fault maps a neighbor anon page */
    fltnomap: c_int,   /* number of times fault maps a neighbor obj page */
    fltlget: c_int,    /* number of times fault does a locked pgo_get */
    fltget: c_int,     /* number of times fault does an unlocked get */
    flt_anon: c_int,   /* number of times fault anon (case 1a) */
    flt_acow: c_int,   /* number of times fault anon cow (case 1b) */
    flt_obj: c_int,    /* number of times fault is on object page (2a) */
    flt_prcopy: c_int, /* number of times fault promotes with copy (2b) */
    flt_przero: c_int, /* number of times fault promotes with zerofill (2b) */

    /* daemon counters */
    pdwoke: c_int,     /* number of times daemon woke up */
    pdrevs: c_int,     /* number of times daemon rev'd clock hand */
    pdswout: c_int,    /* number of times daemon called for swapout */
    pdfreed: c_int,    /* number of pages daemon freed since boot */
    pdscans: c_int,    /* number of pages daemon scanned since boot */
    pdanscan: c_int,   /* number of anonymous pages scanned by daemon */
    pdobscan: c_int,   /* number of object pages scanned by daemon */
    pdreact: c_int,    /* number of pages daemon reactivated since boot */
    pdbusy: c_int,     /* number of times daemon found a busy page */
    pdpageouts: c_int, /* number of times daemon started a pageout */
    pdpending: c_int,  /* number of times daemon got a pending pagout */
    pddeact: c_int,    /* number of pages daemon deactivates */
    pdreanon: c_int,   /* anon pages reactivated due to min threshold */
    pdrevnode: c_int,  /* vnode pages reactivated due to min threshold */
    pdrevtext: c_int,  /* vtext pages reactivated due to min threshold */

    fpswtch: c_int, /* FPU context switches */
    kmapent: c_int, /* number of kernel map entries */
}