0 #!/usr/bin/perl 1 # $Revision$ 2 # 3 # This is a script that will copy the contents of one drive to another. 4 # It's intended to be run nightly. It uses rsync to do the copying. 5 # You have to adjust the list of drive partitions to copy. 6 # 7 use strict; 8 use vars qw($opt_d $opt_f $opt_h $opt_n $opt_v); 9 use Getopt::Std; 10 11 $ENV{PATH} = '/sbin:/bin:/usr/bin'; 12 13 sub Usage { 14 print < "--exclude '/dev/*'", 57 '/var' => "--exclude '/var/log/*' --exclude '/var/spool/dnews/*' --exclude '/var/run/*'", 58 ); 59 60 # List of options for fsck based on filesystem type. 61 my %fsck_options = ( 62 'ext2' => '-f', # force check even if it's "not time yet" 63 'reiserfs' => '-x', # perform minor fixes 64 ); 65 66 #------------------------------------------------------------------------ 67 68 my $mount_point = '/mnt/synchro'; 69 70 # 71 # Given a device spec as input, return the destination spec. 72 # For example, hda7 returns hdc7 and sdc1 returns sda1. 73 # 74 # You will probably have to change this function for your set up. 75 # You could set up a lookup table here if your set up is more complex. 76 # 77 sub get_dest { 78 my $drive = shift; 79 80 # print "INPUT = $drive ===> OUTPUT = "; #debug 81 82 my $first = 'a'; 83 my $second = 'c'; 84 85 if ($drive =~ /[hs]?d$first/) { 86 $drive =~ s/([hs]?)d$first/$1d$second/; 87 } else { 88 $drive =~ s/([hs]?)d$second/$1d$first/; 89 } 90 91 # print "$drive\n"; #debug 92 93 return $drive; 94 } 95 96 my (%mounts, %fstype); 97 98 unless (-d $mount_point) { 99 mkdir ($mount_point, 0700); 100 } 101 102 (-d $mount_point) 103 || die "$mount_point does not exist and could not be created. $!\n"; 104 105 # Use the 'mount' command to determine current mount points and fs types 106 107 open(MOUNT, 'mount|') || die; 108 while () { 109 my ($disk,$on,$mp,$type,$fstype) = split; 110 $mounts{$mp} = $disk; 111 $fstype{$mp} = $fstype; 112 } 113 close MOUNT; 114 115 # Try to unmount first just in case someone left a filesystem mounted. 116 # This typically happens when you mount the filesystem to 117 # manually recover a file and forget to umount when you are done. 118 119 syscmd("umount $mount_point"); 120 121 # Loop over the filesystem list, performing each fsck and rsync operation. 122 123 foreach my $filesystem (@filesystems) { 124 my $extras = $extras{$filesystem}; 125 126 print STDERR "Working on filesystem '$filesystem'\n"; 127 128 # Set $disk to be the destination filesystem. 129 130 my $disk = get_dest($mounts{$filesystem}); 131 132 if ($opt_f) { 133 # Attempt the fsck, but don't stop backups if there is a warning. 134 # The warning will show up in the email from cron. 135 136 my $fstype = $fstype{$filesystem}; 137 syscmd("fsck -t $fstype $fsck_options{$fstype} $disk") 138 && warn "Fsck of $disk returned an error code. $!\n"; 139 } 140 141 # The correct filesystem has to be mounted, so stop if this fails. 142 143 syscmd("mount -t $fstype{$filesystem} $disk $mount_point") 144 && die "Mount of $disk failed. $!\n"; 145 146 # Config errors can cause the rsync to fail for one filesystem 147 # so just issue a warning and proceed if it fails. 148 149 syscmd("rsync $rsync_options $extras ${filesystem}/ $mount_point") 150 && warn "rsync of $filesystem failed. $!\n"; 151 152 # Filesystem umount failure will also stop the script. 153 154 syscmd("umount $mount_point") 155 && die "Mount of $disk failed. $!\n"; 156 } 157 158 # 159 # Accept a command to run as an argument. Print it to STDOUT. 160 # Dryrun mode: That's all. Return 0 (no error) 161 # Normal mode: Execute the command. Return != 0 on error. 162 # 163 sub syscmd { 164 my $cmd = shift; 165 166 print "$cmd\n"; 167 return 0 if $opt_d; 168 169 my $rc = system($cmd); 170 if ($rc > 0x80) { 171 return $rc >> 8; 172 } elsif ($rc > 0) { 173 die "Signal $rc received in $cmd\n"; 174 } 175 return 0; 176 } 177 178 # that's it!