blob: 602a3e2f39c32c9ba7a45c4f05cdd153ec856660 [file] [log] [blame]
// Take a look at the license at the top of the repository in the LICENSE file.
#![doc = include_str!("../README.md")]
#![cfg_attr(feature = "serde", doc = include_str!("../md_doc/serde.md"))]
#![allow(unknown_lints)]
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
#![allow(clippy::upper_case_acronyms)]
#![allow(clippy::non_send_fields_in_send_ty)]
#![allow(renamed_and_removed_lints)]
#![allow(clippy::assertions_on_constants)]
#[macro_use]
mod macros;
cfg_if::cfg_if! {
if #[cfg(feature = "unknown-ci")] {
// This is used in CI to check that the build for unknown targets is compiling fine.
mod unknown;
use crate::unknown as sys;
#[cfg(test)]
pub(crate) const MIN_USERS: usize = 0;
} else if #[cfg(any(
target_os = "macos", target_os = "ios",
target_os = "linux", target_os = "android",
target_os = "freebsd"))]
{
mod unix;
mod network;
use crate::unix::sys as sys;
use crate::unix::network_helper;
#[cfg(test)]
pub(crate) const MIN_USERS: usize = 1;
} else if #[cfg(windows)] {
mod windows;
use crate::windows as sys;
use crate::windows::network_helper;
mod network;
#[cfg(test)]
pub(crate) const MIN_USERS: usize = 1;
} else {
mod unknown;
use crate::unknown as sys;
#[cfg(test)]
pub(crate) const MIN_USERS: usize = 0;
}
}
pub use crate::common::{
get_current_pid, CGroupLimits, Component, Components, Cpu, CpuRefreshKind, Disk, DiskKind,
DiskUsage, Disks, Gid, Group, LoadAvg, MacAddr, MemoryRefreshKind, NetworkData, Networks, Pid,
Process, ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, System, ThreadKind, Uid,
UpdateKind, User, Users,
};
pub(crate) use crate::sys::{
ComponentInner, ComponentsInner, CpuInner, DiskInner, DisksInner, NetworkDataInner,
NetworksInner, ProcessInner, SystemInner, UserInner,
};
pub use crate::sys::{IS_SUPPORTED_SYSTEM, MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
#[cfg(feature = "c-interface")]
pub use crate::c_interface::*;
#[cfg(feature = "c-interface")]
mod c_interface;
mod common;
mod debug;
#[cfg(feature = "serde")]
mod serde;
mod utils;
/// This function is only used on Linux targets, on the other platforms it does nothing and returns
/// `false`.
///
/// On Linux, to improve performance, we keep a `/proc` file open for each process we index with
/// a maximum number of files open equivalent to half of the system limit.
///
/// The problem is that some users might need all the available file descriptors so we need to
/// allow them to change this limit.
///
/// Note that if you set a limit bigger than the system limit, the system limit will be set.
///
/// Returns `true` if the new value has been set.
///
/// ```no_run
/// use sysinfo::{System, set_open_files_limit};
///
/// // We call the function before any call to the processes update.
/// if !set_open_files_limit(10) {
/// // It'll always return false on non-linux targets.
/// eprintln!("failed to update the open files limit...");
/// }
/// let s = System::new_all();
/// ```
pub fn set_open_files_limit(mut _new_limit: isize) -> bool {
cfg_if::cfg_if! {
if #[cfg(all(not(feature = "unknown-ci"), any(target_os = "linux", target_os = "android")))]
{
use crate::sys::system::REMAINING_FILES;
use std::sync::atomic::Ordering;
if _new_limit < 0 {
_new_limit = 0;
}
let max = sys::system::get_max_nb_fds();
if _new_limit > max {
_new_limit = max;
}
// If files are already open, to be sure that the number won't be bigger when those
// files are closed, we subtract the current number of opened files to the new
// limit.
REMAINING_FILES.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |remaining| {
let diff = max.saturating_sub(remaining);
Some(_new_limit.saturating_sub(diff))
}).unwrap();
true
} else {
false
}
}
}
// FIXME: Can be removed once negative trait bounds are supported.
#[cfg(doctest)]
mod doctest {
/// Check that `Process` doesn't implement `Clone`.
///
/// First we check that the "basic" code works:
///
/// ```no_run
/// use sysinfo::{Process, System};
///
/// let mut s = System::new_all();
/// let p: &Process = s.processes().values().next().unwrap();
/// ```
///
/// And now we check if it fails when we try to clone it:
///
/// ```compile_fail
/// use sysinfo::{Process, System};
///
/// let mut s = System::new_all();
/// let p: &Process = s.processes().values().next().unwrap();
/// let p = (*p).clone();
/// ```
mod process_clone {}
/// Check that `System` doesn't implement `Clone`.
///
/// First we check that the "basic" code works:
///
/// ```no_run
/// use sysinfo::{Process, System};
///
/// let s = System::new();
/// ```
///
/// And now we check if it fails when we try to clone it:
///
/// ```compile_fail
/// use sysinfo::{Process, System};
///
/// let s = System::new();
/// let s = s.clone();
/// ```
mod system_clone {}
}
#[cfg(test)]
mod test {
use crate::*;
#[cfg(feature = "unknown-ci")]
#[test]
fn check_unknown_ci_feature() {
assert!(!IS_SUPPORTED_SYSTEM);
}
// If this test doesn't compile, it means the current OS doesn't implement them correctly.
#[test]
fn check_macro_types() {
fn check_is_supported(_: bool) {}
fn check_supported_signals(_: &'static [Signal]) {}
fn check_minimum_cpu_update_interval(_: std::time::Duration) {}
check_is_supported(IS_SUPPORTED_SYSTEM);
check_supported_signals(SUPPORTED_SIGNALS);
check_minimum_cpu_update_interval(MINIMUM_CPU_UPDATE_INTERVAL);
}
#[test]
fn check_process_memory_usage() {
let mut s = System::new();
s.refresh_specifics(RefreshKind::everything());
if IS_SUPPORTED_SYSTEM {
// No process should have 0 as memory usage.
#[cfg(not(feature = "apple-sandbox"))]
assert!(!s.processes().iter().all(|(_, proc_)| proc_.memory() == 0));
} else {
// There should be no process, but if there is one, its memory usage should be 0.
assert!(s.processes().iter().all(|(_, proc_)| proc_.memory() == 0));
}
}
#[test]
fn check_system_implemented_traits() {
fn check<T: Sized + std::fmt::Debug + Default + Send + Sync>(_: T) {}
check(System::new());
}
#[test]
fn check_memory_usage() {
let mut s = System::new();
assert_eq!(s.total_memory(), 0);
assert_eq!(s.free_memory(), 0);
assert_eq!(s.available_memory(), 0);
assert_eq!(s.used_memory(), 0);
assert_eq!(s.total_swap(), 0);
assert_eq!(s.free_swap(), 0);
assert_eq!(s.used_swap(), 0);
s.refresh_memory();
if IS_SUPPORTED_SYSTEM {
assert!(s.total_memory() > 0);
assert!(s.used_memory() > 0);
if s.total_swap() > 0 {
// I think it's pretty safe to assume that there is still some swap left...
assert!(s.free_swap() > 0);
}
} else {
assert_eq!(s.total_memory(), 0);
assert_eq!(s.used_memory(), 0);
assert_eq!(s.total_swap(), 0);
assert_eq!(s.free_swap(), 0);
}
}
#[cfg(target_os = "linux")]
#[test]
fn check_processes_cpu_usage() {
if !IS_SUPPORTED_SYSTEM {
return;
}
let mut s = System::new();
s.refresh_processes();
// All CPU usage will start at zero until the second refresh
assert!(s
.processes()
.iter()
.all(|(_, proc_)| proc_.cpu_usage() == 0.0));
// Wait a bit to update CPU usage values
std::thread::sleep(MINIMUM_CPU_UPDATE_INTERVAL);
s.refresh_processes();
assert!(s
.processes()
.iter()
.all(|(_, proc_)| proc_.cpu_usage() >= 0.0
&& proc_.cpu_usage() <= (s.cpus().len() as f32) * 100.0));
assert!(s
.processes()
.iter()
.any(|(_, proc_)| proc_.cpu_usage() > 0.0));
}
#[test]
fn check_cpu_usage() {
if !IS_SUPPORTED_SYSTEM {
return;
}
let mut s = System::new();
for _ in 0..10 {
s.refresh_cpu_usage();
// Wait a bit to update CPU usage values
std::thread::sleep(MINIMUM_CPU_UPDATE_INTERVAL);
if s.cpus().iter().any(|c| c.cpu_usage() > 0.0) {
// All good!
return;
}
}
panic!("CPU usage is always zero...");
}
#[test]
fn check_list() {
let mut users = Users::new();
assert!(users.list().is_empty());
users.refresh_list();
assert!(users.list().len() >= MIN_USERS);
}
#[test]
fn check_uid_gid() {
let mut users = Users::new();
assert!(users.list().is_empty());
users.refresh_list();
let user_list = users.list();
assert!(user_list.len() >= MIN_USERS);
if IS_SUPPORTED_SYSTEM {
#[cfg(not(target_os = "windows"))]
{
let user = user_list
.iter()
.find(|u| u.name() == "root")
.expect("no root user");
assert_eq!(**user.id(), 0);
assert_eq!(*user.group_id(), 0);
if let Some(user) = users.iter().find(|u| *u.group_id() > 0) {
assert!(**user.id() > 0);
assert!(*user.group_id() > 0);
}
assert!(user_list.iter().filter(|u| **u.id() > 0).count() > 0);
}
// And now check that our `get_user_by_id` method works.
let s = System::new_with_specifics(
RefreshKind::new()
.with_processes(ProcessRefreshKind::new().with_user(UpdateKind::Always)),
);
assert!(s
.processes()
.iter()
.filter_map(|(_, p)| p.user_id())
.any(|uid| users.get_user_by_id(uid).is_some()));
}
}
#[test]
fn check_all_process_uids_resolvable() {
// On linux, some user IDs don't have an associated user (no idea why though).
// If `getent` doesn't find them, we can assume it's a dark secret from the linux land.
if IS_SUPPORTED_SYSTEM && cfg!(not(target_os = "linux")) {
let s = System::new_with_specifics(
RefreshKind::new()
.with_processes(ProcessRefreshKind::new().with_user(UpdateKind::Always)),
);
let users = Users::new_with_refreshed_list();
// For every process where we can get a user ID, we should also be able
// to find that user ID in the global user list
for process in s.processes().values() {
if let Some(uid) = process.user_id() {
assert!(
users.get_user_by_id(uid).is_some(),
"No UID {:?} found",
uid
);
}
}
}
}
#[test]
fn check_system_info() {
// We don't want to test on unsupported systems.
if IS_SUPPORTED_SYSTEM {
assert!(!System::name()
.expect("Failed to get system name")
.is_empty());
assert!(!System::kernel_version()
.expect("Failed to get kernel version")
.is_empty());
assert!(!System::os_version()
.expect("Failed to get os version")
.is_empty());
assert!(!System::long_os_version()
.expect("Failed to get long OS version")
.is_empty());
}
assert!(!System::distribution_id().is_empty());
}
#[test]
fn check_host_name() {
// We don't want to test on unsupported systems.
if IS_SUPPORTED_SYSTEM {
assert!(System::host_name().is_some());
}
}
#[test]
fn check_refresh_process_return_value() {
// We don't want to test on unsupported systems.
if IS_SUPPORTED_SYSTEM {
let _pid = get_current_pid().expect("Failed to get current PID");
#[cfg(not(feature = "apple-sandbox"))]
{
let mut s = System::new();
// First check what happens in case the process isn't already in our process list.
assert!(s.refresh_process(_pid));
// Then check that it still returns true if the process is already in our process list.
assert!(s.refresh_process(_pid));
}
}
}
#[test]
fn ensure_is_supported_is_set_correctly() {
if MIN_USERS > 0 {
assert!(IS_SUPPORTED_SYSTEM);
} else {
assert!(!IS_SUPPORTED_SYSTEM);
}
}
#[test]
fn check_cpus_number() {
let mut s = System::new();
// This information isn't retrieved by default.
assert!(s.cpus().is_empty());
if IS_SUPPORTED_SYSTEM {
// The physical cores count is recomputed every time the function is called, so the
// information must be relevant even with nothing initialized.
let physical_cores_count = s
.physical_core_count()
.expect("failed to get number of physical cores");
s.refresh_cpu_usage();
// The cpus shouldn't be empty anymore.
assert!(!s.cpus().is_empty());
// In case we are running inside a VM, it's possible to not have a physical core, only
// logical ones, which is why we don't test `physical_cores_count > 0`.
let physical_cores_count2 = s
.physical_core_count()
.expect("failed to get number of physical cores");
assert!(physical_cores_count2 <= s.cpus().len());
assert_eq!(physical_cores_count, physical_cores_count2);
} else {
assert_eq!(s.physical_core_count(), None);
}
assert!(s.physical_core_count().unwrap_or(0) <= s.cpus().len());
}
#[test]
fn check_nb_supported_signals() {
if IS_SUPPORTED_SYSTEM {
assert!(
!SUPPORTED_SIGNALS.is_empty(),
"SUPPORTED_SIGNALS shouldn't be empty on supported systems!"
);
} else {
assert!(
SUPPORTED_SIGNALS.is_empty(),
"SUPPORTED_SIGNALS should be empty on not support systems!"
);
}
}
// Ensure that the CPUs frequency isn't retrieved until we ask for it.
#[test]
fn check_cpu_frequency() {
if !IS_SUPPORTED_SYSTEM {
return;
}
let mut s = System::new();
s.refresh_processes();
for proc_ in s.cpus() {
assert_eq!(proc_.frequency(), 0);
}
s.refresh_cpu_usage();
for proc_ in s.cpus() {
assert_eq!(proc_.frequency(), 0);
}
// In a VM, it'll fail.
if std::env::var("APPLE_CI").is_err() && std::env::var("FREEBSD_CI").is_err() {
s.refresh_cpu_specifics(CpuRefreshKind::everything());
for proc_ in s.cpus() {
assert_ne!(proc_.frequency(), 0);
}
}
}
// In case `Process::updated` is misused, `System::refresh_processes` might remove them
// so this test ensures that it doesn't happen.
#[test]
fn check_refresh_process_update() {
if !IS_SUPPORTED_SYSTEM {
return;
}
let mut s = System::new_all();
let total = s.processes().len() as isize;
s.refresh_processes();
let new_total = s.processes().len() as isize;
// There should be almost no difference in the processes count.
assert!(
(new_total - total).abs() <= 5,
"{} <= 5",
(new_total - total).abs()
);
}
#[test]
fn check_cpu_arch() {
assert_eq!(System::cpu_arch().is_some(), IS_SUPPORTED_SYSTEM);
}
// This test only exists to ensure that the `Display` and `Debug` traits are implemented on the
// `ProcessStatus` enum on all targets.
#[test]
fn check_display_impl_process_status() {
println!("{} {:?}", ProcessStatus::Parked, ProcessStatus::Idle);
}
// Ensure that the `Display` and `Debug` traits are implemented on the `MacAddr` struct
#[test]
fn check_display_impl_mac_address() {
println!(
"{} {:?}",
MacAddr([0x1, 0x2, 0x3, 0x4, 0x5, 0x6]),
MacAddr([0xa, 0xb, 0xc, 0xd, 0xe, 0xf])
);
}
#[test]
fn check_mac_address_is_unspecified_true() {
assert!(MacAddr::UNSPECIFIED.is_unspecified());
assert!(MacAddr([0; 6]).is_unspecified());
}
#[test]
fn check_mac_address_is_unspecified_false() {
assert!(!MacAddr([1, 2, 3, 4, 5, 6]).is_unspecified());
}
// This test exists to ensure that the `TryFrom<usize>` and `FromStr` traits are implemented
// on `Uid`, `Gid` and `Pid`.
#[allow(clippy::unnecessary_fallible_conversions)]
#[test]
fn check_uid_gid_from_impls() {
use std::convert::TryFrom;
use std::str::FromStr;
#[cfg(not(windows))]
{
assert!(crate::Uid::try_from(0usize).is_ok());
assert!(crate::Uid::from_str("0").is_ok());
}
#[cfg(windows)]
{
assert!(crate::Uid::from_str("S-1-5-18").is_ok()); // SECURITY_LOCAL_SYSTEM_RID
assert!(crate::Uid::from_str("0").is_err());
}
assert!(crate::Gid::try_from(0usize).is_ok());
assert!(crate::Gid::from_str("0").is_ok());
assert!(crate::Pid::try_from(0usize).is_ok());
// If it doesn't panic, it's fine.
let _ = crate::Pid::from(0);
assert!(crate::Pid::from_str("0").is_ok());
}
}