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::{IpNetwork, MacAddr, NetworkData, Networks};
77#[cfg(feature = "system")]
78pub use crate::common::system::{
79    get_current_pid, CGroupLimits, Cpu, CpuRefreshKind, LoadAvg, MemoryRefreshKind, Pid, Process,
80    ProcessRefreshKind, ProcessStatus, ProcessesToUpdate, RefreshKind, Signal, System, ThreadKind,
81    UpdateKind,
82};
83#[cfg(feature = "user")]
84pub use crate::common::user::{Group, Groups, User, Users};
85#[cfg(any(feature = "user", feature = "system"))]
86pub use crate::common::{Gid, Uid};
87#[cfg(feature = "system")]
88pub use crate::sys::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
89
90#[cfg(any(feature = "system", feature = "disk"))]
91pub use crate::common::DiskUsage;
92
93#[cfg(feature = "user")]
94pub(crate) use crate::common::user::GroupInner;
95#[cfg(feature = "user")]
96pub(crate) use crate::sys::UserInner;
97#[cfg(feature = "component")]
98pub(crate) use crate::sys::{ComponentInner, ComponentsInner};
99#[cfg(feature = "system")]
100pub(crate) use crate::sys::{CpuInner, ProcessInner, SystemInner};
101#[cfg(feature = "disk")]
102pub(crate) use crate::sys::{DiskInner, DisksInner};
103#[cfg(feature = "network")]
104pub(crate) use crate::sys::{NetworkDataInner, NetworksInner};
105
106pub use crate::sys::IS_SUPPORTED_SYSTEM;
107
108#[cfg(feature = "c-interface")]
109pub use crate::c_interface::*;
110
111#[cfg(feature = "c-interface")]
112mod c_interface;
113mod common;
114mod debug;
115#[cfg(feature = "serde")]
116mod serde;
117pub(crate) mod utils;
118
119// Make formattable by rustfmt.
120#[cfg(any())]
121mod network;
122#[cfg(any())]
123mod unix;
124#[cfg(any())]
125mod unknown;
126#[cfg(any())]
127mod windows;
128
129/// This function is only used on Linux targets, when the `system` feature is enabled. In other
130/// cases, it does nothing and returns `false`.
131///
132/// On Linux, to improve performance, we keep a `/proc` file open for each process we index with
133/// a maximum number of files open equivalent to half of the system limit.
134///
135/// The problem is that some users might need all the available file descriptors so we need to
136/// allow them to change this limit.
137///
138/// Note that if you set a limit bigger than the system limit, the system limit will be set.
139///
140/// Returns `true` if the new value has been set.
141///
142#[cfg_attr(feature = "system", doc = "```no_run")]
143#[cfg_attr(not(feature = "system"), doc = "```ignore")]
144/// use sysinfo::{System, set_open_files_limit};
145///
146/// // We call the function before any call to the processes update.
147/// if !set_open_files_limit(10) {
148///     // It'll always return false on non-linux targets.
149///     eprintln!("failed to update the open files limit...");
150/// }
151/// let s = System::new_all();
152/// ```
153pub fn set_open_files_limit(mut _new_limit: isize) -> bool {
154    cfg_if! {
155        if #[cfg(all(feature = "system", not(feature = "unknown-ci"), any(target_os = "linux", target_os = "android")))]
156        {
157            use crate::sys::system::remaining_files;
158            use std::sync::atomic::Ordering;
159
160            if _new_limit < 0 {
161                _new_limit = 0;
162            }
163            let max = sys::system::get_max_nb_fds();
164            if _new_limit > max {
165                _new_limit = max;
166            }
167
168            // If files are already open, to be sure that the number won't be bigger when those
169            // files are closed, we subtract the current number of opened files to the new
170            // limit.
171            remaining_files().fetch_update(Ordering::SeqCst, Ordering::SeqCst, |remaining| {
172                let diff = max.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        LoadAvg,
204        MemoryRefreshKind,
205        Pid,
206        Process,
207        ProcessesToUpdate,
208        ProcessRefreshKind,
209        ProcessStatus,
210        RefreshKind,
211        Signal,
212        System,
213        ThreadKind,
214        UpdateKind,
215    );
216
217    #[cfg(not(feature = "disk"))]
218    compile_fail_import!(
219        no_disk_feature =>
220        Disk,
221        Disks,
222        DiskKind,
223    );
224
225    #[cfg(not(feature = "component"))]
226    compile_fail_import!(
227        no_component_feature =>
228        Component,
229        Components,
230    );
231
232    #[cfg(not(feature = "network"))]
233    compile_fail_import!(
234        no_network_feature =>
235        IpNetwork,
236        MacAddr,
237        NetworkData,
238        Networks,
239    );
240
241    #[cfg(not(feature = "user"))]
242    compile_fail_import!(
243        no_user_feature =>
244        Group,
245        Groups,
246        User,
247        Users,
248    );
249}
250
251#[cfg(test)]
252mod test {
253    use crate::*;
254
255    #[cfg(feature = "unknown-ci")]
256    #[test]
257    fn check_unknown_ci_feature() {
258        assert!(!IS_SUPPORTED_SYSTEM);
259    }
260
261    // If this test doesn't compile, it means the current OS doesn't implement them correctly.
262    #[test]
263    fn check_macro_types() {
264        fn check_is_supported(_: bool) {}
265
266        check_is_supported(IS_SUPPORTED_SYSTEM);
267    }
268
269    // If this test doesn't compile, it means the current OS doesn't implement them correctly.
270    #[cfg(feature = "system")]
271    #[test]
272    fn check_macro_types2() {
273        fn check_supported_signals(_: &'static [Signal]) {}
274        fn check_minimum_cpu_update_interval(_: std::time::Duration) {}
275
276        check_supported_signals(SUPPORTED_SIGNALS);
277        check_minimum_cpu_update_interval(MINIMUM_CPU_UPDATE_INTERVAL);
278    }
279
280    #[cfg(feature = "user")]
281    #[test]
282    fn check_uid_gid() {
283        let mut users = Users::new();
284        assert!(users.list().is_empty());
285        users.refresh();
286        let user_list = users.list();
287        assert!(user_list.len() >= MIN_USERS);
288
289        if IS_SUPPORTED_SYSTEM {
290            #[cfg(not(target_os = "windows"))]
291            {
292                let user = user_list
293                    .iter()
294                    .find(|u| u.name() == "root")
295                    .expect("no root user");
296                assert_eq!(**user.id(), 0);
297                assert_eq!(*user.group_id(), 0);
298                if let Some(user) = users.iter().find(|u| *u.group_id() > 0) {
299                    assert!(**user.id() > 0);
300                    assert!(*user.group_id() > 0);
301                }
302                assert!(user_list.iter().filter(|u| **u.id() > 0).count() > 0);
303            }
304
305            #[cfg(feature = "system")]
306            {
307                // And now check that our `get_user_by_id` method works.
308                let s =
309                    System::new_with_specifics(RefreshKind::nothing().with_processes(
310                        ProcessRefreshKind::nothing().with_user(UpdateKind::Always),
311                    ));
312                assert!(s
313                    .processes()
314                    .iter()
315                    .filter_map(|(_, p)| p.user_id())
316                    .any(|uid| users.get_user_by_id(uid).is_some()));
317            }
318        }
319    }
320
321    #[cfg(feature = "system")]
322    #[test]
323    fn check_all_process_uids_resolvable() {
324        // On linux, some user IDs don't have an associated user (no idea why though).
325        // If `getent` doesn't find them, we can assume it's a dark secret from the linux land.
326        if IS_SUPPORTED_SYSTEM && cfg!(not(target_os = "linux")) {
327            let s = System::new_with_specifics(
328                RefreshKind::nothing()
329                    .with_processes(ProcessRefreshKind::nothing().with_user(UpdateKind::Always)),
330            );
331            let users = Users::new_with_refreshed_list();
332
333            // For every process where we can get a user ID, we should also be able
334            // to find that user ID in the global user list
335            for process in s.processes().values() {
336                if let Some(uid) = process.user_id() {
337                    assert!(
338                        users.get_user_by_id(uid).is_some(),
339                        "No UID {:?} found",
340                        uid
341                    );
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}