blob: ae1d9729861601ac6655a3fd774fda93c9e198ff [file] [log] [blame]
// Take a look at the license at the top of the repository in the LICENSE file.
use std::cmp::Ordering;
use crate::{Gid, Uid, UserInner};
/// Type containing user information.
///
/// It is returned by [`Users`][crate::Users].
///
/// ```no_run
/// use sysinfo::Users;
///
/// let users = Users::new_with_refreshed_list();
/// for user in users.list() {
/// println!("{:?}", user);
/// }
/// ```
pub struct User {
pub(crate) inner: UserInner,
}
impl PartialEq for User {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
&& self.group_id() == other.group_id()
&& self.name() == other.name()
}
}
impl Eq for User {}
impl PartialOrd for User {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for User {
fn cmp(&self, other: &Self) -> Ordering {
self.name().cmp(other.name())
}
}
impl User {
/// Returns the ID of the user.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let users = Users::new_with_refreshed_list();
/// for user in users.list() {
/// println!("{:?}", *user.id());
/// }
/// ```
pub fn id(&self) -> &Uid {
self.inner.id()
}
/// Returns the group ID of the user.
///
/// ⚠️ This information is not set on Windows. Windows doesn't have a `username` specific
/// group assigned to the user. They do however have unique
/// [Security Identifiers](https://docs.microsoft.com/en-us/windows/win32/secauthz/security-identifiers)
/// made up of various [Components](https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-components).
/// Pieces of the SID may be a candidate for this field, but it doesn't map well to a single
/// group ID.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let users = Users::new_with_refreshed_list();
/// for user in users.list() {
/// println!("{}", *user.group_id());
/// }
/// ```
pub fn group_id(&self) -> Gid {
self.inner.group_id()
}
/// Returns the name of the user.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let users = Users::new_with_refreshed_list();
/// for user in users.list() {
/// println!("{}", user.name());
/// }
/// ```
pub fn name(&self) -> &str {
self.inner.name()
}
/// Returns the groups of the user.
///
/// ⚠️ This is computed every time this method is called.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let users = Users::new_with_refreshed_list();
/// for user in users.list() {
/// println!("{} is in {:?}", user.name(), user.groups());
/// }
/// ```
pub fn groups(&self) -> Vec<Group> {
self.inner.groups()
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub(crate) struct GroupInner {
pub(crate) id: Gid,
pub(crate) name: String,
}
/// Type containing group information.
///
/// It is returned by [`User::groups`] or [`Groups::list`].
///
/// ```no_run
/// use sysinfo::Users;
///
/// let mut users = Users::new_with_refreshed_list();
///
/// for user in users.list() {
/// println!(
/// "user: (ID: {:?}, group ID: {:?}, name: {:?})",
/// user.id(),
/// user.group_id(),
/// user.name(),
/// );
/// for group in user.groups() {
/// println!("group: (ID: {:?}, name: {:?})", group.id(), group.name());
/// }
/// }
/// ```
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Group {
pub(crate) inner: GroupInner,
}
impl Group {
/// Returns the ID of the group.
///
/// ⚠️ This information is not set on Windows.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let mut users = Users::new_with_refreshed_list();
///
/// for user in users.list() {
/// for group in user.groups() {
/// println!("{:?}", group.id());
/// }
/// }
/// ```
pub fn id(&self) -> &Gid {
self.inner.id()
}
/// Returns the name of the group.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let mut users = Users::new_with_refreshed_list();
///
/// for user in users.list() {
/// for group in user.groups() {
/// println!("{}", group.name());
/// }
/// }
/// ```
pub fn name(&self) -> &str {
self.inner.name()
}
}
/// Interacting with users.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let mut users = Users::new();
/// for user in users.list() {
/// println!("{} is in {} groups", user.name(), user.groups().len());
/// }
/// ```
pub struct Users {
users: Vec<User>,
}
impl Default for Users {
fn default() -> Self {
Self::new()
}
}
impl From<Users> for Vec<User> {
fn from(users: Users) -> Self {
users.users
}
}
impl From<Vec<User>> for Users {
fn from(users: Vec<User>) -> Self {
Self { users }
}
}
impl std::ops::Deref for Users {
type Target = [User];
fn deref(&self) -> &Self::Target {
self.list()
}
}
impl std::ops::DerefMut for Users {
fn deref_mut(&mut self) -> &mut Self::Target {
self.list_mut()
}
}
impl<'a> IntoIterator for &'a Users {
type Item = &'a User;
type IntoIter = std::slice::Iter<'a, User>;
fn into_iter(self) -> Self::IntoIter {
self.list().iter()
}
}
impl<'a> IntoIterator for &'a mut Users {
type Item = &'a mut User;
type IntoIter = std::slice::IterMut<'a, User>;
fn into_iter(self) -> Self::IntoIter {
self.list_mut().iter_mut()
}
}
impl Users {
/// Creates a new empty [`Users`][crate::Users] type.
///
/// If you want it to be filled directly, take a look at [`Users::new_with_refreshed_list`].
///
/// ```no_run
/// use sysinfo::Users;
///
/// let mut users = Users::new();
/// users.refresh();
/// for user in users.list() {
/// println!("{user:?}");
/// }
/// ```
pub fn new() -> Self {
Self { users: Vec::new() }
}
/// Creates a new [`Users`][crate::Users] type with the user list loaded.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let mut users = Users::new_with_refreshed_list();
/// for user in users.list() {
/// println!("{user:?}");
/// }
/// ```
pub fn new_with_refreshed_list() -> Self {
let mut users = Self::new();
users.refresh();
users
}
/// Returns the users list.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let users = Users::new_with_refreshed_list();
/// for user in users.list() {
/// println!("{user:?}");
/// }
/// ```
pub fn list(&self) -> &[User] {
&self.users
}
/// Returns the users list.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let mut users = Users::new_with_refreshed_list();
/// users.list_mut().sort_by(|user1, user2| {
/// user1.name().partial_cmp(user2.name()).unwrap()
/// });
/// ```
pub fn list_mut(&mut self) -> &mut [User] {
&mut self.users
}
/// The user list will be emptied then completely recomputed.
///
/// ```no_run
/// use sysinfo::Users;
///
/// let mut users = Users::new();
/// users.refresh();
/// ```
pub fn refresh(&mut self) {
crate::sys::get_users(&mut self.users);
}
/// Returns the [`User`] matching the given `user_id`.
///
/// **Important**: The user list must be filled before using this method, otherwise it will
/// always return `None` (through the `refresh_*` methods).
///
/// It is a shorthand for:
///
/// ```ignore
/// # use sysinfo::Users;
/// let users = Users::new_with_refreshed_list();
/// users.list().find(|user| user.id() == user_id);
/// ```
///
/// Full example:
///
#[cfg_attr(feature = "system", doc = "```no_run")]
#[cfg_attr(not(feature = "system"), doc = "```ignore")]
/// use sysinfo::{Pid, System, Users};
///
/// let mut s = System::new_all();
/// let users = Users::new_with_refreshed_list();
///
/// if let Some(process) = s.process(Pid::from(1337)) {
/// if let Some(user_id) = process.user_id() {
/// println!("User for process 1337: {:?}", users.get_user_by_id(user_id));
/// }
/// }
/// ```
pub fn get_user_by_id(&self, user_id: &Uid) -> Option<&User> {
self.users.iter().find(|user| user.id() == user_id)
}
}
/// Interacting with groups.
///
/// ```no_run
/// use sysinfo::Groups;
///
/// let mut groups = Groups::new();
/// for group in groups.list() {
/// println!("{}", group.name());
/// }
/// ```
pub struct Groups {
groups: Vec<Group>,
}
impl Default for Groups {
fn default() -> Self {
Self::new()
}
}
impl From<Groups> for Vec<Group> {
fn from(groups: Groups) -> Self {
groups.groups
}
}
impl From<Vec<Group>> for Groups {
fn from(groups: Vec<Group>) -> Self {
Self { groups }
}
}
impl std::ops::Deref for Groups {
type Target = [Group];
fn deref(&self) -> &Self::Target {
self.list()
}
}
impl std::ops::DerefMut for Groups {
fn deref_mut(&mut self) -> &mut Self::Target {
self.list_mut()
}
}
impl<'a> IntoIterator for &'a Groups {
type Item = &'a Group;
type IntoIter = std::slice::Iter<'a, Group>;
fn into_iter(self) -> Self::IntoIter {
self.list().iter()
}
}
impl<'a> IntoIterator for &'a mut Groups {
type Item = &'a mut Group;
type IntoIter = std::slice::IterMut<'a, Group>;
fn into_iter(self) -> Self::IntoIter {
self.list_mut().iter_mut()
}
}
impl Groups {
/// Creates a new empty [`Groups`][crate::Groups] type.
///
/// If you want it to be filled directly, take a look at [`Groups::new_with_refreshed_list`].
///
/// ```no_run
/// use sysinfo::Groups;
///
/// let mut groups = Groups::new();
/// groups.refresh();
/// for group in groups.list() {
/// println!("{group:?}");
/// }
/// ```
pub fn new() -> Self {
Self { groups: Vec::new() }
}
/// Creates a new [`Groups`][crate::Groups] type with the group list loaded.
///
/// ```no_run
/// use sysinfo::Groups;
///
/// let mut groups = Groups::new_with_refreshed_list();
/// for group in groups.list() {
/// println!("{group:?}");
/// }
/// ```
pub fn new_with_refreshed_list() -> Self {
let mut groups = Self::new();
groups.refresh();
groups
}
/// Returns the groups list.
///
/// ```no_run
/// use sysinfo::Groups;
///
/// let groups = Groups::new_with_refreshed_list();
/// for group in groups.list() {
/// println!("{group:?}");
/// }
/// ```
pub fn list(&self) -> &[Group] {
&self.groups
}
/// Returns the groups list.
///
/// ```no_run
/// use sysinfo::Groups;
///
/// let mut groups = Groups::new_with_refreshed_list();
/// groups.list_mut().sort_by(|user1, user2| {
/// user1.name().partial_cmp(user2.name()).unwrap()
/// });
/// ```
pub fn list_mut(&mut self) -> &mut [Group] {
&mut self.groups
}
/// The group list will be emptied then completely recomputed.
///
/// ```no_run
/// use sysinfo::Groups;
///
/// let mut groups = Groups::new();
/// groups.refresh();
/// ```
pub fn refresh(&mut self) {
crate::sys::get_groups(&mut self.groups);
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn check_list() {
let mut users = Users::new();
assert!(users.list().is_empty());
users.refresh();
assert!(users.list().len() >= MIN_USERS);
}
// This test exists to ensure that the `TryFrom<usize>` and `FromStr` traits are implemented
// on `Uid`, `Gid` and `Pid`.
#[allow(clippy::unnecessary_fallible_conversions)]
#[test]
fn check_uid_gid_from_impls() {
use std::convert::TryFrom;
use std::str::FromStr;
#[cfg(not(windows))]
{
assert!(crate::Uid::try_from(0usize).is_ok());
assert!(crate::Uid::from_str("0").is_ok());
}
#[cfg(windows)]
{
assert!(crate::Uid::from_str("S-1-5-18").is_ok()); // SECURITY_LOCAL_SYSTEM_RID
assert!(crate::Uid::from_str("0").is_err());
}
assert!(crate::Gid::try_from(0usize).is_ok());
assert!(crate::Gid::from_str("0").is_ok());
}
#[test]
fn check_groups() {
if !crate::IS_SUPPORTED_SYSTEM {
return;
}
assert!(!Groups::new_with_refreshed_list().is_empty());
}
}