#!/bin/bash # ============================================================================== # DETECTION LIBRARY: OS, Hardware, and Environment Detection # ============================================================================== # Part of unified bootstrap system for homelab infrastructure # Provides functions to identify OS family, hardware type, CPU, GPU, and # deployment environment for intelligent provisioning decisions. # ============================================================================== # --- OS DETECTION --- detect_os_family() { # Detect OS family: debian, ubuntu, raspbian, or unknown # Returns lowercase OS identifier if [ -f /etc/os-release ]; then . /etc/os-release case "${ID,,}" in debian) echo "debian" return 0 ;; ubuntu) echo "ubuntu" return 0 ;; raspbian) echo "raspbian" return 0 ;; *) echo "unknown" return 1 ;; esac else echo "unknown" return 1 fi } detect_os_version() { # Get OS version codename (e.g., trixie, bookworm, noble) if [ -f /etc/os-release ]; then . /etc/os-release echo "${VERSION_CODENAME:-unknown}" else echo "unknown" fi } is_debian_trixie() { # Special detection for Debian Trixie (requires Docker repo workaround) local os_family=$(detect_os_family) local os_version=$(detect_os_version) [[ "$os_family" == "debian" && "$os_version" == "trixie" ]] } # --- HARDWARE TYPE DETECTION --- detect_hardware_type() { # Auto-detect hardware deployment type # Returns: proxmox, docker-vm, pi, physical-docker, ai-workstation, unknown # Check for Proxmox VE installation if command -v pveversion &>/dev/null; then echo "proxmox" return 0 fi # Check if running inside a VM (common indicators) local is_vm=0 if systemd-detect-virt --vm &>/dev/null; then is_vm=1 elif [ -d /sys/class/dmi/id ]; then local product_name=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "") if [[ "$product_name" =~ (VirtualBox|VMware|KVM|QEMU) ]]; then is_vm=1 fi fi # Check for Raspberry Pi if grep -q "Raspberry Pi" /proc/cpuinfo 2>/dev/null; then echo "pi" return 0 fi # Check for Docker installation (distinguishes VM vs physical) local has_docker=0 if command -v docker &>/dev/null || [ -f /usr/bin/docker ]; then has_docker=1 fi # Check for high-end GPU (indicates AI workstation) local gpu_type=$(detect_gpu) if [[ "$gpu_type" =~ (nvidia|amd) ]]; then local gpu_info=$(lspci 2>/dev/null | grep -i vga | head -n1) # High-end NVIDIA cards (RTX, Tesla, Quadro) or AMD (Radeon Pro, Instinct) if [[ "$gpu_info" =~ (RTX|Tesla|Quadro|A[0-9]{3,4}|Radeon Pro|Instinct) ]]; then echo "ai-workstation" return 0 fi fi # Classify based on VM + Docker if [ $is_vm -eq 1 ]; then echo "docker-vm" return 0 else echo "physical-docker" return 0 fi } # --- CPU DETECTION --- detect_cpu_vendor() { # Detect CPU vendor: intel, amd, arm, or unknown if [ -f /proc/cpuinfo ]; then if grep -qi "intel" /proc/cpuinfo; then echo "intel" return 0 elif grep -qi "amd" /proc/cpuinfo; then echo "amd" return 0 elif grep -qi "arm\|aarch64" /proc/cpuinfo; then echo "arm" return 0 fi fi echo "unknown" return 1 } detect_cpu_generation() { # Detect Intel CPU generation for kernel parameter tuning # Returns generation number (e.g., 12, 13, 14) or "unknown" local vendor=$(detect_cpu_vendor) if [ "$vendor" != "intel" ]; then echo "unknown" return 1 fi local model_name=$(grep "model name" /proc/cpuinfo | head -n1 | cut -d: -f2 | xargs) # Extract generation from model name patterns # Example: "12th Gen Intel(R) Core(TM) i7-12700" if [[ "$model_name" =~ ([0-9]{2})th\ Gen ]]; then echo "${BASH_REMATCH[1]}" return 0 elif [[ "$model_name" =~ i[3579]-([0-9]{2})[0-9]{2,3} ]]; then echo "${BASH_REMATCH[1]}" return 0 fi echo "unknown" return 1 } needs_intel_hybrid_core_tuning() { # 12th Gen+ Intel CPUs with hybrid architecture need special kernel params # Returns 0 (true) if tuning required, 1 (false) otherwise local gen=$(detect_cpu_generation) # 12th Gen (Alder Lake) and newer have P-cores + E-cores if [[ "$gen" =~ ^[0-9]+$ ]] && [ "$gen" -ge 12 ]; then return 0 fi return 1 } # --- GPU DETECTION --- detect_gpu() { # Detect GPU vendor: nvidia, amd, intel, or none if ! command -v lspci &>/dev/null; then echo "none" return 1 fi local gpu_info=$(lspci 2>/dev/null | grep -i vga) if echo "$gpu_info" | grep -qi nvidia; then echo "nvidia" return 0 elif echo "$gpu_info" | grep -qi amd; then echo "amd" return 0 elif echo "$gpu_info" | grep -qi intel; then echo "intel" return 0 fi echo "none" return 1 } get_gpu_model() { # Get detailed GPU model information if ! command -v lspci &>/dev/null; then echo "unknown" return 1 fi local gpu_line=$(lspci 2>/dev/null | grep -i "vga\|3d\|display" | head -n1) if [ -n "$gpu_line" ]; then # Extract model after the vendor info echo "$gpu_line" | cut -d: -f3 | xargs else echo "none" fi } # --- NETWORK INTERFACE DETECTION --- detect_primary_interface() { # Find the primary physical network interface (excludes lo, docker, veth) # Try to find interface with default route local iface=$(ip route show default 2>/dev/null | awk '/^default/ {print $5; exit}') if [ -n "$iface" ]; then echo "$iface" return 0 fi # Fallback: first non-loopback physical interface iface=$(ip -o link show | awk -F': ' '$2 != "lo" && $2 !~ /^(docker|veth|br-)/ {print $2; exit}') if [ -n "$iface" ]; then echo "$iface" return 0 fi echo "unknown" return 1 } get_current_ip() { # Get current IPv4 address of primary interface local iface=$(detect_primary_interface) if [ "$iface" == "unknown" ]; then echo "unknown" return 1 fi local ip=$(ip -4 addr show "$iface" 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n1) if [ -n "$ip" ]; then echo "$ip" else echo "unknown" return 1 fi } # --- ENVIRONMENT DETECTION --- is_running_in_container() { # Check if running inside a container (Docker, LXC, etc.) if [ -f /.dockerenv ]; then return 0 fi if grep -q "lxc\|docker" /proc/1/cgroup 2>/dev/null; then return 0 fi return 1 } detect_init_system() { # Detect init system: systemd, sysvinit, or unknown if [ -d /run/systemd/system ]; then echo "systemd" return 0 elif [ -f /sbin/init ]; then local init_path=$(readlink -f /sbin/init) if [[ "$init_path" =~ systemd ]]; then echo "systemd" return 0 fi fi echo "unknown" return 1 } # --- PACKAGE MANAGER DETECTION --- detect_package_manager() { # Detect primary package manager: apt, dnf, yum, or unknown if command -v apt-get &>/dev/null; then echo "apt" return 0 elif command -v dnf &>/dev/null; then echo "dnf" return 0 elif command -v yum &>/dev/null; then echo "yum" return 0 fi echo "unknown" return 1 } # --- SUMMARY FUNCTION --- print_detection_summary() { # Print comprehensive detection summary to stderr for logging cat >&2 <