| // Take a look at the license at the top of the repository in the LICENSE file. |
| |
| use crate::{ |
| common::{Gid, Uid}, |
| Group, |
| }; |
| |
| #[cfg(not(any(target_os = "macos", target_os = "ios")))] |
| use crate::User; |
| |
| use libc::{getgrgid_r, getgrouplist}; |
| |
| pub(crate) struct UserInner { |
| pub(crate) uid: Uid, |
| pub(crate) gid: Gid, |
| pub(crate) name: String, |
| c_user: Vec<u8>, |
| } |
| |
| impl UserInner { |
| pub(crate) fn new(uid: Uid, gid: Gid, name: String) -> Self { |
| let mut c_user = name.as_bytes().to_vec(); |
| c_user.push(0); |
| Self { |
| uid, |
| gid, |
| name, |
| c_user, |
| } |
| } |
| |
| pub(crate) fn id(&self) -> &Uid { |
| &self.uid |
| } |
| |
| pub(crate) fn group_id(&self) -> Gid { |
| self.gid |
| } |
| |
| pub(crate) fn name(&self) -> &str { |
| &self.name |
| } |
| |
| pub(crate) fn groups(&self) -> Vec<Group> { |
| unsafe { get_user_groups(self.c_user.as_ptr() as *const _, self.gid.0 as _) } |
| } |
| } |
| |
| pub(crate) unsafe fn get_group_name( |
| id: libc::gid_t, |
| buffer: &mut Vec<libc::c_char>, |
| ) -> Option<String> { |
| let mut g = std::mem::MaybeUninit::<libc::group>::uninit(); |
| let mut tmp_ptr = std::ptr::null_mut(); |
| let mut last_errno = 0; |
| loop { |
| if retry_eintr!(set_to_0 => last_errno => getgrgid_r( |
| id as _, |
| g.as_mut_ptr() as _, |
| buffer.as_mut_ptr(), |
| buffer.capacity() as _, |
| &mut tmp_ptr as _ |
| )) != 0 |
| { |
| // If there was not enough memory, we give it more. |
| if last_errno == libc::ERANGE as _ { |
| // Needs to be updated for `Vec::reserve` to actually add additional capacity. |
| // In here it's "fine" since we never read from `buffer`. |
| buffer.set_len(buffer.capacity()); |
| buffer.reserve(2048); |
| continue; |
| } |
| return None; |
| } |
| break; |
| } |
| let g = g.assume_init(); |
| super::utils::cstr_to_rust(g.gr_name) |
| } |
| |
| pub(crate) unsafe fn get_user_groups( |
| name: *const libc::c_char, |
| group_id: libc::gid_t, |
| ) -> Vec<Group> { |
| let mut buffer = Vec::with_capacity(2048); |
| let mut groups = Vec::with_capacity(256); |
| |
| loop { |
| let mut nb_groups = groups.capacity(); |
| if getgrouplist( |
| name, |
| group_id as _, |
| groups.as_mut_ptr(), |
| &mut nb_groups as *mut _ as *mut _, |
| ) == -1 |
| { |
| // Ensure the length matches the number of returned groups. |
| // Needs to be updated for `Vec::reserve` to actually add additional capacity. |
| groups.set_len(nb_groups as _); |
| groups.reserve(256); |
| continue; |
| } |
| groups.set_len(nb_groups as _); |
| return groups |
| .iter() |
| .filter_map(|group_id| { |
| let name = get_group_name(*group_id as _, &mut buffer)?; |
| Some(Group { |
| name, |
| id: Gid(*group_id as _), |
| }) |
| }) |
| .collect(); |
| } |
| } |
| |
| // Not used by mac. |
| #[cfg(not(any(target_os = "macos", target_os = "ios")))] |
| pub(crate) fn get_users(users: &mut Vec<User>) { |
| use std::fs::File; |
| use std::io::Read; |
| |
| #[inline] |
| fn parse_id(id: &str) -> Option<u32> { |
| id.parse::<u32>().ok() |
| } |
| |
| users.clear(); |
| |
| let mut s = String::new(); |
| |
| let _ = File::open("/etc/passwd").and_then(|mut f| f.read_to_string(&mut s)); |
| for line in s.lines() { |
| let mut parts = line.split(':'); |
| if let Some(username) = parts.next() { |
| let mut parts = parts.skip(1); |
| // Skip the user if the uid cannot be parsed correctly |
| if let Some(uid) = parts.next().and_then(parse_id) { |
| if let Some(group_id) = parts.next().and_then(parse_id) { |
| users.push(User { |
| inner: UserInner::new(Uid(uid), Gid(group_id), username.to_owned()), |
| }); |
| } |
| } |
| } |
| } |
| } |
| |
| #[cfg(any(target_os = "macos", target_os = "ios"))] |
| pub(crate) use crate::unix::apple::users::get_users; |