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(
5        feature = "system",
6        feature = "disk",
7        feature = "component",
8        feature = "gpu",
9        feature = "network",
10        feature = "user"
11    ),
12    doc = include_str!("../README.md")
13)]
14#![cfg_attr(
15    not(all(
16        feature = "system",
17        feature = "disk",
18        feature = "component",
19        feature = "gpu",
20        feature = "network",
21        feature = "user"
22    )),
23    doc = "For crate-level documentation, all features need to be enabled."
24)]
25#![cfg_attr(feature = "serde", doc = include_str!("../md_doc/serde.md"))]
26#![allow(unknown_lints)]
27#![deny(missing_docs)]
28#![deny(rustdoc::broken_intra_doc_links)]
29#![allow(clippy::upper_case_acronyms)]
30#![allow(clippy::non_send_fields_in_send_ty)]
31#![allow(renamed_and_removed_lints)]
32#![allow(clippy::assertions_on_constants)]
33
34#[macro_use]
35mod macros;
36
37cfg_select! {
38    feature = "unknown-ci" => {
39        // This is used in CI to check that the build for unknown targets is compiling fine.
40        mod unknown;
41        #[allow(unused_imports)]
42        use crate::unknown as sys;
43
44        #[cfg(test)]
45    #[allow(dead_code)]
46        pub(crate) const MIN_USERS: usize = 0;
47    }
48    any(
49        target_os = "macos", target_os = "ios",
50        target_os = "linux", target_os = "android",
51        target_os = "freebsd", target_os = "netbsd") =>
52    {
53        mod unix;
54        #[allow(unused_imports)]
55        use crate::unix::sys as sys;
56
57        #[cfg(feature = "network")]
58        mod network;
59        #[cfg(feature = "network")]
60        use crate::unix::network_helper;
61
62        #[cfg(test)]
63    #[allow(dead_code)]
64        pub(crate) const MIN_USERS: usize = 1;
65    }
66    windows => {
67        mod windows;
68        #[allow(unused_imports)]
69        use crate::windows as sys;
70
71        #[cfg(feature = "network")]
72        mod network;
73        #[cfg(feature = "network")]
74        use crate::windows::network_helper;
75
76        #[cfg(test)]
77    #[allow(dead_code)]
78        pub(crate) const MIN_USERS: usize = 1;
79    }
80    _ => {
81        mod unknown;
82        #[allow(unused_imports)]
83        use crate::unknown as sys;
84
85        #[cfg(test)]
86    #[allow(dead_code)]
87        pub(crate) const MIN_USERS: usize = 0;
88    }
89}
90
91#[cfg(feature = "component")]
92pub use crate::common::component::{Component, Components};
93#[cfg(feature = "disk")]
94pub use crate::common::disk::{Disk, DiskKind, DiskRefreshKind, Disks};
95#[cfg(feature = "gpu")]
96pub use crate::common::gpu::{Gpu, Gpus, PCI};
97#[cfg(feature = "network")]
98pub use crate::common::network::{
99    InterfaceOperationalState, IpNetwork, IpNetworkFromStrError, MacAddr, MacAddrFromStrError,
100    NetworkData, Networks,
101};
102#[cfg(feature = "system")]
103pub use crate::common::system::{
104    CGroupLimits, Cpu, CpuRefreshKind, KillError, LoadAvg, MemoryRefreshKind, Motherboard, Pid,
105    Process, ProcessRefreshKind, ProcessStatus, ProcessesToUpdate, Product, RefreshKind, Signal,
106    System, ThreadKind, UpdateKind, get_current_pid,
107};
108#[cfg(feature = "user")]
109pub use crate::common::user::{Group, Groups, User, Users};
110#[cfg(any(feature = "user", feature = "system"))]
111pub use crate::common::{Gid, Uid};
112#[cfg(feature = "system")]
113pub use crate::sys::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
114
115#[cfg(any(feature = "system", feature = "disk"))]
116pub use crate::common::DiskUsage;
117
118#[cfg(feature = "user")]
119pub(crate) use crate::common::user::GroupInner;
120#[cfg(feature = "user")]
121pub(crate) use crate::sys::UserInner;
122#[cfg(feature = "component")]
123pub(crate) use crate::sys::{ComponentInner, ComponentsInner};
124#[cfg(feature = "system")]
125pub(crate) use crate::sys::{CpuInner, MotherboardInner, ProcessInner, ProductInner, SystemInner};
126#[cfg(feature = "disk")]
127pub(crate) use crate::sys::{DiskInner, DisksInner};
128#[cfg(feature = "gpu")]
129pub(crate) use crate::sys::{GpuInner, GpusInner};
130#[cfg(feature = "network")]
131pub(crate) use crate::sys::{NetworkDataInner, NetworksInner};
132
133pub use crate::errors::Error;
134
135#[cfg(feature = "c-interface")]
136pub use crate::c_interface::*;
137
138#[cfg(feature = "c-interface")]
139mod c_interface;
140mod common;
141mod debug;
142mod errors;
143#[cfg(feature = "serde")]
144mod serde;
145pub(crate) mod utils;
146
147// Make formattable by rustfmt.
148#[cfg(any())]
149mod network;
150#[cfg(any())]
151mod unix;
152#[cfg(any())]
153mod unknown;
154#[cfg(any())]
155mod windows;
156
157#[cfg(doctest)]
158mod doctest {
159    macro_rules! compile_fail_import {
160        ($mod_name:ident => $($imports:ident),+ $(,)?) => {
161            $(#[doc = concat!(r"```compile_fail
162use sysinfo::", stringify!($imports), r";
163```
164")])+
165            mod $mod_name {}
166        };
167    }
168
169    #[cfg(not(feature = "system"))]
170    compile_fail_import!(
171        no_system_feature =>
172        get_current_pid,
173        CGroupLimits,
174        Cpu,
175        CpuRefreshKind,
176        DiskUsage,
177        KillError,
178        LoadAvg,
179        MemoryRefreshKind,
180        Motherboard,
181        Pid,
182        Process,
183        ProcessesToUpdate,
184        ProcessRefreshKind,
185        ProcessStatus,
186        Product,
187        RefreshKind,
188        Signal,
189        System,
190        ThreadKind,
191        UpdateKind,
192    );
193
194    #[cfg(not(feature = "disk"))]
195    compile_fail_import!(
196        no_disk_feature =>
197        Disk,
198        Disks,
199        DiskKind,
200    );
201
202    #[cfg(not(feature = "component"))]
203    compile_fail_import!(
204        no_component_feature =>
205        Component,
206        Components,
207    );
208
209    #[cfg(not(feature = "network"))]
210    compile_fail_import!(
211        no_network_feature =>
212        IpNetwork,
213        MacAddr,
214        NetworkData,
215        Networks,
216    );
217
218    #[cfg(not(feature = "user"))]
219    compile_fail_import!(
220        no_user_feature =>
221        Group,
222        Groups,
223        User,
224        Users,
225    );
226}
227
228#[cfg(test)]
229mod test {
230    #[allow(unused_imports)]
231    use crate::*;
232
233    #[cfg(all(feature = "unknown-ci", feature = "system"))]
234    #[test]
235    fn check_unknown_ci_feature() {
236        assert!(System::new().is_err());
237    }
238
239    // If this test doesn't compile, it means the current OS doesn't implement them correctly.
240    #[cfg(feature = "system")]
241    #[test]
242    fn check_macro_types() {
243        fn check_is_supported(_: bool) {}
244
245        check_is_supported(System::new().is_ok());
246    }
247
248    // If this test doesn't compile, it means the current OS doesn't implement them correctly.
249    #[cfg(feature = "system")]
250    #[test]
251    fn check_macro_types2() {
252        fn check_supported_signals(_: &'static [Signal]) {}
253        fn check_minimum_cpu_update_interval(_: std::time::Duration) {}
254
255        check_supported_signals(SUPPORTED_SIGNALS);
256        check_minimum_cpu_update_interval(MINIMUM_CPU_UPDATE_INTERVAL);
257    }
258
259    #[cfg(feature = "user")]
260    #[test]
261    fn check_uid_gid() {
262        let mut users = check_unsupported!(Users::new());
263        assert!(users.is_empty());
264        users.refresh();
265        assert!(users.len() >= MIN_USERS);
266
267        #[cfg(not(target_os = "windows"))]
268        {
269            let user = users
270                .iter()
271                .find(|u| u.name() == "root")
272                .expect("no root user");
273            assert_eq!(**user.id(), 0);
274            assert_eq!(*user.group_id(), 0);
275            if let Some(user) = users.iter().find(|u| *u.group_id() > 0) {
276                assert!(**user.id() > 0);
277                assert!(*user.group_id() > 0);
278            }
279            assert!(users.iter().filter(|u| **u.id() > 0).count() > 0);
280        }
281
282        #[cfg(feature = "system")]
283        {
284            // And now check that our `get_user_by_id` method works.
285            if let Ok(s) = System::new_with_specifics(
286                RefreshKind::nothing()
287                    .with_processes(ProcessRefreshKind::nothing().with_user(UpdateKind::Always)),
288            ) {
289                assert!(
290                    s.processes()
291                        .values()
292                        .filter_map(|p| p.user_id())
293                        .any(|uid| users.get_user_by_id(uid).is_some())
294                );
295            }
296        }
297    }
298
299    #[cfg(all(feature = "system", feature = "user"))]
300    #[test]
301    fn check_all_process_uids_resolvable() {
302        // On linux, some user IDs don't have an associated user (no idea why though).
303        // If `getent` doesn't find them, we can assume it's a dark secret from the linux land.
304        if cfg!(target_os = "linux") {
305            return;
306        }
307        let s = check_unsupported!(System::new_with_specifics(
308            RefreshKind::nothing()
309                .with_processes(ProcessRefreshKind::nothing().with_user(UpdateKind::Always)),
310        ));
311        let users = check_unsupported!(Users::new_with_refreshed_list());
312
313        // For every process where we can get a user ID, we should also be able
314        // to find that user ID in the global user list
315        for process in s.processes().values() {
316            if let Some(uid) = process.user_id() {
317                assert!(users.get_user_by_id(uid).is_some(), "No UID {uid:?} found");
318            }
319        }
320    }
321
322    #[cfg(feature = "system")]
323    #[test]
324    fn ensure_is_supported_is_set_correctly() {
325        if MIN_USERS > 0 {
326            assert!(System::new().is_ok());
327        } else {
328            assert!(System::new().is_err());
329        }
330    }
331
332    // If it doesn't compile, it means types don't implement expected traits.
333    #[cfg(any(
334        feature = "system",
335        feature = "disk",
336        feature = "component",
337        feature = "user",
338        feature = "network"
339    ))]
340    #[test]
341    fn test_send_and_sync() {
342        #[allow(dead_code)]
343        trait HasSendAndSync: Send + Sync {}
344
345        // Structs
346        impl HasSendAndSync for CGroupLimits {}
347        impl HasSendAndSync for Component {}
348        impl HasSendAndSync for Components {}
349        impl HasSendAndSync for Cpu {}
350        impl HasSendAndSync for CpuRefreshKind {}
351        impl HasSendAndSync for Disk {}
352        impl HasSendAndSync for Disks {}
353        impl HasSendAndSync for DiskRefreshKind {}
354        impl HasSendAndSync for DiskUsage {}
355        impl HasSendAndSync for Gid {}
356        impl HasSendAndSync for Group {}
357        impl HasSendAndSync for Groups {}
358        impl HasSendAndSync for IpNetwork {}
359        impl HasSendAndSync for LoadAvg {}
360        impl HasSendAndSync for MacAddr {}
361        impl HasSendAndSync for MemoryRefreshKind {}
362        impl HasSendAndSync for NetworkData {}
363        impl HasSendAndSync for Networks {}
364        impl HasSendAndSync for Pid {}
365        impl HasSendAndSync for Process {}
366        impl HasSendAndSync for ProcessRefreshKind {}
367        impl HasSendAndSync for Product {}
368        impl HasSendAndSync for RefreshKind {}
369        impl HasSendAndSync for System {}
370        impl HasSendAndSync for Uid {}
371        impl HasSendAndSync for User {}
372        impl HasSendAndSync for Users {}
373
374        // Enums
375        impl HasSendAndSync for DiskKind {}
376        impl HasSendAndSync for IpNetworkFromStrError {}
377        impl HasSendAndSync for KillError {}
378        impl HasSendAndSync for MacAddrFromStrError {}
379        impl HasSendAndSync for ProcessStatus {}
380        impl HasSendAndSync for ProcessesToUpdate<'_> {}
381        impl HasSendAndSync for Signal {}
382        impl HasSendAndSync for ThreadKind {}
383        impl HasSendAndSync for UpdateKind {}
384    }
385}