Mercurial > crates > nonstick
view libpam-sys/libpam-sys-consts/build.rs @ 158:d5b7b28d754e
Add `__TARGET_DEFAULT__` PamImpl and set up for docsrs build.
Also fixes some formatting stuff.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sat, 12 Jul 2025 17:17:37 -0400 |
parents | 4b3a5095f68c |
children | 09dff285ff5e |
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"); /// The strategy to use to detect PAM. enum Detect { /// Automatically detect PAM, using the installed implementation if present /// or the OS default if not. Auto, /// Use the default PAM implementation based on the OS. TargetDefault, /// Use the named version of PAM. Specified(PamImpl), } const TARGET_DEFAULT: &str = "__TARGET_DEFAULT__"; fn main() { let detection = match option_env!("LIBPAMSYS_IMPL") { None | Some("") => match option_env!("DOCS_RS") { // docs.rs cross-compiles, so we don't want to look at // its currently-installed PAM; instead we want to use the OS. Some(_) => Detect::TargetDefault, // In other cases, just auto-detect the actual installed library. None => Detect::Auto, }, Some(TARGET_DEFAULT) => Detect::TargetDefault, Some(val) => Detect::Specified(PamImpl::try_from(val).unwrap_or_else(|_| { panic!( "unknown PAM implementation {val:?}. \ valid LIBPAMSYS_IMPLs are {:?}, \ {TARGET_DEFAULT:?} to use the OS default, \ or unset to detect", PamImpl::items() ) })), }; let pam_impl = match detection { Detect::Auto => LibPam::probe_detect(), Detect::TargetDefault => LibPam::os_default(), Detect::Specified(other) => other, }; 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 { /// Look at the currently-installed LibPAM, or use [`Self::os_default`] /// if absent. fn probe_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; } } Self::os_default() } /// Guess the PAM implementation based on the current OS. fn os_default() -> PamImpl { 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> { let dlopen = |s: &[u8]| unsafe { libc::dlopen(s.as_ptr().cast(), libc::RTLD_LAZY) }; NonNull::new(dlopen(b"libpam.so\0")) .or_else(|| NonNull::new(dlopen(b"libpam.dylib\0"))) .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()); } } }