sysinfo/common/user.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::cmp::Ordering;
4
5use crate::{Gid, Uid, UserInner};
6
7/// Type containing user information.
8///
9/// It is returned by [`Users`][crate::Users].
10///
11/// ```no_run
12/// use sysinfo::Users;
13///
14/// let users = Users::new_with_refreshed_list();
15/// for user in users.list() {
16/// println!("{:?}", user);
17/// }
18/// ```
19pub struct User {
20 pub(crate) inner: UserInner,
21}
22
23impl PartialEq for User {
24 fn eq(&self, other: &Self) -> bool {
25 self.id() == other.id()
26 && self.group_id() == other.group_id()
27 && self.name() == other.name()
28 }
29}
30
31impl Eq for User {}
32
33impl PartialOrd for User {
34 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
35 Some(self.cmp(other))
36 }
37}
38
39impl Ord for User {
40 fn cmp(&self, other: &Self) -> Ordering {
41 self.name().cmp(other.name())
42 }
43}
44
45impl User {
46 /// Returns the ID of the user.
47 ///
48 /// ```no_run
49 /// use sysinfo::Users;
50 ///
51 /// let users = Users::new_with_refreshed_list();
52 /// for user in users.list() {
53 /// println!("{:?}", *user.id());
54 /// }
55 /// ```
56 pub fn id(&self) -> &Uid {
57 self.inner.id()
58 }
59
60 /// Returns the group ID of the user.
61 ///
62 /// ⚠️ This information is not set on Windows. Windows doesn't have a `username` specific
63 /// group assigned to the user. They do however have unique
64 /// [Security Identifiers](https://docs.microsoft.com/en-us/windows/win32/secauthz/security-identifiers)
65 /// made up of various [Components](https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-components).
66 /// Pieces of the SID may be a candidate for this field, but it doesn't map well to a single
67 /// group ID.
68 ///
69 /// ```no_run
70 /// use sysinfo::Users;
71 ///
72 /// let users = Users::new_with_refreshed_list();
73 /// for user in users.list() {
74 /// println!("{}", *user.group_id());
75 /// }
76 /// ```
77 pub fn group_id(&self) -> Gid {
78 self.inner.group_id()
79 }
80
81 /// Returns the name of the user.
82 ///
83 /// ```no_run
84 /// use sysinfo::Users;
85 ///
86 /// let users = Users::new_with_refreshed_list();
87 /// for user in users.list() {
88 /// println!("{}", user.name());
89 /// }
90 /// ```
91 pub fn name(&self) -> &str {
92 self.inner.name()
93 }
94
95 /// Returns the groups of the user.
96 ///
97 /// ⚠️ This is computed every time this method is called.
98 ///
99 /// ```no_run
100 /// use sysinfo::Users;
101 ///
102 /// let users = Users::new_with_refreshed_list();
103 /// for user in users.list() {
104 /// println!("{} is in {:?}", user.name(), user.groups());
105 /// }
106 /// ```
107 pub fn groups(&self) -> Vec<Group> {
108 self.inner.groups()
109 }
110}
111
112#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
113pub(crate) struct GroupInner {
114 pub(crate) id: Gid,
115 pub(crate) name: String,
116}
117
118/// Type containing group information.
119///
120/// It is returned by [`User::groups`] or [`Groups::list`].
121///
122/// ```no_run
123/// use sysinfo::Users;
124///
125/// let mut users = Users::new_with_refreshed_list();
126///
127/// for user in users.list() {
128/// println!(
129/// "user: (ID: {:?}, group ID: {:?}, name: {:?})",
130/// user.id(),
131/// user.group_id(),
132/// user.name(),
133/// );
134/// for group in user.groups() {
135/// println!("group: (ID: {:?}, name: {:?})", group.id(), group.name());
136/// }
137/// }
138/// ```
139#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
140pub struct Group {
141 pub(crate) inner: GroupInner,
142}
143
144impl Group {
145 /// Returns the ID of the group.
146 ///
147 /// ⚠️ This information is not set on Windows.
148 ///
149 /// ```no_run
150 /// use sysinfo::Users;
151 ///
152 /// let mut users = Users::new_with_refreshed_list();
153 ///
154 /// for user in users.list() {
155 /// for group in user.groups() {
156 /// println!("{:?}", group.id());
157 /// }
158 /// }
159 /// ```
160 pub fn id(&self) -> &Gid {
161 self.inner.id()
162 }
163
164 /// Returns the name of the group.
165 ///
166 /// ```no_run
167 /// use sysinfo::Users;
168 ///
169 /// let mut users = Users::new_with_refreshed_list();
170 ///
171 /// for user in users.list() {
172 /// for group in user.groups() {
173 /// println!("{}", group.name());
174 /// }
175 /// }
176 /// ```
177 pub fn name(&self) -> &str {
178 self.inner.name()
179 }
180}
181
182/// Interacting with users.
183///
184/// ```no_run
185/// use sysinfo::Users;
186///
187/// let mut users = Users::new();
188/// for user in users.list() {
189/// println!("{} is in {} groups", user.name(), user.groups().len());
190/// }
191/// ```
192pub struct Users {
193 users: Vec<User>,
194}
195
196impl Default for Users {
197 fn default() -> Self {
198 Self::new()
199 }
200}
201
202impl From<Users> for Vec<User> {
203 fn from(users: Users) -> Self {
204 users.users
205 }
206}
207
208impl From<Vec<User>> for Users {
209 fn from(users: Vec<User>) -> Self {
210 Self { users }
211 }
212}
213
214impl std::ops::Deref for Users {
215 type Target = [User];
216
217 fn deref(&self) -> &Self::Target {
218 self.list()
219 }
220}
221
222impl std::ops::DerefMut for Users {
223 fn deref_mut(&mut self) -> &mut Self::Target {
224 self.list_mut()
225 }
226}
227
228impl<'a> IntoIterator for &'a Users {
229 type Item = &'a User;
230 type IntoIter = std::slice::Iter<'a, User>;
231
232 fn into_iter(self) -> Self::IntoIter {
233 self.list().iter()
234 }
235}
236
237impl<'a> IntoIterator for &'a mut Users {
238 type Item = &'a mut User;
239 type IntoIter = std::slice::IterMut<'a, User>;
240
241 fn into_iter(self) -> Self::IntoIter {
242 self.list_mut().iter_mut()
243 }
244}
245
246impl Users {
247 /// Creates a new empty [`Users`][crate::Users] type.
248 ///
249 /// If you want it to be filled directly, take a look at [`Users::new_with_refreshed_list`].
250 ///
251 /// ```no_run
252 /// use sysinfo::Users;
253 ///
254 /// let mut users = Users::new();
255 /// users.refresh();
256 /// for user in users.list() {
257 /// println!("{user:?}");
258 /// }
259 /// ```
260 pub fn new() -> Self {
261 Self { users: Vec::new() }
262 }
263
264 /// Creates a new [`Users`][crate::Users] type with the user list loaded.
265 ///
266 /// ```no_run
267 /// use sysinfo::Users;
268 ///
269 /// let mut users = Users::new_with_refreshed_list();
270 /// for user in users.list() {
271 /// println!("{user:?}");
272 /// }
273 /// ```
274 pub fn new_with_refreshed_list() -> Self {
275 let mut users = Self::new();
276 users.refresh();
277 users
278 }
279
280 /// Returns the users list.
281 ///
282 /// ```no_run
283 /// use sysinfo::Users;
284 ///
285 /// let users = Users::new_with_refreshed_list();
286 /// for user in users.list() {
287 /// println!("{user:?}");
288 /// }
289 /// ```
290 pub fn list(&self) -> &[User] {
291 &self.users
292 }
293
294 /// Returns the users list.
295 ///
296 /// ```no_run
297 /// use sysinfo::Users;
298 ///
299 /// let mut users = Users::new_with_refreshed_list();
300 /// users.list_mut().sort_by(|user1, user2| {
301 /// user1.name().partial_cmp(user2.name()).unwrap()
302 /// });
303 /// ```
304 pub fn list_mut(&mut self) -> &mut [User] {
305 &mut self.users
306 }
307
308 /// The user list will be emptied then completely recomputed.
309 ///
310 /// ```no_run
311 /// use sysinfo::Users;
312 ///
313 /// let mut users = Users::new();
314 /// users.refresh();
315 /// ```
316 pub fn refresh(&mut self) {
317 crate::sys::get_users(&mut self.users);
318 }
319
320 /// Returns the [`User`] matching the given `user_id`.
321 ///
322 /// **Important**: The user list must be filled before using this method, otherwise it will
323 /// always return `None` (through the `refresh_*` methods).
324 ///
325 /// It is a shorthand for:
326 ///
327 /// ```ignore
328 /// # use sysinfo::Users;
329 /// let users = Users::new_with_refreshed_list();
330 /// users.list().find(|user| user.id() == user_id);
331 /// ```
332 ///
333 /// Full example:
334 ///
335 #[cfg_attr(feature = "system", doc = "```no_run")]
336 #[cfg_attr(not(feature = "system"), doc = "```ignore")]
337 /// use sysinfo::{Pid, System, Users};
338 ///
339 /// let mut s = System::new_all();
340 /// let users = Users::new_with_refreshed_list();
341 ///
342 /// if let Some(process) = s.process(Pid::from(1337)) {
343 /// if let Some(user_id) = process.user_id() {
344 /// println!("User for process 1337: {:?}", users.get_user_by_id(user_id));
345 /// }
346 /// }
347 /// ```
348 pub fn get_user_by_id(&self, user_id: &Uid) -> Option<&User> {
349 self.users.iter().find(|user| user.id() == user_id)
350 }
351}
352
353/// Interacting with groups.
354///
355/// ```no_run
356/// use sysinfo::Groups;
357///
358/// let mut groups = Groups::new();
359/// for group in groups.list() {
360/// println!("{}", group.name());
361/// }
362/// ```
363pub struct Groups {
364 groups: Vec<Group>,
365}
366
367impl Default for Groups {
368 fn default() -> Self {
369 Self::new()
370 }
371}
372
373impl From<Groups> for Vec<Group> {
374 fn from(groups: Groups) -> Self {
375 groups.groups
376 }
377}
378
379impl From<Vec<Group>> for Groups {
380 fn from(groups: Vec<Group>) -> Self {
381 Self { groups }
382 }
383}
384
385impl std::ops::Deref for Groups {
386 type Target = [Group];
387
388 fn deref(&self) -> &Self::Target {
389 self.list()
390 }
391}
392
393impl std::ops::DerefMut for Groups {
394 fn deref_mut(&mut self) -> &mut Self::Target {
395 self.list_mut()
396 }
397}
398
399impl<'a> IntoIterator for &'a Groups {
400 type Item = &'a Group;
401 type IntoIter = std::slice::Iter<'a, Group>;
402
403 fn into_iter(self) -> Self::IntoIter {
404 self.list().iter()
405 }
406}
407
408impl<'a> IntoIterator for &'a mut Groups {
409 type Item = &'a mut Group;
410 type IntoIter = std::slice::IterMut<'a, Group>;
411
412 fn into_iter(self) -> Self::IntoIter {
413 self.list_mut().iter_mut()
414 }
415}
416
417impl Groups {
418 /// Creates a new empty [`Groups`][crate::Groups] type.
419 ///
420 /// If you want it to be filled directly, take a look at [`Groups::new_with_refreshed_list`].
421 ///
422 /// ```no_run
423 /// use sysinfo::Groups;
424 ///
425 /// let mut groups = Groups::new();
426 /// groups.refresh();
427 /// for group in groups.list() {
428 /// println!("{group:?}");
429 /// }
430 /// ```
431 pub fn new() -> Self {
432 Self { groups: Vec::new() }
433 }
434
435 /// Creates a new [`Groups`][crate::Groups] type with the group list loaded.
436 ///
437 /// ```no_run
438 /// use sysinfo::Groups;
439 ///
440 /// let mut groups = Groups::new_with_refreshed_list();
441 /// for group in groups.list() {
442 /// println!("{group:?}");
443 /// }
444 /// ```
445 pub fn new_with_refreshed_list() -> Self {
446 let mut groups = Self::new();
447 groups.refresh();
448 groups
449 }
450
451 /// Returns the groups list.
452 ///
453 /// ```no_run
454 /// use sysinfo::Groups;
455 ///
456 /// let groups = Groups::new_with_refreshed_list();
457 /// for group in groups.list() {
458 /// println!("{group:?}");
459 /// }
460 /// ```
461 pub fn list(&self) -> &[Group] {
462 &self.groups
463 }
464
465 /// Returns the groups list.
466 ///
467 /// ```no_run
468 /// use sysinfo::Groups;
469 ///
470 /// let mut groups = Groups::new_with_refreshed_list();
471 /// groups.list_mut().sort_by(|user1, user2| {
472 /// user1.name().partial_cmp(user2.name()).unwrap()
473 /// });
474 /// ```
475 pub fn list_mut(&mut self) -> &mut [Group] {
476 &mut self.groups
477 }
478
479 /// The group list will be emptied then completely recomputed.
480 ///
481 /// ```no_run
482 /// use sysinfo::Groups;
483 ///
484 /// let mut groups = Groups::new();
485 /// groups.refresh();
486 /// ```
487 pub fn refresh(&mut self) {
488 crate::sys::get_groups(&mut self.groups);
489 }
490}
491
492#[cfg(test)]
493mod tests {
494 use crate::*;
495
496 #[test]
497 fn check_list() {
498 let mut users = Users::new();
499 assert!(users.list().is_empty());
500 users.refresh();
501 assert!(users.list().len() >= MIN_USERS);
502 }
503
504 // This test exists to ensure that the `TryFrom<usize>` and `FromStr` traits are implemented
505 // on `Uid`, `Gid` and `Pid`.
506 #[allow(clippy::unnecessary_fallible_conversions)]
507 #[test]
508 fn check_uid_gid_from_impls() {
509 use std::convert::TryFrom;
510 use std::str::FromStr;
511
512 #[cfg(not(windows))]
513 {
514 assert!(crate::Uid::try_from(0usize).is_ok());
515 assert!(crate::Uid::from_str("0").is_ok());
516 }
517 #[cfg(windows)]
518 {
519 assert!(crate::Uid::from_str("S-1-5-18").is_ok()); // SECURITY_LOCAL_SYSTEM_RID
520 assert!(crate::Uid::from_str("0").is_err());
521 }
522
523 assert!(crate::Gid::try_from(0usize).is_ok());
524 assert!(crate::Gid::from_str("0").is_ok());
525 }
526
527 #[test]
528 fn check_groups() {
529 if !crate::IS_SUPPORTED_SYSTEM {
530 return;
531 }
532 assert!(!Groups::new_with_refreshed_list().is_empty());
533 }
534}