sysinfo/
users.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{
4    common::{Gid, Uid},
5    User,
6};
7
8use libc::{getgrgid_r, getgrouplist};
9use std::fs::File;
10use std::io::Read;
11
12pub(crate) unsafe fn get_group_name(
13    id: libc::gid_t,
14    buffer: &mut Vec<libc::c_char>,
15) -> Option<String> {
16    let mut g = std::mem::MaybeUninit::<libc::group>::uninit();
17    let mut tmp_ptr = std::ptr::null_mut();
18    let mut last_errno = 0;
19    loop {
20        if retry_eintr!(set_to_0 => last_errno => getgrgid_r(
21            id as _,
22            g.as_mut_ptr() as _,
23            buffer.as_mut_ptr(),
24            buffer.capacity() as _,
25            &mut tmp_ptr as _
26        )) != 0
27        {
28            // If there was not enough memory, we give it more.
29            if last_errno == libc::ERANGE as _ {
30                buffer.reserve(2048);
31                continue;
32            }
33            return None;
34        }
35        break;
36    }
37    let g = g.assume_init();
38    let mut group_name = Vec::new();
39    if g.gr_name.is_null() {
40        return Some(String::new());
41    }
42    let c_group_name = g.gr_name;
43    let mut x = 0;
44    loop {
45        let c = *c_group_name.offset(x);
46        if c == 0 {
47            break;
48        }
49        group_name.push(c as u8);
50        x += 1;
51    }
52    String::from_utf8(group_name).ok()
53}
54
55pub(crate) unsafe fn get_user_groups(
56    name: *const libc::c_char,
57    group_id: libc::gid_t,
58    groups: &mut Vec<crate::GroupId>,
59    buffer: &mut Vec<libc::c_char>,
60) -> Vec<String> {
61    loop {
62        let mut nb_groups = groups.capacity();
63        if getgrouplist(
64            name,
65            group_id as _,
66            groups.as_mut_ptr(),
67            &mut nb_groups as *mut _ as *mut _,
68        ) == -1
69        {
70            groups.reserve(256);
71            continue;
72        }
73        groups.set_len(nb_groups as _);
74        return groups
75            .iter()
76            .filter_map(|group_id| crate::users::get_group_name(*group_id as _, buffer))
77            .collect();
78    }
79}
80
81// Not used by mac.
82#[allow(unused)]
83pub(crate) fn get_users_list() -> Vec<User> {
84    #[inline]
85    fn parse_id(id: &str) -> Option<u32> {
86        id.parse::<u32>().ok()
87    }
88
89    let mut s = String::new();
90    let mut buffer = Vec::with_capacity(2048);
91    let mut groups = Vec::with_capacity(256);
92
93    let _ = File::open("/etc/passwd").and_then(|mut f| f.read_to_string(&mut s));
94    s.lines()
95        .filter_map(|line| {
96            let mut parts = line.split(':');
97            if let Some(username) = parts.next() {
98                let mut parts = parts.skip(1);
99                // Skip the user if the uid cannot be parsed correctly
100                if let Some(uid) = parts.next().and_then(parse_id) {
101                    if let Some(group_id) = parts.next().and_then(parse_id) {
102                        let mut c_user = username.as_bytes().to_vec();
103                        c_user.push(0);
104                        // Let's get all the group names!
105                        return Some(User {
106                            uid: Uid(uid),
107                            gid: Gid(group_id),
108                            name: username.to_owned(),
109                            groups: unsafe {
110                                get_user_groups(
111                                    c_user.as_ptr() as *const _,
112                                    group_id,
113                                    &mut groups,
114                                    &mut buffer,
115                                )
116                            },
117                        });
118                    }
119                }
120            }
121            None
122        })
123        .collect()
124}