I am writing a tool to help me convert user ids and group ids to their respective names on OS X and Linux, and this is my first attempt at interfacing with libc. I have mangled the username code below together from some old sample code here and here. It appears to work, but am I using libc right? Are there any better approaches I can try? I had initially implemented the pgfiles crate, but that only covers the local user database. Thank you!
extern crate libc;
use libc::{c_int, passwd};
use std::str;
use std::ffi::CStr;
extern {
fn getpwuid(uid: c_int) -> *mut passwd;
}
fn main() {
let uid = 501;
let p = unsafe { getpwuid(uid) };
if p.is_null() {
println!("Couldn't map uid ({}) to passwd entry.", uid);
return;
}
let c_str: &CStr = unsafe { CStr::from_ptr((*p).pw_name) };
let buf: &[u8] = c_str.to_bytes();
let str_slice: &str = str::from_utf8(buf).unwrap();
let str_buf: String = str_slice.to_owned();
println!("Current user is: {:#?}", str_buf);
}
You don't need to define getpwuid
, you should import it from libc
. Also getpwuid
is not thread-safe, I recommend you use getpwuid_r
instead. Also, you can go directly from a &CStr
to a &str
using to_str
Thank you @jethrogb! Following your advice I made some changes and also found some reference code for interfacing with getpwuid_r
here, which I have adapted into a function which wraps the unsafe code and returns an Option. The revised code is below:
extern crate libc;
use std::mem;
use std::ptr;
use std::str;
use std::ffi::CStr;
fn get_unix_username(uid: u32) -> Option<String> {
unsafe {
let mut result = ptr::null_mut();
let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
n if n < 0 => 512 as usize,
n => n as usize,
};
let mut buf = Vec::with_capacity(amt);
let mut passwd: libc::passwd = mem::zeroed();
match libc::getpwuid_r(uid, &mut passwd, buf.as_mut_ptr(),
buf.capacity() as libc::size_t,
&mut result) {
0 if !result.is_null() => {
let ptr = passwd.pw_name as *const _;
let username = CStr::from_ptr(ptr).to_str().unwrap().to_owned();
Some(username)
},
_ => None
}
}
}
fn main() {
let uid = 501;
let uname = get_unix_username(uid).unwrap();
println!("User ID {} is {}", uid, uname);
}
And only now have I found the users crate, which does exactly what I need
It was a good learning exercise to try my own way though, and I feel a bit more confident in identifying what I need via libc.