Posted on 09 Feb 2013

Wenn man einen Server verwaltet fallen früher oder später Routineaufgaben an. Dabei kann es sich um einfache Aufräumarbeiten oder Standardaufgaben wie Backups handeln. Alle diese Aufgaben werden massiv erleichtert wenn man sie mit Hilfe von Bash-Scripten (oder wie Borat sagen würde Bash-Apps) selbst automatisieren kann.

In diesem Artikel möchte ich einige Lösungen für dabei auftretende Probleme vorstellen.## Generische Scripte dank Config-Files ##

if [ -f /root/backup/backup_config ]; then
    while read line; do
    if [ ! -z "$line" ]; then
        case $line in
        \#*) /bin/true;;
        remount*) remount=${line#remount=};;
        backup_dir*) backup_dir=${line#backup_dir=};;
        daily*) daily=${line#daily=};;
        device_id*) device_id=${line#device_id=};;
        mysqlpw*) mysqlpw=${line#mysqlpw=};;
        init_backup*) $line;;
        ldap_backup*) $line;;
        mysql_backup*) $line;;
        postgres_backup*) $line;;
        rsnapshot_backup*) $line;;
        file_backup*) $line;;
        finish_backup*) $line;;
        *) log "Error: Unknown command \"$line\"";;
        esac
    fi
    done < "/root/backup/backup_config"
else
    chk 997 "backup_config not found" 1
fi

In der ersten Zeile wird geprüft ob die Konfigurationsdatei /root/backup/backup_config existiert. Falls ja wird diese von while eingelesen (Zeile 2 und 21). Sofern die eingelesene Zeile nicht leer ist (Zeile 3) wird per case nach bestimmten Werten gesucht. Beginnt eine Zeile mit einer `# wird der Befehl/bin/trueausgeführt. Beginnt die Zeile mitremountwird die Umgebungsvariableremount` gesetzt. Der entsprechende Wert für die Variable kommt aus der Konfigurationsdatei.

So geht es immer weiter, beginnt eine Zeile mit ldap_backup wird die gleichnamige Funktion des Backup-Scriptes ausgeführt. Im Zweifel mit zusätzlichen Parametern aus der Konfigurationsdatei.

Hier eine Beispielkonfiguration:

# Am Ende der Datei muss sich eine Leerzeile befinden.

# Ziel für das Backup
backup_dir=/backup

# Wenn daily "true" ist wird ein 7-Tage Backup erzeugt.
daily=false

# Wenn remount "true" ist wird das Backuplaufwerk rw/ro gemountet.
remount=false

init_backup
mysql_backup $password
postgres_backup
#rsnapshot_backup
file_backup /var/www/
file_backup /home/
file_backup /opt/
finish_backup

Wie man sehen kann werden diverse Variablen vorbereitet und dann die Backupfunktionen ausgeführt. rsnapshot_backup ist auskommentiert und wird daher nicht verwendet.## Funktionen ##

Über Funktionen können gleichartige Befehle, über Parameter angesteuert, zentralisiert werden.

log() {
# Write to logfile

    /usr/bin/logger -t "backup" "$hostname: $1";
    echo $1
}

chk() {
# Check if there was an error during the backup

    if [ "$1" -ne "0" ]; then
        # Something went wrong
        log "ERROR #$1: $2";
    warning=true;
    log "[FAIL]";
    else
        # Everything was fine
    log "[ok]";
    fi
    if [ "$3" eq "1" ]; then
         echo "[FATAL] Error $1: $2"
         exit $3
    fi
}

Die hier definierte Funktion log erwartet mindestens einen Parameter $1. Außerdem muss im Script die Variable $hostname gesetzt werden. Die Funktion kann mit log "Nachricht" aus dem Script heraus aufgerufen werden. Sie ruft dann die Funktion logger auf. Hierdurch wird eine Nachricht im Syslog erzeugt. Sie lautet: backup $hostname: Nachricht. Außerdem wird "Nachricht" auf STDOUT ausgegeben.

Die Funktion chk bekommt als Parameter zwei Werte übergeben: $1 und $2
Der Wert $1 enthält den Rückgabewert eines zuvor ausgeführten Befehls. $2 enthält eine beliebige Nachricht. Wenn der übergebene Rückgabewert ($1) nicht 0 ist wird ein Fehler ins Logfile geschrieben (über die Funktion log). Außerdem wird die Variable warning auf true gestellt. Ist der Rückgabewert 0 wird ein ok ins Logfile geschrieben.## If-Abfragen ##

Über if-Abfragen kann man Entscheidungen im Scriptablauf herbeiführen:

init_backup() {

    if (( `id -u` != 0 )); then 
        chk 1000 "Are you root?" 1 
    fi

    if [ -e "/var/lock/daily_backup.lock" ]; then
    log "Checking for Lock-File";
        chk 1000 "Lock-file exists, backup already running? Stopping..." 1
    fi

    log "INFO: Starting Backup `date`";
    log "Writing Lock-File";
    touch /var/lock/daily_backup.lock
    chk $? "Writing Lock-File failed" 1

    if [ $daily == true ]; then
    backup_dir=$backup_dir/`date +%w`
    fi

    if [ $remount == true ]; then
    log "Mounting (rw) Backup-Device"
    mount -o remount,rw $device_id
    chk $? "Mount failed ($device_id)" 1
    fi

    log "INFO: Target $backup_dir";

    if [ ! -d "$backup_dir" ]; then
        log "Creating Backup-Dir";
    mkdir -p "$backup_dir"
        chk $? "mkdir failed ($backup_dir)" 1
    fi
}

Die Funktion init_backup bereitet das eigentlich Backup vor. Zunächst wird aber geprüft ob der aufrufende Benutzer überhaupt berechtigt ist das Backup durchzuführen (id -u). Wenn das Script nicht mit root-Rechten gestartet wird beendet es sich.

Anschließend wird geprüft ob die Datei /var/lock/daily_backup.log existiert. Falls ja wird davon ausgegangen, dass bereits eine andere Backup-Instanz aktiv ist und das Backup wird ebenfalls beendet.

Existiert keine Lock-Datei werden einige Angaben in das Syslog geschrieben und die Lock-Datei erzeugt. Schlägt dies fehl wird ebenfalls abgebrochen.

Ist der Parameter daily auf true gestellt wird dies mit einer entsprechenden Variablen quittiert. Der Parameter remount entscheidet darüber ob das Backupziel neu gemountet werden muss oder nicht.

Abschließend wird der Backupordner erstellt, sofern er nicht bereits existiert. Klappt das nicht wird das Backup beendet.