Using libc to get username from user ID


#1

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);

}

#2

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


#3

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);

}

#4

And only now have I found the users crate, which does exactly what I need :astonished:
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.