comparison libpam-sys/libpam-sys-consts/build.rs @ 160:09dff285ff5e

Switch default PAM detection strategy to target-based. To make cross-compilation easier (like for docs.rs), this change makes OS-based detection of PAM the default, only falling back to probing the actual installed PAM as a last resort. I haven't been able to find a Linux distribution that uses anything but Linux-PAM.
author Paul Fisher <paul@pfish.zone>
date Sun, 13 Jul 2025 15:38:00 -0400
parents d5b7b28d754e
children 46e8ce5cd5d1
comparison
equal deleted inserted replaced
159:634cd5f2ac8b 160:09dff285ff5e
6 6
7 include!("src/pam_impl.rs"); 7 include!("src/pam_impl.rs");
8 8
9 /// The strategy to use to detect PAM. 9 /// The strategy to use to detect PAM.
10 enum Detect { 10 enum Detect {
11 /// Automatically detect PAM, using the installed implementation if present 11 /// Use the default PAM implementation based on the OS,
12 /// or the OS default if not. 12 /// or the currently-installed version if the OS is not recognized.
13 Auto,
14 /// Use the default PAM implementation based on the OS.
15 TargetDefault, 13 TargetDefault,
14 /// Detect the installed implementation.
15 Installed,
16 /// Use the named version of PAM. 16 /// Use the named version of PAM.
17 Specified(PamImpl), 17 Specified(PamImpl),
18 } 18 }
19 19
20 const TARGET_DEFAULT: &str = "__TARGET_DEFAULT__"; 20 const INSTALLED: &str = "__installed__";
21 21
22 fn main() { 22 fn main() {
23 let detection = match option_env!("LIBPAMSYS_IMPL") { 23 let detection = match option_env!("LIBPAMSYS_IMPL") {
24 None | Some("") => match option_env!("DOCS_RS") { 24 Some("") | None => Detect::TargetDefault,
25 // docs.rs cross-compiles, so we don't want to look at 25 Some(INSTALLED) => Detect::Installed,
26 // its currently-installed PAM; instead we want to use the OS.
27 Some(_) => Detect::TargetDefault,
28 // In other cases, just auto-detect the actual installed library.
29 None => Detect::Auto,
30 },
31 Some(TARGET_DEFAULT) => Detect::TargetDefault,
32 Some(val) => Detect::Specified(PamImpl::try_from(val).unwrap_or_else(|_| { 26 Some(val) => Detect::Specified(PamImpl::try_from(val).unwrap_or_else(|_| {
33 panic!( 27 panic!(
34 "unknown PAM implementation {val:?}. \ 28 "unknown PAM implementation {val:?}. \
35 valid LIBPAMSYS_IMPLs are {:?}, \ 29 valid LIBPAMSYS_IMPLs are {:?}, \
36 {TARGET_DEFAULT:?} to use the OS default, \ 30 {INSTALLED:?} to use the OS default, \
37 or unset to detect", 31 or unset to detect",
38 PamImpl::items() 32 PamImpl::items()
39 ) 33 )
40 })), 34 })),
41 }; 35 };
42 let pam_impl = match detection { 36 let pam_impl = match detection {
43 Detect::Auto => LibPam::probe_detect(), 37 Detect::TargetDefault => LibPam::target_default(),
44 Detect::TargetDefault => LibPam::os_default(), 38 Detect::Installed => LibPam::probe_detect(),
45 Detect::Specified(other) => other, 39 Detect::Specified(other) => other,
46 }; 40 };
47 let impl_str = format!("{pam_impl:?}"); 41 let impl_str = format!("{pam_impl:?}");
48 println!("{}", generate_cfg(&impl_str)); 42 println!("{}", generate_cfg(&impl_str));
49 // We set this environment variable to substitute into docstrings. 43 // We set this environment variable to substitute into docstrings.
71 } 65 }
72 66
73 struct LibPam(NonNull<c_void>); 67 struct LibPam(NonNull<c_void>);
74 68
75 impl LibPam { 69 impl LibPam {
76 /// Look at the currently-installed LibPAM, or use [`Self::os_default`]
77 /// if absent.
78 fn probe_detect() -> PamImpl {
79 if let Some(lib) = Self::open() {
80 if lib.has("pam_syslog") {
81 return PamImpl::LinuxPam;
82 } else if lib.has("_openpam_log") {
83 return PamImpl::OpenPam;
84 } else if lib.has("__pam_get_authtok") {
85 return PamImpl::Sun;
86 }
87 }
88 Self::os_default()
89 }
90
91 /// Guess the PAM implementation based on the current OS. 70 /// Guess the PAM implementation based on the current OS.
92 fn os_default() -> PamImpl { 71 fn target_default() -> PamImpl {
93 if cfg!(target_os = "linux") { 72 if cfg!(target_os = "linux") {
94 PamImpl::LinuxPam 73 PamImpl::LinuxPam
95 } else if cfg!(any( 74 } else if cfg!(any(
96 target_os = "macos", 75 target_os = "macos",
97 target_os = "freebsd", 76 target_os = "freebsd",
101 )) { 80 )) {
102 PamImpl::OpenPam 81 PamImpl::OpenPam
103 } else if cfg!(any(target_os = "illumos", target_os = "solaris")) { 82 } else if cfg!(any(target_os = "illumos", target_os = "solaris")) {
104 PamImpl::Sun 83 PamImpl::Sun
105 } else { 84 } else {
106 PamImpl::XSso 85 Self::probe_detect()
107 } 86 }
87 }
88
89 /// Look at the currently-installed LibPAM.
90 fn probe_detect() -> PamImpl {
91 if let Some(lib) = Self::open() {
92 if lib.has("pam_syslog") {
93 return PamImpl::LinuxPam;
94 } else if lib.has("_openpam_log") {
95 return PamImpl::OpenPam;
96 } else if lib.has("__pam_get_authtok") {
97 return PamImpl::Sun;
98 }
99 }
100 // idk
101 PamImpl::XSso
108 } 102 }
109 103
110 fn open() -> Option<Self> { 104 fn open() -> Option<Self> {
111 let dlopen = |s: &[u8]| unsafe { libc::dlopen(s.as_ptr().cast(), libc::RTLD_LAZY) }; 105 let dlopen = |s: &[u8]| unsafe { libc::dlopen(s.as_ptr().cast(), libc::RTLD_LAZY) };
112 NonNull::new(dlopen(b"libpam.so\0")) 106 NonNull::new(dlopen(b"libpam.so\0"))