Skip to content

Commit a9a5f06

Browse files
committed
Rewrite the pwd module to use nix instead of the pwd crate
1 parent e30e2cd commit a9a5f06

File tree

3 files changed

+80
-135
lines changed

3 files changed

+80
-135
lines changed

Cargo.lock

Lines changed: 0 additions & 73 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vm/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,6 @@ unic-ucd-ident = "0.9"
9292
flame = { version = "0.2", optional = true }
9393
flamer = { version = "0.3", optional = true }
9494

95-
[target.'cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))'.dependencies]
96-
pwd = "1"
97-
9895
[target.'cfg(unix)'.dependencies]
9996
exitcode = "1.1.2"
10097
uname = "0.1.1"

vm/src/stdlib/pwd.rs

Lines changed: 80 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,106 @@
1-
use pwd::Passwd;
2-
1+
use super::os::convert_nix_error;
2+
use crate::obj::objint::PyIntRef;
33
use crate::obj::objstr::PyStringRef;
4-
use crate::obj::objtype::PyClassRef;
5-
use crate::pyobject::{PyObjectRef, PyRef, PyResult, PyValue};
4+
use crate::pyobject::{PyClassImpl, PyObjectRef, PyResult};
65
use crate::vm::VirtualMachine;
6+
use std::convert::TryFrom;
7+
use std::ptr::NonNull;
78

8-
impl PyValue for Passwd {
9-
fn class(vm: &VirtualMachine) -> PyClassRef {
10-
vm.class("pwd", "struct_passwd")
11-
}
12-
}
13-
14-
type PasswdRef = PyRef<Passwd>;
15-
16-
impl PasswdRef {
17-
fn pw_name(self) -> String {
18-
self.name.clone()
19-
}
20-
21-
fn pw_passwd(self) -> Option<String> {
22-
self.passwd.clone()
23-
}
24-
25-
fn pw_uid(self) -> u32 {
26-
self.uid
27-
}
28-
29-
fn pw_gid(self) -> u32 {
30-
self.gid
31-
}
9+
use nix::unistd::{self, User};
3210

33-
fn pw_gecos(self) -> Option<String> {
34-
self.gecos.clone()
35-
}
36-
37-
fn pw_dir(self) -> String {
38-
self.dir.clone()
39-
}
11+
#[pystruct_sequence(name = "pwd.struct_passwd")]
12+
struct Passwd {
13+
pw_name: String,
14+
pw_passwd: String,
15+
pw_uid: u32,
16+
pw_gid: u32,
17+
pw_gecos: String,
18+
pw_dir: String,
19+
pw_shell: String,
20+
}
4021

41-
fn pw_shell(self) -> String {
42-
self.shell.clone()
22+
impl From<User> for Passwd {
23+
fn from(user: User) -> Self {
24+
// this is just a pain...
25+
let cstr_lossy = |s: std::ffi::CString| {
26+
s.into_string()
27+
.unwrap_or_else(|e| e.into_cstring().to_string_lossy().into_owned())
28+
};
29+
let pathbuf_lossy = |p: std::path::PathBuf| {
30+
p.into_os_string()
31+
.into_string()
32+
.unwrap_or_else(|s| s.to_string_lossy().into_owned())
33+
};
34+
Passwd {
35+
pw_name: user.name,
36+
pw_passwd: cstr_lossy(user.passwd),
37+
pw_uid: user.uid.as_raw(),
38+
pw_gid: user.gid.as_raw(),
39+
pw_gecos: cstr_lossy(user.gecos),
40+
pw_dir: pathbuf_lossy(user.dir),
41+
pw_shell: pathbuf_lossy(user.shell),
42+
}
4343
}
4444
}
4545

46-
fn pwd_getpwnam(name: PyStringRef, vm: &VirtualMachine) -> PyResult<Passwd> {
47-
match Passwd::from_name(name.as_str()) {
48-
Ok(Some(passwd)) => Ok(passwd),
49-
_ => {
46+
fn pwd_getpwnam(name: PyStringRef, vm: &VirtualMachine) -> PyResult {
47+
match User::from_name(name.as_str()).map_err(|e| convert_nix_error(vm, e))? {
48+
Some(user) => Ok(Passwd::from(user)
49+
.into_struct_sequence(vm, vm.try_class("pwd", "struct_passwd")?)?
50+
.into_object()),
51+
None => {
5052
let name_repr = vm.to_repr(name.as_object())?;
5153
let message = vm.new_str(format!("getpwnam(): name not found: {}", name_repr));
5254
Err(vm.new_key_error(message))
5355
}
5456
}
5557
}
5658

57-
fn pwd_getpwuid(uid: u32, vm: &VirtualMachine) -> PyResult<Passwd> {
58-
match Passwd::from_uid(uid) {
59-
Some(passwd) => Ok(passwd),
60-
_ => {
61-
let message = vm.new_str(format!("getpwuid(): uid not found: {}", uid));
59+
fn pwd_getpwuid(uid: PyIntRef, vm: &VirtualMachine) -> PyResult {
60+
let uid_t = libc::uid_t::try_from(uid.as_bigint()).map(unistd::Uid::from_raw);
61+
let user = match uid_t {
62+
Ok(uid) => User::from_uid(uid).map_err(|e| convert_nix_error(vm, e))?,
63+
Err(_) => None,
64+
};
65+
match user {
66+
Some(user) => Ok(Passwd::from(user)
67+
.into_struct_sequence(vm, vm.try_class("pwd", "struct_passwd")?)?
68+
.into_object()),
69+
None => {
70+
let message = vm.new_str(format!("getpwuid(): uid not found: {}", uid.as_bigint()));
6271
Err(vm.new_key_error(message))
6372
}
6473
}
6574
}
6675

76+
// TODO: maybe merge this functionality into nix?
77+
fn pwd_getpwall(vm: &VirtualMachine) -> PyResult {
78+
// setpwent, getpwent, etc are not thread safe. Could use fgetpwent_r, but this is easier
79+
static GETPWALL: parking_lot::Mutex<()> = parking_lot::const_mutex(());
80+
let _guard = GETPWALL.lock();
81+
let mut list = Vec::new();
82+
let cls = vm.try_class("pwd", "struct_passwd")?;
83+
84+
unsafe { libc::setpwent() };
85+
while let Some(ptr) = NonNull::new(unsafe { libc::getpwent() }) {
86+
let user = User::from(unsafe { ptr.as_ref() });
87+
let passwd = Passwd::from(user)
88+
.into_struct_sequence(vm, cls.clone())?
89+
.into_object();
90+
list.push(passwd);
91+
}
92+
unsafe { libc::endpwent() };
93+
94+
Ok(vm.ctx.new_list(list))
95+
}
96+
6797
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
6898
let ctx = &vm.ctx;
6999

70-
let passwd_type = py_class!(ctx, "struct_passwd", ctx.object(), {
71-
"pw_name" => ctx.new_readonly_getset("pw_name", PasswdRef::pw_name),
72-
"pw_passwd" => ctx.new_readonly_getset("pw_passwd", PasswdRef::pw_passwd),
73-
"pw_uid" => ctx.new_readonly_getset("pw_uid", PasswdRef::pw_uid),
74-
"pw_gid" => ctx.new_readonly_getset("pw_gid", PasswdRef::pw_gid),
75-
"pw_gecos" => ctx.new_readonly_getset("pw_gecos", PasswdRef::pw_gecos),
76-
"pw_dir" => ctx.new_readonly_getset("pw_dir", PasswdRef::pw_dir),
77-
"pw_shell" => ctx.new_readonly_getset("pw_shell", PasswdRef::pw_shell),
78-
});
79-
80100
py_module!(vm, "pwd", {
81-
"struct_passwd" => passwd_type,
82-
"getpwnam" => ctx.new_function(pwd_getpwnam),
83-
"getpwuid" => ctx.new_function(pwd_getpwuid),
101+
"struct_passwd" => Passwd::make_class(ctx),
102+
"getpwnam" => named_function!(ctx, pwd, getpwnam),
103+
"getpwuid" => named_function!(ctx, pwd, getpwuid),
104+
"getpwall" => named_function!(ctx, pwd, getpwall),
84105
})
85106
}

0 commit comments

Comments
 (0)