changeset 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 634cd5f2ac8b
children e9354e655f38
files libpam-sys/Cargo.toml libpam-sys/README.md libpam-sys/libpam-sys-consts/README.md libpam-sys/libpam-sys-consts/build.rs
diffstat 4 files changed, 43 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/libpam-sys/Cargo.toml	Sat Jul 12 18:16:18 2025 -0400
+++ b/libpam-sys/Cargo.toml	Sun Jul 13 15:38:00 2025 -0400
@@ -21,6 +21,15 @@
 rust-version.workspace = true
 license.workspace = true
 
+[package.metadata.docs.rs]
+default-target = "x86_64-unknown-linux-gnu"
+
+targets = [
+    "x86_64-apple-darwin",
+    "x86_64-unknown-freebsd",
+    "x86_64-unknown-illumos",
+]
+
 [dependencies]
 libc = "0.2"
 libpam-sys-consts = { path = "libpam-sys-consts" }
@@ -28,8 +37,5 @@
 [target.'cfg(doc)'.dependencies]
 libpam-sys-helpers = { path = "libpam-sys-helpers" }
 
-[dev-dependencies]
-libpam-sys-helpers = { path = "libpam-sys-helpers" }
-
 [build-dependencies]
 libpam-sys-consts = { path = "libpam-sys-consts" }
--- a/libpam-sys/README.md	Sat Jul 12 18:16:18 2025 -0400
+++ b/libpam-sys/README.md	Sun Jul 13 15:38:00 2025 -0400
@@ -3,17 +3,17 @@
 This crate provides low-level access to PAM, working with multiple PAM implementations.
 You do not need PAM system headers installed to use this!
 
-If you're looking for a nice, safe, Rusty API to PAM, may I recommend [nonstick][nonstick]?
+If you're looking for a nice, safe, Rusty API to PAM, may I recommend [nonstick]?
 
 ## PAM implementations
 
-This crate detects the PAM implementation you have installed, or guesses based on the OS if that's unavailable.
 Supported PAM implementations are defined in the `pam_impl::PamImpl` enum.
 
+This crate automatically chooses the appropriate PAM implementation you are most likely to need installed based on the target OS.
 You can also explicitly specify the PAM implementation you want (if not detected correctly) by setting the `LIBPAMSYS_IMPL` environment variable **at build time**.
 All build-time configuration is performed by the build script of the [`libpam-sys-consts` crate](https://crates.io/crates/libpam-sys-consts).
 
-Each implementation exports all the functionality available in its respective PAM library.
+Normally, this crate exports all functionality available in the selected PAM library.
 `XSso` exports only the subset of the [X/SSO specification][xsso] supported by both OpenPAM and Sun PAM.
 
 ## Testing
--- a/libpam-sys/libpam-sys-consts/README.md	Sat Jul 12 18:16:18 2025 -0400
+++ b/libpam-sys/libpam-sys-consts/README.md	Sun Jul 13 15:38:00 2025 -0400
@@ -53,10 +53,10 @@
 
 If you need to configure this, you can override it **at build time** with the `LIBPAMSYS_IMPL` environment variable:
 
-- Unset or `` (the default): Probe the currently installed LibPAM to figure out which implementation it is.
-  If LibPAM is not present, this will fall back to `__TARGET_DEFAULT__`.
-- `__TARGET_DEFAULT__`: Select the version of LibPAM most likely to be installed on the current OS.
-  This is a useful setting for cross-compiling.
+- Unset or empty (the default): Use the version of PAM most commonly found on the target OS.
+  If we don't know what kind of PAM is usually installed on this OS, we fall back to `__installed__`.
+- `__installed__`: Looks at the PAM library installed on the current machine.
+  If none is recognized, falls back to `XSso`.
 - The name of a `PamEnum` entry: The named PAM implementation.
   For instance, `LIBPAMSYS_IMPL=OpenPam cargo build` will build this library for OpenPAM.
 
--- a/libpam-sys/libpam-sys-consts/build.rs	Sat Jul 12 18:16:18 2025 -0400
+++ b/libpam-sys/libpam-sys-consts/build.rs	Sun Jul 13 15:38:00 2025 -0400
@@ -8,40 +8,34 @@
 
 /// The strategy to use to detect PAM.
 enum Detect {
-    /// Automatically detect PAM, using the installed implementation if present
-    /// or the OS default if not.
-    Auto,
-    /// Use the default PAM implementation based on the OS.
+    /// Use the default PAM implementation based on the OS,
+    /// or the currently-installed version if the OS is not recognized.
     TargetDefault,
+    /// Detect the installed implementation.
+    Installed,
     /// Use the named version of PAM.
     Specified(PamImpl),
 }
 
-const TARGET_DEFAULT: &str = "__TARGET_DEFAULT__";
+const INSTALLED: &str = "__installed__";
 
 fn main() {
     let detection = match option_env!("LIBPAMSYS_IMPL") {
-        None | Some("") => match option_env!("DOCS_RS") {
-            // docs.rs cross-compiles, so we don't want to look at
-            // its currently-installed PAM; instead we want to use the OS.
-            Some(_) => Detect::TargetDefault,
-            // In other cases, just auto-detect the actual installed library.
-            None => Detect::Auto,
-        },
-        Some(TARGET_DEFAULT) => Detect::TargetDefault,
+        Some("") | None => Detect::TargetDefault,
+        Some(INSTALLED) => Detect::Installed,
         Some(val) => Detect::Specified(PamImpl::try_from(val).unwrap_or_else(|_| {
             panic!(
                 "unknown PAM implementation {val:?}. \
                 valid LIBPAMSYS_IMPLs are {:?}, \
-                {TARGET_DEFAULT:?} to use the OS default, \
+                {INSTALLED:?} to use the OS default, \
                 or unset to detect",
                 PamImpl::items()
             )
         })),
     };
     let pam_impl = match detection {
-        Detect::Auto => LibPam::probe_detect(),
-        Detect::TargetDefault => LibPam::os_default(),
+        Detect::TargetDefault => LibPam::target_default(),
+        Detect::Installed => LibPam::probe_detect(),
         Detect::Specified(other) => other,
     };
     let impl_str = format!("{pam_impl:?}");
@@ -73,23 +67,8 @@
 struct LibPam(NonNull<c_void>);
 
 impl LibPam {
-    /// Look at the currently-installed LibPAM, or use [`Self::os_default`]
-    /// if absent.
-    fn probe_detect() -> PamImpl {
-        if let Some(lib) = Self::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;
-            }
-        }
-        Self::os_default()
-    }
-
     /// Guess the PAM implementation based on the current OS.
-    fn os_default() -> PamImpl {
+    fn target_default() -> PamImpl {
         if cfg!(target_os = "linux") {
             PamImpl::LinuxPam
         } else if cfg!(any(
@@ -103,10 +82,25 @@
         } else if cfg!(any(target_os = "illumos", target_os = "solaris")) {
             PamImpl::Sun
         } else {
-            PamImpl::XSso
+            Self::probe_detect()
         }
     }
 
+    /// Look at the currently-installed LibPAM.
+    fn probe_detect() -> PamImpl {
+        if let Some(lib) = Self::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;
+            }
+        }
+        // idk
+        PamImpl::XSso
+    }
+
     fn open() -> Option<Self> {
         let dlopen = |s: &[u8]| unsafe { libc::dlopen(s.as_ptr().cast(), libc::RTLD_LAZY) };
         NonNull::new(dlopen(b"libpam.so\0"))