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    get_current_pid, CGroupLimits, Cpu, CpuRefreshKind, KillError, LoadAvg, MemoryRefreshKind,
82    Motherboard, Pid, Process, ProcessRefreshKind, ProcessStatus, ProcessesToUpdate, Product,
83    RefreshKind, Signal, System, ThreadKind, UpdateKind,
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!(s
316                    .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    #[cfg(all(feature = "system", feature = "user"))]
325    #[test]
326    fn check_all_process_uids_resolvable() {
327        // On linux, some user IDs don't have an associated user (no idea why though).
328        // If `getent` doesn't find them, we can assume it's a dark secret from the linux land.
329        if IS_SUPPORTED_SYSTEM && cfg!(not(target_os = "linux")) {
330            let s = System::new_with_specifics(
331                RefreshKind::nothing()
332                    .with_processes(ProcessRefreshKind::nothing().with_user(UpdateKind::Always)),
333            );
334            let users = Users::new_with_refreshed_list();
335
336            // For every process where we can get a user ID, we should also be able
337            // to find that user ID in the global user list
338            for process in s.processes().values() {
339                if let Some(uid) = process.user_id() {
340                    assert!(users.get_user_by_id(uid).is_some(), "No UID {uid:?} found");
341                }
342            }
343        }
344    }
345
346    #[test]
347    fn ensure_is_supported_is_set_correctly() {
348        if MIN_USERS > 0 {
349            assert!(IS_SUPPORTED_SYSTEM);
350        } else {
351            assert!(!IS_SUPPORTED_SYSTEM);
352        }
353    }
354
355    // If it doesn't compile, it means types don't implement expected traits.
356    #[cfg(any(
357        feature = "system",
358        feature = "disk",
359        feature = "component",
360        feature = "user",
361        feature = "network"
362    ))]
363    #[test]
364    fn test_send_and_sync() {
365        #[allow(dead_code)]
366        trait HasSendAndSync: Send + Sync {}
367
368        // Structs
369        impl HasSendAndSync for CGroupLimits {}
370        impl HasSendAndSync for Component {}
371        impl HasSendAndSync for Components {}
372        impl HasSendAndSync for Cpu {}
373        impl HasSendAndSync for CpuRefreshKind {}
374        impl HasSendAndSync for Disk {}
375        impl HasSendAndSync for Disks {}
376        impl HasSendAndSync for DiskRefreshKind {}
377        impl HasSendAndSync for DiskUsage {}
378        impl HasSendAndSync for Gid {}
379        impl HasSendAndSync for Group {}
380        impl HasSendAndSync for Groups {}
381        impl HasSendAndSync for IpNetwork {}
382        impl HasSendAndSync for LoadAvg {}
383        impl HasSendAndSync for MacAddr {}
384        impl HasSendAndSync for MemoryRefreshKind {}
385        impl HasSendAndSync for NetworkData {}
386        impl HasSendAndSync for Networks {}
387        impl HasSendAndSync for Pid {}
388        impl HasSendAndSync for Process {}
389        impl HasSendAndSync for ProcessRefreshKind {}
390        impl HasSendAndSync for Product {}
391        impl HasSendAndSync for RefreshKind {}
392        impl HasSendAndSync for System {}
393        impl HasSendAndSync for Uid {}
394        impl HasSendAndSync for User {}
395        impl HasSendAndSync for Users {}
396
397        // Enums
398        impl HasSendAndSync for DiskKind {}
399        impl HasSendAndSync for IpNetworkFromStrError {}
400        impl HasSendAndSync for KillError {}
401        impl HasSendAndSync for MacAddrFromStrError {}
402        impl HasSendAndSync for ProcessStatus {}
403        impl HasSendAndSync for ProcessesToUpdate<'_> {}
404        impl HasSendAndSync for Signal {}
405        impl HasSendAndSync for ThreadKind {}
406        impl HasSendAndSync for UpdateKind {}
407    }
408}