#!/usr/bin/perl $version = "0.8"; $T_SCRIPTNAME = "backupscript"; $OPENSSL = "/usr/bin/openssl"; $TAR = "/usr/bin/tar"; $DUMP = "/sbin/dump"; $CHFLAGS = "/bin/chflags"; $MOUNT = "/sbin/mount"; $UMOUNT = "/sbin/umount"; $GZIP = "/usr/bin/gzip"; $KONFIGDATEI = "/usr/local/etc/backupscriptrc"; @KONFWERTE = ("nodump", "device", "ziel", "mounten", "mountpfad", "cipher", "methode", "key", "keyfile", "quelle", "tarfile", "zip"); $methode = "dump"; # Standard und somit Kompartible zu ver < 0.7 $tarErweiterung = "tar"; $default_tar_name = "backup"; ## Ausgabetexte ## $T_KONFDATEINICHTDA = "Konfigurationsdatei $KONFIGDATEI nicht vorhanden!\n"; $T_KONFDATEINICHTOEFFNEN = "Konnte Konfigurationsdatei nicht oeffnen!\n"; $T_KONFDATEIDEFEKT = "Konfigurationsdatei $KONFIGDATEI ist fehlerhaft!\n"; $T_KONFDATEIDEFEKT_TAR_DEVICE = "Konfigurationsdatei $KONFIGDATEI ist fehlerhaft! Wenn die Methode tar gewählt ist darf kein \"device\" definiert sein. \n"; #$T_KONFDATEIDEFEKT_TAR_OUTPUT = "Konfigurationsdatei $KONFIGDATEI ist fehlerhaft! Wenn die Methode tar gewählt ist muss der Outputname in \"tarfile\" definiert sein. \n"; $T_KONFDATEIDEFEKT_DUMP_TAROPTS = "Konfigurationsdatei $KONFIGDATEI ist fehlerhaft! Wenn die Methode dump gewählt ist darf weder \"tarfile\" noch \"quelle\" definiert sein. \n"; $T_KONFDATEIDEFEKT_KEY_KEYFILE = "Konfigurationsdatei $KONFIGDATEI ist fehlerhaft! Es darf entweder \"key\" oder \"keyfile\" definiert sein, nicht beide.\n"; $T_KONFDATEIDEFEKT_KEY_NOCIPHER = "Konfigurationsdatei $KONFIGDATEI ist fehlerhaft! Um die Verschlüsselung der Zieldatei zu aktivieren muss \"cipher\" mit einer openssl-kompatiblen Verschlüsselung gesetzt werden. (z.B. aes256)\n"; $T_KONFDATEIDEFEKT_CIPHER_NOKEY = "Konfigurationsdatei $KONFIGDATEI ist fehlerhaft! \n"; $T_MOUNTFEHLER = "Fehler beim Mounten des Zielpfades\n"; $T_UMOUNTFEHLER = "Fehler beim Unmounten des Zielpfades\n"; $T_MOUNTVARIABLEFALSCH = "Die Variable 'mounten' wurde gesetzt enthaelt aber keinen zulaessigen Wert. Es wird kein Mountversuch unternommen.\n"; $T_NODUMPVERZFALSCH = "Ein 'nodump'-Verzeichnis ist falsch"; $T_ZIELVERZFALSCH = "Das Ziel-Verzeichnis existiert nicht.\n"; $T_UMOUNTWEGENFEHLER = "Wegen eines Fehlers wurde das Mountverzeichnis vorzeitig getrennt.\n"; $T_DEVICEVERZFALSCH = "Ein 'device' ist falsch"; $T_SCHLUESSELLAENGEZUKURZ = "Die Länge des gewählten Schlüssels zur Verschlüsselung ist zu kurz. Zur Sicheren Verschlüsselung bitte einen längeren Schlüssel definieren\n"; $T_KEYFILENICHTNUTZBAR = "Das Keyfile zur Verschlüsselung wurde nicht gefunden. Bitte prüfen Sie ob es existiert und gelesen werden kann.\n"; $T_HILFETEXT = "usage:\t$T_SCRIPTNAME [OPTION]\n\n[Options]:\n\t0-9\tDumplevel - Eine Ziffer die das Dumplevel angibt (nur bei Backups mit dump)\n\t-h\tLange Hilfe\n\n"; $T_LANGEHILFE = "Version: $version Das Script dient der erleichterten Erstellung von Backups unter FreeBSD unter Verwendung des Systemprogrammes 'dump' oder 'tar'. Zusätzlich können die Backups mittels 'OpenSSL' verschlüsselt werden. Zur Steuerung wird eine Konfigurationsdatei unter folgendem Link benoetigt: \t$KONFIGDATEI Die Datei bietet folgende Parameter: Allgemeine Parameter method : Methode mit welcher das Backup erzeugt werden soll. Mögliche Parameter sind 'dump' oder 'tar'. nodump : Verzeichnisse die NICHT gesichert werden sollen. ziel : Zielverzeichnis fuer die Backups. mounten : 'ja' falls vor dem Backup eine Partition gemountet werden muss. mountpfad : Falls das zu mountende Verzeichnis von 'ziel' abweicht. zip : 'ja' falls das Backup komprimiert werden soll. Zur Komprimierung wird gzip verwendet. Parameter für Verschlüsselung cipher : Eine OpenSSL-kompatible Verschlüsselungsmethode. z.B. aes256 key : Passwort welches zur Verschlüsselung verwendet werden soll. Die empfohlene Methode ist 'keyfile' zu verwenden. (schliesst die Verwendung von keyfile aus). keyfile : Datei welche das zu verwendende Passwort enthält. (schliesst die Verwendung von key aus). Parameter ausschließlich für dump-Backups device : Partition die gesichert werden soll Parameter ausschliesslich für tar-Backups tarfile : Name des erzeugten tar-Archivs. Nur der Name, die Erweiterung wird automatisch ergänzt. quelle : Verzeichnisse die gesichert werden sollen. Je ein Eintrag pro Verzeichnis. Das zu mountende Verzeichnis, egal ob extra angegeben oder nicht, muss immer unter /etc/fstab aufgefuehrt sein. Es sollte die 'noauto'-Option in /etc/fstab verwendet werden damit die Backupplatte nicht unnoetig gemountet bleibt. Beispiel einer $KONFIGDATEI für normale nichtverschlüsselte Backups mit dump: ------------------------------------------------ method = dump nodump = /usr/ports nodump = /usr/obj nodump = /usr/src device = /dev/ad4p2 device = /dev/ad4p4 device = /dev/ad4p6 mounten = ja mountpfad = /data ziel = /data/backup ------------------------------------------------- Beispiel einer $KONFIGDATEI für tar-basierte, verschlüsselte Backups: ------------------------------------------------ method = tar tarfile = backup zip = ja ciper = aes256 key = testpasswort nodump = /usr/home/unimportant quelle = /usr/home quelle = /etc mounten = ja mountpfad = /data ziel = /data/backup ------------------------------------------------- \n"; ########################################################################### sub zeigeHilfe{ print $T_HILFETEXT; exit; } ########################################################################### sub zeigeLangeHilfe{ print $T_LANGEHILFE; exit; } ########################################################################### sub KonfdateiEinlesen{ # # Liest die Datei ein und speichert die Werte im Array # Incl. Ueberpruefung der Werte gegen @KONFWERTE. # open KDATEI, "<",$KONFIGDATEI or die $T_KONFDATEINICHTOEFFNEN; foreach (){ unless (/^\s*#/ or /^\s*$/){ # keine Leerzeilen oder Kommentare $ok = 0; $_ =~/^\s*(\S+)\s*=\s*(.*?)\s*$/; # $1=Parameter; $2=Wert foreach (@KONFWERTE){ # Testen ob Werte zulaessig sind $ok = 1 if ($_ eq $1); } die $T_KONFDATEIDEFEKT unless $ok; ## Verarbeitung der Daten if ($1 eq "nodump"){ push @nodump, $2; } elsif($1 eq "device"){ push @device, $2; } elsif($1 eq "quelle"){ push @quellverzeichnisse, $2; } elsif($1 eq "ziel"){ $ziel = $2; } elsif($1 eq "zip"){ if ($2 =~/^[jJ][aA]$/){ $zip = 1; } else{ warn $T_ZIPVARIABLEFALSCH; } } elsif($1 eq "mounten"){ if ($2 =~ /^[jJ][aA]$/){ $mounten = 1; } else{ warn $T_MOUNTVARIABLEFALSCH; } } elsif($1 eq "mountpfad"){ $mountpfad = $2; } elsif($1 eq "cipher"){ $cipher = $2; } elsif($1 eq "tarfile"){ $tarOutput = $2; } elsif($1 eq "methode"){ unless ($2 eq "tar" or $2 eq "dump"){ warn $T_METHODENVARIABLEFALSCH; } if ($2 eq "tar"){ $methode = "tar"; } else { $methode = "dump"; } } elsif($1 eq "key"){ $key = $2; } elsif($1 eq "keyfile"){ $keyfile = $2; } ## Ende Verarbeitung der Daten } } close $KDATEI; } # Ende sub KonfdateiEinlesen ########################################################################### sub zielpfadZuschneiden{ $ziel =~s/^(.*?)\/?$/$1/; } ########################################################################### sub istZielGemountet{ $gefunden = 0; @gemountet = `mount`; foreach (@gemountet){ $gefunden = 1 if (/$_[0]/); } $gefunden; } ########################################################################### sub zielMounten{ unless (defined $mountpfad){ $mountpfad = $ziel; } unless (&istZielGemountet($mountpfad)){ !system "$MOUNT $mountpfad" or die "$T_MOUNTFEHLER"; } } ########################################################################### sub zielUmounten{ unless (defined $mountpfad){ $mountpfad = $ziel; } if (&istZielGemountet($mountpfad)){ !system "$UMOUNT $mountpfad" or die "$T_UMOUNTFEHLER"; } } ########################################################################### sub konfigAufSinnTesten{ die $T_KONFDATEIDEFEKT_TAR_DEVICE if ($methode eq "tar" and defined @device); # die $T_KONFDATEIDEFEKT_TAR_OUTPUT if ($methode eq "tar" and not defined $tarOutput); die $T_KONFDATEIDEFEKT_DUMP_TAROPTS if (($methode eq "dump" and defined @quellverzeichnisse) or ($methode eq "dump" and defined $tarOutput)); die $T_KONFDATEIDEFEKT unless (defined $ziel and (defined @device or defined @quellverzeichnisse)); die $T_KONFDATEIDEFEKT_KEY_KEYFILE if (defined $key and defined $keyfile); die $T_KONFDATEIDEFEKT_CIPHER_NOKEY if (defined $cipher and not (defined $keyfile or defined $key)); die $T_KONFDATEIDEFEKT_KEY_NOCIPHER if ((defined $keyfile or defined $key) and not defined $cipher); } ########################################################################### sub VerzeichnissePruefen{ foreach (@nodump){ warn "$T_NODUMPVERZFALSCH: $_\n" unless (-e $_); } unless (-d $ziel){ &zielUmounten if (defined $mounten); warn $T_UMOUNTWEGENFEHLER; die $T_ZIELVERZFALSCH; } foreach (@device){ die "$T_DEVICEVERZFALSCH: $_\n" unless (-e $_); } } ########################################################################### sub cipherpipeErzeugen{ if (defined $cipher){ $cipherpipe = "| $OPENSSL enc -$cipher -e -salt -pass"; if (defined $key){ warn $T_SCHLUESSELLAENGEZUKURZ if (length $key < 8); $cipherpipe = $cipherpipe." pass:$key" } elsif (defined $keyfile){ die $T_KEYFILENICHTNUTZBAR unless (-e $keyfile and -r $keyfile); $cipherpipe = $cipherpipe." file:$keyfile"; } return "-"; } else { $cipherpipe = ""; return $_[0]; } } ########################################################################### sub dateiinfosAusgeben{ @Einheiten = ("B", "kB", "MB", "GB", "TB"); $division = 0; if (-e $_[0]){ $groesse = -s $_[0]; while ($groesse >= 1024){ $groesse = $groesse / 1024; $division += 1; } $ausgabe = sprintf("%s %.2f %s",$_, $groesse, $Einheiten[$division]); } else { $ausgabe = "$_ wurde nicht angelegt!"; } print "$ausgabe\n"; } ########################################################################### # Parameter abpruefen &zeigeLangeHilfe if ($ARGV[0] eq "-h"); # Ist Konfigdatei da? die $T_KONFDATEINICHTDA unless (-f $KONFIGDATEI); # Konfigdatei abarbeiten &KonfdateiEinlesen; &konfigAufSinnTesten; if ($methode eq "dump"){ &zeigeHilfe unless ($ARGV[0] =~/^\d$/); $dumplevel = $ARGV[0]; } else { &zeigeHilfe if ($#ARGV > -1); } &zielpfadZuschneiden; # Muessen wir mounten? Dann los. &zielMounten if (defined $mounten); # Zielverzeichnis kann ggf erst nach dem Mounten geprueft werden. &VerzeichnissePruefen; # Datumsstring erzeugen ($sek, $min, $std, $mtag, $mon, $jahr, , ,) = localtime(time); $datum = sprintf("%04d%02d%02d",$jahr+1900,$mon+1,$mtag); if ($methode eq "dump"){ # FALLS MIT DUMP GEARBEITET WIRD # Vorarbeiten... gemountete Laufwerke einlesen @mounts = `mount`; foreach (@device){ $deviceName = $_; foreach (@mounts){ $_ =~/^\s*(.*) on (.*)\s+\(/; $devicenodepfadausmount = $1; $devicemountpfad = $2; if ($devicenodepfadausmount=~/$deviceName$/){ if ($devicemountpfad eq "/") { $devicemountpfad="root" } else{ $devicemountpfad =~ s/^\/(.*)$/$1/; $devicemountpfad =~ s/\//_/g; } $devicemountpfad{$deviceName}=$devicemountpfad; } } } # 'nodump' setzen falls dump verwendet wird foreach (@nodump){ system ("$CHFLAGS -R nodump $_") if (-e $_); } # dumps durchfuehren foreach (@device){ /^.*?([^\/]+)$/; $deviceKurzName = $1; $dumpDateiname = "$ziel/$devicemountpfad{$_}-$deviceKurzName-$datum-L$dumplevel"; &cipherpipeErzeugen($dumpDateiname); if (defined $zip){ $zippipe = " | $GZIP"; $dumpDateiname = "$dumpDateiname.gz"; } else { $zippipe = ""; } push @erzeugteDateien, $dumpDateiname; system ("$DUMP -$dumplevel -h0 -Lauf - $_ $zippipe $cipherpipe | dd of=$dumpDateiname"); } } elsif ($methode eq "tar"){ # FALLS MIT TAR GEARBEITET WIRD if (defined @nodump){ $exclude = ""; foreach (@nodump){ $exclude = $exclude." --exclude $_"; } } $tarOutput = $default_tar_name unless (defined $tarOutput); $tarOutput =~ s/^([^\.]*)\.(.*?)$/$1/;; if (length $2 > 1){ $tarErweiterung = $2; } $output = "$ziel/$tarOutput-$datum.$tarErweiterung"; if (defined $zip){ $z = "-z"; $output = "$output.gz"; } else { $z =""; } push @erzeugteDateien, $output; $tarOutputDateiname = &cipherpipeErzeugen($output); system ("$TAR -c $z -f - $exclude --exclude $output @quellverzeichnisse $cipherpipe | dd of=$output"); } # Dateigroessen ausgeben print "\nEs wurden folgende Backups durch $methode angelegt"; print " und mittels $cipher verschlüsselt" if (defined $cipher); print ":\n"; foreach (@erzeugteDateien){ &dateiinfosAusgeben($_); } &zielUmounten if (defined $mounten); ===== Anwendung ===== Wird das Backupscript mit //dump// verwendet erwartet es als einzigen Parameter das Dumplevel welches angewendet werden soll. Ein Dump-0 wird also wie folgt gestartet: # backupscript 0 Bei Verwendung von //tar// ist kein Parameter notwendig und das Backupscript wird einfach wie folgt gestartet: # backupscript