view libpam-sys/libpam-sys-helpers/build.rs @ 141:a508a69c068a

Remove a lot of Results from functions. Many functions are documented to only return failing Results when given improper inputs or when there is a memory allocation failure (which can be verified by looking at the source). In cases where we know our input is correct, we don't need to check for memory allocation errors for the same reason that Rust doesn't do so when you, e.g., create a new Vec.
author Paul Fisher <paul@pfish.zone>
date Sat, 05 Jul 2025 17:16:56 -0400
parents efbc235f01d3
children 4b3a5095f68c
line wrap: on
line source

#![allow(unexpected_cfgs)]

use std::ffi::{c_void, CString};
use std::ptr::NonNull;
use std::{env, fs};

include!("src/pam_impl.rs");

fn main() {
    let pam_impl = match option_env!("LIBPAMSYS_IMPL") {
        // The default option: Guess what PAM impl we're using based on OS.
        None | Some("") => LibPam::detect(),
        Some(other) => match PamImpl::try_from(other) {
            Ok(i) => i,
            Err(_) => {
                panic!(
                    "unknown PAM implementation {other:?}. valid LIBPAMSYS_IMPLs are {:?}, or unset to detect", PamImpl::items()
                )
            }
        },
    };
    let impl_str = format!("{pam_impl:?}");
    println!("{}", generate_cfg(&impl_str));
    // We set this environment variable to substitute into docstrings.
    println!("cargo:rustc-env=LIBPAMSYS_IMPL={impl_str}");
    fs::write(
        format!("{}/pam_impl_const.rs", env::var("OUT_DIR").unwrap()),
        generate_consts(&impl_str),
    )
    .unwrap();
}

fn generate_consts(impl_str: &str) -> String {
    format!(
        "\
impl PamImpl {{
/// The implementation of libpam this was built for (`{impl_str}`).
pub const CURRENT: Self = Self::{impl_str};
}}

/// String name of [`PamImpl::CURRENT`], for substituting into docs.
#[macro_export]
macro_rules! pam_impl_name {{ () => ({impl_str:?}) }}
        "
    )
}

struct LibPam(NonNull<c_void>);

impl LibPam {
    fn detect() -> PamImpl {
        if let Some(lib) = Self::open() {
            if lib.has("pam_syslog") {
                return PamImpl::LinuxPam;
            } else if lib.has("_openpam_log") {
                return PamImpl::OpenPam;
            } else if lib.has("__pam_get_authtok") {
                return PamImpl::Sun;
            }
        }
        if cfg!(target_os = "linux") {
            PamImpl::LinuxPam
        } else if cfg!(any(
            target_os = "macos",
            target_os = "freebsd",
            target_os = "netbsd",
            target_os = "dragonfly",
            target_os = "openbsd",
        )) {
            PamImpl::OpenPam
        } else if cfg!(any(target_os = "illumos", target_os = "solaris")) {
            PamImpl::Sun
        } else {
            PamImpl::XSso
        }
    }

    fn open() -> Option<Self> {
        NonNull::new(unsafe { libc::dlopen(b"libpam.so\0".as_ptr().cast(), libc::RTLD_LAZY) })
            .map(Self)
    }

    fn has(&self, name: &str) -> bool {
        let name = CString::new(name).unwrap();
        let symbol = unsafe { libc::dlsym(self.0.as_ptr(), name.as_ptr()) };
        !symbol.is_null()
    }
}

impl Drop for LibPam {
    fn drop(&mut self) {
        unsafe {
            libc::dlclose(self.0.as_ptr());
        }
    }
}