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 ] |
26 | mod macros; |
27 | |
28 | cfg_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" )] |
72 | pub use crate::common::component::{Component, Components}; |
73 | #[cfg (feature = "disk" )] |
74 | pub use crate::common::disk::{Disk, DiskKind, Disks}; |
75 | #[cfg (feature = "network" )] |
76 | pub use crate::common::network::{IpNetwork, MacAddr, NetworkData, Networks}; |
77 | #[cfg (feature = "system" )] |
78 | pub 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" )] |
84 | pub use crate::common::user::{Group, Groups, User, Users}; |
85 | #[cfg (any(feature = "user" , feature = "system" ))] |
86 | pub use crate::common::{Gid, Uid}; |
87 | #[cfg (feature = "system" )] |
88 | pub use crate::sys::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS}; |
89 | |
90 | #[cfg (feature = "user" )] |
91 | pub(crate) use crate::common::user::GroupInner; |
92 | #[cfg (feature = "user" )] |
93 | pub(crate) use crate::sys::UserInner; |
94 | #[cfg (feature = "component" )] |
95 | pub(crate) use crate::sys::{ComponentInner, ComponentsInner}; |
96 | #[cfg (feature = "system" )] |
97 | pub(crate) use crate::sys::{CpuInner, ProcessInner, SystemInner}; |
98 | #[cfg (feature = "disk" )] |
99 | pub(crate) use crate::sys::{DiskInner, DisksInner}; |
100 | #[cfg (feature = "network" )] |
101 | pub(crate) use crate::sys::{NetworkDataInner, NetworksInner}; |
102 | |
103 | pub use crate::sys::IS_SUPPORTED_SYSTEM; |
104 | |
105 | #[cfg (feature = "c-interface" )] |
106 | pub use crate::c_interface::*; |
107 | |
108 | #[cfg (feature = "c-interface" )] |
109 | mod c_interface; |
110 | mod common; |
111 | mod debug; |
112 | #[cfg (feature = "serde" )] |
113 | mod serde; |
114 | pub(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 | /// ``` |
140 | pub 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)] |
171 | mod doctest { |
172 | macro_rules! compile_fail_import { |
173 | ($mod_name:ident => $($imports:ident),+ $(,)?) => { |
174 | $(#[doc = concat!(r"```compile_fail |
175 | use 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)] |
239 | mod 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 | |