shell_rs/core/
pwd.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 Passwd {
13    /// Username.
14    pub name: String,
15
16    /// Passsword, commonly empty.
17    pub passwd: String,
18
19    /// User ID.
20    pub uid: nc::uid_t,
21
22    /// User default group ID.
23    pub gid: nc::gid_t,
24
25    /// Real name.
26    pub gecos: String,
27
28    /// Home directory.
29    pub home_dir: String,
30
31    /// Default shell.
32    pub shell: String,
33}
34
35impl FromStr for Passwd {
36    type Err = Error;
37
38    fn from_str(s: &str) -> Result<Self, Self::Err> {
39        let parts: Vec<&str> = s.split(':').collect();
40        if parts.len() != 7 {
41            return Err(Error::from_string(
42                ErrorKind::PwdError,
43                format!("Invalid passwd entry: {}", s),
44            ));
45        }
46
47        let uid = parts[2].parse()?;
48        let gid = parts[3].parse()?;
49        Ok(Self {
50            name: parts[0].to_string(),
51            passwd: parts[1].to_string(),
52            uid,
53            gid,
54            gecos: parts[4].to_string(),
55            home_dir: parts[5].to_string(),
56            shell: parts[6].to_string(),
57        })
58    }
59}
60
61#[derive(Debug)]
62pub struct PasswdIter {
63    reader: BufReader<File>,
64}
65
66pub fn getpwent() -> Result<PasswdIter, Error> {
67    let fd = File::open("/etc/passwd")?;
68    let reader = BufReader::new(fd);
69    Ok(PasswdIter { reader })
70}
71
72impl Iterator for PasswdIter {
73    type Item = Passwd;
74
75    fn next(&mut self) -> Option<Self::Item> {
76        let mut line = String::new();
77        let len = self.reader.read_line(&mut line);
78        if len.is_err() {
79            return None;
80        }
81
82        match Passwd::from_str(&line.trim()) {
83            Ok(p) => Some(p),
84            Err(err) => {
85                log::error!("{:?}", err);
86                None
87            }
88        }
89    }
90}
91
92pub fn getpwuid(uid: nc::uid_t) -> Result<Option<Passwd>, Error> {
93    let iter = getpwent()?;
94    for p in iter {
95        if p.uid == uid {
96            return Ok(Some(p));
97        }
98    }
99    Ok(None)
100}
101
102pub fn getpwname(name: &str) -> Result<Option<Passwd>, Error> {
103    let iter = getpwent()?;
104    for p in iter {
105        if p.name == name {
106            return Ok(Some(p));
107        }
108    }
109    Ok(None)
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn test_passwd_iter() {
118        let iter = getpwent();
119        assert!(iter.is_ok());
120        let mut iter = iter.unwrap();
121        let p0 = iter.next();
122        println!("p0: {:#?}", p0);
123        assert!(p0.is_some());
124    }
125
126    #[test]
127    fn test_getpwuid() {
128        let mail_uid = 6;
129        let p = getpwuid(mail_uid);
130        assert!(p.is_ok());
131        let p = p.unwrap();
132        assert!(p.is_some());
133        let p = p.unwrap();
134        assert_eq!(p.uid, mail_uid);
135    }
136
137    #[test]
138    fn test_getpwname() {
139        let mail_name = "mail";
140        let p = getpwname(mail_name);
141        assert!(p.is_ok());
142        let p = p.unwrap();
143        assert!(p.is_some());
144        let p = p.unwrap();
145        assert_eq!(p.name, mail_name);
146    }
147}