Mercurial > crates > nonstick
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() +}