"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "bin/BackupPC" between
BackupPC-4.3.2.tar.gz and BackupPC-4.4.0.tar.gz

About: BackupPC is a high-performance, enterprise-grade system for backing up Linux and WinXX PCs and laptops to a server’s disk (http/cgi user interface).

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

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)