sysinfo/
lib.rs

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