blob: 690aceee1c91890896697b251a2ca5875689efa1 [file] [log] [blame]
// Take a look at the license at the top of the repository in the LICENSE file.
use crate::{
common::{Gid, Uid},
User,
};
use crate::sys::utils;
use libc::{c_char, endpwent, getgrgid, getgrouplist, getpwent, gid_t, setpwent, strlen};
fn get_user_groups(name: *const c_char, group_id: gid_t) -> Vec<String> {
let mut add = 0;
loop {
let mut nb_groups = 256 + add;
let mut groups = Vec::with_capacity(nb_groups as _);
unsafe {
if getgrouplist(name, group_id as _, groups.as_mut_ptr(), &mut nb_groups) == -1 {
add += 100;
continue;
}
groups.set_len(nb_groups as _);
return groups
.into_iter()
.filter_map(|g| {
let group = getgrgid(g as _);
if group.is_null() {
return None;
}
utils::cstr_to_rust((*group).gr_name)
})
.collect();
}
}
}
fn endswith(s1: *const c_char, s2: &[u8]) -> bool {
if s1.is_null() {
return false;
}
unsafe {
let mut len = strlen(s1) as isize - 1;
let mut i = s2.len() as isize - 1;
while len >= 0 && i >= 0 && *s1.offset(len) == s2[i as usize] as _ {
i -= 1;
len -= 1;
}
i == -1
}
}
fn users_list<F>(filter: F) -> Vec<User>
where
F: Fn(*const c_char, u32) -> bool,
{
let mut users = Vec::new();
unsafe {
setpwent();
loop {
let pw = getpwent();
if pw.is_null() {
break;
}
if !filter((*pw).pw_shell, (*pw).pw_uid) {
// This is not a "real" or "local" user.
continue;
}
let groups = get_user_groups((*pw).pw_name, (*pw).pw_gid);
let uid = (*pw).pw_uid;
let gid = (*pw).pw_gid;
if let Some(name) = utils::cstr_to_rust((*pw).pw_name) {
users.push(User {
uid: Uid(uid),
gid: Gid(gid),
name,
groups,
});
}
}
endpwent();
}
users.sort_unstable_by(|x, y| x.name.partial_cmp(&y.name).unwrap());
users.dedup_by(|a, b| a.name == b.name);
users
}
pub(crate) fn get_users_list() -> Vec<User> {
users_list(|shell, uid| {
!endswith(shell, b"/false") && !endswith(shell, b"/uucico") && uid < 65536
})
}
// This was the OSX-based solution. It provides enough information, but what a mess!
// pub fn get_users_list() -> Vec<User> {
// let mut users = Vec::new();
// let node_name = b"/Local/Default\0";
// unsafe {
// let node_name = ffi::CFStringCreateWithCStringNoCopy(
// std::ptr::null_mut(),
// node_name.as_ptr() as *const c_char,
// ffi::kCFStringEncodingMacRoman,
// ffi::kCFAllocatorNull as *mut c_void,
// );
// let node_ref = ffi::ODNodeCreateWithName(
// ffi::kCFAllocatorDefault,
// ffi::kODSessionDefault,
// node_name,
// std::ptr::null_mut(),
// );
// let query = ffi::ODQueryCreateWithNode(
// ffi::kCFAllocatorDefault,
// node_ref,
// ffi::kODRecordTypeUsers as _, // kODRecordTypeGroups
// std::ptr::null(),
// 0,
// std::ptr::null(),
// std::ptr::null(),
// 0,
// std::ptr::null_mut(),
// );
// if query.is_null() {
// return users;
// }
// let results = ffi::ODQueryCopyResults(
// query,
// false as _,
// std::ptr::null_mut(),
// );
// let len = ffi::CFArrayGetCount(results);
// for i in 0..len {
// let name = match get_user_name(ffi::CFArrayGetValueAtIndex(results, i)) {
// Some(n) => n,
// None => continue,
// };
// let groups = get_user_groups(&name);
// users.push(User { name });
// }
// ffi::CFRelease(results as *const c_void);
// ffi::CFRelease(query as *const c_void);
// ffi::CFRelease(node_ref as *const c_void);
// ffi::CFRelease(node_name as *const c_void);
// }
// users.sort_unstable_by(|x, y| x.name.partial_cmp(&y.name).unwrap());
// return users;
// }
// fn get_user_name(result: *const c_void) -> Option<String> {
// let user_name = ffi::ODRecordGetRecordName(result as _);
// let ptr = ffi::CFStringGetCharactersPtr(user_name);
// String::from_utf16(&if ptr.is_null() {
// let len = ffi::CFStringGetLength(user_name); // It returns the len in UTF-16 code pairs.
// if len == 0 {
// continue;
// }
// let mut v = Vec::with_capacity(len as _);
// for x in 0..len {
// v.push(ffi::CFStringGetCharacterAtIndex(user_name, x));
// }
// v
// } else {
// let mut v: Vec<u16> = Vec::new();
// let mut x = 0;
// loop {
// let letter = *ptr.offset(x);
// if letter == 0 {
// break;
// }
// v.push(letter);
// x += 1;
// }
// v
// }.ok()
// }