summaryrefslogtreecommitdiffstats
path: root/vendor/sysinfo/src/users.rs
blob: 6d3343bd7be0c6e5cd6a42a4f302a6acc8dbbcdd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Take a look at the license at the top of the repository in the LICENSE file.

use crate::{
    common::{Gid, Uid},
    User,
};

use libc::{getgrgid_r, getgrouplist};
use std::fs::File;
use std::io::Read;

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 _ {
                buffer.reserve(2048);
                continue;
            }
            return None;
        }
        break;
    }
    let g = g.assume_init();
    let mut group_name = Vec::new();
    let c_group_name = g.gr_name;
    let mut x = 0;
    loop {
        let c = *c_group_name.offset(x);
        if c == 0 {
            break;
        }
        group_name.push(c as u8);
        x += 1;
    }
    String::from_utf8(group_name).ok()
}

pub(crate) unsafe fn get_user_groups(
    name: *const libc::c_char,
    group_id: libc::gid_t,
    groups: &mut Vec<crate::GroupId>,
    buffer: &mut Vec<libc::c_char>,
) -> Vec<String> {
    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
        {
            groups.reserve(256);
            continue;
        }
        groups.set_len(nb_groups as _);
        return groups
            .iter()
            .filter_map(|group_id| crate::users::get_group_name(*group_id as _, buffer))
            .collect();
    }
}

// Not used by mac.
#[allow(unused)]
pub(crate) fn get_users_list() -> Vec<User> {
    #[inline]
    fn parse_id(id: &str) -> Option<u32> {
        id.parse::<u32>().ok()
    }

    let mut s = String::new();
    let mut buffer = Vec::with_capacity(2048);
    let mut groups = Vec::with_capacity(256);

    let _ = File::open("/etc/passwd").and_then(|mut f| f.read_to_string(&mut s));
    s.lines()
        .filter_map(|line| {
            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) {
                        let mut c_user = username.as_bytes().to_vec();
                        c_user.push(0);
                        // Let's get all the group names!
                        return Some(User {
                            uid: Uid(uid),
                            gid: Gid(group_id),
                            name: username.to_owned(),
                            groups: unsafe {
                                get_user_groups(
                                    c_user.as_ptr() as *const _,
                                    group_id,
                                    &mut groups,
                                    &mut buffer,
                                )
                            },
                        });
                    }
                }
            }
            None
        })
        .collect()
}