sysctl-0.4.4/.cargo_vcs_info.json0000644000000001120000000000100123540ustar { "git": { "sha1": "45e1818df35ed4c63c1c622660db4ece84ad86e1" } } sysctl-0.4.4/CHANGELOG.md000064400000000000000000000027210072674642500130150ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. ## [0.4.4] - 2022-03-01 ### Changed - Use fmt to determine the exact type for CtlType::Int on MacOS ## [0.4.3] - 2021-11-01 ### Changed - Remove a leftover debug println. ## [0.4.2] - 2021-08-03 ### Changed - Add Cirrus CI for FreeBSD, macOS and Linux. - Bump thiserror crate. - Use sysctlnametomib(3) where available. - Use sysctlbyname(3) on FreeBSD. - Tell docs.rs to build docs for FreeBSD too. - Don't include docs in package to reduce size. ## [0.4.1] - 2021-04-23 ### Changed - Replace deprecated failure crate with thiserror. - Fix clippy lints. ## [0.4.0] - 2019-07-24 ### Changed - Add Linux support. - Huge refactor. - Improve BSD code to provide a cross platform compatible API. - [BREAKING] Make static functions private, all calls now go through the Ctl object. ## [0.3.0] - 2019-01-07 ### Changed - Improve error handling. - Publish CtlInfo struct. - Add Cirrus CI script. ## [0.2.0] - 2018-05-28 ### Changed - Add iterator support (thanks to Fabian Freyer!). - Add struct interface for control. - Add documentation for macOS. - Use failure create for error handling. ## [0.1.4] - 2018-01-04 ### Changed - Fix documentation link - Fix test on FreeBSD ## [0.1.3] - 2018-01-04 ### Added - Macos support. ## [0.1.2] - 2017-05-23 ### Added - This changelog. - API to get values by OID. - Example value\_oid\_as.rs - Node types can also contain data so treat Nodes same as Struct/Opaque. sysctl-0.4.4/Cargo.lock0000644000000067720000000000100103510ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bitflags" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5cde24d1b2e2216a726368b2363a273739c91f4e3eb4e0dd12d672d396ad989" [[package]] name = "byteorder" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" [[package]] name = "libc" version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0" [[package]] name = "proc-macro2" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f287c234c9b2d0308d692dee5c449c1a171167a6f8150f7cf2a49d8fd96967" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab938ebe6f1c82426b5fb82eaf10c3e3028c53deaa3fbe38f5904b37cf4d767" dependencies = [ "proc-macro2", ] [[package]] name = "same-file" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3257af0472da4b8b8902102a57bafffd9991f0f43772a8af6153d597e6e4ae2" dependencies = [ "winapi", ] [[package]] name = "syn" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f8c8eab7d9f493cd89d4068085651d81ac7d39c56eb64f7158ea514b156e280" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "sysctl" version = "0.4.4" dependencies = [ "bitflags", "byteorder", "libc", "thiserror", "walkdir", ] [[package]] name = "thiserror" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79067843a369b7df0391d33d48636936ee91aa6d6370931eebe1278e6f88a723" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc6215837a07b345fd86880601a9c55bb5974e0cec507c48bbd3aaa006559a9" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "walkdir" version = "2.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7904a7e2bb3cdf0cf5e783f44204a85a37a93151738fa349f06680f59a98b45" dependencies = [ "same-file", "winapi", "winapi-util", ] [[package]] name = "winapi" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3ad91d846a4a5342c1fb7008d26124ee6cf94a3953751618577295373b32117" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a16a8e2ebfc883e2b1771c6482b1fb3c6831eab289ba391619a2d93a7356220f" [[package]] name = "winapi-util" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca29cb03c8ceaf20f8224a18a530938305e9872b1478ea24ff44b4f503a1d1d" sysctl-0.4.4/Cargo.toml0000644000000027020000000000100103610ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "sysctl" version = "0.4.4" authors = ["Johannes Lundberg ", "Ivan Temchenko ", "Fabian Freyer "] include = ["src/**/*", "LICENSE-MIT", "README.md", "CHANGELOG.md"] description = "Simplified interface to libc::sysctl" documentation = "https://docs.rs/sysctl" readme = "README.md" keywords = ["sysctl", "sysfs", "freebsd", "macos", "linux"] license = "MIT" repository = "https://github.com/johalun/sysctl-rs" [package.metadata.docs.rs] targets = ["i686-unknown-freebsd", "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"] [dependencies.bitflags] version = "^1.0" [dependencies.byteorder] version = "^1.0.0" [dependencies.libc] version = "^0.2.34" [dependencies.thiserror] version = "1.0.2" [target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dependencies.walkdir] version = "^2.2.8" sysctl-0.4.4/Cargo.toml.orig000064400000000000000000000015670072674642500141020ustar 00000000000000[package] name = "sysctl" version = "0.4.4" authors = [ "Johannes Lundberg ", "Ivan Temchenko ", "Fabian Freyer " ] description = "Simplified interface to libc::sysctl" keywords = ["sysctl", "sysfs", "freebsd", "macos", "linux"] license = "MIT" readme = "README.md" repository = "https://github.com/johalun/sysctl-rs" documentation = "https://docs.rs/sysctl" include = ["src/**/*", "LICENSE-MIT", "README.md", "CHANGELOG.md"] [package.metadata.docs.rs] targets = [ "i686-unknown-freebsd", "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu", ] [dependencies] libc = "^0.2.34" byteorder = "^1.0.0" thiserror = "1.0.2" bitflags = "^1.0" [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] walkdir = "^2.2.8" sysctl-0.4.4/LICENSE-MIT000064400000000000000000000020730072674642500126400ustar 00000000000000The MIT License (MIT) Copyright (c) 2017 Johannes Lundberg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.sysctl-0.4.4/README.md000064400000000000000000000033760072674642500124720ustar 00000000000000This crate provides a safe interface for reading and writing information to the kernel using the sysctl interface. [![Build Status](https://api.cirrus-ci.com/github/johalun/sysctl-rs.svg)](https://cirrus-ci.com/github/johalun/sysctl-rs/master) [![Current Version](https://img.shields.io/crates/v/sysctl.svg)](https://crates.io/crates/sysctl) *FreeBSD, Linux and macOS are supported.* *Contributions for improvements and other platforms are welcome.* ### Documentation Documentation is available on [docs.rs](https://docs.rs/sysctl) ### Usage Add to `Cargo.toml` ```toml [dependencies] sysctl = "0.4.4" ``` ### macOS * Due to limitations in the sysctl(3) API, many of the methods of the `Ctl` take a mutable reference to `self` on macOS. * Sysctl descriptions are not available on macOS and Linux. * Some tests failures are ignored, as the respective sysctls do not exist on macos. ### Example sysctl comes with several examples, see the examples folder: * `value.rs`: shows how to get a sysctl value * `value_as.rs`: parsing values as structures * `value_string.rs`: parsing values as string. Use this for cross platform compatibility since all sysctls are strings on Linux. * `value_oid_as.rs`: getting a sysctl from OID constants from the `libc` crate. * `set_value.rs`: shows how to set a sysctl value * `struct.rs`: reading data into a struct * `temperature.rs`: parsing temperatures * `iterate.rs`: showcases iteration over the sysctl tree Run with: ```sh $ cargo run --example iterate ``` Or to use in your program: ```rust extern crate sysctl; use sysctl::Sysctl; fn main() { let ctl = sysctl::Ctl::new("kern.osrevision").unwrap(); println!("Description: {}", ctl.description().unwrap()); println!("Value: {}", ctl.value_string().unwrap()); } ``` sysctl-0.4.4/src/consts.rs000064400000000000000000000043470072674642500136600ustar 00000000000000// consts.rs // CTL* constants belong to libc crate but have not been added there yet. // They will be removed from here once in the libc crate. pub const CTL_MAXNAME: libc::c_uint = 24; pub const CTLTYPE: libc::c_uint = 0xf; /* mask for the type */ pub const CTLTYPE_NODE: libc::c_uint = 1; pub const CTLTYPE_INT: libc::c_uint = 2; pub const CTLTYPE_STRING: libc::c_uint = 3; pub const CTLTYPE_S64: libc::c_uint = 4; pub const CTLTYPE_OPAQUE: libc::c_uint = 5; pub const CTLTYPE_STRUCT: libc::c_uint = 5; pub const CTLTYPE_UINT: libc::c_uint = 6; pub const CTLTYPE_LONG: libc::c_uint = 7; pub const CTLTYPE_ULONG: libc::c_uint = 8; pub const CTLTYPE_U64: libc::c_uint = 9; pub const CTLTYPE_U8: libc::c_uint = 10; pub const CTLTYPE_U16: libc::c_uint = 11; pub const CTLTYPE_S8: libc::c_uint = 12; pub const CTLTYPE_S16: libc::c_uint = 13; pub const CTLTYPE_S32: libc::c_uint = 14; pub const CTLTYPE_U32: libc::c_uint = 15; pub const CTLFLAG_RD: libc::c_uint = 0x80000000; pub const CTLFLAG_WR: libc::c_uint = 0x40000000; pub const CTLFLAG_RW: libc::c_uint = 0x80000000 | 0x40000000; pub const CTLFLAG_DORMANT: libc::c_uint = 0x20000000; pub const CTLFLAG_ANYBODY: libc::c_uint = 0x10000000; pub const CTLFLAG_SECURE: libc::c_uint = 0x08000000; pub const CTLFLAG_PRISON: libc::c_uint = 0x04000000; pub const CTLFLAG_DYN: libc::c_uint = 0x02000000; pub const CTLFLAG_SKIP: libc::c_uint = 0x01000000; pub const CTLFLAG_TUN: libc::c_uint = 0x00080000; pub const CTLFLAG_RDTUN: libc::c_uint = CTLFLAG_RD | CTLFLAG_TUN; pub const CTLFLAG_RWTUN: libc::c_uint = CTLFLAG_RW | CTLFLAG_TUN; pub const CTLFLAG_MPSAFE: libc::c_uint = 0x00040000; pub const CTLFLAG_VNET: libc::c_uint = 0x00020000; pub const CTLFLAG_DYING: libc::c_uint = 0x00010000; pub const CTLFLAG_CAPRD: libc::c_uint = 0x00008000; pub const CTLFLAG_CAPWR: libc::c_uint = 0x00004000; pub const CTLFLAG_STATS: libc::c_uint = 0x00002000; pub const CTLFLAG_NOFETCH: libc::c_uint = 0x00001000; pub const CTLFLAG_CAPRW: libc::c_uint = CTLFLAG_CAPRD | CTLFLAG_CAPWR; pub const CTLFLAG_SECURE1: libc::c_uint = 134217728; pub const CTLFLAG_SECURE2: libc::c_uint = 135266304; pub const CTLFLAG_SECURE3: libc::c_uint = 136314880; pub const CTLMASK_SECURE: libc::c_uint = 15728640; pub const CTLSHIFT_SECURE: libc::c_uint = 20; sysctl-0.4.4/src/ctl_error.rs000064400000000000000000000020620072674642500143320ustar 00000000000000// ctl_error.rs use thiserror::Error; #[derive(Debug, Error)] pub enum SysctlError { #[error("no such sysctl: {0}")] NotFound(String), #[error("no matching type for value")] #[cfg(not(target_os = "macos"))] UnknownType, #[error("Error extracting value")] ExtractionError, #[error("Error parsing value")] ParseError, #[error("Support for type not implemented")] MissingImplementation, #[error("IO Error: {0}")] IoError(#[from] std::io::Error), #[error("Error parsing UTF-8 data: {0}")] Utf8Error(#[from] std::str::Utf8Error), #[error("Value is not readable")] NoReadAccess, #[error("Value is not writeable")] NoWriteAccess, #[error("Not supported by this platform")] NotSupported, #[error( "sysctl returned a short read: read {read} bytes, while a size of {reported} was reported" )] ShortRead { read: usize, reported: usize }, #[error("Error reading C String: String was not NUL-terminated.")] InvalidCStr(#[from] std::ffi::FromBytesWithNulError), } sysctl-0.4.4/src/ctl_flags.rs000064400000000000000000000045510072674642500143020ustar 00000000000000// ctl_flags.rs use super::consts::*; // Represents control flags of a sysctl bitflags! { pub struct CtlFlags : libc::c_uint { /// Allow reads of variable const RD = CTLFLAG_RD; /// Allow writes to the variable const WR = CTLFLAG_WR; const RW = Self::RD.bits | Self::WR.bits; /// This sysctl is not active yet const DORMANT = CTLFLAG_DORMANT; /// All users can set this var const ANYBODY = CTLFLAG_ANYBODY; /// Permit set only if securelevel<=0 const SECURE = CTLFLAG_SECURE; /// Prisoned roots can fiddle const PRISON = CTLFLAG_PRISON; /// Dynamic oid - can be freed const DYN = CTLFLAG_DYN; /// Skip this sysctl when listing const SKIP = CTLFLAG_DORMANT; /// Secure level const SECURE_MASK = 0x00F00000; /// Default value is loaded from getenv() const TUN = CTLFLAG_TUN; /// Readable tunable const RDTUN = Self::RD.bits | Self::TUN.bits; /// Readable and writeable tunable const RWTUN = Self::RW.bits | Self::TUN.bits; /// Handler is MP safe const MPSAFE = CTLFLAG_MPSAFE; /// Prisons with vnet can fiddle const VNET = CTLFLAG_VNET; /// Oid is being removed const DYING = CTLFLAG_DYING; /// Can be read in capability mode const CAPRD = CTLFLAG_CAPRD; /// Can be written in capability mode const CAPWR = CTLFLAG_CAPWR; /// Statistics; not a tuneable const STATS = CTLFLAG_STATS; /// Don't fetch tunable from getenv() const NOFETCH = CTLFLAG_NOFETCH; /// Can be read and written in capability mode const CAPRW = Self::CAPRD.bits | Self::CAPWR.bits; } } #[cfg(test)] mod tests { use crate::Sysctl; #[test] fn ctl_flags() { // This sysctl should be read-only. #[cfg(any(target_os = "freebsd", target_os = "macos"))] let ctl: crate::Ctl = crate::Ctl::new("kern.ostype").unwrap(); #[cfg(any(target_os = "android", target_os = "linux"))] let ctl: crate::Ctl = crate::Ctl::new("kernel.ostype").unwrap(); let flags: crate::CtlFlags = ctl.flags().unwrap(); assert_eq!(flags.bits() & crate::CTLFLAG_RD, crate::CTLFLAG_RD); assert_eq!(flags.bits() & crate::CTLFLAG_WR, 0); } } sysctl-0.4.4/src/ctl_info.rs000064400000000000000000000032520072674642500141360ustar 00000000000000// ctl_info.rs use ctl_flags::*; use ctl_type::*; #[derive(Debug, PartialEq)] /// A structure representing control metadata pub struct CtlInfo { /// The control type. pub ctl_type: CtlType, /// A string which specifies the format of the OID in /// a symbolic way. /// /// This format is used as a hint by sysctl(8) to /// apply proper data formatting for display purposes. /// /// Formats defined in sysctl(9): /// * `N` node /// * `A` char * /// * `I` int /// * `IK[n]` temperature in Kelvin, multiplied by an optional single /// digit power of ten scaling factor: 1 (default) gives deciKelvin, /// 0 gives Kelvin, 3 gives milliKelvin /// * `IU` unsigned int /// * `L` long /// * `LU` unsigned long /// * `Q` quad_t /// * `QU` u_quad_t /// * `S,TYPE` struct TYPE structures pub fmt: String, pub flags: u32, } #[cfg(target_os = "freebsd")] impl CtlInfo { /// Is this sysctl a temperature? pub fn is_temperature(&self) -> bool { self.fmt.starts_with("IK") } } impl CtlInfo { /// Return the flags for this sysctl. pub fn flags(&self) -> CtlFlags { CtlFlags::from_bits_truncate(self.flags) } /// If the sysctl is a structure, return the structure type string. /// /// Checks whether the format string starts with `S,` and returns the rest /// of the format string or None if the format String does not have a struct /// hint. pub fn struct_type(&self) -> Option { if !self.fmt.starts_with("S,") { return None; } Some(self.fmt[2..].into()) } } sysctl-0.4.4/src/ctl_type.rs000064400000000000000000000060020072674642500141600ustar 00000000000000// ctl_type.rs use ctl_value::*; /// An Enum that represents a sysctl's type information. /// /// # Example /// /// ``` /// # use sysctl::Sysctl; /// if let Ok(ctl) = sysctl::Ctl::new("kern.osrevision") { /// if let Ok(value) = ctl.value() { /// let val_type: sysctl::CtlType = value.into(); /// assert_eq!(val_type, sysctl::CtlType::Int); /// } /// } /// ``` #[derive(Debug, Copy, Clone, PartialEq)] #[repr(u32)] pub enum CtlType { Node = 1, Int = 2, String = 3, S64 = 4, Struct = 5, Uint = 6, Long = 7, Ulong = 8, U64 = 9, U8 = 10, U16 = 11, S8 = 12, S16 = 13, S32 = 14, U32 = 15, // Added custom types below None = 0, #[cfg(target_os = "freebsd")] Temperature = 16, } impl std::convert::From for CtlType { fn from(t: u32) -> Self { assert!(t <= 16); unsafe { std::mem::transmute(t) } } } impl std::convert::From<&CtlValue> for CtlType { fn from(t: &CtlValue) -> Self { match t { CtlValue::None => CtlType::None, CtlValue::Node(_) => CtlType::Node, CtlValue::Int(_) => CtlType::Int, CtlValue::String(_) => CtlType::String, CtlValue::S64(_) => CtlType::S64, CtlValue::Struct(_) => CtlType::Struct, CtlValue::Uint(_) => CtlType::Uint, CtlValue::Long(_) => CtlType::Long, CtlValue::Ulong(_) => CtlType::Ulong, CtlValue::U64(_) => CtlType::U64, CtlValue::U8(_) => CtlType::U8, CtlValue::U16(_) => CtlType::U16, CtlValue::S8(_) => CtlType::S8, CtlValue::S16(_) => CtlType::S16, CtlValue::S32(_) => CtlType::S32, CtlValue::U32(_) => CtlType::U32, #[cfg(target_os = "freebsd")] CtlValue::Temperature(_) => CtlType::Temperature, } } } impl std::convert::From for CtlType { fn from(t: CtlValue) -> Self { Self::from(&t) } } impl CtlType { pub fn min_type_size(&self) -> usize { match self { CtlType::None => 0, CtlType::Node => 0, CtlType::Int => std::mem::size_of::(), CtlType::String => 0, CtlType::S64 => std::mem::size_of::(), CtlType::Struct => 0, CtlType::Uint => std::mem::size_of::(), CtlType::Long => std::mem::size_of::(), CtlType::Ulong => std::mem::size_of::(), CtlType::U64 => std::mem::size_of::(), CtlType::U8 => std::mem::size_of::(), CtlType::U16 => std::mem::size_of::(), CtlType::S8 => std::mem::size_of::(), CtlType::S16 => std::mem::size_of::(), CtlType::S32 => std::mem::size_of::(), CtlType::U32 => std::mem::size_of::(), // Added custom types below #[cfg(target_os = "freebsd")] CtlType::Temperature => 0, } } } sysctl-0.4.4/src/ctl_value.rs000064400000000000000000000125450072674642500143240ustar 00000000000000// ctl_value.rs #[cfg(target_os = "freebsd")] use temperature::Temperature; /// An Enum that holds all values returned by sysctl calls. /// Extract inner value with `if let` or `match`. /// /// # Example /// /// ``` /// # use sysctl::Sysctl; /// if let Ok(ctl) = sysctl::Ctl::new("kern.osrevision") { /// if let Ok(sysctl::CtlValue::Int(val)) = ctl.value() { /// println!("Value: {}", val); /// } /// } /// ``` #[derive(Debug, PartialEq, PartialOrd)] pub enum CtlValue { None, Node(Vec), Int(i32), String(String), S64(i64), Struct(Vec), Uint(u32), Long(i64), Ulong(u64), U64(u64), U8(u8), U16(u16), S8(i8), S16(i16), S32(i32), U32(u32), #[cfg(target_os = "freebsd")] Temperature(Temperature), } impl std::fmt::Display for CtlValue { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let s = match self { CtlValue::None => "[None]".to_owned(), CtlValue::Int(i) => format!("{}", i), CtlValue::Uint(i) => format!("{}", i), CtlValue::Long(i) => format!("{}", i), CtlValue::Ulong(i) => format!("{}", i), CtlValue::U8(i) => format!("{}", i), CtlValue::U16(i) => format!("{}", i), CtlValue::U32(i) => format!("{}", i), CtlValue::U64(i) => format!("{}", i), CtlValue::S8(i) => format!("{}", i), CtlValue::S16(i) => format!("{}", i), CtlValue::S32(i) => format!("{}", i), CtlValue::S64(i) => format!("{}", i), CtlValue::Struct(_) => "[Opaque Struct]".to_owned(), CtlValue::Node(_) => "[Node]".to_owned(), CtlValue::String(s) => s.to_owned(), #[cfg(target_os = "freebsd")] CtlValue::Temperature(t) => format!("{}", t.kelvin()), }; write!(f, "{}", s) } } #[cfg(all(test, any(target_os = "linux", target_os = "android")))] mod tests_linux { use crate::sys; use crate::Sysctl; #[test] fn ctl_value_string() { // NOTE: Some linux distributions require Root permissions // e.g Debian. let output = std::process::Command::new("sysctl") .arg("-n") .arg("kernel.version") .output() .expect("failed to execute process"); let ver = String::from_utf8_lossy(&output.stdout); let s = match sys::funcs::value("/proc/sys/kernel/version") { Ok(crate::CtlValue::String(s)) => s, _ => panic!("crate::value() returned Error"), }; assert_eq!(s.trim(), ver.trim()); let kernversion = crate::Ctl::new("kernel.version").unwrap(); let s = match kernversion.value() { Ok(crate::CtlValue::String(s)) => s, _ => "...".into(), }; assert_eq!(s.trim(), ver.trim()); } } #[cfg(all(test, any(target_os = "freebsd", target_os = "macos")))] mod tests_unix { use crate::sys; use crate::Sysctl; #[test] fn ctl_value_string() { let output = std::process::Command::new("sysctl") .arg("-n") .arg("kern.version") .output() .expect("failed to execute process"); let ver = String::from_utf8_lossy(&output.stdout); let ctl = crate::Ctl::new("kern.version").expect("Ctl::new"); let s = match ctl.value() { Ok(crate::CtlValue::String(s)) => s, _ => "...".into(), }; assert_eq!(s.trim(), ver.trim()); let kernversion = crate::Ctl::new("kern.version").unwrap(); let s = match kernversion.value() { Ok(crate::CtlValue::String(s)) => s, _ => "...".into(), }; assert_eq!(s.trim(), ver.trim()); } #[test] fn ctl_value_int() { let output = std::process::Command::new("sysctl") .arg("-n") .arg("kern.osrevision") .output() .expect("failed to execute process"); let rev_str = String::from_utf8_lossy(&output.stdout); let rev = rev_str.trim().parse::().unwrap(); let ctl = crate::Ctl::new("kern.osrevision").expect("Could not get kern.osrevision sysctl."); let n = match ctl.value() { Ok(crate::CtlValue::Int(n)) => n, Ok(_) => 0, Err(_) => 0, }; assert_eq!(n, rev); } #[test] fn ctl_value_oid_int() { let output = std::process::Command::new("sysctl") .arg("-n") .arg("kern.osrevision") .output() .expect("failed to execute process"); let rev_str = String::from_utf8_lossy(&output.stdout); let rev = rev_str.trim().parse::().unwrap(); let n = match sys::funcs::value_oid(&mut vec![libc::CTL_KERN, libc::KERN_OSREV]) { Ok(crate::CtlValue::Int(n)) => n, Ok(_) => 0, Err(_) => 0, }; assert_eq!(n, rev); } #[test] fn ctl_struct_type() { let info = crate::CtlInfo { ctl_type: crate::CtlType::Int, fmt: "S,TYPE".into(), flags: 0, }; assert_eq!(info.struct_type(), Some("TYPE".into())); let info = crate::CtlInfo { ctl_type: crate::CtlType::Int, fmt: "I".into(), flags: 0, }; assert_eq!(info.struct_type(), None); } } sysctl-0.4.4/src/lib.rs000064400000000000000000000042240072674642500131070ustar 00000000000000// lib.rs //! A simplified interface to the `sysctl` system call. //! //! # Example: Get value //! ``` //! # use sysctl::Sysctl; //! #[cfg(any(target_os = "macos", target_os = "freebsd"))] //! const CTLNAME: &str = "kern.ostype"; //! #[cfg(any(target_os = "linux", target_os = "android"))] //! const CTLNAME: &str = "kernel.ostype"; //! //! let ctl = sysctl::Ctl::new(CTLNAME).unwrap(); //! let desc = ctl.description().unwrap(); //! println!("Description: {}", desc); //! let val = ctl.value().unwrap(); //! println!("Value: {}", val); //! // On Linux all sysctls are String type. Use the following for //! // cross-platform compatibility: //! let str_val = ctl.value_string().unwrap(); //! println!("String value: {}", val); //! ``` //! //! # Example: Get value as struct //! ``` //! // Not available on Linux //! # use sysctl::Sysctl; //! #[derive(Debug, Default)] //! #[repr(C)] //! struct ClockInfo { //! hz: libc::c_int, /* clock frequency */ //! tick: libc::c_int, /* micro-seconds per hz tick */ //! spare: libc::c_int, //! stathz: libc::c_int, /* statistics clock frequency */ //! profhz: libc::c_int, /* profiling clock frequency */ //! } //! # #[cfg(any(target_os = "macos", target_os = "freebsd"))] //! let val: Box = sysctl::Ctl::new("kern.clockrate").unwrap().value_as().unwrap(); //! # #[cfg(any(target_os = "macos", target_os = "freebsd"))] //! println!("{:?}", val); //! ``` #[macro_use] extern crate bitflags; extern crate byteorder; extern crate libc; extern crate thiserror; #[cfg(any(target_os = "android", target_os = "linux"))] extern crate walkdir; #[cfg(any(target_os = "android", target_os = "linux"))] #[path = "linux/mod.rs"] mod sys; #[cfg(any(target_os = "macos", target_os = "freebsd"))] #[path = "unix/mod.rs"] mod sys; mod consts; mod ctl_error; mod ctl_flags; mod ctl_info; mod ctl_type; mod ctl_value; #[cfg(target_os = "freebsd")] mod temperature; mod traits; pub use consts::*; pub use ctl_error::*; pub use ctl_flags::*; pub use ctl_info::*; pub use ctl_type::*; pub use ctl_value::*; pub use sys::ctl::*; pub use sys::ctl_iter::*; #[cfg(target_os = "freebsd")] pub use temperature::Temperature; pub use traits::Sysctl; sysctl-0.4.4/src/linux/ctl.rs000064400000000000000000000051610072674642500142630ustar 00000000000000// linux/ctl.rs use super::funcs::{path_to_name, set_value, value}; use consts::*; use ctl_error::SysctlError; use ctl_flags::CtlFlags; use ctl_info::CtlInfo; use ctl_type::CtlType; use ctl_value::CtlValue; use std::str::FromStr; use traits::Sysctl; /// This struct represents a system control. #[derive(Debug, Clone, PartialEq)] pub struct Ctl { name: String, } impl FromStr for Ctl { type Err = SysctlError; fn from_str(name: &str) -> Result { let ctl = Ctl { name: path_to_name(name), }; let _ = std::fs::File::open(ctl.path()).map_err(|_| SysctlError::NotFound(name.to_owned()))?; Ok(ctl) } } impl Ctl { pub fn path(&self) -> String { format!("/proc/sys/{}", self.name.replace(".", "/")) } } impl Sysctl for Ctl { fn new(name: &str) -> Result { Ctl::from_str(name) } fn name(&self) -> Result { Ok(self.name.clone()) } fn value_type(&self) -> Result { let md = std::fs::metadata(&self.path()).map_err(SysctlError::IoError)?; if md.is_dir() { Ok(CtlType::Node) } else { Ok(CtlType::String) } } fn description(&self) -> Result { Ok("[N/A]".to_owned()) } fn value(&self) -> Result { value(&self.path()) } fn value_string(&self) -> Result { self.value().map(|v| format!("{}", v)) } fn value_as(&self) -> Result, SysctlError> { Err(SysctlError::NotSupported) } fn set_value(&self, value: CtlValue) -> Result { set_value(&self.path(), value) } fn set_value_string(&self, value: &str) -> Result { self.set_value(CtlValue::String(value.to_owned()))?; self.value_string() } fn flags(&self) -> Result { Ok(self.info()?.flags()) } fn info(&self) -> Result { let md = std::fs::metadata(&self.path()).map_err(SysctlError::IoError)?; let mut flags = 0; if md.permissions().readonly() { flags |= CTLFLAG_RD; } else { flags |= CTLFLAG_RW; } let s = CtlInfo { ctl_type: CtlType::String, fmt: "".to_owned(), flags: flags, }; Ok(s) } } #[cfg(test)] mod tests { use crate::Sysctl; #[test] fn ctl_new() { let _ = super::Ctl::new("kernel.ostype").expect("Ctl::new"); } } sysctl-0.4.4/src/linux/ctl_iter.rs000064400000000000000000000100740072674642500153050ustar 00000000000000// linux/ctl_iter.rs use super::ctl::Ctl; use ctl_error::SysctlError; use traits::Sysctl; /// An iterator over Sysctl entries. pub struct CtlIter { direntries: Vec, base: String, cur_idx: usize, } impl CtlIter { /// Return an iterator over the complete sysctl tree. pub fn root() -> Self { let entries: Vec = walkdir::WalkDir::new("/proc/sys") .sort_by(|a, b| a.path().cmp(b.path())) .follow_links(false) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.file_type().is_file()) .collect(); CtlIter { direntries: entries, base: "/proc/sys".to_owned(), cur_idx: 0, } } /// Return an iterator over all sysctl entries below the given node. pub fn below(node: Ctl) -> Self { let root = node.path(); let entries: Vec = walkdir::WalkDir::new(&root) .sort_by(|a, b| a.path().cmp(b.path())) .follow_links(false) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.file_type().is_file()) .collect(); CtlIter { direntries: entries, base: root, cur_idx: 0, } } } impl Iterator for CtlIter { type Item = Result; fn next(&mut self) -> Option { if self.cur_idx >= self.direntries.len() { return None; } let e: &walkdir::DirEntry = &self.direntries[self.cur_idx]; self.cur_idx += 1; // We continue iterating as long as the oid starts with the base if let Some(path) = e.path().to_str() { if path.starts_with(&self.base) { Some(Ctl::new(path)) } else { None } } else { Some(Err(SysctlError::ParseError)) } } } /// Ctl implements the IntoIterator trait to allow for easy iteration /// over nodes. /// /// # Example /// /// ``` /// # use sysctl::Sysctl; /// # /// let kern = sysctl::Ctl::new("kernel"); /// for ctl in kern { /// println!("{}", ctl.name().unwrap()); /// } /// ``` impl IntoIterator for Ctl { type Item = Result; type IntoIter = CtlIter; fn into_iter(self: Self) -> Self::IntoIter { CtlIter::below(self) } } #[cfg(test)] mod tests { use crate::Sysctl; #[test] fn ctl_iter_iterate_all() { let root = crate::CtlIter::root(); let all_ctls: Vec = root.into_iter().filter_map(Result::ok).collect(); assert_ne!(all_ctls.len(), 0); for ctl in &all_ctls { println!("{:?}", ctl.name()); } } #[test] fn ctl_iter_below_compare_outputs() { // NOTE: Some linux distributions require Root permissions // e.g Debian. let output = std::process::Command::new("sysctl") .arg("user") .output() .expect("failed to execute process"); let expected = String::from_utf8_lossy(&output.stdout); let node = crate::Ctl::new("user").expect("could not get node"); let ctls = crate::CtlIter::below(node); let mut actual: Vec = vec![]; for ctl in ctls { let ctl = match ctl { Err(_) => panic!("ctl error"), Ok(s) => s, }; let name = match ctl.name() { Ok(s) => s, Err(_) => panic!("get ctl name"), }; match ctl.value_type().expect("could not get value type") { crate::CtlType::String => { actual.push(format!( "{} = {}", name, ctl.value_string() .expect(&format!("could not get value as string for {}", name)) )); } _ => panic!("sysctl not string type"), }; } assert_eq!(actual.join("\n").trim(), expected.trim()); } } sysctl-0.4.4/src/linux/funcs.rs000064400000000000000000000026770072674642500146300ustar 00000000000000// linux/funcs.rs use ctl_error::*; use ctl_value::*; use std::io::{Read, Write}; pub fn path_to_name(name: &str) -> String { name.to_owned() .replace("/proc/sys/", "") .replace("..", ".") .replace("/", ".") } pub fn value(name: &str) -> Result { let file_res = std::fs::OpenOptions::new() .read(true) .write(false) .open(&name); file_res .map(|mut file| { let mut v = String::new(); file.read_to_string(&mut v)?; Ok(CtlValue::String(v.trim().to_owned())) }) .map_err(|e| { if e.kind() == std::io::ErrorKind::NotFound { SysctlError::NotFound(name.into()) } else { e.into() } })? } pub fn set_value(name: &str, v: CtlValue) -> Result { let file_res = std::fs::OpenOptions::new() .read(false) .write(true) .open(&name); file_res .map(|mut file| match v { CtlValue::String(v) => { file.write_all(&v.as_bytes())?; value(&name) } _ => Err(std::io::Error::from(std::io::ErrorKind::InvalidData).into()), }) .map_err(|e| { if e.kind() == std::io::ErrorKind::NotFound { SysctlError::NotFound(name.into()) } else { e.into() } })? } sysctl-0.4.4/src/linux/mod.rs000064400000000000000000000000770072674642500142610ustar 00000000000000// linux/mod.rs pub mod ctl; pub mod ctl_iter; pub mod funcs; sysctl-0.4.4/src/temperature.rs000064400000000000000000000075340072674642500147050ustar 00000000000000// temperature.rs use byteorder::ByteOrder; use ctl_error::SysctlError; use ctl_info::CtlInfo; use ctl_type::CtlType; use ctl_value::CtlValue; use std::f32; /// A custom type for temperature sysctls. /// /// # Example /// ``` /// # use sysctl::Sysctl; /// if let Ok(ctl) = sysctl::Ctl::new("dev.cpu.0.temperature") { /// if let Ok(sysctl::CtlValue::Temperature(val)) = ctl.value() { /// println!("Temperature: {:.2}K, {:.2}F, {:.2}C", /// val.kelvin(), /// val.fahrenheit(), /// val.celsius()); /// } else { /// panic!("Error, not a temperature ctl!") /// } /// } /// ``` /// Not available on MacOS #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub struct Temperature { value: f32, // Kelvin } impl Temperature { pub fn kelvin(&self) -> f32 { self.value } pub fn celsius(&self) -> f32 { self.value - 273.15 } pub fn fahrenheit(&self) -> f32 { 1.8 * self.celsius() + 32.0 } } pub fn temperature(info: &CtlInfo, val: &[u8]) -> Result { let prec: u32 = { match info.fmt.len() { l if l > 2 => match info.fmt[2..3].parse::() { Ok(x) if x <= 9 => x, _ => 1, }, _ => 1, } }; let base = 10u32.pow(prec) as f32; let make_temp = move |f: f32| -> Result { Ok(CtlValue::Temperature(Temperature { value: f / base })) }; match info.ctl_type { CtlType::Int => make_temp(byteorder::LittleEndian::read_i32(&val) as f32), CtlType::S64 => make_temp(byteorder::LittleEndian::read_i64(&val) as f32), CtlType::Uint => make_temp(byteorder::LittleEndian::read_u32(&val) as f32), CtlType::Long => make_temp(byteorder::LittleEndian::read_i64(&val) as f32), CtlType::Ulong => make_temp(byteorder::LittleEndian::read_u64(&val) as f32), CtlType::U64 => make_temp(byteorder::LittleEndian::read_u64(&val) as f32), CtlType::U8 => make_temp(val[0] as u8 as f32), CtlType::U16 => make_temp(byteorder::LittleEndian::read_u16(&val) as f32), CtlType::S8 => make_temp(val[0] as i8 as f32), CtlType::S16 => make_temp(byteorder::LittleEndian::read_i16(&val) as f32), CtlType::S32 => make_temp(byteorder::LittleEndian::read_i32(&val) as f32), CtlType::U32 => make_temp(byteorder::LittleEndian::read_u32(&val) as f32), _ => Err(SysctlError::UnknownType), } } #[cfg(all(test, target_os = "freebsd"))] mod tests_freebsd { use byteorder::WriteBytesExt; #[test] fn ctl_temperature_ik() { let info = crate::CtlInfo { ctl_type: crate::CtlType::Int, fmt: "IK".into(), flags: 0, }; let mut val = vec![]; // Default value (IK) in deciKelvin integer val.write_i32::(3330) .expect("Error parsing value to byte array"); let t = super::temperature(&info, &val).unwrap(); if let crate::CtlValue::Temperature(tt) = t { assert!(tt.kelvin() - 333.0 < 0.1); assert!(tt.celsius() - 59.85 < 0.1); assert!(tt.fahrenheit() - 139.73 < 0.1); } else { assert!(false); } } #[test] fn ctl_temperature_ik3() { let info = crate::CtlInfo { ctl_type: crate::CtlType::Int, fmt: "IK3".into(), flags: 0, }; let mut val = vec![]; // Set value in milliKelvin val.write_i32::(333000) .expect("Error parsing value to byte array"); let t = super::temperature(&info, &val).unwrap(); if let crate::CtlValue::Temperature(tt) = t { assert!(tt.kelvin() - 333.0 < 0.1); } else { assert!(false); } } } sysctl-0.4.4/src/traits.rs000064400000000000000000000144520072674642500136530ustar 00000000000000// traits.rs use ctl_error::SysctlError; use ctl_flags::CtlFlags; use ctl_info::CtlInfo; use ctl_type::CtlType; use ctl_value::CtlValue; pub trait Sysctl { /// Construct a Ctl from the name. /// /// Returns a result containing the struct Ctl on success or a SysctlError /// on failure. /// /// # Example /// ``` /// # use sysctl::Sysctl; /// # /// let ctl = sysctl::Ctl::new("kern.ostype"); /// ``` /// /// If the sysctl does not exist, `Err(SysctlError::NotFound)` is returned. /// ``` /// # use sysctl::Sysctl; /// # /// let ctl = sysctl::Ctl::new("this.sysctl.does.not.exist"); /// match ctl { /// Err(sysctl::SysctlError::NotFound(_)) => (), /// Err(e) => panic!(format!("Wrong error type returned: {:?}", e)), /// Ok(_) => panic!("Nonexistent sysctl seems to exist"), /// } /// ``` fn new(name: &str) -> Result where Self: std::marker::Sized; /// Returns a result containing the sysctl name on success, or a /// SysctlError on failure. /// /// # Example /// ``` /// # use sysctl::Sysctl; /// if let Ok(ctl) = sysctl::Ctl::new("kern.ostype") { /// assert_eq!(ctl.name().unwrap(), "kern.ostype"); /// } /// ``` fn name(&self) -> Result; /// Returns a result containing the sysctl value type on success, /// or a Sysctl Error on failure. /// /// # Example /// /// ``` /// # use sysctl::Sysctl; /// if let Ok(ctl) = sysctl::Ctl::new("kern.ostype") { /// let value_type = ctl.value_type().unwrap(); /// assert_eq!(value_type, sysctl::CtlType::String); /// } /// ``` fn value_type(&self) -> Result; /// Returns a result containing the sysctl description if success, or an /// Error on failure. /// /// # Example /// ``` /// # use sysctl::Sysctl; /// if let Ok(ctl) = sysctl::Ctl::new("kern.ostype") { /// println!("Description: {:?}", ctl.description()) /// } /// ``` fn description(&self) -> Result; /// Returns a result containing the sysctl value on success, or a /// SysctlError on failure. /// /// # Example /// ``` /// # use sysctl::Sysctl; /// if let Ok(ctl) = sysctl::Ctl::new("kern.ostype") { /// println!("Value: {:?}", ctl.value()); /// } /// ``` fn value(&self) -> Result; /// A generic method that takes returns a result containing the sysctl /// value if success, or a SysctlError on failure. /// /// May only be called for sysctls of type Opaque or Struct. /// # Example /// ``` /// # use sysctl::Sysctl; /// #[derive(Debug)] /// #[repr(C)] /// struct ClockInfo { /// hz: libc::c_int, /* clock frequency */ /// tick: libc::c_int, /* micro-seconds per hz tick */ /// spare: libc::c_int, /// stathz: libc::c_int, /* statistics clock frequency */ /// profhz: libc::c_int, /* profiling clock frequency */ /// } /// /// if let Ok(ctl) = sysctl::Ctl::new("kern.clockrate") { /// println!("{:?}", ctl.value_as::()); /// } /// ``` fn value_as(&self) -> Result, SysctlError>; /// Returns a result containing the sysctl value as String on /// success, or a SysctlError on failure. /// /// # Example /// ``` /// # use sysctl::Sysctl; /// if let Ok(ctl) = sysctl::Ctl::new("kern.osrevision") { /// println!("Value: {:?}", ctl.value_string()); /// } /// ``` fn value_string(&self) -> Result; #[cfg_attr(feature = "cargo-clippy", allow(clippy::needless_doctest_main))] /// Sets the value of a sysctl. /// Fetches and returns the new value if successful, or returns a /// SysctlError on failure. /// # Example /// ``` /// use sysctl::Sysctl; /// /// fn main() { /// if unsafe { libc::getuid() } == 0 { /// if let Ok(ctl) = sysctl::Ctl::new("hw.usb.debug") { /// let org = ctl.value().unwrap(); /// let set = ctl.set_value(sysctl::CtlValue::Int(1)).unwrap(); /// assert_eq!(set, sysctl::CtlValue::Int(1)); /// ctl.set_value(org).unwrap(); /// } /// } /// } fn set_value(&self, value: CtlValue) -> Result; #[cfg_attr(feature = "cargo-clippy", allow(clippy::needless_doctest_main))] /// Sets the value of a sysctl with input as string. /// Fetches and returns the new value if successful, or returns a /// SysctlError on failure. /// # Example /// ``` /// use sysctl::Sysctl; /// /// fn main() { /// if unsafe { libc::getuid() } == 0 { /// if let Ok(ctl) = sysctl::Ctl::new("hw.usb.debug") { /// let org = ctl.value_string().unwrap(); /// let set = ctl.set_value_string("1"); /// println!("hw.usb.debug: -> {:?}", set); /// ctl.set_value_string(&org).unwrap(); /// } /// } /// } fn set_value_string(&self, value: &str) -> Result; /// Get the flags for a sysctl. /// /// Returns a Result containing the flags on success, /// or a SysctlError on failure. /// /// # Example /// ``` /// # use sysctl::Sysctl; /// if let Ok(ctl) = sysctl::Ctl::new("kern.ostype") { /// let readable = ctl.flags().unwrap().contains(sysctl::CtlFlags::RD); /// assert!(readable); /// } /// ``` fn flags(&self) -> Result; #[cfg_attr(feature = "cargo-clippy", allow(clippy::needless_doctest_main))] /// Returns a Result containing the control metadata for a sysctl. /// /// Returns a Result containing the CtlInfo struct on success, /// or a SysctlError on failure. /// /// # Example /// ``` /// use sysctl::Sysctl; /// /// fn main() { /// if let Ok(ctl) = sysctl::Ctl::new("kern.osrevision") { /// let info = ctl.info().unwrap(); /// /// // kern.osrevision is not a structure. /// assert_eq!(info.struct_type(), None); /// } /// } /// ``` fn info(&self) -> Result; } sysctl-0.4.4/src/unix/ctl.rs000064400000000000000000000133340072674642500141100ustar 00000000000000// unix/ctl.rs use super::funcs::*; use ctl_error::SysctlError; use ctl_flags::CtlFlags; use ctl_info::CtlInfo; use ctl_type::CtlType; use ctl_value::CtlValue; use std::str::FromStr; use traits::Sysctl; /// This struct represents a system control. #[derive(Debug, Clone, PartialEq)] pub struct Ctl { pub oid: Vec, } impl std::str::FromStr for Ctl { type Err = SysctlError; #[cfg_attr(feature = "cargo-clippy", allow(clippy::redundant_field_names))] fn from_str(s: &str) -> Result { let oid = name2oid(s)?; Ok(Ctl { oid: oid }) } } impl Sysctl for Ctl { fn new(name: &str) -> Result { Ctl::from_str(name) } fn name(&self) -> Result { oid2name(&self.oid) } #[cfg(not(target_os = "macos"))] fn value_type(&self) -> Result { let info = oidfmt(&self.oid)?; Ok(info.ctl_type) } #[cfg(target_os = "macos")] fn value_type(&self) -> Result { let info = oidfmt(&self.oid)?; Ok(match info.ctl_type { CtlType::Int => match info.fmt.as_str() { "I" => CtlType::Int, "IU" => CtlType::Uint, "L" => CtlType::Long, "LU" => CtlType::Ulong, _ => return Err(SysctlError::MissingImplementation), }, ctl_type => ctl_type, }) } #[cfg(not(target_os = "macos"))] fn description(&self) -> Result { oid2description(&self.oid) } #[cfg(target_os = "macos")] fn description(&self) -> Result { Ok("[N/A]".to_string()) } #[cfg(not(target_os = "macos"))] fn value(&self) -> Result { value_oid(&self.oid) } #[cfg(target_os = "macos")] fn value(&self) -> Result { let mut oid = self.oid.clone(); value_oid(&mut oid) } #[cfg(not(target_os = "macos"))] fn value_as(&self) -> Result, SysctlError> { value_oid_as::(&self.oid) } fn value_string(&self) -> Result { self.value().map(|v| format!("{}", v)) } #[cfg(target_os = "macos")] fn value_as(&self) -> Result, SysctlError> { let mut oid = self.oid.clone(); value_oid_as::(&mut oid) } #[cfg(not(target_os = "macos"))] fn set_value(&self, value: CtlValue) -> Result { set_oid_value(&self.oid, value) } #[cfg(target_os = "macos")] fn set_value(&self, value: CtlValue) -> Result { let mut oid = self.oid.clone(); set_oid_value(&mut oid, value) } #[cfg(not(target_os = "macos"))] fn set_value_string(&self, value: &str) -> Result { let ctl_type = self.value_type()?; let _ = match ctl_type { CtlType::String => set_oid_value(&self.oid, CtlValue::String(value.to_owned())), CtlType::Uint => set_oid_value( &self.oid, CtlValue::Uint(value.parse::().map_err(|_| SysctlError::ParseError)?), ), CtlType::Int => set_oid_value( &self.oid, CtlValue::Int(value.parse::().map_err(|_| SysctlError::ParseError)?), ), CtlType::Ulong => set_oid_value( &self.oid, CtlValue::Ulong(value.parse::().map_err(|_| SysctlError::ParseError)?), ), CtlType::U8 => set_oid_value( &self.oid, CtlValue::U8(value.parse::().map_err(|_| SysctlError::ParseError)?), ), _ => Err(SysctlError::MissingImplementation), }?; self.value_string() } #[cfg(target_os = "macos")] fn set_value_string(&self, value: &str) -> Result { let ctl_type = self.value_type()?; let mut oid = self.oid.clone(); let _ = match ctl_type { CtlType::String => set_oid_value(&mut oid, CtlValue::String(value.to_owned())), CtlType::Uint => set_oid_value( &mut oid, CtlValue::Uint(value.parse::().map_err(|_| SysctlError::ParseError)?), ), CtlType::Int => set_oid_value( &mut oid, CtlValue::Int(value.parse::().map_err(|_| SysctlError::ParseError)?), ), CtlType::Ulong => set_oid_value( &mut oid, CtlValue::Ulong(value.parse::().map_err(|_| SysctlError::ParseError)?), ), CtlType::U8 => set_oid_value( &mut oid, CtlValue::U8(value.parse::().map_err(|_| SysctlError::ParseError)?), ), _ => Err(SysctlError::MissingImplementation), }?; self.value_string() } fn flags(&self) -> Result { Ok(self.info()?.flags()) } fn info(&self) -> Result { oidfmt(&self.oid) } } #[cfg(test)] mod tests { use crate::Sysctl; #[test] fn ctl_new() { let _ = super::Ctl::new("kern.ostype").expect("Ctl::new"); } #[test] fn ctl_description() { let ctl = super::Ctl::new("kern.ostype").expect("Ctl::new"); let descp = ctl.description(); assert!(descp.is_ok()); let descp = descp.unwrap(); #[cfg(target_os = "freebsd")] assert_eq!(descp, "Operating system type"); #[cfg(any(target_os = "macos", target_os = "linux"))] assert_eq!(descp, "[N/A]"); } } #[cfg(all(test, target_os = "freebsd"))] mod tests_freebsd {} sysctl-0.4.4/src/unix/ctl_iter.rs000064400000000000000000000041270072674642500151330ustar 00000000000000// unix/ctl_iter.rs use super::ctl::Ctl; use super::funcs::next_oid; use ctl_error::SysctlError; /// An iterator over Sysctl entries. pub struct CtlIter { // if we are iterating over a Node, only include OIDs // starting with this base. Set to None if iterating over all // OIDs. base: Ctl, current: Ctl, } impl CtlIter { /// Return an iterator over the complete sysctl tree. pub fn root() -> Self { CtlIter { base: Ctl { oid: vec![] }, current: Ctl { oid: vec![1] }, } } /// Return an iterator over all sysctl entries below the given node. pub fn below(node: Ctl) -> Self { CtlIter { base: node.clone(), current: node, } } } impl Iterator for CtlIter { type Item = Result; fn next(&mut self) -> Option { let oid = match next_oid(&self.current.oid) { Ok(Some(o)) => o, Err(e) => return Some(Err(e)), Ok(None) => return None, }; // We continue iterating as long as the oid starts with the base let cont = oid.starts_with(&self.base.oid); self.current = Ctl { oid }; match cont { true => Some(Ok(self.current.clone())), false => None, } } } /// Ctl implements the IntoIterator trait to allow for easy iteration /// over nodes. /// /// # Example /// /// ``` /// use sysctl::Sysctl; /// /// let kern = sysctl::Ctl::new("kern"); /// for ctl in kern { /// println!("{}", ctl.name().unwrap()); /// } /// ``` impl IntoIterator for Ctl { type Item = Result; type IntoIter = CtlIter; fn into_iter(self) -> Self::IntoIter { CtlIter::below(self) } } #[cfg(test)] mod tests { use crate::Sysctl; #[test] fn ctl_iter_iterate_all() { let root = super::CtlIter::root(); let all_ctls: Vec = root.into_iter().filter_map(Result::ok).collect(); assert_ne!(all_ctls.len(), 0); for ctl in &all_ctls { println!("{:?}", ctl.name()); } } } sysctl-0.4.4/src/unix/funcs.rs000064400000000000000000000604720072674642500144510ustar 00000000000000// unix/funcs.rs use byteorder::{ByteOrder, WriteBytesExt}; use consts::*; use ctl_error::*; use ctl_info::*; use ctl_type::*; use ctl_value::*; #[cfg(target_os = "freebsd")] use temperature::*; pub fn name2oid(name: &str) -> Result, SysctlError> { // We get results in this vector let mut len: usize = CTL_MAXNAME as usize; let mut res = Vec::::with_capacity(len); let rcname = std::ffi::CString::new(name); if rcname.is_err() { return Err(SysctlError::NotFound(name.to_owned())); } let cname = rcname.unwrap(); let ret = unsafe { libc::sysctlnametomib( cname.as_ptr() as *const libc::c_char, res.as_mut_ptr(), &mut 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), }); } else { unsafe { res.set_len(len); } } Ok(res) } #[cfg(not(target_os = "macos"))] pub fn oidfmt(oid: &[libc::c_int]) -> Result { // Request command for type info let mut qoid: Vec = vec![0, 4]; qoid.extend(oid); // Store results here 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())); } // 'Kind' is the first 32 bits of result buffer let kind = byteorder::LittleEndian::read_u32(&buf); // 'Type' is the first 4 bits of 'Kind' let ctltype_val = kind & CTLTYPE as u32; // 'fmt' is after 'Kind' in result buffer let fmt: String = match std::ffi::CStr::from_bytes_with_nul(&buf[std::mem::size_of::()..buf_len]) { Ok(x) => x.to_string_lossy().into(), Err(e) => return Err(SysctlError::InvalidCStr(e)), }; #[cfg_attr(feature = "cargo-clippy", allow(clippy::redundant_field_names))] 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 { // Request command for type info let mut qoid: Vec = vec![0, 4]; qoid.extend(oid); // Store results here 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())); } // 'Kind' is the first 32 bits of result buffer let kind = byteorder::LittleEndian::read_u32(&buf); // 'Type' is the first 4 bits of 'Kind' let ctltype_val = kind & CTLTYPE as u32; // 'fmt' is after 'Kind' in result buffer let bytes = &buf[std::mem::size_of::()..buf_len]; // Ensure we drop the '\0' byte if there are any. let len = match bytes.iter().position(|c| *c == 0) { Some(index) => index, _ => bytes.len(), }; let fmt: String = match std::str::from_utf8(&bytes[..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: &[i32]) -> Result { let info: CtlInfo = oidfmt(&oid)?; // Check if the value is readable if info.flags & CTLFLAG_RD != CTLFLAG_RD { return Err(SysctlError::NoReadAccess); } // First get size of value in bytes 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())); } // If the length reported is shorter than the type we will convert it into, // byteorder::LittleEndian::read_* will panic. Therefore, expand the value length to at // Least the size of the value. let val_minsize = std::cmp::max(val_len, info.ctl_type.min_type_size()); // Then get value let mut val: Vec = 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())); } // Confirm that we did not read out of bounds assert!(new_val_len <= val_len); // Confirm that we got the bytes we requested if new_val_len < val_len { return Err(SysctlError::ShortRead { read: new_val_len, reported: val_len, }); } // Special treatment for temperature ctls. if info.is_temperature() { return temperature(&info, &val); } // Wrap in Enum and return 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(SysctlError::Utf8Error) .map(|s| CtlValue::String(s.into())), }, CtlType::S64 => Ok(CtlValue::S64(byteorder::LittleEndian::read_i64(&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) -> Result { let info: CtlInfo = oidfmt(&oid)?; // Check if the value is readable if !(info.flags & CTLFLAG_RD == CTLFLAG_RD) { return Err(SysctlError::NoReadAccess); } // First get size of value in bytes 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())); } // If the length reported is shorter than the type we will convert it into, // byteorder::LittleEndian::read_* will panic. Therefore, expand the value length to at // Least the size of the value. let val_minsize = std::cmp::max(val_len, info.ctl_type.min_type_size()); // Then get value let mut val: Vec = 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())); } // Confirm that we did not read out of bounds assert!(new_val_len <= val_len); // Confirm that we got the bytes we requested if new_val_len < val_len { return Err(SysctlError::ShortRead { read: new_val_len, reported: val_len, }); } // Wrap in Enum and return match info.ctl_type { CtlType::None => Ok(CtlValue::None), CtlType::Node => Ok(CtlValue::Node(val)), CtlType::Int => match info.fmt.as_str() { "I" => Ok(CtlValue::Int(byteorder::LittleEndian::read_i32(&val))), "IU" => Ok(CtlValue::Uint(byteorder::LittleEndian::read_u32(&val))), "L" => Ok(CtlValue::Long(byteorder::LittleEndian::read_i64(&val))), "LU" => Ok(CtlValue::Ulong(byteorder::LittleEndian::read_u64(&val))), _ => Ok(CtlValue::None), } CtlType::String => match val.len() { 0 => Ok(CtlValue::String("".to_string())), l => std::str::from_utf8(&val[..l - 1]) .map_err(SysctlError::Utf8Error) .map(|s| CtlValue::String(s.into())), }, CtlType::S64 => Ok(CtlValue::S64(byteorder::LittleEndian::read_i64(&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"))] pub fn value_oid_as(oid: &[i32]) -> Result, SysctlError> { let val_enum = value_oid(oid)?; // Some structs are apparently reported as Node so this check is invalid.. // let ctl_type = CtlType::from(&val_enum); // assert_eq!(CtlType::Struct, ctl_type, "Error type is not struct/opaque"); // TODO: refactor this when we have better clue to what's going on if let CtlValue::Struct(val) = val_enum { // Make sure we got correct data size assert_eq!( std::mem::size_of::(), val.len(), "Error memory size mismatch. Size of struct {}, size of data retrieved {}.", std::mem::size_of::(), val.len() ); // val is Vec 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 = unsafe { Box::from_raw(val_raw) }; Ok(val_box) } else if let CtlValue::Node(val) = val_enum { // Make sure we got correct data size assert_eq!( std::mem::size_of::(), val.len(), "Error memory size mismatch. Size of struct {}, size of data retrieved {}.", std::mem::size_of::(), val.len() ); // val is Vec 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 = unsafe { Box::from_raw(val_raw) }; Ok(val_box) } else { Err(SysctlError::ExtractionError) } } #[cfg(target_os = "macos")] pub fn value_oid_as(oid: &mut Vec) -> Result, SysctlError> { let val_enum = value_oid(oid)?; // Some structs are apparently reported as Node so this check is invalid.. // let ctl_type = CtlType::from(&val_enum); // assert_eq!(CtlType::Struct, ctl_type, "Error type is not struct/opaque"); // TODO: refactor this when we have better clue to what's going on if let CtlValue::Struct(val) = val_enum { // Make sure we got correct data size assert_eq!( std::mem::size_of::(), val.len(), "Error memory size mismatch. Size of struct {}, size of data retrieved {}.", std::mem::size_of::(), val.len() ); // val is Vec 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 = unsafe { Box::from_raw(val_raw) }; Ok(val_box) } else if let CtlValue::Node(val) = val_enum { // Make sure we got correct data size assert_eq!( std::mem::size_of::(), val.len(), "Error memory size mismatch. Size of struct {}, size of data retrieved {}.", std::mem::size_of::(), val.len() ); // val is Vec 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 = unsafe { Box::from_raw(val_raw) }; Ok(val_box) } else { Err(SysctlError::ExtractionError) } } fn value_to_bytes(value: CtlValue) -> Result, SysctlError> { // TODO rest of the types match value { CtlValue::String(v) => Ok(v.as_bytes().to_owned()), CtlValue::Ulong(v) => { let mut bytes = vec![]; bytes.write_u64::(v)?; Ok(bytes) } CtlValue::Uint(v) => { let mut bytes = vec![]; bytes.write_u32::(v)?; Ok(bytes) } CtlValue::Int(v) => { let mut bytes = vec![]; bytes.write_i32::(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: &[libc::c_int], value: CtlValue) -> Result { let info: CtlInfo = oidfmt(&oid)?; // Check if the value is writeable 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())); } // Get the new value and return for confirmation self::value_oid(oid) } #[cfg(target_os = "macos")] pub fn set_oid_value(oid: &mut Vec, value: CtlValue) -> Result { let info: CtlInfo = oidfmt(&oid)?; // Check if the value is writeable if !(info.flags & CTLFLAG_WR == CTLFLAG_WR) { return Err(SysctlError::NoWriteAccess); } let ctl_type = CtlType::from(&value); // Get the correct ctl type based on the format string let info_ctl_type = match info.ctl_type { CtlType::Int => match info.fmt.as_str() { "I" => CtlType::Int, "IU" => CtlType::Uint, "L" => CtlType::Long, "LU" => CtlType::Ulong, _ => return Err(SysctlError::MissingImplementation), } ctl_type => ctl_type, }; 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)?; // Set 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())); } // Get the new value and return for confirmation self::value_oid(oid) } #[cfg(not(target_os = "macos"))] pub fn oid2description(oid: &[libc::c_int]) -> Result { // Request command for description let mut qoid: Vec = vec![0, 5]; qoid.extend(oid); // Store results in u8 array 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())); } // Use buf_len - 1 so that we remove the trailing NULL 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: &[libc::c_int]) -> Result { // Request command for name let mut qoid: Vec = vec![0, 1]; qoid.extend(oid); // Store results in u8 array 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())); } // Use buf_len - 1 so that we remove the trailing NULL 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) -> Result { // Request command for name let mut qoid: Vec = vec![0, 1]; qoid.extend(oid); // Store results in u8 array 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())); } // Use buf_len - 1 so that we remove the trailing NULL 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: &[libc::c_int]) -> Result>, SysctlError> { // Request command for next oid let mut qoid: Vec = vec![0, 2]; qoid.extend(oid); let mut len: usize = CTL_MAXNAME as usize * std::mem::size_of::(); // We get results in this vector let mut res: Vec = 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 is in bytes, convert to number of libc::c_ints len /= std::mem::size_of::(); // Trim result vector res.truncate(len); Ok(Some(res)) } #[cfg(target_os = "macos")] pub fn next_oid(oid: &Vec) -> Result>, SysctlError> { // Request command for next oid let mut qoid: Vec = vec![0, 2]; qoid.extend(oid); let mut len: usize = CTL_MAXNAME as usize * std::mem::size_of::(); // We get results in this vector let mut res: Vec = 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 is in bytes, convert to number of libc::c_ints len /= std::mem::size_of::(); // Trim result vector 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); } /// The name must be respresentable as a C String #[test] fn name2oid_invalid_name() { let r = super::name2oid("kern.\0.invalid.utf-8"); assert!(matches!(r, Err(super::SysctlError::NotFound(_)))); } } #[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); } } sysctl-0.4.4/src/unix/mod.rs000064400000000000000000000000760072674642500141040ustar 00000000000000// unix/mod.rs pub mod ctl; pub mod ctl_iter; pub mod funcs;