rustpython_vm/stdlib/
pwd.rs

1pub(crate) use pwd::make_module;
2
3#[pymodule]
4mod pwd {
5    use crate::{
6        builtins::{PyIntRef, PyStrRef},
7        convert::{IntoPyException, ToPyObject},
8        exceptions,
9        types::PyStructSequence,
10        PyObjectRef, PyResult, VirtualMachine,
11    };
12    use nix::unistd::{self, User};
13    use std::ptr::NonNull;
14
15    #[pyattr]
16    #[pyclass(module = "pwd", name = "struct_passwd")]
17    #[derive(PyStructSequence)]
18    struct Passwd {
19        pw_name: String,
20        pw_passwd: String,
21        pw_uid: u32,
22        pw_gid: u32,
23        pw_gecos: String,
24        pw_dir: String,
25        pw_shell: String,
26    }
27    #[pyclass(with(PyStructSequence))]
28    impl Passwd {}
29
30    impl From<User> for Passwd {
31        fn from(user: User) -> Self {
32            // this is just a pain...
33            let cstr_lossy = |s: std::ffi::CString| {
34                s.into_string()
35                    .unwrap_or_else(|e| e.into_cstring().to_string_lossy().into_owned())
36            };
37            let pathbuf_lossy = |p: std::path::PathBuf| {
38                p.into_os_string()
39                    .into_string()
40                    .unwrap_or_else(|s| s.to_string_lossy().into_owned())
41            };
42            Passwd {
43                pw_name: user.name,
44                pw_passwd: cstr_lossy(user.passwd),
45                pw_uid: user.uid.as_raw(),
46                pw_gid: user.gid.as_raw(),
47                pw_gecos: cstr_lossy(user.gecos),
48                pw_dir: pathbuf_lossy(user.dir),
49                pw_shell: pathbuf_lossy(user.shell),
50            }
51        }
52    }
53
54    #[pyfunction]
55    fn getpwnam(name: PyStrRef, vm: &VirtualMachine) -> PyResult<Passwd> {
56        let pw_name = name.as_str();
57        if pw_name.contains('\0') {
58            return Err(exceptions::cstring_error(vm));
59        }
60        let user = User::from_name(name.as_str()).map_err(|err| err.into_pyexception(vm))?;
61        let user = user.ok_or_else(|| {
62            vm.new_key_error(
63                vm.ctx
64                    .new_str(format!("getpwnam(): name not found: {pw_name}"))
65                    .into(),
66            )
67        })?;
68        Ok(Passwd::from(user))
69    }
70
71    #[pyfunction]
72    fn getpwuid(uid: PyIntRef, vm: &VirtualMachine) -> PyResult<Passwd> {
73        let uid_t = libc::uid_t::try_from(uid.as_bigint())
74            .map(unistd::Uid::from_raw)
75            .ok();
76        let user = uid_t
77            .map(User::from_uid)
78            .transpose()
79            .map_err(|err| err.into_pyexception(vm))?
80            .flatten();
81        let user = user.ok_or_else(|| {
82            vm.new_key_error(
83                vm.ctx
84                    .new_str(format!("getpwuid(): uid not found: {}", uid.as_bigint()))
85                    .into(),
86            )
87        })?;
88        Ok(Passwd::from(user))
89    }
90
91    // TODO: maybe merge this functionality into nix?
92    #[pyfunction]
93    fn getpwall(vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
94        // setpwent, getpwent, etc are not thread safe. Could use fgetpwent_r, but this is easier
95        static GETPWALL: parking_lot::Mutex<()> = parking_lot::const_mutex(());
96        let _guard = GETPWALL.lock();
97        let mut list = Vec::new();
98
99        unsafe { libc::setpwent() };
100        while let Some(ptr) = NonNull::new(unsafe { libc::getpwent() }) {
101            let user = User::from(unsafe { ptr.as_ref() });
102            let passwd = Passwd::from(user).to_pyobject(vm);
103            list.push(passwd);
104        }
105        unsafe { libc::endpwent() };
106
107        Ok(list)
108    }
109}