summaryrefslogtreecommitdiffstats
path: root/vendor/sysinfo/src/freebsd
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sysinfo/src/freebsd')
-rw-r--r--vendor/sysinfo/src/freebsd/component.rs71
-rw-r--r--vendor/sysinfo/src/freebsd/cpu.rs44
-rw-r--r--vendor/sysinfo/src/freebsd/disk.rs143
-rw-r--r--vendor/sysinfo/src/freebsd/mod.rs16
-rw-r--r--vendor/sysinfo/src/freebsd/network.rs199
-rw-r--r--vendor/sysinfo/src/freebsd/process.rs254
-rw-r--r--vendor/sysinfo/src/freebsd/system.rs803
-rw-r--r--vendor/sysinfo/src/freebsd/utils.rs296
8 files changed, 1826 insertions, 0 deletions
diff --git a/vendor/sysinfo/src/freebsd/component.rs b/vendor/sysinfo/src/freebsd/component.rs
new file mode 100644
index 000000000..6529be73c
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/component.rs
@@ -0,0 +1,71 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use super::utils::get_sys_value_by_name;
+use crate::ComponentExt;
+
+#[doc = include_str!("../../md_doc/component.md")]
+pub struct Component {
+ id: Vec<u8>,
+ label: String,
+ temperature: f32,
+ max: f32,
+}
+
+impl ComponentExt for Component {
+ fn temperature(&self) -> f32 {
+ self.temperature
+ }
+
+ fn max(&self) -> f32 {
+ self.max
+ }
+
+ fn critical(&self) -> Option<f32> {
+ None
+ }
+
+ fn label(&self) -> &str {
+ &self.label
+ }
+
+ fn refresh(&mut self) {
+ unsafe {
+ if let Some(temperature) = refresh_component(&self.id) {
+ self.temperature = temperature;
+ if self.temperature > self.max {
+ self.max = self.temperature;
+ }
+ }
+ }
+ }
+}
+
+unsafe fn refresh_component(id: &[u8]) -> Option<f32> {
+ let mut temperature: libc::c_int = 0;
+ if !get_sys_value_by_name(id, &mut temperature) {
+ None
+ } else {
+ // convert from Kelvin (x 10 -> 273.2 x 10) to Celsius
+ Some((temperature - 2732) as f32 / 10.)
+ }
+}
+
+pub unsafe fn get_components(nb_cpus: usize) -> Vec<Component> {
+ // For now, we only have temperature for CPUs...
+ let mut components = Vec::with_capacity(nb_cpus);
+
+ for core in 0..nb_cpus {
+ let id = format!("dev.cpu.{}.temperature\0", core)
+ .as_bytes()
+ .to_vec();
+ if let Some(temperature) = refresh_component(&id) {
+ components.push(Component {
+ id,
+ label: format!("CPU {}", core + 1),
+ temperature,
+ max: temperature,
+ });
+ }
+ }
+ components
+}
diff --git a/vendor/sysinfo/src/freebsd/cpu.rs b/vendor/sysinfo/src/freebsd/cpu.rs
new file mode 100644
index 000000000..9d6c8e506
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/cpu.rs
@@ -0,0 +1,44 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::CpuExt;
+
+#[doc = include_str!("../../md_doc/cpu.md")]
+pub struct Cpu {
+ pub(crate) cpu_usage: f32,
+ name: String,
+ pub(crate) vendor_id: String,
+ pub(crate) frequency: u64,
+}
+
+impl Cpu {
+ pub(crate) fn new(name: String, vendor_id: String, frequency: u64) -> Cpu {
+ Cpu {
+ cpu_usage: 0.,
+ name,
+ vendor_id,
+ frequency,
+ }
+ }
+}
+
+impl CpuExt for Cpu {
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn frequency(&self) -> u64 {
+ self.frequency
+ }
+
+ fn vendor_id(&self) -> &str {
+ &self.vendor_id
+ }
+
+ fn brand(&self) -> &str {
+ ""
+ }
+}
diff --git a/vendor/sysinfo/src/freebsd/disk.rs b/vendor/sysinfo/src/freebsd/disk.rs
new file mode 100644
index 000000000..5156f1215
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/disk.rs
@@ -0,0 +1,143 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{DiskExt, DiskType};
+
+use std::ffi::{OsStr, OsString};
+use std::path::{Path, PathBuf};
+
+use super::utils::c_buf_to_str;
+
+#[doc = include_str!("../../md_doc/disk.md")]
+pub struct Disk {
+ name: OsString,
+ c_mount_point: Vec<libc::c_char>,
+ mount_point: PathBuf,
+ total_space: u64,
+ available_space: u64,
+ file_system: Vec<u8>,
+ is_removable: bool,
+}
+
+impl DiskExt for Disk {
+ fn type_(&self) -> DiskType {
+ DiskType::Unknown(-1)
+ }
+
+ fn name(&self) -> &OsStr {
+ &self.name
+ }
+
+ fn file_system(&self) -> &[u8] {
+ &self.file_system
+ }
+
+ fn mount_point(&self) -> &Path {
+ &self.mount_point
+ }
+
+ fn total_space(&self) -> u64 {
+ self.total_space
+ }
+
+ fn available_space(&self) -> u64 {
+ self.available_space
+ }
+
+ fn is_removable(&self) -> bool {
+ self.is_removable
+ }
+
+ fn refresh(&mut self) -> bool {
+ unsafe {
+ let mut vfs: libc::statvfs = std::mem::zeroed();
+ refresh_disk(self, &mut vfs)
+ }
+ }
+}
+
+// FIXME: if you want to get disk I/O usage:
+// statfs.[f_syncwrites, f_asyncwrites, f_syncreads, f_asyncreads]
+
+unsafe fn refresh_disk(disk: &mut Disk, vfs: &mut libc::statvfs) -> bool {
+ if libc::statvfs(disk.c_mount_point.as_ptr() as *const _, vfs) < 0 {
+ return false;
+ }
+ let f_frsize: u64 = vfs.f_frsize as _;
+
+ disk.total_space = vfs.f_blocks.saturating_mul(f_frsize);
+ disk.available_space = vfs.f_favail.saturating_mul(f_frsize);
+ true
+}
+
+pub unsafe fn get_all_disks() -> Vec<Disk> {
+ let mut fs_infos: *mut libc::statfs = std::ptr::null_mut();
+
+ let count = libc::getmntinfo(&mut fs_infos, libc::MNT_WAIT);
+
+ if count < 1 {
+ return Vec::new();
+ }
+ let mut vfs: libc::statvfs = std::mem::zeroed();
+ let fs_infos: &[libc::statfs] = std::slice::from_raw_parts(fs_infos as _, count as _);
+ let mut disks = Vec::new();
+
+ for fs_info in fs_infos {
+ if fs_info.f_mntfromname[0] == 0 || fs_info.f_mntonname[0] == 0 {
+ // If we have missing information, no need to look any further...
+ continue;
+ }
+ let fs_type: &[libc::c_char] =
+ if let Some(pos) = fs_info.f_fstypename.iter().position(|x| *x == 0) {
+ &fs_info.f_fstypename[..pos]
+ } else {
+ &fs_info.f_fstypename
+ };
+ let fs_type: &[u8] = std::slice::from_raw_parts(fs_type.as_ptr() as _, fs_type.len());
+ match fs_type {
+ b"autofs" | b"devfs" | b"linprocfs" | b"procfs" | b"fdesckfs" | b"tmpfs"
+ | b"linsysfs" => {
+ sysinfo_debug!(
+ "Memory filesystem `{:?}`, ignoring it.",
+ c_buf_to_str(&fs_info.f_fstypename).unwrap(),
+ );
+ continue;
+ }
+ _ => {}
+ }
+
+ if libc::statvfs(fs_info.f_mntonname.as_ptr(), &mut vfs) != 0 {
+ continue;
+ }
+
+ let mount_point = match c_buf_to_str(&fs_info.f_mntonname) {
+ Some(m) => m,
+ None => {
+ sysinfo_debug!("Cannot get disk mount point, ignoring it.");
+ continue;
+ }
+ };
+
+ let name = if mount_point == "/" {
+ OsString::from("root")
+ } else {
+ OsString::from(mount_point)
+ };
+
+ // USB keys and CDs are removable.
+ let is_removable =
+ [b"USB", b"usb"].iter().any(|b| b == &fs_type) || fs_type.starts_with(b"/dev/cd");
+
+ let f_frsize: u64 = vfs.f_frsize as _;
+
+ disks.push(Disk {
+ name,
+ c_mount_point: fs_info.f_mntonname.to_vec(),
+ mount_point: PathBuf::from(mount_point),
+ total_space: vfs.f_blocks.saturating_mul(f_frsize),
+ available_space: vfs.f_favail.saturating_mul(f_frsize),
+ file_system: fs_type.to_vec(),
+ is_removable,
+ });
+ }
+ disks
+}
diff --git a/vendor/sysinfo/src/freebsd/mod.rs b/vendor/sysinfo/src/freebsd/mod.rs
new file mode 100644
index 000000000..8a8726812
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/mod.rs
@@ -0,0 +1,16 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+pub mod component;
+pub mod cpu;
+pub mod disk;
+pub mod network;
+pub mod process;
+pub mod system;
+mod utils;
+
+pub use self::component::Component;
+pub use self::cpu::Cpu;
+pub use self::disk::Disk;
+pub use self::network::{NetworkData, Networks};
+pub use self::process::Process;
+pub use self::system::System;
diff --git a/vendor/sysinfo/src/freebsd/network.rs b/vendor/sysinfo/src/freebsd/network.rs
new file mode 100644
index 000000000..e58ad823a
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/network.rs
@@ -0,0 +1,199 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::collections::{hash_map, HashMap};
+use std::mem::MaybeUninit;
+
+use super::utils;
+use crate::{NetworkExt, NetworksExt, NetworksIter};
+
+macro_rules! old_and_new {
+ ($ty_:expr, $name:ident, $old:ident, $data:expr) => {{
+ $ty_.$old = $ty_.$name;
+ $ty_.$name = $data.$name;
+ }};
+}
+
+#[doc = include_str!("../../md_doc/networks.md")]
+pub struct Networks {
+ interfaces: HashMap<String, NetworkData>,
+}
+
+impl Networks {
+ pub(crate) fn new() -> Networks {
+ Networks {
+ interfaces: HashMap::new(),
+ }
+ }
+}
+
+impl NetworksExt for Networks {
+ fn iter(&self) -> NetworksIter {
+ NetworksIter::new(self.interfaces.iter())
+ }
+
+ fn refresh_networks_list(&mut self) {
+ unsafe {
+ self.refresh_interfaces(true);
+ }
+ // Remove interfaces which are gone.
+ self.interfaces.retain(|_, n| n.updated);
+ }
+
+ fn refresh(&mut self) {
+ unsafe {
+ self.refresh_interfaces(false);
+ }
+ }
+}
+
+impl Networks {
+ unsafe fn refresh_interfaces(&mut self, refresh_all: bool) {
+ let mut nb_interfaces: libc::c_int = 0;
+ if !utils::get_sys_value(
+ &[
+ libc::CTL_NET,
+ libc::PF_LINK,
+ libc::NETLINK_GENERIC,
+ libc::IFMIB_SYSTEM,
+ libc::IFMIB_IFCOUNT,
+ ],
+ &mut nb_interfaces,
+ ) {
+ return;
+ }
+ if refresh_all {
+ // We don't need to update this value if we're not updating all interfaces.
+ for interface in self.interfaces.values_mut() {
+ interface.updated = false;
+ }
+ }
+ let mut data: libc::ifmibdata = MaybeUninit::zeroed().assume_init();
+ for row in 1..nb_interfaces {
+ let mib = [
+ libc::CTL_NET,
+ libc::PF_LINK,
+ libc::NETLINK_GENERIC,
+ libc::IFMIB_IFDATA,
+ row,
+ libc::IFDATA_GENERAL,
+ ];
+
+ if !utils::get_sys_value(&mib, &mut data) {
+ continue;
+ }
+ if let Some(name) = utils::c_buf_to_string(&data.ifmd_name) {
+ let data = &data.ifmd_data;
+ match self.interfaces.entry(name) {
+ hash_map::Entry::Occupied(mut e) => {
+ let mut interface = e.get_mut();
+
+ old_and_new!(interface, ifi_ibytes, old_ifi_ibytes, data);
+ old_and_new!(interface, ifi_obytes, old_ifi_obytes, data);
+ old_and_new!(interface, ifi_ipackets, old_ifi_ipackets, data);
+ old_and_new!(interface, ifi_opackets, old_ifi_opackets, data);
+ old_and_new!(interface, ifi_ierrors, old_ifi_ierrors, data);
+ old_and_new!(interface, ifi_oerrors, old_ifi_oerrors, data);
+ interface.updated = true;
+ }
+ hash_map::Entry::Vacant(e) => {
+ if !refresh_all {
+ // This is simply a refresh, we don't want to add new interfaces!
+ continue;
+ }
+ e.insert(NetworkData {
+ ifi_ibytes: data.ifi_ibytes,
+ old_ifi_ibytes: 0,
+ ifi_obytes: data.ifi_obytes,
+ old_ifi_obytes: 0,
+ ifi_ipackets: data.ifi_ipackets,
+ old_ifi_ipackets: 0,
+ ifi_opackets: data.ifi_opackets,
+ old_ifi_opackets: 0,
+ ifi_ierrors: data.ifi_ierrors,
+ old_ifi_ierrors: 0,
+ ifi_oerrors: data.ifi_oerrors,
+ old_ifi_oerrors: 0,
+ updated: true,
+ });
+ }
+ }
+ }
+ }
+ }
+}
+
+#[doc = include_str!("../../md_doc/network_data.md")]
+pub struct NetworkData {
+ /// Total number of bytes received over interface.
+ ifi_ibytes: u64,
+ old_ifi_ibytes: u64,
+ /// Total number of bytes transmitted over interface.
+ ifi_obytes: u64,
+ old_ifi_obytes: u64,
+ /// Total number of packets received.
+ ifi_ipackets: u64,
+ old_ifi_ipackets: u64,
+ /// Total number of packets transmitted.
+ ifi_opackets: u64,
+ old_ifi_opackets: u64,
+ /// Shows the total number of packets received with error. This includes
+ /// too-long-frames errors, ring-buffer overflow errors, CRC errors,
+ /// frame alignment errors, fifo overruns, and missed packets.
+ ifi_ierrors: u64,
+ old_ifi_ierrors: u64,
+ /// similar to `ifi_ierrors`
+ ifi_oerrors: u64,
+ old_ifi_oerrors: u64,
+ /// Whether or not the above data has been updated during refresh
+ updated: bool,
+}
+
+impl NetworkExt for NetworkData {
+ fn received(&self) -> u64 {
+ self.ifi_ibytes.saturating_sub(self.old_ifi_ibytes)
+ }
+
+ fn total_received(&self) -> u64 {
+ self.ifi_ibytes
+ }
+
+ fn transmitted(&self) -> u64 {
+ self.ifi_obytes.saturating_sub(self.old_ifi_obytes)
+ }
+
+ fn total_transmitted(&self) -> u64 {
+ self.ifi_obytes
+ }
+
+ fn packets_received(&self) -> u64 {
+ self.ifi_ipackets.saturating_sub(self.old_ifi_ipackets)
+ }
+
+ fn total_packets_received(&self) -> u64 {
+ self.ifi_ipackets
+ }
+
+ fn packets_transmitted(&self) -> u64 {
+ self.ifi_opackets.saturating_sub(self.old_ifi_opackets)
+ }
+
+ fn total_packets_transmitted(&self) -> u64 {
+ self.ifi_opackets
+ }
+
+ fn errors_on_received(&self) -> u64 {
+ self.ifi_ierrors.saturating_sub(self.old_ifi_ierrors)
+ }
+
+ fn total_errors_on_received(&self) -> u64 {
+ self.ifi_ierrors
+ }
+
+ fn errors_on_transmitted(&self) -> u64 {
+ self.ifi_oerrors.saturating_sub(self.old_ifi_oerrors)
+ }
+
+ fn total_errors_on_transmitted(&self) -> u64 {
+ self.ifi_oerrors
+ }
+}
diff --git a/vendor/sysinfo/src/freebsd/process.rs b/vendor/sysinfo/src/freebsd/process.rs
new file mode 100644
index 000000000..b3302edbe
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/process.rs
@@ -0,0 +1,254 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid};
+
+use std::fmt;
+use std::path::{Path, PathBuf};
+
+use super::utils::{get_sys_value_str, WrapMap};
+
+#[doc(hidden)]
+impl From<libc::c_char> for ProcessStatus {
+ fn from(status: libc::c_char) -> ProcessStatus {
+ match status {
+ libc::SIDL => ProcessStatus::Idle,
+ libc::SRUN => ProcessStatus::Run,
+ libc::SSLEEP => ProcessStatus::Sleep,
+ libc::SSTOP => ProcessStatus::Stop,
+ libc::SZOMB => ProcessStatus::Zombie,
+ libc::SWAIT => ProcessStatus::Dead,
+ libc::SLOCK => ProcessStatus::LockBlocked,
+ x => ProcessStatus::Unknown(x as _),
+ }
+ }
+}
+
+impl fmt::Display for ProcessStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(match *self {
+ ProcessStatus::Idle => "Idle",
+ ProcessStatus::Run => "Runnable",
+ ProcessStatus::Sleep => "Sleeping",
+ ProcessStatus::Stop => "Stopped",
+ ProcessStatus::Zombie => "Zombie",
+ ProcessStatus::Dead => "Dead",
+ ProcessStatus::LockBlocked => "LockBlocked",
+ _ => "Unknown",
+ })
+ }
+}
+
+#[doc = include_str!("../../md_doc/process.md")]
+pub struct Process {
+ pub(crate) name: String,
+ pub(crate) cmd: Vec<String>,
+ pub(crate) exe: PathBuf,
+ pub(crate) pid: Pid,
+ parent: Option<Pid>,
+ pub(crate) environ: Vec<String>,
+ pub(crate) cwd: PathBuf,
+ pub(crate) root: PathBuf,
+ pub(crate) memory: u64,
+ pub(crate) virtual_memory: u64,
+ pub(crate) updated: bool,
+ cpu_usage: f32,
+ start_time: u64,
+ run_time: u64,
+ pub(crate) status: ProcessStatus,
+ user_id: Uid,
+ group_id: Gid,
+ read_bytes: u64,
+ old_read_bytes: u64,
+ written_bytes: u64,
+ old_written_bytes: u64,
+}
+
+impl ProcessExt for Process {
+ fn kill_with(&self, signal: Signal) -> Option<bool> {
+ let c_signal = super::system::convert_signal(signal)?;
+ unsafe { Some(libc::kill(self.pid.0, c_signal) == 0) }
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn cmd(&self) -> &[String] {
+ &self.cmd
+ }
+
+ fn exe(&self) -> &Path {
+ self.exe.as_path()
+ }
+
+ fn pid(&self) -> Pid {
+ self.pid
+ }
+
+ fn environ(&self) -> &[String] {
+ &self.environ
+ }
+
+ fn cwd(&self) -> &Path {
+ self.cwd.as_path()
+ }
+
+ fn root(&self) -> &Path {
+ self.root.as_path()
+ }
+
+ fn memory(&self) -> u64 {
+ self.memory
+ }
+
+ fn virtual_memory(&self) -> u64 {
+ self.virtual_memory
+ }
+
+ fn parent(&self) -> Option<Pid> {
+ self.parent
+ }
+
+ fn status(&self) -> ProcessStatus {
+ self.status
+ }
+
+ fn start_time(&self) -> u64 {
+ self.start_time
+ }
+
+ fn run_time(&self) -> u64 {
+ self.run_time
+ }
+
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn disk_usage(&self) -> DiskUsage {
+ DiskUsage {
+ written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
+ total_written_bytes: self.written_bytes,
+ read_bytes: self.read_bytes.saturating_sub(self.old_read_bytes),
+ total_read_bytes: self.read_bytes,
+ }
+ }
+
+ fn user_id(&self) -> Option<&Uid> {
+ Some(&self.user_id)
+ }
+
+ fn group_id(&self) -> Option<Gid> {
+ Some(self.group_id)
+ }
+}
+
+pub(crate) unsafe fn get_process_data(
+ kproc: &libc::kinfo_proc,
+ wrap: &WrapMap,
+ page_size: isize,
+ fscale: f32,
+ now: u64,
+ refresh_kind: ProcessRefreshKind,
+) -> Result<Option<Process>, ()> {
+ if kproc.ki_pid != 1 && (kproc.ki_flag as libc::c_int & libc::P_SYSTEM) != 0 {
+ // We filter out the kernel threads.
+ return Err(());
+ }
+
+ // We now get the values needed for both new and existing process.
+ let cpu_usage = if refresh_kind.cpu() {
+ (100 * kproc.ki_pctcpu) as f32 / fscale
+ } else {
+ 0.
+ };
+ // Processes can be reparented apparently?
+ let parent = if kproc.ki_ppid != 0 {
+ Some(Pid(kproc.ki_ppid))
+ } else {
+ None
+ };
+ let status = ProcessStatus::from(kproc.ki_stat);
+
+ // from FreeBSD source /src/usr.bin/top/machine.c
+ let virtual_memory = (kproc.ki_size / 1_000) as u64;
+ let memory = (kproc.ki_rssize as u64).saturating_mul(page_size as _) / 1_000;
+ // FIXME: This is to get the "real" run time (in micro-seconds).
+ // let run_time = (kproc.ki_runtime + 5_000) / 10_000;
+
+ if let Some(proc_) = (*wrap.0.get()).get_mut(&Pid(kproc.ki_pid)) {
+ proc_.cpu_usage = cpu_usage;
+ proc_.parent = parent;
+ proc_.status = status;
+ proc_.virtual_memory = virtual_memory;
+ proc_.memory = memory;
+ proc_.run_time = now.saturating_sub(proc_.start_time);
+ proc_.updated = true;
+
+ if refresh_kind.disk_usage() {
+ proc_.old_read_bytes = proc_.read_bytes;
+ proc_.read_bytes = kproc.ki_rusage.ru_inblock as _;
+ proc_.old_written_bytes = proc_.written_bytes;
+ proc_.written_bytes = kproc.ki_rusage.ru_oublock as _;
+ }
+
+ return Ok(None);
+ }
+
+ // This is a new process, we need to get more information!
+ let mut buffer = [0; libc::PATH_MAX as usize + 1];
+
+ let exe = get_sys_value_str(
+ &[
+ libc::CTL_KERN,
+ libc::KERN_PROC,
+ libc::KERN_PROC_PATHNAME,
+ kproc.ki_pid,
+ ],
+ &mut buffer,
+ )
+ .unwrap_or_default();
+ // For some reason, it can return completely invalid path like `p\u{5}`. So we need to use
+ // procstat to get around this problem.
+ // let cwd = get_sys_value_str(
+ // &[
+ // libc::CTL_KERN,
+ // libc::KERN_PROC,
+ // libc::KERN_PROC_CWD,
+ // kproc.ki_pid,
+ // ],
+ // &mut buffer,
+ // )
+ // .map(|s| s.into())
+ // .unwrap_or_else(PathBuf::new);
+
+ let start_time = kproc.ki_start.tv_sec as u64;
+ Ok(Some(Process {
+ pid: Pid(kproc.ki_pid),
+ parent,
+ user_id: Uid(kproc.ki_ruid),
+ group_id: Gid(kproc.ki_rgid),
+ start_time,
+ run_time: now.saturating_sub(start_time),
+ cpu_usage,
+ virtual_memory,
+ memory,
+ // procstat_getfiles
+ cwd: PathBuf::new(),
+ exe: exe.into(),
+ // kvm_getargv isn't thread-safe so we get it in the main thread.
+ name: String::new(),
+ // kvm_getargv isn't thread-safe so we get it in the main thread.
+ cmd: Vec::new(),
+ // kvm_getargv isn't thread-safe so we get it in the main thread.
+ root: PathBuf::new(),
+ // kvm_getenvv isn't thread-safe so we get it in the main thread.
+ environ: Vec::new(),
+ status,
+ read_bytes: kproc.ki_rusage.ru_inblock as _,
+ old_read_bytes: 0,
+ written_bytes: kproc.ki_rusage.ru_oublock as _,
+ old_written_bytes: 0,
+ updated: true,
+ }))
+}
diff --git a/vendor/sysinfo/src/freebsd/system.rs b/vendor/sysinfo/src/freebsd/system.rs
new file mode 100644
index 000000000..16da2de50
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/system.rs
@@ -0,0 +1,803 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{
+ sys::{component::Component, Cpu, Disk, Networks, Process},
+ CpuRefreshKind, LoadAvg, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User,
+};
+
+use std::cell::UnsafeCell;
+use std::collections::HashMap;
+use std::ffi::CStr;
+use std::mem::MaybeUninit;
+use std::path::{Path, PathBuf};
+use std::ptr::NonNull;
+
+use super::utils::{
+ self, boot_time, c_buf_to_string, from_cstr_array, get_frequency_for_cpu, get_sys_value,
+ get_sys_value_array, get_sys_value_by_name, get_sys_value_str_by_name, get_system_info,
+ init_mib,
+};
+
+use libc::c_int;
+
+declare_signals! {
+ c_int,
+ Signal::Hangup => libc::SIGHUP,
+ Signal::Interrupt => libc::SIGINT,
+ Signal::Quit => libc::SIGQUIT,
+ Signal::Illegal => libc::SIGILL,
+ Signal::Trap => libc::SIGTRAP,
+ Signal::Abort => libc::SIGABRT,
+ Signal::IOT => libc::SIGIOT,
+ Signal::Bus => libc::SIGBUS,
+ Signal::FloatingPointException => libc::SIGFPE,
+ Signal::Kill => libc::SIGKILL,
+ Signal::User1 => libc::SIGUSR1,
+ Signal::Segv => libc::SIGSEGV,
+ Signal::User2 => libc::SIGUSR2,
+ Signal::Pipe => libc::SIGPIPE,
+ Signal::Alarm => libc::SIGALRM,
+ Signal::Term => libc::SIGTERM,
+ Signal::Child => libc::SIGCHLD,
+ Signal::Continue => libc::SIGCONT,
+ Signal::Stop => libc::SIGSTOP,
+ Signal::TSTP => libc::SIGTSTP,
+ Signal::TTIN => libc::SIGTTIN,
+ Signal::TTOU => libc::SIGTTOU,
+ Signal::Urgent => libc::SIGURG,
+ Signal::XCPU => libc::SIGXCPU,
+ Signal::XFSZ => libc::SIGXFSZ,
+ Signal::VirtualAlarm => libc::SIGVTALRM,
+ Signal::Profiling => libc::SIGPROF,
+ Signal::Winch => libc::SIGWINCH,
+ Signal::IO => libc::SIGIO,
+ Signal::Sys => libc::SIGSYS,
+ _ => None,
+}
+
+#[doc = include_str!("../../md_doc/system.md")]
+pub struct System {
+ process_list: HashMap<Pid, Process>,
+ mem_total: u64,
+ mem_free: u64,
+ mem_used: u64,
+ swap_total: u64,
+ swap_used: u64,
+ global_cpu: Cpu,
+ cpus: Vec<Cpu>,
+ components: Vec<Component>,
+ disks: Vec<Disk>,
+ networks: Networks,
+ users: Vec<User>,
+ boot_time: u64,
+ system_info: SystemInfo,
+ got_cpu_frequency: bool,
+}
+
+impl SystemExt for System {
+ const IS_SUPPORTED: bool = true;
+ const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals();
+
+ fn new_with_specifics(refreshes: RefreshKind) -> System {
+ let system_info = SystemInfo::new();
+
+ let mut s = System {
+ process_list: HashMap::with_capacity(200),
+ mem_total: 0,
+ mem_free: 0,
+ mem_used: 0,
+ swap_total: 0,
+ swap_used: 0,
+ global_cpu: Cpu::new(String::new(), String::new(), 0),
+ cpus: Vec::with_capacity(system_info.nb_cpus as _),
+ components: Vec::with_capacity(2),
+ disks: Vec::with_capacity(1),
+ networks: Networks::new(),
+ users: Vec::new(),
+ boot_time: boot_time(),
+ system_info,
+ got_cpu_frequency: false,
+ };
+ s.refresh_specifics(refreshes);
+ s
+ }
+
+ fn refresh_memory(&mut self) {
+ if self.mem_total == 0 {
+ self.mem_total = self.system_info.get_total_memory();
+ }
+ self.mem_used = self.system_info.get_used_memory();
+ self.mem_free = self.system_info.get_free_memory();
+ let (swap_used, swap_total) = self.system_info.get_swap_info();
+ self.swap_total = swap_total;
+ self.swap_used = swap_used;
+ }
+
+ fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) {
+ if self.cpus.is_empty() {
+ let mut frequency = 0;
+
+ // We get the CPU vendor ID in here.
+ let vendor_id =
+ get_sys_value_str_by_name(b"hw.model\0").unwrap_or_else(|| "<unknown>".to_owned());
+ for pos in 0..self.system_info.nb_cpus {
+ if refresh_kind.frequency() {
+ unsafe {
+ frequency = get_frequency_for_cpu(pos);
+ }
+ }
+ self.cpus.push(Cpu::new(
+ format!("cpu {}", pos),
+ vendor_id.clone(),
+ frequency,
+ ));
+ }
+ self.global_cpu.vendor_id = vendor_id;
+ self.got_cpu_frequency = refresh_kind.frequency();
+ } else if refresh_kind.frequency() && !self.got_cpu_frequency {
+ for (pos, proc_) in self.cpus.iter_mut().enumerate() {
+ unsafe {
+ proc_.frequency = get_frequency_for_cpu(pos as _);
+ }
+ }
+ self.got_cpu_frequency = true;
+ }
+ if refresh_kind.cpu_usage() {
+ self.system_info
+ .get_cpu_usage(&mut self.global_cpu, &mut self.cpus);
+ }
+ }
+
+ fn refresh_components_list(&mut self) {
+ if self.cpus.is_empty() {
+ self.refresh_cpu();
+ }
+ self.components = unsafe { super::component::get_components(self.cpus.len()) };
+ }
+
+ fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind) {
+ unsafe { self.refresh_procs(refresh_kind) }
+ }
+
+ fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool {
+ unsafe {
+ let kd = self.system_info.kd.as_ptr();
+ let mut count = 0;
+ let procs = libc::kvm_getprocs(kd, libc::KERN_PROC_PROC, 0, &mut count);
+ if count < 1 {
+ sysinfo_debug!("kvm_getprocs returned nothing...");
+ return false;
+ }
+ let now = super::utils::get_now();
+
+ let fscale = self.system_info.fscale;
+ let page_size = self.system_info.page_size as isize;
+ let proc_list = utils::WrapMap(UnsafeCell::new(&mut self.process_list));
+ let procs: &mut [utils::KInfoProc] =
+ std::slice::from_raw_parts_mut(procs as _, count as _);
+
+ #[cfg(feature = "multithread")]
+ use rayon::iter::ParallelIterator;
+
+ macro_rules! multi_iter {
+ ($name:ident, $($iter:tt)+) => {
+ $name = crate::utils::into_iter(procs).$($iter)+;
+ }
+ }
+
+ let ret;
+ #[cfg(not(feature = "multithread"))]
+ multi_iter!(ret, find(|kproc| kproc.ki_pid == pid.0));
+ #[cfg(feature = "multithread")]
+ multi_iter!(ret, find_any(|kproc| kproc.ki_pid == pid.0));
+
+ let kproc = if let Some(kproc) = ret {
+ kproc
+ } else {
+ return false;
+ };
+ match super::process::get_process_data(
+ kproc,
+ &proc_list,
+ page_size,
+ fscale,
+ now,
+ refresh_kind,
+ ) {
+ Ok(Some(proc_)) => {
+ self.add_missing_proc_info(self.system_info.kd.as_ptr(), kproc, proc_);
+ true
+ }
+ Ok(None) => true,
+ Err(_) => false,
+ }
+ }
+ }
+
+ fn refresh_disks_list(&mut self) {
+ self.disks = unsafe { super::disk::get_all_disks() };
+ }
+
+ fn refresh_users_list(&mut self) {
+ self.users = crate::users::get_users_list();
+ }
+
+ // COMMON PART
+ //
+ // Need to be moved into a "common" file to avoid duplication.
+
+ fn processes(&self) -> &HashMap<Pid, Process> {
+ &self.process_list
+ }
+
+ fn process(&self, pid: Pid) -> Option<&Process> {
+ self.process_list.get(&pid)
+ }
+
+ fn networks(&self) -> &Networks {
+ &self.networks
+ }
+
+ fn networks_mut(&mut self) -> &mut Networks {
+ &mut self.networks
+ }
+
+ fn global_cpu_info(&self) -> &Cpu {
+ &self.global_cpu
+ }
+
+ fn cpus(&self) -> &[Cpu] {
+ &self.cpus
+ }
+
+ fn physical_core_count(&self) -> Option<usize> {
+ let mut physical_core_count: u32 = 0;
+
+ unsafe {
+ if get_sys_value_by_name(b"hw.ncpu\0", &mut physical_core_count) {
+ Some(physical_core_count as _)
+ } else {
+ None
+ }
+ }
+ }
+
+ fn total_memory(&self) -> u64 {
+ self.mem_total
+ }
+
+ fn free_memory(&self) -> u64 {
+ self.mem_free
+ }
+
+ fn available_memory(&self) -> u64 {
+ self.mem_free
+ }
+
+ fn used_memory(&self) -> u64 {
+ self.mem_used
+ }
+
+ fn total_swap(&self) -> u64 {
+ self.swap_total
+ }
+
+ fn free_swap(&self) -> u64 {
+ self.swap_total - self.swap_used
+ }
+
+ // TODO: need to be checked
+ fn used_swap(&self) -> u64 {
+ self.swap_used
+ }
+
+ fn components(&self) -> &[Component] {
+ &self.components
+ }
+
+ fn components_mut(&mut self) -> &mut [Component] {
+ &mut self.components
+ }
+
+ fn disks(&self) -> &[Disk] {
+ &self.disks
+ }
+
+ fn disks_mut(&mut self) -> &mut [Disk] {
+ &mut self.disks
+ }
+
+ fn uptime(&self) -> u64 {
+ unsafe {
+ let csec = libc::time(::std::ptr::null_mut());
+
+ libc::difftime(csec, self.boot_time as _) as u64
+ }
+ }
+
+ fn boot_time(&self) -> u64 {
+ self.boot_time
+ }
+
+ fn load_average(&self) -> LoadAvg {
+ let mut loads = vec![0f64; 3];
+ unsafe {
+ libc::getloadavg(loads.as_mut_ptr(), 3);
+ LoadAvg {
+ one: loads[0],
+ five: loads[1],
+ fifteen: loads[2],
+ }
+ }
+ }
+
+ fn users(&self) -> &[User] {
+ &self.users
+ }
+
+ fn name(&self) -> Option<String> {
+ self.system_info.get_os_name()
+ }
+
+ fn long_os_version(&self) -> Option<String> {
+ self.system_info.get_os_release_long()
+ }
+
+ fn host_name(&self) -> Option<String> {
+ self.system_info.get_hostname()
+ }
+
+ fn kernel_version(&self) -> Option<String> {
+ self.system_info.get_kernel_version()
+ }
+
+ fn os_version(&self) -> Option<String> {
+ self.system_info.get_os_release()
+ }
+}
+
+impl Default for System {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl System {
+ unsafe fn refresh_procs(&mut self, refresh_kind: ProcessRefreshKind) {
+ let kd = self.system_info.kd.as_ptr();
+ let procs = {
+ let mut count = 0;
+ let procs = libc::kvm_getprocs(kd, libc::KERN_PROC_PROC, 0, &mut count);
+ if count < 1 {
+ sysinfo_debug!("kvm_getprocs returned nothing...");
+ return;
+ }
+ #[cfg(feature = "multithread")]
+ use rayon::iter::{ParallelIterator, ParallelIterator as IterTrait};
+ #[cfg(not(feature = "multithread"))]
+ use std::iter::Iterator as IterTrait;
+
+ crate::utils::into_iter(&mut self.process_list).for_each(|(_, proc_)| {
+ proc_.updated = false;
+ });
+
+ let fscale = self.system_info.fscale;
+ let page_size = self.system_info.page_size as isize;
+ let now = super::utils::get_now();
+ let proc_list = utils::WrapMap(UnsafeCell::new(&mut self.process_list));
+ let procs: &mut [utils::KInfoProc] =
+ std::slice::from_raw_parts_mut(procs as _, count as _);
+
+ IterTrait::filter_map(crate::utils::into_iter(procs), |kproc| {
+ super::process::get_process_data(
+ kproc,
+ &proc_list,
+ page_size,
+ fscale,
+ now,
+ refresh_kind,
+ )
+ .ok()
+ .and_then(|p| p.map(|p| (kproc, p)))
+ })
+ .collect::<Vec<_>>()
+ };
+
+ // We remove all processes that don't exist anymore.
+ self.process_list.retain(|_, v| v.updated);
+
+ for (kproc, proc_) in procs {
+ self.add_missing_proc_info(kd, kproc, proc_);
+ }
+ }
+
+ unsafe fn add_missing_proc_info(
+ &mut self,
+ kd: *mut libc::kvm_t,
+ kproc: &libc::kinfo_proc,
+ mut proc_: Process,
+ ) {
+ proc_.cmd = from_cstr_array(libc::kvm_getargv(kd, kproc, 0) as _);
+ self.system_info.get_proc_missing_info(kproc, &mut proc_);
+ if !proc_.cmd.is_empty() {
+ // First, we try to retrieve the name from the command line.
+ let p = Path::new(&proc_.cmd[0]);
+ if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
+ proc_.name = name.to_owned();
+ }
+ if proc_.root.as_os_str().is_empty() {
+ if let Some(parent) = p.parent() {
+ proc_.root = parent.to_path_buf();
+ }
+ }
+ }
+ if proc_.name.is_empty() {
+ // The name can be cut short because the `ki_comm` field size is limited,
+ // which is why we prefer to get the name from the command line as much as
+ // possible.
+ proc_.name = c_buf_to_string(&kproc.ki_comm).unwrap_or_default();
+ }
+ proc_.environ = from_cstr_array(libc::kvm_getenvv(kd, kproc, 0) as _);
+ self.process_list.insert(proc_.pid, proc_);
+ }
+}
+
+#[derive(Debug)]
+struct Zfs {
+ enabled: bool,
+ mib_arcstats_size: [c_int; 5],
+}
+
+impl Zfs {
+ fn new() -> Self {
+ let mut zfs = Self {
+ enabled: false,
+ mib_arcstats_size: Default::default(),
+ };
+ unsafe {
+ init_mib(
+ b"kstat.zfs.misc.arcstats.size\0",
+ &mut zfs.mib_arcstats_size,
+ );
+ let mut arc_size: u64 = 0;
+ if get_sys_value(&zfs.mib_arcstats_size, &mut arc_size) {
+ zfs.enabled = arc_size != 0;
+ }
+ }
+ zfs
+ }
+
+ fn arc_size(&self) -> Option<u64> {
+ if self.enabled {
+ let mut arc_size: u64 = 0;
+ unsafe {
+ get_sys_value(&self.mib_arcstats_size, &mut arc_size);
+ Some(arc_size)
+ }
+ } else {
+ None
+ }
+ }
+}
+
+/// This struct is used to get system information more easily.
+#[derive(Debug)]
+struct SystemInfo {
+ hw_physical_memory: [c_int; 2],
+ page_size: c_int,
+ virtual_page_count: [c_int; 4],
+ virtual_wire_count: [c_int; 4],
+ virtual_active_count: [c_int; 4],
+ virtual_cache_count: [c_int; 4],
+ virtual_inactive_count: [c_int; 4],
+ virtual_free_count: [c_int; 4],
+ os_type: [c_int; 2],
+ os_release: [c_int; 2],
+ kern_version: [c_int; 2],
+ hostname: [c_int; 2],
+ buf_space: [c_int; 2],
+ nb_cpus: c_int,
+ kd: NonNull<libc::kvm_t>,
+ // For these two fields, we could use `kvm_getcptime` but the function isn't very efficient...
+ mib_cp_time: [c_int; 2],
+ mib_cp_times: [c_int; 2],
+ // For the global CPU usage.
+ cp_time: utils::VecSwitcher<libc::c_ulong>,
+ // For each CPU usage.
+ cp_times: utils::VecSwitcher<libc::c_ulong>,
+ /// From FreeBSD manual: "The kernel fixed-point scale factor". It's used when computing
+ /// processes' CPU usage.
+ fscale: f32,
+ procstat: *mut libc::procstat,
+ zfs: Zfs,
+}
+
+// This is needed because `kd: *mut libc::kvm_t` isn't thread-safe.
+unsafe impl Send for SystemInfo {}
+unsafe impl Sync for SystemInfo {}
+
+impl SystemInfo {
+ fn new() -> Self {
+ unsafe {
+ let mut errbuf =
+ MaybeUninit::<[libc::c_char; libc::_POSIX2_LINE_MAX as usize]>::uninit();
+ let kd = NonNull::new(libc::kvm_openfiles(
+ std::ptr::null(),
+ b"/dev/null\0".as_ptr() as *const _,
+ std::ptr::null(),
+ 0,
+ errbuf.as_mut_ptr() as *mut _,
+ ))
+ .expect("kvm_openfiles failed");
+
+ let mut smp: c_int = 0;
+ let mut nb_cpus: c_int = 1;
+ if !get_sys_value_by_name(b"kern.smp.active\0", &mut smp) {
+ smp = 0;
+ }
+ #[allow(clippy::collapsible_if)] // I keep as is for readability reasons.
+ if smp != 0 {
+ if !get_sys_value_by_name(b"kern.smp.cpus\0", &mut nb_cpus) || nb_cpus < 1 {
+ nb_cpus = 1;
+ }
+ }
+
+ let mut si = SystemInfo {
+ hw_physical_memory: Default::default(),
+ page_size: 0,
+ virtual_page_count: Default::default(),
+ virtual_wire_count: Default::default(),
+ virtual_active_count: Default::default(),
+ virtual_cache_count: Default::default(),
+ virtual_inactive_count: Default::default(),
+ virtual_free_count: Default::default(),
+ buf_space: Default::default(),
+ os_type: Default::default(),
+ os_release: Default::default(),
+ kern_version: Default::default(),
+ hostname: Default::default(),
+ nb_cpus,
+ kd,
+ mib_cp_time: Default::default(),
+ mib_cp_times: Default::default(),
+ cp_time: utils::VecSwitcher::new(vec![0; libc::CPUSTATES as usize]),
+ cp_times: utils::VecSwitcher::new(vec![
+ 0;
+ nb_cpus as usize * libc::CPUSTATES as usize
+ ]),
+ fscale: 0.,
+ procstat: std::ptr::null_mut(),
+ zfs: Zfs::new(),
+ };
+ let mut fscale: c_int = 0;
+ if !get_sys_value_by_name(b"kern.fscale\0", &mut fscale) {
+ // Default value used in htop.
+ fscale = 2048;
+ }
+ si.fscale = fscale as f32;
+
+ if !get_sys_value_by_name(b"vm.stats.vm.v_page_size\0", &mut si.page_size) {
+ panic!("cannot get page size...");
+ }
+
+ init_mib(b"hw.physmem\0", &mut si.hw_physical_memory);
+ init_mib(b"vm.stats.vm.v_page_count\0", &mut si.virtual_page_count);
+ init_mib(b"vm.stats.vm.v_wire_count\0", &mut si.virtual_wire_count);
+ init_mib(
+ b"vm.stats.vm.v_active_count\0",
+ &mut si.virtual_active_count,
+ );
+ init_mib(b"vm.stats.vm.v_cache_count\0", &mut si.virtual_cache_count);
+ init_mib(
+ b"vm.stats.vm.v_inactive_count\0",
+ &mut si.virtual_inactive_count,
+ );
+ init_mib(b"vm.stats.vm.v_free_count\0", &mut si.virtual_free_count);
+ init_mib(b"vfs.bufspace\0", &mut si.buf_space);
+
+ init_mib(b"kern.ostype\0", &mut si.os_type);
+ init_mib(b"kern.osrelease\0", &mut si.os_release);
+ init_mib(b"kern.version\0", &mut si.kern_version);
+ init_mib(b"kern.hostname\0", &mut si.hostname);
+
+ init_mib(b"kern.cp_time\0", &mut si.mib_cp_time);
+ init_mib(b"kern.cp_times\0", &mut si.mib_cp_times);
+
+ si
+ }
+ }
+
+ fn get_os_name(&self) -> Option<String> {
+ get_system_info(&[self.os_type[0], self.os_type[1]], Some("FreeBSD"))
+ }
+
+ fn get_kernel_version(&self) -> Option<String> {
+ get_system_info(&[self.kern_version[0], self.kern_version[1]], None)
+ }
+
+ fn get_os_release_long(&self) -> Option<String> {
+ get_system_info(&[self.os_release[0], self.os_release[1]], None)
+ }
+
+ fn get_os_release(&self) -> Option<String> {
+ // It returns something like "13.0-RELEASE". We want to keep everything until the "-".
+ get_system_info(&[self.os_release[0], self.os_release[1]], None)
+ .and_then(|s| s.split('-').next().map(|s| s.to_owned()))
+ }
+
+ fn get_hostname(&self) -> Option<String> {
+ get_system_info(&[self.hostname[0], self.hostname[1]], Some(""))
+ }
+
+ /// Returns (used, total).
+ fn get_swap_info(&self) -> (u64, u64) {
+ // Magic number used in htop. Cannot find how they got it when reading `kvm_getswapinfo`
+ // source code so here we go...
+ const LEN: usize = 16;
+ let mut swap = MaybeUninit::<[libc::kvm_swap; LEN]>::uninit();
+ unsafe {
+ let nswap =
+ libc::kvm_getswapinfo(self.kd.as_ptr(), swap.as_mut_ptr() as *mut _, LEN as _, 0)
+ as usize;
+ if nswap < 1 {
+ return (0, 0);
+ }
+ let swap =
+ std::slice::from_raw_parts(swap.as_ptr() as *mut libc::kvm_swap, nswap.min(LEN));
+ let (used, total) = swap.iter().fold((0, 0), |(used, total): (u64, u64), swap| {
+ (
+ used.saturating_add(swap.ksw_used as _),
+ total.saturating_add(swap.ksw_total as _),
+ )
+ });
+ (
+ used.saturating_mul(self.page_size as _) / 1_000,
+ total.saturating_mul(self.page_size as _) / 1_000,
+ )
+ }
+ }
+
+ fn get_total_memory(&self) -> u64 {
+ let mut nb_pages: u64 = 0;
+ unsafe {
+ if get_sys_value(&self.virtual_page_count, &mut nb_pages) {
+ return nb_pages.saturating_mul(self.page_size as _) / 1_000;
+ }
+
+ // This is a fallback. It includes all the available memory, not just the one available for
+ // the users.
+ let mut total_memory: u64 = 0;
+ get_sys_value(&self.hw_physical_memory, &mut total_memory);
+ total_memory / 1_000
+ }
+ }
+
+ fn get_used_memory(&self) -> u64 {
+ let mut mem_active: u64 = 0;
+ let mut mem_wire: u64 = 0;
+
+ unsafe {
+ get_sys_value(&self.virtual_active_count, &mut mem_active);
+ get_sys_value(&self.virtual_wire_count, &mut mem_wire);
+
+ let mut mem_wire = mem_wire.saturating_mul(self.page_size as _);
+ // We need to subtract "ZFS ARC" from the "wired memory" because it should belongs to cache
+ // but the kernel reports it as "wired memory" instead...
+ if let Some(arc_size) = self.zfs.arc_size() {
+ mem_wire -= arc_size;
+ }
+ let used = mem_active
+ .saturating_mul(self.page_size as _)
+ .saturating_add(mem_wire);
+ used / 1_000
+ }
+ }
+
+ fn get_free_memory(&self) -> u64 {
+ let mut buffers_mem: u64 = 0;
+ let mut inactive_mem: u64 = 0;
+ let mut cached_mem: u64 = 0;
+ let mut free_mem: u64 = 0;
+
+ unsafe {
+ get_sys_value(&self.buf_space, &mut buffers_mem);
+ get_sys_value(&self.virtual_inactive_count, &mut inactive_mem);
+ get_sys_value(&self.virtual_cache_count, &mut cached_mem);
+ get_sys_value(&self.virtual_free_count, &mut free_mem);
+ // For whatever reason, buffers_mem is already the right value...
+ let free = buffers_mem
+ .saturating_add(inactive_mem.saturating_mul(self.page_size as u64))
+ .saturating_add(cached_mem.saturating_mul(self.page_size as u64))
+ .saturating_add(free_mem.saturating_mul(self.page_size as u64));
+ free / 1_000
+ }
+ }
+
+ fn get_cpu_usage(&mut self, global: &mut Cpu, cpus: &mut [Cpu]) {
+ unsafe {
+ get_sys_value_array(&self.mib_cp_time, self.cp_time.get_mut());
+ get_sys_value_array(&self.mib_cp_times, self.cp_times.get_mut());
+ }
+
+ fn fill_cpu(proc_: &mut Cpu, new_cp_time: &[libc::c_ulong], old_cp_time: &[libc::c_ulong]) {
+ let mut total_new: u64 = 0;
+ let mut total_old: u64 = 0;
+ let mut cp_diff: libc::c_ulong = 0;
+
+ for i in 0..(libc::CPUSTATES as usize) {
+ // We obviously don't want to get the idle part of the CPU usage, otherwise
+ // we would always be at 100%...
+ if i != libc::CP_IDLE as usize {
+ cp_diff += new_cp_time[i] - old_cp_time[i];
+ }
+ total_new += new_cp_time[i] as u64;
+ total_old += old_cp_time[i] as u64;
+ }
+
+ let total_diff = total_new - total_old;
+ if total_diff < 1 {
+ proc_.cpu_usage = 0.;
+ } else {
+ proc_.cpu_usage = cp_diff as f32 / total_diff as f32 * 100.;
+ }
+ }
+
+ fill_cpu(global, self.cp_time.get_new(), self.cp_time.get_old());
+ let old_cp_times = self.cp_times.get_old();
+ let new_cp_times = self.cp_times.get_new();
+ for (pos, proc_) in cpus.iter_mut().enumerate() {
+ let index = pos * libc::CPUSTATES as usize;
+
+ fill_cpu(proc_, &new_cp_times[index..], &old_cp_times[index..]);
+ }
+ }
+
+ #[allow(clippy::collapsible_if)] // I keep as is for readability reasons.
+ unsafe fn get_proc_missing_info(&mut self, kproc: &libc::kinfo_proc, proc_: &mut Process) {
+ if self.procstat.is_null() {
+ self.procstat = libc::procstat_open_sysctl();
+ }
+ if self.procstat.is_null() {
+ return;
+ }
+ let head = libc::procstat_getfiles(self.procstat, kproc as *const _ as usize as *mut _, 0);
+ if head.is_null() {
+ return;
+ }
+ let mut entry = (*head).stqh_first;
+ let mut done = 0;
+ while !entry.is_null() && done < 2 {
+ {
+ let tmp = &*entry;
+ if tmp.fs_uflags & libc::PS_FST_UFLAG_CDIR != 0 {
+ if !tmp.fs_path.is_null() {
+ if let Ok(p) = CStr::from_ptr(tmp.fs_path).to_str() {
+ proc_.cwd = PathBuf::from(p);
+ done += 1;
+ }
+ }
+ } else if tmp.fs_uflags & libc::PS_FST_UFLAG_RDIR != 0 {
+ if !tmp.fs_path.is_null() {
+ if let Ok(p) = CStr::from_ptr(tmp.fs_path).to_str() {
+ proc_.root = PathBuf::from(p);
+ done += 1;
+ }
+ }
+ }
+ }
+ entry = (*entry).next.stqe_next;
+ }
+ libc::procstat_freefiles(self.procstat, head);
+ }
+}
+
+impl Drop for SystemInfo {
+ fn drop(&mut self) {
+ unsafe {
+ libc::kvm_close(self.kd.as_ptr());
+ if !self.procstat.is_null() {
+ libc::procstat_close(self.procstat);
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/freebsd/utils.rs b/vendor/sysinfo/src/freebsd/utils.rs
new file mode 100644
index 000000000..00de3e9d9
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/utils.rs
@@ -0,0 +1,296 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{Pid, Process};
+use libc::{c_char, c_int, timeval};
+use std::cell::UnsafeCell;
+use std::collections::HashMap;
+use std::ffi::CStr;
+use std::mem;
+use std::time::SystemTime;
+
+/// This struct is used to switch between the "old" and "new" every time you use "get_mut".
+#[derive(Debug)]
+pub(crate) struct VecSwitcher<T> {
+ v1: Vec<T>,
+ v2: Vec<T>,
+ first: bool,
+}
+
+impl<T: Clone> VecSwitcher<T> {
+ pub fn new(v1: Vec<T>) -> Self {
+ let v2 = v1.clone();
+
+ Self {
+ v1,
+ v2,
+ first: true,
+ }
+ }
+
+ pub fn get_mut(&mut self) -> &mut [T] {
+ self.first = !self.first;
+ if self.first {
+ // It means that `v2` will be the "new".
+ &mut self.v2
+ } else {
+ // It means that `v1` will be the "new".
+ &mut self.v1
+ }
+ }
+
+ pub fn get_old(&self) -> &[T] {
+ if self.first {
+ &self.v1
+ } else {
+ &self.v2
+ }
+ }
+
+ pub fn get_new(&self) -> &[T] {
+ if self.first {
+ &self.v2
+ } else {
+ &self.v1
+ }
+ }
+}
+
+#[inline]
+pub unsafe fn init_mib(name: &[u8], mib: &mut [c_int]) {
+ let mut len = mib.len();
+ libc::sysctlnametomib(name.as_ptr() as _, mib.as_mut_ptr(), &mut len);
+}
+
+pub(crate) fn boot_time() -> u64 {
+ let mut boot_time = timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ };
+ let mut len = std::mem::size_of::<timeval>();
+ let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME];
+ unsafe {
+ if libc::sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ &mut boot_time as *mut timeval as *mut _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ ) < 0
+ {
+ 0
+ } else {
+ boot_time.tv_sec as _
+ }
+ }
+}
+
+pub(crate) unsafe fn get_sys_value<T: Sized>(mib: &[c_int], value: &mut T) -> bool {
+ let mut len = mem::size_of::<T>() as libc::size_t;
+ libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ value as *mut _ as *mut _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+}
+
+pub(crate) unsafe fn get_sys_value_array<T: Sized>(mib: &[c_int], value: &mut [T]) -> bool {
+ let mut len = (mem::size_of::<T>() * value.len()) as libc::size_t;
+ libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ value.as_mut_ptr() as *mut _,
+ &mut len as *mut _,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+}
+
+pub(crate) fn c_buf_to_str(buf: &[libc::c_char]) -> Option<&str> {
+ unsafe {
+ let buf: &[u8] = std::slice::from_raw_parts(buf.as_ptr() as _, buf.len());
+ if let Some(pos) = buf.iter().position(|x| *x == 0) {
+ // Shrink buffer to terminate the null bytes
+ std::str::from_utf8(&buf[..pos]).ok()
+ } else {
+ std::str::from_utf8(buf).ok()
+ }
+ }
+}
+
+pub(crate) fn c_buf_to_string(buf: &[libc::c_char]) -> Option<String> {
+ c_buf_to_str(buf).map(|s| s.to_owned())
+}
+
+pub(crate) unsafe fn get_sys_value_str(mib: &[c_int], buf: &mut [libc::c_char]) -> Option<String> {
+ let mut len = (mem::size_of::<libc::c_char>() * buf.len()) as libc::size_t;
+ if libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ buf.as_mut_ptr() as *mut _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ ) != 0
+ {
+ return None;
+ }
+ c_buf_to_string(&buf[..len / mem::size_of::<libc::c_char>()])
+}
+
+pub(crate) unsafe fn get_sys_value_by_name<T: Sized>(name: &[u8], value: &mut T) -> bool {
+ let mut len = mem::size_of::<T>() as libc::size_t;
+ let original = len;
+
+ libc::sysctlbyname(
+ name.as_ptr() as *const c_char,
+ value as *mut _ as *mut _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+ && original == len
+}
+
+pub(crate) fn get_sys_value_str_by_name(name: &[u8]) -> Option<String> {
+ let mut size = 0;
+
+ unsafe {
+ if libc::sysctlbyname(
+ name.as_ptr() as *const c_char,
+ std::ptr::null_mut(),
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+ && size > 0
+ {
+ // now create a buffer with the size and get the real value
+ let mut buf: Vec<libc::c_char> = vec![0; size as usize];
+
+ if libc::sysctlbyname(
+ name.as_ptr() as *const c_char,
+ buf.as_mut_ptr() as *mut _,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+ && size > 0
+ {
+ c_buf_to_string(&buf)
+ } else {
+ // getting the system value failed
+ None
+ }
+ } else {
+ None
+ }
+ }
+}
+
+pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option<String> {
+ let mut size = 0;
+
+ unsafe {
+ // Call first to get size
+ libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ std::ptr::null_mut(),
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ );
+
+ // exit early if we did not update the size
+ if size == 0 {
+ default.map(|s| s.to_owned())
+ } else {
+ // set the buffer to the correct size
+ let mut buf: Vec<libc::c_char> = vec![0; size as usize];
+
+ if libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ buf.as_mut_ptr() as _,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == -1
+ {
+ // If command fails return default
+ default.map(|s| s.to_owned())
+ } else {
+ c_buf_to_string(&buf)
+ }
+ }
+ }
+}
+
+pub(crate) unsafe fn from_cstr_array(ptr: *const *const c_char) -> Vec<String> {
+ if ptr.is_null() {
+ return Vec::new();
+ }
+ let mut max = 0;
+ loop {
+ let ptr = ptr.add(max);
+ if (*ptr).is_null() {
+ break;
+ }
+ max += 1;
+ }
+ if max == 0 {
+ return Vec::new();
+ }
+ let mut ret = Vec::with_capacity(max);
+
+ for pos in 0..max {
+ let p = ptr.add(pos);
+ if let Ok(s) = CStr::from_ptr(*p).to_str() {
+ ret.push(s.to_owned());
+ }
+ }
+ ret
+}
+
+pub(crate) fn get_now() -> u64 {
+ SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .map(|n| n.as_secs())
+ .unwrap_or(0)
+}
+
+// All this is needed because `kinfo_proc` doesn't implement `Send` (because it contains pointers).
+pub(crate) struct WrapMap<'a>(pub UnsafeCell<&'a mut HashMap<Pid, Process>>);
+
+unsafe impl<'a> Send for WrapMap<'a> {}
+unsafe impl<'a> Sync for WrapMap<'a> {}
+
+#[repr(transparent)]
+pub(crate) struct KInfoProc(libc::kinfo_proc);
+unsafe impl Send for KInfoProc {}
+unsafe impl Sync for KInfoProc {}
+
+impl std::ops::Deref for KInfoProc {
+ type Target = libc::kinfo_proc;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+pub(crate) unsafe fn get_frequency_for_cpu(cpu_nb: c_int) -> u64 {
+ let mut frequency = 0;
+
+ // The information can be missing if it's running inside a VM.
+ if !get_sys_value_by_name(
+ format!("dev.cpu.{}.freq\0", cpu_nb).as_bytes(),
+ &mut frequency,
+ ) {
+ frequency = 0;
+ }
+ frequency as _
+}