#!/usr/bin/perl ###!/usr/bin/perl -Tw # This script makes a nightly mirror of the ext2 partitions on primary # disk e.g. hda (excluding afs and other backup partitions) to secondary # disk e.g. hdc or hdd. # It takes care to fix fstab (but not lilo) on disk hdc after copying so # one can boot from it, using a boot floppy (made with a kernel # that the system has) and telling LILO: # linux root=/dev/hdd2 # # NB To make this disk the primary in the bios, in the event of the other disk failing, requires changing the devices in fstab and lilo/grub, and running lilo/grub-install, both of which can be done after booting from a floppy as above. # # It should be run from a cron job, perhaps nightly. # # Prototypes sub unmountall(@); # use File::Basename; # # safe paths and env $ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin'; $ENV{'LD_LIBRARY_PATH'} = ''; $ENV{'ENV'} = ''; $ENV{'BASH_ENV'} = ''; # # Config for this script $fromhd = 'sda'; $tohd = 'ext_desanto'; $backup_root="/mnt/backup"; $backupExt = ".$fromhd.bak"; $echo = ''; # set this to 'echo' to print commands instead of doing them $log = '/var/log/nightly-mirror.log'; # # Open log file open (LOG, ">>$log") or die "cannot open and append to log file $log\n$!"; #open (LOG, ">>-") or die "cannot open and append to stdout\n$!"; # SANITY CHECKS # check "tohd" is not the one in use open (CMD, "/bin/df -k|") or die "$!"; my $rootpart = "undef"; while (){ if (/\/$/ && /\/dev\/([hs]d[abcd])[0-9]+\s+/){ $rootpart = $1; last; } } close CMD; if ($rootpart eq "undef"){ die "root partition could not be identified"; } elsif ($rootpart eq $tohd){ die "root partition cannot be on the tohd disk: $tohd $rootpart"; } elsif ($rootpart ne $fromhd){ die "root partition must be on the fromhd disk: $fromhd $rootpart"; } # check "fromhd" is not the same as "tohd" if ($fromhd eq "$tohd"){ die "fromhd and tohd cannot be the same: both are $fromhd"; } # create backup root dir if (! -d "$backup_root"){ my $mode = oct "755"; mkdir "$backup_root", $mode; } # # Start with a message $date = qx(date); $date =~ /^[\w\:\0-9]+$/; print LOG "\n-------------------------------------------------------------------------------\n$0 running at " . $date . "\n"; # # First get the mount commands from fstab, # then mount each backup filesystem in turn and use rsync to update it # this way mount points are created too. $#mounted = -1; open (FSTAB, "){ next if (/^\#/); $continue=0; if (/^LABEL=(\/[[:alnum:]]*)\s+(\/\w*)\s+(ext[23])\s+(\w+)\s+([0-9])\s+([0-9])\s*$/){ print "fstab label: $_" ; my $label=$1, $mnt=$2, $fs=$3, $opt=$4; print "label=$label, mnt=$mnt, fs=$fs, opt=$opt\n"; $dev=`blkid -t LABEL=$label`; if ($dev =~ /^(\/dev\/[sh]d[[:alnum:]]+):.*$/ ){ $dev=$1; } $dev =~ s/$fromhd/$tohd/g; print "dev is $dev\n"; $continue=1; } print $fromhd."\n"; if (/^(\/dev\/$fromhd[0-9]+)\s+(\/\w*)\s+(ext[23]|vfat)\s+(\w+)\s+([0-9])\s+([0-9])\s*$/){ print "fstab dev: $_" ; my $dev=$1, $mnt=$2, $fs=$3, $opt=$4; $dev =~ s/$fromhd/$tohd/g; print "dev is again $dev\n"; $continue=1; } if ($continue){ print "dev=$dev, mnt=$mnt, fs=$fs, opt=$opt\n"; # my ($dev, $mnt, $fs, $opt, $m, $n) = split; # next if ($dev !~ /$tohd/ || $fs eq "swap"); if (! -d "$backup_root$mnt"){ my $mode = oct "755"; mkdir "$backup_root$mnt", $mode; } # unmount device if mounted system "cat /proc/mounts |grep -q $dev && umount $dev"; print LOG "doing fsck, mount and sync of $dev on $backup_root$mnt\n"; open (CMD, "$echo fsck -p $dev 2>&1|") or die "$!"; while (){ print LOG; } system "$echo mount -t $fs -o $opt $dev $backup_root$mnt"; # check mount succeeded my $status = ($? >> 8); if ($status != 0){ print "ERROR: mount failed: $status\nmount -t $fs -o $opt $dev $backup_root$mnt\n$!\n"; unmountall(@mounted); exit -1; } push @mounted, "$backup_root$mnt"; print "backup_root_mnt=$backup_root$mnt\n"; $mntsuper = dirname ($mnt); print "mnt=$mnt"."\n"; print "mntsuper".$mntsuper."\n"; my $command = "$echo rsync -axHv --stats --delete --exclude \"/afs\" --exclude \"/lost+found\" $mnt $backup_root$mntsuper"; print LOG "$command\n"; open (CMD, "$command 2>&1|"); while (){ print LOG; } close CMD; } } close FSTAB; open (CMD, "df -k |grep [hs]d[ac]|") or die "$!"; while (){ print LOG; } close CMD; # Patch up the mirrored files which refer to primary hd # but **don't** run lilo so the second disk can be booted. #foreach $file ("/etc/fstab", "/etc/lilo.conf"){ foreach $file ("/etc/fstab"){ print LOG "$backup_root$file fixed $fromhd->$tohd\n"; # rename("$backup_root$file", "$backup_root$file$backupExt") # or die "$!"; # open IN, "< $backup_root$file$backupExt" # or die "$!"; # open OUT, "> $backup_root$file" # or die "$!"; # while (){ # s/$fromhd/$tohd/g; # print OUT; # } close IN; close OUT; } print LOG "NOT running lilo on backup disk\n"; #print LOG "running lilo on backup disk\n"; #open (CMD, "$echo lilo -v -r $backup_root 2>&1|") or die "$!"; #while (){ # print LOG; #} #close CMD; # When finished, unmount the backup partitions unmountall(@mounted); close LOG; exit; sub unmountall(@) { my @mounted = @_; while ($#mounted > -1){ my $dir = pop @mounted; system "$echo umount $dir"; } }