AttributionsTogether: 1 IDependOn-Set: 1 IDependOn-Set: 119 IDependOn-Set: 120 IDependOn-Set: 3 LastModifiedSecs: 1053443306 Parent: 119 SequenceNumber: 6 Title: Download Part: 0 Author-Set: sauer@cloudmaster.com LastModifiedSecs: 1053432571 Type: Lines: 1 There's no download yet - I'll package this up someday, but for now, you just get the whole darned shell script and perl script. Paste each into an individual file, chmod +x, edit as needed, and stick backup.sh in /etc/cron.daily/. :) EndPart: 0 Part: 1 Author-Set: sauer@cloudmaster.com LastModifiedSecs: 1053443306 Type: monospaced Lines: 58 #!/bin/bash # # backup.sh - Danny Sauer, 3/2003 # this needs run as root in order to preserve permissions and make device nodes # also, each host needs root's private key in its .ssh/authorized_hosts # # space-separated list of hosts to be backed up HOSTS="dialin www router dev web2" # programs with full paths RSYNC="/usr/bin/rsync" ARCHIVE_PL="/Users/dsauer/perl/archive.pl" DATE="/bin/date" # variables TODAY=`$DATE +'%Y.%m.%d'`; BACKUPDIR="/Volumes/servers" ARCHIVEDIR="$BACKUPDIR/archive" # verify that our output directories exist before using them if [ ! -d $ARCHIVEDIR ]; then mkdir -p $ARCHIVEDIR fi if [ ! -d $BACKUPDIR ]; then mkdir -p $BACKUPDIR fi # backup each host for HOST in $HOSTS; do echo "backing up $HOST:" OUT="$ARCHIVEDIR/$TODAY/$HOST" # archive.pl wants the top output dir to exist and be empty if [ -d $OUT ]; then if rmdir $OUT && mkdir -p $OUT; then echo -e "\twarn: recreated $OUT" else echo -e "\t$OUT exists already and is not empty - there will be problems" echo -e "\ttry 'rm -r $OUT' and run again" #rm -r $OUT fi else mkdir -p $OUT fi # make snapshot directory echo -e "\tarchiving\t("`$DATE +'%T'`")" $ARCHIVE_PL $BACKUPDIR/$HOST $OUT # synchronize backup directory with server echo -e "\tsyncing\t("`$DATE +'%T'`")" $RSYNC -z -qa -e ssh --delete --exclude '/proc/*' root@$HOST:/ $BACKUPDIR/$HOST # print time of completion echo -e "\tdone\t("`$DATE +'%T'`")" done EndPart: 1 Part: 2 Author-Set: sauer@cloudmaster.com LastModifiedSecs: 1053443258 Type: monospaced Lines: 69 #!/usr/bin/perl -w # # archive.pl - Danny Sauer, 4/2003 # given a source and destination directory, makes a hardlinked copy of # the sorce in the destination, recreating symlinks, nodes, and dirs # use strict; my $cp_command = '/sw/bin/cp'; sub archive($$); sub cp($$); my $source = shift or die "usage: $0 source_dir destination_dir"; my $dest = shift or die "usage: $0 source_dir destination_dir"; unless( -d $source ){ die "source must be a directory"; } mkdir($dest); archive($source, $dest); 1; # copy one file # note - symlinks have no permissions, hardlinks have the same permissions # as the source file, and "cp -a" preserves permissions. Don't use # "cp -p", though - must use at least "cp -pR" to copy device nodes sub cp($$){ my $s = shift; my $d = shift; if( -l $s ){ # recreate symlink symlink(readlink($s), $d); }elsif( -p _ || -S _ || -b _ || -c _ ){ # use system copy call (rewrite this in perl someday) system("$cp_command -a $s $d"); }else{ # create hardlink link($s, $d); } } #walk directory tree # this just wouldn't work with a while(readdir()), so now readdir into @fs sub archive($$){ my $s = shift; my $d = shift; opendir(S, $s) or die "can't opendir '$s': $!"; my @fs = readdir S; closedir S; foreach my $f (@fs){ next if($f eq '.' || $f eq '..'); if(-d "$s/$f"){ mkdir("$d/$f"); my $mode = (stat("$s/$f"))[2] & 07777; my $user = (stat(_))[4]; my $group = (stat(_))[5]; chmod($mode, "$d/$f"); chown($user, $group, "$d/$f"); archive("$s/$f", "$d/$f"); }else{ cp("$s/$f", "$d/$f"); } } } EndPart: 2