1use crate::{
4 common::{Gid, Uid},
5 Group,
6};
7
8#[cfg(not(any(target_os = "macos", target_os = "ios")))]
9use crate::User;
10
11use libc::{getgrgid_r, getgrouplist};
12
13pub(crate) struct UserInner {
14 pub(crate) uid: Uid,
15 pub(crate) gid: Gid,
16 pub(crate) name: String,
17 c_user: Vec<u8>,
18}
19
20impl UserInner {
21 pub(crate) fn new(uid: Uid, gid: Gid, name: String) -> Self {
22 let mut c_user = name.as_bytes().to_vec();
23 c_user.push(0);
24 Self {
25 uid,
26 gid,
27 name,
28 c_user,
29 }
30 }
31
32 pub(crate) fn id(&self) -> &Uid {
33 &self.uid
34 }
35
36 pub(crate) fn group_id(&self) -> Gid {
37 self.gid
38 }
39
40 pub(crate) fn name(&self) -> &str {
41 &self.name
42 }
43
44 pub(crate) fn groups(&self) -> Vec<Group> {
45 unsafe { get_user_groups(self.c_user.as_ptr() as *const _, self.gid.0 as _) }
46 }
47}
48
49pub(crate) unsafe fn get_group_name(
50 id: libc::gid_t,
51 buffer: &mut Vec<libc::c_char>,
52) -> Option<String> {
53 let mut g = std::mem::MaybeUninit::<libc::group>::uninit();
54 let mut tmp_ptr = std::ptr::null_mut();
55 let mut last_errno = 0;
56 loop {
57 if retry_eintr!(set_to_0 => last_errno => getgrgid_r(
58 id as _,
59 g.as_mut_ptr() as _,
60 buffer.as_mut_ptr(),
61 buffer.capacity() as _,
62 &mut tmp_ptr as _
63 )) != 0
64 {
65 if last_errno == libc::ERANGE as _ {
67 buffer.set_len(buffer.capacity());
70 buffer.reserve(2048);
71 continue;
72 }
73 return None;
74 }
75 break;
76 }
77 let g = g.assume_init();
78 super::utils::cstr_to_rust(g.gr_name)
79}
80
81pub(crate) unsafe fn get_user_groups(
82 name: *const libc::c_char,
83 group_id: libc::gid_t,
84) -> Vec<Group> {
85 let mut buffer = Vec::with_capacity(2048);
86 let mut groups = Vec::with_capacity(256);
87
88 loop {
89 let mut nb_groups = groups.capacity();
90 if getgrouplist(
91 name,
92 group_id as _,
93 groups.as_mut_ptr(),
94 &mut nb_groups as *mut _ as *mut _,
95 ) == -1
96 {
97 groups.set_len(nb_groups as _);
100 groups.reserve(256);
101 continue;
102 }
103 groups.set_len(nb_groups as _);
104 return groups
105 .iter()
106 .filter_map(|group_id| {
107 let name = get_group_name(*group_id as _, &mut buffer)?;
108 Some(Group {
109 inner: crate::GroupInner::new(Gid(*group_id as _), name),
110 })
111 })
112 .collect();
113 }
114}
115
116#[cfg(not(any(target_os = "macos", target_os = "ios")))]
118pub(crate) fn get_users(users: &mut Vec<User>) {
119 use std::fs::File;
120 use std::io::Read;
121
122 #[inline]
123 fn parse_id(id: &str) -> Option<u32> {
124 id.parse::<u32>().ok()
125 }
126
127 users.clear();
128
129 let mut s = String::new();
130
131 let _ = File::open("/etc/passwd").and_then(|mut f| f.read_to_string(&mut s));
132 for line in s.lines() {
133 let mut parts = line.split(':');
134 if let Some(username) = parts.next() {
135 let mut parts = parts.skip(1);
136 if let Some(uid) = parts.next().and_then(parse_id) {
138 if let Some(group_id) = parts.next().and_then(parse_id) {
139 users.push(User {
140 inner: UserInner::new(Uid(uid), Gid(group_id), username.to_owned()),
141 });
142 }
143 }
144 }
145 }
146}
147
148#[cfg(any(target_os = "macos", target_os = "ios"))]
149pub(crate) use crate::unix::apple::users::get_users;