Mercurial > crates > nonstick
diff libpam-sys/libpam-sys-impls/build.rs @ 176:0730f5f2ee2a
Turn `libpam-sys-consts` back into `libpam-sys-impls`.
This moves the constants into `libpam-sys` and makes `libpam-sys-impls`
responsible solely for detecting the current PAM implementation.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Wed, 30 Jul 2025 17:53:31 -0400 |
parents | libpam-sys/libpam-sys-consts/build.rs@46e8ce5cd5d1 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpam-sys/libpam-sys-impls/build.rs Wed Jul 30 17:53:31 2025 -0400 @@ -0,0 +1,124 @@ +#![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 { + /// Use the default PAM implementation based on the OS, + /// or the currently-installed version if the OS is not recognized. + TargetDefault, + /// Detect the installed implementation. + Installed, + /// Use the named version of PAM. + Specified(PamImpl), +} + +const INSTALLED: &str = "__installed__"; + +fn main() { + let detection = match option_env!("LIBPAMSYS_IMPL") { + Some("") | None => Detect::TargetDefault, + Some(INSTALLED) => Detect::Installed, + Some(val) => Detect::Specified(PamImpl::try_from(val).unwrap_or_else(|_| { + panic!( + "unknown PAM implementation {val:?}. \ + valid LIBPAMSYS_IMPLs are {:?}, \ + {INSTALLED:?} to use the currently-installed version, \ + or unset to use the OS default", + PamImpl::items() + ) + })), + }; + let pam_impl = match detection { + Detect::TargetDefault => LibPam::target_default(), + Detect::Installed => LibPam::probe_detect(), + 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 { + /// Guess the PAM implementation based on the current OS. + fn target_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 { + Self::probe_detect() + } + } + + /// Look at the currently-installed LibPAM. + 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; + } + } + // idk + 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()); + } + } +}