diff --git a/src/ctl_type.rs b/src/ctl_type.rs index 8a314599..13db38b2 100644 --- a/src/ctl_type.rs +++ b/src/ctl_type.rs @@ -37,6 +37,8 @@ pub enum CtlType { None = 0, #[cfg(target_os = "freebsd")] Temperature = 16, + #[cfg(target_os = "freebsd")] + List = 17, } impl std::convert::From for CtlType { fn from(t: u32) -> Self { @@ -65,6 +67,8 @@ impl std::convert::From<&CtlValue> for CtlType { &CtlValue::U32(_) => CtlType::U32, #[cfg(target_os = "freebsd")] &CtlValue::Temperature(_) => CtlType::Temperature, + #[cfg(target_os = "freebsd")] + &CtlValue::List(_) => CtlType::List, } } } @@ -97,6 +101,8 @@ impl CtlType { // Added custom types below #[cfg(target_os = "freebsd")] &CtlType::Temperature => 0, + #[cfg(target_os = "freebsd")] + &CtlType::List => 0, } } } diff --git a/src/ctl_value.rs b/src/ctl_value.rs index a5c1f78c..2358a572 100644 --- a/src/ctl_value.rs +++ b/src/ctl_value.rs @@ -36,6 +36,8 @@ pub enum CtlValue { U32(u32), #[cfg(target_os = "freebsd")] Temperature(Temperature), + #[cfg(target_os = "freebsd")] + List(Vec), } impl std::fmt::Display for CtlValue { @@ -59,6 +61,8 @@ impl std::fmt::Display for CtlValue { CtlValue::String(s) => s.to_owned(), #[cfg(target_os = "freebsd")] CtlValue::Temperature(t) => format!("{}", t.kelvin()), + #[cfg(target_os = "freebsd")] + CtlValue::List(_) => "[List]".to_owned(), }; write!(f, "{}", s) } diff --git a/src/unix/funcs.rs b/src/unix/funcs.rs index 919da603..72ed3fc3 100644 --- a/src/unix/funcs.rs +++ b/src/unix/funcs.rs @@ -233,6 +233,78 @@ pub fn value_oid(oid: &Vec) -> Result { return temperature(&info, &val); } + // This only appears to be used on FreeBSD for kern.cp_time and kern.cp_times which are type Long + // Handling for other types may be unnecessary + #[cfg(target_os = "freebsd")] + { + let expected_length = info.ctl_type.min_type_size(); + // check if sysctl returned a list of values (such as kern.cp_times) + if expected_length > 0 && new_val_len > expected_length { + assert_eq!( + new_val_len % expected_length, + 0, + "Sysctl length {} is not evenly divisible by expected length for {:?} {}", + new_val_len, + info.ctl_type, + expected_length + ); + let list_length: usize = new_val_len / expected_length; + + macro_rules! ctl_match_arm { + ( $tp:tt, read_u8 ) => { + Ok(CtlValue::List( + (0..list_length) + .map(|x| CtlValue::$tp(val[x])) + .collect::>(), + )) + }; + ( $tp:tt, read_i8 ) => { + Ok(CtlValue::List( + (0..list_length) + .map(|x| CtlValue::$tp(val[x] as i8)) + .collect::>(), + )) + }; + ( $tp:tt, $fn:tt) => { + Ok(CtlValue::List( + (0..list_length) + .map(|x| { + CtlValue::$tp(byteorder::LittleEndian::$fn( + &val[x * expected_length..], + )) + }) + .collect::>(), + )) + }; + } + + macro_rules! ctl_expand_list { + ($obj:expr, $( $tp:tt => $fn:tt ),*) => { + match $obj { + $(CtlType::$tp => ctl_match_arm!($tp, $fn)),*, + _ => Err(SysctlError::ExtractionError) + } + } + } + + return ctl_expand_list!( + info.ctl_type, + Int => read_i32, + S64 => read_i64, + Uint => read_u32, + Long => read_i64, + Ulong => read_u64, + U64 => read_u64, + U8 => read_u8, + U16 => read_u16, + S8 => read_i8, + S16 => read_i16, + S32 => read_i32, + U32 => read_u32 + ); + } + } + // Wrap in Enum and return match info.ctl_type { CtlType::None => Ok(CtlValue::None), @@ -759,6 +831,25 @@ mod tests_freebsd { assert_eq!(oid[1], libc::KERN_PROC); assert_eq!(oid[2], libc::KERN_PROC_PID); } + + #[test] + fn list_type_kern_cp_time() { + use traits::Sysctl; + let cp_time = crate::Ctl::new("kern.cp_time").unwrap().value().unwrap(); + if let crate::CtlValue::List(list) = &cp_time { + /* + should contain 5 values for: + * user + * nice + * system + * interrupt + * idle + */ + assert_eq!(list.len(), 5); + } + let val_type: crate::CtlType = cp_time.into(); + assert_eq!(val_type, crate::CtlType::List); + } } #[cfg(all(test, target_os = "macos"))]