Files
Linux-Hello/install.sh
eliott 922f7aa0c9 fix: auto-detect display manager in installer
Support GDM, SDDM, and LightDM for PAM integration. Detect which
display manager is running and configure the correct PAM file. Show
manual instructions if no supported DM is found.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:38:40 +02:00

251 lines
11 KiB
Bash
Executable File

#!/bin/bash
#
# Linux Hello — One-command installer
#
# Usage:
# git clone https://gitea.lab48.be/eliott/Linux-Hello.git && cd Linux-Hello && ./install.sh
#
# What this does:
# 1. Installs build dependencies
# 2. Builds everything (daemon, CLI, PAM module, ONNX models)
# 3. Installs system files (daemon, PAM, systemd, config)
# 4. Sets up IR emitter (interactive — you'll confirm when it lights up)
# 5. Enrolls your face
# 6. Enables face unlock on the lock screen
#
# Your password always works as fallback. To uninstall: ./install.sh --uninstall
#
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# ─── Uninstall ────────────────────────────────────────────────────────────────
if [ "$1" = "--uninstall" ]; then
echo -e "${CYAN}${BOLD}Uninstalling Linux Hello...${NC}"
sudo sed -i '/pam_linux_hello/d' /etc/pam.d/gdm-password /etc/pam.d/sddm /etc/pam.d/lightdm 2>/dev/null || true
sudo systemctl disable --now linux-hello.service 2>/dev/null || true
sudo rm -f /usr/libexec/linux-hello-daemon /usr/local/bin/linux-hello
sudo rm -f /lib/x86_64-linux-gnu/security/pam_linux_hello.so /lib/security/pam_linux_hello.so
sudo rm -f /etc/systemd/system/linux-hello.service
sudo rm -f /etc/pam.d/gdm-linux-hello
sudo rm -f /etc/dbus-1/system.d/org.linuxhello.Daemon.conf
sudo rm -rf /etc/linux-hello /var/lib/linux-hello /usr/share/linux-hello
sudo rm -rf /usr/local/lib/linux-hello
sudo systemctl daemon-reload 2>/dev/null || true
echo -e "${GREEN}Uninstalled.${NC}"
exit 0
fi
# ─── Checks ───────────────────────────────────────────────────────────────────
echo -e "${CYAN}${BOLD}"
echo " ╔═══════════════════════════════════╗"
echo " ║ Linux Hello Installer ║"
echo " ║ Face unlock for your laptop ║"
echo " ╚═══════════════════════════════════╝"
echo -e "${NC}"
# Check we're not root (we'll sudo when needed)
if [ "$EUID" -eq 0 ]; then
echo -e "${RED}Don't run as root — the script will sudo when needed.${NC}"
exit 1
fi
# Check for Rust
if ! command -v cargo &>/dev/null; then
echo -e "${RED}Rust not found. Install it first:${NC}"
echo " curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
exit 1
fi
# Check for IR camera
echo -e "${YELLOW}[1/8]${NC} Checking hardware..."
if ! v4l2-ctl --list-devices 2>/dev/null | grep -qi "ir\|infrared\|integrated i"; then
echo -e "${YELLOW} Warning: No IR camera detected. Face unlock will work but won't block photo attacks.${NC}"
echo -e "${YELLOW} (Tested on: Lenovo Yoga Pro 7 with Chicony IR camera)${NC}"
HAS_IR=false
else
echo -e "${GREEN} IR camera found.${NC}"
HAS_IR=true
fi
# ─── Dependencies ─────────────────────────────────────────────────────────────
echo -e "${YELLOW}[2/8]${NC} Installing dependencies..."
sudo apt-get update -qq
sudo apt-get install -y -qq libpam0g-dev v4l-utils build-essential wget >/dev/null 2>&1
echo -e "${GREEN} Done.${NC}"
# ─── Build ────────────────────────────────────────────────────────────────────
echo -e "${YELLOW}[3/8]${NC} Downloading ONNX models..."
./scripts/download-models.sh
echo -e "${YELLOW}[4/8]${NC} Building (this takes a minute)..."
cargo build --release --features onnx 2>&1 | tail -1
cd pam-module && make -s 2>&1 && cd ..
echo -e "${GREEN} Built successfully.${NC}"
# ─── ONNX Runtime ─────────────────────────────────────────────────────────────
echo -e "${YELLOW}[5/8]${NC} Setting up ONNX Runtime..."
GLIBC_VER=$(ldd --version 2>&1 | head -1 | grep -oP '\d+\.\d+$')
if [ -f "$HOME/.local/lib/linux-hello/libonnxruntime.so" ]; then
echo -e "${GREEN} Already installed.${NC}"
elif echo "$GLIBC_VER" | awk '{ exit ($1 >= 2.38) ? 0 : 1 }'; then
echo "y" | ./scripts/install-onnx-runtime.sh --user 2>&1 | grep -E "^\[|^$" | head -5
else
./scripts/install-onnx-runtime.sh --user 2>&1 | grep -E "^\[|^$" | head -5
fi
# ─── System Install ──────────────────────────────────────────────────────────
echo -e "${YELLOW}[6/8]${NC} Installing system files (needs sudo)..."
# Stop existing service if running
sudo systemctl stop linux-hello.service 2>/dev/null || true
# Binaries
sudo install -m 755 target/release/linux-hello-daemon /usr/libexec/linux-hello-daemon
sudo install -m 755 target/release/linux-hello /usr/local/bin/linux-hello
# ONNX models
sudo install -d /usr/share/linux-hello/models
for model in models/*.onnx; do
[ -f "$model" ] && sudo install -m 644 "$model" /usr/share/linux-hello/models/
done
# ONNX Runtime library
ORT_LIB="$HOME/.local/lib/linux-hello/libonnxruntime.so"
if [ -f "$ORT_LIB" ]; then
sudo install -d /usr/local/lib/linux-hello
sudo install -m 755 "$ORT_LIB" /usr/local/lib/linux-hello/libonnxruntime.so
fi
# Config
sudo install -d /etc/linux-hello
if [ ! -f /etc/linux-hello/config.toml ]; then
sudo install -m 644 dist/config.toml /etc/linux-hello/config.toml
fi
# D-Bus policy
sudo install -m 644 dist/org.linuxhello.Daemon.conf /etc/dbus-1/system.d/
# PAM module
PAM_DIR="/lib/x86_64-linux-gnu/security"
[ ! -d "$PAM_DIR" ] && PAM_DIR="/lib/security"
sudo install -m 755 pam-module/pam_linux_hello.so "$PAM_DIR/"
# Systemd service
sudo cp dist/linux-hello.service /etc/systemd/system/linux-hello.service
sudo systemctl daemon-reload
echo -e "${GREEN} Installed.${NC}"
# ─── IR Emitter ───────────────────────────────────────────────────────────────
if [ "$HAS_IR" = true ]; then
echo -e "${YELLOW}[7/8]${NC} Configuring IR emitter..."
if ! command -v linux-enable-ir-emitter &>/dev/null; then
echo " Building linux-enable-ir-emitter..."
IR_DIR=$(mktemp -d)
git clone -q https://github.com/EmixamPP/linux-enable-ir-emitter.git "$IR_DIR" 2>/dev/null
(cd "$IR_DIR" && cargo build --release -q 2>/dev/null)
sudo install -m 755 "$IR_DIR/target/release/linux-enable-ir-emitter" /usr/local/bin/
rm -rf "$IR_DIR"
fi
echo ""
echo -e "${BOLD} The IR emitter setup is interactive.${NC}"
echo -e " It will show your IR camera feed and try different settings."
echo -e " ${BOLD}Press 'y' when you see a bright red/white IR light, 'n' otherwise.${NC}"
echo ""
read -p " Ready? (press Enter) " _
sudo linux-enable-ir-emitter configure || echo -e "${YELLOW} IR emitter setup skipped or failed. You can retry later with: sudo linux-enable-ir-emitter configure${NC}"
else
echo -e "${YELLOW}[7/8]${NC} Skipping IR emitter (no IR camera detected)."
fi
# ─── Enable & Enroll ─────────────────────────────────────────────────────────
echo -e "${YELLOW}[8/8]${NC} Starting daemon and enrolling your face..."
sudo systemctl enable --now linux-hello.service
echo ""
echo -e "${BOLD} Look at the camera — enrolling your face now.${NC}"
echo ""
sudo ORT_DYLIB_PATH=/usr/local/lib/linux-hello/libonnxruntime.so linux-hello enroll --label default
# ─── PAM Integration ─────────────────────────────────────────────────────────
# Detect display manager and configure PAM accordingly
DM_NAME=""
PAM_CONFIGURED=false
if systemctl is-active --quiet gdm.service 2>/dev/null || systemctl is-active --quiet gdm3.service 2>/dev/null; then
DM_NAME="GDM"
PAM_FILE="/etc/pam.d/gdm-password"
if [ -f "$PAM_FILE" ] && ! grep -q "pam_linux_hello" "$PAM_FILE"; then
sudo sed -i '/@include common-auth/i auth sufficient pam_linux_hello.so timeout=5' "$PAM_FILE"
PAM_CONFIGURED=true
elif grep -q "pam_linux_hello" "$PAM_FILE" 2>/dev/null; then
PAM_CONFIGURED=true
fi
elif systemctl is-active --quiet sddm.service 2>/dev/null; then
DM_NAME="SDDM"
PAM_FILE="/etc/pam.d/sddm"
if [ -f "$PAM_FILE" ] && ! grep -q "pam_linux_hello" "$PAM_FILE"; then
sudo sed -i '/auth.*include.*system-login\|auth.*include.*common-auth\|@include common-auth/i auth sufficient pam_linux_hello.so timeout=5' "$PAM_FILE"
PAM_CONFIGURED=true
elif grep -q "pam_linux_hello" "$PAM_FILE" 2>/dev/null; then
PAM_CONFIGURED=true
fi
elif systemctl is-active --quiet lightdm.service 2>/dev/null; then
DM_NAME="LightDM"
PAM_FILE="/etc/pam.d/lightdm"
if [ -f "$PAM_FILE" ] && ! grep -q "pam_linux_hello" "$PAM_FILE"; then
sudo sed -i '/@include common-auth\|auth.*include.*system-login/i auth sufficient pam_linux_hello.so timeout=5' "$PAM_FILE"
PAM_CONFIGURED=true
elif grep -q "pam_linux_hello" "$PAM_FILE" 2>/dev/null; then
PAM_CONFIGURED=true
fi
fi
# ─── Done ─────────────────────────────────────────────────────────────────────
echo ""
echo -e "${GREEN}${BOLD} ╔═══════════════════════════════════╗"
echo " ║ Installation complete! ║"
echo " ╚═══════════════════════════════════╝${NC}"
echo ""
if [ "$PAM_CONFIGURED" = true ]; then
echo " Lock your screen and look at the camera to unlock."
echo " Your password always works as a fallback."
echo " Display manager: $DM_NAME ($PAM_FILE)"
elif [ -n "$DM_NAME" ]; then
echo -e " ${YELLOW}Could not configure $DM_NAME automatically.${NC}"
echo " Add this line to $PAM_FILE before the auth include:"
echo " auth sufficient pam_linux_hello.so timeout=5"
else
echo -e " ${YELLOW}No supported display manager detected (GDM, SDDM, LightDM).${NC}"
echo " To enable face unlock, add this line to your display manager's PAM config:"
echo " auth sufficient pam_linux_hello.so timeout=5"
fi
echo ""
echo " Commands:"
echo " linux-hello test — test face recognition"
echo " linux-hello enroll — re-enroll your face"
echo " linux-hello status — check system status"
echo " ./install.sh --uninstall — remove everything"
echo ""