#!/usr/bin/perl use strict; # This script is used when switching over to a backup server or # when switching back to the master server. This script is to be # run on the SLAVE (secondary) node! # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Copyright (C) 2003 Jani Reinikainen [ devel@reinikainen.net ] # Version 0.2.0 (20. February 2003) ####################################################### # These are some variables you probably want to set up. my %services = ("apachectl start" => "/var/run/apache.pid", "proftpd" => "/usr/local/var/proftpd.pid", "exim -bd" => "/var/spool/exim/exim-daemon.pid", "amavisd" => "/var/amavis/amavisd.pid", "apache-sslctl start" => "/var/run/apache-ssl.pid", "safe_mysqld \&" => "/var/run/mysqld/mysqld.pid"); my @rsyncpaths = ("/home","/var/mail"); my $rsync = "/usr/bin/rsync"; # No further editing neccessary. ####################################################### # CHECK THAT WE GOT ALL NEEDED ARGUMENTS if ($#ARGV != 1) { print "Error: you need to give this script two arguments.\n"; print "Usage: ./takeover.pl \[set/remove\] \[IP-address\]\n\n"; exit; } # CHECK THAT USER IS ROOT unless ($> == 0 || $< == 0) { print "Error: You must run this script as root!\n"; print "Usage: ./takeover.pl \[set/remove\] \[IP-address\]\n\n"; exit; } if (@ARGV[0] eq "set") { if (!-e "/etc/fake/instance_config/@ARGV[1].cfg") { print "Error: Fake is not configured to takeover this IP address.\n"; print "Please edit fake\'s configuration files in /etc/fake\n\n"; exit; } # Check that a computer with the IP to be taken over is not on the network # NOTE: THIS USES THE ARPING FROM THE IPUTILS PACKAGE, AND _NOT_ THE OTHER # (CONFLICTING!) ARPING FROM http://www.habets.pp.se/synscan/! if (`arping -D -q -I eth0 -c 2 @ARGV[1]; echo \$?` == 1) { print "The IP address @ARGV[1] exists on the network. Did you take down the interface?\n"; print "I will not take over an existing IP address, in order to avoid conflicts.\n\n"; exit; } # CHECK THAT SERVICES ARE RUNNING foreach my $key (keys %services) { # Check if PID file exists and is readable if (-e $services{$key}) { open(FILE,$services{$key}); my $PID = ; chomp($PID); close FILE; # Check if PID found in PID file is actually running if (system("kill -CHLD $PID >/dev/null 2>&1")) { print "PID file '$services{$key}' exists, but no matching service (PID $PID) running. Trying to run '$key'.\n"; system("$key"); # We've got a stale PID file on our hands. Try to start the daemon. } else { print "'$key' running (PID $PID). This is good. Not starting another one.\n"; } # No PID file for this daemon found. Try to start it. } else { print "PID file '$services{$key}' doesn't exist, or is not readable. Trying to run '$key'.\n"; system("$key"); } } # TAKEOVER THE IP system("fake @ARGV[1] \&"); print "All done! This computer should now answer to the IP address $2\n"; print "To remove this fake IP, run './takeover.pl remove @ARGV[1]'\n"; print "To make sure all went as planned, here is the output of ifconfig:\n\n"; system("ifconfig"); } if (@ARGV[0] eq "remove") { print "I need to rsync the changed data back to the master server.\n"; print "Could you tell me at which IP the master currently resides?\n"; my $master_ip = &promptUser("IP","Y"); my $master_user = &promptUser("Okay. I still need to know under which username we are connecting","Y","root"); # First (big) rsync run to master &showDatetime; print "Running first rsync run to $master_ip.\n"; foreach my $rsyncpath (@rsyncpaths) { my $dirname = `dirname $rsyncpath`; system("$rsync -rlopgztvC --delete $rsyncpath $master_user\@$master_ip:$dirname"); } &showDatetime; print "First rsync run to $master_ip completed.\n"; # Close ProFTPd system("killall proftpd"); &showDatetime; print "Closed ProFTPd.\n"; # Close MTA system("killall exim"); &showDatetime; print "Closed Exim.\n"; # Second, small and (hopefully) fast, rsync run to master &showDatetime; print "Running second rsync run to $master_ip.\n"; foreach my $rsyncpath (@rsyncpaths) { my $dirname = `dirname $rsyncpath`; system("$rsync -rlopgztvC --delete $rsyncpath $master_user\@$master_ip:$dirname"); } &showDatetime; print "Second rsync run to $master_ip completed.\n"; # REMOVE IP FAKING system("fake remove @ARGV[1]"); &showDatetime; print "Removed IP faking.\n"; print "All done! This computer should no longer answer to the IP address @ARGV[1]\n"; print "Don't forget to start the master server!\n"; print "To make sure all went as planned, here is the output of ifconfig:\n\n"; system("ifconfig"); } # PURPOSE: Prompt the user for some type of input, and return the # input back to the calling program. # ARGS: $promptString - what you want to prompt the user with # $defaultValue - (optional) a default value for the prompt sub promptUser { (my $promptString, my $notNull, my $defaultValue) = @_; if ($defaultValue) { print $promptString, " [", $defaultValue, "]: "; } else { print $promptString, ": "; } $| = 1; # force a flush after our print $_ = ; # get the input from STDIN (presumably the keyboard) # Remove the newline character from the end of the input the user gave us chomp; # if we had a $default value, and the user gave us input, then # # return the input; if we had a default, and they gave us no # # no input, return the $defaultValue. # if ("$defaultValue") { return $_ ? $_ : $defaultValue; # return $_ if it has a value } else { # Check that user inputted something if we didn't have a default value if ($_ ne "") { return $_; } else { if (lc($notNull) eq "y") { print "Error: Not a valid input, you must input a value.\n"; &promptUser("$promptString","Y"); } } } } sub showDatetime { my @now = localtime(); printf ("\[%02d/%02d/%s %02d:%02d:%02d\] ", @now[3], @now[4]+1, @now[5]+1900, @now[2], @now[1], @now[0]); return; }