BackupPC (BackupPC-4.3.2) | : | BackupPC (BackupPC-4.4.0) | ||
---|---|---|---|---|
skipping to change at line 47 | skipping to change at line 47 | |||
# This program is distributed in the hope that it will be useful, | # This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | # GNU General Public License for more details. | |||
# | # | |||
# You should have received a copy of the GNU General Public License | # You should have received a copy of the GNU General Public License | |||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
# | # | |||
#======================================================================== | #======================================================================== | |||
# | # | |||
# Version 4.3.2, released 17 Feb 2020. | # Version 4.4.0, released 20 Jun 2020. | |||
# | # | |||
# See http://backuppc.sourceforge.net. | # See http://backuppc.sourceforge.net. | |||
# | # | |||
#======================================================================== | #======================================================================== | |||
use strict; | use strict; | |||
no utf8; | no utf8; | |||
use vars qw($Hosts); | use vars qw($Hosts); | |||
use lib "__INSTALLDIR__/lib"; | use lib "__INSTALLDIR__/lib"; | |||
use BackupPC::Lib; | use BackupPC::Lib; | |||
use BackupPC::XS; | use BackupPC::XS; | |||
use Encode qw/decode_utf8/; | use Encode qw/decode_utf8/; | |||
use File::Path; | use File::Path; | |||
use Data::Dumper; | use Data::Dumper; | |||
use Getopt::Std; | use Getopt::Std; | |||
use Socket; | use Socket; | |||
skipping to change at line 87 | skipping to change at line 88 | |||
} | } | |||
########################################################################### | ########################################################################### | |||
# Initialize major data structures and variables | # Initialize major data structures and variables | |||
########################################################################### | ########################################################################### | |||
# | # | |||
# Get an instance of BackupPC::Lib and get some shortcuts. | # Get an instance of BackupPC::Lib and get some shortcuts. | |||
# | # | |||
die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); | die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); | |||
my $TopDir = $bpc->TopDir(); | my $TopDir = $bpc->TopDir(); | |||
my $BinDir = $bpc->BinDir(); | my $BinDir = $bpc->BinDir(); | |||
my $LogDir = $bpc->LogDir(); | my $LogDir = $bpc->LogDir(); | |||
my $RunDir = $bpc->RunDir(); | my $RunDir = $bpc->RunDir(); | |||
my %Conf = $bpc->Conf(); | my %Conf = $bpc->Conf(); | |||
# | # | |||
# Verify we are running as the correct user | # Verify we are running as the correct user | |||
# | # | |||
if ( $Conf{BackupPCUserVerify} | if ( $Conf{BackupPCUserVerify} && $> != (my $uid = (getpwnam($Conf{BackupPCUser} | |||
&& $> != (my $uid = (getpwnam($Conf{BackupPCUser}))[2]) ) { | ))[2]) ) { | |||
print(STDERR "Wrong user: my userid is $>, instead of $uid ($Conf{BackupPCUs er}); exiting in 30s\n"); | print(STDERR "Wrong user: my userid is $>, instead of $uid ($Conf{BackupPCUs er}); exiting in 30s\n"); | |||
sleep(30); | sleep(30); | |||
exit(1); | exit(1); | |||
} | } | |||
########################################################################### | ########################################################################### | |||
# Ensure we don't have old versions of key libraries and executables | # Ensure we don't have old versions of key libraries and executables | |||
########################################################################### | ########################################################################### | |||
my $PackageVersion = { | my $PackageVersion = { | |||
'BackupPC::XS' => '0.57', | 'BackupPC::XS' => '0.62', | |||
rsync_bpc => '3.0.9.8', | rsync_bpc => '3.0.9.15', | |||
}; | }; | |||
if ( $BackupPC::XS::VERSION < $PackageVersion->{'BackupPC::XS'} ) { | if ( $BackupPC::XS::VERSION < $PackageVersion->{'BackupPC::XS'} ) { | |||
print(STDERR "BackupPC: old version $BackupPC::XS::VERSION of BackupPC::XS: need >= $PackageVersion->{'BackupPC::XS'}; exiting in 30s\n"); | print(STDERR "BackupPC: old version $BackupPC::XS::VERSION of BackupPC::XS: need >= $PackageVersion->{'BackupPC::XS'}; exiting in 30s\n"); | |||
sleep(30); | sleep(30); | |||
exit(1); | exit(1); | |||
} | } | |||
if ( $Conf{RsyncBackupPCPath} ne "" && -x $Conf{RsyncBackupPCPath} ) { | if ( $Conf{RsyncBackupPCPath} ne "" && -x $Conf{RsyncBackupPCPath} ) { | |||
my $output = $bpc->cmdSystemOrEval([$Conf{RsyncBackupPCPath}, "--version"]); | my $output = $bpc->cmdSystemOrEval([$Conf{RsyncBackupPCPath}, "--version"]); | |||
if ( $? ) { | if ( $? ) { | |||
print(STDERR "BackupPC: can't run $Conf{RsyncBackupPCPath} for rsync_bpc | print(STDERR | |||
version check; ($output) exiting in 30s\n"); | "BackupPC: can't run $Conf{RsyncBackupPCPath} for rsync_bpc versio | |||
n check; ($output) exiting in 30s\n"); | ||||
sleep(30); | sleep(30); | |||
exit(1); | exit(1); | |||
} | } | |||
my $version = "unknown"; | my $version = "unknown"; | |||
$version = $1 if ( $output =~ /rsync_bpc\s+version\s+([\d.]+?)(\.?beta\d+)?\ s+protocol/ ); | $version = $1 if ( $output =~ /rsync_bpc\s+version\s+([\d.]+?)(\.?beta\d+)?\ s+protocol/ ); | |||
if ( $version eq "unknown" || version->parse($version) < version->parse($Pac kageVersion->{rsync_bpc}) ) { | if ( $version eq "unknown" || version->parse($version) < version->parse($Pac kageVersion->{rsync_bpc}) ) { | |||
print(STDERR "BackupPC: rsync_bpc at $Conf{RsyncBackupPCPath} needs to b | print(STDERR | |||
e upgraded (got version $version; need >= $PackageVersion->{rsync_bpc}); exiting | "BackupPC: rsync_bpc at $Conf{RsyncBackupPCPath} needs to be upgra | |||
in 30s\n"); | ded (got version $version; need >= $PackageVersion->{rsync_bpc}); exiting in 30s | |||
\n" | ||||
); | ||||
sleep(30); | sleep(30); | |||
exit(1); | exit(1); | |||
} | } | |||
} | } | |||
# | # | |||
# $Status maintain status information about each host. | # $Status maintain status information about each host. | |||
# It is a hashref of hashes, whose first index is the host. | # It is a hashref of hashes, whose first index is the host. | |||
# | # | |||
# $Info is a hashref giving general information about BackupPC status. | # $Info is a hashref giving general information about BackupPC status. | |||
skipping to change at line 151 | skipping to change at line 155 | |||
if ( !defined($Info) && ref($Status) ne "HASH" ) { | if ( !defined($Info) && ref($Status) ne "HASH" ) { | |||
print STDERR "$0: status.pl read failed: $Status\n"; | print STDERR "$0: status.pl read failed: $Status\n"; | |||
$Info = {}; | $Info = {}; | |||
$Status = {}; | $Status = {}; | |||
} | } | |||
# | # | |||
# %Jobs maintains information about currently running jobs. | # %Jobs maintains information about currently running jobs. | |||
# It is a hash of hashes, whose first index is the host. | # It is a hash of hashes, whose first index is the host. | |||
# | # | |||
my %Jobs = (); | my %Jobs = (); | |||
# | # | |||
# There are three command queues: | # There are three command queues: | |||
# - @UserQueue is a queue of user initiated backup requests. | # - @UserQueue is a queue of user initiated backup requests. | |||
# - @BgQueue is a queue of automatically scheduled backup requests. | # - @BgQueue is a queue of automatically scheduled backup requests. | |||
# - @CmdQueue is a queue of administrative jobs, including tasks | # - @CmdQueue is a queue of administrative jobs, including tasks | |||
# like BackupPC_nightly | # like BackupPC_nightly | |||
# Each queue is an array of hashes. Each hash stores information | # Each queue is an array of hashes. Each hash stores information | |||
# about the command request. | # about the command request. | |||
# | # | |||
my @UserQueue = (); | my @UserQueue = (); | |||
my @CmdQueue = (); | my @CmdQueue = (); | |||
my @BgQueue = (); | my @BgQueue = (); | |||
# | # | |||
# To quickly lookup if a given host is on a given queue, we keep | # To quickly lookup if a given host is on a given queue, we keep | |||
# a hash of flags for each queue type. | # a hash of flags for each queue type. | |||
# | # | |||
my(%CmdQueueOn, %UserQueueOn, %BgQueueOn); | my(%CmdQueueOn, %UserQueueOn, %BgQueueOn); | |||
# | # | |||
# BackupPC_tarCreate and BackupPC_zipCreate shouldn't run when a backup | # BackupPC_tarCreate and BackupPC_zipCreate shouldn't run when a backup | |||
# is active. We maintain a per-host mutex. A -1 value means a | # is active. We maintain a per-host mutex. A -1 value means a | |||
skipping to change at line 196 | skipping to change at line 200 | |||
# various information about the client connection. | # various information about the client connection. | |||
# | # | |||
my %Clients = (); | my %Clients = (); | |||
my $ClientConnCnt = 0; | my $ClientConnCnt = 0; | |||
# | # | |||
# Read file descriptor mask used by select(). Every file descriptor | # Read file descriptor mask used by select(). Every file descriptor | |||
# on which we expect to read (or accept) has the corresponding bit | # on which we expect to read (or accept) has the corresponding bit | |||
# set. | # set. | |||
# | # | |||
my $FDread = ''; | my $FDread = ''; | |||
# | # | |||
# Unix seconds when we next wakeup. A value of zero forces the scheduler | # Unix seconds when we next wakeup. A value of zero forces the scheduler | |||
# to compute the next wakeup time. | # to compute the next wakeup time. | |||
# | # | |||
my $NextWakeup = 0; | my $NextWakeup = 0; | |||
# | # | |||
# Name of signal saved by catch_signal | # Name of signal saved by catch_signal | |||
# | # | |||
skipping to change at line 227 | skipping to change at line 231 | |||
my $ServerInetPort = -1; | my $ServerInetPort = -1; | |||
# | # | |||
# Complete the rest of the initialization | # Complete the rest of the initialization | |||
# | # | |||
Main_Initialize(); | Main_Initialize(); | |||
########################################################################### | ########################################################################### | |||
# Main loop | # Main loop | |||
########################################################################### | ########################################################################### | |||
while ( 1 ) | while ( 1 ) { | |||
{ | ||||
# | # | |||
# Check if we can/should run BackupPC_nightly | # Check if we can/should run BackupPC_nightly | |||
# | # | |||
Main_TryToRun_nightly(); | Main_TryToRun_nightly(); | |||
# | # | |||
# Check if we can run a new command from @CmdQueue. | # Check if we can run a new command from @CmdQueue. | |||
# | # | |||
Main_TryToRun_CmdQueue(); | Main_TryToRun_CmdQueue(); | |||
skipping to change at line 290 | skipping to change at line 293 | |||
sub Main_Initialize | sub Main_Initialize | |||
{ | { | |||
umask($Conf{UmaskMode}); | umask($Conf{UmaskMode}); | |||
# | # | |||
# Check for another running process, verify executables are configured | # Check for another running process, verify executables are configured | |||
# correctly and make sure $TopDir is on a file system that supports | # correctly and make sure $TopDir is on a file system that supports | |||
# hardlinks. | # hardlinks. | |||
# | # | |||
if ( defined $Info->{pid} && kill(0, $Info->{pid}) && !$bpc->ServerConnect($ Conf{ServerHost}, $Conf{ServerPort}) ) { | if ( defined $Info->{pid} && kill(0, $Info->{pid}) && !$bpc->ServerConnect($ Conf{ServerHost}, $Conf{ServerPort}) ) { | |||
print(STDERR $bpc->timeStamp, | print(STDERR $bpc->timeStamp, "Another BackupPC is running (pid $Info->{ | |||
"Another BackupPC is running (pid $Info->{pid}); quitting...\n" | pid}); quitting...\n"); | |||
); | ||||
exit(1); | exit(1); | |||
} | } | |||
foreach my $progName ( qw(SmbClientPath NmbLookupPath PingPath DfPath | foreach my $progName ( qw(SmbClientPath NmbLookupPath PingPath DfPath | |||
SendmailPath SshPath RsyncBackupPCPath) ) { | SendmailPath SshPath RsyncBackupPCPath) ) { | |||
next if ( !defined $Conf{$progName} || $Conf{$progName} eq "" || -x $Con f{$progName} ); | next if ( !defined $Conf{$progName} || $Conf{$progName} eq "" || -x $Con f{$progName} ); | |||
print(STDERR $bpc->timeStamp, | print(STDERR $bpc->timeStamp, | |||
"\$Conf{$progName} = '$Conf{$progName}' is not a" | "\$Conf{$progName} = '$Conf{$progName}' is not a valid executable pr | |||
. " valid executable program; exiting\n"); | ogram; exiting\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
# | # | |||
# Create $RunDir if it doesn't exist | # Create $RunDir if it doesn't exist | |||
# | # | |||
if ( !-d $RunDir && !mkdir($RunDir, 0755) ) { | if ( !-d $RunDir && !mkdir($RunDir, 0755) ) { | |||
print(STDERR $bpc->timeStamp, "Can't create $RunDir... exiting\n"); | print(STDERR $bpc->timeStamp, "Can't create $RunDir... exiting\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
if ( $Conf{PoolV3Enabled} && !$bpc->HardlinkTest("$TopDir/pc", "$TopDir/cpoo l") ) { | if ( $Conf{PoolV3Enabled} && !$bpc->HardlinkTest("$TopDir/pc", "$TopDir/cpoo l") ) { | |||
print(STDERR $bpc->timeStamp, | print(STDERR $bpc->timeStamp, | |||
"PoolV3Enabled is set, and can't create a test hardlink bet | "PoolV3Enabled is set, and can't create a test hardlink between | |||
ween a" | a" | |||
. " file in $TopDir/pc and $TopDir/cpool. Either these are d | . " file in $TopDir/pc and $TopDir/cpool. Either these are differ | |||
ifferent" | ent" | |||
. " file systems, or this file system doesn't support hardlin | . " file systems, or this file system doesn't support hardlinks," | |||
ks," | . " or these directories don't exist, or there is a permissions" | |||
. " or these directories don't exist, or there is a permissio | . " problem, or the file system is out of inodes or full. Use" | |||
ns" | . " df, df -i, and ls -ld to check each of these possibilities." | |||
. " problem, or the file system is out of inodes or full. Us | . " Exiting...\n" | |||
e" | ); | |||
. " df, df -i, and ls -ld to check each of these possibilitie | ||||
s." | ||||
. " Exiting...\n"); | ||||
exit(1); | exit(1); | |||
} | } | |||
if ( $opts{d} ) { | if ( $opts{d} ) { | |||
# | # | |||
# daemonize by forking; more robust method per: | # daemonize by forking; more robust method per: | |||
# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=301057 | # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=301057 | |||
# | # | |||
my $pid; | my $pid; | |||
defined($pid = fork) or die("Can't fork: $!"); | defined($pid = fork) or die("Can't fork: $!"); | |||
exit if ( $pid ); # parent exits | exit if ( $pid ); # parent exits | |||
POSIX::setsid(); | POSIX::setsid(); | |||
defined($pid = fork) or die("Can't fork: $!"); | defined($pid = fork) or die("Can't fork: $!"); | |||
exit if $pid; # parent exits | exit if $pid; # parent exits | |||
chdir ("/") or die("Cannot chdir to /: $!\n"); | chdir("/") or die("Cannot chdir to /: $!\n"); | |||
close(STDIN); | close(STDIN); | |||
open(STDIN, "<", "/dev/null") or die("Cannot open /dev/null as stdin\n") ; | open(STDIN, "<", "/dev/null") or die("Cannot open /dev/null as stdin\n") ; | |||
# STDOUT and STDERR are handled in LogFileOpen() right below, | # STDOUT and STDERR are handled in LogFileOpen() right below, | |||
# otherwise we would have to reopen them too. | # otherwise we would have to reopen them too. | |||
} | } | |||
# | # | |||
# Open the LOG file and redirect STDOUT, STDERR etc | # Open the LOG file and redirect STDOUT, STDERR etc | |||
# | # | |||
LogFileOpen(); | LogFileOpen(); | |||
# | # | |||
skipping to change at line 375 | skipping to change at line 378 | |||
# | # | |||
# Catch various signals | # Catch various signals | |||
# | # | |||
foreach my $sig ( qw(INT BUS SEGV PIPE TERM ALRM HUP) ) { | foreach my $sig ( qw(INT BUS SEGV PIPE TERM ALRM HUP) ) { | |||
$SIG{$sig} = \&catch_signal; | $SIG{$sig} = \&catch_signal; | |||
} | } | |||
# | # | |||
# Report that we started, and update $Info. | # Report that we started, and update $Info. | |||
# | # | |||
printf(LOG "%sBackupPC %s (Perl v%vd) started, pid %d\n", | printf(LOG "%sBackupPC %s (Perl v%vd) started, pid %d\n", $bpc->timeStamp, $ | |||
$bpc->timeStamp, $bpc->{Version}, $^V, $$); | bpc->{Version}, $^V, $$); | |||
$Info->{ConfigModTime} = $bpc->ConfigMTime(); | $Info->{ConfigModTime} = $bpc->ConfigMTime(); | |||
$Info->{pid} = $$; | $Info->{pid} = $$; | |||
$Info->{startTime} = time; | $Info->{startTime} = time; | |||
$Info->{ConfigLTime} = time; | $Info->{ConfigLTime} = time; | |||
$Info->{Version} = $bpc->{Version}; | $Info->{Version} = $bpc->{Version}; | |||
# | # | |||
# Update the status left over form the last time BackupPC ran. | # Update the status left over form the last time BackupPC ran. | |||
# Requeue any pending links. | # Requeue any pending links. | |||
# | # | |||
foreach my $host ( sort(keys(%$Hosts)) ) { | foreach my $host ( sort(keys(%$Hosts)) ) { | |||
if ( $Status->{$host}{state} eq "Status_backup_in_progress" ) { | if ( $Status->{$host}{state} eq "Status_backup_in_progress" ) { | |||
# | # | |||
# should we restart it? skip it for now. | # should we restart it? skip it for now. | |||
# | # | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{state} = "Status_idle"; | |||
} else { | } else { | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{state} = "Status_idle"; | |||
} | } | |||
$Status->{$host}{activeJob} = 0; | $Status->{$host}{activeJob} = 0; | |||
} | } | |||
foreach my $host ( sort(keys(%$Status)) ) { | foreach my $host ( sort(keys(%$Status)) ) { | |||
next if ( defined($Hosts->{$host}) ); | next if ( defined($Hosts->{$host}) ); | |||
delete($Status->{$host}); | delete($Status->{$host}); | |||
} | } | |||
# | # | |||
# Write out our initial status and save our PID | # Write out our initial status and save our PID | |||
# | # | |||
StatusWrite(); | StatusWrite(); | |||
unlink("$RunDir/BackupPC.pid"); | unlink("$RunDir/BackupPC.pid"); | |||
if ( open(PID, ">", "$RunDir/BackupPC.pid") ) { | if ( open(PID, ">", "$RunDir/BackupPC.pid") ) { | |||
print(PID $$); | print(PID $$); | |||
close(PID); | close(PID); | |||
skipping to change at line 445 | skipping to change at line 447 | |||
SCGIStopStart(); | SCGIStopStart(); | |||
# | # | |||
# Check if we should run BackupPC_nightly. | # Check if we should run BackupPC_nightly. | |||
# BackupPC_nightly is run when the current job queue is empty. | # BackupPC_nightly is run when the current job queue is empty. | |||
# | # | |||
if ( $RunNightlyWhenIdle == 1 ) { | if ( $RunNightlyWhenIdle == 1 ) { | |||
# | # | |||
# Queue multiple nightly jobs based on the configuration | # Queue multiple nightly jobs based on the configuration | |||
# | # | |||
$Conf{MaxBackupPCNightlyJobs} = 1 | $Conf{MaxBackupPCNightlyJobs} = 1 | |||
if ( $Conf{MaxBackupPCNightlyJobs} <= 0 ); | if ( $Conf{MaxBackupPCNightlyJobs} <= 0 ); | |||
$Info->{NightlyPhase} = $Info->{NightlyPhase} % $Conf{BackupPCNightlyPer iod} | $Info->{NightlyPhase} = $Info->{NightlyPhase} % $Conf{BackupPCNightlyPer iod} | |||
if ( $Info->{NightlyPhase} < 0 || $Info->{NightlyPhase} >= $ Conf{BackupPCNightlyPeriod} ); | if ( $Info->{NightlyPhase} < 0 || $Info->{NightlyPhase} >= $Conf{Backu pPCNightlyPeriod} ); | |||
$Info->{PoolSizeNightlyPhase} ||= 0; | $Info->{PoolSizeNightlyPhase} ||= 0; | |||
# | # | |||
# Decide what subset of the 16 top-level directories 0..9a..f | # Decide what subset of the 16 top-level directories 0..9a..f | |||
# we run BackupPC_nightly on, based on $Conf{BackupPCNightlyPeriod}. | # we run BackupPC_nightly on, based on $Conf{BackupPCNightlyPeriod}. | |||
# If $Conf{BackupPCNightlyPeriod} == 1 then we run 0..15 every | # If $Conf{BackupPCNightlyPeriod} == 1 then we run 0..15 every | |||
# time. If $Conf{BackupPCNightlyPeriod} == 2 then we run | # time. If $Conf{BackupPCNightlyPeriod} == 2 then we run | |||
# 0..7 one night and 89a-f the next night. And so on. | # 0..7 one night and 89a-f the next night. And so on. | |||
# | # | |||
# $Info->{NightlyPhase} counts which night, from 0 to | # $Info->{NightlyPhase} counts which night, from 0 to | |||
# $Conf{BackupPCNightlyPeriod} - 1. | # $Conf{BackupPCNightlyPeriod} - 1. | |||
# | # | |||
my $start = int($Info->{NightlyPhase} * 16 | my $start = int($Info->{NightlyPhase} * 16 / $Conf{BackupPCNightlyPeriod | |||
/ $Conf{BackupPCNightlyPeriod}); | }); | |||
my $end = int(($Info->{NightlyPhase} + 1) * 16 | my $end = int(($Info->{NightlyPhase} + 1) * 16 / $Conf{BackupPCNightly | |||
/ $Conf{BackupPCNightlyPeriod}); | Period}); | |||
$end = $start + 1 if ( $end <= $start ); | $end = $start + 1 if ( $end <= $start ); | |||
$Info->{NightlyPhase}++; | $Info->{NightlyPhase}++; | |||
$Info->{NightlyPhase} = 0 if ( $end >= 16 ); | $Info->{NightlyPhase} = 0 if ( $end >= 16 ); | |||
# | # | |||
# Check if there is a job that is still running since the last | # Check if there is a job that is still running since the last | |||
# time BackupPC_nightly finished. If so, add the -r option to | # time BackupPC_nightly finished. If so, add the -r option to | |||
# BackupPC_nightly so it doesn't run BackupPC_refCountUpdate. | # BackupPC_nightly so it doesn't run BackupPC_refCountUpdate. | |||
# | # | |||
my $dontRunRefCountUpdate; | my $dontRunRefCountUpdate; | |||
foreach my $host ( keys(%Jobs) ) { | foreach my $host ( keys(%Jobs) ) { | |||
next if ( $host eq $bpc->scgiJob ); | next if ( $host eq $bpc->scgiJob ); | |||
my $pid = $Jobs{$host}{pid}; | my $pid = $Jobs{$host}{pid}; | |||
skipping to change at line 517 | skipping to change at line 517 | |||
# | # | |||
# Normally BackupPC_refCountUpdate only reports relative cha nges to the | # Normally BackupPC_refCountUpdate only reports relative cha nges to the | |||
# pool size, which is a lot more efficient. BackupPC_refCou ntUpdate | # pool size, which is a lot more efficient. BackupPC_refCou ntUpdate | |||
# computes the exact pool size only for a portion of the poo l each | # computes the exact pool size only for a portion of the poo l each | |||
# night, based on $Conf{PoolSizeNightlyUpdatePeriod}. | # night, based on $Conf{PoolSizeNightlyUpdatePeriod}. | |||
# | # | |||
# So decide when to clear $Info->{pool}{"${p}4"}[$i]{Kb}. | # So decide when to clear $Info->{pool}{"${p}4"}[$i]{Kb}. | |||
# | # | |||
my $clear; | my $clear; | |||
if ( $Conf{PoolSizeNightlyUpdatePeriod} > 0 ) { | if ( $Conf{PoolSizeNightlyUpdatePeriod} > 0 ) { | |||
$clear = ($i % $Conf{PoolSizeNightlyUpdatePeriod}) | $clear = ($i % $Conf{PoolSizeNightlyUpdatePeriod}) == | |||
== ($Info->{PoolSizeNightlyPhase} % $Conf{PoolS | ($Info->{PoolSizeNightlyPhase} % $Conf{PoolSizeNightly | |||
izeNightlyUpdatePeriod}); | UpdatePeriod}); | |||
} | } | |||
#print(LOG $bpc->timeStamp, "updating $p size of $i (clear = $clear, phase = $Info->{PoolSizeNightlyPhase}," | #print(LOG $bpc->timeStamp, "updating $p size of $i (clear = $clear, phase = $Info->{PoolSizeNightlyPhase}," | |||
# . " \$Conf{PoolSizeNightlyUpdatePe riod} = $Conf{PoolSizeNightlyUpdatePeriod})\n"); | # . " \$Conf{PoolSizeNightlyUpdatePe riod} = $Conf{PoolSizeNightlyUpdatePeriod})\n"); | |||
$Info->{pool}{"${p}4"}[$i]{Kb} = 0 if ( $clear ); | $Info->{pool}{"${p}4"}[$i]{Kb} = 0 if ( $clear ); | |||
$Info->{pool}{"${p}4"}[$i]{FileCnt} = 0; | $Info->{pool}{"${p}4"}[$i]{FileCnt} = 0; | |||
$Info->{pool}{"${p}4"}[$i]{DirCnt} = 0; | $Info->{pool}{"${p}4"}[$i]{DirCnt} = 0; | |||
$Info->{pool}{"${p}4"}[$i]{KbRm} = 0; | $Info->{pool}{"${p}4"}[$i]{KbRm} = 0; | |||
$Info->{pool}{"${p}4"}[$i]{FileCntRm} = 0; | $Info->{pool}{"${p}4"}[$i]{FileCntRm} = 0; | |||
$Info->{pool}{"${p}4"}[$i]{FileCntRep} = 0; | $Info->{pool}{"${p}4"}[$i]{FileCntRep} = 0; | |||
$Info->{pool}{"${p}4"}[$i]{FileRepMax} = 0; | $Info->{pool}{"${p}4"}[$i]{FileRepMax} = 0; | |||
$Info->{pool}{"${p}4"}[$i]{FileLinkMax} = 0; | $Info->{pool}{"${p}4"}[$i]{FileLinkMax} = 0; | |||
$Info->{pool}{"${p}4"}[$i]{FileLinkTotal} = 0; | $Info->{pool}{"${p}4"}[$i]{FileLinkTotal} = 0; | |||
$Info->{pool}{"${p}4"}[$i]{Time} = 0; | $Info->{pool}{"${p}4"}[$i]{Time} = 0; | |||
delete $Info->{pool}{"${p}4"}[$i]{FileCntRename}; | delete $Info->{pool}{"${p}4"}[$i]{FileCntRename}; | |||
} | } | |||
} | } | |||
} | } | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, | |||
sprintf("Running %d BackupPC_nightly jobs from %d..%d" | sprintf( | |||
. " (out of 0..15)\n", | "Running %d BackupPC_nightly jobs from %d..%d (out of 0..15)\n", | |||
$Conf{MaxBackupPCNightlyJobs}, $start, $end - 1)); | $Conf{MaxBackupPCNightlyJobs}, | |||
$start, $end - 1 | ||||
# | ) | |||
# Now queue the $Conf{MaxBackupPCNightlyJobs} jobs. | ); | |||
# The granularity on start and end is now 0..255. | ||||
# | # | |||
$start *= 16; | # Now queue the $Conf{MaxBackupPCNightlyJobs} jobs. | |||
$end *= 16; | # The granularity on start and end is now 0..255. | |||
my $start0 = $start; | # | |||
$start *= 16; | ||||
$end *= 16; | ||||
my $start0 = $start; | ||||
for ( my $i = 0 ; $i < $Conf{MaxBackupPCNightlyJobs} ; $i++ ) { | for ( my $i = 0 ; $i < $Conf{MaxBackupPCNightlyJobs} ; $i++ ) { | |||
# | # | |||
# The first nightly job gets the -m option (does email, log aging). | # The first nightly job gets the -m option (does email, log aging). | |||
# All jobs get the start and end options from 0..255 telling | # All jobs get the start and end options from 0..255 telling | |||
# them which parts of the pool to traverse. | # them which parts of the pool to traverse. | |||
# | # | |||
my $cmd = ["$BinDir/BackupPC_nightly"]; | my $cmd = ["$BinDir/BackupPC_nightly"]; | |||
push(@$cmd, "-m") if ( $i == 0 ); | push(@$cmd, "-m") if ( $i == 0 ); | |||
push(@$cmd, "-r") if ( $dontRunRefCountUpdate ); | push(@$cmd, "-r") if ( $dontRunRefCountUpdate ); | |||
push(@$cmd, "-P", $Info->{PoolSizeNightlyPhase}); | push(@$cmd, "-P", $Info->{PoolSizeNightlyPhase}); | |||
push(@$cmd, $start); | push(@$cmd, $start); | |||
$start = $start0 + int(($end - $start0) | $start = $start0 + int(($end - $start0) * ($i + 1) / $Conf{MaxBackup | |||
* ($i + 1) / $Conf{MaxBackupPCNightlyJobs}); | PCNightlyJobs}); | |||
push(@$cmd, $start - 1); | push(@$cmd, $start - 1); | |||
my $job = $bpc->adminJob($i); | my $job = $bpc->adminJob($i); | |||
unshift(@CmdQueue, { | unshift( | |||
@CmdQueue, | ||||
{ | ||||
host => $job, | host => $job, | |||
user => "BackupPC", | user => "BackupPC", | |||
reqTime => time, | reqTime => time, | |||
cmd => $cmd, | cmd => $cmd, | |||
}); | } | |||
); | ||||
$CmdQueueOn{$job} = 1; | $CmdQueueOn{$job} = 1; | |||
} | } | |||
$RunNightlyWhenIdle = 2; | $RunNightlyWhenIdle = 2; | |||
$Info->{PoolSizeNightlyPhase}++; | $Info->{PoolSizeNightlyPhase}++; | |||
$Info->{PoolSizeNightlyPhase} = 0 if ( $Info->{PoolSizeNightlyPhase} >= 16 ); | $Info->{PoolSizeNightlyPhase} = 0 if ( $Info->{PoolSizeNightlyPhase} >= 16 ); | |||
} | } | |||
} | } | |||
############################################################################ | ############################################################################ | |||
# Main_TryToRun_CmdQueue() | # Main_TryToRun_CmdQueue() | |||
skipping to change at line 591 | skipping to change at line 596 | |||
# Decide if we can run a new command from the @CmdQueue. | # Decide if we can run a new command from the @CmdQueue. | |||
# We only run one of these at a time. The @CmdQueue is | # We only run one of these at a time. The @CmdQueue is | |||
# used to run BackupPC_nightly using a fake host name of | # used to run BackupPC_nightly using a fake host name of | |||
# $bpc->adminJob. | # $bpc->adminJob. | |||
############################################################################ | ############################################################################ | |||
sub Main_TryToRun_CmdQueue | sub Main_TryToRun_CmdQueue | |||
{ | { | |||
my($req, $host); | my($req, $host); | |||
while ( $CmdJob eq "" && @CmdQueue > 0 && $RunNightlyWhenIdle != 1 | while ( $CmdJob eq "" && @CmdQueue > 0 && $RunNightlyWhenIdle != 1 | |||
|| @CmdQueue > 0 && $RunNightlyWhenIdle == 2 | || @CmdQueue > 0 && $RunNightlyWhenIdle == 2 && $bpc->isAdminJob($CmdQue | |||
&& $bpc->isAdminJob($CmdQueue[0]->{host}) | ue[0]->{host}) ) { | |||
) { | ||||
local(*FH); | local(*FH); | |||
$req = pop(@CmdQueue); | $req = pop(@CmdQueue); | |||
$host = $req->{host}; | $host = $req->{host}; | |||
if ( defined($Jobs{$host}) ) { | if ( defined($Jobs{$host}) ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Botch on admin job for $host: already in | |||
"Botch on admin job for $host: already in use!! skipping | use!! skipping cmd: $req->{cmd})\n"); | |||
cmd: $req->{cmd})\n"); | ||||
next; | next; | |||
} | } | |||
$CmdQueueOn{$host} = 0; | $CmdQueueOn{$host} = 0; | |||
my $cmd = $req->{cmd}; | my $cmd = $req->{cmd}; | |||
my $pid = open(FH, "-|"); | my $pid = open(FH, "-|"); | |||
if ( !defined($pid) ) { | if ( !defined($pid) ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "can't fork for $host, request by $req->{ | |||
"can't fork for $host, request by $req->{user}\n"); | user}\n"); | |||
close(FH); | close(FH); | |||
next; | next; | |||
} | } | |||
if ( !$pid ) { | if ( !$pid ) { | |||
setpgrp 0,0; | setpgrp 0, 0; | |||
$ENV{BPC_REQUSER} = $req->{user}; | $ENV{BPC_REQUSER} = $req->{user}; | |||
POSIX::nice($Conf{CmdQueueNice}) if ( $Conf{CmdQueueNice} ); | POSIX::nice($Conf{CmdQueueNice}) if ( $Conf{CmdQueueNice} ); | |||
unless ( exec(@$cmd) ) { | unless ( exec(@$cmd) ) { | |||
print( LOG $bpc->timeStamp, "can't exec @$cmd for $host\n" ); | print(LOG $bpc->timeStamp, "can't exec @$cmd for $host\n"); | |||
exit(0); | exit(0); | |||
} | } | |||
} | } | |||
$Jobs{$host}{pid} = $pid; | $Jobs{$host}{pid} = $pid; | |||
$Jobs{$host}{fh} = *FH; | $Jobs{$host}{fh} = *FH; | |||
$Jobs{$host}{fn} = fileno(FH); | $Jobs{$host}{fn} = fileno(FH); | |||
vec($FDread, $Jobs{$host}{fn}, 1) = 1; | vec($FDread, $Jobs{$host}{fn}, 1) = 1; | |||
$Jobs{$host}{startTime} = time; | $Jobs{$host}{startTime} = time; | |||
$Jobs{$host}{reqTime} = $req->{reqTime}; | $Jobs{$host}{reqTime} = $req->{reqTime}; | |||
$cmd = $bpc->execCmd2ShellCmd(@$cmd); | $cmd = $bpc->execCmd2ShellCmd(@$cmd); | |||
$Jobs{$host}{cmd} = $cmd; | $Jobs{$host}{cmd} = $cmd; | |||
$Jobs{$host}{user} = $req->{user}; | $Jobs{$host}{user} = $req->{user}; | |||
$Jobs{$host}{type} = $Status->{$host}{type}; | $Jobs{$host}{type} = $Status->{$host}{type}; | |||
$Status->{$host}{state} = "Status_admin_running"; | $Status->{$host}{state} = "Status_admin_running"; | |||
$Status->{$host}{activeJob} = 1; | $Status->{$host}{activeJob} = 1; | |||
$Status->{$host}{endTime} = time; | $Status->{$host}{endTime} = time; | |||
$CmdJob = $host if ( $host ne $bpc->scgiJob ); | $CmdJob = $host if ( $host ne $bpc->scgiJob ); | |||
$cmd =~ s/$BinDir\///g; | $cmd =~ s/$BinDir\///g; | |||
print(LOG $bpc->timeStamp, "Running $cmd (pid=$pid)\n"); | print(LOG $bpc->timeStamp, "Running $cmd (pid=$pid)\n"); | |||
if ( $cmd =~ /^BackupPC_nightly\s/ ) { | ||||
$BackupPCNightlyJobs++; | if ( $cmd =~ /^BackupPC_nightly\s/ ) { | |||
$BackupPCNightlyLock++; | $BackupPCNightlyJobs++; | |||
} | $BackupPCNightlyLock++; | |||
} | ||||
} | } | |||
} | } | |||
############################################################################ | ############################################################################ | |||
# Main_TryToRun_Bg_or_User_Queue() | # Main_TryToRun_Bg_or_User_Queue() | |||
# | # | |||
# Decide if we can run any new backup requests from @BgQueue | # Decide if we can run any new backup requests from @BgQueue | |||
# or @UserQueue. Several of these can be run at the same time | # or @UserQueue. Several of these can be run at the same time | |||
# based on %Conf settings. Jobs from @UserQueue take priority, | # based on %Conf settings. Jobs from @UserQueue take priority, | |||
# and at total of $Conf{MaxBackups} + $Conf{MaxUserBackups} | # and at total of $Conf{MaxBackups} + $Conf{MaxUserBackups} | |||
# simultaneous jobs can run from @UserQueue. After @UserQueue | # simultaneous jobs can run from @UserQueue. After @UserQueue | |||
# is exhausted, up to $Conf{MaxBackups} simultaneous jobs can | # is exhausted, up to $Conf{MaxBackups} simultaneous jobs can | |||
# run from @BgQueue. | # run from @BgQueue. | |||
############################################################################ | ############################################################################ | |||
sub Main_TryToRun_Bg_or_User_Queue | sub Main_TryToRun_Bg_or_User_Queue | |||
{ | { | |||
my($req, $host); | my($req, $host); | |||
my(@deferUserQueue, @deferBgQueue); | my(@deferUserQueue, @deferBgQueue); | |||
my($du, $duInode); | my($du, $duInode); | |||
if ( !defined $Info->{DUlastValueTime} || time - $Info->{DUlastValueTime} >= 600 ) { | if ( !defined $Info->{DUlastValueTime} || time - $Info->{DUlastValueTime} >= 600 ) { | |||
# | # | |||
# Update our notion of disk usage no more than | # Update our notion of disk usage no more than | |||
# once every 10 minutes | # once every 10 minutes | |||
# | # | |||
$du = $bpc->CheckFileSystemUsage(0); | $du = $bpc->CheckFileSystemUsage(0); | |||
$Info->{DUlastValue} = $du; | $Info->{DUlastValue} = $du; | |||
$Info->{DUlastValueTime} = time; | $Info->{DUlastValueTime} = time; | |||
$duInode = $bpc->CheckFileSystemUsage(1); | $duInode = $bpc->CheckFileSystemUsage(1); | |||
$Info->{DUInodelastValue} = $duInode; | $Info->{DUInodelastValue} = $duInode; | |||
} else { | } else { | |||
# | # | |||
# if we recently checked it then just use the old value | # if we recently checked it then just use the old value | |||
# | # | |||
$du = $Info->{DUlastValue}; | $du = $Info->{DUlastValue}; | |||
$duInode = $Info->{DUInodelastValue}; | $duInode = $Info->{DUInodelastValue}; | |||
} | } | |||
if ( $Info->{DUDailyMaxReset} ) { | if ( $Info->{DUDailyMaxReset} ) { | |||
$Info->{DUDailyMaxStartTime} = time; | $Info->{DUDailyMaxStartTime} = time; | |||
$Info->{DUDailyMaxReset} = 0; | $Info->{DUDailyMaxReset} = 0; | |||
$Info->{DUDailyMax} = 0; | $Info->{DUDailyMax} = 0; | |||
$Info->{DUInodeDailyMax} = 0; | $Info->{DUInodeDailyMax} = 0; | |||
} | } | |||
if ( !defined $Info->{DUDailyMax} || $du > $Info->{DUDailyMax} ) { | if ( !defined $Info->{DUDailyMax} || $du > $Info->{DUDailyMax} ) { | |||
$Info->{DUDailyMax} = $du; | $Info->{DUDailyMax} = $du; | |||
$Info->{DUDailyMaxTime} = time; | $Info->{DUDailyMaxTime} = time; | |||
} | } | |||
if ( !defined $Info->{DUInodeDailyMax} || $duInode > $Info->{DUInodeDailyMax } ) { | if ( !defined $Info->{DUInodeDailyMax} || $duInode > $Info->{DUInodeDailyMax } ) { | |||
$Info->{DUInodeDailyMax} = $duInode; | $Info->{DUInodeDailyMax} = $duInode; | |||
$Info->{DUInodeDailyMaxTime} = time; | $Info->{DUInodeDailyMaxTime} = time; | |||
} | } | |||
if ( $du > $Conf{DfMaxUsagePct} || $duInode > $Conf{DfMaxInodeUsagePct} ) { | if ( $du > $Conf{DfMaxUsagePct} || $duInode > $Conf{DfMaxInodeUsagePct} ) { | |||
my @bgQueue = @BgQueue; | my @bgQueue = @BgQueue; | |||
my $nSkip = 0; | my $nSkip = 0; | |||
# | # | |||
# When the disk is too full, only run backups that will | # When the disk is too full, only run backups that will | |||
# do expires, not regular backups | # do expires, not regular backups | |||
# | # | |||
@BgQueue = (); | @BgQueue = (); | |||
foreach $req ( @bgQueue ) { | foreach $req ( @bgQueue ) { | |||
if ( $req->{dumpExpire} ) { | if ( $req->{dumpExpire} ) { | |||
unshift(@BgQueue, $req); | unshift(@BgQueue, $req); | |||
} else { | } else { | |||
$BgQueueOn{$req->{host}} = 0; | $BgQueueOn{$req->{host}} = 0; | |||
$nSkip++; | $nSkip++; | |||
} | } | |||
} | } | |||
if ( $nSkip ) { | if ( $nSkip ) { | |||
print(LOG $bpc->timeStamp, "Disk too full (usage $du%; inode $duInod | print(LOG $bpc->timeStamp, | |||
e%;" | "Disk too full (usage $du%; inode $duInode%;" | |||
. " thres $Conf{DfMaxUsagePct}%/$Conf{DfMax | . " thres $Conf{DfMaxUsagePct}%/$Conf{DfMaxInodeUsagePct}%); s | |||
InodeUsagePct}%); skipped $nSkip hosts\n"); | kipped $nSkip hosts\n" | |||
); | ||||
$Info->{DUDailySkipHostCnt} += $nSkip; | $Info->{DUDailySkipHostCnt} += $nSkip; | |||
} | } | |||
} | } | |||
# | # | |||
# Run background jobs anytime. Previously they were locked out | # Run background jobs anytime. Previously they were locked out | |||
# when BackupPC_nightly was running or pending with this | # when BackupPC_nightly was running or pending with this | |||
# condition on the while loop: | # condition on the while loop: | |||
# | # | |||
# while ( $RunNightlyWhenIdle == 0 ) | # while ( $RunNightlyWhenIdle == 0 ) | |||
# | # | |||
while ( 1 ) { | while ( 1 ) { | |||
local(*FH); | local(*FH); | |||
my(@args, $progName, $type, $mutexDelta); | my(@args, $progName, $type); | |||
my $nJobs = keys(%Jobs); | my $nJobs = keys(%Jobs); | |||
# | # | |||
# CmdJob doesn't count towards MaxBackups / MaxUserBackups | # CmdJob doesn't count towards MaxBackups / MaxUserBackups | |||
# | # | |||
if ( $CmdJob ne "" ) { | if ( $CmdJob ne "" ) { | |||
if ( $BackupPCNightlyJobs ) { | if ( $BackupPCNightlyJobs ) { | |||
$nJobs -= $BackupPCNightlyJobs; | $nJobs -= $BackupPCNightlyJobs; | |||
} else { | } else { | |||
$nJobs--; | $nJobs--; | |||
} | } | |||
} | } | |||
$nJobs-- if ( defined($Jobs{$bpc->scgiJob} ) ); | $nJobs-- if ( defined($Jobs{$bpc->scgiJob}) ); | |||
if ( $nJobs < $Conf{MaxBackups} + $Conf{MaxUserBackups} | if ( $nJobs < $Conf{MaxBackups} + $Conf{MaxUserBackups} && @UserQueue > | |||
&& @UserQueue > 0 ) { | 0 ) { | |||
$req = pop(@UserQueue); | $req = pop(@UserQueue); | |||
if ( defined($Jobs{$req->{host}}) ) { | if ( defined($Jobs{$req->{host}}) ) { | |||
# | # | |||
# Job is currently running for this host; save it for later | # Job is currently running for this host; save it for later | |||
# | # | |||
push(@deferUserQueue, $req); | push(@deferUserQueue, $req); | |||
next; | next; | |||
} | } | |||
if ( $HostMutex{$req->{host}} > 0 && !$req->{restore} && !$req->{arc hive} ) { | if ( $HostMutex{$req->{host}} > 0 && !$req->{restore} && !$req->{arc hive} ) { | |||
# | # | |||
# Currently there are readers (eg, BackupPC_tarCreate) running, so we | # Currently there are readers (eg, BackupPC_tarCreate) running, so we | |||
# can't run a backup or delete | # can't run a backup or delete | |||
# | # | |||
push(@deferUserQueue, $req); | push(@deferUserQueue, $req); | |||
next; | next; | |||
} | } | |||
$UserQueueOn{$req->{host}} = 0; | $UserQueueOn{$req->{host}} = 0; | |||
} elsif ( $nJobs < $Conf{MaxBackups} | } elsif ( $nJobs < $Conf{MaxBackups} | |||
&& (@CmdQueue + $nJobs) | && (@CmdQueue + $nJobs) <= $Conf{MaxBackups} + $Conf{MaxPendingCmds} | |||
<= $Conf{MaxBackups} + $Conf{MaxPendingCmds} | && @BgQueue > 0 ) { | |||
&& @BgQueue > 0 ) { | ||||
$req = pop(@BgQueue); | $req = pop(@BgQueue); | |||
if ( defined($Jobs{$req->{host}}) ) { | if ( defined($Jobs{$req->{host}}) ) { | |||
# | # | |||
# Job is currently running for this host; save it for later | # Job is currently running for this host; save it for later | |||
# | # | |||
unshift(@deferBgQueue, $req); | unshift(@deferBgQueue, $req); | |||
next; | next; | |||
} | } | |||
if ( $HostMutex{$req->{host}} > 0 && !$req->{restore} && !$req->{arc hive} ) { | if ( $HostMutex{$req->{host}} > 0 && !$req->{restore} && !$req->{arc hive} ) { | |||
# | # | |||
skipping to change at line 789 | skipping to change at line 791 | |||
# | # | |||
# Restore the deferred jobs | # Restore the deferred jobs | |||
# | # | |||
@BgQueue = (@BgQueue, @deferBgQueue); | @BgQueue = (@BgQueue, @deferBgQueue); | |||
@UserQueue = (@UserQueue, @deferUserQueue); | @UserQueue = (@UserQueue, @deferUserQueue); | |||
last; | last; | |||
} | } | |||
$host = $req->{host}; | $host = $req->{host}; | |||
my $user = $req->{user}; | my $user = $req->{user}; | |||
if ( $req->{restore} ) { | if ( $req->{restore} ) { | |||
$progName = "BackupPC_restore"; | $progName = "BackupPC_restore"; | |||
$type = "restore"; | $type = "restore"; | |||
$mutexDelta = 1; | push(@args, $req->{hostIP}, $req->{host}, $req->{reqFileName}); | |||
push(@args, "-m", $req->{hostIP}, $req->{host}, $req->{reqFileName}) | ||||
; | ||||
} elsif ( $req->{delete} ) { | } elsif ( $req->{delete} ) { | |||
$progName = "BackupPC_backupDelete"; | $progName = "BackupPC_backupDelete"; | |||
$type = "delete"; | $type = "delete"; | |||
$mutexDelta = -1; | push(@args, "-L", "-h", $req->{host}, '-n', $req->{num}, $req->{opts | |||
push(@args, "-L", "-m", "-h", $req->{host}, '-n', $req->{num}, $req- | }); | |||
>{opts}); | } elsif ( $req->{archive} ) { | |||
} elsif ( $req->{archive} ) { | $progName = "BackupPC_archive"; | |||
$progName = "BackupPC_archive"; | $type = "archive"; | |||
$type = "archive"; | push(@args, $req->{user}, $req->{host}, $req->{reqFileName}); | |||
$mutexDelta = 1; | ||||
push(@args, "-m", $req->{user}, $req->{host}, $req->{reqFileName}); | ||||
} else { | } else { | |||
$progName = "BackupPC_dump"; | $progName = "BackupPC_dump"; | |||
$type = "backup"; | $type = "backup"; | |||
$mutexDelta = -1; | ||||
push(@args, "-I") if ( $req->{backupType} eq "autoIncr" ); | push(@args, "-I") if ( $req->{backupType} eq "autoIncr" ); | |||
push(@args, "-F") if ( $req->{backupType} eq "autoFull" ); | push(@args, "-F") if ( $req->{backupType} eq "autoFull" ); | |||
push(@args, "-i") if ( $req->{backupType} eq "doIncr" ); | push(@args, "-i") if ( $req->{backupType} eq "doIncr" ); | |||
push(@args, "-f") if ( $req->{backupType} eq "doFull" ); | push(@args, "-f") if ( $req->{backupType} eq "doFull" ); | |||
push(@args, "-d") if ( $req->{backupType} eq "dhcpPoll" ); | push(@args, "-d") if ( $req->{backupType} eq "dhcpPoll" ); | |||
push(@args, "-e") if ( $req->{dumpExpire} ); | push(@args, "-e") if ( $req->{dumpExpire} ); | |||
push(@args, "-m"); | ||||
push(@args, $host); | push(@args, $host); | |||
} | } | |||
my $pid = open(FH, "-|"); | my $pid = open(FH, "-|"); | |||
if ( !defined($pid) ) { | if ( !defined($pid) ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "can't fork to run $progName for $host, r | |||
"can't fork to run $progName for $host, request by $user\n"); | equest by $user\n"); | |||
close(FH); | close(FH); | |||
next; | next; | |||
} | } | |||
if ( !$pid ) { | if ( !$pid ) { | |||
setpgrp 0,0; | setpgrp 0, 0; | |||
unless ( exec( "$BinDir/$progName", @args ) ) { | unless ( exec("$BinDir/$progName", @args) ) { | |||
print( LOG $bpc->timeStamp, "can't exec $progName for $host\n" ) | print(LOG $bpc->timeStamp, "can't exec $progName for $host\n"); | |||
; | ||||
exit(0); | exit(0); | |||
} | } | |||
} | } | |||
$Jobs{$host}{pid} = $pid; | $Jobs{$host}{pid} = $pid; | |||
$Jobs{$host}{fh} = *FH; | $Jobs{$host}{fh} = *FH; | |||
$Jobs{$host}{fn} = fileno(FH); | $Jobs{$host}{fn} = fileno(FH); | |||
$Jobs{$host}{dhcp} = 1 if ( $req->{backupType} eq "dhcpPoll" ); | $Jobs{$host}{dhcp} = 1 if ( $req->{backupType} eq "dhcpPoll" ); | |||
vec($FDread, $Jobs{$host}{fn}, 1) = 1; | vec($FDread, $Jobs{$host}{fn}, 1) = 1; | |||
$Jobs{$host}{startTime} = time; | $Jobs{$host}{startTime} = time; | |||
$Jobs{$host}{reqTime} = $req->{reqTime}; | $Jobs{$host}{reqTime} = $req->{reqTime}; | |||
$Jobs{$host}{userReq} = $req->{userReq}; | $Jobs{$host}{userReq} = $req->{userReq}; | |||
$Jobs{$host}{cmd} = $bpc->execCmd2ShellCmd($progName, @args); | $Jobs{$host}{cmd} = $bpc->execCmd2ShellCmd($progName, @args); | |||
$Jobs{$host}{user} = $user; | $Jobs{$host}{user} = $user; | |||
$Jobs{$host}{type} = $type; | $Jobs{$host}{type} = $type; | |||
$Jobs{$host}{mutexDelta} = $mutexDelta; | $Status->{$host}{userReq} = $req->{userReq} | |||
$HostMutex{$host} += $mutexDelta; | if ( defined($Hosts->{$host}) ); | |||
$HostMutexCurrJob{$host} = $progName; | ||||
$Status->{$host}{userReq} = $req->{userReq} | ||||
if ( defined($Hosts->{$host}) ); | ||||
if ( !$Jobs{$host}{dhcp} ) { | if ( !$Jobs{$host}{dhcp} ) { | |||
$Status->{$host}{state} = "Status_".$type."_starting"; | $Status->{$host}{state} = "Status_" . $type . "_starting"; | |||
$Status->{$host}{activeJob} = 1; | $Status->{$host}{activeJob} = 1; | |||
$Status->{$host}{startTime} = time; | $Status->{$host}{startTime} = time; | |||
$Status->{$host}{endTime} = ""; | $Status->{$host}{endTime} = ""; | |||
} | } | |||
} | } | |||
} | } | |||
############################################################################ | ############################################################################ | |||
# Main_Select() | # Main_Select() | |||
# | # | |||
# If necessary, figure out when to next wakeup based on $Conf{WakeupSchedule}, | # If necessary, figure out when to next wakeup based on $Conf{WakeupSchedule}, | |||
# and then do a select() to wait for the next thing to happen | # and then do a select() to wait for the next thing to happen | |||
# (timeout, signal, someone sends a message, child dies etc). | # (timeout, signal, someone sends a message, child dies etc). | |||
############################################################################ | ############################################################################ | |||
sub Main_Select | sub Main_Select | |||
{ | { | |||
if ( $NextWakeup <= 0 ) { | if ( $NextWakeup <= 0 ) { | |||
# | # | |||
# Figure out when to next wakeup based on $Conf{WakeupSchedule}. | # Figure out when to next wakeup based on $Conf{WakeupSchedule}. | |||
# | # | |||
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) | my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localt | |||
= localtime(time); | ime(time); | |||
my($currHours) = $hour + $min / 60 + $sec / 3600; | my($currHours) = $hour + $min / 60 + $sec / 3600; | |||
if ( $bpc->ConfigMTime() != $Info->{ConfigModTime} ) { | if ( $bpc->ConfigMTime() != $Info->{ConfigModTime} ) { | |||
ServerReload("Re-read config file because mtime changed"); | ServerReload("Re-read config file because mtime changed"); | |||
} | } | |||
my $delta = -1; | my $delta = -1; | |||
foreach my $t ( @{$Conf{WakeupSchedule} || [0..23]} ) { | foreach my $t ( @{$Conf{WakeupSchedule} || [0 .. 23]} ) { | |||
next if ( $t < 0 || $t > 24 ); | next if ( $t < 0 || $t > 24 ); | |||
my $tomorrow = $t + 24; | my $tomorrow = $t + 24; | |||
if ( $delta < 0 | if ( $delta < 0 || ($tomorrow - $currHours > 0 && $delta > $tomorrow | |||
|| ($tomorrow - $currHours > 0 | - $currHours) ) { | |||
&& $delta > $tomorrow - $currHours) ) { | $delta = $tomorrow - $currHours; | |||
$delta = $tomorrow - $currHours; | ||||
$FirstWakeup = $t == $Conf{WakeupSchedule}[0]; | $FirstWakeup = $t == $Conf{WakeupSchedule}[0]; | |||
} | } | |||
if ( $delta < 0 | if ( $delta < 0 || ($t - $currHours > 0 && $delta > $t - $currHours) | |||
|| ($t - $currHours > 0 && $delta > $t - $currHours) ) { | ) { | |||
$delta = $t - $currHours; | $delta = $t - $currHours; | |||
$FirstWakeup = $t == $Conf{WakeupSchedule}[0]; | $FirstWakeup = $t == $Conf{WakeupSchedule}[0]; | |||
} | } | |||
} | } | |||
$NextWakeup = time + $delta * 3600; | $NextWakeup = time + $delta * 3600; | |||
$Info->{nextWakeup} = $NextWakeup; | $Info->{nextWakeup} = $NextWakeup; | |||
print(LOG $bpc->timeStamp, "Next wakeup is ", | print(LOG $bpc->timeStamp, "Next wakeup is ", $bpc->timeStamp($NextWakeu | |||
$bpc->timeStamp($NextWakeup, 1), "\n"); | p, 1), "\n"); | |||
} | } | |||
# | # | |||
# Call select(), waiting until either a signal, a timeout, | # Call select(), waiting until either a signal, a timeout, | |||
# any output from our jobs, or any messages from clients | # any output from our jobs, or any messages from clients | |||
# connected via tcp. | # connected via tcp. | |||
# select() is where we (hopefully) spend most of our time blocked... | # select() is where we (hopefully) spend most of our time blocked... | |||
# | # | |||
my $timeout = $NextWakeup - time; | my $timeout = $NextWakeup - time; | |||
$timeout = 1 if ( $timeout <= 0 ); | $timeout = 1 if ( $timeout <= 0 ); | |||
my $ein = $FDread; | my $ein = $FDread; | |||
skipping to change at line 946 | skipping to change at line 935 | |||
# Process timeouts | # Process timeouts | |||
# | # | |||
return if ( time < $NextWakeup || $NextWakeup <= 0 ); | return if ( time < $NextWakeup || $NextWakeup <= 0 ); | |||
$NextWakeup = 0; | $NextWakeup = 0; | |||
if ( $FirstWakeup ) { | if ( $FirstWakeup ) { | |||
# | # | |||
# This is the first wakeup after midnight. Do log file aging | # This is the first wakeup after midnight. Do log file aging | |||
# and various house keeping. | # and various house keeping. | |||
# | # | |||
$FirstWakeup = 0; | $FirstWakeup = 0; | |||
printf(LOG "%s24hr disk usage: %d%% max, %d%% recent; inode: %d%% max, % | printf(LOG "%s24hr disk usage: %d%% max, %d%% recent; inode: %d%% max, % | |||
d%% recent; " | d%% recent; %d skipped hosts\n", | |||
. " %d skipped hosts\n", $bpc->timeStamp, | $bpc->timeStamp, $Info->{DUDailyMax}, $Info->{DUlastV | |||
$Info->{DUDailyMax}, $Info->{DUlastValue}, | alue}, | |||
$Info->{DUInodeDailyMax}, $Info->{DUInodelastValue}, | $Info->{DUInodeDailyMax}, $Info->{DUInodelastValue}, $Info->{DUDaily | |||
$Info->{DUDailySkipHostCnt}); | SkipHostCnt} | |||
); | ||||
$Info->{DUDailyMaxReset} = 1; | $Info->{DUDailyMaxReset} = 1; | |||
$Info->{DUDailyMaxPrev} = $Info->{DUDailyMax}; | $Info->{DUDailyMaxPrev} = $Info->{DUDailyMax}; | |||
$Info->{DUInodeDailyMaxPrev} = $Info->{DUInodeDailyMax}; | $Info->{DUInodeDailyMaxPrev} = $Info->{DUInodeDailyMax}; | |||
$Info->{DUDailySkipHostCntPrev} = $Info->{DUDailySkipHostCnt}; | $Info->{DUDailySkipHostCntPrev} = $Info->{DUDailySkipHostCnt}; | |||
$Info->{DUDailySkipHostCnt} = 0; | $Info->{DUDailySkipHostCnt} = 0; | |||
my $lastLog = $Conf{MaxOldLogFiles} - 1; | my $lastLog = $Conf{MaxOldLogFiles} - 1; | |||
if ( -f "$LogDir/LOG.$lastLog" ) { | if ( -f "$LogDir/LOG.$lastLog" ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Removing $LogDir/LOG.$lastLog\n"); | |||
"Removing $LogDir/LOG.$lastLog\n"); | ||||
unlink("$LogDir/LOG.$lastLog"); | unlink("$LogDir/LOG.$lastLog"); | |||
} | } | |||
if ( -f "$LogDir/LOG.$lastLog.z" ) { | if ( -f "$LogDir/LOG.$lastLog.z" ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Removing $LogDir/LOG.$lastLog.z\n"); | |||
"Removing $LogDir/LOG.$lastLog.z\n"); | ||||
unlink("$LogDir/LOG.$lastLog.z"); | unlink("$LogDir/LOG.$lastLog.z"); | |||
} | } | |||
print(LOG $bpc->timeStamp, "Aging LOG files, LOG -> LOG.0 -> " | print(LOG $bpc->timeStamp, "Aging LOG files, LOG -> LOG.0 -> LOG.1 -> .. | |||
. "LOG.1 -> ... -> LOG.$lastLog\n"); | . -> LOG.$lastLog\n"); | |||
close(STDERR); # dup of LOG | close(STDERR); # dup of LOG | |||
close(STDOUT); # dup of LOG | close(STDOUT); # dup of LOG | |||
close(LOG); | close(LOG); | |||
for ( my $i = $lastLog - 1 ; $i >= 0 ; $i-- ) { | for ( my $i = $lastLog - 1 ; $i >= 0 ; $i-- ) { | |||
my $j = $i + 1; | my $j = $i + 1; | |||
rename("$LogDir/LOG.$i", "$LogDir/LOG.$j") | rename("$LogDir/LOG.$i", "$LogDir/LOG.$j") | |||
if ( -f "$LogDir/LOG.$i" ); | if ( -f "$LogDir/LOG.$i" ); | |||
rename("$LogDir/LOG.$i.z", "$LogDir/LOG.$j.z") | rename("$LogDir/LOG.$i.z", "$LogDir/LOG.$j.z") | |||
if ( -f "$LogDir/LOG.$i.z" ); | if ( -f "$LogDir/LOG.$i.z" ); | |||
} | } | |||
# | # | |||
# Compress the log file LOG -> LOG.0.z (if enabled). | # Compress the log file LOG -> LOG.0.z (if enabled). | |||
# Otherwise, just rename LOG -> LOG.0. | # Otherwise, just rename LOG -> LOG.0. | |||
# | # | |||
BackupPC::XS::compressCopy("$LogDir/LOG", | BackupPC::XS::compressCopy("$LogDir/LOG", "$LogDir/LOG.0.z", "$LogDir/LO | |||
"$LogDir/LOG.0.z", | G.0", $Conf{CompressLevel}, 1); | |||
"$LogDir/LOG.0", | ||||
$Conf{CompressLevel}, 1); | ||||
LogFileOpen(); | LogFileOpen(); | |||
# | # | |||
# Remember to run the nightly script when the next CmdQueue | # Remember to run the nightly script when the next CmdQueue | |||
# job is done. | # job is done. | |||
# | # | |||
if ( $RunNightlyWhenIdle == 2 || $BackupPCNightlyJobs > 0 ) { | if ( $RunNightlyWhenIdle == 2 || $BackupPCNightlyJobs > 0 ) { | |||
print(LOG $bpc->timeStamp, "BackupPC_nightly is still running after | print(LOG $bpc->timeStamp, | |||
24 hours!!" | "BackupPC_nightly is still running after 24 hours!!" | |||
. " You should adjust the config settings; | . " You should adjust the config settings; Skipping this run\ | |||
Skipping this run\n"); | n" | |||
); | ||||
} else { | } else { | |||
$RunNightlyWhenIdle = 1; | $RunNightlyWhenIdle = 1; | |||
} | } | |||
} | } | |||
# | # | |||
# Write out the current status and then queue all the PCs | # Write out the current status and then queue all the PCs | |||
# | # | |||
HostsUpdate(0); | HostsUpdate(0); | |||
StatusWrite(); | StatusWrite(); | |||
%BgQueueOn = () if ( @BgQueue == 0 ); | %BgQueueOn = () if ( @BgQueue == 0 ); | |||
%UserQueueOn = () if ( @UserQueue == 0 ); | %UserQueueOn = () if ( @UserQueue == 0 ); | |||
%CmdQueueOn = () if ( @CmdQueue == 0 ); | %CmdQueueOn = () if ( @CmdQueue == 0 ); | |||
QueueAllPCs(); | QueueAllPCs(); | |||
} | } | |||
############################################################################ | ############################################################################ | |||
# Main_Check_Job_Messages($fdRead) | # Main_Check_Job_Messages($fdRead) | |||
# | # | |||
# Check if select() says we have bytes waiting from any of our jobs. | # Check if select() says we have bytes waiting from any of our jobs. | |||
# Handle each of the messages when complete (newline terminated). | # Handle each of the messages when complete (newline terminated). | |||
############################################################################ | ############################################################################ | |||
sub Main_Check_Job_Messages | sub Main_Check_Job_Messages | |||
skipping to change at line 1028 | skipping to change at line 1013 | |||
my($fdRead) = @_; | my($fdRead) = @_; | |||
foreach my $host ( keys(%Jobs) ) { | foreach my $host ( keys(%Jobs) ) { | |||
next if ( !vec($fdRead, $Jobs{$host}{fn}, 1) ); | next if ( !vec($fdRead, $Jobs{$host}{fn}, 1) ); | |||
my $mesg; | my $mesg; | |||
# | # | |||
# do a last check to make sure there is something to read so | # do a last check to make sure there is something to read so | |||
# we are absolutely sure we won't block. | # we are absolutely sure we won't block. | |||
# | # | |||
vec(my $readMask, $Jobs{$host}{fn}, 1) = 1; | vec(my $readMask, $Jobs{$host}{fn}, 1) = 1; | |||
if ( !select($readMask, undef, undef, 0.0) ) { | if ( !select($readMask, undef, undef, 0.0) ) { | |||
print(LOG $bpc->timeStamp, "Botch in Main_Check_Job_Messages:" | print(LOG $bpc->timeStamp, "Botch in Main_Check_Job_Messages: nothin | |||
. " nothing to read from $host. Debug dump:\n"); | g to read from $host. Debug dump:\n"); | |||
my($dump) = Data::Dumper->new( | my($dump) = | |||
[ \%Clients, \%Jobs, \$FDread, \$fdRead], | Data::Dumper->new([\%Clients, \%Jobs, \$FDread, \$fdRead], [qw(*Cl | |||
[qw(*Clients *Jobs *FDread *fdRead)]); | ients *Jobs *FDread *fdRead)]); | |||
$dump->Indent(1); | $dump->Indent(1); | |||
print(LOG $dump->Dump); | print(LOG $dump->Dump); | |||
next; | next; | |||
} | } | |||
my $nbytes = sysread($Jobs{$host}{fh}, $mesg, 1024); | my $nbytes = sysread($Jobs{$host}{fh}, $mesg, 1024); | |||
$Jobs{$host}{mesg} .= $mesg if ( $nbytes > 0 ); | $Jobs{$host}{mesg} .= $mesg if ( $nbytes > 0 ); | |||
# | # | |||
# Process any complete lines of output from this jobs. | # Process any complete lines of output from this jobs. | |||
# Any output to STDOUT or STDERR from the children is processed here. | # Any output to STDOUT or STDERR from the children is processed here. | |||
# | # | |||
while ( $Jobs{$host}{mesg} =~ /(.*?)[\n\r]+(.*)/s ) { | while ( $Jobs{$host}{mesg} =~ /(.*?)[\n\r]+(.*)/s ) { | |||
$mesg = $1; | $mesg = $1; | |||
$Jobs{$host}{mesg} = $2; | $Jobs{$host}{mesg} = $2; | |||
if ( $Jobs{$host}{dhcp} ) { | if ( $mesg =~ /^started (.*) dump, share=(.*)/ ) { | |||
if ( $mesg =~ /^DHCP (\S+) (\S+)/ ) { | ||||
my $newHost = $bpc->uriUnesc($2); | ||||
if ( defined($Jobs{$newHost}) ) { | ||||
print(LOG $bpc->timeStamp, | ||||
"Backup on $newHost is already running\n"); | ||||
kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); | ||||
$nbytes = 0; | ||||
last; | ||||
} | ||||
$Jobs{$host}{dhcpHostIP} = $host; | ||||
$Status->{$newHost}{dhcpHostIP} = $host; | ||||
$Jobs{$newHost} = $Jobs{$host}; | ||||
delete($Jobs{$host}); | ||||
$host = $newHost; | ||||
$Status->{$host}{state} = "Status_backup_starting"; | ||||
$Status->{$host}{activeJob} = 1; | ||||
$Status->{$host}{startTime} = $Jobs{$host}{startTime}; | ||||
$Status->{$host}{endTime} = ""; | ||||
$Jobs{$host}{dhcp} = 0; | ||||
} else { | ||||
print(LOG $bpc->timeStamp, "dhcp $host: $mesg\n"); | ||||
} | ||||
} elsif ( $mesg =~ /^started (.*) dump, share=(.*)/ ) { | ||||
$Jobs{$host}{type} = $1; | $Jobs{$host}{type} = $1; | |||
$Jobs{$host}{shareName} = $2; | $Jobs{$host}{shareName} = $2; | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, | |||
"Started $1 backup on $host (pid=$Jobs{$host}{pid}", | "Started $1 backup on $host (pid=$Jobs{$host}{pid}", | |||
$Jobs{$host}{dhcpHostIP} | $Jobs{$host}{dhcpHostIP} ? ", dhcp=$Jobs{$host}{dhcpHostIP}" | |||
? ", dhcp=$Jobs{$host}{dhcpHostIP}" : "", | : "", | |||
", share=$Jobs{$host}{shareName})\n"); | ", share=$Jobs{$host}{shareName})\n" | |||
); | ||||
$Status->{$host}{state} = "Status_backup_in_progress"; | $Status->{$host}{state} = "Status_backup_in_progress"; | |||
$Status->{$host}{reason} = ""; | $Status->{$host}{reason} = ""; | |||
$Status->{$host}{type} = $1; | $Status->{$host}{type} = $1; | |||
$Status->{$host}{startTime} = time; | $Status->{$host}{startTime} = time; | |||
$Status->{$host}{deadCnt} = 0; | $Status->{$host}{deadCnt} = 0; | |||
$Status->{$host}{aliveCnt}++; | $Status->{$host}{aliveCnt}++; | |||
$Status->{$host}{dhcpCheckCnt}-- | $Status->{$host}{dhcpCheckCnt}-- | |||
if ( defined $Status->{$host}{dhcpCheckCnt} && $ Status->{$host}{dhcpCheckCnt} > 0 ); | if ( defined $Status->{$host}{dhcpCheckCnt} && $Status->{$host }{dhcpCheckCnt} > 0 ); | |||
} elsif ( $mesg =~ /^xferPids *(.*)/ ) { | } elsif ( $mesg =~ /^xferPids *(.*)/ ) { | |||
$Jobs{$host}{xferPid} = $1; | $Jobs{$host}{xferPid} = $1; | |||
} elsif ( $mesg =~ /^__bpc_progress_state__ (.*)/ ) { | } elsif ( $mesg =~ /^__bpc_progress_state__ (.*)/ ) { | |||
$Jobs{$host}{xferState} = $1; | $Jobs{$host}{xferState} = $1; | |||
$Jobs{$host}{xferFileCnt} = ""; | $Jobs{$host}{xferFileCnt} = ""; | |||
} elsif ( $mesg =~ /^__bpc_progress_fileCnt__ (.*)/ ) { | } elsif ( $mesg =~ /^__bpc_progress_fileCnt__ (.*)/ ) { | |||
$Jobs{$host}{xferFileCnt} = $1; | $Jobs{$host}{xferFileCnt} = $1; | |||
} elsif ( $mesg =~ /^started_restore/ ) { | } elsif ( $mesg =~ /^started_restore/ ) { | |||
$Jobs{$host}{type} = "restore"; | $Jobs{$host}{type} = "restore"; | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Started restore on $host (pid=$Jobs{ | |||
"Started restore on $host" | $host}{pid})\n"); | |||
. " (pid=$Jobs{$host}{pid})\n"); | ||||
$Status->{$host}{state} = "Status_restore_in_progress"; | $Status->{$host}{state} = "Status_restore_in_progress"; | |||
$Status->{$host}{reason} = ""; | $Status->{$host}{reason} = ""; | |||
$Status->{$host}{type} = "restore"; | $Status->{$host}{type} = "restore"; | |||
$Status->{$host}{startTime} = time; | $Status->{$host}{startTime} = time; | |||
$Status->{$host}{deadCnt} = 0; | $Status->{$host}{deadCnt} = 0; | |||
$Status->{$host}{aliveCnt}++; | $Status->{$host}{aliveCnt}++; | |||
} elsif ( $mesg =~ /^started_archive/ ) { | } elsif ( $mesg =~ /^started_archive/ ) { | |||
$Jobs{$host}{type} = "archive"; | $Jobs{$host}{type} = "archive"; | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Started archive on $host (pid=$Jobs{ | |||
"Started archive on $host" | $host}{pid})\n"); | |||
. " (pid=$Jobs{$host}{pid})\n"); | ||||
$Status->{$host}{state} = "Status_archive_in_progress"; | $Status->{$host}{state} = "Status_archive_in_progress"; | |||
$Status->{$host}{reason} = ""; | $Status->{$host}{reason} = ""; | |||
$Status->{$host}{type} = "archive"; | $Status->{$host}{type} = "archive"; | |||
$Status->{$host}{startTime} = time; | $Status->{$host}{startTime} = time; | |||
$Status->{$host}{deadCnt} = 0; | $Status->{$host}{deadCnt} = 0; | |||
$Status->{$host}{aliveCnt}++; | $Status->{$host}{aliveCnt}++; | |||
} elsif ( $mesg =~ /^(full|incr) backup complete/ ) { | } elsif ( $mesg =~ /^(full|incr) backup complete/ ) { | |||
print(LOG $bpc->timeStamp, "Finished $1 backup on $host\n"); | print(LOG $bpc->timeStamp, "Finished $1 backup on $host\n"); | |||
$Status->{$host}{reason} = "Reason_backup_done"; | $Status->{$host}{reason} = "Reason_backup_done"; | |||
delete($Status->{$host}{error}); | delete($Status->{$host}{error}); | |||
delete($Status->{$host}{errorTime}); | delete($Status->{$host}{errorTime}); | |||
$Status->{$host}{endTime} = time; | $Status->{$host}{endTime} = time; | |||
$Status->{$host}{lastGoodBackupTime} = time; | $Status->{$host}{lastGoodBackupTime} = time; | |||
} elsif ( $mesg =~ /^backups disabled/ ) { | } elsif ( $mesg =~ /^backups disabled/ ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Ignoring old backup error on $host\n | |||
"Ignoring old backup error on $host\n"); | "); | |||
$Status->{$host}{reason} = "Reason_backup_done"; | $Status->{$host}{reason} = "Reason_backup_done"; | |||
delete($Status->{$host}{error}); | delete($Status->{$host}{error}); | |||
delete($Status->{$host}{errorTime}); | delete($Status->{$host}{errorTime}); | |||
$Status->{$host}{endTime} = time; | $Status->{$host}{endTime} = time; | |||
} elsif ( $mesg =~ /^restore complete/ ) { | } elsif ( $mesg =~ /^restore complete/ ) { | |||
print(LOG $bpc->timeStamp, "Finished restore on $host\n"); | print(LOG $bpc->timeStamp, "Finished restore on $host\n"); | |||
$Status->{$host}{reason} = "Reason_restore_done"; | $Status->{$host}{reason} = "Reason_restore_done"; | |||
delete($Status->{$host}{error}); | delete($Status->{$host}{error}); | |||
delete($Status->{$host}{errorTime}); | delete($Status->{$host}{errorTime}); | |||
$Status->{$host}{endTime} = time; | $Status->{$host}{endTime} = time; | |||
} elsif ( $mesg =~ /^archive complete/ ) { | } elsif ( $mesg =~ /^archive complete/ ) { | |||
print(LOG $bpc->timeStamp, "Finished archive on $host\n"); | print(LOG $bpc->timeStamp, "Finished archive on $host\n"); | |||
$Status->{$host}{reason} = "Reason_archive_done"; | $Status->{$host}{reason} = "Reason_archive_done"; | |||
delete($Status->{$host}{error}); | delete($Status->{$host}{error}); | |||
delete($Status->{$host}{errorTime}); | delete($Status->{$host}{errorTime}); | |||
$Status->{$host}{endTime} = time; | $Status->{$host}{endTime} = time; | |||
} elsif ( $mesg =~ /^nothing to do/ ) { | } elsif ( $mesg =~ /^nothing to do/ ) { | |||
if ( $Status->{$host}{reason} ne "Reason_backup_failed" | if ( $Status->{$host}{reason} ne "Reason_backup_failed" | |||
&& $Status->{$host}{reason} ne "Reason_restore_failed" ) | && $Status->{$host}{reason} ne "Reason_restore_failed" ) { | |||
{ | $Status->{$host}{state} = "Status_idle"; | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{reason} = "Reason_nothing_to_do"; | |||
$Status->{$host}{reason} = "Reason_nothing_to_do"; | $Status->{$host}{startTime} = time; | |||
$Status->{$host}{startTime} = time; | } | |||
} | ||||
$Status->{$host}{dhcpCheckCnt}-- | $Status->{$host}{dhcpCheckCnt}-- | |||
if ( defined $Status->{$host}{dhcpCheckCnt} && $ | if ( defined $Status->{$host}{dhcpCheckCnt} && $Status->{$host | |||
Status->{$host}{dhcpCheckCnt} > 0 ); | }{dhcpCheckCnt} > 0 ); | |||
} elsif ( $mesg =~ /^no ping response/ | } elsif ( $mesg =~ /^no ping response/ || $mesg =~ /^ping too slow/ | |||
|| $mesg =~ /^ping too slow/ | || $mesg =~ /^host not found/ ) { | |||
|| $mesg =~ /^host not found/ ) { | $Status->{$host}{state} = "Status_idle"; | |||
$Status->{$host}{state} = "Status_idle"; | if ( $Status->{$host}{userReq} | |||
if ( $Status->{$host}{userReq} | || $Status->{$host}{reason} ne "Reason_backup_failed" | |||
|| $Status->{$host}{reason} ne "Reason_backup_failed" | || $Status->{$host}{error} =~ /^aborted by user/ ) { | |||
|| $Status->{$host}{error} =~ /^aborted by user/ ) { | ||||
$Status->{$host}{reason} = "Reason_no_ping"; | $Status->{$host}{reason} = "Reason_no_ping"; | |||
$Status->{$host}{error} = $mesg; | $Status->{$host}{error} = $mesg; | |||
$Status->{$host}{startTime} = time; | $Status->{$host}{startTime} = time; | |||
} | } | |||
$Status->{$host}{deadCnt}++; | $Status->{$host}{deadCnt}++; | |||
if ( $Status->{$host}{deadCnt} >= $Conf{BlackoutBadPingLimit} ) { | if ( $Status->{$host}{deadCnt} >= $Conf{BlackoutBadPingLimit} ) { | |||
$Status->{$host}{aliveCnt} = 0; | $Status->{$host}{aliveCnt} = 0; | |||
} | } | |||
} elsif ( $mesg =~ /^dump failed: (.*)/ ) { | } elsif ( $mesg =~ /^dump failed: (.*)/ ) { | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{state} = "Status_idle"; | |||
$Status->{$host}{error} = $1; | $Status->{$host}{error} = $1; | |||
$Status->{$host}{errorTime} = time; | $Status->{$host}{errorTime} = time; | |||
$Status->{$host}{endTime} = time; | $Status->{$host}{endTime} = time; | |||
if ( $Status->{$host}{reason} | if ( $Status->{$host}{reason} eq "Reason_backup_canceled_by_user | |||
eq "Reason_backup_canceled_by_user" ) { | " ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Backup canceled on $host ($1)\n" | |||
"Backup canceled on $host ($1)\n"); | ); | |||
} else { | } else { | |||
$Status->{$host}{reason} = "Reason_backup_failed"; | $Status->{$host}{reason} = "Reason_backup_failed"; | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Backup failed on $host ($1)\n"); | |||
"Backup failed on $host ($1)\n"); | } | |||
} | ||||
} elsif ( $mesg =~ /^restore failed: (.*)/ ) { | } elsif ( $mesg =~ /^restore failed: (.*)/ ) { | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{state} = "Status_idle"; | |||
$Status->{$host}{error} = $1; | $Status->{$host}{error} = $1; | |||
$Status->{$host}{errorTime} = time; | $Status->{$host}{errorTime} = time; | |||
$Status->{$host}{endTime} = time; | $Status->{$host}{endTime} = time; | |||
if ( $Status->{$host}{reason} | if ( $Status->{$host}{reason} eq "Reason_restore_canceled_by_use | |||
eq "Reason_restore_canceled_by_user" ) { | r" ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Restore canceled on $host ($1)\n | |||
"Restore canceled on $host ($1)\n"); | "); | |||
} else { | } else { | |||
$Status->{$host}{reason} = "Reason_restore_failed"; | $Status->{$host}{reason} = "Reason_restore_failed"; | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Restore failed on $host ($1)\n") | |||
"Restore failed on $host ($1)\n"); | ; | |||
} | } | |||
} elsif ( $mesg =~ /^archive failed: (.*)/ ) { | } elsif ( $mesg =~ /^archive failed: (.*)/ ) { | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{state} = "Status_idle"; | |||
$Status->{$host}{error} = $1; | $Status->{$host}{error} = $1; | |||
$Status->{$host}{errorTime} = time; | $Status->{$host}{errorTime} = time; | |||
$Status->{$host}{endTime} = time; | $Status->{$host}{endTime} = time; | |||
if ( $Status->{$host}{reason} | if ( $Status->{$host}{reason} eq "Reason_archive_canceled_by_use | |||
eq "Reason_archive_canceled_by_user" ) { | r" ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Archive canceled on $host ($1)\n | |||
"Archive canceled on $host ($1)\n"); | "); | |||
} else { | } else { | |||
$Status->{$host}{reason} = "Reason_archive_failed"; | $Status->{$host}{reason} = "Reason_archive_failed"; | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Archive failed on $host ($1)\n") | |||
"Archive failed on $host ($1)\n"); | ; | |||
} | } | |||
} elsif ( $mesg =~ /^log\s+(.*)/ ) { | } elsif ( $mesg =~ /^log\s+(.*)/ ) { | |||
print(LOG $bpc->timeStamp, "$1\n"); | print(LOG $bpc->timeStamp, "$1\n"); | |||
} elsif ( $mesg =~ /^BackupPC_stats (\d+) = (.*)/ ) { | } elsif ( $mesg =~ /^BackupPC_stats (\d+) = (.*)/ ) { | |||
my $chunk = int($1 / 16); | my $chunk = int($1 / 16); | |||
my @f = split(/,/, $2); | my @f = split(/,/, $2); | |||
$Info->{pool}{$f[0]}[$chunk]{FileCnt} += $f[1]; | $Info->{pool}{$f[0]}[$chunk]{FileCnt} += $f[1]; | |||
$Info->{pool}{$f[0]}[$chunk]{DirCnt} += $f[2]; | $Info->{pool}{$f[0]}[$chunk]{DirCnt} += $f[2]; | |||
$Info->{pool}{$f[0]}[$chunk]{Kb} += $f[3]; | $Info->{pool}{$f[0]}[$chunk]{Kb} += $f[3]; | |||
$Info->{pool}{$f[0]}[$chunk]{KbRm} += $f[4]; | $Info->{pool}{$f[0]}[$chunk]{KbRm} += $f[4]; | |||
$Info->{pool}{$f[0]}[$chunk]{FileCntRm} += $f[5]; | $Info->{pool}{$f[0]}[$chunk]{FileCntRm} += $f[5]; | |||
$Info->{pool}{$f[0]}[$chunk]{FileCntRep} += $f[6]; | $Info->{pool}{$f[0]}[$chunk]{FileCntRep} += $f[6]; | |||
$Info->{pool}{$f[0]}[$chunk]{FileRepMax} = $f[7] | $Info->{pool}{$f[0]}[$chunk]{FileRepMax} = $f[7] | |||
if ( $Info->{pool}{$f[0]}[$chunk]{FileRepMax} < $f[7] ); | if ( $Info->{pool}{$f[0]}[$chunk]{FileRepMax} < $f[7] ); | |||
$Info->{pool}{$f[0]}[$chunk]{FileCntRename} += $f[8]; | $Info->{pool}{$f[0]}[$chunk]{FileCntRename} += $f[8]; | |||
$Info->{pool}{$f[0]}[$chunk]{FileLinkMax} = $f[9] | $Info->{pool}{$f[0]}[$chunk]{FileLinkMax} = $f[9] | |||
if ( $Info->{pool}{$f[0]}[$chunk]{FileLinkMax} < $f[9] ) | if ( $Info->{pool}{$f[0]}[$chunk]{FileLinkMax} < $f[9] ); | |||
; | ||||
$Info->{pool}{$f[0]}[$chunk]{FileLinkTotal} += $f[10]; | $Info->{pool}{$f[0]}[$chunk]{FileLinkTotal} += $f[10]; | |||
$Info->{pool}{$f[0]}[$chunk]{Time} = time; | $Info->{pool}{$f[0]}[$chunk]{Time} = time; | |||
} elsif ( $mesg =~ /^BackupPC_stats4 (\d+) = (.*)/ ) { | } elsif ( $mesg =~ /^BackupPC_stats4 (\d+) = (.*)/ ) { | |||
my $chunk = int($1 / 8); | my $chunk = int($1 / 8); | |||
my @f = split(/,/, $2); | my @f = split(/,/, $2); | |||
$Info->{pool}{$f[0]}[$chunk]{FileCnt} += $f[1]; | $Info->{pool}{$f[0]}[$chunk]{FileCnt} += $f[1]; | |||
$Info->{pool}{$f[0]}[$chunk]{DirCnt} += $f[2]; | $Info->{pool}{$f[0]}[$chunk]{DirCnt} += $f[2]; | |||
$Info->{pool}{$f[0]}[$chunk]{Kb} += $f[3]; | $Info->{pool}{$f[0]}[$chunk]{Kb} += $f[3]; | |||
$Info->{pool}{$f[0]}[$chunk]{KbRm} += $f[4]; | $Info->{pool}{$f[0]}[$chunk]{KbRm} += $f[4]; | |||
$Info->{pool}{$f[0]}[$chunk]{FileCntRm} += $f[5]; | $Info->{pool}{$f[0]}[$chunk]{FileCntRm} += $f[5]; | |||
$Info->{pool}{$f[0]}[$chunk]{FileCntRep} += $f[6]; | $Info->{pool}{$f[0]}[$chunk]{FileCntRep} += $f[6]; | |||
$Info->{pool}{$f[0]}[$chunk]{FileRepMax} = $f[7] | $Info->{pool}{$f[0]}[$chunk]{FileRepMax} = $f[7] | |||
if ( $Info->{pool}{$f[0]}[$chunk]{FileRepMax} < $f[7] ); | if ( $Info->{pool}{$f[0]}[$chunk]{FileRepMax} < $f[7] ); | |||
$Info->{pool}{$f[0]}[$chunk]{FileLinkMax} = $f[8] | $Info->{pool}{$f[0]}[$chunk]{FileLinkMax} = $f[8] | |||
if ( $Info->{pool}{$f[0]}[$chunk]{FileLinkMax} < $f[8] ) | if ( $Info->{pool}{$f[0]}[$chunk]{FileLinkMax} < $f[8] ); | |||
; | ||||
$Info->{pool}{$f[0]}[$chunk]{FileLinkTotal} += $f[9]; | $Info->{pool}{$f[0]}[$chunk]{FileLinkTotal} += $f[9]; | |||
$Info->{pool}{$f[0]}[$chunk]{Time} = time; | $Info->{pool}{$f[0]}[$chunk]{Time} = time; | |||
} elsif ( $mesg =~ /^BackupPC_nightly lock_off/ ) { | } elsif ( $mesg =~ /^BackupPC_nightly lock_off/ ) { | |||
$BackupPCNightlyLock--; | $BackupPCNightlyLock--; | |||
if ( $BackupPCNightlyLock == 0 ) { | if ( $BackupPCNightlyLock == 0 ) { | |||
# | # | |||
# This means the last BackupPC_nightly is done with | # This means the last BackupPC_nightly is done with | |||
# the pool clean, so it's ok to start running regular | # the pool clean, so it's ok to start running regular | |||
# backups again. But starting in 3.0 regular jobs | # backups again. But starting in 3.0 regular jobs | |||
# are decoupled from BackupPC_nightly. | # are decoupled from BackupPC_nightly. | |||
# | # | |||
$RunNightlyWhenIdle = 0; | $RunNightlyWhenIdle = 0; | |||
} | } | |||
} elsif ( $mesg =~ /^processState\s+(.+)/ ) { | } elsif ( $mesg =~ /^processState\s+(.+)/ ) { | |||
$Jobs{$host}{processState} = $1; | $Jobs{$host}{processState} = $1; | |||
} elsif ( $mesg =~ /^link\s+(.+)/ ) { | } elsif ( $mesg =~ /^link\s+(.+)/ ) { | |||
my($h) = $1; | my($h) = $1; | |||
$Status->{$h}{needLink} = 1; | $Status->{$h}{needLink} = 1; | |||
} else { | } else { | |||
print(LOG $bpc->timeStamp, "$host: $mesg\n"); | print(LOG $bpc->timeStamp, "$host: $mesg\n"); | |||
} | } | |||
} | } | |||
# | # | |||
# shut down the client connection if we read EOF | # shut down the client connection if we read EOF | |||
# | # | |||
if ( $nbytes <= 0 ) { | if ( $nbytes <= 0 ) { | |||
close($Jobs{$host}{fh}); | close($Jobs{$host}{fh}); | |||
vec($FDread, $Jobs{$host}{fn}, 1) = 0; | vec($FDread, $Jobs{$host}{fn}, 1) = 0; | |||
if ( $CmdJob eq $host || $bpc->isAdminJob($host) ) { | if ( $CmdJob eq $host || $bpc->isAdminJob($host) ) { | |||
my $cmd = $Jobs{$host}{cmd}; | my $cmd = $Jobs{$host}{cmd}; | |||
$cmd =~ s/$BinDir\///g; | $cmd =~ s/$BinDir\///g; | |||
print(LOG $bpc->timeStamp, "Finished $host ($cmd)\n"); | print(LOG $bpc->timeStamp, "Finished $host ($cmd)\n"); | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{state} = "Status_idle"; | |||
$Status->{$host}{endTime} = time; | $Status->{$host}{endTime} = time; | |||
if ( $cmd =~ /^BackupPC_nightly\s/ ) { | if ( $cmd =~ /^BackupPC_nightly\s/ ) { | |||
$BackupPCNightlyJobs--; | $BackupPCNightlyJobs--; | |||
#print(LOG $bpc->timeStamp, "BackupPC_nightly done; now" | #print(LOG $bpc->timeStamp, "BackupPC_nightly done; now" | |||
# . " have $BackupPCNightlyJobs running\n"); | # . " have $BackupPCNightlyJobs running\n"); | |||
if ( $BackupPCNightlyJobs <= 0 ) { | if ( $BackupPCNightlyJobs <= 0 ) { | |||
# | # | |||
# Last BackupPC_nightly has finished | # Last BackupPC_nightly has finished | |||
# | # | |||
$BackupPCNightlyJobs = 0; | $BackupPCNightlyJobs = 0; | |||
$RunNightlyWhenIdle = 0; | $RunNightlyWhenIdle = 0; | |||
$CmdJob = ""; | $CmdJob = ""; | |||
# | # | |||
# Update the list of currently running jobs, so | # Update the list of currently running jobs, so | |||
# we can detect if a single backup spans two | # we can detect if a single backup spans two | |||
# consecutive BackupPC_nightly runs. | # consecutive BackupPC_nightly runs. | |||
# | # | |||
$Info->{RunningPIDs} = {}; | $Info->{RunningPIDs} = {}; | |||
foreach my $host ( keys(%Jobs) ) { | foreach my $host ( keys(%Jobs) ) { | |||
my $pid = $Jobs{$host}{pid}; | my $pid = $Jobs{$host}{pid}; | |||
$Info->{RunningPIDs}{$pid} = 1 if ( $pid ne "" ); | $Info->{RunningPIDs}{$pid} = 1 if ( $pid ne "" ); | |||
} | } | |||
# | # | |||
# Combine the 16 per-directory results for the | # Combine the 16 per-directory results for the | |||
# old (pool) and new pool (pool4) | # old (pool) and new pool (pool4) | |||
# | # | |||
for my $p ( qw(pool cpool pool4 cpool4) ) { | for my $p ( qw(pool cpool pool4 cpool4) ) { | |||
$Info->{"${p}FileCnt"} = 0; | $Info->{"${p}FileCnt"} = 0; | |||
$Info->{"${p}DirCnt"} = 0; | $Info->{"${p}DirCnt"} = 0; | |||
$Info->{"${p}Kb"} = 0; | $Info->{"${p}Kb"} = 0; | |||
$Info->{"${p}KbRm"} = 0; | $Info->{"${p}KbRm"} = 0; | |||
$Info->{"${p}FileCntRm"} = 0; | $Info->{"${p}FileCntRm"} = 0; | |||
$Info->{"${p}FileCntRep"} = 0; | $Info->{"${p}FileCntRep"} = 0; | |||
$Info->{"${p}FileRepMax"} = 0; | $Info->{"${p}FileRepMax"} = 0; | |||
if ( $p =~ /^c?pool$/ ) { | if ( $p =~ /^c?pool$/ ) { | |||
$Info->{"${p}FileCntRename"} = 0 | $Info->{"${p}FileCntRename"} = 0; | |||
} else { | } else { | |||
delete $Info->{"${p}FileCntRename"}; | delete $Info->{"${p}FileCntRename"}; | |||
} | } | |||
$Info->{"${p}FileLinkMax"} = 0; | $Info->{"${p}FileLinkMax"} = 0; | |||
$Info->{"${p}Time"} = 0; | $Info->{"${p}Time"} = 0; | |||
for ( my $i = 0 ; $i < 16 ; $i++ ) { | for ( my $i = 0 ; $i < 16 ; $i++ ) { | |||
$Info->{"${p}FileCnt"} | $Info->{"${p}FileCnt"} += $Info->{pool}{$p}[$ | |||
+= $Info->{pool}{$p}[$i]{FileCnt}; | i]{FileCnt}; | |||
$Info->{"${p}DirCnt"} | $Info->{"${p}DirCnt"} += $Info->{pool}{$p}[$ | |||
+= $Info->{pool}{$p}[$i]{DirCnt}; | i]{DirCnt}; | |||
$Info->{"${p}Kb"} | $Info->{"${p}Kb"} += $Info->{pool}{$p}[$ | |||
+= $Info->{pool}{$p}[$i]{Kb}; | i]{Kb}; | |||
$Info->{"${p}KbRm"} | $Info->{"${p}KbRm"} += $Info->{pool}{$p}[$ | |||
+= $Info->{pool}{$p}[$i]{KbRm}; | i]{KbRm}; | |||
$Info->{"${p}FileCntRm"} | $Info->{"${p}FileCntRm"} += $Info->{pool}{$p}[$ | |||
+= $Info->{pool}{$p}[$i]{FileCntRm}; | i]{FileCntRm}; | |||
$Info->{"${p}FileCntRep"} | $Info->{"${p}FileCntRep"} += $Info->{pool}{$p}[$ | |||
+= $Info->{pool}{$p}[$i]{FileCntRep}; | i]{FileCntRep}; | |||
$Info->{"${p}FileRepMax"} | $Info->{"${p}FileRepMax"} = $Info->{pool}{$p}[$i | |||
= $Info->{pool}{$p}[$i]{FileRepMax} | ]{FileRepMax} | |||
if ( $Info->{"${p}FileRepMax"} < | if ( $Info->{"${p}FileRepMax"} < $Info->{pool} | |||
$Info->{pool}{$p}[$i]{FileRepMax} ) | {$p}[$i]{FileRepMax} ); | |||
; | $Info->{"${p}FileCntRename"} += $Info->{pool}{$p | |||
$Info->{"${p}FileCntRename"} | }[$i]{FileCntRename} | |||
+= $Info->{pool}{$p}[$i]{FileCntRename} | if ( $p =~ /^c?pool$/ ); | |||
if ( $p =~ /^c?pool$/ ); | $Info->{"${p}FileLinkMax"} = $Info->{pool}{$p}[$ | |||
$Info->{"${p}FileLinkMax"} | i]{FileLinkMax} | |||
= $Info->{pool}{$p}[$i]{FileLinkMax} | if ( $Info->{"${p}FileLinkMax"} < $Info->{pool | |||
if ( $Info->{"${p}FileLinkMax"} < | }{$p}[$i]{FileLinkMax} ); | |||
$Info->{pool}{$p}[$i]{FileLinkMax} ) | $Info->{"${p}Time"} = $Info->{pool}{$p}[$i]{Time | |||
; | } | |||
$Info->{"${p}Time"} = $Info->{pool}{$p}[$i]{Time} | if ( $Info->{"${p}Time"} < $Info->{pool}{$p}[$ | |||
if ( $Info->{"${p}Time"} < | i]{Time} ); | |||
$Info->{pool}{$p}[$i]{Time} ); | } | |||
} | printf(LOG "%s%s nightly clean removed %d files of s | |||
printf(LOG "%s%s nightly clean removed %d files of" | ize %.2fGB\n", | |||
. " size %.2fGB\n", | $bpc->timeStamp, ucfirst($p), | |||
$bpc->timeStamp, ucfirst($p), | $Info->{"${p}FileCntRm"}, | |||
$Info->{"${p}FileCntRm"}, | $Info->{"${p}KbRm"} / (1000 * 1024) | |||
$Info->{"${p}KbRm"} / (1000 * 1024)); | ); | |||
printf(LOG "%s%s is %.2fGB, %d files (%d repeated, " | printf(LOG "%s%s is %.2fGB, %d files (%d repeated, " | |||
. "%d max chain, %d max links), %d directories | . "%d max chain, %d max links), %d directories | |||
\n", | \n", | |||
$bpc->timeStamp, ucfirst($p), | $bpc->timeStamp, ucfirst($p), | |||
$Info->{"${p}Kb"} / (1000 * 1024), | $Info->{"${p}Kb"} / (1000 * 1024), $Info->{"${p} | |||
$Info->{"${p}FileCnt"}, $Info->{"${p}FileCnt | FileCnt"}, | |||
Rep"}, | $Info->{"${p}FileCntRep"}, $Info->{"${p} | |||
$Info->{"${p}FileRepMax"}, | FileRepMax"}, | |||
$Info->{"${p}FileLinkMax"}, $Info->{"${p}Dir | $Info->{"${p}FileLinkMax"}, $Info->{"${p} | |||
Cnt"}); | DirCnt"} | |||
} | ); | |||
} | ||||
# | # | |||
# Queue bin/BackupPC_rrdUpdate so that the pool size gra phs | # Queue bin/BackupPC_rrdUpdate so that the pool size gra phs | |||
# can be updated | # can be updated | |||
# | # | |||
unshift(@CmdQueue, { | unshift( | |||
@CmdQueue, | ||||
{ | ||||
host => $bpc->adminJob(-1), | host => $bpc->adminJob(-1), | |||
user => "BackupPC", | user => "BackupPC", | |||
reqTime => time, | reqTime => time, | |||
cmd => ["$BinDir/BackupPC_rrdUpdate"], | cmd => ["$BinDir/BackupPC_rrdUpdate"], | |||
}); | } | |||
); | ||||
$CmdQueueOn{$bpc->adminJob(-1)} = 1; | $CmdQueueOn{$bpc->adminJob(-1)} = 1; | |||
} | } | |||
} else { | } else { | |||
$CmdJob = ""; | $CmdJob = ""; | |||
} | } | |||
} elsif ( defined($Status->{$host}) ) { | } elsif ( defined($Status->{$host}) ) { | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{state} = "Status_idle"; | |||
} | } | |||
$Status->{$host}{activeJob} = 0 if ( defined($Status->{$host}) ); | $Status->{$host}{activeJob} = 0 if ( defined($Status->{$host}) ); | |||
$HostMutex{$host} -= $Jobs{$host}{mutexDelta}; | ||||
delete($Status->{$host}) if ( $Jobs{$host}{dhcp} ); | delete($Status->{$host}) if ( $Jobs{$host}{dhcp} ); | |||
delete($Jobs{$host}); | delete($Jobs{$host}); | |||
} | } | |||
} | } | |||
# | # | |||
# When we are idle (empty Jobs, CmdQueue, BgQueue, UserQueue) we | # When we are idle (empty Jobs, CmdQueue, BgQueue, UserQueue) we | |||
# do a pass over $Status updating the deadCnt and aliveCnt for | # do a pass over $Status updating the deadCnt and aliveCnt for | |||
# DHCP hosts. The reason we need to do this later is we can't | # DHCP hosts. The reason we need to do this later is we can't | |||
# be sure whether a DHCP host is alive or dead until we have passed | # be sure whether a DHCP host is alive or dead until we have passed | |||
# over all the DHCP pool. | # over all the DHCP pool. | |||
skipping to change at line 1412 | skipping to change at line 1346 | |||
my($fdRead) = @_; | my($fdRead) = @_; | |||
foreach my $client ( keys(%Clients) ) { | foreach my $client ( keys(%Clients) ) { | |||
next if ( !vec($fdRead, $Clients{$client}{fn}, 1) ); | next if ( !vec($fdRead, $Clients{$client}{fn}, 1) ); | |||
my($mesg, $host); | my($mesg, $host); | |||
# | # | |||
# do a last check to make sure there is something to read so | # do a last check to make sure there is something to read so | |||
# we are absolutely sure we won't block. | # we are absolutely sure we won't block. | |||
# | # | |||
vec(my $readMask, $Clients{$client}{fn}, 1) = 1; | vec(my $readMask, $Clients{$client}{fn}, 1) = 1; | |||
if ( !select($readMask, undef, undef, 0.0) ) { | if ( !select($readMask, undef, undef, 0.0) ) { | |||
print(LOG $bpc->timeStamp, "Botch in Main_Check_Client_Messages:" | print(LOG $bpc->timeStamp, | |||
. " nothing to read from $client. Debug dump:\n"); | "Botch in Main_Check_Client_Messages: nothing to read from $clie | |||
my($dump) = Data::Dumper->new( | nt. Debug dump:\n"); | |||
[ \%Clients, \%Jobs, \$FDread, \$fdRead], | my($dump) = | |||
[qw(*Clients *Jobs *FDread *fdRead)]); | Data::Dumper->new([\%Clients, \%Jobs, \$FDread, \$fdRead], [qw(*Cl | |||
ients *Jobs *FDread *fdRead)]); | ||||
$dump->Indent(1); | $dump->Indent(1); | |||
print(LOG $dump->Dump); | print(LOG $dump->Dump); | |||
next; | next; | |||
} | } | |||
my $nbytes = sysread($Clients{$client}{fh}, $mesg, 1024); | my $nbytes = sysread($Clients{$client}{fh}, $mesg, 1024); | |||
$Clients{$client}{mesg} .= $mesg if ( $nbytes > 0 ); | $Clients{$client}{mesg} .= $mesg if ( $nbytes > 0 ); | |||
# | # | |||
# Process any complete lines received from this client. | # Process any complete lines received from this client. | |||
# | # | |||
while ( $Clients{$client}{mesg} =~ /(.*?)[\n\r]+(.*)/s ) { | while ( $Clients{$client}{mesg} =~ /(.*?)[\n\r]+(.*)/s ) { | |||
my $reply; | my $reply; | |||
my $cmd = $1; | my $cmd = $1; | |||
$Clients{$client}{mesg} = $2; | $Clients{$client}{mesg} = $2; | |||
# | # | |||
# Authenticate the message by checking the MD5 digest | # Authenticate the message by checking the MD5 digest | |||
# | # | |||
my $md5 = Digest::MD5->new; | my $md5 = Digest::MD5->new; | |||
if ( $cmd !~ /^(.{22}) (.*)/ | if ( | |||
|| ($md5->add($Clients{$client}{seed} | $cmd !~ /^(.{22}) (.*)/ | |||
. $Clients{$client}{mesgCnt} | || ( | |||
. $Conf{ServerMesgSecret} . $2), | $md5->add($Clients{$client}{seed} . $Clients{$client}{mesgCn | |||
$md5->b64digest ne $1) ) { | t} . $Conf{ServerMesgSecret} . $2), | |||
print(LOG $bpc->timeStamp, "Corrupted message '$cmd' from" | $md5->b64digest ne $1 | |||
. " client '$Clients{$client}{clientName}':" | ) | |||
. " shutting down client connection\n"); | ) { | |||
print(LOG $bpc->timeStamp, | ||||
"Corrupted message '$cmd' from" | ||||
. " client '$Clients{$client}{clientName}':" | ||||
. " shutting down client connection\n" | ||||
); | ||||
$nbytes = 0; | $nbytes = 0; | |||
last; | last; | |||
} | } | |||
$Clients{$client}{mesgCnt}++; | $Clients{$client}{mesgCnt}++; | |||
$cmd = decode_utf8($2); | $cmd = decode_utf8($2); | |||
if ( $cmd =~ /^stop (\S+)\s+(\S+)\s+(\S*)/ ) { | if ( $cmd =~ /^stop (\S+)\s+(\S+)\s+(\S*)/ ) { | |||
$host = $1; | $host = $1; | |||
my $user = $2; | my $user = $2; | |||
my $backoff = $3; | my $backoff = $3; | |||
$host = $bpc->uriUnesc($host); | $host = $bpc->uriUnesc($host); | |||
if ( $CmdJob ne $host && defined($Status->{$host}) | if ( $CmdJob ne $host && defined($Status->{$host}) && defined($J | |||
&& defined($Jobs{$host}) ) { | obs{$host}) ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, | |||
"Stopping current $Jobs{$host}{type} of $host," | "Stopping current $Jobs{$host}{type} of $host, request b | |||
. " request by $user (backoff=$backoff)\n"); | y $user (backoff=$backoff)\n"); | |||
kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); | kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); | |||
# | # | |||
# Don't close the pipe now; wait until the child | # Don't close the pipe now; wait until the child | |||
# really exits later. Otherwise close() will | # really exits later. Otherwise close() will | |||
# block until the child has exited. | # block until the child has exited. | |||
# old code: | # old code: | |||
##vec($FDread, $Jobs{$host}{fn}, 1) = 0; | ##vec($FDread, $Jobs{$host}{fn}, 1) = 0; | |||
##close($Jobs{$host}{fh}); | ##close($Jobs{$host}{fh}); | |||
##delete($Jobs{$host}); | ##delete($Jobs{$host}); | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{state} = "Status_idle"; | |||
if ( $Jobs{$host}{type} eq "restore" ) { | if ( $Jobs{$host}{type} eq "restore" ) { | |||
$Status->{$host}{reason} | $Status->{$host}{reason} = "Reason_restore_canceled_by_u | |||
= "Reason_restore_canceled_by_user"; | ser"; | |||
} elsif ( $Jobs{$host}{type} eq "archive" ) { | } elsif ( $Jobs{$host}{type} eq "archive" ) { | |||
$Status->{$host}{reason} | $Status->{$host}{reason} = "Reason_archive_canceled_by_u | |||
= "Reason_archive_canceled_by_user"; | ser"; | |||
} else { | } else { | |||
$Status->{$host}{reason} | $Status->{$host}{reason} = "Reason_backup_canceled_by_us | |||
= "Reason_backup_canceled_by_user"; | er"; | |||
} | } | |||
$Status->{$host}{activeJob} = 0; | $Status->{$host}{activeJob} = 0; | |||
$Status->{$host}{startTime} = time; | $Status->{$host}{startTime} = time; | |||
$reply = "ok: $Jobs{$host}{type} of $host canceled"; | $reply = "ok: $Jobs{$host}{type} of $ho st canceled"; | |||
} elsif ( $BgQueueOn{$host} || $UserQueueOn{$host} ) { | } elsif ( $BgQueueOn{$host} || $UserQueueOn{$host} ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, | |||
"Stopping pending backup of $host," | "Stopping pending backup of $host, request by $user (bac | |||
. " request by $user (backoff=$backoff)\n"); | koff=$backoff)\n"); | |||
@BgQueue = grep($_->{host} ne $host, @BgQueue); | @BgQueue = grep($_->{host} ne $host, @BgQueue); | |||
@UserQueue = grep($_->{host} ne $host, @UserQueue); | @UserQueue = grep($_->{host} ne $host, @UserQueue); | |||
$BgQueueOn{$host} = $UserQueueOn{$host} = 0; | $BgQueueOn{$host} = $UserQueueOn{$host} = 0; | |||
$reply = "ok: pending backup of $host canceled"; | $reply = "ok: pending backup of $host canceled"; | |||
} else { | } else { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, | |||
"Nothing to do for stop backup of $host," | "Nothing to do for stop backup of $host, request by $use | |||
. " request by $user (backoff=$backoff)\n"); | r (backoff=$backoff)\n"); | |||
$reply = "ok: no backup was pending or running"; | $reply = "ok: no backup was pending or running"; | |||
} | } | |||
if ( defined($Status->{$host}) && $backoff ne "" ) { | if ( defined($Status->{$host}) && $backoff ne "" ) { | |||
if ( $backoff > 0 ) { | if ( $backoff > 0 ) { | |||
$Status->{$host}{backoffTime} = time + $backoff * 3600; | $Status->{$host}{backoffTime} = time + $backoff * 3600; | |||
} else { | } else { | |||
delete($Status->{$host}{backoffTime}); | delete($Status->{$host}{backoffTime}); | |||
} | } | |||
} | } | |||
} elsif ( $cmd =~ /^backup all$/ ) { | } elsif ( $cmd =~ /^backup all$/ ) { | |||
QueueAllPCs(); | QueueAllPCs(); | |||
} elsif ( $cmd =~ /^BackupPC_nightly run$/ ) { | } elsif ( $cmd =~ /^BackupPC_nightly run$/ ) { | |||
if ( $BackupPCNightlyJobs > 0 ) { | if ( $BackupPCNightlyJobs > 0 ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Ignoring request to run BackupPC | |||
"Ignoring request to run BackupPC_nightly: already | _nightly: already running\n"); | |||
running\n"); | ||||
} else { | } else { | |||
$RunNightlyWhenIdle = 1; | $RunNightlyWhenIdle = 1; | |||
} | } | |||
} elsif ( $cmd =~ /^backup (\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ ) { | } elsif ( $cmd =~ /^backup (\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ ) { | |||
my $hostIP = $1; | my $hostIP = $1; | |||
$host = $2; | $host = $2; | |||
my $user = $3; | my $user = $3; | |||
my $backupType = $4; | my $backupType = $4; | |||
$host = $bpc->uriUnesc($host); | $host = $bpc->uriUnesc($host); | |||
$hostIP = $bpc->uriUnesc($hostIP); | $hostIP = $bpc->uriUnesc($hostIP); | |||
if ( !defined($Hosts->{$host}) ) { | if ( !defined($Hosts->{$host}) ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "User $user requested backup of u | |||
"User $user requested backup of unknown host" | nknown host $host\n"); | |||
. " $host\n"); | ||||
$reply = "error: unknown host $host"; | $reply = "error: unknown host $host"; | |||
} else { | } else { | |||
# | # | |||
# Handle numeric backupType for backward compatibility | # Handle numeric backupType for backward compatibility | |||
# (technically -1 is a new feature for auto) | # (technically -1 is a new feature for auto) | |||
# | # | |||
$backupType = 'auto' if ( $backupType eq '-1' ); | $backupType = 'auto' if ( $backupType eq '-1' ); | |||
$backupType = 'doIncr' if ( $backupType eq '0' ); | $backupType = 'doIncr' if ( $backupType eq '0' ); | |||
$backupType = 'doFull' if ( $backupType eq '1' ); | $backupType = 'doFull' if ( $backupType eq '1' ); | |||
if ( $backupType !~ /^doIncr|doFull|autoIncr|autoFull|auto$/ i ) { | if ( $backupType !~ /^doIncr|doFull|autoIncr|autoFull|auto$/ i ) { | |||
$reply = "error: unknown backup type $backupType"; | $reply = "error: unknown backup type $backupType"; | |||
} else { | } else { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "User $user requested backup | |||
"User $user requested backup of $host" | of $host ($hostIP)\n"); | |||
. " ($hostIP)\n"); | ||||
if ( $BgQueueOn{$hostIP} ) { | if ( $BgQueueOn{$hostIP} ) { | |||
@BgQueue = grep($_->{host} ne $hostIP, @BgQueue); | @BgQueue = grep($_->{host} ne $hostIP, @BgQueue); | |||
$BgQueueOn{$hostIP} = 0; | $BgQueueOn{$hostIP} = 0; | |||
} | } | |||
if ( $UserQueueOn{$hostIP} ) { | if ( $UserQueueOn{$hostIP} ) { | |||
@UserQueue = grep($_->{host} ne $hostIP, @UserQueue) ; | @UserQueue = grep($_->{host} ne $hostIP, @UserQueue) ; | |||
$UserQueueOn{$hostIP} = 0; | $UserQueueOn{$hostIP} = 0; | |||
} | } | |||
my $status = QueueOnePC($host, $hostIP, $user, 'user', $ backupType); | my $status = QueueOnePC($host, $hostIP, $user, 'user', $ backupType); | |||
if ( $status == 0 ) { | if ( $status == 0 ) { | |||
$reply = "ok: requested backup of $host ($backupType )"; | $reply = "ok: requested backup of $host ($backupType )"; | |||
} elsif ( $status == 1 ) { | } elsif ( $status == 1 ) { | |||
#should never see this we just dequeued it | #should never see this we just dequeued it | |||
$reply = "warning: $host was already queued." | $reply = "warning: $host was already queued. Ignorin | |||
. " Ignoring this request"; | g this request"; | |||
} elsif ( $status == 2 ) { | } elsif ( $status == 2 ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, | |||
"Disk too full (usage $Info->{DUlastValue}%; | "Disk too full (usage $Info->{DUlastValue}%; | |||
inode $Info->{DUInodelastValue}%)." | inode $Info->{DUInodelastValue}%)." | |||
. " Not queueing backup of $host\n"); | . " Not queueing backup of $host\n" | |||
$reply = "error: disk too full (usage $Info->{DUlast | ); | |||
Value}%; inode $Info->{DUInodelastValue}%)"; | $reply = | |||
"error: disk too full (usage $Info->{DUlastValue}% | ||||
; inode $Info->{DUInodelastValue}%)"; | ||||
$Info->{DUDailySkipHostCnt}++; | $Info->{DUDailySkipHostCnt}++; | |||
} elsif ( $status == 3 ) { | } elsif ( $status == 3 ) { | |||
# should never reach this because | # should never reach this because | |||
# it's set to "user" above | # it's set to "user" above | |||
$reply = "error: unknown queue name"; | $reply = "error: unknown queue name"; | |||
} else { | } else { | |||
$reply = "error: unknown queue status $status"; | $reply = "error: unknown queue status $status"; | |||
if ( $BgQueueOn{$hostIP} || $UserQueueOn{$hostIP} ) { | if ( $BgQueueOn{$hostIP} || $UserQueueOn{$hostIP} ) { | |||
$reply .= ". Host is queued."; | $reply .= ". Host is queued."; | |||
} else { | } else { | |||
$reply .= ". Host is not queued."; | $reply .= ". Host is not queued."; | |||
} | } | |||
} | } | |||
} | } | |||
} | } | |||
} elsif ( $cmd =~ /^archive (\S+)\s+(\S+)\s+(\S+)/ ) { | } elsif ( $cmd =~ /^archive (\S+)\s+(\S+)\s+(\S+)/ ) { | |||
my $user = $1; | my $user = $1; | |||
my $archivehost = $2; | my $archivehost = $2; | |||
my $reqFileName = $3; | my $reqFileName = $3; | |||
$host = $bpc->uriUnesc($archivehost); | $host = $bpc->uriUnesc($archivehost); | |||
if ( !defined($Status->{$host}) ) { | if ( !defined($Status->{$host}) ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "User $user requested archive of | |||
"User $user requested archive of unknown archive | unknown archive host $host"); | |||
host" | ||||
. " $host"); | ||||
$reply = "archive error: unknown archive host $host"; | $reply = "archive error: unknown archive host $host"; | |||
} else { | } else { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "User $user requested archive on | |||
"User $user requested archive on $host" | $host ($host)\n"); | |||
. " ($host)\n"); | ||||
if ( defined($Jobs{$host}) ) { | if ( defined($Jobs{$host}) ) { | |||
$reply = "Archive currently running on $host, please try later"; | $reply = "Archive currently running on $host, please try later"; | |||
} else { | } else { | |||
unshift(@UserQueue, { | unshift( | |||
host => $host, | @UserQueue, | |||
user => $user, | { | |||
host => $host, | ||||
user => $user, | ||||
reqFileName => $reqFileName, | reqFileName => $reqFileName, | |||
reqTime => time, | reqTime => time, | |||
dhcp => 0, | dhcp => 0, | |||
archive => 1, | archive => 1, | |||
userReq => 1, | userReq => 1, | |||
}); | } | |||
); | ||||
$UserQueueOn{$host} = 1; | $UserQueueOn{$host} = 1; | |||
$reply = "ok: requested archive on $host"; | $reply = "ok: requested archive on $host"; | |||
} | } | |||
} | } | |||
} elsif ( $cmd =~ /^restore (\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ ) { | } elsif ( $cmd =~ /^restore (\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ ) { | |||
my $hostIP = $1; | my $hostIP = $1; | |||
$host = $2; | $host = $2; | |||
my $user = $3; | my $user = $3; | |||
my $reqFileName = $4; | my $reqFileName = $4; | |||
$host = $bpc->uriUnesc($host); | $host = $bpc->uriUnesc($host); | |||
$hostIP = $bpc->uriUnesc($hostIP); | $hostIP = $bpc->uriUnesc($hostIP); | |||
if ( !defined($Hosts->{$host}) ) { | if ( !defined($Hosts->{$host}) ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "User $user requested restore to | |||
"User $user requested restore to unknown host" | unknown host $host\n"); | |||
. " $host"); | ||||
$reply = "restore error: unknown host $host"; | $reply = "restore error: unknown host $host"; | |||
} else { | } else { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "User $user requested restore to | |||
"User $user requested restore to $host" | $host ($hostIP)\n"); | |||
. " ($hostIP)\n"); | unshift( | |||
unshift(@UserQueue, { | @UserQueue, | |||
host => $host, | { | |||
hostIP => $hostIP, | host => $host, | |||
reqFileName => $reqFileName, | hostIP => $hostIP, | |||
reqTime => time, | reqFileName => $reqFileName, | |||
dhcp => 0, | reqTime => time, | |||
restore => 1, | dhcp => 0, | |||
userReq => 1, | restore => 1, | |||
}); | userReq => 1, | |||
} | ||||
); | ||||
$UserQueueOn{$host} = 1; | $UserQueueOn{$host} = 1; | |||
if ( defined($Jobs{$host}) ) { | if ( defined($Jobs{$host}) ) { | |||
$reply = "ok: requested restore of $host, but a" | $reply = | |||
. " job is currently running," | "ok: requested restore of $host, but a" | |||
. " so this request will start later"; | . " job is currently running," | |||
. " so this request will start later"; | ||||
} else { | } else { | |||
$reply = "ok: requested restore of $host"; | $reply = "ok: requested restore of $host"; | |||
} | } | |||
} | } | |||
} elsif ( $cmd =~ /^delete (\S+)\s+(\S+)\s+(\d+)\s+(.*)/ ) { | } elsif ( $cmd =~ /^delete (\S+)\s+(\S+)\s+(\d+)\s+(.*)/ ) { | |||
my $user = $1; | my $user = $1; | |||
$host = $bpc->uriUnesc($2); | $host = $bpc->uriUnesc($2); | |||
my $num = $3; | my $num = $3; | |||
my $opts = $4; | my $opts = $4; | |||
if ( !defined($Hosts->{$host}) ) { | if ( !defined($Hosts->{$host}) ) { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "User $user requested delete from | |||
"User $user requested delete from unknown host $h | unknown host $host\n"); | |||
ost"); | ||||
$reply = "delete error: unknown host $host"; | $reply = "delete error: unknown host $host"; | |||
} else { | } else { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "User $user requested delete for | |||
"User $user requested delete for backup #$num fro | backup #$num from $host\n"); | |||
m $host\n"); | unshift( | |||
unshift(@UserQueue, { | @UserQueue, | |||
host => $host, | { | |||
num => $num, | host => $host, | |||
reqTime => time, | num => $num, | |||
delete => 1, | reqTime => time, | |||
opts => $opts, | delete => 1, | |||
userReq => 1, | opts => $opts, | |||
}); | userReq => 1, | |||
} | ||||
); | ||||
$UserQueueOn{$host} = 1; | $UserQueueOn{$host} = 1; | |||
if ( defined($Jobs{$host}) ) { | if ( defined($Jobs{$host}) ) { | |||
$reply = "ok: requested delete from $host, but a" | $reply = | |||
. " job is currently running," | "ok: requested delete from $host, but a" | |||
. " so this request will start later"; | . " job is currently running," | |||
. " so this request will start later"; | ||||
} else { | } else { | |||
$reply = "ok: requested delete from $host"; | $reply = "ok: requested delete from $host"; | |||
} | } | |||
} | } | |||
} elsif ( $cmd =~ /^status\s*(.*)/ ) { | } elsif ( $cmd =~ /^status\s*(.*)/ ) { | |||
my($args) = $1; | my($args) = $1; | |||
my($dump, @values, @names); | my($dump, @values, @names); | |||
foreach my $type ( split(/\s+/, $args) ) { | foreach my $type ( split(/\s+/, $args) ) { | |||
if ( $type =~ /^queues/ ) { | if ( $type =~ /^queues/ ) { | |||
push(@values, \@BgQueue, \@UserQueue, \@CmdQueue); | push(@values, \@BgQueue, \@UserQueue, \@CmdQueue); | |||
push(@names, qw(*BgQueue *UserQueue *CmdQueue)); | push(@names, qw(*BgQueue *UserQueue *CmdQueue)); | |||
} elsif ( $type =~ /^jobs/ ) { | } elsif ( $type =~ /^jobs/ ) { | |||
push(@values, \%Jobs); | push(@values, \%Jobs); | |||
push(@names, qw(*Jobs)); | push(@names, qw(*Jobs)); | |||
} elsif ( $type =~ /^queueLen/ ) { | } elsif ( $type =~ /^queueLen/ ) { | |||
push(@values, { | push( | |||
@values, | ||||
{ | ||||
BgQueue => scalar(@BgQueue), | BgQueue => scalar(@BgQueue), | |||
UserQueue => scalar(@UserQueue), | UserQueue => scalar(@UserQueue), | |||
CmdQueue => scalar(@CmdQueue), | CmdQueue => scalar(@CmdQueue), | |||
}); | } | |||
); | ||||
push(@names, qw(*QueueLen)); | push(@names, qw(*QueueLen)); | |||
} elsif ( $type =~ /^info/ ) { | } elsif ( $type =~ /^info/ ) { | |||
push(@values, $Info); | push(@values, $Info); | |||
push(@names, qw(*Info)); | push(@names, qw(*Info)); | |||
} elsif ( $type =~ /^hosts/ ) { | } elsif ( $type =~ /^hosts/ ) { | |||
push(@values, $Status); | push(@values, $Status); | |||
push(@names, qw(*Status)); | push(@names, qw(*Status)); | |||
} elsif ( $type =~ /^host\((.*)\)/ ) { | } elsif ( $type =~ /^host\((.*)\)/ ) { | |||
my $h = $bpc->uriUnesc($1); | my $h = $bpc->uriUnesc($1); | |||
if ( defined($Status->{$h}) ) { | if ( defined($Status->{$h}) ) { | |||
push(@values, { | push( | |||
@values, | ||||
{ | ||||
%{$Status->{$h}}, | %{$Status->{$h}}, | |||
BgQueueOn => $BgQueueOn{$h}, | BgQueueOn => $BgQueueOn{$h}, | |||
UserQueueOn => $UserQueueOn{$h}, | UserQueueOn => $UserQueueOn{$h}, | |||
CmdQueueOn => $CmdQueueOn{$h}, | CmdQueueOn => $CmdQueueOn{$h}, | |||
Job => $Jobs{$h}, | Job => $Jobs{$h}, | |||
}); | } | |||
); | ||||
push(@names, qw(*StatusHost)); | push(@names, qw(*StatusHost)); | |||
} else { | } else { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Unknown host $h for stat | |||
"Unknown host $h for status request\n"); | us request\n"); | |||
} | } | |||
} else { | } else { | |||
print(LOG $bpc->timeStamp, | print(LOG $bpc->timeStamp, "Unknown status request $type | |||
"Unknown status request $type\n"); | \n"); | |||
} | } | |||
} | } | |||
$dump = Data::Dumper->new(\@values, \@names); | $dump = Data::Dumper->new(\@values, \@names); | |||
$dump->Indent(0); | $dump->Indent(0); | |||
$reply = $dump->Dump; | $reply = $dump->Dump; | |||
} elsif ( $cmd =~ /^log\s+(.*)/ ) { | } elsif ( $cmd =~ /^log\s+(.*)/ ) { | |||
print(LOG $bpc->timeStamp, "$1\n"); | print(LOG $bpc->timeStamp, "$1\n"); | |||
} elsif ( $cmd =~ /^server\s+(\w+)/ ) { | } elsif ( $cmd =~ /^server\s+(\w+)/ ) { | |||
my($type) = $1; | my($type) = $1; | |||
if ( $type eq 'reload' ) { | if ( $type eq 'reload' ) { | |||
ServerReload("Reloading config/host files via CGI request"); | ServerReload("Reloading config/host files via CGI request"); | |||
} elsif ( $type eq 'shutdown' ) { | } elsif ( $type eq 'shutdown' ) { | |||
$reply = "Shutting down...\n"; | $reply = "Shutting down...\n"; | |||
syswrite($Clients{$client}{fh}, $reply, length($reply)); | syswrite($Clients{$client}{fh}, $reply, length($reply)); | |||
ServerShutdown("Server shutting down...", 0); | ServerShutdown("Server shutting down...", 0); | |||
} | } | |||
} elsif ( $cmd =~ /^quit/ || $cmd =~ /^exit/ ) { | } elsif ( $cmd =~ /^quit/ || $cmd =~ /^exit/ ) { | |||
$nbytes = 0; | $nbytes = 0; | |||
last; | last; | |||
} elsif ( $cmd =~ /^hostMutex\s+(\w+)\s+(-?\d+)\s+(.*)/ ) { | } elsif ( $cmd =~ /^hostMutex\s+(\S+)\s+(-?\d+)\s+(.*)/ ) { | |||
$host = $1; | $host = $1; | |||
my $mutexDelta = $2; | my $mutexDelta = $2; | |||
my $progName = $3; | my $progName = $3; | |||
if ( $HostMutex{$host} == 0 || ($HostMutex{$host} > 0 && $mutexD | if ( !defined($Hosts->{$host}) ) { | |||
elta > 0) ) { | print(LOG $bpc->timeStamp, "hostMutex for unknown host $host | |||
ignored: $cmd\n"); | ||||
$reply = "fail: hostMutex: unknown host $host"; | ||||
} elsif ( $HostMutex{$host} == 0 || ($HostMutex{$host} > 0 && $m | ||||
utexDelta > 0) ) { | ||||
push(@{$Clients{$client}{hostMutexList}}, {host => $host, mu texDelta => $mutexDelta}); | push(@{$Clients{$client}{hostMutexList}}, {host => $host, mu texDelta => $mutexDelta}); | |||
$HostMutex{$host} += $mutexDelta; | $HostMutex{$host} += $mutexDelta; | |||
$HostMutexCurrJob{$host} = $progName; | $HostMutexCurrJob{$host} = $progName; | |||
$reply = "ok"; | $reply = "ok"; | |||
} else { | } else { | |||
print(LOG $bpc->timeStamp, "Can't run $progName while $HostM | print(LOG $bpc->timeStamp, | |||
utexCurrJob{$host} is running on host $host\n"); | "Can't run $progName while $HostMutexCurrJob{$host} is r | |||
unning on host $host\n"); | ||||
$reply = "fail: can't run $progName while $HostMutexCurrJob{ $host} is running on host $host"; | $reply = "fail: can't run $progName while $HostMutexCurrJob{ $host} is running on host $host"; | |||
} | } | |||
} elsif ( $cmd =~ /^hostMutexGet\s+(\S+)$/ ) { | ||||
$host = $1; | ||||
if ( !defined($Hosts->{$host}) ) { | ||||
print(LOG $bpc->timeStamp, "hostMutexGet for unknown host $h | ||||
ost ignored: $cmd\n"); | ||||
$reply = "fail: hostMutexGet: unknown host $host"; | ||||
} else { | ||||
$reply = "hostMutexGet: $host == $HostMutex{$host}"; | ||||
} | ||||
} elsif ( $cmd =~ /^DHCP (\S+) (\S+)/ ) { | ||||
my $host = $1; | ||||
my $newHost = $bpc->uriUnesc($2); | ||||
if ( !defined($Jobs{$host}) ) { | ||||
print(LOG $bpc->timeStamp, "DHCP command for invalid job ign | ||||
ored (cmd = $cmd)\n"); | ||||
$reply = "fail: unknown host/job $host"; | ||||
} elsif ( defined($Jobs{$newHost}) ) { | ||||
print(LOG $bpc->timeStamp, "Backup on $newHost is already ru | ||||
nning (DHCP command = $cmd)\n"); | ||||
$reply = "fail: newhost $newHost already has a job running ( | ||||
cmd = $cmd)"; | ||||
} else { | ||||
$Jobs{$host}{dhcpHostIP} = $host; | ||||
$Status->{$newHost}{dhcpHostIP} = $host; | ||||
$Jobs{$newHost} = $Jobs{$host}; | ||||
$HostMutex{$newHost} = $HostMutex{$host}; | ||||
$HostMutexCurrJob{$newHost} = $HostMutexCurrJob{$host}; | ||||
delete($Jobs{$host}); | ||||
delete($HostMutex{$host}); | ||||
delete($HostMutexCurrJob{$host}); | ||||
$host = $newHost; | ||||
$Status->{$host}{state} = "Status_backup_starting"; | ||||
$Status->{$host}{activeJob} = 1; | ||||
$Status->{$host}{startTime} = $Jobs{$host}{startTime}; | ||||
$Status->{$host}{endTime} = ""; | ||||
$Jobs{$host}{dhcp} = 0; | ||||
} | ||||
} else { | } else { | |||
print(LOG $bpc->timeStamp, "Unknown command $cmd\n"); | print(LOG $bpc->timeStamp, "Unknown command $cmd\n"); | |||
$reply = "error: bad command $cmd"; | $reply = "error: bad command $cmd"; | |||
} | } | |||
# | # | |||
# send a reply to the client, at a minimum "ok\n". | # send a reply to the client, at a minimum "ok\n". | |||
# | # | |||
$reply = "ok" if ( !defined $reply ); | $reply = "ok" if ( !defined $reply ); | |||
$reply .= "\n"; | $reply .= "\n"; | |||
syswrite($Clients{$client}{fh}, $reply, length($reply)); | syswrite($Clients{$client}{fh}, $reply, length($reply)); | |||
} | } | |||
# | # | |||
# Detect possible denial-of-service attack from sending a huge line | # Detect possible denial-of-service attack from sending a huge line | |||
# (ie: never terminated). 32K seems to be plenty big enough as | # (ie: never terminated). 32K seems to be plenty big enough as | |||
# a limit. | # a limit. | |||
# | # | |||
if ( length($Clients{$client}{mesg}) > 32 * 1024 ) { | if ( length($Clients{$client}{mesg}) > 32 * 1024 ) { | |||
print(LOG $bpc->timeStamp, "Line too long from client" | print(LOG $bpc->timeStamp, | |||
. " '$Clients{$client}{clientName}':" | "Line too long from client" | |||
. " shutting down client connection\n"); | . " '$Clients{$client}{clientName}':" | |||
. " shutting down client connection\n" | ||||
); | ||||
$nbytes = 0; | $nbytes = 0; | |||
} | } | |||
# | # | |||
# Shut down the client connection if we read EOF | # Shut down the client connection if we read EOF | |||
# | # | |||
if ( $nbytes <= 0 ) { | if ( $nbytes <= 0 ) { | |||
close($Clients{$client}{fh}); | close($Clients{$client}{fh}); | |||
vec($FDread, $Clients{$client}{fn}, 1) = 0; | vec($FDread, $Clients{$client}{fn}, 1) = 0; | |||
if ( defined($Clients{$client}{hostMutexList}) ) { | if ( defined($Clients{$client}{hostMutexList}) ) { | |||
foreach my $d ( @{$Clients{$client}{hostMutexList}} ) { | foreach my $d ( @{$Clients{$client}{hostMutexList}} ) { | |||
skipping to change at line 1782 | skipping to change at line 1754 | |||
} | } | |||
} | } | |||
# | # | |||
# Accept any new connections on each of our listen sockets | # Accept any new connections on each of our listen sockets | |||
# | # | |||
if ( vec($fdRead, fileno(SERVER_UNIX), 1) ) { | if ( vec($fdRead, fileno(SERVER_UNIX), 1) ) { | |||
local(*CLIENT); | local(*CLIENT); | |||
my $paddr = accept(CLIENT, SERVER_UNIX); | my $paddr = accept(CLIENT, SERVER_UNIX); | |||
$ClientConnCnt++; | $ClientConnCnt++; | |||
$Clients{$ClientConnCnt}{clientName} = "unix socket"; | $Clients{$ClientConnCnt}{clientName} = "unix socket"; | |||
$Clients{$ClientConnCnt}{mesg} = ""; | $Clients{$ClientConnCnt}{mesg} = ""; | |||
$Clients{$ClientConnCnt}{fh} = *CLIENT; | $Clients{$ClientConnCnt}{fh} = *CLIENT; | |||
$Clients{$ClientConnCnt}{fn} = fileno(CLIENT); | $Clients{$ClientConnCnt}{fn} = fileno(CLIENT); | |||
vec($FDread, $Clients{$ClientConnCnt}{fn}, 1) = 1; | vec($FDread, $Clients{$ClientConnCnt}{fn}, 1) = 1; | |||
# | # | |||
# Generate and send unique seed for MD5 digests to avoid | # Generate and send unique seed for MD5 digests to avoid | |||
# replay attacks. See BackupPC::Lib::ServerMesg(). | # replay attacks. See BackupPC::Lib::ServerMesg(). | |||
# | # | |||
my $seed = time . ",$ClientConnCnt,$$,0\n"; | my $seed = time . ",$ClientConnCnt,$$,0\n"; | |||
$Clients{$ClientConnCnt}{seed} = $seed; | $Clients{$ClientConnCnt}{seed} = $seed; | |||
$Clients{$ClientConnCnt}{mesgCnt} = 0; | $Clients{$ClientConnCnt}{mesgCnt} = 0; | |||
syswrite($Clients{$ClientConnCnt}{fh}, $seed, length($seed)); | syswrite($Clients{$ClientConnCnt}{fh}, $seed, length($seed)); | |||
} | } | |||
if ( $ServerInetPort > 0 && vec($fdRead, fileno(SERVER_INET), 1) ) { | if ( $ServerInetPort > 0 && vec($fdRead, fileno(SERVER_INET), 1) ) { | |||
local(*CLIENT); | local(*CLIENT); | |||
my $paddr = accept(CLIENT, SERVER_INET); | my $paddr = accept(CLIENT, SERVER_INET); | |||
my($port,$iaddr) = sockaddr_in($paddr); | my($port, $iaddr) = sockaddr_in($paddr); | |||
my $name = gethostbyaddr($iaddr, AF_INET); | my $name = gethostbyaddr($iaddr, AF_INET); | |||
$ClientConnCnt++; | $ClientConnCnt++; | |||
$Clients{$ClientConnCnt}{mesg} = ""; | $Clients{$ClientConnCnt}{mesg} = ""; | |||
$Clients{$ClientConnCnt}{fh} = *CLIENT; | $Clients{$ClientConnCnt}{fh} = *CLIENT; | |||
$Clients{$ClientConnCnt}{fn} = fileno(CLIENT); | $Clients{$ClientConnCnt}{fn} = fileno(CLIENT); | |||
$Clients{$ClientConnCnt}{clientName} = "$name:$port"; | $Clients{$ClientConnCnt}{clientName} = "$name:$port"; | |||
vec($FDread, $Clients{$ClientConnCnt}{fn}, 1) = 1; | vec($FDread, $Clients{$ClientConnCnt}{fn}, 1) = 1; | |||
# | # | |||
# Generate and send unique seed for MD5 digests to avoid | # Generate and send unique seed for MD5 digests to avoid | |||
# replay attacks. See BackupPC::Lib::ServerMesg(). | # replay attacks. See BackupPC::Lib::ServerMesg(). | |||
# | # | |||
my $seed = time . ",$ClientConnCnt,$$,$port\n"; | my $seed = time . ",$ClientConnCnt,$$,$port\n"; | |||
$Clients{$ClientConnCnt}{seed} = $seed; | $Clients{$ClientConnCnt}{seed} = $seed; | |||
$Clients{$ClientConnCnt}{mesgCnt} = 0; | $Clients{$ClientConnCnt}{mesgCnt} = 0; | |||
syswrite($Clients{$ClientConnCnt}{fh}, $seed, length($seed)); | syswrite($Clients{$ClientConnCnt}{fh}, $seed, length($seed)); | |||
skipping to change at line 1844 | skipping to change at line 1816 | |||
sub HostSortCompare | sub HostSortCompare | |||
{ | { | |||
# | # | |||
# Hosts with errors go before hosts without errors | # Hosts with errors go before hosts without errors | |||
# | # | |||
return -1 if ( $Status->{$a}{error} ne "" && $Status->{$b}{error} eq "" ); | return -1 if ( $Status->{$a}{error} ne "" && $Status->{$b}{error} eq "" ); | |||
# | # | |||
# Hosts with no errors go after hosts with errors | # Hosts with no errors go after hosts with errors | |||
# | # | |||
return 1 if ( $Status->{$a}{error} eq "" && $Status->{$b}{error} ne "" ); | return 1 if ( $Status->{$a}{error} eq "" && $Status->{$b}{error} ne "" ); | |||
# | # | |||
# hosts with the older last good backups sort earlier | # hosts with the older last good backups sort earlier | |||
# | # | |||
my $r = $Status->{$a}{lastGoodBackupTime} <=> $Status->{$b}{lastGoodBackupTi me}; | my $r = $Status->{$a}{lastGoodBackupTime} <=> $Status->{$b}{lastGoodBackupTi me}; | |||
return $r if ( $r ); | return $r if ( $r ); | |||
# | # | |||
# Finally, just sort based on host name | # Finally, just sort based on host name | |||
# | # | |||
skipping to change at line 1876 | skipping to change at line 1848 | |||
# $user is the user name, or BackupPC by default | # $user is the user name, or BackupPC by default | |||
# $queue is which queue to use ("bg" by default) | # $queue is which queue to use ("bg" by default) | |||
# $backupType is the backup type (doIncr|doFull|autoIncr|autoFull|auto|dhcpPoll) | # $backupType is the backup type (doIncr|doFull|autoIncr|autoFull|auto|dhcpPoll) | |||
# | # | |||
# Note: starting in 3.2.0, the PC is queued even if it has a current | # Note: starting in 3.2.0, the PC is queued even if it has a current | |||
# job running | # job running | |||
# | # | |||
sub QueueOnePC | sub QueueOnePC | |||
{ | { | |||
my($host, $hostIP, $user, $queue, $backupType) = @_; | my($host, $hostIP, $user, $queue, $backupType) = @_; | |||
my $retVal = 0; | my $retVal = 0; | |||
$user = "BackupPC" if ( $user eq '' ); | $user = "BackupPC" if ( $user eq '' ); | |||
$queue = "bg" if ( $queue eq '' && $user eq 'BackupPC' ); | $queue = "bg" if ( $queue eq '' && $user eq 'BackupPC' ); | |||
$backupType = "auto" if ( $backupType eq '' ); | $backupType = "auto" if ( $backupType eq '' ); | |||
delete($Status->{$host}{backoffTime}) | delete($Status->{$host}{backoffTime}) | |||
if ( defined($Status->{$host}{backoffTime}) | if ( defined($Status->{$host}{backoffTime}) | |||
&& $Status->{$host}{backoffTime} < time ); | && $Status->{$host}{backoffTime} < time ); | |||
return 1 if ( $BgQueueOn{$host} || $UserQueueOn{$host} ); | return 1 if ( $BgQueueOn{$host} || $UserQueueOn{$host} ); | |||
if ( defined($Hosts->{$host}) && $Hosts->{$host}{dhcp} ) { | if ( defined($Hosts->{$host}) && $Hosts->{$host}{dhcp} ) { | |||
$Status->{$host}{dhcpCheckCnt}++; | $Status->{$host}{dhcpCheckCnt}++; | |||
if ( $RunNightlyWhenIdle ) { | if ( $RunNightlyWhenIdle ) { | |||
# | # | |||
# Once per night queue a check for DHCP hosts that just | # Once per night queue a check for DHCP hosts that just | |||
# checks for expired dumps. We need to do this to handle | # checks for expired dumps. We need to do this to handle | |||
# the case when a DHCP host has not been on the network for | # the case when a DHCP host has not been on the network for | |||
# a long time, and some of the old dumps need to be expired. | # a long time, and some of the old dumps need to be expired. | |||
# Normally expiry checks are done by BackupPC_dump only | # Normally expiry checks are done by BackupPC_dump only | |||
# after the DHCP hosts has been detected on the network. | # after the DHCP hosts has been detected on the network. | |||
# | # | |||
unshift(@BgQueue, | unshift( | |||
{host => $hostIP, user => $user, reqTime => time, | @BgQueue, | |||
dhcp => 0, dumpExpire => 1}); | { | |||
host => $hostIP, | ||||
user => $user, | ||||
reqTime => time, | ||||
dhcp => 0, | ||||
dumpExpire => 1 | ||||
} | ||||
); | ||||
$BgQueueOn{$host} = 1; | $BgQueueOn{$host} = 1; | |||
} | } | |||
} else { | } else { | |||
# | # | |||
# this is a fixed ip host or DHCP ip address: queue it | # this is a fixed ip host or DHCP ip address: queue it | |||
# | # | |||
if ( $Info->{DUlastValue} > $Conf{DfMaxUsagePct} | if ( $Info->{DUlastValue} > $Conf{DfMaxUsagePct} || $Info->{DUInodelastV | |||
|| $Info->{DUInodelastValue} > $Conf{DfMaxInodeUsagePct} ) { | alue} > $Conf{DfMaxInodeUsagePct} ) { | |||
# | # | |||
# Since we are out of disk space, instead of queuing | # Since we are out of disk space, instead of queuing | |||
# a regular job, queue an expire check instead. That | # a regular job, queue an expire check instead. That | |||
# way if the admin reduces the number of backups to | # way if the admin reduces the number of backups to | |||
# keep then we will actually delete them. Otherwise | # keep then we will actually delete them. Otherwise | |||
# BackupPC_dump will never run since we have exceeded | # BackupPC_dump will never run since we have exceeded | |||
# the limit. | # the limit. | |||
# | # | |||
$retVal = 2; | $retVal = 2; | |||
unshift(@BgQueue, | unshift(@BgQueue, {host => $hostIP, user => $user, reqTime => time, | |||
{host => $hostIP, user => $user, reqTime => time, dumpExpire => | dumpExpire => 1}); | |||
1}); | ||||
$BgQueueOn{$host} = 1; | $BgQueueOn{$host} = 1; | |||
} elsif( $queue eq 'bg' ) { | } elsif ( $queue eq 'bg' ) { | |||
# | # | |||
# Queue regular background backup | # Queue regular background backup | |||
# | # | |||
unshift(@BgQueue, | unshift(@BgQueue, {host => $hostIP, user => $user, reqTime => time, | |||
{host => $hostIP, user => $user, reqTime => time, backupType => | backupType => $backupType}); | |||
$backupType}); | ||||
$BgQueueOn{$host} = 1; | $BgQueueOn{$host} = 1; | |||
} elsif( $queue eq 'user' ) { | } elsif ( $queue eq 'user' ) { | |||
# | # | |||
# Queue user backup | # Queue user backup | |||
# | # | |||
unshift(@UserQueue, | unshift(@UserQueue, {host => $hostIP, user => $user, reqTime => time | |||
{host => $hostIP, user => $user, reqTime => time, backupType => | , backupType => $backupType}); | |||
$backupType}); | ||||
$UserQueueOn{$host} = 1; | $UserQueueOn{$host} = 1; | |||
} else { | } else { | |||
# unknown $queue type | # unknown $queue type | |||
$retVal = 3; | $retVal = 3; | |||
} | } | |||
} | } | |||
return $retVal; | return $retVal; | |||
} | } | |||
# | # | |||
# Queue all the hosts for backup. This means queuing all the fixed | # Queue all the hosts for backup. This means queuing all the fixed | |||
skipping to change at line 1961 | skipping to change at line 1937 | |||
foreach my $host ( sort HostSortCompare keys(%$Hosts) ) { | foreach my $host ( sort HostSortCompare keys(%$Hosts) ) { | |||
$nSkip++ if ( QueueOnePC($host, $host, 'BackupPC', 'bg', 'auto') == 2 ); | $nSkip++ if ( QueueOnePC($host, $host, 'BackupPC', 'bg', 'auto') == 2 ); | |||
} | } | |||
foreach my $dhcp ( @{$Conf{DHCPAddressRanges}} ) { | foreach my $dhcp ( @{$Conf{DHCPAddressRanges}} ) { | |||
for ( my $i = $dhcp->{first} ; $i <= $dhcp->{last} ; $i++ ) { | for ( my $i = $dhcp->{first} ; $i <= $dhcp->{last} ; $i++ ) { | |||
my $ipAddr = "$dhcp->{ipAddrBase}.$i"; | my $ipAddr = "$dhcp->{ipAddrBase}.$i"; | |||
$nSkip++ if ( QueueOnePC($ipAddr, $ipAddr, 'BackupPC', 'bg', 'dhcpPo ll') == 2 ); | $nSkip++ if ( QueueOnePC($ipAddr, $ipAddr, 'BackupPC', 'bg', 'dhcpPo ll') == 2 ); | |||
} | } | |||
} | } | |||
if ( $nSkip ) { | if ( $nSkip ) { | |||
print(LOG $bpc->timeStamp, "Disk too full (usage $Info->{DUlastValue}%; | print(LOG $bpc->timeStamp, | |||
inode $Info->{DUInodelastValue}%;" | "Disk too full (usage $Info->{DUlastValue}%; inode $Info->{DUIno | |||
. " thres $Conf{DfMaxUsagePct}%/$Conf{DfMaxInod | delastValue}%;" | |||
eUsagePct}%);); skipped $nSkip hosts\n"); | . " thres $Conf{DfMaxUsagePct}%/$Conf{DfMaxInodeUsagePct}%);); ski | |||
pped $nSkip hosts\n" | ||||
); | ||||
$Info->{DUDailySkipHostCnt} += $nSkip; | $Info->{DUDailySkipHostCnt} += $nSkip; | |||
} | } | |||
} | } | |||
# | # | |||
# Read the hosts file, and update Status if any hosts have been | # Read the hosts file, and update Status if any hosts have been | |||
# added or deleted. We also track the mtime so the only need to | # added or deleted. We also track the mtime so the only need to | |||
# update the hosts file on changes. | # update the hosts file on changes. | |||
# | # | |||
# This function is called at startup, SIGHUP, and on each wakeup. | # This function is called at startup, SIGHUP, and on each wakeup. | |||
# It returns 1 on success and undef on failure. | # It returns 1 on success and undef on failure. | |||
# | # | |||
sub HostsUpdate | sub HostsUpdate | |||
{ | { | |||
my($force) = @_; | my($force) = @_; | |||
my $newHosts; | my $newHosts; | |||
# | # | |||
# Nothing to do if we already have the current hosts file | # Nothing to do if we already have the current hosts file | |||
# | # | |||
return 1 if ( !$force && defined($Hosts) | return 1 if ( !$force && defined($Hosts) && $Info->{HostsModTime} == $bpc->H | |||
&& $Info->{HostsModTime} == $bpc->HostsMTime() ); | ostsMTime() ); | |||
if ( !defined($newHosts = $bpc->HostInfoRead()) ) { | if ( !defined($newHosts = $bpc->HostInfoRead()) ) { | |||
print(LOG $bpc->timeStamp, "Can't read hosts file!\n"); | print(LOG $bpc->timeStamp, "Can't read hosts file!\n"); | |||
return; | return; | |||
} | } | |||
print(LOG $bpc->timeStamp, "Reading hosts file\n"); | print(LOG $bpc->timeStamp, "Reading hosts file\n"); | |||
$Hosts = $newHosts; | $Hosts = $newHosts; | |||
$Info->{HostsModTime} = $bpc->HostsMTime(); | $Info->{HostsModTime} = $bpc->HostsMTime(); | |||
# | # | |||
# Now update $Status in case any hosts have been added or deleted | # Now update $Status in case any hosts have been added or deleted | |||
# | # | |||
foreach my $host ( sort(keys(%$Hosts)) ) { | foreach my $host ( sort(keys(%$Hosts)) ) { | |||
next if ( defined($Status->{$host}) ); | next if ( defined($Status->{$host}) ); | |||
$Status->{$host}{state} = "Status_idle"; | $Status->{$host}{state} = "Status_idle"; | |||
print(LOG $bpc->timeStamp, "Added host $host to backup list\n"); | print(LOG $bpc->timeStamp, "Added host $host to backup list\n"); | |||
} | } | |||
foreach my $host ( sort(keys(%$Status)) ) { | foreach my $host ( sort(keys(%$Status)) ) { | |||
next if ( $host eq $bpc->scgiJob | next | |||
|| $bpc->isAdminJob($host) | if ( $host eq $bpc->scgiJob | |||
|| defined($Hosts->{$host}) | || $bpc->isAdminJob($host) | |||
|| defined($Jobs{$host}) | || defined($Hosts->{$host}) | |||
|| $BgQueueOn{$host} | || defined($Jobs{$host}) | |||
|| $UserQueueOn{$host} | || $BgQueueOn{$host} | |||
|| $CmdQueueOn{$host} ); | || $UserQueueOn{$host} | |||
|| $CmdQueueOn{$host} ); | ||||
print(LOG $bpc->timeStamp, "Deleted host $host from backup list\n"); | print(LOG $bpc->timeStamp, "Deleted host $host from backup list\n"); | |||
delete($Status->{$host}); | delete($Status->{$host}); | |||
} | } | |||
return 1; | return 1; | |||
} | } | |||
# | # | |||
# Remember the signal name for later processing | # Remember the signal name for later processing | |||
# | # | |||
sub catch_signal | sub catch_signal | |||
skipping to change at line 2032 | skipping to change at line 2010 | |||
} | } | |||
# | # | |||
# In case we are inside the exit handler, reopen the log file | # In case we are inside the exit handler, reopen the log file | |||
# | # | |||
close(LOG); | close(LOG); | |||
LogFileOpen(); | LogFileOpen(); | |||
print(LOG "Fatal error: unhandled signal $SigName\n"); | print(LOG "Fatal error: unhandled signal $SigName\n"); | |||
unlink("$RunDir/BackupPC.pid"); | unlink("$RunDir/BackupPC.pid"); | |||
confess("Got new signal $SigName... quitting\n"); | confess("Got new signal $SigName... quitting\n"); | |||
} else { | } else { | |||
$SigName = shift; | $SigName = shift; | |||
} | } | |||
} | } | |||
# | # | |||
# Open the log file and point STDOUT and STDERR there too | # Open the log file and point STDOUT and STDERR there too | |||
# | # | |||
sub LogFileOpen | sub LogFileOpen | |||
{ | { | |||
mkpath($LogDir, 0, 0777) if ( !-d $LogDir ); | mkpath($LogDir, 0, 0777) if ( !-d $LogDir ); | |||
open(LOG, ">>$LogDir/LOG") | open(LOG, ">>$LogDir/LOG") | |||
|| die("Can't create LOG file $LogDir/LOG"); | || die("Can't create LOG file $LogDir/LOG"); | |||
close(STDOUT); | close(STDOUT); | |||
close(STDERR); | close(STDERR); | |||
open(STDOUT, ">&LOG"); | open(STDOUT, ">&LOG"); | |||
open(STDERR, ">&LOG"); | open(STDERR, ">&LOG"); | |||
select(LOG); $| = 1; | select(LOG); $| = 1; | |||
select(STDERR); $| = 1; | select(STDERR); $| = 1; | |||
select(STDOUT); $| = 1; | select(STDOUT); $| = 1; | |||
} | } | |||
# | # | |||
skipping to change at line 2071 | skipping to change at line 2049 | |||
# | # | |||
# one-time only: initialize unix-domain socket | # one-time only: initialize unix-domain socket | |||
# | # | |||
if ( !socket(SERVER_UNIX, PF_UNIX, SOCK_STREAM, 0) ) { | if ( !socket(SERVER_UNIX, PF_UNIX, SOCK_STREAM, 0) ) { | |||
print(LOG $bpc->timeStamp, "unix socket() failed: $!; exiting\n"); | print(LOG $bpc->timeStamp, "unix socket() failed: $!; exiting\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
my $sockFile = "$RunDir/BackupPC.sock"; | my $sockFile = "$RunDir/BackupPC.sock"; | |||
unlink($sockFile); | unlink($sockFile); | |||
if ( !bind(SERVER_UNIX, sockaddr_un($sockFile)) ) { | if ( !bind(SERVER_UNIX, sockaddr_un($sockFile)) ) { | |||
print(LOG $bpc->timeStamp, "unix bind($sockFile) failed: $! (does $R | print(LOG $bpc->timeStamp, | |||
unDir exist and writeable by BackupPC?); exiting\n"); | "unix bind($sockFile) failed: $! (does $RunDir exist and writeab | |||
le by BackupPC?); exiting\n"); | ||||
exit(1); | exit(1); | |||
} | } | |||
if ( !listen(SERVER_UNIX, SOMAXCONN) ) { | if ( !listen(SERVER_UNIX, SOMAXCONN) ) { | |||
print(LOG $bpc->timeStamp, "unix listen($sockFile) failed: $!; exiti ng\n"); | print(LOG $bpc->timeStamp, "unix listen($sockFile) failed: $!; exiti ng\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
vec($FDread, fileno(SERVER_UNIX), 1) = 1; | vec($FDread, fileno(SERVER_UNIX), 1) = 1; | |||
} | } | |||
return if ( $ServerInetPort == $Conf{ServerPort} ); | return if ( $ServerInetPort == $Conf{ServerPort} ); | |||
if ( $ServerInetPort > 0 ) { | if ( $ServerInetPort > 0 ) { | |||
skipping to change at line 2095 | skipping to change at line 2074 | |||
} | } | |||
if ( $Conf{ServerPort} > 0 ) { | if ( $Conf{ServerPort} > 0 ) { | |||
# | # | |||
# Setup a socket to listen on $Conf{ServerPort} | # Setup a socket to listen on $Conf{ServerPort} | |||
# | # | |||
my $proto = getprotobyname('tcp'); | my $proto = getprotobyname('tcp'); | |||
if ( !socket(SERVER_INET, PF_INET, SOCK_STREAM, $proto) ) { | if ( !socket(SERVER_INET, PF_INET, SOCK_STREAM, $proto) ) { | |||
print(LOG $bpc->timeStamp, "inet socket() failed: $!; exiting\n"); | print(LOG $bpc->timeStamp, "inet socket() failed: $!; exiting\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
if ( !setsockopt(SERVER_INET, SOL_SOCKET, SO_REUSEADDR, pack("l",1)) ) { | if ( !setsockopt(SERVER_INET, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) ) { | |||
print(LOG $bpc->timeStamp, "setsockopt() failed: $!; exiting\n"); | print(LOG $bpc->timeStamp, "setsockopt() failed: $!; exiting\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
if ( !bind(SERVER_INET, sockaddr_in($Conf{ServerPort}, INADDR_ANY)) ) { | if ( !bind(SERVER_INET, sockaddr_in($Conf{ServerPort}, INADDR_ANY)) ) { | |||
print(LOG $bpc->timeStamp, "inet bind() failed: $!; exiting\n"); | print(LOG $bpc->timeStamp, "inet bind() failed: $!; exiting\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
if ( !listen(SERVER_INET, SOMAXCONN) ) { | if ( !listen(SERVER_INET, SOMAXCONN) ) { | |||
print(LOG $bpc->timeStamp, "inet listen() failed: $!; exiting\n"); | print(LOG $bpc->timeStamp, "inet listen() failed: $!; exiting\n"); | |||
exit(1); | exit(1); | |||
skipping to change at line 2142 | skipping to change at line 2121 | |||
{ | { | |||
if ( $Conf{SCGIServerPort} < 0 && defined($Jobs{$bpc->scgiJob}) ) { | if ( $Conf{SCGIServerPort} < 0 && defined($Jobs{$bpc->scgiJob}) ) { | |||
# | # | |||
# SCGI was disabled - kill it | # SCGI was disabled - kill it | |||
# | # | |||
kill($bpc->sigName2num("INT"), $Jobs{$bpc->scgiJob}{pid}); | kill($bpc->sigName2num("INT"), $Jobs{$bpc->scgiJob}{pid}); | |||
} elsif ( $Conf{SCGIServerPort} > 0 && !defined($Jobs{$bpc->scgiJob}) && !$C mdQueueOn{$bpc->scgiJob} ) { | } elsif ( $Conf{SCGIServerPort} > 0 && !defined($Jobs{$bpc->scgiJob}) && !$C mdQueueOn{$bpc->scgiJob} ) { | |||
# | # | |||
# SCGI is enabled - start it | # SCGI is enabled - start it | |||
# | # | |||
unshift(@CmdQueue, { | unshift( | |||
@CmdQueue, | ||||
{ | ||||
host => $bpc->scgiJob, | host => $bpc->scgiJob, | |||
user => "BackupPC", | user => "BackupPC", | |||
reqTime => time, | reqTime => time, | |||
cmd => ["$BinDir/BackupPC_Admin_SCGI"], | cmd => ["$BinDir/BackupPC_Admin_SCGI"], | |||
}); | } | |||
); | ||||
$CmdQueueOn{$bpc->scgiJob} = 1; | $CmdQueueOn{$bpc->scgiJob} = 1; | |||
} | } | |||
} | } | |||
# | # | |||
# Gracefully shutdown the server. Used by Main_Process_Signal when | # Gracefully shutdown the server. Used by Main_Process_Signal when | |||
# $SigName ne "" && $SigName ne "HUP" or when the command | # $SigName ne "" && $SigName ne "HUP" or when the command | |||
# "server shutdown" is received. | # "server shutdown" is received. | |||
# | # | |||
sub ServerShutdown | sub ServerShutdown | |||
End of changes. 186 change blocks. | ||||
615 lines changed or deleted | 657 lines changed or added |