| 1 | // Take a look at the license at the top of the repository in the LICENSE file. | 
| 2 |  | 
|---|
| 3 | #![ cfg_attr( | 
|---|
| 4 | all(feature = "system", feature = "disk", feature = "component", feature = "system"), | 
|---|
| 5 | doc = include_str!( "../README.md") | 
|---|
| 6 | )] | 
|---|
| 7 | #![ cfg_attr( | 
|---|
| 8 | not(all( | 
|---|
| 9 | feature = "system", | 
|---|
| 10 | feature = "disk", | 
|---|
| 11 | feature = "component", | 
|---|
| 12 | feature = "system" | 
|---|
| 13 | )), | 
|---|
| 14 | doc = "For crate-level documentation, all features need to be enabled." | 
|---|
| 15 | )] | 
|---|
| 16 | #![ cfg_attr(feature = "serde", doc = include_str!( "../md_doc/serde.md"))] | 
|---|
| 17 | #![ allow(unknown_lints)] | 
|---|
| 18 | #![ deny(missing_docs)] | 
|---|
| 19 | #![ deny(rustdoc::broken_intra_doc_links)] | 
|---|
| 20 | #![ allow(clippy::upper_case_acronyms)] | 
|---|
| 21 | #![ allow(clippy::non_send_fields_in_send_ty)] | 
|---|
| 22 | #![ allow(renamed_and_removed_lints)] | 
|---|
| 23 | #![ allow(clippy::assertions_on_constants)] | 
|---|
| 24 |  | 
|---|
| 25 | #[ macro_use] | 
|---|
| 26 | mod macros; | 
|---|
| 27 |  | 
|---|
| 28 | cfg_if! { | 
|---|
| 29 | if #[cfg(feature = "unknown-ci")] { | 
|---|
| 30 | // This is used in CI to check that the build for unknown targets is compiling fine. | 
|---|
| 31 | mod unknown; | 
|---|
| 32 | use crate::unknown as sys; | 
|---|
| 33 |  | 
|---|
| 34 | #[cfg(test)] | 
|---|
| 35 | pub(crate) const MIN_USERS: usize = 0; | 
|---|
| 36 | } else if #[cfg(any( | 
|---|
| 37 | target_os = "macos", target_os = "ios", | 
|---|
| 38 | target_os = "linux", target_os = "android", | 
|---|
| 39 | target_os = "freebsd"))] | 
|---|
| 40 | { | 
|---|
| 41 | mod unix; | 
|---|
| 42 | use crate::unix::sys as sys; | 
|---|
| 43 |  | 
|---|
| 44 | #[ cfg(feature = "network")] | 
|---|
| 45 | mod network; | 
|---|
| 46 | #[ cfg(feature = "network")] | 
|---|
| 47 | use crate::unix::network_helper; | 
|---|
| 48 |  | 
|---|
| 49 | #[ cfg(test)] | 
|---|
| 50 | pub(crate) const MIN_USERS: usize = 1; | 
|---|
| 51 | } else if #[cfg(windows)] { | 
|---|
| 52 | mod windows; | 
|---|
| 53 | use crate::windows as sys; | 
|---|
| 54 |  | 
|---|
| 55 | #[cfg(feature = "network")] | 
|---|
| 56 | mod network; | 
|---|
| 57 | #[cfg(feature = "network")] | 
|---|
| 58 | use crate::windows::network_helper; | 
|---|
| 59 |  | 
|---|
| 60 | #[cfg(test)] | 
|---|
| 61 | pub(crate) const MIN_USERS: usize = 1; | 
|---|
| 62 | } else { | 
|---|
| 63 | mod unknown; | 
|---|
| 64 | use crate::unknown as sys; | 
|---|
| 65 |  | 
|---|
| 66 | #[cfg(test)] | 
|---|
| 67 | pub(crate) const MIN_USERS: usize = 0; | 
|---|
| 68 | } | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | #[ cfg(feature = "component")] | 
|---|
| 72 | pub use crate::common::component::{Component, Components}; | 
|---|
| 73 | #[ cfg(feature = "disk")] | 
|---|
| 74 | pub use crate::common::disk::{Disk, DiskKind, Disks}; | 
|---|
| 75 | #[ cfg(feature = "network")] | 
|---|
| 76 | pub use crate::common::network::{IpNetwork, MacAddr, NetworkData, Networks}; | 
|---|
| 77 | #[ cfg(feature = "system")] | 
|---|
| 78 | pub use crate::common::system::{ | 
|---|
| 79 | get_current_pid, CGroupLimits, Cpu, CpuRefreshKind, DiskUsage, LoadAvg, MemoryRefreshKind, Pid, | 
|---|
| 80 | Process, ProcessRefreshKind, ProcessStatus, ProcessesToUpdate, RefreshKind, Signal, System, | 
|---|
| 81 | ThreadKind, UpdateKind, | 
|---|
| 82 | }; | 
|---|
| 83 | #[ cfg(feature = "user")] | 
|---|
| 84 | pub use crate::common::user::{Group, Groups, User, Users}; | 
|---|
| 85 | #[ cfg(any(feature = "user", feature = "system"))] | 
|---|
| 86 | pub use crate::common::{Gid, Uid}; | 
|---|
| 87 | #[ cfg(feature = "system")] | 
|---|
| 88 | pub use crate::sys::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS}; | 
|---|
| 89 |  | 
|---|
| 90 | #[ cfg(feature = "user")] | 
|---|
| 91 | pub(crate) use crate::common::user::GroupInner; | 
|---|
| 92 | #[ cfg(feature = "user")] | 
|---|
| 93 | pub(crate) use crate::sys::UserInner; | 
|---|
| 94 | #[ cfg(feature = "component")] | 
|---|
| 95 | pub(crate) use crate::sys::{ComponentInner, ComponentsInner}; | 
|---|
| 96 | #[ cfg(feature = "system")] | 
|---|
| 97 | pub(crate) use crate::sys::{CpuInner, ProcessInner, SystemInner}; | 
|---|
| 98 | #[ cfg(feature = "disk")] | 
|---|
| 99 | pub(crate) use crate::sys::{DiskInner, DisksInner}; | 
|---|
| 100 | #[ cfg(feature = "network")] | 
|---|
| 101 | pub(crate) use crate::sys::{NetworkDataInner, NetworksInner}; | 
|---|
| 102 |  | 
|---|
| 103 | pub use crate::sys::IS_SUPPORTED_SYSTEM; | 
|---|
| 104 |  | 
|---|
| 105 | #[ cfg(feature = "c-interface")] | 
|---|
| 106 | pub use crate::c_interface::*; | 
|---|
| 107 |  | 
|---|
| 108 | #[ cfg(feature = "c-interface")] | 
|---|
| 109 | mod c_interface; | 
|---|
| 110 | mod common; | 
|---|
| 111 | mod debug; | 
|---|
| 112 | #[ cfg(feature = "serde")] | 
|---|
| 113 | mod serde; | 
|---|
| 114 | pub(crate) mod utils; | 
|---|
| 115 |  | 
|---|
| 116 | /// This function is only used on Linux targets, when the `system` feature is enabled. In other | 
|---|
| 117 | /// cases, it does nothing and returns `false`. | 
|---|
| 118 | /// | 
|---|
| 119 | /// On Linux, to improve performance, we keep a `/proc` file open for each process we index with | 
|---|
| 120 | /// a maximum number of files open equivalent to half of the system limit. | 
|---|
| 121 | /// | 
|---|
| 122 | /// The problem is that some users might need all the available file descriptors so we need to | 
|---|
| 123 | /// allow them to change this limit. | 
|---|
| 124 | /// | 
|---|
| 125 | /// Note that if you set a limit bigger than the system limit, the system limit will be set. | 
|---|
| 126 | /// | 
|---|
| 127 | /// Returns `true` if the new value has been set. | 
|---|
| 128 | /// | 
|---|
| 129 | #[ cfg_attr(feature = "system", doc = "```no_run")] | 
|---|
| 130 | #[ cfg_attr(not(feature = "system"), doc = "```ignore")] | 
|---|
| 131 | /// use sysinfo::{System, set_open_files_limit}; | 
|---|
| 132 | /// | 
|---|
| 133 | /// // We call the function before any call to the processes update. | 
|---|
| 134 | /// if !set_open_files_limit(10) { | 
|---|
| 135 | ///     // It'll always return false on non-linux targets. | 
|---|
| 136 | ///     eprintln!( "failed to update the open files limit..."); | 
|---|
| 137 | /// } | 
|---|
| 138 | /// let s = System::new_all(); | 
|---|
| 139 | /// ``` | 
|---|
| 140 | pub fn set_open_files_limit(mut _new_limit: isize) -> bool { | 
|---|
| 141 | cfg_if! { | 
|---|
| 142 | if #[cfg(all(feature = "system", not(feature = "unknown-ci"), any(target_os = "linux", target_os = "android")))] | 
|---|
| 143 | { | 
|---|
| 144 | use crate::sys::system::remaining_files; | 
|---|
| 145 | use std::sync::atomic::Ordering; | 
|---|
| 146 |  | 
|---|
| 147 | if _new_limit < 0 { | 
|---|
| 148 | _new_limit = 0; | 
|---|
| 149 | } | 
|---|
| 150 | let max = sys::system::get_max_nb_fds(); | 
|---|
| 151 | if _new_limit > max { | 
|---|
| 152 | _new_limit = max; | 
|---|
| 153 | } | 
|---|
| 154 |  | 
|---|
| 155 | // If files are already open, to be sure that the number won't be bigger when those | 
|---|
| 156 | // files are closed, we subtract the current number of opened files to the new | 
|---|
| 157 | // limit. | 
|---|
| 158 | remaining_files().fetch_update(Ordering::SeqCst, Ordering::SeqCst, |remaining| { | 
|---|
| 159 | let diff = max.saturating_sub(remaining); | 
|---|
| 160 | Some(_new_limit.saturating_sub(diff)) | 
|---|
| 161 | }).unwrap(); | 
|---|
| 162 |  | 
|---|
| 163 | true | 
|---|
| 164 | } else { | 
|---|
| 165 | false | 
|---|
| 166 | } | 
|---|
| 167 | } | 
|---|
| 168 | } | 
|---|
| 169 |  | 
|---|
| 170 | #[ cfg(doctest)] | 
|---|
| 171 | mod doctest { | 
|---|
| 172 | macro_rules! compile_fail_import { | 
|---|
| 173 | ($mod_name:ident => $($imports:ident),+ $(,)?) => { | 
|---|
| 174 | $(#[doc = concat!( r"```compile_fail | 
|---|
| 175 | use sysinfo::", stringify!($imports), r"; | 
|---|
| 176 | ``` | 
|---|
| 177 | ")])+ | 
|---|
| 178 | mod $mod_name {} | 
|---|
| 179 | }; | 
|---|
| 180 | } | 
|---|
| 181 |  | 
|---|
| 182 | #[ cfg(not(feature = "system"))] | 
|---|
| 183 | compile_fail_import!( | 
|---|
| 184 | no_system_feature => | 
|---|
| 185 | get_current_pid, | 
|---|
| 186 | CGroupLimits, | 
|---|
| 187 | Cpu, | 
|---|
| 188 | CpuRefreshKind, | 
|---|
| 189 | DiskUsage, | 
|---|
| 190 | LoadAvg, | 
|---|
| 191 | MemoryRefreshKind, | 
|---|
| 192 | Pid, | 
|---|
| 193 | Process, | 
|---|
| 194 | ProcessesToUpdate, | 
|---|
| 195 | ProcessRefreshKind, | 
|---|
| 196 | ProcessStatus, | 
|---|
| 197 | RefreshKind, | 
|---|
| 198 | Signal, | 
|---|
| 199 | System, | 
|---|
| 200 | ThreadKind, | 
|---|
| 201 | UpdateKind, | 
|---|
| 202 | ); | 
|---|
| 203 |  | 
|---|
| 204 | #[ cfg(not(feature = "disk"))] | 
|---|
| 205 | compile_fail_import!( | 
|---|
| 206 | no_disk_feature => | 
|---|
| 207 | Disk, | 
|---|
| 208 | Disks, | 
|---|
| 209 | DiskKind, | 
|---|
| 210 | ); | 
|---|
| 211 |  | 
|---|
| 212 | #[ cfg(not(feature = "component"))] | 
|---|
| 213 | compile_fail_import!( | 
|---|
| 214 | no_component_feature => | 
|---|
| 215 | Component, | 
|---|
| 216 | Components, | 
|---|
| 217 | ); | 
|---|
| 218 |  | 
|---|
| 219 | #[ cfg(not(feature = "network"))] | 
|---|
| 220 | compile_fail_import!( | 
|---|
| 221 | no_network_feature => | 
|---|
| 222 | IpNetwork, | 
|---|
| 223 | MacAddr, | 
|---|
| 224 | NetworkData, | 
|---|
| 225 | Networks, | 
|---|
| 226 | ); | 
|---|
| 227 |  | 
|---|
| 228 | #[ cfg(not(feature = "user"))] | 
|---|
| 229 | compile_fail_import!( | 
|---|
| 230 | no_user_feature => | 
|---|
| 231 | Group, | 
|---|
| 232 | Groups, | 
|---|
| 233 | User, | 
|---|
| 234 | Users, | 
|---|
| 235 | ); | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 | #[ cfg(test)] | 
|---|
| 239 | mod test { | 
|---|
| 240 | use crate::*; | 
|---|
| 241 |  | 
|---|
| 242 | #[ cfg(feature = "unknown-ci")] | 
|---|
| 243 | #[ test] | 
|---|
| 244 | fn check_unknown_ci_feature() { | 
|---|
| 245 | assert!(!IS_SUPPORTED_SYSTEM); | 
|---|
| 246 | } | 
|---|
| 247 |  | 
|---|
| 248 | // If this test doesn't compile, it means the current OS doesn't implement them correctly. | 
|---|
| 249 | #[ test] | 
|---|
| 250 | fn check_macro_types() { | 
|---|
| 251 | fn check_is_supported(_: bool) {} | 
|---|
| 252 |  | 
|---|
| 253 | check_is_supported(IS_SUPPORTED_SYSTEM); | 
|---|
| 254 | } | 
|---|
| 255 |  | 
|---|
| 256 | // If this test doesn't compile, it means the current OS doesn't implement them correctly. | 
|---|
| 257 | #[ cfg(feature = "system")] | 
|---|
| 258 | #[ test] | 
|---|
| 259 | fn check_macro_types2() { | 
|---|
| 260 | fn check_supported_signals(_: &'static [Signal]) {} | 
|---|
| 261 | fn check_minimum_cpu_update_interval(_: std::time::Duration) {} | 
|---|
| 262 |  | 
|---|
| 263 | check_supported_signals(SUPPORTED_SIGNALS); | 
|---|
| 264 | check_minimum_cpu_update_interval(MINIMUM_CPU_UPDATE_INTERVAL); | 
|---|
| 265 | } | 
|---|
| 266 |  | 
|---|
| 267 | #[ cfg(feature = "user")] | 
|---|
| 268 | #[ test] | 
|---|
| 269 | fn check_uid_gid() { | 
|---|
| 270 | let mut users = Users::new(); | 
|---|
| 271 | assert!(users.list().is_empty()); | 
|---|
| 272 | users.refresh_list(); | 
|---|
| 273 | let user_list = users.list(); | 
|---|
| 274 | assert!(user_list.len() >= MIN_USERS); | 
|---|
| 275 |  | 
|---|
| 276 | if IS_SUPPORTED_SYSTEM { | 
|---|
| 277 | #[ cfg(not(target_os = "windows"))] | 
|---|
| 278 | { | 
|---|
| 279 | let user = user_list | 
|---|
| 280 | .iter() | 
|---|
| 281 | .find(|u| u.name() == "root") | 
|---|
| 282 | .expect( "no root user"); | 
|---|
| 283 | assert_eq!(**user.id(), 0); | 
|---|
| 284 | assert_eq!(*user.group_id(), 0); | 
|---|
| 285 | if let Some(user) = users.iter().find(|u| *u.group_id() > 0) { | 
|---|
| 286 | assert!(**user.id() > 0); | 
|---|
| 287 | assert!(*user.group_id() > 0); | 
|---|
| 288 | } | 
|---|
| 289 | assert!(user_list.iter().filter(|u| **u.id() > 0).count() > 0); | 
|---|
| 290 | } | 
|---|
| 291 |  | 
|---|
| 292 | #[ cfg(feature = "system")] | 
|---|
| 293 | { | 
|---|
| 294 | // And now check that our `get_user_by_id` method works. | 
|---|
| 295 | let s = System::new_with_specifics( | 
|---|
| 296 | RefreshKind::new() | 
|---|
| 297 | .with_processes(ProcessRefreshKind::new().with_user(UpdateKind::Always)), | 
|---|
| 298 | ); | 
|---|
| 299 | assert!(s | 
|---|
| 300 | .processes() | 
|---|
| 301 | .iter() | 
|---|
| 302 | .filter_map(|(_, p)| p.user_id()) | 
|---|
| 303 | .any(|uid| users.get_user_by_id(uid).is_some())); | 
|---|
| 304 | } | 
|---|
| 305 | } | 
|---|
| 306 | } | 
|---|
| 307 |  | 
|---|
| 308 | #[ cfg(feature = "system")] | 
|---|
| 309 | #[ test] | 
|---|
| 310 | fn check_all_process_uids_resolvable() { | 
|---|
| 311 | // On linux, some user IDs don't have an associated user (no idea why though). | 
|---|
| 312 | // If `getent` doesn't find them, we can assume it's a dark secret from the linux land. | 
|---|
| 313 | if IS_SUPPORTED_SYSTEM && cfg!(not(target_os = "linux")) { | 
|---|
| 314 | let s = System::new_with_specifics( | 
|---|
| 315 | RefreshKind::new() | 
|---|
| 316 | .with_processes(ProcessRefreshKind::new().with_user(UpdateKind::Always)), | 
|---|
| 317 | ); | 
|---|
| 318 | let users = Users::new_with_refreshed_list(); | 
|---|
| 319 |  | 
|---|
| 320 | // For every process where we can get a user ID, we should also be able | 
|---|
| 321 | // to find that user ID in the global user list | 
|---|
| 322 | for process in s.processes().values() { | 
|---|
| 323 | if let Some(uid) = process.user_id() { | 
|---|
| 324 | assert!( | 
|---|
| 325 | users.get_user_by_id(uid).is_some(), | 
|---|
| 326 | "No UID {:?} found", | 
|---|
| 327 | uid | 
|---|
| 328 | ); | 
|---|
| 329 | } | 
|---|
| 330 | } | 
|---|
| 331 | } | 
|---|
| 332 | } | 
|---|
| 333 |  | 
|---|
| 334 | #[ test] | 
|---|
| 335 | fn ensure_is_supported_is_set_correctly() { | 
|---|
| 336 | if MIN_USERS > 0 { | 
|---|
| 337 | assert!(IS_SUPPORTED_SYSTEM); | 
|---|
| 338 | } else { | 
|---|
| 339 | assert!(!IS_SUPPORTED_SYSTEM); | 
|---|
| 340 | } | 
|---|
| 341 | } | 
|---|
| 342 | } | 
|---|
| 343 |  | 
|---|