sysinfo/
lib.rs

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]
26mod macros;
27
28cfg_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")]
72pub use crate::common::component::{Component, Components};
73#[cfg(feature = "disk")]
74pub use crate::common::disk::{Disk, DiskKind, DiskRefreshKind, Disks};
75#[cfg(feature = "network")]
76pub use crate::common::network::{
77    IpNetwork, IpNetworkFromStrError, MacAddr, MacAddrFromStrError, NetworkData, Networks,
78};
79#[cfg(feature = "system")]
80pub use crate::common::system::{
81    CGroupLimits, Cpu, CpuRefreshKind, KillError, LoadAvg, MemoryRefreshKind, Motherboard, Pid,
82    Process, ProcessRefreshKind, ProcessStatus, ProcessesToUpdate, Product, RefreshKind, Signal,
83    System, ThreadKind, UpdateKind, get_current_pid,
84};
85#[cfg(feature = "user")]
86pub use crate::common::user::{Group, Groups, User, Users};
87#[cfg(any(feature = "user", feature = "system"))]
88pub use crate::common::{Gid, Uid};
89#[cfg(feature = "system")]
90pub use crate::sys::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
91
92#[cfg(any(feature = "system", feature = "disk"))]
93pub use crate::common::DiskUsage;
94
95#[cfg(feature = "user")]
96pub(crate) use crate::common::user::GroupInner;
97#[cfg(feature = "user")]
98pub(crate) use crate::sys::UserInner;
99#[cfg(feature = "component")]
100pub(crate) use crate::sys::{ComponentInner, ComponentsInner};
101#[cfg(feature = "system")]
102pub(crate) use crate::sys::{CpuInner, MotherboardInner, ProcessInner, ProductInner, SystemInner};
103#[cfg(feature = "disk")]
104pub(crate) use crate::sys::{DiskInner, DisksInner};
105#[cfg(feature = "network")]
106pub(crate) use crate::sys::{NetworkDataInner, NetworksInner};
107
108pub use crate::sys::IS_SUPPORTED_SYSTEM;
109
110#[cfg(feature = "c-interface")]
111pub use crate::c_interface::*;
112
113#[cfg(feature = "c-interface")]
114mod c_interface;
115mod common;
116mod debug;
117#[cfg(feature = "serde")]
118mod serde;
119pub(crate) mod utils;
120
121// Make formattable by rustfmt.
122#[cfg(any())]
123mod network;
124#[cfg(any())]
125mod unix;
126#[cfg(any())]
127mod unknown;
128#[cfg(any())]
129mod windows;
130
131/// This function is only used on Linux targets, when the `system` feature is enabled. In other
132/// cases, it does nothing and returns `false`.
133///
134/// On Linux, to improve performance, we keep a `/proc` file open for each process we index with
135/// a maximum number of files open equivalent to half of the system limit.
136///
137/// The problem is that some users might need all the available file descriptors so we need to
138/// allow them to change this limit.
139///
140/// Note that if you set a limit bigger than the system limit, the system limit will be set.
141///
142/// Returns `true` if the new value has been set.
143///
144#[cfg_attr(feature = "system", doc = "```no_run")]
145#[cfg_attr(not(feature = "system"), doc = "```ignore")]
146/// use sysinfo::{System, set_open_files_limit};
147///
148/// // We call the function before any call to the processes update.
149/// if !set_open_files_limit(10) {
150///     // It'll always return false on non-linux targets.
151///     eprintln!("failed to update the open files limit...");
152/// }
153/// let s = System::new_all();
154/// ```
155pub fn set_open_files_limit(mut _new_limit: usize) -> bool {
156    cfg_if! {
157        if #[cfg(all(feature = "system", not(feature = "unknown-ci"), any(target_os = "linux", target_os = "android")))]
158        {
159            use crate::sys::system::remaining_files;
160            use std::sync::atomic::Ordering;
161
162            let max = sys::system::get_max_nb_fds();
163            if _new_limit > max {
164                _new_limit = max;
165            }
166
167            // If files are already open, to be sure that the number won't be bigger when those
168            // files are closed, we subtract the current number of opened files to the new
169            // limit.
170            remaining_files().fetch_update(Ordering::SeqCst, Ordering::SeqCst, |remaining| {
171                let _new_limit = _new_limit as isize;
172                let diff = (max as isize).saturating_sub(remaining);
173                Some(_new_limit.saturating_sub(diff))
174            }).unwrap();
175
176            true
177        } else {
178            false
179        }
180    }
181}
182
183#[cfg(doctest)]
184mod doctest {
185    macro_rules! compile_fail_import {
186        ($mod_name:ident => $($imports:ident),+ $(,)?) => {
187            $(#[doc = concat!(r"```compile_fail
188use sysinfo::", stringify!($imports), r";
189```
190")])+
191            mod $mod_name {}
192        };
193    }
194
195    #[cfg(not(feature = "system"))]
196    compile_fail_import!(
197        no_system_feature =>
198        get_current_pid,
199        CGroupLimits,
200        Cpu,
201        CpuRefreshKind,
202        DiskUsage,
203        KillError,
204        LoadAvg,
205        MemoryRefreshKind,
206        Motherboard,
207        Pid,
208        Process,
209        ProcessesToUpdate,
210        ProcessRefreshKind,
211        ProcessStatus,
212        Product,
213        RefreshKind,
214        Signal,
215        System,
216        ThreadKind,
217        UpdateKind,
218    );
219
220    #[cfg(not(feature = "disk"))]
221    compile_fail_import!(
222        no_disk_feature =>
223        Disk,
224        Disks,
225        DiskKind,
226    );
227
228    #[cfg(not(feature = "component"))]
229    compile_fail_import!(
230        no_component_feature =>
231        Component,
232        Components,
233    );
234
235    #[cfg(not(feature = "network"))]
236    compile_fail_import!(
237        no_network_feature =>
238        IpNetwork,
239        MacAddr,
240        NetworkData,
241        Networks,
242    );
243
244    #[cfg(not(feature = "user"))]
245    compile_fail_import!(
246        no_user_feature =>
247        Group,
248        Groups,
249        User,
250        Users,
251    );
252}
253
254#[cfg(test)]
255mod test {
256    use crate::*;
257
258    #[cfg(feature = "unknown-ci")]
259    #[test]
260    fn check_unknown_ci_feature() {
261        assert!(!IS_SUPPORTED_SYSTEM);
262    }
263
264    // If this test doesn't compile, it means the current OS doesn't implement them correctly.
265    #[test]
266    fn check_macro_types() {
267        fn check_is_supported(_: bool) {}
268
269        check_is_supported(IS_SUPPORTED_SYSTEM);
270    }
271
272    // If this test doesn't compile, it means the current OS doesn't implement them correctly.
273    #[cfg(feature = "system")]
274    #[test]
275    fn check_macro_types2() {
276        fn check_supported_signals(_: &'static [Signal]) {}
277        fn check_minimum_cpu_update_interval(_: std::time::Duration) {}
278
279        check_supported_signals(SUPPORTED_SIGNALS);
280        check_minimum_cpu_update_interval(MINIMUM_CPU_UPDATE_INTERVAL);
281    }
282
283    #[cfg(feature = "user")]
284    #[test]
285    fn check_uid_gid() {
286        let mut users = Users::new();
287        assert!(users.list().is_empty());
288        users.refresh();
289        let user_list = users.list();
290        assert!(user_list.len() >= MIN_USERS);
291
292        if IS_SUPPORTED_SYSTEM {
293            #[cfg(not(target_os = "windows"))]
294            {
295                let user = user_list
296                    .iter()
297                    .find(|u| u.name() == "root")
298                    .expect("no root user");
299                assert_eq!(**user.id(), 0);
300                assert_eq!(*user.group_id(), 0);
301                if let Some(user) = users.iter().find(|u| *u.group_id() > 0) {
302                    assert!(**user.id() > 0);
303                    assert!(*user.group_id() > 0);
304                }
305                assert!(user_list.iter().filter(|u| **u.id() > 0).count() > 0);
306            }
307
308            #[cfg(feature = "system")]
309            {
310                // And now check that our `get_user_by_id` method works.
311                let s =
312                    System::new_with_specifics(RefreshKind::nothing().with_processes(
313                        ProcessRefreshKind::nothing().with_user(UpdateKind::Always),
314                    ));
315                assert!(
316                    s.processes()
317                        .iter()
318                        .filter_map(|(_, p)| p.user_id())
319                        .any(|uid| users.get_user_by_id(uid).is_some())
320                );
321            }
322        }
323    }
324
325    #[cfg(all(feature = "system", feature = "user"))]
326    #[test]
327    fn check_all_process_uids_resolvable() {
328        // On linux, some user IDs don't have an associated user (no idea why though).
329        // If `getent` doesn't find them, we can assume it's a dark secret from the linux land.
330        if IS_SUPPORTED_SYSTEM && cfg!(not(target_os = "linux")) {
331            let s = System::new_with_specifics(
332                RefreshKind::nothing()
333                    .with_processes(ProcessRefreshKind::nothing().with_user(UpdateKind::Always)),
334            );
335            let users = Users::new_with_refreshed_list();
336
337            // For every process where we can get a user ID, we should also be able
338            // to find that user ID in the global user list
339            for process in s.processes().values() {
340                if let Some(uid) = process.user_id() {
341                    assert!(users.get_user_by_id(uid).is_some(), "No UID {uid:?} found");
342                }
343            }
344        }
345    }
346
347    #[test]
348    fn ensure_is_supported_is_set_correctly() {
349        if MIN_USERS > 0 {
350            assert!(IS_SUPPORTED_SYSTEM);
351        } else {
352            assert!(!IS_SUPPORTED_SYSTEM);
353        }
354    }
355
356    // If it doesn't compile, it means types don't implement expected traits.
357    #[cfg(any(
358        feature = "system",
359        feature = "disk",
360        feature = "component",
361        feature = "user",
362        feature = "network"
363    ))]
364    #[test]
365    fn test_send_and_sync() {
366        #[allow(dead_code)]
367        trait HasSendAndSync: Send + Sync {}
368
369        // Structs
370        impl HasSendAndSync for CGroupLimits {}
371        impl HasSendAndSync for Component {}
372        impl HasSendAndSync for Components {}
373        impl HasSendAndSync for Cpu {}
374        impl HasSendAndSync for CpuRefreshKind {}
375        impl HasSendAndSync for Disk {}
376        impl HasSendAndSync for Disks {}
377        impl HasSendAndSync for DiskRefreshKind {}
378        impl HasSendAndSync for DiskUsage {}
379        impl HasSendAndSync for Gid {}
380        impl HasSendAndSync for Group {}
381        impl HasSendAndSync for Groups {}
382        impl HasSendAndSync for IpNetwork {}
383        impl HasSendAndSync for LoadAvg {}
384        impl HasSendAndSync for MacAddr {}
385        impl HasSendAndSync for MemoryRefreshKind {}
386        impl HasSendAndSync for NetworkData {}
387        impl HasSendAndSync for Networks {}
388        impl HasSendAndSync for Pid {}
389        impl HasSendAndSync for Process {}
390        impl HasSendAndSync for ProcessRefreshKind {}
391        impl HasSendAndSync for Product {}
392        impl HasSendAndSync for RefreshKind {}
393        impl HasSendAndSync for System {}
394        impl HasSendAndSync for Uid {}
395        impl HasSendAndSync for User {}
396        impl HasSendAndSync for Users {}
397
398        // Enums
399        impl HasSendAndSync for DiskKind {}
400        impl HasSendAndSync for IpNetworkFromStrError {}
401        impl HasSendAndSync for KillError {}
402        impl HasSendAndSync for MacAddrFromStrError {}
403        impl HasSendAndSync for ProcessStatus {}
404        impl HasSendAndSync for ProcessesToUpdate<'_> {}
405        impl HasSendAndSync for Signal {}
406        impl HasSendAndSync for ThreadKind {}
407        impl HasSendAndSync for UpdateKind {}
408    }
409}