#![allow(non_camel_case_types)]
use crate::Error;
use libc::{c_char, c_int, c_longlong, c_uchar, c_uint, c_ulonglong, c_void, size_t};
use std::convert::TryFrom;
use std::ffi::CStr;
use std::fmt::{self, Debug};
use std::mem::size_of;
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct kstat_ctl_t {
pub kc_chain_id: kid_t,
pub kc_chain: *mut kstat_t,
pub kc_kd: c_int,
}
pub type hrtime_t = c_longlong;
pub type kid_t = c_int;
pub const KSTAT_STRLEN: usize = 31;
pub const KSTAT_TYPE_RAW: u8 = 0;
pub const KSTAT_TYPE_NAMED: u8 = 1;
pub const KSTAT_TYPE_INTR: u8 = 2;
pub const KSTAT_TYPE_IO: u8 = 3;
pub const KSTAT_TYPE_TIMER: u8 = 4;
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct kstat_t {
pub ks_crtime: hrtime_t,
pub ks_next: *mut kstat_t,
pub ks_kid: kid_t,
pub ks_module: [c_char; KSTAT_STRLEN],
_ks_resv: c_uchar,
pub ks_instance: c_int,
pub ks_name: [c_char; KSTAT_STRLEN],
pub ks_type: c_uchar,
pub ks_class: [c_char; KSTAT_STRLEN],
pub ks_flags: c_char,
pub ks_data: *mut c_void,
pub ks_ndata: c_uint,
pub ks_data_size: size_t,
pub ks_snaptime: hrtime_t,
}
pub const KSTAT_DATA_CHAR: u8 = 0;
pub const KSTAT_DATA_INT32: u8 = 1;
pub const KSTAT_DATA_UINT32: u8 = 2;
pub const KSTAT_DATA_INT64: u8 = 3;
pub const KSTAT_DATA_UINT64: u8 = 4;
pub const KSTAT_DATA_STRING: u8 = 9;
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct kstat_named_t {
pub name: [c_char; KSTAT_STRLEN],
pub data_type: c_uchar,
pub value: NamedDataUnion,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union NamedDataUnion {
pub charc: [c_uchar; 16],
pub str: NamedStr,
pub i32: i32,
pub ui32: u32,
pub i64: i64,
pub ui64: u64,
}
impl Debug for NamedDataUnion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "NamedDataUnion(0x{:#x?})", self as *const _)
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct NamedStr {
pub addr: *const c_char,
pub len: u32,
}
impl TryFrom<&NamedStr> for &str {
type Error = Error;
fn try_from(n: &NamedStr) -> Result<Self, Self::Error> {
if n.addr.is_null() {
Err(Error::NullData)
} else {
unsafe { CStr::from_ptr(n.addr) }
.to_str()
.map_err(|_| Error::InvalidString)
}
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct kstat_intr_t {
pub intr_hard: u32,
pub intr_soft: u32,
pub intr_watchdog: u32,
pub intr_spurious: u32,
pub intr_multisvc: u32,
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct kstat_timer_t {
pub name: [c_char; KSTAT_STRLEN],
_resv: c_uchar,
pub num_events: c_ulonglong,
pub elapsed_time: hrtime_t,
pub min_time: hrtime_t,
pub max_time: hrtime_t,
pub start_time: hrtime_t,
pub stop_time: hrtime_t,
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct kstat_io_t {
pub nread: c_ulonglong,
pub nwritten: c_ulonglong,
pub reads: c_uint,
pub writes: c_uint,
pub wtime: hrtime_t,
pub wlentime: hrtime_t,
pub wlastupdate: hrtime_t,
pub rtime: hrtime_t,
pub rlentime: hrtime_t,
pub rlastupdate: hrtime_t,
pub wcnt: c_uint,
pub rcnt: c_uint,
}
pub fn kstat_data_raw(kstat: &kstat_t) -> Vec<&[u8]> {
let n_data: usize = kstat.ks_ndata as _;
if n_data == 0 {
Vec::new()
} else {
let item_size = kstat.ks_data_size / n_data;
let mut start = kstat.ks_data as *const u8;
let mut out = Vec::with_capacity(n_data);
for _ in 0..kstat.ks_ndata {
out.push(unsafe { std::slice::from_raw_parts(start, item_size) });
start = unsafe { start.add(item_size) };
}
out
}
}
pub fn kstat_data_io(kstat: &kstat_t) -> &kstat_io_t {
assert!(kstat.ks_ndata == 1);
assert!(kstat.ks_data_size == size_of::<kstat_io_t>());
unsafe { (kstat.ks_data as *const kstat_io_t).as_ref() }.unwrap()
}
pub fn kstat_data_intr(kstat: &kstat_t) -> &kstat_intr_t {
assert!(kstat.ks_ndata == 1);
assert!(kstat.ks_data_size == size_of::<kstat_intr_t>());
unsafe { (kstat.ks_data as *const kstat_intr_t).as_ref() }.unwrap()
}
pub fn kstat_data_timer(kstat: &kstat_t) -> &[kstat_timer_t] {
assert!(kstat.ks_data_size == (kstat.ks_ndata as usize * size_of::<kstat_timer_t>()));
unsafe { std::slice::from_raw_parts(kstat.ks_ndata as *const _, kstat.ks_ndata as _) }
}
pub fn kstat_data_named(kstat: &kstat_t) -> &[kstat_named_t] {
let reported_count = kstat.ks_ndata as usize;
let actual_count = kstat.ks_data_size / size_of::<kstat_named_t>();
let count = reported_count.min(actual_count);
unsafe { std::slice::from_raw_parts(kstat.ks_data as *const _, count) }
}
#[cfg(target_os = "illumos")]
mod sys_impl {
use super::*;
#[link(name = "kstat")]
extern "C" {
pub fn kstat_open() -> *mut kstat_ctl_t;
pub fn kstat_close(_: *mut kstat_ctl_t) -> i32;
pub fn kstat_read(_: *mut kstat_ctl_t, _: *mut kstat_t, _: *mut c_void) -> kid_t;
pub fn kstat_chain_update(_: *mut kstat_ctl_t) -> kid_t;
}
pub fn open() -> Result<*mut kstat_ctl_t, Error> {
let ctl = unsafe { kstat_open() };
if ctl.is_null() {
Err(std::io::Error::last_os_error().into())
} else {
Ok(ctl)
}
}
pub fn close(ctl: *mut kstat_ctl_t) -> Result<(), Error> {
let kid = unsafe { kstat_close(ctl) };
if kid == -1 {
Err(std::io::Error::last_os_error().into())
} else {
Ok(())
}
}
pub fn read(ctl: *mut kstat_ctl_t, kstat: *mut kstat_t, kid: *mut c_void) -> Result<(), Error> {
if unsafe { kstat_read(ctl, kstat, kid) } == -1 {
Err(std::io::Error::last_os_error().into())
} else {
Ok(())
}
}
pub fn update(ctl: *mut kstat_ctl_t) -> Result<kid_t, Error> {
let kid = unsafe { kstat_chain_update(ctl) };
if kid == -1 {
Err(std::io::Error::last_os_error().into())
} else {
Ok(kid)
}
}
}
#[cfg(not(target_os = "illumos"))]
mod sys_impl {
use super::*;
#[inline(always)]
fn notsup() -> Error {
Error::Io(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"kstats are only supported on illumos",
))
}
pub fn open() -> Result<*mut kstat_ctl_t, Error> {
Err(notsup())
}
pub fn close(_: *mut kstat_ctl_t) -> Result<(), Error> {
Err(notsup())
}
pub fn read(_: *mut kstat_ctl_t, _: *mut kstat_t, _: *mut c_void) -> Result<(), Error> {
Err(notsup())
}
pub fn update(_: *mut kstat_ctl_t) -> Result<kid_t, Error> {
Err(notsup())
}
}
pub use sys_impl::*;
pub(crate) fn array_to_cstr(s: &[c_char; KSTAT_STRLEN]) -> Result<&str, Error> {
unsafe { CStr::from_ptr(s.as_ptr() as *const _) }
.to_str()
.map_err(|_| Error::InvalidString)
}
#[cfg(all(test, target_os = "illumos"))]
mod test {
use super::*;
#[test]
fn test_kstat_ctl() {
let ctl = open().expect("Failed to open kstat_ctl");
assert!(!ctl.is_null());
close(ctl).expect("Failed to close kstat_ctl");
}
}