#!/bin/bash set -euo pipefail PROJECT_ID="pve-guests-state" ORG_ID="xdev" INSTALL_DIR="/usr/local/lib/${ORG_ID}/${PROJECT_ID}" DOC_DIR="/usr/local/share/doc/${ORG_ID}/${PROJECT_ID}" STATE_DIR="/var/lib/${ORG_ID}/${PROJECT_ID}" COMMAND_PATH="/usr/local/sbin/pgs" UNINSTALL_PATH="${INSTALL_DIR}/uninstall.sh" UNINSTALL_WRAPPER="/usr/local/sbin/${ORG_ID}-${PROJECT_ID}-uninstall" SOURCE_DIR="" usage() { cat <] Install ${PROJECT_ID} on the current host. EOF } require_root() { if [[ "${EUID}" -ne 0 ]]; then echo "ERROR: this script must be run as root" >&2 exit 1 fi } resolve_source_dir() { if [[ -n "${SOURCE_DIR}" ]]; then SOURCE_DIR="$(cd "${SOURCE_DIR}" && pwd)" else SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" fi } validate_source_tree() { local required_files=( "${SOURCE_DIR}/bin/pgs" "${SOURCE_DIR}/scripts/uninstall.sh" "${SOURCE_DIR}/README.md" "${SOURCE_DIR}/INSTALL.md" "${SOURCE_DIR}/CHANGELOG.md" "${SOURCE_DIR}/LICENSE" "${SOURCE_DIR}/docs/DECISIONS.md" "${SOURCE_DIR}/docs/TECHNICAL.md" ) local file="" for file in "${required_files[@]}"; do if [[ ! -f "${file}" ]]; then echo "ERROR: missing required source file: ${file}" >&2 exit 1 fi done } cleanup_legacy_artifacts() { rm -f /usr/local/sbin/pve-reboot-manager.sh rm -f /usr/local/sbin/pve-guest-state.sh rm -f /root/bin/pgs rm -f /root/bin/pve-reboot-manager.sh rm -f /root/bin/pve-guest-state.sh systemctl disable pve-suspend-vms.service pve-resume-vms.service >/dev/null 2>&1 || true systemctl stop pve-suspend-vms.service pve-resume-vms.service >/dev/null 2>&1 || true rm -f /etc/systemd/system/pve-suspend-vms.service rm -f /etc/systemd/system/pve-resume-vms.service systemctl daemon-reload systemctl reset-failed pve-suspend-vms.service pve-resume-vms.service >/dev/null 2>&1 || true } run_existing_uninstall() { if [[ -x "${UNINSTALL_PATH}" ]]; then echo "Existing installation detected. Running canonical uninstall first..." "${UNINSTALL_PATH}" --force || true else bash "${SOURCE_DIR}/scripts/uninstall.sh" --force || true fi } install_docs() { mkdir -p "${DOC_DIR}/docs" cp "${SOURCE_DIR}/README.md" "${DOC_DIR}/" cp "${SOURCE_DIR}/INSTALL.md" "${DOC_DIR}/" cp "${SOURCE_DIR}/CHANGELOG.md" "${DOC_DIR}/" cp "${SOURCE_DIR}/LICENSE" "${DOC_DIR}/" cp "${SOURCE_DIR}/docs/DECISIONS.md" "${DOC_DIR}/docs/" cp "${SOURCE_DIR}/docs/TECHNICAL.md" "${DOC_DIR}/docs/" } main() { while [[ $# -gt 0 ]]; do case "$1" in --source-dir) SOURCE_DIR="$2" shift 2 ;; -h|--help) usage exit 0 ;; *) echo "ERROR: unknown option: $1" >&2 usage exit 1 ;; esac done require_root resolve_source_dir validate_source_tree echo "=== Installing ${PROJECT_ID} ===" run_existing_uninstall mkdir -p "${INSTALL_DIR}" "${DOC_DIR}" "${STATE_DIR}" /usr/local/sbin cleanup_legacy_artifacts install -m 0755 "${SOURCE_DIR}/bin/pgs" "${COMMAND_PATH}" install -m 0755 "${SOURCE_DIR}/scripts/uninstall.sh" "${UNINSTALL_PATH}" ln -sfn "${UNINSTALL_PATH}" "${UNINSTALL_WRAPPER}" install_docs echo "Installed paths:" echo " command: ${COMMAND_PATH}" echo " uninstall: ${UNINSTALL_PATH}" echo " docs: ${DOC_DIR}" echo " state: ${STATE_DIR}" echo "" echo "Running dry-run verification..." "${COMMAND_PATH}" suspend --dry-run -v 2>&1 | tail -3 || true echo "" echo "Installation completed." } main "$@"