#!/bin/bash
# ROSA automatic offline updates download helper
#
# Downloads updates using dnf offline-upgrade and schedules them to be
# installed on next boot without rebooting immediately.

set -eou pipefail

readonly SCRIPT_NAME="rosa-offline-updates"
readonly LOCK_FILE="/run/${SCRIPT_NAME}.lock"
readonly DATADIR="/var/lib/dnf/system-upgrade"
readonly SYMLINK="/system-update"
readonly MIN_FREE_KB=$((1 * 1024 * 1024))  # 1 GiB minimum free space

log() {
    echo "${SCRIPT_NAME}: $*"
}

die() {
    echo "${SCRIPT_NAME}: ERROR: $*" >&2
    exit 1
}

# Prevent concurrent runs.
exec 200>"${LOCK_FILE}"
if ! flock -n 200; then
    log "Another instance is already running, exiting."
    exit 0
fi

# We hold the lock now; remove the lock file on exit so that the file's
# existence reliably signals an active run to other tools (e.g. the
# rosa-update-system applet, which runs as a regular user and cannot
# take this root-owned lock).
cleanup() {
    rm -f "${LOCK_FILE}"
}
trap cleanup EXIT

# Only run on systems where offline-upgrade is available.
if ! command -v dnf >/dev/null 2>&1; then
    die "dnf not found"
fi

if ! dnf offline-upgrade --help >/dev/null 2>&1; then
    die "dnf offline-upgrade plugin not available"
fi

# Check free disk space where the offline-upgrade cache will be written.
# The target directory may not exist yet, so create it first; otherwise df
# would report the parent mount, which is wrong if /var/lib/dnf/system-upgrade
# is supposed to be on a separate mount.
mkdir -p "$DATADIR"
avail_kb=$(df --block-size=1K --output=avail "$DATADIR" 2>/dev/null | tail -n 1 | tr -d ' ' || true)
if [ -n "${avail_kb}" ] && [ "${avail_kb}" -lt "${MIN_FREE_KB}" ]; then
    die "Insufficient disk space on $DATADIR (${avail_kb} KiB free, ${MIN_FREE_KB} KiB required)"
fi

# An offline update transaction with the same symlink to another
# destination may belong to packagekit/GNOME Software/Discover
if [ -L "${SYMLINK}" ]; then
    if [ "$(readlink "${SYMLINK}")" = "${DATADIR}" ]; then
        rm -f "${SYMLINK}"
    else
        log "${SYMLINK} belongs to another tool, skipping this run"
        exit 0
    fi
fi
if [ -e "${DATADIR}/system-upgrade-state.json" ]; then
    log "Cleaning up previous offline upgrade state"
    dnf offline-upgrade clean || true
fi

log "Starting offline upgrade download"
dnf offline-upgrade download -y || die "dnf offline-upgrade download failed"

# "offline-upgrade reboot" will fail if there are no updates
if [ ! -s "${DATADIR}/system-upgrade-transaction.json" ]; then
    log "System is already up to date, nothing to schedule"
    exit 0
fi

log "Scheduling offline upgrade for next boot"
DNF_SYSTEM_UPGRADE_NO_REBOOT=1 dnf offline-upgrade reboot -y || die "dnf offline-upgrade reboot failed"

log "Offline updates downloaded and scheduled successfully"
