comparison 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
comparison
equal deleted inserted replaced
105:13b4d2a19674 106:49d9e2b5c189
1 use bindgen::MacroTypeVariation;
2 use std::error::Error;
3 use std::fmt::{Debug, Display, Formatter};
4 use std::path::PathBuf;
5 use std::{env, fs};
6
7 enum PamImpl {
8 Illumos,
9 LinuxPam,
10 OpenPam,
11 }
12
13 #[derive(Debug)]
14 struct InvalidEnum(String);
15
16 impl Display for InvalidEnum {
17 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
18 write!(f, "invalid PAM impl {:?}", self.0)
19 }
20 }
21
22 impl Error for InvalidEnum {}
23
24 impl TryFrom<&str> for PamImpl {
25 type Error = InvalidEnum;
26 fn try_from(value: &str) -> Result<Self, Self::Error> {
27 Ok(match value {
28 "illumos" => Self::Illumos,
29 "linux-pam" => Self::LinuxPam,
30 "openpam" => Self::OpenPam,
31 other => return Err(InvalidEnum(other.to_owned())),
32 })
33 }
34 }
35
36 impl Debug for PamImpl {
37 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38 Debug::fmt(
39 match self {
40 Self::Illumos => "illumos",
41 Self::LinuxPam => "linux-pam",
42 Self::OpenPam => "openpam",
43 },
44 f,
45 )
46 }
47 }
48
49 fn main() {
50 println!("cargo:rustc-link-lib=pam");
51 let out_file = PathBuf::from(env::var("OUT_DIR").unwrap()).join("constants.rs");
52 let pam_impl = do_detection();
53
54 if cfg!(feature = "use-system-headers") {
55 let builder = bindgen::Builder::default()
56 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
57 .blocklist_function(".*")
58 .blocklist_type(".*")
59 .allowlist_var(".*")
60 .default_macro_constant_type(MacroTypeVariation::Unsigned);
61
62 let builder = match pam_impl {
63 PamImpl::Illumos => builder.header_contents(
64 "illumos.h",
65 "\
66 #include <security/pam_appl.h>
67 #include <security/pam_modules.h>
68 ",
69 ),
70 PamImpl::LinuxPam => builder.header_contents(
71 "linux-pam.h",
72 "\
73 #include <security/_pam_types.h>
74 #include <security/pam_appl.h>
75 #include <security/pam_ext.h>
76 #include <security/pam_modules.h>
77 ",
78 ),
79 PamImpl::OpenPam => builder.header_contents(
80 "openpam.h",
81 "\
82 #include <security/pam_types.h>
83 #include <security/openpam.h>
84 #include <security/pam_appl.h>
85 #include <security/pam_constants.h>
86 ",
87 ),
88 };
89 let bindings = builder.generate().unwrap();
90 bindings.write_to_file(out_file).unwrap();
91 } else {
92 // Just write empty data to the file to avoid conditional compilation
93 // shenanigans.
94 fs::write(out_file, "").unwrap();
95 }
96 }
97
98 fn do_detection() -> PamImpl {
99 println!(r#"cargo:rustc-check-cfg=cfg(pam_impl, values("illumos", "linux-pam", "openpam"))"#);
100 let pam_impl = _detect_internal();
101 println!("cargo:rustc-cfg=pam_impl={pam_impl:?}");
102 pam_impl
103 }
104
105 fn _detect_internal() -> PamImpl {
106 if let Some(pam_impl) = option_env!("LIBPAMSYS_PAM_IMPL") {
107 pam_impl.try_into().unwrap()
108 } else if cfg!(feature = "use-system-headers") {
109 // Detect which impl it is from system headers.
110 if header_exists("security/_pam_types.h") {
111 PamImpl::LinuxPam
112 } else if header_exists("security/openpam.h") {
113 PamImpl::OpenPam
114 } else if header_exists("security/pam_appl.h") {
115 PamImpl::Illumos
116 } else {
117 panic!("could not detect PAM implementation")
118 }
119 } else {
120 // Otherwise, guess what PAM impl we're using based on the OS.
121 if cfg!(target_os = "linux") {
122 PamImpl::LinuxPam
123 } else if cfg!(any(
124 target_os = "macos",
125 target_os = "freebsd",
126 target_os = "netbsd",
127 target_os = "dragonfly",
128 target_os = "openbsd"
129 )) {
130 PamImpl::OpenPam
131 } else {
132 PamImpl::Illumos
133 }
134 }
135 }
136
137 fn header_exists(header: &str) -> bool {
138 bindgen::Builder::default()
139 .blocklist_item(".*")
140 .header_contents("header.h", &format!("#include <{header}>"))
141 .generate()
142 .is_ok()
143 }