use byteorder::{ByteOrder, WriteBytesExt};
use consts::*;
use ctl_error::*;
use ctl_info::*;
use ctl_type::*;
use ctl_value::*;
#[cfg(target_os = "freebsd")]
use temperature::*;
#[cfg(not(target_os = "macos"))]
pub fn name2oid(name: &str) -> Result<Vec<libc::c_int>, SysctlError> {
let oid: [libc::c_int; 2] = [0, 3];
let mut len: usize = CTL_MAXNAME as usize * std::mem::size_of::<libc::c_int>();
let mut res: Vec<libc::c_int> = vec![0; CTL_MAXNAME as usize];
let ret = unsafe {
libc::sysctl(
oid.as_ptr(),
2,
res.as_mut_ptr() as *mut libc::c_void,
&mut len,
name.as_ptr() as *const libc::c_void,
name.len(),
)
};
if ret < 0 {
let e = std::io::Error::last_os_error();
return Err(match e.kind() {
std::io::ErrorKind::NotFound => SysctlError::NotFound(name.into()),
_ => SysctlError::IoError(e),
});
}
len /= std::mem::size_of::<libc::c_int>();
res.truncate(len);
Ok(res)
}
#[cfg(target_os = "macos")]
pub fn name2oid(name: &str) -> Result<Vec<libc::c_int>, SysctlError> {
let mut oid: [libc::c_int; 2] = [0, 3];
let mut len: usize = CTL_MAXNAME as usize * std::mem::size_of::<libc::c_int>();
let mut res: Vec<libc::c_int> = vec![0; CTL_MAXNAME as usize];
let ret = unsafe {
libc::sysctl(
oid.as_mut_ptr(),
2,
res.as_mut_ptr() as *mut libc::c_void,
&mut len,
name.as_ptr() as *mut libc::c_void,
name.len(),
)
};
if ret < 0 {
let e = std::io::Error::last_os_error();
return Err(match e.kind() {
std::io::ErrorKind::NotFound => SysctlError::NotFound(name.into()),
_ => SysctlError::IoError(e),
});
}
len /= std::mem::size_of::<libc::c_int>();
res.truncate(len);
Ok(res)
}
#[cfg(not(target_os = "macos"))]
pub fn oidfmt(oid: &[libc::c_int]) -> Result<CtlInfo, SysctlError> {
let mut qoid: Vec<libc::c_int> = vec![0, 4];
qoid.extend(oid);
let mut buf: [libc::c_uchar; libc::BUFSIZ as usize] = [0; libc::BUFSIZ as usize];
let mut buf_len = std::mem::size_of_val(&buf);
let ret = unsafe {
libc::sysctl(
qoid.as_ptr(),
qoid.len() as u32,
buf.as_mut_ptr() as *mut libc::c_void,
&mut buf_len,
std::ptr::null(),
0,
)
};
if ret != 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
let kind = byteorder::LittleEndian::read_u32(&buf);
let ctltype_val = kind & CTLTYPE as u32;
let fmt: String =
match std::ffi::CStr::from_bytes_with_nul(&buf[std::mem::size_of::<u32>()..buf_len]) {
Ok(x) => x.to_string_lossy().into(),
Err(e) => return Err(SysctlError::InvalidCStr(e)),
};
let s = CtlInfo {
ctl_type: CtlType::from(ctltype_val),
fmt: fmt,
flags: kind,
};
Ok(s)
}
#[cfg(target_os = "macos")]
pub fn oidfmt(oid: &[libc::c_int]) -> Result<CtlInfo, SysctlError> {
let mut qoid: Vec<libc::c_int> = vec![0, 4];
qoid.extend(oid);
let mut buf: [libc::c_uchar; libc::BUFSIZ as usize] = [0; libc::BUFSIZ as usize];
let mut buf_len = std::mem::size_of_val(&buf);
let ret = unsafe {
libc::sysctl(
qoid.as_mut_ptr(),
qoid.len() as u32,
buf.as_mut_ptr() as *mut libc::c_void,
&mut buf_len,
std::ptr::null_mut(),
0,
)
};
if ret != 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
let kind = byteorder::LittleEndian::read_u32(&buf);
let ctltype_val = kind & CTLTYPE as u32;
let fmt: String = match std::str::from_utf8(&buf[std::mem::size_of::<u32>()..buf_len]) {
Ok(x) => x.to_owned(),
Err(e) => return Err(SysctlError::Utf8Error(e)),
};
let s = CtlInfo {
ctl_type: CtlType::from(ctltype_val),
fmt: fmt,
flags: kind,
};
Ok(s)
}
#[cfg(not(target_os = "macos"))]
pub fn value_oid(oid: &Vec<i32>) -> Result<CtlValue, SysctlError> {
let info: CtlInfo = try!(oidfmt(&oid));
if !(info.flags & CTLFLAG_RD == CTLFLAG_RD) {
return Err(SysctlError::NoReadAccess);
}
let mut val_len = 0;
let ret = unsafe {
libc::sysctl(
oid.as_ptr(),
oid.len() as u32,
std::ptr::null_mut(),
&mut val_len,
std::ptr::null(),
0,
)
};
if ret < 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
let val_minsize = std::cmp::max(val_len, info.ctl_type.min_type_size());
let mut val: Vec<libc::c_uchar> = vec![0; val_minsize];
let mut new_val_len = val_len;
let ret = unsafe {
libc::sysctl(
oid.as_ptr(),
oid.len() as u32,
val.as_mut_ptr() as *mut libc::c_void,
&mut new_val_len,
std::ptr::null(),
0,
)
};
if ret < 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
assert!(new_val_len <= val_len);
if new_val_len < val_len {
return Err(SysctlError::ShortRead {
read: new_val_len,
reported: val_len,
});
}
if info.is_temperature() {
return temperature(&info, &val);
}
match info.ctl_type {
CtlType::None => Ok(CtlValue::None),
CtlType::Node => Ok(CtlValue::Node(val)),
CtlType::Int => Ok(CtlValue::Int(byteorder::LittleEndian::read_i32(&val))),
CtlType::String => match val.len() {
0 => Ok(CtlValue::String("".to_string())),
l => std::str::from_utf8(&val[..l - 1])
.map_err(|e| SysctlError::Utf8Error(e))
.map(|s| CtlValue::String(s.into())),
},
CtlType::S64 => Ok(CtlValue::S64(byteorder::LittleEndian::read_u64(&val))),
CtlType::Struct => Ok(CtlValue::Struct(val)),
CtlType::Uint => Ok(CtlValue::Uint(byteorder::LittleEndian::read_u32(&val))),
CtlType::Long => Ok(CtlValue::Long(byteorder::LittleEndian::read_i64(&val))),
CtlType::Ulong => Ok(CtlValue::Ulong(byteorder::LittleEndian::read_u64(&val))),
CtlType::U64 => Ok(CtlValue::U64(byteorder::LittleEndian::read_u64(&val))),
CtlType::U8 => Ok(CtlValue::U8(val[0])),
CtlType::U16 => Ok(CtlValue::U16(byteorder::LittleEndian::read_u16(&val))),
CtlType::S8 => Ok(CtlValue::S8(val[0] as i8)),
CtlType::S16 => Ok(CtlValue::S16(byteorder::LittleEndian::read_i16(&val))),
CtlType::S32 => Ok(CtlValue::S32(byteorder::LittleEndian::read_i32(&val))),
CtlType::U32 => Ok(CtlValue::U32(byteorder::LittleEndian::read_u32(&val))),
_ => Err(SysctlError::UnknownType),
}
}
#[cfg(target_os = "macos")]
pub fn value_oid(oid: &mut Vec<i32>) -> Result<CtlValue, SysctlError> {
let info: CtlInfo = try!(oidfmt(&oid));
if !(info.flags & CTLFLAG_RD == CTLFLAG_RD) {
return Err(SysctlError::NoReadAccess);
}
let mut val_len = 0;
let ret = unsafe {
libc::sysctl(
oid.as_mut_ptr(),
oid.len() as u32,
std::ptr::null_mut(),
&mut val_len,
std::ptr::null_mut(),
0,
)
};
if ret < 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
let val_minsize = std::cmp::max(val_len, info.ctl_type.min_type_size());
let mut val: Vec<libc::c_uchar> = vec![0; val_minsize];
let mut new_val_len = val_len;
let ret = unsafe {
libc::sysctl(
oid.as_mut_ptr(),
oid.len() as u32,
val.as_mut_ptr() as *mut libc::c_void,
&mut new_val_len,
std::ptr::null_mut(),
0,
)
};
if ret < 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
assert!(new_val_len <= val_len);
if new_val_len < val_len {
return Err(SysctlError::ShortRead {
read: new_val_len,
reported: val_len,
});
}
match info.ctl_type {
CtlType::None => Ok(CtlValue::None),
CtlType::Node => Ok(CtlValue::Node(val)),
CtlType::Int => Ok(CtlValue::Int(byteorder::LittleEndian::read_i32(&val))),
CtlType::String => match std::str::from_utf8(&val[..val.len() - 1]) {
Ok(s) => Ok(CtlValue::String(s.into())),
Err(e) => Err(SysctlError::Utf8Error(e)),
},
CtlType::S64 => Ok(CtlValue::S64(byteorder::LittleEndian::read_u64(&val))),
CtlType::Struct => Ok(CtlValue::Struct(val)),
CtlType::Uint => Ok(CtlValue::Uint(byteorder::LittleEndian::read_u32(&val))),
CtlType::Long => Ok(CtlValue::Long(byteorder::LittleEndian::read_i64(&val))),
CtlType::Ulong => Ok(CtlValue::Ulong(byteorder::LittleEndian::read_u64(&val))),
CtlType::U64 => Ok(CtlValue::U64(byteorder::LittleEndian::read_u64(&val))),
CtlType::U8 => Ok(CtlValue::U8(val[0])),
CtlType::U16 => Ok(CtlValue::U16(byteorder::LittleEndian::read_u16(&val))),
CtlType::S8 => Ok(CtlValue::S8(val[0] as i8)),
CtlType::S16 => Ok(CtlValue::S16(byteorder::LittleEndian::read_i16(&val))),
CtlType::S32 => Ok(CtlValue::S32(byteorder::LittleEndian::read_i32(&val))),
CtlType::U32 => Ok(CtlValue::U32(byteorder::LittleEndian::read_u32(&val))),
#[cfg(not(target_os = "macos"))]
_ => Err(SysctlError::UnknownType),
}
}
#[cfg(not(target_os = "macos"))]
pub fn value_oid_as<T>(oid: &Vec<i32>) -> Result<Box<T>, SysctlError> {
let val_enum = try!(value_oid(oid));
if let CtlValue::Struct(val) = val_enum {
assert_eq!(
std::mem::size_of::<T>(),
val.len(),
"Error memory size mismatch. Size of struct {}, size of data retrieved {}.",
std::mem::size_of::<T>(),
val.len()
);
let val_array: Box<[u8]> = val.into_boxed_slice();
let val_raw: *mut T = Box::into_raw(val_array) as *mut T;
let val_box: Box<T> = unsafe { Box::from_raw(val_raw) };
Ok(val_box)
} else if let CtlValue::Node(val) = val_enum {
assert_eq!(
std::mem::size_of::<T>(),
val.len(),
"Error memory size mismatch. Size of struct {}, size of data retrieved {}.",
std::mem::size_of::<T>(),
val.len()
);
let val_array: Box<[u8]> = val.into_boxed_slice();
let val_raw: *mut T = Box::into_raw(val_array) as *mut T;
let val_box: Box<T> = unsafe { Box::from_raw(val_raw) };
Ok(val_box)
} else {
Err(SysctlError::ExtractionError)
}
}
#[cfg(target_os = "macos")]
pub fn value_oid_as<T>(oid: &mut Vec<i32>) -> Result<Box<T>, SysctlError> {
let val_enum = try!(value_oid(oid));
if let CtlValue::Struct(val) = val_enum {
assert_eq!(
std::mem::size_of::<T>(),
val.len(),
"Error memory size mismatch. Size of struct {}, size of data retrieved {}.",
std::mem::size_of::<T>(),
val.len()
);
let val_array: Box<[u8]> = val.into_boxed_slice();
let val_raw: *mut T = Box::into_raw(val_array) as *mut T;
let val_box: Box<T> = unsafe { Box::from_raw(val_raw) };
Ok(val_box)
} else if let CtlValue::Node(val) = val_enum {
assert_eq!(
std::mem::size_of::<T>(),
val.len(),
"Error memory size mismatch. Size of struct {}, size of data retrieved {}.",
std::mem::size_of::<T>(),
val.len()
);
let val_array: Box<[u8]> = val.into_boxed_slice();
let val_raw: *mut T = Box::into_raw(val_array) as *mut T;
let val_box: Box<T> = unsafe { Box::from_raw(val_raw) };
Ok(val_box)
} else {
Err(SysctlError::ExtractionError)
}
}
fn value_to_bytes(value: CtlValue) -> Result<Vec<u8>, SysctlError> {
match value {
CtlValue::String(v) => Ok(v.as_bytes().to_owned()),
CtlValue::Ulong(v) => {
let mut bytes = vec![];
bytes.write_u64::<byteorder::LittleEndian>(v)?;
Ok(bytes)
}
CtlValue::Uint(v) => {
let mut bytes = vec![];
bytes.write_u32::<byteorder::LittleEndian>(v)?;
Ok(bytes)
}
CtlValue::Int(v) => {
let mut bytes = vec![];
bytes.write_i32::<byteorder::LittleEndian>(v)?;
Ok(bytes)
}
CtlValue::U8(v) => {
let mut bytes = vec![];
bytes.write_u8(v)?;
Ok(bytes)
}
_ => Err(SysctlError::MissingImplementation),
}
}
#[cfg(not(target_os = "macos"))]
pub fn set_oid_value(oid: &Vec<libc::c_int>, value: CtlValue) -> Result<CtlValue, SysctlError> {
let info: CtlInfo = try!(oidfmt(&oid));
if !(info.flags & CTLFLAG_WR == CTLFLAG_WR) {
return Err(SysctlError::NoWriteAccess);
}
let ctl_type = CtlType::from(&value);
assert_eq!(
info.ctl_type, ctl_type,
"Error type mismatch. Type given {:?}, sysctl type: {:?}",
ctl_type, info.ctl_type
);
let bytes = value_to_bytes(value)?;
let ret = unsafe {
libc::sysctl(
oid.as_ptr(),
oid.len() as u32,
std::ptr::null_mut(),
std::ptr::null_mut(),
bytes.as_ptr() as *const libc::c_void,
bytes.len(),
)
};
if ret < 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
self::value_oid(oid)
}
#[cfg(target_os = "macos")]
pub fn set_oid_value(oid: &mut Vec<libc::c_int>, value: CtlValue) -> Result<CtlValue, SysctlError> {
let info: CtlInfo = try!(oidfmt(&oid));
if !(info.flags & CTLFLAG_WR == CTLFLAG_WR) {
return Err(SysctlError::NoWriteAccess);
}
let ctl_type = CtlType::from(&value);
assert_eq!(
info.ctl_type, ctl_type,
"Error type mismatch. Type given {:?}, sysctl type: {:?}",
ctl_type, info.ctl_type
);
let bytes = value_to_bytes(value)?;
let ret = unsafe {
libc::sysctl(
oid.as_mut_ptr(),
oid.len() as u32,
std::ptr::null_mut(),
std::ptr::null_mut(),
bytes.as_ptr() as *mut libc::c_void,
bytes.len(),
)
};
if ret < 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
self::value_oid(oid)
}
#[cfg(not(target_os = "macos"))]
pub fn oid2description(oid: &Vec<libc::c_int>) -> Result<String, SysctlError> {
let mut qoid: Vec<libc::c_int> = vec![0, 5];
qoid.extend(oid);
let mut buf: [libc::c_uchar; libc::BUFSIZ as usize] = [0; libc::BUFSIZ as usize];
let mut buf_len = std::mem::size_of_val(&buf);
let ret = unsafe {
libc::sysctl(
qoid.as_ptr(),
qoid.len() as u32,
buf.as_mut_ptr() as *mut libc::c_void,
&mut buf_len,
std::ptr::null(),
0,
)
};
if ret != 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
match std::str::from_utf8(&buf[..buf_len - 1]) {
Ok(s) => Ok(s.to_owned()),
Err(e) => Err(SysctlError::Utf8Error(e)),
}
}
#[cfg(not(target_os = "macos"))]
pub fn oid2name(oid: &Vec<libc::c_int>) -> Result<String, SysctlError> {
let mut qoid: Vec<libc::c_int> = vec![0, 1];
qoid.extend(oid);
let mut buf: [libc::c_uchar; libc::BUFSIZ as usize] = [0; libc::BUFSIZ as usize];
let mut buf_len = std::mem::size_of_val(&buf);
let ret = unsafe {
libc::sysctl(
qoid.as_ptr(),
qoid.len() as u32,
buf.as_mut_ptr() as *mut libc::c_void,
&mut buf_len,
std::ptr::null(),
0,
)
};
if ret != 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
match std::str::from_utf8(&buf[..buf_len - 1]) {
Ok(s) => Ok(s.to_owned()),
Err(e) => Err(SysctlError::Utf8Error(e)),
}
}
#[cfg(target_os = "macos")]
pub fn oid2name(oid: &Vec<libc::c_int>) -> Result<String, SysctlError> {
let mut qoid: Vec<libc::c_int> = vec![0, 1];
qoid.extend(oid);
let mut buf: [libc::c_uchar; libc::BUFSIZ as usize] = [0; libc::BUFSIZ as usize];
let mut buf_len = std::mem::size_of_val(&buf);
let ret = unsafe {
libc::sysctl(
qoid.as_mut_ptr(),
qoid.len() as u32,
buf.as_mut_ptr() as *mut libc::c_void,
&mut buf_len,
std::ptr::null_mut(),
0,
)
};
if ret != 0 {
return Err(SysctlError::IoError(std::io::Error::last_os_error()));
}
match std::str::from_utf8(&buf[..buf_len - 1]) {
Ok(s) => Ok(s.to_owned()),
Err(e) => Err(SysctlError::Utf8Error(e)),
}
}
#[cfg(not(target_os = "macos"))]
pub fn next_oid(oid: &Vec<libc::c_int>) -> Result<Option<Vec<libc::c_int>>, SysctlError> {
let mut qoid: Vec<libc::c_int> = vec![0, 2];
qoid.extend(oid);
let mut len: usize = CTL_MAXNAME as usize * std::mem::size_of::<libc::c_int>();
let mut res: Vec<libc::c_int> = vec![0; CTL_MAXNAME as usize];
let ret = unsafe {
libc::sysctl(
qoid.as_ptr(),
qoid.len() as u32,
res.as_mut_ptr() as *mut libc::c_void,
&mut len,
std::ptr::null(),
0,
)
};
if ret != 0 {
let e = std::io::Error::last_os_error();
if e.raw_os_error() == Some(libc::ENOENT) {
return Ok(None);
}
return Err(SysctlError::IoError(e));
}
len /= std::mem::size_of::<libc::c_int>();
res.truncate(len);
Ok(Some(res))
}
#[cfg(target_os = "macos")]
pub fn next_oid(oid: &Vec<libc::c_int>) -> Result<Option<Vec<libc::c_int>>, SysctlError> {
let mut qoid: Vec<libc::c_int> = vec![0, 2];
qoid.extend(oid);
let mut len: usize = CTL_MAXNAME as usize * std::mem::size_of::<libc::c_int>();
let mut res: Vec<libc::c_int> = vec![0; CTL_MAXNAME as usize];
let ret = unsafe {
libc::sysctl(
qoid.as_mut_ptr(),
qoid.len() as u32,
res.as_mut_ptr() as *mut libc::c_void,
&mut len,
std::ptr::null_mut(),
0,
)
};
if ret != 0 {
let e = std::io::Error::last_os_error();
if e.raw_os_error() == Some(libc::ENOENT) {
return Ok(None);
}
return Err(SysctlError::IoError(e));
}
len /= std::mem::size_of::<libc::c_int>();
res.truncate(len);
Ok(Some(res))
}
#[cfg(test)]
mod tests {
use crate::Sysctl;
#[test]
fn ctl_name() {
let oid = vec![libc::CTL_KERN, libc::KERN_OSREV];
let name = super::oid2name(&oid).expect("Could not get name of kern.osrevision sysctl.");
assert_eq!(name, "kern.osrevision");
let ctl = crate::Ctl { oid };
let name = ctl
.name()
.expect("Could not get name of kern.osrevision sysctl.");
assert_eq!(name, "kern.osrevision");
}
#[test]
fn ctl_type() {
let oid = super::name2oid("kern").unwrap();
let fmt = super::oidfmt(&oid).unwrap();
assert_eq!(fmt.ctl_type, crate::CtlType::Node);
let kern = crate::Ctl::new("kern").expect("Could not get kern node");
let value_type = kern.value_type().expect("Could not get kern value type");
assert_eq!(value_type, crate::CtlType::Node);
let oid = super::name2oid("kern.osrelease").unwrap();
let fmt = super::oidfmt(&oid).unwrap();
assert_eq!(fmt.ctl_type, crate::CtlType::String);
let osrelease =
crate::Ctl::new("kern.osrelease").expect("Could not get kern.osrelease sysctl");
let value_type = osrelease
.value_type()
.expect("Could not get kern.osrelease value type");
assert_eq!(value_type, crate::CtlType::String);
let oid = super::name2oid("kern.osrevision").unwrap();
let fmt = super::oidfmt(&oid).unwrap();
assert_eq!(fmt.ctl_type, crate::CtlType::Int);
let osrevision =
crate::Ctl::new("kern.osrevision").expect("Could not get kern.osrevision sysctl");
let value_type = osrevision
.value_type()
.expect("Could notget kern.osrevision value type");
assert_eq!(value_type, crate::CtlType::Int);
}
}
#[cfg(all(test, target_os = "freebsd"))]
mod tests_freebsd {
#[test]
fn ctl_mib() {
let oid = super::name2oid("kern.proc.pid").unwrap();
assert_eq!(oid.len(), 3);
assert_eq!(oid[0], libc::CTL_KERN);
assert_eq!(oid[1], libc::KERN_PROC);
assert_eq!(oid[2], libc::KERN_PROC_PID);
}
}
#[cfg(all(test, target_os = "macos"))]
mod tests_macos {
#[test]
fn ctl_mib() {
let oid = super::name2oid("kern.proc.pid").unwrap();
assert_eq!(oid.len(), 3);
assert_eq!(oid[0], libc::CTL_KERN);
assert_eq!(oid[1], libc::KERN_PROC);
assert_eq!(oid[2], libc::KERN_PROC_PID);
}
}