#!/bin/bash
# skillfish-snapshots-read — legge l'elenco degli snapshot e lo stampa in una
# forma che l'applicazione sa leggere. NON modifica niente.
#
# PERCHE' E' UN PROGRAMMA A PARTE
# /.snapshots e' drwxr-x--- root:root: un utente normale non riesce nemmeno a
# elencarla, e `snapper list` gli risponde "No permissions". Quindi anche solo
# per MOSTRARE la finestra serve passare da pkexec.
#
# Se l'elenco e la parte che cancella fossero lo stesso programma, pkexec
# userebbe la stessa autorizzazione per entrambi e ci sarebbero due strade,
# tutte e due brutte: chiedere la password ogni volta che si apre la finestra,
# oppure non chiederla mai, nemmeno per cancellare uno snapshot.
#
# Separandoli, l'autorizzazione la si puo' dare per programma:
#   questo (sola lettura)  -> senza password
#   skillfish-snapshots-helper (modifica) -> con password
#
# ⚠️ Per questo qui non si accetta NESSUN argomento: quello che gira senza
# password non deve avere niente su cui si possa influire da fuori.
set -u

CARTELLA=/.snapshots

# Nessun argomento: vedi sopra. Se ne arriva uno, e' un errore di chi chiama.
[ "$#" -eq 0 ] || { echo "ERR:argomenti this program takes no arguments" >&2; exit 2; }

# --- spazio libero ----------------------------------------------------------
# Serve a rispondere alla domanda che uno si fa davvero ("quanto mi resta?"),
# ed e' la sola misura sensata: le quote btrfs sono spente, quindi lo spazio
# occupato dal singolo snapshot non e' un dato che si possa avere in fretta.
# Con le quote accese `btrfs qgroup show` costa un'attesa lunga a ogni apertura.
if command -v findmnt >/dev/null 2>&1; then
    read -r LIB TOT < <(df -B1 --output=avail,size / 2>/dev/null | tail -1)
    printf 'SPAZIO\t%s\t%s\n' "${LIB:-0}" "${TOT:-0}"
fi

# --- il sistema di prima, messo da parte da un ripristino -------------------
# skillfish-rollback non cancella il sistema che sostituisce: lo lascia come
# @rootfs-rotto-<data>. L'applicazione deve poterlo dire, e offrire di tornare
# indietro: e' la rete di sicurezza di chi ha ripristinato per sbaglio.
CIMA=$(mktemp -d /tmp/skillfish-snapshots.XXXXXX)
DISPOSITIVO=$(findmnt -no SOURCE / 2>/dev/null | sed 's/\[.*//')
if [ -n "${DISPOSITIVO:-}" ] && mount -o subvolid=5 "$DISPOSITIVO" "$CIMA" 2>/dev/null; then
    for v in "$CIMA"/*-rotto-* "$CIMA"/*-annullato-*; do
        [ -d "$v" ] || continue
        printf 'DAPARTE\t%s\n' "$(basename "$v")"
    done
    umount "$CIMA" 2>/dev/null
fi
rmdir "$CIMA" 2>/dev/null

# --- gli snapshot -----------------------------------------------------------
# ⚠️ Qui prima si usciva con  [ -d "$CARTELLA" ] || exit 0.
# Nella sessione live /.snapshots non esiste, quindi il programma finiva
# proprio dove comincia il caso che interessa: cercare i sistemi installati
# su disco. Ora si salta il ciclo e si prosegue.

campo() {   # campo <file> <nome>: il contenuto di <nome> dentro info.xml
    sed -n "s:.*<$2>\(.*\)</$2>.*:\1:p" "$1" 2>/dev/null | head -1
}

# ordine NUMERICO: col glob semplice l'elenco esce 1, 10, 11, 2, 3...
if [ -d "$CARTELLA" ]; then
for n in $(ls -1 "$CARTELLA" 2>/dev/null | grep -xE '[0-9]+' | sort -n); do
    d="$CARTELLA/$n"
    [ -d "$d/snapshot" ] || continue
    i="$d/info.xml"
    [ -r "$i" ] || continue
    # ⚠️ La data dentro info.xml e' in UTC, mentre `snapper list` la mostra
    # nell'ora locale. Qui si passa cosi' com'e' ed e' l'applicazione a
    # convertirla: se la convertissimo qui, la finestra non avrebbe modo di
    # sapere se il fuso e' gia' stato applicato.
    printf 'SNAP\t%s\t%s\t%s\t%s\t%s\n' \
        "$n" \
        "$(campo "$i" date)" \
        "$(campo "$i" type)" \
        "$(campo "$i" pre_num)" \
        "$(campo "$i" description)"
done
fi

# --- i sistemi installati, quando si gira dalla chiavetta live ---------------
# Il ripristino serve soprattutto quando il sistema non parte piu'. In quel caso
# l'unica strada e' avviare dalla chiavetta, e da li' /.snapshots non esiste:
# quello che c'e' sopra ha gia' stampato zero snapshot ed e' giusto cosi'.
#
# ⚠️ NIENTE ARGOMENTI, vedi la nota in cima: e' questo programma a cercarsi i
# dischi, non chi lo chiama a indicarglieli.
#
# Si riconosce la live da due segni indipendenti: la radice e' un overlay e
# esiste /run/live. Basta uno dei due.
in_live() {
    [ -d /run/live ] && return 0
    [ "$(findmnt -no FSTYPE / 2>/dev/null)" = "overlay" ] && return 0
    return 1
}

if in_live; then
    VIVO=$(findmnt -no SOURCE / 2>/dev/null | sed 's/\[.*//')
    for dev in $(blkid -t TYPE=btrfs -o device 2>/dev/null | sort); do
        [ "$dev" = "$VIVO" ] && continue
        T=$(mktemp -d /tmp/skillfish-snapshots-live.XXXXXX)
        # in sola lettura: qui si guarda e basta, non si ripara niente
        if mount -o ro,subvolid=5 "$dev" "$T" 2>/dev/null; then
            for c in $(ls -1 "$T" 2>/dev/null); do
                case "$c" in *-rotto-*|*-annullato-*) continue ;; esac
                [ -f "$T/$c/etc/fstab" ] && [ -d "$T/$c/usr" ] || continue
                SNAPDIR="$T/$c/.snapshots"
                quanti=$(ls -1 "$SNAPDIR" 2>/dev/null | grep -cxE '[0-9]+')
                nome=$(sed -n 's/^PRETTY_NAME="\(.*\)"/\1/p' "$T/$c/etc/os-release" 2>/dev/null | head -1)
                printf 'BERSAGLIO\t%s\t%s\t%s\t%s\n' \
                       "$dev" "$c" "${quanti:-0}" "${nome:-SkillFishOS}"
                # Lo spazio del DISCO CHE SI RIPARA, non quello della
                # chiavetta: mostrare i quattro giga della live mentre si guarda
                # un disco da mezzo terabyte e' un numero giusto nel posto
                # sbagliato, e nessuno lo mette in dubbio.
                read -r bl bt < <(df -B1 --output=avail,size "$T" 2>/dev/null | tail -1)
                printf 'SPAZIOB\t%s\t%s\t%s\n' "$dev" "${bl:-0}" "${bt:-0}"
                for n in $(ls -1 "$SNAPDIR" 2>/dev/null | grep -xE '[0-9]+' | sort -n); do
                    d="$SNAPDIR/$n"
                    [ -d "$d/snapshot" ] || continue
                    i="$d/info.xml"
                    [ -r "$i" ] || continue
                    printf 'SNAPB\t%s\t%s\t%s\t%s\t%s\t%s\n' \
                        "$dev" "$n" \
                        "$(campo "$i" date)" \
                        "$(campo "$i" type)" \
                        "$(campo "$i" pre_num)" \
                        "$(campo "$i" description)"
                done
                # i sistemi messi da parte da ripristini precedenti, per poter
                # offrire «torna indietro» anche da qui
                for v in "$T/${c}-rotto-"* "$T/${c}-annullato-"*; do
                    [ -d "$v" ] || continue
                    printf 'DAPARTEB\t%s\t%s\n' "$dev" "$(basename "$v")"
                done
            done
            umount "$T" 2>/dev/null
        fi
        rmdir "$T" 2>/dev/null
    done
fi

# --- la manutenzione programmata ---------------------------------------------
# Sta qui e non in un programma a parte perche' anche questa e' SOLA LETTURA, e
# perche' meta' dei dati vuole comunque i privilegi: `btrfs scrub status` legge
# /var/lib/btrfs, che e' di root, e `btrfs filesystem df` passa da una ioctl
# privilegiata. Le periodicita' e i timer si leggerebbero anche da utente, ma
# farne un programma a parte vorrebbe dire una terza autorizzazione polkit per
# meta' delle informazioni.
[ -x /usr/local/bin/skillfish-btrfs-manutenzione ] \
    && /usr/local/bin/skillfish-btrfs-manutenzione stato 2>/dev/null

exit 0
