#!/usr/bin/env bash # install-macos.sh — build and install ahfail on macOS # Run from the repository root: bash scripts/install-macos.sh set -euo pipefail INSTALL_DIR="/usr/local/lib/ahfail" BOLD=$(tput bold 2>/dev/null || true) RESET=$(tput sgr0 2>/dev/null || true) step() { echo "${BOLD}==> $*${RESET}"; } die() { echo "ERROR: $*" >&2; exit 1; } [[ "$(uname)" == "Darwin" ]] || die "This script is for macOS only." # ── 1. Homebrew ─────────────────────────────────────────────────────────────── if ! command -v brew &>/dev/null; then die "Homebrew is required. Install it from https://brew.sh then re-run this script." fi step "Installing Homebrew dependencies..." brew install --quiet gtk+3 gstreamer gst-plugins-base gst-plugins-good meson ninja # ── 2. Rust ─────────────────────────────────────────────────────────────────── if ! command -v cargo &>/dev/null; then step "Installing Rust via rustup..." curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path # shellcheck source=/dev/null source "$HOME/.cargo/env" fi # ── 3. Build ────────────────────────────────────────────────────────────────── SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" cd "$PROJECT_DIR" step "Building ahfail..." if [[ -d builddir ]]; then meson setup builddir --wipe else meson setup builddir fi meson compile -C builddir # ── 4. Install binaries ─────────────────────────────────────────────────────── step "Installing binaries to ${INSTALL_DIR}/ (requires sudo)..." sudo mkdir -p "$INSTALL_DIR" sudo cp builddir/libahfail_pam.so "$INSTALL_DIR/" sudo cp builddir/ahfail-display "$INSTALL_DIR/" sudo chmod 755 "$INSTALL_DIR/libahfail_pam.so" "$INSTALL_DIR/ahfail-display" # ── 5. Configure PAM ───────────────────────────────────────────────────────── step "Configuring PAM..." # macOS 13+ uses screensaverui; 12 and earlier uses screensaver if [[ -f /etc/pam.d/screensaverui ]]; then PAM_FILE="/etc/pam.d/screensaverui" elif [[ -f /etc/pam.d/screensaver ]]; then PAM_FILE="/etc/pam.d/screensaver" else echo "" echo " Could not find a screensaver PAM file. Add this line manually" echo " to the appropriate file in /etc/pam.d/ (after the auth entries):" echo "" echo " auth optional ${INSTALL_DIR}/libahfail_pam.so" echo "" echo "Done (PAM not configured automatically)." exit 0 fi PAM_LINE="auth optional ${INSTALL_DIR}/libahfail_pam.so" if grep -qF "$PAM_LINE" "$PAM_FILE"; then echo " PAM already configured in ${PAM_FILE}." else # Insert after the last 'auth' line so we stay in the auth block. sudo python3 - "$PAM_FILE" "$PAM_LINE" <<'PYEOF' import sys pam_file, new_line = sys.argv[1], sys.argv[2] + "\n" lines = open(pam_file).readlines() if new_line in lines: sys.exit(0) # Find the last line that starts with 'auth' last_auth = max( (i for i, l in enumerate(lines) if l.strip().startswith("auth")), default=len(lines) - 1, ) lines.insert(last_auth + 1, new_line) open(pam_file, "w").writelines(lines) PYEOF echo " Added to ${PAM_FILE}." fi # ── Done ────────────────────────────────────────────────────────────────────── echo "" echo "${BOLD}Done.${RESET} Lock your screen and type the wrong password to test."