blob: 41f83c1c80f451006b7a972d4f73dbac974e804d [file] [log] [blame]
// 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;