#!/usr/bin/perl

#
# A backup program for OTC -- converted from ancient shell scripts into
# a revolutionary hack by Craig Kelley
#

use strict;
use Proc::Daemon;		# free up cron to do other things
Proc::Daemon::Init;		# become a background process

# paths
my $DATE = "/bin/date";
my $INIT_D = "/etc/rc.d/init.d";
my $INSMOD = "/sbin/insmod";
my $KILLALL = "/usr/bin/killall";
my $LOG_DIR = "/usr/local/backup";
my $MAIL = "/bin/mail";
my $MT = "/bin/mt";
my $RMMOD = "/sbin/rmmod";
my $SLEEP = "/bin/sleep";
my $SMBMOUNT = "/usr/sbin/smbmount";
my $SMBUMOUNT = "/usr/sbin/smbumount";
my $SYNC = "/bin/sync";
my $TAR = "/bin/tar";
my $TAPE_DRIVE = "/dev/tape";

# configuration items
my @recipients = qw ( kelleyc vince );
my @BACKUP_TYPES = qw ( full user system );

# paths for the various backup methods (a path beginning with a "-"
# indicates that that directory should be skipped)
my @USER_BACKUP = qw ( 
		      /home 
		      /usr/local/profiles
		     );
my @SYSTEM_BACKUP = qw (
			/var/lib
			/var/named
			/var/spool
			/usr/share/fax
			/var/state
			/etc
			/root
			/usr/local
			-/usr/local/backup
			/apps
			/mnt/windowsbox
		       );

# close output streams
close STDOUT;			
close STDERR;

# dup output all to the mail pipe
open MAIL, ("| $MAIL -s BACKUP " . join (" ", @recipients)) or die $!;
select MAIL; $| = 1; select STDOUT;
open STDERR, (">&MAIL") or die $!;
select STDERR; $| = 1; select STDOUT;
open STDOUT, (">&MAIL") or die $!;
$| = 1;

# get arguments
my $backup_type = $ARGV[0];
my $eject_tape = ($ARGV[1] =~ /^eject$/) ? 1 : undef;

# sanity check
unless (grep (@BACKUP_TYPES, $backup_type)) {
   print STDERR "Error parsing: " . join (" ", @ARGV) . "\n";
   print STDERR "\t.\n";
   print STDERR "Usage: backup.pl <" . join (" ", @BACKUP_TYPES) . 
      "> [eject]\n";
   exit (1);
}
if ((!defined $eject_tape) && (defined $ARGV[1])) {
   print STDERR "Error parsing: " . join (" ", @ARGV) . "\n";
   print STDERR "\tSecond argument must either be empty or 'eject', not '" .
      $ARGV[1] . "'\n";
   print STDERR "Usage: backup.pl <" . join (" ", @BACKUP_TYPES) . 
      "> [eject]\n";
   exit (1);
}

# do the backup

#
# FULL BACKUP (SYSTEM and USER)
#

if ($backup_type eq "full") {

   my $ret_val;

   print "Starting FULL backup.\n";
   print `$DATE`;
   print "Rewinding tape.\n";
   die "ERROR: Unable to rewind tape!\n"
      if (system ("$MT rewind") != 0);
   print "Mounting WINDOWSBOX, to backup FrontPage web sites.\n";
   $ret_val = mount_windowsbox();
   if ($ret_val != 0) {
      # first, try to unmount and then re-mount
      umount_windowsbox();
      $ret_val = mount_windowsbox();
      if ($ret_val != 0) {
	 # second try failed, give up
	 umount_windowsbox();
	 die "ABORTING BACKUP: $ret_val\n";
      }
   }
   sendmail_stop();
   inet_stop();
   $ret_val = backup_files($backup_type, @SYSTEM_BACKUP, @USER_BACKUP);
   sendmail_start();		# startup sendmail regardless
   inet_start();		# and IMAP/POP3
   if ($ret_val != 0) {
      umount_windowsbox();
      die "ABORTING BACKUP: $ret_val\n";
   }

   # settle the system down
   sleep(60);
   system ($SYNC);
   sleep(60);
   system ($SYNC);

   # finish up
   print "Unmounting WINDOWSBOX.\n";
   $ret_val = umount_windowsbox();
   die "NON-FATAL ERROR: $ret_val\n"
      if ($ret_val != 0);
   print "Backup complete.\n";
}

#
# USER AND GROUP backup
#  

elsif ($backup_type eq "user") {

   my $ret_val;

   print "Starting USER and GROUP backup.\n";
   print `$DATE`;
   print "Rewinding tape.\n";
   die "ERROR: Unable to rewind tape!\n"
      if (system ("$MT rewind") != 0);
   inet_stop();
   $ret_val = backup_files($backup_type, @USER_BACKUP);
   if ($ret_val != 0) {
      inet_start();
      die "ABORTING BACKUP: $ret_val\n";
   }
   inet_start();
   system ($SYNC);

   # finish up
   print "Backup complete.\n";
}

#
# SYSTEM ONLY backup
#

elsif ($backup_type eq "system") {

   my $ret_val;

   print "Starting SYSTEM backup.\n";
   print `$DATE`;
   print "Rewinding tape.\n";
   die "ERROR: Unable to rewind tape!\n"
      if (system ("$MT rewind") != 0);
   print "Mounting WINDOWSBOX, to backup FrontPage web sites.\n";
   $ret_val = mount_windowsbox();
   if ($ret_val != 0) {
      # first, try to unmount and then re-mount
      umount_windowsbox();
      $ret_val = mount_windowsbox();
      if ($ret_val != 0) {
	 # second try failed, give up
	 umount_windowsbox();
	 die "ABORTING BACKUP: Unable to mount WINDOWSBOX: $ret_val\n";
      }
   }
   sendmail_stop();
   inet_stop();
   $ret_val = backup_files($backup_type, @SYSTEM_BACKUP);
   sendmail_start();		# startup sendmail regardless
   inet_start();		# and IMAP/POP3

   print "System backup finished: $ret_val\n";

   # settle the system down
   sleep(60);
   system ($SYNC);
   sleep(60);
   system ($SYNC);

   # finish up
   print "Unmounting WINDOWSBOX.\n";
   $ret_val = umount_windowsbox();
   warn "NON-FATAL ERROR: $ret_val\n"
      if ($ret_val != 0);
   print "Backup complete.\n";
}

if (defined $eject_tape) {

   print "Ejecting tape.\n";
   print `$MT offline`;

}

close STDOUT;
close STDERR;
close MAIL;
exit (0);

#
# Subroutines
#

sub backup_files {

   my $type = shift;
   my @afiles = @_;

   # pick out any paths we need to avoid
   my (@skips, @paths);
   foreach my $path (@afiles) {
      if ($path =~ /^-/) {
	 $path =~ s/^-//;
	 push (@skips, "--exclude $path");
      }
      else {
	 push (@paths, $path);
      }
   }

   my $files = join (" ", @paths);
   my $exclusions = join (" ", @skips);
      
   my $log = "$LOG_DIR/$type." . `$DATE +%Y%m%d`;
   my $command_line = "$TAR cvf $TAPE_DRIVE $files $exclusions > $log";

   return "ERROR: TAR command failed!\n"
      if (system($command_line) != 0);

   return 0;
}

sub mount_windowsbox {

   return "ERROR: Unable to insmod smbfs!"
      if (system ($INSMOD, "smbfs") != 0);

   return "ERROR: Unable to call smbmount!"
      if (system ($SMBMOUNT, 
		  "//windowsbox/InetPub",
		  "/mnt/windowsbox",
		  "-U",
		  "backup",
		  "-P",
		  "PASSWORD GOES HERE"
		 ) != 0);

   return 0;			# everything worked
}

sub umount_windowsbox {

   my $ret_val = 0;

   $ret_val = "ERROR: Unable to call smbumount!"
      if (system ($SMBUMOUNT,
		  "/mnt/windowsbox",
		 ) != 0);

   $ret_val .= "ERROR: Unable to rmmod smbfs!"
      if (system ($RMMOD, "smbfs") != 0);

   return $ret_val;
}

sub sendmail_stop {

   if ( system("$INIT_D/sendmail", "stop") != 0 ) {
      print MAIL "ERROR: Unable to shut down sendmail!";
   }

}

sub inet_stop {

   if ( system("$INIT_D/inet", "stop") != 0) {
      print MAIL "ERROR: Unable to shut down inted!";
   }
   print "Killing stray IMAP connections: "; 
   print `$KILLALL imapd`;
   print "Killing stray POP3 connections: ";
   print `$KILLALL ipop3d`;


}

sub sendmail_start {

   # send HUP
   print `$KILLALL sendmail`;
   print `$SLEEP 10`;
   # send death
   print `$KILLALL -9 sendmail 2>&1 >/dev/null`;
   print `$SLEEP 10`;
   if ( system("$INIT_D/sendmail", "start") != 0 ) {
      print MAIL "ERROR: Unable to restart sendmail!";
   }
}

sub inet_start {

   if ( system("$INIT_D/inet", "start") != 0) {
      print MAIL "ERROR: Unable to start inted!";
   }

}

