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}