]> git.etc.gen.nz Git - mirror-perl.git/commitdiff
Add in more functionality.
authorAndrew Ruthven <andrew@etc.gen.nz>
Sat, 16 Feb 2008 09:51:51 +0000 (22:51 +1300)
committerAndrew Ruthven <andrew@etc.gen.nz>
Sat, 16 Feb 2008 09:51:51 +0000 (22:51 +1300)
mirror

diff --git a/mirror b/mirror
index c971635e04bb617471902089c30f14175586cb67..45fea35638e71758a21ce0a443899e68c3faf6a6 100644 (file)
--- a/mirror
+++ b/mirror
@@ -38,7 +38,7 @@
 # limited testing under their belt.
 #
 # Instructions:
-# 
+#
 # The first thing you need to do is go down to the end of this header section
 # and set all the user defined variables in the user settings section.
 # They have a brief description of what they do and how they should be set.
 # Once that is done, you can run the program.
 #
 # mirror supports one command line switches ( -s ).
-#      -s = run sync mode only
+# -s = run sync mode only
 #
-# Sync mode should be ran the first time you setup mirror. Otherwise, use 
+# Sync mode should be ran the first time you setup mirror. Otherwise, use
 # no switch.
 # Once you have run mirror for the first time, you will find that it has setup
 # a directory hierarchy in your backup root directory:
 #
-#      /<backuproot>/<hostid>         ( host root )
-#      /<backuproot>/<hostid>/working ( working directory )
-#      /<backuproot>/<hostid>/hourly  ( hourly saves )
-#      /<backuproot>/<hostid>/daily   ( daily saves )
-#      /<backuproot>/<hostid>/weekly  ( weekly saves )
+# /<backuproot>/<hostid>         ( host root )
+# /<backuproot>/<hostid>/working ( working directory )
+# /<backuproot>/<hostid>/hourly  ( hourly saves )
+# /<backuproot>/<hostid>/daily   ( daily saves )
+# /<backuproot>/<hostid>/weekly  ( weekly saves )
 #
 # The directories are named 'image-<year>-<month>-<day>-<hour>'
 #
 
 use YAML::Syck;
 use POSIX qw/strftime/;
+use LockFile::Simple;
+use Sys::Hostname;
 
-use GetOptions::Long;
+use Getopt::Long;
 
 my $config_file = "mirror.yaml";
 my $sync = undef;
+my $lock_file = "/var/lock/mirror-" . hostname;
 
 GetOptions("config|c=s"  => $config_file,
-                                        "sync|s"      => $sync);
+           "sync|s"      => $sync);
 
 my @errors   = ();
 my @warnings = ();
 
 my $c = load_config();
 
+# Try and obtain a lock.
+my $lockmgr = LockFile::Simple->make(-stale     => 1,
+                                     -autoclean => 1,
+                                     -hold      => 60 * 60 * 3);
+if (! $lockmgr->lock($lock_file)) {
+  push @errors, "Failed to obtain lockfile";
+  die_gracefully();
+}
+
+dodir();
+# do work
+
+$lockmgr->unlock($lock_file);
+
+
 
 
 
 sub load_config {
-       my $c = LoadFile($config_file);
+  my $c = eval { LoadFile($config_file) };
+  die_gracefully("Failed to load $config_file: $@")
+    if $@;
 
-       for my $required ('backuproot', 'hostid') {
-               die_gracefully("No $required set")
-                       unless defined $c->{$required} || $c->{required} ne '';
-       }
+  for my $required ('backuproot', 'hostid') {
+    die_gracefully("No $required set")
+      unless defined $c->{$required} && $c->{$required} ne '';
+  }
 
   $c->{'cp'}       ||= "cp -alf";
-       $c->{'weekdir'}  = "$c->{'backuproot'}/$c->{'hostid'}/weekly";
+  $c->{'weekdir'}  = "$c->{'backuproot'}/$c->{'hostid'}/weekly";
   $c->{'daydir'}   = "$c->{'backuproot'}/$c->{'hostid'}/daily";
-       $c->{'hourdir'}  = "$c->{'backuproot'}/$c->{'hostid'}/hourly";
-       $c->{'working'}  = "$c->{'backuproot'}/$c->{'hostid'}/working";
-       $c->{'lockfile'} = "$c->{'backuproot'}/$c->{'hostid'}/syncing-now";
+  $c->{'hourdir'}  = "$c->{'backuproot'}/$c->{'hostid'}/hourly";
+  $c->{'working'}  = "$c->{'backuproot'}/$c->{'hostid'}/working";
+  $c->{'lockfile'} = "$c->{'backuproot'}/$c->{'hostid'}/syncing-now";
 
-       $c->{'datedir'}  = strftime("image-%Y-%m-%d-%H");
-       $c->{'dow'}      = strftime("%w");
-       $c->{'chour'}    = strftime("%H");
-       $c->{'lday'}     = strftime("image-%Y-%m-%d-%H", localtime() - 3600 * 24);
-       $c->{'1week'}    = strftime("image-%Y-%m-%d-%H", localtime() - 3600 * 24 * 7);
+  $c->{'datedir'}  = strftime("image-%Y-%m-%d-%H");
+  $c->{'dow'}      = strftime("%w");
+  $c->{'chour'}    = strftime("%H");
+  $c->{'lday'}     = strftime("image-%Y-%m-%d-%H", localtime() - 3600 * 24);
+  $c->{'1week'}    = strftime("image-%Y-%m-%d-%H", localtime() - 3600 * 24 * 7);
 
-       return \%c;
+  return $c;
 };
 
 sub dodir {
-       for my dir (qw'weekdir daydir hourdir working') {
-               if (test("! -d $c->{$dir}")) {
-                       run_and_check("mkdir -p $c->{$dir}");
-               }
-       }
+  for my $dir (qw'weekdir daydir hourdir working') {
+    if (test("! -d $c->{$dir}")) {
+      run_and_check("mkdir -p $c->{$dir}");
+    }
+  }
 }
 
-sub doweek {   
-       return
-               if $c->{'saveweeks'} == 0;
+sub doweek {
+  return
+    if defined $c->{'save'}{'weeks'} && $c->{'save'}{'weeks'} == 0;
 
-       if (test("-d $c->{'daydir'}/$c->{'lweek'}")) {
-               run_and_check("mv $c->{'daydir'}/$c->{'lweek'} $c->{'weekdir'}")
-       } else {
-               push @warnings, "I can't find a daily snapshot that is a week old...";
-       }
+  if (test("-d $c->{'daydir'}/$c->{'lweek'}")) {
+    run_and_check("mv $c->{'daydir'}/$c->{'lweek'} $c->{'weekdir'}")
+  } else {
+    push @warnings, "I can't find a daily snapshot that is a week old...";
+  }
 }
 
 sub doday {
-       return 
-               if $c->{'savedays'} == 0;
-
-       if (test("-d $c->{'hourdir'}/$c->{'lday'}")) {
-               run_and_check("mv $c->{'hourdir'}/$c->{'lday'} $c->{'daydir'}");
-       } else {
-               push @warnings, "I can't find an hourly snapshot that is 24hrs old..."
-       }
+  return
+    if defined $c->{'save'}{'days'} && $c->{'save'}{'days'} == 0;
+
+  if (test("-d $c->{'hourdir'}/$c->{'lday'}")) {
+    run_and_check("mv $c->{'hourdir'}/$c->{'lday'} $c->{'daydir'}");
+  } else {
+    push @warnings, "I can't find an hourly snapshot that is 24hrs old..."
+  }
 }
 
 sub dohour {
-       run_and_check("$c->{'cp'} $c->{'working'} $c->{'hourdir'}/$c->{'datedir'}");
-       run_and_check("touch $c->{'hourdir'}/$c->{'datedir'}");
+  run_and_check("$c->{'cp'} $c->{'working'} $c->{'hourdir'}/$c->{'datedir'}");
+  run_and_check("touch $c->{'hourdir'}/$c->{'datedir'}");
 }
 
 sub dosync {
-       if (defined $c->{'autosrcdir'}) {
-               allsrcdir=`ssh ${backedhost} "ls -d /*"`
-       }
-
-       for srcdir in ${allsrcdir}; do
-  if [ "${backedhost}" != "" ]; then
-    if [ ${advexcludes} -gt 0 ]; then
-      run_and_check "${rsync} ${EXCLUDES} ${backedhost}:${srcdir} ${working}"
-    else
-      run_and_check "${rsync} ${backedhost}:${srcdir} ${working}"
-    fi
-  else
-    if [ ${advexcludes} -gt 0 ]; then
-      run_and_check "${rsync} ${EXCLUDES} ${srcdir} ${working}"
-    else
-      run_and_check "${rsync} ${srcdir} ${working}"
-    fi
-  fi
-done
+  # See if we should backup everything...
+  my $srcdirs;
+  if (defined $c->{'autosrcdir'}) {
+    if (defined $c->{'backedhost'}) {
+      push @{ $srcdirs }, split("\n", `ssh $c->{'backedhost'} "ls -d /*"`);
+    } else {
+      push @{ $srcdirs }, split("\n", `ls -d /*`);
+    }
+  } else {
+    $srcdirs = $c->{'allsrcdir'};
+  }
+
+  my $dest = (defined $c->{'rmthost'} ? "$c->{'rmtlogin'}@$c->{'rmthost'}:" : '')
+    . $c->{'working'};
+
+  for my $dir (@{ $srcdirs }) {
+    my $srcdir = (defined $c->{'backedhost'} ? "$c->{backedhost}:" : '')
+      . $dir;
+
+    if (defined $c->{advexcludes} && $c->{advexcludes} == 1) {
+      run_and_check("$c->{'rsync'} $EXCLUDES $srcdir $dest");
+    } else {
+      run_and_check("$c->{'rsync'} $srcdir $dest");
+    }
+  }
 }
 
+sub docleanup {
+  $c->{'save'}{'hours'} ||= 1;
+
+  my @hours = split("\n", run_and_return("ls -t $c->{'hourdir'}"));
+
+  my $count = 0;
+  for my $hour (@hours) {
+    $count++;
+
+    if ($count > $c->{'save'}{'hours'}) {
+      run_and_check("rm -Rf $c->{'hourdir'}/$hour");
+    }
+  }
+
+  if ($c->{'save'}{'weeks'} > 0 && $c->{'save'}{'days'} < 7) {
+    $c->{'save'}{'days'} = 7;
+  }
+
+  my @days = split("\n", run_and_return("ls -t $c->{'daydir'}"));
+
+  $count = 0;
+  for my $day (@days) {
+    $count++;
+
+    if ($count > $c->{'save'}{'days'}) {
+      run_and_check("rm -Rf $c->{'daydir'}/$day");
+    }
+  }
+
+  my @weeks = split("\n", run_and_return("ls -t $c->{'weekdir'}"));
+
+  $count = 0;
+  for my $week (@weeks) {
+    $count++;
+
+    if ($count > $c->{'save'}{'weeks'}) {
+      run_and_check("rm -Rf $c->{'weekdir'}/$week");
+    }
+  }
+}
+
+
 
 # Perform a test, either via ssh or using a local perl check
 sub test {
-       my ($test) = shift;
-
-       if (defined $c->{rmtssh}) {
-               system("$c->{rmtssh} [ $test ]");
-               
-               return ($? >> 8);
-       } else {
-               return $test;
-       }
+  my ($test) = shift;
+
+  if (defined $c->{rmtssh}) {
+    system("$c->{rmtssh} [ $test ]");
+
+    return ($? >> 8);
+  } else {
+    return $test;
+  }
 }
-               
+
 
 # Run a command and check its return code.
 sub run_and_check {
   my ($cmd, $die) = shift;
 
-       $cmd = "$c->{$rmtssh} $cmd"
-               if defined $c->{rmtssh};
+  $cmd = "$c->{$rmtssh} \"$cmd\""
+    if defined $c->{rmtssh};
 
   system($cmd);
   if ($? >> 8 != 0) {
-    push @errors "Command \"$cmd\" failed with return code " . ($? >> 8) . "\n";
+    push @errors, "Command \"$cmd\" failed with return code " . ($? >> 8);
+
+    die_gracefully()
+      if defined $die;
+
+    return 0;
+  }
+}
+
+# Run a command and return its output.
+sub run_and_return {
+  my ($cmd, $die) = shift;
+
+  $cmd = "$c->{$rmtssh} \"$cmd\""
+    if defined $c->{rmtssh};
+
+  my $output = `$cmd`;
+  if ($? >> 8 != 0) {
+    push @errors, "Command \"$cmd\" failed with return code " . ($? >> 8);
+
+    die_gracefully()
+      if defined $die;
+  }
+
+  return $output;
+}
+
+
+sub die_gracefully {
+  my $message = shift;
+
+  if ($#errors > 0 || $#warnings > 0 || defined $message) {
+    handle_output($message);
+  }
+
+  print "$message\n"
+    if defined $message;
+
+  exit 0;
+}
 
-               die_gracefully()
-                       if defined $die;
+# Either print the status out, or email it.
+sub handle_output {
+  my $output = shift;
+  $output .= "\n"
+    if defined $output && $output ne "";
 
-               return 0;
+  my $title  = "mirror report from " . hostname;
+  my $suffix = undef;
+
+  if ($#errors > 0) {
+    $output .= "Errors:\n";
+    $output .= join("\n", @errors);
+    $output .= "\n";
+    $suffix = "ERRORS";
+  }
+
+  if ($#warnings > 0) {
+    $output .= "Warnings:\n";
+    $output .= join("\n", @warnings);
+    $suffix ||= "WARNINGS";
+  }
+
+  if (scalar(@{ $c->{'emails'} }) == 0) {
+    print "\n$output";
+  } else {
+    my $mail = MIME::Entity->build(
+      To      => \@{ $c->{emails} },
+      Subject => $title . (defined $suffix ? " - $suffix" : ''),
+      Data    => $output
+    );
+
+    $mail->send('sendmail');
   }
 }
+
+