diff 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 diff
--- a/libpam-sys/build.rs	Wed Jul 02 03:33:09 2025 -0400
+++ b/libpam-sys/build.rs	Thu Jul 03 11:03:36 2025 -0400
@@ -1,13 +1,93 @@
-use libpam_sys_impls::__pam_impl_enum__;
-use strum::{EnumIter, IntoEnumIterator};
+#![allow(unexpected_cfgs)]
 
-__pam_impl_enum__!(#[derive(EnumIter)]);
+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_strs: Vec<_> = PamImpl::iter().map(|e| format!("\"{:?}\"", e)).collect();
-    let pam_impls = pam_impl_strs.join(",");
-    // We use this for ctest. Don't do what we've done; just use cfg_pam_impl.
-    println!("cargo:rustc-check-cfg=cfg(_hack_impl, values({pam_impls}))");
-    println!("cargo:rustc-cfg=_hack_impl=\"{:?}\"", PamImpl::CURRENT);
+
+    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());
+        }
+    }
+}