shell_rs/core/
grp.rs

1// Copyright (c) 2021 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
2// Use of this source is governed by Apache-2.0 License that can be found
3// in the LICENSE file.
4
5use std::fs::File;
6use std::io::{BufRead, BufReader};
7use std::str::FromStr;
8
9use crate::error::{Error, ErrorKind};
10
11#[derive(Debug, Default, Clone)]
12pub struct Group {
13    /// Group name.
14    pub name: String,
15
16    /// Group passwd, commonly empty.
17    pub passwd: String,
18
19    /// Group ID.
20    pub gid: nc::gid_t,
21
22    /// Usernames in this group.
23    pub mem: Vec<String>,
24}
25
26impl FromStr for Group {
27    type Err = Error;
28
29    fn from_str(s: &str) -> Result<Self, Self::Err> {
30        let parts: Vec<&str> = s.split(':').collect();
31        if parts.len() != 4 {
32            return Err(Error::from_string(
33                ErrorKind::PwdGroupError,
34                format!("Invalid group entry: {}", s),
35            ));
36        }
37
38        let gid = parts[2].parse()?;
39        let mem = parts[3]
40            .split(",")
41            .map(|name| name.trim().to_string())
42            .collect();
43        Ok(Self {
44            name: parts[0].to_string(),
45            passwd: parts[1].to_string(),
46            gid,
47            mem,
48        })
49    }
50}
51
52#[derive(Debug)]
53pub struct GroupIter {
54    reader: BufReader<File>,
55}
56
57pub fn getgrent() -> Result<GroupIter, Error> {
58    let fd = File::open("/etc/group")?;
59    let reader = BufReader::new(fd);
60    Ok(GroupIter { reader })
61}
62
63impl Iterator for GroupIter {
64    type Item = Group;
65
66    fn next(&mut self) -> Option<Self::Item> {
67        let mut line = String::new();
68        let len = self.reader.read_line(&mut line);
69        if len.is_err() {
70            return None;
71        }
72
73        match Group::from_str(&line.trim()) {
74            Ok(p) => Some(p),
75            Err(err) => {
76                log::error!("{:?}", err);
77                None
78            }
79        }
80    }
81}
82
83pub fn getgrgid(gid: nc::gid_t) -> Result<Option<Group>, Error> {
84    let iter = getgrent()?;
85    for g in iter {
86        if g.gid == gid {
87            return Ok(Some(g));
88        }
89    }
90    Ok(None)
91}
92
93pub fn getgrname(name: &str) -> Result<Option<Group>, Error> {
94    let iter = getgrent()?;
95    for g in iter {
96        if g.name == name {
97            return Ok(Some(g));
98        }
99    }
100    Ok(None)
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_group_iter() {
109        let iter = getgrent();
110        assert!(iter.is_ok());
111        let mut iter = iter.unwrap();
112        let g0 = iter.next();
113        println!("g0: {:#?}", g0);
114        assert!(g0.is_some());
115    }
116
117    #[test]
118    fn test_getgrgid() {
119        let mail_gid = 8;
120        let g = getgrgid(mail_gid);
121        assert!(g.is_ok());
122        let g = g.unwrap();
123        assert!(g.is_some());
124        let g = g.unwrap();
125        assert_eq!(g.gid, mail_gid);
126    }
127
128    #[test]
129    fn test_getgrname() {
130        let mail_name = "mail";
131        let g = getgrname(mail_name);
132        assert!(g.is_ok());
133        let g = g.unwrap();
134        assert!(g.is_some());
135        let g = g.unwrap();
136        assert_eq!(g.name, mail_name);
137    }
138}