c-gull 0.22.3

A libc implementation in Rust
Documentation
use core::cell::OnceCell;
use core::ffi::CStr;
use core::mem::{size_of, zeroed};
use core::ptr::null_mut;
use core::slice;
use errno::{set_errno, Errno};
use libc::{c_char, c_int, utmpx};
use std::ffi::OsStr;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use std::sync::Mutex;

static LOCK: Mutex<OnceCell<(PathBuf, Option<File>, utmpx)>> = Mutex::new(OnceCell::new());

#[no_mangle]
unsafe extern "C" fn getutxent() -> *mut libc::utmpx {
    libc!(libc::getutxent());

    let mut lock = LOCK.lock().unwrap();

    ensure_open_file(&mut lock);

    let (_path, Some(ref mut file), entry) = lock.get_mut().unwrap() else {
        set_errno(Errno(libc::ENOENT));
        return null_mut();
    };

    match file.read_exact(unsafe {
        slice::from_raw_parts_mut(entry as *mut utmpx as *mut u8, size_of::<utmpx>())
    }) {
        Ok(()) => entry,
        Err(err) => {
            set_errno(Errno(err.raw_os_error().unwrap_or(libc::EIO)));
            null_mut()
        }
    }
}

#[no_mangle]
unsafe extern "C" fn getutxid(ut: *const libc::utmpx) -> *mut libc::utmpx {
    libc!(libc::getutxid(ut));

    let mut lock = LOCK.lock().unwrap();

    ensure_open_file(&mut lock);

    let (_path, Some(ref mut file), entry) = lock.get_mut().unwrap() else {
        set_errno(Errno(libc::ENOENT));
        return null_mut();
    };

    loop {
        match file.read_exact(unsafe {
            slice::from_raw_parts_mut(entry as *mut utmpx as *mut u8, size_of::<utmpx>())
        }) {
            Ok(()) => {}
            Err(err) => {
                set_errno(Errno(err.raw_os_error().unwrap_or(libc::EIO)));
                return null_mut();
            }
        }

        match (*ut).ut_type {
            libc::RUN_LVL | libc::BOOT_TIME | libc::NEW_TIME | libc::OLD_TIME => {
                if entry.ut_type == (*ut).ut_type {
                    return entry;
                }
            }
            libc::INIT_PROCESS | libc::LOGIN_PROCESS | libc::USER_PROCESS | libc::DEAD_PROCESS => {
                if entry.ut_id == (*ut).ut_id {
                    return entry;
                }
            }
            _ => {}
        }
    }
}

#[no_mangle]
unsafe extern "C" fn getutxline(ut: *const libc::utmpx) -> *mut libc::utmpx {
    libc!(libc::getutxline(ut));

    let mut lock = LOCK.lock().unwrap();

    ensure_open_file(&mut lock);

    let (_path, Some(ref mut file), entry) = lock.get_mut().unwrap() else {
        set_errno(Errno(libc::ENOENT));
        return null_mut();
    };

    loop {
        match file.read_exact(unsafe {
            slice::from_raw_parts_mut(entry as *mut utmpx as *mut u8, size_of::<utmpx>())
        }) {
            Ok(()) => {}
            Err(err) => {
                set_errno(Errno(err.raw_os_error().unwrap_or(0)));
                return null_mut();
            }
        }

        match entry.ut_type {
            libc::USER_PROCESS | libc::LOGIN_PROCESS => {
                if entry.ut_line == (*ut).ut_line {
                    return entry;
                }
            }
            _ => {}
        }
    }
}

#[no_mangle]
unsafe extern "C" fn setutxent() {
    libc!(libc::setutxent());

    let mut lock = LOCK.lock().unwrap();

    let (_path, _file, _entry) =
        lock.get_or_init(|| (PathBuf::from("/var/run/utmp"), None, unsafe { zeroed() }));

    match &mut lock.get_mut().unwrap().1 {
        Some(file) => {
            file.seek(SeekFrom::Start(0)).ok();
        }
        None => {}
    }
}

#[no_mangle]
unsafe extern "C" fn endutxent() {
    libc!(libc::endutxent());

    let mut lock = LOCK.lock().unwrap();

    let (_path, _file, _entry) =
        lock.get_or_init(|| (PathBuf::from("/var/run/utmp"), None, unsafe { zeroed() }));

    lock.get_mut().unwrap().1 = None;
}

#[no_mangle]
unsafe extern "C" fn utmpxname(file: *const c_char) -> c_int {
    libc!(libc::utmpxname(file));

    let mut lock = LOCK.lock().unwrap();

    let (_path, _file, _entry) =
        lock.get_or_init(|| (PathBuf::from("/var/run/utmp"), None, unsafe { zeroed() }));

    let tuple = lock.get_mut().unwrap();
    tuple.0 = PathBuf::from(OsStr::from_bytes(CStr::from_ptr(file).to_bytes()));
    tuple.1 = None;

    0
}

fn ensure_open_file(lock: &mut OnceCell<(PathBuf, Option<File>, utmpx)>) {
    let (path, file, _entry) =
        lock.get_or_init(|| (PathBuf::from("/var/run/utmp"), None, unsafe { zeroed() }));

    match file {
        Some(_file) => {}
        None => match File::open(path) {
            Ok(opened) => lock.get_mut().unwrap().1 = Some(opened),
            Err(_) => {}
        },
    }
}