[go: up one dir, main page]

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, LoadAvg, MemoryRefreshKind, Pid, Process,
82    ProcessRefreshKind, ProcessStatus, ProcessesToUpdate, RefreshKind, Signal, System, ThreadKind,
83    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, ProcessInner, 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: isize) -> 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            if _new_limit < 0 {
163                _new_limit = 0;
164            }
165            let max = sys::system::get_max_nb_fds();
166            if _new_limit > max {
167                _new_limit = max;
168            }
169
170            // If files are already open, to be sure that the number won't be bigger when those
171            // files are closed, we subtract the current number of opened files to the new
172            // limit.
173            remaining_files().fetch_update(Ordering::SeqCst, Ordering::SeqCst, |remaining| {
174                let diff = max.saturating_sub(remaining);
175                Some(_new_limit.saturating_sub(diff))
176            }).unwrap();
177
178            true
179        } else {
180            false
181        }
182    }
183}
184
185#[cfg(doctest)]
186mod doctest {
187    macro_rules! compile_fail_import {
188        ($mod_name:ident => $($imports:ident),+ $(,)?) => {
189            $(#[doc = concat!(r"```compile_fail
190use sysinfo::", stringify!($imports), r";
191```
192")])+
193            mod $mod_name {}
194        };
195    }
196
197    #[cfg(not(feature = "system"))]
198    compile_fail_import!(
199        no_system_feature =>
200        get_current_pid,
201        CGroupLimits,
202        Cpu,
203        CpuRefreshKind,
204        DiskUsage,
205        LoadAvg,
206        MemoryRefreshKind,
207        Pid,
208        Process,
209        ProcessesToUpdate,
210        ProcessRefreshKind,
211        ProcessStatus,
212        RefreshKind,
213        Signal,
214        System,
215        ThreadKind,
216        UpdateKind,
217    );
218
219    #[cfg(not(feature = "disk"))]
220    compile_fail_import!(
221        no_disk_feature =>
222        Disk,
223        Disks,
224        DiskKind,
225    );
226
227    #[cfg(not(feature = "component"))]
228    compile_fail_import!(
229        no_component_feature =>
230        Component,
231        Components,
232    );
233
234    #[cfg(not(feature = "network"))]
235    compile_fail_import!(
236        no_network_feature =>
237        IpNetwork,
238        MacAddr,
239        NetworkData,
240        Networks,
241    );
242
243    #[cfg(not(feature = "user"))]
244    compile_fail_import!(
245        no_user_feature =>
246        Group,
247        Groups,
248        User,
249        Users,
250    );
251}
252
253#[cfg(test)]
254mod test {
255    use crate::*;
256
257    #[cfg(feature = "unknown-ci")]
258    #[test]
259    fn check_unknown_ci_feature() {
260        assert!(!IS_SUPPORTED_SYSTEM);
261    }
262
263    // If this test doesn't compile, it means the current OS doesn't implement them correctly.
264    #[test]
265    fn check_macro_types() {
266        fn check_is_supported(_: bool) {}
267
268        check_is_supported(IS_SUPPORTED_SYSTEM);
269    }
270
271    // If this test doesn't compile, it means the current OS doesn't implement them correctly.
272    #[cfg(feature = "system")]
273    #[test]
274    fn check_macro_types2() {
275        fn check_supported_signals(_: &'static [Signal]) {}
276        fn check_minimum_cpu_update_interval(_: std::time::Duration) {}
277
278        check_supported_signals(SUPPORTED_SIGNALS);
279        check_minimum_cpu_update_interval(MINIMUM_CPU_UPDATE_INTERVAL);
280    }
281
282    #[cfg(feature = "user")]
283    #[test]
284    fn check_uid_gid() {
285        let mut users = Users::new();
286        assert!(users.list().is_empty());
287        users.refresh();
288        let user_list = users.list();
289        assert!(user_list.len() >= MIN_USERS);
290
291        if IS_SUPPORTED_SYSTEM {
292            #[cfg(not(target_os = "windows"))]
293            {
294                let user = user_list
295                    .iter()
296                    .find(|u| u.name() == "root")
297                    .expect("no root user");
298                assert_eq!(**user.id(), 0);
299                assert_eq!(*user.group_id(), 0);
300                if let Some(user) = users.iter().find(|u| *u.group_id() > 0) {
301                    assert!(**user.id() > 0);
302                    assert!(*user.group_id() > 0);
303                }
304                assert!(user_list.iter().filter(|u| **u.id() > 0).count() > 0);
305            }
306
307            #[cfg(feature = "system")]
308            {
309                // And now check that our `get_user_by_id` method works.
310                let s =
311                    System::new_with_specifics(RefreshKind::nothing().with_processes(
312                        ProcessRefreshKind::nothing().with_user(UpdateKind::Always),
313                    ));
314                assert!(s
315                    .processes()
316                    .iter()
317                    .filter_map(|(_, p)| p.user_id())
318                    .any(|uid| users.get_user_by_id(uid).is_some()));
319            }
320        }
321    }
322
323    #[cfg(all(feature = "system", feature = "user"))]
324    #[test]
325    fn check_all_process_uids_resolvable() {
326        // On linux, some user IDs don't have an associated user (no idea why though).
327        // If `getent` doesn't find them, we can assume it's a dark secret from the linux land.
328        if IS_SUPPORTED_SYSTEM && cfg!(not(target_os = "linux")) {
329            let s = System::new_with_specifics(
330                RefreshKind::nothing()
331                    .with_processes(ProcessRefreshKind::nothing().with_user(UpdateKind::Always)),
332            );
333            let users = Users::new_with_refreshed_list();
334
335            // For every process where we can get a user ID, we should also be able
336            // to find that user ID in the global user list
337            for process in s.processes().values() {
338                if let Some(uid) = process.user_id() {
339                    assert!(
340                        users.get_user_by_id(uid).is_some(),
341                        "No UID {:?} found",
342                        uid
343                    );
344                }
345            }
346        }
347    }
348
349    #[test]
350    fn ensure_is_supported_is_set_correctly() {
351        if MIN_USERS > 0 {
352            assert!(IS_SUPPORTED_SYSTEM);
353        } else {
354            assert!(!IS_SUPPORTED_SYSTEM);
355        }
356    }
357}