view libpam-sys/build.rs @ 134:6c1e1bdb4164

Use standard #[cfg] directives rather than custom proc macros. Instead of having to do a bunch of custom parsing and other logic that tools often choke on, this change introduces an easy way to depend upon custom #[cfg]s provided by the libpam-sys crate.
author Paul Fisher <paul@pfish.zone>
date Thu, 03 Jul 2025 11:03:36 -0400
parents a632a8874131
children efbc235f01d3
line wrap: on
line source

#![allow(unexpected_cfgs)]

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

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

fn main() {
    println!("cargo:rustc-link-lib=pam");

    let pam_impl = match option_env!("LIBPAMSYS_IMPL") {
        // The default option: Guess what PAM impl we're using based on OS.
        None | Some("") => 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()
                )
            }
        },
    };
    println!("{}", checkcfg());
    let impl_str = format!("{pam_impl:?}");
    println!("{}", implcfg(&impl_str));
    println!("cargo:rustc-env=LIBPAMSYS_IMPL={impl_str}");
    fs::write(
        format!("{}/pam_impl_const.rs", env::var("OUT_DIR").unwrap()),
        format!(
            "\
            impl PamImpl {{\
            /// The implementation of libpam this was built for (`{impl_str}`).
            pub const CURRENT: Self = Self::{impl_str};\
            /// String version for internal consumption.
            const CURRENT_STR: &'static str = {impl_str:?};\
            }}
            "
        ),
    )
    .unwrap();
}

fn detect() -> PamImpl {
    if let Some(lib) = LibPam::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
    }
}

struct LibPam(NonNull<c_void>);

impl LibPam {
    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());
        }
    }
}