1// Take a look at the license at the top of the repository in the LICENSE file.
2
3#![doc = include_str!("../README.md")]
4#![cfg_attr(feature = "serde", doc = include_str!("../md_doc/serde.md"))]
5#![allow(unknown_lints)]
6#![deny(missing_docs)]
7#![deny(rustdoc::broken_intra_doc_links)]
8#![allow(clippy::upper_case_acronyms)]
9#![allow(clippy::non_send_fields_in_send_ty)]
10#![allow(renamed_and_removed_lints)]
11#![allow(clippy::assertions_on_constants)]
12#![allow(unknown_lints)]
13
14#[macro_use]
15mod macros;
16
17cfg_if::cfg_if! {
18 if #[cfg(feature = "unknown-ci")] {
19 // This is used in CI to check that the build for unknown targets is compiling fine.
20 mod unknown;
21 use unknown as sys;
22
23 #[cfg(test)]
24 pub(crate) const MIN_USERS: usize = 0;
25 } else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
26 mod apple;
27 use apple as sys;
28 pub(crate) mod users;
29 mod network_helper_nix;
30 use network_helper_nix as network_helper;
31 mod network;
32
33 // This is needed because macos uses `int*` for `getgrouplist`...
34 pub(crate) type GroupId = libc::c_int;
35 pub(crate) use libc::__error as libc_errno;
36
37 #[cfg(test)]
38 pub(crate) const MIN_USERS: usize = 1;
39 } else if #[cfg(windows)] {
40 mod windows;
41 use windows as sys;
42 mod network_helper_win;
43 use network_helper_win as network_helper;
44 mod network;
45
46 #[cfg(test)]
47 pub(crate) const MIN_USERS: usize = 1;
48 } else if #[cfg(any(target_os = "linux", target_os = "android"))] {
49 mod linux;
50 use linux as sys;
51 pub(crate) mod users;
52 mod network_helper_nix;
53 use network_helper_nix as network_helper;
54 mod network;
55
56 // This is needed because macos uses `int*` for `getgrouplist`...
57 pub(crate) type GroupId = libc::gid_t;
58 #[cfg(target_os = "linux")]
59 pub(crate) use libc::__errno_location as libc_errno;
60 #[cfg(target_os = "android")]
61 pub(crate) use libc::__errno as libc_errno;
62
63 #[cfg(test)]
64 pub(crate) const MIN_USERS: usize = 1;
65 } else if #[cfg(target_os = "freebsd")] {
66 mod freebsd;
67 use freebsd as sys;
68 pub(crate) mod users;
69 mod network_helper_nix;
70 use network_helper_nix as network_helper;
71 mod network;
72
73 // This is needed because macos uses `int*` for `getgrouplist`...
74 pub(crate) type GroupId = libc::gid_t;
75 pub(crate) use libc::__error as libc_errno;
76
77 #[cfg(test)]
78 pub(crate) const MIN_USERS: usize = 1;
79 } else {
80 mod unknown;
81 use unknown as sys;
82
83 #[cfg(test)]
84 pub(crate) const MIN_USERS: usize = 0;
85 }
86}
87
88pub use common::{
89 get_current_pid, CpuRefreshKind, DiskKind, DiskUsage, Gid, LoadAvg, MacAddr, NetworksIter, Pid,
90 PidExt, ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, Uid, User,
91};
92pub use sys::{Component, Cpu, Disk, NetworkData, Networks, Process, System};
93pub use traits::{
94 ComponentExt, CpuExt, DiskExt, NetworkExt, NetworksExt, ProcessExt, SystemExt, UserExt,
95};
96
97#[cfg(feature = "c-interface")]
98pub use c_interface::*;
99
100#[cfg(feature = "c-interface")]
101mod c_interface;
102mod common;
103mod debug;
104#[cfg(feature = "serde")]
105mod serde;
106mod system;
107mod traits;
108mod utils;
109
110/// This function is only used on Linux targets, on the other platforms it does nothing and returns
111/// `false`.
112///
113/// On Linux, to improve performance, we keep a `/proc` file open for each process we index with
114/// a maximum number of files open equivalent to half of the system limit.
115///
116/// The problem is that some users might need all the available file descriptors so we need to
117/// allow them to change this limit.
118///
119/// Note that if you set a limit bigger than the system limit, the system limit will be set.
120///
121/// Returns `true` if the new value has been set.
122///
123/// ```no_run
124/// use sysinfo::{System, SystemExt, set_open_files_limit};
125///
126/// // We call the function before any call to the processes update.
127/// if !set_open_files_limit(10) {
128/// // It'll always return false on non-linux targets.
129/// eprintln!("failed to update the open files limit...");
130/// }
131/// let s = System::new_all();
132/// ```
133pub fn set_open_files_limit(mut _new_limit: isize) -> bool {
134 cfg_if::cfg_if! {
135 if #[cfg(all(not(feature = "unknown-ci"), any(target_os = "linux", target_os = "android")))]
136 {
137 if _new_limit < 0 {
138 _new_limit = 0;
139 }
140 let max = sys::system::get_max_nb_fds();
141 if _new_limit > max {
142 _new_limit = max;
143 }
144 unsafe {
145 if let Ok(ref mut x) = sys::system::REMAINING_FILES.lock() {
146 // If files are already open, to be sure that the number won't be bigger when those
147 // files are closed, we subtract the current number of opened files to the new
148 // limit.
149 let diff = max.saturating_sub(**x);
150 **x = _new_limit.saturating_sub(diff);
151 true
152 } else {
153 false
154 }
155 }
156 } else {
157 false
158 }
159 }
160}
161
162// FIXME: Can be removed once negative trait bounds are supported.
163#[cfg(doctest)]
164mod doctest {
165 /// Check that `Process` doesn't implement `Clone`.
166 ///
167 /// First we check that the "basic" code works:
168 ///
169 /// ```no_run
170 /// use sysinfo::{Process, System, SystemExt};
171 ///
172 /// let mut s = System::new_all();
173 /// let p: &Process = s.processes().values().next().unwrap();
174 /// ```
175 ///
176 /// And now we check if it fails when we try to clone it:
177 ///
178 /// ```compile_fail
179 /// use sysinfo::{Process, System, SystemExt};
180 ///
181 /// let mut s = System::new_all();
182 /// let p: &Process = s.processes().values().next().unwrap();
183 /// let p = (*p).clone();
184 /// ```
185 mod process_clone {}
186
187 /// Check that `System` doesn't implement `Clone`.
188 ///
189 /// First we check that the "basic" code works:
190 ///
191 /// ```no_run
192 /// use sysinfo::{Process, System, SystemExt};
193 ///
194 /// let s = System::new();
195 /// ```
196 ///
197 /// And now we check if it fails when we try to clone it:
198 ///
199 /// ```compile_fail
200 /// use sysinfo::{Process, System, SystemExt};
201 ///
202 /// let s = System::new();
203 /// let s = s.clone();
204 /// ```
205 mod system_clone {}
206}
207
208#[cfg(test)]
209mod test {
210 use crate::*;
211
212 #[cfg(feature = "unknown-ci")]
213 #[test]
214 fn check_unknown_ci_feature() {
215 assert!(!System::IS_SUPPORTED);
216 }
217
218 #[test]
219 fn check_process_memory_usage() {
220 let mut s = System::new();
221 s.refresh_all();
222
223 if System::IS_SUPPORTED {
224 // No process should have 0 as memory usage.
225 #[cfg(not(feature = "apple-sandbox"))]
226 assert!(!s.processes().iter().all(|(_, proc_)| proc_.memory() == 0));
227 } else {
228 // There should be no process, but if there is one, its memory usage should be 0.
229 assert!(s.processes().iter().all(|(_, proc_)| proc_.memory() == 0));
230 }
231 }
232
233 #[test]
234 fn check_memory_usage() {
235 let mut s = System::new();
236
237 assert_eq!(s.total_memory(), 0);
238 assert_eq!(s.free_memory(), 0);
239 assert_eq!(s.available_memory(), 0);
240 assert_eq!(s.used_memory(), 0);
241 assert_eq!(s.total_swap(), 0);
242 assert_eq!(s.free_swap(), 0);
243 assert_eq!(s.used_swap(), 0);
244
245 s.refresh_memory();
246 if System::IS_SUPPORTED {
247 assert!(s.total_memory() > 0);
248 assert!(s.used_memory() > 0);
249 if s.total_swap() > 0 {
250 // I think it's pretty safe to assume that there is still some swap left...
251 assert!(s.free_swap() > 0);
252 }
253 } else {
254 assert_eq!(s.total_memory(), 0);
255 assert_eq!(s.used_memory(), 0);
256 assert_eq!(s.total_swap(), 0);
257 assert_eq!(s.free_swap(), 0);
258 }
259 }
260
261 #[cfg(target_os = "linux")]
262 #[test]
263 fn check_processes_cpu_usage() {
264 if !System::IS_SUPPORTED {
265 return;
266 }
267 let mut s = System::new();
268
269 s.refresh_processes();
270 // All CPU usage will start at zero until the second refresh
271 assert!(s
272 .processes()
273 .iter()
274 .all(|(_, proc_)| proc_.cpu_usage() == 0.0));
275
276 // Wait a bit to update CPU usage values
277 std::thread::sleep(System::MINIMUM_CPU_UPDATE_INTERVAL);
278 s.refresh_processes();
279 assert!(s
280 .processes()
281 .iter()
282 .all(|(_, proc_)| proc_.cpu_usage() >= 0.0
283 && proc_.cpu_usage() <= (s.cpus().len() as f32) * 100.0));
284 assert!(s
285 .processes()
286 .iter()
287 .any(|(_, proc_)| proc_.cpu_usage() > 0.0));
288 }
289
290 #[test]
291 fn check_cpu_usage() {
292 if !System::IS_SUPPORTED {
293 return;
294 }
295 let mut s = System::new();
296 for _ in 0..10 {
297 s.refresh_cpu();
298 // Wait a bit to update CPU usage values
299 std::thread::sleep(System::MINIMUM_CPU_UPDATE_INTERVAL);
300 if s.cpus().iter().any(|c| c.cpu_usage() > 0.0) {
301 // All good!
302 return;
303 }
304 }
305 panic!("CPU usage is always zero...");
306 }
307
308 #[test]
309 fn check_users() {
310 let mut s = System::new();
311 assert!(s.users().is_empty());
312 s.refresh_users_list();
313 assert!(s.users().len() >= MIN_USERS);
314
315 let mut s = System::new();
316 assert!(s.users().is_empty());
317 s.refresh_all();
318 assert!(s.users().is_empty());
319
320 let s = System::new_all();
321 assert!(s.users().len() >= MIN_USERS);
322 }
323
324 #[test]
325 fn check_uid_gid() {
326 let mut s = System::new();
327 assert!(s.users().is_empty());
328 s.refresh_users_list();
329 let users = s.users();
330 assert!(users.len() >= MIN_USERS);
331
332 if System::IS_SUPPORTED {
333 #[cfg(not(target_os = "windows"))]
334 {
335 let user = users
336 .iter()
337 .find(|u| u.name() == "root")
338 .expect("no root user");
339 assert_eq!(**user.id(), 0);
340 assert_eq!(*user.group_id(), 0);
341 if let Some(user) = users.iter().find(|u| *u.group_id() > 0) {
342 assert!(**user.id() > 0);
343 assert!(*user.group_id() > 0);
344 }
345 assert!(users.iter().filter(|u| **u.id() > 0).count() > 0);
346 }
347
348 // And now check that our `get_user_by_id` method works.
349 s.refresh_processes();
350 assert!(s
351 .processes()
352 .iter()
353 .filter_map(|(_, p)| p.user_id())
354 .any(|uid| s.get_user_by_id(uid).is_some()));
355 }
356 }
357
358 #[test]
359 fn check_all_process_uids_resolvable() {
360 if System::IS_SUPPORTED {
361 let s = System::new_with_specifics(
362 RefreshKind::new()
363 .with_processes(ProcessRefreshKind::new().with_user())
364 .with_users_list(),
365 );
366
367 // For every process where we can get a user ID, we should also be able
368 // to find that user ID in the global user list
369 for process in s.processes().values() {
370 if let Some(uid) = process.user_id() {
371 assert!(s.get_user_by_id(uid).is_some(), "No UID {:?} found", uid);
372 }
373 }
374 }
375 }
376
377 #[test]
378 fn check_system_info() {
379 let s = System::new();
380
381 // We don't want to test on unsupported systems.
382 if System::IS_SUPPORTED {
383 assert!(!s.name().expect("Failed to get system name").is_empty());
384
385 assert!(!s
386 .kernel_version()
387 .expect("Failed to get kernel version")
388 .is_empty());
389
390 assert!(!s.os_version().expect("Failed to get os version").is_empty());
391
392 assert!(!s
393 .long_os_version()
394 .expect("Failed to get long OS version")
395 .is_empty());
396 }
397
398 assert!(!s.distribution_id().is_empty());
399 }
400
401 #[test]
402 fn check_host_name() {
403 // We don't want to test on unsupported systems.
404 if System::IS_SUPPORTED {
405 let s = System::new();
406 assert!(s.host_name().is_some());
407 }
408 }
409
410 #[test]
411 fn check_refresh_process_return_value() {
412 // We don't want to test on unsupported systems.
413 if System::IS_SUPPORTED {
414 let _pid = get_current_pid().expect("Failed to get current PID");
415
416 #[cfg(not(feature = "apple-sandbox"))]
417 {
418 let mut s = System::new();
419 // First check what happens in case the process isn't already in our process list.
420 assert!(s.refresh_process(_pid));
421 // Then check that it still returns true if the process is already in our process list.
422 assert!(s.refresh_process(_pid));
423 }
424 }
425 }
426
427 #[test]
428 fn ensure_is_supported_is_set_correctly() {
429 if MIN_USERS > 0 {
430 assert!(System::IS_SUPPORTED);
431 } else {
432 assert!(!System::IS_SUPPORTED);
433 }
434 }
435
436 #[test]
437 fn check_cpus_number() {
438 let mut s = System::new();
439
440 // This information isn't retrieved by default.
441 assert!(s.cpus().is_empty());
442 if System::IS_SUPPORTED {
443 // The physical cores count is recomputed every time the function is called, so the
444 // information must be relevant even with nothing initialized.
445 let physical_cores_count = s
446 .physical_core_count()
447 .expect("failed to get number of physical cores");
448
449 s.refresh_cpu();
450 // The cpus shouldn't be empty anymore.
451 assert!(!s.cpus().is_empty());
452
453 // In case we are running inside a VM, it's possible to not have a physical core, only
454 // logical ones, which is why we don't test `physical_cores_count > 0`.
455 let physical_cores_count2 = s
456 .physical_core_count()
457 .expect("failed to get number of physical cores");
458 assert!(physical_cores_count2 <= s.cpus().len());
459 assert_eq!(physical_cores_count, physical_cores_count2);
460 } else {
461 assert_eq!(s.physical_core_count(), None);
462 }
463 assert!(s.physical_core_count().unwrap_or(0) <= s.cpus().len());
464 }
465
466 #[test]
467 fn check_nb_supported_signals() {
468 if System::IS_SUPPORTED {
469 assert!(
470 !System::SUPPORTED_SIGNALS.is_empty(),
471 "SUPPORTED_SIGNALS shoudn't be empty on supported systems!"
472 );
473 } else {
474 assert!(
475 System::SUPPORTED_SIGNALS.is_empty(),
476 "SUPPORTED_SIGNALS should be empty on not support systems!"
477 );
478 }
479 }
480
481 // Ensure that the CPUs frequency isn't retrieved until we ask for it.
482 #[test]
483 fn check_cpu_frequency() {
484 if !System::IS_SUPPORTED {
485 return;
486 }
487 let mut s = System::new();
488 s.refresh_processes();
489 for proc_ in s.cpus() {
490 assert_eq!(proc_.frequency(), 0);
491 }
492 s.refresh_cpu();
493 for proc_ in s.cpus() {
494 assert_eq!(proc_.frequency(), 0);
495 }
496 // In a VM, it'll fail.
497 if std::env::var("APPLE_CI").is_err() && std::env::var("FREEBSD_CI").is_err() {
498 s.refresh_cpu_specifics(CpuRefreshKind::everything());
499 for proc_ in s.cpus() {
500 assert_ne!(proc_.frequency(), 0);
501 }
502 }
503 }
504
505 // In case `Process::updated` is misused, `System::refresh_processes` might remove them
506 // so this test ensures that it doesn't happen.
507 #[test]
508 fn check_refresh_process_update() {
509 if !System::IS_SUPPORTED {
510 return;
511 }
512 let mut s = System::new_all();
513 let total = s.processes().len() as isize;
514 s.refresh_processes();
515 let new_total = s.processes().len() as isize;
516 // There should be almost no difference in the processes count.
517 assert!(
518 (new_total - total).abs() <= 5,
519 "{} <= 5",
520 (new_total - total).abs()
521 );
522 }
523
524 // We ensure that the `Process` cmd information is retrieved as expected.
525 #[test]
526 fn check_cmd_line() {
527 if !System::IS_SUPPORTED {
528 return;
529 }
530 let mut sys = System::new();
531 sys.refresh_processes_specifics(ProcessRefreshKind::new());
532
533 assert!(sys
534 .processes()
535 .iter()
536 .any(|(_, process)| !process.cmd().is_empty()));
537 }
538}
539