printenv2 0.1.0

A printenv rewrite in Rust
use crate::AppResult;

#[allow(warnings)]
#[allow(clippy::all)]
#[allow(clippy::pedantic)]
#[allow(clippy::nursery)]
#[allow(clippy::cargo)]
mod kvm_bindings {
    include!(concat!(env!("OUT_DIR"), "/kvm-bindings.rs"));
}

mod oxidation {
    use super::kvm_bindings::{
        kinfo_proc, kvm_close, kvm_getenvv, kvm_geterr, kvm_getprocs, kvm_openfiles, kvm_t,
        KERN_PROC_PID, O_RDONLY, _POSIX2_LINE_MAX,
    };
    use super::AppResult;

    use crate::definition::AppError;
    use std::ffi::{CStr, CString};

    #[derive(Debug)]
    pub struct Kvm {
        kd: *mut kvm_t,
    }

    impl Kvm {
        pub fn try_new() -> AppResult<Self> {
            let mut errbuf = [0i8; _POSIX2_LINE_MAX as usize];

            let corefile = CString::new("/dev/null")?;
            let kd = unsafe {
                kvm_openfiles(
                    std::ptr::null(),
                    corefile.as_ptr(),
                    std::ptr::null(),
                    O_RDONLY.try_into()?,
                    errbuf.as_mut_ptr(),
                )
            };
            if kd.is_null() {
                Err(AppError::UnixErrorString(
                    unsafe { CStr::from_ptr(errbuf.as_ptr()) }.into(),
                ))
            } else {
                Ok(Self { kd })
            }
        }

        pub fn get_proc(&self, pid: u32) -> AppResult<*mut kinfo_proc> {
            let mut cnt = 1;
            let proc = unsafe {
                kvm_getprocs(
                    self.kd,
                    KERN_PROC_PID.try_into()?,
                    pid.try_into()?,
                    &mut cnt,
                )
            };
            if proc.is_null() {
                let errbuf = unsafe { kvm_geterr(self.kd) };
                Err(AppError::UnixErrorString(
                    unsafe { CStr::from_ptr(errbuf) }.into(),
                ))
            } else {
                assert_eq!(cnt, 1);
                Ok(proc)
            }
        }

        pub fn get_env(&self, proc: *mut kinfo_proc) -> AppResult<Vec<u8>> {
            let mut environ = unsafe { kvm_getenvv(self.kd, proc, 0) };
            if environ.is_null() {
                Err(std::io::Error::last_os_error().into())
            } else {
                let mut result = Vec::new();
                unsafe {
                    while !(*environ).is_null() {
                        let record = CStr::from_ptr(*environ).to_bytes_with_nul();
                        {
                            result.extend(record);
                        }
                        environ = environ.add(1);
                    }
                }

                Ok(result)
            }
        }
    }

    impl Drop for Kvm {
        fn drop(&mut self) {
            let ret = unsafe { kvm_close(self.kd) };
            assert!(ret == 0, "{}", std::io::Error::last_os_error());
        }
    }
}

pub fn get_environment_string(pid: u32) -> AppResult<Vec<u8>> {
    let kvm = oxidation::Kvm::try_new()?;
    let proc = kvm.get_proc(pid)?;
    kvm.get_env(proc)
}