Skip to main content

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