#!/bin/bash
# Script zur inkrementellen Sicherung eines vollstaendigen
# invis-Servers
# 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,2013,2014,2017,2020,2022 Stefan Schaefer -- invis-server.org
# 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/bbunet.conf"
passfile="$confdir/invis-pws.conf"

backupdir="/mnt/backup"
logfile="/var/log/bbunet.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")
netbuactive=$(getconfvalue "netBackupActive" "2" "$conffile")
mounttype=$(getconfvalue "mountType" "2" "$conffile")
bboptions=$(getconfvalue "bbOptions" "2" "$conffile")
bbrelocate=$(getconfvalue "bbRelocate" "2" "$conffile")

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

# Wenn die Sicherung nicht direkt via SSH erfolgt Zugangsdaten auslesen
if [[ $mounttype != "none" ]]; then
    mountlogin=$(getconfvalue "mountLogin" "2" "$conffile")
    mountpw=$(getconfvalue "mountPassword" "2" "$conffile")
fi

# Kontrollverzeichnis für Ergebnisdateien
controldirectory="/var/spool/results/backup"
bootpartdir="/srv/shares/archiv/bootpart"

# Kontrollverzeichnis anlegen.
if [[ ! -d $controldirectory ]]; then
    mkdir -p $controldirectory
    chown -R .www $controldirectory
fi
# Sicherungsverzeichnis fuer das boot-Partitions-Image anlegen
if [[ ! -d $bootpartdir ]]; then
    mkdir -p $bootpartdir
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 Netzwerkbackup als aktiv gekennzeichnet ist
# nicht vorhanden ist.
if [[ $netbuactive == "1" ]]; then
    # Aktuelles Datum ermitteln und in Variable $datum stecken
    echo "Datensynchronisation vom $datum" > $logfile
    echo -e `date +%s` > $controldirectory/status

    # Zieldaten ermitteln
    targetdir=$(getconfvalue "targetDir" "2" "$conffile")
    targethost=$(getconfvalue "targetHost" "2" "$conffile")
    targetuser=$(getconfvalue "buServerUser" "2" "$conffile")
    sshport=$(getconfvalue "thSSHPort" "2" "$conffile")
    if [[ $targethost == "localhost" ]] || [[ $mounttype != "none" ]]; then
	# Sicherungsverzeichnis erstellen
	if [[ ! -d  $targetdir ]]; then
	    mkdir -p $targetdir
	fi
    fi
    
    # Sicherungsziel einhaengen, wenn notwendig
    if [[ $mounttype == "cifs" ]]; then
	mountshare=$(getconfvalue "mountSahre" "2" "$conffile")
	mount -o user=$mountlogin,password=$mountpw //$targethost/$mountshare $targetdir
    fi 

    # 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`
		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

		# Unterscheidung: Sichrung via SSH oder lokal bzw. lokaler mount
		if [[ $targethost == "localhost" ]] || [[ $mounttype != "none" ]]; then
		    borg create $bboptions $targetdir/$svolume::$archivedate $backupdir >> $logfile 2>&1
		else
		    targetuser=$(getconfvalue "buServerUser" "2" "$conffile")
		    borg create $bboptions ssh://$targetuser@$targethost:$sshport/$targetdir/$svolume::$archivedate $backupdir >> $logfile 2>&1
		fi
		ecode=${?}

		# Benachrichigung senden
		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 - Backup-Vorgang 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
		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 2
		    else
			echo "$datum - Backup-Volume wurde erfolgreich gelöscht." >> $logfile
		    fi
		done
	    fi
	done
    done

    # Fuellstand ermitteln 
    targetmountpoint=`echo "$targetdir" |cut -d "/" -f2`
    ssh $targetuser@$targethost 'df -h' |grep srv |tr -s " " |cut -d " " -f5|tr -d "%" > $controldirectory/full

    if [[ -f $controldirectory/full ]]; then
	usedspace=`cat $controldirectory/full`
	## Warnung, wenn Plattenplatz unter 10% fällt
	if (( $usedspace  > 90 )); then
	    # Use emergmailer?
	    if [[ $emm == 1 ]]; then
		echo "$datum - Achtung, der Festplattenplatz des Datensicherungsservers ist zu $usedspace% belegt."| tee -a $logfile > $directory/alarm
		emergmailer $directory
	    else
		echo "$datum: Achtung, der Festplattenplatz des Datensicherungsservers ist zu $usedspace% belegt."| tee -a $logfile |mailx -s "$datum: Datensicherungsserver fast voll" -r $mailfrom $mailto
	    fi
	fi
    else
	# Use emergmailer?
	if [[ $emm == 1 ]]; then
	    echo "$datum - Achtung, es konnte nicht ermittelt werden, wie weit die Festplattenkapazität des Sicherungsservers genutzt ist."| tee -a $logfile > $directory/alarm
	    emergmailer $directory
	else
	    echo "$datum: Achtung, es konnte nicht ermittelt werden, wie weit die Festplattenkapazität des Sicherungsservers genutzt ist."| tee -a $logfile |mailx -s "$datum: Freier Speicherplatz auf Sicherungsmedium unbekannt." -r $mailfrom $mailto
	fi
    fi

    # Beenden
    # Sicherungsziel aushaengen, wenn notwendig
    if [[ $mounttype == "cifs" ]]; then
	umount $targetdir
    fi 

    datum=$(date "+%d.%m.%Y - %H:%M")
    echo "$datum - Backup-Vorgang wurde vollständig abgeschlossen." >> $logfile
    
else
    # Kontrolldatei löschen
    datum=$(date "+%d.%m.%Y - %H:%M")
    echo "$datum - Datensicherung ist nicht aktiv." >> $logfile
    # Mit Fehler 1 beenden - verhindert umount
    #exit 1
fi

