utmpx 0.2.0

Rust bindings and wrapper around `utmpx.h`
Documentation
//! Contains definitions for system-provided function and types.

use std::ffi::{c_char, c_int, c_short, c_uint};
#[cfg(test)]
use std::{
    mem::{align_of, size_of, MaybeUninit},
    ptr::addr_of,
};

use libc::{pid_t, timeval};

pub const UT_LINESIZE: usize = 32;
pub const UT_NAMESIZE: usize = 32;
pub const UT_HOSTSIZE: usize = 256;

/// Values for `ut_type` field.
#[cfg(target_os = "linux")]
#[cfg(any(target_env = "musl", target_env = "gnu"))]
#[repr(i16)]
#[derive(Clone, Copy, Debug)]
#[allow(non_camel_case_types)]
pub enum UtType {
    /// Record does not contain valid info (formerly known as UT_UNKNOWN on Linux)
    EMPTY = 0,
    /// Change in system run-level (see init(1))
    RUN_LVL = 1,
    /// Time of system boot (in ut_tv)
    BOOT_TIME = 2,
    /// Time after system clock change (in ut_tv)
    NEW_TIME = 3,
    /// Time before system clock change (in ut_tv)
    OLD_TIME = 4,
    /// Process spawned by init(1)
    INIT_PROCESS = 5,
    /// Session leader process for user login
    LOGIN_PROCESS = 6,
    /// Normal process
    USER_PROCESS = 7,
    /// Terminated process
    DEAD_PROCESS = 8,
    /// Not implemented
    ACCOUNTING = 9,
}

#[test]
fn test_layout_ut_type() {
    assert_eq!(
        size_of::<UtType>(),
        size_of::<c_short>(),
        concat!("Size of: ", stringify!(timeval))
    );
}

#[test]
fn bindgen_test_layout_timeval() {
    const UNINIT: MaybeUninit<timeval> = MaybeUninit::uninit();
    let ptr = UNINIT.as_ptr();
    assert_eq!(
        size_of::<timeval>(),
        16usize,
        concat!("Size of: ", stringify!(timeval))
    );
    assert_eq!(
        align_of::<timeval>(),
        8usize,
        concat!("Alignment of ", stringify!(timeval))
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).tv_sec) as usize - ptr as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(timeval),
            "::",
            stringify!(tv_sec)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).tv_usec) as usize - ptr as usize },
        8usize,
        concat!(
            "Offset of field: ",
            stringify!(timeval),
            "::",
            stringify!(tv_usec)
        )
    );
}

/// Type for `ut_exit`, below.
#[repr(C)]
#[derive(Debug, Copy, Clone, Default)]
pub struct ExitStatus {
    /// Process termination status.
    pub e_termination: c_short,
    /// Process exit status.
    pub e_exit: c_short,
}

/// A utmp structure as defined in `utmpx.h`
///
/// This interface of this type is not yet final, and MAY evolve as support for new platforms is
/// added.
///
/// # See also
///
/// - <https://man7.org/linux/man-pages/man5/utmp.5.html>
/// - <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/utmpx.h.html>
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Utmpx {
    /// Type of record (POSIX).
    pub ut_type: UtType,
    /// Padding.
    pub __ut_pad1: c_short,
    /// PID of login process (POSIX).
    pub ut_pid: pid_t,
    /// Device name of tty (POSIX).
    pub ut_line: [c_char; UT_LINESIZE],
    /// Terminal name suffix, or inittab(5) ID (POSIX).
    pub ut_id: [c_char; 4usize],
    /// Username (POSIX).
    pub ut_user: [c_char; UT_NAMESIZE],
    /// Hostname for remote login, or kernel version for run-level messages.
    pub ut_host: [c_char; UT_HOSTSIZE],
    /// Exit status of a process marked as DEAD_PROCESS; not used by Linux init(1).
    pub ut_exit: ExitStatus,

    /// Session ID (getsid(2)), used for windowing.
    #[cfg(target_endian = "little")]
    pub ut_session: c_int,
    /// Padding.
    #[cfg(target_endian = "little")]
    pub __ut_pad2: c_int,

    /// Padding
    #[cfg(target_endian = "big")]
    pub __ut_pad2: c_int,
    /// Session ID (getsid(2)), used for windowing.
    #[cfg(target_endian = "big")]
    pub ut_session: c_int,

    /// Time entry was made (POSIX).
    pub ut_tv: timeval,
    /// Internet address of remote host; IPv4 address uses just `ut_addr_v6[0]`.
    pub ut_addr_v6: [c_uint; 4usize],
    /// Reserved for future use.
    pub __unused: [c_char; 20usize],
}

#[test]
fn bindgen_test_layout_utmpx_bindgen_ty_1() {
    const UNINIT: MaybeUninit<ExitStatus> = MaybeUninit::uninit();
    let ptr = UNINIT.as_ptr();
    assert_eq!(
        size_of::<ExitStatus>(),
        4usize,
        concat!("Size of: ", stringify!(utmpx__bindgen_ty_1))
    );
    assert_eq!(
        align_of::<ExitStatus>(),
        2usize,
        concat!("Alignment of ", stringify!(utmpx__bindgen_ty_1))
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).e_termination) as usize - ptr as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx__bindgen_ty_1),
            "::",
            stringify!(__e_termination)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).e_exit) as usize - ptr as usize },
        2usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx__bindgen_ty_1),
            "::",
            stringify!(__e_exit)
        )
    );
}
#[test]
fn bindgen_test_layout_utmpx() {
    const UNINIT: MaybeUninit<Utmpx> = MaybeUninit::uninit();
    let ptr = UNINIT.as_ptr();
    assert_eq!(
        size_of::<Utmpx>(),
        400usize,
        concat!("Size of: ", stringify!(utmpx))
    );
    assert_eq!(
        align_of::<Utmpx>(),
        8usize,
        concat!("Alignment of ", stringify!(utmpx))
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_type) as usize - ptr as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_type)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).__ut_pad1) as usize - ptr as usize },
        2usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(__ut_pad1)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_pid) as usize - ptr as usize },
        4usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_pid)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_line) as usize - ptr as usize },
        8usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_line)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_id) as usize - ptr as usize },
        40usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_id)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_user) as usize - ptr as usize },
        44usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_user)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_host) as usize - ptr as usize },
        76usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_host)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_exit) as usize - ptr as usize },
        332usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_exit)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_session) as usize - ptr as usize },
        336usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_session)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).__ut_pad2) as usize - ptr as usize },
        340usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(__ut_pad2)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_tv) as usize - ptr as usize },
        344usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_tv)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).ut_addr_v6) as usize - ptr as usize },
        360usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(ut_addr_v6)
        )
    );
    assert_eq!(
        unsafe { addr_of!((*ptr).__unused) as usize - ptr as usize },
        376usize,
        concat!(
            "Offset of field: ",
            stringify!(utmpx),
            "::",
            stringify!(__unused)
        )
    );
}

extern "C" {
    pub fn endutxent();
    pub fn getutxent() -> *mut Utmpx;
    pub fn getutxid(b: *const Utmpx) -> *mut Utmpx;
    pub fn getutxline(b: *const Utmpx) -> *mut Utmpx;
    pub fn pututxline(b: *const Utmpx) -> *mut Utmpx;
    pub fn setutxent();

    pub fn utmpxname(file: *const c_char) -> c_int;
}