#!/bin/bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" PROJECT_ID="autonas" ORG_ID="xdev" RUNTIME_DIR="/usr/local/lib/${ORG_ID}/${PROJECT_ID}" DOC_DIR="/usr/local/share/doc/${ORG_ID}/${PROJECT_ID}" COMMAND_PATH="/usr/local/sbin/autonas" UNINSTALL_PATH="${RUNTIME_DIR}/uninstall.sh" UNINSTALL_WRAPPER="/usr/local/sbin/${ORG_ID}-${PROJECT_ID}-uninstall" DEFAULTS_PATH="/etc/default/${ORG_ID}-${PROJECT_ID}" CONFIG_DIR="/etc/pve/autonas" CONFIG_FILE="${CONFIG_DIR}/disks.conf" MOUNT_BASE="/mnt/autonas" UNIFIED_SCRIPT="autonas.sh" CORE_SCRIPT="autonas-core.sh" DISK_HANDLER_SCRIPT="autonas-disk-handler.sh" UDEV_WRAPPER_SCRIPT="autonas-udev-wrapper.sh" BOOT_SCAN_SCRIPT="autonas-boot-scan.sh" NETWORK_HANDLER_SCRIPT="autonas-network-handler.sh" MEDIA_IMPORTER_SCRIPT="autonas-media-importer.sh" SYNC_ORDERING_SCRIPT="autonas-sync-systemd-ordering.sh" DISK_RULES="99-autonas-disk.rules" INTERFACE_RULES="98-autonas-interfaces.rules" ATTACH_UNIT="autonas-attach@.service" MAIN_UNIT="autonas.service" BOOT_SCAN_UNIT="autonas-boot-scan.service" require_root() { if [[ "${EUID}" -ne 0 ]]; then echo "ERROR: this action must be run as root" >&2 exit 1 fi } is_package_installed() { local package="$1" dpkg -l "$package" 2>/dev/null | grep -q "^ii" } check_dependencies() { local missing_packages=() local required_packages=("nfs-kernel-server" "autofs" "udev" "libimage-exiftool-perl") local package="" echo "Checking AutoNAS dependencies..." for package in "${required_packages[@]}"; do if is_package_installed "${package}"; then echo "✓ ${package}" else echo "✗ ${package}" missing_packages+=("${package}") fi done if [[ ${#missing_packages[@]} -gt 0 ]]; then echo "" echo "Missing packages detected:" printf " %s\n" "${missing_packages[@]}" echo "" echo "Run:" echo " $0 install-deps" return 1 fi echo "✓ All dependencies are installed" } install_dependencies() { require_root if ! command -v apt >/dev/null 2>&1; then echo "ERROR: automatic dependency installation requires apt" >&2 exit 1 fi apt update apt install -y nfs-kernel-server autofs udev libimage-exiftool-perl } run_existing_uninstall() { if [[ -x "${UNINSTALL_PATH}" ]]; then "${UNINSTALL_PATH}" --force || true elif [[ -x "/usr/local/lib/autonas/autonas-uninstall.sh" ]]; then bash /usr/local/lib/autonas/autonas-uninstall.sh --force || true elif [[ -x "/usr/local/bin/autonas-uninstall.sh" ]]; then bash /usr/local/bin/autonas-uninstall.sh --force || true fi } install_runtime() { mkdir -p "${RUNTIME_DIR}" "${DOC_DIR}" "${CONFIG_DIR}" "${MOUNT_BASE}" /usr/local/sbin /etc/default install -m 0755 "${SCRIPT_DIR}/${UNIFIED_SCRIPT}" "${COMMAND_PATH}" install -m 0755 "${SCRIPT_DIR}/${CORE_SCRIPT}" "${RUNTIME_DIR}/${CORE_SCRIPT}" install -m 0755 "${SCRIPT_DIR}/${DISK_HANDLER_SCRIPT}" "${RUNTIME_DIR}/${DISK_HANDLER_SCRIPT}" install -m 0755 "${SCRIPT_DIR}/${UDEV_WRAPPER_SCRIPT}" "${RUNTIME_DIR}/${UDEV_WRAPPER_SCRIPT}" install -m 0755 "${SCRIPT_DIR}/${BOOT_SCAN_SCRIPT}" "${RUNTIME_DIR}/${BOOT_SCAN_SCRIPT}" install -m 0755 "${SCRIPT_DIR}/${NETWORK_HANDLER_SCRIPT}" "${RUNTIME_DIR}/${NETWORK_HANDLER_SCRIPT}" install -m 0755 "${SCRIPT_DIR}/${MEDIA_IMPORTER_SCRIPT}" "${RUNTIME_DIR}/${MEDIA_IMPORTER_SCRIPT}" install -m 0755 "${SCRIPT_DIR}/${SYNC_ORDERING_SCRIPT}" "${RUNTIME_DIR}/${SYNC_ORDERING_SCRIPT}" install -m 0755 "${SCRIPT_DIR}/autonas-uninstall.sh" "${UNINSTALL_PATH}" ln -sfn "${UNINSTALL_PATH}" "${UNINSTALL_WRAPPER}" if [[ ! -f "${DEFAULTS_PATH}" ]]; then install -m 0644 "${PROJECT_DIR}/config/autonas" "${DEFAULTS_PATH}" else echo "Preserving existing defaults: ${DEFAULTS_PATH}" fi } cleanup_legacy_paths() { rm -f /usr/local/bin/autonas rm -f /usr/local/bin/autonas.sh rm -f /usr/local/bin/autonas-core.sh rm -f /usr/local/bin/autonas-disk-handler.sh rm -f /usr/local/bin/autonas-udev-wrapper.sh rm -f /usr/local/bin/autonas-boot-scan.sh rm -f /usr/local/bin/autonas-network-handler.sh rm -f /usr/local/bin/autonas-media-importer.sh rm -f /usr/local/bin/autonas-uninstall.sh } install_system_integration() { install -m 0644 "${PROJECT_DIR}/config/${DISK_RULES}" "/etc/udev/rules.d/${DISK_RULES}" install -m 0644 "${PROJECT_DIR}/config/${INTERFACE_RULES}" "/etc/udev/rules.d/${INTERFACE_RULES}" install -m 0644 "${PROJECT_DIR}/config/${ATTACH_UNIT}" "/etc/systemd/system/${ATTACH_UNIT}" install -m 0644 "${PROJECT_DIR}/config/${MAIN_UNIT}" "/etc/systemd/system/${MAIN_UNIT}" install -m 0644 "${PROJECT_DIR}/config/${BOOT_SCAN_UNIT}" "/etc/systemd/system/${BOOT_SCAN_UNIT}" } install_config() { if [[ ! -f "${CONFIG_FILE}" ]]; then install -m 0644 "${PROJECT_DIR}/config/disks.conf" "${CONFIG_FILE}" echo "Installed initial config template: ${CONFIG_FILE}" else echo "Preserving existing configuration: ${CONFIG_FILE}" fi if [[ ! -f /etc/exports ]]; then touch /etc/exports fi } install_docs() { rm -rf "${DOC_DIR}" mkdir -p "${DOC_DIR}" cp "${PROJECT_DIR}/README.md" "${DOC_DIR}/README.md" cp "${PROJECT_DIR}/DEVELOPMENT.md" "${DOC_DIR}/DEVELOPMENT.md" cp "${PROJECT_DIR}/CHANGELOG.md" "${DOC_DIR}/CHANGELOG.md" cp "${PROJECT_DIR}/INSTALL.md" "${DOC_DIR}/INSTALL.md" } activate_installation() { "${RUNTIME_DIR}/${SYNC_ORDERING_SCRIPT}" systemctl daemon-reload systemctl enable --now nfs-kernel-server >/dev/null 2>&1 || true systemctl enable --now autonas.service >/dev/null 2>&1 || true systemctl enable --now autonas-boot-scan.service >/dev/null 2>&1 || true udevadm control --reload-rules || true udevadm trigger --subsystem-match=block --action=add || true } verify_installation() { local required_paths=( "${COMMAND_PATH}" "${RUNTIME_DIR}/${CORE_SCRIPT}" "${RUNTIME_DIR}/${DISK_HANDLER_SCRIPT}" "${RUNTIME_DIR}/${UDEV_WRAPPER_SCRIPT}" "${RUNTIME_DIR}/${BOOT_SCAN_SCRIPT}" "${RUNTIME_DIR}/${NETWORK_HANDLER_SCRIPT}" "${RUNTIME_DIR}/${MEDIA_IMPORTER_SCRIPT}" "${RUNTIME_DIR}/${SYNC_ORDERING_SCRIPT}" "${UNINSTALL_PATH}" "${DOC_DIR}" "${DEFAULTS_PATH}" "/etc/udev/rules.d/${DISK_RULES}" "/etc/udev/rules.d/${INTERFACE_RULES}" "/etc/systemd/system/${ATTACH_UNIT}" "/etc/systemd/system/${MAIN_UNIT}" "/etc/systemd/system/${BOOT_SCAN_UNIT}" "${CONFIG_FILE}" ) local path="" for path in "${required_paths[@]}"; do if [[ ! -e "${path}" ]]; then echo "ERROR: expected installed path missing: ${path}" >&2 exit 1 fi done } do_start() { require_root "${RUNTIME_DIR}/${SYNC_ORDERING_SCRIPT}" systemctl enable --now nfs-kernel-server >/dev/null 2>&1 || true systemctl enable --now autonas.service >/dev/null 2>&1 || true systemctl enable --now autonas-boot-scan.service >/dev/null 2>&1 || true udevadm control --reload-rules || true udevadm trigger --subsystem-match=block --action=add || true echo "AutoNAS started." } do_restart() { require_root "${RUNTIME_DIR}/${SYNC_ORDERING_SCRIPT}" systemctl restart nfs-kernel-server || true systemctl restart autonas.service || true systemctl restart autonas-boot-scan.service || true udevadm control --reload-rules || true udevadm trigger --subsystem-match=block --action=add || true echo "AutoNAS restarted." } do_stop() { require_root systemctl stop autonas-boot-scan.service || true systemctl stop autonas.service || true systemctl stop nfs-kernel-server || true echo "AutoNAS stopped." } do_status() { echo "=== Services ===" systemctl status nfs-kernel-server --no-pager -l || true systemctl status autonas.service --no-pager -l || true systemctl status autonas-boot-scan.service --no-pager -l || true echo "" echo "=== Config ===" cat "${CONFIG_FILE}" 2>/dev/null || echo "No config found" echo "" echo "=== Mount Points ===" ls -la "${MOUNT_BASE}/" 2>/dev/null || echo "No AutoNAS mount base" echo "" echo "=== Paths ===" [ -x "${COMMAND_PATH}" ] && echo "✓ ${COMMAND_PATH}" [ -d "${RUNTIME_DIR}" ] && echo "✓ ${RUNTIME_DIR}" [ -x "${UNINSTALL_PATH}" ] && echo "✓ ${UNINSTALL_PATH}" } do_install() { require_root echo "=== Installing AutoNAS ===" check_dependencies run_existing_uninstall install_runtime cleanup_legacy_paths install_system_integration install_config install_docs activate_installation verify_installation echo "Installed paths:" echo " command: ${COMMAND_PATH}" echo " runtime: ${RUNTIME_DIR}" echo " uninstall: ${UNINSTALL_PATH}" echo " defaults: ${DEFAULTS_PATH}" echo " docs: ${DOC_DIR}" echo " config: ${CONFIG_FILE} (cluster-shared exception)" echo " mounts: ${MOUNT_BASE} (operational data exception)" echo " services: enabled and started" echo "" echo "Installation completed." } cmd="${1:-install}" case "${cmd}" in install) do_install ;; install-deps) install_dependencies ;; check-deps) check_dependencies ;; start) do_start ;; restart) do_restart ;; stop) do_stop ;; status) do_status ;; *) echo "Usage: $0 [install|install-deps|check-deps|start|restart|stop|status]" >&2 exit 2 ;; esac