#!/bin/bash
# Script zur inkrementellen Sicherung von invis-Servern.
# Systemvoraussetzungen LVM-basiertes Festplattenmanagement und 
# das Tool borg-backup
# Weiterhin muss ein Backup-Server erreichbar sein, an dem sich
# der lokale Benutzer "root" per SSH-Schluessel anmelden kann.
# Auf dem Zielhost muss ebenfalls borg-backup installiert sein.

# Version 0.12

# (c) 2012,2014,2016,2020,2022 Stefan Schaefer -- FSP Computer & Netzwerke
# License GPLv3

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Dieses Programm ist Freie Software: Sie können es unter den Bedingungen
# der GNU General Public License, wie von der Free Software Foundation,
# Version 3 der Lizenz oder (nach Ihrer Option) jeder späteren
# veröffentlichten Version, weiterverbreiten und/oder modifizieren.

# Dieses Programm wird in der Hoffnung, dass es nützlich sein wird, aber
# OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
# Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
# Siehe die GNU General Public License für weitere Details.

# Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
# Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>

# Globale Einstellungen
confdir="/etc/invis"
conffile="$confdir/bbudisk.conf"
passfile="$confdir/invis-pws.conf"

backupdir="/mnt/backup"
logfile="/var/log/bbackup.log"
datum=$(date "+%d.%m.%Y - %H:%M")
archivedate=`date "+%Y-%m-%d"`
getconfvalue() {
    cat $3 | grep "$1:" | cut -d ":" -f "$2"
}

# Mountpoint fuer Backuvolumes erstellen
if [[ ! -d  $backupdir ]]; then
    mkdir -p $backupdir
fi

# LVM-Snapshot erzeugen und mounten
snapshotvolume=$(getconfvalue "snapshotVolume" "2" "$conffile")
volumegroup=$(getconfvalue "volumeGroup" "2" "$conffile")
sourcevolume=($(getconfvalue "sourceVolume" "2" "$conffile"))
snapshotsize=$(getconfvalue "snapshotSize" "2" "$conffile")
mailto=$(getconfvalue "mailTo" "2" "$conffile")
mailfrom=$(getconfvalue "mailFrom" "2" "$conffile")
mailwhen=$(getconfvalue "mailWhen" "2" "$conffile")
bboptions=$(getconfvalue "rdbOptions" "2" "$conffile")
# Kontrollverzeichnis für Ergebnisdateien
controldirectory="/var/spool/results/backup"

# Borg Repo-Passphrase als Umgebungsvariable exportieren
repopw=$(getconfvalue "borgRepoPass" "2" "$passfile")
export BORG_PASSPHRASE="$repopw"

# Kontrollverzeichnis anlegen.
if [[ ! -d $controldirectory ]]; then
    mkdir -p $controldirectory
    chown -R .www $controldirectory
fi

# Ist auf diesem System das Tool emergmailer installiert?
if [[ `which emergmailer 2>/dev/null` ]]; then 
    emm=1
    # Verzeichnis für Testergebnisse
    directory="/var/spool/results/backup"
fi

# Datensicherungsmodus nur starten, wenn die Datei /var/spool/results/backup/restore
# nicht vorhanden ist.
if [[ ! -f $controldirectory/restore ]]; then
    # Aktuelles Datum ermitteln und in Variable $datum stecken
    echo "Datensynchronisation vom $datum" >> $logfile

    ## Warnung, wenn Plattenplatz unter 10% fällt
    usedspace=`df -h /mnt/udevsync/|grep "/"|tr -s " "|cut -d " " -f 5|cut -d "%" -f 1`
    echo "$usedspace" > $controldirectory/full
    # Per email warnen, wenn Platte zu mehr als 90% voll
    if (( $usedspace > 90 )); then
	    # Use emergmailer?
	    if [[ $emm == 1 ]]; then
		echo "$datum - Achtung die Datensicherungsfestplatte ist zu $usedspace% belegt."| tee -a $logfile > $directory/alarm
		emergmailer $directory
	    else
		echo "$datum: Achtung die Datensicherungsfestplatte ist zu $usedspace% belegt."| tee -a $logfile |mailx -s "$datum: Datensicherungsserver fast voll" -r $mailfrom $mailto
	    fi
    fi

    echo -e `date +%s` > $controldirectory/status

    # Anzahl der zu sichernden Volumes ermitteln
    volumecount=${#sourcevolume[*]}
    echo $volumecount >> $controldirectory/status
    # Volume-Groups durchgehen
    for svg in ${volumegroup[*]}; do
        echo "/dev/$svg/"
        # Testen, ob die Kombination aus vg und lv existiert, nur dann weiter machen
        # Source Volumes abarbeiten
        for svolume in ${sourcevolume[*]}; do
            echo "/dev/$svg/$svolume"
            if [[ -b /dev/$svg/$svolume ]] ; then
		datum=$(date "+%d.%m.%Y - %H:%M")
		echo "$datum - Gesichert wird Volume: $svolume" >> $logfile

		/sbin/lvcreate -L $snapshotsize -s -n $snapshotvolume /dev/$svg/$svolume >/dev/null 2>&1
		ecode=${?}
		# Programm abbrechen, wenn Snapshot nicht erstellt werden konnte
		if [[ $ecode != 0 ]]; then
		    # Use emergmailer?
		    if [[ $emm == 1 ]]; then
			echo "$datum - Backup-Volume konnte nicht erstellt werden, Programm wird abgebrochen. Exit-Code: $ecode"| tee -a $logfile > $directory/alarm
			emergmailer $directory
		    else
			echo "$datum - Backup-Volume konnte nicht erstellt werden, Programm wird abgebrochen. Exit-Code: $ecode"| tee -a $logfile |mailx -s "Datensicherung fehlerhaft" -r $mailfrom $mailto
		    fi
		    exit 1
		fi
                
                # Dateisystem des Snapshots ermitteln
                fs=`lsblk -nf /dev/$svg/$snapshotvolume | tr -s " " | cut -d " " -f2`
		# Im Falle von BTRFS muss der Snapshot eine neue UUID erhalten.
                if [[ $fs == "btrfs" ]]; then
                    btrfstune -m /dev/$svg/$snapshotvolume
                fi
		mount /dev/$svg/$snapshotvolume $backupdir
		ecode=${?}
	
		if [[ $ecode != 0 ]]; then
		    # Use emergmailer?
		    if [[ $emm == 1 ]]; then
			echo "$datum - Backup-Volume konnte nicht eingebunden werden, Programm wird abgebrochen. Exit-Code: $ecode"| tee -a $logfile > $directory/alarm
			emergmailer $directory
		    else
			echo "$datum - Backup-Volume konnte nicht eingebunden werden, Programm wird abgebrochen. Exit-Code: $ecode"| tee -a $logfile |mailx -s "Datensicherung fehlerhaft" -r $mailfrom $mailto
		    fi
		    exit 1
		fi

		# Backup durchfuehren
		# Zieldaten ermitteln
		targetdir=$(getconfvalue "targetDirUDEV" "2" "$conffile")
		targethost=$(getconfvalue "targetHost" "2" "$conffile")

		# Unterscheidung lokale oder entfernte Sicherung
		if [[ $targethost == "localhost" ]];then
		    # Sicherungsverzeichnis erstellen
		    if [[ ! -d  $targetdir ]]; then
			mkdir -p $targetdir
		    fi
		    borg create $bboptions $targetdir/$svolume::$archivedate $backupdir >> $logfile 2>&1
		fi
		ecode=${?}

		datum=$(date "+%d.%m.%Y - %H:%M")
		if [[ $ecode != 0 ]]; then
		    # Use emergmailer?
		    if [[ $emm == 1 ]]; then
			echo "$datum - Während der Datensicherung von $svolume sind Fehler aufgetreten. Exit-Code: $ecode."| tee -a $logfile > $directory/alarm
			emergmailer $directory
		    else
			echo "$datum - Während der Datensicherung von $svolume sind Fehler aufgetreten. Exit-Code: $ecode."| tee -a $logfile |mailx -s "Datensicherung fehlerhaft" -r $mailfrom $mailto
		    fi
		elif [[ $mailwhen == "always" ]]; then
		    echo="$datum - Datensicherung des Volumes $svolume  wurde fehlerfrei abgeschlossen."|tee -a $logfile | mailx -s "Datensicherung erfolgreich"  -r $mailfrom $mailto
		fi
		echo "$svolume $ecode" >> $controldirectory/status
	
		# Backup-Volume aushaengen und loeschen
		umount $backupdir
		ecode=${?}
		if [[ $ecode != 0 ]]; then
		    # Use emergmailer?
		    if [[ $emm == 1 ]]; then
			echo "$datum - Backup-Volume konnte nicht ausgehängt werden, Programm wird abgebrochen. Exit-Code: $ecode."| tee -a $logfile > $directory/alarm
			emergmailer $directory
		    else
			echo "$datum - Backup-Volume konnte nicht ausgehängt werden, Programm wird abgebrochen."| tee -a $logfile |mailx -s "Fehler bei Datensicherung" -r $mailfrom $mailto
		    fi
		    exit 1
		fi

		# Synchronisation der Festplattenpuffer erzwingen und warten bis alles abgeschlossen ist.
		sync
	
		# Warten bis das Snapshot-Volume sauber ist.
		while [[ `dmsetup info /dev/$svg/$snapshotvolume|grep ^"Open count" |tr -d " "| cut -d ":" -f2` != 0 ]]; do
		    sleep 3
		done

		# Dirty Workaround. Sollte nicht mehr notwendig sein, wenn vorherige Warteaktion funktioniert.
		ecode=1
		while [[ $ecode != 0 ]]; do
		    datum=$(date "+%d.%m.%Y - %H:%M")
		    /sbin/lvremove -f --noudevsync /dev/$svg/$snapshotvolume >> $logfile 
		    ecode=${?} 
		    if [[ $ecode != 0 ]]; then
			echo "$datum - Das Backup-Volume konnte nicht gelöscht werden. Exit-Code: $ecode." >> $logfile
			sleep 3
		    else
			echo "$datum - Backup-Volume wurde erfolgreich gelöscht." >> $logfile
		    fi
		done
	    fi
	done
    done
    # Beenden
    datum=$(date "+%d.%m.%Y - %H:%M")
    echo "$datum - Backup-Vorgang wurde vollständig abgeschlossen." >> $logfile
else
    # Kontrolldatei löschen
    rm -f $controldirectory/restore
    echo "Hängen Sie die Datensicherungsfestplatte nach erfolgter Datenrücksicherung mit umount /dev/backup wieder aus"
    # Mit Fehler 1 beenden - verhindert umount
    exit 1
fi
