mysqladm/core/
common.rs

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
use anyhow::Context;
use nix::unistd::{Group as LibcGroup, User as LibcUser};

#[cfg(not(target_os = "macos"))]
use std::ffi::CString;

pub const DEFAULT_CONFIG_PATH: &str = "/etc/mysqladm/config.toml";
pub const DEFAULT_SOCKET_PATH: &str = "/run/mysqladm/mysqladm.sock";

pub struct UnixUser {
    pub username: String,
    pub groups: Vec<String>,
}

// TODO: these functions are somewhat critical, and should have integration tests

#[cfg(target_os = "macos")]
fn get_unix_groups(_user: &LibcUser) -> anyhow::Result<Vec<LibcGroup>> {
    // Return an empty list on macOS since there is no `getgrouplist` function
    Ok(vec![])
}

#[cfg(not(target_os = "macos"))]
fn get_unix_groups(user: &LibcUser) -> anyhow::Result<Vec<LibcGroup>> {
    let user_cstr =
        CString::new(user.name.as_bytes()).context("Failed to convert username to CStr")?;
    let groups = nix::unistd::getgrouplist(&user_cstr, user.gid)?
        .iter()
        .filter_map(|gid| match LibcGroup::from_gid(*gid) {
            Ok(Some(group)) => Some(group),
            Ok(None) => None,
            Err(e) => {
                log::warn!(
                    "Failed to look up group with GID {}: {}\nIgnoring...",
                    gid,
                    e
                );
                None
            }
        })
        .collect::<Vec<LibcGroup>>();

    Ok(groups)
}

impl UnixUser {
    pub fn from_uid(uid: u32) -> anyhow::Result<Self> {
        let libc_uid = nix::unistd::Uid::from_raw(uid);
        let libc_user = LibcUser::from_uid(libc_uid)
            .context("Failed to look up your UNIX username")?
            .ok_or(anyhow::anyhow!("Failed to look up your UNIX username"))?;

        let groups = get_unix_groups(&libc_user)?;

        Ok(UnixUser {
            username: libc_user.name,
            groups: groups.iter().map(|g| g.name.to_owned()).collect(),
        })
    }

    pub fn from_enviroment() -> anyhow::Result<Self> {
        let libc_uid = nix::unistd::getuid();
        UnixUser::from_uid(libc_uid.as_raw())
    }
}

#[inline]
pub(crate) fn yn(b: bool) -> &'static str {
    if b {
        "Y"
    } else {
        "N"
    }
}

#[inline]
pub(crate) fn rev_yn(s: &str) -> Option<bool> {
    match s.to_lowercase().as_str() {
        "y" => Some(true),
        "n" => Some(false),
        _ => None,
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_yn() {
        assert_eq!(yn(true), "Y");
        assert_eq!(yn(false), "N");
    }

    #[test]
    fn test_rev_yn() {
        assert_eq!(rev_yn("Y"), Some(true));
        assert_eq!(rev_yn("y"), Some(true));
        assert_eq!(rev_yn("N"), Some(false));
        assert_eq!(rev_yn("n"), Some(false));
        assert_eq!(rev_yn("X"), None);
    }
}