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