diff libpam-sys/build.rs @ 106:49d9e2b5c189

An irresponsible mix of implementing libpam-sys and other stuff.
author Paul Fisher <paul@pfish.zone>
date Thu, 26 Jun 2025 22:41:28 -0400
parents
children e97534be35e3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpam-sys/build.rs	Thu Jun 26 22:41:28 2025 -0400
@@ -0,0 +1,143 @@
+use bindgen::MacroTypeVariation;
+use std::error::Error;
+use std::fmt::{Debug, Display, Formatter};
+use std::path::PathBuf;
+use std::{env, fs};
+
+enum PamImpl {
+    Illumos,
+    LinuxPam,
+    OpenPam,
+}
+
+#[derive(Debug)]
+struct InvalidEnum(String);
+
+impl Display for InvalidEnum {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(f, "invalid PAM impl {:?}", self.0)
+    }
+}
+
+impl Error for InvalidEnum {}
+
+impl TryFrom<&str> for PamImpl {
+    type Error = InvalidEnum;
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        Ok(match value {
+            "illumos" => Self::Illumos,
+            "linux-pam" => Self::LinuxPam,
+            "openpam" => Self::OpenPam,
+            other => return Err(InvalidEnum(other.to_owned())),
+        })
+    }
+}
+
+impl Debug for PamImpl {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        Debug::fmt(
+            match self {
+                Self::Illumos => "illumos",
+                Self::LinuxPam => "linux-pam",
+                Self::OpenPam => "openpam",
+            },
+            f,
+        )
+    }
+}
+
+fn main() {
+    println!("cargo:rustc-link-lib=pam");
+    let out_file = PathBuf::from(env::var("OUT_DIR").unwrap()).join("constants.rs");
+    let pam_impl = do_detection();
+
+    if cfg!(feature = "use-system-headers") {
+        let builder = bindgen::Builder::default()
+            .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+            .blocklist_function(".*")
+            .blocklist_type(".*")
+            .allowlist_var(".*")
+            .default_macro_constant_type(MacroTypeVariation::Unsigned);
+
+        let builder = match pam_impl {
+            PamImpl::Illumos => builder.header_contents(
+                "illumos.h",
+                "\
+                        #include <security/pam_appl.h>
+                        #include <security/pam_modules.h>
+                    ",
+            ),
+            PamImpl::LinuxPam => builder.header_contents(
+                "linux-pam.h",
+                "\
+                        #include <security/_pam_types.h>
+                        #include <security/pam_appl.h>
+                        #include <security/pam_ext.h>
+                        #include <security/pam_modules.h>
+                    ",
+            ),
+            PamImpl::OpenPam => builder.header_contents(
+                "openpam.h",
+                "\
+                        #include <security/pam_types.h>
+                        #include <security/openpam.h>
+                        #include <security/pam_appl.h>
+                        #include <security/pam_constants.h>
+                    ",
+            ),
+        };
+        let bindings = builder.generate().unwrap();
+        bindings.write_to_file(out_file).unwrap();
+    } else {
+        // Just write empty data to the file to avoid conditional compilation
+        // shenanigans.
+        fs::write(out_file, "").unwrap();
+    }
+}
+
+fn do_detection() -> PamImpl {
+    println!(r#"cargo:rustc-check-cfg=cfg(pam_impl, values("illumos", "linux-pam", "openpam"))"#);
+    let pam_impl = _detect_internal();
+    println!("cargo:rustc-cfg=pam_impl={pam_impl:?}");
+    pam_impl
+}
+
+fn _detect_internal() -> PamImpl {
+    if let Some(pam_impl) = option_env!("LIBPAMSYS_PAM_IMPL") {
+        pam_impl.try_into().unwrap()
+    } else if cfg!(feature = "use-system-headers") {
+        // Detect which impl it is from system headers.
+        if header_exists("security/_pam_types.h") {
+            PamImpl::LinuxPam
+        } else if header_exists("security/openpam.h") {
+            PamImpl::OpenPam
+        } else if header_exists("security/pam_appl.h") {
+            PamImpl::Illumos
+        } else {
+            panic!("could not detect PAM implementation")
+        }
+    } else {
+        // Otherwise, guess what PAM impl we're using based on the OS.
+        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 {
+            PamImpl::Illumos
+        }
+    }
+}
+
+fn header_exists(header: &str) -> bool {
+    bindgen::Builder::default()
+        .blocklist_item(".*")
+        .header_contents("header.h", &format!("#include <{header}>"))
+        .generate()
+        .is_ok()
+}