1use crate::{
4 common::{Gid, Uid},
5 Group, User,
6};
7use libc::{getgrgid_r, getgrouplist};
8
9#[cfg(not(target_os = "android"))]
10use libc::{endpwent, getpwent, setpwent};
11
12#[cfg(target_os = "android")]
14extern "C" {
15 fn getpwent() -> *mut libc::passwd;
16 fn setpwent();
17 fn endpwent();
18}
19
20pub(crate) struct UserInner {
21 pub(crate) uid: Uid,
22 pub(crate) gid: Gid,
23 pub(crate) name: String,
24 c_user: Vec<u8>,
25}
26
27impl UserInner {
28 pub(crate) fn new(uid: Uid, gid: Gid, name: String) -> Self {
29 let mut c_user = name.as_bytes().to_vec();
30 c_user.push(0);
31 Self {
32 uid,
33 gid,
34 name,
35 c_user,
36 }
37 }
38
39 pub(crate) fn id(&self) -> &Uid {
40 &self.uid
41 }
42
43 pub(crate) fn group_id(&self) -> Gid {
44 self.gid
45 }
46
47 pub(crate) fn name(&self) -> &str {
48 &self.name
49 }
50
51 pub(crate) fn groups(&self) -> Vec<Group> {
52 unsafe { get_user_groups(self.c_user.as_ptr() as *const _, self.gid.0 as _) }
53 }
54}
55
56pub(crate) unsafe fn get_group_name(
57 id: libc::gid_t,
58 buffer: &mut Vec<libc::c_char>,
59) -> Option<String> {
60 let mut g = std::mem::MaybeUninit::<libc::group>::uninit();
61 let mut tmp_ptr = std::ptr::null_mut();
62 let mut last_errno = 0;
63 loop {
64 if retry_eintr!(set_to_0 => last_errno => getgrgid_r(
65 id as _,
66 g.as_mut_ptr() as _,
67 buffer.as_mut_ptr(),
68 buffer.capacity() as _,
69 &mut tmp_ptr as _
70 )) != 0
71 {
72 if last_errno == libc::ERANGE as _ {
74 buffer.set_len(buffer.capacity());
77 buffer.reserve(2048);
78 continue;
79 }
80 return None;
81 }
82 break;
83 }
84 let g = g.assume_init();
85 super::utils::cstr_to_rust(g.gr_name)
86}
87
88pub(crate) unsafe fn get_user_groups(
89 name: *const libc::c_char,
90 group_id: libc::gid_t,
91) -> Vec<Group> {
92 let mut buffer = Vec::with_capacity(2048);
93 let mut groups = Vec::with_capacity(256);
94
95 loop {
96 let mut nb_groups = groups.capacity();
97 if getgrouplist(
98 name,
99 group_id as _,
100 groups.as_mut_ptr(),
101 &mut nb_groups as *mut _ as *mut _,
102 ) == -1
103 {
104 groups.set_len(nb_groups as _);
107 groups.reserve(256);
108 continue;
109 }
110 groups.set_len(nb_groups as _);
111 return groups
112 .iter()
113 .filter_map(|group_id| {
114 let name = get_group_name(*group_id as _, &mut buffer)?;
115 Some(Group {
116 inner: crate::GroupInner::new(Gid(*group_id as _), name),
117 })
118 })
119 .collect();
120 }
121}
122
123pub(crate) fn get_users(users: &mut Vec<User>) {
124 fn filter(shell: *const std::ffi::c_char, uid: u32) -> bool {
125 !endswith(shell, b"/false") && !endswith(shell, b"/uucico") && uid < 65536
126 }
127
128 users.clear();
129
130 let mut users_map = std::collections::HashMap::with_capacity(10);
131
132 unsafe {
133 setpwent();
134 loop {
135 let pw = getpwent();
136 if pw.is_null() {
137 if std::io::Error::last_os_error().kind() == std::io::ErrorKind::Interrupted {
139 continue;
140 }
141 break;
142 }
143
144 if !filter((*pw).pw_shell, (*pw).pw_uid) {
145 continue;
147 }
148 if let Some(name) = crate::unix::utils::cstr_to_rust((*pw).pw_name) {
149 if users_map.contains_key(&name) {
150 continue;
151 }
152
153 let uid = (*pw).pw_uid;
154 let gid = (*pw).pw_gid;
155 users_map.insert(name, (Uid(uid), Gid(gid)));
156 }
157 }
158 endpwent();
159 }
160 for (name, (uid, gid)) in users_map {
161 users.push(User {
162 inner: UserInner::new(uid, gid, name),
163 });
164 }
165}
166
167fn endswith(s1: *const std::ffi::c_char, s2: &[u8]) -> bool {
168 if s1.is_null() {
169 return false;
170 }
171 unsafe {
172 let mut len = libc::strlen(s1) as isize - 1;
173 let mut i = s2.len() as isize - 1;
174 while len >= 0 && i >= 0 && *s1.offset(len) == s2[i as usize] as _ {
175 i -= 1;
176 len -= 1;
177 }
178 i == -1
179 }
180}