BackupPC-3.3.2/0000755000076500000240000000000013042250554012117 5ustar craigstaffBackupPC-3.3.2/bin/0000755000076500000240000000000013042250554012667 5ustar craigstaffBackupPC-3.3.2/bin/BackupPC0000555000076500000240000022224213042250554014247 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC: Main program for PC backups. # # DESCRIPTION # # BackupPC reads the configuration and status information from # $ConfDir/conf. It then runs and manages all the backup activity. # # As specified by $Conf{WakeupSchedule}, BackupPC wakes up periodically # to queue backups on all the PCs. This is a four step process: # 1) For each host and DHCP address backup requests are queued on the # background command queue. # 2) For each PC, BackupPC_dump is forked. Several of these may # be run in parallel, based on the configuration. # 3) For each complete, good, backup, BackupPC_link is forked. # Only one of these tasks runs at a time. # 4) In the background BackupPC_trashClean is run to remove any expired # backups. Once each night, BackupPC_nightly is run to complete some # additional administrative tasks (pool cleaning etc). # # BackupPC also listens for connections on a unix domain socket and # the tcp port $Conf{ServerPort}, which are used by various # sub-programs and the CGI script BackupPC_Admin for status reporting # and user-initiated backup or backup cancel requests. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use vars qw(%Status %Info $Hosts); use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; use BackupPC::FileZIO; use Encode qw/decode_utf8/; use File::Path; use Data::Dumper; use Getopt::Std; use Socket; use Carp; use Digest::MD5; use POSIX qw(setsid); ########################################################################### # Handle command line options ########################################################################### my %opts; if ( !getopts("d", \%opts) || @ARGV != 0 ) { print("usage: $0 [-d]\n"); exit(1); } ########################################################################### # Initialize major data structures and variables ########################################################################### # # Get an instance of BackupPC::Lib and get some shortcuts. # die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my $LogDir = $bpc->LogDir(); my %Conf = $bpc->Conf(); # # Verify we are running as the correct user # if ( $Conf{BackupPCUserVerify} && $> != (my $uid = (getpwnam($Conf{BackupPCUser}))[2]) ) { die "Wrong user: my userid is $>, instead of $uid ($Conf{BackupPCUser})\n"; } # # %Status maintain status information about each host. # It is a hash of hashes, whose first index is the host. # %Status = (); # # %Info is a hash giving general information about BackupPC status. # %Info = (); # # Read old status # if ( -f "$LogDir/status.pl" && !(my $ret = do "$LogDir/status.pl") ) { if ( $@ ) { print STDERR "couldn't parse $LogDir/status.pl: $@"; } elsif ( !defined($ret) ) { print STDERR "couldn't do $LogDir/status.pl: $!"; } else { print STDERR "couldn't run $LogDir/status.pl"; } } # # %Jobs maintains information about currently running jobs. # It is a hash of hashes, whose first index is the host. # my %Jobs = (); # # There are three command queues: # - @UserQueue is a queue of user initiated backup requests. # - @BgQueue is a queue of automatically scheduled backup requests. # - @CmdQueue is a queue of administrative jobs, including tasks # like BackupPC_link, BackupPC_trashClean, and BackupPC_nightly # Each queue is an array of hashes. Each hash stores information # about the command request. # my @UserQueue = (); my @CmdQueue = (); my @BgQueue = (); # # To quickly lookup if a given host is on a given queue, we keep # a hash of flags for each queue type. # my(%CmdQueueOn, %UserQueueOn, %BgQueueOn); # # One or more clients can connect to the server to get status information # or request/cancel backups etc. The %Clients hash maintains information # about each of these socket connections. The hash key is an incrementing # number stored in $ClientConnCnt. Each entry is a hash that contains # various information about the client connection. # my %Clients = (); my $ClientConnCnt; # # Read file descriptor mask used by select(). Every file descriptor # on which we expect to read (or accept) has the corresponding bit # set. # my $FDread = ''; # # Unix seconds when we next wakeup. A value of zero forces the scheduler # to compute the next wakeup time. # my $NextWakeup = 0; # # Name of signal saved by catch_signal # my $SigName = ""; # # Misc variables # my($RunNightlyWhenIdle, $FirstWakeup, $CmdJob, $ServerInetPort); my($BackupPCNightlyJobs, $BackupPCNightlyLock); # # Complete the rest of the initialization # Main_Initialize(); ########################################################################### # Main loop ########################################################################### while ( 1 ) { # # Check if we can/should run BackupPC_nightly # Main_TryToRun_nightly(); # # Check if we can run a new command from @CmdQueue. # Main_TryToRun_CmdQueue(); # # Check if we can run a new command from @UserQueue or @BgQueue. # Main_TryToRun_Bg_or_User_Queue(); # # Do a select() to wait for the next interesting thing to happen # (timeout, signal, someone sends a message, child dies etc). # my $fdRead = Main_Select(); # # Process a signal if we received one. # if ( $SigName ) { Main_Process_Signal(); $fdRead = undef; } # # Check if a timeout has occurred. # Main_Check_Timeout(); # # Check for, and process, any messages (output) from our jobs # Main_Check_Job_Messages($fdRead); # # Check for, and process, any output from our clients. Also checks # for new connections to our SERVER_UNIX and SERVER_INET sockets. # Main_Check_Client_Messages($fdRead); } ############################################################################ # Main_Initialize() # # Main initialization routine. Called once at statup. ############################################################################ sub Main_Initialize { umask($Conf{UmaskMode}); # # Check for another running process, verify executables are configured # correctly and make sure $TopDir is on a file system that supports # hardlinks. # if ( $Info{pid} ne "" && kill(0, $Info{pid}) ) { print(STDERR $bpc->timeStamp, "Another BackupPC is running (pid $Info{pid}); quitting...\n"); exit(1); } foreach my $progName ( qw(SmbClientPath NmbLookupPath PingPath DfPath SendmailPath SshPath) ) { next if ( $Conf{$progName} eq "" || -x $Conf{$progName} ); print(STDERR $bpc->timeStamp, "\$Conf{$progName} = '$Conf{$progName}' is not a" . " valid executable program\n"); exit(1); } if ( !$bpc->HardlinkTest("$TopDir/pc", "$TopDir/cpool") ) { print(STDERR $bpc->timeStamp, "Can't create a test hardlink between a file" . " in $TopDir/pc and $TopDir/cpool. Either these are different" . " file systems, or this file system doesn't support hardlinks," . " or these directories don't exist, or there is a permissions" . " problem, or the file system is out of inodes or full. Use" . " df, df -i, and ls -ld to check each of these possibilities." . " Quitting...\n"); exit(1); } if ( $opts{d} ) { # # daemonize by forking; more robust method per: # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=301057 # my $pid; defined($pid = fork) or die("Can't fork: $!"); exit if ( $pid ); # parent exits POSIX::setsid(); defined($pid = fork) or die("Can't fork: $!"); exit if $pid; # parent exits chdir ("/") or die("Cannot chdir to /: $!\n"); close(STDIN); open(STDIN , ">/dev/null") or die("Cannot open /dev/null as stdin\n"); # STDOUT and STDERR are handled in LogFileOpen() right below, # otherwise we would have to reopen them too. } # # Open the LOG file and redirect STDOUT, STDERR etc # LogFileOpen(); # # Read the hosts file (force a read). # exit(1) if ( !HostsUpdate(1) ); # # Clean up %ENV for taint checking # delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; $ENV{PATH} = $Conf{MyPath}; # # Initialize server sockets # ServerSocketInit(); # # Catch various signals # foreach my $sig ( qw(INT BUS SEGV PIPE TERM ALRM HUP) ) { $SIG{$sig} = \&catch_signal; } # # Report that we started, and update %Info. # print(LOG $bpc->timeStamp, "BackupPC started, pid $$\n"); $Info{ConfigModTime} = $bpc->ConfigMTime(); $Info{pid} = $$; $Info{startTime} = time; $Info{ConfigLTime} = time; $Info{Version} = $bpc->{Version}; # # Update the status left over form the last time BackupPC ran. # Requeue any pending links. # foreach my $host ( sort(keys(%$Hosts)) ) { if ( $Status{$host}{state} eq "Status_backup_in_progress" ) { # # should we restart it? skip it for now. # $Status{$host}{state} = "Status_idle"; } elsif ( $Status{$host}{state} eq "Status_link_pending" || $Status{$host}{state} eq "Status_link_running" ) { QueueLink($host); } else { $Status{$host}{state} = "Status_idle"; } $Status{$host}{activeJob} = 0; } foreach my $host ( sort(keys(%Status)) ) { next if ( defined($Hosts->{$host}) ); delete($Status{$host}); } # # Write out our initial status and save our PID # StatusWrite(); unlink("$LogDir/BackupPC.pid"); if ( open(PID, ">", "$LogDir/BackupPC.pid") ) { print(PID $$); close(PID); chmod(0444, "$LogDir/BackupPC.pid"); } # # For unknown reasons there is a very infrequent error about not # being able to coerce GLOBs inside the XS Data::Dumper. I've # only seen this on a particular platform and perl version. # For now the workaround appears to be use the perl version of # XS Data::Dumper. # $Data::Dumper::Useqq = 1; } ############################################################################ # Main_TryToRun_nightly() # # Checks to see if we can/should run BackupPC_nightly or # BackupPC_trashClean. If so we push the appropriate command onto # @CmdQueue. ############################################################################ sub Main_TryToRun_nightly { # # Check if we should run BackupPC_nightly or BackupPC_trashClean. # BackupPC_nightly is run when the current job queue is empty. # BackupPC_trashClean is run in the background always. # my $trashCleanRunning = defined($Jobs{$bpc->trashJob}) ? 1 : 0; if ( !$trashCleanRunning && !$CmdQueueOn{$bpc->trashJob} ) { # # This should only happen once at startup, but just in case this # code will re-start BackupPC_trashClean if it quits # unshift(@CmdQueue, { host => $bpc->trashJob, user => "BackupPC", reqTime => time, cmd => ["$BinDir/BackupPC_trashClean"], }); $CmdQueueOn{$bpc->trashJob} = 1; } if ( $RunNightlyWhenIdle == 1 ) { # # Queue multiple nightly jobs based on the configuration # $Conf{MaxBackupPCNightlyJobs} = 1 if ( $Conf{MaxBackupPCNightlyJobs} <= 0 ); $Conf{BackupPCNightlyPeriod} = 1 if ( $Conf{BackupPCNightlyPeriod} <= 0 ); # # Decide what subset of the 16 top-level directories 0..9a..f # we run BackupPC_nightly on, based on $Conf{BackupPCNightlyPeriod}. # If $Conf{BackupPCNightlyPeriod} == 1 then we run 0..15 every # time. If $Conf{BackupPCNightlyPeriod} == 2 then we run # 0..7 one night and 89a-f the next night. And so on. # # $Info{NightlyPhase} counts which night, from 0 to # $Conf{BackupPCNightlyPeriod} - 1. # my $start = int($Info{NightlyPhase} * 16 / $Conf{BackupPCNightlyPeriod}); my $end = int(($Info{NightlyPhase} + 1) * 16 / $Conf{BackupPCNightlyPeriod}); $end = $start + 1 if ( $end <= $start ); $Info{NightlyPhase}++; $Info{NightlyPhase} = 0 if ( $end >= 16 ); # # Zero out the data we expect to get from BackupPC_nightly. # for my $p ( qw(pool cpool) ) { for ( my $i = $start ; $i < $end ; $i++ ) { $Info{pool}{$p}[$i]{FileCnt} = 0; $Info{pool}{$p}[$i]{DirCnt} = 0; $Info{pool}{$p}[$i]{Kb} = 0; $Info{pool}{$p}[$i]{Kb2} = 0; $Info{pool}{$p}[$i]{KbRm} = 0; $Info{pool}{$p}[$i]{FileCntRm} = 0; $Info{pool}{$p}[$i]{FileCntRep} = 0; $Info{pool}{$p}[$i]{FileRepMax} = 0; $Info{pool}{$p}[$i]{FileCntRename} = 0; $Info{pool}{$p}[$i]{FileLinkMax} = 0; $Info{pool}{$p}[$i]{Time} = 0; } } print(LOG $bpc->timeStamp, sprintf("Running %d BackupPC_nightly jobs from %d..%d" . " (out of 0..15)\n", $Conf{MaxBackupPCNightlyJobs}, $start, $end - 1)); # # Now queue the $Conf{MaxBackupPCNightlyJobs} jobs. # The granularity on start and end is now 0..255. # $start *= 16; $end *= 16; my $start0 = $start; for ( my $i = 0 ; $i < $Conf{MaxBackupPCNightlyJobs} ; $i++ ) { # # The first nightly job gets the -m option (does email, log aging). # All jobs get the start and end options from 0..255 telling # them which parts of the pool to traverse. # my $cmd = ["$BinDir/BackupPC_nightly"]; push(@$cmd, "-m") if ( $i == 0 ); push(@$cmd, $start); $start = $start0 + int(($end - $start0) * ($i + 1) / $Conf{MaxBackupPCNightlyJobs}); push(@$cmd, $start - 1); my $job = $bpc->adminJob($i); unshift(@CmdQueue, { host => $job, user => "BackupPC", reqTime => time, cmd => $cmd, }); $CmdQueueOn{$job} = 1; } $RunNightlyWhenIdle = 2; } } ############################################################################ # Main_TryToRun_CmdQueue() # # Decide if we can run a new command from the @CmdQueue. # We only run one of these at a time. The @CmdQueue is # used to run BackupPC_link (for the corresponding host), # BackupPC_trashClean, and BackupPC_nightly using a fake # host name of $bpc->adminJob. ############################################################################ sub Main_TryToRun_CmdQueue { my($req, $host); while ( $CmdJob eq "" && @CmdQueue > 0 && $RunNightlyWhenIdle != 1 || @CmdQueue > 0 && $RunNightlyWhenIdle == 2 && $bpc->isAdminJob($CmdQueue[0]->{host}) ) { local(*FH); $req = pop(@CmdQueue); $host = $req->{host}; if ( defined($Jobs{$host}) ) { print(LOG $bpc->timeStamp, "Botch on admin job for $host: already in use!!\n"); # # This could happen during normal opertion: a user could # request a backup while a BackupPC_link is queued from # a previous backup. But it is unlikely. Just put this # request back on the end of the queue. # unshift(@CmdQueue, $req); return; } $CmdQueueOn{$host} = 0; my $cmd = $req->{cmd}; my $pid = open(FH, "-|"); if ( !defined($pid) ) { print(LOG $bpc->timeStamp, "can't fork for $host, request by $req->{user}\n"); close(FH); next; } if ( !$pid ) { setpgrp 0,0; $ENV{BPC_REQUSER} = $req->{user}; POSIX::nice($Conf{CmdQueueNice}) if ( $Conf{CmdQueueNice} ); exec(@$cmd); print(LOG $bpc->timeStamp, "can't exec @$cmd for $host\n"); exit(0); } $Jobs{$host}{pid} = $pid; $Jobs{$host}{fh} = *FH; $Jobs{$host}{fn} = fileno(FH); vec($FDread, $Jobs{$host}{fn}, 1) = 1; $Jobs{$host}{startTime} = time; $Jobs{$host}{reqTime} = $req->{reqTime}; $cmd = $bpc->execCmd2ShellCmd(@$cmd); $Jobs{$host}{cmd} = $cmd; $Jobs{$host}{user} = $req->{user}; $Jobs{$host}{type} = $Status{$host}{type}; $Status{$host}{state} = "Status_link_running"; $Status{$host}{activeJob} = 1; $Status{$host}{endTime} = time; $CmdJob = $host if ( $host ne $bpc->trashJob ); $cmd =~ s/$BinDir\///g; print(LOG $bpc->timeStamp, "Running $cmd (pid=$pid)\n"); if ( $cmd =~ /^BackupPC_nightly\s/ ) { $BackupPCNightlyJobs++; $BackupPCNightlyLock++; } } } ############################################################################ # Main_TryToRun_Bg_or_User_Queue() # # Decide if we can run any new backup requests from @BgQueue # or @UserQueue. Several of these can be run at the same time # based on %Conf settings. Jobs from @UserQueue take priority, # and at total of $Conf{MaxBackups} + $Conf{MaxUserBackups} # simultaneous jobs can run from @UserQueue. After @UserQueue # is exhausted, up to $Conf{MaxBackups} simultaneous jobs can # run from @BgQueue. ############################################################################ sub Main_TryToRun_Bg_or_User_Queue { my($req, $host); my(@deferUserQueue, @deferBgQueue); my $du; if ( time - $Info{DUlastValueTime} >= 600 ) { # # Update our notion of disk usage no more than # once every 10 minutes # $du = $bpc->CheckFileSystemUsage($TopDir); $Info{DUlastValue} = $du; $Info{DUlastValueTime} = time; } else { # # if we recently checked it then just use the old value # $du = $Info{DUlastValue}; } if ( $Info{DUDailyMaxReset} ) { $Info{DUDailyMaxStartTime} = time; $Info{DUDailyMaxReset} = 0; $Info{DUDailyMax} = 0; } if ( $du > $Info{DUDailyMax} ) { $Info{DUDailyMax} = $du; $Info{DUDailyMaxTime} = time; } if ( $du > $Conf{DfMaxUsagePct} ) { my @bgQueue = @BgQueue; my $nSkip = 0; # # When the disk is too full, only run backups that will # do expires, not regular backups # @BgQueue = (); foreach $req ( @bgQueue ) { if ( $req->{dumpExpire} ) { unshift(@BgQueue, $req); } else { $BgQueueOn{$req->{host}} = 0; $nSkip++; } } if ( $nSkip ) { print(LOG $bpc->timeStamp, "Disk too full ($du%); skipped $nSkip hosts\n"); $Info{DUDailySkipHostCnt} += $nSkip; } } # # Run background jobs anytime. Previously they were locked out # when BackupPC_nightly was running or pending with this # condition on the while loop: # # while ( $RunNightlyWhenIdle == 0 ) # while ( 1 ) { local(*FH); my(@args, $progName, $type); my $nJobs = keys(%Jobs); # # CmdJob and trashClean don't count towards MaxBackups / MaxUserBackups # if ( $CmdJob ne "" ) { if ( $BackupPCNightlyJobs ) { $nJobs -= $BackupPCNightlyJobs; } else { $nJobs--; } } $nJobs-- if ( defined($Jobs{$bpc->trashJob} ) ); if ( $nJobs < $Conf{MaxBackups} + $Conf{MaxUserBackups} && @UserQueue > 0 ) { $req = pop(@UserQueue); if ( defined($Jobs{$req->{host}}) ) { push(@deferUserQueue, $req); next; } $UserQueueOn{$req->{host}} = 0; } elsif ( $nJobs < $Conf{MaxBackups} && (@CmdQueue + $nJobs) <= $Conf{MaxBackups} + $Conf{MaxPendingCmds} && @BgQueue > 0 ) { $req = pop(@BgQueue); if ( defined($Jobs{$req->{host}}) ) { # # Job is currently running for this host; save it for later # unshift(@deferBgQueue, $req); next; } $BgQueueOn{$req->{host}} = 0; } else { # # Restore the deferred jobs # @BgQueue = (@BgQueue, @deferBgQueue); @UserQueue = (@UserQueue, @deferUserQueue); last; } $host = $req->{host}; my $user = $req->{user}; if ( $req->{restore} ) { $progName = "BackupPC_restore"; $type = "restore"; push(@args, $req->{hostIP}, $req->{host}, $req->{reqFileName}); } elsif ( $req->{archive} ) { $progName = "BackupPC_archive"; $type = "archive"; push(@args, $req->{user}, $req->{host}, $req->{reqFileName}); } else { $progName = "BackupPC_dump"; $type = "backup"; push(@args, "-I") if ( $req->{backupType} eq "autoIncr" ); push(@args, "-F") if ( $req->{backupType} eq "autoFull" ); push(@args, "-i") if ( $req->{backupType} eq "doIncr" ); push(@args, "-f") if ( $req->{backupType} eq "doFull" ); push(@args, "-d") if ( $req->{backupType} eq "dhcpPoll" ); push(@args, "-e") if ( $req->{dumpExpire} ); push(@args, $host); } my $pid = open(FH, "-|"); if ( !defined($pid) ) { print(LOG $bpc->timeStamp, "can't fork to run $progName for $host, request by $user\n"); close(FH); next; } if ( !$pid ) { setpgrp 0,0; exec("$BinDir/$progName", @args); print(LOG $bpc->timeStamp, "can't exec $progName for $host\n"); exit(0); } $Jobs{$host}{pid} = $pid; $Jobs{$host}{fh} = *FH; $Jobs{$host}{fn} = fileno(FH); $Jobs{$host}{dhcp} = $req->{dhcp}; vec($FDread, $Jobs{$host}{fn}, 1) = 1; $Jobs{$host}{startTime} = time; $Jobs{$host}{reqTime} = $req->{reqTime}; $Jobs{$host}{userReq} = $req->{userReq}; $Jobs{$host}{cmd} = $bpc->execCmd2ShellCmd($progName, @args); $Jobs{$host}{user} = $user; $Jobs{$host}{type} = $type; $Status{$host}{userReq} = $req->{userReq} if ( defined($Hosts->{$host}) ); if ( !$req->{dhcp} ) { $Status{$host}{state} = "Status_".$type."_starting"; $Status{$host}{activeJob} = 1; $Status{$host}{startTime} = time; $Status{$host}{endTime} = ""; } } } ############################################################################ # Main_Select() # # 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 # (timeout, signal, someone sends a message, child dies etc). ############################################################################ sub Main_Select { if ( $NextWakeup <= 0 ) { # # Figure out when to next wakeup based on $Conf{WakeupSchedule}. # my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my($currHours) = $hour + $min / 60 + $sec / 3600; if ( $bpc->ConfigMTime() != $Info{ConfigModTime} ) { ServerReload("Re-read config file because mtime changed"); } my $delta = -1; foreach my $t ( @{$Conf{WakeupSchedule} || [0..23]} ) { next if ( $t < 0 || $t > 24 ); my $tomorrow = $t + 24; if ( $delta < 0 || ($tomorrow - $currHours > 0 && $delta > $tomorrow - $currHours) ) { $delta = $tomorrow - $currHours; $FirstWakeup = $t == $Conf{WakeupSchedule}[0]; } if ( $delta < 0 || ($t - $currHours > 0 && $delta > $t - $currHours) ) { $delta = $t - $currHours; $FirstWakeup = $t == $Conf{WakeupSchedule}[0]; } } $NextWakeup = time + $delta * 3600; $Info{nextWakeup} = $NextWakeup; print(LOG $bpc->timeStamp, "Next wakeup is ", $bpc->timeStamp($NextWakeup, 1), "\n"); } # # Call select(), waiting until either a signal, a timeout, # any output from our jobs, or any messages from clients # connected via tcp. # select() is where we (hopefully) spend most of our time blocked... # my $timeout = $NextWakeup - time; $timeout = 1 if ( $timeout <= 0 ); my $ein = $FDread; select(my $rout = $FDread, undef, $ein, $timeout); return $rout; } ############################################################################ # Main_Process_Signal() # # Signal handler. ############################################################################ sub Main_Process_Signal { # # Process signals # if ( $SigName eq "HUP" ) { ServerReload("Re-read config file because of a SIG_HUP"); } elsif ( $SigName ) { ServerShutdown("Got signal $SigName... cleaning up"); } $SigName = ""; } ############################################################################ # Main_Check_Timeout() # # Check if a timeout has occured, and if so, queue all the PCs for backups. # Also does log file aging on the first timeout after midnight. ############################################################################ sub Main_Check_Timeout { # # Process timeouts # return if ( time < $NextWakeup || $NextWakeup <= 0 ); $NextWakeup = 0; if ( $FirstWakeup ) { # # This is the first wakeup after midnight. Do log file aging # and various house keeping. # $FirstWakeup = 0; printf(LOG "%s24hr disk usage: %d%% max, %d%% recent," . " %d skipped hosts\n", $bpc->timeStamp, $Info{DUDailyMax}, $Info{DUlastValue}, $Info{DUDailySkipHostCnt}); $Info{DUDailyMaxReset} = 1; $Info{DUDailyMaxPrev} = $Info{DUDailyMax}; $Info{DUDailySkipHostCntPrev} = $Info{DUDailySkipHostCnt}; $Info{DUDailySkipHostCnt} = 0; my $lastLog = $Conf{MaxOldLogFiles} - 1; if ( -f "$LogDir/LOG.$lastLog" ) { print(LOG $bpc->timeStamp, "Removing $LogDir/LOG.$lastLog\n"); unlink("$LogDir/LOG.$lastLog"); } if ( -f "$LogDir/LOG.$lastLog.z" ) { print(LOG $bpc->timeStamp, "Removing $LogDir/LOG.$lastLog.z\n"); unlink("$LogDir/LOG.$lastLog.z"); } print(LOG $bpc->timeStamp, "Aging LOG files, LOG -> LOG.0 -> " . "LOG.1 -> ... -> LOG.$lastLog\n"); close(STDERR); # dup of LOG close(STDOUT); # dup of LOG close(LOG); for ( my $i = $lastLog - 1 ; $i >= 0 ; $i-- ) { my $j = $i + 1; rename("$LogDir/LOG.$i", "$LogDir/LOG.$j") if ( -f "$LogDir/LOG.$i" ); rename("$LogDir/LOG.$i.z", "$LogDir/LOG.$j.z") if ( -f "$LogDir/LOG.$i.z" ); } # # Compress the log file LOG -> LOG.0.z (if enabled). # Otherwise, just rename LOG -> LOG.0. # BackupPC::FileZIO->compressCopy("$LogDir/LOG", "$LogDir/LOG.0.z", "$LogDir/LOG.0", $Conf{CompressLevel}, 1); LogFileOpen(); # # Remember to run the nightly script when the next CmdQueue # job is done. # if ( $RunNightlyWhenIdle == 2 ) { print(LOG $bpc->timeStamp, "BackupPC_nightly is still running after 24 hours!!" . " You should adjust the config settings; Skipping this run\n"); } else { $RunNightlyWhenIdle = 1; } } # # Write out the current status and then queue all the PCs # HostsUpdate(0); StatusWrite(); %BgQueueOn = () if ( @BgQueue == 0 ); %UserQueueOn = () if ( @UserQueue == 0 ); %CmdQueueOn = () if ( @CmdQueue == 0 ); QueueAllPCs(); } ############################################################################ # Main_Check_Job_Messages($fdRead) # # Check if select() says we have bytes waiting from any of our jobs. # Handle each of the messages when complete (newline terminated). ############################################################################ sub Main_Check_Job_Messages { my($fdRead) = @_; foreach my $host ( keys(%Jobs) ) { next if ( !vec($fdRead, $Jobs{$host}{fn}, 1) ); my $mesg; # # do a last check to make sure there is something to read so # we are absolutely sure we won't block. # vec(my $readMask, $Jobs{$host}{fn}, 1) = 1; if ( !select($readMask, undef, undef, 0.0) ) { print(LOG $bpc->timeStamp, "Botch in Main_Check_Job_Messages:" . " nothing to read from $host. Debug dump:\n"); my($dump) = Data::Dumper->new( [ \%Clients, \%Jobs, \$FDread, \$fdRead], [qw(*Clients, *Jobs *FDread, *fdRead)]); $dump->Indent(1); print(LOG $dump->Dump); next; } my $nbytes = sysread($Jobs{$host}{fh}, $mesg, 1024); $Jobs{$host}{mesg} .= $mesg if ( $nbytes > 0 ); # # Process any complete lines of output from this jobs. # Any output to STDOUT or STDERR from the children is processed here. # while ( $Jobs{$host}{mesg} =~ /(.*?)[\n\r]+(.*)/s ) { $mesg = $1; $Jobs{$host}{mesg} = $2; if ( $Jobs{$host}{dhcp} ) { 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}{shareName} = $2; print(LOG $bpc->timeStamp, "Started $1 backup on $host (pid=$Jobs{$host}{pid}", $Jobs{$host}{dhcpHostIP} ? ", dhcp=$Jobs{$host}{dhcpHostIP}" : "", ", share=$Jobs{$host}{shareName})\n"); $Status{$host}{state} = "Status_backup_in_progress"; $Status{$host}{reason} = ""; $Status{$host}{type} = $1; $Status{$host}{startTime} = time; $Status{$host}{deadCnt} = 0; $Status{$host}{aliveCnt}++; $Status{$host}{dhcpCheckCnt}-- if ( $Status{$host}{dhcpCheckCnt} > 0 ); } elsif ( $mesg =~ /^xferPids (.*)/ ) { $Jobs{$host}{xferPid} = $1; } elsif ( $mesg =~ /^completionPercent (.*)/ ) { $Jobs{$host}{completionPercent} = $1; } elsif ( $mesg =~ /^started_restore/ ) { $Jobs{$host}{type} = "restore"; print(LOG $bpc->timeStamp, "Started restore on $host" . " (pid=$Jobs{$host}{pid})\n"); $Status{$host}{state} = "Status_restore_in_progress"; $Status{$host}{reason} = ""; $Status{$host}{type} = "restore"; $Status{$host}{startTime} = time; $Status{$host}{deadCnt} = 0; $Status{$host}{aliveCnt}++; } elsif ( $mesg =~ /^started_archive/ ) { $Jobs{$host}{type} = "archive"; print(LOG $bpc->timeStamp, "Started archive on $host" . " (pid=$Jobs{$host}{pid})\n"); $Status{$host}{state} = "Status_archive_in_progress"; $Status{$host}{reason} = ""; $Status{$host}{type} = "archive"; $Status{$host}{startTime} = time; $Status{$host}{deadCnt} = 0; $Status{$host}{aliveCnt}++; } elsif ( $mesg =~ /^(full|incr) backup complete/ ) { print(LOG $bpc->timeStamp, "Finished $1 backup on $host\n"); $Status{$host}{reason} = "Reason_backup_done"; delete($Status{$host}{error}); delete($Status{$host}{errorTime}); $Status{$host}{endTime} = time; $Status{$host}{lastGoodBackupTime} = time; } elsif ( $mesg =~ /^backups disabled/ ) { print(LOG $bpc->timeStamp, "Ignoring old backup error on $host\n"); $Status{$host}{reason} = "Reason_backup_done"; delete($Status{$host}{error}); delete($Status{$host}{errorTime}); $Status{$host}{endTime} = time; } elsif ( $mesg =~ /^restore complete/ ) { print(LOG $bpc->timeStamp, "Finished restore on $host\n"); $Status{$host}{reason} = "Reason_restore_done"; delete($Status{$host}{error}); delete($Status{$host}{errorTime}); $Status{$host}{endTime} = time; } elsif ( $mesg =~ /^archive complete/ ) { print(LOG $bpc->timeStamp, "Finished archive on $host\n"); $Status{$host}{reason} = "Reason_archive_done"; delete($Status{$host}{error}); delete($Status{$host}{errorTime}); $Status{$host}{endTime} = time; } elsif ( $mesg =~ /^nothing to do/ ) { if ( $Status{$host}{reason} ne "Reason_backup_failed" && $Status{$host}{reason} ne "Reason_restore_failed" ) { $Status{$host}{state} = "Status_idle"; $Status{$host}{reason} = "Reason_nothing_to_do"; $Status{$host}{startTime} = time; } $Status{$host}{dhcpCheckCnt}-- if ( $Status{$host}{dhcpCheckCnt} > 0 ); } elsif ( $mesg =~ /^no ping response/ || $mesg =~ /^ping too slow/ || $mesg =~ /^host not found/ ) { $Status{$host}{state} = "Status_idle"; if ( $Status{$host}{userReq} || $Status{$host}{reason} ne "Reason_backup_failed" || $Status{$host}{error} =~ /^aborted by user/ ) { $Status{$host}{reason} = "Reason_no_ping"; $Status{$host}{error} = $mesg; $Status{$host}{startTime} = time; } $Status{$host}{deadCnt}++; if ( $Status{$host}{deadCnt} >= $Conf{BlackoutBadPingLimit} ) { $Status{$host}{aliveCnt} = 0; } } elsif ( $mesg =~ /^dump failed: (.*)/ ) { $Status{$host}{state} = "Status_idle"; $Status{$host}{error} = $1; $Status{$host}{errorTime} = time; $Status{$host}{endTime} = time; if ( $Status{$host}{reason} eq "Reason_backup_canceled_by_user" ) { print(LOG $bpc->timeStamp, "Backup canceled on $host ($1)\n"); } else { $Status{$host}{reason} = "Reason_backup_failed"; print(LOG $bpc->timeStamp, "Backup failed on $host ($1)\n"); } } elsif ( $mesg =~ /^restore failed: (.*)/ ) { $Status{$host}{state} = "Status_idle"; $Status{$host}{error} = $1; $Status{$host}{errorTime} = time; $Status{$host}{endTime} = time; if ( $Status{$host}{reason} eq "Reason_restore_canceled_by_user" ) { print(LOG $bpc->timeStamp, "Restore canceled on $host ($1)\n"); } else { $Status{$host}{reason} = "Reason_restore_failed"; print(LOG $bpc->timeStamp, "Restore failed on $host ($1)\n"); } } elsif ( $mesg =~ /^archive failed: (.*)/ ) { $Status{$host}{state} = "Status_idle"; $Status{$host}{error} = $1; $Status{$host}{errorTime} = time; $Status{$host}{endTime} = time; if ( $Status{$host}{reason} eq "Reason_archive_canceled_by_user" ) { print(LOG $bpc->timeStamp, "Archive canceled on $host ($1)\n"); } else { $Status{$host}{reason} = "Reason_archive_failed"; print(LOG $bpc->timeStamp, "Archive failed on $host ($1)\n"); } } elsif ( $mesg =~ /^log\s+(.*)/ ) { print(LOG $bpc->timeStamp, "$1\n"); } elsif ( $mesg =~ /^BackupPC_stats (\d+) = (.*)/ ) { my $chunk = int($1 / 16); my @f = split(/,/, $2); $Info{pool}{$f[0]}[$chunk]{FileCnt} += $f[1]; $Info{pool}{$f[0]}[$chunk]{DirCnt} += $f[2]; $Info{pool}{$f[0]}[$chunk]{Kb} += $f[3]; $Info{pool}{$f[0]}[$chunk]{Kb2} += $f[4]; $Info{pool}{$f[0]}[$chunk]{KbRm} += $f[5]; $Info{pool}{$f[0]}[$chunk]{FileCntRm} += $f[6]; $Info{pool}{$f[0]}[$chunk]{FileCntRep} += $f[7]; $Info{pool}{$f[0]}[$chunk]{FileRepMax} = $f[8] if ( $Info{pool}{$f[0]}[$chunk]{FileRepMax} < $f[8] ); $Info{pool}{$f[0]}[$chunk]{FileCntRename} += $f[9]; $Info{pool}{$f[0]}[$chunk]{FileLinkMax} = $f[10] if ( $Info{pool}{$f[0]}[$chunk]{FileLinkMax} < $f[10] ); $Info{pool}{$f[0]}[$chunk]{FileLinkTotal} += $f[11]; $Info{pool}{$f[0]}[$chunk]{Time} = time; } elsif ( $mesg =~ /^BackupPC_nightly lock_off/ ) { $BackupPCNightlyLock--; if ( $BackupPCNightlyLock == 0 ) { # # This means the last BackupPC_nightly is done with # the pool clean, so it's ok to start running regular # backups again. But starting in 3.0 regular jobs # are decoupled from BackupPC_nightly. # $RunNightlyWhenIdle = 0; } } elsif ( $mesg =~ /^processState\s+(.+)/ ) { $Jobs{$host}{processState} = $1; } elsif ( $mesg =~ /^link\s+(.+)/ ) { my($h) = $1; $Status{$h}{needLink} = 1; } else { print(LOG $bpc->timeStamp, "$host: $mesg\n"); } } # # shut down the client connection if we read EOF # if ( $nbytes <= 0 ) { close($Jobs{$host}{fh}); vec($FDread, $Jobs{$host}{fn}, 1) = 0; if ( $CmdJob eq $host || $bpc->isAdminJob($host) ) { my $cmd = $Jobs{$host}{cmd}; $cmd =~ s/$BinDir\///g; print(LOG $bpc->timeStamp, "Finished $host ($cmd)\n"); $Status{$host}{state} = "Status_idle"; $Status{$host}{endTime} = time; if ( $cmd =~ /^BackupPC_nightly\s/ ) { $BackupPCNightlyJobs--; #print(LOG $bpc->timeStamp, "BackupPC_nightly done; now" # . " have $BackupPCNightlyJobs running\n"); if ( $BackupPCNightlyJobs <= 0 ) { # # Last BackupPC_nightly has finished # $BackupPCNightlyJobs = 0; $RunNightlyWhenIdle = 0; $CmdJob = ""; # # Combine the 16 per-directory results # for my $p ( qw(pool cpool) ) { $Info{"${p}FileCnt"} = 0; $Info{"${p}DirCnt"} = 0; $Info{"${p}Kb"} = 0; $Info{"${p}Kb2"} = 0; $Info{"${p}KbRm"} = 0; $Info{"${p}FileCntRm"} = 0; $Info{"${p}FileCntRep"} = 0; $Info{"${p}FileRepMax"} = 0; $Info{"${p}FileCntRename"} = 0; $Info{"${p}FileLinkMax"} = 0; $Info{"${p}Time"} = 0; for ( my $i = 0 ; $i < 16 ; $i++ ) { $Info{"${p}FileCnt"} += $Info{pool}{$p}[$i]{FileCnt}; $Info{"${p}DirCnt"} += $Info{pool}{$p}[$i]{DirCnt}; $Info{"${p}Kb"} += $Info{pool}{$p}[$i]{Kb}; $Info{"${p}Kb2"} += $Info{pool}{$p}[$i]{Kb2}; $Info{"${p}KbRm"} += $Info{pool}{$p}[$i]{KbRm}; $Info{"${p}FileCntRm"} += $Info{pool}{$p}[$i]{FileCntRm}; $Info{"${p}FileCntRep"} += $Info{pool}{$p}[$i]{FileCntRep}; $Info{"${p}FileRepMax"} = $Info{pool}{$p}[$i]{FileRepMax} if ( $Info{"${p}FileRepMax"} < $Info{pool}{$p}[$i]{FileRepMax} ); $Info{"${p}FileCntRename"} += $Info{pool}{$p}[$i]{FileCntRename}; $Info{"${p}FileLinkMax"} = $Info{pool}{$p}[$i]{FileLinkMax} if ( $Info{"${p}FileLinkMax"} < $Info{pool}{$p}[$i]{FileLinkMax} ); $Info{"${p}Time"} = $Info{pool}{$p}[$i]{Time} if ( $Info{"${p}Time"} < $Info{pool}{$p}[$i]{Time} ); } printf(LOG "%s%s nightly clean removed %d files of" . " size %.2fGB\n", $bpc->timeStamp, ucfirst($p), $Info{"${p}FileCntRm"}, $Info{"${p}KbRm"} / (1000 * 1024)); printf(LOG "%s%s is %.2fGB, %d files (%d repeated, " . "%d max chain, %d max links), %d directories\n", $bpc->timeStamp, ucfirst($p), $Info{"${p}Kb"} / (1000 * 1024), $Info{"${p}FileCnt"}, $Info{"${p}FileCntRep"}, $Info{"${p}FileRepMax"}, $Info{"${p}FileLinkMax"}, $Info{"${p}DirCnt"}); } } } else { $CmdJob = ""; } } else { # # Queue BackupPC_link to complete the backup # processing for this host. # if ( defined($Status{$host}) && ($Status{$host}{reason} eq "Reason_backup_done" || $Status{$host}{needLink}) ) { QueueLink($host); } elsif ( defined($Status{$host}) ) { $Status{$host}{state} = "Status_idle"; } } delete($Jobs{$host}); $Status{$host}{activeJob} = 0 if ( defined($Status{$host}) ); } } # # When we are idle (empty Jobs, CmdQueue, BgQueue, UserQueue) we # 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 # be sure whether a DHCP host is alive or dead until we have passed # over all the DHCP pool. # return if ( @CmdQueue || @BgQueue || @UserQueue || keys(%Jobs) > 1 ); foreach my $host ( keys(%Status) ) { next if ( $Status{$host}{dhcpCheckCnt} <= 0 ); $Status{$host}{deadCnt} += $Status{$host}{dhcpCheckCnt}; $Status{$host}{dhcpCheckCnt} = 0; if ( $Status{$host}{deadCnt} >= $Conf{BlackoutBadPingLimit} ) { $Status{$host}{aliveCnt} = 0; } } } ############################################################################ # Main_Check_Client_Messages($fdRead) # # Check for, and process, any output from our clients. Also checks # for new connections to our SERVER_UNIX and SERVER_INET sockets. ############################################################################ sub Main_Check_Client_Messages { my($fdRead) = @_; foreach my $client ( keys(%Clients) ) { next if ( !vec($fdRead, $Clients{$client}{fn}, 1) ); my($mesg, $host); # # do a last check to make sure there is something to read so # we are absolutely sure we won't block. # vec(my $readMask, $Clients{$client}{fn}, 1) = 1; if ( !select($readMask, undef, undef, 0.0) ) { print(LOG $bpc->timeStamp, "Botch in Main_Check_Client_Messages:" . " nothing to read from $client. Debug dump:\n"); my($dump) = Data::Dumper->new( [ \%Clients, \%Jobs, \$FDread, \$fdRead], [qw(*Clients, *Jobs *FDread, *fdRead)]); $dump->Indent(1); print(LOG $dump->Dump); next; } my $nbytes = sysread($Clients{$client}{fh}, $mesg, 1024); $Clients{$client}{mesg} .= $mesg if ( $nbytes > 0 ); # # Process any complete lines received from this client. # while ( $Clients{$client}{mesg} =~ /(.*?)[\n\r]+(.*)/s ) { my($reply); my $cmd = $1; $Clients{$client}{mesg} = $2; # # Authenticate the message by checking the MD5 digest # my $md5 = Digest::MD5->new; if ( $cmd !~ /^(.{22}) (.*)/ || ($md5->add($Clients{$client}{seed} . $Clients{$client}{mesgCnt} . $Conf{ServerMesgSecret} . $2), $md5->b64digest ne $1) ) { print(LOG $bpc->timeStamp, "Corrupted message '$cmd' from" . " client '$Clients{$client}{clientName}':" . " shutting down client connection\n"); $nbytes = 0; last; } $Clients{$client}{mesgCnt}++; $cmd = decode_utf8($2); if ( $cmd =~ /^stop (\S+)\s+(\S+)\s+(\S*)/ ) { $host = $1; my $user = $2; my $backoff = $3; $host = $bpc->uriUnesc($host); if ( $CmdJob ne $host && defined($Status{$host}) && defined($Jobs{$host}) ) { print(LOG $bpc->timeStamp, "Stopping current $Jobs{$host}{type} of $host," . " request by $user (backoff=$backoff)\n"); kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); # # Don't close the pipe now; wait until the child # really exits later. Otherwise close() will # block until the child has exited. # old code: ##vec($FDread, $Jobs{$host}{fn}, 1) = 0; ##close($Jobs{$host}{fh}); ##delete($Jobs{$host}); $Status{$host}{state} = "Status_idle"; if ( $Jobs{$host}{type} eq "restore" ) { $Status{$host}{reason} = "Reason_restore_canceled_by_user"; } elsif ( $Jobs{$host}{type} eq "archive" ) { $Status{$host}{reason} = "Reason_archive_canceled_by_user"; } else { $Status{$host}{reason} = "Reason_backup_canceled_by_user"; } $Status{$host}{activeJob} = 0; $Status{$host}{startTime} = time; $reply = "ok: $Jobs{$host}{type} of $host canceled"; } elsif ( $BgQueueOn{$host} || $UserQueueOn{$host} ) { print(LOG $bpc->timeStamp, "Stopping pending backup of $host," . " request by $user (backoff=$backoff)\n"); @BgQueue = grep($_->{host} ne $host, @BgQueue); @UserQueue = grep($_->{host} ne $host, @UserQueue); $BgQueueOn{$host} = $UserQueueOn{$host} = 0; $reply = "ok: pending backup of $host canceled"; } else { print(LOG $bpc->timeStamp, "Nothing to do for stop backup of $host," . " request by $user (backoff=$backoff)\n"); $reply = "ok: no backup was pending or running"; } if ( defined($Status{$host}) && $backoff ne "" ) { if ( $backoff > 0 ) { $Status{$host}{backoffTime} = time + $backoff * 3600; } else { delete($Status{$host}{backoffTime}); } } } elsif ( $cmd =~ /^backup all$/ ) { QueueAllPCs(); } elsif ( $cmd =~ /^BackupPC_nightly run$/ ) { $RunNightlyWhenIdle = 1; } elsif ( $cmd =~ /^backup (\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ ) { my $hostIP = $1; $host = $2; my $user = $3; my $backupType = $4; $host = $bpc->uriUnesc($host); $hostIP = $bpc->uriUnesc($hostIP); if ( !defined($Hosts->{$host}) ) { print(LOG $bpc->timeStamp, "User $user requested backup of unknown host" . " $host\n"); $reply = "error: unknown host $host"; } else { # # Handle numeric backupType for backward compatibility # (technically -1 is a new feature for auto) # $backupType = 'auto' if ( $backupType eq '-1' ); $backupType = 'doIncr' if ( $backupType eq '0' ); $backupType = 'doFull' if ( $backupType eq '1' ); if ( $backupType !~ /^doIncr|doFull|autoIncr|autoFull|auto$/i ) { $reply = "error: unknown backup type $backupType"; } else { print(LOG $bpc->timeStamp, "User $user requested backup of $host" . " ($hostIP)\n"); if ( $BgQueueOn{$hostIP} ) { @BgQueue = grep($_->{host} ne $hostIP, @BgQueue); $BgQueueOn{$hostIP} = 0; } if ( $UserQueueOn{$hostIP} ) { @UserQueue = grep($_->{host} ne $hostIP, @UserQueue); $UserQueueOn{$hostIP} = 0; } my $status = QueueOnePC($host, $hostIP, $user, 'user', $backupType); if ( $status == 0 ) { $reply = "ok: requested backup of $host ($backupType)"; } elsif ( $status == 1 ) { #should never see this we just dequeued it $reply = "warning: $host was already queued." . " Ignoring this request"; } elsif ( $status == 2 ) { print(LOG $bpc->timeStamp, "Disk too full ($Info{DUlastValue}%)." . " Not queueing backup of $host\n"); $reply = "error: disk too full ($Info{DUlastValue}%)"; $Info{DUDailySkipHostCnt}++; } elsif ( $status == 3 ) { # should never reach this because # it's set to "user" above $reply = "error: unknown queue name"; } else { $reply = "error: unknown queue status $status"; if ( $BgQueueOn{$hostIP} || $UserQueueOn{$hostIP} ) { $reply .= ". Host is queued."; } else { $reply .= ". Host is not queued."; } } } } } elsif ( $cmd =~ /^archive (\S+)\s+(\S+)\s+(\S+)/ ) { my $user = $1; my $archivehost = $2; my $reqFileName = $3; $host = $bpc->uriUnesc($archivehost); if ( !defined($Status{$host}) ) { print(LOG $bpc->timeStamp, "User $user requested archive of unknown archive host" . " $host"); $reply = "archive error: unknown archive host $host"; } else { print(LOG $bpc->timeStamp, "User $user requested archive on $host" . " ($host)\n"); if ( defined($Jobs{$host}) ) { $reply = "Archive currently running on $host, please try later"; } else { unshift(@UserQueue, { host => $host, user => $user, reqFileName => $reqFileName, reqTime => time, dhcp => 0, archive => 1, userReq => 1, }); $UserQueueOn{$host} = 1; $reply = "ok: requested archive on $host"; } } } elsif ( $cmd =~ /^restore (\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ ) { my $hostIP = $1; $host = $2; my $user = $3; my $reqFileName = $4; $host = $bpc->uriUnesc($host); $hostIP = $bpc->uriUnesc($hostIP); if ( !defined($Hosts->{$host}) ) { print(LOG $bpc->timeStamp, "User $user requested restore to unknown host" . " $host"); $reply = "restore error: unknown host $host"; } else { print(LOG $bpc->timeStamp, "User $user requested restore to $host" . " ($hostIP)\n"); unshift(@UserQueue, { host => $host, hostIP => $hostIP, reqFileName => $reqFileName, reqTime => time, dhcp => 0, restore => 1, userReq => 1, }); $UserQueueOn{$host} = 1; if ( defined($Jobs{$host}) ) { $reply = "ok: requested restore of $host, but a" . " job is currently running," . " so this request will start later"; } else { $reply = "ok: requested restore of $host"; } } } elsif ( $cmd =~ /^status\s*(.*)/ ) { my($args) = $1; my($dump, @values, @names); foreach my $type ( split(/\s+/, $args) ) { if ( $type =~ /^queues/ ) { push(@values, \@BgQueue, \@UserQueue, \@CmdQueue); push(@names, qw(*BgQueue *UserQueue *CmdQueue)); } elsif ( $type =~ /^jobs/ ) { push(@values, \%Jobs); push(@names, qw(*Jobs)); } elsif ( $type =~ /^queueLen/ ) { push(@values, { BgQueue => scalar(@BgQueue), UserQueue => scalar(@UserQueue), CmdQueue => scalar(@CmdQueue), }); push(@names, qw(*QueueLen)); } elsif ( $type =~ /^info/ ) { push(@values, \%Info); push(@names, qw(*Info)); } elsif ( $type =~ /^hosts/ ) { push(@values, \%Status); push(@names, qw(*Status)); } elsif ( $type =~ /^host\((.*)\)/ ) { my $h = $bpc->uriUnesc($1); if ( defined($Status{$h}) ) { push(@values, { %{$Status{$h}}, BgQueueOn => $BgQueueOn{$h}, UserQueueOn => $UserQueueOn{$h}, CmdQueueOn => $CmdQueueOn{$h}, }); push(@names, qw(*StatusHost)); } else { print(LOG $bpc->timeStamp, "Unknown host $h for status request\n"); } } else { print(LOG $bpc->timeStamp, "Unknown status request $type\n"); } } $dump = Data::Dumper->new(\@values, \@names); $dump->Indent(0); $reply = $dump->Dump; } elsif ( $cmd =~ /^link\s+(.+)/ ) { my($host) = $1; $host = $bpc->uriUnesc($host); QueueLink($host); } elsif ( $cmd =~ /^log\s+(.*)/ ) { print(LOG $bpc->timeStamp, "$1\n"); } elsif ( $cmd =~ /^server\s+(\w+)/ ) { my($type) = $1; if ( $type eq 'reload' ) { ServerReload("Reloading config/host files via CGI request"); } elsif ( $type eq 'shutdown' ) { $reply = "Shutting down...\n"; syswrite($Clients{$client}{fh}, $reply, length($reply)); ServerShutdown("Server shutting down..."); } } elsif ( $cmd =~ /^quit/ || $cmd =~ /^exit/ ) { $nbytes = 0; last; } else { print(LOG $bpc->timeStamp, "Unknown command $cmd\n"); $reply = "error: bad command $cmd"; } # # send a reply to the client, at a minimum "ok\n". # $reply = "ok" if ( $reply eq "" ); $reply .= "\n"; syswrite($Clients{$client}{fh}, $reply, length($reply)); } # # Detect possible denial-of-service attack from sending a huge line # (ie: never terminated). 32K seems to be plenty big enough as # a limit. # if ( length($Clients{$client}{mesg}) > 32 * 1024 ) { print(LOG $bpc->timeStamp, "Line too long from client" . " '$Clients{$client}{clientName}':" . " shutting down client connection\n"); $nbytes = 0; } # # Shut down the client connection if we read EOF # if ( $nbytes <= 0 ) { close($Clients{$client}{fh}); vec($FDread, $Clients{$client}{fn}, 1) = 0; delete($Clients{$client}); } } # # Accept any new connections on each of our listen sockets # if ( vec($fdRead, fileno(SERVER_UNIX), 1) ) { local(*CLIENT); my $paddr = accept(CLIENT, SERVER_UNIX); $ClientConnCnt++; $Clients{$ClientConnCnt}{clientName} = "unix socket"; $Clients{$ClientConnCnt}{mesg} = ""; $Clients{$ClientConnCnt}{fh} = *CLIENT; $Clients{$ClientConnCnt}{fn} = fileno(CLIENT); vec($FDread, $Clients{$ClientConnCnt}{fn}, 1) = 1; # # Generate and send unique seed for MD5 digests to avoid # replay attacks. See BackupPC::Lib::ServerMesg(). # my $seed = time . ",$ClientConnCnt,$$,0\n"; $Clients{$ClientConnCnt}{seed} = $seed; $Clients{$ClientConnCnt}{mesgCnt} = 0; syswrite($Clients{$ClientConnCnt}{fh}, $seed, length($seed)); } if ( $ServerInetPort > 0 && vec($fdRead, fileno(SERVER_INET), 1) ) { local(*CLIENT); my $paddr = accept(CLIENT, SERVER_INET); my($port,$iaddr) = sockaddr_in($paddr); my $name = gethostbyaddr($iaddr, AF_INET); $ClientConnCnt++; $Clients{$ClientConnCnt}{mesg} = ""; $Clients{$ClientConnCnt}{fh} = *CLIENT; $Clients{$ClientConnCnt}{fn} = fileno(CLIENT); $Clients{$ClientConnCnt}{clientName} = "$name:$port"; vec($FDread, $Clients{$ClientConnCnt}{fn}, 1) = 1; # # Generate and send unique seed for MD5 digests to avoid # replay attacks. See BackupPC::Lib::ServerMesg(). # my $seed = time . ",$ClientConnCnt,$$,$port\n"; $Clients{$ClientConnCnt}{seed} = $seed; $Clients{$ClientConnCnt}{mesgCnt} = 0; syswrite($Clients{$ClientConnCnt}{fh}, $seed, length($seed)); } } ########################################################################### # Miscellaneous subroutines ########################################################################### # # Write the current status to $LogDir/status.pl # sub StatusWrite { my($dump) = Data::Dumper->new( [ \%Info, \%Status], [qw(*Info *Status)]); $dump->Indent(1); my $text = $dump->Dump; $bpc->{storage}->TextFileWrite("$LogDir/status.pl", $text); } # # Compare function for host sort. Hosts with errors go first, # sorted with the oldest errors first. The remaining hosts # are sorted so that those with the oldest backups go first. # sub HostSortCompare { # # Hosts with errors go before hosts without errors # return -1 if ( $Status{$a}{error} ne "" && $Status{$b}{error} eq "" ); # # Hosts with no errors go after hosts with errors # return 1 if ( $Status{$a}{error} eq "" && $Status{$b}{error} ne "" ); # # hosts with the older last good backups sort earlier # my $r = $Status{$a}{lastGoodBackupTime} <=> $Status{$b}{lastGoodBackupTime}; return $r if ( $r ); # # Finally, just sort based on host name # return $a cmp $b; } # # Attempt to queue a host. # Returns 0 on success; 1 if host is already queued; # 2 if host was skipped; 3 on invalid queue name # # $host is the client's host name # $hostIP is usually the client's host name too, or IP address # if the user specified it in the manual backup command # $user is the user name, or BackupPC by default # $queue is which queue to use ("bg" by default) # $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 # job running # sub QueueOnePC { my($host, $hostIP, $user, $queue, $backupType) = @_; my $retVal = 0; $user = "BackupPC" if ( $user eq '' ); $queue = "bg" if ( $queue eq '' && $user eq 'BackupPC' ); $backupType = "auto" if ( $backupType eq '' ); delete($Status{$host}{backoffTime}) if ( defined($Status{$host}{backoffTime}) && $Status{$host}{backoffTime} < time ); return 1 if ( $BgQueueOn{$host} || $UserQueueOn{$host} ); if ( $Hosts->{$host}{dhcp} ) { $Status{$host}{dhcpCheckCnt}++; if ( $RunNightlyWhenIdle ) { # # Once per night queue a check for DHCP hosts that just # checks for expired dumps. We need to do this to handle # 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. # Normally expiry checks are done by BackupPC_dump only # after the DHCP hosts has been detected on the network. # unshift(@BgQueue, {host => $hostIP, user => $user, reqTime => time, dhcp => 0, dumpExpire => 1}); $BgQueueOn{$host} = 1; } } else { # # this is a fixed ip host or DHCP ip address: queue it # if ( $Info{DUlastValue} > $Conf{DfMaxUsagePct} ) { # # Since we are out of disk space, instead of queuing # a regular job, queue an expire check instead. That # way if the admin reduces the number of backups to # keep then we will actually delete them. Otherwise # BackupPC_dump will never run since we have exceeded # the limit. # $retVal = 2; unshift(@BgQueue, {host => $hostIP, user => $user, reqTime => time, dumpExpire => 1}); $BgQueueOn{$host} = 1; } elsif( $queue eq 'bg' ) { # # Queue regular background backup # unshift(@BgQueue, {host => $hostIP, user => $user, reqTime => time, backupType => $backupType}); $BgQueueOn{$host} = 1; } elsif( $queue eq 'user' ) { # # Queue user backup # unshift(@UserQueue, {host => $hostIP, user => $user, reqTime => time, backupType => $backupType}); $UserQueueOn{$host} = 1; } else { # unknown $queue type $retVal = 3; } } return $retVal; } # # Queue all the hosts for backup. This means queuing all the fixed # ip hosts and all the dhcp address ranges. We also additionally # queue the dhcp hosts with a -e flag to check for expired dumps. # sub QueueAllPCs { my $nSkip = 0; foreach my $host ( sort HostSortCompare keys(%$Hosts) ) { $nSkip++ if ( QueueOnePC($host, $host, 'BackupPC', 'bg', 'auto') == 2 ); } foreach my $dhcp ( @{$Conf{DHCPAddressRanges}} ) { for ( my $i = $dhcp->{first} ; $i <= $dhcp->{last} ; $i++ ) { my $ipAddr = "$dhcp->{ipAddrBase}.$i"; $nSkip++ if ( QueueOnePC($ipAddr, $ipAddr, 'BackupPC', 'bg', 'dhcpPoll') == 2 ); } } if ( $nSkip ) { print(LOG $bpc->timeStamp, "Disk too full ($Info{DUlastValue}%); skipped $nSkip hosts\n"); $Info{DUDailySkipHostCnt} += $nSkip; } } # # Queue a BackupPC_link for the given host # sub QueueLink { my($host) = @_; return if ( $CmdQueueOn{$host} ); $Status{$host}{state} = "Status_link_pending"; $Status{$host}{needLink} = 0; unshift(@CmdQueue, { host => $host, user => "BackupPC", reqTime => time, cmd => ["$BinDir/BackupPC_link", $host], }); $CmdQueueOn{$host} = 1; } # # 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 # update the hosts file on changes. # # This function is called at startup, SIGHUP, and on each wakeup. # It returns 1 on success and undef on failure. # sub HostsUpdate { my($force) = @_; my $newHosts; # # Nothing to do if we already have the current hosts file # return 1 if ( !$force && defined($Hosts) && $Info{HostsModTime} == $bpc->HostsMTime() ); if ( !defined($newHosts = $bpc->HostInfoRead()) ) { print(LOG $bpc->timeStamp, "Can't read hosts file!\n"); return; } print(LOG $bpc->timeStamp, "Reading hosts file\n"); $Hosts = $newHosts; $Info{HostsModTime} = $bpc->HostsMTime(); # # Now update %Status in case any hosts have been added or deleted # foreach my $host ( sort(keys(%$Hosts)) ) { next if ( defined($Status{$host}) ); $Status{$host}{state} = "Status_idle"; print(LOG $bpc->timeStamp, "Added host $host to backup list\n"); } foreach my $host ( sort(keys(%Status)) ) { next if ( $host eq $bpc->trashJob || $bpc->isAdminJob($host) || defined($Hosts->{$host}) || defined($Jobs{$host}) || $BgQueueOn{$host} || $UserQueueOn{$host} || $CmdQueueOn{$host} ); print(LOG $bpc->timeStamp, "Deleted host $host from backup list\n"); delete($Status{$host}); } return 1; } # # Remember the signal name for later processing # sub catch_signal { if ( $SigName ) { $SigName = shift; foreach my $host ( keys(%Jobs) ) { kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); } # # In case we are inside the exit handler, reopen the log file # close(LOG); LogFileOpen(); print(LOG "Fatal error: unhandled signal $SigName\n"); unlink("$LogDir/BackupPC.pid"); confess("Got new signal $SigName... quitting\n"); } else { $SigName = shift; } } # # Open the log file and point STDOUT and STDERR there too # sub LogFileOpen { mkpath($LogDir, 0, 0777) if ( !-d $LogDir ); open(LOG, ">>$LogDir/LOG") || die("Can't create LOG file $LogDir/LOG"); close(STDOUT); close(STDERR); open(STDOUT, ">&LOG"); open(STDERR, ">&LOG"); select(LOG); $| = 1; select(STDERR); $| = 1; select(STDOUT); $| = 1; } # # Initialize the unix-domain and internet-domain sockets that # we listen to for client connections (from the CGI script and # some of the BackupPC sub-programs). # sub ServerSocketInit { if ( !defined(fileno(SERVER_UNIX)) ) { # # one-time only: initialize unix-domain socket # if ( !socket(SERVER_UNIX, PF_UNIX, SOCK_STREAM, 0) ) { print(LOG $bpc->timeStamp, "unix socket() failed: $!\n"); exit(1); } my $sockFile = "$LogDir/BackupPC.sock"; unlink($sockFile); if ( !bind(SERVER_UNIX, sockaddr_un($sockFile)) ) { print(LOG $bpc->timeStamp, "unix bind() failed: $!\n"); exit(1); } if ( !listen(SERVER_UNIX, SOMAXCONN) ) { print(LOG $bpc->timeStamp, "unix listen() failed: $!\n"); exit(1); } vec($FDread, fileno(SERVER_UNIX), 1) = 1; } return if ( $ServerInetPort == $Conf{ServerPort} ); if ( $ServerInetPort > 0 ) { vec($FDread, fileno(SERVER_INET), 1) = 0; close(SERVER_INET); $ServerInetPort = -1; } if ( $Conf{ServerPort} > 0 ) { # # Setup a socket to listen on $Conf{ServerPort} # my $proto = getprotobyname('tcp'); if ( !socket(SERVER_INET, PF_INET, SOCK_STREAM, $proto) ) { print(LOG $bpc->timeStamp, "inet socket() failed: $!\n"); exit(1); } if ( !setsockopt(SERVER_INET, SOL_SOCKET, SO_REUSEADDR, pack("l",1)) ) { print(LOG $bpc->timeStamp, "setsockopt() failed: $!\n"); exit(1); } if ( !bind(SERVER_INET, sockaddr_in($Conf{ServerPort}, INADDR_ANY)) ) { print(LOG $bpc->timeStamp, "inet bind() failed: $!\n"); exit(1); } if ( !listen(SERVER_INET, SOMAXCONN) ) { print(LOG $bpc->timeStamp, "inet listen() failed: $!\n"); exit(1); } vec($FDread, fileno(SERVER_INET), 1) = 1; $ServerInetPort = $Conf{ServerPort}; } } # # Reload the server. Used by Main_Process_Signal when $SigName eq "HUP" # or when the command "server reload" is received. # sub ServerReload { my($mesg) = @_; $mesg = $bpc->ConfigRead() || $mesg; print(LOG $bpc->timeStamp, "$mesg\n"); $Info{ConfigModTime} = $bpc->ConfigMTime(); %Conf = $bpc->Conf(); umask($Conf{UmaskMode}); ServerSocketInit(); HostsUpdate(0); $NextWakeup = 0; $Info{ConfigLTime} = time; } # # Gracefully shutdown the server. Used by Main_Process_Signal when # $SigName ne "" && $SigName ne "HUP" or when the command # "server shutdown" is received. # sub ServerShutdown { my($mesg) = @_; print(LOG $bpc->timeStamp, "$mesg\n"); if ( keys(%Jobs) ) { foreach my $host ( keys(%Jobs) ) { kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); } sleep(1); foreach my $host ( keys(%Jobs) ) { kill($bpc->sigName2num("KILL"), $Jobs{$host}{pid}); } %Jobs = (); } delete($Info{pid}); StatusWrite(); unlink("$LogDir/BackupPC.pid"); exit(1); } BackupPC-3.3.2/bin/BackupPC_archive0000444000076500000240000002622713042250554015752 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_archive: Archive files for an archive client. # # DESCRIPTION # # Usage: BackupPC_archive # # AUTHOR # Josh Marshall # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; use BackupPC::FileZIO; use BackupPC::Xfer::Archive; use vars qw( %ArchiveReq ); ########################################################################### # Initialize ########################################################################### die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my $NeedPostCmd; my($user, $host, $client, $reqFileName, %stat); $bpc->ChildInit(); if ( @ARGV != 3 ) { print("usage: $0 \n"); exit(1); } $user = $1 if ( $ARGV[0] =~ /(.+)/ ); $client = $1 if ( $ARGV[1] =~ /(.+)/ ); if ( $ARGV[2] !~ /^([\w\.\s-]+)$/ ) { print("$0: bad reqFileName (arg #3): $ARGV[2]\n"); exit(1); } $reqFileName = $1; my $startTime = time(); my $Dir = "$TopDir/pc/$client"; my @xferPid = (); # # Catch various signals # $SIG{INT} = \&catch_signal; $SIG{ALRM} = \&catch_signal; $SIG{TERM} = \&catch_signal; $SIG{PIPE} = \&catch_signal; $SIG{STOP} = \&catch_signal; $SIG{TSTP} = \&catch_signal; $SIG{TTIN} = \&catch_signal; my $Pid = $$; mkpath($Dir, 0, 0777) if ( !-d $Dir ); if ( !-f "$Dir/LOCK" ) { open(LOCK, ">", "$Dir/LOCK") && close(LOCK); } my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $logPath = sprintf("$Dir/LOG.%02d%04d", $mon + 1, $year + 1900); if ( !-f $logPath ) { # # Compress and prune old log files # my $lastLog = $Conf{MaxOldPerPCLogFiles} - 1; foreach my $file ( $bpc->sortedPCLogFiles($client) ) { if ( $lastLog <= 0 ) { unlink($file); next; } $lastLog--; next if ( $file =~ /\.z$/ || !$Conf{CompressLevel} ); BackupPC::FileZIO->compressCopy($file, "$file.z", undef, $Conf{CompressLevel}, 1); } } open(LOG, ">>", $logPath); select(LOG); $| = 1; select(STDOUT); # # Read the request file # if ( !(my $ret = do "$Dir/$reqFileName") ) { my $err; if ( $@ ) { $err = "couldn't parse $Dir/$reqFileName: $@"; } elsif ( !defined($ret) ) { $err = "couldn't do $Dir/$reqFileName: $!"; } else { $err = "couldn't run $Dir/$reqFileName"; } $stat{hostError} = $err; exit(ArchiveCleanup($client)); } # # Re-read config file, so we can include the PC-specific config # if ( defined(my $error = $bpc->ConfigRead($client)) ) { $stat{hostError} = "Can't read PC's config file: $error"; exit(ArchiveCleanup($client)); } %Conf = $bpc->Conf(); # # Make sure we eventually timeout if there is no activity from # the data transport program. # alarm($Conf{ClientTimeout}); # # See if the host name is aliased # if ( $Conf{ClientNameAlias} ne "" ) { $host = $Conf{ClientNameAlias}; } else { $host = $client; } # # Setup file extension for compression and open ArchiveLOG output file # if ( $Conf{CompressLevel} && !BackupPC::FileZIO->compOk ) { $stat{hostError} = "Compress::Zlib not found"; exit(ArchiveCleanup($client)); } my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; my $ArchiveLOG = BackupPC::FileZIO->open("$Dir/ArchiveLOG$fileExt", 1, $Conf{CompressLevel}); my($logMsg, $xfer); $stat{xferOK} = 1; $stat{hostAbort} = undef; $stat{hostError} = $stat{lastOutputLine} = undef; local(*RH, *WH); # # Run an optional pre-archive command # UserCommandRun("ArchivePreUserCmd"); if ( $? && $Conf{UserCmdCheckStatus} ) { $stat{hostError} = "ArchivePreUserCmd returned error status $?"; exit(ArchiveCleanup($client)); } $NeedPostCmd = 1; $xfer = BackupPC::Xfer::Archive->new($bpc); # # Run the transport program # my $xferArgs = { client => $client, host => $host, user => $ArchiveReq{user}, type => "archive", XferLOG => $ArchiveLOG, XferMethod => $Conf{XferMethod}, pathHdrSrc => $ArchiveReq{pathHdrSrc}, pathHdrDest => $ArchiveReq{pathHdrDest}, HostList => \@{$ArchiveReq{HostList}}, BackupList => \@{$ArchiveReq{BackupList}}, archiveloc => $ArchiveReq{archiveloc}, parfile => $ArchiveReq{parfile}, compression => $ArchiveReq{compression}, compext => $ArchiveReq{compext}, splitsize => $ArchiveReq{splitsize}, pidHandler => \&pidHandler, }; $xfer->args($xferArgs); if ( !defined($logMsg = $xfer->start()) ) { UserCommandRun("ArchivePostUserCmd") if ( $NeedPostCmd ); $stat{hostError} = "xfer start failed: ", $xfer->errStr; exit(ArchiveCleanup($client)); } print(LOG $bpc->timeStamp, "Starting archive\n"); print("started_archive\n"); $xfer->run(); $stat{xferOK} = 0 if ( defined($stat{hostError} = $xfer->errStr) ); alarm(0); exit(ArchiveCleanup($client)); ########################################################################### # Subroutines ########################################################################### sub catch_signal { my $signame = shift; # # Children quit quietly on ALRM # exit(1) if ( $Pid != $$ && $signame eq "ALRM" ); # # Ignore signals in children # return if ( $Pid != $$ ); # # Note: needs to be tested for each kind of XferMethod # print(LOG $bpc->timeStamp, "cleaning up after signal $signame\n"); $SIG{$signame} = 'IGNORE'; $ArchiveLOG->write(\"exiting after signal $signame\n"); $stat{xferOK} = 0; if ( $signame eq "INT" ) { $stat{hostError} = "aborted by user (signal=$signame)"; } else { $stat{hostError} = "aborted by signal=$signame"; } exit(ArchiveCleanup($client)); } # # Cleanup and update the archive status # sub ArchiveCleanup { my($client) = @_; $stat{xferOK} = 0 if ( $stat{hostError} || $stat{hostAbort} ); if ( !$stat{xferOK} ) { # # Kill off the tranfer program, first nicely then forcefully. # We use negative PIDs to make sure all processes in each # group get the signal. # if ( @xferPid ) { foreach my $pid ( @xferPid ) { kill($bpc->sigName2num("INT"), -$pid); } sleep(1); foreach my $pid ( @xferPid ) { kill($bpc->sigName2num("KILL"), -$pid); } } } my $lastNum = -1; my @Archives; @Archives = $bpc->ArchiveInfoRead($client); for ( my $i = 0 ; $i < @Archives ; $i++ ) { $lastNum = $Archives[$i]{num} if ( $lastNum < $Archives[$i]{num} ); } $lastNum++; # # Run an optional post-archive command # if ( $NeedPostCmd ) { UserCommandRun("ArchivePostUserCmd"); if ( $? && $Conf{UserCmdCheckStatus} ) { $stat{hostError} = "RestorePreUserCmd returned error status $?"; $stat{xferOK} = 0; } } rename("$Dir/ArchiveLOG$fileExt", "$Dir/ArchiveLOG.$lastNum$fileExt"); rename("$Dir/$reqFileName", "$Dir/ArchiveInfo.$lastNum"); my $endTime = time(); # # If the archive failed, clean up # if ( !$stat{xferOK} ) { $stat{hostError} = $stat{lastOutputLine} if ( $stat{hostError} eq "" ); $stat{hostAbort} = 1; $ArchiveLOG->write(\"Archive failed: $stat{hostError}") if ( defined($ArchiveLOG) ); } $ArchiveLOG->close() if ( defined($ArchiveLOG) ); # # Add the new archive information to the archive file # @Archives = $bpc->ArchiveInfoRead($client); my $i = @Archives; $Archives[$i]{num} = $lastNum; $Archives[$i]{startTime} = $startTime; $Archives[$i]{endTime} = $endTime; $Archives[$i]{result} = $stat{xferOK} ? "ok" : "failed"; $Archives[$i]{errorMsg} = $stat{hostError}; while ( @Archives > $Conf{ArchiveInfoKeepCnt} ) { my $num = $Archives[0]{num}; unlink("$Dir/ArchiveLOG.$num.z"); unlink("$Dir/ArchiveLOG.$num"); unlink("$Dir/ArchiveInfo.$num"); shift(@Archives); } $bpc->ArchiveInfoWrite($client, @Archives); if ( !$stat{xferOK} ) { print(LOG $bpc->timeStamp, "Archive failed ($stat{hostError})\n"); print("archive failed: $stat{hostError}\n"); return 1; } else { print(LOG $bpc->timeStamp, "Archive Complete\n"); print("archive complete\n"); return; } } # # The Xfer method might tell us from time to time about processes # it forks. We tell BackupPC about this (for status displays) and # keep track of the pids in case we cancel the backup # sub pidHandler { @xferPid = @_; @xferPid = grep(/./, @xferPid); return if ( !@xferPid ); my @pids = @xferPid; my $str = join(",", @pids); $ArchiveLOG->write(\"Xfer PIDs are now $str\n") if ( defined($ArchiveLOG) ); print("xferPids $str\n"); } # # Run an optional pre- or post-dump command # sub UserCommandRun { my($cmdType) = @_; return if ( !defined($Conf{$cmdType}) ); my $vars = { xfer => $xfer, client => $client, host => $host, user => $user, share => $ArchiveReq{shareDest}, XferMethod => $Conf{XferMethod}, HostList => \@{$ArchiveReq{HostList}}, BackupList => \@{$ArchiveReq{BackupList}}, archiveloc => $ArchiveReq{archiveloc}, parfile => $ArchiveReq{parfile}, compression => $ArchiveReq{compression}, compext => $ArchiveReq{compext}, splitsize => $ArchiveReq{splitsize}, sshPath => $Conf{SshPath}, LOG => *LOG, XferLOG => $ArchiveLOG, stat => \%stat, xferOK => $stat{xferOK} || 0, type => "archive", cmdType => $cmdType, }; my $cmd = $bpc->cmdVarSubstitute($Conf{$cmdType}, $vars); $ArchiveLOG->write(\"Executing $cmdType: @$cmd\n"); # # Run the user's command, dumping the stdout/stderr into the # Xfer log file. Also supply the optional $vars and %Conf in # case the command is really perl code instead of a shell # command. # $bpc->cmdSystemOrEval($cmd, sub { $ArchiveLOG->write(\$_[0]); }, $vars, \%Conf); } BackupPC-3.3.2/bin/BackupPC_archiveHost0000555000076500000240000001233613042250554016607 0ustar craigstaff#!/usr/bin/perl #============================================================= # # BackupPC_archiveHost: Archive files for a single host # # DESCRIPTION # # Usage: BackupPC_archiveHost tarCreatePath splitPath parPath host bkupNum \ # compPath fileExt splitSize outLoc parFile share # # This script is run for each host to create an archive. # # This script is executed by BackupPC_archive, based on the setting # of $Conf{ArchiveClientCmd}. This script can be copied and modified # for site-specific behavior. Update $Conf{ArchiveClientCmd} to point # at your customized archive script. # # AUTHOR # Craig Barratt # Josh Marshall # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; use File::Path; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; # # Pick up the command-line arguments # if ( @ARGV != 11 ) { print <new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); # # Make sure the specified programs are executable # foreach my $prog ( ($tarCreate, $compPath, $splitPath, $parPath) ) { if ( $prog =~ /[][;&()<>{}|^\n\r\t *\$\\'"`?]/ ) { print("Error: executable path $prog contains suspicious characters\n"); exit(1); } next if ( $prog eq "" || -x $prog ); print("Error: $prog is not an executable program\n"); exit(1); } my $mesg = "Writing tar archive for host $host, backup #$bkupNum"; # # Build the command we will run # $share = $bpc->shellEscape($share); $host = $bpc->shellEscape($host); $bkupNum = $bpc->shellEscape($bkupNum); $fileExt = $bpc->shellEscape($fileExt); $splitSize = $bpc->shellEscape($splitSize); $parfile = $bpc->shellEscape($parfile); my $outLocE = $bpc->shellEscape($outLoc); # # We prefer to use /bin/csh because the exit status of a pipeline # is non-zero if any command is non zero. In contrast, /bin/sh # and /bin/bash use the convention that the exit status is just # the exit status of the last command of the pipeline. # my @shell; if ( -x "/bin/csh" ) { @shell = ("/bin/csh", "-cf"); } elsif ( -x "/bin/sh" ) { @shell = ("/bin/sh", "-c"); } else { print("Error: Can't find executable /bin/csh or /bin/sh\n"); exit(1); } my $cmd = "$tarCreate -t -h $host -n $bkupNum -s $share . "; $cmd .= "| $compPath " if ( $compPath ne "cat" && $compPath ne "/bin/cat" && $compPath ne "" ); if ( -b $outLoc || -c $outLoc || -f $outLoc ) { # # Output file is a device or a regular file, so don't use split # $cmd .= ">> $outLocE"; $mesg .= " to $outLoc"; } else { mkpath($outLoc) if ( !-d $outLoc ); if ( !-d $outLoc ) { print("Error: unable to create output directory $outLoc\n"); exit(1); } if ( $splitSize > 0 && -x $splitPath ) { $cmd .= "| $splitPath -b $splitSize - $outLocE/$host.$bkupNum.tar$fileExt."; $mesg .= ", split to output files $outLocE/$host.$bkupNum.tar$fileExt.*"; } else { $cmd .= "> $outLocE/$host.$bkupNum.tar$fileExt"; $mesg .= " to output file $outLocE/$host.$bkupNum.tar$fileExt"; } } print("$mesg\n"); # # Run the command # my $ret = system(@shell, $cmd); if ( $ret ) { print("Executing: @shell $cmd\n"); print("Error: $tarCreate, compress or split failed\n"); exit(1); } # # Run optional parity file generation (only if the output is a directory, # ie: not a tape device). # if ( -d $outLoc && -x $parPath ) { if ( length($parfile) && $parfile != 0 ) { print("Running $parPath to create parity files\n"); my $parCmd = "$parPath c -r$parfile $outLocE/$host.$bkupNum.tar$fileExt.par2 $outLocE/$host.$bkupNum.tar$fileExt*"; $ret = system($parCmd); if ( $ret ) { print("Executing: $parCmd\n"); print("Error: $parPath failed\n"); exit(1); } } } BackupPC-3.3.2/bin/BackupPC_archiveStart0000555000076500000240000000767713042250554017003 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_archiveStart: start an archive request from the # command line. # # DESCRIPTION # # Usage: BackupPC_archiveStart archiveHost userName hosts... # # Initiates an archive request on archive host archiveHost # for the listed hosts. The latest backup for each host is # archived. The userName is name of the requesting user, # which appears in the log files. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2007-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use Getopt::Std; use BackupPC::Lib; die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my %opts; # no options currently if ( !getopts("", \%opts) || @ARGV < 3 ) { print STDERR <HostInfoRead(); my $ArchiveHost = $ARGV[0]; my $UserName = $ARGV[1]; my $TopDir = $bpc->{Conf}{TopDir}; if ( !defined($Hosts->{$ArchiveHost}) ) { print(STDERR "$0: archive host $ArchiveHost doesn't exist... quitting\n"); exit(1); } $bpc->ConfigRead($ArchiveHost); my(@HostList, @BackupList); for ( my $i = 2 ; $i < @ARGV ; $i++ ) { my $host = $ARGV[$i]; if ( !defined($Hosts->{$host}) ) { print(STDERR "$0: host $host doesn't exist... quitting\n"); exit(1); } my @backups = $bpc->BackupInfoRead($host); if ( !@backups ) { print(STDERR "$0: host $host doesn't have any backups... quitting\n"); exit(1); } push(@HostList, $host); push(@BackupList, $backups[$#backups]{num}); } my $ReqFileName; for ( my $i = 0 ; ; $i++ ) { $ReqFileName="archiveReq.$$.$i"; last if ( !-f "$TopDir/pc/$ArchiveHost/$ReqFileName" ); } my %ArchiveReq = ( archiveloc => $bpc->{Conf}{ArchiveDest}, archtype => 0, compression => $bpc->{Conf}{ArchiveComp} eq 'none' ? $bpc->{Conf}{CatPath} : ($bpc->{Conf}{ArchiveComp} eq 'gzip' ? $bpc->{Conf}{GzipPath} : $bpc->{Conf}{Bzip2Path}), compext => $bpc->{Conf}{ArchiveComp} eq 'none' ? '' : ($bpc->{Conf}{ArchiveComp} eq 'gzip' ? '.gz' : '.bz2'), parfile => $bpc->{Conf}{ArchivePar}, splitsize => '0000000', host => $ArchiveHost, HostList => \@HostList, BackupList => \@BackupList, user => $UserName, reqTime => time, ); my $archive = Data::Dumper->new([\%ArchiveReq], [qw(*ArchiveReq)]); $archive->Indent(1); if ( !open(REQ, ">", "$TopDir/pc/$ArchiveHost/$ReqFileName") ) { print(STDERR "$0: can't open/write request file $TopDir/pc/$ArchiveHost/$ReqFileName... quitting\n"); exit(1); } binmode(REQ); print REQ $archive->Dump; close(REQ); $bpc->ServerConnect($bpc->{Conf}{ServerHost}, $bpc->{Conf}{ServerPort}); my $reply = $bpc->ServerMesg("archive $UserName $ArchiveHost $ReqFileName"); $bpc->ServerDisconnect(); print("Sent archive request, reply: $reply\n"); exit(0); BackupPC-3.3.2/bin/BackupPC_attribPrint0000555000076500000240000000424213042250554016627 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_attribPrint: print the contents of attrib files. # # DESCRIPTION # # Usage: BackupPC_attribPrint attribPath # # Compression status of attrib path is based on $Conf{CompressLevel}. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2005-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; use BackupPC::Attrib qw(:all); use BackupPC::FileZIO; use Data::Dumper; die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); if ( @ARGV != 1 ) { print STDERR "Usage: $0 attribPath\n"; exit(1); } if ( !-f $ARGV[0] ) { print STDERR "$ARGV[0] does not exist\n"; exit(1); } my $attrib = BackupPC::Attrib->new({ compress => $Conf{CompressLevel} }); my($dir, $file); if ( $ARGV[0] =~ m{(.+)/(.+)} ) { $dir = $1; $file = $2; } else { $dir = $ARGV[0]; } if ( !$attrib->read($dir, $file) ) { print STDERR "Cannot read attrib file $ARGV[0]\n"; exit(1); } my $info = $attrib->get(); $Data::Dumper::Indent = 1; print Dumper($info); BackupPC-3.3.2/bin/BackupPC_dump0000555000076500000240000015135113042250554015276 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_dump: Dump a single client. # # DESCRIPTION # # Usage: BackupPC_dump [-i] [-f] [-F] [-I] [-d] [-e] [-v] # # Flags: # # -i Do an incremental dump, overriding any scheduling (but a full # dump will be done if no dumps have yet succeeded) # # -f Do a full dump, overriding any scheduling. # # -I Do an increment dump if the regular schedule requires a # full or incremental, otherwise do nothing (a full is done # if no dumps have yet succeeded) # # -F Do a full dump if the regular schedule requires a # full or incremental, otherwise do nothing # # -d Host is a DHCP pool address, and the client argument # just an IP address. We lookup the NetBios name from # the IP address. # # -e Just do an dump expiry check for the client. Don't do anything # else. This is used periodically by BackupPC to make sure that # dhcp hosts have correctly expired old backups. Without this, # dhcp hosts that are no longer on the network will not expire # old backups. # # -v verbose. for manual usage: prints failure reasons in more detail. # # BackupPC_dump is run periodically by BackupPC to backup $client. # The file $TopDir/pc/$client/backups is read to decide whether a # full or incremental backup needs to be run. If no backup is # scheduled, or a ping to $client fails, then BackupPC_dump quits. # # The backup is done using the selected XferMethod (smb, tar, rsync, # backuppcd etc), extracting the dump into $TopDir/pc/$client/new. # The xfer output is put into $TopDir/pc/$client/XferLOG. # # If the dump succeeds (based on parsing the output of the XferMethod): # - $TopDir/pc/$client/new is renamed to $TopDir/pc/$client/nnn, where # nnn is the next sequential dump number. # - $TopDir/pc/$client/XferLOG is renamed to $TopDir/pc/$client/XferLOG.nnn. # - $TopDir/pc/$client/backups is updated. # # If the dump fails: # - $TopDir/pc/$client/new is moved to $TopDir/trash for later removal. # - $TopDir/pc/$client/XferLOG is renamed to $TopDir/pc/$client/XferLOG.bad # for later viewing. # # BackupPC_dump communicates to BackupPC via printing to STDOUT. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; use BackupPC::FileZIO; use BackupPC::Storage; use BackupPC::Xfer; use Encode; use Socket; use File::Path; use File::Find; use Getopt::Std; ########################################################################### # Initialize ########################################################################### die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my $NeedPostCmd; my $Hosts; my $SigName; my $Abort; $bpc->ChildInit(); my %opts; if ( !getopts("defivFI", \%opts) || @ARGV != 1 ) { print("usage: $0 [-d] [-e] [-f] [-i] [-F] [-I] [-v] \n"); exit(1); } if ( $ARGV[0] !~ /^([\w\.\s-]+)$/ ) { print("$0: bad client name '$ARGV[0]'\n"); exit(1); } if ( (defined($opts{f}) + defined($opts{i}) + defined($opts{F}) + defined($opts{I})) > 1 ) { print("$0: exiting because you can only use one of -f, -i, -F, and -I\n"); exit(1); } my $client = $1; # BackupPC's client name (might not be real host name) my $hostIP; # this is the IP address my $host; # this is the real host name my($clientURI, $user); $bpc->verbose(1) if ( $opts{v} ); if ( $opts{d} ) { # # The client name $client is simply a DHCP address. We need to check # if there is any machine at this address, and if so, get the actual # host name via NetBios using nmblookup. # $hostIP = $client; if ( $bpc->CheckHostAlive($hostIP) < 0 ) { print(STDERR "Exiting because CheckHostAlive($hostIP) failed\n") if ( $opts{v} ); exit(1); } if ( $Conf{NmbLookupCmd} eq "" ) { print(STDERR "Exiting because \$Conf{NmbLookupCmd} is empty\n") if ( $opts{v} ); exit(1); } ($client, $user) = $bpc->NetBiosInfoGet($hostIP); if ( $client !~ /^([\w\.\s-]+)$/ ) { print(STDERR "Exiting because NetBiosInfoGet($hostIP) returned" . " '$client', an invalid host name\n") if ( $opts{v} ); exit(1) } $Hosts = $bpc->HostInfoRead($client); $host = $client; } else { $Hosts = $bpc->HostInfoRead($client); } if ( !defined($Hosts->{$client}) ) { print(STDERR "Exiting because host $client does not exist in the" . " hosts file\n") if ( $opts{v} ); exit(1) } my $Dir = "$TopDir/pc/$client"; my @xferPid = (); my $tarPid = -1; my $completionPercent; # # Re-read config file, so we can include the PC-specific config # $clientURI = $bpc->uriEsc($client); if ( defined(my $error = $bpc->ConfigRead($client)) ) { print("dump failed: Can't read PC's config file: $error\n"); exit(1); } %Conf = $bpc->Conf(); # # Catch various signals # $SIG{INT} = \&catch_signal; $SIG{ALRM} = \&catch_signal; $SIG{TERM} = \&catch_signal; $SIG{PIPE} = \&catch_signal; $SIG{STOP} = \&catch_signal; $SIG{TSTP} = \&catch_signal; $SIG{TTIN} = \&catch_signal; my $Pid = $$; # # Make sure we eventually timeout if there is no activity from # the data transport program. # alarm($Conf{ClientTimeout}); mkpath($Dir, 0, 0777) if ( !-d $Dir ); if ( !-f "$Dir/LOCK" ) { open(LOCK, ">", "$Dir/LOCK") && close(LOCK); } my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $logPath = sprintf("$Dir/LOG.%02d%04d", $mon + 1, $year + 1900); if ( !-f $logPath ) { # # Compress and prune old log files # my $lastLog = $Conf{MaxOldPerPCLogFiles} - 1; foreach my $file ( $bpc->sortedPCLogFiles($client) ) { if ( $lastLog <= 0 ) { unlink($file); next; } $lastLog--; next if ( $file =~ /\.z$/ || !$Conf{CompressLevel} ); BackupPC::FileZIO->compressCopy($file, "$file.z", undef, $Conf{CompressLevel}, 1); } } open(LOG, ">>", $logPath); select(LOG); $| = 1; select(STDOUT); # # For the -e option we just expire backups and quit # if ( $opts{e} ) { BackupExpire($client); exit(0); } # # For archive hosts we don't bother any further # if ($Conf{XferMethod} eq "archive" ) { print(STDERR "Exiting because the XferMethod is set to archive\n") if ( $opts{v} ); exit(0); } ########################################################################### # Figure out what to do and do it ########################################################################### # # See if we should skip this host during a certain range # of times. # my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}); if ( $err ne "" ) { print("Can't connect to server ($err)\n"); print(LOG $bpc->timeStamp, "Can't connect to server ($err)\n"); exit(1); } my $reply = $bpc->ServerMesg("status host($clientURI)"); $reply = $1 if ( $reply =~ /(.*)/s ); my(%StatusHost); eval($reply); $bpc->ServerDisconnect(); # # For DHCP tell BackupPC which host this is # if ( $opts{d} ) { if ( $StatusHost{activeJob} ) { # oops, something is already running for this host print(STDERR "Exiting because backup is already running for $client\n") if ( $opts{v} ); exit(0); } print("DHCP $hostIP $clientURI\n"); } my($needLink, @Backups, $type); my($incrBaseTime, $incrBaseBkupNum, $incrBaseLevel, $incrLevel); my $lastFullTime = 0; my $lastIncrTime = 0; my $partialIdx = -1; my $partialNum; my $partialFileCnt; my $lastBkupNum; my $lastPartial = 0; # # Maintain backward compatibility with $Conf{FullPeriod} == -1 or -2 # meaning disable backups # $Conf{BackupsDisable} = -$Conf{FullPeriod} if ( !$Conf{BackupsDisable} && $Conf{FullPeriod} < 0 ); if ( $Conf{BackupsDisable} == 1 && !$opts{f} && !$opts{i} || $Conf{BackupsDisable} == 2 ) { print(STDERR "Exiting because backups are disabled with" . " \$Conf{BackupsDisable} = $Conf{BackupsDisable}\n") if ( $opts{v} ); # # Tell BackupPC to ignore old failed backups on hosts that # have backups disabled. # print("backups disabled\n") if ( defined($StatusHost{errorTime}) && $StatusHost{reason} ne "Reason_backup_done" && time - $StatusHost{errorTime} > 4 * 24 * 3600 ); NothingToDo($needLink); } if ( !$opts{i} && !$opts{f} && $Conf{BlackoutGoodCnt} >= 0 && $StatusHost{aliveCnt} >= $Conf{BlackoutGoodCnt} ) { my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my($currHours) = $hour + $min / 60 + $sec / 3600; my $blackout; foreach my $p ( @{$Conf{BlackoutPeriods}} ) { # # Allow blackout to span midnight (specified by hourBegin # being greater than hourEnd) # next if ( ref($p->{weekDays}) ne "ARRAY" || !defined($p->{hourBegin}) || !defined($p->{hourEnd}) ); my $matchWday = $wday; if ( $p->{hourBegin} > $p->{hourEnd} ) { $blackout = $p->{hourBegin} <= $currHours || $currHours <= $p->{hourEnd}; if ( $currHours <= $p->{hourEnd} ) { # # This is after midnight, so decrement the weekday for the # weekday check (eg: Monday 11pm-1am means Monday 2300 to # Tuesday 0100, not Monday 2300-2400 plus Monday 0000-0100). # $matchWday--; $matchWday += 7 if ( $matchWday < 0 ); } } else { $blackout = $p->{hourBegin} <= $currHours && $currHours <= $p->{hourEnd}; } if ( $blackout && grep($_ == $matchWday, @{$p->{weekDays}}) ) { # print(LOG $bpc->timeStamp, "skipping because of blackout" # . " (alive $StatusHost{aliveCnt} times)\n"); print(STDERR "Skipping $client because of blackout\n") if ( $opts{v} ); NothingToDo($needLink); } } } if ( !$opts{i} && !$opts{f} && $StatusHost{backoffTime} > time ) { printf(LOG "%sskipping because of user requested delay (%.1f hours left)\n", $bpc->timeStamp, ($StatusHost{backoffTime} - time) / 3600); NothingToDo($needLink); } # # Now see if there are any old backups we should delete # BackupExpire($client); my(@lastIdxByLevel, $incrCntSinceFull); # # Read Backup information, and find times of the most recent full and # incremental backups. Also figure out which backup we will use # as a starting point for an incremental. # @Backups = $bpc->BackupInfoRead($client); for ( my $i = 0 ; $i < @Backups ; $i++ ) { $needLink = 1 if ( $Backups[$i]{nFilesNew} eq "" || -f "$Dir/NewFileList.$Backups[$i]{num}" ); if ( $Backups[$i]{type} eq "full" ) { $incrCntSinceFull = 0; $lastBkupNum = $Backups[$i]{num}; $lastIdxByLevel[0] = $i; if ( $lastFullTime < $Backups[$i]{startTime} ) { $lastFullTime = $Backups[$i]{startTime}; } } elsif ( $Backups[$i]{type} eq "incr" ) { $incrCntSinceFull++; $lastBkupNum = $Backups[$i]{num}; $lastIdxByLevel[$Backups[$i]{level}] = $i; $lastIncrTime = $Backups[$i]{startTime} if ( $lastIncrTime < $Backups[$i]{startTime} ); } elsif ( $Backups[$i]{type} eq "partial" ) { $partialIdx = $i; $lastPartial = $Backups[$i]{startTime}; $partialNum = $Backups[$i]{num}; $partialFileCnt = $Backups[$i]{nFiles}; } } # # Decide whether we do nothing, or a full or incremental backup. # my $needs_full = (time - $lastFullTime > $Conf{FullPeriod} * 24 * 3600 && time - $lastIncrTime > $Conf{IncrPeriod} * 24 * 3600); my $needs_incr = (time - $lastIncrTime > $Conf{IncrPeriod} * 24 * 3600 && time - $lastFullTime > $Conf{IncrPeriod} * 24 * 3600); if ( $lastFullTime == 0 || $opts{f} || (!$opts{i} && !$opts{I} && $needs_full) || ( $opts{F} && $needs_incr) ) { $type = "full"; $incrLevel = 0; $incrBaseBkupNum = $lastBkupNum; } elsif ( $opts{i} || $needs_incr || ($opts{I} && $needs_full) ) { $type = "incr"; # # For an incremental backup, figure out which level we should # do and the index of the reference backup, which is the most # recent backup at any lower level. # @{$Conf{IncrLevels}} = [$Conf{IncrLevels}] unless ref($Conf{IncrLevels}) eq "ARRAY"; @{$Conf{IncrLevels}} = [1] if ( !@{$Conf{IncrLevels}} ); $incrCntSinceFull = $incrCntSinceFull % @{$Conf{IncrLevels}}; $incrLevel = $Conf{IncrLevels}[$incrCntSinceFull]; for ( my $i = 0 ; $i < $incrLevel ; $i++ ) { my $idx = $lastIdxByLevel[$i]; next if ( !defined($idx) ); if ( !defined($incrBaseTime) || $Backups[$idx]{startTime} > $incrBaseTime ) { $incrBaseBkupNum = $Backups[$idx]{num}; $incrBaseLevel = $Backups[$idx]{level}; $incrBaseTime = $Backups[$idx]{startTime}; } } # # Can't find any earlier lower-level backup! Shouldn't # happen - just do full instead # if ( !defined($incrBaseBkupNum) || $incrLevel < 1 ) { $type = "full"; $incrBaseBkupNum = $lastBkupNum; } } else { NothingToDo($needLink); } # # Create top-level directories if they don't exist # foreach my $dir ( ( "$Conf{TopDir}", "$Conf{TopDir}/pool", "$Conf{TopDir}/cpool", "$Conf{TopDir}/pc", "$Conf{TopDir}/trash", ) ) { next if ( -d $dir ); mkpath($dir, 0, 0750); if ( !-d $dir ) { print("Failed to create $dir\n"); printf(LOG "%sFailed to create directory %s\n", $bpc->timeStamp, $dir); print("link $clientURI\n") if ( $needLink ); exit(1); } else { printf(LOG "%sCreated directory %s\n", $bpc->timeStamp, $dir); } } if ( !$bpc->HardlinkTest($Dir, "$TopDir/cpool") ) { print(LOG $bpc->timeStamp, "Can't create a test hardlink between a file" . " in $Dir and $TopDir/cpool. Either these are different" . " file systems, or this file system doesn't support hardlinks," . " or these directories don't exist, or there is a permissions" . " problem, or the file system is out of inodes or full. Use" . " df, df -i, and ls -ld to check each of these possibilities." . " Quitting...\n"); print("test hardlink between $Dir and $TopDir/cpool failed\n"); print("link $clientURI\n") if ( $needLink ); exit(1); } if ( !$opts{d} ) { # # In the non-DHCP case, make sure the host can be looked up # via NS, or otherwise find the IP address via NetBios. # if ( $Conf{ClientNameAlias} ne "" ) { $host = $Conf{ClientNameAlias}; } else { $host = $client; } if ( !defined(gethostbyname($host)) ) { # # Ok, NS doesn't know about it. Maybe it is a NetBios name # instead. # print(STDERR "Name server doesn't know about $host; trying NetBios\n") if ( $opts{v} ); if ( !defined($hostIP = $bpc->NetBiosHostIPFind($host)) ) { print(LOG $bpc->timeStamp, "Can't find host $host via netbios\n"); print("host not found\n"); exit(1); } } else { $hostIP = $host; } } # # Check if $host is alive # my $delay = $bpc->CheckHostAlive($hostIP); if ( $delay < 0 ) { print(LOG $bpc->timeStamp, "no ping response\n"); print("no ping response\n"); print("link $clientURI\n") if ( $needLink ); exit(1); } elsif ( $delay > $Conf{PingMaxMsec} ) { printf(LOG "%sping too slow: %.4gmsec\n", $bpc->timeStamp, $delay); printf("ping too slow: %.4gmsec (threshold is %gmsec)\n", $delay, $Conf{PingMaxMsec}); print("link $clientURI\n") if ( $needLink ); exit(1); } # # Make sure it is really the machine we expect (only for fixed addresses, # since we got the DHCP address above). # if ( !$opts{d} && (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { print(LOG $bpc->timeStamp, "dump failed: $errMsg\n"); print("dump failed: $errMsg\n"); exit(1); } elsif ( $opts{d} ) { print(LOG $bpc->timeStamp, "$host is dhcp $hostIP, user is $user\n"); } # # Get a clean directory $Dir/new # $bpc->RmTreeDefer("$TopDir/trash", "$Dir/new") if ( -d "$Dir/new" ); # # Setup file extension for compression and open XferLOG output file # if ( $Conf{CompressLevel} && !BackupPC::FileZIO->compOk ) { print(LOG $bpc->timeStamp, "dump failed: can't find Compress::Zlib\n"); print("dump failed: can't find Compress::Zlib\n"); exit(1); } my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; my $XferLOG = BackupPC::FileZIO->open("$Dir/XferLOG$fileExt", 1, $Conf{CompressLevel}); if ( !defined($XferLOG) ) { print(LOG $bpc->timeStamp, "dump failed: unable to open/create" . " $Dir/XferLOG$fileExt\n"); print("dump failed: unable to open/create $Dir/XferLOG$fileExt\n"); exit(1); } # # Ignore the partial dump in the case of an incremental # or when the partial is too old. A partial is a partial full. # if ( $type ne "full" || time - $lastPartial > $Conf{PartialAgeMax} * 24*3600 ) { $partialNum = undef; $partialIdx = -1; } # # If this is a partial, copy the old XferLOG file # if ( $partialNum ) { my($compress, $fileName); if ( -f "$Dir/XferLOG.$partialNum.z" ) { $fileName = "$Dir/XferLOG.$partialNum.z"; $compress = 1; } elsif ( -f "$Dir/XferLOG.$partialNum" ) { $fileName = "$Dir/XferLOG.$partialNum"; $compress = 0; } if ( my $oldLOG = BackupPC::FileZIO->open($fileName, 0, $compress) ) { my $data; while ( $oldLOG->read(\$data, 65536) > 0 ) { $XferLOG->write(\$data); } $oldLOG->close; } } $XferLOG->writeTeeStderr(1) if ( $opts{v} ); unlink("$Dir/NewFileList") if ( -f "$Dir/NewFileList" ); my $startTime = time(); my $tarErrs = 0; my $nFilesExist = 0; my $sizeExist = 0; my $sizeExistComp = 0; my $nFilesTotal = 0; my $sizeTotal = 0; my($logMsg, %stat, $xfer, $ShareNames, $noFilesErr); my $newFilesFH; $ShareNames = BackupPC::Xfer::getShareNames(\%Conf); # # Run an optional pre-dump command # UserCommandRun("DumpPreUserCmd"); if ( $? && $Conf{UserCmdCheckStatus} ) { print(LOG $bpc->timeStamp, "DumpPreUserCmd returned error status $?... exiting\n"); $XferLOG->write(\"DumpPreUserCmd returned error status $?... exiting\n"); $stat{hostError} = "DumpPreUserCmd returned error status $?"; BackupFailCleanup(); } $NeedPostCmd = 1; # # Now backup each of the shares # my $shareDuplicate = {}; for my $shareName ( @$ShareNames ) { local(*RH, *WH); # # Convert $shareName to utf8 octets # $shareName = encode("utf8", $shareName); $stat{xferOK} = $stat{hostAbort} = undef; $stat{hostError} = $stat{lastOutputLine} = undef; if ( $shareName eq "" ) { print(LOG $bpc->timeStamp, "unexpected empty share name skipped\n"); next; } if ( $shareDuplicate->{$shareName} ) { print(LOG $bpc->timeStamp, "unexpected repeated share name $shareName skipped\n"); next; } $shareDuplicate->{$shareName} = 1; UserCommandRun("DumpPreShareCmd", $shareName); if ( $? && $Conf{UserCmdCheckStatus} ) { print(LOG $bpc->timeStamp, "DumpPreShareCmd returned error status $?... exiting\n"); UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); $XferLOG->write(\"DumpPreShareCmd returned error status $?... exiting\n"); $stat{hostError} = "DumpPreShareCmd returned error status $?"; BackupFailCleanup(); } $xfer = BackupPC::Xfer::create($Conf{XferMethod}, $bpc); if ( !defined($xfer) ) { my $errStr = BackupPC::Xfer::errStr(); print(LOG $bpc->timeStamp, "dump failed: $errStr\n"); UserCommandRun("DumpPostShareCmd", $shareName) if ( $NeedPostCmd ); UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); $XferLOG->write(\"BackupPC::Xfer::create failed: $errStr\n"); $stat{hostError} = $errStr; BackupFailCleanup(); } my $useTar = $xfer->useTar; if ( $useTar ) { # # This xfer method outputs a tar format file, so we start a # BackupPC_tarExtract to extract the data. # # Create a socketpair to connect the Xfer method to BackupPC_tarExtract # WH is the write handle for writing, provided to the transport # program, and RH is the other end of the socket for reading, # provided to BackupPC_tarExtract. # if ( socketpair(RH, WH, AF_UNIX, SOCK_STREAM, PF_UNSPEC) ) { shutdown(RH, 1); # no writing to this socket shutdown(WH, 0); # no reading from this socket setsockopt(RH, SOL_SOCKET, SO_RCVBUF, 8 * 65536); setsockopt(WH, SOL_SOCKET, SO_SNDBUF, 8 * 65536); } else { # # Default to pipe() if socketpair() doesn't work. # pipe(RH, WH); } # # fork a child for BackupPC_tarExtract. TAR is a file handle # on which we (the parent) read the stdout & stderr from # BackupPC_tarExtract. # if ( !defined($tarPid = open(TAR, "-|")) ) { print(LOG $bpc->timeStamp, "can't fork to run tar\n"); print("can't fork to run tar\n"); close(RH); close(WH); last; } binmode(TAR); if ( !$tarPid ) { # # This is the tar child. Close the write end of the pipe, # clone STDERR to STDOUT, clone STDIN from RH, and then # exec BackupPC_tarExtract. # setpgrp 0,0; close(WH); close(STDERR); open(STDERR, ">&STDOUT"); close(STDIN); open(STDIN, "<&RH"); alarm(0); exec("$BinDir/BackupPC_tarExtract", $client, $shareName, $Conf{CompressLevel}); print(LOG $bpc->timeStamp, "can't exec $BinDir/BackupPC_tarExtract\n"); exit(0); } } elsif ( !defined($newFilesFH) ) { # # We need to create the NewFileList output file # local(*NEW_FILES); open(NEW_FILES, ">", "$TopDir/pc/$client/NewFileList") || die("can't open $TopDir/pc/$client/NewFileList"); $newFilesFH = *NEW_FILES; binmode(NEW_FILES); } # # Run the transport program # $xfer->args({ host => $host, client => $client, hostIP => $hostIP, shareName => $shareName, pipeRH => *RH, pipeWH => *WH, XferLOG => $XferLOG, newFilesFH => $newFilesFH, outDir => $Dir, type => $type, incrBaseTime => $incrBaseTime, incrBaseBkupNum => $incrBaseBkupNum, backups => \@Backups, compress => $Conf{CompressLevel}, XferMethod => $Conf{XferMethod}, logLevel => $Conf{XferLogLevel}, partialNum => $partialNum, pidHandler => \&pidHandler, completionPercent => \&completionPercent, }); if ( !defined($logMsg = $xfer->start()) ) { my $errStr = "xfer start failed: " . $xfer->errStr . "\n"; print(LOG $bpc->timeStamp, $errStr); # # kill off the tar process, first nicely then forcefully # if ( $tarPid > 0 ) { kill($bpc->sigName2num("INT"), $tarPid); sleep(1); kill($bpc->sigName2num("KILL"), $tarPid); } if ( @xferPid ) { kill($bpc->sigName2num("INT"), @xferPid); sleep(1); kill($bpc->sigName2num("KILL"), @xferPid); } UserCommandRun("DumpPostShareCmd", $shareName) if ( $NeedPostCmd ); UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); $XferLOG->write(\"xfer start failed: $errStr\n"); $stat{hostError} = $errStr; BackupFailCleanup(); } @xferPid = $xfer->xferPid; if ( $useTar ) { # # The parent must close both handles on the pipe since the children # are using these handles now. # close(RH); close(WH); } print(LOG $bpc->timeStamp, $logMsg, "\n"); $XferLOG->write(\"$logMsg\n"); print("started $type dump, share=$shareName\n"); pidHandler(@xferPid); if ( $useTar ) { # # Parse the output of the transfer program and BackupPC_tarExtract # while they run. Since we might be reading from two or more children # we use a select. # my($FDread, $tarOut, $mesg); vec($FDread, fileno(TAR), 1) = 1; $xfer->setSelectMask(\$FDread); SCAN: while ( 1 ) { my $ein = $FDread; last if ( $FDread =~ /^\0*$/ ); select(my $rout = $FDread, undef, $ein, undef); if ( vec($rout, fileno(TAR), 1) ) { if ( sysread(TAR, $mesg, 8192) <= 0 ) { vec($FDread, fileno(TAR), 1) = 0; close(TAR); } else { $tarOut .= $mesg; } } while ( $tarOut =~ /(.*?)[\n\r]+(.*)/s ) { $_ = $1; $tarOut = $2; if ( /^ / ) { $XferLOG->write(\"$_\n"); } else { $XferLOG->write(\"tarExtract: $_\n"); } if ( /^BackupPC_tarExtact aborting \((.*)\)/ ) { $stat{hostError} = $1; } if ( /^Done: (\d+) errors, (\d+) filesExist, (\d+) sizeExist, (\d+) sizeExistComp, (\d+) filesTotal, (\d+) sizeTotal/ ) { $tarErrs += $1; $nFilesExist += $2; $sizeExist += $3; $sizeExistComp += $4; $nFilesTotal += $5; $sizeTotal += $6; } } last if ( !$xfer->readOutput(\$FDread, $rout) ); while ( my $str = $xfer->logMsgGet ) { print(LOG $bpc->timeStamp, "xfer: $str\n"); } if ( $xfer->getStats->{fileCnt} == 1 ) { # # Make sure it is still the machine we expect. We do this while # the transfer is running to avoid a potential race condition if # the ip address was reassigned by dhcp just before we started # the transfer. # if ( my $errMsg = CorrectHostCheck($hostIP, $host) ) { $stat{hostError} = $errMsg if ( $stat{hostError} eq "" ); last SCAN; } } } } else { # # otherwise the xfer module does everything for us # my @results = $xfer->run(); $tarErrs += $results[0]; $nFilesExist += $results[1]; $sizeExist += $results[2]; $sizeExistComp += $results[3]; $nFilesTotal += $results[4]; $sizeTotal += $results[5]; } # # Merge the xfer status (need to accumulate counts) # my $newStat = $xfer->getStats; # MAKSYM 14082016: forcing the right file count if some bytes were transferred; ensures compatibility with at least Samba-4.3 $newStat->{fileCnt} = $nFilesTotal if ( $useTar && $newStat->{fileCnt} == 0 && $xfer->getStats->{byteCnt} > 0 ); if ( $newStat->{fileCnt} == 0 ) { $noFilesErr ||= "No files dumped for share $shareName"; } foreach my $k ( (keys(%stat), keys(%$newStat)) ) { next if ( !defined($newStat->{$k}) ); if ( $k =~ /Cnt$/ ) { $stat{$k} += $newStat->{$k}; delete($newStat->{$k}); next; } if ( !defined($stat{$k}) ) { $stat{$k} = $newStat->{$k}; delete($newStat->{$k}); next; } } if ( $NeedPostCmd ) { UserCommandRun("DumpPostShareCmd", $shareName); if ( $? && $Conf{UserCmdCheckStatus} ) { print(LOG $bpc->timeStamp, "DumpPostShareCmd returned error status $?... exiting\n"); $stat{hostError} = "DumpPostShareCmd returned error status $?"; } } $stat{xferOK} = 0 if ( $stat{hostError} || $stat{hostAbort} ); if ( !$stat{xferOK} ) { # # kill off the transfer program, first nicely then forcefully # if ( @xferPid ) { kill($bpc->sigName2num("INT"), @xferPid); sleep(1); kill($bpc->sigName2num("KILL"), @xferPid); } # # kill off the tar process, first nicely then forcefully # if ( $tarPid > 0 ) { kill($bpc->sigName2num("INT"), $tarPid); sleep(1); kill($bpc->sigName2num("KILL"), $tarPid); } # # don't do any more shares on this host # last; } # # Wait for any child processes to exit # # 1 while ( wait() >= 0 ); } # # If this is a full, and any share had zero files then consider the dump bad # if ( $type eq "full" && $stat{hostError} eq "" && length($noFilesErr) && $Conf{BackupZeroFilesIsFatal} ) { $stat{hostError} = $noFilesErr; $stat{xferOK} = 0; } $stat{xferOK} = 0 if ( $Abort ); # # If there is no "new" directory then the backup is bad # if ( $stat{xferOK} && !-d "$Dir/new" ) { $stat{hostError} = "No backup directory $Dir/new" if ( $stat{hostError} eq "" ); $stat{xferOK} = 0; } # # Do one last check to make sure it is still the machine we expect. # if ( $stat{xferOK} && (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { $stat{hostError} = $errMsg; $stat{xferOK} = 0; } UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); if ( $? && $Conf{UserCmdCheckStatus} ) { print(LOG $bpc->timeStamp, "DumpPostUserCmd returned error status $?... exiting\n"); $stat{hostError} = "DumpPostUserCmd returned error status $?"; $stat{xferOK} = 0; } close($newFilesFH) if ( defined($newFilesFH) ); my $endTime = time(); # # If the dump failed, clean up # if ( !$stat{xferOK} ) { $stat{hostError} = $stat{lastOutputLine} if ( $stat{hostError} eq "" ); if ( $stat{hostError} ) { print(LOG $bpc->timeStamp, "Got fatal error during xfer ($stat{hostError})\n"); $XferLOG->write(\"Got fatal error during xfer ($stat{hostError})\n"); } if ( !$Abort ) { # # wait a short while and see if the system is still alive # sleep(5); if ( $bpc->CheckHostAlive($hostIP) < 0 ) { $stat{hostAbort} = 1; } if ( $stat{hostAbort} ) { $stat{hostError} = "lost network connection during backup"; } print(LOG $bpc->timeStamp, "Backup aborted ($stat{hostError})\n"); $XferLOG->write(\"Backup aborted ($stat{hostError})\n"); } else { $XferLOG->write(\"Backup aborted by user signal\n"); } # # Close the log file and call BackupFailCleanup, which exits. # BackupFailCleanup(); } my $newNum = BackupSave(); my $otherCount = $stat{xferErrCnt} - $stat{xferBadFileCnt} - $stat{xferBadShareCnt}; $stat{fileCnt} ||= 0; $stat{byteCnt} ||= 0; $stat{xferErrCnt} ||= 0; $stat{xferBadFileCnt} ||= 0; $stat{xferBadShareCnt} ||= 0; print(LOG $bpc->timeStamp, "$type backup $newNum complete, $stat{fileCnt} files," . " $stat{byteCnt} bytes," . " $stat{xferErrCnt} xferErrs ($stat{xferBadFileCnt} bad files," . " $stat{xferBadShareCnt} bad shares, $otherCount other)\n"); BackupExpire($client); print("$type backup complete\n"); ########################################################################### # Subroutines ########################################################################### sub NothingToDo { my($needLink) = @_; print("nothing to do\n"); print("link $clientURI\n") if ( $needLink ); exit(0); } sub catch_signal { my $sigName = shift; # # The first time we receive a signal we try to gracefully # abort the backup. This allows us to keep a partial dump # with the in-progress file deleted and attribute caches # flushed to disk etc. # if ( !length($SigName) ) { my $reason; if ( $sigName eq "INT" ) { $reason = "aborted by user (signal=$sigName)"; } else { $reason = "aborted by signal=$sigName"; } if ( $Pid == $$ ) { # # Parent logs a message # print(LOG $bpc->timeStamp, "Aborting backup up after signal $sigName\n"); # # Tell xfer to abort, but only if we actually started one # $xfer->abort($reason) if ( defined($xfer) ); # # Send ALRMs to BackupPC_tarExtract if we are using it # if ( $tarPid > 0 ) { kill($bpc->sigName2num("ARLM"), $tarPid); } # # Schedule a 20 second timer in case the clean # abort doesn't complete # alarm(20); } else { # # Children ignore anything other than ALRM and INT # if ( $sigName ne "ALRM" && $sigName ne "INT" ) { return; } # # The child also tells xfer to abort # $xfer->abort($reason); # # Schedule a 15 second timer in case the clean # abort doesn't complete # alarm(15); } $SigName = $sigName; $Abort = 1; return; } # # This is a second signal: time to clean up. # if ( $Pid != $$ && ($sigName eq "ALRM" || $sigName eq "INT") ) { # # Children quit quietly on ALRM or INT # exit(1) } # # Ignore other signals in children # return if ( $Pid != $$ ); $SIG{$sigName} = 'IGNORE'; UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); $XferLOG->write(\"exiting after signal $sigName\n"); if ( @xferPid ) { kill($bpc->sigName2num("INT"), @xferPid); sleep(1); kill($bpc->sigName2num("KILL"), @xferPid); } if ( $tarPid > 0 ) { kill($bpc->sigName2num("INT"), $tarPid); sleep(1); kill($bpc->sigName2num("KILL"), $tarPid); } if ( $sigName eq "INT" ) { $stat{hostError} = "aborted by user (signal=$sigName)"; } else { $stat{hostError} = "received signal=$sigName"; } BackupFailCleanup(); } sub CheckForNewFiles { if ( -f _ && $File::Find::name !~ /\/fattrib$/ ) { $nFilesTotal++; } elsif ( -d _ ) { # # No need to check entire tree # $File::Find::prune = 1 if ( $nFilesTotal ); } } sub BackupFailCleanup { my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; my $keepPartial = 0; # # We keep this backup if it is a full and we actually backed # up some files. If the prior backup was a partial too, we # only keep this backup if it has more files than the previous # partial. # if ( $type eq "full" ) { if ( $nFilesTotal == 0 && $xfer->getStats->{fileCnt} == 0 ) { # # Xfer didn't report any files, but check in the new # directory just in case. # find(\&CheckForNewFiles, "$Dir/new"); } my $str; if ( $nFilesTotal > $partialFileCnt || $xfer->getStats->{fileCnt} > $partialFileCnt ) { # # If the last backup wasn't a partial then # $partialFileCnt is undefined, so the above # test is simply $nFilesTotal > 0 # $keepPartial = 1; if ( $partialFileCnt ) { $str = "Saving this as a partial backup\n"; } else { $str = sprintf("Saving this as a partial backup, replacing the" . " prior one (got %d and %d files versus %d)\n", $nFilesTotal, $xfer->getStats->{fileCnt}, $partialFileCnt); } } else { $str = sprintf("Not saving this as a partial backup since it has fewer" . " files than the prior one (got %d and %d files versus %d)\n", $nFilesTotal, $xfer->getStats->{fileCnt}, $partialFileCnt); } $XferLOG->write(\$str); } # # Don't keep partials if they are disabled # $keepPartial = 0 if ( $Conf{PartialAgeMax} < 0 ); if ( !$keepPartial ) { # # No point in saving this dump; get rid of eveything. # $XferLOG->close(); unlink("$Dir/timeStamp.level0") if ( -f "$Dir/timeStamp.level0" ); unlink("$Dir/SmbLOG.bad") if ( -f "$Dir/SmbLOG.bad" ); unlink("$Dir/SmbLOG.bad$fileExt") if ( -f "$Dir/SmbLOG.bad$fileExt" ); unlink("$Dir/XferLOG.bad") if ( -f "$Dir/XferLOG.bad" ); unlink("$Dir/XferLOG.bad$fileExt") if ( -f "$Dir/XferLOG.bad$fileExt" ); unlink("$Dir/NewFileList") if ( -f "$Dir/NewFileList" ); rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.bad$fileExt"); $bpc->RmTreeDefer("$TopDir/trash", "$Dir/new") if ( -d "$Dir/new" ); print("dump failed: $stat{hostError}\n"); $XferLOG->close(); print("link $clientURI\n") if ( $needLink ); exit(1); } # # Ok, now we should save this as a partial dump # $type = "partial"; my $newNum = BackupSave(); print("dump failed: $stat{hostError}\n"); print("link $clientURI\n") if ( $needLink ); print(LOG $bpc->timeStamp, "Saved partial dump $newNum\n"); exit(2); } # # Decide which old backups should be expired by moving them # to $TopDir/trash. # sub BackupExpire { my($client) = @_; my($Dir) = "$TopDir/pc/$client"; my(@Backups) = $bpc->BackupInfoRead($client); my($cntFull, $cntIncr, $firstFull, $firstIncr, $oldestIncr, $oldestFull, $changes); if ( $Conf{FullKeepCnt} <= 0 ) { print(LOG $bpc->timeStamp, "Invalid value for \$Conf{FullKeepCnt}=$Conf{FullKeepCnt}\n"); print(STDERR "Invalid value for \$Conf{FullKeepCnt}=$Conf{FullKeepCnt}\n") if ( $opts{v} ); return; } while ( 1 ) { $cntFull = $cntIncr = 0; $oldestIncr = $oldestFull = 0; for ( my $i = 0 ; $i < @Backups ; $i++ ) { if ( $Backups[$i]{type} eq "full" ) { $firstFull = $i if ( $cntFull == 0 ); $cntFull++; } elsif ( $Backups[$i]{type} eq "incr" ) { $firstIncr = $i if ( $cntIncr == 0 ); $cntIncr++; } } $oldestIncr = (time - $Backups[$firstIncr]{startTime}) / (24 * 3600) if ( $cntIncr > 0 ); $oldestFull = (time - $Backups[$firstFull]{startTime}) / (24 * 3600) if ( $cntFull > 0 ); # # With multi-level incrementals, several of the following # incrementals might depend upon this one, so we have to # delete all of the them. Figure out if that is possible # by counting the number of consecutive incrementals that # are unfilled and have a level higher than this one. # my $cntIncrDel = 1; my $earliestIncr = $oldestIncr; if ( defined($firstIncr) ) { for ( my $i = $firstIncr + 1 ; $i < @Backups ; $i++ ) { last if ( $Backups[$i]{level} <= $Backups[$firstIncr]{level} || !$Backups[$i]{noFill} ); $cntIncrDel++; $earliestIncr = (time - $Backups[$i]{startTime}) / (24 * 3600); } } if ( $cntIncr >= $Conf{IncrKeepCnt} + $cntIncrDel || ($cntIncr >= $Conf{IncrKeepCntMin} + $cntIncrDel && $earliestIncr > $Conf{IncrAgeMax}) ) { # # Only delete an incr backup if the Conf settings are satisfied # for all $cntIncrDel incrementals. Since BackupRemove() does # a splice() we need to do the deletes in the reverse order. # for ( my $i = $firstIncr + $cntIncrDel - 1 ; $i >= $firstIncr ; $i-- ) { print(LOG $bpc->timeStamp, "removing incr backup $Backups[$i]{num}\n"); BackupRemove($client, \@Backups, $i); $changes++; } next; } # # Delete any old full backups, according to $Conf{FullKeepCntMin} # and $Conf{FullAgeMax}. # # First make sure that $Conf{FullAgeMax} is at least bigger # than $Conf{FullPeriod} * $Conf{FullKeepCnt}, including # the exponential array case. # my $fullKeepCnt = $Conf{FullKeepCnt}; $fullKeepCnt = [$fullKeepCnt] if ( ref($fullKeepCnt) ne "ARRAY" ); my $fullAgeMax; my $fullPeriod = int(0.5 + $Conf{FullPeriod}); $fullPeriod = 7 if ( $fullPeriod <= 0 ); for ( my $i = 0 ; $i < @$fullKeepCnt ; $i++ ) { $fullAgeMax += $fullKeepCnt->[$i] * $fullPeriod; $fullPeriod *= 2; } $fullAgeMax += $fullPeriod; # add some buffer if ( $cntFull > $Conf{FullKeepCntMin} && $oldestFull > $Conf{FullAgeMax} && $oldestFull > $fullAgeMax && $Conf{FullKeepCntMin} > 0 && $Conf{FullAgeMax} > 0 && (@Backups <= $firstFull + 1 || !$Backups[$firstFull + 1]{noFill}) ) { # # Only delete a full backup if the Conf settings are satisfied. # We also must make sure that either this backup is the most # recent one, or the next backup is filled. # (We can't deleted a full backup if the next backup is not # filled.) # print(LOG $bpc->timeStamp, "removing old full backup $Backups[$firstFull]{num}\n"); BackupRemove($client, \@Backups, $firstFull); $changes++; next; } # # Do new-style full backup expiry, which includes the the case # where $Conf{FullKeepCnt} is an array. # last if ( !BackupFullExpire($client, \@Backups) ); $changes++; } $bpc->BackupInfoWrite($client, @Backups) if ( $changes ); } # # Handle full backup expiry, using exponential periods. # sub BackupFullExpire { my($client, $Backups) = @_; my $fullCnt = 0; my $fullPeriod = $Conf{FullPeriod}; my $origFullPeriod = $fullPeriod; my $fullKeepCnt = $Conf{FullKeepCnt}; my $fullKeepIdx = 0; my(@delete, @fullList); # # Don't delete anything if $Conf{FullPeriod} or $Conf{FullKeepCnt} are # not defined - possibly a corrupted config.pl file. # return if ( !defined($Conf{FullPeriod}) || !defined($Conf{FullKeepCnt}) ); # # If regular backups are still disabled with $Conf{FullPeriod} < 0, # we still expire backups based on a typical FullPeriod value - weekly. # $fullPeriod = 7 if ( $fullPeriod <= 0 ); $fullKeepCnt = [$fullKeepCnt] if ( ref($fullKeepCnt) ne "ARRAY" ); for ( my $i = 0 ; $i < @$Backups ; $i++ ) { next if ( $Backups->[$i]{type} ne "full" ); push(@fullList, $i); } for ( my $k = @fullList - 1 ; $k >= 0 ; $k-- ) { my $i = $fullList[$k]; my $prevFull = $fullList[$k-1] if ( $k > 0 ); # # Don't delete any full that is followed by an unfilled backup, # since it is needed for restore. # my $noDelete = $i + 1 < @$Backups ? $Backups->[$i+1]{noFill} : 0; if ( !$noDelete && ($fullKeepIdx >= @$fullKeepCnt || $k > 0 && $fullKeepIdx > 0 && $Backups->[$i]{startTime} - $Backups->[$prevFull]{startTime} < ($fullPeriod - $origFullPeriod / 2) * 24 * 3600 ) ) { # # Delete the full backup # #print("Deleting backup $i ($prevFull)\n"); unshift(@delete, $i); } else { $fullCnt++; while ( $fullKeepIdx < @$fullKeepCnt && $fullCnt >= $fullKeepCnt->[$fullKeepIdx] ) { $fullKeepIdx++; $fullCnt = 0; $fullPeriod = 2 * $fullPeriod; } } } # # Now actually delete the backups # for ( my $i = @delete - 1 ; $i >= 0 ; $i-- ) { print(LOG $bpc->timeStamp, "removing full backup $Backups->[$delete[$i]]{num}\n"); BackupRemove($client, $Backups, $delete[$i]); } return @delete; } # # Removes any partial backups # sub BackupPartialRemove { my($client, $Backups) = @_; for ( my $i = @$Backups - 1 ; $i >= 0 ; $i-- ) { next if ( $Backups->[$i]{type} ne "partial" ); BackupRemove($client, $Backups, $i); } } sub BackupSave { my @Backups = $bpc->BackupInfoRead($client); my $num = -1; my $newFilesFH; # # Since we got a good backup we should remove any partial dumps # (the new backup might also be a partial, but that's ok). # BackupPartialRemove($client, \@Backups); $needLink = 1 if ( -f "$Dir/NewFileList" ); # # Number the new backup # for ( my $i = 0 ; $i < @Backups ; $i++ ) { $num = $Backups[$i]{num} if ( $num < $Backups[$i]{num} ); } $num++; $bpc->RmTreeDefer("$TopDir/trash", "$Dir/$num") if ( -d "$Dir/$num" ); if ( !rename("$Dir/new", "$Dir/$num") ) { print(LOG $bpc->timeStamp, "Rename $Dir/new -> $Dir/$num failed\n"); $stat{xferOK} = 0; return; } # # Add the new backup information to the backup file # my $i = @Backups; $Backups[$i]{num} = $num; $Backups[$i]{type} = $type; $Backups[$i]{startTime} = $startTime; $Backups[$i]{endTime} = $endTime; $Backups[$i]{size} = $sizeTotal; $Backups[$i]{nFiles} = $nFilesTotal; $Backups[$i]{xferErrs} = $stat{xferErrCnt} || 0; $Backups[$i]{xferBadFile} = $stat{xferBadFileCnt} || 0; $Backups[$i]{xferBadShare} = $stat{xferBadShareCnt} || 0; $Backups[$i]{nFilesExist} = $nFilesExist; $Backups[$i]{sizeExist} = $sizeExist; $Backups[$i]{sizeExistComp} = $sizeExistComp; $Backups[$i]{tarErrs} = $tarErrs; $Backups[$i]{compress} = $Conf{CompressLevel}; $Backups[$i]{noFill} = $type eq "incr" ? 1 : 0; $Backups[$i]{level} = $incrLevel; $Backups[$i]{mangle} = 1; # name mangling always on for v1.04+ $Backups[$i]{xferMethod} = $Conf{XferMethod}; $Backups[$i]{charset} = $Conf{ClientCharset}; $Backups[$i]{version} = $bpc->Version(); # # Save the main backups file # $bpc->BackupInfoWrite($client, @Backups); # # Save just this backup's info in case the main backups file # gets corrupted # BackupPC::Storage->backupInfoWrite($Dir, $Backups[$i]{num}, $Backups[$i]); unlink("$Dir/timeStamp.level0") if ( -f "$Dir/timeStamp.level0" ); foreach my $ext ( qw(bad bad.z) ) { next if ( !-f "$Dir/XferLOG.$ext" ); unlink("$Dir/XferLOG.$ext.old") if ( -f "$Dir/XferLOG.$ext" ); rename("$Dir/XferLOG.$ext", "$Dir/XferLOG.$ext.old"); } # # Now remove the bad files, replacing them if possible with links to # earlier backups. # foreach my $f ( $xfer->getBadFiles ) { my $j; my $shareM = $bpc->fileNameEltMangle($f->{share}); my $fileM = $bpc->fileNameMangle($f->{file}); unlink("$Dir/$num/$shareM/$fileM"); for ( $j = $i - 1 ; $j >= 0 ; $j-- ) { my $file; if ( $Backups[$j]{mangle} ) { $file = "$shareM/$fileM"; } else { $file = "$f->{share}/$f->{file}"; } next if ( !-f "$Dir/$Backups[$j]{num}/$file" ); my($exists, $digest, $origSize, $outSize, $errs) = BackupPC::PoolWrite::LinkOrCopy( $bpc, "$Dir/$Backups[$j]{num}/$file", $Backups[$j]{compress}, "$Dir/$num/$shareM/$fileM", $Conf{CompressLevel}); if ( !$exists ) { # # the hard link failed, most likely because the target # file has too many links. We have copied the file # instead, so add this to the new file list. # if ( !defined($newFilesFH) ) { my $str = "Appending to NewFileList for $shareM/$fileM\n"; $XferLOG->write(\$str); open($newFilesFH, ">>", "$TopDir/pc/$client/NewFileList") || die("can't open $TopDir/pc/$client/NewFileList"); binmode($newFilesFH); } if ( -f "$Dir/$num/$shareM/$fileM" ) { print($newFilesFH "$digest $origSize $shareM/$fileM\n"); } else { my $str = "Unable to link/copy $num/$f->{share}/$f->{file}" . " to $Backups[$j]{num}/$f->{share}/$f->{file}\n"; $XferLOG->write(\$str); } } else { my $str = "Bad file $num/$f->{share}/$f->{file} replaced" . " by link to" . " $Backups[$j]{num}/$f->{share}/$f->{file}\n"; $XferLOG->write(\$str); } last; } if ( $j < 0 ) { my $str = "Removed bad file $num/$f->{share}/$f->{file}" . " (no older copy to link to)\n"; $XferLOG->write(\$str); } } close($newFilesFH) if ( defined($newFilesFH) ); $XferLOG->close(); rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.$num$fileExt"); rename("$Dir/NewFileList", "$Dir/NewFileList.$num"); return $num; } # # Removes a specific backup # sub BackupRemove { my($client, $Backups, $idx) = @_; my($Dir) = "$TopDir/pc/$client"; if ( $Backups->[$idx]{num} eq "" ) { print("BackupRemove: ignoring empty backup number for idx $idx\n"); return; } $bpc->RmTreeDefer("$TopDir/trash", "$Dir/$Backups->[$idx]{num}"); unlink("$Dir/SmbLOG.$Backups->[$idx]{num}") if ( -f "$Dir/SmbLOG.$Backups->[$idx]{num}" ); unlink("$Dir/SmbLOG.$Backups->[$idx]{num}.z") if ( -f "$Dir/SmbLOG.$Backups->[$idx]{num}.z" ); unlink("$Dir/XferLOG.$Backups->[$idx]{num}") if ( -f "$Dir/XferLOG.$Backups->[$idx]{num}" ); unlink("$Dir/XferLOG.$Backups->[$idx]{num}.z") if ( -f "$Dir/XferLOG.$Backups->[$idx]{num}.z" ); splice(@{$Backups}, $idx, 1); } sub CorrectHostCheck { my($hostIP, $host) = @_; return if ( $hostIP eq $host && !$Conf{FixedIPNetBiosNameCheck} || $Conf{NmbLookupCmd} eq "" ); my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($hostIP); return "host $host has mismatching netbios name $netBiosHost" if ( lc($netBiosHost) ne lc(substr($host, 0, 15)) ); return; } # # The Xfer method might tell us from time to time about processes # it forks. We tell BackupPC about this (for status displays) and # keep track of the pids in case we cancel the backup # sub pidHandler { @xferPid = @_; @xferPid = grep(/./, @xferPid); return if ( !@xferPid && $tarPid < 0 ); my @pids = @xferPid; push(@pids, $tarPid) if ( $tarPid > 0 ); my $str = join(",", @pids); $XferLOG->write(\"Xfer PIDs are now $str\n") if ( defined($XferLOG) ); print("xferPids $str\n"); } # # The Xfer method might tell us from time to time about progress # in the backup or restore # sub completionPercent { my($percent) = @_; $percent = 100 if ( $percent > 100 ); $percent = 0 if ( $percent < 0 ); if ( !defined($completionPercent) || int($completionPercent + 0.5) != int($percent) ) { printf("completionPercent %.0f\n", $percent); } $completionPercent = $percent; } # # Run an optional pre- or post-dump command # sub UserCommandRun { my($cmdType, $sharename) = @_; return if ( !defined($Conf{$cmdType}) ); my $vars = { xfer => $xfer, client => $client, host => $host, hostIP => $hostIP, user => $Hosts->{$client}{user}, moreUsers => $Hosts->{$client}{moreUsers}, share => $ShareNames->[0], shares => $ShareNames, XferMethod => $Conf{XferMethod}, sshPath => $Conf{SshPath}, LOG => *LOG, XferLOG => $XferLOG, stat => \%stat, xferOK => $stat{xferOK} || 0, hostError => $stat{hostError}, type => $type, cmdType => $cmdType, }; if ($cmdType eq 'DumpPreShareCmd' || $cmdType eq 'DumpPostShareCmd') { $vars->{share} = $sharename; } my $cmd = $bpc->cmdVarSubstitute($Conf{$cmdType}, $vars); $XferLOG->write(\"Executing $cmdType: @$cmd\n"); # # Run the user's command, dumping the stdout/stderr into the # Xfer log file. Also supply the optional $vars and %Conf in # case the command is really perl code instead of a shell # command. # $bpc->cmdSystemOrEval($cmd, sub { $XferLOG->write(\$_[0]); print(LOG $bpc->timeStamp, "Output from $cmdType: ", $_[0]); }, $vars, \%Conf); } BackupPC-3.3.2/bin/BackupPC_fixupBackupSummary0000555000076500000240000001714213042250554020167 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_fixupBackupSummary: recreate backups file in case # it was lost. # # DESCRIPTION # # Usage: BackupPC_fixupBackupSummary [clients...] # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2005-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use Getopt::Std; use Data::Dumper; use Time::ParseDate; use BackupPC::Lib; use BackupPC::Attrib qw(:all); use BackupPC::FileZIO; use BackupPC::Storage; die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my $Hosts = $bpc->HostInfoRead(); my @hostList; our(%backupInfo); my %opts; if ( !getopts("l", \%opts) ) { print STDERR <{$host}) ) { print("$host doesn't exist in BackupPC's host file... skipping\n"); next; } my $dir = "$TopDir/pc/$host"; print("Doing host $host\n"); if ( !opendir(DIR, $dir) ) { print("$host: Can't open $dir... skipping $host\n"); next; } # # Read the backups file # @Backups = $bpc->BackupInfoRead($host); # # Look through the LOG files to get information about # completed backups. The data from the LOG file is # incomplete, but enough to get some useful info. # # Also, try to pick up the new-style of information # that is kept in each backup tree. This info is # complete. This data is only saved after version # 2.1.2. # my @files = readdir(DIR); closedir(DIR); foreach my $file ( @files ) { if ( $opts{l} && $file =~ /^LOG(.\d+\.z)?/ ) { push(@LogFiles, $file); } elsif ( $file =~ /^(\d+)$/ ) { my $bkupNum = $1; $BkupNums->{$bkupNum} = 1; next if ( !-f "$dir/$bkupNum/backupInfo" ); # # Read backup info # %backupInfo = (); print(" Reading $dir/$bkupNum/backupInfo\n"); if ( !(my $ret = do "$dir/$bkupNum/backupInfo") ) { print(" couldn't parse $dir/$bkupNum/backupInfo: $@\n") if $@; print(" couldn't do $dir/$bkupNum/backupInfo: $!\n") unless defined $ret; print(" couldn't run $dir/$bkupNum/backupInfo\n"); next; } if ( !keys(%backupInfo) || !defined($backupInfo{num}) ) { print(" $dir/$bkupNum/backupInfo is empty\n"); next; } %{$BkupFromInfo->{$backupInfo{num}}} = %backupInfo; } } # # Read through LOG files from oldest to newest # @LogFiles = sort({-M "$dir/$a" <=> -M "$dir/$b"} @LogFiles); my $startTime; my $fillFromNum; foreach my $file ( @LogFiles ) { my $f = BackupPC::FileZIO->open("$dir/$file", 0, $file =~ /\.z/); if ( !defined($f) ) { print("$host: unable to open file $dir/$file\n"); next; } print(" Reading $file\n"); while ( (my $str = $f->readLine()) ne "" ) { if ( $str =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (full|incr|partial) backup started / ) { $startTime = parsedate($1); next; } next if ( $str !~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (full|incr|partial) backup (\d+) complete, (\d+) files, (\d+) bytes, (\d+) xferErrs \((\d+) bad files, (\d+) bad shares, (\d+) other\)/ ); my $type = $2; my $bkupNum = $3; my $nFilesTotal = $4; my $sizeTotal = $5; my $xferErrs = $6; my $badFiles = $7; my $badShare = $8; my $endTime = parsedate($1); print(" Got $type backup $bkupNum at $endTime\n"); next if ( !-d "$dir/$bkupNum" ); $BkupFromLOG->{$bkupNum} = { num => $bkupNum, type => $type, startTime => $startTime, endTime => $endTime, size => $sizeTotal, nFiles => $nFilesTotal, xferErrs => $xferErrs, xferBadFile => $badFiles, xferBadShare => $badShare, nFilesExist => 0, sizeExist => 0, sizeExistComp => 0, tarErrs => 0, compress => $Conf{CompressLevel}, noFill => $type eq "incr" ? 1 : 0, level => $type eq "incr" ? 1 : 0, mangle => 1, fillFromNum => $fillFromNum, }; $fillFromNum = $bkupNum if ( $type eq "full" ); } } # # Now merge any info from $BkupFromInfo and $BkupFromLOG # that is missing from @Backups. # # First, anything in @Backups overrides the other data # # foreach ( my $i = 0 ; $i < @Backups ; $i++ ) { my $bkupNum = $Backups[$i]{num}; delete($BkupFromLOG->{$bkupNum}); delete($BkupFromInfo->{$bkupNum}); delete($BkupNums->{$bkupNum}); } # # Now merge in data from the LOG and backupInfo files. # backupInfo files override LOG files. # my $changes; foreach my $bkupNum ( keys(%$BkupFromLOG) ) { next if ( defined($BkupFromInfo->{$bkupNum}) ); print(" Adding info for backup $bkupNum from LOG file\n"); push(@Backups, $BkupFromLOG->{$bkupNum}); delete($BkupNums->{$bkupNum}); $changes++; } foreach my $bkupNum ( keys(%$BkupFromInfo) ) { print(" Adding info for backup $bkupNum from backupInfo file\n"); push(@Backups, $BkupFromInfo->{$bkupNum}); delete($BkupNums->{$bkupNum}); $changes++; } foreach my $bkupNum ( keys(%$BkupNums) ) { print(" *** No info for backup number $bkupNum\n"); } if ( $changes ) { @Backups = sort({$a->{num} <=> $b->{num}} @Backups); # print Dumper \@Backups; $bpc->BackupInfoWrite($host, @Backups); } else { print(" No changes for host $host\n"); } } BackupPC-3.3.2/bin/BackupPC_link0000555000076500000240000002206013042250554015260 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_link: link new backup into pool # # DESCRIPTION # # BackupPC_link inspects every file in a new backup and # checks if an existing file from any previous backup is # identical. If so, the file is removed and replaced by # a hardlink to the existing file. If the file is new, # a hardlink to the file is made in the pool area, so that # this file is available for checking against future backups. # # Then, for incremental backups, hardlinks are made in the # backup directories to all files that were not extracted during # the incremental backups. The means the incremental dump looks # like a complete image of the PC. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; use BackupPC::Attrib; use BackupPC::PoolWrite; use BackupPC::Storage; use File::Find; use File::Path; use Digest::MD5; ########################################################################### # Initialize ########################################################################### die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); $bpc->ChildInit(); if ( @ARGV != 1 ) { print("usage: $0 \n"); exit(1); } if ( $ARGV[0] !~ /^([\w\.\s-]+)$/ ) { print("$0: bad host name '$ARGV[0]'\n"); exit(1); } my $host = $1; my $Dir = "$TopDir/pc/$host"; my($CurrDumpDir, $Compress); # # Re-read config file, so we can include the PC-specific config # $bpc->ConfigRead($host); %Conf = $bpc->Conf(); ########################################################################### # Process any backups that haven't been linked ########################################################################### my $md5 = Digest::MD5->new; my($nFilesNew, $sizeNew, $sizeNewComp); my($nFilesExist, $sizeExist, $sizeExistComp); while ( 1 ) { my @Backups = $bpc->BackupInfoRead($host); $nFilesNew = $sizeNew = $sizeNewComp = 0; $nFilesExist = $sizeExist = $sizeExistComp = 0; my($num); for ( $num = 0 ; $num < @Backups ; $num++ ) { last if ( $Backups[$num]{nFilesNew} eq "" || -f "$Dir/NewFileList.$Backups[$num]{num}" ); } last if ( $num >= @Backups ); # # Process list of new files left by BackupPC_dump # $CurrDumpDir = "$Dir/$Backups[$num]{num}"; $Compress = $Backups[$num]{compress}; if ( open(NEW, "<", "$Dir/NewFileList.$Backups[$num]{num}") ) { my(@shareAttribArgs); binmode(NEW); while ( ) { chomp; next if ( !/(\w+) (\d+) (.*)/ ); if ( $3 eq "attrib" ) { # # Defer linking top-level attrib file until the end # since it can appear multiple times when multiple shares # are dumped. # @shareAttribArgs = ($1, $2, "$CurrDumpDir/$3"); } else { LinkNewFile($1, $2, "$CurrDumpDir/$3"); } } LinkNewFile(@shareAttribArgs) if ( @shareAttribArgs ); close(NEW); } unlink("$Dir/NewFileList.$Backups[$num]{num}") if ( -f "$Dir/NewFileList.$Backups[$num]{num}" ); # # See if we should fill in this dump. We only need to fill # in incremental dumps. We can only fill in the incremental # dump if there is an existing filled in dump with the same # type of compression (on or off). Eg, we can't fill in # a compressed incremental if the most recent filled in dump # is not compressed. # my $noFill = 1; my $fillFromNum; if ( $Backups[$num]{type} ne "incr" ) { $noFill = 0 } elsif ( $Conf{IncrFill} ) { my $i; for ( $i = $num - 1 ; $i >= 0 ; $i-- ) { last if ( !$Backups[$i]{noFill} && ($Backups[$i]{compress} ? 1 : 0) == ($Compress ? 1 : 0) ); } my $prevDump = "$Dir/$Backups[$i]{num}"; if ( $i >= 0 && -d $prevDump ) { find({wanted => \&FillIncr, no_chdir => 1}, $prevDump); $noFill = 0; $fillFromNum = $Backups[$i]{num}; } } # # Update the backup info file in $TopDir/pc/$host/backups # @Backups = $bpc->BackupInfoRead($host); $Backups[$num]{nFilesExist} += $nFilesExist; $Backups[$num]{sizeExist} += $sizeExist; $Backups[$num]{sizeExistComp} += $sizeExistComp; $Backups[$num]{nFilesNew} += $nFilesNew; $Backups[$num]{sizeNew} += $sizeNew; $Backups[$num]{sizeNewComp} += $sizeNewComp; $Backups[$num]{noFill} = $noFill; $Backups[$num]{fillFromNum} = $fillFromNum; # # Save just this backup's info in case the main backups file # gets corrupted # BackupPC::Storage->backupInfoWrite($Dir, $Backups[$num]{num}, $Backups[$num], 1); # # Save the main backups file # $bpc->BackupInfoWrite($host, @Backups); } ########################################################################### # Subroutines ########################################################################### # # Fill in an incremental dump by making hardlinks to the previous # dump. # sub FillIncr { my($name) = $File::Find::name; my($newName); $name = $1 if ( $name =~ /(.*)/ ); return if ( $name !~ m{\Q$Dir\E/(\d+)/(.*)} ); $newName = "$CurrDumpDir/$2"; if ( -d $name && -d $newName ) { # # Merge the file attributes. # my $newAttr = BackupPC::Attrib->new({ compress => $Compress }); my $attr = BackupPC::Attrib->new({ compress => $Compress }); $newAttr->read($newName) if ( -f $newAttr->fileName($newName) ); $attr->read($name) if ( -f $attr->fileName($name) ); $newAttr->merge($attr); # # Now write it out, adding a link to the pool if necessary # my $data = $newAttr->writeData; my $origSize = length($data); my $fileName = $newAttr->fileName($newName); my $poolWrite = BackupPC::PoolWrite->new($bpc, $fileName, length($data), $Compress); $poolWrite->write(\$data); my($exists, $digest, $outSize, $errs) = $poolWrite->close; if ( @$errs ) { print("log ", join("", @$errs)); } if ( $exists ) { $nFilesExist++; $sizeExist += $origSize; $sizeExistComp += $outSize; } elsif ( $outSize > 0 ) { $nFilesNew++; $sizeNew += $origSize; $sizeNewComp += -s $outSize; LinkNewFile($digest, $origSize, $fileName); } } elsif ( -f $name && !-f $newName ) { # # Exists in the older filled backup, and not in the new, so link it # my($exists, $digest, $origSize, $outSize, $errs) = BackupPC::PoolWrite::LinkOrCopy( $bpc, $name, $Compress, $newName, $Compress); if ( $exists ) { $nFilesExist++; $sizeExist += $origSize; $sizeExistComp += $outSize; } elsif ( $outSize > 0 ) { $nFilesNew++; $sizeNew += $origSize; $sizeNewComp += -s $outSize; LinkNewFile($digest, $origSize, $newName); } } } # # Add a link in the pool to a new file # sub LinkNewFile { my($d, $size, $fileName) = @_; my $res = $bpc->MakeFileLink($fileName, $d, 1, $Compress); if ( $res == 1 ) { $nFilesExist++; $sizeExist += $size; $sizeExistComp += -s $fileName; } elsif ( $res == 2 ) { $nFilesNew++; $sizeNew += $size; $sizeNewComp += -s $fileName; } elsif ( $res != 0 && $res != -1 ) { print("log BackupPC_link got error $res when calling" . " MakeFileLink($fileName, $d, 1)\n"); } } BackupPC-3.3.2/bin/BackupPC_nightly0000555000076500000240000002616213042250554016010 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_nightly: Nightly cleanup & statistics script. # # DESCRIPTION # # BackupPC_nightly performs several administrative tasks: # # - monthly aging of per-PC log files (only with -m option) # # - pruning files from pool no longer used (ie: those with only one # hard link). # # - sending email to users and administrators (only with -m option) # # Usage: BackupPC_nightly [-m] poolRangeStart poolRangeEnd # # Flags: # # -m Do monthly aging of per-PC log files and sending of email. # Otherise, BackupPC_nightly just does pool pruning. # Since several BackupPC_nightly processes might run # concurrently, just the first one is given the -m flag # by BackupPC. # # The poolRangeStart and poolRangeEnd arguments are integers from 0 to 255. # These specify which parts of the pool to process. There are 256 2nd-level # directories in the pool (0/0, 0/1, ..., f/e, f/f). BackupPC_nightly # processes the given subset of this list (0 means 0/0, 255 means f/f). # Therefore, arguments of 0 255 process the entire pool, 0 127 does # the first half (ie: 0/0 through 7/f), 127 255 does the other half # (eg: 8/0 through f/f) and 0 15 does just the first 1/16 of the pool # (ie: 0/0 through 0/f). # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib qw( :BPC_DT_ALL ); use BackupPC::FileZIO; use Getopt::Std; use File::Path; use Data::Dumper; die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my(%Status, %Info, %Jobs, @BgQueue, @UserQueue, @CmdQueue); # # We delete unused pool files (link count 1) in sorted inode # order by gathering batches. We delete the first half of # each batch (ie: $PendingDeleteMax / 2 at a time). # my @PendingDelete; my $PendingDeleteMax = 10240; $bpc->ChildInit(); my %opts; if ( !getopts("m", \%opts) || @ARGV != 2 ) { print("usage: $0 [-m] poolRangeStart poolRangeEnd\n"); exit(1); } if ( $ARGV[0] !~ /^(\d+)$/ || $1 > 255 ) { print("$0: bad poolRangeStart '$ARGV[0]'\n"); exit(1); } my $poolRangeStart = $1; if ( $ARGV[1] !~ /^(\d+)$/ || $1 > 255 ) { print("$0: bad poolRangeEnd '$ARGV[1]'\n"); exit(1); } my $poolRangeEnd = $1; if ( $opts{m} ) { my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}); if ( $err ) { print("Can't connect to server ($err)\n"); exit(1); } my $reply = $bpc->ServerMesg("status hosts"); $reply = $1 if ( $reply =~ /(.*)/s ); eval($reply); } ########################################################################### # Get statistics on the pool, and remove files that have only one link. ########################################################################### my $fileCnt; # total number of files my $dirCnt; # total number of directories my $blkCnt; # total block size of files my $fileCntRm; # total number of removed files my $blkCntRm; # total block size of removed files my $blkCnt2; # total block size of files with just 2 links # (ie: files that only occur once among all backups) my $fileCntRep; # total number of file names containing "_", ie: files # that have repeated md5 checksums my $fileRepMax; # worse case number of files that have repeated checksums # (ie: max(nnn+1) for all names xxxxxxxxxxxxxxxx_nnn) my $fileLinkMax; # maximum number of hardlinks on a pool file my $fileLinkTotal; # total number of hardlinks on entire pool my $fileCntRename; # number of renamed files (to keep file numbering # contiguous) my %FixList; # list of paths that need to be renamed to avoid # new holes my @hexChars = qw(0 1 2 3 4 5 6 7 8 9 a b c d e f); for my $pool ( qw(pool cpool) ) { for ( my $i = $poolRangeStart ; $i <= $poolRangeEnd ; $i++ ) { my $dir = "$hexChars[int($i / 16)]/$hexChars[$i % 16]"; # print("Doing $pool/$dir\n") if ( ($i % 16) == 0 ); $fileCnt = 0; $dirCnt = 0; $blkCnt = 0; $fileCntRm = 0; $blkCntRm = 0; $blkCnt2 = 0; $fileCntRep = 0; $fileRepMax = 0; $fileLinkMax = 0; $fileCntRename = 0; %FixList = (); $bpc->find({wanted => \&GetPoolStats}, "$TopDir/$pool/$dir") if ( -d "$TopDir/$pool/$dir" ); my $kb = $blkCnt / 2; my $kbRm = $blkCntRm / 2; my $kb2 = $blkCnt2 / 2; # # Main BackupPC_nightly counts the top-level directory # $dirCnt++ if ( $opts{m} && -d "$TopDir/$pool" && $i == 0 ); # # Also count the next level directories # $dirCnt++ if ( ($i % 16) == 0 && -d "$TopDir/$pool/$hexChars[int($i / 16)]" ); # # We need to process all pending deletes before we do the # renames # if ( @PendingDelete ) { sleep(1); processPendingDeletes(1); } # # Now make sure that files with repeated checksums are still # sequentially numbered # foreach my $name ( sort(keys(%FixList)) ) { my $rmCnt = $FixList{$name} + 1; my $new = -1; for ( my $old = -1 ; ; $old++ ) { my $oldName = $name; $oldName .= "_$old" if ( $old >= 0 ); if ( !-f $oldName ) { # # We know we are done when we have missed at least # the number of files that were removed from this # base name, plus a couple just to be sure # last if ( $rmCnt-- <= 0 ); next; } my $newName = $name; $newName .= "_$new" if ( $new >= 0 ); $new++; next if ( $oldName eq $newName ); rename($oldName, $newName); $fileCntRename++; } } print("BackupPC_stats $i = $pool,$fileCnt,$dirCnt,$kb,$kb2,$kbRm," . "$fileCntRm,$fileCntRep,$fileRepMax," . "$fileCntRename,$fileLinkMax,$fileLinkTotal\n"); } } sleep(1); processPendingDeletes(1); ########################################################################### # Tell BackupPC that it is now ok to start running BackupPC_dump # commands. We are guaranteed that no BackupPC_link commands will # run since only a single CmdQueue command runs at a time, and # that means we are safe. As of 3.x this is irrelevant since # BackupPC_dump runs independent of BackupPC_dump. ########################################################################### printf("BackupPC_nightly lock_off\n"); ########################################################################### # Send email and generation of backupInfo files for each backup ########################################################################### if ( $opts{m} ) { print("log BackupPC_nightly now running BackupPC_sendEmail\n"); system("$BinDir/BackupPC_sendEmail"); doBackupInfoUpdate(); } # # Update the backupInfo files based on the backups file. # We do this just once a week (on Sun) since it is only # needed for old backups with BackupPC <= 2.1.2. # sub doBackupInfoUpdate { my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); return if ( $wday != 0 ); foreach my $host ( sort(keys(%{$bpc->HostInfoRead()})) ) { my @Backups = $bpc->BackupInfoRead($host); for ( my $i = 0 ; $i < @Backups ; $i++ ) { # # BackupPC::Storage->backupInfoWrite won't overwrite # an existing file # BackupPC::Storage->backupInfoWrite("$TopDir/pc/$host", $Backups[$i]{num}, $Backups[$i]); } } } sub GetPoolStats { my($file, $fullPath) = @_; my($inode, $nlinks, $nblocks) = (lstat($file))[1, 3, 12]; if ( -d _ ) { $dirCnt++; return; } elsif ( ! -f _ ) { return; } if ( $nlinks == 1 ) { $blkCntRm += $nblocks; $fileCntRm++; # # Save the files for later batch deletion. # # This is so we can remove them in inode order, and additionally # reduce any remaining chance of race condition of linking to # pool files vs removing pool files. (Other aspects of the # design should eliminate race conditions.) # push(@PendingDelete, { inode => $inode, path => $fullPath } ); if ( @PendingDelete > $PendingDeleteMax ) { processPendingDeletes(0); } # # We must keep repeated files numbered sequential (ie: files # that have the same checksum are appended with _0, _1 etc). # There are two cases: we remove the base file xxxx, but xxxx_0 # exists, or we remove any file of the form xxxx_nnn. We remember # the base name and fix it up later (not in the middle of find). # $fullPath =~ s/_\d+$//; $FixList{$fullPath}++; } else { if ( $file =~ /_(\d+)$/ ) { $fileRepMax = $1 + 1 if ( $fileRepMax <= $1 ); $fileCntRep++; } $fileCnt += 1; $blkCnt += $nblocks; $blkCnt2 += $nblocks if ( $nlinks == 2 ); $fileLinkMax = $nlinks if ( $fileLinkMax < $nlinks ); $fileLinkTotal += $nlinks - 1; } } sub processPendingDeletes { my($doAll) = @_; my @delete; if ( !$doAll ) { @delete = splice(@PendingDelete, 0, $PendingDeleteMax / 2); } else { @delete = @PendingDelete; @PendingDelete = (); } for my $f ( sort({ $a->{inode} <=> $b->{inode} } @delete) ) { my($nlinks) = (lstat($f->{path}))[3]; next if ( $nlinks != 1 ); # print("Deleting $f->{path} ($f->{inode})\n"); unlink($f->{path}); } } BackupPC-3.3.2/bin/BackupPC_restore0000555000076500000240000004513013042250554016011 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_restore: Restore files to a client. # # DESCRIPTION # # Usage: BackupPC_restore # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; use BackupPC::FileZIO; use BackupPC::Xfer; use Socket; use File::Path; use Getopt::Std; use vars qw( %RestoreReq ); ########################################################################### # Initialize ########################################################################### die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my $NeedPostCmd; my($hostIP, $host, $client, $reqFileName, %stat); $bpc->ChildInit(); if ( @ARGV != 3 ) { print("usage: $0 \n"); exit(1); } $hostIP = $1 if ( $ARGV[0] =~ /(.+)/ ); $client = $1 if ( $ARGV[1] =~ /(.+)/ ); if ( $ARGV[2] !~ /^([\w.]+)$/ ) { print("$0: bad reqFileName (arg #3): $ARGV[2]\n"); exit(1); } $reqFileName = $1; my $startTime = time(); my $Hosts = $bpc->HostInfoRead($client); my $Dir = "$TopDir/pc/$client"; my @xferPid = (); my $tarPid = -1; # # Catch various signals # $SIG{INT} = \&catch_signal; $SIG{ALRM} = \&catch_signal; $SIG{TERM} = \&catch_signal; $SIG{PIPE} = \&catch_signal; $SIG{STOP} = \&catch_signal; $SIG{TSTP} = \&catch_signal; $SIG{TTIN} = \&catch_signal; my $Pid = $$; mkpath($Dir, 0, 0777) if ( !-d $Dir ); if ( !-f "$Dir/LOCK" ) { open(LOCK, ">", "$Dir/LOCK") && close(LOCK); } my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $logPath = sprintf("$Dir/LOG.%02d%04d", $mon + 1, $year + 1900); if ( !-f $logPath ) { # # Compress and prune old log files # my $lastLog = $Conf{MaxOldPerPCLogFiles} - 1; foreach my $file ( $bpc->sortedPCLogFiles($client) ) { if ( $lastLog <= 0 ) { unlink($file); next; } $lastLog--; next if ( $file =~ /\.z$/ || !$Conf{CompressLevel} ); BackupPC::FileZIO->compressCopy($file, "$file.z", undef, $Conf{CompressLevel}, 1); } } open(LOG, ">>", $logPath); select(LOG); $| = 1; select(STDOUT); # # Read the request file # if ( !(my $ret = do "$Dir/$reqFileName") ) { my $err; if ( $@ ) { $err = "couldn't parse $Dir/$reqFileName: $@"; } elsif ( !defined($ret) ) { $err = "couldn't do $Dir/$reqFileName: $!"; } else { $err = "couldn't run $Dir/$reqFileName"; } $stat{hostError} = $err; exit(RestoreCleanup($client)); } # # Re-read config file, so we can include the PC-specific config # if ( defined(my $error = $bpc->ConfigRead($client)) ) { $stat{hostError} = "Can't read PC's config file: $error"; exit(RestoreCleanup($client)); } %Conf = $bpc->Conf(); # # Make sure we eventually timeout if there is no activity from # the data transport program. # alarm($Conf{ClientTimeout}); # # See if the host name is aliased # if ( $Conf{ClientNameAlias} ne "" ) { $host = $Conf{ClientNameAlias}; } else { $host = $client; } # # Find its IP address # if ( $hostIP !~ /\d+\.\d+\.\d+\.\d+/ ) { if ( !defined(gethostbyname($host)) ) { # # Ok, NS doesn't know about it. Maybe it is a NetBios name # instead. # if ( !defined($hostIP = $bpc->NetBiosHostIPFind($host)) ) { $stat{hostError} = "Can't find host $host"; exit(RestoreCleanup($client)); } } else { $hostIP = $host; } } # # Check if $host is alive # my $delay = $bpc->CheckHostAlive($hostIP); if ( $delay < 0 ) { $stat{hostError} = "no ping response from $host ($hostIP)"; exit(RestoreCleanup($client)); } elsif ( $delay > $Conf{PingMaxMsec} ) { $stat{hostError} = sprintf("ping too slow: %.4gmsec (max is %gmsec)\n", $delay, $Conf{PingMaxMsec}); exit(RestoreCleanup($client)); } # # Make sure it is really the machine we expect # if ( (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { $stat{hostError} = $errMsg; exit(RestoreCleanup($client)); } # # Setup file extension for compression and open RestoreLOG output file # if ( $Conf{CompressLevel} && !BackupPC::FileZIO->compOk ) { $stat{hostError} = "Compress:Zlib not found"; exit(RestoreCleanup($client)); } my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; my $RestoreLOG = BackupPC::FileZIO->open("$Dir/RestoreLOG$fileExt", 1, $Conf{CompressLevel}); my $tarCreateFileCnt = 0; my $tarCreateByteCnt = 0; my $tarCreateDirCnt = 0; my $tarCreateErrCnt = 1; # assume not ok until we learn otherwise my $tarCreateErr; my($logMsg, $xfer); $stat{xferOK} = $stat{hostAbort} = undef; $stat{hostError} = $stat{lastOutputLine} = undef; local(*RH, *WH); # # Run an optional pre-restore command # UserCommandRun("RestorePreUserCmd"); if ( $? && $Conf{UserCmdCheckStatus} ) { $stat{hostError} = "RestorePreUserCmd returned error status $?"; exit(RestoreCleanup($client)); } $NeedPostCmd = 1; $xfer = BackupPC::Xfer::create($Conf{XferMethod}, $bpc); if ( !defined($xfer) ) { my $errStr = BackupPC::Xfer::errStr(); UserCommandRun("RestorePostUserCmd") if ( $NeedPostCmd ); $stat{hostError} = $errStr; exit(RestoreCleanup($client)); } my $useTar = $xfer->useTar; if ( $useTar ) { # # Create a socketpair to connect BackupPC_tarCreate to the transport # program (smbclient, tar, etc). # WH is the write handle for writing, provided to BackupPC_tarCreate # and RH is the other end of the pipe for reading provided to the # transport program. # if ( socketpair(RH, WH, AF_UNIX, SOCK_STREAM, PF_UNSPEC) ) { shutdown(RH, 1); # no writing to this socket shutdown(WH, 0); # no reading from this socket setsockopt(RH, SOL_SOCKET, SO_RCVBUF, 8 * 65536); setsockopt(WH, SOL_SOCKET, SO_SNDBUF, 8 * 65536); } else { # # Default to pipe() if socketpair() doesn't work. # pipe(RH, WH); } } # # Run the transport program, which reads from RH and extracts the data. # my @Backups = $bpc->BackupInfoRead($RestoreReq{hostSrc}); my $xferArgs = { client => $client, host => $host, hostIP => $hostIP, type => "restore", shareName => $RestoreReq{shareDest}, pipeRH => *RH, pipeWH => *WH, XferLOG => $RestoreLOG, XferMethod => $Conf{XferMethod}, logLevel => $Conf{XferLogLevel}, bkupSrcHost => $RestoreReq{hostSrc}, bkupSrcShare => $RestoreReq{shareSrc}, bkupSrcNum => $RestoreReq{num}, backups => \@Backups, pathHdrSrc => $RestoreReq{pathHdrSrc}, pathHdrDest => $RestoreReq{pathHdrDest}, fileList => $RestoreReq{fileList}, pidHandler => \&pidHandler, }; $xfer->args($xferArgs); if ( !defined($logMsg = $xfer->start()) ) { UserCommandRun("RestorePostUserCmd") if ( $NeedPostCmd ); $stat{hostError} = "xfer start failed: ", $xfer->errStr; exit(RestoreCleanup($client)); } if ( $useTar ) { # # Now do the restore by running BackupPC_tarCreate # # The parent must close the read handle since the transport program # is using it. # close(RH); # # fork a child for BackupPC_tarCreate. TAR is a file handle # on which we (the parent) read the stderr from BackupPC_tarCreate. # my @tarPathOpts; if ( defined($RestoreReq{pathHdrDest}) && $RestoreReq{pathHdrDest} ne $RestoreReq{pathHdrSrc} ) { @tarPathOpts = ("-r", $RestoreReq{pathHdrSrc}, "-p", $RestoreReq{pathHdrDest} ); } my @tarArgs = ( "-h", $RestoreReq{hostSrc}, "-n", $RestoreReq{num}, "-s", $RestoreReq{shareSrc}, "-t", @tarPathOpts, @{$RestoreReq{fileList}}, ); my $runMsg = "Running: " . $bpc->execCmd2ShellCmd("$BinDir/BackupPC_tarCreate", @tarArgs) . "\n"; $RestoreLOG->write(\$runMsg); if ( !defined($tarPid = open(TAR, "-|")) ) { close(WH); # FIX: need to cleanup xfer UserCommandRun("RestorePostUserCmd") if ( $NeedPostCmd ); $stat{hostError} = "Can't fork to run tar"; exit(RestoreCleanup($client)); } binmode(TAR); if ( !$tarPid ) { # # This is the tarCreate child. Clone STDERR to STDOUT, # STDOUT to WH, and then exec BackupPC_tarCreate. # setpgrp 0,0; close(STDERR); open(STDERR, ">&STDOUT"); close(STDOUT); open(STDOUT, ">&WH"); alarm(0); exec("$BinDir/BackupPC_tarCreate", @tarArgs); print(LOG $bpc->timeStamp, "can't exec $BinDir/BackupPC_tarCreate\n"); # FIX: need to cleanup xfer exit(0); } # # The parent must close the write handle since BackupPC_tarCreate # is using it. # close(WH); @xferPid = $xfer->xferPid; print(LOG $bpc->timeStamp, $logMsg, "\n"); print("started_restore\n"); pidHandler(@xferPid); # # Parse the output of the transfer program and BackupPC_tarCreate # while they run. Since we are reading from two or more children # we use a select. # my($FDread, $tarOut, $mesg); vec($FDread, fileno(TAR), 1) = 1; $xfer->setSelectMask(\$FDread); SCAN: while ( 1 ) { my $ein = $FDread; last if ( $FDread =~ /^\0*$/ ); alarm($Conf{ClientTimeout}); select(my $rout = $FDread, undef, $ein, undef); if ( vec($rout, fileno(TAR), 1) ) { if ( sysread(TAR, $mesg, 8192) <= 0 ) { vec($FDread, fileno(TAR), 1) = 0; if ( !close(TAR) ) { $tarCreateErrCnt = 1; $tarCreateErr = "BackupPC_tarCreate failed"; } } else { $tarOut .= $mesg; } } while ( $tarOut =~ /(.*?)[\n\r]+(.*)/s ) { $_ = $1; $tarOut = $2; $RestoreLOG->write(\"tarCreate: $_\n"); if ( /^Done: (\d+) files, (\d+) bytes, (\d+) dirs, (\d+) specials, (\d+) errors/ ) { $tarCreateFileCnt = $1; $tarCreateByteCnt = $2; $tarCreateDirCnt = $3; $tarCreateErrCnt = $5; } } last if ( !$xfer->readOutput(\$FDread, $rout) ); while ( my $str = $xfer->logMsgGet ) { print(LOG $bpc->timeStamp, "xfer: $str\n"); } if ( $xfer->getStats->{fileCnt} == 1 ) { # # Make sure it is still the machine we expect. We do this while # the transfer is running to avoid a potential race condition if # the ip address was reassigned by dhcp just before we started # the transfer. # if ( my $errMsg = CorrectHostCheck($hostIP, $host) ) { $stat{hostError} = $errMsg; last SCAN; } } } } else { # # otherwise the xfer module does everything for us # print(LOG $bpc->timeStamp, $logMsg . "\n"); print("started_restore\n"); ($tarCreateFileCnt, $tarCreateByteCnt, $tarCreateErrCnt, $tarCreateErr) = $xfer->run(); } alarm(0); # # Merge the xfer status (need to accumulate counts) # my $newStat = $xfer->getStats; foreach my $k ( (keys(%stat), keys(%$newStat)) ) { next if ( !defined($newStat->{$k}) ); if ( $k =~ /Cnt$/ ) { $stat{$k} += $newStat->{$k}; delete($newStat->{$k}); next; } if ( !defined($stat{$k}) ) { $stat{$k} = $newStat->{$k}; delete($newStat->{$k}); next; } } exit(RestoreCleanup($client)); ########################################################################### # Subroutines ########################################################################### sub CorrectHostCheck { my($hostIP, $host) = @_; return if ( $hostIP eq $host && !$Conf{FixedIPNetBiosNameCheck} || $Conf{NmbLookupCmd} eq "" ); my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($hostIP); return "host $host has mismatching netbios name $netBiosHost" if ( lc($netBiosHost) ne lc(substr($host, 0, 15)) ); return; } sub catch_signal { my $signame = shift; # # Children quit quietly on ALRM # exit(1) if ( $Pid != $$ && $signame eq "ALRM" ); # # Ignore signals in children # return if ( $Pid != $$ ); # # Note: needs to be tested for each kind of XferMethod # print(LOG $bpc->timeStamp, "cleaning up after signal $signame\n"); $SIG{$signame} = 'IGNORE'; $RestoreLOG->write(\"exiting after signal $signame\n"); $stat{xferOK} = 0; if ( $signame eq "INT" ) { $stat{hostError} = "aborted by user (signal=$signame)"; } else { $stat{hostError} = "aborted by signal=$signame"; } exit(RestoreCleanup($client)); } # # Cleanup and update the restore status # sub RestoreCleanup { my($client) = @_; $stat{xferOK} = 0 if ( $stat{hostError} || $stat{hostAbort} || $tarCreateErr ); if ( !$stat{xferOK} ) { # # kill off the tranfer program, first nicely then forcefully # if ( @xferPid ) { kill($bpc->sigName2num("INT"), @xferPid); sleep(1); kill($bpc->sigName2num("KILL"), @xferPid); } # # kill off the tar process, first nicely then forcefully # if ( $tarPid > 0 ) { kill($bpc->sigName2num("INT"), $tarPid); sleep(1); kill($bpc->sigName2num("KILL"), $tarPid); } } my $lastNum = -1; my @Restores; # # Do one last check to make sure it is still the machine we expect. # if ( $stat{xferOK} && (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { $stat{hostError} = $errMsg; $stat{xferOK} = 0; } @Restores = $bpc->RestoreInfoRead($client); for ( my $i = 0 ; $i < @Restores ; $i++ ) { $lastNum = $Restores[$i]{num} if ( $lastNum < $Restores[$i]{num} ); } $lastNum++; # # Run an optional post-restore command # if ( $NeedPostCmd ) { UserCommandRun("RestorePostUserCmd"); if ( $? && $Conf{UserCmdCheckStatus} ) { $stat{hostError} = "RestorePostUserCmd returned error status $?"; $stat{xferOK} = 0; } } rename("$Dir/RestoreLOG$fileExt", "$Dir/RestoreLOG.$lastNum$fileExt"); rename("$Dir/$reqFileName", "$Dir/RestoreInfo.$lastNum"); my $endTime = time(); # # If the restore failed, clean up # if ( !$stat{xferOK} ) { # # wait a short while and see if the system is still alive # $stat{hostError} ||= $tarCreateErr if ( $tarCreateErr ne "" ); $stat{hostError} = $stat{lastOutputLine} if ( $stat{hostError} eq "" ); sleep(2); if ( $bpc->CheckHostAlive($hostIP) < 0 ) { $stat{hostAbort} = 1; } if ( $stat{hostAbort} && $stat{hostError} eq "" ) { $stat{hostError} = "lost network connection during restore"; } $RestoreLOG->write(\"restore failed: $stat{hostError}\n") if ( defined($RestoreLOG) ); } $RestoreLOG->close() if ( defined($RestoreLOG) ); # # Add the new restore information to the restore file # @Restores = $bpc->RestoreInfoRead($client); my $i = @Restores; $Restores[$i]{num} = $lastNum; $Restores[$i]{startTime} = $startTime; $Restores[$i]{endTime} = $endTime; $Restores[$i]{result} = $stat{xferOK} ? "ok" : "failed"; $Restores[$i]{errorMsg} = $stat{hostError}; $Restores[$i]{nFiles} = $tarCreateFileCnt; $Restores[$i]{size} = $tarCreateByteCnt; $Restores[$i]{tarCreateErrs} = $tarCreateErrCnt; $Restores[$i]{xferErrs} = $stat{xferErrCnt} || 0; while ( @Restores > $Conf{RestoreInfoKeepCnt} ) { my $num = $Restores[0]{num}; unlink("$Dir/RestoreLOG.$num.z"); unlink("$Dir/RestoreLOG.$num"); unlink("$Dir/RestoreInfo.$num"); shift(@Restores); } $bpc->RestoreInfoWrite($client, @Restores); if ( !$stat{xferOK} ) { print(LOG $bpc->timeStamp, "restore failed ($stat{hostError})\n"); print("restore failed: $stat{hostError}\n"); return 1; } else { $stat{xferErrCnt} ||= 0; print(LOG $bpc->timeStamp, "restore $lastNum complete" . " ($tarCreateFileCnt files, $tarCreateByteCnt bytes," . " $tarCreateDirCnt dirs, $stat{xferErrCnt} xferErrs)\n"); print("restore complete\n"); return; } } # # The Xfer method might tell us from time to time about processes # it forks. We tell BackupPC about this (for status displays) and # keep track of the pids in case we cancel the backup # sub pidHandler { @xferPid = @_; @xferPid = grep(/./, @xferPid); return if ( !@xferPid && $tarPid < 0 ); my @pids = @xferPid; push(@pids, $tarPid) if ( $tarPid > 0 ); my $str = join(",", @pids); $RestoreLOG->write(\"Xfer PIDs are now $str\n") if ( defined($RestoreLOG) ); print("xferPids $str\n"); } # # Run an optional pre- or post-dump command # sub UserCommandRun { my($cmdType) = @_; return if ( !defined($Conf{$cmdType}) ); my $vars = { xfer => $xfer, client => $client, host => $host, hostIP => $hostIP, share => $RestoreReq{shareDest}, XferMethod => $Conf{XferMethod}, sshPath => $Conf{SshPath}, LOG => *LOG, user => $Hosts->{$client}{user}, moreUsers => $Hosts->{$client}{moreUsers}, XferLOG => $RestoreLOG, stat => \%stat, xferOK => $stat{xferOK} || 0, hostError => $stat{hostError}, type => "restore", bkupSrcHost => $RestoreReq{hostSrc}, bkupSrcShare => $RestoreReq{shareSrc}, bkupSrcNum => $RestoreReq{num}, backups => \@Backups, pathHdrSrc => $RestoreReq{pathHdrSrc}, pathHdrDest => $RestoreReq{pathHdrDest}, fileList => $RestoreReq{fileList}, cmdType => $cmdType, }; my $cmd = $bpc->cmdVarSubstitute($Conf{$cmdType}, $vars); $RestoreLOG->write(\"Executing $cmdType: @$cmd\n"); # # Run the user's command, dumping the stdout/stderr into the # Xfer log file. Also supply the optional $vars and %Conf in # case the command is really perl code instead of a shell # command. # $bpc->cmdSystemOrEval($cmd, sub { $RestoreLOG->write(\$_[0]); }, $vars, \%Conf); } BackupPC-3.3.2/bin/BackupPC_sendEmail0000555000076500000240000003655213042250554016237 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_sendEmail: send status emails to users and admins # # DESCRIPTION # # BackupPC_sendEmail: send status emails to users and admins. # BackupPC_sendEmail is run by BackupPC_nightly, so it runs # once every night. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; use BackupPC::FileZIO; use Encode; use Data::Dumper; use Getopt::Std; use DirHandle (); use vars qw($Lang $TopDir $BinDir $LogDir %Conf %HostConf $Hosts); die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); $TopDir = $bpc->TopDir(); $LogDir = $bpc->LogDir(); $BinDir = $bpc->BinDir(); %Conf = $bpc->Conf(); $Lang = $bpc->Lang(); $Hosts = $bpc->HostInfoRead(); $bpc->ChildInit(); use vars qw(%UserEmailInfo); do "$LogDir/UserEmailInfo.pl"; my %opts; if ( !getopts("ctu:", \%opts) || @ARGV != 0 ) { print <{$host}) ); delete($UserEmailInfo{$user}{$host}); } next if ( $UserEmailInfo{$user} ); delete($UserEmailInfo{$user}); } my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}); if ( $err ) { if ( $opts{c} && $Conf{EMailAdminUserName} ne "" ) { my $headers = $Conf{EMailHeaders}; $headers .= "\n" if ( $headers !~ /\n$/ ); my $mesg = <ServerMesg("status hosts info"); $reply = $1 if ( $reply =~ /(.*)/s ); my(%Status, %Info, %Jobs, @BgQueue, @UserQueue, @CmdQueue); eval($reply); ########################################################################### # Generate test message if required ########################################################################### if ( $opts{u} ne "" ) { my $headers = $Conf{EMailHeaders}; $headers .= "\n" if ( $headers !~ /\n$/ ); my $mesg = <ConfigRead($host); %HostConf = $bpc->Conf(); my $user = $Hosts->{$host}{user}; # # Accumulate host errors for the admin email below # if ( ($Status{$host}{reason} eq "Reason_backup_failed" || $Status{$host}{reason} eq "Reason_restore_failed") && $Status{$host}{error} !~ /^lost network connection to host/ && !$HostConf{BackupsDisable} ) { push(@AdminBadHostsEmail, $HostConf{EMailAdminUserName}); push(@AdminBadHosts, "$host ($Status{$host}{error})"); } next if ( time - $UserEmailInfo{$user}{$host}{lastTime} < $HostConf{EMailNotifyMinDays} * 24*3600 || $HostConf{XferMethod} eq "archive" || $HostConf{BackupsDisable} || $Hosts->{$host}{user} eq "" || $user eq "" ); my @Backups = $bpc->BackupInfoRead($host); my $numBackups = @Backups; if ( $numBackups == 0 ) { my $subj = defined($HostConf{EMailNoBackupEverSubj}) ? $HostConf{EMailNoBackupEverSubj} : $Lang->{EMailNoBackupEverSubj}; my $mesg = defined($HostConf{EMailNoBackupEverMesg}) ? $HostConf{EMailNoBackupEverMesg} : $Lang->{EMailNoBackupEverMesg}; sendUserEmail($user, $host, $mesg, $subj, { userName => user2name($user) }) if ( !defined($Jobs{$host}) ); next; } my $last = my $lastFull = my $lastIncr = 0; my $lastGoodOutlook = 0; my $lastNum = -1; my $numBadOutlook = 0; for ( my $i = 0 ; $i < @Backups ; $i++ ) { my $fh; # # ignore partials -> only fulls and incrs should be used # in figuring out when the last good backup was # next if ( $Backups[$i]{type} eq "partial" ); $lastNum = $Backups[$i]{num} if ( $lastNum < $Backups[$i]{num} ); if ( $Backups[$i]{type} eq "full" ) { $lastFull = $Backups[$i]{startTime} if ( $lastFull < $Backups[$i]{startTime} ); } else { $lastIncr = $Backups[$i]{startTime} if ( $lastIncr < $Backups[$i]{startTime} ); } $last = $Backups[$i]{startTime} if ( $last < $Backups[$i]{startTime} ); my $badOutlook = 0; my $file = "$TopDir/pc/$host/SmbLOG.$Backups[$i]{num}"; my $comp = 0; if ( !-f $file ) { $file = "$TopDir/pc/$host/XferLOG.$Backups[$i]{num}"; if ( !-f $file ) { $comp = 1; $file = "$TopDir/pc/$host/SmbLOG.$Backups[$i]{num}.z"; $file = "$TopDir/pc/$host/XferLOG.$Backups[$i]{num}.z" if ( !-f $file ); } } next if ( !defined($fh = BackupPC::FileZIO->open($file, 0, $comp)) ); while ( 1 ) { my $s = $fh->readLine(); last if ( $s eq "" ); if ( $s =~ /^\s*Error reading file.*\.pst : (ERRDOS - ERRlock|NT_STATUS_FILE_LOCK_CONFLICT)/ || $s =~ /^\s*Error reading file.*\.pst\. Got 0 bytes/ ) { $badOutlook = 1; last; } } $fh->close(); $numBadOutlook += $badOutlook; if ( !$badOutlook ) { $lastGoodOutlook = $Backups[$i]{startTime} if ( $lastGoodOutlook < $Backups[$i]{startTime} ); } } if ( time - $last > $HostConf{EMailNotifyOldBackupDays} * 24*3600 ) { my $subj = defined($HostConf{EMailNoBackupRecentSubj}) ? $HostConf{EMailNoBackupRecentSubj} : $Lang->{EMailNoBackupRecentSubj}; my $mesg = defined($HostConf{EMailNoBackupRecentMesg}) ? $HostConf{EMailNoBackupRecentMesg} : $Lang->{EMailNoBackupRecentMesg}; my $firstTime = sprintf("%.1f", (time - $Backups[0]{startTime}) / (24*3600)); my $days = sprintf("%.1f", (time - $last) / (24 * 3600)); sendUserEmail($user, $host, $mesg, $subj, { firstTime => $firstTime, days => $days, userName => user2name($user), numBackups => $numBackups, }) if ( !defined($Jobs{$host}) ); next; } if ( $numBadOutlook > 0 && time - $lastGoodOutlook > $HostConf{EMailNotifyOldOutlookDays} * 24 * 3600 ) { my($days, $howLong); if ( $lastGoodOutlook == 0 ) { $howLong = eval("qq{$Lang->{howLong_not_been_backed_up}}"); } else { $days = sprintf("%.1f", (time - $lastGoodOutlook) / (24*3600)); $howLong = eval("qq{$Lang->{howLong_not_been_backed_up_for_days_days}}"); } my $subj = defined($HostConf{EMailOutlookBackupSubj}) ? $HostConf{EMailOutlookBackupSubj} : $Lang->{EMailOutlookBackupSubj}; my $mesg = defined($HostConf{EMailOutlookBackupMesg}) ? $HostConf{EMailOutlookBackupMesg} : $Lang->{EMailOutlookBackupMesg}; my $firstTime = sprintf("%.1f", (time - $Backups[0]{startTime}) / (24*3600)); my $lastTime = sprintf("%.1f", (time - $Backups[$#Backups]{startTime}) / (24*3600)); sendUserEmail($user, $host, $mesg, $subj, { days => $days, firstTime => $firstTime, lastTime => $lastTime, numBackups => $numBackups, userName => user2name($user), howLong => $howLong, serverHost => $HostConf{ServerHost}, }) if ( !defined($Jobs{$host}) ); } } # # Send per-host errors to per-host admin email # if ( @AdminBadHosts ) { for my $i (0 .. $#AdminBadHosts) { my $badHost = $AdminBadHosts[$i]; if ( $AdminBadHostsEmail[$i] ne "" ) { my $badHostMesg .= < 0 ) { my $n = $Info{DUDailySkipHostCntPrev}; my $m = $Conf{DfMaxUsagePct}; $adminMesg .= <new("$TopDir/pc") or die("Can't read $TopDir/pc: $!"); my @oldDirs = (); my @files = $d->read; $d->close; foreach my $host ( @files ) { next if ( $host =~ /^\./ || defined($Status{$host}) ); push(@oldDirs, "$TopDir/pc/$host"); } if ( @oldDirs ) { my $oldDirs = join("\n - ", sort(@oldDirs)); $adminMesg .= <Dump( [\%UserEmailInfo], [qw(*UserEmailInfo)]); if ( open(HOST, ">", "$LogDir/UserEmailInfo.pl") ) { binmode(HOST); print(HOST $dumpStr); close(HOST); } } exit(0); sub user2name { my($user) = @_; my($name) = (getpwnam($user))[6]; $name =~ s/\s.*//; $name = $user if ( $name eq "" ); return $name; } sub sendUserEmail { my($user, $host, $mesg, $subj, $vars) = @_; return if ( $Conf{BackupsDisable} ); $vars->{user} = $user; $vars->{host} = $host; $vars->{headers} = $Conf{EMailHeaders}; $vars->{headers} .= "\n" if ( $vars->{headers} !~ /\n$/ ); $vars->{domain} = $Conf{EMailUserDestDomain}; $vars->{CgiURL} = $Conf{CgiURL}; $subj =~ s/\$(\w+)/defined($vars->{$1}) ? $vars->{$1} : "\$$1"/eg; $vars->{subj} = encode('MIME-Header', $subj); $mesg =~ s/\$(\w+)/defined($vars->{$1}) ? $vars->{$1} : "\$$1"/eg; SendMail($mesg); $UserEmailInfo{$user}{$host}{lastTime} = time; $UserEmailInfo{$user}{$host}{lastSubj} = $subj; } sub SendMail { my($mesg) = @_; my $from = $Conf{EMailFromUserName}; my $utf8 = 1 if ( $Conf{EMailHeaders} =~ /Content-Type:.*charset="utf-?8"/i ); local(*MAIL); if ( $opts{t} ) { binmode(STDOUT, ":utf8") if ( $utf8 ); print("#" x 75, "\n"); print $mesg; return; } $from = "-f $from" if ( $from ne "" ); print("Sending test email using $Conf{SendmailPath} -t $from\n") if ( $opts{u} ne "" ); if ( !open(MAIL, "|$Conf{SendmailPath} -t $from") ) { print("Can't run sendmail ($Conf{SendmailPath}): $!\n"); return; } binmode(MAIL, ":utf8") if ( $utf8 ); print MAIL $mesg; close(MAIL); } BackupPC-3.3.2/bin/BackupPC_serverMesg0000555000076500000240000000466513042250554016460 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_serverMesg: Send one or more commands to the BackupPC server. # # DESCRIPTION # As of v1.5.0 the BackupPC server communicates via a unix or internet # domain socket. Every message is protected with an MD5 digest, based # on a shared secret, a sequence number, and a per-connection unique # key. This minimizes the risk of an attacked issuing fake commands # to the BackupPC server. # # Previously, telnet could be used to talk to the BackupPC server. # As of v1.5.0 that is no longer possible. # # This script replaces telnet as a mechanism for sending BackupPC # messages. Usage: # # BackupPC_serverMesg mesg # # Example: # BackupPC_serverMesg status info # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; use BackupPC::FileZIO; use File::Find; use File::Path; use Data::Dumper; die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); $bpc->ChildInit(); if ( !@ARGV ) { print("usage: $0 mesg\n"); exit(1); } my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}); if ( $err ) { print("Can't connect to server ($err)\n"); exit(1); } my $reply = $bpc->ServerMesg(join(" ", @ARGV)); print("Got reply: $reply"); BackupPC-3.3.2/bin/BackupPC_tarCreate0000555000076500000240000004540413042250554016244 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_tarCreate: create a tar archive of an existing dump # for restore on a client. # # DESCRIPTION # # Usage: BackupPC_tarCreate [options] files/directories... # # Flags: # Required options: # # -h host Host from which the tar archive is created. # -n dumpNum Dump number from which the tar archive is created. # A negative number means relative to the end (eg -1 # means the most recent dump, -2 2nd most recent etc). # -s shareName Share name from which the tar archive is created. # # Other options: # -t print summary totals # -r pathRemove path prefix that will be replaced with pathAdd # -p pathAdd new path prefix # -b BLOCKS output write buffer size in 512-byte blocks (default 20; same as tar) # -w readBufSz buffer size for reading files (default 1048576 = 1MB) # -e charset charset for encoding file names (default: value of # $Conf{ClientCharset} when backup was done) # -l just print a file listing; don't generate an archive # -L just print a detailed file listing; don't generate an archive # # The -h, -n and -s options specify which dump is used to generate # the tar archive. The -r and -p options can be used to relocate # the paths in the tar archive so extracted files can be placed # in a location different from their original location. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use File::Path; use Getopt::Std; use Encode qw/from_to/; use BackupPC::Lib; use BackupPC::Attrib qw(:all); use BackupPC::FileZIO; use BackupPC::View; die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my %opts; if ( !getopts("Llte:h:n:p:r:s:b:w:", \%opts) || @ARGV < 1 ) { print STDERR <BackupInfoRead($Host); my $FileCnt = 0; my $ByteCnt = 0; my $DirCnt = 0; my $SpecialCnt = 0; my $ErrorCnt = 0; my $i; $Num = $Backups[@Backups + $Num]{num} if ( -@Backups <= $Num && $Num < 0 ); for ( $i = 0 ; $i < @Backups ; $i++ ) { last if ( $Backups[$i]{num} == $Num ); } if ( $i >= @Backups ) { print(STDERR "$0: bad backup number $Num for host $Host\n"); exit(1); } my $Charset = $Backups[$i]{charset}; $Charset = $opts{e} if ( $opts{e} ne "" ); my $PathRemove = $1 if ( $opts{r} =~ /(.+)/ ); my $PathAdd = $1 if ( $opts{p} =~ /(.+)/ ); if ( $opts{s} =~ m{(^|/)\.\.(/|$)} ) { print(STDERR "$0: bad share name '$opts{s}'\n"); exit(1); } our $ShareName = $opts{s}; our $view = BackupPC::View->new($bpc, $Host, \@Backups); # # This constant and the line of code below that uses it are borrowed # from Archive::Tar. Thanks to Calle Dybedahl and Stephen Zander. # See www.cpan.org. # # Archive::Tar is Copyright 1997 Calle Dybedahl. All rights reserved. # Copyright 1998 Stephen Zander. All rights reserved. # my $tar_pack_header = 'a100 a8 a8 a8 a12 a12 A8 a1 a100 a6 a2 a32 a32 a8 a8 a155 x12'; my $tar_header_length = 512; my $BufSize = $opts{w} || 1048576; # 1MB or 2^20 my $WriteBuf = ""; my $WriteBufSz = ($opts{b} || 20) * $tar_header_length; my(%UidCache, %GidCache); my(%HardLinkExtraFiles, @HardLinks); # # Write out all the requested files/directories # binmode(STDOUT); my $fh = *STDOUT; if ( $ShareName eq "*" ) { my $PathRemoveOrig = $PathRemove; my $PathAddOrig = $PathAdd; foreach $ShareName ( $view->shareList($Num) ) { #print(STDERR "Doing share ($ShareName)\n"); $PathRemove = "/" if ( !defined($PathRemoveOrig) ); ($PathAdd = "/$ShareName/$PathAddOrig") =~ s{//+}{/}g; foreach my $dir ( @ARGV ) { archiveWrite($fh, $dir); } archiveWriteHardLinks($fh); } } else { foreach my $dir ( @ARGV ) { archiveWrite($fh, $dir); } archiveWriteHardLinks($fh); } if ( !$opts{l} && !$opts{L} ) { # # Finish with two null 512 byte headers, and then round out a full # block. # my $data = "\0" x ($tar_header_length * 2); TarWrite($fh, \$data); TarWrite($fh, undef); } # # print out totals if requested # if ( $opts{t} ) { print STDERR "Done: $FileCnt files, $ByteCnt bytes, $DirCnt dirs,", " $SpecialCnt specials, $ErrorCnt errors\n"; } if ( $ErrorCnt && !$FileCnt && !$DirCnt ) { # # Got errors, with no files or directories; exit with non-zero # status # exit(1); } exit(0); ########################################################################### # Subroutines ########################################################################### sub archiveWrite { my($fh, $dir, $tarPathOverride) = @_; if ( $dir =~ m{(^|/)\.\.(/|$)} ) { print(STDERR "$0: bad directory '$dir'\n"); $ErrorCnt++; return; } $dir = "/" if ( $dir eq "." ); #print(STDERR "calling find with $Num, $ShareName, $dir\n"); if ( $view->find($Num, $ShareName, $dir, 0, \&TarWriteFile, $fh, $tarPathOverride) < 0 ) { print(STDERR "$0: bad share or directory '$ShareName/$dir'\n"); $ErrorCnt++; return; } } # # Write out any hardlinks (if any) # sub archiveWriteHardLinks { my($fh) = @_; foreach my $hdr ( @HardLinks ) { $hdr->{size} = 0; my $name = $hdr->{linkname}; $name =~ s{^\./}{/}; if ( defined($HardLinkExtraFiles{$name}) ) { $hdr->{linkname} = $HardLinkExtraFiles{$name}; } if ( defined($PathRemove) && substr($hdr->{linkname}, 0, length($PathRemove)+1) eq ".$PathRemove" ) { substr($hdr->{linkname}, 0, length($PathRemove)+1) = ".$PathAdd"; } TarWriteFileInfo($fh, $hdr); } @HardLinks = (); %HardLinkExtraFiles = (); } sub UidLookup { my($uid) = @_; $UidCache{$uid} = (getpwuid($uid))[0] if ( !exists($UidCache{$uid}) ); return $UidCache{$uid}; } sub GidLookup { my($gid) = @_; $GidCache{$gid} = (getgrgid($gid))[0] if ( !exists($GidCache{$gid}) ); return $GidCache{$gid}; } sub TarWrite { my($fh, $dataRef) = @_; if ( !defined($dataRef) ) { # # do flush by padding to a full $WriteBufSz # my $data = "\0" x ($WriteBufSz - length($WriteBuf)); $dataRef = \$data; } if ( length($WriteBuf) + length($$dataRef) < $WriteBufSz ) { # # just buffer and return # $WriteBuf .= $$dataRef; return; } my $done = $WriteBufSz - length($WriteBuf); if ( syswrite($fh, $WriteBuf . substr($$dataRef, 0, $done)) != $WriteBufSz ) { print(STDERR "Unable to write to output file ($!)\n"); exit(1); } while ( $done + $WriteBufSz <= length($$dataRef) ) { if ( syswrite($fh, substr($$dataRef, $done, $WriteBufSz)) != $WriteBufSz ) { print(STDERR "Unable to write to output file ($!)\n"); exit(1); } $done += $WriteBufSz; } $WriteBuf = substr($$dataRef, $done); } sub TarWritePad { my($fh, $size) = @_; if ( $size % $tar_header_length ) { my $data = "\0" x ($tar_header_length - ($size % $tar_header_length)); TarWrite($fh, \$data); } } sub TarWriteHeader { my($fh, $hdr) = @_; $hdr->{uname} = UidLookup($hdr->{uid}) if ( !defined($hdr->{uname}) ); $hdr->{gname} = GidLookup($hdr->{gid}) if ( !defined($hdr->{gname}) ); my $devmajor = defined($hdr->{devmajor}) ? sprintf("%07o", $hdr->{devmajor}) : ""; my $devminor = defined($hdr->{devminor}) ? sprintf("%07o", $hdr->{devminor}) : ""; my $sizeStr; if ( $hdr->{size} >= 2 * 65536 * 65536 ) { # # GNU extension for files >= 8GB: send size in big-endian binary # $sizeStr = pack("c4 N N", 0x80, 0, 0, 0, $hdr->{size} / (65536 * 65536), $hdr->{size} % (65536 * 65536)); } elsif ( $hdr->{size} >= 1 * 65536 * 65536 ) { # # sprintf octal only handles up to 2^32 - 1 # $sizeStr = sprintf("%03o", $hdr->{size} / (1 << 24)) . sprintf("%08o", $hdr->{size} % (1 << 24)); } else { $sizeStr = sprintf("%011o", $hdr->{size}); } my $data = pack($tar_pack_header, substr($hdr->{name}, 0, 99), sprintf("%07o", $hdr->{mode}), sprintf("%07o", $hdr->{uid}), sprintf("%07o", $hdr->{gid}), $sizeStr, sprintf("%011o", $hdr->{mtime}), "", #checksum field - space padded by pack("A8") $hdr->{type}, substr($hdr->{linkname}, 0, 99), $hdr->{magic} || 'ustar ', $hdr->{version} || ' ', $hdr->{uname}, $hdr->{gname}, $devmajor, $devminor, "" # prefix is empty ); substr($data, 148, 7) = sprintf("%06o\0", unpack("%16C*",$data)); TarWrite($fh, \$data); } sub TarWriteFileInfo { my($fh, $hdr) = @_; # # Convert path names to requested (eg: client) charset # if ( $Charset ne "" ) { from_to($hdr->{name}, "utf8", $Charset); from_to($hdr->{linkname}, "utf8", $Charset); } if ( $opts{l} ) { print($hdr->{name} . "\n"); return; } elsif ( $opts{L} ) { my $owner = "$hdr->{uid}/$hdr->{gid}"; my $name = $hdr->{name}; if ( $hdr->{type} == BPC_FTYPE_SYMLINK || $hdr->{type} == BPC_FTYPE_HARDLINK ) { $name .= " -> $hdr->{linkname}"; } $name =~ s/\n/\\n/g; printf("%6o %9s %11.0f %s\n", $hdr->{mode}, $owner, $hdr->{size}, $name); return; } # # Handle long link names (symbolic links) # if ( length($hdr->{linkname}) > 99 ) { my %h; my $data = $hdr->{linkname} . "\0"; $h{name} = "././\@LongLink"; $h{type} = "K"; $h{size} = length($data); TarWriteHeader($fh, \%h); TarWrite($fh, \$data); TarWritePad($fh, length($data)); } # # Handle long file names # if ( length($hdr->{name}) > 99 ) { my %h; my $data = $hdr->{name} . "\0"; $h{name} = "././\@LongLink"; $h{type} = "L"; $h{size} = length($data); TarWriteHeader($fh, \%h); TarWrite($fh, \$data); TarWritePad($fh, length($data)); } TarWriteHeader($fh, $hdr); } my $Attr; my $AttrDir; sub TarWriteFile { my($hdr, $fh, $tarPathOverride) = @_; my $tarPath = $hdr->{relPath}; $tarPath = $tarPathOverride if ( defined($tarPathOverride) ); $tarPath =~ s{//+}{/}g; if ( defined($PathRemove) && substr($tarPath, 0, length($PathRemove)) eq $PathRemove ) { substr($tarPath, 0, length($PathRemove)) = $PathAdd; } $tarPath = "./" . $tarPath if ( $tarPath !~ /^\.\// ); $tarPath =~ s{//+}{/}g; $hdr->{name} = $tarPath; if ( $hdr->{type} == BPC_FTYPE_DIR ) { # # Directory: just write the header # $hdr->{name} .= "/" if ( $hdr->{name} !~ m{/$} ); TarWriteFileInfo($fh, $hdr); $DirCnt++; } elsif ( $hdr->{type} == BPC_FTYPE_FILE ) { my($data, $size); # # Regular file: write the header and file # my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, $hdr->{compress}); if ( !defined($f) ) { print(STDERR "Unable to open file $hdr->{fullPath}\n"); $ErrorCnt++; return; } TarWriteFileInfo($fh, $hdr); if ( $opts{l} || $opts{L} ) { $size = $hdr->{size}; } else { while ( $f->read(\$data, $BufSize) > 0 ) { if ( $size + length($data) > $hdr->{size} ) { print(STDERR "Error: truncating $hdr->{fullPath} to" . " $hdr->{size} bytes\n"); $data = substr($data, 0, $hdr->{size} - $size); $ErrorCnt++; } TarWrite($fh, \$data); $size += length($data); } $f->close; if ( $size != $hdr->{size} ) { print(STDERR "Error: padding $hdr->{fullPath} to $hdr->{size}" . " bytes from $size bytes\n"); $ErrorCnt++; while ( $size < $hdr->{size} ) { my $len = $hdr->{size} - $size; $len = $BufSize if ( $len > $BufSize ); $data = "\0" x $len; TarWrite($fh, \$data); $size += $len; } } TarWritePad($fh, $size); } $FileCnt++; $ByteCnt += $size; } elsif ( $hdr->{type} == BPC_FTYPE_HARDLINK ) { # # Hardlink file: either write a hardlink or the complete file # depending upon whether the linked-to file will be written # to the archive. # # Start by reading the contents of the link. # my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, $hdr->{compress}); if ( !defined($f) ) { print(STDERR "Unable to open file $hdr->{fullPath}\n"); $ErrorCnt++; return; } my $data; while ( $f->read(\$data, $BufSize) > 0 ) { $hdr->{linkname} .= $data; } $f->close; # # Check @ARGV and the list of hardlinked files we have explicity # dumped to see if we have dumped this file or not # my $done = 0; my $name = $hdr->{linkname}; $name =~ s{^\./}{/}; if ( defined($HardLinkExtraFiles{$name}) ) { $done = 1; } else { foreach my $arg ( @ARGV ) { $arg = "/" if ( $arg eq "." ); $arg =~ s{^\./+}{/}; $arg =~ s{/+$}{}; $done = 1 if ( $name eq $arg || $name =~ /^\Q$arg\// || $arg eq "" ); } } if ( $done ) { # # Target file will be or was written, so just remember # the hardlink so we can dump it later. # push(@HardLinks, $hdr); $SpecialCnt++; } else { # # Have to dump the original file. Just call the top-level # routine, so that we save the hassle of dealing with # mangling, merging and attributes. # my $name = $hdr->{linkname}; $name =~ s{^\./}{/}; $HardLinkExtraFiles{$name} = $hdr->{name}; archiveWrite($fh, $name, $hdr->{name}); } } elsif ( $hdr->{type} == BPC_FTYPE_SYMLINK ) { # # Symbolic link: read the symbolic link contents into the header # and write the header. # my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, $hdr->{compress}); if ( !defined($f) ) { print(STDERR "Unable to open symlink file $hdr->{fullPath}\n"); $ErrorCnt++; return; } my $data; while ( $f->read(\$data, $BufSize) > 0 ) { $hdr->{linkname} .= $data; } $f->close; $hdr->{size} = 0; TarWriteFileInfo($fh, $hdr); $SpecialCnt++; } elsif ( $hdr->{type} == BPC_FTYPE_CHARDEV || $hdr->{type} == BPC_FTYPE_BLOCKDEV || $hdr->{type} == BPC_FTYPE_FIFO ) { # # Special files: for char and block special we read the # major and minor numbers from a plain file. # if ( $hdr->{type} != BPC_FTYPE_FIFO ) { my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, $hdr->{compress}); my $data; if ( !defined($f) || $f->read(\$data, $BufSize) < 0 ) { print(STDERR "Unable to open/read char/block special file" . " $hdr->{fullPath}\n"); $f->close if ( defined($f) ); $ErrorCnt++; return; } $f->close; if ( $data =~ /(\d+),(\d+)/ ) { $hdr->{devmajor} = $1; $hdr->{devminor} = $2; } } $hdr->{size} = 0; TarWriteFileInfo($fh, $hdr); $SpecialCnt++; } elsif ( $hdr->{type} == BPC_FTYPE_SOCKET || $hdr->{type} == BPC_FTYPE_UNKNOWN ) { # # ignore these two file types - these are dynamic file types created # by applications as needed # } else { print(STDERR "Got unknown type $hdr->{type} for $hdr->{name}\n"); $ErrorCnt++; } } BackupPC-3.3.2/bin/BackupPC_tarExtract0000555000076500000240000004427613042250554016461 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_tarExtract: extract data from a dump # # DESCRIPTION # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use Encode qw/from_to/; use BackupPC::Lib; use BackupPC::Attrib qw(:all); use BackupPC::FileZIO; use BackupPC::PoolWrite; use File::Path; use constant S_IFMT => 0170000; # type of file die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); if ( @ARGV != 3 ) { print("usage: $0 \n"); exit(1); } if ( $ARGV[0] !~ /^([\w\.\s-]+)$/ ) { print("$0: bad client name '$ARGV[0]'\n"); exit(1); } my $client = $1; if ( $ARGV[1] =~ m{(^|/)\.\.(/|$)} ) { print("$0: bad share name '$ARGV[1]'\n"); exit(1); } my $ShareNameUM = $1 if ( $ARGV[1] =~ /(.*)/ ); my $ShareName = $bpc->fileNameEltMangle($ShareNameUM); if ( $ARGV[2] !~ /^(\d+)$/ ) { print("$0: bad compress level '$ARGV[2]'\n"); exit(1); } my $Compress = $1; my $Abort = 0; my $AbortReason; # # Re-read config file, so we can include the PC-specific config # if ( defined(my $error = $bpc->ConfigRead($client)) ) { print("BackupPC_tarExtract: Can't read PC's config file: $error\n"); exit(1); } %Conf = $bpc->Conf(); # # Catch various signals # $SIG{INT} = \&catch_signal; $SIG{ALRM} = \&catch_signal; $SIG{TERM} = \&catch_signal; $SIG{PIPE} = \&catch_signal; $SIG{STOP} = \&catch_signal; $SIG{TSTP} = \&catch_signal; $SIG{TTIN} = \&catch_signal; # # This constant and the line of code below that uses it is borrowed # from Archive::Tar. Thanks to Calle Dybedahl and Stephen Zander. # See www.cpan.org. # # Archive::Tar is Copyright 1997 Calle Dybedahl. All rights reserved. # Copyright 1998 Stephen Zander. All rights reserved. # my $tar_unpack_header = 'Z100 A8 A8 A8 a12 A12 A8 A1 Z100 A6 A2 Z32 Z32 A8 A8 A155 x12'; my $tar_header_length = 512; my $BufSize = 1048576; # 1MB or 2^20 my $MaxFiles = 20; my $Errors = 0; my $OutDir = "$TopDir/pc/$client/new"; my %Attrib = (); my $ExistFileCnt = 0; my $ExistFileSize = 0; my $ExistFileCompSize = 0; my $TotalFileCnt = 0; my $TotalFileSize = 0; my $TarReadHdrCnt = 0; sub TarRead { my($fh, $totBytes) = @_; my($numBytes, $newBytes, $data); $data = "\0" x $totBytes; while ( $numBytes < $totBytes ) { return if ( $Abort ); $newBytes = sysread($fh, substr($data, $numBytes, $totBytes - $numBytes), $totBytes - $numBytes); if ( $newBytes <= 0 ) { return if ( $TarReadHdrCnt == 1 ); # empty tar file ok print("Unexpected end of tar archive (tot = $totBytes," . " num = $numBytes, posn = " . sysseek($fh, 0, 1) . ")\n"); $Abort = 1; $AbortReason = "Unexpected end of tar archive"; $Errors++; return; } $numBytes += $newBytes; } return $data; } sub TarReadHeader { my($fh) = @_; $TarReadHdrCnt++; return $1 if ( TarRead($fh, $tar_header_length) =~ /(.*)/s ); return; } sub TarFlush { my($fh, $size) = @_; if ( $size % $tar_header_length ) { TarRead($fh, $tar_header_length - ($size % $tar_header_length)); } } sub TarReadFileInfo { my($fh) = @_; my($head, $longName, $longLink); my($name, $mode, $uid, $gid, $size, $mtime, $chksum, $type, $linkname, $magic, $version, $uname, $gname, $devmajor, $devminor, $prefix); while ( 1 ) { $head = TarReadHeader($fh); return if ( $Abort || $head eq "" || $head eq "\0" x $tar_header_length ); ($name, # string $mode, # octal number $uid, # octal number $gid, # octal number $size, # octal number $mtime, # octal number $chksum, # octal number $type, # character $linkname, # string $magic, # string $version, # two bytes $uname, # string $gname, # string $devmajor, # octal number $devminor, # octal number $prefix) = unpack($tar_unpack_header, $head); $mode = oct $mode; $uid = oct $uid; $gid = oct $gid; if ( ord($size) == 128 ) { # # GNU tar extension: for >=8GB files the size is stored # in big endian binary. # $size = 65536 * 65536 * unpack("N", substr($size, 4, 4)) + unpack("N", substr($size, 8, 4)); } else { # # We used to have a patch here for smbclient 2.2.x. For file # sizes between 2 and 4GB it sent the wrong size. But since # samba 3.0.0 has been released we no longer support this # patch since valid files could have sizes that start with # 6 or 7 in octal (eg: 6-8GB files). # # $size =~ s/^6/2/; # fix bug in smbclient for >=2GB files # $size =~ s/^7/3/; # fix bug in smbclient for >=2GB files # # To avoid integer overflow in case we are in the 4GB - 8GB # range, we do the conversion in two parts. # if ( $size =~ /([0-9]{9,})/ ) { my $len = length($1); $size = oct(substr($1, 0, $len - 8)) * (1 << 24) + oct(substr($1, $len - 8)); } else { $size = oct($size); } } $mtime = oct $mtime; $chksum = oct $chksum; $devmajor = oct $devmajor; $devminor = oct $devminor; $name = "$prefix/$name" if $prefix; $prefix = ""; substr ($head, 148, 8) = " "; if (unpack ("%16C*", $head) != $chksum) { print("$name: checksum error at " . sysseek($fh, 0, 1) , "\n"); $Errors++; } if ( $type eq "L" ) { $longName = TarRead($fh, $size) || return; # remove trailing NULL $longName = substr($longName, 0, $size - 1); TarFlush($fh, $size); next; } elsif ( $type eq "K" ) { $longLink = TarRead($fh, $size) || return; # remove trailing NULL $longLink = substr($longLink, 0, $size - 1); TarFlush($fh, $size); next; } printf("Got file '%s', mode 0%o, size %g, type %d\n", $name, $mode, $size, $type) if ( $Conf{XferLogLevel} >= 3 ); $name = $longName if ( defined($longName) ); $linkname = $longLink if ( defined($longLink) ); # # Map client charset encodings to utf8 # # printf("File $name (hex: %s)\n", unpack("H*", $name)); if ( $Conf{ClientCharset} ne "" ) { from_to($name, $Conf{ClientCharset}, "utf8"); from_to($linkname, $Conf{ClientCharset}, "utf8"); } # printf("File now $name (hex: %s)\n", unpack("H*", $name)); $name =~ s{^\./+}{}; $name =~ s{/+\.?$}{}; $name =~ s{//+}{/}g; return { name => $name, mangleName => $bpc->fileNameMangle($name), mode => $mode, uid => $uid, gid => $gid, size => $size, mtime => $mtime, type => $type, linkname => $linkname, devmajor => $devmajor, devminor => $devminor, }; } } sub TarReadFile { my($fh) = @_; my $f = TarReadFileInfo($fh) || return; my($dir, $file); if ( $f->{name} eq "" ) { # top-level dir $dir = ""; $file = $ShareNameUM; } else { ($file = $f->{name}) =~ s{.*?([^/]*)$}{$1}; # unmangled file if ( ($dir = $f->{mangleName}) =~ m{(.*)/.*} ) { $dir = "$ShareName/$1"; } else { $dir = $ShareName; } } if ( !defined($Attrib{$dir}) ) { foreach my $d ( keys(%Attrib) ) { next if ( $dir =~ m{^\Q$d/} ); attributeWrite($d); } $Attrib{$dir} = BackupPC::Attrib->new({ compress => $Compress }); if ( -f $Attrib{$dir}->fileName("$OutDir/$dir") && !$Attrib{$dir}->read("$OutDir/$dir") ) { printf("Unable to read attribute file %s\n", $Attrib{$dir}->fileName("$OutDir/$dir")); $Errors++; } } if ( $f->{type} == BPC_FTYPE_DIR ) { # # Directory # logFileAction("create", $f) if ( $Conf{XferLogLevel} >= 1 ); mkpath("$OutDir/$ShareName/$f->{mangleName}", 0, 0777) if ( !-d "$OutDir/$ShareName/$f->{mangleName}" ); } elsif ( $f->{type} == BPC_FTYPE_FILE ) { # # Regular file # my($nRead); #print("Reading $f->{name}, $f->{size} bytes, type $f->{type}\n"); pathCreate($dir, "$OutDir/$ShareName/$f->{mangleName}", $f); my $poolWrite = BackupPC::PoolWrite->new($bpc, "$OutDir/$ShareName/$f->{mangleName}", $f->{size}, $Compress); while ( $nRead < $f->{size} ) { my $thisRead = $f->{size} - $nRead < $BufSize ? $f->{size} - $nRead : $BufSize; my $data = TarRead($fh, $thisRead); if ( $data eq "" ) { if ( !$Abort ) { print("Unexpected end of tar archive during read\n"); $AbortReason = "Unexpected end of tar archive"; $Errors++; } $poolWrite->abort; $Abort = 1; unlink("$OutDir/$ShareName/$f->{mangleName}"); print("Removing partial file $f->{name}\n"); return; } $poolWrite->write(\$data); $nRead += $thisRead; } my $exist = processClose($poolWrite, "$ShareName/$f->{mangleName}", $f->{size}); logFileAction($exist ? "pool" : "create", $f) if ( $Conf{XferLogLevel} >= 1 ); TarFlush($fh, $f->{size}); } elsif ( $f->{type} == BPC_FTYPE_HARDLINK ) { # # Hardlink to another file. GNU tar is clever about files # that are hardlinks to each other. The first link will be # sent as a regular file. The additional links will be sent # as this type. We store the hardlink just like a symlink: # the link name (path of the linked-to file) is stored in # a plain file. # $f->{size} = length($f->{linkname}); pathCreate($dir, "$OutDir/$ShareName/$f->{mangleName}", $f); my $poolWrite = BackupPC::PoolWrite->new($bpc, "$OutDir/$ShareName/$f->{mangleName}", $f->{size}, $Compress); $poolWrite->write(\$f->{linkname}); my $exist = processClose($poolWrite, "$ShareName/$f->{mangleName}", $f->{size}); logFileAction($exist ? "pool" : "create", $f) if ( $Conf{XferLogLevel} >= 1 ); } elsif ( $f->{type} == BPC_FTYPE_SYMLINK ) { # # Symbolic link: write the value of the link to a plain file, # that we pool as usual (ie: we don't create a symlink). # The attributes remember the original file type. # We also change the size to reflect the size of the link # contents. # $f->{size} = length($f->{linkname}); pathCreate($dir, "$OutDir/$ShareName/$f->{mangleName}", $f); my $poolWrite = BackupPC::PoolWrite->new($bpc, "$OutDir/$ShareName/$f->{mangleName}", $f->{size}, $Compress); $poolWrite->write(\$f->{linkname}); my $exist = processClose($poolWrite, "$ShareName/$f->{mangleName}", $f->{size}); logFileAction($exist ? "pool" : "create", $f) if ( $Conf{XferLogLevel} >= 1 ); } elsif ( $f->{type} == BPC_FTYPE_CHARDEV || $f->{type} == BPC_FTYPE_BLOCKDEV || $f->{type} == BPC_FTYPE_FIFO ) { # # Special files: for char and block special we write the # major and minor numbers to a plain file, that we pool # as usual. For a pipe file we create an empty file. # The attributes remember the original file type. # my $data; if ( $f->{type} == BPC_FTYPE_FIFO ) { $data = ""; } else { $data = "$f->{devmajor},$f->{devminor}"; } pathCreate($dir, "$OutDir/$ShareName/$f->{mangleName}", $f); my $poolWrite = BackupPC::PoolWrite->new($bpc, "$OutDir/$ShareName/$f->{mangleName}", length($data), $Compress); $poolWrite->write(\$data); $f->{size} = length($data); my $exist = processClose($poolWrite, "$ShareName/$f->{mangleName}", length($data)); logFileAction($exist ? "pool" : "create", $f) if ( $Conf{XferLogLevel} >= 1 ); } else { print("Got unknown type $f->{type} for $f->{name}\n"); $Errors++; } $Attrib{$dir}->set($file, { type => $f->{type}, mode => $f->{mode}, uid => $f->{uid}, gid => $f->{gid}, size => $f->{size}, mtime => $f->{mtime}, }); return 1; } sub attributeWrite { my($d) = @_; my($poolWrite); return if ( !defined($Attrib{$d}) ); if ( $Attrib{$d}->fileCount ) { my $data = $Attrib{$d}->writeData; my $fileName = $Attrib{$d}->fileName("$OutDir/$d"); my $poolWrite = BackupPC::PoolWrite->new($bpc, $fileName, length($data), $Compress); $poolWrite->write(\$data); processClose($poolWrite, $Attrib{$d}->fileName($d), length($data), 1); } delete($Attrib{$d}); } sub processClose { my($poolWrite, $fileName, $origSize, $noStats) = @_; my($exists, $digest, $outSize, $errs) = $poolWrite->close; if ( @$errs ) { print(join("", @$errs)); $Errors += @$errs; } if ( !$noStats ) { $TotalFileCnt++; $TotalFileSize += $origSize; } if ( $exists ) { if ( !$noStats ) { $ExistFileCnt++; $ExistFileSize += $origSize; $ExistFileCompSize += $outSize; } } elsif ( $outSize > 0 ) { print(NEW_FILES "$digest $origSize $fileName\n"); } return $exists && $origSize > 0; } # # Generate a log file message for a completed file # sub logFileAction { my($action, $f) = @_; my $owner = "$f->{uid}/$f->{gid}"; my $name = $f->{name}; $name = "." if ( $name eq "" ); my $type = (("", "p", "c", "", "d", "", "b", "", "", "", "l", "", "s")) [($f->{mode} & S_IFMT) >> 12]; $type = "h" if ( $f->{type} == BPC_FTYPE_HARDLINK ); printf(" %-6s %1s%4o %9s %11.0f %s\n", $action, $type, $f->{mode} & 07777, $owner, $f->{size}, $name); } # # Create the parent directory of $file if necessary # sub pathCreate { my($dir, $fullPath, $f) = @_; # # Get parent directory of each of $dir and $fullPath # # print("pathCreate: dir = $dir, fullPath = $fullPath\n"); $dir =~ s{/([^/]*)$}{}; my $file = $bpc->fileNameUnmangle($1); $fullPath =~ s{/[^/]*$}{}; return if ( -d $fullPath || $file eq "" ); unlink($fullPath) if ( -e $fullPath ); mkpath($fullPath, 0, 0777); $Attrib{$dir} = BackupPC::Attrib->new({ compress => $Compress }) if ( !defined($Attrib{$dir}) ); # print("pathCreate: adding file = $file to dir = $dir\n"); $Attrib{$dir}->set($file, { type => BPC_FTYPE_DIR, mode => 0755, uid => $f->{uid}, gid => $f->{gid}, size => 0, mtime => 0, }); } sub catch_signal { my $sigName = shift; # # The first time we receive a signal we try to gracefully # abort the backup. This allows us to keep a partial dump # with the in-progress file deleted and attribute caches # flushed to disk etc. # print("BackupPC_tarExtract: got signal $sigName\n"); if ( !$Abort ) { $Abort++; $AbortReason = "received signal $sigName"; return; } # # This is a second signal: time to clean up. # print("BackupPC_tarExtract: quitting on second signal $sigName\n"); close(NEW_FILES); exit(1) } mkpath("$OutDir/$ShareName", 0, 0777); open(NEW_FILES, ">>", "$TopDir/pc/$client/NewFileList") || die("can't open $TopDir/pc/$client/NewFileList"); binmode(NEW_FILES); binmode(STDIN); 1 while ( !$Abort && TarReadFile(*STDIN) ); 1 while ( !$Abort && sysread(STDIN, my $discard, 1024) ); # # Flush out remaining attributes. # foreach my $d ( keys(%Attrib) ) { attributeWrite($d); } close(NEW_FILES); if ( $Abort ) { print("BackupPC_tarExtact aborting ($AbortReason)\n"); } # # Report results to BackupPC_dump # print("Done: $Errors errors, $ExistFileCnt filesExist," . " $ExistFileSize sizeExist, $ExistFileCompSize sizeExistComp," . " $TotalFileCnt filesTotal, $TotalFileSize sizeTotal\n"); BackupPC-3.3.2/bin/BackupPC_tarPCCopy0000555000076500000240000004254413042250554016200 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_tarPCCopy: create a tar archive of the PC directory # for copying the entire PC data directory. The archive will # contain hardlinks to the pool directory, which should be copied # before BackupPC_tarPCCopy is run. # # See the documentation for use. # # DESCRIPTION # # Usage: BackupPC_tarPCCopy [options] files/directories... # # Flags: # -c don't cache inode data (reduces memory usage at the # expense of longer run time) # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2005-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use File::Find; use File::Path; use Getopt::Std; use BackupPC::Lib; use BackupPC::Attrib qw(:all); use BackupPC::FileZIO; use BackupPC::View; use constant S_IFMT => 0170000; # type of file die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my %opts; if ( !getopts("c", \%opts) || @ARGV < 1 ) { print STDERR < sub { archiveFile($fh) } }, $path); # # To avoid using too much memory for the inode cache, # remove it after each top-level directory is done. # %Inode2Path = (); # # Print some stats # print STDERR "Done $path ($argCnt of $argMax): $DirCnt dirs," . " $FileCnt files, $HLinkCnt hardlinks\n"; $FileCnt = 0; $HLinkCnt = 0; $ByteCnt = 0; $DirCnt = 0; $argCnt++; } # # Finish with two null 512 byte headers, and then round out a full # block. # my $data = "\0" x ($tar_header_length * 2); TarWrite($fh, \$data); TarWrite($fh, undef); if ( $ErrorCnt ) { # # Got errors so exit with a non-zero status # print STDERR "Got $ErrorCnt warnings/errors\n"; exit(1); } exit(0); ########################################################################### # Subroutines ########################################################################### sub archiveFile { my($fh) = @_; my($hdr); my @s = stat($_); # # Default type - we'll update later if it is a symlink, hardlink etc # $hdr->{type} = -d _ ? BPC_FTYPE_DIR : -f _ ? BPC_FTYPE_FILE : -1; $hdr->{fullPath} = $File::Find::name; $hdr->{inode} = $s[1]; $hdr->{nlink} = $s[3]; $hdr->{size} = $s[7]; $hdr->{devmajor} = $s[6] >> 8; $hdr->{devminor} = $s[6] & 0xff; $hdr->{uid} = $s[4]; $hdr->{gid} = $s[5]; $hdr->{mode} = $s[2]; $hdr->{mtime} = $s[9]; $hdr->{compress} = 1; if ( $hdr->{fullPath} !~ m{\Q$TopDir\E/pc/(.*)} ) { print STDERR "Can't extract TopDir ($TopDir) from" . " $hdr->{fullPath}\n"; $ErrorCnt++; return; } $hdr->{relPath} = $1; if ( $hdr->{relPath} =~ m{(.*)/(.*)} ) { $hdr->{name} = $2; } else { $hdr->{name} = $hdr->{relPath}; } if ( $hdr->{relPath} =~ m{(.*?)/} ) { my $clientName = $1; if ( $ClientName ne $clientName ) { $ClientName = $clientName; $ClientBackups = [ $bpc->BackupInfoRead($ClientName) ]; #print STDERR "Setting Client to $ClientName\n"; } if ( $hdr->{relPath} =~ m{(.*?)/(\d+)/} || $hdr->{relPath} =~ m{(.*?)/(\d+)$} ) { my $backupNum = $2; if ( $ClientBkupNum != $backupNum ) { my $i; $ClientBkupNum = $backupNum; # print STDERR "Setting ClientBkupNum to $ClientBkupNum\n"; for ( $i = 0 ; $i < @$ClientBackups ; $i++ ) { if ( $ClientBackups->[$i]{num} == $ClientBkupNum ) { $ClientBkupCompress = $ClientBackups->[$i]{compress}; $ClientBkupMangle = $ClientBackups->[$i]{mangle}; # print STDERR "Setting $ClientBkupNum compress to $ClientBkupCompress, mangle to $ClientBkupMangle\n"; last; } } } $hdr->{compress} = $ClientBkupCompress; if ( $hdr->{type} == BPC_FTYPE_FILE && $hdr->{name} =~ /^f/ ) { (my $dir = $hdr->{fullPath}) =~ s{(.*)/.*}{$1}; if ( $ClientDir ne $dir ) { $ClientDir = $dir; $ClientDirAttr = BackupPC::Attrib->new( { compress => $ClientBkupCompress } ); if ( -f $ClientDirAttr->fileName($dir) && !$ClientDirAttr->read($dir) ) { print STDERR "Can't read attrib file in $dir\n"; $ErrorCnt++; } } my $name = $hdr->{name}; $name = $bpc->fileNameUnmangle($name) if ( $ClientBkupMangle ); my $attr = $ClientDirAttr->get($name); if ( defined($attr) ) { $hdr->{type} = $attr->{type}; $hdr->{realSize} = $attr->{size} if ( $attr->{type} == BPC_FTYPE_FILE ); } #print STDERR "$hdr->{fullPath} has type $hdr->{type} and real size $hdr->{realSize}\n"; } } } else { $hdr->{compress} = 0; $hdr->{realSize} = $hdr->{size}; } #print STDERR "$File::Find::name\n"; TarWriteFile($hdr, $fh); } sub UidLookup { my($uid) = @_; $UidCache{$uid} = (getpwuid($uid))[0] if ( !exists($UidCache{$uid}) ); return $UidCache{$uid}; } sub GidLookup { my($gid) = @_; $GidCache{$gid} = (getgrgid($gid))[0] if ( !exists($GidCache{$gid}) ); return $GidCache{$gid}; } sub TarWrite { my($fh, $dataRef) = @_; if ( !defined($dataRef) ) { # # do flush by padding to a full $WriteBufSz # my $data = "\0" x ($WriteBufSz - length($WriteBuf)); $dataRef = \$data; } if ( length($WriteBuf) + length($$dataRef) < $WriteBufSz ) { # # just buffer and return # $WriteBuf .= $$dataRef; return; } my $done = $WriteBufSz - length($WriteBuf); if ( (my $n = syswrite($fh, $WriteBuf . substr($$dataRef, 0, $done))) != $WriteBufSz ) { print(STDERR "Unable to write to output file ($!) ($n vs $WriteBufSz)\n"); exit(1); } while ( $done + $WriteBufSz <= length($$dataRef) ) { if ( (my $n = syswrite($fh, substr($$dataRef, $done, $WriteBufSz))) != $WriteBufSz ) { print(STDERR "Unable to write to output file ($!) ($n v $WriteBufSz)\n"); exit(1); } $done += $WriteBufSz; } $WriteBuf = substr($$dataRef, $done); } sub TarWritePad { my($fh, $size) = @_; if ( $size % $tar_header_length ) { my $data = "\0" x ($tar_header_length - ($size % $tar_header_length)); TarWrite($fh, \$data); } } sub TarWriteHeader { my($fh, $hdr) = @_; $hdr->{uname} = UidLookup($hdr->{uid}) if ( !defined($hdr->{uname}) ); $hdr->{gname} = GidLookup($hdr->{gid}) if ( !defined($hdr->{gname}) ); my $devmajor = defined($hdr->{devmajor}) ? sprintf("%07o", $hdr->{devmajor}) : ""; my $devminor = defined($hdr->{devminor}) ? sprintf("%07o", $hdr->{devminor}) : ""; my $sizeStr; if ( $hdr->{size} >= 2 * 65536 * 65536 ) { # # GNU extension for files >= 8GB: send size in big-endian binary # $sizeStr = pack("c4 N N", 0x80, 0, 0, 0, $hdr->{size} / (65536 * 65536), $hdr->{size} % (65536 * 65536)); } elsif ( $hdr->{size} >= 1 * 65536 * 65536 ) { # # sprintf octal only handles up to 2^32 - 1 # $sizeStr = sprintf("%03o", $hdr->{size} / (1 << 24)) . sprintf("%08o", $hdr->{size} % (1 << 24)); } else { $sizeStr = sprintf("%011o", $hdr->{size}); } my $data = pack($tar_pack_header, substr($hdr->{name}, 0, 99), sprintf("%07o", $hdr->{mode}), sprintf("%07o", $hdr->{uid}), sprintf("%07o", $hdr->{gid}), $sizeStr, sprintf("%011o", $hdr->{mtime}), "", #checksum field - space padded by pack("A8") $hdr->{type}, substr($hdr->{linkname}, 0, 99), $hdr->{magic} || 'ustar ', $hdr->{version} || ' ', $hdr->{uname}, $hdr->{gname}, $devmajor, $devminor, "" # prefix is empty ); substr($data, 148, 7) = sprintf("%06o\0", unpack("%16C*",$data)); TarWrite($fh, \$data); } sub TarWriteFileInfo { my($fh, $hdr) = @_; # # Handle long link names (symbolic links) # if ( length($hdr->{linkname}) > 99 ) { my %h; my $data = $hdr->{linkname} . "\0"; $h{name} = "././\@LongLink"; $h{type} = "K"; $h{size} = length($data); TarWriteHeader($fh, \%h); TarWrite($fh, \$data); TarWritePad($fh, length($data)); } # # Handle long file names # if ( length($hdr->{name}) > 99 ) { my %h; my $data = $hdr->{name} . "\0"; $h{name} = "././\@LongLink"; $h{type} = "L"; $h{size} = length($data); TarWriteHeader($fh, \%h); TarWrite($fh, \$data); TarWritePad($fh, length($data)); } TarWriteHeader($fh, $hdr); } my $Attr; my $AttrDir; sub TarWriteFile { my($hdr, $fh) = @_; my $tarPath = $hdr->{relPath}; $tarPath =~ s{//+}{/}g; $tarPath = "./" . $tarPath if ( $tarPath !~ /^\.\// ); $tarPath =~ s{//+}{/}g; $hdr->{name} = $tarPath; if ( $hdr->{type} == BPC_FTYPE_DIR ) { # # Directory: just write the header # $hdr->{name} .= "/" if ( $hdr->{name} !~ m{/$} ); TarWriteFileInfo($fh, $hdr); $DirCnt++; } elsif ( $hdr->{type} == BPC_FTYPE_FILE || $hdr->{type} == BPC_FTYPE_HARDLINK || $hdr->{type} == BPC_FTYPE_SYMLINK || $hdr->{type} == BPC_FTYPE_CHARDEV || $hdr->{type} == BPC_FTYPE_BLOCKDEV || $hdr->{type} == BPC_FTYPE_FIFO || $hdr->{type} == BPC_FTYPE_SOCKET ) { # # Underlying file is a regular file: write the header and file # my($data, $dataMD5, $size, $linkName); if ( defined($Inode2Path{$hdr->{inode}}) ) { $linkName = $Inode2Path{$hdr->{inode}}; #print STDERR "Got cache hit for $linkName\n"; } else { my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, $hdr->{compress}); if ( !defined($f) ) { print(STDERR "Unable to open file $hdr->{fullPath}\n"); $ErrorCnt++; return; } # # Try to find the hardlink it points to by computing # the pool file digest. # $f->read(\$dataMD5, $BufSize); if ( !defined($hdr->{realSize}) ) { # # Need to get the real size # $size = length($dataMD5); while ( $f->read(\$data, $BufSize) > 0 ) { $size += length($data); } $hdr->{realSize} = $size; } $f->close(); my $md5 = Digest::MD5->new; my $len = length($dataMD5); if ( $hdr->{realSize} < 1048576 && length($dataMD5) != $hdr->{realSize} ) { print(STDERR "File $hdr->{fullPath} has bad size" . " (expect $hdr->{realSize}, got $len)\n"); } else { my $digest = $bpc->Buffer2MD5($md5, $hdr->{realSize}, \$dataMD5); my $path = $bpc->MD52Path($digest, $hdr->{compress}); my $i = -1; # print(STDERR "Looking up $hdr->{fullPath} at $path\n"); while ( 1 ) { my $testPath = $path; $testPath .= "_$i" if ( $i >= 0 ); last if ( !-f $testPath ); my $inode = (stat(_))[1]; if ( $inode == $hdr->{inode} ) { # # Found it! Just emit a tar hardlink # $testPath =~ s{\Q$TopDir\E}{..}; $linkName = $testPath; last; } $i++; } } } if ( defined($linkName) ) { $hdr->{type} = BPC_FTYPE_HARDLINK; $hdr->{linkname} = $linkName; TarWriteFileInfo($fh, $hdr); $HLinkCnt++; #print STDERR "$hdr->{relPath} matches $testPath\n"; if ( !$opts{c} && $hdr->{nlink} > 2 ) { # # add it to the cache if there are more # than 2 links (pool + current file), # since there are more to go # $Inode2Path{$hdr->{inode}} = $linkName; } return; } $size = 0; if ( $hdr->{nlink} > 1 ) { print STDERR "Can't find $hdr->{relPath} in pool, will copy file\n"; $ErrorCnt++; } $hdr->{type} = BPC_FTYPE_FILE; my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, 0); if ( !defined($f) ) { print(STDERR "Unable to open file $hdr->{fullPath}\n"); $ErrorCnt++; return; } TarWriteFileInfo($fh, $hdr); while ( $f->read(\$data, $BufSize) > 0 ) { if ( $size + length($data) > $hdr->{size} ) { print(STDERR "Error: truncating $hdr->{fullPath} to" . " $hdr->{size} bytes\n"); $data = substr($data, 0, $hdr->{size} - $size); $ErrorCnt++; } TarWrite($fh, \$data); $size += length($data); } $f->close; if ( $size != $hdr->{size} ) { print(STDERR "Error: padding $hdr->{fullPath} to $hdr->{size}" . " bytes from $size bytes\n"); $ErrorCnt++; while ( $size < $hdr->{size} ) { my $len = $hdr->{size} - $size; $len = $BufSize if ( $len > $BufSize ); $data = "\0" x $len; TarWrite($fh, \$data); $size += $len; } } TarWritePad($fh, $size); $FileCnt++; $ByteCnt += $size; } else { print(STDERR "Got unknown type $hdr->{type} for $hdr->{name}\n"); $ErrorCnt++; } } BackupPC-3.3.2/bin/BackupPC_trashClean0000555000076500000240000000410313042250554016405 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_trashClean: remove all the files in $TopDir/trash. # # DESCRIPTION # BackupPC_trashClean is started once by BackupPC. Every 5 minutes # it wakes up and removes all the files or directories in $TopDir/trash. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); $bpc->ChildInit(); ########################################################################### # Empty trash every so often (eg: every 5 minutes) ########################################################################### while ( 1 ) { print("processState running\n"); if ( $bpc->RmTreeTrashEmpty("$TopDir/trash") < 0 ) { print("log BackupPC_trashClean failed to empty $TopDir/trash\n"); } print("processState idle\n"); sleep($Conf{TrashCleanSleepSec} || 300); } BackupPC-3.3.2/bin/BackupPC_zcat0000555000076500000240000000415613042250554015272 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_zcat: uncompress files to stdout # # DESCRIPTION # # Usage: BackupPC_zcat [files...] # # BackupPC_zcat is a command-line utility for uncompressing BackupPC # compressed files. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use Compress::Zlib; use BackupPC::FileZIO; sub zcat { my($fh, $fileName) = @_; my($data, $r, $ret); while ( ($r = $fh->read(\$data, 65536)) > 0 ) { print($data); } if ( $r < 0 ) { print(STDERR "$0: can't uncompress $fileName\n"); $ret = 1; } $fh->close(); return $ret; } my $ret = 0; if ( @ARGV ) { while ( @ARGV ) { if ( defined(my $fh = BackupPC::FileZIO->open($ARGV[0], 0, 1)) ) { $ret ||= zcat($fh, $ARGV[0]); } else { print(STDERR "$0: can't open $ARGV[0]\n"); $ret = 1; last; } shift @ARGV; } } else { my $fh = BackupPC::FileZIO->open(*STDIN, 0, 1); $ret ||= zcat($fh, "stdin"); } exit($ret); BackupPC-3.3.2/bin/BackupPC_zipCreate0000555000076500000240000002414213042250554016254 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*- # # BackupPC_zipCreate: create a zip archive of an existing dump # for restore on a client. # # DESCRIPTION # # Usage: BackupPC_zipCreate [options] files/directories... # # Flags: # Required options: # -h host host from which the zip archive is created # -n dumpNum dump number from which the zip archive is created # A negative number means relative to the end (eg -1 # means the most recent dump, -2 2nd most recent etc). # -s shareName share name from which the zip archive is created # # Other options: # -t print summary totals # -r pathRemove path prefix that will be replaced with pathAdd # -p pathAdd new path prefix # -c level compression level (default is 0, no compression) # -e charset charset for encoding file names (default: utf8) # # The -h, -n and -s options specify which dump is used to generate # the zip archive. The -r and -p options can be used to relocate # the paths in the zip archive so extracted files can be placed # in a location different from their original location. # # AUTHOR # Guillaume Filion # Based on Backup_tarCreate by Craig Barratt # # COPYRIGHT # Copyright (C) 2002-2017 Craig Barratt and Guillaume Filion # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use lib "__INSTALLDIR__/lib"; use Archive::Zip qw(:ERROR_CODES); use File::Path; use Getopt::Std; use Encode qw/from_to/; use IO::Handle; use BackupPC::Lib; use BackupPC::Attrib qw(:all); use BackupPC::FileZIO; use BackupPC::Zip::FileMember; use BackupPC::View; die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my %opts; if ( !getopts("te:h:n:p:r:s:c:", \%opts) || @ARGV < 1 ) { print STDERR <BackupInfoRead($Host); my $FileCnt = 0; my $ByteCnt = 0; my $DirCnt = 0; my $SpecialCnt = 0; my $ErrorCnt = 0; my $i; $Num = $Backups[@Backups + $Num]{num} if ( -@Backups <= $Num && $Num < 0 ); for ( $i = 0 ; $i < @Backups ; $i++ ) { last if ( $Backups[$i]{num} == $Num ); } if ( $i >= @Backups ) { print(STDERR "$0: bad backup number $Num for host $Host\n"); exit(1); } my $Charset = ""; # default: utf8 $Charset = $opts{e} if ( $opts{e} ne "" ); my $PathRemove = $1 if ( $opts{r} =~ /(.+)/ ); my $PathAdd = $1 if ( $opts{p} =~ /(.+)/ ); if ( $opts{s} =~ m{(^|/)\.\.(/|$)} ) { print(STDERR "$0: bad share name '$opts{s}'\n"); exit(1); } my $ShareName = $opts{s}; my $BufSize = 1048576; # 1MB or 2^20 my(%UidCache, %GidCache); #my $fh = *STDOUT; my $fh = new IO::Handle; $fh->fdopen(fileno(STDOUT),"w"); my $zipfh = Archive::Zip->new(); binmode(STDOUT); foreach my $dir ( @ARGV ) { archiveWrite($zipfh, $dir); } sub archiveWrite { my($zipfh, $dir, $zipPathOverride) = @_; my $view = BackupPC::View->new($bpc, $Host, \@Backups); if ( $dir =~ m{(^|/)\.\.(/|$)} || $dir !~ /^(.*)$/ ) { print(STDERR "$0: bad directory '$dir'\n"); $ErrorCnt++; return; } $dir = "/" if ( $dir eq "." ); $view->find($Num, $ShareName, $dir, 0, \&ZipWriteFile, $zipfh, $zipPathOverride); } # Create Zip file print STDERR "Can't write Zip file\n" unless $zipfh->writeToFileHandle($fh, 0) == Archive::Zip::AZ_OK; # # print out totals if requested # if ( $opts{t} ) { print STDERR "Done: $FileCnt files, $ByteCnt bytes, $DirCnt dirs,", " $SpecialCnt specials ignored, $ErrorCnt errors\n"; } exit(0); ########################################################################### # Subroutines ########################################################################### sub UidLookup { my($uid) = @_; $UidCache{$uid} = (getpwuid($uid))[0] if ( !exists($UidCache{$uid}) ); return $UidCache{$uid}; } sub GidLookup { my($gid) = @_; $GidCache{$gid} = (getgrgid($gid))[0] if ( !exists($GidCache{$gid}) ); return $GidCache{$gid}; } my $Attr; my $AttrDir; sub ZipWriteFile { my($hdr, $zipfh, $zipPathOverride) = @_; my $tarPath = $hdr->{relPath}; $tarPath = $zipPathOverride if ( defined($zipPathOverride) ); if ( defined($PathRemove) && substr($tarPath, 0, length($PathRemove)) eq $PathRemove ) { substr($tarPath, 0, length($PathRemove)) = $PathAdd; } $tarPath = $1 if ( $tarPath =~ m{^\.?/+(.*)} ); $tarPath =~ s{//+}{/}g; $hdr->{name} = $tarPath; return if ( $tarPath eq "." || $tarPath eq "./" || $tarPath eq "" ); my $zipmember; # Container to hold the file/directory to zip. if ( $hdr->{type} == BPC_FTYPE_DIR ) { # # Directory: just write the header # $hdr->{name} .= "/" if ( $hdr->{name} !~ m{/$} ); from_to($hdr->{name}, "utf8", $Charset) if ( $Charset ne "" ); $zipmember = Archive::Zip::Member->newDirectoryNamed($hdr->{name}); $DirCnt++; } elsif ( $hdr->{type} == BPC_FTYPE_FILE ) { # # Regular file: write the header and file # from_to($hdr->{name}, "utf8", $Charset) if ( $Charset ne "" ); $zipmember = BackupPC::Zip::FileMember->newFromFileNamed( $hdr->{fullPath}, $hdr->{name}, $hdr->{size}, $hdr->{compress} ); $FileCnt++; $ByteCnt += $hdr->{size}; } elsif ( $hdr->{type} == BPC_FTYPE_HARDLINK ) { # # Hardlink file: not supported by Zip, so just make a copy # of the pointed-to file. # # Start by reading the contents of the link. # my $f = BackupPC::FileZIO->open($hdr->{fullPath}, 0, $hdr->{compress}); if ( !defined($f) ) { print(STDERR "Unable to open file $hdr->{fullPath}\n"); $ErrorCnt++; return; } my $data; while ( $f->read(\$data, $BufSize) > 0 ) { $hdr->{linkname} .= $data; } $f->close; # # Dump the original file. Just call the top-level # routine, so that we save the hassle of dealing with # mangling, merging and attributes. # archiveWrite($zipfh, $hdr->{linkname}, $hdr->{name}); } elsif ( $hdr->{type} == BPC_FTYPE_SYMLINK ) { # # Symlinks can't be Zipped. 8( # We could zip the pointed-to dir/file (just like hardlink), but we # have to avoid the infinite-loop case of a symlink pointed to a # directory above us. Ignore for now. Could be a comand-line # option later. # $SpecialCnt++; } elsif ( $hdr->{type} == BPC_FTYPE_CHARDEV || $hdr->{type} == BPC_FTYPE_BLOCKDEV || $hdr->{type} == BPC_FTYPE_FIFO ) { # # Special files can't be Zipped. 8( # $SpecialCnt++; } else { print(STDERR "Got unknown type $hdr->{type} for $hdr->{name}\n"); $ErrorCnt++; } return if ( !$zipmember ); # # Set the attributes and permissions. The standard zip file # header cannot handle dates prior to 1/1/1980, or 315561600 # unix seconds, so we round up the mtime. # my $mtime = $hdr->{mtime}; $mtime = 315561600 if ( $mtime < 315561600 ); $zipmember->setLastModFileDateTimeFromUnix($mtime); $zipmember->unixFileAttributes($hdr->{mode}); # Zip files don't accept uid and gid, so we put them in the comment field. $zipmember->fileComment("uid=".$hdr->{uid}." gid=".$hdr->{gid}) if ( $hdr->{uid} || $hdr->{gid} ); # Specify the compression level for this member $zipmember->desiredCompressionLevel($compLevel) if ($compLevel =~ /[0-9]/); if ( $Charset =~ /^(?:utf[-_]?8)?$/i ) { # Set general purpose bit 11 for UTF-8 code page $zipmember->{bitFlag} = $zipmember->{bitFlag} | 0x0800 ; } elsif ( $Charset =~ /^cp(?:437|720|737|775|85[02578]|86[069]|874|93[26]|949|950)$/i ) { # Set "version made by" field to 0 (MS-DOS) for OEM code pages $zipmember->fileAttributeFormat('FA_MSDOS'); } # Finally Zip the member $zipfh->addMember($zipmember); } BackupPC-3.3.2/cgi-bin/0000755000076500000240000000000013042250554013427 5ustar craigstaffBackupPC-3.3.2/cgi-bin/BackupPC_Admin0000555000076500000240000000761713042250554016126 0ustar craigstaff#!/usr/bin/perl #============================================================= -*-perl-*-w # # BackupPC_Admin: Apache/CGI interface for BackupPC. # # DESCRIPTION # BackupPC_Admin provides a flexible web interface for BackupPC. # It is a CGI script that runs under Apache. # # It requires that Apache pass in $ENV{SCRIPT_NAME} and # $ENV{REMOTE_USER}. The latter requires .ht_access style # authentication. Replace the code below if you are using some other # type of authentication, and have a different way of getting the # user name. # # Also, this script needs to run as the BackupPC user. To accomplish # this the script is typically installed as setuid to the BackupPC user, # or it can run under mod_perl with httpd running as the BackupPC user. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use CGI; use CGI::Carp qw(fatalsToBrowser); use lib "__INSTALLDIR__/lib"; use BackupPC::Lib; use BackupPC::CGI::Lib qw(:all); BackupPC::CGI::Lib::NewRequest; my %ActionDispatch = ( "summary" => "Summary", "Start_Incr_Backup" => "StartStopBackup", "Start_Full_Backup" => "StartStopBackup", "Stop_Dequeue_Backup" => "StartStopBackup", "Stop_Dequeue_Archive" => "StartStopBackup", "queue" => "Queue", "view" => "View", "LOGlist" => "LOGlist", "emailSummary" => "EmailSummary", "browse" => "Browse", "dirHistory" => "DirHistory", "Restore" => "Restore", "RestoreFile" => "RestoreFile", "hostInfo" => "HostInfo", "generalInfo" => "GeneralInfo", "restoreInfo" => "RestoreInfo", "archiveInfo" => "ArchiveInfo", "Start_Archive" => "Archive", "Archive" => "Archive", "Reload" => "ReloadServer", "startServer" => "StartServer", "Stop" => "StopServer", "adminOpts" => "AdminOptions", "editConfig" => "EditConfig", "rss" => "RSS", ); # # Set default actions, then call sub handler # if ( !defined($ActionDispatch{$In{action}}) ) { $In{action} = defined($In{host}) ? "hostInfo" : "generalInfo"; } my $action = $ActionDispatch{$In{action}}; # # For some reason under mod_perl, the use lib above is unreliable, # and sometimes the module below cannot be found. Explicitly push # the directory onto INC if it is missing. This is an ugly hack; # need to figure out what's really going on... # my $installDir = '__INSTALLDIR__/lib'; push(@INC, $installDir) if ( !grep($_ eq $installDir, @INC) ); # # Load the relevant action script and run it # require "BackupPC/CGI/$action.pm" if ( !defined($BackupPC::CGI::{"${action}::"}) ); $BackupPC::CGI::{"${action}::"}{action}(); BackupPC-3.3.2/ChangeLog0000444000076500000240000023362613042250554013703 0ustar craigstaff#======================================================================== # # ChangeLog - change log for BackupPC. # # DESCRIPTION # Revision history for BackupPC, detailing significant changes between # versions, most recent first. # # AUTHOR # Craig Barratt # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== #------------------------------------------------------------------------ # Version 3.3.2, 25 Jan 2017 #------------------------------------------------------------------------ * Updates to bin/BackupPC_dump and lib/BackupPC/Xfer/Smb.pm from maksyms to fix incompatabilities with Samba 4.3 (#22) * Sort hash of config editor tabs in lib/BackupPC/CGI/EditConfig.pm from polo (#23) * Fixes to bin/BackupPC_sendEmail to avoid per-host overwrite of EMailAdminUserName from derrickdominic (#30) * Fixed minor typos in several language files; from Alexander Moisseev * Updated deprecated syntax (defined(@array) and "{" in regexps) from Alexander Moisseev. * Documentation updates from Alexander Moisseev. #------------------------------------------------------------------------ # Version 3.3.1, 11 Jan 2015 #------------------------------------------------------------------------ * Added more helpful text (instead of "New Key") for BackupFilesOnly and BackupFilesExclude in the CGI editor. * Removed deprecated defined(@Backups) from lib/BackupPC/CGI/Browse.pm; patch from Alexander Moisseev. * Updated Spanish language file lib/BackupPC/Lang/es.pm from Luis Bustamante. * Updated init.d/debian-backuppc from Eduardo Díaz Rodríguez. #------------------------------------------------------------------------ # Version 3.3.0, 14 Apr 2013 #------------------------------------------------------------------------ * Changed restore file name from restore.{zip|tar} to restore_$host_YYYY-MM-DD.{zip|tar}, where the date is the start date of the backup. Originally suggested by Brad Alexander, with a healthy debate among Les, Holger, Jeffrey, Adam, Carl and others. * Changed the timeStamp2 function in lib/BackupPC/CGI/Lib.pm so that times more than 330 days ago also include the year. More recent times continue to use just the day of month and month. * Made the directory path display (when browsing backups or history) a sequence of links, allowing any parent directory to be quickly selected. * Added Japanese language file lib/BackupPC/Lang/ja.pm submitted by Rikiya Yamamoto. * Added Ukrainian language file lib/BackupPC/Lang/uk.pm submitted by Serhiy Yakimchuck. * Added Russian language file lib/BackupPC/Lang/ru.pm submitted by Sergei Butakov. * Patch from Alexander Moisseev that fixed file name encodings in zip files. The default charset is now utf8. Added a menu option to override the codepage. * Removed -N option from smbclient command in conf/config.pl to remain compatible with more recent versions (3.2.3 and later) of smbclient. Reported and discussed by various people on the mail list, most recently by Jeff Boyce, Les Mikesell and Holger Parplies. Alexander Moisseev also submitted a patch. Using smbclient >= 3.2.3 with the -N option will give a "tree connect failed: NT_STATUS_ACCESS_DENIED" error. * Reapplied a patch from Tyler Wagner for lib/BackupPC/CGI/HostInfo.pl so that empty email status info doesn't appear. Somehow this missed 3.2.1. * Fixed check on $parfile in bin/BackupPC_archiveHost since it is numeric. Fix submitted by Tim Massey. * Ensure $num is numeric in lib/BackupPC/CGI/View.pm error message to avoid XSS attack. Report and patch by Jamie Strandboge. * Ensure $num and $share in lib/BackupPC/CGI/RestoreFile.pm error messages are escaped, to avoid XSS vulnerability. Report and patch by Jamie Strandboge. Also added some additional error checking and tweaked the handling of the invalid number error message. * Fixed qw(...) deprecated syntax warnings in lib/BackupPC/Storage/Text.pm and lib/BackupPC/Lib.pm. Patch supplied by Juergen Harms. Also got a patch from Alexander Moisseev and report from Richard Shaw. * Fixed error in bin/BackupPC_sendEmail that caused accumulation of per-host errors in the admin email to be skipped if a host's user is not defined. Reported by Marco Dalla Via. * Fixed lib/BackupPC/CGI/RSS.pm so that the base_url is correct for https. Report and fix by Samuel Monsarrat. * Added more careful checking that IO::Dirent returns valid inodes and file types. Suggested by Daniel Harvey. * Removed redundant setting of $Lang{CfgEdit_Title_Other} from all the Lang files. * Applied couple of fixes to Lib.pm suggested by Jeffrey Kosowsky for special case of where configuration commands are fragments of perl code. #------------------------------------------------------------------------ # Version 3.2.1, 24 Apr 2011 #------------------------------------------------------------------------ * Ensure $num is numeric in lib/BackupPC/CGI/Browse.pm to avoid XSS attack. Report and patch by Adam E. * Fixed application of "*" in $Conf{BackupFilesOnly} and $Conf{BackupFilesExclud} for 2nd and later shares. Reported by Alessandro and Alexander Maringer. * Fixed email status check in lib/BackupPC/CGI/HostInfo.pl so that empty email info doesn't appear; reported by Wayne Trevena, and based on patch from Tyler Wagner. * Several fixes to FTP xfer mode related to file excludes, from Dave Pearce. * Wrapped eval() around unpack() in lib/BackupPC/Attrib.pm to avoid failures on corrupted attrib files; patch from Tim Connors. * Applied documention patch from Alexander Moisseev. #------------------------------------------------------------------------ # Version 3.2.0, 31 Jul 2010 #------------------------------------------------------------------------ * Fixed code that detects duplicate shares in bin/BackupPC_dump * Added fix to lib/BackupPC/Zip/FileMember.pm to avoid bug in Archive::Zip 1.30 when creating compressed archives. * Added Czech translation from Petr Pokorny. #------------------------------------------------------------------------ # Version 3.2.0beta1, 24 Jan 2010 #------------------------------------------------------------------------ * Fixed FTP xfer method, with help from Holger Parplies and Mirco Piccin. FTP restores are still not supported. * Fixed bug in BackupPC_sendEmail where a user only receives email about one host. * Fixed bug where top-level attrib file was linked into the pool with the wrong digest, caused by it being updated multiple times with multiple shares. Reported by Jeff Kosowsky who also supplied a patch. * Fixed bug in blackout calculation when multiple periods span midnight. Report and patch from Joachim Falk. * Wrapped eval {} around attribute unpacking to make it more robust to data corruption. Path submitted by Tim Connors. * Ignore fileType 8 and 9 in BackupPC_tarCreate rather than consider then errors. These are sockets and unknown (eg: solaris door) files that are created dynamicaly by applications - there is no meaningful restore for these file types. * Changed lib/BackupPC/Lib.pm and lib/BackupPC/Storage/Text.pm based on patches from Davide Brini and Holger Parplies so that main config %Conf values are available in the host config file, allowing more flexibility in perl expressions in the config files. Users beware, since the CGI editor won't work correctly if the config file have perl expressions. * Obscure password values in LOG file when CGI editor is used to change values. Proposed by Steve Ling. * Added favicon.ico from Axel Beckert. Thanks to Tyler Wagner for submitting another version and reminding me about the first. * Replace "sort(HostSortCompare keys(%$Hosts))" with "sort HostSortCompare keys(%$Hosts)" in bin/BackupPC to avoid an error with certain versions of perl. * Fixed $Conf{XX} links in the BackupPC.html and the CGI editor so they correctly reference the definition. * Support ${VAR} style variable substitution in commands, in addition to existing $VAR style. Suggested by Jeffrey Kosowsky. * Clarified usage of -b and -w options to BackupPC_tarCreate. Submitted by Michael Selway. * Repaired Unable_to_connect_to_BackupPC_server Lang string and added new string Unable_to_connect_to_BackupPC_server_error_message. Proposed and explained by Holger Parplies. * Added 'use utf8' to lib/BackupPC/Lang/pl.pm. Reported by Michal Sawicz. * Minor updates to lib/BackupPC/Lang/fr.pm from Hubert Tournier. * Minor update to lib/BackupPC/Lang/en.pm from David Relson. #------------------------------------------------------------------------ # Version 3.2.0beta0, 5 April 2009 #------------------------------------------------------------------------ * Added BackupPC::Xfer::Protocol as a common class for each Xfer method. This simplifies some of the xfer specific code. Implemented by Paul Mantz. * Added FTP xfer method, implemented by Paul Mantz. * Added BackupPC::Xfer module to provide a common interface to the different xfer methods. Implemented by Paul Mantz. * Moved setting of $bpc->{PoolDir} and $bpc->{CPoolDir} after the config file is read in BackupPC::Lib. Fix proposed by Tim Taylor and Joe Krahn, and rediscovered by several others including Holger Parplies. * Create $TopDir and related data directories in BackupPC_dump prior to hardlink test. Requested by Les Stott. * Fixed encoding of email subject header in bin/BackupPC_sendEmail as suggested by Jean-Claude Repetto. Also changed $Conf{EMailHeaders} charset to utf-8. Also changed bin/BackupPC_sendEmail to not send any per-client email if $Conf{BackupsDisable} is set. * Modified bin/BackupPC_dump to fix the case of a single partial backup followed by a successful incremental resulting in a full backup of level 1, rather than level 0. Reported by Jeff Kosowsky. * Fixed BackupPC::PoolWrite to always create the parent directory. This fixed a case with rsync/rsyncd where a file like "-i" in the top-level directory sorts before ".", which meant the directory creation is after the file creation. Also PoolWrite errors now increment xferError count. Reported by Jeff Kosowsky. * BackupPC now gives a more useful error message if BackupPC_nightly takes more than 24 hours (ie: when the next one is meant to start). Reported by Tony Schreiner. * Fixed IO::Dirent run-time check. Reported by Bernhard Ott and Tino Schwarze debugged it. * Added more options to server backup command: rather than just forcing an incremental or full backup, a regular (auto) backup can be queued (ie: do nothing/incr/full based on schedule), as well as doing just an incremental or full or nothing based on the client schedule. Based on patches submitted by Joe Digilio. * Modified lib/BackupPC/CGI/RSS.pm to replace \n with \r\n in the RSS http response headers. Patch submitted by Thomas Eckhardt. * Modified bin/BackupPC_archive to allow the archive request file name to contain spaces and dashes, requested by Tim Massey. * Fix to configure.pl for --no-fhs case to initialize ConfigDir from Dan Pritts. Also changed perl path to #!/usr/bin/env perl. * Modified bin/BackupPC_archiveHost to shell escape the output file name. That allows it to contain spaces and other special characters. Requested by Toni Van Remortel. Also updated bin/BackupPC_archiveHost to shell escape and check other arguments. * Added $Conf{CmdQueueNice} to specify nice level for command queue commands (eg: BackupPC_link and BackupPC_nightly). Suggested by Carl Soderstrom. * Added --config-override to configure.pl, allow config settings to be set on the command line. Proposed by Les Stott and Holger Parplies. * Moved call to NmbLookupFindHostCmd in BackupPC_dump to after the check of whether a backup needs to be done. This makes wakeonlan work correctly, rather than waking up the client every WakeupSchedule. Reported by David Lasker. * Improved settings for compression and compext in BackupPC_archiveStart based on compression type, as proposed by Paul Dugas. compext is now empty, .gz or .bz2 based on ArchiveComp. * Changed bin/BackupPC_dump to not ping or lookup the host if $Conf{BackupsDisable} is set. Requested by John Rouillard. * Changed BackupPC_tarCreate to disable output of final nulls in tar archive when -l or -L option is used. Reported by John Rouillard. * Added error check in BackupPC::Xfer::RsyncFileIO after call to BackupPC::Xfer::RsyncDigest->digestStart(), reported by Jeff Kosowsky. * Added variable substitution for host, confDir, client in RsyncArgs, and also added option RsyncArgsExtra to allow more easy customization of RsyncArgs on a per-client basis. Proposed (with patch) by Raman Gupta. * Added Xfer error column to the host summary table in the CGI interface. Based on patch submitted by Jan Kratochvíl. * Minor fix to sprintf arguments in BackupPC::Attrib, reported by Jonathan Kamens. * Fixed sort compareLOGName syntax in bin/BackupPC for perl 5.10.x, reported by Jeff Kosowsky and Holger Parplies. * Fixed bin/BackupPC_archiveStart to set compression correctly, and also set the file extension to .gz when compression is on. Reported by Stephen Vaughan. * Fixed netbios name comparison in bin/BackupPC_dump and bin/BackupPC_restore to just use the first 15 characters of the host name. Patch from Dan MacNeil. * Fixed nmblookup parsing in BackupPC::Lib::NetBiosInfoGet to ignore entries with the tag. Based on patch from Dan MacNeil. * Fixed BackupPC_dump so that the XferLOG file is saved when DumpPreUserCmd fails. Reported by John Rouillard. * Updated BackupPC.pod for $Conf{BackupsDisable}, reported by Nils Breunese. * Added alternate freebsd-backuppc2 init.d script that is more compact. Submitted by Dan Niles. * Minor updates to lib/BackupPC/Lang/fr.pm from Nicolas STRANSKY applied by GFK, and also from Vincent Fleuranceau. * Minor updates to lib/BackupPC/Lang/de.pm from Klaus Weidenbach. * Updates to makeDist for command-line setting of version and release date from Paul Mantz. * Add output from Pre/Post commands to per-client LOG file, in addition to existing output in the XferLOG file. Patch from Stuart Teasdale. * lib/BackupPC/Xfer/Smb.pm now increments xferErrCnt on NT_STATUS_ACCESS_DENIED and ERRnoaccess errors from smbclient. Reported by Jesús Martel. * Removed BackupPC_compressPool and BackupPC::Xfer::BackupPCd. #------------------------------------------------------------------------ # Version 3.1.0, 25 Nov 2007 #------------------------------------------------------------------------ * Fixed config editor bug for case where override is unchecked on an array where the current array is shorter than the main config's array. * Fixed missing close quote in BackupPC_archiveHost reported by Franky Van Liedekerke. * Replaced "$BinDir/.." with $bpc->InstallDir() for path to BackupPC docs, mentioned by Kenneth Porter. * Moved default of $Conf{IncrLevels} from lib/BackupPC/Storage/Text.pm to lib/BackupPC/Lib.pm (after the merge of the config files). This fixes a bug that caused $Conf{IncrLevels} to get over-ridden if it was only defined in the main config file. Reported by John Rouillard. * Fixed the completion status message in BackupPC_dump so that missing error counts appear as 0, rather than empty. Reported by Bill. * Changed lib/BackupPC/Xfer/RsyncFileIO.pm to only increment the error count when the md4 checksum fails on the second phase, not the first. Reported by Adrian Bridgett. * Updated a comment in config.pl about BackupPC_nightly, reported by Dan Pritts. * Modified lib/BackupPC/CGI/Restore.pm to ensure that the list of hosts presented for direct restore do have direct restore enabled. Reported by Stephen Joyce. * Modified lib/BackupPC/CGI/RestoreFile.pm to replace \n with \r\n in the restore http response headers. Patch submitted by Thomas Eckhardt. #------------------------------------------------------------------------ # Version 3.1.0beta1, 21 Oct 2007 #------------------------------------------------------------------------ * When there is an existing partial, a new partials is only saved if it has more files than the existing partial. Requested by Carl Soderstrom. * Fixed handling of $Conf{BackupFilesExclude} for tar XferMethod. Patch supplied by Frans Pop. * Fixed numeric column sorting in host summary table, reported by Michael Pellegrino. * Fixed host CGI editor so it creates the new host's config.pl file using the lower-case host name, since host names are mapped to lower case when they are read from the hosts file. Reported by Alexander Onic. * Applied documentation patches from Frans Pop. Also updated Pod::Html to improve documentation formatting. * Added Polish translation from Semper. * Fixed BackupPC_nightly reporting of repeated pool file hashes. * Add run-time check that IO::Dirent is functioning correctly, reported by Doug Lytle. * Added comment to Cmd settings in conf/config.pl that they are not executed by a shell, as suggested by Erik van Linstee. * Added undefIfEmpty => 1 to lib/BackupPC/Config/Meta.pm for RsyncRestoreArgs, TarClientRestoreCmd and SmbClientRestoreCmd so that restores can be disabled by clear these fields in the CGI editor. Patch supplied by Stephen Joyce * Replaced the FAQ link with Wiki in the navigation bar and added mention of the Wiki to the documentation. Since these navigation bar links are specified in the config file, upgrades will keep the old FAQ link. The FAQ opening page will have a prominent link to the Wiki. #------------------------------------------------------------------------ # Version 3.1.0beta0, 3 Sep 2007 #------------------------------------------------------------------------ * Added new script BackupPC_archiveStart that allows command-line starting of archives. Based on script written by Sergey Kovzik, which in turn was based on an earlier version by Holger Parplies. * Added Simplified Chinese CGI translation from Youlin Feng, plus fixed a couple of cases where utf8 share names were not displayed correctly. * Added sorting by column feature to host summary table in CGI interface. Implemented by Jeremy Tietsort. * Added optional support for IO::Dirent which allows inode information to be extracted from the dirent directory structure. This allows BackupPC to order some directory operations by inode, which on some file systems (eg: ext3) can results in a 20-30% performance gain. On other file systems there is no real improvement. This optimization is turned on automatically if IO::Dirent is installed. * Added some performance improvements to BackupPC::Xfer::RsyncFileIO for the case of small files with cached checksums. * Added check to BackupPC at startup that $TopDir can support hardlinks. Also added check to BackupPC_dump that a hardlink below $TopDir/pc/HOST can be made to below $TopDir/cpool. Also added the need for a hard-link capable file system to the documentation. Suggested by Nils Breunese. * Added FreeBSD init.d file provided by Gabriel Rossetti. * Added -l and -L options to BackupPC_tarCreate so that provide a file list (without creating the archive). Requested by Dirk. * Made the default charset for BackupPC_zipCreate cp1252, which appears to work correctly with WinZip. Unfortunately there is no clear standard for charset encoding in zip files. * Added support so that pre-3.0 backups with non-utf8 charsets can be viewed and restored correctly. A new config variable $Conf{ClientCharsetLegacy} specifies the charset used to encode file names in legacy backups. This is only relevant if you are trying to view or restore a backup made with BackupPC 2.x and some of the file names have non-ascii characters. * Added setting of the environment variable BPC_REQUSER to the requesting user name in BackupPC prior to fork(), so each child process inherits the value. Submitted by Holger Parplies. * Fixed bug in rsync incrementals that happens on particular file names when a file being backed up fails in both rsync phases. Reported by Dan Smisko. * Fixed single-restore file name charsets for IE, reported by Francis Lessard. * Fixed makeDist so that the --config-dir option to configure.pl works correctly. Reported by Randy Barlow, Tony Shadwick and others. * Removed ConfDir from config editor (since it is hardcoded in lib/BackupPC/Lib.pm). Also made TopDir and LogDir only visible if useFHS (for non-FHS they are hardcoded in lib/BackupPC/Lib.pm). * Applied patch from Holger Parplies that fixes cleanup of early abort in BackupPC_dump. * Applied small patch from Sergey to lib/BackupPC/Xfer/Tar.pm that makes it ignore "socket ignored" error on incrementals. * Applied small patch from Sergey to bin/BackupPC_archiveHost. * Changed BackupPC_sendEmail so that summary admin email doesn't include errors from hosts that have $Conf{BackupsDisable} set. Reported by James Kyle. Also, per-user email is now disabled when $Conf{BackupsDisable} is set. * Added RsyncdUserName to the config editor. Reported by Vicent Roca Daniel. * $Conf{IncrLevels} is now defaulted if it is not defined. * configure.pl clears $Conf{ParPath} if it doesn't point to a valid executable. * Added documentation for BackupPC_tarPCCopy, including use of -P option to tar suggested by Daniel Berteaud. * Config editor now removes white space at start of exec path. Reported by Christoph Iwasjuta. * CgiDateFormatMMDD == 2 gives a YYYY-MM-DD format for CGI dates, suggested by Imre. #------------------------------------------------------------------------ # Version 3.0.0, 28 Jan 2007 #------------------------------------------------------------------------ * BackupPC_sendEmail now correctly sends admin email if backups were skipped because the disk was too full, reported by Dan Pritts. * BackupPC_Admin now uses $Conf{UmaskMode}, so config.pl files written by the editor have more restrictive permissions. Reported by Tim Massey. * Host summary now shows active backups on disabled hosts, from Jono Woodhouse. * Fixed host LOG link and LOG list order, reported by Tim Massey. * Moved Encode.pm version check to start of configure.pl so it produces a useful error message if Encode.pm is too old. * Fixed hrefs to configuration documentation to handle changes in the way perl generates the anchors. Reported by Philip Gleghorn. * Host name links in LOG files now allow "." in the host name. Reported by Jean-Michel Beuken. * Fixes to lib/BackupPC/Xfer/Tar.pm for tar 1.16: allow 1 (ie: 256) as a successful exit status and match "Total bytes read" message for restores. First reported by Torsten Sadowski and debugged by Ralf Gross and Holger Parplies. #------------------------------------------------------------------------ # Version 3.0.0beta3, 3 Dec 2006 #------------------------------------------------------------------------ * Removed default paths from conf/config.pl so configure.pl will determine the correct ones at install time. Avoids problem of the config editor complaining about bad executable paths the first time you use it. * Changed first byte of compressed files with rsync checksums appended to 0xd7 to allow correct protocol_version >= 27 md4 checksums to be written. Old cached checksum files have a first byte 0xd6 and are now considered to be uncached. They will be automatically updated as needed. This avoids the cached checksum warnings in beta2. * BackupPC_tarPCCopy now handles all file types correctly. Reported by George Avrunin. * Fixed BackupPC_nightly to finish pending deletes before renaming pool chains. * Fixes for rsync restore where hardlink is to file outside of the top-level restore directory. Reported by George Avrunin, who helped with debugging. * Fixes for checksum mismatch on restore for certain file sizes. Reported by George Avrunin and others. * Fix for config.pl writing code to handle multi-line expressions. Reported by David Relson and others. * Fix for CGI editor when deleting hash entries whose keys are non alphanumeric. Report by David Relson and Aaron Ciarlotta. * Two fixes to configure.pl from Andreas Vögele. #------------------------------------------------------------------------ # Version 3.0.0beta2, 18 Nov 2006 #------------------------------------------------------------------------ * Fix for final md4 digest check on rsync transfers >= 512MB when protocol version >= 27 and checksums are not cached. Reported by Garith Dugmore and Dale Renton. * Config Editor "Save" button is now always visible, but greyed out until there are changes to save. * Config editor allows other tabs to be selected when there is an error, which allows you to fix an error (eg: missing binary) in an exiting config file. Errors are now displayed at the top of the page in addition to next to the erroneous setting. * configure.pl checks version of Encode.pm. Reported by Chris Stone. * Several fixes to bin/BackupPC_fixupBackupSummary from Stian Jordet. * Fixed config.pl editor writing to solve bug with multi-line text strings ending in newline. Reported and root caused by Les Stott and Jerry Groendyke. * Fixed error recovery case in BackupPC::PoolWrite, reported by Samuel Bancal. * Fixed table width in backup browsing to avoid Firefox layout anomoly, provided by Jono Woodhouse. * CSS file updates from Jono Woodhouse. Prior (v2) version is included as BackupPC_stnd_orig.css in case people prefer the old skin. * More compact host summary, including disabled host indication, from Jono Woodhouse. * New directory/file/hardlink and symlink image icons from Sean Cameron and Jono Woodhouse, making directory browse more compact. * BackupPC.pid is now world readable, suggested by Casper Thomsen. * Reordered the Server navigation bar links, suggested by David Relson. * Fixed typos in init.d/src/gentoo-backuppc, configure.pl and config.pl reported by David Relson. #------------------------------------------------------------------------ # Version 3.0.0beta1, 30 Jul 2006 #------------------------------------------------------------------------ * Fixed several Xfer charset conversions. * Added some CGI utf8 conversions from Rodrigo Real and Vincent Fleuranceau. * Rsync transfers now correctly handle file names with \n or \r. * Host name is forced to lower case, to match 2.x. * Fixed LOG file naming in BackupPC_restore and BackupPC_archive. * GFK applied fr.pm corrections from Nicolas Stransky. * Updated init.d/src scripts for FHS (ie: replaced __TOPDIR__/log with __LOGDIR__ and __TOPDIR__/conf with __CONFDIR__). Patch provided by Rodrigo Real. * Added --log-dir and --conf-dir options to configure.pl. Reported by Vincent Fleuranceau. * Updated File::RsyncP version check in configure.pl, reported by Vincent Fleuranceau. Changed File::RsyncP version to 0.64. #------------------------------------------------------------------------ # Version 3.0.0beta0, 11 Jul 2006 #------------------------------------------------------------------------ * Added configuration and host CGI editor. * Added rsync hardlink support. Requires latest version of File::RsyncP (0.62). * Decoupled BackupPC_dump from BackupPC_nightly by making asynchronous file linking/delete robust to race conditions. Now only BackupPC_nightly and BackupPC_link are mutually exclusive so only one runs at a time, and BackupPC_dump and BackupPC_restore can run anytime. * Added support for multi-level incrementals. In the style of dump(1), the level of each incremental can be specified. Each incremental backups up everything since the most recent backup of a lower level (fulls are always level 0). Previous behavior was all incrementals were level 1, meaning they backed up everything since the last full (level 0). Default configuration is all incrementals are level 1. * Server file names are now in utf8 and optional conversion to/from client name charsets can be configured. All CGI pages now use the utf8 charset. * Backup metadata is now additionally saved to pc/HOST/nnn/backupInfo, in addition to pc/HOST/backups. In case pc/HOST/backups gets trashed, then a new script BackupPC_fixupBackupSummary can read the per-backup metadata from pc/HOST/nnn/backupInfo and reconstruct the backups file. Roberto Moreno also pointed out an early error in the CVS version. * Added Storage module and Storage::Text which localizes all the text data file reading/writing (eg: backups, restores, archives and config.pl files). Added read verify after all write operations for robustness. Additional backends (eg: SQL) can be added in the future as new subclasses of the Storage module. * Added Config module, and Config::Meta that contains meta data about configuration parameters. * Added RSS support from Rich Duzenbury. * Translations of new 3.0 language strings from Guillaume Filion, Reginaldo Ferreira, Ralph Passgang, Lieven Bridts, Guus Houtzager, Rodrigo Real. * Added optional checking of exit status of Dump/Restore/Archive Pre/Post UserCmd, requested by Kiko Jover, Matthias Bertschy and others. * For new installations configure.pl tries to comply with the file system hierarchy standard, which means all the configuration files below /etc/BackupPC and log files go below /var/log/BackupPC. * Added Slackware init.d script from Tony Nelson. * Fixed error reporting when restore/archive fail to write the request file to the client directory. * Applied patch from Marc Prewitt for DumpPreShareCmd and DumpPostShareCmd. * Apply patch from Pete Wenzel to add smbClientPath => $Conf{SmbClientPath} to DumpPreUserCmd etc. * Added Portuguese Brazillian pt_br.pm from Reginaldo Ferreira. * Jean-Michel Beuken reported several bugs in configure.pl in CVS 3.0.0. * Old backup email warnings now ignore partials requested by Samuel Bancal * Applied patch to bin/BackupPC_sendEmail from Marc Prewitt that ignores any file starting with "." in the pc directory when it is generating warnings about old/unused files/directories. * Applied patch from Marc Prewitt to fix host queue order. * Applied Lorenzo Cappelletti's it.pm patch. * Applied Wander Winkelhorst's nl.pm patch. * Applied Alberto Marconi's it.pm patch. * Add NT_STATUS_FILE_LOCK_CONFLICT to pst read error check in BackupPC_sendEmail to fix bug reported by Dale Renton. * Added fixup of $ENV{REMOTE_USER} to lib/BackupPC/CGI/Lib.pm in the case of using mod_authz_ldap; patch submitted by Alain Perrier. * Added env LC_ALL=C to $Conf{TarClientCmd} and $Conf{TarClientRestoreCmd} to avoid locale problems, suggested by Ludovic Drolez. * Changed ping output parsing to pick out average rtt time, based on patch from Ron Bickers. * Removed leading "./" and top-level "./" directory from zip archives generated by BackupPC_zipCreate. Reported by Josh (hecktarzuli). * BackupPC_tarCreate and BackupPC_zipCreate now allow "@" in share names. Reported by Robert Waldner. * NT_STATUS_INSUFF_SERVER_RESOURCES is now a fatal error for smbclient transfers, suggested by Brian Shand. * Changed bin/BackupPC_archiveHost to use /bin/csh instead of /bin/sh. That way any errors in the pipeline are reported via the exit status, instead of just the last. * Added $Conf{EMailHeaders} for additional email headers, requested by Ludovic Gasc. If the Content-Type charset is set to utf8 then the body of the email is sent in utf8 coding. * Made shareName argument regexp checking more general to allow parens. * Added some debian init.d instructions to init.d/README from Bob de Wildt. * Documentation updates from Richard Ames, JP Vossen, Torsten Finke. #------------------------------------------------------------------------ # Version 2.1.2pl2, 18 Jun 2006 #------------------------------------------------------------------------ * In conf/config.pl, changed --devices to -D in $Conf{RsyncArgs} and $Conf{RsyncRestoreArgs} to fix "fileListReceive failed" and "Can't open .../f%2f for empty output" errors with rsync 2.6.7+. Fix proposed by Justin Pessa and Vincent Ho, and confirmed by Dan Niles. * Added patch from Michael (mna.news) to ignore "file is unchanged" message from tar 1.15.x during incremental backups. * Fixed creation of .rsrc directories in bin/BackupPC_tarExtract when used with xtar on MacOS. Reported by Samuel Bancal and Matthew Radey, who helped with debugging. * Fixed bug in BackupPC_tarExtract for files >8GB in size whose lengths are multiples of 256. Reported by Jamie Myers and Marko Tukiainen, who both helped debugging the problem. * Fixed bug in lib/BackupPC/Xfer/RsyncFileIO.pm that caused incorrectly deleted attributes to be set in directories where one of the files had an rsync phase 1 retry during an incremental. Reported by Tony Nelson. #------------------------------------------------------------------------ # Version 2.1.2, 5 Sep 2005 #------------------------------------------------------------------------ * Fixed simple but serious bug in bin/BackupPC_tarCreate that prevented hardlinks being saved correctly. Debugged by Michael (mna.news) with several other people. * Fixed serious bug in bin/BackupPC_dump reported/debugged by Dan Niles that can happen when multiple full backups are deleted after $Conf{FullKeepCnt} is changed. * Changed lib/BackupPC/CGI/Lib.pm so that link to "$TopDir/conf/$host.pl" is displayed if it exists. Patch from Andreas Vögele. * Applied daemonize patch to bin/BackupPC from: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=301057 * It's now a fatal error if $Conf{CompressLevel} is set, but Compress::Zlib is not found. Before $Conf{CompressLevel} was silently set to 0, which made all the backups uncompressed. That meant the user never knew if they forget to install Compress::Zlib but were expecting compression to be on. * Finally increased $Conf{ClientTimeout} to 72000 (20 hours). * Added sleep 1 in restart() function in init.d/src/gentoo-backuppc, suggested by Jon Hood. * Added $DestDir to the path of the CGI image directory in configure.pl. Patch submitted by Andreas Vögele. * Applied extensive patch to French translation from Frederic Lehobey. * Minor change to Dutch language $Lang{Pool_Stat} from Wander Winkelhorst. * Minor change to French language $Lang{EMailOutlookBackupMesg} and $Lang{EMailOutlookBackupSubj} from Ludovic Gasc. #------------------------------------------------------------------------ # Version 2.1.1, 13 Mar 2005 #------------------------------------------------------------------------ * Fixed bug in top-level restore using rsync XferMethod. BackupPC::View was leaving an extra leading "/" at the start of top-level directories, messing up the FileList sort order. Reported and debugged by Gail Edwards. * Added pathCreate() to BackupPC_tarExtract so that new directories are created. Required for xtar on MacOSX since the virtual resource fork directories (.rsrc) are not explicitly included in the tar archive - just the files below .rsrc are. * Changed $host.$bkupNum.tar$fileExt.* to $host.$bkupNum.tar$fileExt* in $parCmd in bin/BackupPC_archiveHost. * Fixed HostSortCompare() in BackupPC to correctly sort hosts so those with the oldest backups get queued first. * Changed test in BackupPC_sendEmail so that directories/files starting with "." in $TopDir/pc are ignored, rather than just "." and "..". * Changed BackupPC_sendEmail to include NT_STATUS_FILE_LOCK_CONFLICT in check for bad outlook files. * Ensure that %Status and %StatusHost are empty if requesting status on hosts in lib/BackupPC/CGI/Lib.pm GetStatusInfo(). Fixes problem with new hosts under mod_perl. * Added images/icon-hardlink.gif so that hardlinks show file type icon. #------------------------------------------------------------------------ # Version 2.1.0pl1, 15 Aug 2004 #------------------------------------------------------------------------ * Added fix to nl.pm from Lieven Bridts. * Added patch from Tony Nelson to remove $Info{pid} before BackupPC writes the status and shuts down. * Changed BackupPC_nightly so that it doesn't call find() if the directory doesn't exist. This avoids errors in certain versions of perl. Reported by Bernd Rilling. * Fixed BackupPC::CGI::Lib to correctly re-load config.pl for mod_perl. Reported by Tony Nelson and Jimmy Liang. * Explicitly untaint $In{host} in BackupPC::CGI::Lib to fix problem reported by Thomas Tempé. * Added newline to "...skipping because of user requested delay..." log message in BackupPC_dump. Reported by Wayne Scott. * Added read file size error checking to BackupPC_tarCreate. Reported by Brandon Evans. * Added check in BackupPC::Xfer::RsyncFileIO to ensure that when compression is toggled on/off, a compressed backup doesn't link to an uncompressed pool file (and an uncompressed backup doesn't link to a compressed pool file). Reported by Brandon Evans. * Updated documentation with new dirvish URL and a typo from Todd Curry. * Fix to BackupPC_sendEmail so that it correctly sends admin emails for hosts that have failed backups. Reported by Simon Kuhn. #------------------------------------------------------------------------ # Version 2.1.0, 20 Jun 2004 #------------------------------------------------------------------------ * Added Dutch translation from Lieven Bridts, with tweaks from Guus Houtzager. * Added PC-specific config file read in CGI/Archive.pm. Patch from Pete Wenzel. * Added non-zero exit code to BackupPC_zcat when uncompress fails. Patch from Pete Wenzel. * Cosmetic changes to PC Summary and Log file language strings from Pete Wenzel. * BackupPC::Lib tries to be more careful when renaming the backups file to backups.old. There have been reports of backups being empty, perhaps when the BackupPC data file system fills up. Now backups is not renamed to backups.old if backups is empty. * BackupPC now closes stderr and stdout before renaming and re-opening the log file. * Pre/post backup/restore/archive commands now correctly set "type" to either incr/full/restore/archive, and now cmdType is the type of Pre/post backup/restore/archive command. * BackupPC_archive correctly terminates archive processes on alarm or cancel. * Updates to BackupPC_stnd.css with absolute font sizes instead of relative. * BackupPC_dump now makes sure that the $Conf{FullAgeMax} check also ensures the full backup is older than the maximum age expected from $Conf{FullPeriod}. #------------------------------------------------------------------------ # Version 2.1.0beta2pl1, 30 May 2004 #------------------------------------------------------------------------ * Fixed bug in rsync checksum caching code in BackupPC::Xfer::RsyncDigest. * BackupPC_zipCreate now ensures the earliest mtime is 1/1/1980, since zip file formats don't support earlier dates. Reported by Dan Niles. * CGI restore via zip and tar now makes sure stderr is ignored when BackupPC_tarCreate and BackupPC_zipCreate are run. Previously any stderr output would get mixed in the archive, corrupting it. Reported by Dan Niles. #------------------------------------------------------------------------ # Version 2.1.0beta2, 23 May 2004 #------------------------------------------------------------------------ * $Conf{BackupFilesOnly} and $Conf{BackupFilesExclude} now apply to every share, rather than just the first, in the case where they are arrays and there are multiple shares. Suggested by Andy Evans. * On the phase 2 retry pass with rsync, verify the cached checksums if checksum caching is turned on. This will catch the case of cached checksums being incorrectly appended to the compressed pool file. Added new config parameter $Conf{RsyncCsumCacheVerifyProb} so that cached checksums are verified with a selectable probability. Also, increased File::RsyncP version number to 0.51. * configure.pl now supports an optional batch mode. Command-line options are used to specify all the information that configure.pl needs. This is useful for building auto-install packages. Also, configure.pl now includes pod documentation, so you can do "perldoc configure.pl" to see all the command-line options. Suggested, tested and tweaked by Stuart Herbert for possible Gentoo inclusion. * At each wakeup, clients are now queued based on how old the most recent backup is. Clients with errors are queued first, with the oldest error times going first. The rest of the clients are queued next, with the clients with the oldest backup going first. Previously the clients were simply queued in alphabetic order. Suggested by Mike Trisko and Tony Nelson. * Added config parameter $Conf{PartialAgeMax} that controls whether partials are saved at all, and if so, whether the partial will be ignored at the next full backup if it is too old. * BackupPC_tarExtract now allows empty archives without reporting an error. Reported by Don Silvia. * Removed Browse Backups link from Nav Bar in Archive Info display. Reported by Ralph Paßgang. * Fixed documentation display for regular users. Reported by Ralph Paßgang. * Status and PC Summary now work for regular users and only show that user's hosts. Server general status information only appears for admins. Suggested by Ralph Paßgang. * Moved the last three navigation-bar links (docs, FAQ and SF) to a new config parameter $Conf{CgiNavBarLinks}. This allows these links to be locally configured. Based on a patch submitted by Ralph Paßgang. * Allow the navigation bar search box to be disabled by setting $Conf{CgiSearchBoxEnable} to 0. Based on a patch submitted by Ralph Paßgang. * Updates to de.pm from Ralph Paßgang. * Made the BackupPC icon a link to the SF BackupPC project page. #------------------------------------------------------------------------ # Version 2.1.0beta1, 4 Apr 2004 #------------------------------------------------------------------------ * The CSS definition has been removed from the config.pl file and is now a separate file, BackupPC_stnd.css. A new config variable, $Conf{CgiCSSFile}, gives the name of the CSS file to use. Suggested by Ender Mathias. * Fixed the filling of the host name select box for admins. The default $Conf{CgiNavBarAdminAllHosts} is now 1. Reported by Doug Lytle. * Cleaned up warning message for restore using rsync when checksum caching is on, but when file didn't have cached checksums. * Fixed BackupPC_archiveHost to support par2 (par2cmdline). Patch submitted by Jaco Bongers and adapted by Josh Marshall. * Improved stat() usage in BackupPC_nightly, plus some other cleanup, giving a significant performance improvement. Patch submitted by Wayne Scott. * Allow several BackupPC_nightly processes to run in parallel based on new $Conf{BackupPCNightlyJobs} setting. This speeds up the traversal of the pool, reducing the overall run time for BackupPC_nightly. * Allow BackupPC_nightly to split the pool traversal across several nightly runs. This improves the running time per night, at the expense of a slight increase in disk storage as unused pool files might not be deleted for a couple of days. Controller by new config setting $Conf{BackupPCNightlyPeriod}. #------------------------------------------------------------------------ # Version 2.1.0beta0, 20 Mar 2004 #------------------------------------------------------------------------ * A failed full dump is now saved as a partial (incomplete) dump, provided it includes some files. This can be used for browsing, restoring etc, and will also form the basis of resuming full dumps. Only one partial is kept, and it is removed as soon as a successful full (or a new partial) is done. * Added support for resuming a full dump for rsync. The partial full is kept, and to resume an incremental is done against the partial, and a full is done for the rest. * Added support for Rsync checksum caching. Rsync checksum are appended to the compressed pool files. This means that block and file checksums do not need to be recomputed on the server when using rsync. Requires a patch to rsync to support fixed checksum seeds. This patch is included in the cygwin-rsyncd release on http://backuppc.sourceforge.net. * Major addition of Archive feature from Josh Marshall. Special clients can be configured to be archive targets (eg: tape drives, CD-R). Any subset of the backup clients can be selected and tar archives are created, optionally compressed and split and written to the output device. Logs are maintained and are browsable. * Major changes from Ryan Kucera to add style sheets to the CGI interface, allowing easy customization. Added new icons and BackupPC logo. Numerous navigation improvements. * Added directory history display to BackupPC_Admin, allowing the user to quickly see which files changed between backups on a per-directory basis. * Added exponential expiry option for full dumps. This allows you to specify - how many fulls to keep at intervals of $Conf{FullPeriod}, followed by - how many fulls to keep at intervals of 2 * $Conf{FullPeriod}, - how many fulls to keep at intervals of 4 * $Conf{FullPeriod}, - how many fulls to keep at intervals of 8 * $Conf{FullPeriod}, - how many fulls to keep at intervals of 16 * $Conf{FullPeriod}, and so on. This allows you, for example, to keep 4 weekly fulls, followed by 6 fulls every 4 weeks (approx 1 month) and 2 fulls at 16 weeks, for roughly 1 year of history. This works by deleting every other full as each expiry boundary is crossed. Suggested by David Cramblett. * Added Italian language translation it.pm from Lorenzo Cappelletti. * Major updates to language files for new features and tags changes. Updated makeDist to do pedantic consistency checking of language files. * Addition of administration options from Paul Lukins. Initial page allows server to be started/stopped/reloaded. This still needs some i18n work. Currently the server start/stop is commented out. * Split BackupPC_Admin into a set of modules, one for each major action. Each action is now a seperate module in lib/BackupPC/CGI. * Allow the blackout period begin/end to span midnight. Adapted from patch submitted by David Smith. * Allow multiple blackout periods, with new config variable $Conf{BlackoutPeriods} that replaces the old variables $Conf{BlackoutHourBegin}, $Conf{BlackoutHourEnd}, and $Conf{BlackoutWeekDays}. Based on patch submitted by Lorenzo Cappelletti. * Disabled alarms after forks to avoid timeouts in children that do not reset their alarm. Prompted by ideas from James Leu. * Added options for block size, buffer size and share wild-card to BackupPC_tarCreate. Also added negative backup number options that are relative to the last (so -1 is the last), suggested by William McKee and Carl Soderstrom. * The "Wrong user" message in BackupPC::Lib now goes to stderr, so that the user is more likely to see the error with BackupPC_tarCreate. Reported by Paul Fox. * Add creation of per-PC directory in BackupPC/CGI/Restore.pm in case it doesn't already exist. * Added -q -x to all ssh commands in conf/config.pl. Suggested by SI Reasoning and Niranjan Ghate. * Changed restore code so that option #1 (direct restore) can be disabled if the restore command is undefined. Disabling direct restore is necessary if the share is read-only. Suggested by Rich B from SAIC. * Changed regexp in lib/BackupPC/Smb.pm to allow numbers with both a decimal point or comma for international versions of Samba. Patch submitted by Frank Gard. * Browsing and directory history now sort the files in a case-insensitive manner. * Changed exec() syntax to allow executing commands whose path contains spaces. * BackupPC_dump no longer removes backups if $Conf{FullKeepCnt} is zero or undefined. The protects the existing backups in the case of a bad config.pl file. Suggested by Christian Warden. * Swapped the Server and Hosts sections on the Nav bar. Moved the host search text box to the top of the hosts section. This was done to move the variable-length part of the Nav bar (when all hosts are displayed) to the bottom. * Fixed a bug in tar restore related to how the common prefix path is removed. Now ensure that the common path is at a directory boundary. Reported by Patrick Neuner. * Added --chuid ${USER} to init.d/src/gentoo-backuppc. Suggested by SI Reasoning, Pascal Pochol, Michael Evanoff and others. * Added Suse notes to init.d/README from Bruno Vernay. * Added Apache 2 documentation fix from Michael Tuzi. #------------------------------------------------------------------------ # Version 2.0.2, 6 Oct 2003 #------------------------------------------------------------------------ * Fixed stupid last-minute change in octal size conversion in Backup_tarExtract. #----------------------------------------------------------------------- # Version 2.0.1, 5 Oct 2003 #------------------------------------------------------------------------ * Fixed handling of >= 8GB files in BackupPC_tarExtract and >= 4GB files in BackupPC_tarCreate. * Removed smbclient size repair in BackupPC_tarExtract for files between 2GB and 4GB. This means that BackupPC_tarExtract 2.0.1 doesn't behave the same as 2.0.0 for file sizes between 2GB and 4GB extacted using smbclient 2.2.x. If you have problems backing up files whose size is between 2GB and 4GB using smbclient 2.2.x you should upgrade smbclient to 3.0, since it now generates correct file sizes. * Replace PingClientPath with PingPath in configure.pl. * Removed -T (taint mode) on binaries installed in configure.pl. * Added support for smbclient from samba version 3.0.0. * Fixed $Conf{HardLinkMax} limit check in BackupPC::Lib; reported by Ross Skaliotis. * In BackupPC_Admin, default REMOTE_USER to $Conf{BackupPCUser} if it is not defined. This allows the CGI interface to work when AdminUsers = '*'. Reported by Quentin Arce. * For SMB, code that detected files with a read-locked region (eg: outlook .pst files), removed them and then tried to link with an earlier version was broken. This code missed a step of mangling the file names. This is now fixed. Reported by Pierre Bourgin. * A backup of a share that has zero files is now considered fatal. This is used to catch miscellaneous Xfer errors that result in no files being backed up. A new config parameter $Conf{BackupZeroFilesIsFatal} (defaults to 1) and can be set to zero to turn off this check. Suggested by Guillaume Filion. Additional change: this check only applies to a full dump. * SMB: now detect NT_STATUS_ACCESS_DENIED on entire share or BackupFilesOnly (also ERRDOS - ERRnoaccess (Access denied.) for older versions of smbclient.) Suggested by Guillaume Filion. * SMB: now detects "tree connect failed: NT_STATUS_BAD_NETWORK_NAME" and the dump is considered failed. * Rsync: Previously BackupFilesOnly = '/' did --include '/' --exclude '/*', which just included the '/' directory and nothing below. Now it does just --include '/', which should include everything. Reported by denon. * Add hostError to DumpPostUserCmd variable substitutions for both dump and restore. * Verbose output in Lib.pm goes to STDERR, not STDOUT. This now makes BackupPC_dump -v work better. * Don't allow browsing with ".." in directory in case a user tries to trick BackupPC_Admin into displaying directories outside where they are allowed. * Required File::RsyncP version is now 0.44, since File::RsyncP 0.44 fixes large file (>2GB) bugs. Large file bugs reported by Steve Waltner. #------------------------------------------------------------------------ # Version 2.0.0, 14 Jun 2003 #------------------------------------------------------------------------ * Minor tweaks to disable utf8 on utf8-capable machines (eg: RH8+). Added "no utf8" to all programs, and added binmode() to relevant file handles. #------------------------------------------------------------------------ # Version 2.0.0beta3, 1 Jun 2003 #------------------------------------------------------------------------ * Several improvements to restore: cancel now reports the correct message and cleans up correctly. * Rsync with whitespace and wildcard excludes fixed by replacing argList with argList+ in config.pl plus a fix to Lib.pm for shell escaping array arguments. * Fixed rsync restore for character and block special devices (major and minor device numbers weren't correctly restored). * Fixed typo in bin/BackupPC_restore (XferLOG -> RestoreLOG). * (Re)-fixed "Bad command" in log file when restore via tar or zip file download is done. * Added untaint to exec in Lib.pm to avoid tainted errors. * Applied additional tweak to hilight patch from Tim Demarest. * $Conf{CgiAdminUsers} = '*' now allows privileged even with REMOTE_USER not set. * Don't display RsyncdPasswd when displaying config.pl files. * Replace pipe with socketpair in bin/BackupPC_dump and bin/BackupPC_restore, which increases typical buffering from 4K to 16K-64K. This improves the performance. * Add check on $ENV{LANG} setting do configure.pl: if LANG includes utf then a warning is printed. #------------------------------------------------------------------------ # Version 2.0.0beta2, 11 May 2003 #------------------------------------------------------------------------ * Added German translation, provided by Manfred Herrmann. * Fixed large-file problem with rsync, reported by Manfred Herrmann. * Fixed zip and tar file download from CGI under mod_perl. Reported by Pierre Bourgin and Paul Lukins. * Fixed directory browsing and top-level directory browsing in 2.0.0beta0. Reported by several users. * Added -v option to BackupPC_dump for verbose output (useful when you run the command manually). Added messages for all exits. * If nmblookup returns multiple IP addresses, NetBiosHostIPFind() now returns the first IP address that matches the subnet mask. Suggested by Tim Demarest. * Fixed BackupPC::View so the top-level directory is handled correctly. This allows the top-level share/directory to be restored via the CGI interface. Reported by several users. * Fixed RsyncFileIO failures on certain large files by replacing seek() with sysseek(). Reported by Manfred Herrmann. * Added configurable highlighting of PC status in the CGI summary screen; submitted by Tim Demarest. * Fixed command queue CGI display; submitted by Tim Demarest. * BackupPC_trashClean now logs an error if it can't remove all the trash and then goes back to sleep, rather than continually trying. * Moved correct user (uid) check into BackupPC::Lib so that all applications do a user check if $Cong{BackupPCUserVerify} is set. The avoids the risk of manually running BackupPC_dump as the wrong user. * Loss of blackout now applies to "host not found" as well as no ping. Reported by Dale Renton. * "Host not found" is now treated in a similar manner to "no ping". * Added suse-linux init.d script from Leon Letto. * Added Gentoo linux init.d script from Tim Demarest. * Applied additional i18n strings from GFK and the translation team. * Fixed option parsing so that getopts errors are reported and we exit. * Changed reporting of Xfer PIDs so that rsync cancel works correctly. #------------------------------------------------------------------------ # Version 2.0.0beta1, 30 Mar 2003 #------------------------------------------------------------------------ * Added Spanish translation es.pm from Javier Gonzalez. * Fixed CGI browse navigation bug that causes BackupPC_Admin to wedge when directories were selected in a certain order. * Fixed BackupPC::PoolWrite so that it can recover when the initial file size is wrong. This is needed since rsync could write a file whose size is different from the initial size returned in the file list when that file is updated while rsync is running. * Added binmode(STDIN) to BackupPC_tarExtract, suggested by Pat LoPresti to fix a problem a RedHat8 with perl 5.8.0. It's unclear why this helps, but it should be benign. See: http://sourceforge.net/mailarchive/forum.php?thread_id=1853018&forum_id=503 #------------------------------------------------------------------------ # Version 2.0.0beta0, 23 Feb 2003 #------------------------------------------------------------------------ * Support for rsync and rsyncd backup and restore. Changes to BackupPC_dump, BackupPC_restore, and new modules BackupPC::Xfer::Rsync and BackupPC::Xfer::RsyncFileIO. * Added internationalization (i18n) code from Xavier Nicollet, with additions from Guillaume Filion. Voila! BackupPC_Admin now supports English and French, and adding more languages is now easy. New config paramater $Conf{Language} sets the language. * Added optional user-defined pre/post dump/restore commands, allowing things like database shutdown/startup for dumps. * Changed the way hosts are found. * Added $Conf{ClientNameAlias}, which allows the name of the physical client machine to be set. This allows several different backup "hosts" to all refer to the same physical machine, which is convenient if several different types of data need to be backed up, or if different parameters are needed for different parts of the host. * Replaced $Conf{PingArgs} with $Conf{PingCmd}, added $Conf{DfCmd}, $Conf{NmbLookupCmd} allowing all these commands to be fully configured. Also, all commands can also now be fragments of perl code. * Moved all smbclient commands into the config.pl file so the specific arguments can be customized. New config parameters are $Conf{SmbClientFullCmd}, $Conf{SmbClientIncrCmd} and $Conf{SmbClientRestoreCmd}. * Added new BackupPC::View module that creates views of backups (handling merging etc). Updated BackupPC_Admin, BackupPC_zipCreate and BackupPC_tarCreate to use BackupPC::View. This removes lots of merging and mangling code from the higher-level code. * Added code from Toby Johnson that allows additional users to be specified in the hosts file; these users can also view/start/stop and restore backups for that host. Also added a new config setting $Conf{CgiNavBarAdminAllHosts} that allows all hosts to be listed in the left nav bar for admins. * Added $Conf{HardLinkMax} (default 31999) which sets the limit on the maximum number of hardlinks per file in the pool. If a file ever gets to this number of links a new pool file is created to handle additional links. * Added $Conf{PerlModuleLoad}, which allows optional additional perl modules to be loaded. * Added $Conf{EMailUserDestDomain} and other EMail config settings to allow language-specific default messages to be overridden. * Added BPC_FTYPE_DELETED to lib/BackupPC/Attrib.pm, allowing deleted files to be represented in the attrib file correctly. * Added support for environment variable BPC_SMB_PASSWD, which is the client's smb password. This overrides the old environment variable PASSWD. * Added taint cleanup for perl5.8 to lib/BackupPC/Lib.pm. * Changed $tar_unpack_header format in BackupPC_tarExtract to correctly handle files with trailing spaces. * Added catching of SIG_PIPE to BackupPC_dump, and changed catch_signal to ignore multiple signals of the same type. * Added reporting of the largest number of hardlinks in the pool to the log file. * Adding reporting of syntax errors in the per-PC config.pl file. * Updated BackupPC_sendEmail to handle language-specific email messages. * Allow client (host) names to contain spaces. Spaces in host names need to be escaped via "\" in the hosts file. The user of spaces in host names is discouraged, but they should work. One feature that doesn't work with host names that contain spaces is the highlighting of that name in the log file display in the CGI interface. There are no plans to fix this. * Renamed $Conf{SmbClientTimeout} to $Conf{ClientTimeout}. * Fixed all open() calls to use 3 argument form to fix handling of file names with trailing whitespace. Also fixed CGI interface so these file names are displayed correctly. * Fixed new 2.0.0 CGI navigation bug that causes the top-level directory to have a URL "&share=//boot&dir=" instead of "&share=/boot&dir=/". Reported by Pascal Schelcher. Fixed similar problem reported by Doug Lytle. * Added "PerlTaintCheck On" to the mod_perl section in the docs, suggested by Tim Demarest. #------------------------------------------------------------------------ # Version 1.5.0, 2 Aug 2002 #------------------------------------------------------------------------ * Changed conf/config.pl so that $Conf{TarIncrArgs} uses the --newer option instead of --newer-mtime. Also removed --atime-preserve from $Conf{TarClientCmd}. This makes the default settings work better with tripwire. * Fixed configure.pl so it correctly detects a running BackupPC <= v1.4.0 so it can correctly warn the user to stop it before upgrading. Reported by David Holland. * Added missing ";" to entity escape in EscapeHTML in BackupPC_Admin. Reported by Guillaume Filion. * Added LDAP setup to documentation from David Holland. * Tar.pm now adds a "." to file paths that start with "/", so that all tar paths are relative. From Ludovic Drolez. #------------------------------------------------------------------------ # Version 1.5.0beta0, 30 Jun 2002 #------------------------------------------------------------------------ * A full set of restore options is now supported, including direct restore via smbclient or tar or downloading a zip or tar file. * Major additions to CGI script to support better directory navigation, restore features and mod_perl. Also, file downloads from the CGI interface now correctly preserve the file name and provide the correct Content-Type for the most common types of files. Improved directory navigation was contributed by Ryan Kucera. * New script BackupPC_zipCreate (contributed by Guillaume Filion) is the zip analog of BackupPC_tarCreate. BackupPC_zipCreate can be used to create a zip archive of any portion of a backup. * Substantial additions to BackupPC_tarCreate to support restore, including modifying path names, handling hardlinks, fixing support of old backups without attributes (pre-v1.4.0). Plus BackupPC_tarCreate is now an offical part of the release. (Lack of support for hardlinks was reported by John Stanley.) * BackupPC_tarExtract now supports hardlinks and fixed pooling of attribute files. * A unix domain socket is now used for communication between the CGI interface and BackupPC. The original TCP socket is optional. Sockets are correctly re-initialized if config.pl is updated with new socket settings. * For improved security messages over the unix or TCP socket are protected via an MD5 digest based on a shared secret, a sequence number, a time stamp and a unique per-connection number. * Additions to configure.pl to support install of directory navigation images. * Fixed case where $Conf{BackupFilesOnly} or $Conf{BackupFilesExclude} were set to a single string or list (in v1.4.0 only the case of hash worked correctly). Reported by Phillip Bertolus. * Fixed case of $Conf{BackoutGoodCnt} == 0. This setting now makes the client always subject to blackout, matching the comments in config.pl. Also fixed handling of $Conf{BackoutGoodCnt} < 0 in the CGI script reported by Pascal Schelcher. * Fixed byte and file totals for tar backups, reported by several users. * Fixed --newer-mtime date/timestamp format to make it ISO 8601 compliant, suggested by Erminio Baranzini. * Fixed handling of $Conf{BackupFilesOnly} in BackupPC::Xfer::Tar.pm, as well as shell escaping of tar arguments. * Fixed entity encoding of 8-bit characters in the CGI interface. * Added optional CGI headers in $Conf{CgiHeaders} that by default is set to a no-cache pragma. Suggested by Benno Zuure. #------------------------------------------------------------------------ # Version 1.4.0, 16 Mar 2002 #------------------------------------------------------------------------ * BackupPC now supports tar (in addition to smb) for extracting host data. This is the most convenient option for linux/unix hosts. Tar can be configured to run over ssh, rsh or to backup a local nfs mount from the host. * Support for special files, including symbolic links, fifo, character and block device files has been added, so that all native linux/unix file types can be correctly backed up when using tar transport. Special files are all stored as regular files and the type attributes are used to remember the original file type. * All unix file attributes are now saved (and pooled when possible). This includes user and group ownership, permissions, and modification time. Smbclient also does a reasonable job of emulating unix permissions (such as mtime), and these attributes get saved too. * The new default is to not fill incremental dumps. configure.pl automatically sets $Conf{IncrFill} to 0. The default was 1 (incrementals were filled with hardlinks). Since the CGI script does filling at browsing time, there is no need to fill incremental dumps. * Backup file names are now stored in "mangled" form. Each node of a path is preceded by "f", and special characters (\n, \r, % and /) are URI-encoded as "%xx", where xx is the ascii character's hex value. So c:/craig/example.txt is now stored as fc/fcraig/fexample.txt. This was done mainly so meta-data could be stored alongside the backup files without name collisions. In particular, the attributes for the files in a directory are stored in a file called "attrib", and mangling avoids file name collisions (I discarded the idea of having a duplicate directory tree for every backup just to store the attributes). Other meta-data (eg: rsync checksums) could be stored in file names preceded by, eg, "c". There are two other benefits to mangling: the share name might contain "/" (eg: "/home/craig" for tar transport), and I wanted that represented as a single level in the storage tree. Secondly, as files are written to NewFileList for later processing by BackupPC_link, embedded newlines in the file's path will cause problems which are avoided by mangling. The CGI script undoes the mangling, so it is invisible to the user. Of course, old (unmangled) backups are still supported by the CGI interface. * Various changes to the CGI interface, BackupPC_Admin: + Added button that allows users to manually start a full dump in addition to the existing incremental dump. + Added display of file attributes when browsing backups. + Added an optional holdoff time specified by the user when canceling a backup. BackupPC will not attempt any new backups for at least the specified time. This holdoff time can be changed whether or not a backup is running. + Added supports for file mangling, and correct merging of unfilled backups from mangled or unmangled (and compressed or uncompressed) fulls when browsing or restoring. + Only displays a "Start Incr Backup" button if there are already some backups. + For DHCP hosts, when a user tries to manually start a backup, add a check for the netbios name of both the host the request came from (REMOTE_ADDR) and the last known DHCP address for that host to see if either address matches the host. If not, an error message is display. The previous behavior was that only requests from the client itself succeeded, and requests from other machines quietly failed. * Changed the version numbering to X.Y.Z, instead of X.0Y. This release is 1.4.0. The first digit is for major new releases, the middle digit is for significant feature releases and improvements, and the last digit is for bug fixes. You should think of the old 1.00, 1.01, 1.02 and 1.03 as 1.0.0, ..., 1.3.0. * BackupPC and the CGI script BackupPC_Admin now check that the effective user id is correct to avoid accidentally launching BackupPC as the wrong user or detecting CGI configuration problems. This behavior can be turned off using the $Conf{BackupPCUserVerify} option. * In numerous places changed "Smb" to "Xfer" (eg: log file names) to support generic names for both smb and tar transport methods. The CGI script checks for old names for backward compatibility. * Major changed to Backup_dump to support new tar transport. All transport specific code moved into BackupPC::Xfer::Smb and BackupPC::Xfer::Tar objects. * Added workaround for a bug in Samba's smbclient for files between 2GB and 4GB. The file size in the tar header is incorrect. This allows files up to 4GB to work with smbclient, rather than 2GB. To support files larger than 2GB you must make sure perl is compiled with the uselargefiles option (use "perl -V | egrep largefiles" to check) and the pool directory must be on a file system that supports large files. * Moved the pool writing code into a module BackupPC::PoolWrite. This allows the clever file pool checking (digest, uncompressing, comparing etc with minimum disk IO) to be used easily in multiple places (eg: it is now used for writing attribute files so they can be pooled). * Changed MD5 to Digest::MD5 to avoid use of the depreceated MD5 module. * Shortened default $Conf{MyPath} so that perl's taint mode is more likely to be happy. The old $Conf{MyPath} contained /usr/local/bin, which on one user's machine was world writable and perl -T correctly complained about it. * Fixed ping command options in Lib.pm so that it works on OpenBSD. Thanks to Kyle Amon for sending the fix. Decided to move the ping options from Lib.pm into config.pl (as $Conf{PingArgs}) and now configure.pl tries to come up with a sensible default based on the OS. * Fixed argument checking in BackupPC_tarExtract to allow '$' in the share name (eg: C$). Thanks to Jules Agee for this fix. Also changed the default config.pl so that single quotes are used everywhere so that people don't get tripped up putting '$' inside double-quoted strings. #------------------------------------------------------------------------ # Version 1.03, 9 Dec 2001 #------------------------------------------------------------------------ * BackupPC now has full support for compression. There are now two pool areas, the original pool for uncompressed files, and cpool for compressed files. The compression is done by Compress::Zlib. Compression reduces the pool disk usage by around 40%, although your mileage may vary. Compression is optional and can also be specified on a per-PC basis (although this will cost more pool storage since many backup files will have to be stored in both compressed and uncompressed forms. * A new script, BackupPC_compressPool, can be run to compress the entire pool. This is used once to migrate all the pool data from uncompressed to compressed on existing installations. Read the documentation (Installing BackupPC/Compressing an existing pool) before running BackupPC_compressPool! Alternatively, compression can simply be turned on and all new backups will be compressed. Both old (uncompressed) and new (compressed) backups can be browsed and viewed. Eventually, the old backups will expire and all the pool data will be compressed. However, until the old backups expire, this approach could require 60% or more additional pool storage space to store both uncompressed and compressed versions of the backup files. * Significant improvements to the cgi interface, BackupPC_Admin: - much better layout navigation - handles compressed backup files and compressed log files - handles unfilled incremental dumps - better backup directory browsing navigation - reports compression statistics - $Conf{CgiDateFormatMMDD} allows you to set date format (MM/DD or DD/MM) - Additional customization with $Conf{CgiHeaderFontType}, $Conf{CgiHeaderFontSize}, $Conf{CgiNavBarBgColor}, and $Conf{CgiHeaderBgColor}. * Eliminated BackupPC_queueAll. BackupPC directly reads the hosts file and queues the PCs itself. Like config.pl, BackupPC will re-read the hosts file on each wakeup if its modification time changes, or upon a SIGHUP. This also makes for better behavior when adding a host: if you add hosts, simply send a SIGHUP to BackupPC or wait for the next wakeup. * BackupPC_dump now compresses the SmbLOG file if compression is enabled. * BackupPC_dump keeps track of compressed file sizes so that compression statistics can be reported by the cgi interface. * Aging of old log files now handles compressed log files (.z extension). * Added configuration option $Conf{IncrFill} to specify whether incremental dumps should be filled in. Old behavior was that filling was on. Now it's optional. See config.pl for more details. * BackupPC_nightly now cleans and generates statistics for both the uncompressed pool and compressed pool (cpool). * Added new utility script BackupPC_zcat that can be used to uncompresses BackupPC files. * configure.pl offers various options related to compression, depending upon whether this is a new install or upgrade, and whether or not Compress::Zlib is installed. * configure.pl now makes a backup copy of config.pl before config.pl is updated. * added three new fields to the backups file to handle optional filling and compression stats. * Added -e option to BackupPC_dump. BackupPC now invokes BackupPC_dump -e on each dhcp host once each night to verify that very old backups are expired. This ensures that very old backups are expired even if the dhcp host has not been on the network for a long time. * fixed bug in BackupPC::FileZIO.pm that required Compress::Zlib, even if compression was off. Thanks to Steve Holmes for reporting this. * fixed bug that caused a BackupPC queue to get blocked when a backup cancel attempt was made during the BackupPC_link phase. #------------------------------------------------------------------------ # Version 1.02, 28 Oct 2001. #------------------------------------------------------------------------ * Added new script BackupPC_tarExtract to extract the smbclient tar archive. This reduces disk writes by perhaps 90-95% and disk reads by 50%. Previously, tar was used to extract and write everything to disk. Then BackupPC_dump would read enough of each file to compute the MD5 digest, and then compare the full file with candidate pool files. So for each 1MB file that matches a single file in the pool, there would be 1MB of disk writes and 2MB of disk reads (to compare two 1MB files). BackupPC_tarExtract instead extracts the archive using a 1MB memory buffer. This allows the MD5 digest to be computed without touching the disk. Next, any potential pool file compares are done by comparing the pool file against the incoming tar data in memory, which only requires the pool file to be read. So for each 1MB file that matches a single file in the pool, there are now no disk writes, and only 1MB of reads. BackupPC_tarExtract handles arbitrary size files and repeated potential pool matches. If the incoming file doesn't match the pool then it is written to disk (once the pool is mature this happens maybe 5-10% of the time). * Substantial changes to BackupPC_dump: + BackupPC_tarExtract is now used in place of tar. + BackupPC_dump now reads the output from both smbclient and BackupPC_tarExtract and merges them into SmbLOG. + Named pipes are no longer used to connect smbclient to tar (now BackupPC_tarExtract). Regular pipes are used instead. This avoids the need to system mknod or mkfifo. + Locked files on the client that can't be read by smbclient previously were filled with 0x0 bytes by smbclient, meaning tar extracted a useless file filled with 0x0 bytes. Now, BackupPC_dump watches the output of smbclient and removes any files that smbclient couldn't read. This avoids storing useless files. It tries to replace such files with a hard link to a previous dump. These actions appear in the log file. * added new module lib/BackupPC/FileZIO.pm. This handles pool file I/O and is used by BackupPC_tarExtract. BackupPC::FileIO supports reading and writing compressed and regular files and provides all the hooks for compression support in BackupPC (should be supported in next version). BackupPC::FileIO also does efficient writing of files that contain leading 0x0 bytes (by seeking past the 0x0 bytes). This is helpful when smbclient reads a locked file, and it fills the tar output with a file of the correct size but all 0x0. Such files will be later removed by BackupPC_dump. But in the meantime, BackupPC::FileIO writes such files efficiently (as sparse files), meaning just a few blocks of disk space will be needed even if the file is large. * alive/dead counting for blackout now works correctly for DHCP hosts. * BackupPC resets activeJob on startup, to fix bug when BackupPC was killed and restarted with backups running. * added extra non blocking select() in BackupPC to make sure the socket reads don't block. * BackupPC avoids queuing multiple BackupPC_queueAll's on the CmdQueue. * Updated BackupPC_sendEmail to correctly parse the locked file error from 2.2.1a smbclient, so that missing Outlook file emails can be correctly sent. * Changed HostInfoRead() in lib/BackupPC/Lib.pm to lowercase the hostname read from the hosts file. * BackupPC_Admin provides general summary when the host name is empty. * configure.pl (and BackupPC) now requires perl 5.6.0 or later. * configure.pl complains if BackupPC is already running, reminding you to stop it before upgrading. * updated documentation, and fixed auto-insertion of config.pl into BackupPC.pod (previously the last config parameter was left out of BackupPC.pod). #------------------------------------------------------------------------ # Version 1.01, 30 Sep 2001 #------------------------------------------------------------------------ * Documentation cleanup in README, doc/BackupPC.pod, conf/config.pl. * BackupPC_sendMail now reads the optional per-PC config file, allowing email configuration parameters to be set on a per-PC basis. * Removed the unused 4096-length MD5 digest code in lib/BackupPC/Lib.pm. #------------------------------------------------------------------------ # Version 1.00, 21 Sep 2001 #------------------------------------------------------------------------ * Initial release of BackupPC on sourceforge.net. BackupPC-3.3.2/conf/0000755000076500000240000000000013042250554013044 5ustar craigstaffBackupPC-3.3.2/conf/BackupPC_stnd.css0000444000076500000240000000766513042250554016252 0ustar craigstaff/* * BackupPC standard CSS definitions * * Version 3.3.2, released 25 Jan 2017. * * See http://backuppc.sourceforge.net. * * AUTHOR * Craig Barratt * * COPYRIGHT * Copyright (C) 2004-2017 Craig Barratt */ body { font-family:tahoma,arial,sans-serif,helvetica; font-size:10pt; background-color:#ffffff; margin:2px 5px 0px 2px; height:100% } table { border-collapse: collapse; color: #000000; padding: 2px; } h1 { font-family:Trebuchet MS,Trebuchet,tahoma,arial,sans-serif,helvetica; font-size:16pt; color:#000000 } h2 { font-family:Trebuchet MS,Trebuchet,tahoma,arial,sans-serif,helvetica; font-size:12pt; color:#000000 } a { font-family:tahoma,arial,sans-serif,helvetica; color:#3333ff } dt { color:#3333ff } a:hover { color:#cc0000; text-decoration:none } a.NavCurrent { font-weight:bold; } a.navbar { padding-left:5px; padding-right:5px; } .h1 { font-family:Trebuchet MS,Trebuchet,tahoma,arial,sans-serif,helvetica; font-size:16pt; color:#000000; font-weight:bold; background-color:#95B8DB; padding:3px; padding-left:6px; margin-bottom:5px; } .h2 { font-family:Trebuchet MS,Trebuchet,tahoma,arial,sans-serif,helvetica; font-size:12pt; color:#000000; font-weight:bold; background-color:#eeeeee; padding:3px; padding-left:6px; margin-top:3px; margin-bottom:1px; } .tableStnd { font-size:10pt; } .tableheader { font-size:10pt; font-weight:bold; background-color:#eeeeee; } table.sortable a.sortheader { background-color:#eeeeee; font-weight: bold; text-decoration: none; display: block; } .border { font-size:10pt; } .editHeader { font-family:arial,sans-serif; font-size:12pt; color:#000000; font-weight:bold; background-color:#95B8DB; } .editTabSel { font-family:arial,sans-serif; font-size:14pt; color:#000000; font-weight:bold; background-color:#95B8DB; padding:3px; padding-left:6px; margin-bottom:5px; } .editTabNoSel { font-family:arial,sans-serif; font-size:14pt; color:#000000; font-weight:bold; padding:3px; padding-left:6px; margin-bottom:5px; } .editSaveButton { color:#c0c0c0; font-size:14pt; font-weight:bold; } .editError { color:#ff0000; font-weight:bold; } .editComment { font-size:10pt; } .editTextInput { font-family:courier; font-size:10pt; } .fviewheader { font-weight:bold; font-size:9pt; color:#ffffff; background-color:#999999; } .fviewborder { border-left:1px solid #666666; border-bottom:1px solid #000000; border-right:1px solid #666666; background-color:#eeeeee; font-size:9pt; } .fviewon { background-color:#cccccc; } .fviewoff { background-color:#ffffff; } .fview { font-size:9pt; font-family:tahoma,arial,sans-serif,helvetica; text-decoration:none; line-height:15px; } .fviewbold { font-size:9pt; font-family:tahoma,arial,sans-serif,helvetica; text-decoration:none; line-height:15px; font-weight:bold; } .histView { border-bottom:1px solid #000000; border-left:1px solid #000000; background-color:#eeeeee; font-size:10pt; } .histViewMis { border-bottom:1px solid #000000; border-left:1px solid #000000; background-color:#ffdddd; } div.NavMenu { width:18%; margin:0px; font-size:9pt; background-color:#eeeeee; } div.NavMenu a { font-size:9pt; font-family:arial,sans-serif; display:block; margin-left:8px; padding:2px; } div.NavTitle { font-size:12pt; padding-left:10px; background-color:#95B8DB; font-family:Trebuchet MS,Trebuchet,tahoma,arial,sans-serif,helvetica; color:#000000; font-weight:bold; margin-bottom:2px; } #Content { float:right; width:79%; left:20%; top:10px; right:10px; position:absolute; } BackupPC-3.3.2/conf/BackupPC_stnd_orig.css0000444000076500000240000000644313042250554017263 0ustar craigstaff/* * BackupPC standard CSS definitions * * Version 3.3.2, released 25 Jan 2017. * * See http://backuppc.sourceforge.net. * * AUTHOR * Craig Barratt * * COPYRIGHT * Copyright (C) 2004-2017 Craig Barratt */ body { font-family:arial,sans-serif; font-size:11pt; background-color:#ffffff; margin:2px 5px 0px 2px; height:100% } h1 { font-family:arial,sans-serif; font-size:16pt; color:#000000 } h2 { font-family:arial,sans-serif; font-size:12pt; color:#000000 } a { font-family:arial,sans-serif; color:#3333ff } dt { color:#3333ff } a:hover { color:#cc0000; text-decoration:none } a.NavCurrent { font-weight:bold; } a.navbar { padding-left:5px; padding-right:5px; } .h1 { font-family:arial,sans-serif; font-size:16pt; color:#000000; font-weight:bold; background-color:#99cc33; padding:3px; padding-left:6px; margin-bottom:5px; } .h2 { font-family:arial,sans-serif; font-size:12pt; color:#000000; font-weight:bold; background-color:#ddeeee; padding:3px; padding-left:6px; margin-top:3px; margin-bottom:1px; } .tableStnd { font-size:11pt; } .tableheader { font-size:9pt; font-weight:bold; background-color:#cccccc; } .border { font-size:10pt; } .editHeader { font-family:arial,sans-serif; font-size:12pt; color:#000000; font-weight:bold; background-color:#99cc33; } .editTabSel { font-family:arial,sans-serif; font-size:14pt; color:#000000; font-weight:bold; background-color:#99cc33; padding:3px; padding-left:6px; margin-bottom:5px; } .editTabNoSel { font-family:arial,sans-serif; font-size:14pt; color:#000000; font-weight:bold; padding:3px; padding-left:6px; margin-bottom:5px; } .editSaveButton { color:#ff0000; font-size:14pt; font-weight:bold; } .editError { color:#ff0000; font-weight:bold; } .editComment { font-size:10pt; } .editTextInput { font-family:courier; } .fviewheader { font-weight:bold; font-size:10pt; color:#ffffff; background-color:#999999; } .fviewborder { border-bottom:1px solid #000000; border-left:1px dotted #666666; background-color:#dddddd; font-size:11pt; } .fviewon { background-color:#cccccc; } .fviewoff { background-color:#ffffff; } .fview { font-size:9pt; font-family:arial,sans-serif; text-decoration:none; line-height:15px; } .fviewbold { font-size:10pt; font-family:arial,sans-serif; text-decoration:none; line-height:15px; font-weight:bold; } .histView { border-bottom:1px solid #000000; border-left:2px solid #ffffff; background-color:#dddddd; font-size:10pt; } .histViewMis { border-bottom:1px solid #000000; background-color:#ffdddd; } div.NavMenu { width:18%; margin:0px; background-color:#ddeeee; } div.NavMenu a { font-size:10pt; display:block; margin-left:8px; padding:2px; } div.NavTitle { padding-left:10px; background-color:#99cc33; font-family:arial,sans-serif; color:#000000; font-weight:bold; margin-bottom:2px; } #Content { float:right; width:80%; left:20%; top:10px; position:absolute; } BackupPC-3.3.2/conf/config.pl0000444000076500000240000024643213042250554014657 0ustar craigstaff#============================================================= -*-perl-*- # # Configuration file for BackupPC. # # DESCRIPTION # # This is the main configuration file for BackupPC. # # This file must be valid perl source, so make sure the punctuation, # quotes, and other syntax are valid. # # This file is read by BackupPC at startup, when a HUP (-1) signal # is sent to BackupPC and also at each wakeup time whenever the # modification time of this file changes. # # The configuration parameters are divided into four general groups. # The first group (general server configuration) provides general # configuration for BackupPC. The next two groups describe what # to backup, when to do it, and how long to keep it. The fourth # group are settings for the CGI http interface. # # Configuration settings can also be specified on a per-PC basis. # Simply put the relevant settings in a config.pl file in the # PC's backup directory (ie: in __TOPDIR__/pc/hostName). # All configuration settings in the second, third and fourth # groups can be overridden by the per-PC config.pl file. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # See http://backuppc.sourceforge.net. # #======================================================================== ########################################################################### # General server configuration ########################################################################### # # Host name on which the BackupPC server is running. # $Conf{ServerHost} = ''; # # TCP port number on which the BackupPC server listens for and accepts # connections. Normally this should be disabled (set to -1). The TCP # port is only needed if apache runs on a different machine from BackupPC. # In that case, set this to any spare port number over 1024 (eg: 2359). # If you enable the TCP port, make sure you set $Conf{ServerMesgSecret} # too! # $Conf{ServerPort} = -1; # # Shared secret to make the TCP port secure. Set this to a hard to guess # string if you enable the TCP port (ie: $Conf{ServerPort} > 0). # # To avoid possible attacks via the TCP socket interface, every client # message is protected by an MD5 digest. The MD5 digest includes four # items: # - a seed that is sent to the client when the connection opens # - a sequence number that increments for each message # - a shared secret that is stored in $Conf{ServerMesgSecret} # - the message itself. # # The message is sent in plain text preceded by the MD5 digest. A # snooper can see the plain-text seed sent by BackupPC and plain-text # message from the client, but cannot construct a valid MD5 digest since # the secret $Conf{ServerMesgSecret} is unknown. A replay attack is # not possible since the seed changes on a per-connection and # per-message basis. # $Conf{ServerMesgSecret} = ''; # # PATH setting for BackupPC. An explicit value is necessary # for taint mode. Value shouldn't matter too much since # all execs use explicit paths. However, taint mode in perl # will complain if this directory is world writable. # $Conf{MyPath} = '/bin'; # # Permission mask for directories and files created by BackupPC. # Default value prevents any access from group other, and prevents # group write. # $Conf{UmaskMode} = 027; # # Times at which we wake up, check all the PCs, and schedule necessary # backups. Times are measured in hours since midnight. Can be # fractional if necessary (eg: 4.25 means 4:15am). # # If the hosts you are backing up are always connected to the network # you might have only one or two wakeups each night. This will keep # the backup activity after hours. On the other hand, if you are backing # up laptops that are only intermittently connected to the network you # will want to have frequent wakeups (eg: hourly) to maximize the chance # that each laptop is backed up. # # Examples: # $Conf{WakeupSchedule} = [22.5]; # once per day at 10:30 pm. # $Conf{WakeupSchedule} = [2,4,6,8,10,12,14,16,18,20,22]; # every 2 hours # # The default value is every hour except midnight. # # The first entry of $Conf{WakeupSchedule} is when BackupPC_nightly is run. # You might want to re-arrange the entries in $Conf{WakeupSchedule} # (they don't have to be ascending) so that the first entry is when # you want BackupPC_nightly to run (eg: when you don't expect a lot # of regular backups to run). # $Conf{WakeupSchedule} = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]; # # Maximum number of simultaneous backups to run. If there # are no user backup requests then this is the maximum number # of simultaneous backups. # $Conf{MaxBackups} = 4; # # Additional number of simultaneous backups that users can run. # As many as $Conf{MaxBackups} + $Conf{MaxUserBackups} requests can # run at the same time. # $Conf{MaxUserBackups} = 4; # # Maximum number of pending link commands. New backups will only be # started if there are no more than $Conf{MaxPendingCmds} plus # $Conf{MaxBackups} number of pending link commands, plus running jobs. # This limit is to make sure BackupPC doesn't fall too far behind in # running BackupPC_link commands. # $Conf{MaxPendingCmds} = 15; # # Nice level at which CmdQueue commands (eg: BackupPC_link and # BackupPC_nightly) are run at. # $Conf{CmdQueueNice} = 10; # # How many BackupPC_nightly processes to run in parallel. # # Each night, at the first wakeup listed in $Conf{WakeupSchedule}, # BackupPC_nightly is run. Its job is to remove unneeded files # in the pool, ie: files that only have one link. To avoid race # conditions, BackupPC_nightly and BackupPC_link cannot run at # the same time. Starting in v3.0.0, BackupPC_nightly can run # concurrently with backups (BackupPC_dump). # # So to reduce the elapsed time, you might want to increase this # setting to run several BackupPC_nightly processes in parallel # (eg: 4, or even 8). # $Conf{MaxBackupPCNightlyJobs} = 2; # # How many days (runs) it takes BackupPC_nightly to traverse the # entire pool. Normally this is 1, which means every night it runs, # it does traverse the entire pool removing unused pool files. # # Other valid values are 2, 4, 8, 16. This causes BackupPC_nightly to # traverse 1/2, 1/4, 1/8 or 1/16th of the pool each night, meaning it # takes 2, 4, 8 or 16 days to completely traverse the pool. The # advantage is that each night the running time of BackupPC_nightly # is reduced roughly in proportion, since the total job is split # over multiple days. The disadvantage is that unused pool files # take longer to get deleted, which will slightly increase disk # usage. # # Note that even when $Conf{BackupPCNightlyPeriod} > 1, BackupPC_nightly # still runs every night. It just does less work each time it runs. # # Examples: # # $Conf{BackupPCNightlyPeriod} = 1; # entire pool is checked every night # # $Conf{BackupPCNightlyPeriod} = 2; # two days to complete pool check # # (different half each night) # # $Conf{BackupPCNightlyPeriod} = 4; # four days to complete pool check # # (different quarter each night) # $Conf{BackupPCNightlyPeriod} = 1; # # Maximum number of log files we keep around in log directory. # These files are aged nightly. A setting of 14 means the log # directory will contain about 2 weeks of old log files, in # particular at most the files LOG, LOG.0, LOG.1, ... LOG.13 # (except today's LOG, these files will have a .z extension if # compression is on). # # If you decrease this number after BackupPC has been running for a # while you will have to manually remove the older log files. # $Conf{MaxOldLogFiles} = 14; # # Full path to the df command. Security caution: normal users # should not allowed to write to this file or directory. # $Conf{DfPath} = ''; # # Command to run df. The following variables are substituted at run-time: # # $dfPath path to df ($Conf{DfPath}) # $topDir top-level BackupPC data directory # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{DfCmd} = '$dfPath $topDir'; # # Full path to various commands for archiving # $Conf{SplitPath} = ''; $Conf{ParPath} = ''; $Conf{CatPath} = ''; $Conf{GzipPath} = ''; $Conf{Bzip2Path} = ''; # # Maximum threshold for disk utilization on the __TOPDIR__ filesystem. # If the output from $Conf{DfPath} reports a percentage larger than # this number then no new regularly scheduled backups will be run. # However, user requested backups (which are usually incremental and # tend to be small) are still performed, independent of disk usage. # Also, currently running backups will not be terminated when the disk # usage exceeds this number. # $Conf{DfMaxUsagePct} = 95; # # How long BackupPC_trashClean sleeps in seconds between each check # of the trash directory. Once every 5 minutes should be reasonable. # $Conf{TrashCleanSleepSec} = 300; # # List of DHCP address ranges we search looking for PCs to backup. # This is an array of hashes for each class C address range. # This is only needed if hosts in the conf/hosts file have the # dhcp flag set. # # Examples: # # to specify 192.10.10.20 to 192.10.10.250 as the DHCP address pool # $Conf{DHCPAddressRanges} = [ # { # ipAddrBase => '192.10.10', # first => 20, # last => 250, # }, # ]; # # to specify two pools (192.10.10.20-250 and 192.10.11.10-50) # $Conf{DHCPAddressRanges} = [ # { # ipAddrBase => '192.10.10', # first => 20, # last => 250, # }, # { # ipAddrBase => '192.10.11', # first => 10, # last => 50, # }, # ]; # $Conf{DHCPAddressRanges} = []; # # The BackupPC user. # $Conf{BackupPCUser} = ''; # # Important installation directories: # # TopDir - where all the backup data is stored # ConfDir - where the main config and hosts files resides # LogDir - where log files and other transient information # InstallDir - where the bin, lib and doc installation dirs reside. # Note: you cannot change this value since all the # perl scripts include this path. You must reinstall # with configure.pl to change InstallDir. # CgiDir - Apache CGI directory for BackupPC_Admin # # Note: it is STRONGLY recommended that you don't change the # values here. These are set at installation time and are here # for reference and are used during upgrades. # # Instead of changing TopDir here it is recommended that you use # a symbolic link to the new location, or mount the new BackupPC # store at the existing $Conf{TopDir} setting. # $Conf{TopDir} = ''; $Conf{ConfDir} = ''; $Conf{LogDir} = ''; $Conf{InstallDir} = ''; $Conf{CgiDir} = ''; # # Whether BackupPC and the CGI script BackupPC_Admin verify that they # are really running as user $Conf{BackupPCUser}. If this flag is set # and the effective user id (euid) differs from $Conf{BackupPCUser} # then both scripts exit with an error. This catches cases where # BackupPC might be accidently started as root or the wrong user, # or if the CGI script is not installed correctly. # $Conf{BackupPCUserVerify} = 1; # # Maximum number of hardlinks supported by the $TopDir file system # that BackupPC uses. Most linux or unix file systems should support # at least 32000 hardlinks per file, or 64000 in other cases. If a pool # file already has this number of hardlinks, a new pool file is created # so that new hardlinks can be accommodated. This limit will only # be hit if an identical file appears at least this number of times # across all the backups. # $Conf{HardLinkMax} = 31999; # # Advanced option for asking BackupPC to load additional perl modules. # Can be a list (array ref) of module names to load at startup. # $Conf{PerlModuleLoad} = undef; # # Path to init.d script and command to use that script to start the # server from the CGI interface. The following variables are substituted # at run-time: # # $sshPath path to ssh ($Conf{SshPath}) # $serverHost same as $Conf{ServerHost} # $serverInitdPath path to init.d script ($Conf{ServerInitdPath}) # # Example: # # $Conf{ServerInitdPath} = '/etc/init.d/backuppc'; # $Conf{ServerInitdStartCmd} = '$sshPath -q -x -l root $serverHost' # . ' $serverInitdPath start' # . ' < /dev/null >& /dev/null'; # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{ServerInitdPath} = ''; $Conf{ServerInitdStartCmd} = ''; ########################################################################### # What to backup and when to do it # (can be overridden in the per-PC config.pl) ########################################################################### # # Minimum period in days between full backups. A full dump will only be # done if at least this much time has elapsed since the last full dump, # and at least $Conf{IncrPeriod} days has elapsed since the last # successful dump. # # Typically this is set slightly less than an integer number of days. The # time taken for the backup, plus the granularity of $Conf{WakeupSchedule} # will make the actual backup interval a bit longer. # $Conf{FullPeriod} = 6.97; # # Minimum period in days between incremental backups (a user requested # incremental backup will be done anytime on demand). # # Typically this is set slightly less than an integer number of days. The # time taken for the backup, plus the granularity of $Conf{WakeupSchedule} # will make the actual backup interval a bit longer. # $Conf{IncrPeriod} = 0.97; # # Number of full backups to keep. Must be >= 1. # # In the steady state, each time a full backup completes successfully # the oldest one is removed. If this number is decreased, the # extra old backups will be removed. # # If filling of incremental dumps is off the oldest backup always # has to be a full (ie: filled) dump. This might mean one or two # extra full dumps are kept until the oldest incremental backups expire. # # Exponential backup expiry is also supported. This allows you to specify: # # - num fulls to keep at intervals of 1 * $Conf{FullPeriod}, followed by # - num fulls to keep at intervals of 2 * $Conf{FullPeriod}, # - num fulls to keep at intervals of 4 * $Conf{FullPeriod}, # - num fulls to keep at intervals of 8 * $Conf{FullPeriod}, # - num fulls to keep at intervals of 16 * $Conf{FullPeriod}, # # and so on. This works by deleting every other full as each expiry # boundary is crossed. # # Exponential expiry is specified using an array for $Conf{FullKeepCnt}: # # $Conf{FullKeepCnt} = [4, 2, 3]; # # Entry #n specifies how many fulls to keep at an interval of # 2^n * $Conf{FullPeriod} (ie: 1, 2, 4, 8, 16, 32, ...). # # The example above specifies keeping 4 of the most recent full backups # (1 week interval) two full backups at 2 week intervals, and 3 full # backups at 4 week intervals, eg: # # full 0 19 weeks old \ # full 1 15 weeks old >--- 3 backups at 4 * $Conf{FullPeriod} # full 2 11 weeks old / # full 3 7 weeks old \____ 2 backups at 2 * $Conf{FullPeriod} # full 4 5 weeks old / # full 5 3 weeks old \ # full 6 2 weeks old \___ 4 backups at 1 * $Conf{FullPeriod} # full 7 1 week old / # full 8 current / # # On a given week the spacing might be less than shown as each backup # ages through each expiry period. For example, one week later, a # new full is completed and the oldest is deleted, giving: # # full 0 16 weeks old \ # full 1 12 weeks old >--- 3 backups at 4 * $Conf{FullPeriod} # full 2 8 weeks old / # full 3 6 weeks old \____ 2 backups at 2 * $Conf{FullPeriod} # full 4 4 weeks old / # full 5 3 weeks old \ # full 6 2 weeks old \___ 4 backups at 1 * $Conf{FullPeriod} # full 7 1 week old / # full 8 current / # # You can specify 0 as a count (except in the first entry), and the # array can be as long as you wish. For example: # # $Conf{FullKeepCnt} = [4, 0, 4, 0, 0, 2]; # # This will keep 10 full dumps, 4 most recent at 1 * $Conf{FullPeriod}, # followed by 4 at an interval of 4 * $Conf{FullPeriod} (approx 1 month # apart), and then 2 at an interval of 32 * $Conf{FullPeriod} (approx # 7-8 months apart). # # Example: these two settings are equivalent and both keep just # the four most recent full dumps: # # $Conf{FullKeepCnt} = 4; # $Conf{FullKeepCnt} = [4]; # $Conf{FullKeepCnt} = 1; # # Very old full backups are removed after $Conf{FullAgeMax} days. However, # we keep at least $Conf{FullKeepCntMin} full backups no matter how old # they are. # # Note that $Conf{FullAgeMax} will be increased to $Conf{FullKeepCnt} # times $Conf{FullPeriod} if $Conf{FullKeepCnt} specifies enough # full backups to exceed $Conf{FullAgeMax}. # $Conf{FullKeepCntMin} = 1; $Conf{FullAgeMax} = 90; # # Number of incremental backups to keep. Must be >= 1. # # In the steady state, each time an incr backup completes successfully # the oldest one is removed. If this number is decreased, the # extra old backups will be removed. # $Conf{IncrKeepCnt} = 6; # # Very old incremental backups are removed after $Conf{IncrAgeMax} days. # However, we keep at least $Conf{IncrKeepCntMin} incremental backups no # matter how old they are. # $Conf{IncrKeepCntMin} = 1; $Conf{IncrAgeMax} = 30; # # Level of each incremental. "Level" follows the terminology # of dump(1). A full backup has level 0. A new incremental # of level N will backup all files that have changed since # the most recent backup of a lower level. # # The entries of $Conf{IncrLevels} apply in order to each # incremental after each full backup. It wraps around until # the next full backup. For example, these two settings # have the same effect: # # $Conf{IncrLevels} = [1, 2, 3]; # $Conf{IncrLevels} = [1, 2, 3, 1, 2, 3]; # # This means the 1st and 4th incrementals (level 1) go all # the way back to the full. The 2nd and 3rd (and 5th and # 6th) backups just go back to the immediate preceeding # incremental. # # Specifying a sequence of multi-level incrementals will # usually mean more than $Conf{IncrKeepCnt} incrementals will # need to be kept, since lower level incrementals are needed # to merge a complete view of a backup. For example, with # # $Conf{FullPeriod} = 7; # $Conf{IncrPeriod} = 1; # $Conf{IncrKeepCnt} = 6; # $Conf{IncrLevels} = [1, 2, 3, 4, 5, 6]; # # there will be up to 11 incrementals in this case: # # backup #0 (full, level 0, oldest) # backup #1 (incr, level 1) # backup #2 (incr, level 2) # backup #3 (incr, level 3) # backup #4 (incr, level 4) # backup #5 (incr, level 5) # backup #6 (incr, level 6) # backup #7 (full, level 0) # backup #8 (incr, level 1) # backup #9 (incr, level 2) # backup #10 (incr, level 3) # backup #11 (incr, level 4) # backup #12 (incr, level 5, newest) # # Backup #1 (the oldest level 1 incremental) can't be deleted # since backups 2..6 depend on it. Those 6 incrementals can't # all be deleted since that would only leave 5 (#8..12). # When the next incremental happens (level 6), the complete # set of 6 older incrementals (#1..6) will be deleted, since # that maintains the required number ($Conf{IncrKeepCnt}) # of incrementals. This situation is reduced if you set # shorter chains of multi-level incrementals, eg: # # $Conf{IncrLevels} = [1, 2, 3]; # # would only have up to 2 extra incremenals before all 3 # are deleted. # # BackupPC as usual merges the full and the sequence # of incrementals together so each incremental can be # browsed and restored as though it is a complete backup. # If you specify a long chain of incrementals then more # backups need to be merged when browsing, restoring, # or getting the starting point for rsync backups. # In the example above (levels 1..6), browing backup # #6 requires 7 different backups (#0..6) to be merged. # # Because of this merging and the additional incrementals # that need to be kept, it is recommended that some # level 1 incrementals be included in $Conf{IncrLevels}. # # Prior to version 3.0 incrementals were always level 1, # meaning each incremental backed up all the files that # changed since the last full. # $Conf{IncrLevels} = [1]; # # Disable all full and incremental backups. These settings are # useful for a client that is no longer being backed up # (eg: a retired machine), but you wish to keep the last # backups available for browsing or restoring to other machines. # # There are three values for $Conf{BackupsDisable}: # # 0 Backups are enabled. # # 1 Don't do any regular backups on this client. Manually # requested backups (via the CGI interface) will still occur. # # 2 Don't do any backups on this client. Manually requested # backups (via the CGI interface) will be ignored. # # In versions prior to 3.0 Backups were disabled by setting # $Conf{FullPeriod} to -1 or -2. # $Conf{BackupsDisable} = 0; # # A failed full backup is saved as a partial backup. The rsync # XferMethod can take advantage of the partial full when the next # backup is run. This parameter sets the age of the partial full # in days: if the partial backup is older than this number of # days, then rsync will ignore (not use) the partial full when # the next backup is run. If you set this to a negative value # then no partials will be saved. If you set this to 0, partials # will be saved, but will not be used by the next backup. # # The default setting of 3 days means that a partial older than # 3 days is ignored when the next full backup is done. # $Conf{PartialAgeMax} = 3; # # Whether incremental backups are filled. "Filling" means that the # most recent full (or filled) dump is merged into the new incremental # dump using hardlinks. This makes an incremental dump look like a # full dump. Prior to v1.03 all incremental backups were filled. # In v1.4.0 and later the default is off. # # BackupPC, and the cgi interface in particular, do the right thing on # un-filled incremental backups. It will correctly display the merged # incremental backup with the most recent filled backup, giving the # un-filled incremental backups a filled appearance. That means it # invisible to the user whether incremental dumps are filled or not. # # Filling backups takes a little extra disk space, and it does cost # some extra disk activity for filling, and later removal. Filling # is no longer useful, since file mangling and compression doesn't # make a filled backup very useful. It's likely the filling option # will be removed from future versions: filling will be delegated to # the display and extraction of backup data. # # If filling is off, BackupPC makes sure that the oldest backup is # a full, otherwise the following incremental backups will be # incomplete. This might mean an extra full backup has to be # kept until the following incremental backups expire. # # The default is off. You can turn this on or off at any # time without affecting existing backups. # $Conf{IncrFill} = 0; # # Number of restore logs to keep. BackupPC remembers information about # each restore request. This number per client will be kept around before # the oldest ones are pruned. # # Note: files/dirs delivered via Zip or Tar downloads don't count as # restores. Only the first restore option (where the files and dirs # are written to the host) count as restores that are logged. # $Conf{RestoreInfoKeepCnt} = 10; # # Number of archive logs to keep. BackupPC remembers information # about each archive request. This number per archive client will # be kept around before the oldest ones are pruned. # $Conf{ArchiveInfoKeepCnt} = 10; # # List of directories or files to backup. If this is defined, only these # directories or files will be backed up. # # When editing from the web interface, you should add a valid ShareName # (based on $Conf{XferMethod}), and then enter the directories specific # to that ShareName. A special ShareName "*" matches any ShareName that # doesn't have an explicit entry. # # For Smb, only one of $Conf{BackupFilesExclude} and $Conf{BackupFilesOnly} # can be specified per share. If both are set for a particular share, then # $Conf{BackupFilesOnly} takes precedence and $Conf{BackupFilesExclude} # is ignored. # # This can be set to a string, an array of strings, or, in the case # of multiple shares, a hash of strings or arrays. A hash is used # to give a list of directories or files to backup for each share # (the share name is the key). If this is set to just a string or # array, and $Conf{SmbShareName} contains multiple share names, then # the setting is assumed to apply all shares. # # If a hash is used, a special key "*" means it applies to all # shares that don't have a specific entry. # # Examples: # $Conf{BackupFilesOnly} = '/myFiles'; # $Conf{BackupFilesOnly} = ['/myFiles']; # same as first example # $Conf{BackupFilesOnly} = ['/myFiles', '/important']; # $Conf{BackupFilesOnly} = { # 'c' => ['/myFiles', '/important'], # these are for 'c' share # 'd' => ['/moreFiles', '/archive'], # these are for 'd' share # }; # $Conf{BackupFilesOnly} = { # 'c' => ['/myFiles', '/important'], # these are for 'c' share # '*' => ['/myFiles', '/important'], # these are other shares # }; # $Conf{BackupFilesOnly} = undef; # # List of directories or files to exclude from the backup. For Smb, # only one of $Conf{BackupFilesExclude} and $Conf{BackupFilesOnly} # can be specified per share. If both are set for a particular share, # then $Conf{BackupFilesOnly} takes precedence and # $Conf{BackupFilesExclude} is ignored. # # When editing from the web interface, you should add a valid ShareName # (based on $Conf{XferMethod}), and then enter the directories or files # specific to that ShareName. A special ShareName "*" matches any # ShareName that doesn't have an explicit entry. # # This can be set to a string, an array of strings, or, in the case # of multiple shares, a hash of strings or arrays. A hash is used # to give a list of directories or files to exclude for each share # (the share name is the key). If this is set to just a string or # array, and $Conf{SmbShareName} contains multiple share names, then # the setting is assumed to apply to all shares. # # The exact behavior is determined by the underlying transport program, # smbclient or tar. For smbclient the exlclude file list is passed into # the X option. Simple shell wild-cards using "*" or "?" are allowed. # # For tar, if the exclude file contains a "/" it is assumed to be anchored # at the start of the string. Since all the tar paths start with "./", # BackupPC prepends a "." if the exclude file starts with a "/". Note # that GNU tar version >= 1.13.7 is required for the exclude option to # work correctly. For linux or unix machines you should add # "/proc" to $Conf{BackupFilesExclude} unless you have specified # --one-file-system in $Conf{TarClientCmd} or --one-file-system in # $Conf{RsyncArgs}. Also, for tar, do not use a trailing "/" in # the directory name: a trailing "/" causes the name to not match # and the directory will not be excluded. # # Users report that for smbclient you should specify a directory # followed by "/*", eg: "/proc/*", instead of just "/proc". # # FTP servers are traversed recursively so excluding directories will # also exclude its contents. You can use the wildcard characters "*" # and "?" to define files for inclusion and exclusion. Both # attributes $Conf{BackupFilesOnly} and $Conf{BackupFilesExclude} can # be defined for the same share. # # If a hash is used, a special key "*" means it applies to all # shares that don't have a specific entry. # # Examples: # $Conf{BackupFilesExclude} = '/temp'; # $Conf{BackupFilesExclude} = ['/temp']; # same as first example # $Conf{BackupFilesExclude} = ['/temp', '/winnt/tmp']; # $Conf{BackupFilesExclude} = { # 'c' => ['/temp', '/winnt/tmp'], # these are for 'c' share # 'd' => ['/junk', '/dont_back_this_up'], # these are for 'd' share # }; # $Conf{BackupFilesExclude} = { # 'c' => ['/temp', '/winnt/tmp'], # these are for 'c' share # '*' => ['/junk', '/dont_back_this_up'], # these are for other shares # }; # $Conf{BackupFilesExclude} = undef; # # PCs that are always or often on the network can be backed up after # hours, to reduce PC, network and server load during working hours. For # each PC a count of consecutive good pings is maintained. Once a PC has # at least $Conf{BlackoutGoodCnt} consecutive good pings it is subject # to "blackout" and not backed up during hours and days specified by # $Conf{BlackoutPeriods}. # # To allow for periodic rebooting of a PC or other brief periods when a # PC is not on the network, a number of consecutive bad pings is allowed # before the good ping count is reset. This parameter is # $Conf{BlackoutBadPingLimit}. # # Note that bad and good pings don't occur with the same interval. If a # machine is always on the network, it will only be pinged roughly once # every $Conf{IncrPeriod} (eg: once per day). So a setting for # $Conf{BlackoutGoodCnt} of 7 means it will take around 7 days for a # machine to be subject to blackout. On the other hand, if a ping is # failed, it will be retried roughly every time BackupPC wakes up, eg, # every one or two hours. So a setting for $Conf{BlackoutBadPingLimit} of # 3 means that the PC will lose its blackout status after 3-6 hours of # unavailability. # # To disable the blackout feature set $Conf{BlackoutGoodCnt} to a negative # value. A value of 0 will make all machines subject to blackout. But # if you don't want to do any backups during the day it would be easier # to just set $Conf{WakeupSchedule} to a restricted schedule. # $Conf{BlackoutBadPingLimit} = 3; $Conf{BlackoutGoodCnt} = 7; # # One or more blackout periods can be specified. If a client is # subject to blackout then no regular (non-manual) backups will # be started during any of these periods. hourBegin and hourEnd # specify hours fro midnight and weekDays is a list of days of # the week where 0 is Sunday, 1 is Monday etc. # # For example: # # $Conf{BlackoutPeriods} = [ # { # hourBegin => 7.0, # hourEnd => 19.5, # weekDays => [1, 2, 3, 4, 5], # }, # ]; # # specifies one blackout period from 7:00am to 7:30pm local time # on Mon-Fri. # # The blackout period can also span midnight by setting # hourBegin > hourEnd, eg: # # $Conf{BlackoutPeriods} = [ # { # hourBegin => 7.0, # hourEnd => 19.5, # weekDays => [1, 2, 3, 4, 5], # }, # { # hourBegin => 23, # hourEnd => 5, # weekDays => [5, 6], # }, # ]; # # This specifies one blackout period from 7:00am to 7:30pm local time # on Mon-Fri, and a second period from 11pm to 5am on Friday and # Saturday night. # $Conf{BlackoutPeriods} = [ { hourBegin => 7.0, hourEnd => 19.5, weekDays => [1, 2, 3, 4, 5], }, ]; # # A backup of a share that has zero files is considered fatal. This is # used to catch miscellaneous Xfer errors that result in no files being # backed up. If you have shares that might be empty (and therefore an # empty backup is valid) you should set this flag to 0. # $Conf{BackupZeroFilesIsFatal} = 1; ########################################################################### # How to backup a client # (can be overridden in the per-PC config.pl) ########################################################################### # # What transport method to use to backup each host. If you have # a mixed set of WinXX and linux/unix hosts you will need to override # this in the per-PC config.pl. # # The valid values are: # # - 'smb': backup and restore via smbclient and the SMB protocol. # Easiest choice for WinXX. # # - 'rsync': backup and restore via rsync (via rsh or ssh). # Best choice for linux/unix. Good choice also for WinXX. # # - 'rsyncd': backup and restore via rsync daemon on the client. # Best choice for linux/unix if you have rsyncd running on # the client. Good choice also for WinXX. # # - 'tar': backup and restore via tar, tar over ssh, rsh or nfs. # Good choice for linux/unix. # # - 'archive': host is a special archive host. Backups are not done. # An archive host is used to archive other host's backups # to permanent media, such as tape, CDR or DVD. # # $Conf{XferMethod} = 'smb'; # # Level of verbosity in Xfer log files. 0 means be quiet, 1 will give # will give one line per file, 2 will also show skipped files on # incrementals, higher values give more output. # $Conf{XferLogLevel} = 1; # # Filename charset encoding on the client. BackupPC uses utf8 # on the server for filename encoding. If this is empty, then # utf8 is assumed and client filenames will not be modified. # If set to a different encoding then filenames will converted # to/from utf8 automatically during backup and restore. # # If the file names displayed in the browser (eg: accents or special # characters) don't look right then it is likely you haven't set # $Conf{ClientCharset} correctly. # # If you are using smbclient on a WinXX machine, smbclient will convert # to the "unix charset" setting in smb.conf. The default is utf8, # in which case leave $Conf{ClientCharset} empty since smbclient does # the right conversion. # # If you are using rsync on a WinXX machine then it does no conversion. # A typical WinXX encoding for latin1/western europe is 'cp1252', # so in this case set $Conf{ClientCharset} to 'cp1252'. # # On a linux or unix client, run "locale charmap" to see the client's # charset. Set $Conf{ClientCharset} to this value. A typical value # for english/US is 'ISO-8859-1'. # # Do "perldoc Encode::Supported" to see the list of possible charset # values. The FAQ at http://www.cl.cam.ac.uk/~mgk25/unicode.html # is excellent, and http://czyborra.com/charsets/iso8859.html # provides more information on the iso-8859 charsets. # $Conf{ClientCharset} = ''; # # Prior to 3.x no charset conversion was done by BackupPC. Backups were # stored in what ever charset the XferMethod provided - typically utf8 # for smbclient and the client's locale settings for rsync and tar (eg: # cp1252 for rsync on WinXX and perhaps iso-8859-1 with rsync on linux). # This setting tells BackupPC the charset that was used to store file # names in old backups taken with BackupPC 2.x, so that non-ascii file # names in old backups can be viewed and restored. # $Conf{ClientCharsetLegacy} = 'iso-8859-1'; ########################################################################### # Samba Configuration # (can be overwritten in the per-PC log file) ########################################################################### # # Name of the host share that is backed up when using SMB. This can be a # string or an array of strings if there are multiple shares per host. # Examples: # # $Conf{SmbShareName} = 'c'; # backup 'c' share # $Conf{SmbShareName} = ['c', 'd']; # backup 'c' and 'd' shares # # This setting only matters if $Conf{XferMethod} = 'smb'. # $Conf{SmbShareName} = 'C$'; # # Smbclient share user name. This is passed to smbclient's -U argument. # # This setting only matters if $Conf{XferMethod} = 'smb'. # $Conf{SmbShareUserName} = ''; # # Smbclient share password. This is passed to smbclient via its PASSWD # environment variable. There are several ways you can tell BackupPC # the smb share password. In each case you should be very careful about # security. If you put the password here, make sure that this file is # not readable by regular users! See the "Setting up config.pl" section # in the documentation for more information. # # This setting only matters if $Conf{XferMethod} = 'smb'. # $Conf{SmbSharePasswd} = ''; # # Full path for smbclient. Security caution: normal users should not # allowed to write to this file or directory. # # smbclient is from the Samba distribution. smbclient is used to # actually extract the incremental or full dump of the share filesystem # from the PC. # # This setting only matters if $Conf{XferMethod} = 'smb'. # $Conf{SmbClientPath} = ''; # # Command to run smbclient for a full dump. # This setting only matters if $Conf{XferMethod} = 'smb'. # # The following variables are substituted at run-time: # # $smbClientPath same as $Conf{SmbClientPath} # $host host to backup/restore # $hostIP host IP address # $shareName share name # $userName user name # $fileList list of files to backup (based on exclude/include) # $I_option optional -I option to smbclient # $X_option exclude option (if $fileList is an exclude list) # $timeStampFile start time for incremental dump # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{SmbClientFullCmd} = '$smbClientPath \\\\$host\\$shareName' . ' $I_option -U $userName -E -d 1' . ' -c tarmode\\ full -Tc$X_option - $fileList'; # # Command to run smbclient for an incremental dump. # This setting only matters if $Conf{XferMethod} = 'smb'. # # Same variable substitutions are applied as $Conf{SmbClientFullCmd}. # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{SmbClientIncrCmd} = '$smbClientPath \\\\$host\\$shareName' . ' $I_option -U $userName -E -d 1' . ' -c tarmode\\ full -TcN$X_option $timeStampFile - $fileList'; # # Command to run smbclient for a restore. # This setting only matters if $Conf{XferMethod} = 'smb'. # # Same variable substitutions are applied as $Conf{SmbClientFullCmd}. # # If your smb share is read-only then direct restores will fail. # You should set $Conf{SmbClientRestoreCmd} to undef and the # corresponding CGI restore option will be removed. # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{SmbClientRestoreCmd} = '$smbClientPath \\\\$host\\$shareName' . ' $I_option -U $userName -E -d 1' . ' -c tarmode\\ full -Tx -'; ########################################################################### # Tar Configuration # (can be overwritten in the per-PC log file) ########################################################################### # # Which host directories to backup when using tar transport. This can be a # string or an array of strings if there are multiple directories to # backup per host. Examples: # # $Conf{TarShareName} = '/'; # backup everything # $Conf{TarShareName} = '/home'; # only backup /home # $Conf{TarShareName} = ['/home', '/src']; # backup /home and /src # # The fact this parameter is called 'TarShareName' is for historical # consistency with the Smb transport options. You can use any valid # directory on the client: there is no need for it to correspond to # any Smb share or device mount point. # # Note also that you can also use $Conf{BackupFilesOnly} to specify # a specific list of directories to backup. It's more efficient to # use this option instead of $Conf{TarShareName} since a new tar is # run for each entry in $Conf{TarShareName}. # # On the other hand, if you add --one-file-system to $Conf{TarClientCmd} # you can backup each file system separately, which makes restoring one # bad file system easier. In this case you would list all of the mount # points here, since you can't get the same result with # $Conf{BackupFilesOnly}: # # $Conf{TarShareName} = ['/', '/var', '/data', '/boot']; # # This setting only matters if $Conf{XferMethod} = 'tar'. # $Conf{TarShareName} = '/'; # # Command to run tar on the client. GNU tar is required. You will # need to fill in the correct paths for ssh2 on the local host (server) # and GNU tar on the client. Security caution: normal users should not # allowed to write to these executable files or directories. # # $Conf{TarClientCmd} is appended with with either $Conf{TarFullArgs} or # $Conf{TarIncrArgs} to create the final command that is run. # # See the documentation for more information about setting up ssh2 keys. # # If you plan to use NFS then tar just runs locally and ssh2 is not needed. # For example, assuming the client filesystem is mounted below /mnt/hostName, # you could use something like: # # $Conf{TarClientCmd} = '$tarPath -c -v -f - -C /mnt/$host/$shareName' # . ' --totals'; # # In the case of NFS or rsh you need to make sure BackupPC's privileges # are sufficient to read all the files you want to backup. Also, you # will probably want to add "/proc" to $Conf{BackupFilesExclude}. # # The following variables are substituted at run-time: # # $host host name # $hostIP host's IP address # $incrDate newer-than date for incremental backups # $shareName share name to backup (ie: top-level directory path) # $fileList specific files to backup or exclude # $tarPath same as $Conf{TarClientPath} # $sshPath same as $Conf{SshPath} # # If a variable is followed by a "+" it is shell escaped. This is # necessary for the command part of ssh or rsh, since it ends up # getting passed through the shell. # # This setting only matters if $Conf{XferMethod} = 'tar'. # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{TarClientCmd} = '$sshPath -q -x -n -l root $host' . ' env LC_ALL=C $tarPath -c -v -f - -C $shareName+' . ' --totals'; # # Extra tar arguments for full backups. Several variables are substituted at # run-time. See $Conf{TarClientCmd} for the list of variable substitutions. # # If you are running tar locally (ie: without rsh or ssh) then remove the # "+" so that the argument is no longer shell escaped. # # This setting only matters if $Conf{XferMethod} = 'tar'. # $Conf{TarFullArgs} = '$fileList+'; # # Extra tar arguments for incr backups. Several variables are substituted at # run-time. See $Conf{TarClientCmd} for the list of variable substitutions. # # Note that GNU tar has several methods for specifying incremental backups, # including: # # --newer-mtime $incrDate+ # This causes a file to be included if the modification time is # later than $incrDate (meaning its contents might have changed). # But changes in the ownership or modes will not qualify the # file to be included in an incremental. # # --newer=$incrDate+ # This causes the file to be included if any attribute of the # file is later than $incrDate, meaning either attributes or # the modification time. This is the default method. Do # not use --atime-preserve in $Conf{TarClientCmd} above, # otherwise resetting the atime (access time) counts as an # attribute change, meaning the file will always be included # in each new incremental dump. # # If you are running tar locally (ie: without rsh or ssh) then remove the # "+" so that the argument is no longer shell escaped. # # This setting only matters if $Conf{XferMethod} = 'tar'. # $Conf{TarIncrArgs} = '--newer=$incrDate+ $fileList+'; # # Full command to run tar for restore on the client. GNU tar is required. # This can be the same as $Conf{TarClientCmd}, with tar's -c replaced by -x # and ssh's -n removed. # # See $Conf{TarClientCmd} for full details. # # This setting only matters if $Conf{XferMethod} = "tar". # # If you want to disable direct restores using tar, you should set # $Conf{TarClientRestoreCmd} to undef and the corresponding CGI # restore option will be removed. # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{TarClientRestoreCmd} = '$sshPath -q -x -l root $host' . ' env LC_ALL=C $tarPath -x -p --numeric-owner --same-owner' . ' -v -f - -C $shareName+'; # # Full path for tar on the client. Security caution: normal users should not # allowed to write to this file or directory. # # This setting only matters if $Conf{XferMethod} = 'tar'. # $Conf{TarClientPath} = ''; ########################################################################### # Rsync/Rsyncd Configuration # (can be overwritten in the per-PC log file) ########################################################################### # # Path to rsync executable on the client # $Conf{RsyncClientPath} = ''; # # Full command to run rsync on the client machine. The following variables # are substituted at run-time: # # $host host name being backed up # $hostIP host's IP address # $shareName share name to backup (ie: top-level directory path) # $rsyncPath same as $Conf{RsyncClientPath} # $sshPath same as $Conf{SshPath} # $argList argument list, built from $Conf{RsyncArgs}, # $shareName, $Conf{BackupFilesExclude} and # $Conf{BackupFilesOnly} # # This setting only matters if $Conf{XferMethod} = 'rsync'. # $Conf{RsyncClientCmd} = '$sshPath -q -x -l root $host $rsyncPath $argList+'; # # Full command to run rsync for restore on the client. The following # variables are substituted at run-time: # # $host host name being backed up # $hostIP host's IP address # $shareName share name to backup (ie: top-level directory path) # $rsyncPath same as $Conf{RsyncClientPath} # $sshPath same as $Conf{SshPath} # $argList argument list, built from $Conf{RsyncArgs}, # $shareName, $Conf{BackupFilesExclude} and # $Conf{BackupFilesOnly} # # This setting only matters if $Conf{XferMethod} = 'rsync'. # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{RsyncClientRestoreCmd} = '$sshPath -q -x -l root $host $rsyncPath $argList+'; # # Share name to backup. For $Conf{XferMethod} = "rsync" this should # be a file system path, eg '/' or '/home'. # # For $Conf{XferMethod} = "rsyncd" this should be the name of the module # to backup (ie: the name from /etc/rsynd.conf). # # This can also be a list of multiple file system paths or modules. # For example, by adding --one-file-system to $Conf{RsyncArgs} you # can backup each file system separately, which makes restoring one # bad file system easier. In this case you would list all of the mount # points: # # $Conf{RsyncShareName} = ['/', '/var', '/data', '/boot']; # $Conf{RsyncShareName} = '/'; # # Rsync daemon port on the client, for $Conf{XferMethod} = "rsyncd". # $Conf{RsyncdClientPort} = 873; # # Rsync daemon user name on client, for $Conf{XferMethod} = "rsyncd". # The user name and password are stored on the client in whatever file # the "secrets file" parameter in rsyncd.conf points to # (eg: /etc/rsyncd.secrets). # $Conf{RsyncdUserName} = ''; # # Rsync daemon user name on client, for $Conf{XferMethod} = "rsyncd". # The user name and password are stored on the client in whatever file # the "secrets file" parameter in rsyncd.conf points to # (eg: /etc/rsyncd.secrets). # $Conf{RsyncdPasswd} = ''; # # Whether authentication is mandatory when connecting to the client's # rsyncd. By default this is on, ensuring that BackupPC will refuse to # connect to an rsyncd on the client that is not password protected. # Turn off at your own risk. # $Conf{RsyncdAuthRequired} = 1; # # When rsync checksum caching is enabled (by adding the # --checksum-seed=32761 option to $Conf{RsyncArgs}), the cached # checksums can be occasionally verified to make sure the file # contents matches the cached checksums. This is to avoid the # risk that disk problems might cause the pool file contents to # get corrupted, but the cached checksums would make BackupPC # think that the file still matches the client. # # This setting is the probability (0 means never and 1 means always) # that a file will be rechecked. Setting it to 0 means the checksums # will not be rechecked (unless there is a phase 0 failure). Setting # it to 1 (ie: 100%) means all files will be checked, but that is # not a desirable setting since you are better off simply turning # caching off (ie: remove the --checksum-seed option). # # The default of 0.01 means 1% (on average) of the files during a full # backup will have their cached checksum re-checked. # # This setting has no effect unless checksum caching is turned on. # $Conf{RsyncCsumCacheVerifyProb} = 0.01; # # Arguments to rsync for backup. Do not edit the first set unless you # have a thorough understanding of how File::RsyncP works. # $Conf{RsyncArgs} = [ # # Do not edit these! # '--numeric-ids', '--perms', '--owner', '--group', '-D', '--links', '--hard-links', '--times', '--block-size=2048', '--recursive', # # Rsync >= 2.6.3 supports the --checksum-seed option # which allows rsync checksum caching on the server. # Uncomment this to enable rsync checksum caching if # you have a recent client rsync version and you want # to enable checksum caching. # #'--checksum-seed=32761', ]; # # Additional arguments added to RsyncArgs. This can be used in # conbination with $Conf{RsyncArgs} to allow customization of # the rsync arguments on a part-client basis. The standard # arguments go in $Conf{RsyncArgs} and $Conf{RsyncArgsExtra} # can be set on a per-client basis. # # Examples of additional arguments that should work are --exclude/--include, # eg: # # $Conf{RsyncArgsExtra} = [ # '--exclude', '/proc', # '--exclude', '*.tmp', # ]; # # Both $Conf{RsyncArgs} and $Conf{RsyncArgsExtra} are subject # to the following variable substitutions: # # $client client name being backed up # $host host name (could be different from client name if # $Conf{ClientNameAlias} is set) # $hostIP IP address of host # $confDir configuration directory path # # This allows settings of the form: # # $Conf{RsyncArgsExtra} = [ # '--exclude-from=$confDir/pc/$host.exclude', # ]; # $Conf{RsyncArgsExtra} = []; # # Arguments to rsync for restore. Do not edit the first set unless you # have a thorough understanding of how File::RsyncP works. # # If you want to disable direct restores using rsync (eg: is the module # is read-only), you should set $Conf{RsyncRestoreArgs} to undef and # the corresponding CGI restore option will be removed. # # $Conf{RsyncRestoreArgs} is subject to the following variable # substitutions: # # $client client name being backed up # $host host name (could be different from client name if # $Conf{ClientNameAlias} is set) # $hostIP IP address of host # $confDir configuration directory path # # Note: $Conf{RsyncArgsExtra} doesn't apply to $Conf{RsyncRestoreArgs}. # $Conf{RsyncRestoreArgs} = [ # # Do not edit these! # '--numeric-ids', '--perms', '--owner', '--group', '-D', '--links', '--hard-links', '--times', '--block-size=2048', '--relative', '--ignore-times', '--recursive', # # Rsync >= 2.6.3 supports the --checksum-seed option # which allows rsync checksum caching on the server. # Uncomment this to enable rsync checksum caching if # you have a recent client rsync version and you want # to enable checksum caching. # #'--checksum-seed=32761', # # Add additional arguments here # ]; ########################################################################### # FTP Configuration # (can be overwritten in the per-PC log file) ########################################################################## # # Which host directories to backup when using FTP. This can be a # string or an array of strings if there are multiple shares per host. # # This value must be specified in one of two ways: either as a # subdirectory of the 'share root' on the server, or as the absolute # path of the directory. # # In the following example, if the directory /home/username is the # root share of the ftp server with the given username, the following # two values will back up the same directory: # # $Conf{FtpShareName} = 'www'; # www directory # $Conf{FtpShareName} = '/home/username/www'; # same directory # # Path resolution is not supported; i.e.; you may not have an ftp # share path defined as '../otheruser' or '~/games'. # # Multiple shares may also be specified, as with other protocols: # # $Conf{FtpShareName} = [ 'www', # 'bin', # 'config' ]; # # Note also that you can also use $Conf{BackupFilesOnly} to specify # a specific list of directories to backup. It's more efficient to # use this option instead of $Conf{FtpShareName} since a new tar is # run for each entry in $Conf{FtpShareName}. # # This setting only matters if $Conf{XferMethod} = 'ftp'. # $Conf{FtpShareName} = ''; # # FTP user name. This is used to log into the server. # # This setting is used only if $Conf{XferMethod} = 'ftp'. # $Conf{FtpUserName} = ''; # # FTP user password. This is used to log into the server. # # This setting is used only if $Conf{XferMethod} = 'ftp'. # $Conf{FtpPasswd} = ''; # # Whether passive mode is used. The correct setting depends upon # whether local or remote ports are accessible from the other machine, # which is affected by any firewall or routers between the FTP server # on the client and the BackupPC server. # # This setting is used only if $Conf{XferMethod} = 'ftp'. # $Conf{FtpPassive} = 1; # # Transfer block size. This sets the size of the amounts of data in # each frame. While undefined, this value takes the default value. # # This setting is used only if $Conf{XferMethod} = 'ftp'. # $Conf{FtpBlockSize} = 10240; # # The port of the ftp server. If undefined, 21 is used. # # This setting is used only if $Conf{XferMethod} = 'ftp'. # $Conf{FtpPort} = 21; # # Connection timeout for FTP. When undefined, the default is 120 seconds. # # This setting is used only if $Conf{XferMethod} = 'ftp'. # $Conf{FtpTimeout} = 120; # # Behaviour when BackupPC encounters symlinks on the FTP share. # # Symlinks cannot be restored via FTP, so the desired behaviour will # be different depending on the setup of the share. The default for # this behavor is 1. Directory shares with more complicated directory # structures should consider other protocols. # $Conf{FtpFollowSymlinks} = 0; ########################################################################### # Archive Configuration # (can be overwritten in the per-PC log file) ########################################################################### # # Archive Destination # # The Destination of the archive # e.g. /tmp for file archive or /dev/nst0 for device archive # $Conf{ArchiveDest} = '/tmp'; # # Archive Compression type # # The valid values are: # # - 'none': No Compression # # - 'gzip': Medium Compression. Recommended. # # - 'bzip2': High Compression but takes longer. # $Conf{ArchiveComp} = 'gzip'; # # Archive Parity Files # # The amount of Parity data to generate, as a percentage # of the archive size. # Uses the commandline par2 (par2cmdline) available from # http://parchive.sourceforge.net # # Only useful for file dumps. # # Set to 0 to disable this feature. # $Conf{ArchivePar} = 0; # # Archive Size Split # # Only for file archives. Splits the output into # the specified size * 1,000,000. # e.g. to split into 650,000,000 bytes, specify 650 below. # # If the value is 0, or if $Conf{ArchiveDest} is an existing file or # device (e.g. a streaming tape drive), this feature is disabled. # $Conf{ArchiveSplit} = 0; # # Archive Command # # This is the command that is called to actually run the archive process # for each host. The following variables are substituted at run-time: # # $Installdir The installation directory of BackupPC # $tarCreatePath The path to BackupPC_tarCreate # $splitpath The path to the split program # $parpath The path to the par2 program # $host The host to archive # $backupnumber The backup number of the host to archive # $compression The path to the compression program # $compext The extension assigned to the compression type # $splitsize The number of bytes to split archives into # $archiveloc The location to put the archive # $parfile The amount of parity data to create (percentage) # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{ArchiveClientCmd} = '$Installdir/bin/BackupPC_archiveHost' . ' $tarCreatePath $splitpath $parpath $host $backupnumber' . ' $compression $compext $splitsize $archiveloc $parfile *'; # # Full path for ssh. Security caution: normal users should not # allowed to write to this file or directory. # $Conf{SshPath} = ''; # # Full path for nmblookup. Security caution: normal users should not # allowed to write to this file or directory. # # nmblookup is from the Samba distribution. nmblookup is used to get the # netbios name, necessary for DHCP hosts. # $Conf{NmbLookupPath} = ''; # # NmbLookup command. Given an IP address, does an nmblookup on that # IP address. The following variables are substituted at run-time: # # $nmbLookupPath path to nmblookup ($Conf{NmbLookupPath}) # $host IP address # # This command is only used for DHCP hosts: given an IP address, this # command should try to find its NetBios name. # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{NmbLookupCmd} = '$nmbLookupPath -A $host'; # # NmbLookup command. Given a netbios name, finds that host by doing # a NetBios lookup. Several variables are substituted at run-time: # # $nmbLookupPath path to nmblookup ($Conf{NmbLookupPath}) # $host NetBios name # # In some cases you might need to change the broadcast address, for # example if nmblookup uses 192.168.255.255 by default and you find # that doesn't work, try 192.168.1.255 (or your equivalent class C # address) using the -B option: # # $Conf{NmbLookupFindHostCmd} = '$nmbLookupPath -B 192.168.1.255 $host'; # # If you use a WINS server and your machines don't respond to # multicast NetBios requests you can use this (replace 1.2.3.4 # with the IP address of your WINS server): # # $Conf{NmbLookupFindHostCmd} = '$nmbLookupPath -R -U 1.2.3.4 $host'; # # This is preferred over multicast since it minimizes network traffic. # # Experiment manually for your site to see what form of nmblookup command # works. # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{NmbLookupFindHostCmd} = '$nmbLookupPath $host'; # # For fixed IP address hosts, BackupPC_dump can also verify the netbios # name to ensure it matches the host name. An error is generated if # they do not match. Typically this flag is off. But if you are going # to transition a bunch of machines from fixed host addresses to DHCP, # setting this flag is a great way to verify that the machines have # their netbios name set correctly before turning on DCHP. # $Conf{FixedIPNetBiosNameCheck} = 0; # # Full path to the ping command. Security caution: normal users # should not be allowed to write to this file or directory. # # If you want to disable ping checking, set this to some program # that exits with 0 status, eg: # # $Conf{PingPath} = '/bin/echo'; # $Conf{PingPath} = ''; # # Ping command. The following variables are substituted at run-time: # # $pingPath path to ping ($Conf{PingPath}) # $host host name # # Wade Brown reports that on solaris 2.6 and 2.7 ping -s returns the wrong # exit status (0 even on failure). Replace with "ping $host 1", which # gets the correct exit status but we don't get the round-trip time. # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{PingCmd} = '$pingPath -c 1 $host'; # # Maximum round-trip ping time in milliseconds. This threshold is set # to avoid backing up PCs that are remotely connected through WAN or # dialup connections. The output from ping -s (assuming it is supported # on your system) is used to check the round-trip packet time. On your # local LAN round-trip times should be much less than 20msec. On most # WAN or dialup connections the round-trip time will be typically more # than 20msec. Tune if necessary. # $Conf{PingMaxMsec} = 20; # # Compression level to use on files. 0 means no compression. Compression # levels can be from 1 (least cpu time, slightly worse compression) to # 9 (most cpu time, slightly better compression). The recommended value # is 3. Changing to 5, for example, will take maybe 20% more cpu time # and will get another 2-3% additional compression. See the zlib # documentation for more information about compression levels. # # Changing compression on or off after backups have already been done # will require both compressed and uncompressed pool files to be stored. # This will increase the pool storage requirements, at least until all # the old backups expire and are deleted. # # It is ok to change the compression value (from one non-zero value to # another non-zero value) after dumps are already done. Since BackupPC # matches pool files by comparing the uncompressed versions, it will still # correctly match new incoming files against existing pool files. The # new compression level will take effect only for new files that are # newly compressed and added to the pool. # # If compression was off and you are enabling compression for the first # time you can use the BackupPC_compressPool utility to compress the # pool. This avoids having the pool grow to accommodate both compressed # and uncompressed backups. See the documentation for more information. # # Note: compression needs the Compress::Zlib perl library. If the # Compress::Zlib library can't be found then $Conf{CompressLevel} is # forced to 0 (compression off). # $Conf{CompressLevel} = 0; # # Timeout in seconds when listening for the transport program's # (smbclient, tar etc) stdout. If no output is received during this # time, then it is assumed that something has wedged during a backup, # and the backup is terminated. # # Note that stdout buffering combined with huge files being backed up # could cause longish delays in the output from smbclient that # BackupPC_dump sees, so in rare cases you might want to increase # this value. # # Despite the name, this parameter sets the timeout for all transport # methods (tar, smb etc). # $Conf{ClientTimeout} = 72000; # # Maximum number of log files we keep around in each PC's directory # (ie: pc/$host). These files are aged monthly. A setting of 12 # means there will be at most the files LOG, LOG.0, LOG.1, ... LOG.11 # in the pc/$host directory (ie: about a years worth). (Except this # month's LOG, these files will have a .z extension if compression # is on). # # If you decrease this number after BackupPC has been running for a # while you will have to manually remove the older log files. # $Conf{MaxOldPerPCLogFiles} = 12; # # Optional commands to run before and after dumps and restores, # and also before and after each share of a dump. # # Stdout from these commands will be written to the Xfer (or Restore) # log file. One example of using these commands would be to # shut down and restart a database server, dump a database # to files for backup, or doing a snapshot of a share prior # to a backup. Example: # # $Conf{DumpPreUserCmd} = '$sshPath -q -x -l root $host /usr/bin/dumpMysql'; # # The following variable substitutions are made at run time for # $Conf{DumpPreUserCmd}, $Conf{DumpPostUserCmd}, $Conf{DumpPreShareCmd} # and $Conf{DumpPostShareCmd}: # # $type type of dump (incr or full) # $xferOK 1 if the dump succeeded, 0 if it didn't # $client client name being backed up # $host host name (could be different from client name if # $Conf{ClientNameAlias} is set) # $hostIP IP address of host # $user user name from the hosts file # $moreUsers list of additional users from the hosts file # $share the first share name (or current share for # $Conf{DumpPreShareCmd} and $Conf{DumpPostShareCmd}) # $shares list of all the share names # $XferMethod value of $Conf{XferMethod} (eg: tar, rsync, smb) # $sshPath value of $Conf{SshPath}, # $cmdType set to DumpPreUserCmd or DumpPostUserCmd # # The following variable substitutions are made at run time for # $Conf{RestorePreUserCmd} and $Conf{RestorePostUserCmd}: # # $client client name being backed up # $xferOK 1 if the restore succeeded, 0 if it didn't # $host host name (could be different from client name if # $Conf{ClientNameAlias} is set) # $hostIP IP address of host # $user user name from the hosts file # $moreUsers list of additional users from the hosts file # $share the first share name # $XferMethod value of $Conf{XferMethod} (eg: tar, rsync, smb) # $sshPath value of $Conf{SshPath}, # $type set to "restore" # $bkupSrcHost host name of the restore source # $bkupSrcShare share name of the restore source # $bkupSrcNum backup number of the restore source # $pathHdrSrc common starting path of restore source # $pathHdrDest common starting path of destination # $fileList list of files being restored # $cmdType set to RestorePreUserCmd or RestorePostUserCmd # # The following variable substitutions are made at run time for # $Conf{ArchivePreUserCmd} and $Conf{ArchivePostUserCmd}: # # $client client name being backed up # $xferOK 1 if the archive succeeded, 0 if it didn't # $host Name of the archive host # $user user name from the hosts file # $share the first share name # $XferMethod value of $Conf{XferMethod} (eg: tar, rsync, smb) # $HostList list of hosts being archived # $BackupList list of backup numbers for the hosts being archived # $archiveloc location where the archive is sent to # $parfile amount of parity data being generated (percentage) # $compression compression program being used (eg: cat, gzip, bzip2) # $compext extension used for compression type (eg: raw, gz, bz2) # $splitsize size of the files that the archive creates # $sshPath value of $Conf{SshPath}, # $type set to "archive" # $cmdType set to ArchivePreUserCmd or ArchivePostUserCmd # # Note: all Cmds are executed directly without a shell, so the prog name # needs to be a full path and you can't include shell syntax like # redirection and pipes; put that in a script if you need it. # $Conf{DumpPreUserCmd} = undef; $Conf{DumpPostUserCmd} = undef; $Conf{DumpPreShareCmd} = undef; $Conf{DumpPostShareCmd} = undef; $Conf{RestorePreUserCmd} = undef; $Conf{RestorePostUserCmd} = undef; $Conf{ArchivePreUserCmd} = undef; $Conf{ArchivePostUserCmd} = undef; # # Whether the exit status of each PreUserCmd and # PostUserCmd is checked. # # If set and the Dump/Restore/Archive Pre/Post UserCmd # returns a non-zero exit status then the dump/restore/archive # is aborted. To maintain backward compatibility (where # the exit status in early versions was always ignored), # this flag defaults to 0. # # If this flag is set and the Dump/Restore/Archive PreUserCmd # fails then the matching Dump/Restore/Archive PostUserCmd is # not executed. If DumpPreShareCmd returns a non-exit status, # then DumpPostShareCmd is not executed, but the DumpPostUserCmd # is still run (since DumpPreUserCmd must have previously # succeeded). # # An example of a DumpPreUserCmd that might fail is a script # that snapshots or dumps a database which fails because # of some database error. # $Conf{UserCmdCheckStatus} = 0; # # Override the client's host name. This allows multiple clients # to all refer to the same physical host. This should only be # set in the per-PC config file and is only used by BackupPC at # the last moment prior to generating the command used to backup # that machine (ie: the value of $Conf{ClientNameAlias} is invisible # everywhere else in BackupPC). The setting can be a host name or # IP address, eg: # # $Conf{ClientNameAlias} = 'realHostName'; # $Conf{ClientNameAlias} = '192.1.1.15'; # # will cause the relevant smb/tar/rsync backup/restore commands to be # directed to realHostName, not the client name. # # Note: this setting doesn't work for hosts with DHCP set to 1. # $Conf{ClientNameAlias} = undef; ########################################################################### # Email reminders, status and messages # (can be overridden in the per-PC config.pl) ########################################################################### # # Full path to the sendmail command. Security caution: normal users # should not allowed to write to this file or directory. # $Conf{SendmailPath} = ''; # # Minimum period between consecutive emails to a single user. # This tries to keep annoying email to users to a reasonable # level. Email checks are done nightly, so this number is effectively # rounded up (ie: 2.5 means a user will never receive email more # than once every 3 days). # $Conf{EMailNotifyMinDays} = 2.5; # # Name to use as the "from" name for email. Depending upon your mail # handler this is either a plain name (eg: "admin") or a fully-qualified # name (eg: "admin@mydomain.com"). # $Conf{EMailFromUserName} = ''; # # Destination address to an administrative user who will receive a # nightly email with warnings and errors. If there are no warnings # or errors then no email will be sent. Depending upon your mail # handler this is either a plain name (eg: "admin") or a fully-qualified # name (eg: "admin@mydomain.com"). # $Conf{EMailAdminUserName} = ''; # # Destination domain name for email sent to users. By default # this is empty, meaning email is sent to plain, unqualified # addresses. Otherwise, set it to the destintation domain, eg: # # $Cong{EMailUserDestDomain} = '@mydomain.com'; # # With this setting user email will be set to 'user@mydomain.com'. # $Conf{EMailUserDestDomain} = ''; # # This subject and message is sent to a user if their PC has never been # backed up. # # These values are language-dependent. The default versions can be # found in the language file (eg: lib/BackupPC/Lang/en.pm). If you # need to change the message, copy it here and edit it, eg: # # $Conf{EMailNoBackupEverMesg} = <<'EOF'; # To: $user$domain # cc: # Subject: $subj # # Dear $userName, # # This is a site-specific email message. # EOF # $Conf{EMailNoBackupEverSubj} = undef; $Conf{EMailNoBackupEverMesg} = undef; # # How old the most recent backup has to be before notifying user. # When there have been no backups in this number of days the user # is sent an email. # $Conf{EMailNotifyOldBackupDays} = 7.0; # # This subject and message is sent to a user if their PC has not recently # been backed up (ie: more than $Conf{EMailNotifyOldBackupDays} days ago). # # These values are language-dependent. The default versions can be # found in the language file (eg: lib/BackupPC/Lang/en.pm). If you # need to change the message, copy it here and edit it, eg: # # $Conf{EMailNoBackupRecentMesg} = <<'EOF'; # To: $user$domain # cc: # Subject: $subj # # Dear $userName, # # This is a site-specific email message. # EOF # $Conf{EMailNoBackupRecentSubj} = undef; $Conf{EMailNoBackupRecentMesg} = undef; # # How old the most recent backup of Outlook files has to be before # notifying user. # $Conf{EMailNotifyOldOutlookDays} = 5.0; # # This subject and message is sent to a user if their Outlook files have # not recently been backed up (ie: more than $Conf{EMailNotifyOldOutlookDays} # days ago). # # These values are language-dependent. The default versions can be # found in the language file (eg: lib/BackupPC/Lang/en.pm). If you # need to change the message, copy it here and edit it, eg: # # $Conf{EMailOutlookBackupMesg} = <<'EOF'; # To: $user$domain # cc: # Subject: $subj # # Dear $userName, # # This is a site-specific email message. # EOF # $Conf{EMailOutlookBackupSubj} = undef; $Conf{EMailOutlookBackupMesg} = undef; # # Additional email headers. This sets to charset to # utf8. # $Conf{EMailHeaders} = < administrative users are the union of group admin, plus # craig and celia. # # $Conf{CgiAdminUserGroup} = ''; # $Conf{CgiAdminUsers} = 'craig celia'; # --> administrative users are only craig and celia'. # $Conf{CgiAdminUserGroup} = ''; $Conf{CgiAdminUsers} = ''; # # URL of the BackupPC_Admin CGI script. Used for email messages. # $Conf{CgiURL} = undef; # # Language to use. See lib/BackupPC/Lang for the list of supported # languages, which include English (en), French (fr), Spanish (es), # German (de), Italian (it), Dutch (nl), Polish (pl), Portuguese # Brazillian (pt_br) and Chinese (zh_CH). # # Currently the Language setting applies to the CGI interface and email # messages sent to users. Log files and other text are still in English. # $Conf{Language} = 'en'; # # User names that are rendered by the CGI interface can be turned # into links into their home page or other information about the # user. To set this up you need to create two sprintf() strings, # that each contain a single '%s' that will be replaced by the user # name. The default is a mailto: link. # # $Conf{CgiUserHomePageCheck} should be an absolute file path that # is used to check (via "-f") that the user has a valid home page. # Set this to undef or an empty string to turn off this check. # # $Conf{CgiUserUrlCreate} should be a full URL that points to the # user's home page. Set this to undef or an empty string to turn # off generation of URLs for user names. # # Example: # $Conf{CgiUserHomePageCheck} = '/var/www/html/users/%s.html'; # $Conf{CgiUserUrlCreate} = 'http://myhost/users/%s.html'; # --> if /var/www/html/users/craig.html exists, then 'craig' will # be rendered as a link to http://myhost/users/craig.html. # $Conf{CgiUserHomePageCheck} = ''; $Conf{CgiUserUrlCreate} = 'mailto:%s'; # # Date display format for CGI interface. A value of 1 uses US-style # dates (MM/DD), a value of 2 uses full YYYY-MM-DD format, and zero # for international dates (DD/MM). # $Conf{CgiDateFormatMMDD} = 1; # # If set, the complete list of hosts appears in the left navigation # bar pull-down for administrators. Otherwise, just the hosts for which # the user is listed in the host file (as either the user or in moreUsers) # are displayed. # $Conf{CgiNavBarAdminAllHosts} = 1; # # Enable/disable the search box in the navigation bar. # $Conf{CgiSearchBoxEnable} = 1; # # Additional navigation bar links. These appear for both regular users # and administrators. This is a list of hashes giving the link (URL) # and the text (name) for the link. Specifying lname instead of name # uses the language specific string (ie: $Lang->{lname}) instead of # just literally displaying name. # $Conf{CgiNavBarLinks} = [ { link => "?action=view&type=docs", lname => "Documentation", # actually displays $Lang->{Documentation} }, { link => "https://github.com/backuppc/backuppc/wiki", name => "Wiki", # displays literal "Wiki" }, { link => "http://backuppc.sourceforge.net", name => "SourceForge", # displays literal "SourceForge" }, ]; # # Hilight colors based on status that are used in the PC summary page. # $Conf{CgiStatusHilightColor} = { Reason_backup_failed => '#ffcccc', Reason_backup_done => '#ccffcc', Reason_no_ping => '#ffff99', Reason_backup_canceled_by_user => '#ff9900', Status_backup_in_progress => '#66cc99', Disabled_OnlyManualBackups => '#d1d1d1', Disabled_AllBackupsDisabled => '#d1d1d1', }; # # Additional CGI header text. # $Conf{CgiHeaders} = ''; # # Directory where images are stored. This directory should be below # Apache's DocumentRoot. This value isn't used by BackupPC but is # used by configure.pl when you upgrade BackupPC. # # Example: # $Conf{CgiImageDir} = '/var/www/htdocs/BackupPC'; # $Conf{CgiImageDir} = ''; # # Additional mappings of file name extenions to Content-Type for # individual file restore. See $Ext2ContentType in BackupPC_Admin # for the default setting. You can add additional settings here, # or override any default settings. Example: # # $Conf{CgiExt2ContentType} = { # 'pl' => 'text/plain', # }; # $Conf{CgiExt2ContentType} = { }; # # URL (without the leading http://host) for BackupPC's image directory. # The CGI script uses this value to serve up image files. # # Example: # $Conf{CgiImageDirURL} = '/BackupPC'; # $Conf{CgiImageDirURL} = ''; # # CSS stylesheet "skin" for the CGI interface. It is stored # in the $Conf{CgiImageDir} directory and accessed via the # $Conf{CgiImageDirURL} URL. # # For BackupPC v3.x several color, layout and font changes were made. # The previous v2.x version is available as BackupPC_stnd_orig.css, so # if you prefer the old skin, change this to BackupPC_stnd_orig.css. # $Conf{CgiCSSFile} = 'BackupPC_stnd.css'; # # Whether the user is allowed to edit their per-PC config. # $Conf{CgiUserConfigEditEnable} = 1; # # Which per-host config variables a non-admin user is allowed # to edit. Admin users can edit all per-host config variables, # even if disabled in this list. # # SECURITY WARNING: Do not let users edit any of the Cmd # config variables! That's because a user could set a # Cmd to a shell script of their choice and it will be # run as the BackupPC user. That script could do all # sorts of bad things. # $Conf{CgiUserConfigEdit} = { FullPeriod => 1, IncrPeriod => 1, FullKeepCnt => 1, FullKeepCntMin => 1, FullAgeMax => 1, IncrKeepCnt => 1, IncrKeepCntMin => 1, IncrAgeMax => 1, IncrLevels => 1, IncrFill => 1, PartialAgeMax => 1, RestoreInfoKeepCnt => 1, ArchiveInfoKeepCnt => 1, BackupFilesOnly => 1, BackupFilesExclude => 1, BackupsDisable => 1, BlackoutBadPingLimit => 1, BlackoutGoodCnt => 1, BlackoutPeriods => 1, BackupZeroFilesIsFatal => 1, ClientCharset => 1, ClientCharsetLegacy => 1, XferMethod => 1, XferLogLevel => 1, SmbShareName => 1, SmbShareUserName => 1, SmbSharePasswd => 1, SmbClientFullCmd => 0, SmbClientIncrCmd => 0, SmbClientRestoreCmd => 0, TarShareName => 1, TarFullArgs => 1, TarIncrArgs => 1, TarClientCmd => 0, TarClientRestoreCmd => 0, TarClientPath => 0, RsyncShareName => 1, RsyncdClientPort => 1, RsyncdPasswd => 1, RsyncdUserName => 1, RsyncdAuthRequired => 1, RsyncCsumCacheVerifyProb => 1, RsyncArgs => 1, RsyncArgsExtra => 1, RsyncRestoreArgs => 1, RsyncClientCmd => 0, RsyncClientRestoreCmd => 0, RsyncClientPath => 0, FtpShareName => 1, FtpUserName => 1, FtpPasswd => 1, FtpBlockSize => 1, FtpPort => 1, FtpTimeout => 1, FtpFollowSymlinks => 1, FtpRestoreEnabled => 1, ArchiveDest => 1, ArchiveComp => 1, ArchivePar => 1, ArchiveSplit => 1, ArchiveClientCmd => 0, FixedIPNetBiosNameCheck => 1, NmbLookupCmd => 0, NmbLookupFindHostCmd => 0, PingMaxMsec => 1, PingCmd => 0, ClientTimeout => 1, MaxOldPerPCLogFiles => 1, CompressLevel => 1, ClientNameAlias => 1, DumpPreUserCmd => 0, DumpPostUserCmd => 0, RestorePreUserCmd => 0, RestorePostUserCmd => 0, ArchivePreUserCmd => 0, ArchivePostUserCmd => 0, DumpPostShareCmd => 0, DumpPreShareCmd => 0, UserCmdCheckStatus => 0, EMailNotifyMinDays => 1, EMailFromUserName => 1, EMailAdminUserName => 1, EMailUserDestDomain => 1, EMailNoBackupEverSubj => 1, EMailNoBackupEverMesg => 1, EMailNotifyOldBackupDays => 1, EMailNoBackupRecentSubj => 1, EMailNoBackupRecentMesg => 1, EMailNotifyOldOutlookDays => 1, EMailOutlookBackupSubj => 1, EMailOutlookBackupMesg => 1, EMailHeaders => 1, }; BackupPC-3.3.2/conf/hosts0000444000076500000240000000424613042250554014133 0ustar craigstaff#============================================================= -*-perl-*- # # Host file list for BackupPC. # # DESCRIPTION # # This file lists all the hosts that should be backed up by # BackupPC. # # Each line in the hosts file contains three fields, separated # by white space: # # - The host name. If this host is a static IP address this # must the machine's IP host name (ie: something that can # be looked up using nslookup or DNS). If this is a DHCP # host then the host name must be the netbios name of the # machine. It is possible to have a host name that contains # spaces, but that is discouraged. Escape a space with "\", eg: # # craigs\ pc # # - DHCP flag. Set to 0 if this is a static IP address host # or if the machine can be found using nmblookup. Otherwise, # if the client can only be found by looking through the DHCP # pool then set this to 1. # # - User name (unix login/email name) of the user who "owns" # or uses this machine. This is the user who will be sent # email about this machine, and this user will have permission # to stop/start/browse/restore backups for this host. This # user name must match the name the user authenticates with # via apache. # # - Optional additional user names (comma separated, no white space) of # users who are also allowed to stop/start/browse/restore backups # for this client via the CGI interface. These users are not sent # email. These do not need to be valid email names; they simply # need to match the name the user authenticates with via apache. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # See http://backuppc.sourceforge.net. # #======================================================================== # # The first non-comment non-empty line gives the field names and should # not be edited!! # host dhcp user moreUsers # <--- do not edit this line #farside 0 craig jill,jeff # <--- example static IP host entry #larson 1 bill # <--- example DHCP host entry BackupPC-3.3.2/conf/sorttable.js0000444000076500000240000001447713042250554015414 0ustar craigstaff// Used with under license grant from http://kryogenix.org/code/browser/sorttable/ // Credit for this code goes to Stuart Langridge - http://kryogenix.org/contact addEvent(window, "load", sortables_init); var SORT_COLUMN_INDEX; function sortables_init() { // Find all tables with class sortable and make them sortable if (!document.getElementsByTagName) return; tbls = document.getElementsByTagName("table"); for (ti=0;ti 0) { var firstRow = table.rows[0]; } if (!firstRow) return; // We have a first row: assume it's the header, and make its contents clickable links for (var i=0;i' + txt+'   '; } } function ts_getInnerText(el) { if (typeof el == "string") return el; if (typeof el == "undefined") { return el }; if (el.innerText) return el.innerText; //Not needed but it is faster var str = ""; var cs = el.childNodes; var l = cs.length; for (var i = 0; i < l; i++) { switch (cs[i].nodeType) { case 1: //ELEMENT_NODE str += ts_getInnerText(cs[i]); break; case 3: //TEXT_NODE str += cs[i].nodeValue; break; } } return str; } function ts_resortTable(lnk,clid) { // get the span var span; for (var ci=0;ci # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; no utf8; use vars qw(%Conf %OrigConf); use lib "./lib"; use Encode; my $EncodeVersion = eval($Encode::VERSION); if ( $EncodeVersion < 1.99 ) { print("Error: you need to upgrade perl's Encode package.\n" . "I found $EncodeVersion and BackupPC needs >= 1.99\n" . "Please go to www.cpan.org or use the cpan command.\n"); exit(1); } my @Packages = qw(File::Path File::Spec File::Copy DirHandle Digest::MD5 Data::Dumper Getopt::Std Getopt::Long Pod::Usage BackupPC::Lib BackupPC::FileZIO); foreach my $pkg ( @Packages ) { eval "use $pkg"; next if ( !$@ ); if ( $pkg =~ /BackupPC/ ) { die < 0, -verbose => 2) if $opts{man}; my $DestDir = $opts{"dest-dir"}; $DestDir = "" if ( $DestDir eq "/" ); if ( !$opts{"uid-ignore"} && $< != 0 ) { print < Do you want to continue?", "y") !~ /y/i ); exit(1) if ( $opts{batch} && !$opts{"uid-ignore"} ); } # # Whether we use the file system hierarchy conventions or not. # Older versions did not. BackupPC used to be installed in # two main directories (in addition to CGI and html pages) # # TopDir which includes subdirs conf, log, pc, pool, cpool # # InstallDir which includes subdirs bin, lib, doc # # With FHS enabled (which is the default for new installations) # the config files move to /etc/BackupPC and log files to /var/log: # # /etc/BackupPC/config.pl main config file (was $TopDir/conf/config.pl) # /etc/BackupPC/hosts hosts file (was $TopDir/conf/hosts) # /etc/BackupPC/pc/HOST.pl per-pc config file (was $TopDir/pc/HOST/config.pl) # /var/log/BackupPC log files (was $TopDir/log) # /var/log/BackupPC Pid, status and email info (was $TopDir/log) # # # Check if this is an upgrade, in which case read the existing # config file to get all the defaults. # my $ConfigPath = ""; my $ConfigFileOK = 1; while ( 1 ) { if ( $ConfigFileOK && -f "/etc/BackupPC/config.pl" && (!defined($opts{fhs}) || $opts{fhs}) && !defined($opts{"config-path"}) ) { $ConfigPath = "/etc/BackupPC/config.pl"; $opts{fhs} = 1 if ( !defined($opts{fhs}) ); print < Full path to existing main config.pl", $ConfigPath, "config-path"); } last if ( $ConfigPath eq "" || ($ConfigPath =~ /^\// && -f $ConfigPath && -w $ConfigPath) ); my $problem = "is not an absolute path"; $problem = "is not writable" if ( !-w $ConfigPath ); $problem = "is not readable" if ( !-r $ConfigPath ); $problem = "is not a regular file" if ( !-f $ConfigPath ); $problem = "doesn't exist" if ( !-e $ConfigPath ); print("The file '$ConfigPath' $problem.\n"); if ( $opts{batch} ) { print("Need to specify a valid --config-path for upgrade\n"); exit(1); } $ConfigFileOK = 0; } $opts{fhs} = 1 if ( !defined($opts{fhs}) && $ConfigPath eq "" ); $opts{fhs} = 0 if ( !defined($opts{fhs}) ); my $bpc; if ( $ConfigPath ne "" && -r $ConfigPath ) { (my $confDir = $ConfigPath) =~ s{/[^/]+$}{}; die("BackupPC::Lib->new failed\n") if ( !($bpc = BackupPC::Lib->new(".", ".", $confDir, 1)) ); %Conf = $bpc->Conf(); %OrigConf = %Conf; if ( !$opts{fhs} ) { ($Conf{TopDir} = $ConfigPath) =~ s{/[^/]+/[^/]+$}{} if ( $Conf{TopDir} eq '' ); $bpc->{LogDir} = $Conf{LogDir} = "$Conf{TopDir}/log" if ( $Conf{LogDir} eq '' ); } $bpc->{ConfDir} = $Conf{ConfDir} = $confDir; my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}, 1); if ( $err eq "" ) { print < "PerlPath", 'gtar/tar' => "TarClientPath", smbclient => "SmbClientPath", nmblookup => "NmbLookupPath", rsync => "RsyncClientPath", ping => "PingPath", df => "DfPath", 'ssh/ssh2' => "SshPath", sendmail => "SendmailPath", hostname => "HostnamePath", split => "SplitPath", par2 => "ParPath", cat => "CatPath", gzip => "GzipPath", bzip2 => "Bzip2Path", ); foreach my $prog ( sort(keys(%Programs)) ) { my $path; foreach my $subProg ( split(/\//, $prog) ) { $path = FindProgram("$ENV{PATH}:/usr/bin:/bin:/sbin:/usr/sbin", $subProg) if ( !length($path) ); } $Conf{$Programs{$prog}} = $path if ( !length($Conf{$Programs{$prog}}) ); } while ( 1 ) { print < %s\n", $prog, $Conf{$Programs{$prog}}); } print "\n"; last if (prompt('--> Are these paths correct?', 'y') =~ /^y/i); foreach my $prog ( sort(keys(%Programs)) ) { $Conf{$Programs{$prog}} = prompt("--> $prog path", $Conf{$Programs{$prog}}); } } my $Perl58 = system($Conf{PerlPath} . q{ -e 'exit($^V && $^V ge v5.8.0 ? 1 : 0);'}); if ( !$Perl58 ) { print < BackupPC will run on host", $Conf{ServerHost}, "hostname"); print < BackupPC should run as user", $Conf{BackupPCUser} || "backuppc", "backuppc-user"); if ( $opts{"set-perms"} ) { ($name, $passwd, $Uid, $Gid) = getpwnam($Conf{BackupPCUser}); last if ( $name ne "" ); print < Install directory (full path)", $Conf{InstallDir}, "install-dir"); last if ( $Conf{InstallDir} =~ /^\// ); if ( $opts{batch} ) { print("Need to specify --install-dir for new installation\n"); exit(1); } } print < Data directory (full path)", $Conf{TopDir}, "data-dir"); last if ( $Conf{TopDir} =~ /^\// ); if ( $opts{batch} ) { print("Need to specify --data-dir for new installation\n"); exit(1); } } $Conf{CompressLevel} = $opts{"compress-level"} if ( defined($opts{"compress-level"}) ); if ( !defined($Conf{CompressLevel}) ) { $Conf{CompressLevel} = BackupPC::FileZIO->compOk ? 3 : 0; if ( $ConfigPath eq "" && $Conf{CompressLevel} ) { print < Compression level", $Conf{CompressLevel}); last if ( $Conf{CompressLevel} =~ /^\d+$/ ); } } print < CGI bin directory (full path)", $Conf{CgiDir}, "cgi-dir"); last if ( $Conf{CgiDir} =~ /^\// || $Conf{CgiDir} eq "" ); if ( $opts{batch} ) { print("Need to specify --cgi-dir for new installation\n"); exit(1); } } if ( $Conf{CgiDir} ne "" ) { print < Apache image directory (full path)", $Conf{CgiImageDir}, "html-dir"); last if ( $Conf{CgiImageDir} =~ /^\// ); if ( $opts{batch} ) { print("Need to specify --html-dir for new installation\n"); exit(1); } } while ( 1 ) { $Conf{CgiImageDirURL} = prompt("--> URL for image directory (omit http://host; starts with '/')", $Conf{CgiImageDirURL}, "html-dir-url"); last if ( $Conf{CgiImageDirURL} =~ /^\// ); if ( $opts{batch} ) { print("Need to specify --html-dir-url for new installation\n"); exit(1); } } } print < Do you want to continue?", "y") =~ /y/i; # # Create install directories # foreach my $dir ( qw(bin doc lib/BackupPC/CGI lib/BackupPC/Config lib/BackupPC/Lang lib/BackupPC/Storage lib/BackupPC/Xfer lib/BackupPC/Zip lib/Net/FTP ) ) { next if ( -d "$DestDir$Conf{InstallDir}/$dir" ); mkpath("$DestDir$Conf{InstallDir}/$dir", 0, 0755); if ( !-d "$DestDir$Conf{InstallDir}/$dir" || !my_chown($Uid, $Gid, "$DestDir$Conf{InstallDir}/$dir") ) { die("Failed to create or chown $DestDir$Conf{InstallDir}/$dir\n"); } else { print("Created $DestDir$Conf{InstallDir}/$dir\n"); } } # # Create CGI image directory # foreach my $dir ( ($Conf{CgiImageDir}) ) { next if ( $dir eq "" || -d "$DestDir$dir" ); mkpath("$DestDir$dir", 0, 0755); if ( !-d "$DestDir$dir" || !my_chown($Uid, $Gid, "$DestDir$dir") ) { die("Failed to create or chown $DestDir$dir"); } else { print("Created $DestDir$dir\n"); } } # # Create other directories # foreach my $dir ( ( "$Conf{TopDir}", "$Conf{TopDir}/pool", "$Conf{TopDir}/cpool", "$Conf{TopDir}/pc", "$Conf{TopDir}/trash", "$Conf{ConfDir}", "$Conf{LogDir}", ) ) { mkpath("$DestDir$dir", 0, 0750) if ( !-d "$DestDir$dir" ); if ( !-d "$DestDir$dir" || !my_chown($Uid, $Gid, "$DestDir$dir") ) { die("Failed to create or chown $DestDir$dir\n"); } else { print("Created $DestDir$dir\n"); } } printf("Installing binaries in $DestDir$Conf{InstallDir}/bin\n"); foreach my $prog ( qw( bin/BackupPC bin/BackupPC_archive bin/BackupPC_archiveHost bin/BackupPC_archiveStart bin/BackupPC_attribPrint bin/BackupPC_dump bin/BackupPC_fixupBackupSummary bin/BackupPC_link bin/BackupPC_nightly bin/BackupPC_restore bin/BackupPC_sendEmail bin/BackupPC_serverMesg bin/BackupPC_trashClean bin/BackupPC_tarExtract bin/BackupPC_tarCreate bin/BackupPC_tarPCCopy bin/BackupPC_zipCreate bin/BackupPC_zcat ) ) { InstallFile($prog, "$DestDir$Conf{InstallDir}/$prog", 0555); } printf("Installing library in $DestDir$Conf{InstallDir}/lib\n"); foreach my $lib ( qw( lib/BackupPC/Attrib.pm lib/BackupPC/Config.pm lib/BackupPC/FileZIO.pm lib/BackupPC/Lib.pm lib/BackupPC/PoolWrite.pm lib/BackupPC/Storage.pm lib/BackupPC/View.pm lib/BackupPC/CGI/AdminOptions.pm lib/BackupPC/CGI/Archive.pm lib/BackupPC/CGI/ArchiveInfo.pm lib/BackupPC/CGI/Browse.pm lib/BackupPC/CGI/DirHistory.pm lib/BackupPC/CGI/EditConfig.pm lib/BackupPC/CGI/EmailSummary.pm lib/BackupPC/CGI/GeneralInfo.pm lib/BackupPC/CGI/HostInfo.pm lib/BackupPC/CGI/Lib.pm lib/BackupPC/CGI/LOGlist.pm lib/BackupPC/CGI/Queue.pm lib/BackupPC/CGI/ReloadServer.pm lib/BackupPC/CGI/RestoreFile.pm lib/BackupPC/CGI/RestoreInfo.pm lib/BackupPC/CGI/Restore.pm lib/BackupPC/CGI/RSS.pm lib/BackupPC/CGI/StartServer.pm lib/BackupPC/CGI/StartStopBackup.pm lib/BackupPC/CGI/StopServer.pm lib/BackupPC/CGI/Summary.pm lib/BackupPC/CGI/View.pm lib/BackupPC/Config/Meta.pm lib/BackupPC/Lang/cz.pm lib/BackupPC/Lang/de.pm lib/BackupPC/Lang/en.pm lib/BackupPC/Lang/es.pm lib/BackupPC/Lang/fr.pm lib/BackupPC/Lang/it.pm lib/BackupPC/Lang/ja.pm lib/BackupPC/Lang/nl.pm lib/BackupPC/Lang/pl.pm lib/BackupPC/Lang/pt_br.pm lib/BackupPC/Lang/ru.pm lib/BackupPC/Lang/uk.pm lib/BackupPC/Lang/zh_CN.pm lib/BackupPC/Storage/Text.pm lib/BackupPC/Xfer.pm lib/BackupPC/Xfer/Archive.pm lib/BackupPC/Xfer/Ftp.pm lib/BackupPC/Xfer/Protocol.pm lib/BackupPC/Xfer/Rsync.pm lib/BackupPC/Xfer/RsyncDigest.pm lib/BackupPC/Xfer/RsyncFileIO.pm lib/BackupPC/Xfer/Smb.pm lib/BackupPC/Xfer/Tar.pm lib/BackupPC/Zip/FileMember.pm lib/Net/FTP/AutoReconnect.pm lib/Net/FTP/RetrHandle.pm ) ) { InstallFile($lib, "$DestDir$Conf{InstallDir}/$lib", 0444); } if ( $Conf{CgiImageDir} ne "" ) { printf("Installing images in $DestDir$Conf{CgiImageDir}\n"); foreach my $img ( ) { (my $destImg = $img) =~ s{^images/}{}; InstallFile($img, "$DestDir$Conf{CgiImageDir}/$destImg", 0444, 1); } # # Install new CSS file, making a backup copy if necessary # my $cssBackup = "$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css.pre-3.3.2"; if ( -f "$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css" && !-f $cssBackup ) { rename("$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css", $cssBackup); } InstallFile("conf/BackupPC_stnd.css", "$DestDir$Conf{CgiImageDir}/BackupPC_stnd.css", 0444, 0); InstallFile("conf/BackupPC_stnd_orig.css", "$DestDir$Conf{CgiImageDir}/BackupPC_stnd_orig.css", 0444, 0); InstallFile("conf/sorttable.js", "$DestDir$Conf{CgiImageDir}/sorttable.js", 0444, 0); } printf("Making init.d scripts\n"); foreach my $init ( qw(gentoo-backuppc gentoo-backuppc.conf linux-backuppc solaris-backuppc debian-backuppc freebsd-backuppc freebsd-backuppc2 suse-backuppc slackware-backuppc ) ) { InstallFile("init.d/src/$init", "init.d/$init", 0444); } printf("Making Apache configuration file for suid-perl\n"); InstallFile("httpd/src/BackupPC.conf", "httpd/BackupPC.conf", 0644); printf("Installing docs in $DestDir$Conf{InstallDir}/doc\n"); foreach my $doc ( qw(BackupPC.pod BackupPC.html) ) { InstallFile("doc/$doc", "$DestDir$Conf{InstallDir}/doc/$doc", 0444); } printf("Installing config.pl and hosts in $DestDir$Conf{ConfDir}\n"); InstallFile("conf/hosts", "$DestDir$Conf{ConfDir}/hosts", 0644) if ( !-f "$DestDir$Conf{ConfDir}/hosts" ); # # Now do the config file. If there is an existing config file we # merge in the new config file, adding any new configuration # parameters and deleting ones that are no longer needed. # my $dest = "$DestDir$Conf{ConfDir}/config.pl"; my ($distConf, $distVars) = ConfigParse("conf/config.pl"); my ($oldConf, $oldVars); my ($newConf, $newVars) = ($distConf, $distVars); if ( -f $dest ) { ($oldConf, $oldVars) = ConfigParse($dest); ($newConf, $newVars) = ConfigMerge($oldConf, $oldVars, $distConf, $distVars); } # # Update various config parameters. The old config is in Conf{} # and the new config is an array in text form in $newConf->[]. # $Conf{EMailFromUserName} ||= $Conf{BackupPCUser}; $Conf{EMailAdminUserName} ||= $Conf{BackupPCUser}; # # Guess $Conf{CgiURL} # if ( !defined($Conf{CgiURL}) ) { if ( $Conf{CgiDir} =~ m{cgi-bin(/.*)} ) { $Conf{CgiURL} = "'http://$Conf{ServerHost}/cgi-bin$1/BackupPC_Admin'"; } else { $Conf{CgiURL} = "'http://$Conf{ServerHost}/cgi-bin/BackupPC_Admin'"; } } # # The smbclient commands have moved from hard-coded to the config file. # $Conf{SmbClientArgs} no longer exists, so merge it into the new # commands if it still exists. # if ( defined($Conf{SmbClientArgs}) ) { if ( $Conf{SmbClientArgs} ne "" ) { foreach my $param ( qw(SmbClientRestoreCmd SmbClientFullCmd SmbClientIncrCmd) ) { $newConf->[$newVars->{$param}]{text} =~ s/(-E\s+-N)/$1 $Conf{SmbClientArgs}/; } } delete($Conf{SmbClientArgs}); } # # CSS is now stored in a file rather than a big config variable. # delete($Conf{CSSstylesheet}); # # The blackout timing settings are now stored in a list of hashes, rather # than three scalar parameters. # if ( defined($Conf{BlackoutHourBegin}) ) { $Conf{BlackoutPeriods} = [ { hourBegin => $Conf{BlackoutHourBegin}, hourEnd => $Conf{BlackoutHourEnd}, weekDays => $Conf{BlackoutWeekDays}, } ]; delete($Conf{BlackoutHourBegin}); delete($Conf{BlackoutHourEnd}); delete($Conf{BlackoutWeekDays}); } # # $Conf{RsyncLogLevel} has been replaced by $Conf{XferLogLevel} # if ( defined($Conf{RsyncLogLevel}) ) { $Conf{XferLogLevel} = $Conf{RsyncLogLevel}; delete($Conf{RsyncLogLevel}); } # # In 2.1.0 the default for $Conf{CgiNavBarAdminAllHosts} is now 1 # $Conf{CgiNavBarAdminAllHosts} = 1; # # IncrFill should now be off # $Conf{IncrFill} = 0; # # Empty $Conf{ParPath} if it isn't a valid executable # (pre-3.0.0 configure.pl incorrectly set it to a # hardcoded value). # $Conf{ParPath} = '' if ( $Conf{ParPath} ne '' && !-x $Conf{ParPath} ); # # Figure out sensible arguments for the ping command # if ( defined($Conf{PingArgs}) ) { $Conf{PingCmd} = '$pingPath ' . $Conf{PingArgs}; } elsif ( !defined($Conf{PingCmd}) ) { if ( $^O eq "solaris" || $^O eq "sunos" ) { $Conf{PingCmd} = '$pingPath -s $host 56 1'; } elsif ( ($^O eq "linux" || $^O eq "openbsd" || $^O eq "netbsd") && !system("$Conf{PingPath} -c 1 -w 3 localhost") ) { $Conf{PingCmd} = '$pingPath -c 1 -w 3 $host'; } else { $Conf{PingCmd} = '$pingPath -c 1 $host'; } delete($Conf{PingArgs}); } # # Figure out sensible arguments for the df command # if ( !defined($Conf{DfCmd}) ) { if ( $^O eq "solaris" || $^O eq "sunos" ) { $Conf{DfCmd} = '$dfPath -k $topDir'; } } # # $Conf{SmbClientTimeout} is now $Conf{ClientTimeout} # if ( defined($Conf{SmbClientTimeout}) ) { $Conf{ClientTimeout} = $Conf{SmbClientTimeout}; delete($Conf{SmbClientTimeout}); } # # Replace --devices with -D in RsyncArgs and RsyncRestoreArgs # foreach my $param ( qw(RsyncArgs RsyncRestoreArgs) ) { next if ( !defined($newVars->{$param}) ); $newConf->[$newVars->{$param}]{text} =~ s/--devices/-D/g; } # # Merge any new user-editable parameters into CgiUserConfigEdit # by copying the old settings forward. # if ( defined($Conf{CgiUserConfigEdit}) ) { # # This is a real hack. The config file merging is done in text # form without actually instantiating the new conf structure. # So we need to extract the new hash of settings, update it, # and merge the text. Ugh... # my $new; my $str = $distConf->[$distVars->{CgiUserConfigEdit}]{text}; $str =~ s/^\s*\$Conf\{.*?\}\s*=\s*/\$new = /m; eval($str); foreach my $p ( keys(%$new) ) { $new->{$p} = $Conf{CgiUserConfigEdit}{$p} if ( defined($Conf{CgiUserConfigEdit}{$p}) ); } $Conf{CgiUserConfigEdit} = $new; my $d = Data::Dumper->new([$new], [*value]); $d->Indent(1); $d->Terse(1); my $value = $d->Dump; $value =~ s/(.*)\n/$1;\n/s; $newConf->[$newVars->{CgiUserConfigEdit}]{text} =~ s/(\s*\$Conf\{.*?\}\s*=\s*).*/$1$value/s; } # # Apply any command-line configuration parameter settings # foreach my $param ( keys(%{$opts{"config-override"}}) ) { my $val = eval { $opts{"config-override"}{$param} }; if ( @$ ) { printf("Can't eval --config-override setting %s=%s\n", $param, $opts{"config-override"}{$param}); exit(1); } if ( !defined($newVars->{$param}) ) { printf("Unkown config parameter %s in --config-override\n", $param); exit(1); } $newConf->[$newVars->{$param}]{text} = $opts{"config-override"}{$param}; } # # Now backup and write the config file # my $confCopy = "$dest.pre-3.3.2"; if ( -f $dest && !-f $confCopy ) { # # Make copy of config file, preserving ownership and modes # printf("Making backup copy of $dest -> $confCopy\n"); my @stat = stat($dest); my $mode = $stat[2]; my $uid = $stat[4]; my $gid = $stat[5]; die("can't copy($dest, $confCopy)\n") unless copy($dest, $confCopy); die("can't chown $uid, $gid $confCopy\n") unless my_chown($uid, $gid, $confCopy); die("can't chmod $mode $confCopy\n") unless my_chmod($mode, $confCopy); } open(OUT, ">", $dest) || die("can't open $dest for writing\n"); binmode(OUT); my $blockComment; foreach my $var ( @$newConf ) { if ( length($blockComment) && substr($var->{text}, 0, length($blockComment)) eq $blockComment ) { $var->{text} = substr($var->{text}, length($blockComment)); $blockComment = undef; } $blockComment = $1 if ( $var->{text} =~ /^([\s\n]*#{70}.*#{70}[\s\n]+)/s ); $var->{text} =~ s/^\s*\$Conf\{(.*?)\}(\s*=\s*['"]?)(.*?)(['"]?\s*;)/ defined($Conf{$1}) && ref($Conf{$1}) eq "" && $Conf{$1} ne $OrigConf{$1} ? "\$Conf{$1}$2$Conf{$1}$4" : "\$Conf{$1}$2$3$4"/emg; print OUT $var->{text}; } close(OUT); if ( !defined($oldConf) ) { die("can't chmod 0640 mode $dest\n") unless my_chmod(0640, $dest); die("can't chown $Uid, $Gid $dest\n") unless my_chown($Uid, $Gid, $dest); } if ( $Conf{CgiDir} ne "" ) { printf("Installing cgi script BackupPC_Admin in $DestDir$Conf{CgiDir}\n"); mkpath("$DestDir$Conf{CgiDir}", 0, 0755); InstallFile("cgi-bin/BackupPC_Admin", "$DestDir$Conf{CgiDir}/BackupPC_Admin", 04554); } print <", $dest) || die("can't open $dest for writing\n"); binmode(PROG); binmode(OUT); while ( ) { s/__INSTALLDIR__/$Conf{InstallDir}/g; s/__LOGDIR__/$Conf{LogDir}/g; s/__CONFDIR__/$Conf{ConfDir}/g; s/__TOPDIR__/$Conf{TopDir}/g; s/^(\s*my \$useFHS\s*=\s*)\d;/${1}$opts{fhs};/ if ( $prog =~ /Lib.pm/ ); s/__BACKUPPCUSER__/$Conf{BackupPCUser}/g; s/__CGIDIR__/$Conf{CgiDir}/g; s/__IMAGEDIR__/$Conf{CgiImageDir}/g; s/__IMAGEDIRURL__/$Conf{CgiImageDirURL}/g; if ( $first && /^#.*bin\/perl/ ) { # # Fill in correct path to perl (no taint for >= 2.0.1). # print OUT "#!$Conf{PerlPath}\n"; } else { print OUT; } $first = 0; } close(PROG); close(OUT); } die("can't chown $uid, $gid $dest") unless my_chown($uid, $gid, $dest); die("can't chmod $mode $dest") unless my_chmod($mode, $dest); } sub FindProgram { my($path, $prog) = @_; if ( defined($opts{"bin-path"}{$prog}) ) { return $opts{"bin-path"}{$prog}; } foreach my $dir ( split(/:/, $path) ) { my $file = File::Spec->catfile($dir, $prog); return $file if ( -x $file ); } return; } sub ConfigParse { my($file) = @_; open(C, $file) || die("can't open $file"); binmode(C); my($out, @conf, $var); my $comment = 1; my $allVars = {}; my $endLine = undef; while ( ) { if ( /^#/ && !defined($endLine) ) { if ( $comment ) { $out .= $_; } else { if ( $out ne "" ) { $allVars->{$var} = @conf if ( defined($var) ); push(@conf, { text => $out, var => $var, }); } $var = undef; $comment = 1; $out = $_; } } elsif ( /^\s*\$Conf\{([^}]*)/ ) { $comment = 0; if ( defined($var) ) { $allVars->{$var} = @conf if ( defined($var) ); push(@conf, { text => $out, var => $var, }); $out = $_; } else { $out .= $_; } $var = $1; $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<(.*);/ ); $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<'(.*)';/ ); } else { $endLine = undef if ( defined($endLine) && /^\Q$endLine[\n\r]*$/ ); $out .= $_; } } if ( $out ne "" ) { $allVars->{$var} = @conf if ( defined($var) ); push(@conf, { text => $out, var => $var, }); } close(C); return (\@conf, $allVars); } sub ConfigMerge { my($old, $oldVars, $new, $newVars) = @_; my $posn = 0; my($res, $resVars); # # Find which config parameters are not needed any longer # foreach my $var ( @$old ) { next if ( !defined($var->{var}) || defined($newVars->{$var->{var}}) ); #print(STDERR "Deleting old config parameter $var->{var}\n"); $var->{delete} = 1; } # # Find which config parameters are new # foreach my $var ( @$new ) { next if ( !defined($var->{var}) ); if ( defined($oldVars->{$var->{var}}) ) { $posn = $oldVars->{$var->{var}}; } else { #print(STDERR "New config parameter $var->{var}: $var->{text}\n"); push(@{$old->[$posn]{new}}, $var); } } # # Create merged config file # foreach my $var ( @$old ) { next if ( $var->{delete} ); push(@$res, $var); foreach my $new ( @{$var->{new}} ) { push(@$res, $new); } } for ( my $i = 0 ; $i < @$res ; $i++ ) { $resVars->{$res->[$i]{var}} = $i; } return ($res, $resVars); } sub my_chown { my($uid, $gid, $file) = @_; return 1 if ( !$opts{"set-perms"} ); return chown($uid, $gid, $file); } sub my_chmod { my ($mode, $file) = @_; return 1 if ( !$opts{"set-perms"} ); return chmod($mode, $file); } sub prompt { my($question, $default, $option) = @_; $default = $opts{$option} if ( defined($opts{$option}) ); if ( $opts{batch} ) { print("$question [$default]\n"); return $default; } print("$question [$default]? "); my $reply = ; $reply =~ s/[\n\r]*//g; return $reply if ( $reply !~ /^$/ ); return $default; } __END__ =head1 SYNOPSIS configure.pl [options] =head1 DESCRIPTION configure.pl is a script that is used to install or upgrade a BackupPC installation. It is usually run interactively without arguments. It also supports a batch mode where all the options can be specified via the command-line. For upgrading BackupPC you need to make sure that BackupPC is not running prior to running BackupPC. Typically configure.pl needs to run as the super user (root). =head1 OPTIONS =over 8 =item B<--batch> Run configure.pl in batch mode. configure.pl will run without prompting the user. The other command-line options are used to specify the settings that the user is usually prompted for. =item B<--backuppc-user=USER> Specify the BackupPC user name that owns all the BackupPC files and runs the BackupPC programs. Default is backuppc. =item B<--bin-path PROG=PATH> Specify the path for various external programs that BackupPC uses. Several --bin-path options may be specified. configure.pl usually finds sensible defaults based on searching the PATH. The format is: --bin-path PROG=PATH where PROG is one of perl, tar, smbclient, nmblookup, rsync, ping, df, ssh, sendmail, hostname, split, par2, cat, gzip, bzip2 and PATH is that full path to that program. Examples --bin-path cat=/bin/cat --bin-path bzip2=/home/user/bzip2 =item B<--compress-level=N> Set the configuration compression level to N. Default is 3 if Compress::Zlib is installed. =item B<--config-dir CONFIG_DIR> Configuration directory for new installations. Defaults to /etc/BackupPC with FHS. Automatically extracted from --config-path for existing installations. =item B<--config-path CONFIG_PATH> Path to the existing config.pl configuration file for BackupPC. This option should be specified for batch upgrades to an existing installation. The option should be omitted when doing a batch new install. =item B<--cgi-dir CGI_DIR> Path to Apache's cgi-bin directory where the BackupPC_Admin script will be installed. This option only needs to be specified for a batch new install. =item B<--data-dir DATA_DIR> Path to the BackupPC data directory. This is where all the backup data is stored, and it should be on a large file system. This option only needs to be specified for a batch new install. Example: --data-dir /data/BackupPC =item B<--dest-dir DEST_DIR> An optional prefix to apply to all installation directories. Usually this is not needed, but certain auto-installers like to stage an install in a temporary directory, and then copy the files to their real destination. This option can be used to specify the temporary directory prefix. Note that if you specify this option, BackupPC won't run correctly if you try to run it from below the --dest-dir directory, since all the paths are set assuming BackupPC is installed in the intended final locations. =item B<--fhs> Use locations specified by the Filesystem Hierarchy Standard for installing BackupPC. This is enabled by default for new installations. To use the pre-3.0 installation locations, specify --no-fhs. =item B<--help|?> Print a brief help message and exits. =item B<--hostname HOSTNAME> Host name (this machine's name) on which BackupPC is being installed. This option only needs to be specified for a batch new install. =item B<--html-dir HTML_DIR> Path to an Apache html directory where various BackupPC image files and the CSS files will be installed. This is typically a directory below Apache's DocumentRoot directory. This option only needs to be specified for a batch new install. Example: --html-dir /var/www/htdocs/BackupPC =item B<--html-dir-url URL> The URL (without http://hostname) required to access the BackupPC html directory specified with the --html-dir option. This option only needs to be specified for a batch new install. Example: --html-dir-url /BackupPC =item B<--install-dir INSTALL_DIR> Installation directory for BackupPC scripts, libraries, and documentation. This option only needs to be specified for a batch new install. Example: --install-dir /usr/local/BackupPC =item B<--log-dir LOG_DIR> Log directory. Defaults to /var/log/BackupPC with FHS. =item B<--man> Prints the manual page and exits. =item B<--set-perms> When installing files and creating directories, chown them to the BackupPC user and chmod them too. This is enabled by default. To disable (for example, if staging a destination directory) then specify --no-set-perms. =item B<--uid-ignore> configure.pl verifies that the script is being run as the super user (root). Without the --uid-ignore option, in batch mode the script will exit with an error if not run as the super user, and in interactive mode the user will be prompted. Specifying this option will cause the script to continue even if the user id is not root. =head1 EXAMPLES For a standard interactive install, run without arguments: configure.pl For a batch new install you need to specify answers to all the questions that are normally prompted: configure.pl \ --batch \ --cgi-dir /var/www/cgi-bin/BackupPC \ --data-dir /data/BackupPC \ --hostname myHost \ --html-dir /var/www/html/BackupPC \ --html-dir-url /BackupPC \ --install-dir /usr/local/BackupPC For a batch upgrade, you only need to specify the path to the configuration file: configure.pl --batch --config-path /data/BackupPC/conf/config.pl =head1 AUTHOR Craig Barratt =head1 COPYRIGHT Copyright (C) 2001-2010 Craig Barratt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. =cut BackupPC-3.3.2/doc/0000755000076500000240000000000013042250554012664 5ustar craigstaffBackupPC-3.3.2/doc/BackupPC.html0000444000076500000240000070114613042250554015211 0ustar craigstaff BackupPC
 BackupPC

BackupPC Introduction

This documentation describes BackupPC version 3.3.2, released on 25 Jan 2017.

Overview

BackupPC is a high-performance, enterprise-grade system for backing up Unix, Linux, WinXX, and MacOSX PCs, desktops and laptops to a server's disk. BackupPC is highly configurable and easy to install and maintain.

Given the ever decreasing cost of disks and raid systems, it is now practical and cost effective to backup a large number of machines onto a server's local disk or network storage. For some sites this might be the complete backup solution. For other sites additional permanent archives could be created by periodically backing up the server to tape.

Features include:

  • A clever pooling scheme minimizes disk storage and disk I/O. Identical files across multiple backups of the same or different PC are stored only once (using hard links), resulting in substantial savings in disk storage and disk writes.

  • Optional compression provides additional reductions in storage (around 40%). The CPU impact of compression is low since only new files (those not already in the pool) need to be compressed.

  • A powerful http/cgi user interface allows administrators to view the current status, edit configuration, add/delete hosts, view log files, and allows users to initiate and cancel backups and browse and restore files from backups.

  • The http/cgi user interface has internationalization (i18n) support, currently providing English, French, German, Spanish, Italian, Dutch, Polish, Portuguese-Brazilian and Chinese

  • No client-side software is needed. On WinXX the standard smb protocol is used to extract backup data. On linux, unix or MacOSX clients, rsync, tar (over ssh/rsh/nfs) or ftp is used to extract backup data. Alternatively, rsync can also be used on WinXX (using cygwin), and Samba could be installed on the linux or unix client to provide smb shares).

  • Flexible restore options. Single files can be downloaded from any backup directly from the CGI interface. Zip or Tar archives for selected files or directories from any backup can also be downloaded from the CGI interface. Finally, direct restore to the client machine (using smb or tar) for selected files or directories is also supported from the CGI interface.

  • BackupPC supports mobile environments where laptops are only intermittently connected to the network and have dynamic IP addresses (DHCP). Configuration settings allow machines connected via slower WAN connections (eg: dial up, DSL, cable) to not be backed up, even if they use the same fixed or dynamic IP address as when they are connected directly to the LAN.

  • Flexible configuration parameters allow multiple backups to be performed in parallel, specification of which shares to backup, which directories to backup or not backup, various schedules for full and incremental backups, schedules for email reminders to users and so on. Configuration parameters can be set system-wide or also on a per-PC basis.

  • Users are sent periodic email reminders if their PC has not recently been backed up. Email content, timing and policies are configurable.

  • BackupPC is Open Source software hosted by SourceForge.

Backup basics

Full Backup

A full backup is a complete backup of a share. BackupPC can be configured to do a full backup at a regular interval (typically weekly). BackupPC can be configured to keep a certain number of full backups. Exponential expiry is also supported, allowing full backups with various vintages to be kept (for example, a settable number of most recent weekly fulls, plus a settable number of older fulls that are 2, 4, 8, or 16 weeks apart).

Incremental Backup

An incremental backup is a backup of files that have changed since the last successful full or incremental backup. Starting in BackupPC 3.0 multi-level incrementals are supported. A full backup has level 0. A new incremental of level N will backup all files that have changed since the most recent backup of a lower level. $Conf{IncrLevels} is used to specify the level of each successive incremental. The default value is all level 1, which makes the behavior the same as earlier versions of BackupPC: each incremental will back up all the files that changed since the last full (level 0).

For SMB and tar, BackupPC uses the modification time (mtime) to determine which files have changed since the last lower-level backup. That means SMB and tar incrementals are not able to detect deleted files, renamed files or new files whose modification time is prior to the last lower-level backup.

Rsync is more clever: any files whose attributes have changed (ie: uid, gid, mtime, modes, size) since the last full are backed up. Deleted, new files and renamed files are detected by Rsync incrementals.

BackupPC can also be configured to keep a certain number of incremental backups, and to keep a smaller number of very old incremental backups. If multi-level incrementals are specified then it is likely that more incrementals will need to be kept since lower-level incrementals (and the full backup) are needed to reconstruct a higher-level incremental.

BackupPC "fills-in" incremental backups when browsing or restoring, based on the levels of each backup, giving every backup a "full" appearance. This makes browsing and restoring backups much easier: you can restore from any one backup independent of whether it was an incremental or full.

Partial Backup

When a full backup fails or is canceled, and some files have already been backed up, BackupPC keeps a partial backup containing just the files that were backed up successfully. The partial backup is removed when the next successful backup completes, or if another full backup fails resulting in a newer partial backup. A failed full backup that has not backed up any files, or any failed incremental backup, is removed; no partial backup is saved in these cases.

The partial backup may be browsed or used to restore files just like a successful full or incremental backup.

With the rsync transfer method the partial backup is used to resume the next full backup, avoiding the need to retransfer the file data already in the partial backup.

Identical Files

BackupPC pools identical files using hardlinks. By "identical files" we mean files with identical contents, not necessary the same permissions, ownership or modification time. Two files might have different permissions, ownership, or modification time but will still be pooled whenever the contents are identical. This is possible since BackupPC stores the file meta-data (permissions, ownership, and modification time) separately from the file contents.

Backup Policy

Based on your site's requirements you need to decide what your backup policy is. BackupPC is not designed to provide exact re-imaging of failed disks. See "Some Limitations" for more information. However, the addition of tar transport for linux/unix clients, plus full support for special file types and unix attributes in v1.4.0 likely means an exact image of a linux/unix file system can be made.

BackupPC saves backups onto disk. Because of pooling you can relatively economically keep several weeks of old backups.

At some sites the disk-based backup will be adequate, without a secondary tape backup. This system is robust to any single failure: if a client disk fails or loses files, the BackupPC server can be used to restore files. If the server disk fails, BackupPC can be restarted on a fresh file system, and create new backups from the clients. The chance of the server disk failing can be made very small by spending more money on increasingly better RAID systems. However, there is still the risk of catastrophic events like fires or earthquakes that can destroy both the BackupPC server and the clients it is backing up if they are physically nearby.

Some sites might choose to do periodic backups to tape or cd/dvd. This backup can be done perhaps weekly using the archive function of BackupPC.

Other users have reported success with removable disks to rotate the BackupPC data drives, or using rsync to mirror the BackupPC data pool offsite.

Resources

BackupPC home page

The BackupPC Open Source project is hosted on Github and SourceForge. The project home page can be found at:

    http://backuppc.sourceforge.net

This page has links to the current documentation, the Github and SourceForge project pages and general information.

Github and SourceForge locations

The Github project page is at:

    https://github.com/backuppc/backuppc

The SourceForge project page is at:

    http://sourceforge.net/projects/backuppc

Generally use of SourceForge has been deprecated in favor of Github. All source code and development has moved to Github starting in 2016. Releases will continue to be available on both Github and SourceForge.

This page has links to the current releases of BackupPC.

BackupPC Wiki

BackupPC has a Wiki at https://github.com/backuppc/backuppc/wiki. Everyone is encouraged to contribute to the Wiki.

Mailing lists

Three BackupPC mailing lists exist for announcements (backuppc-announce), developers (backuppc-devel), and a general user list for support, asking questions or any other topic relevant to BackupPC (backuppc-users).

The lists are archived on SourceForge and Gmane. The SourceForge lists are not always up to date and the searching is limited, so Gmane is a good alternative. See:

    http://news.gmane.org/index.php?prefix=gmane.comp.sysutils.backup.backuppc
    http://sourceforge.net/mailarchive/forum.php?forum=backuppc-users

You can subscribe to these lists by visiting:

    http://lists.sourceforge.net/lists/listinfo/backuppc-announce
    http://lists.sourceforge.net/lists/listinfo/backuppc-users
    http://lists.sourceforge.net/lists/listinfo/backuppc-devel

The backuppc-announce list is moderated and is used only for important announcements (eg: new versions). It is low traffic. You only need to subscribe to one of backuppc-announce and backuppc-users: backuppc-users also receives any messages on backuppc-announce.

The backuppc-devel list is only for developers who are working on BackupPC. Do not post questions or support requests there. But detailed technical discussions should happen on this list.

To post a message to the backuppc-users list, send an email to

    backuppc-users@lists.sourceforge.net

Do not send subscription requests to this address!

Other Programs of Interest

If you want to mirror linux or unix files or directories to a remote server you should use rsync, http://rsync.samba.org. BackupPC uses rsync as a transport mechanism; if you are already an rsync user you can think of BackupPC as adding efficient storage (compression and pooling) and a convenient user interface to rsync.

Two popular open source packages that do tape backup are Amanda (http://www.amanda.org) and Bacula (http://www.bacula.org). These packages can be used as complete solutions, or also as back ends to BackupPC to backup the BackupPC server data to tape.

Various programs and scripts use rsync to provide hardlinked backups. See, for example, Mike Rubel's site (http://www.mikerubel.org/computers/rsync_snapshots), JW Schultz's dirvish (http://www.dirvish.org/), Ben Escoto's rdiff-backup (http://www.nongnu.org/rdiff-backup), and John Bowman's rlbackup (http://www.math.ualberta.ca/imaging/rlbackup).

Unison is a utility that can do two-way, interactive, synchronization. See http://freshmeat.net/projects/unison. An external wrapper around rsync that maintains transfer data to enable two-way synchronization is drsync; see http://freshmeat.net/projects/drsync.

BackupPC provides many additional features, such as compressed storage, hardlinking any matching files (rather than just files with the same name), and storing special files without root privileges. But these other programs provide simple, effective and fast solutions and are definitely worthy of consideration.

Road map

The new features planned for future releases of BackupPC are on the Wiki at http://backuppc.wiki.sourceforge.net.

Comments and suggestions are welcome.

You can help

BackupPC is free. I work on BackupPC because I enjoy doing it and I like to contribute to the open source community.

BackupPC already has more than enough features for my own needs. The main compensation for continuing to work on BackupPC is knowing that more and more people find it useful. So feedback is certainly appreciated, both positive and negative.

Beyond being a satisfied user and telling other people about it, everyone is encouraged to add links to http://backuppc.sourceforge.net (I'll see them via Google) or otherwise publicize BackupPC. Unlike the commercial products in this space, I have a zero budget (in both time and money) for marketing, PR and advertising, so it's up to all of you! Feel free to vote for BackupPC at http://freshmeat.net/projects/backuppc.

Also, everyone is encouraged to contribute patches, bug reports, feature and design suggestions, new code, Wiki additions (you can do those directly) and documentation corrections or improvements. Answering questions on the mailing list is a big help too.


Installing BackupPC

Requirements

BackupPC requires:

  • A linux, solaris, or unix based server with a substantial amount of free disk space (see the next section for what that means). The CPU and disk performance on this server will determine how many simultaneous backups you can run. You should be able to run 4-8 simultaneous backups on a moderately configured server.

    Several users have reported significantly better performance using reiserfs compared to ext3 for the BackupPC data file system. It is also recommended you consider either an LVM or RAID setup (either in HW or SW; eg: 3Ware RAID10 or RAID5) so that you can expand the file system as necessary.

    When BackupPC starts with an empty pool, all the backup data will be written to the pool on disk. After more backups are done, a higher percentage of incoming files will already be in the pool. BackupPC is able to avoid writing to disk new files that are already in the pool. So over time disk writes will reduce significantly (by perhaps a factor of 20 or more), since eventually 95% or more of incoming backup files are typically in the pool. Disk reads from the pool are still needed to do file compares to verify files are an exact match. So, with a mature pool, if a relatively fast client generates data at say 1MB/sec, and you run 4 simultaneous backups, there will be an average server disk load of about 4MB/sec reads and 0.2MB/sec writes (assuming 95% of the incoming files are in the pool). These rates will be perhaps 40% lower if compression is on.

  • Perl version 5.8.0 or later. If you don't have perl, please see http://www.cpan.org.

  • Perl modules Compress::Zlib, Archive::Zip and File::RsyncP. Try "perldoc Compress::Zlib" and "perldoc Archive::Zip" to see if you have these modules. If not, fetch them from http://www.cpan.org and see the instructions below for how to build and install them.

    The CGI Perl module is required for the http/cgi user interface. CGI was a core module, but from version 5.22 Perl no longer ships with it.

    The File::RsyncP module is available from http://perlrsync.sourceforge.net or CPAN. You'll need to install the File::RsyncP module if you want to use Rsync as a transport method.

  • If you are using smb to backup WinXX machines you need smbclient and nmblookup from the samba package. You will also need nmblookup if you are backing up linux/unix DHCP machines. See http://www.samba.org. Samba versions 3.x are stable and now recommended instead of 2.x.

    See http://www.samba.org for source and binaries. It's pretty easy to fetch and compile samba, and just grab smbclient and nmblookup, without doing the installation. Alternatively, http://www.samba.org has binary distributions for most platforms.

  • If you are using tar to backup linux/unix machines, those machines should have version 1.13.7 at a minimum, with version 1.13.20 or higher recommended. Use "tar --version" to check your version. Various GNU mirrors have the newest versions of tar; see http://www.gnu.org/software/tar/.

  • If you are using rsync to backup linux/unix machines you should have version 2.6.3 or higher on each client machine. See http://rsync.samba.org. Use "rsync --version" to check your version.

    For BackupPC to use Rsync you will also need to install the perl File::RsyncP module, which is available from http://perlrsync.sourceforge.net. Version 0.68 or later is required.

  • The Apache web server, see http://www.apache.org, preferably built with mod_perl support.

What type of storage space do I need?

BackupPC uses hardlinks to pool files common to different backups. Therefore BackupPC's data store (__TOPDIR__) must point to a single file system that supports hardlinks. You cannot split this file system with multiple mount points or using symbolic links to point a sub-directory to a different file system (it is ok to use a single symbolic link at the top-level directory (__TOPDIR__) to point the entire data store somewhere else). You can of course use any kind of RAID system or logical volume manager that combines the capacity of multiple disks into a single, larger, file system. Such approaches have the advantage that the file system can be expanded without having to copy it.

Any standard linux or unix file system supports hardlinks. NFS mounted file systems work too (provided the underlying file system supports hardlinks). But windows based FAT and NTFS file systems will not work.

Starting with BackupPC 3.1.0, run-time checks are done at startup and at the start of each backup to ensure that the file system can support hardlinks, since this is a common area of configuration problems.

How much disk space do I need?

Here's one real example for an environment that is backing up 65 laptops with compression off. Each full backup averages 3.2GB. Each incremental backup averages about 0.2GB. Storing one full backup and two incremental backups per laptop is around 240GB of raw data. But because of the pooling of identical files, only 87GB is used. This is without compression.

Another example, with compression on: backing up 95 laptops, where each backup averages 3.6GB and each incremental averages about 0.3GB. Keeping three weekly full backups, and six incrementals is around 1200GB of raw data. Because of pooling and compression, only 150GB is needed.

Here's a rule of thumb. Add up the disk usage of all the machines you want to backup (210GB in the first example above). This is a rough minimum space estimate that should allow a couple of full backups and at least half a dozen incremental backups per machine. If compression is on you can reduce the storage requirements by maybe 30-40%. Add some margin in case you add more machines or decide to keep more old backups.

Your actual mileage will depend upon the types of clients, operating systems and applications you have. The more uniform the clients and applications the bigger the benefit from pooling common files.

For example, the Eudora email tool stores each mail folder in a separate file, and attachments are extracted as separate files. So in the sadly common case of a large attachment emailed to many recipients, Eudora will extract the attachment into a new file. When these machines are backed up, only one copy of the file will be stored on the server, even though the file appears in many different full or incremental backups. In this sense Eudora is a "friendly" application from the point of view of backup storage requirements.

An example at the other end of the spectrum is Outlook. Everything (email bodies, attachments, calendar, contact lists) is stored in a single file, which often becomes huge. Any change to this file requires a separate copy of the file to be saved during backup. Outlook is even more troublesome, since it keeps this file locked all the time, so it cannot be read by smbclient whenever Outlook is running. See the "Some Limitations" section for more discussion of this problem.

In addition to total disk space, you should make sure you have plenty of inodes on your BackupPC data partition. Some users have reported running out of inodes on their BackupPC data partition. So even if you have plenty of disk space, BackupPC will report failures when the inodes are exhausted. This is a particular problem with ext2/ext3 file systems that have a fixed number of inodes when the file system is built. Use "df -i" to see your inode usage.

Step 1: Getting BackupPC

Some linux distributions now include BackupPC. The Debian distribution, supported by Ludovic Drolez, can be found at http://packages.debian.org/backuppc and is included in the current stable Debian release. On Debian, BackupPC can be installed with the command:

    apt-get install backuppc

In the future there might be packages for Gentoo and other linux flavors. If the packaged version is older than the released version then you may want to install the latest version as described below.

Otherwise, manually fetching and installing BackupPC is easy. Start by downloading the latest version from http://backuppc.sourceforge.net. Hit the "Code" button, then select the "backuppc" or "backuppc-beta" package and download the latest version.

Step 2: Installing the distribution

Note: most information in this step is only relevant if you build and install BackupPC yourself. If you use a package provided by a distribution, the package management system should take of installing any needed dependencies.

First off, there are five perl modules you should install. These are all optional, but highly recommended:

Compress::Zlib

To enable compression, you will need to install Compress::Zlib from http://www.cpan.org. You can run "perldoc Compress::Zlib" to see if this module is installed.

Archive::Zip

To support restore via Zip archives you will need to install Archive::Zip, also from http://www.cpan.org. You can run "perldoc Archive::Zip" to see if this module is installed.

XML::RSS

To support the RSS feature you will need to install XML::RSS, also from http://www.cpan.org. There is not need to install this module if you don't plan on using RSS. You can run "perldoc XML::RSS" to see if this module is installed.

File::RsyncP

To use rsync and rsyncd with BackupPC you will need to install File::RsyncP. You can run "perldoc File::RsyncP" to see if this module is installed. File::RsyncP is available from http://perlrsync.sourceforge.net. Version 0.68 or later is required.

File::Listing, Net::FTP, Net::FTP::RetrHandle, Net::FTP::AutoReconnect

To use ftp with BackupPC you will need four libraries, but actually need to install only File::Listing from http://www.cpan.org. You can run "perldoc File::Listing" to see if this module is installed. Net::FTP is a standard module. Net::FTP::RetrHandle and Net::FTP::AutoReconnect included in BackupPC distribution.

To build and install these packages you should use the cpan program. Alternatively, you can fetch the tar.gz file from http://www.cpan.org and then run these commands:

    tar zxvf Archive-Zip-1.26.tar.gz
    cd Archive-Zip-1.26
    perl Makefile.PL
    make
    make test
    make install

The same sequence of commands can be used for each module.

Now let's move onto BackupPC itself. After fetching BackupPC-3.3.2.tar.gz, run these commands as root:

    tar zxf BackupPC-3.3.2.tar.gz
    cd BackupPC-3.3.2
    perl configure.pl

In the future this release might also have patches available on the SourceForge site. These patch files are text files, with a name of the form

    BackupPC-3.3.2plN.diff

where N is the patch level, eg: pl2 is patch-level 2. These patch files are cumulative: you only need apply the last patch file, not all the earlier patch files. If a patch file is available, eg: BackupPC-3.3.2pl2.diff, you should apply the patch after extracting the tar file:

     # fetch BackupPC-3.3.2.tar.gz
     # fetch BackupPC-3.3.2pl2.diff
     tar zxf BackupPC-3.3.2.tar.gz
     cd BackupPC-3.3.2
     patch -p0 < ../BackupPC-3.3.2pl2.diff
     perl configure.pl

A patch file includes comments that describe that bug fixes and changes. Feel free to review it before you apply the patch.

The configure.pl script also accepts command-line options if you wish to run it in a non-interactive manner. It has self-contained documentation for all the command-line options, which you can read with perldoc:

    perldoc configure.pl

Starting with BackupPC 3.0.0, the configure.pl script by default complies with the file system hierarchy (FHS) conventions. The major difference compared to earlier versions is that by default configuration files will be stored in /etc/BackupPC rather than below the data directory, __TOPDIR__/conf, and the log files will be stored in /var/log/BackupPC rather than below the data directory, __TOPDIR__/log.

Note that distributions may choose to use different locations for BackupPC files than these defaults.

If you are upgrading from an earlier version the configure.pl script will keep the configuration files and log files in their original location.

When you run configure.pl you will be prompted for the full paths of various executables, and you will be prompted for the following information.

BackupPC User

It is best if BackupPC runs as a special user, eg backuppc, that has limited privileges. It is preferred that backuppc belongs to a system administrator group so that sys admin members can browse BackupPC files, edit the configuration files and so on. Although configurable, the default settings leave group read permission on pool files, so make sure the BackupPC user's group is chosen restrictively.

On this installation, this is __BACKUPPCUSER__.

For security purposes you might choose to configure the BackupPC user with the shell set to /bin/false. Since you might need to run some BackupPC programs as the BackupPC user for testing purposes, you can use the -s option to su to explicitly run a shell, eg:

    su -s /bin/bash __BACKUPPCUSER__

Depending upon your configuration you might also need the -l option.

Data Directory

You need to decide where to put the data directory, below which all the BackupPC data is stored. This needs to be a big file system.

On this installation, this is __TOPDIR__.

Install Directory

You should decide where the BackupPC scripts, libraries and documentation should be installed, eg: /usr/local/BackupPC.

On this installation, this is __INSTALLDIR__.

CGI bin Directory

You should decide where the BackupPC CGI script resides. This will usually be below Apache's cgi-bin directory.

It is also possible to use a different directory and use Apache's ``<Directory>'' directive to specifiy that location. See the Apache HTTP Server documentation for additional information.

On this installation, this is __CGIDIR__.

Apache image Directory

A directory where BackupPC's images are stored so that Apache can serve them. You should ensure this directory is readable by Apache and create a symlink to this directory from the BackupPC CGI bin Directory.

Config and Log Directories

In this installation the configuration and log directories are located in the following locations:

    __CONFDIR__/config.pl    main config file
    __CONFDIR__/hosts        hosts file
    __CONFDIR__/pc/HOST.pl   per-pc config file
    __LOGDIR__/BackupPC      log files, pid, status

The configure.pl script doesn't prompt for these locations but they can be set for new installations using command-line options.

Step 3: Setting up config.pl

After running configure.pl, browse through the config file, __CONFDIR__/config.pl, and make sure all the default settings are correct. In particular, you will need to decide whether to use smb, tar,or rsync or ftp transport (or whether to set it on a per-PC basis) and set the relevant parameters for that transport method. See the section "Step 5: Client Setup" for more details.

Step 4: Setting up the hosts file

The file __CONFDIR__/hosts contains the list of clients to backup. BackupPC reads this file in three cases:

  • Upon startup.

  • When BackupPC is sent a HUP (-1) signal. Assuming you installed the init.d script, you can also do this with "/etc/init.d/backuppc reload".

  • When the modification time of the hosts file changes. BackupPC checks the modification time once during each regular wakeup.

Whenever you change the hosts file (to add or remove a host) you can either do a kill -HUP BackupPC_pid or simply wait until the next regular wakeup period.

Each line in the hosts file contains three fields, separated by white space:

Host name

This is typically the host name or NetBios name of the client machine and should be in lower case. The host name can contain spaces (escape with a backslash), but it is not recommended.

Please read the section "How BackupPC Finds Hosts".

In certain cases you might want several distinct clients to refer to the same physical machine. For example, you might have a database you want to backup, and you want to bracket the backup of the database with shutdown/restart using $Conf{DumpPreUserCmd} and $Conf{DumpPostUserCmd}. But you also want to backup the rest of the machine while the database is still running. In the case you can specify two different clients in the host file, using any mnemonic name (eg: myhost_mysql and myhost), and use $Conf{ClientNameAlias} in myhost_mysql's config.pl to specify the real host name of the machine.

DHCP flag

Starting with v2.0.0 the way hosts are discovered has changed and now in most cases you should specify 0 for the DHCP flag, even if the host has a dynamically assigned IP address. Please read the section "How BackupPC Finds Hosts" to understand whether you need to set the DHCP flag.

You only need to set DHCP to 1 if your client machine doesn't respond to the NetBios multicast request:

    nmblookup myHost

but does respond to a request directed to its IP address:

    nmblookup -A W.X.Y.Z

If you do set DHCP to 1 on any client you will need to specify the range of DHCP addresses to search is specified in $Conf{DHCPAddressRanges}.

Note also that the $Conf{ClientNameAlias} feature does not work for clients with DHCP set to 1.

User name

This should be the unix login/email name of the user who "owns" or uses this machine. This is the user who will be sent email about this machine, and this user will have permission to stop/start/browse/restore backups for this host. Leave this blank if no specific person should receive email or be allowed to stop/start/browse/restore backups for this host. Administrators will still have full permissions.

More users

Additional user names, separate by commas and with no white space, can be specified. These users will also have full permission in the CGI interface to stop/start/browse/restore backups for this host. These users will not be sent email about this host.

The first non-comment line of the hosts file is special: it contains the names of the columns and should not be edited.

Here's a simple example of a hosts file:

    host        dhcp    user      moreUsers
    farside     0       craig     jim,dave
    larson      1       gary      andy

Step 5: Client Setup

Four methods for getting backup data from a client are supported: smb, tar, rsync and ftp. Smb or rsync are the preferred methods for WinXX clients and rsync or tar are the preferred methods for linux/unix/MacOSX clients.

The transfer method is set using the $Conf{XferMethod} configuration setting. If you have a mixed environment (ie: you will use smb for some clients and tar for others), you will need to pick the most common choice for $Conf{XferMethod} for the main config.pl file, and then override it in the per-PC config file for those hosts that will use the other method. (Or you could run two completely separate instances of BackupPC, with different data directories, one for WinXX and the other for linux/unix, but then common files between the different machine types will duplicated.)

Here are some brief client setup notes:

WinXX

One setup for WinXX clients is to set $Conf{XferMethod} to "smb". Actually, rsyncd is the better method for WinXX if you are prepared to run rsync/cygwin on your WinXX client.

If you want to use rsyncd for WinXX clients you can find a pre-packaged zip file on http://backuppc.sourceforge.net. The package is called cygwin-rsync. It contains rsync.exe, template setup files and the minimal set of cygwin libraries for everything to run. The README file contains instructions for running rsync as a service, so it starts automatically everytime you boot your machine. If you use rsync to backup WinXX machines, be sure to set $Conf{ClientCharset} correctly (eg: 'cp1252') so that the WinXX file name encoding is correctly converted to utf8.

Otherwise, to use SMB, you can either create shares for the data you want to backup or your can use the existing C$ share. To create a new share, open "My Computer", right click on the drive (eg: C), and select "Sharing..." (or select "Properties" and select the "Sharing" tab). In this dialog box you can enable sharing, select the share name and permissions.

All Windows NT based OS (NT, 2000, XP Pro), are configured by default to share the entire C drive as C$. This is a special share used for various administration functions, one of which is to grant access to backup operators. All you need to do is create a new domain user, specifically for backup. Then add the new backup user to the built in "Backup Operators" group. You now have backup capability for any directory on any computer in the domain in one easy step. This avoids using administrator accounts and only grants permission to do exactly what you want for the given user, i.e.: backup. Also, for additional security, you may wish to deny the ability for this user to logon to computers in the default domain policy.

If this machine uses DHCP you will also need to make sure the NetBios name is set. Go to Control Panel|System|Network Identification (on Win2K) or Control Panel|System|Computer Name (on WinXP). Also, you should go to Control Panel|Network Connections|Local Area Connection|Properties|Internet Protocol (TCP/IP)|Properties|Advanced|WINS and verify that NetBios is not disabled.

The relevant configuration settings are $Conf{SmbShareName}, $Conf{SmbShareUserName}, $Conf{SmbSharePasswd}, $Conf{SmbClientPath}, $Conf{SmbClientFullCmd}, $Conf{SmbClientIncrCmd} and $Conf{SmbClientRestoreCmd}.

BackupPC needs to know the smb share user name and password for a client machine that uses smb. The user name is specified in $Conf{SmbShareUserName}. There are four ways to tell BackupPC the smb share password:

  • As an environment variable BPC_SMB_PASSWD set before BackupPC starts. If you start BackupPC manually the BPC_SMB_PASSWD variable must be set manually first. For backward compatibility for v1.5.0 and prior, the environment variable PASSWD can be used if BPC_SMB_PASSWD is not set. Warning: on some systems it is possible to see environment variables of running processes.

  • Alternatively the BPC_SMB_PASSWD setting can be included in /etc/init.d/backuppc, in which case you must make sure this file is not world (other) readable.

  • As a configuration variable $Conf{SmbSharePasswd} in __CONFDIR__/config.pl. If you put the password here you must make sure this file is not world (other) readable.

  • As a configuration variable $Conf{SmbSharePasswd} in the per-PC configuration file (__CONFDIR__/pc/$host.pl or __TOPDIR__/pc/$host/config.pl in non-FHS versions of BackupPC). You will have to use this option if the smb share password is different for each host. If you put the password here you must make sure this file is not world (other) readable.

Placement and protection of the smb share password is a possible security risk, so please double-check the file and directory permissions. In a future version there might be support for encryption of this password, but a private key will still have to be stored in a protected place. Suggestions are welcome.

As an alternative to setting $Conf{XferMethod} to "smb" (using smbclient) for WinXX clients, you can use an smb network filesystem (eg: ksmbfs or similar) on your linux/unix server to mount the share, and then set $Conf{XferMethod} to "tar" (use tar on the network mounted file system).

Also, to make sure that file names with special characters are correctly transferred by smbclient you should make sure that the smb.conf file has (for samba 3.x):

    [global]
        unix charset = UTF8

UTF8 is the default setting, so if the parameter is missing then it is ok. With this setting $Conf{ClientCharset} should be emtpy, since smbclient has already converted the file names to utf8.

Linux/Unix

The preferred setup for linux/unix clients is to set $Conf{XferMethod} to "rsync", "rsyncd" or "tar".

You can use either rsync, smb, or tar for linux/unix machines. Smb requires that the Samba server (smbd) be run to provide the shares. Since the smb protocol can't represent special files like symbolic links and fifos, tar and rsync are the better transport methods for linux/unix machines. (In fact, by default samba makes symbolic links look like the file or directory that they point to, so you could get an infinite loop if a symbolic link points to the current or parent directory. If you really need to use Samba shares for linux/unix backups you should turn off the "follow symlinks" samba config setting. See the smb.conf manual page.)

The requirements for each Xfer Method are:

tar

You must have GNU tar on the client machine. Use "tar --version" or "gtar --version" to verify. The version should be at least 1.13.7, and 1.13.20 or greater is recommended. Tar is run on the client machine via rsh or ssh.

The relevant configuration settings are $Conf{TarClientPath}, $Conf{TarShareName}, $Conf{TarClientCmd}, $Conf{TarFullArgs}, $Conf{TarIncrArgs}, and $Conf{TarClientRestoreCmd}.

rsync

You should have at least rsync 2.6.3, and the latest version is recommended. Rsync is run on the remote client via rsh or ssh.

The relevant configuration settings are $Conf{RsyncClientPath}, $Conf{RsyncClientCmd}, $Conf{RsyncClientRestoreCmd}, $Conf{RsyncShareName}, $Conf{RsyncArgs}, and $Conf{RsyncRestoreArgs}.

rsyncd

You should have at least rsync 2.6.3, and the latest version is recommended. In this case the rsync daemon should be running on the client machine and BackupPC connects directly to it.

The relevant configuration settings are $Conf{RsyncdClientPort}, $Conf{RsyncdUserName}, $Conf{RsyncdPasswd}, $Conf{RsyncdAuthRequired}, $Conf{RsyncShareName}, $Conf{RsyncArgs}, $Conf{RsyncArgsExtra}, and $Conf{RsyncRestoreArgs}. $Conf{RsyncShareName} is the name of an rsync module (ie: the thing in square brackets in rsyncd's conf file -- see rsyncd.conf), not a file system path.

Be aware that rsyncd will remove the leading '/' from path names in symbolic links if you specify "use chroot = no" in the rsynd.conf file. See the rsyncd.conf manual page for more information.

ftp

You need to be running an ftp server on the client machine. The relevant configuration settings are $Conf{FtpShareName}, $Conf{FtpUserName}, $Conf{FtpPasswd}, $Conf{FtpBlockSize}, $Conf{FtpPort}, $Conf{FtpTimeout}, and $Conf{FtpFollowSymlinks}.

You need to set $Conf{ClientCharset} to the client's charset so that file names are correctly converted to utf8. Use "locale charmap" on the client to see its charset.

For linux/unix machines you should not backup "/proc". This directory contains a variety of files that look like regular files but they are special files that don't need to be backed up (eg: /proc/kcore is a regular file that contains physical memory). See $Conf{BackupFilesExclude}. It is safe to back up /dev since it contains mostly character-special and block-special files, which are correctly handed by BackupPC (eg: backing up /dev/hda5 just saves the block-special file information, not the contents of the disk).

Alternatively, rather than backup all the file systems as a single share ("/"), it is easier to restore a single file system if you backup each file system separately. To do this you should list each file system mount point in $Conf{TarShareName} or $Conf{RsyncShareName}, and add the --one-file-system option to $Conf{TarClientCmd} or $Conf{RsyncArgs}. In this case there is no need to exclude /proc explicitly since it looks like a different file system.

Next you should decide whether to run tar over ssh, rsh or nfs. Ssh is the preferred method. Rsh is not secure and therefore not recommended. Nfs will work, but you need to make sure that the BackupPC user (running on the server) has sufficient permissions to read all the files below the nfs mount.

Ssh allows BackupPC to run as a privileged user on the client (eg: root), since it needs sufficient permissions to read all the backup files. Ssh is setup so that BackupPC on the server (an otherwise low privileged user) can ssh as root on the client, without being prompted for a password. There are two common versions of ssh: v1 and v2. Here are some instructions for one way to setup ssh. (Check which version of SSH you have by typing "ssh" or "man ssh".)

MacOSX

In general this should be similar to Linux/Unix machines. In versions 10.4 and later, the native MacOSX tar works, and also supports resource forks. xtar is another option, and rsync works too (although the MacOSX-supplied rsync has an extension for extended attributes that is not compatible with standard rsync).

SSH Setup

SSH is a secure way to run tar or rsync on a backup client to extract the data. SSH provides strong authentication and encryption of the network data.

Note that if you run rsyncd (rsync daemon), ssh is not used. In this case, rsyncd provides its own authentication, but there is no encryption of network data. If you want encryption of network data you can use ssh to create a tunnel, or use a program like stunnel.

Setup instructions for ssh can be found at http://backuppc.sourceforge.net/faq/ssh.html or on the Wiki.

Clients that use DHCP

If a client machine uses DHCP BackupPC needs some way to find the IP address given the host name. One alternative is to set dhcp to 1 in the hosts file, and BackupPC will search a pool of IP addresses looking for hosts. More efficiently, it is better to set dhcp = 0 and provide a mechanism for BackupPC to find the IP address given the host name.

For WinXX machines BackupPC uses the NetBios name server to determine the IP address given the host name. For unix machines you can run nmbd (the NetBios name server) from the Samba distribution so that the machine responds to a NetBios name request. See the manual page and Samba documentation for more information.

Alternatively, you can set $Conf{NmbLookupFindHostCmd} to any command that returns the IP address given the host name.

Please read the section "How BackupPC Finds Hosts" for more details.

Step 6: Running BackupPC

The installation contains an init.d backuppc script that can be copied to /etc/init.d so that BackupPC can auto-start on boot. See init.d/README for further instructions.

BackupPC should be ready to start. If you installed the init.d script, then you should be able to run BackupPC with:

    /etc/init.d/backuppc start

(This script can also be invoked with "stop" to stop BackupPC and "reload" to tell BackupPC to reload config.pl and the hosts file.)

Otherwise, just run

     __INSTALLDIR__/bin/BackupPC -d

as user __BACKUPPCUSER__. The -d option tells BackupPC to run as a daemon (ie: it does an additional fork).

Any immediate errors will be printed to stderr and BackupPC will quit. Otherwise, look in __LOGDIR__/LOG and verify that BackupPC reports it has started and all is ok.

Step 7: Talking to BackupPC

You should verify that BackupPC is running by using BackupPC_serverMesg. This sends a message to BackupPC via the unix (or TCP) socket and prints the response. Like all BackupPC programs, BackupPC_serverMesg should be run as the BackupPC user (__BACKUPPCUSER__), so you should

    su __BACKUPPCUSER__

before running BackupPC_serverMesg. If the BackupPC user is configured with /bin/false as the shell, you can use the -s option to su to explicitly run a shell, eg:

    su -s /bin/bash __BACKUPPCUSER__

Depending upon your configuration you might also need the -l option.

You can request status information and start and stop backups using this interface. This socket interface is mainly provided for the CGI interface (and some of the BackupPC sub-programs use it too). But right now we just want to make sure BackupPC is happy. Each of these commands should produce some status output:

    __INSTALLDIR__/bin/BackupPC_serverMesg status info
    __INSTALLDIR__/bin/BackupPC_serverMesg status jobs
    __INSTALLDIR__/bin/BackupPC_serverMesg status hosts

The output should be some hashes printed with Data::Dumper. If it looks cryptic and confusing, and doesn't look like an error message, then all is ok.

The jobs status should initially show just BackupPC_trashClean. The hosts status should produce a list of every host you have listed in __CONFDIR__/hosts as part of a big cryptic output line.

You can also request that all hosts be queued:

    __INSTALLDIR__/bin/BackupPC_serverMesg backup all

At this point you should make sure the CGI interface works since it will be much easier to see what is going on. That's our next subject.

Step 8: Checking email delivery

The script BackupPC_sendEmail sends status and error emails to the administrator and users. It is usually run each night by BackupPC_nightly.

To verify that it can run sendmail and deliver email correctly you should ask it to send a test email to you:

    su __BACKUPPCUSER__
    __INSTALLDIR__/bin/BackupPC_sendEmail -u MYNAME@MYDOMAIN.COM

BackupPC_sendEmail also takes a -c option that checks if BackupPC is running, and it sends an email to $Conf{EMailAdminUserName} if it is not. That can be used as a keep-alive check by adding

    __INSTALLDIR__/bin/BackupPC_sendEmail -c

to __BACKUPPCUSER__'s cron.

The -t option to BackupPC_sendEmail causes it to print the email message instead of invoking sendmail to deliver the message.

Step 9: CGI interface

The CGI interface script, BackupPC_Admin, is a powerful and flexible way to see and control what BackupPC is doing. It is written for an Apache server. If you don't have Apache, see http://www.apache.org.

There are two options for setting up the CGI interface: standard mode and using mod_perl. Mod_perl provides much higher performance (around 15x) and is the best choice if your Apache was built with mod_perl support. To see if your apache was built with mod_perl run this command:

    httpd -l | egrep mod_perl

If this prints mod_perl.c then your Apache supports mod_perl.

Note: on some distributions (like Debian) the command is not ``httpd'', but ``apache'' or ``apache2''. Those distributions will generally also use ``apache'' for the Apache user account and configuration files.

Using mod_perl with BackupPC_Admin requires a dedicated Apache to be run as the BackupPC user (__BACKUPPCUSER__). This is because BackupPC_Admin needs permission to access various files in BackupPC's data directories. In contrast, the standard installation (without mod_perl) solves this problem by having BackupPC_Admin installed as setuid to the BackupPC user, so that BackupPC_Admin runs as the BackupPC user.

Here are some specifics for each setup:

Standard Setup

The CGI interface should have been installed by the configure.pl script in __CGIDIR__/BackupPC_Admin. BackupPC_Admin should have been installed as setuid to the BackupPC user (__BACKUPPCUSER__), in addition to user and group execute permission.

You should be very careful about permissions on BackupPC_Admin and the directory __CGIDIR__: it is important that normal users cannot directly execute or change BackupPC_Admin, otherwise they can access backup files for any PC. You might need to change the group ownership of BackupPC_Admin to a group that Apache belongs to so that Apache can execute it (don't add "other" execute permission!). The permissions should look like this:

    ls -l __CGIDIR__/BackupPC_Admin
    -swxr-x---    1 __BACKUPPCUSER__   web      82406 Jun 17 22:58 __CGIDIR__/BackupPC_Admin

The setuid script won't work unless perl on your machine was installed with setuid emulation. This is likely the problem if you get an error saying such as "Wrong user: my userid is 25, instead of 150", meaning the script is running as the httpd user, not the BackupPC user. This is because setuid scripts are disabled by the kernel in most flavors of unix and linux.

To see if your perl has setuid emulation, see if there is a program called sperl5.8.0 (or sperl5.8.2 etc, based on your perl version) in the place where perl is installed. If you can't find this program, then you have two options: rebuild and reinstall perl with the setuid emulation turned on (answer "y" to the question "Do you want to do setuid/setgid emulation?" when you run perl's configure script), or switch to the mod_perl alternative for the CGI script (which doesn't need setuid to work).

Mod_perl Setup

The advantage of the mod_perl setup is that no setuid script is needed, and there is a huge performance advantage. Not only does all the perl code need to be parsed just once, the config.pl and hosts files, plus the connection to the BackupPC server are cached between requests. The typical speedup is around 15 times.

To use mod_perl you need to run Apache as user __BACKUPPCUSER__. If you need to run multiple Apache's for different services then you need to create multiple top-level Apache directories, each with their own config file. You can make copies of /etc/init.d/httpd and use the -d option to httpd to point each http to a different top-level directory. Or you can use the -f option to explicitly point to the config file. Multiple Apache's will run on different Ports (eg: 80 is standard, 8080 is a typical alternative port accessed via http://yourhost.com:8080).

Inside BackupPC's Apache http.conf file you should check the settings for ServerRoot, DocumentRoot, User, Group, and Port. See http://httpd.apache.org/docs/server-wide.html for more details.

For mod_perl, BackupPC_Admin should not have setuid permission, so you should turn it off:

    chmod u-s __CGIDIR__/BackupPC_Admin

To tell Apache to use mod_perl to execute BackupPC_Admin, add this to Apache's 1.x httpd.conf file:

    <IfModule mod_perl.c>
        PerlModule Apache::Registry
        PerlTaintCheck On
        <Location /cgi-bin/BackupPC/BackupPC_Admin>   # <--- change path as needed
           SetHandler perl-script
           PerlHandler Apache::Registry
           Options ExecCGI
           PerlSendHeader On
        </Location>
    </IfModule>

Apache 2.0.44 with Perl 5.8.0 on RedHat 7.1, Don Silvia reports that this works (with tweaks from Michael Tuzi):

    LoadModule perl_module modules/mod_perl.so
    PerlModule Apache2

    <Directory /path/to/cgi/>
        SetHandler perl-script
        PerlResponseHandler ModPerl::Registry
        PerlOptions +ParseHeaders
        Options +ExecCGI
        Order deny,allow
        Deny from all
        Allow from 192.168.0  
        AuthName "Backup Admin"
        AuthType Basic
        AuthUserFile /path/to/user_file
        Require valid-user
    </Directory>

There are other optimizations and options with mod_perl. For example, you can tell mod_perl to preload various perl modules, which saves memory compared to loading separate copies in every Apache process after they are forked. See Stas's definitive mod_perl guide at http://perl.apache.org/guide.

BackupPC_Admin requires that users are authenticated by Apache. Specifically, it expects that Apache sets the REMOTE_USER environment variable when it runs. There are several ways to do this. One way is to create a .htaccess file in the cgi-bin directory that looks like:

    AuthGroupFile /etc/httpd/conf/group    # <--- change path as needed
    AuthUserFile /etc/http/conf/passwd     # <--- change path as needed
    AuthType basic
    AuthName "access"
    require valid-user

You will also need "AllowOverride Indexes AuthConfig" in the Apache httpd.conf file to enable the .htaccess file. Alternatively, everything can go in the Apache httpd.conf file inside a Location directive. The list of users and password file above can be extracted from the NIS passwd file.

One alternative is to use LDAP. In Apache's http.conf add these lines:

    LoadModule auth_ldap_module   modules/auth_ldap.so
    AddModule auth_ldap.c

    # cgi-bin - auth via LDAP (for BackupPC)
    <Location /cgi-binBackupPC/BackupPC_Admin>    # <--- change path as needed
      AuthType Basic
      AuthName "BackupPC login"
      # replace MYDOMAIN, PORT, ORG and CO as needed
      AuthLDAPURL ldap://ldap.MYDOMAIN.com:PORT/o=ORG,c=CO?uid?sub?(objectClass=*)
      require valid-user
    </Location>

If you want to disable the user authentication you can set $Conf{CgiAdminUsers} to '*', which allows any user to have full access to all hosts and backups. In this case the REMOTE_USER environment variable does not have to be set by Apache.

Alternatively, you can force a particular user name by getting Apache to set REMOTE_USER, eg, to hardcode the user to www you could add this to Apache's httpd.conf:

    <Location /cgi-bin/BackupPC/BackupPC_Admin>   # <--- change path as needed
        Setenv REMOTE_USER www
    </Location>

Finally, you should also edit the config.pl file and adjust, as necessary, the CGI-specific settings. They're near the end of the config file. In particular, you should specify which users or groups have administrator (privileged) access: see the config settings $Conf{CgiAdminUserGroup} and $Conf{CgiAdminUsers}. Also, the configure.pl script placed various images into $Conf{CgiImageDir} that BackupPC_Admin needs to serve up. You should make sure that $Conf{CgiImageDirURL} is the correct URL for the image directory.

See the section "Fixing installation problems" for suggestions on debugging the Apache authentication setup.

How BackupPC Finds Hosts

Starting with v2.0.0 the way hosts are discovered has changed. In most cases you should specify 0 for the DHCP flag in the conf/hosts file, even if the host has a dynamically assigned IP address.

BackupPC (starting with v2.0.0) looks up hosts with DHCP = 0 in this manner:

  • First DNS is used to lookup the IP address given the client's name using perl's gethostbyname() function. This should succeed for machines that have fixed IP addresses that are known via DNS. You can manually see whether a given host have a DNS entry according to perl's gethostbyname function with this command:

        perl -e 'print(gethostbyname("myhost") ? "ok\n" : "not found\n");'
  • If gethostbyname() fails, BackupPC then attempts a NetBios multicast to find the host. Provided your client machine is configured properly, it should respond to this NetBios multicast request. Specifically, BackupPC runs a command of this form:

        nmblookup myhost

    If this fails you will see output like:

        querying myhost on 10.10.255.255
        name_query failed to find name myhost

    If it is successful you will see output like:

        querying myhost on 10.10.255.255
        10.10.1.73 myhost<00>

    Depending on your netmask you might need to specify the -B option to nmblookup. For example:

        nmblookup -B 10.10.1.255 myhost

    If necessary, experiment with the nmblookup command which will return the IP address of the client given its name. Then update $Conf{NmbLookupFindHostCmd} with any necessary options to nmblookup.

For hosts that have the DHCP flag set to 1, these machines are discovered as follows:

  • A DHCP address pool ($Conf{DHCPAddressRanges}) needs to be specified. BackupPC will check the NetBIOS name of each machine in the range using a command of the form:

        nmblookup -A W.X.Y.Z

    where W.X.Y.Z is each candidate address from $Conf{DHCPAddressRanges}. Any host that has a valid NetBIOS name returned by this command (ie: matching an entry in the hosts file) will be backed up. You can modify the specific nmblookup command if necessary via $Conf{NmbLookupCmd}.

  • You only need to use this DHCP feature if your client machine doesn't respond to the NetBios multicast request:

        nmblookup myHost

    but does respond to a request directed to its IP address:

        nmblookup -A W.X.Y.Z

Other installation topics

Removing a client

If there is a machine that no longer needs to be backed up (eg: a retired machine) you have two choices. First, you can keep the backups accessible and browsable, but disable all new backups. Alternatively, you can completely remove the client and all its backups.

To disable backups for a client $Conf{BackupsDisable} can be set to two different values in that client's per-PC config.pl file:

  1. Don't do any regular backups on this machine. Manually requested backups (via the CGI interface) will still occur.

  2. Don't do any backups on this machine. Manually requested backups (via the CGI interface) will be ignored.

This will still allow the client's old backups to be browsable and restorable.

To completely remove a client and all its backups, you should remove its entry in the conf/hosts file, and then delete the __TOPDIR__/pc/$host directory. Whenever you change the hosts file, you should send BackupPC a HUP (-1) signal so that it re-reads the hosts file. If you don't do this, BackupPC will automatically re-read the hosts file at the next regular wakeup.

Note that when you remove a client's backups you won't initially recover much disk space. That's because the client's files are still in the pool. Overnight, when BackupPC_nightly next runs, all the unused pool files will be deleted and this will recover the disk space used by the client's backups.

Copying the pool

If the pool disk requirements grow you might need to copy the entire data directory to a new (bigger) file system. Hopefully you are lucky enough to avoid this by having the data directory on a RAID file system or LVM that allows the capacity to be grown in place by adding disks.

The backup data directories contain large numbers of hardlinks. If you try to copy the pool the target directory will occupy a lot more space if the hardlinks aren't re-established.

The best way to copy a pool file system, if possible, is by copying the raw device at the block level (eg: using dd). Application level programs that understand hardlinks include the GNU cp program with the -a option and rsync -H. However, the large number of hardlinks in the pool will make the memory usage large and the copy very slow. Don't forget to stop BackupPC while the copy runs.

Starting in 3.0.0 a new script bin/BackupPC_tarPCCopy can be used to assist the copy process. Given one or more pc paths (eg: TOPDIR/pc/HOST or TOPDIR/pc/HOST/nnn), BackupPC_tarPCCopy creates a tar archive with all the hardlinks pointing to ../cpool/.... Any files not hardlinked (eg: backups, LOG etc) are included verbatim.

You will need to specify the -P option to tar when you extract the archive generated by BackupPC_tarPCCopy since the hardlink targets are outside of the directory being extracted.

To copy a complete store (ie: __TOPDIR__) using BackupPC_tarPCCopy you should:

  • stop BackupPC so that the store is static.

  • copy the cpool, conf and log directory trees using any technique (like cp, rsync or tar) without the need to preserve hardlinks.

  • copy the pc directory using BackupPC_tarPCCopy:

        su __BACKUPPCUSER__
        cd NEW_TOPDIR
        mkdir pc
        cd pc
        __INSTALLDIR__/bin/BackupPC_tarPCCopy __TOPDIR__/pc | tar xvPf -

Fixing installation problems

Please see the Wiki at http://backuppc.wiki.sourceforge.net for debugging suggestions. If you find a solution to your problem that could help other users please add it to the Wiki!


Restore functions

BackupPC supports several different methods for restoring files. The most convenient restore options are provided via the CGI interface. Alternatively, backup files can be restored using manual commands.

CGI restore options

By selecting a host in the CGI interface, a list of all the backups for that machine will be displayed. By selecting the backup number you can navigate the shares and directory tree for that backup.

BackupPC's CGI interface automatically fills incremental backups with the corresponding full backup, which means each backup has a filled appearance. Therefore, there is no need to do multiple restores from the incremental and full backups: BackupPC does all the hard work for you. You simply select the files and directories you want from the correct backup vintage in one step.

You can download a single backup file at any time simply by selecting it. Your browser should prompt you with the file name and ask you whether to open the file or save it to disk.

Alternatively, you can select one or more files or directories in the currently selected directory and select "Restore selected files". (If you need to restore selected files and directories from several different parent directories you will need to do that in multiple steps.)

If you select all the files in a directory, BackupPC will replace the list of files with the parent directory. You will be presented with a screen that has three options:

Option 1: Direct Restore

With this option the selected files and directories are restored directly back onto the host, by default in their original location. Any old files with the same name will be overwritten, so use caution. You can optionally change the target host name, target share name, and target path prefix for the restore, allowing you to restore the files to a different location.

Once you select "Start Restore" you will be prompted one last time with a summary of the exact source and target files and directories before you commit. When you give the final go ahead the restore operation will be queued like a normal backup job, meaning that it will be deferred if there is a backup currently running for that host. When the restore job is run, smbclient, tar, rsync or rsyncd is used (depending upon $Conf{XferMethod}) to actually restore the files. Sorry, there is currently no option to cancel a restore that has been started. Currently ftp restores are not fully implemented.

A record of the restore request, including the result and list of files and directories, is kept. It can be browsed from the host's home page. $Conf{RestoreInfoKeepCnt} specifies how many old restore status files to keep.

Note that for direct restore to work, the $Conf{XferMethod} must be able to write to the client. For example, that means an SMB share for smbclient needs to be writable, and the rsyncd module needs "read only" set to "false". This creates additional security risks. If you only create read-only SMB shares (which is a good idea), then the direct restore will fail. You can disable the direct restore option by setting $Conf{SmbClientRestoreCmd}, $Conf{TarClientRestoreCmd} and $Conf{RsyncRestoreArgs} to undef.

Option 2: Download Zip archive

With this option a zip file containing the selected files and directories is downloaded. The zip file can then be unpacked or individual files extracted as necessary on the host machine. The compression level can be specified. A value of 0 turns off compression.

When you select "Download Zip File" you should be prompted where to save the restore.zip file.

BackupPC does not consider downloading a zip file as an actual restore operation, so the details are not saved for later browsing as in the first case. However, a mention that a zip file was downloaded by a particular user, and a list of the files, does appear in BackupPC's log file.

Option 3: Download Tar archive

This is identical to the previous option, except a tar file is downloaded rather than a zip file (and there is currently no compression option).

Command-line restore options

Apart from the CGI interface, BackupPC allows you to restore files and directories from the command line. The following programs can be used:

BackupPC_zcat

For each file name argument it inflates (uncompresses) the file and writes it to stdout. To use BackupPC_zcat you could give it the full file name, eg:

    __INSTALLDIR__/bin/BackupPC_zcat __TOPDIR__/pc/host/5/fc/fcraig/fexample.txt > example.txt

It's your responsibility to make sure the file is really compressed: BackupPC_zcat doesn't check which backup the requested file is from. BackupPC_zcat returns a non-zero status if it fails to uncompress a file.

BackupPC_tarCreate

BackupPC_tarCreate creates a tar file for any files or directories in a particular backup. Merging of incrementals is done automatically, so you don't need to worry about whether certain files appear in the incremental or full backup.

The usage is:

    BackupPC_tarCreate [options] files/directories...
    Required options:
       -h host         host from which the tar archive is created
       -n dumpNum      dump number from which the tar archive is created
                       A negative number means relative to the end (eg -1
                       means the most recent dump, -2 2nd most recent etc).
       -s shareName    share name from which the tar archive is created
  
    Other options:
       -t              print summary totals
       -r pathRemove   path prefix that will be replaced with pathAdd
       -p pathAdd      new path prefix
       -b BLOCKS       BLOCKS x 512 bytes per record (default 20; same as tar)
       -w writeBufSz   write buffer size (default 1048576 = 1MB)
       -e charset      charset for encoding file names (default: value of
                       $Conf{ClientCharset} when backup was done)
       -l              just print a file listing; don't generate an archive
       -L              just print a detailed file listing; don't generate an archive

The command-line files and directories are relative to the specified shareName. The tar file is written to stdout.

The -h, -n and -s options specify which dump is used to generate the tar archive. The -r and -p options can be used to relocate the paths in the tar archive so extracted files can be placed in a location different from their original location.

BackupPC_zipCreate

BackupPC_zipCreate creates a zip file for any files or directories in a particular backup. Merging of incrementals is done automatically, so you don't need to worry about whether certain files appear in the incremental or full backup.

The usage is:

    BackupPC_zipCreate [options] files/directories...
    Required options:
       -h host         host from which the zip archive is created
       -n dumpNum      dump number from which the tar archive is created
                       A negative number means relative to the end (eg -1
                       means the most recent dump, -2 2nd most recent etc).
       -s shareName    share name from which the zip archive is created
  
    Other options:
       -t              print summary totals
       -r pathRemove   path prefix that will be replaced with pathAdd
       -p pathAdd      new path prefix
       -c level        compression level (default is 0, no compression)
       -e charset      charset for encoding file names (default: utf8)

The command-line files and directories are relative to the specified shareName. The zip file is written to stdout. The -h, -n and -s options specify which dump is used to generate the zip archive. The -r and -p options can be used to relocate the paths in the zip archive so extracted files can be placed in a location different from their original location.

Each of these programs reside in __INSTALLDIR__/bin.


Archive functions

BackupPC supports archiving to removable media. For users that require offsite backups, BackupPC can create archives that stream to tape devices, or create files of specified sizes to fit onto cd or dvd media.

Each archive type is specified by a BackupPC host with its XferMethod set to 'archive'. This allows for multiple configurations at sites where there might be a combination of tape and cd/dvd backups being made.

BackupPC provides a menu that allows one or more hosts to be archived. The most recent backup of each host is archived using BackupPC_tarCreate, and the output is optionally compressed and split into fixed-sized files (eg: 650MB).

The archive for each host is done by default using __INSTALLDIR__/bin/BackupPC_archiveHost. This script can be copied and customized as needed.

Configuring an Archive Host

To create an Archive Host, add it to the hosts file just as any other host and call it a name that best describes the type of archive, e.g. ArchiveDLT

To tell BackupPC that the Host is for Archives, create a config.pl file in the Archive Hosts's pc directory, adding the following line:

$Conf{XferMethod} = 'archive';

To further customise the archive's parameters you can adding the changed parameters in the host's config.pl file. The parameters are explained in the config.pl file. Parameters may be fixed or the user can be allowed to change them (eg: output device).

The per-host archive command is $Conf{ArchiveClientCmd}. By default this invokes

     __INSTALLDIR__/bin/BackupPC_archiveHost

which you can copy and customize as necessary.

Starting an Archive

In the web interface, click on the Archive Host you wish to use. You will see a list of previous archives and a summary on each. By clicking the "Start Archive" button you are presented with the list of hosts and the approximate backup size (note this is raw size, not projected compressed size) Select the hosts you wish to archive and press the "Archive Selected Hosts" button.

The next screen allows you to adjust the parameters for this archive run. Press the "Start the Archive" to start archiving the selected hosts with the parameters displayed.

Starting an Archive from the command line

The script BackupPC_archiveStart can be used to start an archive from the command line (or cron etc). The usage is:

    BackupPC_archiveStart archiveHost userName hosts...

This creates an archive of the most recent backup of each of the specified hosts. The first two arguments are the archive host and the user name making the request.


Other CGI Functions

Configuration and Host Editor

The CGI interface has a complete configuration and host editor. Only the administrator can edit the main configuration settings and hosts. The edit links are in the left navigation bar.

When changes are made to any parameter a "Save" button appears at the top of the page. If you are editing a text box you will need to click outside of the text box to make the Save button appear. If you don't select Save then the changes won't be saved.

The host-specific configuration can be edited from the host summary page using the link in the left navigation bar. The administrator can edit any of the host-specific configuration settings.

When editing the host-specific configuration, each parameter has an "override" setting that denotes the value is host-specific, meaning that it overrides the setting in the main configuration. If you unselect "override" then the setting is removed from the host-specific configuration, and the main configuration file is displayed.

User's can edit their host-specific configuration if enabled via $Conf{CgiUserConfigEditEnable}. The specific subset of configuration settings that a user can edit is specified with $Conf{CgiUserConfigEdit}. It is recommended to make this list short as possible (you probably don't want your users saving dozens of backups) and it is essential that they can't edit any of the Cmd configuration settings, otherwise they can specify an arbitrary command that will be executed as the BackupPC user.

RSS

BackupPC supports a very basic RSS feed. Provided you have the XML::RSS perl module installed, a URL similar to this will provide RSS information:

    http://localhost/cgi-bin/BackupPC/BackupPC_Admin?action=rss

This feature is experimental. The information included will probably change.


BackupPC Design

Some design issues

Pooling common files

To quickly see if a file is already in the pool, an MD5 digest of the file length and contents is used as the file name in the pool. This can't guarantee a file is identical: it just reduces the search to often a single file or handful of files. A complete file comparison is always done to verify if two files are really the same.

Identical files on multiples backups are represented by hard links. Hardlinks are used so that identical files all refer to the same physical file on the server's disk. Also, hard links maintain reference counts so that BackupPC knows when to delete unused files from the pool.

For the computer-science majors among you, you can think of the pooling system used by BackupPC as just a chained hash table stored on a (big) file system.

The hashing function

There is a tradeoff between how much of file is used for the MD5 digest and the time taken comparing all the files that have the same hash.

Using the file length and just the first 4096 bytes of the file for the MD5 digest produces some repetitions. One example: with 900,000 unique files in the pool, this hash gives about 7,000 repeated files, and in the worst case 500 files have the same hash. That's not bad: we only have to do a single file compare 99.2% of the time. But in the worst case we have to compare as many as 500 files checking for a match.

With a modest increase in CPU time, if we use the file length and the first 256K of the file we now only have 500 repeated files and in the worst case around 20 files have the same hash. Furthermore, if we instead use the first and last 128K of the file (more specifically, the first and eighth 128K chunks for files larger than 1MB) we get only 300 repeated files and in the worst case around 20 files have the same hash.

Based on this experimentation, this is the hash function used by BackupPC. It is important that you don't change the hash function after files are already in the pool. Otherwise your pool will grow to twice the size until all the old backups (and all the old files with old hashes) eventually expire.

Compression

BackupPC supports compression. It uses the deflate and inflate methods in the Compress::Zlib module, which is based on the zlib compression library (see http://www.gzip.org/zlib/).

The $Conf{CompressLevel} setting specifies the compression level to use. Zero (0) means no compression. Compression levels can be from 1 (least cpu time, slightly worse compression) to 9 (most cpu time, slightly better compression). The recommended value is 3. Changing it to 5, for example, will take maybe 20% more cpu time and will get another 2-3% additional compression. Diminishing returns set in above 5. See the zlib documentation for more information about compression levels.

BackupPC implements compression with minimal CPU load. Rather than compressing every incoming backup file and then trying to match it against the pool, BackupPC computes the MD5 digest based on the uncompressed file, and matches against the candidate pool files by comparing each uncompressed pool file against the incoming backup file. Since inflating a file takes roughly a factor of 10 less CPU time than deflating there is a big saving in CPU time.

The combination of pooling common files and compression can yield a factor of 8 or more overall saving in backup storage.

BackupPC operation

BackupPC reads the configuration information from __CONFDIR__/config.pl. It then runs and manages all the backup activity. It maintains queues of pending backup requests, user backup requests and administrative commands. Based on the configuration various requests will be executed simultaneously.

As specified by $Conf{WakeupSchedule}, BackupPC wakes up periodically to queue backups on all the PCs. This is a four step process:

  1. For each host and DHCP address backup requests are queued on the background command queue.

  2. For each PC, BackupPC_dump is forked. Several of these may be run in parallel, based on the configuration. First a ping is done to see if the machine is alive. If this is a DHCP address, nmblookup is run to get the netbios name, which is used as the host name. If DNS lookup fails, $Conf{NmbLookupFindHostCmd} is run to find the IP address from the host name. The file __TOPDIR__/pc/$host/backups is read to decide whether a full or incremental backup needs to be run. If no backup is scheduled, or the ping to $host fails, then BackupPC_dump exits.

    The backup is done using the specified XferMethod. Either samba's smbclient or tar over ssh/rsh/nfs piped into BackupPC_tarExtract, or rsync over ssh/rsh is run, or rsyncd is connected to, with the incoming data extracted to __TOPDIR__/pc/$host/new. The XferMethod output is put into __TOPDIR__/pc/$host/XferLOG.

    The letter in the XferLOG file shows the type of object, similar to the first letter of the modes displayed by ls -l:

        d -> directory
        l -> symbolic link
        b -> block special file
        c -> character special file
        p -> pipe file (fifo)
        nothing -> regular file

    The words mean:

    create

    new for this backup (ie: directory or file not in pool)

    pool

    found a match in the pool

    same

    file is identical to previous backup (contents were checksummed and verified during full dump).

    skip

    file skipped in incremental because attributes are the same (only displayed if $Conf{XferLogLevel} >= 2).

    As BackupPC_tarExtract extracts the files from smbclient or tar, or as rsync or ftp runs, it checks each file in the backup to see if it is identical to an existing file from any previous backup of any PC. It does this without needed to write the file to disk. If the file matches an existing file, a hardlink is created to the existing file in the pool. If the file does not match any existing files, the file is written to disk and the file name is saved in __TOPDIR__/pc/$host/NewFileList for later processing by BackupPC_link. BackupPC_tarExtract and rsync can handle arbitrarily large files and multiple candidate matching files without needing to write the file to disk in the case of a match. This significantly reduces disk writes (and also reads, since the pool file comparison is done disk to memory, rather than disk to disk).

    Based on the configuration settings, BackupPC_dump checks each old backup to see if any should be removed. Any expired backups are moved to __TOPDIR__/trash for later removal by BackupPC_trashClean.

  3. For each complete, good, backup, BackupPC_link is run. To avoid race conditions as new files are linked into the pool area, only a single BackupPC_link program runs at a time and the rest are queued.

    BackupPC_link reads the NewFileList written by BackupPC_dump and inspects each new file in the backup. It re-checks if there is a matching file in the pool (another BackupPC_link could have added the file since BackupPC_dump checked). If so, the file is removed and replaced by a hard link to the existing file. If the file is new, a hard link to the file is made in the pool area, so that this file is available for checking against each new file and new backup.

    Then, if $Conf{IncrFill} is set (note that the default setting is off), for each incremental backup, hard links are made in the new backup to all files that were not extracted during the incremental backups. The means the incremental backup looks like a complete image of the PC (with the exception that files that were removed on the PC since the last full backup will still appear in the backup directory tree).

    The CGI interface knows how to merge unfilled incremental backups will the most recent prior filled (full) backup, giving the incremental backups a filled appearance. The default for $Conf{IncrFill} is off, since there is no need to fill incremental backups. This saves some level of disk activity, since lots of extra hardlinks are no longer needed (and don't have to be deleted when the backup expires).

  4. BackupPC_trashClean is always run in the background to remove any expired backups. Every 5 minutes it wakes up and removes all the files in __TOPDIR__/trash.

    Also, once each night, BackupPC_nightly is run to complete some additional administrative tasks, such as cleaning the pool. This involves removing any files in the pool that only have a single hard link (meaning no backups are using that file). Again, to avoid race conditions, BackupPC_nightly is only run when there are no BackupPC_link processes running. When BackupPC_nightly is run no new BackupPC_link jobs are started. If BackupPC_nightly takes too long to run, the settings $Conf{MaxBackupPCNightlyJobs} and $Conf{BackupPCNightlyPeriod} can be used to run several BackupPC_nightly processes in parallel, and to split its job over several nights.

BackupPC also listens for TCP connections on $Conf{ServerPort}, which is used by the CGI script BackupPC_Admin for status reporting and user-initiated backup or backup cancel requests.

Storage layout

BackupPC resides in several directories:

__INSTALLDIR__

Perl scripts comprising BackupPC reside in __INSTALLDIR__/bin, libraries are in __INSTALLDIR__/lib and documentation is in __INSTALLDIR__/doc.

__CGIDIR__

The CGI script BackupPC_Admin resides in this cgi binary directory.

__CONFDIR__

All the configuration information resides below __CONFDIR__. This directory contains:

The directory __CONFDIR__ contains:

config.pl

Configuration file. See "Configuration File" below for more details.

hosts

Hosts file, which lists all the PCs to backup.

pc

The directory __CONFDIR__/pc contains per-client configuration files that override settings in the main configuration file. Each file is named __CONFDIR__/pc/HOST.pl, where HOST is the host name.

In pre-FHS versions of BackupPC these files were located in __TOPDIR__/pc/HOST/config.pl.

__LOGDIR__

The directory __LOGDIR__ (__TOPDIR__/log on pre-FHS versions of BackupPC) contains:

LOG

Current (today's) log file output from BackupPC.

LOG.0 or LOG.0.z

Yesterday's log file output. Log files are aged daily and compressed (if compression is enabled), and old LOG files are deleted.

BackupPC.pid

Contains BackupPC's process id.

status.pl

A summary of BackupPC's status written periodically by BackupPC so that certain state information can be maintained if BackupPC is restarted. Should not be edited.

UserEmailInfo.pl

A summary of what email was last sent to each user, and when the last email was sent. Should not be edited.

__TOPDIR__

All of BackupPC's data (PC backup images, logs, configuration information) is stored below this directory.

Below __TOPDIR__ are several directories:

__TOPDIR__/trash

Any directories and files below this directory are periodically deleted whenever BackupPC_trashClean checks. When a backup is aborted or when an old backup expires, BackupPC_dump simply moves the directory to __TOPDIR__/trash for later removal by BackupPC_trashClean.

__TOPDIR__/pool

All uncompressed files from PC backups are stored below __TOPDIR__/pool. Each file's name is based on the MD5 hex digest of the file contents. Specifically, for files less than 256K, the file length and the entire file is used. For files up to 1MB, the file length and the first and last 128K are used. Finally, for files longer than 1MB, the file length, and the first and eighth 128K chunks for the file are used.

Each file is stored in a subdirectory X/Y/Z, where X, Y, Z are the first 3 hex digits of the MD5 digest.

For example, if a file has an MD5 digest of 123456789abcdef0, the file is stored in __TOPDIR__/pool/1/2/3/123456789abcdef0.

The MD5 digest might not be unique (especially since not all the file's contents are used for files bigger than 256K). Different files that have the same MD5 digest are stored with a trailing suffix "_n" where n is an incrementing number starting at 0. So, for example, if two additional files were identical to the first, except the last byte was different, and assuming the file was larger than 1MB (so the MD5 digests are the same but the files are actually different), the three files would be stored as:

        __TOPDIR__/pool/1/2/3/123456789abcdef0
        __TOPDIR__/pool/1/2/3/123456789abcdef0_0
        __TOPDIR__/pool/1/2/3/123456789abcdef0_1

Both BackupPC_dump (actually, BackupPC_tarExtract) and BackupPC_link are responsible for checking newly backed up files against the pool. For each file, the MD5 digest is used to generate a file name in the pool directory. If the file exists in the pool, the contents are compared. If there is no match, additional files ending in "_n" are checked. (Actually, BackupPC_tarExtract compares multiple candidate files in parallel.) If the file contents exactly match, the file is created by simply making a hard link to the pool file (this is done by BackupPC_tarExtract as the backup proceeds). Otherwise, BackupPC_tarExtract writes the new file to disk and a new hard link is made in the pool to the file (this is done later by BackupPC_link).

Therefore, every file in the pool will have at least 2 hard links (one for the pool file and one for the backup file below __TOPDIR__/pc). Identical files from different backups or PCs will all be linked to the same file. When old backups are deleted, some files in the pool might only have one link. BackupPC_nightly checks the entire pool and removes all files that have only a single link, thereby recovering the storage for that file.

One other issue: zero length files are not pooled, since there are a lot of these files and on most file systems it doesn't save any disk space to turn these files into hard links.

__TOPDIR__/cpool

All compressed files from PC backups are stored below __TOPDIR__/cpool. Its layout is the same as __TOPDIR__/pool, and the hashing function is the same (and, importantly, based on the uncompressed file, not the compressed file).

__TOPDIR__/pc/$host

For each PC $host, all the backups for that PC are stored below the directory __TOPDIR__/pc/$host. This directory contains the following files:

LOG

Current log file for this PC from BackupPC_dump.

LOG.DDMMYYYY or LOG.DDMMYYYY.z

Last month's log file. Log files are aged monthly and compressed (if compression is enabled), and old LOG files are deleted. In earlier versions of BackupPC these files used to have a suffix of 0, 1, ....

XferERR or XferERR.z

Output from the transport program (ie: smbclient, tar, rsync or ftp) for the most recent failed backup.

new

Subdirectory in which the current backup is stored. This directory is renamed if the backup succeeds.

XferLOG or XferLOG.z

Output from the transport program (ie: smbclient, tar, rsync or ftp) for the current backup.

nnn (an integer)

Successful backups are in directories numbered sequentially starting at 0.

XferLOG.nnn or XferLOG.nnn.z

Output from the transport program (ie: smbclient, tar, rsync or ftp) corresponding to backup number nnn.

RestoreInfo.nnn

Information about restore request #nnn including who, what, when, and why. This file is in Data::Dumper format. (Note that the restore numbers are not related to the backup number.)

RestoreLOG.nnn.z

Output from smbclient, tar or rsync during restore #nnn. (Note that the restore numbers are not related to the backup number.)

ArchiveInfo.nnn

Information about archive request #nnn including who, what, when, and why. This file is in Data::Dumper format. (Note that the archive numbers are not related to the restore or backup number.)

ArchiveLOG.nnn.z

Output from archive #nnn. (Note that the archive numbers are not related to the backup or restore number.)

config.pl

Old location of optional configuration settings specific to this host. Settings in this file override the main configuration file. In new versions of BackupPC the per-host configuration files are stored in __CONFDIR__/pc/HOST.pl.

backups

A tab-delimited ascii table listing information about each successful backup, one per row. The columns are:

num

The backup number, an integer that starts at 0 and increments for each successive backup. The corresponding backup is stored in the directory num (eg: if this field is 5, then the backup is stored in __TOPDIR__/pc/$host/5).

type

Set to "full" or "incr" for full or incremental backup.

startTime

Start time of the backup in unix seconds.

endTime

Stop time of the backup in unix seconds.

nFiles

Number of files backed up (as reported by smbclient, tar, rsync or ftp).

size

Total file size backed up (as reported by smbclient, tar, rsync or ftp).

nFilesExist

Number of files that were already in the pool (as determined by BackupPC_dump and BackupPC_link).

sizeExist

Total size of files that were already in the pool (as determined by BackupPC_dump and BackupPC_link).

nFilesNew

Number of files that were not in the pool (as determined by BackupPC_link).

sizeNew

Total size of files that were not in the pool (as determined by BackupPC_link).

xferErrs

Number of errors or warnings from smbclient, tar, rsync or ftp.

xferBadFile

Number of errors from smbclient that were bad file errors (zero otherwise).

xferBadShare

Number of errors from smbclient that were bad share errors (zero otherwise).

tarErrs

Number of errors from BackupPC_tarExtract.

compress

The compression level used on this backup. Zero or empty means no compression.

sizeExistComp

Total compressed size of files that were already in the pool (as determined by BackupPC_dump and BackupPC_link).

sizeNewComp

Total compressed size of files that were not in the pool (as determined by BackupPC_link).

noFill

Set if this backup has not been filled in with the most recent previous filled or full backup. See $Conf{IncrFill}.

fillFromNum

If this backup was filled (ie: noFill is 0) then this is the number of the backup that it was filled from

mangle

Set if this backup has mangled file names and attributes. Always true for backups in v1.4.0 and above. False for all backups prior to v1.4.0.

xferMethod

Set to the value of $Conf{XferMethod} when this dump was done.

level

The level of this dump. A full dump is level 0. Currently incrementals are 1. But when multi-level incrementals are supported this will reflect each dump's incremental level.

restores

A tab-delimited ascii table listing information about each requested restore, one per row. The columns are:

num

Restore number (matches the suffix of the RestoreInfo.nnn and RestoreLOG.nnn.z file), unrelated to the backup number.

startTime

Start time of the restore in unix seconds.

endTime

End time of the restore in unix seconds.

result

Result (ok or failed).

errorMsg

Error message if restore failed.

nFiles

Number of files restored.

size

Size in bytes of the restored files.

tarCreateErrs

Number of errors from BackupPC_tarCreate during restore.

xferErrs

Number of errors from smbclient, tar, rsync or ftp during restore.

archives

A tab-delimited ascii table listing information about each requested archive, one per row. The columns are:

num

Archive number (matches the suffix of the ArchiveInfo.nnn and ArchiveLOG.nnn.z file), unrelated to the backup or restore number.

startTime

Start time of the restore in unix seconds.

endTime

End time of the restore in unix seconds.

result

Result (ok or failed).

errorMsg

Error message if archive failed.

Compressed file format

The compressed file format is as generated by Compress::Zlib::deflate with one minor, but important, tweak. Since Compress::Zlib::inflate fully inflates its argument in memory, it could take large amounts of memory if it was inflating a highly compressed file. For example, a 200MB file of 0x0 bytes compresses to around 200K bytes. If Compress::Zlib::inflate was called with this single 200K buffer, it would need to allocate 200MB of memory to return the result.

BackupPC watches how efficiently a file is compressing. If a big file has very high compression (meaning it will use too much memory when it is inflated), BackupPC calls the flush() method, which gracefully completes the current compression. BackupPC then starts another deflate and simply appends the output file. So the BackupPC compressed file format is one or more concatenated deflations/flushes. The specific ratios that BackupPC uses is that if a 6MB chunk compresses to less than 64K then a flush will be done.

Back to the example of the 200MB file of 0x0 bytes. Adding flushes every 6MB adds only 200 or so bytes to the 200K output. So the storage cost of flushing is negligible.

To easily decompress a BackupPC compressed file, the script BackupPC_zcat can be found in __INSTALLDIR__/bin. For each file name argument it inflates the file and writes it to stdout.

Rsync checksum caching

An incremental backup with rsync compares attributes on the client with the last full backup. Any files with identical attributes are skipped. A full backup with rsync sets the --ignore-times option, which causes every file to be examined independent of attributes.

Each file is examined by generating block checksums (default 2K blocks) on the receiving side (that's the BackupPC side), sending those checksums to the client, where the remote rsync matches those checksums with the corresponding file. The matching blocks and new data is sent back, allowing the client file to be reassembled. A checksum for the entire file is sent to as an extra check the the reconstructed file is correct.

This results in significant disk IO and computation for BackupPC: every file in a full backup, or any file with non-matching attributes in an incremental backup, needs to be uncompressed, block checksums computed and sent. Then the receiving side reassembles the file and has to verify the whole-file checksum. Even if the file is identical, prior to 2.1.0, BackupPC had to read and uncompress the file twice, once to compute the block checksums and later to verify the whole-file checksum.

Starting in 2.1.0, BackupPC supports optional checksum caching, which means the block and file checksums only need to be computed once for each file. This results in a significant performance improvement. This only works for compressed pool files. It is enabled by adding

        '--checksum-seed=32761',

to $Conf{RsyncArgs} and $Conf{RsyncRestoreArgs}.

Rsync versions prior to and including rsync-2.6.2 need a small patch to add support for the --checksum-seed option. This patch is available in the cygwin-rsyncd package at http://backuppc.sourceforge.net. This patch is already included in rsync CVS, so it will be standard in future versions of rsync.

When this option is present, BackupPC will add block and file checksums to the compressed pool file the next time a pool file is used and it doesn't already have cached checksums. The first time a new file is written to the pool, the checksums are not appended. The next time checksums are needed for a file, they are computed and added. So the full performance benefit of checksum caching won't be noticed until the third time a pool file is used (eg: the third full backup).

With checksum caching enabled, there is a risk that should a file's contents in the pool be corrupted due to a disk problem, but the cached checksums are still correct, the corruption will not be detected by a full backup, since the file contents are no longer read and compared. To reduce the chance that this remains undetected, BackupPC can recheck cached checksums for a fraction of the files. This fraction is set with the $Conf{RsyncCsumCacheVerifyProb} setting. The default value of 0.01 means that 1% of the time a file's checksums are read, the checksums are verified. This reduces performance slightly, but, over time, ensures that files contents are in sync with the cached checksums.

The format of the cached checksum data can be discovered by looking at the code. Basically, the first byte of the compressed file is changed to denote that checksums are appended. The block and file checksum data, plus some other information and magic word, are appended to the compressed file. This allows the cache update to be done in-place.

File name mangling

Backup file names are stored in "mangled" form. Each node of a path is preceded by "f" (mnemonic: file), and special characters (\n, \r, % and /) are URI-encoded as "%xx", where xx is the ascii character's hex value. So c:/craig/example.txt is now stored as fc/fcraig/fexample.txt.

This was done mainly so meta-data could be stored alongside the backup files without name collisions. In particular, the attributes for the files in a directory are stored in a file called "attrib", and mangling avoids file name collisions (I discarded the idea of having a duplicate directory tree for every backup just to store the attributes). Other meta-data (eg: rsync checksums) could be stored in file names preceded by, eg, "c". There are two other benefits to mangling: the share name might contain "/" (eg: "/home/craig" for tar transport), and I wanted that represented as a single level in the storage tree. Secondly, as files are written to NewFileList for later processing by BackupPC_link, embedded newlines in the file's path will cause problems which are avoided by mangling.

The CGI script undoes the mangling, so it is invisible to the user. Old (unmangled) backups are still supported by the CGI interface.

Special files

Linux/unix file systems support several special file types: symbolic links, character and block device files, fifos (pipes) and unix-domain sockets. All except unix-domain sockets are supported by BackupPC (there's no point in backing up or restoring unix-domain sockets since they only have meaning after a process creates them). Symbolic links are stored as a plain file whose contents are the contents of the link (not the file it points to). This file is compressed and pooled like any normal file. Character and block device files are also stored as plain files, whose contents are two integers separated by a comma; the numbers are the major and minor device number. These files are compressed and pooled like any normal file. Fifo files are stored as empty plain files (which are not pooled since they have zero size). In all cases, the original file type is stored in the attrib file so it can be correctly restored.

Hardlinks are also supported. When GNU tar first encounters a file with more than one link (ie: hardlinks) it dumps it as a regular file. When it sees the second and subsequent hardlinks to the same file, it dumps just the hardlink information. BackupPC correctly recognizes these hardlinks and stores them just like symlinks: a regular text file whose contents is the path of the file linked to. The CGI script will download the original file when you click on a hardlink.

Also, BackupPC_tarCreate has enough magic to re-create the hardlinks dynamically based on whether or not the original file and hardlinks are both included in the tar file. For example, imagine a/b/x is a hardlink to a/c/y. If you use BackupPC_tarCreate to restore directory a, then the tar file will include a/b/x as the original file and a/c/y will be a hardlink to a/b/x. If, instead you restore a/c, then the tar file will include a/c/y as the original file, not a hardlink.

Attribute file format

The unix attributes for the contents of a directory (all the files and directories in that directory) are stored in a file called attrib. There is a single attrib file for each directory in a backup. For example, if c:/craig contains a single file c:/craig/example.txt, that file would be stored as fc/fcraig/fexample.txt and there would be an attribute file in fc/fcraig/attrib (and also fc/attrib and ./attrib). The file fc/fcraig/attrib would contain a single entry containing the attributes for fc/fcraig/fexample.txt.

The attrib file starts with a magic number, followed by the concatenation of the following information for each file:

  • File name length in perl's pack "w" format (variable length base 128).

  • File name.

  • The unix file type, mode, uid, gid and file size divided by 4GB and file size modulo 4GB (type mode uid gid sizeDiv4GB sizeMod4GB), in perl's pack "w" format (variable length base 128).

  • The unix mtime (unix seconds) in perl's pack "N" format (32 bit integer).

The attrib file is also compressed if compression is enabled. See the lib/BackupPC/Attrib.pm module for full details.

Attribute files are pooled just like normal backup files. This saves space if all the files in a directory have the same attributes across multiple backups, which is common.

Optimizations

BackupPC doesn't care about the access time of files in the pool since it saves attribute meta-data separate from the files. Since BackupPC mostly does reads from disk, maintaining the access time of files generates a lot of unnecessary disk writes. So, provided BackupPC has a dedicated data disk, you should consider mounting BackupPC's data directory with the noatime (or, with Linux kernels >=2.6.20, relatime) attribute (see mount(1)).

Some Limitations

BackupPC isn't perfect (but it is getting better). Please see http://backuppc.sourceforge.net/faq/limitations.html for a discussion of some of BackupPC's limitations.

Security issues

Please see http://backuppc.sourceforge.net/faq/security.html for a discussion of some of various security issues.


Configuration File

The BackupPC configuration file resides in __CONFDIR__/config.pl. Optional per-PC configuration files reside in __CONFDIR__/pc/$host.pl (or __TOPDIR__/pc/$host/config.pl in non-FHS versions of BackupPC). This file can be used to override settings just for a particular PC.

Modifying the main configuration file

The configuration file is a perl script that is executed by BackupPC, so you should be careful to preserve the file syntax (punctuation, quotes etc) when you edit it. It is recommended that you use CVS, RCS or some other method of source control for changing config.pl.

BackupPC reads or re-reads the main configuration file and the hosts file in three cases:

  • Upon startup.

  • When BackupPC is sent a HUP (-1) signal. Assuming you installed the init.d script, you can also do this with "/etc/init.d/backuppc reload".

  • When the modification time of config.pl file changes. BackupPC checks the modification time once during each regular wakeup.

Whenever you change the configuration file you can either do a kill -HUP BackupPC_pid or simply wait until the next regular wakeup period.

Each time the configuration file is re-read a message is reported in the LOG file, so you can tail it (or view it via the CGI interface) to make sure your kill -HUP worked. Errors in parsing the configuration file are also reported in the LOG file.

The optional per-PC configuration file (__CONFDIR__/pc/$host.pl or __TOPDIR__/pc/$host/config.pl in non-FHS versions of BackupPC) is read whenever it is needed by BackupPC_dump, BackupPC_link and others.


Configuration Parameters

The configuration parameters are divided into five general groups. The first group (general server configuration) provides general configuration for BackupPC. The next two groups describe what to backup, when to do it, and how long to keep it. The fourth group are settings for email reminders, and the final group contains settings for the CGI interface.

All configuration settings in the second through fifth groups can be overridden by the per-PC config.pl file.

General server configuration

$Conf{ServerHost} = '';

Host name on which the BackupPC server is running.

$Conf{ServerPort} = -1;

TCP port number on which the BackupPC server listens for and accepts connections. Normally this should be disabled (set to -1). The TCP port is only needed if apache runs on a different machine from BackupPC. In that case, set this to any spare port number over 1024 (eg: 2359). If you enable the TCP port, make sure you set $Conf{ServerMesgSecret} too!

$Conf{ServerMesgSecret} = '';

Shared secret to make the TCP port secure. Set this to a hard to guess string if you enable the TCP port (ie: $Conf{ServerPort} > 0).

To avoid possible attacks via the TCP socket interface, every client message is protected by an MD5 digest. The MD5 digest includes four items: - a seed that is sent to the client when the connection opens - a sequence number that increments for each message - a shared secret that is stored in $Conf{ServerMesgSecret} - the message itself.

The message is sent in plain text preceded by the MD5 digest. A snooper can see the plain-text seed sent by BackupPC and plain-text message from the client, but cannot construct a valid MD5 digest since the secret $Conf{ServerMesgSecret} is unknown. A replay attack is not possible since the seed changes on a per-connection and per-message basis.

$Conf{MyPath} = '/bin';

PATH setting for BackupPC. An explicit value is necessary for taint mode. Value shouldn't matter too much since all execs use explicit paths. However, taint mode in perl will complain if this directory is world writable.

$Conf{UmaskMode} = 027;

Permission mask for directories and files created by BackupPC. Default value prevents any access from group other, and prevents group write.

$Conf{WakeupSchedule} = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23];

Times at which we wake up, check all the PCs, and schedule necessary backups. Times are measured in hours since midnight. Can be fractional if necessary (eg: 4.25 means 4:15am).

If the hosts you are backing up are always connected to the network you might have only one or two wakeups each night. This will keep the backup activity after hours. On the other hand, if you are backing up laptops that are only intermittently connected to the network you will want to have frequent wakeups (eg: hourly) to maximize the chance that each laptop is backed up.

Examples:

    $Conf{WakeupSchedule} = [22.5];         # once per day at 10:30 pm.
    $Conf{WakeupSchedule} = [2,4,6,8,10,12,14,16,18,20,22];  # every 2 hours

The default value is every hour except midnight.

The first entry of $Conf{WakeupSchedule} is when BackupPC_nightly is run. You might want to re-arrange the entries in $Conf{WakeupSchedule} (they don't have to be ascending) so that the first entry is when you want BackupPC_nightly to run (eg: when you don't expect a lot of regular backups to run).

$Conf{MaxBackups} = 4;

Maximum number of simultaneous backups to run. If there are no user backup requests then this is the maximum number of simultaneous backups.

$Conf{MaxUserBackups} = 4;

Additional number of simultaneous backups that users can run. As many as $Conf{MaxBackups} + $Conf{MaxUserBackups} requests can run at the same time.

$Conf{MaxPendingCmds} = 15;

Maximum number of pending link commands. New backups will only be started if there are no more than $Conf{MaxPendingCmds} plus $Conf{MaxBackups} number of pending link commands, plus running jobs. This limit is to make sure BackupPC doesn't fall too far behind in running BackupPC_link commands.

$Conf{CmdQueueNice} = 10;

Nice level at which CmdQueue commands (eg: BackupPC_link and BackupPC_nightly) are run at.

$Conf{MaxBackupPCNightlyJobs} = 2;

How many BackupPC_nightly processes to run in parallel.

Each night, at the first wakeup listed in $Conf{WakeupSchedule}, BackupPC_nightly is run. Its job is to remove unneeded files in the pool, ie: files that only have one link. To avoid race conditions, BackupPC_nightly and BackupPC_link cannot run at the same time. Starting in v3.0.0, BackupPC_nightly can run concurrently with backups (BackupPC_dump).

So to reduce the elapsed time, you might want to increase this setting to run several BackupPC_nightly processes in parallel (eg: 4, or even 8).

$Conf{BackupPCNightlyPeriod} = 1;

How many days (runs) it takes BackupPC_nightly to traverse the entire pool. Normally this is 1, which means every night it runs, it does traverse the entire pool removing unused pool files.

Other valid values are 2, 4, 8, 16. This causes BackupPC_nightly to traverse 1/2, 1/4, 1/8 or 1/16th of the pool each night, meaning it takes 2, 4, 8 or 16 days to completely traverse the pool. The advantage is that each night the running time of BackupPC_nightly is reduced roughly in proportion, since the total job is split over multiple days. The disadvantage is that unused pool files take longer to get deleted, which will slightly increase disk usage.

Note that even when $Conf{BackupPCNightlyPeriod} > 1, BackupPC_nightly still runs every night. It just does less work each time it runs.

Examples:

   $Conf{BackupPCNightlyPeriod} = 1;   # entire pool is checked every night

   $Conf{BackupPCNightlyPeriod} = 2;   # two days to complete pool check
                                       # (different half each night)

   $Conf{BackupPCNightlyPeriod} = 4;   # four days to complete pool check
                                       # (different quarter each night)
$Conf{MaxOldLogFiles} = 14;

Maximum number of log files we keep around in log directory. These files are aged nightly. A setting of 14 means the log directory will contain about 2 weeks of old log files, in particular at most the files LOG, LOG.0, LOG.1, ... LOG.13 (except today's LOG, these files will have a .z extension if compression is on).

If you decrease this number after BackupPC has been running for a while you will have to manually remove the older log files.

$Conf{DfPath} = '';

Full path to the df command. Security caution: normal users should not allowed to write to this file or directory.

$Conf{DfCmd} = '$dfPath $topDir';

Command to run df. The following variables are substituted at run-time:

  $dfPath      path to df ($Conf{DfPath})
  $topDir      top-level BackupPC data directory

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{SplitPath} = '';
$Conf{ParPath} = '';
$Conf{CatPath} = '';
$Conf{GzipPath} = '';
$Conf{Bzip2Path} = '';

Full path to various commands for archiving

$Conf{DfMaxUsagePct} = 95;

Maximum threshold for disk utilization on the __TOPDIR__ filesystem. If the output from $Conf{DfPath} reports a percentage larger than this number then no new regularly scheduled backups will be run. However, user requested backups (which are usually incremental and tend to be small) are still performed, independent of disk usage. Also, currently running backups will not be terminated when the disk usage exceeds this number.

$Conf{TrashCleanSleepSec} = 300;

How long BackupPC_trashClean sleeps in seconds between each check of the trash directory. Once every 5 minutes should be reasonable.

$Conf{DHCPAddressRanges} = [];

List of DHCP address ranges we search looking for PCs to backup. This is an array of hashes for each class C address range. This is only needed if hosts in the conf/hosts file have the dhcp flag set.

Examples:

   # to specify 192.10.10.20 to 192.10.10.250 as the DHCP address pool
   $Conf{DHCPAddressRanges} = [
       {
           ipAddrBase => '192.10.10',
           first => 20,
           last  => 250,
       },
   ];
   # to specify two pools (192.10.10.20-250 and 192.10.11.10-50)
   $Conf{DHCPAddressRanges} = [
       {
           ipAddrBase => '192.10.10',
           first => 20,
           last  => 250,
       },
       {
           ipAddrBase => '192.10.11',
           first => 10,
           last  => 50,
       },
   ];
$Conf{BackupPCUser} = '';

The BackupPC user.

$Conf{TopDir} = '';
$Conf{ConfDir} = '';
$Conf{LogDir} = '';
$Conf{InstallDir} = '';
$Conf{CgiDir} = '';

Important installation directories:

  TopDir     - where all the backup data is stored
  ConfDir    - where the main config and hosts files resides
  LogDir     - where log files and other transient information
  InstallDir - where the bin, lib and doc installation dirs reside.
               Note: you cannot change this value since all the
               perl scripts include this path.  You must reinstall
               with configure.pl to change InstallDir.
  CgiDir     - Apache CGI directory for BackupPC_Admin

Note: it is STRONGLY recommended that you don't change the values here. These are set at installation time and are here for reference and are used during upgrades.

Instead of changing TopDir here it is recommended that you use a symbolic link to the new location, or mount the new BackupPC store at the existing $Conf{TopDir} setting.

$Conf{BackupPCUserVerify} = 1;

Whether BackupPC and the CGI script BackupPC_Admin verify that they are really running as user $Conf{BackupPCUser}. If this flag is set and the effective user id (euid) differs from $Conf{BackupPCUser} then both scripts exit with an error. This catches cases where BackupPC might be accidently started as root or the wrong user, or if the CGI script is not installed correctly.

$Conf{HardLinkMax} = 31999;

Maximum number of hardlinks supported by the $TopDir file system that BackupPC uses. Most linux or unix file systems should support at least 32000 hardlinks per file, or 64000 in other cases. If a pool file already has this number of hardlinks, a new pool file is created so that new hardlinks can be accommodated. This limit will only be hit if an identical file appears at least this number of times across all the backups.

$Conf{PerlModuleLoad} = undef;

Advanced option for asking BackupPC to load additional perl modules. Can be a list (array ref) of module names to load at startup.

$Conf{ServerInitdPath} = '';
$Conf{ServerInitdStartCmd} = '';

Path to init.d script and command to use that script to start the server from the CGI interface. The following variables are substituted at run-time:

  $sshPath           path to ssh ($Conf{SshPath})
  $serverHost        same as $Conf{ServerHost}
  $serverInitdPath   path to init.d script ($Conf{ServerInitdPath})

Example:

$Conf{ServerInitdPath} = '/etc/init.d/backuppc'; $Conf{ServerInitdStartCmd} = '$sshPath -q -x -l root $serverHost' . ' $serverInitdPath start' . ' < /dev/null >& /dev/null';

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

What to backup and when to do it

$Conf{FullPeriod} = 6.97;

Minimum period in days between full backups. A full dump will only be done if at least this much time has elapsed since the last full dump, and at least $Conf{IncrPeriod} days has elapsed since the last successful dump.

Typically this is set slightly less than an integer number of days. The time taken for the backup, plus the granularity of $Conf{WakeupSchedule} will make the actual backup interval a bit longer.

$Conf{IncrPeriod} = 0.97;

Minimum period in days between incremental backups (a user requested incremental backup will be done anytime on demand).

Typically this is set slightly less than an integer number of days. The time taken for the backup, plus the granularity of $Conf{WakeupSchedule} will make the actual backup interval a bit longer.

$Conf{FullKeepCnt} = 1;

Number of full backups to keep. Must be >= 1.

In the steady state, each time a full backup completes successfully the oldest one is removed. If this number is decreased, the extra old backups will be removed.

If filling of incremental dumps is off the oldest backup always has to be a full (ie: filled) dump. This might mean one or two extra full dumps are kept until the oldest incremental backups expire.

Exponential backup expiry is also supported. This allows you to specify:

  - num fulls to keep at intervals of 1 * $Conf{FullPeriod}, followed by
  - num fulls to keep at intervals of 2 * $Conf{FullPeriod},
  - num fulls to keep at intervals of 4 * $Conf{FullPeriod},
  - num fulls to keep at intervals of 8 * $Conf{FullPeriod},
  - num fulls to keep at intervals of 16 * $Conf{FullPeriod},

and so on. This works by deleting every other full as each expiry boundary is crossed.

Exponential expiry is specified using an array for $Conf{FullKeepCnt}:

  $Conf{FullKeepCnt} = [4, 2, 3];

Entry #n specifies how many fulls to keep at an interval of 2^n * $Conf{FullPeriod} (ie: 1, 2, 4, 8, 16, 32, ...).

The example above specifies keeping 4 of the most recent full backups (1 week interval) two full backups at 2 week intervals, and 3 full backups at 4 week intervals, eg:

   full 0 19 weeks old   \
   full 1 15 weeks old    >---  3 backups at 4 * $Conf{FullPeriod}
   full 2 11 weeks old   /
   full 3  7 weeks old   \____  2 backups at 2 * $Conf{FullPeriod}
   full 4  5 weeks old   /
   full 5  3 weeks old   \
   full 6  2 weeks old    \___  4 backups at 1 * $Conf{FullPeriod}
   full 7  1 week old     /
   full 8  current       /

On a given week the spacing might be less than shown as each backup ages through each expiry period. For example, one week later, a new full is completed and the oldest is deleted, giving:

   full 0 16 weeks old   \
   full 1 12 weeks old    >---  3 backups at 4 * $Conf{FullPeriod}
   full 2  8 weeks old   /
   full 3  6 weeks old   \____  2 backups at 2 * $Conf{FullPeriod}
   full 4  4 weeks old   /
   full 5  3 weeks old   \
   full 6  2 weeks old    \___  4 backups at 1 * $Conf{FullPeriod}
   full 7  1 week old     /
   full 8  current       /

You can specify 0 as a count (except in the first entry), and the array can be as long as you wish. For example:

  $Conf{FullKeepCnt} = [4, 0, 4, 0, 0, 2];

This will keep 10 full dumps, 4 most recent at 1 * $Conf{FullPeriod}, followed by 4 at an interval of 4 * $Conf{FullPeriod} (approx 1 month apart), and then 2 at an interval of 32 * $Conf{FullPeriod} (approx 7-8 months apart).

Example: these two settings are equivalent and both keep just the four most recent full dumps:

   $Conf{FullKeepCnt} = 4;
   $Conf{FullKeepCnt} = [4];
$Conf{FullKeepCntMin} = 1;
$Conf{FullAgeMax} = 90;

Very old full backups are removed after $Conf{FullAgeMax} days. However, we keep at least $Conf{FullKeepCntMin} full backups no matter how old they are.

Note that $Conf{FullAgeMax} will be increased to $Conf{FullKeepCnt} times $Conf{FullPeriod} if $Conf{FullKeepCnt} specifies enough full backups to exceed $Conf{FullAgeMax}.

$Conf{IncrKeepCnt} = 6;

Number of incremental backups to keep. Must be >= 1.

In the steady state, each time an incr backup completes successfully the oldest one is removed. If this number is decreased, the extra old backups will be removed.

$Conf{IncrKeepCntMin} = 1;
$Conf{IncrAgeMax} = 30;

Very old incremental backups are removed after $Conf{IncrAgeMax} days. However, we keep at least $Conf{IncrKeepCntMin} incremental backups no matter how old they are.

$Conf{IncrLevels} = [1];

Level of each incremental. "Level" follows the terminology of dump(1). A full backup has level 0. A new incremental of level N will backup all files that have changed since the most recent backup of a lower level.

The entries of $Conf{IncrLevels} apply in order to each incremental after each full backup. It wraps around until the next full backup. For example, these two settings have the same effect:

      $Conf{IncrLevels} = [1, 2, 3];
      $Conf{IncrLevels} = [1, 2, 3, 1, 2, 3];

This means the 1st and 4th incrementals (level 1) go all the way back to the full. The 2nd and 3rd (and 5th and 6th) backups just go back to the immediate preceeding incremental.

Specifying a sequence of multi-level incrementals will usually mean more than $Conf{IncrKeepCnt} incrementals will need to be kept, since lower level incrementals are needed to merge a complete view of a backup. For example, with

      $Conf{FullPeriod}  = 7;
      $Conf{IncrPeriod}  = 1;
      $Conf{IncrKeepCnt} = 6;
      $Conf{IncrLevels}  = [1, 2, 3, 4, 5, 6];

there will be up to 11 incrementals in this case:

      backup #0  (full, level 0, oldest)
      backup #1  (incr, level 1)
      backup #2  (incr, level 2)
      backup #3  (incr, level 3)
      backup #4  (incr, level 4)
      backup #5  (incr, level 5)
      backup #6  (incr, level 6)
      backup #7  (full, level 0)
      backup #8  (incr, level 1)
      backup #9  (incr, level 2)
      backup #10 (incr, level 3)
      backup #11 (incr, level 4)
      backup #12 (incr, level 5, newest)

Backup #1 (the oldest level 1 incremental) can't be deleted since backups 2..6 depend on it. Those 6 incrementals can't all be deleted since that would only leave 5 (#8..12). When the next incremental happens (level 6), the complete set of 6 older incrementals (#1..6) will be deleted, since that maintains the required number ($Conf{IncrKeepCnt}) of incrementals. This situation is reduced if you set shorter chains of multi-level incrementals, eg:

      $Conf{IncrLevels}  = [1, 2, 3];

would only have up to 2 extra incremenals before all 3 are deleted.

BackupPC as usual merges the full and the sequence of incrementals together so each incremental can be browsed and restored as though it is a complete backup. If you specify a long chain of incrementals then more backups need to be merged when browsing, restoring, or getting the starting point for rsync backups. In the example above (levels 1..6), browing backup #6 requires 7 different backups (#0..6) to be merged.

Because of this merging and the additional incrementals that need to be kept, it is recommended that some level 1 incrementals be included in $Conf{IncrLevels}.

Prior to version 3.0 incrementals were always level 1, meaning each incremental backed up all the files that changed since the last full.

$Conf{BackupsDisable} = 0;

Disable all full and incremental backups. These settings are useful for a client that is no longer being backed up (eg: a retired machine), but you wish to keep the last backups available for browsing or restoring to other machines.

There are three values for $Conf{BackupsDisable}:

  0    Backups are enabled.

  1    Don't do any regular backups on this client.  Manually
       requested backups (via the CGI interface) will still occur.

  2    Don't do any backups on this client.  Manually requested
       backups (via the CGI interface) will be ignored.

In versions prior to 3.0 Backups were disabled by setting $Conf{FullPeriod} to -1 or -2.

$Conf{PartialAgeMax} = 3;

A failed full backup is saved as a partial backup. The rsync XferMethod can take advantage of the partial full when the next backup is run. This parameter sets the age of the partial full in days: if the partial backup is older than this number of days, then rsync will ignore (not use) the partial full when the next backup is run. If you set this to a negative value then no partials will be saved. If you set this to 0, partials will be saved, but will not be used by the next backup.

The default setting of 3 days means that a partial older than 3 days is ignored when the next full backup is done.

$Conf{IncrFill} = 0;

Whether incremental backups are filled. "Filling" means that the most recent full (or filled) dump is merged into the new incremental dump using hardlinks. This makes an incremental dump look like a full dump. Prior to v1.03 all incremental backups were filled. In v1.4.0 and later the default is off.

BackupPC, and the cgi interface in particular, do the right thing on un-filled incremental backups. It will correctly display the merged incremental backup with the most recent filled backup, giving the un-filled incremental backups a filled appearance. That means it invisible to the user whether incremental dumps are filled or not.

Filling backups takes a little extra disk space, and it does cost some extra disk activity for filling, and later removal. Filling is no longer useful, since file mangling and compression doesn't make a filled backup very useful. It's likely the filling option will be removed from future versions: filling will be delegated to the display and extraction of backup data.

If filling is off, BackupPC makes sure that the oldest backup is a full, otherwise the following incremental backups will be incomplete. This might mean an extra full backup has to be kept until the following incremental backups expire.

The default is off. You can turn this on or off at any time without affecting existing backups.

$Conf{RestoreInfoKeepCnt} = 10;

Number of restore logs to keep. BackupPC remembers information about each restore request. This number per client will be kept around before the oldest ones are pruned.

Note: files/dirs delivered via Zip or Tar downloads don't count as restores. Only the first restore option (where the files and dirs are written to the host) count as restores that are logged.

$Conf{ArchiveInfoKeepCnt} = 10;

Number of archive logs to keep. BackupPC remembers information about each archive request. This number per archive client will be kept around before the oldest ones are pruned.

$Conf{BackupFilesOnly} = undef;

List of directories or files to backup. If this is defined, only these directories or files will be backed up.

When editing from the web interface, you should add a valid ShareName (based on $Conf{XferMethod}), and then enter the directories specific to that ShareName. A special ShareName "*" matches any ShareName that doesn't have an explicit entry.

For Smb, only one of $Conf{BackupFilesExclude} and $Conf{BackupFilesOnly} can be specified per share. If both are set for a particular share, then $Conf{BackupFilesOnly} takes precedence and $Conf{BackupFilesExclude} is ignored.

This can be set to a string, an array of strings, or, in the case of multiple shares, a hash of strings or arrays. A hash is used to give a list of directories or files to backup for each share (the share name is the key). If this is set to just a string or array, and $Conf{SmbShareName} contains multiple share names, then the setting is assumed to apply all shares.

If a hash is used, a special key "*" means it applies to all shares that don't have a specific entry.

Examples:

   $Conf{BackupFilesOnly} = '/myFiles';
   $Conf{BackupFilesOnly} = ['/myFiles'];     # same as first example
   $Conf{BackupFilesOnly} = ['/myFiles', '/important'];
   $Conf{BackupFilesOnly} = {
      'c' => ['/myFiles', '/important'],      # these are for 'c' share
      'd' => ['/moreFiles', '/archive'],      # these are for 'd' share
   };
   $Conf{BackupFilesOnly} = {
      'c' => ['/myFiles', '/important'],      # these are for 'c' share
      '*' => ['/myFiles', '/important'],      # these are other shares
   };
$Conf{BackupFilesExclude} = undef;

List of directories or files to exclude from the backup. For Smb, only one of $Conf{BackupFilesExclude} and $Conf{BackupFilesOnly} can be specified per share. If both are set for a particular share, then $Conf{BackupFilesOnly} takes precedence and $Conf{BackupFilesExclude} is ignored.

When editing from the web interface, you should add a valid ShareName (based on $Conf{XferMethod}), and then enter the directories or files specific to that ShareName. A special ShareName "*" matches any ShareName that doesn't have an explicit entry.

This can be set to a string, an array of strings, or, in the case of multiple shares, a hash of strings or arrays. A hash is used to give a list of directories or files to exclude for each share (the share name is the key). If this is set to just a string or array, and $Conf{SmbShareName} contains multiple share names, then the setting is assumed to apply to all shares.

The exact behavior is determined by the underlying transport program, smbclient or tar. For smbclient the exlclude file list is passed into the X option. Simple shell wild-cards using "*" or "?" are allowed.

For tar, if the exclude file contains a "/" it is assumed to be anchored at the start of the string. Since all the tar paths start with "./", BackupPC prepends a "." if the exclude file starts with a "/". Note that GNU tar version >= 1.13.7 is required for the exclude option to work correctly. For linux or unix machines you should add "/proc" to $Conf{BackupFilesExclude} unless you have specified --one-file-system in $Conf{TarClientCmd} or --one-file-system in $Conf{RsyncArgs}. Also, for tar, do not use a trailing "/" in the directory name: a trailing "/" causes the name to not match and the directory will not be excluded.

Users report that for smbclient you should specify a directory followed by "/*", eg: "/proc/*", instead of just "/proc".

FTP servers are traversed recursively so excluding directories will also exclude its contents. You can use the wildcard characters "*" and "?" to define files for inclusion and exclusion. Both attributes $Conf{BackupFilesOnly} and $Conf{BackupFilesExclude} can be defined for the same share.

If a hash is used, a special key "*" means it applies to all shares that don't have a specific entry.

Examples:

   $Conf{BackupFilesExclude} = '/temp';
   $Conf{BackupFilesExclude} = ['/temp'];     # same as first example
   $Conf{BackupFilesExclude} = ['/temp', '/winnt/tmp'];
   $Conf{BackupFilesExclude} = {
      'c' => ['/temp', '/winnt/tmp'],         # these are for 'c' share
      'd' => ['/junk', '/dont_back_this_up'], # these are for 'd' share
   };
   $Conf{BackupFilesExclude} = {
      'c' => ['/temp', '/winnt/tmp'],         # these are for 'c' share
      '*' => ['/junk', '/dont_back_this_up'], # these are for other shares
   };
$Conf{BlackoutBadPingLimit} = 3;
$Conf{BlackoutGoodCnt} = 7;

PCs that are always or often on the network can be backed up after hours, to reduce PC, network and server load during working hours. For each PC a count of consecutive good pings is maintained. Once a PC has at least $Conf{BlackoutGoodCnt} consecutive good pings it is subject to "blackout" and not backed up during hours and days specified by $Conf{BlackoutPeriods}.

To allow for periodic rebooting of a PC or other brief periods when a PC is not on the network, a number of consecutive bad pings is allowed before the good ping count is reset. This parameter is $Conf{BlackoutBadPingLimit}.

Note that bad and good pings don't occur with the same interval. If a machine is always on the network, it will only be pinged roughly once every $Conf{IncrPeriod} (eg: once per day). So a setting for $Conf{BlackoutGoodCnt} of 7 means it will take around 7 days for a machine to be subject to blackout. On the other hand, if a ping is failed, it will be retried roughly every time BackupPC wakes up, eg, every one or two hours. So a setting for $Conf{BlackoutBadPingLimit} of 3 means that the PC will lose its blackout status after 3-6 hours of unavailability.

To disable the blackout feature set $Conf{BlackoutGoodCnt} to a negative value. A value of 0 will make all machines subject to blackout. But if you don't want to do any backups during the day it would be easier to just set $Conf{WakeupSchedule} to a restricted schedule.

$Conf{BlackoutPeriods} = [ ... ];

One or more blackout periods can be specified. If a client is subject to blackout then no regular (non-manual) backups will be started during any of these periods. hourBegin and hourEnd specify hours fro midnight and weekDays is a list of days of the week where 0 is Sunday, 1 is Monday etc.

For example:

   $Conf{BlackoutPeriods} = [
        {
            hourBegin =>  7.0,
            hourEnd   => 19.5,
            weekDays  => [1, 2, 3, 4, 5],
        },
   ];

specifies one blackout period from 7:00am to 7:30pm local time on Mon-Fri.

The blackout period can also span midnight by setting hourBegin > hourEnd, eg:

   $Conf{BlackoutPeriods} = [
        {
            hourBegin =>  7.0,
            hourEnd   => 19.5,
            weekDays  => [1, 2, 3, 4, 5],
        },
        {
            hourBegin => 23,
            hourEnd   =>  5,
            weekDays  => [5, 6],
        },
   ];

This specifies one blackout period from 7:00am to 7:30pm local time on Mon-Fri, and a second period from 11pm to 5am on Friday and Saturday night.

$Conf{BackupZeroFilesIsFatal} = 1;

A backup of a share that has zero files is considered fatal. This is used to catch miscellaneous Xfer errors that result in no files being backed up. If you have shares that might be empty (and therefore an empty backup is valid) you should set this flag to 0.

How to backup a client

$Conf{XferMethod} = 'smb';

What transport method to use to backup each host. If you have a mixed set of WinXX and linux/unix hosts you will need to override this in the per-PC config.pl.

The valid values are:

  - 'smb':     backup and restore via smbclient and the SMB protocol.
               Easiest choice for WinXX.

  - 'rsync':   backup and restore via rsync (via rsh or ssh).
               Best choice for linux/unix.  Good choice also for WinXX.

  - 'rsyncd':  backup and restore via rsync daemon on the client.
               Best choice for linux/unix if you have rsyncd running on
               the client.  Good choice also for WinXX.

  - 'tar':    backup and restore via tar, tar over ssh, rsh or nfs.
              Good choice for linux/unix.

  - 'archive': host is a special archive host.  Backups are not done.
               An archive host is used to archive other host's backups
               to permanent media, such as tape, CDR or DVD.
$Conf{XferLogLevel} = 1;

Level of verbosity in Xfer log files. 0 means be quiet, 1 will give will give one line per file, 2 will also show skipped files on incrementals, higher values give more output.

$Conf{ClientCharset} = '';

Filename charset encoding on the client. BackupPC uses utf8 on the server for filename encoding. If this is empty, then utf8 is assumed and client filenames will not be modified. If set to a different encoding then filenames will converted to/from utf8 automatically during backup and restore.

If the file names displayed in the browser (eg: accents or special characters) don't look right then it is likely you haven't set $Conf{ClientCharset} correctly.

If you are using smbclient on a WinXX machine, smbclient will convert to the "unix charset" setting in smb.conf. The default is utf8, in which case leave $Conf{ClientCharset} empty since smbclient does the right conversion.

If you are using rsync on a WinXX machine then it does no conversion. A typical WinXX encoding for latin1/western europe is 'cp1252', so in this case set $Conf{ClientCharset} to 'cp1252'.

On a linux or unix client, run "locale charmap" to see the client's charset. Set $Conf{ClientCharset} to this value. A typical value for english/US is 'ISO-8859-1'.

Do "perldoc Encode::Supported" to see the list of possible charset values. The FAQ at http://www.cl.cam.ac.uk/~mgk25/unicode.html is excellent, and http://czyborra.com/charsets/iso8859.html provides more information on the iso-8859 charsets.

$Conf{ClientCharsetLegacy} = 'iso-8859-1';

Prior to 3.x no charset conversion was done by BackupPC. Backups were stored in what ever charset the XferMethod provided - typically utf8 for smbclient and the client's locale settings for rsync and tar (eg: cp1252 for rsync on WinXX and perhaps iso-8859-1 with rsync on linux). This setting tells BackupPC the charset that was used to store file names in old backups taken with BackupPC 2.x, so that non-ascii file names in old backups can be viewed and restored.

Samba Configuration

$Conf{SmbShareName} = 'C$';

Name of the host share that is backed up when using SMB. This can be a string or an array of strings if there are multiple shares per host. Examples:

  $Conf{SmbShareName} = 'c';          # backup 'c' share
  $Conf{SmbShareName} = ['c', 'd'];   # backup 'c' and 'd' shares

This setting only matters if $Conf{XferMethod} = 'smb'.

$Conf{SmbShareUserName} = '';

Smbclient share user name. This is passed to smbclient's -U argument.

This setting only matters if $Conf{XferMethod} = 'smb'.

$Conf{SmbSharePasswd} = '';

Smbclient share password. This is passed to smbclient via its PASSWD environment variable. There are several ways you can tell BackupPC the smb share password. In each case you should be very careful about security. If you put the password here, make sure that this file is not readable by regular users! See the "Setting up config.pl" section in the documentation for more information.

This setting only matters if $Conf{XferMethod} = 'smb'.

$Conf{SmbClientPath} = '';

Full path for smbclient. Security caution: normal users should not allowed to write to this file or directory.

smbclient is from the Samba distribution. smbclient is used to actually extract the incremental or full dump of the share filesystem from the PC.

This setting only matters if $Conf{XferMethod} = 'smb'.

$Conf{SmbClientFullCmd} = '$smbClientPath \\\\$host\\$shareName' ...

Command to run smbclient for a full dump. This setting only matters if $Conf{XferMethod} = 'smb'.

The following variables are substituted at run-time:

   $smbClientPath   same as $Conf{SmbClientPath}
   $host            host to backup/restore
   $hostIP          host IP address
   $shareName       share name
   $userName        user name
   $fileList        list of files to backup (based on exclude/include)
   $I_option        optional -I option to smbclient
   $X_option        exclude option (if $fileList is an exclude list)
   $timeStampFile   start time for incremental dump

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{SmbClientIncrCmd} = '$smbClientPath \\\\$host\\$shareName' ...

Command to run smbclient for an incremental dump. This setting only matters if $Conf{XferMethod} = 'smb'.

Same variable substitutions are applied as $Conf{SmbClientFullCmd}.

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{SmbClientRestoreCmd} = '$smbClientPath \\\\$host\\$shareName' ...

Command to run smbclient for a restore. This setting only matters if $Conf{XferMethod} = 'smb'.

Same variable substitutions are applied as $Conf{SmbClientFullCmd}.

If your smb share is read-only then direct restores will fail. You should set $Conf{SmbClientRestoreCmd} to undef and the corresponding CGI restore option will be removed.

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

Tar Configuration

$Conf{TarShareName} = '/';

Which host directories to backup when using tar transport. This can be a string or an array of strings if there are multiple directories to backup per host. Examples:

  $Conf{TarShareName} = '/';                    # backup everything
  $Conf{TarShareName} = '/home';                # only backup /home
  $Conf{TarShareName} = ['/home', '/src'];      # backup /home and /src

The fact this parameter is called 'TarShareName' is for historical consistency with the Smb transport options. You can use any valid directory on the client: there is no need for it to correspond to any Smb share or device mount point.

Note also that you can also use $Conf{BackupFilesOnly} to specify a specific list of directories to backup. It's more efficient to use this option instead of $Conf{TarShareName} since a new tar is run for each entry in $Conf{TarShareName}.

On the other hand, if you add --one-file-system to $Conf{TarClientCmd} you can backup each file system separately, which makes restoring one bad file system easier. In this case you would list all of the mount points here, since you can't get the same result with $Conf{BackupFilesOnly}:

    $Conf{TarShareName} = ['/', '/var', '/data', '/boot'];

This setting only matters if $Conf{XferMethod} = 'tar'.

$Conf{TarClientCmd} = '$sshPath -q -x -n -l root $host' ...

Command to run tar on the client. GNU tar is required. You will need to fill in the correct paths for ssh2 on the local host (server) and GNU tar on the client. Security caution: normal users should not allowed to write to these executable files or directories.

$Conf{TarClientCmd} is appended with with either $Conf{TarFullArgs} or $Conf{TarIncrArgs} to create the final command that is run.

See the documentation for more information about setting up ssh2 keys.

If you plan to use NFS then tar just runs locally and ssh2 is not needed. For example, assuming the client filesystem is mounted below /mnt/hostName, you could use something like:

   $Conf{TarClientCmd} = '$tarPath -c -v -f - -C /mnt/$host/$shareName'
                       . ' --totals';

In the case of NFS or rsh you need to make sure BackupPC's privileges are sufficient to read all the files you want to backup. Also, you will probably want to add "/proc" to $Conf{BackupFilesExclude}.

The following variables are substituted at run-time:

  $host        host name
  $hostIP      host's IP address
  $incrDate    newer-than date for incremental backups
  $shareName   share name to backup (ie: top-level directory path)
  $fileList    specific files to backup or exclude
  $tarPath     same as $Conf{TarClientPath}
  $sshPath     same as $Conf{SshPath}

If a variable is followed by a "+" it is shell escaped. This is necessary for the command part of ssh or rsh, since it ends up getting passed through the shell.

This setting only matters if $Conf{XferMethod} = 'tar'.

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{TarFullArgs} = '$fileList+';

Extra tar arguments for full backups. Several variables are substituted at run-time. See $Conf{TarClientCmd} for the list of variable substitutions.

If you are running tar locally (ie: without rsh or ssh) then remove the "+" so that the argument is no longer shell escaped.

This setting only matters if $Conf{XferMethod} = 'tar'.

$Conf{TarIncrArgs} = '--newer=$incrDate+ $fileList+';

Extra tar arguments for incr backups. Several variables are substituted at run-time. See $Conf{TarClientCmd} for the list of variable substitutions.

Note that GNU tar has several methods for specifying incremental backups, including:

  --newer-mtime $incrDate+
         This causes a file to be included if the modification time is
         later than $incrDate (meaning its contents might have changed).
         But changes in the ownership or modes will not qualify the
         file to be included in an incremental.

  --newer=$incrDate+
         This causes the file to be included if any attribute of the
         file is later than $incrDate, meaning either attributes or
         the modification time.  This is the default method.  Do
         not use --atime-preserve in $Conf{TarClientCmd} above,
         otherwise resetting the atime (access time) counts as an
         attribute change, meaning the file will always be included
         in each new incremental dump.

If you are running tar locally (ie: without rsh or ssh) then remove the "+" so that the argument is no longer shell escaped.

This setting only matters if $Conf{XferMethod} = 'tar'.

$Conf{TarClientRestoreCmd} = '$sshPath -q -x -l root $host' ...

Full command to run tar for restore on the client. GNU tar is required. This can be the same as $Conf{TarClientCmd}, with tar's -c replaced by -x and ssh's -n removed.

See $Conf{TarClientCmd} for full details.

This setting only matters if $Conf{XferMethod} = "tar".

If you want to disable direct restores using tar, you should set $Conf{TarClientRestoreCmd} to undef and the corresponding CGI restore option will be removed.

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{TarClientPath} = '';

Full path for tar on the client. Security caution: normal users should not allowed to write to this file or directory.

This setting only matters if $Conf{XferMethod} = 'tar'.

Rsync/Rsyncd Configuration

$Conf{RsyncClientPath} = '';

Path to rsync executable on the client

$Conf{RsyncClientCmd} = '$sshPath -q -x -l root $host $rsyncPath $argList+';

Full command to run rsync on the client machine. The following variables are substituted at run-time:

       $host           host name being backed up
       $hostIP         host's IP address
       $shareName      share name to backup (ie: top-level directory path)
       $rsyncPath      same as $Conf{RsyncClientPath}
       $sshPath        same as $Conf{SshPath}
       $argList        argument list, built from $Conf{RsyncArgs},
                       $shareName, $Conf{BackupFilesExclude} and
                       $Conf{BackupFilesOnly}

This setting only matters if $Conf{XferMethod} = 'rsync'.

$Conf{RsyncClientRestoreCmd} = '$sshPath -q -x -l root $host $rsyncPath $argList+';

Full command to run rsync for restore on the client. The following variables are substituted at run-time:

       $host           host name being backed up
       $hostIP         host's IP address
       $shareName      share name to backup (ie: top-level directory path)
       $rsyncPath      same as $Conf{RsyncClientPath}
       $sshPath        same as $Conf{SshPath}
       $argList        argument list, built from $Conf{RsyncArgs},
                       $shareName, $Conf{BackupFilesExclude} and
                       $Conf{BackupFilesOnly}

This setting only matters if $Conf{XferMethod} = 'rsync'.

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{RsyncShareName} = '/';

Share name to backup. For $Conf{XferMethod} = "rsync" this should be a file system path, eg '/' or '/home'.

For $Conf{XferMethod} = "rsyncd" this should be the name of the module to backup (ie: the name from /etc/rsynd.conf).

This can also be a list of multiple file system paths or modules. For example, by adding --one-file-system to $Conf{RsyncArgs} you can backup each file system separately, which makes restoring one bad file system easier. In this case you would list all of the mount points:

    $Conf{RsyncShareName} = ['/', '/var', '/data', '/boot'];
$Conf{RsyncdClientPort} = 873;

Rsync daemon port on the client, for $Conf{XferMethod} = "rsyncd".

$Conf{RsyncdUserName} = '';

Rsync daemon user name on client, for $Conf{XferMethod} = "rsyncd". The user name and password are stored on the client in whatever file the "secrets file" parameter in rsyncd.conf points to (eg: /etc/rsyncd.secrets).

$Conf{RsyncdPasswd} = '';

Rsync daemon user name on client, for $Conf{XferMethod} = "rsyncd". The user name and password are stored on the client in whatever file the "secrets file" parameter in rsyncd.conf points to (eg: /etc/rsyncd.secrets).

$Conf{RsyncdAuthRequired} = 1;

Whether authentication is mandatory when connecting to the client's rsyncd. By default this is on, ensuring that BackupPC will refuse to connect to an rsyncd on the client that is not password protected. Turn off at your own risk.

$Conf{RsyncCsumCacheVerifyProb} = 0.01;

When rsync checksum caching is enabled (by adding the --checksum-seed=32761 option to $Conf{RsyncArgs}), the cached checksums can be occasionally verified to make sure the file contents matches the cached checksums. This is to avoid the risk that disk problems might cause the pool file contents to get corrupted, but the cached checksums would make BackupPC think that the file still matches the client.

This setting is the probability (0 means never and 1 means always) that a file will be rechecked. Setting it to 0 means the checksums will not be rechecked (unless there is a phase 0 failure). Setting it to 1 (ie: 100%) means all files will be checked, but that is not a desirable setting since you are better off simply turning caching off (ie: remove the --checksum-seed option).

The default of 0.01 means 1% (on average) of the files during a full backup will have their cached checksum re-checked.

This setting has no effect unless checksum caching is turned on.

$Conf{RsyncArgs} = [ ... ];

Arguments to rsync for backup. Do not edit the first set unless you have a thorough understanding of how File::RsyncP works.

$Conf{RsyncArgsExtra} = [];

Additional arguments added to RsyncArgs. This can be used in conbination with $Conf{RsyncArgs} to allow customization of the rsync arguments on a part-client basis. The standard arguments go in $Conf{RsyncArgs} and $Conf{RsyncArgsExtra} can be set on a per-client basis.

Examples of additional arguments that should work are --exclude/--include, eg:

    $Conf{RsyncArgsExtra} = [
          '--exclude', '/proc',
          '--exclude', '*.tmp',
    ];

Both $Conf{RsyncArgs} and $Conf{RsyncArgsExtra} are subject to the following variable substitutions:

       $client       client name being backed up
       $host         host name (could be different from client name if
                                $Conf{ClientNameAlias} is set)
       $hostIP       IP address of host
       $confDir      configuration directory path

This allows settings of the form:

    $Conf{RsyncArgsExtra} = [
            '--exclude-from=$confDir/pc/$host.exclude',
    ];
$Conf{RsyncRestoreArgs} = [ ... ];

Arguments to rsync for restore. Do not edit the first set unless you have a thorough understanding of how File::RsyncP works.

If you want to disable direct restores using rsync (eg: is the module is read-only), you should set $Conf{RsyncRestoreArgs} to undef and the corresponding CGI restore option will be removed.

$Conf{RsyncRestoreArgs} is subject to the following variable substitutions:

       $client       client name being backed up
       $host         host name (could be different from client name if
                                $Conf{ClientNameAlias} is set)
       $hostIP       IP address of host
       $confDir      configuration directory path

Note: $Conf{RsyncArgsExtra} doesn't apply to $Conf{RsyncRestoreArgs}.

FTP Configuration

$Conf{FtpShareName} = '';

Which host directories to backup when using FTP. This can be a string or an array of strings if there are multiple shares per host.

This value must be specified in one of two ways: either as a subdirectory of the 'share root' on the server, or as the absolute path of the directory.

In the following example, if the directory /home/username is the root share of the ftp server with the given username, the following two values will back up the same directory:

   $Conf{FtpShareName} = 'www';                # www directory
   $Conf{FtpShareName} = '/home/username/www'; # same directory

Path resolution is not supported; i.e.; you may not have an ftp share path defined as '../otheruser' or '~/games'.

 Multiple shares may also be specified, as with other protocols:

   $Conf{FtpShareName} = [ 'www',
                           'bin',
                           'config' ];

Note also that you can also use $Conf{BackupFilesOnly} to specify a specific list of directories to backup. It's more efficient to use this option instead of $Conf{FtpShareName} since a new tar is run for each entry in $Conf{FtpShareName}.

This setting only matters if $Conf{XferMethod} = 'ftp'.

$Conf{FtpUserName} = '';

FTP user name. This is used to log into the server.

This setting is used only if $Conf{XferMethod} = 'ftp'.

$Conf{FtpPasswd} = '';

FTP user password. This is used to log into the server.

This setting is used only if $Conf{XferMethod} = 'ftp'.

$Conf{FtpPassive} = 1;

Whether passive mode is used. The correct setting depends upon whether local or remote ports are accessible from the other machine, which is affected by any firewall or routers between the FTP server on the client and the BackupPC server.

This setting is used only if $Conf{XferMethod} = 'ftp'.

$Conf{FtpBlockSize} = 10240;

Transfer block size. This sets the size of the amounts of data in each frame. While undefined, this value takes the default value.

This setting is used only if $Conf{XferMethod} = 'ftp'.

$Conf{FtpPort} = 21;

The port of the ftp server. If undefined, 21 is used.

This setting is used only if $Conf{XferMethod} = 'ftp'.

$Conf{FtpTimeout} = 120;

Connection timeout for FTP. When undefined, the default is 120 seconds.

This setting is used only if $Conf{XferMethod} = 'ftp'.

Behaviour when BackupPC encounters symlinks on the FTP share.

Symlinks cannot be restored via FTP, so the desired behaviour will be different depending on the setup of the share. The default for this behavor is 1. Directory shares with more complicated directory structures should consider other protocols.

Archive Configuration

$Conf{ArchiveDest} = '/tmp';

Archive Destination

The Destination of the archive e.g. /tmp for file archive or /dev/nst0 for device archive

$Conf{ArchiveComp} = 'gzip';

Archive Compression type

The valid values are:

  - 'none':  No Compression

  - 'gzip':  Medium Compression. Recommended.

  - 'bzip2': High Compression but takes longer.
$Conf{ArchivePar} = 0;

Archive Parity Files

The amount of Parity data to generate, as a percentage of the archive size. Uses the commandline par2 (par2cmdline) available from http://parchive.sourceforge.net

Only useful for file dumps.

Set to 0 to disable this feature.

$Conf{ArchiveSplit} = 0;

Archive Size Split

Only for file archives. Splits the output into the specified size * 1,000,000. e.g. to split into 650,000,000 bytes, specify 650 below.

If the value is 0, or if $Conf{ArchiveDest} is an existing file or device (e.g. a streaming tape drive), this feature is disabled.

$Conf{ArchiveClientCmd} = '$Installdir/bin/BackupPC_archiveHost' ...

Archive Command

This is the command that is called to actually run the archive process for each host. The following variables are substituted at run-time:

  $Installdir    The installation directory of BackupPC
  $tarCreatePath The path to BackupPC_tarCreate
  $splitpath     The path to the split program
  $parpath       The path to the par2 program
  $host          The host to archive
  $backupnumber  The backup number of the host to archive
  $compression   The path to the compression program
  $compext       The extension assigned to the compression type
  $splitsize     The number of bytes to split archives into
  $archiveloc    The location to put the archive
  $parfile       The amount of parity data to create (percentage)

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{SshPath} = '';

Full path for ssh. Security caution: normal users should not allowed to write to this file or directory.

$Conf{NmbLookupPath} = '';

Full path for nmblookup. Security caution: normal users should not allowed to write to this file or directory.

nmblookup is from the Samba distribution. nmblookup is used to get the netbios name, necessary for DHCP hosts.

$Conf{NmbLookupCmd} = '$nmbLookupPath -A $host';

NmbLookup command. Given an IP address, does an nmblookup on that IP address. The following variables are substituted at run-time:

  $nmbLookupPath      path to nmblookup ($Conf{NmbLookupPath})
  $host               IP address

This command is only used for DHCP hosts: given an IP address, this command should try to find its NetBios name.

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{NmbLookupFindHostCmd} = '$nmbLookupPath $host';

NmbLookup command. Given a netbios name, finds that host by doing a NetBios lookup. Several variables are substituted at run-time:

  $nmbLookupPath      path to nmblookup ($Conf{NmbLookupPath})
  $host               NetBios name

In some cases you might need to change the broadcast address, for example if nmblookup uses 192.168.255.255 by default and you find that doesn't work, try 192.168.1.255 (or your equivalent class C address) using the -B option:

   $Conf{NmbLookupFindHostCmd} = '$nmbLookupPath -B 192.168.1.255 $host';

If you use a WINS server and your machines don't respond to multicast NetBios requests you can use this (replace 1.2.3.4 with the IP address of your WINS server):

   $Conf{NmbLookupFindHostCmd} = '$nmbLookupPath -R -U 1.2.3.4 $host';

This is preferred over multicast since it minimizes network traffic.

Experiment manually for your site to see what form of nmblookup command works.

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{FixedIPNetBiosNameCheck} = 0;

For fixed IP address hosts, BackupPC_dump can also verify the netbios name to ensure it matches the host name. An error is generated if they do not match. Typically this flag is off. But if you are going to transition a bunch of machines from fixed host addresses to DHCP, setting this flag is a great way to verify that the machines have their netbios name set correctly before turning on DCHP.

$Conf{PingPath} = '';

Full path to the ping command. Security caution: normal users should not be allowed to write to this file or directory.

If you want to disable ping checking, set this to some program that exits with 0 status, eg:

    $Conf{PingPath} = '/bin/echo';
$Conf{PingCmd} = '$pingPath -c 1 $host';

Ping command. The following variables are substituted at run-time:

  $pingPath      path to ping ($Conf{PingPath})
  $host          host name

Wade Brown reports that on solaris 2.6 and 2.7 ping -s returns the wrong exit status (0 even on failure). Replace with "ping $host 1", which gets the correct exit status but we don't get the round-trip time.

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{PingMaxMsec} = 20;

Maximum round-trip ping time in milliseconds. This threshold is set to avoid backing up PCs that are remotely connected through WAN or dialup connections. The output from ping -s (assuming it is supported on your system) is used to check the round-trip packet time. On your local LAN round-trip times should be much less than 20msec. On most WAN or dialup connections the round-trip time will be typically more than 20msec. Tune if necessary.

$Conf{CompressLevel} = 0;

Compression level to use on files. 0 means no compression. Compression levels can be from 1 (least cpu time, slightly worse compression) to 9 (most cpu time, slightly better compression). The recommended value is 3. Changing to 5, for example, will take maybe 20% more cpu time and will get another 2-3% additional compression. See the zlib documentation for more information about compression levels.

Changing compression on or off after backups have already been done will require both compressed and uncompressed pool files to be stored. This will increase the pool storage requirements, at least until all the old backups expire and are deleted.

It is ok to change the compression value (from one non-zero value to another non-zero value) after dumps are already done. Since BackupPC matches pool files by comparing the uncompressed versions, it will still correctly match new incoming files against existing pool files. The new compression level will take effect only for new files that are newly compressed and added to the pool.

If compression was off and you are enabling compression for the first time you can use the BackupPC_compressPool utility to compress the pool. This avoids having the pool grow to accommodate both compressed and uncompressed backups. See the documentation for more information.

Note: compression needs the Compress::Zlib perl library. If the Compress::Zlib library can't be found then $Conf{CompressLevel} is forced to 0 (compression off).

$Conf{ClientTimeout} = 72000;

Timeout in seconds when listening for the transport program's (smbclient, tar etc) stdout. If no output is received during this time, then it is assumed that something has wedged during a backup, and the backup is terminated.

Note that stdout buffering combined with huge files being backed up could cause longish delays in the output from smbclient that BackupPC_dump sees, so in rare cases you might want to increase this value.

Despite the name, this parameter sets the timeout for all transport methods (tar, smb etc).

$Conf{MaxOldPerPCLogFiles} = 12;

Maximum number of log files we keep around in each PC's directory (ie: pc/$host). These files are aged monthly. A setting of 12 means there will be at most the files LOG, LOG.0, LOG.1, ... LOG.11 in the pc/$host directory (ie: about a years worth). (Except this month's LOG, these files will have a .z extension if compression is on).

If you decrease this number after BackupPC has been running for a while you will have to manually remove the older log files.

$Conf{DumpPreUserCmd} = undef;
$Conf{DumpPostUserCmd} = undef;
$Conf{DumpPreShareCmd} = undef;
$Conf{DumpPostShareCmd} = undef;
$Conf{RestorePreUserCmd} = undef;
$Conf{RestorePostUserCmd} = undef;
$Conf{ArchivePreUserCmd} = undef;
$Conf{ArchivePostUserCmd} = undef;

Optional commands to run before and after dumps and restores, and also before and after each share of a dump.

Stdout from these commands will be written to the Xfer (or Restore) log file. One example of using these commands would be to shut down and restart a database server, dump a database to files for backup, or doing a snapshot of a share prior to a backup. Example:

   $Conf{DumpPreUserCmd} = '$sshPath -q -x -l root $host /usr/bin/dumpMysql';

The following variable substitutions are made at run time for $Conf{DumpPreUserCmd}, $Conf{DumpPostUserCmd}, $Conf{DumpPreShareCmd} and $Conf{DumpPostShareCmd}:

       $type         type of dump (incr or full)
       $xferOK       1 if the dump succeeded, 0 if it didn't
       $client       client name being backed up
       $host         host name (could be different from client name if
                                $Conf{ClientNameAlias} is set)
       $hostIP       IP address of host
       $user         user name from the hosts file
       $moreUsers    list of additional users from the hosts file
       $share        the first share name (or current share for
                       $Conf{DumpPreShareCmd} and $Conf{DumpPostShareCmd})
       $shares       list of all the share names
       $XferMethod   value of $Conf{XferMethod} (eg: tar, rsync, smb)
       $sshPath      value of $Conf{SshPath},
       $cmdType      set to DumpPreUserCmd or DumpPostUserCmd

The following variable substitutions are made at run time for $Conf{RestorePreUserCmd} and $Conf{RestorePostUserCmd}:

       $client       client name being backed up
       $xferOK       1 if the restore succeeded, 0 if it didn't
       $host         host name (could be different from client name if
                                $Conf{ClientNameAlias} is set)
       $hostIP       IP address of host
       $user         user name from the hosts file
       $moreUsers    list of additional users from the hosts file
       $share        the first share name
       $XferMethod   value of $Conf{XferMethod} (eg: tar, rsync, smb)
       $sshPath      value of $Conf{SshPath},
       $type         set to "restore"
       $bkupSrcHost  host name of the restore source
       $bkupSrcShare share name of the restore source
       $bkupSrcNum   backup number of the restore source
       $pathHdrSrc   common starting path of restore source
       $pathHdrDest  common starting path of destination
       $fileList     list of files being restored
       $cmdType      set to RestorePreUserCmd or RestorePostUserCmd

The following variable substitutions are made at run time for $Conf{ArchivePreUserCmd} and $Conf{ArchivePostUserCmd}:

       $client       client name being backed up
       $xferOK       1 if the archive succeeded, 0 if it didn't
       $host         Name of the archive host
       $user         user name from the hosts file
       $share        the first share name
       $XferMethod   value of $Conf{XferMethod} (eg: tar, rsync, smb)
       $HostList     list of hosts being archived
       $BackupList   list of backup numbers for the hosts being archived
       $archiveloc   location where the archive is sent to
       $parfile      amount of parity data being generated (percentage)
       $compression  compression program being used (eg: cat, gzip, bzip2)
       $compext      extension used for compression type (eg: raw, gz, bz2)
       $splitsize    size of the files that the archive creates
       $sshPath      value of $Conf{SshPath},
       $type         set to "archive"
       $cmdType      set to ArchivePreUserCmd or ArchivePostUserCmd

Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it.

$Conf{UserCmdCheckStatus} = 0;

Whether the exit status of each PreUserCmd and PostUserCmd is checked.

If set and the Dump/Restore/Archive Pre/Post UserCmd returns a non-zero exit status then the dump/restore/archive is aborted. To maintain backward compatibility (where the exit status in early versions was always ignored), this flag defaults to 0.

If this flag is set and the Dump/Restore/Archive PreUserCmd fails then the matching Dump/Restore/Archive PostUserCmd is not executed. If DumpPreShareCmd returns a non-exit status, then DumpPostShareCmd is not executed, but the DumpPostUserCmd is still run (since DumpPreUserCmd must have previously succeeded).

An example of a DumpPreUserCmd that might fail is a script that snapshots or dumps a database which fails because of some database error.

$Conf{ClientNameAlias} = undef;

Override the client's host name. This allows multiple clients to all refer to the same physical host. This should only be set in the per-PC config file and is only used by BackupPC at the last moment prior to generating the command used to backup that machine (ie: the value of $Conf{ClientNameAlias} is invisible everywhere else in BackupPC). The setting can be a host name or IP address, eg:

        $Conf{ClientNameAlias} = 'realHostName';
        $Conf{ClientNameAlias} = '192.1.1.15';

will cause the relevant smb/tar/rsync backup/restore commands to be directed to realHostName, not the client name.

Note: this setting doesn't work for hosts with DHCP set to 1.

Email reminders, status and messages

$Conf{SendmailPath} = '';

Full path to the sendmail command. Security caution: normal users should not allowed to write to this file or directory.

$Conf{EMailNotifyMinDays} = 2.5;

Minimum period between consecutive emails to a single user. This tries to keep annoying email to users to a reasonable level. Email checks are done nightly, so this number is effectively rounded up (ie: 2.5 means a user will never receive email more than once every 3 days).

$Conf{EMailFromUserName} = '';

Name to use as the "from" name for email. Depending upon your mail handler this is either a plain name (eg: "admin") or a fully-qualified name (eg: "admin@mydomain.com").

$Conf{EMailAdminUserName} = '';

Destination address to an administrative user who will receive a nightly email with warnings and errors. If there are no warnings or errors then no email will be sent. Depending upon your mail handler this is either a plain name (eg: "admin") or a fully-qualified name (eg: "admin@mydomain.com").

$Conf{EMailUserDestDomain} = '';

Destination domain name for email sent to users. By default this is empty, meaning email is sent to plain, unqualified addresses. Otherwise, set it to the destintation domain, eg:

   $Cong{EMailUserDestDomain} = '@mydomain.com';

With this setting user email will be set to 'user@mydomain.com'.

$Conf{EMailNoBackupEverSubj} = undef;
$Conf{EMailNoBackupEverMesg} = undef;

This subject and message is sent to a user if their PC has never been backed up.

These values are language-dependent. The default versions can be found in the language file (eg: lib/BackupPC/Lang/en.pm). If you need to change the message, copy it here and edit it, eg:

  $Conf{EMailNoBackupEverMesg} = <<'EOF';
  To: $user$domain
  cc:
  Subject: $subj

  Dear $userName,

  This is a site-specific email message.
  EOF
$Conf{EMailNotifyOldBackupDays} = 7.0;

How old the most recent backup has to be before notifying user. When there have been no backups in this number of days the user is sent an email.

$Conf{EMailNoBackupRecentSubj} = undef;
$Conf{EMailNoBackupRecentMesg} = undef;

This subject and message is sent to a user if their PC has not recently been backed up (ie: more than $Conf{EMailNotifyOldBackupDays} days ago).

These values are language-dependent. The default versions can be found in the language file (eg: lib/BackupPC/Lang/en.pm). If you need to change the message, copy it here and edit it, eg:

  $Conf{EMailNoBackupRecentMesg} = <<'EOF';
  To: $user$domain
  cc:
  Subject: $subj

  Dear $userName,

  This is a site-specific email message.
  EOF
$Conf{EMailNotifyOldOutlookDays} = 5.0;

How old the most recent backup of Outlook files has to be before notifying user.

$Conf{EMailOutlookBackupSubj} = undef;
$Conf{EMailOutlookBackupMesg} = undef;

This subject and message is sent to a user if their Outlook files have not recently been backed up (ie: more than $Conf{EMailNotifyOldOutlookDays} days ago).

These values are language-dependent. The default versions can be found in the language file (eg: lib/BackupPC/Lang/en.pm). If you need to change the message, copy it here and edit it, eg:

  $Conf{EMailOutlookBackupMesg} = <<'EOF';
  To: $user$domain
  cc:
  Subject: $subj

  Dear $userName,

  This is a site-specific email message.
  EOF
$Conf{EMailHeaders} = <<EOF;

Additional email headers. This sets to charset to utf8.

CGI user interface configuration settings

$Conf{CgiAdminUserGroup} = '';
$Conf{CgiAdminUsers} = '';

Normal users can only access information specific to their host. They can start/stop/browse/restore backups.

Administrative users have full access to all hosts, plus overall status and log information.

The administrative users are the union of the unix/linux group $Conf{CgiAdminUserGroup} and the manual list of users, separated by spaces, in $Conf{CgiAdminUsers}. If you don't want a group or manual list of users set the corresponding configuration setting to undef or an empty string.

If you want every user to have admin privileges (careful!), set $Conf{CgiAdminUsers} = '*'.

Examples:

   $Conf{CgiAdminUserGroup} = 'admin';
   $Conf{CgiAdminUsers}     = 'craig celia';
   --> administrative users are the union of group admin, plus
     craig and celia.

   $Conf{CgiAdminUserGroup} = '';
   $Conf{CgiAdminUsers}     = 'craig celia';
   --> administrative users are only craig and celia'.
$Conf{CgiURL} = undef;

URL of the BackupPC_Admin CGI script. Used for email messages.

$Conf{Language} = 'en';

Language to use. See lib/BackupPC/Lang for the list of supported languages, which include English (en), French (fr), Spanish (es), German (de), Italian (it), Dutch (nl), Polish (pl), Portuguese Brazillian (pt_br) and Chinese (zh_CH).

Currently the Language setting applies to the CGI interface and email messages sent to users. Log files and other text are still in English.

$Conf{CgiUserHomePageCheck} = '';
$Conf{CgiUserUrlCreate} = 'mailto:%s';

User names that are rendered by the CGI interface can be turned into links into their home page or other information about the user. To set this up you need to create two sprintf() strings, that each contain a single '%s' that will be replaced by the user name. The default is a mailto: link.

$Conf{CgiUserHomePageCheck} should be an absolute file path that is used to check (via "-f") that the user has a valid home page. Set this to undef or an empty string to turn off this check.

$Conf{CgiUserUrlCreate} should be a full URL that points to the user's home page. Set this to undef or an empty string to turn off generation of URLs for user names.

Example:

   $Conf{CgiUserHomePageCheck} = '/var/www/html/users/%s.html';
   $Conf{CgiUserUrlCreate}     = 'http://myhost/users/%s.html';
   --> if /var/www/html/users/craig.html exists, then 'craig' will
     be rendered as a link to http://myhost/users/craig.html.
$Conf{CgiDateFormatMMDD} = 1;

Date display format for CGI interface. A value of 1 uses US-style dates (MM/DD), a value of 2 uses full YYYY-MM-DD format, and zero for international dates (DD/MM).

$Conf{CgiNavBarAdminAllHosts} = 1;

If set, the complete list of hosts appears in the left navigation bar pull-down for administrators. Otherwise, just the hosts for which the user is listed in the host file (as either the user or in moreUsers) are displayed.

$Conf{CgiSearchBoxEnable} = 1;

Enable/disable the search box in the navigation bar.

Additional navigation bar links. These appear for both regular users and administrators. This is a list of hashes giving the link (URL) and the text (name) for the link. Specifying lname instead of name uses the language specific string (ie: $Lang->{lname}) instead of just literally displaying name.

$Conf{CgiStatusHilightColor} = { ...

Hilight colors based on status that are used in the PC summary page.

$Conf{CgiHeaders} = '<meta http-equiv="pragma" content="no-cache">';

Additional CGI header text.

$Conf{CgiImageDir} = '';

Directory where images are stored. This directory should be below Apache's DocumentRoot. This value isn't used by BackupPC but is used by configure.pl when you upgrade BackupPC.

Example:

    $Conf{CgiImageDir} = '/var/www/htdocs/BackupPC';
$Conf{CgiExt2ContentType} = { };

Additional mappings of file name extenions to Content-Type for individual file restore. See $Ext2ContentType in BackupPC_Admin for the default setting. You can add additional settings here, or override any default settings. Example:

    $Conf{CgiExt2ContentType} = {
                'pl'  => 'text/plain',
         };
$Conf{CgiImageDirURL} = '';

URL (without the leading http://host) for BackupPC's image directory. The CGI script uses this value to serve up image files.

Example:

    $Conf{CgiImageDirURL} = '/BackupPC';
$Conf{CgiCSSFile} = 'BackupPC_stnd.css';

CSS stylesheet "skin" for the CGI interface. It is stored in the $Conf{CgiImageDir} directory and accessed via the $Conf{CgiImageDirURL} URL.

For BackupPC v3.x several color, layout and font changes were made. The previous v2.x version is available as BackupPC_stnd_orig.css, so if you prefer the old skin, change this to BackupPC_stnd_orig.css.

$Conf{CgiUserConfigEditEnable} = 1;

Whether the user is allowed to edit their per-PC config.

$Conf{CgiUserConfigEdit} = { ...

Which per-host config variables a non-admin user is allowed to edit. Admin users can edit all per-host config variables, even if disabled in this list.

SECURITY WARNING: Do not let users edit any of the Cmd config variables! That's because a user could set a Cmd to a shell script of their choice and it will be run as the BackupPC user. That script could do all sorts of bad things.


Version Numbers

Starting with v1.4.0 BackupPC uses a X.Y.Z version numbering system, instead of X.0Y. The first digit is for major new releases, the middle digit is for significant feature releases and improvements (most of the releases have been in this category), and the last digit is for bug fixes. You should think of the old 1.00, 1.01, 1.02 and 1.03 as 1.0.0, 1.1.0, 1.2.0 and 1.3.0.

Additionally, patches might be made available. A patched version number is of the form X.Y.ZplN (eg: 2.1.0pl2), where N is the patch level.


Author

Craig Barratt <cbarratt@users.sourceforge.net>

See http://backuppc.sourceforge.net.


Copyright

Copyright (C) 2001-2015 Craig Barratt


Credits

Ryan Kucera contributed the directory navigation code and images for v1.5.0. He contributed the first skeleton of BackupPC_restore. He also added a significant revision to the CGI interface, including CSS tags, in v2.1.0, and designed the BackupPC logo.

Xavier Nicollet, with additions from Guillaume Filion, added the internationalization (i18n) support to the CGI interface for v2.0.0. Xavier provided the French translation fr.pm, with additions from Guillaume.

Guillaume Filion wrote BackupPC_zipCreate and added the CGI support for zip download, in addition to some CGI cleanup, for v1.5.0. Guillaume continues to support fr.pm updates for each new version.

Josh Marshall implemented the Archive feature in v2.1.0.

Ludovic Drolez supports the BackupPC Debian package.

Javier Gonzalez provided the Spanish translation, es.pm for v2.0.0.

Manfred Herrmann provided the German translation, de.pm for v2.0.0. Manfred continues to support de.pm updates for each new version, together with some help from Ralph Paßgang.

Lorenzo Cappelletti provided the Italian translation, it.pm for v2.1.0. Giuseppe Iuculano and Vittorio Macchi updated it for 3.0.0.

Lieven Bridts provided the Dutch translation, nl.pm, for v2.1.0, with some tweaks from Guus Houtzager, and updates for 3.0.0.

Reginaldo Ferreira provided the Portuguese Brazillian translation pt_br.pm for v2.2.0.

Rich Duzenbury provided the RSS feed option to the CGI interface.

Jono Woodhouse from CapeSoft Software (www.capesoft.com) provided a new CSS skin for 3.0.0 with several layout improvements. Sean Cameron (also from CapeSoft) designed new and more compact file icons for 3.0.0.

Youlin Feng provided the Chinese translation for 3.1.0.

Karol 'Semper' Stelmaczonek provided the Polish translation for 3.1.0.

Jeremy Tietsort provided the host summary table sorting feature for 3.1.0.

Paul Mantz contributed the ftp Xfer method for 3.2.0.

Petr Pokorny provided the Czech translation for 3.2.1.

Rikiya Yamamoto provided the Japanese translation for 3.3.0.

Yakim provided the Ukrainian translation for 3.3.0.

Sergei Butakov provided the Russian translation for 3.3.0.

Many people have reported bugs, made useful suggestions and helped with testing; see the ChangeLog and the mailing lists.

Your name could appear here in the next version!


License

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License in the LICENSE file along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

 BackupPC
BackupPC-3.3.2/doc/BackupPC.pod0000444000076500000240000054510013042250554015023 0ustar craigstaff=encoding ISO8859-1 =head1 BackupPC Introduction This documentation describes BackupPC version 3.3.2, released on 25 Jan 2017. =head2 Overview BackupPC is a high-performance, enterprise-grade system for backing up Unix, Linux, WinXX, and MacOSX PCs, desktops and laptops to a server's disk. BackupPC is highly configurable and easy to install and maintain. Given the ever decreasing cost of disks and raid systems, it is now practical and cost effective to backup a large number of machines onto a server's local disk or network storage. For some sites this might be the complete backup solution. For other sites additional permanent archives could be created by periodically backing up the server to tape. Features include: =over 4 =item * A clever pooling scheme minimizes disk storage and disk I/O. Identical files across multiple backups of the same or different PC are stored only once (using hard links), resulting in substantial savings in disk storage and disk writes. =item * Optional compression provides additional reductions in storage (around 40%). The CPU impact of compression is low since only new files (those not already in the pool) need to be compressed. =item * A powerful http/cgi user interface allows administrators to view the current status, edit configuration, add/delete hosts, view log files, and allows users to initiate and cancel backups and browse and restore files from backups. =item * The http/cgi user interface has internationalization (i18n) support, currently providing English, French, German, Spanish, Italian, Dutch, Polish, Portuguese-Brazilian and Chinese =item * No client-side software is needed. On WinXX the standard smb protocol is used to extract backup data. On linux, unix or MacOSX clients, rsync, tar (over ssh/rsh/nfs) or ftp is used to extract backup data. Alternatively, rsync can also be used on WinXX (using cygwin), and Samba could be installed on the linux or unix client to provide smb shares). =item * Flexible restore options. Single files can be downloaded from any backup directly from the CGI interface. Zip or Tar archives for selected files or directories from any backup can also be downloaded from the CGI interface. Finally, direct restore to the client machine (using smb or tar) for selected files or directories is also supported from the CGI interface. =item * BackupPC supports mobile environments where laptops are only intermittently connected to the network and have dynamic IP addresses (DHCP). Configuration settings allow machines connected via slower WAN connections (eg: dial up, DSL, cable) to not be backed up, even if they use the same fixed or dynamic IP address as when they are connected directly to the LAN. =item * Flexible configuration parameters allow multiple backups to be performed in parallel, specification of which shares to backup, which directories to backup or not backup, various schedules for full and incremental backups, schedules for email reminders to users and so on. Configuration parameters can be set system-wide or also on a per-PC basis. =item * Users are sent periodic email reminders if their PC has not recently been backed up. Email content, timing and policies are configurable. =item * BackupPC is Open Source software hosted by SourceForge. =back =head2 Backup basics =over 4 =item Full Backup A full backup is a complete backup of a share. BackupPC can be configured to do a full backup at a regular interval (typically weekly). BackupPC can be configured to keep a certain number of full backups. Exponential expiry is also supported, allowing full backups with various vintages to be kept (for example, a settable number of most recent weekly fulls, plus a settable number of older fulls that are 2, 4, 8, or 16 weeks apart). =item Incremental Backup An incremental backup is a backup of files that have changed since the last successful full or incremental backup. Starting in BackupPC 3.0 multi-level incrementals are supported. A full backup has level 0. A new incremental of level N will backup all files that have changed since the most recent backup of a lower level. $Conf{IncrLevels} is used to specify the level of each successive incremental. The default value is all level 1, which makes the behavior the same as earlier versions of BackupPC: each incremental will back up all the files that changed since the last full (level 0). For SMB and tar, BackupPC uses the modification time (mtime) to determine which files have changed since the last lower-level backup. That means SMB and tar incrementals are not able to detect deleted files, renamed files or new files whose modification time is prior to the last lower-level backup. Rsync is more clever: any files whose attributes have changed (ie: uid, gid, mtime, modes, size) since the last full are backed up. Deleted, new files and renamed files are detected by Rsync incrementals. BackupPC can also be configured to keep a certain number of incremental backups, and to keep a smaller number of very old incremental backups. If multi-level incrementals are specified then it is likely that more incrementals will need to be kept since lower-level incrementals (and the full backup) are needed to reconstruct a higher-level incremental. BackupPC "fills-in" incremental backups when browsing or restoring, based on the levels of each backup, giving every backup a "full" appearance. This makes browsing and restoring backups much easier: you can restore from any one backup independent of whether it was an incremental or full. =item Partial Backup When a full backup fails or is canceled, and some files have already been backed up, BackupPC keeps a partial backup containing just the files that were backed up successfully. The partial backup is removed when the next successful backup completes, or if another full backup fails resulting in a newer partial backup. A failed full backup that has not backed up any files, or any failed incremental backup, is removed; no partial backup is saved in these cases. The partial backup may be browsed or used to restore files just like a successful full or incremental backup. With the rsync transfer method the partial backup is used to resume the next full backup, avoiding the need to retransfer the file data already in the partial backup. =item Identical Files BackupPC pools identical files using hardlinks. By "identical files" we mean files with identical contents, not necessary the same permissions, ownership or modification time. Two files might have different permissions, ownership, or modification time but will still be pooled whenever the contents are identical. This is possible since BackupPC stores the file meta-data (permissions, ownership, and modification time) separately from the file contents. =item Backup Policy Based on your site's requirements you need to decide what your backup policy is. BackupPC is not designed to provide exact re-imaging of failed disks. See L for more information. However, the addition of tar transport for linux/unix clients, plus full support for special file types and unix attributes in v1.4.0 likely means an exact image of a linux/unix file system can be made. BackupPC saves backups onto disk. Because of pooling you can relatively economically keep several weeks of old backups. At some sites the disk-based backup will be adequate, without a secondary tape backup. This system is robust to any single failure: if a client disk fails or loses files, the BackupPC server can be used to restore files. If the server disk fails, BackupPC can be restarted on a fresh file system, and create new backups from the clients. The chance of the server disk failing can be made very small by spending more money on increasingly better RAID systems. However, there is still the risk of catastrophic events like fires or earthquakes that can destroy both the BackupPC server and the clients it is backing up if they are physically nearby. Some sites might choose to do periodic backups to tape or cd/dvd. This backup can be done perhaps weekly using the archive function of BackupPC. Other users have reported success with removable disks to rotate the BackupPC data drives, or using rsync to mirror the BackupPC data pool offsite. =back =head2 Resources =over 4 =item BackupPC home page The BackupPC Open Source project is hosted on Github and SourceForge. The project home page can be found at: http://backuppc.sourceforge.net This page has links to the current documentation, the Github and SourceForge project pages and general information. =item Github and SourceForge locations The Github project page is at: https://github.com/backuppc/backuppc The SourceForge project page is at: http://sourceforge.net/projects/backuppc Generally use of SourceForge has been deprecated in favor of Github. All source code and development has moved to Github starting in 2016. Releases will continue to be available on both Github and SourceForge. This page has links to the current releases of BackupPC. =item BackupPC Wiki BackupPC has a Wiki at L. Everyone is encouraged to contribute to the Wiki. =item Mailing lists Three BackupPC mailing lists exist for announcements (backuppc-announce), developers (backuppc-devel), and a general user list for support, asking questions or any other topic relevant to BackupPC (backuppc-users). The lists are archived on SourceForge and Gmane. The SourceForge lists are not always up to date and the searching is limited, so Gmane is a good alternative. See: http://news.gmane.org/index.php?prefix=gmane.comp.sysutils.backup.backuppc http://sourceforge.net/mailarchive/forum.php?forum=backuppc-users You can subscribe to these lists by visiting: http://lists.sourceforge.net/lists/listinfo/backuppc-announce http://lists.sourceforge.net/lists/listinfo/backuppc-users http://lists.sourceforge.net/lists/listinfo/backuppc-devel The backuppc-announce list is moderated and is used only for important announcements (eg: new versions). It is low traffic. You only need to subscribe to one of backuppc-announce and backuppc-users: backuppc-users also receives any messages on backuppc-announce. The backuppc-devel list is only for developers who are working on BackupPC. Do not post questions or support requests there. But detailed technical discussions should happen on this list. To post a message to the backuppc-users list, send an email to backuppc-users@lists.sourceforge.net Do not send subscription requests to this address! =item Other Programs of Interest If you want to mirror linux or unix files or directories to a remote server you should use rsync, L. BackupPC uses rsync as a transport mechanism; if you are already an rsync user you can think of BackupPC as adding efficient storage (compression and pooling) and a convenient user interface to rsync. Two popular open source packages that do tape backup are Amanda (L) and Bacula (L). These packages can be used as complete solutions, or also as back ends to BackupPC to backup the BackupPC server data to tape. Various programs and scripts use rsync to provide hardlinked backups. See, for example, Mike Rubel's site (L), JW Schultz's dirvish (L), Ben Escoto's rdiff-backup (L), and John Bowman's rlbackup (L). Unison is a utility that can do two-way, interactive, synchronization. See L. An external wrapper around rsync that maintains transfer data to enable two-way synchronization is drsync; see L. BackupPC provides many additional features, such as compressed storage, hardlinking any matching files (rather than just files with the same name), and storing special files without root privileges. But these other programs provide simple, effective and fast solutions and are definitely worthy of consideration. =back =head2 Road map The new features planned for future releases of BackupPC are on the Wiki at L. Comments and suggestions are welcome. =head2 You can help BackupPC is free. I work on BackupPC because I enjoy doing it and I like to contribute to the open source community. BackupPC already has more than enough features for my own needs. The main compensation for continuing to work on BackupPC is knowing that more and more people find it useful. So feedback is certainly appreciated, both positive and negative. Beyond being a satisfied user and telling other people about it, everyone is encouraged to add links to L (I'll see them via Google) or otherwise publicize BackupPC. Unlike the commercial products in this space, I have a zero budget (in both time and money) for marketing, PR and advertising, so it's up to all of you! Feel free to vote for BackupPC at L. Also, everyone is encouraged to contribute patches, bug reports, feature and design suggestions, new code, Wiki additions (you can do those directly) and documentation corrections or improvements. Answering questions on the mailing list is a big help too. =head1 Installing BackupPC =head2 Requirements BackupPC requires: =over 4 =item * A linux, solaris, or unix based server with a substantial amount of free disk space (see the next section for what that means). The CPU and disk performance on this server will determine how many simultaneous backups you can run. You should be able to run 4-8 simultaneous backups on a moderately configured server. Several users have reported significantly better performance using reiserfs compared to ext3 for the BackupPC data file system. It is also recommended you consider either an LVM or RAID setup (either in HW or SW; eg: 3Ware RAID10 or RAID5) so that you can expand the file system as necessary. When BackupPC starts with an empty pool, all the backup data will be written to the pool on disk. After more backups are done, a higher percentage of incoming files will already be in the pool. BackupPC is able to avoid writing to disk new files that are already in the pool. So over time disk writes will reduce significantly (by perhaps a factor of 20 or more), since eventually 95% or more of incoming backup files are typically in the pool. Disk reads from the pool are still needed to do file compares to verify files are an exact match. So, with a mature pool, if a relatively fast client generates data at say 1MB/sec, and you run 4 simultaneous backups, there will be an average server disk load of about 4MB/sec reads and 0.2MB/sec writes (assuming 95% of the incoming files are in the pool). These rates will be perhaps 40% lower if compression is on. =item * Perl version 5.8.0 or later. If you don't have perl, please see L. =item * Perl modules Compress::Zlib, Archive::Zip and File::RsyncP. Try "perldoc Compress::Zlib" and "perldoc Archive::Zip" to see if you have these modules. If not, fetch them from L and see the instructions below for how to build and install them. The CGI Perl module is required for the http/cgi user interface. CGI was a core module, but from version 5.22 Perl no longer ships with it. The File::RsyncP module is available from L or CPAN. You'll need to install the File::RsyncP module if you want to use Rsync as a transport method. =item * If you are using smb to backup WinXX machines you need smbclient and nmblookup from the samba package. You will also need nmblookup if you are backing up linux/unix DHCP machines. See L. Samba versions 3.x are stable and now recommended instead of 2.x. See L for source and binaries. It's pretty easy to fetch and compile samba, and just grab smbclient and nmblookup, without doing the installation. Alternatively, L has binary distributions for most platforms. =item * If you are using tar to backup linux/unix machines, those machines should have version 1.13.7 at a minimum, with version 1.13.20 or higher recommended. Use "tar --version" to check your version. Various GNU mirrors have the newest versions of tar; see L. =item * If you are using rsync to backup linux/unix machines you should have version 2.6.3 or higher on each client machine. See L. Use "rsync --version" to check your version. For BackupPC to use Rsync you will also need to install the perl File::RsyncP module, which is available from L. Version 0.68 or later is required. =item * The Apache web server, see L, preferably built with mod_perl support. =back =head2 What type of storage space do I need? BackupPC uses hardlinks to pool files common to different backups. Therefore BackupPC's data store (__TOPDIR__) must point to a single file system that supports hardlinks. You cannot split this file system with multiple mount points or using symbolic links to point a sub-directory to a different file system (it is ok to use a single symbolic link at the top-level directory (__TOPDIR__) to point the entire data store somewhere else). You can of course use any kind of RAID system or logical volume manager that combines the capacity of multiple disks into a single, larger, file system. Such approaches have the advantage that the file system can be expanded without having to copy it. Any standard linux or unix file system supports hardlinks. NFS mounted file systems work too (provided the underlying file system supports hardlinks). But windows based FAT and NTFS file systems will not work. Starting with BackupPC 3.1.0, run-time checks are done at startup and at the start of each backup to ensure that the file system can support hardlinks, since this is a common area of configuration problems. =head2 How much disk space do I need? Here's one real example for an environment that is backing up 65 laptops with compression off. Each full backup averages 3.2GB. Each incremental backup averages about 0.2GB. Storing one full backup and two incremental backups per laptop is around 240GB of raw data. But because of the pooling of identical files, only 87GB is used. This is without compression. Another example, with compression on: backing up 95 laptops, where each backup averages 3.6GB and each incremental averages about 0.3GB. Keeping three weekly full backups, and six incrementals is around 1200GB of raw data. Because of pooling and compression, only 150GB is needed. Here's a rule of thumb. Add up the disk usage of all the machines you want to backup (210GB in the first example above). This is a rough minimum space estimate that should allow a couple of full backups and at least half a dozen incremental backups per machine. If compression is on you can reduce the storage requirements by maybe 30-40%. Add some margin in case you add more machines or decide to keep more old backups. Your actual mileage will depend upon the types of clients, operating systems and applications you have. The more uniform the clients and applications the bigger the benefit from pooling common files. For example, the Eudora email tool stores each mail folder in a separate file, and attachments are extracted as separate files. So in the sadly common case of a large attachment emailed to many recipients, Eudora will extract the attachment into a new file. When these machines are backed up, only one copy of the file will be stored on the server, even though the file appears in many different full or incremental backups. In this sense Eudora is a "friendly" application from the point of view of backup storage requirements. An example at the other end of the spectrum is Outlook. Everything (email bodies, attachments, calendar, contact lists) is stored in a single file, which often becomes huge. Any change to this file requires a separate copy of the file to be saved during backup. Outlook is even more troublesome, since it keeps this file locked all the time, so it cannot be read by smbclient whenever Outlook is running. See the L section for more discussion of this problem. In addition to total disk space, you should make sure you have plenty of inodes on your BackupPC data partition. Some users have reported running out of inodes on their BackupPC data partition. So even if you have plenty of disk space, BackupPC will report failures when the inodes are exhausted. This is a particular problem with ext2/ext3 file systems that have a fixed number of inodes when the file system is built. Use "df -i" to see your inode usage. =head2 Step 1: Getting BackupPC Some linux distributions now include BackupPC. The Debian distribution, supported by Ludovic Drolez, can be found at L and is included in the current stable Debian release. On Debian, BackupPC can be installed with the command: apt-get install backuppc In the future there might be packages for Gentoo and other linux flavors. If the packaged version is older than the released version then you may want to install the latest version as described below. Otherwise, manually fetching and installing BackupPC is easy. Start by downloading the latest version from L. Hit the "Code" button, then select the "backuppc" or "backuppc-beta" package and download the latest version. =head2 Step 2: Installing the distribution Note: most information in this step is only relevant if you build and install BackupPC yourself. If you use a package provided by a distribution, the package management system should take of installing any needed dependencies. First off, there are five perl modules you should install. These are all optional, but highly recommended: =over 4 =item Compress::Zlib To enable compression, you will need to install Compress::Zlib from L. You can run "perldoc Compress::Zlib" to see if this module is installed. =item Archive::Zip To support restore via Zip archives you will need to install Archive::Zip, also from L. You can run "perldoc Archive::Zip" to see if this module is installed. =item XML::RSS To support the RSS feature you will need to install XML::RSS, also from L. There is not need to install this module if you don't plan on using RSS. You can run "perldoc XML::RSS" to see if this module is installed. =item File::RsyncP To use rsync and rsyncd with BackupPC you will need to install File::RsyncP. You can run "perldoc File::RsyncP" to see if this module is installed. File::RsyncP is available from L. Version 0.68 or later is required. =item File::Listing, Net::FTP, Net::FTP::RetrHandle, Net::FTP::AutoReconnect To use ftp with BackupPC you will need four libraries, but actually need to install only File::Listing from L. You can run "perldoc File::Listing" to see if this module is installed. Net::FTP is a standard module. Net::FTP::RetrHandle and Net::FTP::AutoReconnect included in BackupPC distribution. =back To build and install these packages you should use the cpan program. Alternatively, you can fetch the tar.gz file from L and then run these commands: tar zxvf Archive-Zip-1.26.tar.gz cd Archive-Zip-1.26 perl Makefile.PL make make test make install The same sequence of commands can be used for each module. Now let's move onto BackupPC itself. After fetching BackupPC-3.3.2.tar.gz, run these commands as root: tar zxf BackupPC-3.3.2.tar.gz cd BackupPC-3.3.2 perl configure.pl In the future this release might also have patches available on the SourceForge site. These patch files are text files, with a name of the form BackupPC-3.3.2plN.diff where N is the patch level, eg: pl2 is patch-level 2. These patch files are cumulative: you only need apply the last patch file, not all the earlier patch files. If a patch file is available, eg: BackupPC-3.3.2pl2.diff, you should apply the patch after extracting the tar file: # fetch BackupPC-3.3.2.tar.gz # fetch BackupPC-3.3.2pl2.diff tar zxf BackupPC-3.3.2.tar.gz cd BackupPC-3.3.2 patch -p0 < ../BackupPC-3.3.2pl2.diff perl configure.pl A patch file includes comments that describe that bug fixes and changes. Feel free to review it before you apply the patch. The configure.pl script also accepts command-line options if you wish to run it in a non-interactive manner. It has self-contained documentation for all the command-line options, which you can read with perldoc: perldoc configure.pl Starting with BackupPC 3.0.0, the configure.pl script by default complies with the file system hierarchy (FHS) conventions. The major difference compared to earlier versions is that by default configuration files will be stored in /etc/BackupPC rather than below the data directory, __TOPDIR__/conf, and the log files will be stored in /var/log/BackupPC rather than below the data directory, __TOPDIR__/log. Note that distributions may choose to use different locations for BackupPC files than these defaults. If you are upgrading from an earlier version the configure.pl script will keep the configuration files and log files in their original location. When you run configure.pl you will be prompted for the full paths of various executables, and you will be prompted for the following information. =over 4 =item BackupPC User It is best if BackupPC runs as a special user, eg backuppc, that has limited privileges. It is preferred that backuppc belongs to a system administrator group so that sys admin members can browse BackupPC files, edit the configuration files and so on. Although configurable, the default settings leave group read permission on pool files, so make sure the BackupPC user's group is chosen restrictively. On this installation, this is __BACKUPPCUSER__. For security purposes you might choose to configure the BackupPC user with the shell set to /bin/false. Since you might need to run some BackupPC programs as the BackupPC user for testing purposes, you can use the -s option to su to explicitly run a shell, eg: su -s /bin/bash __BACKUPPCUSER__ Depending upon your configuration you might also need the -l option. =item Data Directory You need to decide where to put the data directory, below which all the BackupPC data is stored. This needs to be a big file system. On this installation, this is __TOPDIR__. =item Install Directory You should decide where the BackupPC scripts, libraries and documentation should be installed, eg: /usr/local/BackupPC. On this installation, this is __INSTALLDIR__. =item CGI bin Directory You should decide where the BackupPC CGI script resides. This will usually be below Apache's cgi-bin directory. It is also possible to use a different directory and use Apache's ``'' directive to specifiy that location. See the Apache HTTP Server documentation for additional information. On this installation, this is __CGIDIR__. =item Apache image Directory A directory where BackupPC's images are stored so that Apache can serve them. You should ensure this directory is readable by Apache and create a symlink to this directory from the BackupPC CGI bin Directory. =item Config and Log Directories In this installation the configuration and log directories are located in the following locations: __CONFDIR__/config.pl main config file __CONFDIR__/hosts hosts file __CONFDIR__/pc/HOST.pl per-pc config file __LOGDIR__/BackupPC log files, pid, status The configure.pl script doesn't prompt for these locations but they can be set for new installations using command-line options. =back =head2 Step 3: Setting up config.pl After running configure.pl, browse through the config file, __CONFDIR__/config.pl, and make sure all the default settings are correct. In particular, you will need to decide whether to use smb, tar,or rsync or ftp transport (or whether to set it on a per-PC basis) and set the relevant parameters for that transport method. See the section L for more details. =head2 Step 4: Setting up the hosts file The file __CONFDIR__/hosts contains the list of clients to backup. BackupPC reads this file in three cases: =over 4 =item * Upon startup. =item * When BackupPC is sent a HUP (-1) signal. Assuming you installed the init.d script, you can also do this with "/etc/init.d/backuppc reload". =item * When the modification time of the hosts file changes. BackupPC checks the modification time once during each regular wakeup. =back Whenever you change the hosts file (to add or remove a host) you can either do a kill -HUP BackupPC_pid or simply wait until the next regular wakeup period. Each line in the hosts file contains three fields, separated by white space: =over 4 =item Host name This is typically the host name or NetBios name of the client machine and should be in lower case. The host name can contain spaces (escape with a backslash), but it is not recommended. Please read the section L. In certain cases you might want several distinct clients to refer to the same physical machine. For example, you might have a database you want to backup, and you want to bracket the backup of the database with shutdown/restart using $Conf{DumpPreUserCmd} and $Conf{DumpPostUserCmd}. But you also want to backup the rest of the machine while the database is still running. In the case you can specify two different clients in the host file, using any mnemonic name (eg: myhost_mysql and myhost), and use $Conf{ClientNameAlias} in myhost_mysql's config.pl to specify the real host name of the machine. =item DHCP flag Starting with v2.0.0 the way hosts are discovered has changed and now in most cases you should specify 0 for the DHCP flag, even if the host has a dynamically assigned IP address. Please read the section L to understand whether you need to set the DHCP flag. You only need to set DHCP to 1 if your client machine doesn't respond to the NetBios multicast request: nmblookup myHost but does respond to a request directed to its IP address: nmblookup -A W.X.Y.Z If you do set DHCP to 1 on any client you will need to specify the range of DHCP addresses to search is specified in $Conf{DHCPAddressRanges}. Note also that the $Conf{ClientNameAlias} feature does not work for clients with DHCP set to 1. =item User name This should be the unix login/email name of the user who "owns" or uses this machine. This is the user who will be sent email about this machine, and this user will have permission to stop/start/browse/restore backups for this host. Leave this blank if no specific person should receive email or be allowed to stop/start/browse/restore backups for this host. Administrators will still have full permissions. =item More users Additional user names, separate by commas and with no white space, can be specified. These users will also have full permission in the CGI interface to stop/start/browse/restore backups for this host. These users will not be sent email about this host. =back The first non-comment line of the hosts file is special: it contains the names of the columns and should not be edited. Here's a simple example of a hosts file: host dhcp user moreUsers farside 0 craig jim,dave larson 1 gary andy =head2 Step 5: Client Setup Four methods for getting backup data from a client are supported: smb, tar, rsync and ftp. Smb or rsync are the preferred methods for WinXX clients and rsync or tar are the preferred methods for linux/unix/MacOSX clients. The transfer method is set using the $Conf{XferMethod} configuration setting. If you have a mixed environment (ie: you will use smb for some clients and tar for others), you will need to pick the most common choice for $Conf{XferMethod} for the main config.pl file, and then override it in the per-PC config file for those hosts that will use the other method. (Or you could run two completely separate instances of BackupPC, with different data directories, one for WinXX and the other for linux/unix, but then common files between the different machine types will duplicated.) Here are some brief client setup notes: =over 4 =item WinXX One setup for WinXX clients is to set $Conf{XferMethod} to "smb". Actually, rsyncd is the better method for WinXX if you are prepared to run rsync/cygwin on your WinXX client. If you want to use rsyncd for WinXX clients you can find a pre-packaged zip file on L. The package is called cygwin-rsync. It contains rsync.exe, template setup files and the minimal set of cygwin libraries for everything to run. The README file contains instructions for running rsync as a service, so it starts automatically everytime you boot your machine. If you use rsync to backup WinXX machines, be sure to set $Conf{ClientCharset} correctly (eg: 'cp1252') so that the WinXX file name encoding is correctly converted to utf8. Otherwise, to use SMB, you can either create shares for the data you want to backup or your can use the existing C$ share. To create a new share, open "My Computer", right click on the drive (eg: C), and select "Sharing..." (or select "Properties" and select the "Sharing" tab). In this dialog box you can enable sharing, select the share name and permissions. All Windows NT based OS (NT, 2000, XP Pro), are configured by default to share the entire C drive as C$. This is a special share used for various administration functions, one of which is to grant access to backup operators. All you need to do is create a new domain user, specifically for backup. Then add the new backup user to the built in "Backup Operators" group. You now have backup capability for any directory on any computer in the domain in one easy step. This avoids using administrator accounts and only grants permission to do exactly what you want for the given user, i.e.: backup. Also, for additional security, you may wish to deny the ability for this user to logon to computers in the default domain policy. If this machine uses DHCP you will also need to make sure the NetBios name is set. Go to Control Panel|System|Network Identification (on Win2K) or Control Panel|System|Computer Name (on WinXP). Also, you should go to Control Panel|Network Connections|Local Area Connection|Properties|Internet Protocol (TCP/IP)|Properties|Advanced|WINS and verify that NetBios is not disabled. The relevant configuration settings are $Conf{SmbShareName}, $Conf{SmbShareUserName}, $Conf{SmbSharePasswd}, $Conf{SmbClientPath}, $Conf{SmbClientFullCmd}, $Conf{SmbClientIncrCmd} and $Conf{SmbClientRestoreCmd}. BackupPC needs to know the smb share user name and password for a client machine that uses smb. The user name is specified in $Conf{SmbShareUserName}. There are four ways to tell BackupPC the smb share password: =over 4 =item * As an environment variable BPC_SMB_PASSWD set before BackupPC starts. If you start BackupPC manually the BPC_SMB_PASSWD variable must be set manually first. For backward compatibility for v1.5.0 and prior, the environment variable PASSWD can be used if BPC_SMB_PASSWD is not set. Warning: on some systems it is possible to see environment variables of running processes. =item * Alternatively the BPC_SMB_PASSWD setting can be included in /etc/init.d/backuppc, in which case you must make sure this file is not world (other) readable. =item * As a configuration variable $Conf{SmbSharePasswd} in __CONFDIR__/config.pl. If you put the password here you must make sure this file is not world (other) readable. =item * As a configuration variable $Conf{SmbSharePasswd} in the per-PC configuration file (__CONFDIR__/pc/$host.pl or __TOPDIR__/pc/$host/config.pl in non-FHS versions of BackupPC). You will have to use this option if the smb share password is different for each host. If you put the password here you must make sure this file is not world (other) readable. =back Placement and protection of the smb share password is a possible security risk, so please double-check the file and directory permissions. In a future version there might be support for encryption of this password, but a private key will still have to be stored in a protected place. Suggestions are welcome. As an alternative to setting $Conf{XferMethod} to "smb" (using smbclient) for WinXX clients, you can use an smb network filesystem (eg: ksmbfs or similar) on your linux/unix server to mount the share, and then set $Conf{XferMethod} to "tar" (use tar on the network mounted file system). Also, to make sure that file names with special characters are correctly transferred by smbclient you should make sure that the smb.conf file has (for samba 3.x): [global] unix charset = UTF8 UTF8 is the default setting, so if the parameter is missing then it is ok. With this setting $Conf{ClientCharset} should be emtpy, since smbclient has already converted the file names to utf8. =item Linux/Unix The preferred setup for linux/unix clients is to set $Conf{XferMethod} to "rsync", "rsyncd" or "tar". You can use either rsync, smb, or tar for linux/unix machines. Smb requires that the Samba server (smbd) be run to provide the shares. Since the smb protocol can't represent special files like symbolic links and fifos, tar and rsync are the better transport methods for linux/unix machines. (In fact, by default samba makes symbolic links look like the file or directory that they point to, so you could get an infinite loop if a symbolic link points to the current or parent directory. If you really need to use Samba shares for linux/unix backups you should turn off the "follow symlinks" samba config setting. See the smb.conf manual page.) The requirements for each Xfer Method are: =over 4 =item tar You must have GNU tar on the client machine. Use "tar --version" or "gtar --version" to verify. The version should be at least 1.13.7, and 1.13.20 or greater is recommended. Tar is run on the client machine via rsh or ssh. The relevant configuration settings are $Conf{TarClientPath}, $Conf{TarShareName}, $Conf{TarClientCmd}, $Conf{TarFullArgs}, $Conf{TarIncrArgs}, and $Conf{TarClientRestoreCmd}. =item rsync You should have at least rsync 2.6.3, and the latest version is recommended. Rsync is run on the remote client via rsh or ssh. The relevant configuration settings are $Conf{RsyncClientPath}, $Conf{RsyncClientCmd}, $Conf{RsyncClientRestoreCmd}, $Conf{RsyncShareName}, $Conf{RsyncArgs}, and $Conf{RsyncRestoreArgs}. =item rsyncd You should have at least rsync 2.6.3, and the latest version is recommended. In this case the rsync daemon should be running on the client machine and BackupPC connects directly to it. The relevant configuration settings are $Conf{RsyncdClientPort}, $Conf{RsyncdUserName}, $Conf{RsyncdPasswd}, $Conf{RsyncdAuthRequired}, $Conf{RsyncShareName}, $Conf{RsyncArgs}, $Conf{RsyncArgsExtra}, and $Conf{RsyncRestoreArgs}. $Conf{RsyncShareName} is the name of an rsync module (ie: the thing in square brackets in rsyncd's conf file -- see rsyncd.conf), not a file system path. Be aware that rsyncd will remove the leading '/' from path names in symbolic links if you specify "use chroot = no" in the rsynd.conf file. See the rsyncd.conf manual page for more information. =item ftp You need to be running an ftp server on the client machine. The relevant configuration settings are $Conf{FtpShareName}, $Conf{FtpUserName}, $Conf{FtpPasswd}, $Conf{FtpBlockSize}, $Conf{FtpPort}, $Conf{FtpTimeout}, and $Conf{FtpFollowSymlinks}. =back You need to set $Conf{ClientCharset} to the client's charset so that file names are correctly converted to utf8. Use "locale charmap" on the client to see its charset. For linux/unix machines you should not backup "/proc". This directory contains a variety of files that look like regular files but they are special files that don't need to be backed up (eg: /proc/kcore is a regular file that contains physical memory). See $Conf{BackupFilesExclude}. It is safe to back up /dev since it contains mostly character-special and block-special files, which are correctly handed by BackupPC (eg: backing up /dev/hda5 just saves the block-special file information, not the contents of the disk). Alternatively, rather than backup all the file systems as a single share ("/"), it is easier to restore a single file system if you backup each file system separately. To do this you should list each file system mount point in $Conf{TarShareName} or $Conf{RsyncShareName}, and add the --one-file-system option to $Conf{TarClientCmd} or $Conf{RsyncArgs}. In this case there is no need to exclude /proc explicitly since it looks like a different file system. Next you should decide whether to run tar over ssh, rsh or nfs. Ssh is the preferred method. Rsh is not secure and therefore not recommended. Nfs will work, but you need to make sure that the BackupPC user (running on the server) has sufficient permissions to read all the files below the nfs mount. Ssh allows BackupPC to run as a privileged user on the client (eg: root), since it needs sufficient permissions to read all the backup files. Ssh is setup so that BackupPC on the server (an otherwise low privileged user) can ssh as root on the client, without being prompted for a password. There are two common versions of ssh: v1 and v2. Here are some instructions for one way to setup ssh. (Check which version of SSH you have by typing "ssh" or "man ssh".) =item MacOSX In general this should be similar to Linux/Unix machines. In versions 10.4 and later, the native MacOSX tar works, and also supports resource forks. xtar is another option, and rsync works too (although the MacOSX-supplied rsync has an extension for extended attributes that is not compatible with standard rsync). =item SSH Setup SSH is a secure way to run tar or rsync on a backup client to extract the data. SSH provides strong authentication and encryption of the network data. Note that if you run rsyncd (rsync daemon), ssh is not used. In this case, rsyncd provides its own authentication, but there is no encryption of network data. If you want encryption of network data you can use ssh to create a tunnel, or use a program like stunnel. Setup instructions for ssh can be found at L or on the Wiki. =item Clients that use DHCP If a client machine uses DHCP BackupPC needs some way to find the IP address given the host name. One alternative is to set dhcp to 1 in the hosts file, and BackupPC will search a pool of IP addresses looking for hosts. More efficiently, it is better to set dhcp = 0 and provide a mechanism for BackupPC to find the IP address given the host name. For WinXX machines BackupPC uses the NetBios name server to determine the IP address given the host name. For unix machines you can run nmbd (the NetBios name server) from the Samba distribution so that the machine responds to a NetBios name request. See the manual page and Samba documentation for more information. Alternatively, you can set $Conf{NmbLookupFindHostCmd} to any command that returns the IP address given the host name. Please read the section L for more details. =back =head2 Step 6: Running BackupPC The installation contains an init.d backuppc script that can be copied to /etc/init.d so that BackupPC can auto-start on boot. See init.d/README for further instructions. BackupPC should be ready to start. If you installed the init.d script, then you should be able to run BackupPC with: /etc/init.d/backuppc start (This script can also be invoked with "stop" to stop BackupPC and "reload" to tell BackupPC to reload config.pl and the hosts file.) Otherwise, just run __INSTALLDIR__/bin/BackupPC -d as user __BACKUPPCUSER__. The -d option tells BackupPC to run as a daemon (ie: it does an additional fork). Any immediate errors will be printed to stderr and BackupPC will quit. Otherwise, look in __LOGDIR__/LOG and verify that BackupPC reports it has started and all is ok. =head2 Step 7: Talking to BackupPC You should verify that BackupPC is running by using BackupPC_serverMesg. This sends a message to BackupPC via the unix (or TCP) socket and prints the response. Like all BackupPC programs, BackupPC_serverMesg should be run as the BackupPC user (__BACKUPPCUSER__), so you should su __BACKUPPCUSER__ before running BackupPC_serverMesg. If the BackupPC user is configured with /bin/false as the shell, you can use the -s option to su to explicitly run a shell, eg: su -s /bin/bash __BACKUPPCUSER__ Depending upon your configuration you might also need the -l option. You can request status information and start and stop backups using this interface. This socket interface is mainly provided for the CGI interface (and some of the BackupPC sub-programs use it too). But right now we just want to make sure BackupPC is happy. Each of these commands should produce some status output: __INSTALLDIR__/bin/BackupPC_serverMesg status info __INSTALLDIR__/bin/BackupPC_serverMesg status jobs __INSTALLDIR__/bin/BackupPC_serverMesg status hosts The output should be some hashes printed with Data::Dumper. If it looks cryptic and confusing, and doesn't look like an error message, then all is ok. The jobs status should initially show just BackupPC_trashClean. The hosts status should produce a list of every host you have listed in __CONFDIR__/hosts as part of a big cryptic output line. You can also request that all hosts be queued: __INSTALLDIR__/bin/BackupPC_serverMesg backup all At this point you should make sure the CGI interface works since it will be much easier to see what is going on. That's our next subject. =head2 Step 8: Checking email delivery The script BackupPC_sendEmail sends status and error emails to the administrator and users. It is usually run each night by BackupPC_nightly. To verify that it can run sendmail and deliver email correctly you should ask it to send a test email to you: su __BACKUPPCUSER__ __INSTALLDIR__/bin/BackupPC_sendEmail -u MYNAME@MYDOMAIN.COM BackupPC_sendEmail also takes a -c option that checks if BackupPC is running, and it sends an email to $Conf{EMailAdminUserName} if it is not. That can be used as a keep-alive check by adding __INSTALLDIR__/bin/BackupPC_sendEmail -c to __BACKUPPCUSER__'s cron. The -t option to BackupPC_sendEmail causes it to print the email message instead of invoking sendmail to deliver the message. =head2 Step 9: CGI interface The CGI interface script, BackupPC_Admin, is a powerful and flexible way to see and control what BackupPC is doing. It is written for an Apache server. If you don't have Apache, see L. There are two options for setting up the CGI interface: standard mode and using mod_perl. Mod_perl provides much higher performance (around 15x) and is the best choice if your Apache was built with mod_perl support. To see if your apache was built with mod_perl run this command: httpd -l | egrep mod_perl If this prints mod_perl.c then your Apache supports mod_perl. Note: on some distributions (like Debian) the command is not ``httpd'', but ``apache'' or ``apache2''. Those distributions will generally also use ``apache'' for the Apache user account and configuration files. Using mod_perl with BackupPC_Admin requires a dedicated Apache to be run as the BackupPC user (__BACKUPPCUSER__). This is because BackupPC_Admin needs permission to access various files in BackupPC's data directories. In contrast, the standard installation (without mod_perl) solves this problem by having BackupPC_Admin installed as setuid to the BackupPC user, so that BackupPC_Admin runs as the BackupPC user. Here are some specifics for each setup: =over 4 =item Standard Setup The CGI interface should have been installed by the configure.pl script in __CGIDIR__/BackupPC_Admin. BackupPC_Admin should have been installed as setuid to the BackupPC user (__BACKUPPCUSER__), in addition to user and group execute permission. You should be very careful about permissions on BackupPC_Admin and the directory __CGIDIR__: it is important that normal users cannot directly execute or change BackupPC_Admin, otherwise they can access backup files for any PC. You might need to change the group ownership of BackupPC_Admin to a group that Apache belongs to so that Apache can execute it (don't add "other" execute permission!). The permissions should look like this: ls -l __CGIDIR__/BackupPC_Admin -swxr-x--- 1 __BACKUPPCUSER__ web 82406 Jun 17 22:58 __CGIDIR__/BackupPC_Admin The setuid script won't work unless perl on your machine was installed with setuid emulation. This is likely the problem if you get an error saying such as "Wrong user: my userid is 25, instead of 150", meaning the script is running as the httpd user, not the BackupPC user. This is because setuid scripts are disabled by the kernel in most flavors of unix and linux. To see if your perl has setuid emulation, see if there is a program called sperl5.8.0 (or sperl5.8.2 etc, based on your perl version) in the place where perl is installed. If you can't find this program, then you have two options: rebuild and reinstall perl with the setuid emulation turned on (answer "y" to the question "Do you want to do setuid/setgid emulation?" when you run perl's configure script), or switch to the mod_perl alternative for the CGI script (which doesn't need setuid to work). =item Mod_perl Setup The advantage of the mod_perl setup is that no setuid script is needed, and there is a huge performance advantage. Not only does all the perl code need to be parsed just once, the config.pl and hosts files, plus the connection to the BackupPC server are cached between requests. The typical speedup is around 15 times. To use mod_perl you need to run Apache as user __BACKUPPCUSER__. If you need to run multiple Apache's for different services then you need to create multiple top-level Apache directories, each with their own config file. You can make copies of /etc/init.d/httpd and use the -d option to httpd to point each http to a different top-level directory. Or you can use the -f option to explicitly point to the config file. Multiple Apache's will run on different Ports (eg: 80 is standard, 8080 is a typical alternative port accessed via http://yourhost.com:8080). Inside BackupPC's Apache http.conf file you should check the settings for ServerRoot, DocumentRoot, User, Group, and Port. See L for more details. For mod_perl, BackupPC_Admin should not have setuid permission, so you should turn it off: chmod u-s __CGIDIR__/BackupPC_Admin To tell Apache to use mod_perl to execute BackupPC_Admin, add this to Apache's 1.x httpd.conf file: PerlModule Apache::Registry PerlTaintCheck On # <--- change path as needed SetHandler perl-script PerlHandler Apache::Registry Options ExecCGI PerlSendHeader On Apache 2.0.44 with Perl 5.8.0 on RedHat 7.1, Don Silvia reports that this works (with tweaks from Michael Tuzi): LoadModule perl_module modules/mod_perl.so PerlModule Apache2 SetHandler perl-script PerlResponseHandler ModPerl::Registry PerlOptions +ParseHeaders Options +ExecCGI Order deny,allow Deny from all Allow from 192.168.0 AuthName "Backup Admin" AuthType Basic AuthUserFile /path/to/user_file Require valid-user There are other optimizations and options with mod_perl. For example, you can tell mod_perl to preload various perl modules, which saves memory compared to loading separate copies in every Apache process after they are forked. See Stas's definitive mod_perl guide at L. =back BackupPC_Admin requires that users are authenticated by Apache. Specifically, it expects that Apache sets the REMOTE_USER environment variable when it runs. There are several ways to do this. One way is to create a .htaccess file in the cgi-bin directory that looks like: AuthGroupFile /etc/httpd/conf/group # <--- change path as needed AuthUserFile /etc/http/conf/passwd # <--- change path as needed AuthType basic AuthName "access" require valid-user You will also need "AllowOverride Indexes AuthConfig" in the Apache httpd.conf file to enable the .htaccess file. Alternatively, everything can go in the Apache httpd.conf file inside a Location directive. The list of users and password file above can be extracted from the NIS passwd file. One alternative is to use LDAP. In Apache's http.conf add these lines: LoadModule auth_ldap_module modules/auth_ldap.so AddModule auth_ldap.c # cgi-bin - auth via LDAP (for BackupPC) # <--- change path as needed AuthType Basic AuthName "BackupPC login" # replace MYDOMAIN, PORT, ORG and CO as needed AuthLDAPURL ldap://ldap.MYDOMAIN.com:PORT/o=ORG,c=CO?uid?sub?(objectClass=*) require valid-user If you want to disable the user authentication you can set $Conf{CgiAdminUsers} to '*', which allows any user to have full access to all hosts and backups. In this case the REMOTE_USER environment variable does not have to be set by Apache. Alternatively, you can force a particular user name by getting Apache to set REMOTE_USER, eg, to hardcode the user to www you could add this to Apache's httpd.conf: # <--- change path as needed Setenv REMOTE_USER www Finally, you should also edit the config.pl file and adjust, as necessary, the CGI-specific settings. They're near the end of the config file. In particular, you should specify which users or groups have administrator (privileged) access: see the config settings $Conf{CgiAdminUserGroup} and $Conf{CgiAdminUsers}. Also, the configure.pl script placed various images into $Conf{CgiImageDir} that BackupPC_Admin needs to serve up. You should make sure that $Conf{CgiImageDirURL} is the correct URL for the image directory. See the section L for suggestions on debugging the Apache authentication setup. =head2 How BackupPC Finds Hosts Starting with v2.0.0 the way hosts are discovered has changed. In most cases you should specify 0 for the DHCP flag in the conf/hosts file, even if the host has a dynamically assigned IP address. BackupPC (starting with v2.0.0) looks up hosts with DHCP = 0 in this manner: =over 4 =item * First DNS is used to lookup the IP address given the client's name using perl's gethostbyname() function. This should succeed for machines that have fixed IP addresses that are known via DNS. You can manually see whether a given host have a DNS entry according to perl's gethostbyname function with this command: perl -e 'print(gethostbyname("myhost") ? "ok\n" : "not found\n");' =item * If gethostbyname() fails, BackupPC then attempts a NetBios multicast to find the host. Provided your client machine is configured properly, it should respond to this NetBios multicast request. Specifically, BackupPC runs a command of this form: nmblookup myhost If this fails you will see output like: querying myhost on 10.10.255.255 name_query failed to find name myhost If it is successful you will see output like: querying myhost on 10.10.255.255 10.10.1.73 myhost<00> Depending on your netmask you might need to specify the -B option to nmblookup. For example: nmblookup -B 10.10.1.255 myhost If necessary, experiment with the nmblookup command which will return the IP address of the client given its name. Then update $Conf{NmbLookupFindHostCmd} with any necessary options to nmblookup. =back For hosts that have the DHCP flag set to 1, these machines are discovered as follows: =over 4 =item * A DHCP address pool ($Conf{DHCPAddressRanges}) needs to be specified. BackupPC will check the NetBIOS name of each machine in the range using a command of the form: nmblookup -A W.X.Y.Z where W.X.Y.Z is each candidate address from $Conf{DHCPAddressRanges}. Any host that has a valid NetBIOS name returned by this command (ie: matching an entry in the hosts file) will be backed up. You can modify the specific nmblookup command if necessary via $Conf{NmbLookupCmd}. =item * You only need to use this DHCP feature if your client machine doesn't respond to the NetBios multicast request: nmblookup myHost but does respond to a request directed to its IP address: nmblookup -A W.X.Y.Z =back =head2 Other installation topics =over 4 =item Removing a client If there is a machine that no longer needs to be backed up (eg: a retired machine) you have two choices. First, you can keep the backups accessible and browsable, but disable all new backups. Alternatively, you can completely remove the client and all its backups. To disable backups for a client $Conf{BackupsDisable} can be set to two different values in that client's per-PC config.pl file: =over 4 =item 1 Don't do any regular backups on this machine. Manually requested backups (via the CGI interface) will still occur. =item 2 Don't do any backups on this machine. Manually requested backups (via the CGI interface) will be ignored. =back This will still allow the client's old backups to be browsable and restorable. To completely remove a client and all its backups, you should remove its entry in the conf/hosts file, and then delete the __TOPDIR__/pc/$host directory. Whenever you change the hosts file, you should send BackupPC a HUP (-1) signal so that it re-reads the hosts file. If you don't do this, BackupPC will automatically re-read the hosts file at the next regular wakeup. Note that when you remove a client's backups you won't initially recover much disk space. That's because the client's files are still in the pool. Overnight, when BackupPC_nightly next runs, all the unused pool files will be deleted and this will recover the disk space used by the client's backups. =item Copying the pool If the pool disk requirements grow you might need to copy the entire data directory to a new (bigger) file system. Hopefully you are lucky enough to avoid this by having the data directory on a RAID file system or LVM that allows the capacity to be grown in place by adding disks. The backup data directories contain large numbers of hardlinks. If you try to copy the pool the target directory will occupy a lot more space if the hardlinks aren't re-established. The best way to copy a pool file system, if possible, is by copying the raw device at the block level (eg: using dd). Application level programs that understand hardlinks include the GNU cp program with the -a option and rsync -H. However, the large number of hardlinks in the pool will make the memory usage large and the copy very slow. Don't forget to stop BackupPC while the copy runs. Starting in 3.0.0 a new script bin/BackupPC_tarPCCopy can be used to assist the copy process. Given one or more pc paths (eg: TOPDIR/pc/HOST or TOPDIR/pc/HOST/nnn), BackupPC_tarPCCopy creates a tar archive with all the hardlinks pointing to ../cpool/.... Any files not hardlinked (eg: backups, LOG etc) are included verbatim. You will need to specify the -P option to tar when you extract the archive generated by BackupPC_tarPCCopy since the hardlink targets are outside of the directory being extracted. To copy a complete store (ie: __TOPDIR__) using BackupPC_tarPCCopy you should: =over 4 =item * stop BackupPC so that the store is static. =item * copy the cpool, conf and log directory trees using any technique (like cp, rsync or tar) without the need to preserve hardlinks. =item * copy the pc directory using BackupPC_tarPCCopy: su __BACKUPPCUSER__ cd NEW_TOPDIR mkdir pc cd pc __INSTALLDIR__/bin/BackupPC_tarPCCopy __TOPDIR__/pc | tar xvPf - =back =back =head2 Fixing installation problems Please see the Wiki at L for debugging suggestions. If you find a solution to your problem that could help other users please add it to the Wiki! =head1 Restore functions BackupPC supports several different methods for restoring files. The most convenient restore options are provided via the CGI interface. Alternatively, backup files can be restored using manual commands. =head2 CGI restore options By selecting a host in the CGI interface, a list of all the backups for that machine will be displayed. By selecting the backup number you can navigate the shares and directory tree for that backup. BackupPC's CGI interface automatically fills incremental backups with the corresponding full backup, which means each backup has a filled appearance. Therefore, there is no need to do multiple restores from the incremental and full backups: BackupPC does all the hard work for you. You simply select the files and directories you want from the correct backup vintage in one step. You can download a single backup file at any time simply by selecting it. Your browser should prompt you with the file name and ask you whether to open the file or save it to disk. Alternatively, you can select one or more files or directories in the currently selected directory and select "Restore selected files". (If you need to restore selected files and directories from several different parent directories you will need to do that in multiple steps.) If you select all the files in a directory, BackupPC will replace the list of files with the parent directory. You will be presented with a screen that has three options: =over 4 =item Option 1: Direct Restore With this option the selected files and directories are restored directly back onto the host, by default in their original location. Any old files with the same name will be overwritten, so use caution. You can optionally change the target host name, target share name, and target path prefix for the restore, allowing you to restore the files to a different location. Once you select "Start Restore" you will be prompted one last time with a summary of the exact source and target files and directories before you commit. When you give the final go ahead the restore operation will be queued like a normal backup job, meaning that it will be deferred if there is a backup currently running for that host. When the restore job is run, smbclient, tar, rsync or rsyncd is used (depending upon $Conf{XferMethod}) to actually restore the files. Sorry, there is currently no option to cancel a restore that has been started. Currently ftp restores are not fully implemented. A record of the restore request, including the result and list of files and directories, is kept. It can be browsed from the host's home page. $Conf{RestoreInfoKeepCnt} specifies how many old restore status files to keep. Note that for direct restore to work, the $Conf{XferMethod} must be able to write to the client. For example, that means an SMB share for smbclient needs to be writable, and the rsyncd module needs "read only" set to "false". This creates additional security risks. If you only create read-only SMB shares (which is a good idea), then the direct restore will fail. You can disable the direct restore option by setting $Conf{SmbClientRestoreCmd}, $Conf{TarClientRestoreCmd} and $Conf{RsyncRestoreArgs} to undef. =item Option 2: Download Zip archive With this option a zip file containing the selected files and directories is downloaded. The zip file can then be unpacked or individual files extracted as necessary on the host machine. The compression level can be specified. A value of 0 turns off compression. When you select "Download Zip File" you should be prompted where to save the restore.zip file. BackupPC does not consider downloading a zip file as an actual restore operation, so the details are not saved for later browsing as in the first case. However, a mention that a zip file was downloaded by a particular user, and a list of the files, does appear in BackupPC's log file. =item Option 3: Download Tar archive This is identical to the previous option, except a tar file is downloaded rather than a zip file (and there is currently no compression option). =back =head2 Command-line restore options Apart from the CGI interface, BackupPC allows you to restore files and directories from the command line. The following programs can be used: =over 4 =item BackupPC_zcat For each file name argument it inflates (uncompresses) the file and writes it to stdout. To use BackupPC_zcat you could give it the full file name, eg: __INSTALLDIR__/bin/BackupPC_zcat __TOPDIR__/pc/host/5/fc/fcraig/fexample.txt > example.txt It's your responsibility to make sure the file is really compressed: BackupPC_zcat doesn't check which backup the requested file is from. BackupPC_zcat returns a non-zero status if it fails to uncompress a file. =item BackupPC_tarCreate BackupPC_tarCreate creates a tar file for any files or directories in a particular backup. Merging of incrementals is done automatically, so you don't need to worry about whether certain files appear in the incremental or full backup. The usage is: BackupPC_tarCreate [options] files/directories... Required options: -h host host from which the tar archive is created -n dumpNum dump number from which the tar archive is created A negative number means relative to the end (eg -1 means the most recent dump, -2 2nd most recent etc). -s shareName share name from which the tar archive is created Other options: -t print summary totals -r pathRemove path prefix that will be replaced with pathAdd -p pathAdd new path prefix -b BLOCKS BLOCKS x 512 bytes per record (default 20; same as tar) -w writeBufSz write buffer size (default 1048576 = 1MB) -e charset charset for encoding file names (default: value of $Conf{ClientCharset} when backup was done) -l just print a file listing; don't generate an archive -L just print a detailed file listing; don't generate an archive The command-line files and directories are relative to the specified shareName. The tar file is written to stdout. The -h, -n and -s options specify which dump is used to generate the tar archive. The -r and -p options can be used to relocate the paths in the tar archive so extracted files can be placed in a location different from their original location. =item BackupPC_zipCreate BackupPC_zipCreate creates a zip file for any files or directories in a particular backup. Merging of incrementals is done automatically, so you don't need to worry about whether certain files appear in the incremental or full backup. The usage is: BackupPC_zipCreate [options] files/directories... Required options: -h host host from which the zip archive is created -n dumpNum dump number from which the tar archive is created A negative number means relative to the end (eg -1 means the most recent dump, -2 2nd most recent etc). -s shareName share name from which the zip archive is created Other options: -t print summary totals -r pathRemove path prefix that will be replaced with pathAdd -p pathAdd new path prefix -c level compression level (default is 0, no compression) -e charset charset for encoding file names (default: utf8) The command-line files and directories are relative to the specified shareName. The zip file is written to stdout. The -h, -n and -s options specify which dump is used to generate the zip archive. The -r and -p options can be used to relocate the paths in the zip archive so extracted files can be placed in a location different from their original location. =back Each of these programs reside in __INSTALLDIR__/bin. =head1 Archive functions BackupPC supports archiving to removable media. For users that require offsite backups, BackupPC can create archives that stream to tape devices, or create files of specified sizes to fit onto cd or dvd media. Each archive type is specified by a BackupPC host with its XferMethod set to 'archive'. This allows for multiple configurations at sites where there might be a combination of tape and cd/dvd backups being made. BackupPC provides a menu that allows one or more hosts to be archived. The most recent backup of each host is archived using BackupPC_tarCreate, and the output is optionally compressed and split into fixed-sized files (eg: 650MB). The archive for each host is done by default using __INSTALLDIR__/bin/BackupPC_archiveHost. This script can be copied and customized as needed. =head2 Configuring an Archive Host To create an Archive Host, add it to the hosts file just as any other host and call it a name that best describes the type of archive, e.g. ArchiveDLT To tell BackupPC that the Host is for Archives, create a config.pl file in the Archive Hosts's pc directory, adding the following line: $Conf{XferMethod} = 'archive'; To further customise the archive's parameters you can adding the changed parameters in the host's config.pl file. The parameters are explained in the config.pl file. Parameters may be fixed or the user can be allowed to change them (eg: output device). The per-host archive command is $Conf{ArchiveClientCmd}. By default this invokes __INSTALLDIR__/bin/BackupPC_archiveHost which you can copy and customize as necessary. =head2 Starting an Archive In the web interface, click on the Archive Host you wish to use. You will see a list of previous archives and a summary on each. By clicking the "Start Archive" button you are presented with the list of hosts and the approximate backup size (note this is raw size, not projected compressed size) Select the hosts you wish to archive and press the "Archive Selected Hosts" button. The next screen allows you to adjust the parameters for this archive run. Press the "Start the Archive" to start archiving the selected hosts with the parameters displayed. =head2 Starting an Archive from the command line The script BackupPC_archiveStart can be used to start an archive from the command line (or cron etc). The usage is: BackupPC_archiveStart archiveHost userName hosts... This creates an archive of the most recent backup of each of the specified hosts. The first two arguments are the archive host and the user name making the request. =head1 Other CGI Functions =head2 Configuration and Host Editor The CGI interface has a complete configuration and host editor. Only the administrator can edit the main configuration settings and hosts. The edit links are in the left navigation bar. When changes are made to any parameter a "Save" button appears at the top of the page. If you are editing a text box you will need to click outside of the text box to make the Save button appear. If you don't select Save then the changes won't be saved. The host-specific configuration can be edited from the host summary page using the link in the left navigation bar. The administrator can edit any of the host-specific configuration settings. When editing the host-specific configuration, each parameter has an "override" setting that denotes the value is host-specific, meaning that it overrides the setting in the main configuration. If you unselect "override" then the setting is removed from the host-specific configuration, and the main configuration file is displayed. User's can edit their host-specific configuration if enabled via $Conf{CgiUserConfigEditEnable}. The specific subset of configuration settings that a user can edit is specified with $Conf{CgiUserConfigEdit}. It is recommended to make this list short as possible (you probably don't want your users saving dozens of backups) and it is essential that they can't edit any of the Cmd configuration settings, otherwise they can specify an arbitrary command that will be executed as the BackupPC user. =head2 RSS BackupPC supports a very basic RSS feed. Provided you have the XML::RSS perl module installed, a URL similar to this will provide RSS information: http://localhost/cgi-bin/BackupPC/BackupPC_Admin?action=rss This feature is experimental. The information included will probably change. =head1 BackupPC Design =head2 Some design issues =over 4 =item Pooling common files To quickly see if a file is already in the pool, an MD5 digest of the file length and contents is used as the file name in the pool. This can't guarantee a file is identical: it just reduces the search to often a single file or handful of files. A complete file comparison is always done to verify if two files are really the same. Identical files on multiples backups are represented by hard links. Hardlinks are used so that identical files all refer to the same physical file on the server's disk. Also, hard links maintain reference counts so that BackupPC knows when to delete unused files from the pool. For the computer-science majors among you, you can think of the pooling system used by BackupPC as just a chained hash table stored on a (big) file system. =item The hashing function There is a tradeoff between how much of file is used for the MD5 digest and the time taken comparing all the files that have the same hash. Using the file length and just the first 4096 bytes of the file for the MD5 digest produces some repetitions. One example: with 900,000 unique files in the pool, this hash gives about 7,000 repeated files, and in the worst case 500 files have the same hash. That's not bad: we only have to do a single file compare 99.2% of the time. But in the worst case we have to compare as many as 500 files checking for a match. With a modest increase in CPU time, if we use the file length and the first 256K of the file we now only have 500 repeated files and in the worst case around 20 files have the same hash. Furthermore, if we instead use the first and last 128K of the file (more specifically, the first and eighth 128K chunks for files larger than 1MB) we get only 300 repeated files and in the worst case around 20 files have the same hash. Based on this experimentation, this is the hash function used by BackupPC. It is important that you don't change the hash function after files are already in the pool. Otherwise your pool will grow to twice the size until all the old backups (and all the old files with old hashes) eventually expire. =item Compression BackupPC supports compression. It uses the deflate and inflate methods in the Compress::Zlib module, which is based on the zlib compression library (see L). The $Conf{CompressLevel} setting specifies the compression level to use. Zero (0) means no compression. Compression levels can be from 1 (least cpu time, slightly worse compression) to 9 (most cpu time, slightly better compression). The recommended value is 3. Changing it to 5, for example, will take maybe 20% more cpu time and will get another 2-3% additional compression. Diminishing returns set in above 5. See the zlib documentation for more information about compression levels. BackupPC implements compression with minimal CPU load. Rather than compressing every incoming backup file and then trying to match it against the pool, BackupPC computes the MD5 digest based on the uncompressed file, and matches against the candidate pool files by comparing each uncompressed pool file against the incoming backup file. Since inflating a file takes roughly a factor of 10 less CPU time than deflating there is a big saving in CPU time. The combination of pooling common files and compression can yield a factor of 8 or more overall saving in backup storage. =back =head2 BackupPC operation BackupPC reads the configuration information from __CONFDIR__/config.pl. It then runs and manages all the backup activity. It maintains queues of pending backup requests, user backup requests and administrative commands. Based on the configuration various requests will be executed simultaneously. As specified by $Conf{WakeupSchedule}, BackupPC wakes up periodically to queue backups on all the PCs. This is a four step process: =over 4 =item 1 For each host and DHCP address backup requests are queued on the background command queue. =item 2 For each PC, BackupPC_dump is forked. Several of these may be run in parallel, based on the configuration. First a ping is done to see if the machine is alive. If this is a DHCP address, nmblookup is run to get the netbios name, which is used as the host name. If DNS lookup fails, $Conf{NmbLookupFindHostCmd} is run to find the IP address from the host name. The file __TOPDIR__/pc/$host/backups is read to decide whether a full or incremental backup needs to be run. If no backup is scheduled, or the ping to $host fails, then BackupPC_dump exits. The backup is done using the specified XferMethod. Either samba's smbclient or tar over ssh/rsh/nfs piped into BackupPC_tarExtract, or rsync over ssh/rsh is run, or rsyncd is connected to, with the incoming data extracted to __TOPDIR__/pc/$host/new. The XferMethod output is put into __TOPDIR__/pc/$host/XferLOG. The letter in the XferLOG file shows the type of object, similar to the first letter of the modes displayed by ls -l: d -> directory l -> symbolic link b -> block special file c -> character special file p -> pipe file (fifo) nothing -> regular file The words mean: =over 4 =item create new for this backup (ie: directory or file not in pool) =item pool found a match in the pool =item same file is identical to previous backup (contents were checksummed and verified during full dump). =item skip file skipped in incremental because attributes are the same (only displayed if $Conf{XferLogLevel} >= 2). =back As BackupPC_tarExtract extracts the files from smbclient or tar, or as rsync or ftp runs, it checks each file in the backup to see if it is identical to an existing file from any previous backup of any PC. It does this without needed to write the file to disk. If the file matches an existing file, a hardlink is created to the existing file in the pool. If the file does not match any existing files, the file is written to disk and the file name is saved in __TOPDIR__/pc/$host/NewFileList for later processing by BackupPC_link. BackupPC_tarExtract and rsync can handle arbitrarily large files and multiple candidate matching files without needing to write the file to disk in the case of a match. This significantly reduces disk writes (and also reads, since the pool file comparison is done disk to memory, rather than disk to disk). Based on the configuration settings, BackupPC_dump checks each old backup to see if any should be removed. Any expired backups are moved to __TOPDIR__/trash for later removal by BackupPC_trashClean. =item 3 For each complete, good, backup, BackupPC_link is run. To avoid race conditions as new files are linked into the pool area, only a single BackupPC_link program runs at a time and the rest are queued. BackupPC_link reads the NewFileList written by BackupPC_dump and inspects each new file in the backup. It re-checks if there is a matching file in the pool (another BackupPC_link could have added the file since BackupPC_dump checked). If so, the file is removed and replaced by a hard link to the existing file. If the file is new, a hard link to the file is made in the pool area, so that this file is available for checking against each new file and new backup. Then, if $Conf{IncrFill} is set (note that the default setting is off), for each incremental backup, hard links are made in the new backup to all files that were not extracted during the incremental backups. The means the incremental backup looks like a complete image of the PC (with the exception that files that were removed on the PC since the last full backup will still appear in the backup directory tree). The CGI interface knows how to merge unfilled incremental backups will the most recent prior filled (full) backup, giving the incremental backups a filled appearance. The default for $Conf{IncrFill} is off, since there is no need to fill incremental backups. This saves some level of disk activity, since lots of extra hardlinks are no longer needed (and don't have to be deleted when the backup expires). =item 4 BackupPC_trashClean is always run in the background to remove any expired backups. Every 5 minutes it wakes up and removes all the files in __TOPDIR__/trash. Also, once each night, BackupPC_nightly is run to complete some additional administrative tasks, such as cleaning the pool. This involves removing any files in the pool that only have a single hard link (meaning no backups are using that file). Again, to avoid race conditions, BackupPC_nightly is only run when there are no BackupPC_link processes running. When BackupPC_nightly is run no new BackupPC_link jobs are started. If BackupPC_nightly takes too long to run, the settings $Conf{MaxBackupPCNightlyJobs} and $Conf{BackupPCNightlyPeriod} can be used to run several BackupPC_nightly processes in parallel, and to split its job over several nights. =back BackupPC also listens for TCP connections on $Conf{ServerPort}, which is used by the CGI script BackupPC_Admin for status reporting and user-initiated backup or backup cancel requests. =head2 Storage layout BackupPC resides in several directories: =over 4 =item __INSTALLDIR__ Perl scripts comprising BackupPC reside in __INSTALLDIR__/bin, libraries are in __INSTALLDIR__/lib and documentation is in __INSTALLDIR__/doc. =item __CGIDIR__ The CGI script BackupPC_Admin resides in this cgi binary directory. =item __CONFDIR__ All the configuration information resides below __CONFDIR__. This directory contains: The directory __CONFDIR__ contains: =over 4 =item config.pl Configuration file. See L below for more details. =item hosts Hosts file, which lists all the PCs to backup. =item pc The directory __CONFDIR__/pc contains per-client configuration files that override settings in the main configuration file. Each file is named __CONFDIR__/pc/HOST.pl, where HOST is the host name. In pre-FHS versions of BackupPC these files were located in __TOPDIR__/pc/HOST/config.pl. =back =item __LOGDIR__ The directory __LOGDIR__ (__TOPDIR__/log on pre-FHS versions of BackupPC) contains: =over 4 =item LOG Current (today's) log file output from BackupPC. =item LOG.0 or LOG.0.z Yesterday's log file output. Log files are aged daily and compressed (if compression is enabled), and old LOG files are deleted. =item BackupPC.pid Contains BackupPC's process id. =item status.pl A summary of BackupPC's status written periodically by BackupPC so that certain state information can be maintained if BackupPC is restarted. Should not be edited. =item UserEmailInfo.pl A summary of what email was last sent to each user, and when the last email was sent. Should not be edited. =back =item __TOPDIR__ All of BackupPC's data (PC backup images, logs, configuration information) is stored below this directory. Below __TOPDIR__ are several directories: =over 4 =item __TOPDIR__/trash Any directories and files below this directory are periodically deleted whenever BackupPC_trashClean checks. When a backup is aborted or when an old backup expires, BackupPC_dump simply moves the directory to __TOPDIR__/trash for later removal by BackupPC_trashClean. =item __TOPDIR__/pool All uncompressed files from PC backups are stored below __TOPDIR__/pool. Each file's name is based on the MD5 hex digest of the file contents. Specifically, for files less than 256K, the file length and the entire file is used. For files up to 1MB, the file length and the first and last 128K are used. Finally, for files longer than 1MB, the file length, and the first and eighth 128K chunks for the file are used. Each file is stored in a subdirectory X/Y/Z, where X, Y, Z are the first 3 hex digits of the MD5 digest. For example, if a file has an MD5 digest of 123456789abcdef0, the file is stored in __TOPDIR__/pool/1/2/3/123456789abcdef0. The MD5 digest might not be unique (especially since not all the file's contents are used for files bigger than 256K). Different files that have the same MD5 digest are stored with a trailing suffix "_n" where n is an incrementing number starting at 0. So, for example, if two additional files were identical to the first, except the last byte was different, and assuming the file was larger than 1MB (so the MD5 digests are the same but the files are actually different), the three files would be stored as: __TOPDIR__/pool/1/2/3/123456789abcdef0 __TOPDIR__/pool/1/2/3/123456789abcdef0_0 __TOPDIR__/pool/1/2/3/123456789abcdef0_1 Both BackupPC_dump (actually, BackupPC_tarExtract) and BackupPC_link are responsible for checking newly backed up files against the pool. For each file, the MD5 digest is used to generate a file name in the pool directory. If the file exists in the pool, the contents are compared. If there is no match, additional files ending in "_n" are checked. (Actually, BackupPC_tarExtract compares multiple candidate files in parallel.) If the file contents exactly match, the file is created by simply making a hard link to the pool file (this is done by BackupPC_tarExtract as the backup proceeds). Otherwise, BackupPC_tarExtract writes the new file to disk and a new hard link is made in the pool to the file (this is done later by BackupPC_link). Therefore, every file in the pool will have at least 2 hard links (one for the pool file and one for the backup file below __TOPDIR__/pc). Identical files from different backups or PCs will all be linked to the same file. When old backups are deleted, some files in the pool might only have one link. BackupPC_nightly checks the entire pool and removes all files that have only a single link, thereby recovering the storage for that file. One other issue: zero length files are not pooled, since there are a lot of these files and on most file systems it doesn't save any disk space to turn these files into hard links. =item __TOPDIR__/cpool All compressed files from PC backups are stored below __TOPDIR__/cpool. Its layout is the same as __TOPDIR__/pool, and the hashing function is the same (and, importantly, based on the uncompressed file, not the compressed file). =item __TOPDIR__/pc/$host For each PC $host, all the backups for that PC are stored below the directory __TOPDIR__/pc/$host. This directory contains the following files: =over 4 =item LOG Current log file for this PC from BackupPC_dump. =item LOG.DDMMYYYY or LOG.DDMMYYYY.z Last month's log file. Log files are aged monthly and compressed (if compression is enabled), and old LOG files are deleted. In earlier versions of BackupPC these files used to have a suffix of 0, 1, .... =item XferERR or XferERR.z Output from the transport program (ie: smbclient, tar, rsync or ftp) for the most recent failed backup. =item new Subdirectory in which the current backup is stored. This directory is renamed if the backup succeeds. =item XferLOG or XferLOG.z Output from the transport program (ie: smbclient, tar, rsync or ftp) for the current backup. =item nnn (an integer) Successful backups are in directories numbered sequentially starting at 0. =item XferLOG.nnn or XferLOG.nnn.z Output from the transport program (ie: smbclient, tar, rsync or ftp) corresponding to backup number nnn. =item RestoreInfo.nnn Information about restore request #nnn including who, what, when, and why. This file is in Data::Dumper format. (Note that the restore numbers are not related to the backup number.) =item RestoreLOG.nnn.z Output from smbclient, tar or rsync during restore #nnn. (Note that the restore numbers are not related to the backup number.) =item ArchiveInfo.nnn Information about archive request #nnn including who, what, when, and why. This file is in Data::Dumper format. (Note that the archive numbers are not related to the restore or backup number.) =item ArchiveLOG.nnn.z Output from archive #nnn. (Note that the archive numbers are not related to the backup or restore number.) =item config.pl Old location of optional configuration settings specific to this host. Settings in this file override the main configuration file. In new versions of BackupPC the per-host configuration files are stored in __CONFDIR__/pc/HOST.pl. =item backups A tab-delimited ascii table listing information about each successful backup, one per row. The columns are: =over 4 =item num The backup number, an integer that starts at 0 and increments for each successive backup. The corresponding backup is stored in the directory num (eg: if this field is 5, then the backup is stored in __TOPDIR__/pc/$host/5). =item type Set to "full" or "incr" for full or incremental backup. =item startTime Start time of the backup in unix seconds. =item endTime Stop time of the backup in unix seconds. =item nFiles Number of files backed up (as reported by smbclient, tar, rsync or ftp). =item size Total file size backed up (as reported by smbclient, tar, rsync or ftp). =item nFilesExist Number of files that were already in the pool (as determined by BackupPC_dump and BackupPC_link). =item sizeExist Total size of files that were already in the pool (as determined by BackupPC_dump and BackupPC_link). =item nFilesNew Number of files that were not in the pool (as determined by BackupPC_link). =item sizeNew Total size of files that were not in the pool (as determined by BackupPC_link). =item xferErrs Number of errors or warnings from smbclient, tar, rsync or ftp. =item xferBadFile Number of errors from smbclient that were bad file errors (zero otherwise). =item xferBadShare Number of errors from smbclient that were bad share errors (zero otherwise). =item tarErrs Number of errors from BackupPC_tarExtract. =item compress The compression level used on this backup. Zero or empty means no compression. =item sizeExistComp Total compressed size of files that were already in the pool (as determined by BackupPC_dump and BackupPC_link). =item sizeNewComp Total compressed size of files that were not in the pool (as determined by BackupPC_link). =item noFill Set if this backup has not been filled in with the most recent previous filled or full backup. See $Conf{IncrFill}. =item fillFromNum If this backup was filled (ie: noFill is 0) then this is the number of the backup that it was filled from =item mangle Set if this backup has mangled file names and attributes. Always true for backups in v1.4.0 and above. False for all backups prior to v1.4.0. =item xferMethod Set to the value of $Conf{XferMethod} when this dump was done. =item level The level of this dump. A full dump is level 0. Currently incrementals are 1. But when multi-level incrementals are supported this will reflect each dump's incremental level. =back =item restores A tab-delimited ascii table listing information about each requested restore, one per row. The columns are: =over 4 =item num Restore number (matches the suffix of the RestoreInfo.nnn and RestoreLOG.nnn.z file), unrelated to the backup number. =item startTime Start time of the restore in unix seconds. =item endTime End time of the restore in unix seconds. =item result Result (ok or failed). =item errorMsg Error message if restore failed. =item nFiles Number of files restored. =item size Size in bytes of the restored files. =item tarCreateErrs Number of errors from BackupPC_tarCreate during restore. =item xferErrs Number of errors from smbclient, tar, rsync or ftp during restore. =back =item archives A tab-delimited ascii table listing information about each requested archive, one per row. The columns are: =over 4 =item num Archive number (matches the suffix of the ArchiveInfo.nnn and ArchiveLOG.nnn.z file), unrelated to the backup or restore number. =item startTime Start time of the restore in unix seconds. =item endTime End time of the restore in unix seconds. =item result Result (ok or failed). =item errorMsg Error message if archive failed. =back =back =back =back =head2 Compressed file format The compressed file format is as generated by Compress::Zlib::deflate with one minor, but important, tweak. Since Compress::Zlib::inflate fully inflates its argument in memory, it could take large amounts of memory if it was inflating a highly compressed file. For example, a 200MB file of 0x0 bytes compresses to around 200K bytes. If Compress::Zlib::inflate was called with this single 200K buffer, it would need to allocate 200MB of memory to return the result. BackupPC watches how efficiently a file is compressing. If a big file has very high compression (meaning it will use too much memory when it is inflated), BackupPC calls the flush() method, which gracefully completes the current compression. BackupPC then starts another deflate and simply appends the output file. So the BackupPC compressed file format is one or more concatenated deflations/flushes. The specific ratios that BackupPC uses is that if a 6MB chunk compresses to less than 64K then a flush will be done. Back to the example of the 200MB file of 0x0 bytes. Adding flushes every 6MB adds only 200 or so bytes to the 200K output. So the storage cost of flushing is negligible. To easily decompress a BackupPC compressed file, the script BackupPC_zcat can be found in __INSTALLDIR__/bin. For each file name argument it inflates the file and writes it to stdout. =head2 Rsync checksum caching An incremental backup with rsync compares attributes on the client with the last full backup. Any files with identical attributes are skipped. A full backup with rsync sets the --ignore-times option, which causes every file to be examined independent of attributes. Each file is examined by generating block checksums (default 2K blocks) on the receiving side (that's the BackupPC side), sending those checksums to the client, where the remote rsync matches those checksums with the corresponding file. The matching blocks and new data is sent back, allowing the client file to be reassembled. A checksum for the entire file is sent to as an extra check the the reconstructed file is correct. This results in significant disk IO and computation for BackupPC: every file in a full backup, or any file with non-matching attributes in an incremental backup, needs to be uncompressed, block checksums computed and sent. Then the receiving side reassembles the file and has to verify the whole-file checksum. Even if the file is identical, prior to 2.1.0, BackupPC had to read and uncompress the file twice, once to compute the block checksums and later to verify the whole-file checksum. Starting in 2.1.0, BackupPC supports optional checksum caching, which means the block and file checksums only need to be computed once for each file. This results in a significant performance improvement. This only works for compressed pool files. It is enabled by adding '--checksum-seed=32761', to $Conf{RsyncArgs} and $Conf{RsyncRestoreArgs}. Rsync versions prior to and including rsync-2.6.2 need a small patch to add support for the --checksum-seed option. This patch is available in the cygwin-rsyncd package at L. This patch is already included in rsync CVS, so it will be standard in future versions of rsync. When this option is present, BackupPC will add block and file checksums to the compressed pool file the next time a pool file is used and it doesn't already have cached checksums. The first time a new file is written to the pool, the checksums are not appended. The next time checksums are needed for a file, they are computed and added. So the full performance benefit of checksum caching won't be noticed until the third time a pool file is used (eg: the third full backup). With checksum caching enabled, there is a risk that should a file's contents in the pool be corrupted due to a disk problem, but the cached checksums are still correct, the corruption will not be detected by a full backup, since the file contents are no longer read and compared. To reduce the chance that this remains undetected, BackupPC can recheck cached checksums for a fraction of the files. This fraction is set with the $Conf{RsyncCsumCacheVerifyProb} setting. The default value of 0.01 means that 1% of the time a file's checksums are read, the checksums are verified. This reduces performance slightly, but, over time, ensures that files contents are in sync with the cached checksums. The format of the cached checksum data can be discovered by looking at the code. Basically, the first byte of the compressed file is changed to denote that checksums are appended. The block and file checksum data, plus some other information and magic word, are appended to the compressed file. This allows the cache update to be done in-place. =head2 File name mangling Backup file names are stored in "mangled" form. Each node of a path is preceded by "f" (mnemonic: file), and special characters (\n, \r, % and /) are URI-encoded as "%xx", where xx is the ascii character's hex value. So c:/craig/example.txt is now stored as fc/fcraig/fexample.txt. This was done mainly so meta-data could be stored alongside the backup files without name collisions. In particular, the attributes for the files in a directory are stored in a file called "attrib", and mangling avoids file name collisions (I discarded the idea of having a duplicate directory tree for every backup just to store the attributes). Other meta-data (eg: rsync checksums) could be stored in file names preceded by, eg, "c". There are two other benefits to mangling: the share name might contain "/" (eg: "/home/craig" for tar transport), and I wanted that represented as a single level in the storage tree. Secondly, as files are written to NewFileList for later processing by BackupPC_link, embedded newlines in the file's path will cause problems which are avoided by mangling. The CGI script undoes the mangling, so it is invisible to the user. Old (unmangled) backups are still supported by the CGI interface. =head2 Special files Linux/unix file systems support several special file types: symbolic links, character and block device files, fifos (pipes) and unix-domain sockets. All except unix-domain sockets are supported by BackupPC (there's no point in backing up or restoring unix-domain sockets since they only have meaning after a process creates them). Symbolic links are stored as a plain file whose contents are the contents of the link (not the file it points to). This file is compressed and pooled like any normal file. Character and block device files are also stored as plain files, whose contents are two integers separated by a comma; the numbers are the major and minor device number. These files are compressed and pooled like any normal file. Fifo files are stored as empty plain files (which are not pooled since they have zero size). In all cases, the original file type is stored in the attrib file so it can be correctly restored. Hardlinks are also supported. When GNU tar first encounters a file with more than one link (ie: hardlinks) it dumps it as a regular file. When it sees the second and subsequent hardlinks to the same file, it dumps just the hardlink information. BackupPC correctly recognizes these hardlinks and stores them just like symlinks: a regular text file whose contents is the path of the file linked to. The CGI script will download the original file when you click on a hardlink. Also, BackupPC_tarCreate has enough magic to re-create the hardlinks dynamically based on whether or not the original file and hardlinks are both included in the tar file. For example, imagine a/b/x is a hardlink to a/c/y. If you use BackupPC_tarCreate to restore directory a, then the tar file will include a/b/x as the original file and a/c/y will be a hardlink to a/b/x. If, instead you restore a/c, then the tar file will include a/c/y as the original file, not a hardlink. =head2 Attribute file format The unix attributes for the contents of a directory (all the files and directories in that directory) are stored in a file called attrib. There is a single attrib file for each directory in a backup. For example, if c:/craig contains a single file c:/craig/example.txt, that file would be stored as fc/fcraig/fexample.txt and there would be an attribute file in fc/fcraig/attrib (and also fc/attrib and ./attrib). The file fc/fcraig/attrib would contain a single entry containing the attributes for fc/fcraig/fexample.txt. The attrib file starts with a magic number, followed by the concatenation of the following information for each file: =over 4 =item * File name length in perl's pack "w" format (variable length base 128). =item * File name. =item * The unix file type, mode, uid, gid and file size divided by 4GB and file size modulo 4GB (type mode uid gid sizeDiv4GB sizeMod4GB), in perl's pack "w" format (variable length base 128). =item * The unix mtime (unix seconds) in perl's pack "N" format (32 bit integer). =back The attrib file is also compressed if compression is enabled. See the lib/BackupPC/Attrib.pm module for full details. Attribute files are pooled just like normal backup files. This saves space if all the files in a directory have the same attributes across multiple backups, which is common. =head2 Optimizations BackupPC doesn't care about the access time of files in the pool since it saves attribute meta-data separate from the files. Since BackupPC mostly does reads from disk, maintaining the access time of files generates a lot of unnecessary disk writes. So, provided BackupPC has a dedicated data disk, you should consider mounting BackupPC's data directory with the noatime (or, with Linux kernels >=2.6.20, relatime) attribute (see mount(1)). =head2 Some Limitations BackupPC isn't perfect (but it is getting better). Please see L for a discussion of some of BackupPC's limitations. =head2 Security issues Please see L for a discussion of some of various security issues. =head1 Configuration File The BackupPC configuration file resides in __CONFDIR__/config.pl. Optional per-PC configuration files reside in __CONFDIR__/pc/$host.pl (or __TOPDIR__/pc/$host/config.pl in non-FHS versions of BackupPC). This file can be used to override settings just for a particular PC. =head2 Modifying the main configuration file The configuration file is a perl script that is executed by BackupPC, so you should be careful to preserve the file syntax (punctuation, quotes etc) when you edit it. It is recommended that you use CVS, RCS or some other method of source control for changing config.pl. BackupPC reads or re-reads the main configuration file and the hosts file in three cases: =over 4 =item * Upon startup. =item * When BackupPC is sent a HUP (-1) signal. Assuming you installed the init.d script, you can also do this with "/etc/init.d/backuppc reload". =item * When the modification time of config.pl file changes. BackupPC checks the modification time once during each regular wakeup. =back Whenever you change the configuration file you can either do a kill -HUP BackupPC_pid or simply wait until the next regular wakeup period. Each time the configuration file is re-read a message is reported in the LOG file, so you can tail it (or view it via the CGI interface) to make sure your kill -HUP worked. Errors in parsing the configuration file are also reported in the LOG file. The optional per-PC configuration file (__CONFDIR__/pc/$host.pl or __TOPDIR__/pc/$host/config.pl in non-FHS versions of BackupPC) is read whenever it is needed by BackupPC_dump, BackupPC_link and others. =head1 Configuration Parameters The configuration parameters are divided into five general groups. The first group (general server configuration) provides general configuration for BackupPC. The next two groups describe what to backup, when to do it, and how long to keep it. The fourth group are settings for email reminders, and the final group contains settings for the CGI interface. All configuration settings in the second through fifth groups can be overridden by the per-PC config.pl file. =head2 General server configuration =over 4 =item $Conf{ServerHost} = ''; Host name on which the BackupPC server is running. =item $Conf{ServerPort} = -1; TCP port number on which the BackupPC server listens for and accepts connections. Normally this should be disabled (set to -1). The TCP port is only needed if apache runs on a different machine from BackupPC. In that case, set this to any spare port number over 1024 (eg: 2359). If you enable the TCP port, make sure you set $Conf{ServerMesgSecret} too! =item $Conf{ServerMesgSecret} = ''; Shared secret to make the TCP port secure. Set this to a hard to guess string if you enable the TCP port (ie: $Conf{ServerPort} > 0). To avoid possible attacks via the TCP socket interface, every client message is protected by an MD5 digest. The MD5 digest includes four items: - a seed that is sent to the client when the connection opens - a sequence number that increments for each message - a shared secret that is stored in $Conf{ServerMesgSecret} - the message itself. The message is sent in plain text preceded by the MD5 digest. A snooper can see the plain-text seed sent by BackupPC and plain-text message from the client, but cannot construct a valid MD5 digest since the secret $Conf{ServerMesgSecret} is unknown. A replay attack is not possible since the seed changes on a per-connection and per-message basis. =item $Conf{MyPath} = '/bin'; PATH setting for BackupPC. An explicit value is necessary for taint mode. Value shouldn't matter too much since all execs use explicit paths. However, taint mode in perl will complain if this directory is world writable. =item $Conf{UmaskMode} = 027; Permission mask for directories and files created by BackupPC. Default value prevents any access from group other, and prevents group write. =item $Conf{WakeupSchedule} = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]; Times at which we wake up, check all the PCs, and schedule necessary backups. Times are measured in hours since midnight. Can be fractional if necessary (eg: 4.25 means 4:15am). If the hosts you are backing up are always connected to the network you might have only one or two wakeups each night. This will keep the backup activity after hours. On the other hand, if you are backing up laptops that are only intermittently connected to the network you will want to have frequent wakeups (eg: hourly) to maximize the chance that each laptop is backed up. Examples: $Conf{WakeupSchedule} = [22.5]; # once per day at 10:30 pm. $Conf{WakeupSchedule} = [2,4,6,8,10,12,14,16,18,20,22]; # every 2 hours The default value is every hour except midnight. The first entry of $Conf{WakeupSchedule} is when BackupPC_nightly is run. You might want to re-arrange the entries in $Conf{WakeupSchedule} (they don't have to be ascending) so that the first entry is when you want BackupPC_nightly to run (eg: when you don't expect a lot of regular backups to run). =item $Conf{MaxBackups} = 4; Maximum number of simultaneous backups to run. If there are no user backup requests then this is the maximum number of simultaneous backups. =item $Conf{MaxUserBackups} = 4; Additional number of simultaneous backups that users can run. As many as $Conf{MaxBackups} + $Conf{MaxUserBackups} requests can run at the same time. =item $Conf{MaxPendingCmds} = 15; Maximum number of pending link commands. New backups will only be started if there are no more than $Conf{MaxPendingCmds} plus $Conf{MaxBackups} number of pending link commands, plus running jobs. This limit is to make sure BackupPC doesn't fall too far behind in running BackupPC_link commands. =item $Conf{CmdQueueNice} = 10; Nice level at which CmdQueue commands (eg: BackupPC_link and BackupPC_nightly) are run at. =item $Conf{MaxBackupPCNightlyJobs} = 2; How many BackupPC_nightly processes to run in parallel. Each night, at the first wakeup listed in $Conf{WakeupSchedule}, BackupPC_nightly is run. Its job is to remove unneeded files in the pool, ie: files that only have one link. To avoid race conditions, BackupPC_nightly and BackupPC_link cannot run at the same time. Starting in v3.0.0, BackupPC_nightly can run concurrently with backups (BackupPC_dump). So to reduce the elapsed time, you might want to increase this setting to run several BackupPC_nightly processes in parallel (eg: 4, or even 8). =item $Conf{BackupPCNightlyPeriod} = 1; How many days (runs) it takes BackupPC_nightly to traverse the entire pool. Normally this is 1, which means every night it runs, it does traverse the entire pool removing unused pool files. Other valid values are 2, 4, 8, 16. This causes BackupPC_nightly to traverse 1/2, 1/4, 1/8 or 1/16th of the pool each night, meaning it takes 2, 4, 8 or 16 days to completely traverse the pool. The advantage is that each night the running time of BackupPC_nightly is reduced roughly in proportion, since the total job is split over multiple days. The disadvantage is that unused pool files take longer to get deleted, which will slightly increase disk usage. Note that even when $Conf{BackupPCNightlyPeriod} > 1, BackupPC_nightly still runs every night. It just does less work each time it runs. Examples: $Conf{BackupPCNightlyPeriod} = 1; # entire pool is checked every night $Conf{BackupPCNightlyPeriod} = 2; # two days to complete pool check # (different half each night) $Conf{BackupPCNightlyPeriod} = 4; # four days to complete pool check # (different quarter each night) =item $Conf{MaxOldLogFiles} = 14; Maximum number of log files we keep around in log directory. These files are aged nightly. A setting of 14 means the log directory will contain about 2 weeks of old log files, in particular at most the files LOG, LOG.0, LOG.1, ... LOG.13 (except today's LOG, these files will have a .z extension if compression is on). If you decrease this number after BackupPC has been running for a while you will have to manually remove the older log files. =item $Conf{DfPath} = ''; Full path to the df command. Security caution: normal users should not allowed to write to this file or directory. =item $Conf{DfCmd} = '$dfPath $topDir'; Command to run df. The following variables are substituted at run-time: $dfPath path to df ($Conf{DfPath}) $topDir top-level BackupPC data directory Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{SplitPath} = ''; =item $Conf{ParPath} = ''; =item $Conf{CatPath} = ''; =item $Conf{GzipPath} = ''; =item $Conf{Bzip2Path} = ''; Full path to various commands for archiving =item $Conf{DfMaxUsagePct} = 95; Maximum threshold for disk utilization on the __TOPDIR__ filesystem. If the output from $Conf{DfPath} reports a percentage larger than this number then no new regularly scheduled backups will be run. However, user requested backups (which are usually incremental and tend to be small) are still performed, independent of disk usage. Also, currently running backups will not be terminated when the disk usage exceeds this number. =item $Conf{TrashCleanSleepSec} = 300; How long BackupPC_trashClean sleeps in seconds between each check of the trash directory. Once every 5 minutes should be reasonable. =item $Conf{DHCPAddressRanges} = []; List of DHCP address ranges we search looking for PCs to backup. This is an array of hashes for each class C address range. This is only needed if hosts in the conf/hosts file have the dhcp flag set. Examples: # to specify 192.10.10.20 to 192.10.10.250 as the DHCP address pool $Conf{DHCPAddressRanges} = [ { ipAddrBase => '192.10.10', first => 20, last => 250, }, ]; # to specify two pools (192.10.10.20-250 and 192.10.11.10-50) $Conf{DHCPAddressRanges} = [ { ipAddrBase => '192.10.10', first => 20, last => 250, }, { ipAddrBase => '192.10.11', first => 10, last => 50, }, ]; =item $Conf{BackupPCUser} = ''; The BackupPC user. =item $Conf{TopDir} = ''; =item $Conf{ConfDir} = ''; =item $Conf{LogDir} = ''; =item $Conf{InstallDir} = ''; =item $Conf{CgiDir} = ''; Important installation directories: TopDir - where all the backup data is stored ConfDir - where the main config and hosts files resides LogDir - where log files and other transient information InstallDir - where the bin, lib and doc installation dirs reside. Note: you cannot change this value since all the perl scripts include this path. You must reinstall with configure.pl to change InstallDir. CgiDir - Apache CGI directory for BackupPC_Admin Note: it is STRONGLY recommended that you don't change the values here. These are set at installation time and are here for reference and are used during upgrades. Instead of changing TopDir here it is recommended that you use a symbolic link to the new location, or mount the new BackupPC store at the existing $Conf{TopDir} setting. =item $Conf{BackupPCUserVerify} = 1; Whether BackupPC and the CGI script BackupPC_Admin verify that they are really running as user $Conf{BackupPCUser}. If this flag is set and the effective user id (euid) differs from $Conf{BackupPCUser} then both scripts exit with an error. This catches cases where BackupPC might be accidently started as root or the wrong user, or if the CGI script is not installed correctly. =item $Conf{HardLinkMax} = 31999; Maximum number of hardlinks supported by the $TopDir file system that BackupPC uses. Most linux or unix file systems should support at least 32000 hardlinks per file, or 64000 in other cases. If a pool file already has this number of hardlinks, a new pool file is created so that new hardlinks can be accommodated. This limit will only be hit if an identical file appears at least this number of times across all the backups. =item $Conf{PerlModuleLoad} = undef; Advanced option for asking BackupPC to load additional perl modules. Can be a list (array ref) of module names to load at startup. =item $Conf{ServerInitdPath} = ''; =item $Conf{ServerInitdStartCmd} = ''; Path to init.d script and command to use that script to start the server from the CGI interface. The following variables are substituted at run-time: $sshPath path to ssh ($Conf{SshPath}) $serverHost same as $Conf{ServerHost} $serverInitdPath path to init.d script ($Conf{ServerInitdPath}) Example: $Conf{ServerInitdPath} = '/etc/init.d/backuppc'; $Conf{ServerInitdStartCmd} = '$sshPath -q -x -l root $serverHost' . ' $serverInitdPath start' . ' < /dev/null >& /dev/null'; Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =back =head2 What to backup and when to do it =over 4 =item $Conf{FullPeriod} = 6.97; Minimum period in days between full backups. A full dump will only be done if at least this much time has elapsed since the last full dump, and at least $Conf{IncrPeriod} days has elapsed since the last successful dump. Typically this is set slightly less than an integer number of days. The time taken for the backup, plus the granularity of $Conf{WakeupSchedule} will make the actual backup interval a bit longer. =item $Conf{IncrPeriod} = 0.97; Minimum period in days between incremental backups (a user requested incremental backup will be done anytime on demand). Typically this is set slightly less than an integer number of days. The time taken for the backup, plus the granularity of $Conf{WakeupSchedule} will make the actual backup interval a bit longer. =item $Conf{FullKeepCnt} = 1; Number of full backups to keep. Must be >= 1. In the steady state, each time a full backup completes successfully the oldest one is removed. If this number is decreased, the extra old backups will be removed. If filling of incremental dumps is off the oldest backup always has to be a full (ie: filled) dump. This might mean one or two extra full dumps are kept until the oldest incremental backups expire. Exponential backup expiry is also supported. This allows you to specify: - num fulls to keep at intervals of 1 * $Conf{FullPeriod}, followed by - num fulls to keep at intervals of 2 * $Conf{FullPeriod}, - num fulls to keep at intervals of 4 * $Conf{FullPeriod}, - num fulls to keep at intervals of 8 * $Conf{FullPeriod}, - num fulls to keep at intervals of 16 * $Conf{FullPeriod}, and so on. This works by deleting every other full as each expiry boundary is crossed. Exponential expiry is specified using an array for $Conf{FullKeepCnt}: $Conf{FullKeepCnt} = [4, 2, 3]; Entry #n specifies how many fulls to keep at an interval of 2^n * $Conf{FullPeriod} (ie: 1, 2, 4, 8, 16, 32, ...). The example above specifies keeping 4 of the most recent full backups (1 week interval) two full backups at 2 week intervals, and 3 full backups at 4 week intervals, eg: full 0 19 weeks old \ full 1 15 weeks old >--- 3 backups at 4 * $Conf{FullPeriod} full 2 11 weeks old / full 3 7 weeks old \____ 2 backups at 2 * $Conf{FullPeriod} full 4 5 weeks old / full 5 3 weeks old \ full 6 2 weeks old \___ 4 backups at 1 * $Conf{FullPeriod} full 7 1 week old / full 8 current / On a given week the spacing might be less than shown as each backup ages through each expiry period. For example, one week later, a new full is completed and the oldest is deleted, giving: full 0 16 weeks old \ full 1 12 weeks old >--- 3 backups at 4 * $Conf{FullPeriod} full 2 8 weeks old / full 3 6 weeks old \____ 2 backups at 2 * $Conf{FullPeriod} full 4 4 weeks old / full 5 3 weeks old \ full 6 2 weeks old \___ 4 backups at 1 * $Conf{FullPeriod} full 7 1 week old / full 8 current / You can specify 0 as a count (except in the first entry), and the array can be as long as you wish. For example: $Conf{FullKeepCnt} = [4, 0, 4, 0, 0, 2]; This will keep 10 full dumps, 4 most recent at 1 * $Conf{FullPeriod}, followed by 4 at an interval of 4 * $Conf{FullPeriod} (approx 1 month apart), and then 2 at an interval of 32 * $Conf{FullPeriod} (approx 7-8 months apart). Example: these two settings are equivalent and both keep just the four most recent full dumps: $Conf{FullKeepCnt} = 4; $Conf{FullKeepCnt} = [4]; =item $Conf{FullKeepCntMin} = 1; =item $Conf{FullAgeMax} = 90; Very old full backups are removed after $Conf{FullAgeMax} days. However, we keep at least $Conf{FullKeepCntMin} full backups no matter how old they are. Note that $Conf{FullAgeMax} will be increased to $Conf{FullKeepCnt} times $Conf{FullPeriod} if $Conf{FullKeepCnt} specifies enough full backups to exceed $Conf{FullAgeMax}. =item $Conf{IncrKeepCnt} = 6; Number of incremental backups to keep. Must be >= 1. In the steady state, each time an incr backup completes successfully the oldest one is removed. If this number is decreased, the extra old backups will be removed. =item $Conf{IncrKeepCntMin} = 1; =item $Conf{IncrAgeMax} = 30; Very old incremental backups are removed after $Conf{IncrAgeMax} days. However, we keep at least $Conf{IncrKeepCntMin} incremental backups no matter how old they are. =item $Conf{IncrLevels} = [1]; Level of each incremental. "Level" follows the terminology of dump(1). A full backup has level 0. A new incremental of level N will backup all files that have changed since the most recent backup of a lower level. The entries of $Conf{IncrLevels} apply in order to each incremental after each full backup. It wraps around until the next full backup. For example, these two settings have the same effect: $Conf{IncrLevels} = [1, 2, 3]; $Conf{IncrLevels} = [1, 2, 3, 1, 2, 3]; This means the 1st and 4th incrementals (level 1) go all the way back to the full. The 2nd and 3rd (and 5th and 6th) backups just go back to the immediate preceeding incremental. Specifying a sequence of multi-level incrementals will usually mean more than $Conf{IncrKeepCnt} incrementals will need to be kept, since lower level incrementals are needed to merge a complete view of a backup. For example, with $Conf{FullPeriod} = 7; $Conf{IncrPeriod} = 1; $Conf{IncrKeepCnt} = 6; $Conf{IncrLevels} = [1, 2, 3, 4, 5, 6]; there will be up to 11 incrementals in this case: backup #0 (full, level 0, oldest) backup #1 (incr, level 1) backup #2 (incr, level 2) backup #3 (incr, level 3) backup #4 (incr, level 4) backup #5 (incr, level 5) backup #6 (incr, level 6) backup #7 (full, level 0) backup #8 (incr, level 1) backup #9 (incr, level 2) backup #10 (incr, level 3) backup #11 (incr, level 4) backup #12 (incr, level 5, newest) Backup #1 (the oldest level 1 incremental) can't be deleted since backups 2..6 depend on it. Those 6 incrementals can't all be deleted since that would only leave 5 (#8..12). When the next incremental happens (level 6), the complete set of 6 older incrementals (#1..6) will be deleted, since that maintains the required number ($Conf{IncrKeepCnt}) of incrementals. This situation is reduced if you set shorter chains of multi-level incrementals, eg: $Conf{IncrLevels} = [1, 2, 3]; would only have up to 2 extra incremenals before all 3 are deleted. BackupPC as usual merges the full and the sequence of incrementals together so each incremental can be browsed and restored as though it is a complete backup. If you specify a long chain of incrementals then more backups need to be merged when browsing, restoring, or getting the starting point for rsync backups. In the example above (levels 1..6), browing backup #6 requires 7 different backups (#0..6) to be merged. Because of this merging and the additional incrementals that need to be kept, it is recommended that some level 1 incrementals be included in $Conf{IncrLevels}. Prior to version 3.0 incrementals were always level 1, meaning each incremental backed up all the files that changed since the last full. =item $Conf{BackupsDisable} = 0; Disable all full and incremental backups. These settings are useful for a client that is no longer being backed up (eg: a retired machine), but you wish to keep the last backups available for browsing or restoring to other machines. There are three values for $Conf{BackupsDisable}: 0 Backups are enabled. 1 Don't do any regular backups on this client. Manually requested backups (via the CGI interface) will still occur. 2 Don't do any backups on this client. Manually requested backups (via the CGI interface) will be ignored. In versions prior to 3.0 Backups were disabled by setting $Conf{FullPeriod} to -1 or -2. =item $Conf{PartialAgeMax} = 3; A failed full backup is saved as a partial backup. The rsync XferMethod can take advantage of the partial full when the next backup is run. This parameter sets the age of the partial full in days: if the partial backup is older than this number of days, then rsync will ignore (not use) the partial full when the next backup is run. If you set this to a negative value then no partials will be saved. If you set this to 0, partials will be saved, but will not be used by the next backup. The default setting of 3 days means that a partial older than 3 days is ignored when the next full backup is done. =item $Conf{IncrFill} = 0; Whether incremental backups are filled. "Filling" means that the most recent full (or filled) dump is merged into the new incremental dump using hardlinks. This makes an incremental dump look like a full dump. Prior to v1.03 all incremental backups were filled. In v1.4.0 and later the default is off. BackupPC, and the cgi interface in particular, do the right thing on un-filled incremental backups. It will correctly display the merged incremental backup with the most recent filled backup, giving the un-filled incremental backups a filled appearance. That means it invisible to the user whether incremental dumps are filled or not. Filling backups takes a little extra disk space, and it does cost some extra disk activity for filling, and later removal. Filling is no longer useful, since file mangling and compression doesn't make a filled backup very useful. It's likely the filling option will be removed from future versions: filling will be delegated to the display and extraction of backup data. If filling is off, BackupPC makes sure that the oldest backup is a full, otherwise the following incremental backups will be incomplete. This might mean an extra full backup has to be kept until the following incremental backups expire. The default is off. You can turn this on or off at any time without affecting existing backups. =item $Conf{RestoreInfoKeepCnt} = 10; Number of restore logs to keep. BackupPC remembers information about each restore request. This number per client will be kept around before the oldest ones are pruned. Note: files/dirs delivered via Zip or Tar downloads don't count as restores. Only the first restore option (where the files and dirs are written to the host) count as restores that are logged. =item $Conf{ArchiveInfoKeepCnt} = 10; Number of archive logs to keep. BackupPC remembers information about each archive request. This number per archive client will be kept around before the oldest ones are pruned. =item $Conf{BackupFilesOnly} = undef; List of directories or files to backup. If this is defined, only these directories or files will be backed up. When editing from the web interface, you should add a valid ShareName (based on $Conf{XferMethod}), and then enter the directories specific to that ShareName. A special ShareName "*" matches any ShareName that doesn't have an explicit entry. For Smb, only one of $Conf{BackupFilesExclude} and $Conf{BackupFilesOnly} can be specified per share. If both are set for a particular share, then $Conf{BackupFilesOnly} takes precedence and $Conf{BackupFilesExclude} is ignored. This can be set to a string, an array of strings, or, in the case of multiple shares, a hash of strings or arrays. A hash is used to give a list of directories or files to backup for each share (the share name is the key). If this is set to just a string or array, and $Conf{SmbShareName} contains multiple share names, then the setting is assumed to apply all shares. If a hash is used, a special key "*" means it applies to all shares that don't have a specific entry. Examples: $Conf{BackupFilesOnly} = '/myFiles'; $Conf{BackupFilesOnly} = ['/myFiles']; # same as first example $Conf{BackupFilesOnly} = ['/myFiles', '/important']; $Conf{BackupFilesOnly} = { 'c' => ['/myFiles', '/important'], # these are for 'c' share 'd' => ['/moreFiles', '/archive'], # these are for 'd' share }; $Conf{BackupFilesOnly} = { 'c' => ['/myFiles', '/important'], # these are for 'c' share '*' => ['/myFiles', '/important'], # these are other shares }; =item $Conf{BackupFilesExclude} = undef; List of directories or files to exclude from the backup. For Smb, only one of $Conf{BackupFilesExclude} and $Conf{BackupFilesOnly} can be specified per share. If both are set for a particular share, then $Conf{BackupFilesOnly} takes precedence and $Conf{BackupFilesExclude} is ignored. When editing from the web interface, you should add a valid ShareName (based on $Conf{XferMethod}), and then enter the directories or files specific to that ShareName. A special ShareName "*" matches any ShareName that doesn't have an explicit entry. This can be set to a string, an array of strings, or, in the case of multiple shares, a hash of strings or arrays. A hash is used to give a list of directories or files to exclude for each share (the share name is the key). If this is set to just a string or array, and $Conf{SmbShareName} contains multiple share names, then the setting is assumed to apply to all shares. The exact behavior is determined by the underlying transport program, smbclient or tar. For smbclient the exlclude file list is passed into the X option. Simple shell wild-cards using "*" or "?" are allowed. For tar, if the exclude file contains a "/" it is assumed to be anchored at the start of the string. Since all the tar paths start with "./", BackupPC prepends a "." if the exclude file starts with a "/". Note that GNU tar version >= 1.13.7 is required for the exclude option to work correctly. For linux or unix machines you should add "/proc" to $Conf{BackupFilesExclude} unless you have specified --one-file-system in $Conf{TarClientCmd} or --one-file-system in $Conf{RsyncArgs}. Also, for tar, do not use a trailing "/" in the directory name: a trailing "/" causes the name to not match and the directory will not be excluded. Users report that for smbclient you should specify a directory followed by "/*", eg: "/proc/*", instead of just "/proc". FTP servers are traversed recursively so excluding directories will also exclude its contents. You can use the wildcard characters "*" and "?" to define files for inclusion and exclusion. Both attributes $Conf{BackupFilesOnly} and $Conf{BackupFilesExclude} can be defined for the same share. If a hash is used, a special key "*" means it applies to all shares that don't have a specific entry. Examples: $Conf{BackupFilesExclude} = '/temp'; $Conf{BackupFilesExclude} = ['/temp']; # same as first example $Conf{BackupFilesExclude} = ['/temp', '/winnt/tmp']; $Conf{BackupFilesExclude} = { 'c' => ['/temp', '/winnt/tmp'], # these are for 'c' share 'd' => ['/junk', '/dont_back_this_up'], # these are for 'd' share }; $Conf{BackupFilesExclude} = { 'c' => ['/temp', '/winnt/tmp'], # these are for 'c' share '*' => ['/junk', '/dont_back_this_up'], # these are for other shares }; =item $Conf{BlackoutBadPingLimit} = 3; =item $Conf{BlackoutGoodCnt} = 7; PCs that are always or often on the network can be backed up after hours, to reduce PC, network and server load during working hours. For each PC a count of consecutive good pings is maintained. Once a PC has at least $Conf{BlackoutGoodCnt} consecutive good pings it is subject to "blackout" and not backed up during hours and days specified by $Conf{BlackoutPeriods}. To allow for periodic rebooting of a PC or other brief periods when a PC is not on the network, a number of consecutive bad pings is allowed before the good ping count is reset. This parameter is $Conf{BlackoutBadPingLimit}. Note that bad and good pings don't occur with the same interval. If a machine is always on the network, it will only be pinged roughly once every $Conf{IncrPeriod} (eg: once per day). So a setting for $Conf{BlackoutGoodCnt} of 7 means it will take around 7 days for a machine to be subject to blackout. On the other hand, if a ping is failed, it will be retried roughly every time BackupPC wakes up, eg, every one or two hours. So a setting for $Conf{BlackoutBadPingLimit} of 3 means that the PC will lose its blackout status after 3-6 hours of unavailability. To disable the blackout feature set $Conf{BlackoutGoodCnt} to a negative value. A value of 0 will make all machines subject to blackout. But if you don't want to do any backups during the day it would be easier to just set $Conf{WakeupSchedule} to a restricted schedule. =item $Conf{BlackoutPeriods} = [ ... ]; One or more blackout periods can be specified. If a client is subject to blackout then no regular (non-manual) backups will be started during any of these periods. hourBegin and hourEnd specify hours fro midnight and weekDays is a list of days of the week where 0 is Sunday, 1 is Monday etc. For example: $Conf{BlackoutPeriods} = [ { hourBegin => 7.0, hourEnd => 19.5, weekDays => [1, 2, 3, 4, 5], }, ]; specifies one blackout period from 7:00am to 7:30pm local time on Mon-Fri. The blackout period can also span midnight by setting hourBegin > hourEnd, eg: $Conf{BlackoutPeriods} = [ { hourBegin => 7.0, hourEnd => 19.5, weekDays => [1, 2, 3, 4, 5], }, { hourBegin => 23, hourEnd => 5, weekDays => [5, 6], }, ]; This specifies one blackout period from 7:00am to 7:30pm local time on Mon-Fri, and a second period from 11pm to 5am on Friday and Saturday night. =item $Conf{BackupZeroFilesIsFatal} = 1; A backup of a share that has zero files is considered fatal. This is used to catch miscellaneous Xfer errors that result in no files being backed up. If you have shares that might be empty (and therefore an empty backup is valid) you should set this flag to 0. =back =head2 How to backup a client =over 4 =item $Conf{XferMethod} = 'smb'; What transport method to use to backup each host. If you have a mixed set of WinXX and linux/unix hosts you will need to override this in the per-PC config.pl. The valid values are: - 'smb': backup and restore via smbclient and the SMB protocol. Easiest choice for WinXX. - 'rsync': backup and restore via rsync (via rsh or ssh). Best choice for linux/unix. Good choice also for WinXX. - 'rsyncd': backup and restore via rsync daemon on the client. Best choice for linux/unix if you have rsyncd running on the client. Good choice also for WinXX. - 'tar': backup and restore via tar, tar over ssh, rsh or nfs. Good choice for linux/unix. - 'archive': host is a special archive host. Backups are not done. An archive host is used to archive other host's backups to permanent media, such as tape, CDR or DVD. =item $Conf{XferLogLevel} = 1; Level of verbosity in Xfer log files. 0 means be quiet, 1 will give will give one line per file, 2 will also show skipped files on incrementals, higher values give more output. =item $Conf{ClientCharset} = ''; Filename charset encoding on the client. BackupPC uses utf8 on the server for filename encoding. If this is empty, then utf8 is assumed and client filenames will not be modified. If set to a different encoding then filenames will converted to/from utf8 automatically during backup and restore. If the file names displayed in the browser (eg: accents or special characters) don't look right then it is likely you haven't set $Conf{ClientCharset} correctly. If you are using smbclient on a WinXX machine, smbclient will convert to the "unix charset" setting in smb.conf. The default is utf8, in which case leave $Conf{ClientCharset} empty since smbclient does the right conversion. If you are using rsync on a WinXX machine then it does no conversion. A typical WinXX encoding for latin1/western europe is 'cp1252', so in this case set $Conf{ClientCharset} to 'cp1252'. On a linux or unix client, run "locale charmap" to see the client's charset. Set $Conf{ClientCharset} to this value. A typical value for english/US is 'ISO-8859-1'. Do "perldoc Encode::Supported" to see the list of possible charset values. The FAQ at http://www.cl.cam.ac.uk/~mgk25/unicode.html is excellent, and http://czyborra.com/charsets/iso8859.html provides more information on the iso-8859 charsets. =item $Conf{ClientCharsetLegacy} = 'iso-8859-1'; Prior to 3.x no charset conversion was done by BackupPC. Backups were stored in what ever charset the XferMethod provided - typically utf8 for smbclient and the client's locale settings for rsync and tar (eg: cp1252 for rsync on WinXX and perhaps iso-8859-1 with rsync on linux). This setting tells BackupPC the charset that was used to store file names in old backups taken with BackupPC 2.x, so that non-ascii file names in old backups can be viewed and restored. =back =head2 Samba Configuration =over 4 =item $Conf{SmbShareName} = 'C$'; Name of the host share that is backed up when using SMB. This can be a string or an array of strings if there are multiple shares per host. Examples: $Conf{SmbShareName} = 'c'; # backup 'c' share $Conf{SmbShareName} = ['c', 'd']; # backup 'c' and 'd' shares This setting only matters if $Conf{XferMethod} = 'smb'. =item $Conf{SmbShareUserName} = ''; Smbclient share user name. This is passed to smbclient's -U argument. This setting only matters if $Conf{XferMethod} = 'smb'. =item $Conf{SmbSharePasswd} = ''; Smbclient share password. This is passed to smbclient via its PASSWD environment variable. There are several ways you can tell BackupPC the smb share password. In each case you should be very careful about security. If you put the password here, make sure that this file is not readable by regular users! See the "Setting up config.pl" section in the documentation for more information. This setting only matters if $Conf{XferMethod} = 'smb'. =item $Conf{SmbClientPath} = ''; Full path for smbclient. Security caution: normal users should not allowed to write to this file or directory. smbclient is from the Samba distribution. smbclient is used to actually extract the incremental or full dump of the share filesystem from the PC. This setting only matters if $Conf{XferMethod} = 'smb'. =item $Conf{SmbClientFullCmd} = '$smbClientPath \\\\$host\\$shareName' ... Command to run smbclient for a full dump. This setting only matters if $Conf{XferMethod} = 'smb'. The following variables are substituted at run-time: $smbClientPath same as $Conf{SmbClientPath} $host host to backup/restore $hostIP host IP address $shareName share name $userName user name $fileList list of files to backup (based on exclude/include) $I_option optional -I option to smbclient $X_option exclude option (if $fileList is an exclude list) $timeStampFile start time for incremental dump Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{SmbClientIncrCmd} = '$smbClientPath \\\\$host\\$shareName' ... Command to run smbclient for an incremental dump. This setting only matters if $Conf{XferMethod} = 'smb'. Same variable substitutions are applied as $Conf{SmbClientFullCmd}. Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{SmbClientRestoreCmd} = '$smbClientPath \\\\$host\\$shareName' ... Command to run smbclient for a restore. This setting only matters if $Conf{XferMethod} = 'smb'. Same variable substitutions are applied as $Conf{SmbClientFullCmd}. If your smb share is read-only then direct restores will fail. You should set $Conf{SmbClientRestoreCmd} to undef and the corresponding CGI restore option will be removed. Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =back =head2 Tar Configuration =over 4 =item $Conf{TarShareName} = '/'; Which host directories to backup when using tar transport. This can be a string or an array of strings if there are multiple directories to backup per host. Examples: $Conf{TarShareName} = '/'; # backup everything $Conf{TarShareName} = '/home'; # only backup /home $Conf{TarShareName} = ['/home', '/src']; # backup /home and /src The fact this parameter is called 'TarShareName' is for historical consistency with the Smb transport options. You can use any valid directory on the client: there is no need for it to correspond to any Smb share or device mount point. Note also that you can also use $Conf{BackupFilesOnly} to specify a specific list of directories to backup. It's more efficient to use this option instead of $Conf{TarShareName} since a new tar is run for each entry in $Conf{TarShareName}. On the other hand, if you add --one-file-system to $Conf{TarClientCmd} you can backup each file system separately, which makes restoring one bad file system easier. In this case you would list all of the mount points here, since you can't get the same result with $Conf{BackupFilesOnly}: $Conf{TarShareName} = ['/', '/var', '/data', '/boot']; This setting only matters if $Conf{XferMethod} = 'tar'. =item $Conf{TarClientCmd} = '$sshPath -q -x -n -l root $host' ... Command to run tar on the client. GNU tar is required. You will need to fill in the correct paths for ssh2 on the local host (server) and GNU tar on the client. Security caution: normal users should not allowed to write to these executable files or directories. $Conf{TarClientCmd} is appended with with either $Conf{TarFullArgs} or $Conf{TarIncrArgs} to create the final command that is run. See the documentation for more information about setting up ssh2 keys. If you plan to use NFS then tar just runs locally and ssh2 is not needed. For example, assuming the client filesystem is mounted below /mnt/hostName, you could use something like: $Conf{TarClientCmd} = '$tarPath -c -v -f - -C /mnt/$host/$shareName' . ' --totals'; In the case of NFS or rsh you need to make sure BackupPC's privileges are sufficient to read all the files you want to backup. Also, you will probably want to add "/proc" to $Conf{BackupFilesExclude}. The following variables are substituted at run-time: $host host name $hostIP host's IP address $incrDate newer-than date for incremental backups $shareName share name to backup (ie: top-level directory path) $fileList specific files to backup or exclude $tarPath same as $Conf{TarClientPath} $sshPath same as $Conf{SshPath} If a variable is followed by a "+" it is shell escaped. This is necessary for the command part of ssh or rsh, since it ends up getting passed through the shell. This setting only matters if $Conf{XferMethod} = 'tar'. Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{TarFullArgs} = '$fileList+'; Extra tar arguments for full backups. Several variables are substituted at run-time. See $Conf{TarClientCmd} for the list of variable substitutions. If you are running tar locally (ie: without rsh or ssh) then remove the "+" so that the argument is no longer shell escaped. This setting only matters if $Conf{XferMethod} = 'tar'. =item $Conf{TarIncrArgs} = '--newer=$incrDate+ $fileList+'; Extra tar arguments for incr backups. Several variables are substituted at run-time. See $Conf{TarClientCmd} for the list of variable substitutions. Note that GNU tar has several methods for specifying incremental backups, including: --newer-mtime $incrDate+ This causes a file to be included if the modification time is later than $incrDate (meaning its contents might have changed). But changes in the ownership or modes will not qualify the file to be included in an incremental. --newer=$incrDate+ This causes the file to be included if any attribute of the file is later than $incrDate, meaning either attributes or the modification time. This is the default method. Do not use --atime-preserve in $Conf{TarClientCmd} above, otherwise resetting the atime (access time) counts as an attribute change, meaning the file will always be included in each new incremental dump. If you are running tar locally (ie: without rsh or ssh) then remove the "+" so that the argument is no longer shell escaped. This setting only matters if $Conf{XferMethod} = 'tar'. =item $Conf{TarClientRestoreCmd} = '$sshPath -q -x -l root $host' ... Full command to run tar for restore on the client. GNU tar is required. This can be the same as $Conf{TarClientCmd}, with tar's -c replaced by -x and ssh's -n removed. See $Conf{TarClientCmd} for full details. This setting only matters if $Conf{XferMethod} = "tar". If you want to disable direct restores using tar, you should set $Conf{TarClientRestoreCmd} to undef and the corresponding CGI restore option will be removed. Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{TarClientPath} = ''; Full path for tar on the client. Security caution: normal users should not allowed to write to this file or directory. This setting only matters if $Conf{XferMethod} = 'tar'. =back =head2 Rsync/Rsyncd Configuration =over 4 =item $Conf{RsyncClientPath} = ''; Path to rsync executable on the client =item $Conf{RsyncClientCmd} = '$sshPath -q -x -l root $host $rsyncPath $argList+'; Full command to run rsync on the client machine. The following variables are substituted at run-time: $host host name being backed up $hostIP host's IP address $shareName share name to backup (ie: top-level directory path) $rsyncPath same as $Conf{RsyncClientPath} $sshPath same as $Conf{SshPath} $argList argument list, built from $Conf{RsyncArgs}, $shareName, $Conf{BackupFilesExclude} and $Conf{BackupFilesOnly} This setting only matters if $Conf{XferMethod} = 'rsync'. =item $Conf{RsyncClientRestoreCmd} = '$sshPath -q -x -l root $host $rsyncPath $argList+'; Full command to run rsync for restore on the client. The following variables are substituted at run-time: $host host name being backed up $hostIP host's IP address $shareName share name to backup (ie: top-level directory path) $rsyncPath same as $Conf{RsyncClientPath} $sshPath same as $Conf{SshPath} $argList argument list, built from $Conf{RsyncArgs}, $shareName, $Conf{BackupFilesExclude} and $Conf{BackupFilesOnly} This setting only matters if $Conf{XferMethod} = 'rsync'. Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{RsyncShareName} = '/'; Share name to backup. For $Conf{XferMethod} = "rsync" this should be a file system path, eg '/' or '/home'. For $Conf{XferMethod} = "rsyncd" this should be the name of the module to backup (ie: the name from /etc/rsynd.conf). This can also be a list of multiple file system paths or modules. For example, by adding --one-file-system to $Conf{RsyncArgs} you can backup each file system separately, which makes restoring one bad file system easier. In this case you would list all of the mount points: $Conf{RsyncShareName} = ['/', '/var', '/data', '/boot']; =item $Conf{RsyncdClientPort} = 873; Rsync daemon port on the client, for $Conf{XferMethod} = "rsyncd". =item $Conf{RsyncdUserName} = ''; Rsync daemon user name on client, for $Conf{XferMethod} = "rsyncd". The user name and password are stored on the client in whatever file the "secrets file" parameter in rsyncd.conf points to (eg: /etc/rsyncd.secrets). =item $Conf{RsyncdPasswd} = ''; Rsync daemon user name on client, for $Conf{XferMethod} = "rsyncd". The user name and password are stored on the client in whatever file the "secrets file" parameter in rsyncd.conf points to (eg: /etc/rsyncd.secrets). =item $Conf{RsyncdAuthRequired} = 1; Whether authentication is mandatory when connecting to the client's rsyncd. By default this is on, ensuring that BackupPC will refuse to connect to an rsyncd on the client that is not password protected. Turn off at your own risk. =item $Conf{RsyncCsumCacheVerifyProb} = 0.01; When rsync checksum caching is enabled (by adding the --checksum-seed=32761 option to $Conf{RsyncArgs}), the cached checksums can be occasionally verified to make sure the file contents matches the cached checksums. This is to avoid the risk that disk problems might cause the pool file contents to get corrupted, but the cached checksums would make BackupPC think that the file still matches the client. This setting is the probability (0 means never and 1 means always) that a file will be rechecked. Setting it to 0 means the checksums will not be rechecked (unless there is a phase 0 failure). Setting it to 1 (ie: 100%) means all files will be checked, but that is not a desirable setting since you are better off simply turning caching off (ie: remove the --checksum-seed option). The default of 0.01 means 1% (on average) of the files during a full backup will have their cached checksum re-checked. This setting has no effect unless checksum caching is turned on. =item $Conf{RsyncArgs} = [ ... ]; Arguments to rsync for backup. Do not edit the first set unless you have a thorough understanding of how File::RsyncP works. =item $Conf{RsyncArgsExtra} = []; Additional arguments added to RsyncArgs. This can be used in conbination with $Conf{RsyncArgs} to allow customization of the rsync arguments on a part-client basis. The standard arguments go in $Conf{RsyncArgs} and $Conf{RsyncArgsExtra} can be set on a per-client basis. Examples of additional arguments that should work are --exclude/--include, eg: $Conf{RsyncArgsExtra} = [ '--exclude', '/proc', '--exclude', '*.tmp', ]; Both $Conf{RsyncArgs} and $Conf{RsyncArgsExtra} are subject to the following variable substitutions: $client client name being backed up $host host name (could be different from client name if $Conf{ClientNameAlias} is set) $hostIP IP address of host $confDir configuration directory path This allows settings of the form: $Conf{RsyncArgsExtra} = [ '--exclude-from=$confDir/pc/$host.exclude', ]; =item $Conf{RsyncRestoreArgs} = [ ... ]; Arguments to rsync for restore. Do not edit the first set unless you have a thorough understanding of how File::RsyncP works. If you want to disable direct restores using rsync (eg: is the module is read-only), you should set $Conf{RsyncRestoreArgs} to undef and the corresponding CGI restore option will be removed. $Conf{RsyncRestoreArgs} is subject to the following variable substitutions: $client client name being backed up $host host name (could be different from client name if $Conf{ClientNameAlias} is set) $hostIP IP address of host $confDir configuration directory path Note: $Conf{RsyncArgsExtra} doesn't apply to $Conf{RsyncRestoreArgs}. =back =head2 FTP Configuration =over 4 =item $Conf{FtpShareName} = ''; Which host directories to backup when using FTP. This can be a string or an array of strings if there are multiple shares per host. This value must be specified in one of two ways: either as a subdirectory of the 'share root' on the server, or as the absolute path of the directory. In the following example, if the directory /home/username is the root share of the ftp server with the given username, the following two values will back up the same directory: $Conf{FtpShareName} = 'www'; # www directory $Conf{FtpShareName} = '/home/username/www'; # same directory Path resolution is not supported; i.e.; you may not have an ftp share path defined as '../otheruser' or '~/games'. Multiple shares may also be specified, as with other protocols: $Conf{FtpShareName} = [ 'www', 'bin', 'config' ]; Note also that you can also use $Conf{BackupFilesOnly} to specify a specific list of directories to backup. It's more efficient to use this option instead of $Conf{FtpShareName} since a new tar is run for each entry in $Conf{FtpShareName}. This setting only matters if $Conf{XferMethod} = 'ftp'. =item $Conf{FtpUserName} = ''; FTP user name. This is used to log into the server. This setting is used only if $Conf{XferMethod} = 'ftp'. =item $Conf{FtpPasswd} = ''; FTP user password. This is used to log into the server. This setting is used only if $Conf{XferMethod} = 'ftp'. =item $Conf{FtpPassive} = 1; Whether passive mode is used. The correct setting depends upon whether local or remote ports are accessible from the other machine, which is affected by any firewall or routers between the FTP server on the client and the BackupPC server. This setting is used only if $Conf{XferMethod} = 'ftp'. =item $Conf{FtpBlockSize} = 10240; Transfer block size. This sets the size of the amounts of data in each frame. While undefined, this value takes the default value. This setting is used only if $Conf{XferMethod} = 'ftp'. =item $Conf{FtpPort} = 21; The port of the ftp server. If undefined, 21 is used. This setting is used only if $Conf{XferMethod} = 'ftp'. =item $Conf{FtpTimeout} = 120; Connection timeout for FTP. When undefined, the default is 120 seconds. This setting is used only if $Conf{XferMethod} = 'ftp'. =item $Conf{FtpFollowSymlinks} = 0; Behaviour when BackupPC encounters symlinks on the FTP share. Symlinks cannot be restored via FTP, so the desired behaviour will be different depending on the setup of the share. The default for this behavor is 1. Directory shares with more complicated directory structures should consider other protocols. =back =head2 Archive Configuration =over 4 =item $Conf{ArchiveDest} = '/tmp'; Archive Destination The Destination of the archive e.g. /tmp for file archive or /dev/nst0 for device archive =item $Conf{ArchiveComp} = 'gzip'; Archive Compression type The valid values are: - 'none': No Compression - 'gzip': Medium Compression. Recommended. - 'bzip2': High Compression but takes longer. =item $Conf{ArchivePar} = 0; Archive Parity Files The amount of Parity data to generate, as a percentage of the archive size. Uses the commandline par2 (par2cmdline) available from http://parchive.sourceforge.net Only useful for file dumps. Set to 0 to disable this feature. =item $Conf{ArchiveSplit} = 0; Archive Size Split Only for file archives. Splits the output into the specified size * 1,000,000. e.g. to split into 650,000,000 bytes, specify 650 below. If the value is 0, or if $Conf{ArchiveDest} is an existing file or device (e.g. a streaming tape drive), this feature is disabled. =item $Conf{ArchiveClientCmd} = '$Installdir/bin/BackupPC_archiveHost' ... Archive Command This is the command that is called to actually run the archive process for each host. The following variables are substituted at run-time: $Installdir The installation directory of BackupPC $tarCreatePath The path to BackupPC_tarCreate $splitpath The path to the split program $parpath The path to the par2 program $host The host to archive $backupnumber The backup number of the host to archive $compression The path to the compression program $compext The extension assigned to the compression type $splitsize The number of bytes to split archives into $archiveloc The location to put the archive $parfile The amount of parity data to create (percentage) Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{SshPath} = ''; Full path for ssh. Security caution: normal users should not allowed to write to this file or directory. =item $Conf{NmbLookupPath} = ''; Full path for nmblookup. Security caution: normal users should not allowed to write to this file or directory. nmblookup is from the Samba distribution. nmblookup is used to get the netbios name, necessary for DHCP hosts. =item $Conf{NmbLookupCmd} = '$nmbLookupPath -A $host'; NmbLookup command. Given an IP address, does an nmblookup on that IP address. The following variables are substituted at run-time: $nmbLookupPath path to nmblookup ($Conf{NmbLookupPath}) $host IP address This command is only used for DHCP hosts: given an IP address, this command should try to find its NetBios name. Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{NmbLookupFindHostCmd} = '$nmbLookupPath $host'; NmbLookup command. Given a netbios name, finds that host by doing a NetBios lookup. Several variables are substituted at run-time: $nmbLookupPath path to nmblookup ($Conf{NmbLookupPath}) $host NetBios name In some cases you might need to change the broadcast address, for example if nmblookup uses 192.168.255.255 by default and you find that doesn't work, try 192.168.1.255 (or your equivalent class C address) using the -B option: $Conf{NmbLookupFindHostCmd} = '$nmbLookupPath -B 192.168.1.255 $host'; If you use a WINS server and your machines don't respond to multicast NetBios requests you can use this (replace 1.2.3.4 with the IP address of your WINS server): $Conf{NmbLookupFindHostCmd} = '$nmbLookupPath -R -U 1.2.3.4 $host'; This is preferred over multicast since it minimizes network traffic. Experiment manually for your site to see what form of nmblookup command works. Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{FixedIPNetBiosNameCheck} = 0; For fixed IP address hosts, BackupPC_dump can also verify the netbios name to ensure it matches the host name. An error is generated if they do not match. Typically this flag is off. But if you are going to transition a bunch of machines from fixed host addresses to DHCP, setting this flag is a great way to verify that the machines have their netbios name set correctly before turning on DCHP. =item $Conf{PingPath} = ''; Full path to the ping command. Security caution: normal users should not be allowed to write to this file or directory. If you want to disable ping checking, set this to some program that exits with 0 status, eg: $Conf{PingPath} = '/bin/echo'; =item $Conf{PingCmd} = '$pingPath -c 1 $host'; Ping command. The following variables are substituted at run-time: $pingPath path to ping ($Conf{PingPath}) $host host name Wade Brown reports that on solaris 2.6 and 2.7 ping -s returns the wrong exit status (0 even on failure). Replace with "ping $host 1", which gets the correct exit status but we don't get the round-trip time. Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{PingMaxMsec} = 20; Maximum round-trip ping time in milliseconds. This threshold is set to avoid backing up PCs that are remotely connected through WAN or dialup connections. The output from ping -s (assuming it is supported on your system) is used to check the round-trip packet time. On your local LAN round-trip times should be much less than 20msec. On most WAN or dialup connections the round-trip time will be typically more than 20msec. Tune if necessary. =item $Conf{CompressLevel} = 0; Compression level to use on files. 0 means no compression. Compression levels can be from 1 (least cpu time, slightly worse compression) to 9 (most cpu time, slightly better compression). The recommended value is 3. Changing to 5, for example, will take maybe 20% more cpu time and will get another 2-3% additional compression. See the zlib documentation for more information about compression levels. Changing compression on or off after backups have already been done will require both compressed and uncompressed pool files to be stored. This will increase the pool storage requirements, at least until all the old backups expire and are deleted. It is ok to change the compression value (from one non-zero value to another non-zero value) after dumps are already done. Since BackupPC matches pool files by comparing the uncompressed versions, it will still correctly match new incoming files against existing pool files. The new compression level will take effect only for new files that are newly compressed and added to the pool. If compression was off and you are enabling compression for the first time you can use the BackupPC_compressPool utility to compress the pool. This avoids having the pool grow to accommodate both compressed and uncompressed backups. See the documentation for more information. Note: compression needs the Compress::Zlib perl library. If the Compress::Zlib library can't be found then $Conf{CompressLevel} is forced to 0 (compression off). =item $Conf{ClientTimeout} = 72000; Timeout in seconds when listening for the transport program's (smbclient, tar etc) stdout. If no output is received during this time, then it is assumed that something has wedged during a backup, and the backup is terminated. Note that stdout buffering combined with huge files being backed up could cause longish delays in the output from smbclient that BackupPC_dump sees, so in rare cases you might want to increase this value. Despite the name, this parameter sets the timeout for all transport methods (tar, smb etc). =item $Conf{MaxOldPerPCLogFiles} = 12; Maximum number of log files we keep around in each PC's directory (ie: pc/$host). These files are aged monthly. A setting of 12 means there will be at most the files LOG, LOG.0, LOG.1, ... LOG.11 in the pc/$host directory (ie: about a years worth). (Except this month's LOG, these files will have a .z extension if compression is on). If you decrease this number after BackupPC has been running for a while you will have to manually remove the older log files. =item $Conf{DumpPreUserCmd} = undef; =item $Conf{DumpPostUserCmd} = undef; =item $Conf{DumpPreShareCmd} = undef; =item $Conf{DumpPostShareCmd} = undef; =item $Conf{RestorePreUserCmd} = undef; =item $Conf{RestorePostUserCmd} = undef; =item $Conf{ArchivePreUserCmd} = undef; =item $Conf{ArchivePostUserCmd} = undef; Optional commands to run before and after dumps and restores, and also before and after each share of a dump. Stdout from these commands will be written to the Xfer (or Restore) log file. One example of using these commands would be to shut down and restart a database server, dump a database to files for backup, or doing a snapshot of a share prior to a backup. Example: $Conf{DumpPreUserCmd} = '$sshPath -q -x -l root $host /usr/bin/dumpMysql'; The following variable substitutions are made at run time for $Conf{DumpPreUserCmd}, $Conf{DumpPostUserCmd}, $Conf{DumpPreShareCmd} and $Conf{DumpPostShareCmd}: $type type of dump (incr or full) $xferOK 1 if the dump succeeded, 0 if it didn't $client client name being backed up $host host name (could be different from client name if $Conf{ClientNameAlias} is set) $hostIP IP address of host $user user name from the hosts file $moreUsers list of additional users from the hosts file $share the first share name (or current share for $Conf{DumpPreShareCmd} and $Conf{DumpPostShareCmd}) $shares list of all the share names $XferMethod value of $Conf{XferMethod} (eg: tar, rsync, smb) $sshPath value of $Conf{SshPath}, $cmdType set to DumpPreUserCmd or DumpPostUserCmd The following variable substitutions are made at run time for $Conf{RestorePreUserCmd} and $Conf{RestorePostUserCmd}: $client client name being backed up $xferOK 1 if the restore succeeded, 0 if it didn't $host host name (could be different from client name if $Conf{ClientNameAlias} is set) $hostIP IP address of host $user user name from the hosts file $moreUsers list of additional users from the hosts file $share the first share name $XferMethod value of $Conf{XferMethod} (eg: tar, rsync, smb) $sshPath value of $Conf{SshPath}, $type set to "restore" $bkupSrcHost host name of the restore source $bkupSrcShare share name of the restore source $bkupSrcNum backup number of the restore source $pathHdrSrc common starting path of restore source $pathHdrDest common starting path of destination $fileList list of files being restored $cmdType set to RestorePreUserCmd or RestorePostUserCmd The following variable substitutions are made at run time for $Conf{ArchivePreUserCmd} and $Conf{ArchivePostUserCmd}: $client client name being backed up $xferOK 1 if the archive succeeded, 0 if it didn't $host Name of the archive host $user user name from the hosts file $share the first share name $XferMethod value of $Conf{XferMethod} (eg: tar, rsync, smb) $HostList list of hosts being archived $BackupList list of backup numbers for the hosts being archived $archiveloc location where the archive is sent to $parfile amount of parity data being generated (percentage) $compression compression program being used (eg: cat, gzip, bzip2) $compext extension used for compression type (eg: raw, gz, bz2) $splitsize size of the files that the archive creates $sshPath value of $Conf{SshPath}, $type set to "archive" $cmdType set to ArchivePreUserCmd or ArchivePostUserCmd Note: all Cmds are executed directly without a shell, so the prog name needs to be a full path and you can't include shell syntax like redirection and pipes; put that in a script if you need it. =item $Conf{UserCmdCheckStatus} = 0; Whether the exit status of each PreUserCmd and PostUserCmd is checked. If set and the Dump/Restore/Archive Pre/Post UserCmd returns a non-zero exit status then the dump/restore/archive is aborted. To maintain backward compatibility (where the exit status in early versions was always ignored), this flag defaults to 0. If this flag is set and the Dump/Restore/Archive PreUserCmd fails then the matching Dump/Restore/Archive PostUserCmd is not executed. If DumpPreShareCmd returns a non-exit status, then DumpPostShareCmd is not executed, but the DumpPostUserCmd is still run (since DumpPreUserCmd must have previously succeeded). An example of a DumpPreUserCmd that might fail is a script that snapshots or dumps a database which fails because of some database error. =item $Conf{ClientNameAlias} = undef; Override the client's host name. This allows multiple clients to all refer to the same physical host. This should only be set in the per-PC config file and is only used by BackupPC at the last moment prior to generating the command used to backup that machine (ie: the value of $Conf{ClientNameAlias} is invisible everywhere else in BackupPC). The setting can be a host name or IP address, eg: $Conf{ClientNameAlias} = 'realHostName'; $Conf{ClientNameAlias} = '192.1.1.15'; will cause the relevant smb/tar/rsync backup/restore commands to be directed to realHostName, not the client name. Note: this setting doesn't work for hosts with DHCP set to 1. =back =head2 Email reminders, status and messages =over 4 =item $Conf{SendmailPath} = ''; Full path to the sendmail command. Security caution: normal users should not allowed to write to this file or directory. =item $Conf{EMailNotifyMinDays} = 2.5; Minimum period between consecutive emails to a single user. This tries to keep annoying email to users to a reasonable level. Email checks are done nightly, so this number is effectively rounded up (ie: 2.5 means a user will never receive email more than once every 3 days). =item $Conf{EMailFromUserName} = ''; Name to use as the "from" name for email. Depending upon your mail handler this is either a plain name (eg: "admin") or a fully-qualified name (eg: "admin@mydomain.com"). =item $Conf{EMailAdminUserName} = ''; Destination address to an administrative user who will receive a nightly email with warnings and errors. If there are no warnings or errors then no email will be sent. Depending upon your mail handler this is either a plain name (eg: "admin") or a fully-qualified name (eg: "admin@mydomain.com"). =item $Conf{EMailUserDestDomain} = ''; Destination domain name for email sent to users. By default this is empty, meaning email is sent to plain, unqualified addresses. Otherwise, set it to the destintation domain, eg: $Cong{EMailUserDestDomain} = '@mydomain.com'; With this setting user email will be set to 'user@mydomain.com'. =item $Conf{EMailNoBackupEverSubj} = undef; =item $Conf{EMailNoBackupEverMesg} = undef; This subject and message is sent to a user if their PC has never been backed up. These values are language-dependent. The default versions can be found in the language file (eg: lib/BackupPC/Lang/en.pm). If you need to change the message, copy it here and edit it, eg: $Conf{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj Dear $userName, This is a site-specific email message. EOF =item $Conf{EMailNotifyOldBackupDays} = 7.0; How old the most recent backup has to be before notifying user. When there have been no backups in this number of days the user is sent an email. =item $Conf{EMailNoBackupRecentSubj} = undef; =item $Conf{EMailNoBackupRecentMesg} = undef; This subject and message is sent to a user if their PC has not recently been backed up (ie: more than $Conf{EMailNotifyOldBackupDays} days ago). These values are language-dependent. The default versions can be found in the language file (eg: lib/BackupPC/Lang/en.pm). If you need to change the message, copy it here and edit it, eg: $Conf{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj Dear $userName, This is a site-specific email message. EOF =item $Conf{EMailNotifyOldOutlookDays} = 5.0; How old the most recent backup of Outlook files has to be before notifying user. =item $Conf{EMailOutlookBackupSubj} = undef; =item $Conf{EMailOutlookBackupMesg} = undef; This subject and message is sent to a user if their Outlook files have not recently been backed up (ie: more than $Conf{EMailNotifyOldOutlookDays} days ago). These values are language-dependent. The default versions can be found in the language file (eg: lib/BackupPC/Lang/en.pm). If you need to change the message, copy it here and edit it, eg: $Conf{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj Dear $userName, This is a site-specific email message. EOF =item $Conf{EMailHeaders} = < administrative users are the union of group admin, plus craig and celia. $Conf{CgiAdminUserGroup} = ''; $Conf{CgiAdminUsers} = 'craig celia'; --> administrative users are only craig and celia'. =item $Conf{CgiURL} = undef; URL of the BackupPC_Admin CGI script. Used for email messages. =item $Conf{Language} = 'en'; Language to use. See lib/BackupPC/Lang for the list of supported languages, which include English (en), French (fr), Spanish (es), German (de), Italian (it), Dutch (nl), Polish (pl), Portuguese Brazillian (pt_br) and Chinese (zh_CH). Currently the Language setting applies to the CGI interface and email messages sent to users. Log files and other text are still in English. =item $Conf{CgiUserHomePageCheck} = ''; =item $Conf{CgiUserUrlCreate} = 'mailto:%s'; User names that are rendered by the CGI interface can be turned into links into their home page or other information about the user. To set this up you need to create two sprintf() strings, that each contain a single '%s' that will be replaced by the user name. The default is a mailto: link. $Conf{CgiUserHomePageCheck} should be an absolute file path that is used to check (via "-f") that the user has a valid home page. Set this to undef or an empty string to turn off this check. $Conf{CgiUserUrlCreate} should be a full URL that points to the user's home page. Set this to undef or an empty string to turn off generation of URLs for user names. Example: $Conf{CgiUserHomePageCheck} = '/var/www/html/users/%s.html'; $Conf{CgiUserUrlCreate} = 'http://myhost/users/%s.html'; --> if /var/www/html/users/craig.html exists, then 'craig' will be rendered as a link to http://myhost/users/craig.html. =item $Conf{CgiDateFormatMMDD} = 1; Date display format for CGI interface. A value of 1 uses US-style dates (MM/DD), a value of 2 uses full YYYY-MM-DD format, and zero for international dates (DD/MM). =item $Conf{CgiNavBarAdminAllHosts} = 1; If set, the complete list of hosts appears in the left navigation bar pull-down for administrators. Otherwise, just the hosts for which the user is listed in the host file (as either the user or in moreUsers) are displayed. =item $Conf{CgiSearchBoxEnable} = 1; Enable/disable the search box in the navigation bar. =item $Conf{CgiNavBarLinks} = [ ... ]; Additional navigation bar links. These appear for both regular users and administrators. This is a list of hashes giving the link (URL) and the text (name) for the link. Specifying lname instead of name uses the language specific string (ie: $Lang->{lname}) instead of just literally displaying name. =item $Conf{CgiStatusHilightColor} = { ... Hilight colors based on status that are used in the PC summary page. =item $Conf{CgiHeaders} = ''; Additional CGI header text. =item $Conf{CgiImageDir} = ''; Directory where images are stored. This directory should be below Apache's DocumentRoot. This value isn't used by BackupPC but is used by configure.pl when you upgrade BackupPC. Example: $Conf{CgiImageDir} = '/var/www/htdocs/BackupPC'; =item $Conf{CgiExt2ContentType} = { }; Additional mappings of file name extenions to Content-Type for individual file restore. See $Ext2ContentType in BackupPC_Admin for the default setting. You can add additional settings here, or override any default settings. Example: $Conf{CgiExt2ContentType} = { 'pl' => 'text/plain', }; =item $Conf{CgiImageDirURL} = ''; URL (without the leading http://host) for BackupPC's image directory. The CGI script uses this value to serve up image files. Example: $Conf{CgiImageDirURL} = '/BackupPC'; =item $Conf{CgiCSSFile} = 'BackupPC_stnd.css'; CSS stylesheet "skin" for the CGI interface. It is stored in the $Conf{CgiImageDir} directory and accessed via the $Conf{CgiImageDirURL} URL. For BackupPC v3.x several color, layout and font changes were made. The previous v2.x version is available as BackupPC_stnd_orig.css, so if you prefer the old skin, change this to BackupPC_stnd_orig.css. =item $Conf{CgiUserConfigEditEnable} = 1; Whether the user is allowed to edit their per-PC config. =item $Conf{CgiUserConfigEdit} = { ... Which per-host config variables a non-admin user is allowed to edit. Admin users can edit all per-host config variables, even if disabled in this list. SECURITY WARNING: Do not let users edit any of the Cmd config variables! That's because a user could set a Cmd to a shell script of their choice and it will be run as the BackupPC user. That script could do all sorts of bad things. =back =head1 Version Numbers Starting with v1.4.0 BackupPC uses a X.Y.Z version numbering system, instead of X.0Y. The first digit is for major new releases, the middle digit is for significant feature releases and improvements (most of the releases have been in this category), and the last digit is for bug fixes. You should think of the old 1.00, 1.01, 1.02 and 1.03 as 1.0.0, 1.1.0, 1.2.0 and 1.3.0. Additionally, patches might be made available. A patched version number is of the form X.Y.ZplN (eg: 2.1.0pl2), where N is the patch level. =head1 Author Craig Barratt See L. =head1 Copyright Copyright (C) 2001-2015 Craig Barratt =head1 Credits Ryan Kucera contributed the directory navigation code and images for v1.5.0. He contributed the first skeleton of BackupPC_restore. He also added a significant revision to the CGI interface, including CSS tags, in v2.1.0, and designed the BackupPC logo. Xavier Nicollet, with additions from Guillaume Filion, added the internationalization (i18n) support to the CGI interface for v2.0.0. Xavier provided the French translation fr.pm, with additions from Guillaume. Guillaume Filion wrote BackupPC_zipCreate and added the CGI support for zip download, in addition to some CGI cleanup, for v1.5.0. Guillaume continues to support fr.pm updates for each new version. Josh Marshall implemented the Archive feature in v2.1.0. Ludovic Drolez supports the BackupPC Debian package. Javier Gonzalez provided the Spanish translation, es.pm for v2.0.0. Manfred Herrmann provided the German translation, de.pm for v2.0.0. Manfred continues to support de.pm updates for each new version, together with some help from Ralph Paßgang. Lorenzo Cappelletti provided the Italian translation, it.pm for v2.1.0. Giuseppe Iuculano and Vittorio Macchi updated it for 3.0.0. Lieven Bridts provided the Dutch translation, nl.pm, for v2.1.0, with some tweaks from Guus Houtzager, and updates for 3.0.0. Reginaldo Ferreira provided the Portuguese Brazillian translation pt_br.pm for v2.2.0. Rich Duzenbury provided the RSS feed option to the CGI interface. Jono Woodhouse from CapeSoft Software (www.capesoft.com) provided a new CSS skin for 3.0.0 with several layout improvements. Sean Cameron (also from CapeSoft) designed new and more compact file icons for 3.0.0. Youlin Feng provided the Chinese translation for 3.1.0. Karol 'Semper' Stelmaczonek provided the Polish translation for 3.1.0. Jeremy Tietsort provided the host summary table sorting feature for 3.1.0. Paul Mantz contributed the ftp Xfer method for 3.2.0. Petr Pokorny provided the Czech translation for 3.2.1. Rikiya Yamamoto provided the Japanese translation for 3.3.0. Yakim provided the Ukrainian translation for 3.3.0. Sergei Butakov provided the Russian translation for 3.3.0. Many people have reported bugs, made useful suggestions and helped with testing; see the ChangeLog and the mailing lists. Your name could appear here in the next version! =head1 License This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License in the LICENSE file along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. BackupPC-3.3.2/httpd/0000755000076500000240000000000013042250554013242 5ustar craigstaffBackupPC-3.3.2/httpd/src/0000755000076500000240000000000013042250554014031 5ustar craigstaffBackupPC-3.3.2/httpd/src/BackupPC.conf0000444000076500000240000000153013042250554016325 0ustar craigstaff# # DESCRIPTION # # This file controls access and configuration for the BackupPC CGI # interface. # # Distributed with BackupPC version 3.1.1, released 22 Dec 2008. # # This section tells apache which machines can access the interface. # You can change the allow line to allow access from your local # network, or comment out this region to allow access from all # machines. # order deny,allow deny from all allow from 127.0.0.1 # # You can change the authorization method to LDAP or another method # besides htaccess here if you are so inclined. # AuthType Basic AuthUserFile __CONFDIR__/BackupPC.users AuthName "BackupPC Community Edition Administrative Interface" require valid-user Alias __IMAGEDIRURL__ __IMAGEDIR__ ScriptAlias /BackupPC_Admin __CGIDIR__/BackupPC_Admin BackupPC-3.3.2/images/0000755000076500000240000000000013042250554013364 5ustar craigstaffBackupPC-3.3.2/images/0000000.gif0000444000076500000240000000006413042250554014650 0ustar craigstaffGIF89a €ÿÿÿ!ù,  „©Ëí£œ´Ú;BackupPC-3.3.2/images/0000011.gif0000444000076500000240000000006713042250554014655 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©ËíO˜”Æ‹³V;BackupPC-3.3.2/images/0001000.gif0000444000076500000240000000006113042250554014646 0ustar craigstaffGIF89a €ÿÿÿ, „©ËíW`SÒk³ª›;BackupPC-3.3.2/images/0010000.gif0000444000076500000240000000006413042250554014651 0ustar craigstaffGIF89a €ÿÿÿ!ù,  „©Ëí£œ´Ú;BackupPC-3.3.2/images/0010001.gif0000444000076500000240000000007613042250554014655 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—ÍàksUuD¡ç †âHŽ;BackupPC-3.3.2/images/0011000.gif0000444000076500000240000000010013042250554014641 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—ÍàksUuŒtgóq–GJ¥Q;BackupPC-3.3.2/images/0011001.gif0000444000076500000240000000010113042250554014643 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—ÍàksUuD¡×l€µ‰f‰;BackupPC-3.3.2/images/1000000.gif0000444000076500000240000000006713042250554014654 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©ËíO˜”Æ‹³V;BackupPC-3.3.2/images/1000100.gif0000444000076500000240000000006713042250554014655 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©ËíO˜”Æ‹³V;BackupPC-3.3.2/images/1001000.gif0000444000076500000240000000007013042250554014647 0ustar craigstaffGIF89a €ÿÿÿ, „©Ëãz3Ô«h¶;ÊÃeL˜Y;BackupPC-3.3.2/images/1001100.gif0000444000076500000240000000007013042250554014650 0ustar craigstaffGIF89a €ÿÿÿ, „©Ëãz3Ô«h¶;ÊÃeL˜Y;BackupPC-3.3.2/images/1010000.gif0000444000076500000240000000007113042250554014650 0ustar craigstaffGIF89a €ÿÿÿ, „§¸—Íà3¡Ú+§ª¹7þiÎEB扦;BackupPC-3.3.2/images/1010001.gif0000444000076500000240000000007113042250554014651 0ustar craigstaffGIF89a €ÿÿÿ, „§¸—Íà3¡Ú+§ª¹7þiÎEB扦;BackupPC-3.3.2/images/1011000.gif0000444000076500000240000000007313042250554014653 0ustar craigstaffGIF89a €ÿÿÿ, „§¸—Íà3¡Ú+§ª¹7þiÎEnPô¢£;BackupPC-3.3.2/images/1100000.gif0000444000076500000240000000010213042250554014643 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz'Tk0 ÑÒ|U¡Òu͉¦G;BackupPC-3.3.2/images/1100100.gif0000444000076500000240000000007713042250554014657 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz3T-U;u”eÍH–G;BackupPC-3.3.2/images/1100101.gif0000444000076500000240000000007713042250554014660 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz3T-U;u”eÍH–G;BackupPC-3.3.2/images/1100110.gif0000444000076500000240000000007713042250554014660 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz3T-U;u”eÍH–G;BackupPC-3.3.2/images/1100111.gif0000444000076500000240000000007713042250554014661 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz3T-U;u”eÍH–G;BackupPC-3.3.2/images/1101000.gif0000444000076500000240000000010513042250554014647 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz'Tk0 ÑÒ|U¡Òuˆ– '£ŠG;BackupPC-3.3.2/images/1101100.gif0000444000076500000240000000010013042250554014643 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz3T-U;u”e cMäy;BackupPC-3.3.2/images/1101101.gif0000444000076500000240000000010013042250554014644 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz3T-U;u”e cMäy;BackupPC-3.3.2/images/1101110.gif0000444000076500000240000000010013042250554014644 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz3T-U;u”e cMäy;BackupPC-3.3.2/images/1101111.gif0000444000076500000240000000010013042250554014645 0ustar craigstaffGIF89a €ÿÿÿ!ù, „©Ëãz3T-U;u”e cMäy;BackupPC-3.3.2/images/1110000.gif0000444000076500000240000000010313042250554014645 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§Ö.]ÏQ!2)ߥêÊ;BackupPC-3.3.2/images/1110001.gif0000444000076500000240000000010313042250554014646 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§Ö.]ÏQ!2)ߥêÊ;BackupPC-3.3.2/images/1110100.gif0000444000076500000240000000010313042250554014646 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§ªaÞ€‹¸]&„¦êZ;BackupPC-3.3.2/images/1110101.gif0000444000076500000240000000010313042250554014647 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§ªaÞ€‹¸]&„¦êZ;BackupPC-3.3.2/images/1110110.gif0000444000076500000240000000010313042250554014647 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§ªaÞ€‹¸]&„¦êZ;BackupPC-3.3.2/images/1110111.gif0000444000076500000240000000010313042250554014650 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§ªaÞ€‹¸]&„¦êZ;BackupPC-3.3.2/images/1111000.gif0000444000076500000240000000010613042250554014651 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§Ö.]ÏQ!2)ßgBQS¦’R;BackupPC-3.3.2/images/1111001.gif0000444000076500000240000000010613042250554014652 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§Ö.]ÏQ!2)ßgBQS¦’R;BackupPC-3.3.2/images/1111100.gif0000444000076500000240000000010513042250554014651 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§ªaÞ€‹¸]æEaª¡G;BackupPC-3.3.2/images/1111101.gif0000444000076500000240000000010513042250554014652 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§ªaÞ€‹¸]æEaª¡G;BackupPC-3.3.2/images/1111110.gif0000444000076500000240000000010513042250554014652 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§ªaÞ€‹¸]æEaª¡G;BackupPC-3.3.2/images/1111111.gif0000444000076500000240000000010513042250554014653 0ustar craigstaffGIF89a €ÿÿÿ!ù, „§¸—Íà3¡Ú+§ªaÞ€‹¸]æEaª¡G;BackupPC-3.3.2/images/favicon.ico0000444000076500000240000000137613042250554015512 0ustar craigstaff è(@ 000@@@PPP```ppp€€€   °°°ÀÀÀàààðððÊÍëtnës c @ &¬à&­À­À À À À À À   0 P€ à     À À À · À·0 À À +À`Ð ·0 ·0°̸¾ÿÿƒÀÿðÀÿÀø@ð@à@àð@à?ð@àÿð@àÿð@àÿð@àÿð@àÿð@àÿð@àÿà@àÿ€@àþ@àþÀàþÀàþÀàþÀàþÀàþÀààÀàÀàÀà0ÀðpÀøÀþÀÿàÀÿþÀBackupPC-3.3.2/images/icon-dir.png0000444000076500000240000000064413042250554015600 0ustar craigstaff‰PNG  IHDRóÿakIDATxœ¥’=KA†Ÿsµ±R‚°°! jia'X˜¿ ZXØ ~• !bi©‚‚JKÁtjú$wÉev,îòaÅawaç™w†þNŸ·þ˦TU±Öb­eaó% €r_qñxÌL+0 ÅÚ3R˦4µžÎÚ°ë<O!"ˆHC™ˆ°zTH_€ïª*' ÒȰ´óÖ¡özwcÌšˆ\yçùtVçÒiCž./»Ž†!µZ­‘ªÑnWö?’®µJ%žnnXÜzí»/> ±ÇÅö0ƘµP,ây^—=tF%ŸÇKœ!ÑÈ®ˆ@4d=¾ïãÖZÇ/T«ÕèG±Ø0ÒàÆRš€R©' \Ž|š€J¥òk@}du€ÉÛÃñ÷ºIêg«Zd­EUÙ8¸sçÙ!²êŒ1fëÙþgDä¾î顸xð(|²!ç[7{IEND®B`‚BackupPC-3.3.2/images/icon-file.png0000444000076500000240000000067113042250554015741 0ustar craigstaff‰PNG  IHDRóÿa€IDATxœ…“½ŽÚ@…¿ù±Ç Þ`óDB¬ÒТ¦ÙŽ·CZE Ôô)x)•e„=s·Hf4öí‘FÖhÆß=÷\›Ýn÷ãz½ÊÿÖår‘Ãáð³®ëO"B\QÖZ««ª¢( ´ÖŒÕ4 Ëåò9„ðºÙl^öûýïüüý#…˜Íf¬V«çívû½®ë§ü܈!”R(¥¥mÛB`>Ÿ/Œ1‡õzýø“ˆÞ{Bƒþ&“ ÓéçÎ9‹Åçóùüeà ë:¬µc’“ܙ֚®ë¨ª  n·[ê5o%&Þ÷=MÓжm 3hÛ–¾ïqÎa­MYxïéºï=eY2–°ÖR–%EQ`ŒAkFªµÆCß÷„Œ18ç(ŠbP=JD0Æà½rD'±ÿ"")Èûýþ/Œyű³ÐZ§*9(â8Çö 'çÆßÂ#¥)ÄâÅü]ÁßqÇãñÛétùðÇú§üŠ›7ßÏ¥2IEND®B`‚BackupPC-3.3.2/images/icon-hardlink.png0000444000076500000240000000130613042250554016612 0ustar craigstaff‰PNG  IHDRóÿasBIT|dˆ pHYs ð ðB¬4˜ tEXtSoftwareMacromedia Fireworks MX»‘*$I§Ó'»óÀZ‹1!Bˆ}B<ÏÃÃÄÄDR)Uœ››»lD ¬µt:Œ1ûü$ zzzp]×uI&“g777Ï…³Ðív­5J©H‰µ–jµÊàçpl~·Át`öÚcq3ÍF@àû~äµÛе–íímX{ #§`ütÚp¤Z>´š{Cô½Z…[ùú. RF»»K)ãAv^¯@³LJ}?ñÊíáòòe^[÷å$Z˜_àªøožA»@>Ÿÿ’¿Ÿ)Ã-t1, I”R,^_äîi}ýQ³©©)_—J¥|¹\¶ÖÚÿ~,€ü0«°¾ÿ‚zîÑVÛ¥AIEND®B`‚BackupPC-3.3.2/images/icon-symlink.png0000444000076500000240000000122313042250554016502 0ustar craigstaff‰PNG  IHDRóÿaZIDATxœ…“KH”QÇ÷ñÍïó…¨Y«®-DZ’¤l!-F ƒÂE«6îÜAû m«`(†Í®#DZ–.œãÌ83÷¶o•èÀY\ÿÇùß{I¥R³{{{î_ËåÜÜÜ܇d2Ùãœ#ì°´ÖZF£Q<ÏCJÉé* $‰!kíÛñññÉL&³Õ8?‹8UÖZš››šššz—L&»çÀ9‡µ!BˆBŠÅ"ÖZúûûãJ©¹±±±{ÀvÝsŽZ­†µöÄ~¾ïÆŒ1Äãñ«ÙlöF˜…¨T*h­PJÕ8çØßßgzá1‘ÎC •2åZG/.¼l~öû l5@©TªïÚ¸ŠsŽ|>ø°uå'=Ã*G\jkíÞ/•º·óòØ@±X¤Z­bŒAk]Ï¢££ƒÛ‰QÞ,å8º–§µÉP³”Lõ8­5‘H„H$‚R )%J)”RxžÇ@b€¶ëŽ®®sÆ :ššh÷£Ç”Rcð<ï„zX©…×Än:¢Ñ(e|©i‰˜c¡RB ¥DJÉÎÎß6Wiø¾O± ˜º¶¹’ÙÊùÊûë …¡‹ÝÝ]ŠelÙGMaµ ó¥eþû§lg¥foIà 0T—RÒ××ÇûS¬ÍîrxXeàü½{W&îÜ^^ žÈðµ5¾¼ÓD““´ÿºÌç÷Y66H§Ó_Ó¯ÒKõ[h 1†$J)f¦gÕŽÖÅKzqq1½¼¼ìœsÿýXë?Ö-°žÿÏûýýÙþvIEND®B`‚BackupPC-3.3.2/images/logo.gif0000444000076500000240000000256213042250554015016 0ustar craigstaffGIF89a—/Õ f™ÌÙæòÀÀÀ€€€@@@³ÌæðððŒ³Ù°°°``` 000   ÐÐÐPPPpŸÏìóùpppöùüààà¼Ó驯ây¦Òƒ¬ÖÆÙ얹ܠ¿ßÐßïãìöÿÿÿ!ù ,—/ÿ@pH,ȤrÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿GÁL.››DÀpn»‡Á@APªL`Ï~ûÉ  …† S{|Zƒ†”•P}žR •£„…Ni››Ÿ®L ¤†C†Ly««­¯¾F²” ‰ ¸J‹»¼¿ÍF¦Â†ÆÈašË«ÜàΔ ‚ЕÇ…GªØðñõÝáäV”. ‘$ÊvŠè’ǰ¡Ã=ì!Èg€ß¸ F!eCй+‚ÄmÝ6ð“`F£‘†:"ËD²¦Í› #ÚÃo_8ÿ(.‹À,$³~H“*]Ê´©Ó§P£JÀrIP"CMˆ±+•«C²f” X¯]΂ÈZ©¥!¯.`xoƒ?w0Ú£m µl ¡*£öx.‘О†&jCjp§È:9,Á< 2‘ÂY.ÃE²A†!öbSü÷m* ¢  ¥< ©«RÑAcj°6fJ³ Ä|À6Ál$zö˜Ô»Šeé]õ®UµêZÉ€ÇLˆFyÈ𲦠qÙ»Pù¬Ï‡ ¡¼ô• Q³ç´Æ\àÁ*x@BÖd…0êU€Ÿ,šeäZƒ¥TßqÆÕÿVGlâ—›` Z´` Ñ^)€Ÿ~ ´rxxãzoa˜€g~Öa%8 ãE|°ÉE$6r"W€6D‹ÆP"D´‡Ê”@@„‡ ðXˆø%P¡AÊ7¤!4 q‹AE ؇a³ÝW+"A刂”œ By&Œ ÅÅ¡¢:ê~^a@# ñÁ‰›¤ežGì)Ä@`›`ôQÒ)Å„eœŒ†'©Dð@Üp°Ê‰#V!¥{0ê(¨èxê( ¼ª(« z8ãR¦»h0i#`±+5ÈZI@°¦r'K|‰‚ˆlr°áÿí² H`ÓîQí¦…Œ¦g¸¶S™²¥âèA†RâÀ‹@®ªf«Õ¨êìuF‹AUñ0¯Šî9P ‹¦>p•–Ø¢Þ§‡h6_æ( ÀDh¼f!©‚À¥!(oGÝ%é&¬Yá­! ,`±->×ø †ží|‡K: ”U±CìŒ.ÏžÙØ²­{@‰„œÒÙLŃÄúÜo¶CòpšÕFÉ`GN@À¨4ºD4¬6B €M}ˆÛlA=Ä5k$AÁ‰Ü9†®fâPÎ(HZX¯-wÞÑ´LÁ&qÖݨ!ohÁ§Æè@¥bæÁŒ–ï6²¹¡‡ æq `¸BÈÃd’´N qCð6£f àüÇ 8ÀVèæ¼ó‚â}}1؆#\? ¿’Ù‡(8™‹¬ËtN…$º/à(Z_¦»€ àÄÁGûûqpÀ.F?$ð« @J]qÀ:ð | ¥Á Z° F" wÁzðƒ ¡§;BackupPC-3.3.2/init.d/0000755000076500000240000000000013042250554013304 5ustar craigstaffBackupPC-3.3.2/init.d/README0000444000076500000240000000636013042250554014167 0ustar craigstaffThis directory contains some scripts for auto-starting BackupPC on boot. The subdirectory src contains source versions of these scripts (before various absolute paths have been filled in). Here are instrcutions for different OSs. Please submit additions or improvements to this list! RedHat Linux: ============ When configure.pl is run, the script linux-backuppc is created. It should be copied to /etc/init.d/backuppc: cp linux-backuppc /etc/init.d/backuppc After copying it, you can test it by running these commands as root: /etc/init.d/backuppc start /etc/init.d/backuppc status /etc/init.d/backuppc stop You should then run the following commands as root: chkconfig --add backuppc chkconfig --level 345 backuppc on chkconfig --list backuppc This will auto-start backuppc at run levels 3, 4 and 5. Debian Linux: ============ When configure.pl is run, the script debian-backuppc is created. Copy the debian startup script: cp debian-backuppc /etc/init.d/backuppc Run the following command to install in rc.d: update-rc.d backuppc defaults Set the correct init.d rights: chmod 755 /etc/init.d/backuppc Usage: /etc/init.d/backuppc {start|stop|restart|reload} Suse Linux: ========== When configure.pl is run, the script suse-backuppc is created. Using Suse 9.0 "chkconfig --level" doesn't work, so you should run: chkconfig backuppc 345 chkconfig --list backuppc Gentoo Linux: ============ When configure.pl is run, the script gentoo-backuppc and the init conf files gentoo-backuppc.conf are created. They should be copied to the following locations: cp gentoo-backuppc /etc/init.d/backuppc cp gentoo-backuppc.conf /etc/conf.d/backuppc You can test it by running these commands as root: /etc/init.d/backuppc start /etc/init.d/backuppc status /etc/init.d/backuppc stop After copying these files, run the following as root to make BackupPC to start automatically at boot (at the default run level): rc-update add backuppc default FreeBSD: ======= When configure.pl is run, the script freebsd-backuppc is created. An alternative more compact script is freebsd-backuppc2, submitted by Dan Niles. Copy one of these scripts to /usr/local/etc/rc.d/backuppc and make execuatble. Add the following line to /etc/rc.conf to enable BackupPC: backuppc_enable=(bool): Set to "NO" by default. Set it to "YES" to enable BackupPC. Example: backuppc_enable="YES" The script accepts: start, stop, restart, reload, status Slackware: ========= When configure.pl is run, the script slackware-backuppc is created. Install it by running these commands as root: cp slackware-backuppc /etc/rc.d/rc.backuppc chmod 755 /etc/rc.d/rc.backuppc then use an editor to add /etc/rc.d/rc.backuppc to /etc/rc.d/rc.local Solaris: ======= When configure.pl is run the shell script solaris-backuppc is created. This should be copied to /etc/init.d and links made in /etc/rc3.d and /etc/rc0.d. cp solaris-backuppc /etc/init.d/backuppc ln -s /etc/init.d/backuppc /etc/rc3.d/S85backuppc ln -s /etc/init.d/backuppc /etc/rc0.d/K85backuppc This will auto-start backuppc at run level 3 and stop it at run level 0. (Can a Solaris user please tell me if these instructions are correct?) BackupPC-3.3.2/init.d/src/0000755000076500000240000000000013042250554014073 5ustar craigstaffBackupPC-3.3.2/init.d/src/debian-backuppc0000555000076500000240000000323113042250554017026 0ustar craigstaff#!/bin/sh ### BEGIN INIT INFO # Provides: backuppc # Required-Start: $syslog $network $remote_fs # Required-Stop: $syslog $network $remote_fs # Should-Start: $local_fs # Should-Stop: $local_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Launch backuppc server # Description: Launch backuppc server, a high-performance, # enterprise-grade system for backing up PCs. ### END INIT INFO # DESCRIPTION # # Startup init script for BackupPC on Debian. # # Distributed with BackupPC version 3.3.2, released 25 Jan 2017. # set -e # BINDIR=__INSTALLDIR__/bin DATADIR=__TOPDIR__ LOGDIR=__LOGDIR__ USER=__BACKUPPCUSER__ # NAME=backuppc DAEMON=BackupPC test -x $BINDIR/$DAEMON || exit 0 case "$1" in start) echo -n "Starting $NAME: " start-stop-daemon --start --pidfile $LOGDIR/BackupPC.pid \ -c $USER --exec $BINDIR/$DAEMON -- -d echo "ok." ;; stop) echo -n "Stopping $NAME: " start-stop-daemon --stop --pidfile $LOGDIR/BackupPC.pid -u $USER \ --oknodo --retry 30 -x /usr/bin/perl echo "ok." ;; restart) echo -n "Restarting $NAME: " start-stop-daemon --stop --pidfile $LOGDIR/BackupPC.pid -u $USER \ --oknodo --retry 30 -x /usr/bin/perl start-stop-daemon --start --pidfile $LOGDIR/BackupPC.pid \ -c $USER --exec $BINDIR/$DAEMON -- -d echo "ok." ;; reload|force-reload) echo "Reloading $NAME configuration files" start-stop-daemon --stop --pidfile $LOGDIR/BackupPC.pid \ --signal 1 -x /usr/bin/perl ;; *) echo "Usage: /etc/init.d/$NAME {start|stop|restart|reload}" exit 1 ;; esac exit 0 BackupPC-3.3.2/init.d/src/freebsd-backuppc0000444000076500000240000000253613042250554017222 0ustar craigstaff#!/bin/sh # PROVIDE: backuppc # REQUIRE: DAEMON # BEFORE: LOGIN # KEYWORD: shutdown # # Copy to /usr/local/etc/rc.d/backuppc and make execuatble # # Add the following line to /etc/rc.conf to enable BackupPC: # backuppc_enable=(bool): Set to "NO" by default. # Set it to "YES" to enable BackupPC. # # Example: # # backuppc_enable="YES" # # It accepts : start, stop, restart, reload, status # # Provided by : Gabriel Rossetti # . /etc/rc.subr name="backuppc" rcvar=`set_rcvar` start_cmd="backuppc_start" restart_cmd="backuppc_restart" stop_cmd="backuppc_stop" status_cmd="backuppc_status" reload_cmd="backuppc_reload" load_rc_config $name eval "${rcvar}=\${${rcvar}:-'NO'}" : ${backuppc_enable="NO"} #backuppc_enable=${backuppc_enable:-"NO"} backuppc_start() { su backuppc -c '__INSTALLDIR__/bin/BackupPC -d' echo "${name} started" } backuppc_restart() { backuppc_stop sleep 1 backuppc_start } backuppc_stop() { /usr/bin/pkill -f "__INSTALLDIR__/bin/BackupPC -d" echo "${name} stopped" } backuppc_status() { if [ "`ps ax | grep "BackupPC -d" | grep perl`" = "" ] ; then echo "${name} not running" else echo "${name} running" fi } backuppc_reload() { /usr/bin/pkill -1 -f "__INSTALLDIR__/bin/BackupPC -d" echo "${name} reloaded" } extra_commands="reload status" run_rc_command "$1" BackupPC-3.3.2/init.d/src/freebsd-backuppc20000444000076500000240000000076413042250554017305 0ustar craigstaff#!/bin/sh # PROVIDE: backuppc # REQUIRE: LOGIN # # Add the following line to /etc/rc.conf to enable BackupPC: # # backuppc_enable="YES" # # Submitted by Dan Niles . /etc/rc.subr name="backuppc" rcvar=`set_rcvar` load_rc_config $name : ${backuppc_enable:="NO"} : ${backuppc_program:="__INSTALLDIR__/bin/BackupPC"} : ${backuppc_flags:="-d"} : ${backuppc_user:="__BACKUPPCUSER__"} command_interpreter="/usr/bin/perl" command=$backuppc_program pidfile="__LOGDIR__/BackupPC.pid" run_rc_command "$1" BackupPC-3.3.2/init.d/src/gentoo-backuppc0000555000076500000240000000156113042250554017103 0ustar craigstaff#!/sbin/runscript # # DESCRIPTION # # Startup init script for BackupPC on Gentoo` linux. # # Distributed with BackupPC version 3.3.2, released 25 Jan 2017. # # description: Starts and stops the BackupPC server # Copy to /etc/init.d and run 'rc-update add backuppc default' # get our configuration options source /etc/conf.d/backuppc checkconfig() { if [ ! -f ${CONF_FILE} ] ; then eerror "No ${CONF_FILE} exists!" fi } start() { checkconfig || return 1 ebegin "Starting BackupPC" start-stop-daemon --start --chuid ${USER} --user ${USER} --pidfile ${PID_FILE} --exec ${EXEC} -- ${EXEC_OPTIONS} eend $? } stop() { ebegin "Stopping BackupPC" start-stop-daemon --stop --pidfile ${PID_FILE} --name BackupPC eend $? } restart() { ebegin "Restarting BackupPC" svc_stop sleep 1 svc_start eend $? "Failed to restart BackupPC" } status() { return eend $? } BackupPC-3.3.2/init.d/src/gentoo-backuppc.conf0000444000076500000240000000021013042250554020012 0ustar craigstaffCONF_FILE=__CONFDIR__/config.pl USER=__BACKUPPCUSER__ PID_FILE=__LOGDIR__/BackupPC.pid EXEC=__INSTALLDIR__/bin/BackupPC EXEC_OPTIONS=-d BackupPC-3.3.2/init.d/src/linux-backuppc0000555000076500000240000000332013042250554016742 0ustar craigstaff#!/bin/sh # # DESCRIPTION # # Startup init script for BackupPC on Redhat linux. # # Distributed with BackupPC version 3.3.2, released 25 Jan 2017. # # chkconfig: - 91 35 # description: Starts and stops the BackupPC server # Source function library. if [ -f /etc/init.d/functions ] ; then . /etc/init.d/functions elif [ -f /etc/rc.d/init.d/functions ] ; then . /etc/rc.d/init.d/functions else exit 0 fi RETVAL=0 start() { # # You can set the SMB share password here is you wish. Otherwise # you should put it in the config.pl script. # If you put it here make sure this file has no read permissions # for normal users! See the documentation for more information. # # Replace the daemon line below with this: # # daemon --user __BACKUPPCUSER__ /usr/bin/env BPC_SMB_PASSWD=xxxxx \ # __INSTALLDIR__/bin/BackupPC -d # echo -n "Starting BackupPC: " daemon --user __BACKUPPCUSER__ __INSTALLDIR__/bin/BackupPC -d RETVAL=$? echo [ $RETVAL -eq 0 ] && touch /var/lock/subsys/backuppc || \ RETVAL=1 return $RETVAL } stop() { echo -n "Shutting down BackupPC: " killproc __INSTALLDIR__/bin/BackupPC RETVAL=$? [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/backuppc echo "" return $RETVAL } restart() { stop start } reload() { echo -n "Reloading config.pl file: " killproc __INSTALLDIR__/bin/BackupPC -HUP RETVAL=$? echo return $RETVAL } rhstatus() { status __INSTALLDIR__/bin/BackupPC } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; status) rhstatus ;; *) echo "Usage: $0 {start|stop|restart|reload|status}" exit 1 esac exit $? BackupPC-3.3.2/init.d/src/slackware-backuppc0000555000076500000240000000162513042250554017565 0ustar craigstaff#!/bin/sh # # DESCRIPTION # # Startup init script for BackupPC for Slackware. # # Distributed with BackupPC version 3.3.2, released 25 Jan 2017. # # Provided by Tony Nelson. # start() { # # You can set the SMB share password here is you wish. Otherwise # you should put it in the config.pl script. # If you put it here make sure this file has no read permissions # for normal users! See the documentation for more information. # #BPC_SMB_PASSWD= #export BPC_SMB_PASSWD # su backuppc -c "__INSTALLDIR__/bin/BackupPC -d" } stop() { /usr/bin/pkill -f "__INSTALLDIR__/bin/BackupPC -d" } restart() { stop start } reload() { /usr/bin/pkill -1 -f "__INSTALLDIR__/bin/BackupPC -d" } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; *) echo "Usage: $0 {start|stop|restart|reload}" exit 1 esac exit $? BackupPC-3.3.2/init.d/src/solaris-backuppc0000555000076500000240000000162313042250554017263 0ustar craigstaff#!/bin/sh # # DESCRIPTION # # Startup init script for BackupPC for solaris. # # Distributed with BackupPC version 3.3.2, released 25 Jan 2017. # # Not tested... # start() { # # You can set the SMB share password here is you wish. Otherwise # you should put it in the config.pl script. # If you put it here make sure this file has no read permissions # for normal users! See the documentation for more information. # #BPC_SMB_PASSWD= #export BPC_SMB_PASSWD # su __BACKUPPCUSER__ -c "__INSTALLDIR__/bin/BackupPC -d" } stop() { /usr/bin/pkill -f "__INSTALLDIR__/bin/BackupPC -d" } restart() { stop start } reload() { /usr/bin/pkill -1 -f "__INSTALLDIR__/bin/BackupPC -d" } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; *) echo "Usage: $0 {start|stop|restart|reload}" exit 1 esac exit $? BackupPC-3.3.2/init.d/src/suse-backuppc0000555000076500000240000000217713042250554016573 0ustar craigstaff#!/bin/sh # # DESCRIPTION # # Startup init script for BackupPC on suse linux. # # Distributed with BackupPC version 3.3.2, released 25 Jan 2017. # set -e # BINDIR=__INSTALLDIR__/bin DATADIR=__TOPDIR__ LOGDIR=__LOGDIR__ USER=__BACKUPPCUSER__ # NAME=backuppc DAEMON=BackupPC test -x $BINDIR/$DAEMON || exit 0 case "$1" in start) echo -n "Starting $NAME: " startproc -f -p $LOGDIR/BackupPC.pid -u $USER $BINDIR/$DAEMON -d echo "ok." ;; stop) echo -n "Stopping $NAME: " start-stop-daemon --stop --pidfile $LOGDIR/BackupPC.pid -u $USER \ --oknodo echo "ok." ;; restart) echo -n "Stopping $NAME: " start-stop-daemon --stop --pidfile $LOGDIR/BackupPC.pid -u $USER \ --oknodo echo "ok." echo -n "Starting $NAME: " startproc -f -p $LOGDIR/BackupPC.pid -u $USER $BINDIR/$DAEMON -d echo "ok." ;; reload|force-reload) echo "Reloading $NAME configuration files" start-stop-daemon --stop --pidfile $LOGDIR/BackupPC.pid \ --signal 1 -x /usr/bin/perl ;; *) echo "Usage: /etc/init.d/$NAME {start|stop|restart|reload}" exit 1 ;; esac exit 0 BackupPC-3.3.2/lib/0000755000076500000240000000000013042250554012665 5ustar craigstaffBackupPC-3.3.2/lib/BackupPC/0000755000076500000240000000000013042250554014315 5ustar craigstaffBackupPC-3.3.2/lib/BackupPC/Attrib.pm0000444000076500000240000002113213042250554016075 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Attrib package # # DESCRIPTION # # This library defines a BackupPC::Attrib class for maintaining # file attribute data. One object instance stores attributes for # all the files in a single directory. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Attrib; use strict; use Carp; use File::Path; use BackupPC::FileZIO; use Encode qw/from_to/; require Exporter; use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS ); # # These must match the file types used by tar # use constant BPC_FTYPE_FILE => 0; use constant BPC_FTYPE_HARDLINK => 1; use constant BPC_FTYPE_SYMLINK => 2; use constant BPC_FTYPE_CHARDEV => 3; use constant BPC_FTYPE_BLOCKDEV => 4; use constant BPC_FTYPE_DIR => 5; use constant BPC_FTYPE_FIFO => 6; use constant BPC_FTYPE_SOCKET => 8; use constant BPC_FTYPE_UNKNOWN => 9; use constant BPC_FTYPE_DELETED => 10; my @FILE_TYPES = qw( BPC_FTYPE_FILE BPC_FTYPE_HARDLINK BPC_FTYPE_SYMLINK BPC_FTYPE_CHARDEV BPC_FTYPE_BLOCKDEV BPC_FTYPE_DIR BPC_FTYPE_FIFO BPC_FTYPE_SOCKET BPC_FTYPE_UNKNOWN BPC_FTYPE_DELETED ); # # The indexes in this list must match the numbers above # my @FileType2Text = ( "file", "hardlink", "symlink", "chardev", "blockdev", "dir", "fifo", "?", "socket", "?", "deleted", ); # # Type of attribute file. This is saved as a magic number at the # start of the file. Later there might be other types. # use constant BPC_ATTRIB_TYPE_UNIX => 0x17555555; my @ATTRIB_TYPES = qw( BPC_ATTRIB_TYPE_UNIX ); @ISA = qw(Exporter); @EXPORT = qw( ); @EXPORT_OK = ( @FILE_TYPES, @ATTRIB_TYPES, ); %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ], ); # # These fields are packed using the "w" pack format (variable length # base 128). We use two values to store up to 64 bit size: sizeDiv4GB # is size / 4GB and sizeMod4GB is size % 4GB (although perl can # only represent around 2^52, the size of an IEEE double mantissa). # my @FldsUnixW = qw(type mode uid gid sizeDiv4GB sizeMod4GB); # # These fields are packed using the "N" pack format (32 bit integer) # my @FldsUnixN = qw(mtime); sub new { my($class, $options) = @_; my $self = bless { type => BPC_ATTRIB_TYPE_UNIX, %$options, files => { }, }, $class; return $self; } sub set { my($a, $fileName, $attrib) = @_; if ( !defined($attrib) ) { delete($a->{files}{$fileName}); } else { $a->{files}{$fileName} = $attrib; } } sub get { my($a, $fileName) = @_; return $a->{files}{$fileName} if ( defined($fileName) ); return $a->{files}; } sub fileType2Text { my($a, $type) = @_; return "?" if ( $type < 0 || $type >= @FileType2Text ); return $FileType2Text[$type]; } sub fileCount { my($a) = @_; return scalar(keys(%{$a->{files}})); } sub delete { my($a, $fileName) = @_; if ( defined($fileName) ) { delete($a->{files}{$fileName}); } else { $a->{files} = { }; } } # # Given the directory, return the full path of the attribute file. # sub fileName { my($a, $dir, $file) = @_; $file = "attrib" if ( !defined($file) ); return "$dir/$file"; } sub read { my($a, $dir, $file) = @_; my($data); $file = $a->fileName($dir, $file); from_to($file, "utf8", $a->{charsetLegacy}) if ( $a->{charsetLegacy} ne "" ); my $fd = BackupPC::FileZIO->open($file, 0, $a->{compress}); if ( !$fd ) { $a->{_errStr} = "Can't open $file"; return; } $fd->read(\$data, 65536); if ( length($data) < 4 ) { $a->{_errStr} = "Can't read magic number from $file"; $fd->close; return; } (my $magic, $data) = unpack("N a*", $data); if ( $magic != $a->{type} ) { $a->{_errStr} = sprintf("Wrong magic number in %s" . " (got 0x%x, expected 0x%x)", $file, $magic, $a->{type}); $fd->close; return; } while ( length($data) ) { my $newData; if ( length($data) < 4 ) { $fd->read(\$newData, 65536); $data .= $newData; if ( length($data) < 4 ) { $a->{_errStr} = "Can't read file length from $file"; $fd->close; return; } } (my $len, $data) = unpack("w a*", $data); if ( length($data) < $len ) { $fd->read(\$newData, $len + 65536); $data .= $newData; if ( length($data) < $len ) { $a->{_errStr} = "Can't read file name (length $len)" . " from $file"; $fd->close; return; } } (my $fileName, $data) = unpack("a$len a*", $data); from_to($fileName, $a->{charsetLegacy}, "utf8") if ( $a->{charsetLegacy} ne "" ); my $nFldsW = @FldsUnixW; my $nFldsN = @FldsUnixN; if ( length($data) < 5 * $nFldsW + 4 * $nFldsN ) { $fd->read(\$newData, 65536); $data .= $newData; } eval { ( @{$a->{files}{$fileName}}{@FldsUnixW}, @{$a->{files}{$fileName}}{@FldsUnixN}, $data ) = unpack("w$nFldsW N$nFldsN a*", $data); }; if ( $@ ) { $a->{_errStr} = "unpack: Can't read attributes for $fileName from $file ($@)"; $fd->close; return; } if ( $a->{files}{$fileName}{$FldsUnixN[-1]} eq "" ) { $a->{_errStr} = "Can't read attributes for $fileName" . " from $file"; $fd->close; return; } # # Convert the two 32 bit size values into a single size # $a->{files}{$fileName}{size} = $a->{files}{$fileName}{sizeMod4GB} + $a->{files}{$fileName}{sizeDiv4GB} * 4096 * 1024 * 1024; } $fd->close; $a->{_errStr} = ""; return 1; } sub writeData { my($a) = @_; my($data); $data = pack("N", BPC_ATTRIB_TYPE_UNIX); foreach my $file ( sort(keys(%{$a->{files}})) ) { my $nFldsW = @FldsUnixW; my $nFldsN = @FldsUnixN; # # Convert the size into two 32 bit size values. # $a->{files}{$file}{sizeMod4GB} = $a->{files}{$file}{size} % (4096 * 1024 * 1024); $a->{files}{$file}{sizeDiv4GB} = int($a->{files}{$file}{size} / (4096 * 1024 * 1024)); eval { $data .= pack("w a* w$nFldsW N$nFldsN", length($file), $file, @{$a->{files}{$file}}{@FldsUnixW}, @{$a->{files}{$file}}{@FldsUnixN}, ); }; if ( $@ ) { $a->{_errStr} = "Can't pack attr for $file: " . Dumper($a->{files}{$file}); } } return $data; } sub write { my($a, $dir, $file) = @_; my($data) = $a->writeData; $file = $a->fileName($dir, $file); if ( !-d $dir ) { eval { mkpath($dir, 0, 0777) }; if ( $@ ) { $a->{_errStr} = "Can't create directory $dir"; return; } } my $fd = BackupPC::FileZIO->open($file, 1, $a->{compress}); if ( !$fd ) { $a->{_errStr} = "Can't open/write to $file"; return; } if ( $fd->write(\$data) != length($data) ) { $a->{_errStr} = "Can't write to $file"; $fd->close; return; } $fd->close; $a->{_errStr} = ""; return 1; } sub merge { my($a1, $a2) = @_; foreach my $f ( keys(%{$a2->{files}}) ) { next if ( defined($a1->{files}{$f}) ); $a1->{files}{$f} = $a2->{files}{$f}; } } sub errStr { my($a) = @_; return $a->{_errStr}; } 1; BackupPC-3.3.2/lib/BackupPC/CGI/0000755000076500000240000000000013042250554014717 5ustar craigstaffBackupPC-3.3.2/lib/BackupPC/CGI/AdminOptions.pm0000444000076500000240000000314513042250554017662 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::AdminOptions package # # DESCRIPTION # # This module implements the AdminOptions action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::AdminOptions; use strict; use BackupPC::CGI::Lib qw(:all); sub action { unless ( CheckPermission() ) { ErrorExit($Lang->{Only_privileged_users_can_view_admin_options}); } my $content = eval("qq{$Lang->{Admin_Options_Page}}"); Header(eval("qq{$Lang->{H_Admin_Options}}"), $content); Trailer(); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/Archive.pm0000444000076500000240000002172213042250554016640 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::Archive package # # DESCRIPTION # # This module implements the Archive action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::Archive; use strict; use BackupPC::CGI::Lib qw(:all); use Data::Dumper; sub action { my $archHost = $In{host}; my $Privileged = CheckPermission(); if ( !$Privileged ) { ErrorExit($Lang->{Only_privileged_users_can_archive} ); } if ( $In{type} == 0 ) { my($fullTot, $fullSizeTot, $incrTot, $incrSizeTot, $str, $strNone, $strGood, $hostCntGood, $hostCntNone, $checkBoxCnt, $backupnumber); $hostCntGood = $hostCntNone = $checkBoxCnt = $fullSizeTot = 0; GetStatusInfo("hosts"); foreach my $host ( sort(keys(%Status)) ) { my($fullDur, $incrCnt, $fullSize, $fullRate); my @Backups = $bpc->BackupInfoRead($host); my $fullCnt = $incrCnt = 0; for ( my $i = 0 ; $i < @Backups ; $i++ ) { if ( $Backups[$i]{type} eq "full" ) { $fullSize = $Backups[$i]{size} / (1024 * 1024); $incrSizeTot = 0; } else { $incrSizeTot = $Backups[$i]{size} / (1024 * 1024); } $backupnumber = $Backups[$i]{num}; } $fullSizeTot += $fullSize + $incrSizeTot; $fullSize = sprintf("%.2f", ($fullSize + $incrSizeTot) / 1000); $str = <  ${HostLink($host)} ${UserLink($Hosts->{$host}{user})} $fullSize EOF $checkBoxCnt++; if ( @Backups == 0 ) { $hostCntNone++; $strNone .= $str; } else { $hostCntGood++; $strGood .= $str; } } $fullSizeTot = sprintf("%.2f", $fullSizeTot / 1000); my $now = timeStamp2(time); my $checkAllHosts = $Lang->{checkAllHosts}; $strGood .= < EOF my $content = eval("qq{$Lang->{BackupPC_Archive}}"); Header(eval("qq{$Lang->{BackupPC__Archive}}"), $content, 1); Trailer(); } else { my(@HostList, @BackupList, $HostListStr, $hiddenStr, $pathHdr, $badFileCnt, $reply, $str); # # Pick up the archive host's config file # $bpc->ConfigRead($archHost); %Conf = $bpc->Conf(); my $args = { SplitPath => $Conf{SplitPath}, ParPath => $Conf{ParPath}, CatPath => $Conf{CatPath}, GzipPath => $Conf{GzipPath}, Bzip2Path => $Conf{Bzip2Path}, ArchiveDest => $Conf{ArchiveDest}, ArchiveComp => $Conf{ArchiveComp}, ArchivePar => $Conf{ArchivePar}, ArchiveSplit => $Conf{ArchiveSplit}, topDir => $bpc->{TopDir}, }; ServerConnect(); for ( my $i = 0 ; $i < $In{fcbMax} ; $i++ ) { next if ( !defined($In{"fcb$i"}) ); my $name = $In{"fcb$i"}; my $backupno = $In{"backup$i"}; push(@HostList, $name); push(@BackupList, $backupno); $hiddenStr .= < EOF $HostListStr .= < ${EscHTML($name)} EOF } $hiddenStr .= < EOF $hiddenStr .= "\n"; if ( @HostList == 0 ) { ErrorExit($Lang->{You_haven_t_selected_any_hosts}); } my ($ArchiveDest, $ArchiveCompNone, $ArchiveCompGzip, $ArchiveCompBzip2, $ArchivePar, $ArchiveSplit); $ArchiveDest = $Conf{ArchiveDest}; if ( $Conf{ArchiveComp} eq "none" ) { $ArchiveCompNone = "checked"; } else { $ArchiveCompNone = ""; } if ( $Conf{ArchiveComp} eq "gzip" ) { $ArchiveCompGzip = "checked"; } else { $ArchiveCompGzip = ""; } if ( $Conf{ArchiveComp} eq "bzip2" ) { $ArchiveCompBzip2 = "checked"; } else { $ArchiveCompBzip2 = ""; } $ArchivePar = $Conf{ArchivePar}; $ArchiveSplit = $Conf{ArchiveSplit}; if ( $In{type} == 1 ) { # # Tell the user what options they have # my $paramStr = ""; if ( $Conf{ArchiveClientCmd} =~ /\$archiveloc\b/ ) { $paramStr .= eval("qq{$Lang->{BackupPC_Archive2_location}}"); } if ( $Conf{ArchiveClientCmd} =~ /\$compression\b/ ) { $paramStr .= eval("qq{$Lang->{BackupPC_Archive2_compression}}"); } if ( $Conf{ArchiveClientCmd} =~ /\$parfile\b/ && -x $Conf{ParPath} ) { $paramStr .= eval("qq{$Lang->{BackupPC_Archive2_parity}}"); } if ( $Conf{ArchiveClientCmd} =~ /\$splitsize\b/ && -x $Conf{SplitPath} ) { $paramStr .= eval("qq{$Lang->{BackupPC_Archive2_split}}"); } my $content = eval("qq{$Lang->{BackupPC_Archive2}}"); Header(eval("qq{$Lang->{BackupPC__Archive}}"), $content, 1); Trailer(); } elsif ( $In{type} == 2 ) { my $reqFileName; my $archivehost = $1 if ( $In{archivehost} =~ /(.+)/ ); for ( my $i = 0 ; ; $i++ ) { $reqFileName = "archiveReq.$$.$i"; last if ( !-f "$TopDir/pc/$archivehost/$reqFileName" ); } my($compname, $compext); if ( $In{compression} == 2 ) { # bzip2 compression $compname = $Conf{Bzip2Path}; $compext = '.bz2'; } elsif ( $In{compression} == 1 ) { # gzip compression $compname = $Conf{GzipPath}; $compext = '.gz'; } else { # No Compression $compname = $Conf{CatPath}; $compext = '.raw'; } my $fullsplitsize = $In{splitsize} . '000000'; my %ArchiveReq = ( # parameters for the archive archiveloc => $In{archive_device}, archtype => $In{archive_type}, compression => $compname, compext => $compext, parfile => $In{par}, splitsize => $fullsplitsize, host => $archivehost, # list of hosts to restore HostList => \@HostList, BackupList => \@BackupList, # other info user => $User, reqTime => time, ); my($archive) = Data::Dumper->new( [ \%ArchiveReq], [qw(*ArchiveReq)]); $archive->Indent(1); my $openPath = "$TopDir/pc/$archivehost/$reqFileName"; if ( open(REQ, ">", $openPath) ) { binmode(REQ); print(REQ $archive->Dump); close(REQ); } else { ErrorExit(eval("qq{$Lang->{Can_t_open_create__openPath}}")); } $reply = $bpc->ServerMesg("archive $User $archivehost $reqFileName"); $str = eval("qq{$Lang->{Archive_requested}}"); my $content = eval("qq{$Lang->{BackupPC_Archive_Reply_from_server}}"); Header(eval("qq{$Lang->{BackupPC__Archive}}"), $content, 1); Trailer(); } } } 1; BackupPC-3.3.2/lib/BackupPC/CGI/ArchiveInfo.pm0000444000076500000240000000527113042250554017455 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::ArchiveInfo package # # DESCRIPTION # # This module implements the ArchiveInfo action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::ArchiveInfo; use strict; use BackupPC::CGI::Lib qw(:all); sub action { my $Privileged = CheckPermission($In{host}); my $host = $1 if ( $In{host} =~ /(.*)/ ); my $num = $In{num}; my $i; if ( !$Privileged ) { ErrorExit($Lang->{Only_privileged_users_can_view_archive_information}); } # # Find the requested archive # my @Archives = $bpc->ArchiveInfoRead($host); for ( $i = 0 ; $i < @Archives ; $i++ ) { last if ( $Archives[$i]{num} == $num ); } if ( $i >= @Archives ) { ErrorExit(eval("qq{$Lang->{Archive_number__num_for_host__does_not_exist}}")); } %ArchiveReq = (); do "$TopDir/pc/$host/ArchiveInfo.$Archives[$i]{num}" if ( -f "$TopDir/pc/$host/ArchiveInfo.$Archives[$i]{num}" ); my $startTime = timeStamp2($Archives[$i]{startTime}); my $reqTime = timeStamp2($ArchiveReq{reqTime}); my $dur = $Archives[$i]{endTime} - $Archives[$i]{startTime}; $dur = 1 if ( $dur <= 0 ); my $duration = sprintf("%.1f", $dur / 60); my $HostListStr = ""; my $counter=0; foreach my $f ( @{$ArchiveReq{HostList}} ) { $HostListStr .= <$f@{$ArchiveReq{BackupList}}[$counter] EOF $counter++; } my $content = eval("qq{$Lang->{Archive___num_details_for__host2 }}"); Header(eval("qq{$Lang->{Archive___num_details_for__host}}"), $content, 1); Trailer(); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/Browse.pm0000444000076500000240000002624413042250554016524 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::Browse package # # DESCRIPTION # # This module implements the Browse action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::Browse; use strict; use Encode qw/decode_utf8/; use BackupPC::CGI::Lib qw(:all); use BackupPC::View; use BackupPC::Attrib qw(:all); sub action { my $Privileged = CheckPermission($In{host}); my($i, $dirStr, $fileStr, $attr); my $checkBoxCnt = 0; if ( !$Privileged ) { ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_browse_backup_files}}")); } my $host = $In{host}; my $num = $In{num}; my $share = $In{share}; my $dir = $In{dir}; ErrorExit($Lang->{Empty_host_name}) if ( $host eq "" ); # # Find the requested backup and the previous filled backup # my @Backups = $bpc->BackupInfoRead($host); # # default to the newest backup # if ( !defined($In{num}) && @Backups > 0 ) { $i = @Backups - 1; $num = $Backups[$i]{num}; } for ( $i = 0 ; $i < @Backups ; $i++ ) { last if ( $Backups[$i]{num} == $num ); } if ( $i >= @Backups || $num !~ /^\d+$/ ) { ErrorExit("Backup number ${EscHTML($num)} for host ${EscHTML($host)} does" . " not exist."); } my $backupTime = timeStamp2($Backups[$i]{startTime}); my $backupAge = sprintf("%.1f", (time - $Backups[$i]{startTime}) / (24 * 3600)); my $view = BackupPC::View->new($bpc, $host, \@Backups, {nlink => 1}); if ( $dir eq "" || $dir eq "." || $dir eq ".." ) { $attr = $view->dirAttrib($num, "", ""); if ( keys(%$attr) > 0 ) { $share = (sort(keys(%$attr)))[0]; $dir = '/'; } else { ErrorExit(eval("qq{$Lang->{Directory___EscHTML}}")); } } $dir = "/$dir" if ( $dir !~ /^\// ); my $relDir = $dir; my $currDir = undef; if ( $dir =~ m{(^|/)\.\.(/|$)} ) { ErrorExit($Lang->{Nice_try__but_you_can_t_put}); } # # Loop up the directory tree until we hit the top. # my(@DirStrPrev); while ( 1 ) { my($fLast, $fLastum, @DirStr); $attr = $view->dirAttrib($num, $share, $relDir); if ( !defined($attr) ) { $relDir = decode_utf8($relDir); ErrorExit(eval("qq{$Lang->{Can_t_browse_bad_directory_name2}}")); } my $fileCnt = 0; # file counter $fLast = $dirStr = ""; # # Loop over each of the files in this directory # foreach my $f ( sort {uc($a) cmp uc($b)} keys(%$attr) ) { my($dirOpen, $gotDir, $imgStr, $img, $path); my $fURI = $f; # URI escaped $f my $shareURI = $share; # URI escaped $share if ( $relDir eq "" ) { $path = "/$f"; } else { ($path = "$relDir/$f") =~ s{//+}{/}g; } if ( $shareURI eq "" ) { $shareURI = $f; $path = "/"; } $path =~ s{^/+}{/}; $path =~ s/([^\w.\/-])/uc sprintf("%%%02X", ord($1))/eg; $fURI =~ s/([^\w.\/-])/uc sprintf("%%%02X", ord($1))/eg; $shareURI =~ s/([^\w.\/-])/uc sprintf("%%%02X", ord($1))/eg; $dirOpen = 1 if ( defined($currDir) && $f eq $currDir ); if ( $attr->{$f}{type} == BPC_FTYPE_DIR ) { # # Display directory if it exists in current backup. # First find out if there are subdirs # my $tdStyle; my $linkStyle = "fview"; $img |= 1 << 6; $img |= 1 << 5 if ( $attr->{$f}{nlink} > 2 ); if ( $dirOpen ) { $linkStyle = "fviewbold"; $img |= 1 << 2; $img |= 1 << 3 if ( $attr->{$f}{nlink} > 2 ); } my $imgFileName = sprintf("%07b.gif", $img); $imgStr = ""; if ( "$relDir/$f" eq $dir ) { $tdStyle = "fviewon"; } else { $tdStyle = "fviewoff"; } my $dirName = $f; $dirName =~ s/ / /g; $dirName = decode_utf8($dirName); push(@DirStr, {needTick => 1, tdArgs => " class=\"$tdStyle\"", link => <$imgStr $dirName EOF $fileCnt++; $gotDir = 1; if ( $dirOpen ) { my($lastTick, $doneLastTick); foreach my $d ( @DirStrPrev ) { $lastTick = $d if ( $d->{needTick} ); } $doneLastTick = 1 if ( !defined($lastTick) ); foreach my $d ( @DirStrPrev ) { $img = 0; if ( $d->{needTick} ) { $img |= 1 << 0; } if ( $d == $lastTick ) { $img |= 1 << 4; $doneLastTick = 1; } elsif ( !$doneLastTick ) { $img |= 1 << 3 | 1 << 4; } my $imgFileName = sprintf("%07b.gif", $img); $imgStr = ""; push(@DirStr, {needTick => 0, tdArgs => $d->{tdArgs}, link => $imgStr . $d->{link} }); } } } if ( $relDir eq $dir ) { # # This is the selected directory, so display all the files # my ($attrStr, $iconStr); if ( defined($a = $attr->{$f}) ) { my $mtimeStr = $bpc->timeStamp($a->{mtime}); # UGH -> fix this my $typeStr = BackupPC::Attrib::fileType2Text(undef, $a->{type}); my $modeStr = sprintf("0%o", $a->{mode} & 07777); $iconStr = < EOF $attrStr .= <$typeStr $modeStr $a->{backupNum} $a->{size} $mtimeStr EOF } else { $attrStr .= " \n"; } (my $fDisp = "${EscHTML($f)}") =~ s/ / /g; $fDisp = decode_utf8($fDisp); if ( $gotDir ) { $fileStr .= <  $iconStr $fDisp $attrStr EOF } else { $fileStr .= <  $iconStr $fDisp $attrStr EOF } $checkBoxCnt++; } } @DirStrPrev = @DirStr; last if ( $relDir eq "" && $share eq "" ); # # Prune the last directory off $relDir, or at the very end # do the top-level directory. # if ( $relDir eq "" || $relDir eq "/" || $relDir !~ /(.*)\/(.*)/ ) { $currDir = $share; $share = ""; $relDir = ""; } else { $relDir = $1; $currDir = $2; } } $share = $currDir; my $shareURI = $share; $shareURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; # # allow each level of the directory path to be navigated to # my($thisPath, $dirDisplay); my $dirClean = $dir; $dirClean =~ s{//+}{/}g; $dirClean =~ s{/+$}{}; my @dirElts = split(/\//, $dirClean); @dirElts = ("/") if ( !@dirElts ); foreach my $d ( @dirElts ) { my($thisDir); if ( $thisPath eq "" ) { $thisDir = decode_utf8($share); $thisPath = "/"; } else { $thisPath .= "/" if ( $thisPath ne "/" ); $thisPath .= "$d"; $thisDir = decode_utf8($d); } my $thisPathURI = $thisPath; $thisPathURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; $dirDisplay .= "/" if ( $dirDisplay ne "" ); $dirDisplay .= "${EscHTML($thisDir)}"; } my $filledBackup; if ( (my @mergeNums = @{$view->mergeNums}) > 1 ) { shift(@mergeNums); my $numF = join(", #", @mergeNums); $filledBackup = eval("qq{$Lang->{This_display_is_merged_with_backup}}"); } foreach my $d ( @DirStrPrev ) { $dirStr .= "{tdArgs}>$d->{link}\n"; } ### hide checkall button if there are no files my ($topCheckAll, $checkAll, $fileHeader); if ( $fileStr ) { $fileHeader = eval("qq{$Lang->{fileHeader}}"); $checkAll = $Lang->{checkAll}; # and put a checkall box on top if there are at least 20 files if ( $checkBoxCnt >= 20 ) { $topCheckAll = $checkAll; $topCheckAll =~ s{allFiles}{allFilestop}g; } } else { $fileStr = eval("qq{$Lang->{The_directory_is_empty}}"); } my $pathURI = $dir; $pathURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; if ( my @otherDirs = $view->backupList($share, $dir) ) { my $otherDirs; foreach my $i ( @otherDirs ) { my $selected; my $showDate = timeStamp2($Backups[$i]{startTime}); my $backupNum = $Backups[$i]{num}; $selected = " selected" if ( $backupNum == $num ); $otherDirs .= "\n"; } $filledBackup .= eval("qq{$Lang->{Visit_this_directory_in_backup}}"); } $dir = decode_utf8($dir); $share = decode_utf8($share); my $content = eval("qq{$Lang->{Backup_browse_for__host}}"); Header(eval("qq{$Lang->{Browse_backup__num_for__host}}"), $content); Trailer(); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/DirHistory.pm0000444000076500000240000001426113042250554017357 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::DirHistory package # # DESCRIPTION # # This module implements the DirHistory action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::DirHistory; use strict; use BackupPC::CGI::Lib qw(:all); use BackupPC::View; use BackupPC::Attrib qw(:all); use Encode; sub action { my $Privileged = CheckPermission($In{host}); my($i, $dirStr, $fileStr, $attr); my $checkBoxCnt = 0; if ( !$Privileged ) { ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_browse_backup_files}}")); } my $host = $In{host}; my $share = $In{share}; my $dir = $In{dir}; my $dirURI = $dir; my $shareURI = $share; $dirURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; $shareURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; ErrorExit($Lang->{Empty_host_name}) if ( $host eq "" ); my @Backups = $bpc->BackupInfoRead($host); my $view = BackupPC::View->new($bpc, $host, \@Backups, {inode => 1}); my $hist = $view->dirHistory($share, $dir); my($backupNumStr, $backupTimeStr, $fileStr); $dir = "/$dir" if ( $dir !~ /^\// ); if ( "/$host/$share/$dir/" =~ m{/\.\./} ) { ErrorExit($Lang->{Nice_try__but_you_can_t_put}); } my @backupList = $view->backupList($share, $dir); foreach $i ( @backupList ) { my $backupTime = timeStamp2($Backups[$i]{startTime}); my $num = $Backups[$i]{num}; $backupNumStr .= "$num"; $backupTimeStr .= "$backupTime"; } foreach my $f ( sort {uc($a) cmp uc($b)} keys(%$hist) ) { my %inode2name; my $nameCnt = 0; (my $fDisp = "${EscHTML($f)}") =~ s/ / /g; $fDisp = decode_utf8($fDisp); $fileStr .= "$fDisp"; my($colSpan, $url, $inode, $type); my $tdClass = ' class="histView"'; foreach $i ( @backupList ) { my($path); if ( $colSpan > 0 ) { # # The file is the same if it also size==0 (inode == -1) # or if it is a directory and the previous one is (inode == -2) # or if the inodes agree and the types are the same. # if ( defined($hist->{$f}[$i]) && $hist->{$f}[$i]{type} == $type && (($hist->{$f}[$i]{size} == 0 && $inode == -1) || ($hist->{$f}[$i]{type} == BPC_FTYPE_DIR && $inode == -2) || $hist->{$f}[$i]{inode} == $inode) ) { $colSpan++; next; } # # Also handle the case of a sequence of missing files # if ( !defined($hist->{$f}[$i]) && $inode == -3 ) { $colSpan++; next; } $fileStr .= "" . "$url"; $colSpan = 0; $tdClass = ' class="histView"'; } if ( !defined($hist->{$f}[$i]) ) { $colSpan = 1; $url = " "; $inode = -3; # special value for missing $tdClass = ' class="histViewMis"'; next; } if ( $dir eq "" ) { $path = "/$f"; } else { ($path = "$dir/$f") =~ s{//+}{/}g; } $path =~ s{^/+}{/}; $path =~ s/([^\w.\/-])/uc sprintf("%%%02X", ord($1))/eg; my $num = $hist->{$f}[$i]{backupNum}; if ( $hist->{$f}[$i]{type} == BPC_FTYPE_DIR ) { $inode = -2; # special value for dir $type = $hist->{$f}[$i]{type}; $url = <$Lang->{DirHistory_dirLink} EOF } else { $inode = $hist->{$f}[$i]{inode}; $type = $hist->{$f}[$i]{type}; # # special value for empty file # $inode = -1 if ( $hist->{$f}[$i]{size} == 0 ); if ( !defined($inode2name{$inode}) ) { $inode2name{$inode} = "$Lang->{DirHistory_fileLink}$nameCnt"; $nameCnt++; } $url = <$inode2name{$inode} EOF } $colSpan = 1; } if ( $colSpan > 0 ) { $fileStr .= "$url"; $colSpan = 0; } $fileStr .= "\n"; } # # allow each level of the directory path to be navigated to # my($thisPath, $dirDisplay); my $dirClean = $dir; $dirClean =~ s{//+}{/}g; $dirClean =~ s{/+$}{}; my @dirElts = split(/\//, $dirClean); @dirElts = ("/") if ( !@dirElts ); foreach my $d ( @dirElts ) { my($thisDir); if ( $thisPath eq "" ) { $thisDir = decode_utf8($share); $thisPath = "/"; } else { $thisPath .= "/" if ( $thisPath ne "/" ); $thisPath .= "$d"; $thisDir = decode_utf8($d); } my $thisPathURI = $thisPath; $thisPathURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; $dirDisplay .= "/" if ( $dirDisplay ne "" ); $dirDisplay .= "${EscHTML($thisDir)}"; } my $content = eval("qq{$Lang->{DirHistory_for__host}}"); Header(eval("qq{$Lang->{DirHistory_backup_for__host}}"), $content); Trailer(); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/EditConfig.pm0000444000076500000240000015627513042250554017306 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::EditConfig package # # DESCRIPTION # # This module implements the EditConfig action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2005-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::EditConfig; use strict; use BackupPC::CGI::Lib qw(:all); use BackupPC::Config::Meta qw(:all); use BackupPC::Storage; use Data::Dumper; use Encode; our %ConfigMenu = ( server => { text => "CfgEdit_Title_Server", param => [ {text => "CfgEdit_Title_General_Parameters"}, {name => "ServerHost"}, {name => "BackupPCUser"}, {name => "BackupPCUserVerify"}, {name => "MaxOldLogFiles"}, {name => "TrashCleanSleepSec"}, {text => "CfgEdit_Title_Wakeup_Schedule"}, {name => "WakeupSchedule"}, {text => "CfgEdit_Title_Concurrent_Jobs"}, {name => "MaxBackups"}, {name => "MaxUserBackups"}, {name => "MaxPendingCmds"}, {name => "MaxBackupPCNightlyJobs"}, {name => "BackupPCNightlyPeriod"}, {text => "CfgEdit_Title_Pool_Filesystem_Limits"}, {name => "DfCmd"}, {name => "DfMaxUsagePct"}, {name => "HardLinkMax"}, {text => "CfgEdit_Title_Other_Parameters"}, {name => "UmaskMode"}, {name => "MyPath"}, {name => "DHCPAddressRanges"}, {name => "CmdQueueNice"}, {name => "PerlModuleLoad"}, {name => "ServerInitdPath"}, {name => "ServerInitdStartCmd"}, {text => "CfgEdit_Title_Remote_Apache_Settings"}, {name => "ServerPort"}, {name => "ServerMesgSecret"}, {text => "CfgEdit_Title_Program_Paths"}, {name => "SshPath"}, {name => "NmbLookupPath"}, {name => "PingPath"}, {name => "DfPath"}, {name => "SplitPath"}, {name => "ParPath"}, {name => "CatPath"}, {name => "GzipPath"}, {name => "Bzip2Path"}, {text => "CfgEdit_Title_Install_Paths"}, # # Can only edit TopDir and LogDir if we are in FHS mode. # Otherwise they are hardcoded in lib/BackupPC/Lib.pm. # {name => "TopDir", visible => sub { return $_[1]->useFHS(); } }, {name => "LogDir", visible => sub { return $_[1]->useFHS(); } }, {name => "CgiDir"}, # # Cannot edit ConfDir or InstallDir, since the real value is hardcoded in # lib/BackupPC/Lib.pm. # {name => "ConfDir"}, # {name => "InstallDir"}, # ], }, email => { text => "CfgEdit_Title_Email", param => [ {text => "CfgEdit_Title_Email_settings"}, {name => "SendmailPath"}, {name => "EMailNotifyMinDays"}, {name => "EMailFromUserName"}, {name => "EMailAdminUserName"}, {name => "EMailUserDestDomain"}, {text => "CfgEdit_Title_Email_User_Messages"}, {name => "EMailNoBackupEverSubj"}, {name => "EMailNoBackupEverMesg"}, {name => "EMailNotifyOldBackupDays"}, {name => "EMailNoBackupRecentSubj"}, {name => "EMailNoBackupRecentMesg"}, {name => "EMailNotifyOldOutlookDays"}, {name => "EMailOutlookBackupSubj"}, {name => "EMailOutlookBackupMesg"}, {name => "EMailHeaders"}, ], }, cgi => { text => "CfgEdit_Title_CGI", param => [ {text => "CfgEdit_Title_Admin_Privileges"}, {name => "CgiAdminUserGroup"}, {name => "CgiAdminUsers"}, {text => "CfgEdit_Title_Page_Rendering"}, {name => "Language"}, {name => "CgiNavBarAdminAllHosts"}, {name => "CgiSearchBoxEnable"}, {name => "CgiNavBarLinks"}, {name => "CgiStatusHilightColor"}, {name => "CgiDateFormatMMDD"}, {name => "CgiHeaders"}, {name => "CgiExt2ContentType"}, {name => "CgiCSSFile"}, {text => "CfgEdit_Title_Paths"}, {name => "CgiURL"}, {name => "CgiImageDir"}, {name => "CgiImageDirURL"}, {text => "CfgEdit_Title_User_URLs"}, {name => "CgiUserHomePageCheck"}, {name => "CgiUserUrlCreate"}, {text => "CfgEdit_Title_User_Config_Editing"}, {name => "CgiUserConfigEditEnable"}, {name => "CgiUserConfigEdit"}, ], }, xfer => { text => "CfgEdit_Title_Xfer", param => [ {text => "CfgEdit_Title_Xfer_Settings"}, {name => "XferMethod", onchangeSubmit => 1}, {name => "XferLogLevel"}, {name => "ClientCharset"}, {name => "ClientCharsetLegacy"}, ### Smb Settings {text => "CfgEdit_Title_Smb_Settings", visible => sub { return $_[0]->{XferMethod} eq "smb"; } }, {name => "SmbShareName", visible => sub { return $_[0]->{XferMethod} eq "smb"; } }, {name => "SmbShareUserName", visible => sub { return $_[0]->{XferMethod} eq "smb"; } }, {name => "SmbSharePasswd", visible => sub { return $_[0]->{XferMethod} eq "smb"; } }, ### Tar Settings {text => "CfgEdit_Title_Tar_Settings", visible => sub { return $_[0]->{XferMethod} eq "tar"; } }, {name => "TarShareName", visible => sub { return $_[0]->{XferMethod} eq "tar"; } }, ### Rsync Settings {text => "CfgEdit_Title_Rsync_Settings", visible => sub { return $_[0]->{XferMethod} eq "rsync"; } }, {text => "CfgEdit_Title_Rsyncd_Settings", visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } }, {name => "RsyncShareName", visible => sub { return $_[0]->{XferMethod} =~ /rsync/; } }, {name => "RsyncdUserName", visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } }, {name => "RsyncdPasswd", visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } }, {name => "RsyncdAuthRequired", visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } }, {name => "RsyncCsumCacheVerifyProb", visible => sub { return $_[0]->{XferMethod} =~ /rsync/; } }, ### Ftp Settings {text => "CfgEdit_Title_Ftp_Settings", visible => sub { return $_[0]->{XferMethod} eq "ftp"; } }, {name => "FtpShareName", visible => sub { return $_[0]->{XferMethod} eq "ftp"; } }, {name => "FtpUserName", visible => sub { return $_[0]->{XferMethod} eq "ftp"; } }, {name => "FtpPasswd", visible => sub { return $_[0]->{XferMethod} eq "ftp"; } }, {name => "FtpPassive", visible => sub { return $_[0]->{XferMethod} eq "ftp"; } }, {name => "FtpBlockSize", visible => sub { return $_[0]->{XferMethod} eq "ftp"; } }, {name => "FtpPort", visible => sub { return $_[0]->{XferMethod} eq "ftp"; } }, {name => "FtpTimeout", visible => sub { return $_[0]->{XferMethod} eq "ftp"; } }, {name => "FtpFollowSymlinks", visible => sub { return $_[0]->{XferMethod} eq "ftp"; } }, ### Archive Settings {text => "CfgEdit_Title_Archive_Settings", visible => sub { return $_[0]->{XferMethod} eq "archive"; } }, {name => "ArchiveDest", visible => sub { return $_[0]->{XferMethod} eq "archive"; } }, {name => "ArchiveComp", visible => sub { return $_[0]->{XferMethod} eq "archive"; } }, {name => "ArchivePar", visible => sub { return $_[0]->{XferMethod} eq "archive"; } }, {name => "ArchiveSplit", visible => sub { return $_[0]->{XferMethod} eq "archive"; } }, ### Include/Exclude Settings {text => "CfgEdit_Title_Include_Exclude", visible => sub { return $_[0]->{XferMethod} ne "archive"; } }, {name => "BackupFilesOnly", visible => sub { return $_[0]->{XferMethod} ne "archive"; } }, {name => "BackupFilesExclude", visible => sub { return $_[0]->{XferMethod} ne "archive"; } }, ### Samba paths and commands {text => "CfgEdit_Title_Smb_Paths_Commands", visible => sub { return $_[0]->{XferMethod} eq "smb"; } }, {name => "SmbClientPath", visible => sub { return $_[0]->{XferMethod} eq "smb"; } }, {name => "SmbClientFullCmd", visible => sub { return $_[0]->{XferMethod} eq "smb"; } }, {name => "SmbClientIncrCmd", visible => sub { return $_[0]->{XferMethod} eq "smb"; } }, {name => "SmbClientRestoreCmd", visible => sub { return $_[0]->{XferMethod} eq "smb"; } }, ### Tar paths and commands {text => "CfgEdit_Title_Tar_Paths_Commands", visible => sub { return $_[0]->{XferMethod} eq "tar"; } }, {name => "TarClientPath", visible => sub { return $_[0]->{XferMethod} eq "tar"; } }, {name => "TarClientCmd", visible => sub { return $_[0]->{XferMethod} eq "tar"; } }, {name => "TarFullArgs", visible => sub { return $_[0]->{XferMethod} eq "tar"; } }, {name => "TarIncrArgs", visible => sub { return $_[0]->{XferMethod} eq "tar"; } }, {name => "TarClientRestoreCmd", visible => sub { return $_[0]->{XferMethod} eq "tar"; } }, ### Rsync paths and commands {text => "CfgEdit_Title_Rsync_Paths_Commands_Args", visible => sub { return $_[0]->{XferMethod} eq "rsync"; } }, {text => "CfgEdit_Title_Rsyncd_Port_Args", visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } }, {name => "RsyncClientPath", visible => sub { return $_[0]->{XferMethod} eq "rsync"; } }, {name => "RsyncClientCmd", visible => sub { return $_[0]->{XferMethod} eq "rsync"; } }, {name => "RsyncClientRestoreCmd", visible => sub { return $_[0]->{XferMethod} eq "rsync"; } }, {name => "RsyncdClientPort", visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } }, {name => "RsyncArgs", visible => sub { return $_[0]->{XferMethod} =~ /rsync/; } }, {name => "RsyncArgsExtra", visible => sub { return $_[0]->{XferMethod} =~ /rsync/; } }, {name => "RsyncRestoreArgs", visible => sub { return $_[0]->{XferMethod} =~ /rsync/; } }, ### Archive paths and commands {text => "CfgEdit_Title_Archive_Paths_Commands", visible => sub { return $_[0]->{XferMethod} eq "archive"; } }, {name => "ArchiveClientCmd", visible => sub { return $_[0]->{XferMethod} eq "archive"; } }, ], }, schedule => { text => "CfgEdit_Title_Schedule", param => [ {text => "CfgEdit_Title_Full_Backups"}, {name => "FullPeriod"}, {name => "FullKeepCnt"}, {name => "FullKeepCntMin"}, {name => "FullAgeMax"}, {text => "CfgEdit_Title_Incremental_Backups"}, {name => "IncrPeriod"}, {name => "IncrKeepCnt"}, {name => "IncrKeepCntMin"}, {name => "IncrAgeMax"}, {name => "IncrLevels"}, {name => "IncrFill"}, {text => "CfgEdit_Title_Blackouts"}, {name => "BackupsDisable"}, {name => "BlackoutBadPingLimit"}, {name => "BlackoutGoodCnt"}, {name => "BlackoutPeriods"}, {text => "CfgEdit_Title_Other"}, {name => "PartialAgeMax"}, {name => "RestoreInfoKeepCnt"}, {name => "ArchiveInfoKeepCnt"}, {name => "BackupZeroFilesIsFatal"}, ], }, backup => { text => "CfgEdit_Title_Backup_Settings", param => [ {text => "CfgEdit_Title_Client_Lookup"}, {name => "ClientNameAlias"}, {name => "NmbLookupCmd"}, {name => "NmbLookupFindHostCmd"}, {name => "FixedIPNetBiosNameCheck"}, {name => "PingCmd"}, {name => "PingMaxMsec"}, {text => "CfgEdit_Title_Other"}, {name => "ClientTimeout"}, {name => "MaxOldPerPCLogFiles"}, {name => "CompressLevel"}, {text => "CfgEdit_Title_User_Commands"}, {name => "DumpPreUserCmd"}, {name => "DumpPostUserCmd"}, {name => "DumpPreShareCmd"}, {name => "DumpPostShareCmd"}, {name => "RestorePreUserCmd"}, {name => "RestorePostUserCmd"}, {name => "ArchivePreUserCmd"}, {name => "ArchivePostUserCmd"}, {name => "UserCmdCheckStatus"}, ], }, hosts => { text => "CfgEdit_Title_Hosts", param => [ {text => "CfgEdit_Title_Hosts"}, {name => "Hosts", comment => "CfgEdit_Hosts_Comment"}, ], }, ); sub action { my $pc_dir = "$TopDir/pc"; my($content, $contentHidden, $newConf, $override, $mainConf, $hostConf); my $errors = {}; my $host = $In{host}; my $menu = $In{menu} || "server"; my $hosts_path = $Hosts; my $config_path = $host eq "" ? "$TopDir/conf/config.pl" : "$TopDir/pc/$host/config.pl"; my $Privileged = CheckPermission($host) && ($PrivAdmin || $Conf{CgiUserConfigEditEnable}); my $userHost = 1 if ( defined($host) ); my $debugText; if ( !$Privileged ) { ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_edit_config_files}}")); } if ( defined($In{menu}) || $In{saveAction} eq "Save" ) { $errors = errorCheck(); if ( %$errors ) { # # If there are errors, then go back to the same menu # $In{saveAction} = ""; #$In{newMenu} = ""; } if ( (my $var = $In{overrideUncheck}) ne "" ) { # # a compound variable was unchecked; delete or # add extra variables to make the shape the same. # #print STDERR Dumper(\%In); foreach my $v ( keys(%In) ) { if ( $v =~ /^v_zZ_(\Q$var\E(_zZ_.*|$))/ ) { delete($In{$v}) if ( !defined($In{"orig_zZ_$1"}) ); } if ( $v =~ /^orig_zZ_(\Q$var\E(_zZ_.*|$))/ ) { $In{"v_zZ_$1"} = $In{$v}; } } delete($In{"vflds.$var"}); } ($newConf, $override) = inputParse($bpc, $userHost); $override = undef if ( $host eq "" ); } else { # # First time: pick up the current config settings # $mainConf = $bpc->ConfigDataRead(); if ( $host ne "" ) { $hostConf = $bpc->ConfigDataRead($host); $override = {}; foreach my $param ( keys(%$hostConf) ) { $override->{$param} = 1; } } else { my $hostInfo = $bpc->HostInfoRead(); $hostConf = {}; $mainConf->{Hosts} = [map($hostInfo->{$_}, sort(keys(%$hostInfo)))]; } $newConf = { %$mainConf, %$hostConf }; } if ( $In{saveAction} ne "Save" && $In{newMenu} ne "" && defined($ConfigMenu{$In{newMenu}}) ) { $menu = $In{newMenu}; } my %menuDisable; if ( $userHost ) { # # For a non-admin user editing the host config, we need to # figure out which subsets of the menu tree will be visible, # based on what is enabled. Admin users can edit all the # available per-host settings. # foreach my $m ( sort(keys(%ConfigMenu)) ) { my $enabled = 0; my $text = -1; my $n = 0; my @mask = (); foreach my $paramInfo ( @{$ConfigMenu{$m}{param}} ) { my $param = $paramInfo->{name}; if ( defined($paramInfo->{text}) ) { $text = $n; $mask[$text] = 1; } else { if ( $bpc->{Conf}{CgiUserConfigEdit}{$param} || (defined($bpc->{Conf}{CgiUserConfigEdit}{$param}) && $PrivAdmin) ) { $mask[$text] = 0 if ( $text >= 0 ); $mask[$n] = 0; $enabled ||= 1; } else { $mask[$n] = 1; } } $n++; } $menuDisable{$m}{mask} = \@mask; $menuDisable{$m}{top} = !$enabled; } if ( $menuDisable{$menu}{top} ) { # # Find an enabled menu if the current menu is empty # foreach my $m ( sort(keys(%menuDisable)) ) { if ( !$menuDisable{$m}{top} ) { $menu = $m; last; } } } } my $groupText; foreach my $m ( sort(keys(%ConfigMenu)) ) { next if ( $menuDisable{$m}{top} ); my $text = eval("qq($Lang->{$ConfigMenu{$m}{text}})"); if ( $m eq $menu ) { $groupText .= <$text EOF } else { $groupText .= <$text EOF } } if ( $host eq "" ) { $content .= eval("qq($Lang->{CfgEdit_Header_Main})"); } else { $content .= eval("qq($Lang->{CfgEdit_Header_Host})"); } my $saveStyle = ""; my $saveColor = "#ff0000"; if ( $In{modified} && $In{saveAction} ne "Save" && !%$errors ) { $saveStyle = "style=\"color:$saveColor\""; } else { $In{modified} = 0; } # # Add action and host to the URL so the nav bar link is # highlighted # my $url = "$MyURL?action=editConfig"; $url .= "&host=$host" if ( $host ne "" ); $content .= < $groupText

$debugText EOF $content .= < EOF my $doneParam = {}; my $tblContent; # # There is a special case of the user deleting just the field # that has the error(s). So if the delete variable is a match # or parent to all the errors then ignore the errors. # if ( $In{deleteVar} ne "" && %$errors > 0 ) { my $matchAll = 1; foreach my $v ( keys(%$errors) ) { if ( $v ne $In{deleteVar} && $v !~ /^\Q$In{deleteVar}_zZ_/ ) { $matchAll = 0; last; } } $errors = {} if ( $matchAll ); } my $isError = %$errors; if ( !$isError && $In{saveAction} eq "Save" ) { my($mesg, $err); if ( $host ne "" ) { $hostConf = $bpc->ConfigDataRead($host) if ( !defined($hostConf) ); my %hostConf2 = %$hostConf; foreach my $param ( keys(%$newConf) ) { if ( $override->{$param} ) { $hostConf->{$param} = $newConf->{$param} } else { delete($hostConf->{$param}); } } $mesg = configDiffMesg($host, \%hostConf2, $hostConf); $err .= $bpc->ConfigDataWrite($host, $hostConf); } else { $mainConf = $bpc->ConfigDataRead() if ( !defined($mainConf) ); my $hostsSave = []; my($hostsNew, $allHosts, $copyConf); foreach my $entry ( @{$newConf->{Hosts}} ) { next if ( $entry->{host} eq "" ); $allHosts->{$entry->{host}} = 1; $allHosts->{$1} = 1 if ( $entry->{host} =~ /(.+?)\s*=/ ); } foreach my $entry ( @{$newConf->{Hosts}} ) { next if ( $entry->{host} eq "" || defined($hostsNew->{$entry->{host}}) ); if ( $entry->{host} =~ /(.+?)\s*=\s*(.+)/ ) { if ( defined($allHosts->{$2}) ) { $entry->{host} = $1; $copyConf->{$1} = $2; } else { my $fullHost = $entry->{host}; my $copyHost = $2; $err .= eval("qq($Lang->{CfgEdit_Error_Copy_host_does_not_exist})"); } } push(@$hostsSave, $entry); $hostsNew->{$entry->{host}} = $entry; } ($mesg, my $hostChange) = hostsDiffMesg($hostsNew); $bpc->HostInfoWrite($hostsNew) if ( $hostChange ); foreach my $host ( keys(%$copyConf) ) { # # Currently host names are forced to lc when they # are read from the hosts file. Therefore we need # to force the from and to hosts to lc. # my $confData = $bpc->ConfigDataRead(lc($copyConf->{$host})); my $fromHost = $copyConf->{$host}; $err .= $bpc->ConfigDataWrite(lc($host), $confData); $mesg .= eval("qq($Lang->{CfgEdit_Log_Copy_host_config})"); } delete($newConf->{Hosts}); $mesg .= configDiffMesg(undef, $mainConf, $newConf); $mainConf = { %$mainConf, %$newConf }; $err .= $bpc->ConfigDataWrite(undef, $mainConf); $newConf->{Hosts} = $hostsSave; } if ( defined($err) ) { $tblContent .= <$err EOF } $bpc->ServerConnect(); if ( $mesg ne "" ) { (my $mesgBR = $mesg) =~ s/\n/
\n/g; # uncomment this if you want the changes to be displayed # $tblContent .= <$mesgBR #EOF foreach my $str ( split(/\n/, $mesg) ) { $bpc->ServerMesg("log $str") if ( $str ne "" ); } } # # Tell the server to reload, unless we only changed # a client config # $bpc->ServerMesg("server reload") if ( $host eq "" ); } my @mask = @{$menuDisable{$menu}{mask} || []}; foreach my $paramInfo ( @{$ConfigMenu{$menu}{param}} ) { my $param = $paramInfo->{name}; my $disabled = shift(@mask); next if ( $disabled || $menuDisable{$menu}{top} ); if ( ref($paramInfo->{visible}) eq "CODE" && !&{$paramInfo->{visible}}($newConf, $bpc) ) { next; } if ( defined($paramInfo->{text}) ) { my $text = eval("qq($Lang->{$paramInfo->{text}})"); $tblContent .= <$text EOF next; } # # TODO: get parameter documentation # my $comment = ""; #$comment =~ s/\'//g; #$comment =~ s/\"//g; #$comment =~ s/\n/ /g; $doneParam->{$param} = 1; $tblContent .= fieldEditBuild($ConfigMeta{$param}, $param, $newConf->{$param}, $errors, 0, $comment, $isError, $paramInfo->{onchangeSubmit}, defined($override) ? $param : undef, defined($override) ? $override->{$param} : undef ); if ( defined($paramInfo->{comment}) ) { my $topDir = $bpc->TopDir; my $text = eval("qq($Lang->{$paramInfo->{comment}})"); $tblContent .= <$text EOF } } # # Emit a summary of all the errors # my $errorTxt; if ( %$errors ) { $errorTxt .= <$Lang->{CfgEdit_Error_No_Save} EOF } foreach my $param ( sort(keys(%$errors)) ) { $errorTxt .= <$errors->{$param} EOF } $content .= < EOF # # Emit all the remaining editable config settings as hidden values # foreach my $param ( keys(%ConfigMeta) ) { next if ( $doneParam->{$param} ); next if ( $userHost && (!defined($bpc->{Conf}{CgiUserConfigEdit}{$param}) || (!$PrivAdmin && !$bpc->{Conf}{CgiUserConfigEdit}{$param})) ); $content .= fieldHiddenBuild($ConfigMeta{$param}, $param, $newConf->{$param}, "v" ); if ( defined($override) ) { $content .= < EOF } $doneParam->{$param} = 1; } if ( defined($In{menu}) || $In{saveAction} eq "Save" ) { if ( $In{saveAction} eq "Save" && !$userHost ) { # # Emit the new settings as orig_zZ_ parameters # $doneParam = {}; foreach my $param ( keys(%ConfigMeta) ) { next if ( $doneParam->{$param} ); next if ( $userHost && (!defined($bpc->{Conf}{CgiUserConfigEdit}{$param}) || (!$PrivAdmin && !$bpc->{Conf}{CgiUserConfigEdit}{$param})) ); $contentHidden .= fieldHiddenBuild($ConfigMeta{$param}, $param, $newConf->{$param}, "orig", ); $doneParam->{$param} = 1; $In{modified} = 0; } } else { # # Just switching menus: copy all the orig_zZ_ input parameters # foreach my $var ( keys(%In) ) { next if ( $var !~ /^orig_zZ_/ ); my $val = decode_utf8($In{$var}); $contentHidden .= < EOF } } } else { # # First time: emit all the original config settings # $doneParam = {}; foreach my $param ( keys(%ConfigMeta) ) { next if ( $doneParam->{$param} ); next if ( $userHost && (!defined($bpc->{Conf}{CgiUserConfigEdit}{$param}) || (!$PrivAdmin && !$bpc->{Conf}{CgiUserConfigEdit}{$param})) ); $contentHidden .= fieldHiddenBuild($ConfigMeta{$param}, $param, $mainConf->{$param}, "orig", ); $doneParam->{$param} = 1; } } $content .= < EOF Header("Config Edit", $content); Trailer(); } sub fieldHiddenBuild { my($type, $varName, $varValue, $prefix) = @_; my $content; $type = { type => $type } if ( ref($type) ne "HASH" ); if ( $type->{type} eq "list" ) { $varValue = [] if ( !defined($varValue) ); $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" ); for ( my $i = 0 ; $i < @$varValue ; $i++ ) { $content .= fieldHiddenBuild($type->{child}, "${varName}_zZ_$i", $varValue->[$i], $prefix); } } elsif ( $type->{type} eq "hash" || $type->{type} eq "horizHash" ) { $varValue = {} if ( ref($varValue) ne "HASH" ); my(@order, $childType); if ( defined($type->{order}) ) { @order = @{$type->{order}}; } elsif ( defined($type->{child}) ) { @order = sort(keys(%{$type->{child}})); } else { @order = sort(keys(%$varValue)); } foreach my $fld ( @order ) { if ( defined($type->{child}) ) { $childType = $type->{child}{$fld}; } else { $childType = $type->{childType}; # # emit list of fields since they are user-defined # rather than hard-coded # $content .= < EOF } $content .= fieldHiddenBuild($childType, "${varName}_zZ_$fld", $varValue->{$fld}, $prefix); } } elsif ( $type->{type} eq "shortlist" ) { $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" ); $varValue = join(", ", @$varValue); $content .= < EOF } else { $content .= < EOF } return $content; } sub fieldEditBuild { my($type, $varName, $varValue, $errors, $level, $comment, $isError, $onchangeSubmit, $overrideVar, $overrideSet) = @_; my $content; my $size = 50 - 10 * $level; $type = { type => $type } if ( ref($type) ne "HASH" ); $size = $type->{size} if ( defined($type->{size}) ); # # These fragments allow inline content to be turned on and off # # # $varName # if ( $level == 0 ) { my $lcVarName = lc($varName); $content .= <$varName EOF if ( defined($overrideVar) ) { my $override_checked = ""; if ( !$isError && $In{deleteVar} =~ /^\Q${varName}_zZ_/ || !$isError && $In{insertVar} =~ /^\Q${varName}\E(_zZ_|$)/ || !$isError && $In{addVar} =~ /^\Q${varName}\E(_zZ_|$)/ ) { $overrideSet = 1; } if ( $overrideSet ) { $override_checked = "checked"; } $content .= <\ ${EscHTML($Lang->{CfgEdit_Button_Override})} EOF } $content .= "\n"; } if ( $type->{type} eq "list" ) { $content .= "\n"; $varValue = [] if ( !defined($varValue) ); $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" ); if ( !$isError && $In{deleteVar} =~ /^\Q${varName}_zZ_\E(\d+)$/ && $1 < @$varValue ) { # # User deleted entry in this array # splice(@$varValue, $1, 1) if ( @$varValue > 1 || $type->{emptyOk} ); $In{deleteVar} = ""; } if ( !$isError && $In{insertVar} =~ /^\Q${varName}_zZ_\E(\d+)$/ && $1 < @$varValue ) { # # User inserted entry in this array # splice(@$varValue, $1, 0, "") if ( @$varValue > 1 || $type->{emptyOk} ); $In{insertVar} = ""; } if ( !$isError && $In{addVar} eq $varName ) { # # User added entry to this array # push(@$varValue, undef); $In{addVar} = ""; } $content .= "\n"; my $colspan; if ( ref($type) eq "HASH" && ref($type->{child}) eq "HASH" && $type->{child}{type} eq "horizHash" ) { my @order; if ( defined($type->{child}{order}) ) { @order = @{$type->{child}{order}}; } else { @order = sort(keys(%{$type->{child}{child}})); } $content .= "\n"; for ( my $i = 0 ; $i < @order ; $i++ ) { $content .= "\n"; } $colspan = @order + 1; $content .= "\n"; for ( my $i = 0 ; $i < @$varValue ; $i++ ) { if ( @$varValue > 1 || $type->{emptyOk} ) { $content .= < EOF } $content .= fieldEditBuild($type->{child}, "${varName}_zZ_$i", $varValue->[$i], $errors, $level + 1, undef, $isError, $onchangeSubmit, $overrideVar, $overrideSet); $content .= "\n"; } } else { for ( my $i = 0 ; $i < @$varValue ; $i++ ) { $content .= <\n"; $content .= fieldEditBuild($type->{child}, "${varName}_zZ_$i", $varValue->[$i], $errors, $level + 1, undef, $isError, $onchangeSubmit, $overrideVar, $overrideSet); $content .= "\n"; } $colspan = 2; } $content .= <
$order[$i]
EOF if ( @$varValue > 1 || $type->{emptyOk} ) { $content .= < EOF } $content .= "
EOF $content .= "\n"; } elsif ( $type->{type} eq "hash" ) { $content .= "\n"; $content .= "\n"; $varValue = {} if ( ref($varValue) ne "HASH" ); if ( !$isError && !$type->{noKeyEdit} && $In{deleteVar} !~ /^\Q${varName}_zZ_\E.*_zZ_/ && $In{deleteVar} =~ /^\Q${varName}_zZ_\E(.*)$/ ) { # # User deleted entry in this hash # delete($varValue->{$1}) if ( keys(%$varValue) > 1 || $type->{emptyOk} ); $In{deleteVar} = ""; } if ( !$isError && !defined($type->{child}) && $In{addVar} eq $varName ) { # # User added entry to this array # $varValue->{$In{"addVarKey_$varName"}} = "" if ( !defined($varValue->{$In{"addVarKey_$varName"}}) ); $In{addVar} = ""; } my(@order, $childType); if ( defined($type->{order}) ) { @order = @{$type->{order}}; } elsif ( defined($type->{child}) ) { @order = sort(keys(%{$type->{child}})); } else { @order = sort(keys(%$varValue)); } foreach my $fld ( @order ) { $content .= <\n"; $content .= fieldEditBuild($childType, "${varName}_zZ_$fld", $varValue->{$fld}, $errors, $level + 1, undef, $isError, $onchangeSubmit, $overrideVar, $overrideSet); $content .= "\n"; } if ( !$type->{noKeyEdit} ) { my $keyText = defined($type->{keyText}) ? $Lang->{$type->{keyText}} : $Lang->{CfgEdit_Button_New_Key}; $content .= < EOF } $content .= "
$fld EOF if ( !$type->{noKeyEdit} && (keys(%$varValue) > 1 || $type->{emptyOk}) ) { $content .= < EOF } if ( defined($type->{child}) ) { $childType = $type->{child}{$fld}; } else { $childType = $type->{childType}; # # emit list of fields since they are user-defined # rather than hard-coded # $content .= < EOF } $content .= "
$keyText:
\n"; $content .= "\n"; } elsif ( $type->{type} eq "horizHash" ) { $varValue = {} if ( ref($varValue) ne "HASH" ); my(@order, $childType); if ( defined($type->{order}) ) { @order = @{$type->{order}}; } elsif ( defined($type->{child}) ) { @order = sort(keys(%{$type->{child}})); } else { @order = sort(keys(%$varValue)); } foreach my $fld ( @order ) { if ( defined($type->{child}) ) { $childType = $type->{child}{$fld}; } else { $childType = $type->{childType}; # # emit list of fields since they are user-defined # rather than hard-coded # $content .= < EOF } $content .= fieldEditBuild($childType, "${varName}_zZ_$fld", $varValue->{$fld}, $errors, $level + 1, undef, $isError, $onchangeSubmit, $overrideVar, $overrideSet); } } else { $content .= "\n"; if ( $isError ) { # # If there was an error, we use the original post values # in %In, rather than the parsed values in $varValue. # This is so that the user's erroneous input is preserved. # $varValue = $In{"v_zZ_$varName"} if ( defined($In{"v_zZ_$varName"}) ); } if ( defined($errors->{$varName}) ) { $content .= <$errors->{$varName}
EOF } my $onChange; if ( defined($overrideVar) ) { $onChange .= "checkboxSet('$overrideVar');"; } else { $onChange .= "varChange('$varName');"; } if ( $onchangeSubmit ) { $onChange .= "document.editForm.submit();"; } if ( $onChange ne "" ) { $onChange = " onChange=\"$onChange\""; } if ( $varValue !~ /\n/ && ($type->{type} eq "integer" || $type->{type} eq "string" || $type->{type} eq "execPath" || $type->{type} eq "shortlist" || $type->{type} eq "float") ) { # simple input box if ( $type->{type} eq "shortlist" ) { $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" ); $varValue = join(", ", @$varValue); } my $textType = ($varName =~ /Passwd/) ? "password" : "text"; $content .= < EOF } elsif ( $type->{type} eq "boolean" ) { # checkbox my $checked = "checked" if ( $varValue ); $content .= < EOF } elsif ( $type->{type} eq "select" ) { $content .= < EOF foreach my $option ( @{$type->{values}} ) { my $sel = " selected" if ( $varValue eq $option ); $content .= "$option\n"; } $content .= "\n"; } else { # multi-line text area - count number of lines my $rowCnt = $varValue =~ tr/\n//; $rowCnt = 1 if ( $rowCnt < 1 ); $content .= <${EscHTML($varValue)} EOF } $content .= "\n"; } return $content; } sub errorCheck { my $errors = {}; foreach my $param ( keys(%ConfigMeta) ) { fieldErrorCheck($ConfigMeta{$param}, $param, $errors); } return $errors; } sub fieldErrorCheck { my($type, $varName, $errors) = @_; $type = { type => $type } if ( ref($type) ne "HASH" ); if ( $type->{type} eq "list" ) { for ( my $i = 0 ; ; $i++ ) { last if ( fieldErrorCheck($type->{child}, "${varName}_zZ_$i", $errors) ); } } elsif ( $type->{type} eq "hash" || $type->{type} eq "horizHash" ) { my(@order, $childType); my $ret; if ( defined($type->{order}) ) { @order = @{$type->{order}}; } elsif ( defined($type->{child}) ) { @order = sort(keys(%{$type->{child}})); } else { @order = split(/\0/, $In{"vflds.$varName"}); } foreach my $fld ( @order ) { if ( defined($type->{child}) ) { $childType = $type->{child}{$fld}; } else { $childType = $type->{childType}; } $ret ||= fieldErrorCheck($childType, "${varName}_zZ_$fld", $errors); } return $ret; } else { $In{"v_zZ_$varName"} = "0" if ( $type->{type} eq "boolean" && $In{"v_zZ_$varName"} eq "" ); return 1 if ( !exists($In{"v_zZ_$varName"}) ); (my $var = $varName) =~ s/_zZ_/./g; if ( $type->{type} eq "integer" || $type->{type} eq "boolean" ) { if ( $In{"v_zZ_$varName"} !~ /^-?\d+\s*$/s && $In{"v_zZ_$varName"} ne "" ) { $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_an_integer}}"); } } elsif ( $type->{type} eq "float" ) { if ( $In{"v_zZ_$varName"} !~ /^-?\d*(\.\d*)?\s*$/s && $In{"v_zZ_$varName"} ne "" ) { $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_real_valued_number}}"); } } elsif ( $type->{type} eq "shortlist" ) { my @vals = split(/[,\s]+/, $In{"v_zZ_$varName"}); for ( my $i = 0 ; $i < @vals ; $i++ ) { if ( $type->{child} eq "integer" && $vals[$i] !~ /^-?\d+\s*$/s && $vals[$i] ne "" ) { my $k = $i + 1; $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__entry__must_be_an_integer}}"); } elsif ( $type->{child} eq "float" && $vals[$i] !~ /^-?\d*(\.\d*)?\s*$/s && $vals[$i] ne "" ) { my $k = $i + 1; $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__entry__must_be_real_valued_number}}"); } } } elsif ( $type->{type} eq "select" ) { my $match = 0; foreach my $option ( @{$type->{values}} ) { if ( $In{"v_zZ_$varName"} eq $option ) { $match = 1; last; } } $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_valid_option}}") if ( !$match ); } elsif ( $type->{type} eq "execPath" ) { if ( $In{"v_zZ_$varName"} ne "" && !-x $In{"v_zZ_$varName"} ) { $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_executable_program}}"); } } else { # # $type->{type} eq "string": no error checking # } } return 0; } sub inputParse { my($bpc, $userHost) = @_; my $conf = {}; my $override = {}; foreach my $param ( keys(%ConfigMeta) ) { my $value; next if ( $userHost && (!defined($bpc->{Conf}{CgiUserConfigEdit}{$param}) || (!$PrivAdmin && !$bpc->{Conf}{CgiUserConfigEdit}{$param})) ); fieldInputParse($ConfigMeta{$param}, $param, \$value); $conf->{$param} = $value; $override->{$param} = $In{"override_$param"}; } return ($conf, $override); } sub fieldInputParse { my($type, $varName, $value) = @_; $type = { type => $type } if ( ref($type) ne "HASH" ); if ( $type->{type} eq "list" ) { $$value = []; for ( my $i = 0 ; ; $i++ ) { my $val; last if ( fieldInputParse($type->{child}, "${varName}_zZ_$i", \$val) ); push(@$$value, $val); } $$value = undef if ( $type->{undefIfEmpty} && @$$value == 0 ); } elsif ( $type->{type} eq "hash" || $type->{type} eq "horizHash" ) { my(@order, $childType); my $ret; $$value = {}; if ( defined($type->{order}) ) { @order = @{$type->{order}}; } elsif ( defined($type->{child}) ) { @order = sort(keys(%{$type->{child}})); } else { @order = split(/\0/, $In{"vflds.$varName"}); } foreach my $fld ( @order ) { my $val; if ( defined($type->{child}) ) { $childType = $type->{child}{$fld}; } else { $childType = $type->{childType}; } $ret ||= fieldInputParse($childType, "${varName}_zZ_$fld", \$val); last if ( $ret ); $$value->{$fld} = $val; } return $ret; } else { if ( $type->{type} eq "boolean" ) { $$value = 0 + $In{"v_zZ_$varName"}; } elsif ( !exists($In{"v_zZ_$varName"}) ) { return 1; } my $v = $In{"v_zZ_$varName"}; if ( $type->{type} eq "integer" ) { if ( $v =~ /^-?\d+\s*$/s || $v eq "" ) { $$value = 0 + $v; } else { # error value - keep in string form $$value = $v; } } elsif ( $type->{type} eq "float" ) { if ( $v =~ /^-?\d*(\.\d*)?\s*$/s || $v eq "" ) { $$value = 0 + $v; } else { # error value - keep in string form $$value = $v; } } elsif ( $type->{type} eq "shortlist" ) { $$value = [split(/[,\s]+/, $v)]; if ( $type->{child} eq "float" ) { foreach ( @$$value ) { if ( /^-?\d*(\.\d*)?\s*$/s || $v eq "" ) { $_ += 0; } } } elsif ( $type->{child} eq "integer" || $type->{child} eq "boolean" ) { foreach ( @$$value ) { if ( /^-?\d+\s*$/s || $v eq "" ) { $_ += 0; } } } } else { $$value = decode_utf8($In{"v_zZ_$varName"}); $$value =~ s/\r\n/\n/g; # remove leading space from exec paths $$value =~ s/^\s+// if ( $type->{type} eq "execPath" ); } $$value = undef if ( $type->{undefIfEmpty} && $$value eq "" ); } return 0; } sub configDiffMesg { my($host, $oldConf, $newConf) = @_; my $mesg; my $conf; if ( $host ne "" ) { $conf = "host $host config"; } else { $conf = "main config"; } foreach my $p ( keys(%ConfigMeta) ) { if ( !exists($oldConf->{$p}) && !exists($newConf->{$p}) ) { next; } elsif ( exists($oldConf->{$p}) && !exists($newConf->{$p}) ) { $mesg .= eval("qq($Lang->{CfgEdit_Log_Delete_param})"); } elsif ( !exists($oldConf->{$p}) && exists($newConf->{$p}) ) { my $dump = Data::Dumper->new([$newConf->{$p}]); $dump->Indent(0); $dump->Sortkeys(1); $dump->Terse(1); my $value = $dump->Dump; $value =~ s/\n/\\n/g; $value =~ s/\r/\\r/g; if ( $p =~ /Passwd/ || $p =~ /Secret/ ) { $value = "'*'"; } $mesg .= eval("qq($Lang->{CfgEdit_Log_Add_param_value})"); } else { my $dump = Data::Dumper->new([$newConf->{$p}]); $dump->Indent(0); $dump->Sortkeys(1); $dump->Terse(1); my $valueNew = $dump->Dump; my $v = $oldConf->{$p}; if ( ref($newConf->{$p}) eq "ARRAY" && ref($v) eq "" ) { $v = [$v]; } $dump = Data::Dumper->new([$v]); $dump->Indent(0); $dump->Sortkeys(1); $dump->Terse(1); my $valueOld = $dump->Dump; (my $valueNew2 = $valueNew) =~ s/['\n\r]//g; (my $valueOld2 = $valueOld) =~ s/['\n\r]//g; next if ( $valueOld2 eq $valueNew2 ); $valueNew =~ s/\n/\\n/g; $valueOld =~ s/\n/\\n/g; $valueNew =~ s/\r/\\r/g; $valueOld =~ s/\r/\\r/g; if ( $p =~ /Passwd/ || $p =~ /Secret/ ) { $valueNew = "'*'"; $valueOld = "'*'"; } $mesg .= eval("qq($Lang->{CfgEdit_Log_Change_param_value})"); } } return $mesg; } sub hostsDiffMesg { my($hostsNew) = @_; my $hostsOld = $bpc->HostInfoRead(); my($mesg, $hostChange); foreach my $host ( keys(%$hostsOld) ) { if ( !defined($hostsNew->{$host}) ) { $mesg .= eval("qq($Lang->{CfgEdit_Log_Host_Delete})"); $hostChange++; next; } foreach my $key ( keys(%{$hostsNew->{$host}}) ) { next if ( $hostsNew->{$host}{$key} eq $hostsOld->{$host}{$key} ); my $valueOld = $hostsOld->{$host}{$key}; my $valueNew = $hostsNew->{$host}{$key}; $mesg .= eval("qq($Lang->{CfgEdit_Log_Host_Change})"); $hostChange++; } } foreach my $host ( keys(%$hostsNew) ) { next if ( defined($hostsOld->{$host}) ); my $dump = Data::Dumper->new([$hostsNew->{$host}]); $dump->Indent(0); $dump->Sortkeys(1); $dump->Terse(1); my $value = $dump->Dump; $value =~ s/\n/\\n/g; $value =~ s/\r/\\r/g; $mesg .= eval("qq($Lang->{CfgEdit_Log_Host_Add})"); $hostChange++; } return ($mesg, $hostChange); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/EmailSummary.pm0000444000076500000240000000536713042250554017673 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::EmailSummary package # # DESCRIPTION # # This module implements the EmailSummary action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::EmailSummary; use strict; use BackupPC::CGI::Lib qw(:all); sub action { my $Privileged = CheckPermission(); if ( !$Privileged ) { ErrorExit($Lang->{Only_privileged_users_can_view_email_summaries}); } GetStatusInfo("hosts"); ReadUserEmailInfo(); my(%EmailStr, $str); foreach my $u ( keys(%UserEmailInfo) ) { my $info; if ( defined($UserEmailInfo{$u}{lastTime}) && ref($UserEmailInfo{$u}{lastTime}) ne 'HASH' ) { # # old format $UserEmailInfo - pre 3.2.0. # my $host = $UserEmailInfo{$u}{lastHost}; $info = { $host => { lastTime => $UserEmailInfo{$u}{lastTime}, lastSubj => $UserEmailInfo{$u}{lastSubj}, }, }; } else { $info = $UserEmailInfo{$u}; } foreach my $host ( keys(%$info) ) { next if ( !defined($info->{$host}{lastTime}) ); my $emailTimeStr = timeStamp2($info->{$host}{lastTime}); $EmailStr{$info->{$host}{lastTime}} .= <${UserLink($u)} ${HostLink($host)} $emailTimeStr $info->{$host}{lastSubj} EOF } } foreach my $t ( sort({$b <=> $a} keys(%EmailStr)) ) { $str .= $EmailStr{$t}; } my $content = eval("qq{$Lang->{Recent_Email_Summary}}"); Header($Lang->{Email_Summary}, $content); Trailer(); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/GeneralInfo.pm0000444000076500000240000001270213042250554017446 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::GeneralInfo package # # DESCRIPTION # # This module implements the GeneralInfo action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::GeneralInfo; use strict; use BackupPC::CGI::Lib qw(:all); sub action { GetStatusInfo("info jobs hosts queueLen"); my $Privileged = CheckPermission(); my($jobStr, $statusStr); foreach my $host ( sort(keys(%Jobs)) ) { my $startTime = timeStamp2($Jobs{$host}{startTime}); next if ( $host eq $bpc->trashJob && $Jobs{$host}{processState} ne "running" ); next if ( !$Privileged && !CheckPermission($host) ); $Jobs{$host}{type} = $Status{$host}{type} if ( $Jobs{$host}{type} eq "" && defined($Status{$host})); (my $cmd = $Jobs{$host}{cmd}) =~ s/$BinDir\///g; (my $xferPid = $Jobs{$host}{xferPid}) =~ s/,/, /g; $jobStr .= < ${HostLink($host)} $Jobs{$host}{type} ${UserLink(defined($Hosts->{$host}) ? $Hosts->{$host}{user} : "")} $startTime $cmd $Jobs{$host}{pid} $xferPid EOF $jobStr .= "\n"; } foreach my $host ( sort(keys(%Status)) ) { next if ( $Status{$host}{reason} ne "Reason_backup_failed" && $Status{$host}{reason} ne "Reason_restore_failed" && (!$Status{$host}{userReq} || $Status{$host}{reason} ne "Reason_no_ping") ); next if ( !$Privileged && !CheckPermission($host) ); my $startTime = timeStamp2($Status{$host}{startTime}); my($errorTime, $XferViewStr); if ( $Status{$host}{errorTime} > 0 ) { $errorTime = timeStamp2($Status{$host}{errorTime}); } if ( -f "$TopDir/pc/$host/SmbLOG.bad" || -f "$TopDir/pc/$host/SmbLOG.bad.z" || -f "$TopDir/pc/$host/XferLOG.bad" || -f "$TopDir/pc/$host/XferLOG.bad.z" ) { $XferViewStr = <$Lang->{XferLOG}, $Lang->{Errors} EOF } else { $XferViewStr = ""; } (my $shortErr = $Status{$host}{error}) =~ s/(.{48}).*/$1.../; $statusStr .= < ${HostLink($host)} $Status{$host}{type} ${UserLink(defined($Hosts->{$host}) ? $Hosts->{$host}{user} : "")} $startTime $XferViewStr $errorTime ${EscHTML($shortErr)} EOF } my $now = timeStamp2(time); my $nextWakeupTime = timeStamp2($Info{nextWakeup}); my $DUlastTime = timeStamp2($Info{DUlastValueTime}); my $DUmaxTime = timeStamp2($Info{DUDailyMaxTime}); my $numBgQueue = $QueueLen{BgQueue}; my $numUserQueue = $QueueLen{UserQueue}; my $numCmdQueue = $QueueLen{CmdQueue}; my $serverStartTime = timeStamp2($Info{startTime}); my $configLoadTime = timeStamp2($Info{ConfigLTime}); my $poolInfo = genPoolInfo("pool", \%Info); my $cpoolInfo = genPoolInfo("cpool", \%Info); if ( $Info{poolFileCnt} > 0 && $Info{cpoolFileCnt} > 0 ) { $poolInfo = <Uncompressed pool:

    $poolInfo
  • Compressed pool:
      $cpoolInfo
    EOF } elsif ( $Info{cpoolFileCnt} > 0 ) { $poolInfo = $cpoolInfo; } my $generalInfo = eval("qq{$Lang->{BackupPC_Server_Status_General_Info}}") if ( $Privileged ); my $content = eval("qq{$Lang->{BackupPC_Server_Status}}"); Header($Lang->{H_BackupPC_Server_Status}, $content); Trailer(); } sub genPoolInfo { my($name, $info) = @_; my $poolSize = sprintf("%.2f", $info->{"${name}Kb"} / (1000 * 1024)); my $poolRmSize = sprintf("%.2f", $info->{"${name}KbRm"} / (1000 * 1024)); my $poolTime = timeStamp2($info->{"${name}Time"}); $info->{"${name}FileCntRm"} = $info->{"${name}FileCntRm"} + 0; return eval("qq{$Lang->{Pool_Stat}}"); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/HostInfo.pm0000444000076500000240000004022513042250554017007 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::HostInfo package # # DESCRIPTION # # This module implements the HostInfo action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::HostInfo; use strict; use BackupPC::CGI::Lib qw(:all); sub action { my $host = $1 if ( $In{host} =~ /(.*)/ ); my($statusStr, $startIncrStr); $host =~ s/^\s+//; $host =~ s/\s+$//; if ( $host eq "" ) { ErrorExit(eval("qq{$Lang->{Unknown_host_or_user}}")); } $host = lc($host) if ( !-d "$TopDir/pc/$host" && -d "$TopDir/pc/" . lc($host) ); if ( $host =~ /\.\./ || !-d "$TopDir/pc/$host" ) { # # try to lookup by user name # if ( $host eq "" || !defined($Hosts->{$host}) ) { foreach my $h ( keys(%$Hosts) ) { if ( $Hosts->{$h}{user} eq $host || lc($Hosts->{$h}{user}) eq lc($host) ) { $host = $h; last; } } CheckPermission(); ErrorExit(eval("qq{$Lang->{Unknown_host_or_user}}")) if ( !defined($Hosts->{$host}) ); } $In{host} = $host; } GetStatusInfo("host(${EscURI($host)})"); $bpc->ConfigRead($host); %Conf = $bpc->Conf(); my $Privileged = CheckPermission($host); if ( !$Privileged ) { ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_view_information_about}}")); } ReadUserEmailInfo(); if ( $Conf{XferMethod} eq "archive" ) { my @Archives = $bpc->ArchiveInfoRead($host); my ($ArchiveStr,$warnStr); for ( my $i = 0 ; $i < @Archives ; $i++ ) { my $startTime = timeStamp2($Archives[$i]{startTime}); my $dur = $Archives[$i]{endTime} - $Archives[$i]{startTime}; $dur = 1 if ( $dur <= 0 ); my $duration = sprintf("%.1f", $dur / 60); my $Archives_Result = $Lang->{failed}; if ($Archives[$i]{result} ne "failed") { $Archives_Result = $Lang->{success}; } $ArchiveStr .= <$Archives[$i]{num} $Archives_Result $startTime $duration EOF } if ( $ArchiveStr ne "" ) { $ArchiveStr = eval("qq{$Lang->{Archive_Summary}}"); } if ( @Archives == 0 ) { $warnStr = $Lang->{There_have_been_no_archives}; } if ( $StatusHost{BgQueueOn} ) { $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon}}"); } if ( $StatusHost{UserQueueOn} ) { $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon}}"); } if ( $StatusHost{CmdQueueOn} ) { $statusStr .= eval("qq{$Lang->{A_command_for_host_is_on_the_command_queue_will_run_soon}}"); } my $content = eval("qq{$Lang->{Host__host_Archive_Summary2}}"); Header(eval("qq{$Lang->{Host__host_Archive_Summary}}"), $content, 1); Trailer(); return; } # # Normal, non-archive case # my @Backups = $bpc->BackupInfoRead($host); my($str, $sizeStr, $compStr, $errStr, $warnStr); for ( my $i = 0 ; $i < @Backups ; $i++ ) { my $startTime = timeStamp2($Backups[$i]{startTime}); my $dur = $Backups[$i]{endTime} - $Backups[$i]{startTime}; $dur = 1 if ( $dur <= 0 ); my $duration = sprintf("%.1f", $dur / 60); my $MB = sprintf("%.1f", $Backups[$i]{size} / (1024*1024)); my $MBperSec = sprintf("%.2f", $Backups[$i]{size} / (1024*1024*$dur)); my $MBExist = sprintf("%.1f", $Backups[$i]{sizeExist} / (1024*1024)); my $MBNew = sprintf("%.1f", $Backups[$i]{sizeNew} / (1024*1024)); my($MBExistComp, $ExistComp, $MBNewComp, $NewComp); if ( $Backups[$i]{sizeExist} && $Backups[$i]{sizeExistComp} ) { $MBExistComp = sprintf("%.1f", $Backups[$i]{sizeExistComp} / (1024 * 1024)); $ExistComp = sprintf("%.1f%%", 100 * (1 - $Backups[$i]{sizeExistComp} / $Backups[$i]{sizeExist})); } if ( $Backups[$i]{sizeNew} && $Backups[$i]{sizeNewComp} ) { $MBNewComp = sprintf("%.1f", $Backups[$i]{sizeNewComp} / (1024 * 1024)); $NewComp = sprintf("%.1f%%", 100 * (1 - $Backups[$i]{sizeNewComp} / $Backups[$i]{sizeNew})); } my $age = sprintf("%.1f", (time - $Backups[$i]{startTime}) / (24*3600)); my $browseURL = "$MyURL?action=browse&host=${EscURI($host)}&num=$Backups[$i]{num}"; my $level = $Backups[$i]{level}; my $filled = $Backups[$i]{noFill} ? $Lang->{No} : $Lang->{Yes}; $filled .= " ($Backups[$i]{fillFromNum}) " if ( $Backups[$i]{fillFromNum} ne "" ); my $ltype = $Lang->{"backupType_$Backups[$i]{type}"}; $str .= < $Backups[$i]{num} $ltype $filled $level $startTime $duration $age $TopDir/pc/$host/$Backups[$i]{num} EOF $sizeStr .= < $Backups[$i]{num} $ltype $Backups[$i]{nFiles} $MB $MBperSec $Backups[$i]{nFilesExist} $MBExist $Backups[$i]{nFilesNew} $MBNew EOF my $is_compress = $Backups[$i]{compress} || $Lang->{off}; if (! $ExistComp) { $ExistComp = " "; } if (! $MBExistComp) { $MBExistComp = " "; } $compStr .= < $Backups[$i]{num} $ltype $is_compress $MBExist $MBExistComp $ExistComp $MBNew $MBNewComp $NewComp EOF $errStr .= < $Backups[$i]{num} $ltype $Lang->{XferLOG}, $Lang->{Errors} $Backups[$i]{xferErrs} $Backups[$i]{xferBadFile} $Backups[$i]{xferBadShare} $Backups[$i]{tarErrs} EOF } my @Restores = $bpc->RestoreInfoRead($host); my $restoreStr; for ( my $i = 0 ; $i < @Restores ; $i++ ) { my $startTime = timeStamp2($Restores[$i]{startTime}); my $dur = $Restores[$i]{endTime} - $Restores[$i]{startTime}; $dur = 1 if ( $dur <= 0 ); my $duration = sprintf("%.1f", $dur / 60); my $MB = sprintf("%.1f", $Restores[$i]{size} / (1024*1024)); my $MBperSec = sprintf("%.2f", $Restores[$i]{size} / (1024*1024*$dur)); my $Restores_Result = $Lang->{failed}; if ($Restores[$i]{result} ne "failed") { $Restores_Result = $Lang->{success}; } $restoreStr .= <$Restores[$i]{num} $Restores_Result $startTime $duration $Restores[$i]{nFiles} $MB $Restores[$i]{tarCreateErrs} $Restores[$i]{xferErrs} EOF } if ( $restoreStr ne "" ) { $restoreStr = eval("qq{$Lang->{Restore_Summary}}"); } if ( @Backups == 0 ) { $warnStr = $Lang->{This_PC_has_never_been_backed_up}; } if ( defined($Hosts->{$host}) ) { my $user = $Hosts->{$host}{user}; my @moreUsers = sort(keys(%{$Hosts->{$host}{moreUsers}})); my $moreUserStr; foreach my $u ( sort(keys(%{$Hosts->{$host}{moreUsers}})) ) { $moreUserStr .= ", " if ( $moreUserStr ne "" ); $moreUserStr .= "${UserLink($u)}"; } if ( $moreUserStr ne "" ) { $moreUserStr = " ($Lang->{and} $moreUserStr).\n"; } else { $moreUserStr = ".\n"; } if ( $user ne "" ) { $statusStr .= eval("qq{$Lang->{This_PC_is_used_by}$moreUserStr}"); } if ( defined($UserEmailInfo{$user}) && defined($UserEmailInfo{$user}{$host}) && defined($UserEmailInfo{$user}{$host}{lastSubj}) ) { my $mailTime = timeStamp2($UserEmailInfo{$user}{$host}{lastTime}); my $subj = $UserEmailInfo{$user}{$host}{lastSubj}; $statusStr .= eval("qq{$Lang->{Last_email_sent_to__was_at___subject}}"); } elsif ( defined($UserEmailInfo{$user}) && $UserEmailInfo{$user}{lastHost} eq $host && defined($UserEmailInfo{$user}{lastSubj}) ) { # # Old format %UserEmailInfo - pre 3.2.0. # my $mailTime = timeStamp2($UserEmailInfo{$user}{lastTime}); my $subj = $UserEmailInfo{$user}{lastSubj}; $statusStr .= eval("qq{$Lang->{Last_email_sent_to__was_at___subject}}"); } } if ( defined($Jobs{$host}) ) { my $startTime = timeStamp2($Jobs{$host}{startTime}); (my $cmd = $Jobs{$host}{cmd}) =~ s/$BinDir\///g; $statusStr .= eval("qq{$Lang->{The_command_cmd_is_currently_running_for_started}}"); } if ( $StatusHost{BgQueueOn} ) { $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon}}"); } if ( $StatusHost{UserQueueOn} ) { $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon}}"); } if ( $StatusHost{CmdQueueOn} ) { $statusStr .= eval("qq{$Lang->{A_command_for_host_is_on_the_command_queue_will_run_soon}}"); } my $startTime = timeStamp2($StatusHost{endTime} == 0 ? $StatusHost{startTime} : $StatusHost{endTime}); my $reason = ""; if ( $StatusHost{reason} ne "" ) { $reason = " ($Lang->{$StatusHost{reason}})"; } $statusStr .= eval("qq{$Lang->{Last_status_is_state_StatusHost_state_reason_as_of_startTime}}"); if ( $StatusHost{state} ne "Status_backup_in_progress" && $StatusHost{state} ne "Status_restore_in_progress" && $StatusHost{error} ne "" ) { $statusStr .= eval("qq{$Lang->{Last_error_is____EscHTML_StatusHost_error}}"); } my $priorStr = "Pings"; if ( $StatusHost{deadCnt} > 0 ) { $statusStr .= eval("qq{$Lang->{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times}}"); $priorStr = $Lang->{Prior_to_that__pings}; } if ( $StatusHost{aliveCnt} > 0 ) { $statusStr .= eval("qq{$Lang->{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times}}"); if ( (@{$Conf{BlackoutPeriods}} || defined($Conf{BlackoutHourBegin})) && $StatusHost{aliveCnt} >= $Conf{BlackoutGoodCnt} && $Conf{BlackoutGoodCnt} >= 0 ) { # # Handle backward compatibility with original separate scalar # blackout parameters. # if ( defined($Conf{BlackoutHourBegin}) ) { push(@{$Conf{BlackoutPeriods}}, { hourBegin => $Conf{BlackoutHourBegin}, hourEnd => $Conf{BlackoutHourEnd}, weekDays => $Conf{BlackoutWeekDays}, } ); } # # TODO: this string needs i18n. Also, comma-separated # list with "and" for the last element might not translate # correctly. # my(@days) = qw(Sun Mon Tue Wed Thu Fri Sat); my $blackoutStr; my $periodCnt = 0; foreach my $p ( @{$Conf{BlackoutPeriods}} ) { next if ( ref($p->{weekDays}) ne "ARRAY" || !defined($p->{hourBegin}) || !defined($p->{hourEnd}) ); my $days = join(", ", @days[@{$p->{weekDays}}]); my $t0 = sprintf("%d:%02d", $p->{hourBegin}, 60 * ($p->{hourBegin} - int($p->{hourBegin}))); my $t1 = sprintf("%d:%02d", $p->{hourEnd}, 60 * ($p->{hourEnd} - int($p->{hourEnd}))); if ( $periodCnt ) { $blackoutStr .= ", "; if ( $periodCnt == @{$Conf{BlackoutPeriods}} - 1 ) { $blackoutStr .= eval("qq{$Lang->{and}}"); $blackoutStr .= " "; } } $blackoutStr .= eval("qq{$Lang->{__time0_to__time1_on__days}}"); $periodCnt++; } $statusStr .= eval("qq{$Lang->{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___}}"); } } if ( $StatusHost{backoffTime} > time ) { my $hours = sprintf("%.1f", ($StatusHost{backoffTime} - time) / 3600); $statusStr .= eval("qq{$Lang->{Backups_are_deferred_for_hours_hours_change_this_number}}"); } if ( @Backups ) { # only allow incremental if there are already some backups $startIncrStr = < EOF } $startIncrStr = eval("qq{$startIncrStr}"); my $content = eval("qq{$Lang->{Host__host_Backup_Summary2}}"); Header(eval("qq{$Lang->{Host__host_Backup_Summary}}"), $content); Trailer(); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/Lib.pm0000444000076500000240000004427313042250554015773 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::Lib package # # DESCRIPTION # # This library defines a BackupPC::Lib class and a variety of utility # functions used by BackupPC. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::Lib; use strict; use BackupPC::Lib; require Exporter; use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS ); use vars qw($Cgi %In $MyURL $User %Conf $TopDir $LogDir $BinDir $bpc); use vars qw(%Status %Info %Jobs @BgQueue @UserQueue @CmdQueue %QueueLen %StatusHost); use vars qw($Hosts $HostsMTime $ConfigMTime $PrivAdmin); use vars qw(%UserEmailInfo $UserEmailInfoMTime %RestoreReq %ArchiveReq); use vars qw($Lang); @ISA = qw(Exporter); @EXPORT = qw( ); @EXPORT_OK = qw( timeStamp2 HostLink UserLink EscHTML EscURI ErrorExit ServerConnect GetStatusInfo ReadUserEmailInfo CheckPermission GetUserHosts ConfirmIPAddress Header Trailer NavSectionTitle NavSectionStart NavSectionEnd NavLink h1 h2 $Cgi %In $MyURL $User %Conf $TopDir $LogDir $BinDir $bpc %Status %Info %Jobs @BgQueue @UserQueue @CmdQueue %QueueLen %StatusHost $Hosts $HostsMTime $ConfigMTime $PrivAdmin %UserEmailInfo $UserEmailInfoMTime %RestoreReq %ArchiveReq $Lang ); %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ], ); sub NewRequest { $Cgi = new CGI; %In = $Cgi->Vars; if ( !defined($bpc) ) { ErrorExit($Lang->{BackupPC__Lib__new_failed__check_apache_error_log}) if ( !($bpc = BackupPC::Lib->new(undef, undef, undef, 1)) ); $TopDir = $bpc->TopDir(); $LogDir = $bpc->LogDir(); $BinDir = $bpc->BinDir(); %Conf = $bpc->Conf(); $Lang = $bpc->Lang(); $ConfigMTime = $bpc->ConfigMTime(); umask($Conf{UmaskMode}); } elsif ( $bpc->ConfigMTime() != $ConfigMTime ) { $bpc->ConfigRead(); $TopDir = $bpc->TopDir(); $LogDir = $bpc->LogDir(); $BinDir = $bpc->BinDir(); %Conf = $bpc->Conf(); $Lang = $bpc->Lang(); $ConfigMTime = $bpc->ConfigMTime(); umask($Conf{UmaskMode}); } # # Default REMOTE_USER so in a miminal installation the user # has a sensible default. # $ENV{REMOTE_USER} = $Conf{BackupPCUser} if ( $ENV{REMOTE_USER} eq "" ); # # We require that Apache pass in $ENV{SCRIPT_NAME} and $ENV{REMOTE_USER}. # The latter requires .ht_access style authentication. Replace this # code if you are using some other type of authentication, and have # a different way of getting the user name. # $MyURL = $ENV{SCRIPT_NAME}; $User = $ENV{REMOTE_USER}; # # Handle LDAP uid=user when using mod_authz_ldap and otherwise untaint # $User = $1 if ( $User =~ /uid=([^,]+)/i || $User =~ /(.*)/ ); # # Clean up %ENV for taint checking # delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; $ENV{PATH} = $Conf{MyPath}; # # Verify we are running as the correct user # if ( $Conf{BackupPCUserVerify} && $> != (my $uid = (getpwnam($Conf{BackupPCUser}))[2]) ) { ErrorExit(eval("qq{$Lang->{Wrong_user__my_userid_is___}}"), < This is an installation problem. If you are using mod_perl then it appears that Apache is not running as user $Conf{BackupPCUser}. If you are not using mod_perl, then most like setuid is not working properly on BackupPC_Admin. Check the permissions on $Conf{CgiDir}/BackupPC_Admin and look at the documentation. EOF } if ( !defined($Hosts) || $bpc->HostsMTime() != $HostsMTime ) { $HostsMTime = $bpc->HostsMTime(); $Hosts = $bpc->HostInfoRead(); # turn moreUsers list into a hash for quick lookups foreach my $host (keys %$Hosts) { $Hosts->{$host}{moreUsers} = {map {$_, 1} split(",", $Hosts->{$host}{moreUsers}) } } } # # Untaint the host name # if ( $In{host} =~ /^([\w.\s-]+)$/ ) { $In{host} = $1; } else { delete($In{host}); } } sub timeStamp2 { my $now = $_[0] == 0 ? time : $_[0]; my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now); $mon++; if ( $Conf{CgiDateFormatMMDD} == 2 ) { $year += 1900; return sprintf("%04d-%02d-%02d %02d:%02d", $year, $mon, $mday, $hour, $min); } elsif ( $Conf{CgiDateFormatMMDD} ) { # # Add the year if the time is more than 330 days ago # if ( time - $now > 330 * 24 * 3600 ) { $year -= 100; return sprintf("$mon/$mday/%02d %02d:%02d", $year, $hour, $min); } else { return sprintf("$mon/$mday %02d:%02d", $hour, $min); } } else { # # Add the year if the time is more than 330 days ago # if ( time - $now > 330 * 24 * 3600 ) { $year -= 100; return sprintf("$mday/$mon/%02d %02d:%02d", $year, $hour, $min); } else { return sprintf("$mday/$mon %02d:%02d", $hour, $min); } } } sub HostLink { my($host) = @_; my($s); if ( defined($Hosts->{$host}) || defined($Status{$host}) ) { $s = "$host"; } else { $s = $host; } return \$s; } sub UserLink { my($user) = @_; my($s); return \$user if ( $user eq "" || $Conf{CgiUserUrlCreate} eq "" ); if ( $Conf{CgiUserHomePageCheck} eq "" || -f sprintf($Conf{CgiUserHomePageCheck}, $user, $user, $user) ) { $s = "$user"; } else { $s = $user; } return \$s; } sub EscHTML { my($s) = @_; $s =~ s/&/&/g; $s =~ s/\"/"/g; $s =~ s/>/>/g; $s =~ s/\n

    ", @mesg); if ( !defined($ENV{REMOTE_USER}) ) { $mesg .= < Note: \$ENV{REMOTE_USER} is not set, which could mean there is an installation problem. BackupPC_Admin expects Apache to authenticate the user and pass their user name into this script as the REMOTE_USER environment variable. See the documentation. EOF } $bpc->ServerMesg("log User $User (host=$In{host}) got CGI error: $head") if ( defined($bpc) ); if ( !defined($Lang->{Error}) ) { $mesg = <$mesg

    EOF Header("BackupPC: Error", $content); Trailer(); } else { my $content = eval("qq{$Lang->{Error____head}}"); Header(eval("qq{$Lang->{Error}}"), $content); Trailer(); } exit(1); } sub ServerConnect { # # Verify that the server connection is ok # return if ( $bpc->ServerOK() ); $bpc->ServerDisconnect(); if ( my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}) ) { if ( CheckPermission() && -f $Conf{ServerInitdPath} && $Conf{ServerInitdStartCmd} ne "" ) { my $content = eval("qq{$Lang->{Admin_Start_Server}}"); Header(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}"), $content); Trailer(); exit(1); } else { ErrorExit(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}"), eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server_error_message}}")); } } } sub GetStatusInfo { my($status) = @_; ServerConnect(); %Status = () if ( $status =~ /\bhosts\b/ ); %StatusHost = () if ( $status =~ /\bhost\(/ ); my $reply = $bpc->ServerMesg("status $status"); $reply = $1 if ( $reply =~ /(.*)/s ); eval($reply); # ignore status related to admin and trashClean jobs if ( $status =~ /\bhosts\b/ ) { foreach my $host ( grep(/admin/, keys(%Status)) ) { delete($Status{$host}) if ( $bpc->isAdminJob($host) ); } delete($Status{$bpc->trashJob}); } } sub ReadUserEmailInfo { if ( (stat("$LogDir/UserEmailInfo.pl"))[9] != $UserEmailInfoMTime ) { do "$LogDir/UserEmailInfo.pl"; $UserEmailInfoMTime = (stat("$LogDir/UserEmailInfo.pl"))[9]; } } # # Check if the user is privileged. A privileged user can access # any information (backup files, logs, status pages etc). # # A user is privileged if they belong to the group # $Conf{CgiAdminUserGroup}, or they are in $Conf{CgiAdminUsers} # or they are the user assigned to a host in the host file. # sub CheckPermission { my($host) = @_; my $Privileged = 0; return 0 if ( $User eq "" && $Conf{CgiAdminUsers} ne "*" || $host ne "" && !defined($Hosts->{$host}) ); if ( $Conf{CgiAdminUserGroup} ne "" ) { my($n,$p,$gid,$mem) = getgrnam($Conf{CgiAdminUserGroup}); $Privileged ||= ($mem =~ /\b\Q$User\E\b/); } if ( $Conf{CgiAdminUsers} ne "" ) { $Privileged ||= ($Conf{CgiAdminUsers} =~ /\b\Q$User\E\b/); $Privileged ||= $Conf{CgiAdminUsers} eq "*"; } $PrivAdmin = $Privileged; return $Privileged if ( !defined($host) ); $Privileged ||= $User eq $Hosts->{$host}{user}; $Privileged ||= defined($Hosts->{$host}{moreUsers}{$User}); return $Privileged; } # # Returns the list of hosts that should appear in the navigation bar # for this user. If $getAll is set, the admin gets all the hosts. # Otherwise, regular users get hosts for which they are the user or # are listed in the moreUsers column in the hosts file. # sub GetUserHosts { my($getAll) = @_; my @hosts; if ( $getAll && CheckPermission() ) { @hosts = sort keys %$Hosts; } else { @hosts = sort grep { $Hosts->{$_}{user} eq $User || defined($Hosts->{$_}{moreUsers}{$User}) } keys(%$Hosts); } return @hosts; } # # Given a host name tries to find the IP address. For non-dhcp hosts # we just return the host name. For dhcp hosts we check the address # the user is using ($ENV{REMOTE_ADDR}) and also the last-known IP # address for $host. (Later we should replace this with a broadcast # nmblookup.) # sub ConfirmIPAddress { my($host) = @_; my $ipAddr = $host; if ( defined($Hosts->{$host}) && $Hosts->{$host}{dhcp} && $ENV{REMOTE_ADDR} =~ /^(\d+[\.\d]*)$/ ) { $ipAddr = $1; my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($ipAddr); if ( $netBiosHost ne $host ) { my($tryIP); GetStatusInfo("host(${EscURI($host)})"); if ( defined($StatusHost{dhcpHostIP}) && $StatusHost{dhcpHostIP} ne $ipAddr ) { $tryIP = eval("qq{$Lang->{tryIP}}"); ($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($StatusHost{dhcpHostIP}); } if ( $netBiosHost ne $host ) { ErrorExit(eval("qq{$Lang->{Can_t_find_IP_address_for}}"), eval("qq{$Lang->{host_is_a_DHCP_host}}")); } $ipAddr = $StatusHost{dhcpHostIP}; } } return $ipAddr; } ########################################################################### # HTML layout subroutines ########################################################################### sub Header { my($title, $content, $noBrowse, $contentSub, $contentPost) = @_; my @adminLinks = ( { link => "", name => $Lang->{Status}}, { link => "?action=summary", name => $Lang->{PC_Summary}}, { link => "?action=editConfig", name => $Lang->{CfgEdit_Edit_Config}, priv => 1}, { link => "?action=editConfig&newMenu=hosts", name => $Lang->{CfgEdit_Edit_Hosts}, priv => 1}, { link => "?action=adminOpts", name => $Lang->{Admin_Options}, priv => 1}, { link => "?action=view&type=LOG", name => $Lang->{LOG_file}, priv => 1}, { link => "?action=LOGlist", name => $Lang->{Old_LOGs}, priv => 1}, { link => "?action=emailSummary", name => $Lang->{Email_summary}, priv => 1}, { link => "?action=queue", name => $Lang->{Current_queues}, priv => 1}, @{$Conf{CgiNavBarLinks} || []}, ); my $host = $In{host}; binmode(STDOUT, ":utf8"); print $Cgi->header(-charset => "utf-8"); print < $title $Conf{CgiHeaders}
    EOF if ( defined($Hosts) && defined($host) && defined($Hosts->{$host}) ) { print "
    "; NavSectionTitle("${EscHTML($host)}"); print < \n"; } print("
    \n$content\n"); if ( defined($contentSub) && ref($contentSub) eq "CODE" ) { while ( (my $s = &$contentSub()) ne "" ) { print($s); } } print($contentPost) if ( defined($contentPost) ); print <

    EOF } sub Trailer { print < EOF } sub NavSectionTitle { my($head) = @_; print <$head
    EOF } sub NavSectionStart { } sub NavSectionEnd { } sub NavLink { my($link, $text) = @_; if ( defined($link) ) { my($class); $class = " class=\"NavCurrent\"" if ( length($link) && $ENV{REQUEST_URI} =~ /\Q$link\E$/ || $link eq "" && $ENV{REQUEST_URI} !~ /\?/ ); $link = "$MyURL$link" if ( $link eq "" || $link =~ /^\?/ ); print <$text EOF } else { print < EOF } } sub h1 { my($str) = @_; return \<$str EOF } sub h2 { my($str) = @_; return \<$str EOF } BackupPC-3.3.2/lib/BackupPC/CGI/LOGlist.pm0000444000076500000240000000572413042250554016600 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::LOGlist package # # DESCRIPTION # # This module implements the LOGlist action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::LOGlist; use strict; use BackupPC::CGI::Lib qw(:all); sub action { my $Privileged = CheckPermission($In{host}); if ( !$Privileged ) { ErrorExit($Lang->{Only_privileged_users_can_view_log_files}); } my $host = $In{host}; my($url0, $hdr, @files, $str); if ( $host ne "" ) { $url0 = "&host=${EscURI($host)}"; $hdr = "for host $host"; } else { $url0 = ""; $hdr = ""; } foreach my $file ( $bpc->sortedPCLogFiles($host) ) { my $url1 = "&num=$1" if ( $file =~ /LOG\.(\d+)(\.z)?$/ ); $url1 = "&num=" if ( $file =~ /LOG(\.z)?$/ ); next if ( !-f $file ); my $mtimeStr = $bpc->timeStamp((stat($file))[9], 1); my $size = (stat($file))[7]; (my $fStr = $file) =~ s{.*/}{}; $str .= < $fStr $size $mtimeStr EOF } my $content = eval("qq{$Lang->{Log_File_History__hdr}}"); Header($Lang->{BackupPC__Log_File_History}, $content, !-f "$TopDir/pc/$host/backups"); Trailer(); } sub compareLOGName { #my($a, $b) = @_; my $na = $1 if ( $a =~ /LOG\.(\d+)/ ); my $nb = $1 if ( $b =~ /LOG\.(\d+)/ ); if ( length($na) >= 5 && length($nb) >= 5 ) { # # Both new style. Bigger numbers are more recent. # return $nb <=> $na; } elsif ( length($na) >= 5 && length($nb) < 5 ) { return -1; } elsif ( length($na) < 5 && length($nb) >= 5 ) { return 1; } else { # # Both old style. Smaller numbers are more recent. # return $na - $nb; } } 1; BackupPC-3.3.2/lib/BackupPC/CGI/Queue.pm0000444000076500000240000000510013042250554016333 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::Queue package # # DESCRIPTION # # This module implements the Queue action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::Queue; use strict; use BackupPC::CGI::Lib qw(:all); sub action { my($strBg, $strUser, $strCmd); GetStatusInfo("queues"); my $Privileged = CheckPermission(); if ( !$Privileged ) { ErrorExit($Lang->{Only_privileged_users_can_view_queues_}); } while ( @BgQueue ) { my $req = pop(@BgQueue); my($reqTime) = timeStamp2($req->{reqTime}); $strBg .= < ${HostLink($req->{host})} $reqTime $req->{user} EOF } while ( @UserQueue ) { my $req = pop(@UserQueue); my $reqTime = timeStamp2($req->{reqTime}); $strUser .= < ${HostLink($req->{host})} $reqTime $req->{user} EOF } while ( @CmdQueue ) { my $req = pop(@CmdQueue); my $reqTime = timeStamp2($req->{reqTime}); (my $cmd = $bpc->execCmd2ShellCmd(@{$req->{cmd}})) =~ s/$BinDir\///; $strCmd .= < ${HostLink($req->{host})} $reqTime $req->{user} $cmd EOF } my $content = eval ( "qq{$Lang->{Backup_Queue_Summary}}"); Header($Lang->{BackupPC__Queue_Summary}, $content); Trailer(); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/ReloadServer.pm0000444000076500000240000000303013042250554017644 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::ReloadServer package # # DESCRIPTION # # This module implements the ReloadServer action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::ReloadServer; use strict; use BackupPC::CGI::Lib qw(:all); sub action { ServerConnect(); $bpc->ServerMesg("log User $User requested server configuration reload"); $bpc->ServerMesg("server reload"); print $Cgi->redirect($MyURL); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/Restore.pm0000444000076500000240000003061113042250554016677 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::Restore package # # DESCRIPTION # # This module implements the Restore action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::Restore; use strict; use BackupPC::CGI::Lib qw(:all); use BackupPC::Xfer; use Data::Dumper; use File::Path; use Encode qw/decode_utf8/; sub action { my($str, $reply, $content); my $Privileged = CheckPermission($In{host}); if ( !$Privileged ) { ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_restore_backup_files}}")); } my $host = $In{host}; my $num = $In{num}; my $share = $In{share}; my(@fileList, $fileListStr, $hiddenStr, $pathHdr, $badFileCnt); my @Backups = $bpc->BackupInfoRead($host); ServerConnect(); if ( !defined($Hosts->{$host}) ) { ErrorExit(eval("qq{$Lang->{Bad_host_name}}")); } for ( my $i = 0 ; $i < $In{fcbMax} ; $i++ ) { next if ( !defined($In{"fcb$i"}) ); (my $name = $In{"fcb$i"}) =~ s/%([0-9A-F]{2})/chr(hex($1))/eg; $badFileCnt++ if ( $name =~ m{(^|/)\.\.(/|$)} ); if ( @fileList == 0 ) { $pathHdr = substr($name, 0, rindex($name, "/")); } else { while ( substr($name, 0, length($pathHdr)) ne $pathHdr ) { $pathHdr = substr($pathHdr, 0, rindex($pathHdr, "/")); } } push(@fileList, $name); $hiddenStr .= < EOF $name = decode_utf8($name); $fileListStr .= < ${EscHTML($name)} EOF } $hiddenStr .= "\n"; $hiddenStr .= "\n"; $badFileCnt++ if ( $In{pathHdr} =~ m{(^|/)\.\.(/|$)} ); $badFileCnt++ if ( $In{num} =~ m{(^|/)\.\.(/|$)} ); if ( @fileList == 0 ) { ErrorExit($Lang->{You_haven_t_selected_any_files__please_go_Back_to}); } if ( $badFileCnt ) { ErrorExit($Lang->{Nice_try__but_you_can_t_put}); } $pathHdr = "/" if ( $pathHdr eq "" ); if ( $In{type} != 0 && @fileList == $In{fcbMax} ) { # # All the files in the list were selected, so just restore the # entire parent directory # @fileList = ( $pathHdr ); } if ( $In{type} == 0 ) { # # Build list of hosts # my($hostDestSel, @hosts, $gotThisHost, $directHost); # # Check all the hosts this user has permissions for # and make sure direct restore is enabled. # Note: after this loop we have the config for the # last host in @hosts, not the original $In{host}!! # $directHost = $host; foreach my $h ( GetUserHosts(1) ) { # # Pick up the host's config file # $bpc->ConfigRead($h); %Conf = $bpc->Conf(); if ( BackupPC::Xfer::restoreEnabled( \%Conf ) ) { # # Direct restore is enabled # push(@hosts, $h); $gotThisHost = 1 if ( $h eq $host ); } } $directHost = $hosts[0] if ( !$gotThisHost && @hosts ); foreach my $h ( @hosts ) { my $sel = " selected" if ( $h eq $directHost ); $hostDestSel .= ""; } # # Tell the user what options they have # $pathHdr = decode_utf8($pathHdr); $share = decode_utf8($share); $content = eval("qq{$Lang->{Restore_Options_for__host2}}"); # # Decide if option 1 (direct restore) is available based # on whether the restore command is set. # if ( $hostDestSel ne "" ) { $content .= eval( "qq{$Lang->{Restore_Options_for__host_Option1}}"); } else { my $hostDest = $In{host}; $content .= eval( "qq{$Lang->{Restore_Options_for__host_Option1_disabled}}"); } # # Verify that Archive::Zip is available before showing the # zip restore option # if ( eval { require Archive::Zip } ) { $content .= eval("qq{$Lang->{Option_2__Download_Zip_archive}}"); } else { $content .= eval("qq{$Lang->{Option_2__Download_Zip_archive2}}"); } $content .= eval("qq{$Lang->{Option_3__Download_Zip_archive}}"); Header(eval("qq{$Lang->{Restore_Options_for__host}}"), $content); Trailer(); } elsif ( $In{type} == 1 ) { # # Provide the selected files via a tar archive. # my @fileListTrim = @fileList; if ( @fileListTrim > 10 ) { @fileListTrim = (@fileListTrim[0..9], '...'); } $bpc->ServerMesg("log User $User downloaded tar archive for $host," . " backup $num; files were: " . join(", ", @fileListTrim)); my @pathOpts; if ( $In{relative} ) { @pathOpts = ("-r", $pathHdr, "-p", ""); } # # generate a file name based on the host and backup date # my $fileName = "restore_$host"; for ( my $i = 0 ; $i < @Backups ; $i++ ) { next if ( $Backups[$i]{num} != $num ); my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Backups[$i]{startTime}); $fileName .= sprintf("_%d-%02d-%02d", 1900 + $year, 1 + $mon, $mday); last; } print(STDOUT <cmdSystemOrEvalLong(["$BinDir/BackupPC_tarCreate", "-h", $host, "-n", $num, "-s", $share, @pathOpts, @fileList ], sub { print(@_); }, 1, # ignore stderr ); } elsif ( $In{type} == 2 ) { # # Provide the selected files via a zip archive. # my @fileListTrim = @fileList; if ( @fileListTrim > 10 ) { @fileListTrim = (@fileListTrim[0..9], '...'); } $bpc->ServerMesg("log User $User downloaded zip archive for $host," . " backup $num; files were: " . join(", ", @fileListTrim)); my @pathOpts; if ( $In{relative} ) { @pathOpts = ("-r", $pathHdr, "-p", ""); } # # generate a file name based on the host and backup date # my $fileName = "restore_$host"; for ( my $i = 0 ; $i < @Backups ; $i++ ) { next if ( $Backups[$i]{num} != $num ); my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Backups[$i]{startTime}); $fileName .= sprintf("_%d-%02d-%02d", 1900 + $year, 1 + $mon, $mday); last; } print(STDOUT <cmdSystemOrEvalLong(["$BinDir/BackupPC_zipCreate", "-h", $host, "-n", $num, "-c", $In{compressLevel}, "-s", $share, "-e", $In{codePage}, @pathOpts, @fileList ], sub { print(@_); }, 1, # ignore stderr ); } elsif ( $In{type} == 3 ) { # # Do restore directly onto host # if ( !defined($Hosts->{$In{hostDest}}) ) { ErrorExit(eval("qq{$Lang->{Host__doesn_t_exist}}")); } if ( !CheckPermission($In{hostDest}) ) { ErrorExit(eval("qq{$Lang->{You_don_t_have_permission_to_restore_onto_host}}")); } # # Pick up the destination host's config file # my $hostDest = $1 if ( $In{hostDest} =~ /(.*)/ ); $bpc->ConfigRead($hostDest); %Conf = $bpc->Conf(); # # Decide if option 1 (direct restore) is available based # on whether the restore command is set. # unless ( BackupPC::Xfer::restoreEnabled( \%Conf ) ) { ErrorExit(eval("qq{$Lang->{Restore_Options_for__host_Option1_disabled}}")); } $fileListStr = ""; foreach my $f ( @fileList ) { my $targetFile = $f; (my $strippedShare = $share) =~ s/^\///; (my $strippedShareDest = $In{shareDest}) =~ s/^\///; substr($targetFile, 0, length($pathHdr)) = "/$In{pathHdr}/"; $targetFile =~ s{//+}{/}g; $strippedShareDest = decode_utf8($strippedShareDest); $targetFile = decode_utf8($targetFile); $strippedShare = decode_utf8($strippedShare); $f = decode_utf8($f); $fileListStr .= <$host:/$strippedShare$f$In{hostDest}:/$strippedShareDest$targetFile EOF } $In{shareDest} = decode_utf8($In{shareDest}); $In{pathHdr} = decode_utf8($In{pathHdr}); my $content = eval("qq{$Lang->{Are_you_sure}}"); Header(eval("qq{$Lang->{Restore_Confirm_on__host}}"), $content); Trailer(); } elsif ( $In{type} == 4 ) { if ( !defined($Hosts->{$In{hostDest}}) ) { ErrorExit(eval("qq{$Lang->{Host__doesn_t_exist}}")); } if ( !CheckPermission($In{hostDest}) ) { ErrorExit(eval("qq{$Lang->{You_don_t_have_permission_to_restore_onto_host}}")); } my $hostDest = $1 if ( $In{hostDest} =~ /(.+)/ ); my $ipAddr = ConfirmIPAddress($hostDest); # # Prepare and send the restore request. We write the request # information using Data::Dumper to a unique file, # $TopDir/pc/$hostDest/restoreReq.$$.n. We use a file # in case the list of files to restore is very long. # my $reqFileName; for ( my $i = 0 ; ; $i++ ) { $reqFileName = "restoreReq.$$.$i"; last if ( !-f "$TopDir/pc/$hostDest/$reqFileName" ); } my $inPathHdr = $In{pathHdr}; $inPathHdr = "/$inPathHdr" if ( $inPathHdr !~ m{^/} ); $inPathHdr = "$inPathHdr/" if ( $inPathHdr !~ m{/$} ); my %restoreReq = ( # source of restore is hostSrc, #num, path shareSrc/pathHdrSrc num => $In{num}, hostSrc => $host, shareSrc => $share, pathHdrSrc => $pathHdr, # destination of restore is hostDest:shareDest/pathHdrDest hostDest => $hostDest, shareDest => $In{shareDest}, pathHdrDest => $inPathHdr, # list of files to restore fileList => \@fileList, # other info user => $User, reqTime => time, ); my($dump) = Data::Dumper->new( [ \%restoreReq], [qw(*RestoreReq)]); $dump->Indent(1); eval { mkpath("$TopDir/pc/$hostDest", 0, 0777) } if ( !-d "$TopDir/pc/$hostDest" ); my $openPath = "$TopDir/pc/$hostDest/$reqFileName"; if ( open(REQ, ">", $openPath) ) { binmode(REQ); print(REQ $dump->Dump); close(REQ); } else { ErrorExit(eval("qq{$Lang->{Can_t_open_create__openPath}}")); } $reply = $bpc->ServerMesg("restore ${EscURI($ipAddr)}" . " ${EscURI($hostDest)} $User $reqFileName"); $str = eval("qq{$Lang->{Restore_requested_to_host__hostDest__backup___num}}"); my $content = eval("qq{$Lang->{Reply_from_server_was___reply}}"); Header(eval("qq{$Lang->{Restore_Requested_on__hostDest}}"), $content); Trailer(); } } 1; BackupPC-3.3.2/lib/BackupPC/CGI/RestoreFile.pm0000444000076500000240000001550213042250554017501 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::RestoreFile package # # DESCRIPTION # # This module implements the RestoreFile action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::RestoreFile; use strict; use BackupPC::CGI::Lib qw(:all); use BackupPC::FileZIO; use BackupPC::Attrib qw(:all); use BackupPC::View; use Encode qw/from_to decode_utf8/; sub action { my $num = $In{num}; my $share = $In{share}; my $dir = $In{dir}; ErrorExit(eval("qq{$Lang->{Invalid_number__num}}")) if ( $num ne "" && $num !~ /^\d+$/ ); ErrorExit($Lang->{Nice_try__but_you_can_t_put}) if ( $dir =~ m{(^|/)\.\.(/|$)} ); restoreFile($In{host}, $num, $share, $dir); } sub restoreFile { my($host, $num, $share, $dir, $skipHardLink, $origName) = @_; my($Privileged) = CheckPermission($host); # # Some common content (media) types from www.iana.org (via MIME::Types). # my $Ext2ContentType = { 'asc' => 'text/plain', 'avi' => 'video/x-msvideo', 'bmp' => 'image/bmp', 'book' => 'application/x-maker', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'csh' => 'application/x-csh', 'csv' => 'text/comma-separated-values', 'c' => 'text/plain', 'deb' => 'application/x-debian-package', 'doc' => 'application/msword', 'dot' => 'application/msword', 'dtd' => 'text/xml', 'dvi' => 'application/x-dvi', 'eps' => 'application/postscript', 'fb' => 'application/x-maker', 'fbdoc'=> 'application/x-maker', 'fm' => 'application/x-maker', 'frame'=> 'application/x-maker', 'frm' => 'application/x-maker', 'gif' => 'image/gif', 'gtar' => 'application/x-gtar', 'gz' => 'application/x-gzip', 'hh' => 'text/plain', 'hpp' => 'text/plain', 'h' => 'text/plain', 'html' => 'text/html', 'htmlx'=> 'text/html', 'htm' => 'text/html', 'iges' => 'model/iges', 'igs' => 'model/iges', 'jpeg' => 'image/jpeg', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'js' => 'application/x-javascript', 'latex'=> 'application/x-latex', 'maker'=> 'application/x-maker', 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'movie'=> 'video/x-sgi-movie', 'mov' => 'video/quicktime', 'mp2' => 'audio/mpeg', 'mp3' => 'audio/mpeg', 'mpeg' => 'video/mpeg', 'mpg' => 'video/mpeg', 'mpp' => 'application/vnd.ms-project', 'pdf' => 'application/pdf', 'pgp' => 'application/pgp-signature', 'php' => 'application/x-httpd-php', 'pht' => 'application/x-httpd-php', 'phtml'=> 'application/x-httpd-php', 'png' => 'image/png', 'ppm' => 'image/x-portable-pixmap', 'ppt' => 'application/powerpoint', 'ppt' => 'application/vnd.ms-powerpoint', 'ps' => 'application/postscript', 'qt' => 'video/quicktime', 'rgb' => 'image/x-rgb', 'rtf' => 'application/rtf', 'rtf' => 'text/rtf', 'shar' => 'application/x-shar', 'shtml'=> 'text/html', 'swf' => 'application/x-shockwave-flash', 'tex' => 'application/x-tex', 'texi' => 'application/x-texinfo', 'texinfo'=> 'application/x-texinfo', 'tgz' => 'application/x-gtar', 'tiff' => 'image/tiff', 'tif' => 'image/tiff', 'txt' => 'text/plain', 'vcf' => 'text/x-vCard', 'vrml' => 'model/vrml', 'wav' => 'audio/x-wav', 'wmls' => 'text/vnd.wap.wmlscript', 'wml' => 'text/vnd.wap.wml', 'wrl' => 'model/vrml', 'xls' => 'application/vnd.ms-excel', 'xml' => 'text/xml', 'xwd' => 'image/x-xwindowdump', 'z' => 'application/x-compress', 'zip' => 'application/zip', %{$Conf{CgiExt2ContentType}}, # add site-specific values }; if ( !$Privileged ) { ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_restore_backup_files2}}")); } $bpc->ConfigRead($host); %Conf = $bpc->Conf(); ServerConnect(); ErrorExit($Lang->{Empty_host_name}) if ( $host eq "" ); $dir = "/" if ( $dir eq "" ); my @Backups = $bpc->BackupInfoRead($host); my $view = BackupPC::View->new($bpc, $host, \@Backups); my $a = $view->fileAttrib($num, $share, $dir); if ( $dir =~ m{(^|/)\.\.(/|$)} || !defined($a) ) { $dir = decode_utf8($dir); ErrorExit("Can't restore bad file ${EscHTML($dir)} (${EscHTML($num)}, ${EscHTML($share)})"); } my $f = BackupPC::FileZIO->open($a->{fullPath}, 0, $a->{compress}); if ( !defined($f) ) { my $fullPath = decode_utf8($a->{fullPath}); ErrorExit("Unable to open file ${EscHTML($fullPath)} (${EscHTML($num)}, ${EscHTML($share)})"); } my $data; if ( !$skipHardLink && $a->{type} == BPC_FTYPE_HARDLINK ) { # # hardlinks should look like the file they point to # my $linkName; while ( $f->read(\$data, 65536) > 0 ) { $linkName .= $data; } $f->close; $linkName =~ s/^\.\///; restoreFile($host, $num, $share, $linkName, 1, $dir); return; } $bpc->ServerMesg("log User $User recovered file $host/$num:$share/$dir ($a->{fullPath})"); $dir = $origName if ( defined($origName) ); my $ext = $1 if ( $dir =~ /\.([^\/\.]+)$/ ); my $contentType = $Ext2ContentType->{lc($ext)} || "application/octet-stream"; my $fileName = $1 if ( $dir =~ /.*\/(.*)/ ); $fileName =~ s/"/\\"/g; print "Content-Type: $contentType\r\n"; print "Content-Transfer-Encoding: binary\r\n"; if ( $ENV{HTTP_USER_AGENT} =~ /\bmsie\b/i && $ENV{HTTP_USER_AGENT} !~ /\bopera\b/i ) { # # Convert to cp1252 for MS IE. TODO: find a way to get IE # to accept UTF8 encoding. Firefox accepts inline encoding # using the "=?UTF-8?B?base64?=" format, but IE doesn't. # from_to($fileName, "utf8", "cp1252") if ( $Conf{ClientCharset} ne "" ); } print "Content-Disposition: attachment; filename=\"$fileName\"\r\n\r\n"; while ( $f->read(\$data, 1024 * 1024) > 0 ) { print STDOUT $data; } $f->close; } 1; BackupPC-3.3.2/lib/BackupPC/CGI/RestoreInfo.pm0000444000076500000240000000670613042250554017523 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::RestoreInfo package # # DESCRIPTION # # This module implements the RestoreInfo action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::RestoreInfo; use strict; use BackupPC::CGI::Lib qw(:all); use Encode qw/decode_utf8/; sub action { my $Privileged = CheckPermission($In{host}); my $host = $1 if ( $In{host} =~ /(.*)/ ); my $num = $In{num}; my $i; if ( !$Privileged ) { ErrorExit($Lang->{Only_privileged_users_can_view_restore_information}); } # # Find the requested restore # my @Restores = $bpc->RestoreInfoRead($host); for ( $i = 0 ; $i < @Restores ; $i++ ) { last if ( $Restores[$i]{num} == $num ); } if ( $i >= @Restores ) { ErrorExit(eval("qq{$Lang->{Restore_number__num_for_host__does_not_exist}}")); } %RestoreReq = (); do "$TopDir/pc/$host/RestoreInfo.$Restores[$i]{num}" if ( -f "$TopDir/pc/$host/RestoreInfo.$Restores[$i]{num}" ); my $startTime = timeStamp2($Restores[$i]{startTime}); my $reqTime = timeStamp2($RestoreReq{reqTime}); my $dur = $Restores[$i]{endTime} - $Restores[$i]{startTime}; $dur = 1 if ( $dur <= 0 ); my $duration = sprintf("%.1f", $dur / 60); my $MB = sprintf("%.1f", $Restores[$i]{size} / (1024*1024)); my $MBperSec = sprintf("%.2f", $Restores[$i]{size} / (1024*1024*$dur)); my $fileListStr = ""; foreach my $f ( @{$RestoreReq{fileList}} ) { my $targetFile = $f; (my $strippedShareSrc = $RestoreReq{shareSrc}) =~ s/^\///; (my $strippedShareDest = $RestoreReq{shareDest}) =~ s/^\///; substr($targetFile, 0, length($RestoreReq{pathHdrSrc})) = $RestoreReq{pathHdrDest}; $targetFile =~ s{//+}{/}g; $strippedShareDest = decode_utf8($strippedShareDest); $targetFile = decode_utf8($targetFile); $strippedShareSrc = decode_utf8($strippedShareSrc); $f = decode_utf8($f); $fileListStr .= <$RestoreReq{hostSrc}:/$strippedShareSrc$f$RestoreReq{hostDest}:/$strippedShareDest$targetFile EOF } $RestoreReq{shareSrc} = decode_utf8($RestoreReq{shareSrc}); $RestoreReq{shareDest} = decode_utf8($RestoreReq{shareDest}); my $content = eval("qq{$Lang->{Restore___num_details_for__host2}}"); Header(eval("qq{$Lang->{Restore___num_details_for__host}}"),$content); Trailer(); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/RSS.pm0000444000076500000240000001177013042250554015730 0ustar craigstaff#============================================================= # # BackupPC::CGI::RSS package # # DESCRIPTION # # This module implements an RSS page for the CGI interface. # # AUTHOR # Rich Duzenbury (rduz at theduz dot com) # # COPYRIGHT # Copyright (C) 2005-2013 Rich Duzenbury and Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::RSS; use strict; use BackupPC::CGI::Lib qw(:all); use XML::RSS; sub action { my $protocol = $ENV{HTTPS} eq "on" ? 'https://' : 'http://'; my $base_url = $protocol . $ENV{'SERVER_NAME'} . $ENV{SCRIPT_NAME}; my($fullTot, $fullSizeTot, $incrTot, $incrSizeTot, $str, $strNone, $strGood, $hostCntGood, $hostCntNone); binmode(STDOUT, ":utf8"); my $rss = new XML::RSS (version => '0.91', encoding => 'utf-8'); $rss->channel( title => eval("qq{$Lang->{RSS_Doc_Title}}"), link => $base_url, language => $Conf{Language}, description => eval("qq{$Lang->{RSS_Doc_Description}}"), ); $hostCntGood = $hostCntNone = 0; GetStatusInfo("hosts"); my $Privileged = CheckPermission(); foreach my $host ( GetUserHosts(1) ) { my($fullDur, $incrCnt, $incrAge, $fullSize, $fullRate, $reasonHilite); my($shortErr); my @Backups = $bpc->BackupInfoRead($host); my $fullCnt = $incrCnt = 0; my $fullAge = $incrAge = -1; $bpc->ConfigRead($host); %Conf = $bpc->Conf(); next if ( $Conf{XferMethod} eq "archive" ); next if ( !$Privileged && !CheckPermission($host) ); for ( my $i = 0 ; $i < @Backups ; $i++ ) { if ( $Backups[$i]{type} eq "full" ) { $fullCnt++; if ( $fullAge < 0 || $Backups[$i]{startTime} > $fullAge ) { $fullAge = $Backups[$i]{startTime}; $fullSize = $Backups[$i]{size} / (1024 * 1024); $fullDur = $Backups[$i]{endTime} - $Backups[$i]{startTime}; } $fullSizeTot += $Backups[$i]{size} / (1024 * 1024); } else { $incrCnt++; if ( $incrAge < 0 || $Backups[$i]{startTime} > $incrAge ) { $incrAge = $Backups[$i]{startTime}; } $incrSizeTot += $Backups[$i]{size} / (1024 * 1024); } } if ( $fullAge < 0 ) { $fullAge = ""; $fullRate = ""; } else { $fullAge = sprintf("%.1f", (time - $fullAge) / (24 * 3600)); $fullRate = sprintf("%.2f", $fullSize / ($fullDur <= 0 ? 1 : $fullDur)); } if ( $incrAge < 0 ) { $incrAge = ""; } else { $incrAge = sprintf("%.1f", (time - $incrAge) / (24 * 3600)); } $fullTot += $fullCnt; $incrTot += $incrCnt; $fullSize = sprintf("%.2f", $fullSize / 1000); $incrAge = " " if ( $incrAge eq "" ); $reasonHilite = $Conf{CgiStatusHilightColor}{$Status{$host}{reason}} || $Conf{CgiStatusHilightColor}{$Status{$host}{state}}; $reasonHilite = " bgcolor=\"$reasonHilite\"" if ( $reasonHilite ne "" ); if ( $Status{$host}{state} ne "Status_backup_in_progress" && $Status{$host}{state} ne "Status_restore_in_progress" && $Status{$host}{error} ne "" ) { ($shortErr = $Status{$host}{error}) =~ s/(.{48}).*/$1.../; $shortErr = " ($shortErr)"; } my $host_state = $Lang->{$Status{$host}{state}}; my $host_last_attempt = $Lang->{$Status{$host}{reason}} . $shortErr; $str = eval("qq{$Lang->{RSS_Host_Summary}}"); $rss->add_item(title => $host . ', ' . $host_state . ', ' . $host_last_attempt, link => $base_url . '?host=' . $host, description => $str); } $fullSizeTot = sprintf("%.2f", $fullSizeTot / 1000); $incrSizeTot = sprintf("%.2f", $incrSizeTot / 1000); my $now = timeStamp2(time); print 'Content-type: text/xml', "\r\n\r\n", $rss->as_string; } 1; BackupPC-3.3.2/lib/BackupPC/CGI/StartServer.pm0000444000076500000240000000407413042250554017544 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::StartServer package # # DESCRIPTION # # This module implements the StartServer action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::StartServer; use strict; use BackupPC::CGI::Lib qw(:all); sub action { if ( -f $Conf{ServerInitdPath} && $bpc->{Conf}{ServerInitdStartCmd} ne "" && !$bpc->ServerOK() ) { my $args = { serverInitdPath => $bpc->{Conf}{ServerInitdPath}, sshPath => $bpc->{Conf}{SshPath}, serverHost => $bpc->{Conf}{ServerHost}, }; my $serverInitdStartCmd = $bpc->cmdVarSubstitute($bpc->{Conf}{ServerInitdStartCmd}, $args); $bpc->cmdSystemOrEval($serverInitdStartCmd, undef, $args); for ( my $i = 0; $i < 10; $i++ ) { last unless ( $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}) ); sleep(1); } $bpc->ServerDisconnect(); } print $Cgi->redirect($MyURL); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/StartStopBackup.pm0000444000076500000240000000723113042250554020347 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::StartStopBackup package # # DESCRIPTION # # This module implements the StartStopBackup action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::StartStopBackup; use strict; use BackupPC::CGI::Lib qw(:all); sub action { my($str, $reply); my $start = 1 if ( $In{action} eq "Start_Incr_Backup" || $In{action} eq "Start_Full_Backup" ); my $doFull = $In{action} eq "Start_Full_Backup" ? 1 : 0; my $type = $doFull ? $Lang->{Type_full} : $Lang->{Type_incr}; my $host = $In{host}; my $Privileged = CheckPermission($host); if ( !$Privileged ) { ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_stop_or_start_backups}}")); } ServerConnect(); if ( $In{doit} ) { if ( $start ) { if ( $Hosts->{$host}{dhcp} ) { $reply = $bpc->ServerMesg("backup $In{hostIP} ${EscURI($host)}" . " $User $doFull"); $str = eval("qq{$Lang->{Backup_requested_on_DHCP__host}}"); } else { $reply = $bpc->ServerMesg("backup ${EscURI($host)}" . " ${EscURI($host)} $User $doFull"); $str = eval("qq{$Lang->{Backup_requested_on__host_by__User}}"); } } else { $reply = $bpc->ServerMesg("stop ${EscURI($host)} $User $In{backoff}"); $str = eval("qq{$Lang->{Backup_stopped_dequeued_on__host_by__User}}"); } my $content = eval ("qq{$Lang->{REPLY_FROM_SERVER}}"); Header(eval ("qq{$Lang->{BackupPC__Backup_Requested_on__host}}"),$content); Trailer(); } else { if ( $start ) { $bpc->ConfigRead($host); %Conf = $bpc->Conf(); my $checkHost = $host; $checkHost = $Conf{ClientNameAlias} if ( $Conf{ClientNameAlias} ne "" ); my $ipAddr = ConfirmIPAddress($checkHost); my $buttonText = $Lang->{$In{action}}; my $content = eval("qq{$Lang->{Are_you_sure_start}}"); Header(eval("qq{$Lang->{BackupPC__Start_Backup_Confirm_on__host}}"),$content); } else { my $backoff = ""; GetStatusInfo("host(${EscURI($host)})"); if ( $StatusHost{backoffTime} > time ) { $backoff = sprintf("%.1f", ($StatusHost{backoffTime} - time) / 3600); } my $buttonText = $Lang->{$In{action}}; my $content = eval ("qq{$Lang->{Are_you_sure_stop}}"); Header(eval("qq{$Lang->{BackupPC__Stop_Backup_Confirm_on__host}}"), $content); } Trailer(); } } 1; BackupPC-3.3.2/lib/BackupPC/CGI/StopServer.pm0000444000076500000240000000326713042250554017377 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::StopServer package # # DESCRIPTION # # This module implements the StopServer action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::StopServer; use strict; use BackupPC::CGI::Lib qw(:all); sub action { if ( defined($bpc) && $bpc->ServerOK() ) { $bpc->ServerMesg("log User $User requested server shutdown"); $bpc->ServerMesg("server shutdown"); for ( my $i = 0; $i < 10; $i++ ) { last unless $bpc->ServerOK(); sleep(1); } sleep(2); } print $Cgi->redirect($MyURL); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/Summary.pm0000444000076500000240000001457313042250554016722 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::Summary package # # DESCRIPTION # # This module implements the Summary action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::Summary; use strict; use BackupPC::CGI::Lib qw(:all); sub action { my($fullTot, $fullSizeTot, $incrTot, $incrSizeTot, $str, $strNone, $strGood, $hostCntGood, $hostCntNone); $hostCntGood = $hostCntNone = 0; GetStatusInfo("hosts info"); my $Privileged = CheckPermission(); foreach my $host ( GetUserHosts(1) ) { my($fullDur, $incrCnt, $incrAge, $fullSize, $fullRate, $reasonHilite, $lastAge, $tempState, $tempReason, $lastXferErrors); my($shortErr); my @Backups = $bpc->BackupInfoRead($host); my $fullCnt = $incrCnt = 0; my $fullAge = $incrAge = $lastAge = -1; $bpc->ConfigRead($host); %Conf = $bpc->Conf(); next if ( $Conf{XferMethod} eq "archive" ); next if ( !$Privileged && !CheckPermission($host) ); for ( my $i = 0 ; $i < @Backups ; $i++ ) { if ( $Backups[$i]{type} eq "full" ) { $fullCnt++; if ( $fullAge < 0 || $Backups[$i]{startTime} > $fullAge ) { $fullAge = $Backups[$i]{startTime}; $fullSize = $Backups[$i]{size} / (1024 * 1024); $fullDur = $Backups[$i]{endTime} - $Backups[$i]{startTime}; } $fullSizeTot += $Backups[$i]{size} / (1024 * 1024); } else { $incrCnt++; if ( $incrAge < 0 || $Backups[$i]{startTime} > $incrAge ) { $incrAge = $Backups[$i]{startTime}; } $incrSizeTot += $Backups[$i]{size} / (1024 * 1024); } } if ( $fullAge > $incrAge && $fullAge >= 0 ) { $lastAge = $fullAge; } else { $lastAge = $incrAge; } if ( $lastAge < 0 ) { $lastAge = ""; } else { $lastAge = sprintf("%.1f", (time - $lastAge) / (24 * 3600)); } if ( $fullAge < 0 ) { $fullAge = ""; $fullRate = ""; } else { $fullAge = sprintf("%.1f", (time - $fullAge) / (24 * 3600)); $fullRate = sprintf("%.2f", $fullSize / ($fullDur <= 0 ? 1 : $fullDur)); } if ( $incrAge < 0 ) { $incrAge = ""; } else { $incrAge = sprintf("%.1f", (time - $incrAge) / (24 * 3600)); } $fullTot += $fullCnt; $incrTot += $incrCnt; $fullSize = sprintf("%.2f", $fullSize / 1000); $incrAge = " " if ( $incrAge eq "" ); $lastXferErrors = $Backups[@Backups-1]{xferErrs} if ( @Backups ); $reasonHilite = $Conf{CgiStatusHilightColor}{$Status{$host}{reason}} || $Conf{CgiStatusHilightColor}{$Status{$host}{state}}; if ( $Conf{BackupsDisable} == 1 ) { if ( $Status{$host}{state} ne "Status_backup_in_progress" && $Status{$host}{state} ne "Status_restore_in_progress" ) { $reasonHilite = $Conf{CgiStatusHilightColor}{Disabled_OnlyManualBackups}; $tempState = "Disabled_OnlyManualBackups"; $tempReason = ""; } else { $tempState = $Status{$host}{state}; $tempReason = $Status{$host}{reason}; } } elsif ($Conf{BackupsDisable} == 2 ) { $reasonHilite = $Conf{CgiStatusHilightColor}{Disabled_AllBackupsDisabled}; $tempState = "Disabled_AllBackupsDisabled"; $tempReason = ""; } else { $tempState = $Status{$host}{state}; $tempReason = $Status{$host}{reason}; } $reasonHilite = " bgcolor=\"$reasonHilite\"" if ( $reasonHilite ne "" ); if ( $tempState ne "Status_backup_in_progress" && $tempState ne "Status_restore_in_progress" && $Conf{BackupsDisable} == 0 && $Status{$host}{error} ne "" ) { ($shortErr = $Status{$host}{error}) =~ s/(.{48}).*/$1.../; $shortErr = " ($shortErr)"; } $str = <${HostLink($host)} ${UserLink(defined($Hosts->{$host}) ? $Hosts->{$host}{user} : "")} $fullCnt $fullAge $fullSize $fullRate $incrCnt $incrAge $lastAge $Lang->{$tempState} $lastXferErrors $Lang->{$tempReason}$shortErr EOF if ( @Backups == 0 ) { $hostCntNone++; $strNone .= $str; } else { $hostCntGood++; $strGood .= $str; } } $fullSizeTot = sprintf("%.2f", $fullSizeTot / 1000); $incrSizeTot = sprintf("%.2f", $incrSizeTot / 1000); my $now = timeStamp2(time); my $DUlastTime = timeStamp2($Info{DUlastValueTime}); my $DUmaxTime = timeStamp2($Info{DUDailyMaxTime}); my $content = eval ("qq{$Lang->{BackupPC_Summary}}"); Header($Lang->{BackupPC__Server_Summary}, $content); Trailer(); } 1; BackupPC-3.3.2/lib/BackupPC/CGI/View.pm0000444000076500000240000002027013042250554016166 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::CGI::View package # # DESCRIPTION # # This module implements the View action for the CGI interface. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2003-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::CGI::View; use strict; use BackupPC::CGI::Lib qw(:all); use BackupPC::FileZIO; sub action { my $Privileged = CheckPermission($In{host}); my $compress = 0; my $fh; my $host = $In{host}; my $num = $In{num}; my $type = $In{type}; my $linkHosts = 0; my($file, $comment); my $ext = $num ne "" ? ".$num" : ""; ErrorExit(eval("qq{$Lang->{Invalid_number__num}}")) if ( $num ne "" && $num !~ /^\d+$/ ); if ( $type ne "docs" && !$Privileged ) { ErrorExit($Lang->{Only_privileged_users_can_view_log_or_config_files}); } if ( $type eq "XferLOG" ) { $file = "$TopDir/pc/$host/SmbLOG$ext"; $file = "$TopDir/pc/$host/XferLOG$ext" if ( !-f $file && !-f "$file.z"); } elsif ( $type eq "XferLOGbad" ) { $file = "$TopDir/pc/$host/SmbLOG.bad"; $file = "$TopDir/pc/$host/XferLOG.bad" if ( !-f $file && !-f "$file.z"); } elsif ( $type eq "XferErrbad" ) { $file = "$TopDir/pc/$host/SmbLOG.bad"; $file = "$TopDir/pc/$host/XferLOG.bad" if ( !-f $file && !-f "$file.z"); $comment = $Lang->{Extracting_only_Errors}; } elsif ( $type eq "XferErr" ) { $file = "$TopDir/pc/$host/SmbLOG$ext"; $file = "$TopDir/pc/$host/XferLOG$ext" if ( !-f $file && !-f "$file.z"); $comment = $Lang->{Extracting_only_Errors}; } elsif ( $type eq "RestoreLOG" ) { $file = "$TopDir/pc/$host/RestoreLOG$ext"; } elsif ( $type eq "RestoreErr" ) { $file = "$TopDir/pc/$host/RestoreLOG$ext"; $comment = $Lang->{Extracting_only_Errors}; } elsif ( $type eq "ArchiveLOG" ) { $file = "$TopDir/pc/$host/ArchiveLOG$ext"; } elsif ( $type eq "ArchiveErr" ) { $file = "$TopDir/pc/$host/ArchiveLOG$ext"; $comment = $Lang->{Extracting_only_Errors}; } elsif ( $type eq "config" ) { # Note: only works for Storage::Text $file = $bpc->{storage}->ConfigPath($host); } elsif ( $type eq "hosts" ) { # Note: only works for Storage::Text $file = $bpc->ConfDir() . "/hosts"; $linkHosts = 1; } elsif ( $type eq "docs" ) { $file = $bpc->InstallDir() . "/doc/BackupPC.html"; } elsif ( $host ne "" ) { if ( !defined($In{num}) ) { # get the latest LOG file $file = ($bpc->sortedPCLogFiles($host))[0]; $file =~ s/\.z$//; } else { $file = "$TopDir/pc/$host/LOG$ext"; } $linkHosts = 1; } else { $file = "$LogDir/LOG$ext"; $linkHosts = 1; } if ( !-f $file && -f "$file.z" ) { $file .= ".z"; $compress = 1; } my($contentPre, $contentSub, $contentPost); $contentPre .= eval("qq{$Lang->{Log_File__file__comment}}"); if ( $file ne "" && defined($fh = BackupPC::FileZIO->open($file, 0, $compress)) ) { $fh->utf8(1); my $mtimeStr = $bpc->timeStamp((stat($file))[9], 1); $contentPre .= eval("qq{$Lang->{Contents_of_log_file}}"); $contentPre .= "
    ";
            if ( $type eq "XferErr" || $type eq "XferErrbad"
    				|| $type eq "RestoreErr"
    				|| $type eq "ArchiveErr" ) {
    	    $contentSub = sub {
    		#
    		# Because the content might be large, we use
    		# a sub to return the data in 64K chunks.
    		#
    		my($skipped, $c, $s);
    		while ( length($c) < 65536 ) {
    		    $s = $fh->readLine();
    		    if ( $s eq "" ) {
    			$c .= eval("qq{$Lang->{skipped__skipped_lines}}")
    							if ( $skipped );
    			last;
    		    }
    		    $s =~ s/[\n\r]+//g;
    		    if ( $s =~ /smb: \\>/
    			    || $s =~ /^\s*(\d+) \(\s*\d+\.\d kb\/s\) (.*)$/
    			    || $s =~ /^tar: dumped \d+ files/
    			    || $s =~ /^\s*added interface/i
    			    || $s =~ /^\s*restore tar file /i
    			    || $s =~ /^\s*restore directory /i
    			    || $s =~ /^\s*tarmode is now/i
    			    || $s =~ /^\s*Total bytes written/i
    			    || $s =~ /^\s*Domain=/i
    			    || $s =~ /^\s*Getting files newer than/i
    			    || $s =~ /^\s*Output is \/dev\/null/
    			    || $s =~ /^\s*\([\d.,]* kb\/s\) \(average [\d\.]* kb\/s\)$/
    			    || $s =~ /^\s+directory \\/
    			    || $s =~ /^\s*Timezone is/
    			    || $s =~ /^\s*creating lame (up|low)case table/i
    			    || $s =~ /^\.\//
    			    || $s =~ /^  / ) {
    			$skipped++;
    			next;
    		    }
    		    $c .= eval("qq{$Lang->{skipped__skipped_lines}}")
    							 if ( $skipped );
    		    $skipped = 0;
    		    $c .= ${EscHTML($s)} . "\n";
    		}
    		return $c;
    	    };
            } elsif ( $linkHosts ) {
    	    #
    	    # Because the content might be large, we use
    	    # a sub to return the data in 64K chunks.
    	    #
    	    $contentSub = sub {
    		my($c, $s);
    		while ( length($c) < 65536 ) {
    		    $s = $fh->readLine();
    		    last if ( $s eq "" );
    		    $s =~ s/[\n\r]+//g;
    		    $s = ${EscHTML($s)};
    		    $s =~ s/\b([\w-.]+)\b/defined($Hosts->{$1})
    					    ? ${HostLink($1)} : $1/eg;
    		    $c .= $s . "\n";
    		}
    		return $c;
                };
            } elsif ( $type eq "config" ) {
    	    #
    	    # Because the content might be large, we use
    	    # a sub to return the data in 64K chunks.
    	    #
    	    $contentSub = sub {
    		my($c, $s);
    		while ( length($c) < 65536 ) {
    		    $s = $fh->readLine();
    		    last if ( $s eq "" );
    		    $s =~ s/[\n\r]+//g;
    		    # remove any passwords and user names
    		    $s =~ s/(SmbSharePasswd.*=.*['"]).*(['"])/$1****$2/ig;
    		    $s =~ s/(SmbShareUserName.*=.*['"]).*(['"])/$1****$2/ig;
    		    $s =~ s/(RsyncdPasswd.*=.*['"]).*(['"])/$1****$2/ig;
    		    $s =~ s/(ServerMesgSecret.*=.*['"]).*(['"])/$1****$2/ig;
    		    $s = ${EscHTML($s)};
    		    $s =~ s[(\$Conf\{.*?\})][
    			my $c = $1;
    			my $s = lc($c);
    			$s =~ s{(\W)}{_}g;
    			"$c"
    		    ]eg;
    		    $c .= $s . "\n";
    		}
    		return $c;
                };
            } elsif ( $type eq "docs" ) {
    	    #
    	    # Because the content might be large, we use
    	    # a sub to return the data in 64K chunks.
    	    #
    	    $contentSub = sub {
    		my($c, $s);
    		while ( length($c) < 65536 ) {
    		    $s = $fh->readLine();
    		    last if ( $s eq "" );
    		    $c .= $s;
    		}
    		return $c;
                };
    	    #
    	    # Documentation has a different header and no pre or post text,
    	    # so just handle it here
    	    #
                Header($Lang->{BackupPC__Documentation}, "", 0, $contentSub);
                Trailer();
    	    return;
            } else {
    	    #
    	    # Because the content might be large, we use
    	    # a sub to return the data in 64K chunks.
    	    #
    	    $contentSub = sub {
    		my($c, $s);
    		while ( length($c) < 65536 ) {
    		    $s = $fh->readLine();
    		    last if ( $s eq "" );
    		    $s =~ s/[\n\r]+//g;
    		    $s = ${EscHTML($s)};
    		    $c .= $s . "\n";
    		}
    		return $c;
                };
            }
        } else {
    	if ( $type eq "docs" ) {
    	    ErrorExit(eval("qq{$Lang->{Unable_to_open__file__configuration_problem}}"));
    	}
    	$contentPre .= eval("qq{$Lang->{_pre___Can_t_open_log_file__file}}");
        }
        $contentPost .= "
    \n" if ( $type ne "docs" ); Header(eval("qq{$Lang->{Backup_PC__Log_File__file}}"), $contentPre, !-f "$TopDir/pc/$host/backups", $contentSub, $contentPost); Trailer(); $fh->close() if ( defined($fh) ); } 1; BackupPC-3.3.2/lib/BackupPC/Config/0000755000076500000240000000000013042250554015522 5ustar craigstaffBackupPC-3.3.2/lib/BackupPC/Config/Meta.pm0000444000076500000240000004542613042250554016757 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Config::Meta package # # DESCRIPTION # # This library defines a BackupPC::Config::Meta class. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2004-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Config::Meta; use strict; require Exporter; use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS ); use vars qw(%ConfigMeta); @ISA = qw(Exporter); @EXPORT = qw( ); @EXPORT_OK = qw( %ConfigMeta ); %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ], ); # # Define the data types for all the config variables # %ConfigMeta = ( ###################################################################### # General server configuration ###################################################################### ServerHost => "string", ServerPort => "integer", ServerMesgSecret => "string", MyPath => {type => "string", undefIfEmpty => 1}, UmaskMode => "integer", WakeupSchedule => { type => "shortlist", child => "float", }, MaxBackups => "integer", MaxUserBackups => "integer", MaxPendingCmds => "integer", MaxBackupPCNightlyJobs => "integer", BackupPCNightlyPeriod => "integer", MaxOldLogFiles => "integer", CmdQueueNice => "integer", SshPath => {type => "execPath", undefIfEmpty => 1}, NmbLookupPath => {type => "execPath", undefIfEmpty => 1}, PingPath => {type => "execPath", undefIfEmpty => 1}, DfPath => {type => "execPath", undefIfEmpty => 1}, DfCmd => "string", SplitPath => {type => "execPath", undefIfEmpty => 1}, ParPath => {type => "execPath", undefIfEmpty => 1}, CatPath => {type => "execPath", undefIfEmpty => 1}, GzipPath => {type => "execPath", undefIfEmpty => 1}, Bzip2Path => {type => "execPath", undefIfEmpty => 1}, DfMaxUsagePct => "float", TrashCleanSleepSec => "integer", DHCPAddressRanges => { type => "list", emptyOk => 1, child => { type => "hash", noKeyEdit => 1, order => [qw(ipAddrBase first last)], child => { ipAddrBase => "string", first => "integer", last => "integer", }, }, }, BackupPCUser => "string", CgiDir => "string", InstallDir => "string", TopDir => "string", ConfDir => "string", LogDir => "string", BackupPCUserVerify => "boolean", HardLinkMax => "integer", PerlModuleLoad => { type => "list", emptyOk => 1, undefIfEmpty => 1, child => "string", }, ServerInitdPath => {type => "string", undefIfEmpty => 1}, ServerInitdStartCmd => "string", ###################################################################### # What to backup and when to do it # (can be overridden in the per-PC config.pl) ###################################################################### FullPeriod => "float", IncrPeriod => "float", FullKeepCnt => { type => "shortlist", child => "integer", }, FullKeepCntMin => "integer", FullAgeMax => "float", IncrKeepCnt => "integer", IncrKeepCntMin => "integer", IncrAgeMax => "float", IncrLevels => { type => "shortlist", child => "integer", }, PartialAgeMax => "float", BackupsDisable => "integer", IncrFill => "boolean", RestoreInfoKeepCnt => "integer", ArchiveInfoKeepCnt => "integer", BackupFilesOnly => { type => "hash", keyText => "CfgEdit_Button_New_Share", emptyOk => 1, childType => { type => "list", emptyOk => 1, child => "string", }, }, BackupFilesExclude => { type => "hash", keyText => "CfgEdit_Button_New_Share", emptyOk => 1, childType => { type => "list", emptyOk => 1, child => "string", }, }, BlackoutBadPingLimit => "integer", BlackoutGoodCnt => "integer", BlackoutPeriods => { type => "list", emptyOk => 1, child => { type => "hash", noKeyEdit => 1, child => { hourBegin => "float", hourEnd => "float", weekDays => { type => "shortlist", child => "integer", }, }, }, }, BackupZeroFilesIsFatal => "boolean", ###################################################################### # How to backup a client ###################################################################### XferMethod => { type => "select", values => [qw(archive ftp rsync rsyncd smb tar)], }, XferLogLevel => "integer", ClientCharset => "string", ClientCharsetLegacy => "string", ###################################################################### # Smb Configuration ###################################################################### SmbShareName => { type => "list", child => "string", }, SmbShareUserName => "string", SmbSharePasswd => "string", SmbClientPath => {type => "execPath", undefIfEmpty => 1}, SmbClientFullCmd => "string", SmbClientIncrCmd => "string", SmbClientRestoreCmd => {type => "string", undefIfEmpty => 1}, ###################################################################### # Tar Configuration ###################################################################### TarShareName => { type => "list", child => "string", }, TarClientCmd => "string", TarFullArgs => "string", TarIncrArgs => "string", TarClientRestoreCmd => {type => "string", undefIfEmpty => 1}, TarClientPath => {type => "string", undefIfEmpty => 1}, ###################################################################### # Rsync Configuration ###################################################################### RsyncShareName => { type => "list", child => "string", }, RsyncClientPath => {type => "string", undefIfEmpty => 1}, RsyncClientCmd => "string", RsyncClientRestoreCmd => "string", ###################################################################### # Rsyncd Configuration ###################################################################### RsyncdClientPort => "integer", RsyncdUserName => "string", RsyncdPasswd => "string", RsyncdAuthRequired => "boolean", ###################################################################### # Rsync(d) Options ###################################################################### RsyncCsumCacheVerifyProb => "float", RsyncArgs => { type => "list", emptyOk => 1, child => "string", }, RsyncArgsExtra => { type => "list", emptyOk => 1, child => "string", }, RsyncRestoreArgs => { type => "list", emptyOk => 1, undefIfEmpty => 1, child => "string", }, ###################################################################### # FTP Configuration ###################################################################### FtpShareName => { type => "list", child => "string", }, FtpUserName => "string", FtpPasswd => "string", FtpPassive => "boolean", FtpBlockSize => "integer", FtpPort => "integer", FtpTimeout => "integer", FtpFollowSymlinks => "boolean", ###################################################################### # Archive Configuration ###################################################################### ArchiveDest => "string", ArchiveComp => { type => "select", values => [qw(none bzip2 gzip)], }, ArchivePar => "boolean", ArchiveSplit => "float", ArchiveClientCmd => "string", ###################################################################### # Other Client Configuration ###################################################################### NmbLookupCmd => "string", NmbLookupFindHostCmd => "string", FixedIPNetBiosNameCheck => "boolean", PingCmd => "string", PingMaxMsec => "float", ClientTimeout => "integer", MaxOldPerPCLogFiles => "integer", CompressLevel => "integer", DumpPreUserCmd => {type => "string", undefIfEmpty => 1}, DumpPostUserCmd => {type => "string", undefIfEmpty => 1}, DumpPreShareCmd => {type => "string", undefIfEmpty => 1}, DumpPostShareCmd => {type => "string", undefIfEmpty => 1}, RestorePreUserCmd => {type => "string", undefIfEmpty => 1}, RestorePostUserCmd => {type => "string", undefIfEmpty => 1}, ArchivePreUserCmd => {type => "string", undefIfEmpty => 1}, ArchivePostUserCmd => {type => "string", undefIfEmpty => 1}, UserCmdCheckStatus => "boolean", ClientNameAlias => {type => "string", undefIfEmpty => 1}, ###################################################################### # Email reminders, status and messages # (can be overridden in the per-PC config.pl) ###################################################################### SendmailPath => {type => "execPath", undefIfEmpty => 1}, EMailNotifyMinDays => "float", EMailFromUserName => "string", EMailAdminUserName => "string", EMailUserDestDomain => "string", EMailNoBackupEverSubj => {type => "string", undefIfEmpty => 1}, EMailNoBackupEverMesg => {type => "bigstring", undefIfEmpty => 1}, EMailNotifyOldBackupDays => "float", EMailNoBackupRecentSubj => {type => "string", undefIfEmpty => 1}, EMailNoBackupRecentMesg => {type => "bigstring", undefIfEmpty => 1}, EMailNotifyOldOutlookDays => "float", EMailOutlookBackupSubj => {type => "string", undefIfEmpty => 1}, EMailOutlookBackupMesg => {type => "bigstring", undefIfEmpty => 1}, EMailHeaders => {type => "bigstring", undefIfEmpty => 1}, ###################################################################### # CGI user interface configuration settings ###################################################################### CgiAdminUserGroup => "string", CgiAdminUsers => "string", CgiURL => "string", Language => { type => "select", values => [qw(cz de en es fr it ja nl pl pt_br ru uk zh_CN)], }, CgiUserHomePageCheck => "string", CgiUserUrlCreate => "string", CgiDateFormatMMDD => "integer", CgiNavBarAdminAllHosts => "boolean", CgiSearchBoxEnable => "boolean", CgiNavBarLinks => { type => "list", emptyOk => 1, child => { type => "hash", noKeyEdit => 1, child => { link => "string", lname => {type => "string", undefIfEmpty => 1}, name => {type => "string", undefIfEmpty => 1}, }, }, }, CgiStatusHilightColor => { type => "hash", noKeyEdit => 1, child => { Reason_backup_failed => "string", Reason_backup_done => "string", Reason_no_ping => "string", Reason_backup_canceled_by_user => "string", Status_backup_in_progress => "string", Disabled_OnlyManualBackups => "string", Disabled_AllBackupsDisabled => "string", }, }, CgiHeaders => "bigstring", CgiImageDir => "string", CgiExt2ContentType => { type => "hash", emptyOk => 1, childType => "string", }, CgiImageDirURL => "string", CgiCSSFile => "string", CgiUserConfigEditEnable => "boolean", CgiUserConfigEdit => { type => "hash", noKeyEdit => 1, child => { FullPeriod => "boolean", IncrPeriod => "boolean", FullKeepCnt => "boolean", FullKeepCntMin => "boolean", FullAgeMax => "boolean", IncrKeepCnt => "boolean", IncrKeepCntMin => "boolean", IncrAgeMax => "boolean", IncrLevels => "boolean", PartialAgeMax => "boolean", IncrFill => "boolean", RestoreInfoKeepCnt => "boolean", ArchiveInfoKeepCnt => "boolean", BackupFilesOnly => "boolean", BackupFilesExclude => "boolean", BackupsDisable => "boolean", BlackoutBadPingLimit => "boolean", BlackoutGoodCnt => "boolean", BlackoutPeriods => "boolean", BackupZeroFilesIsFatal => "boolean", XferMethod => "boolean", XferLogLevel => "boolean", ClientCharset => "boolean", ClientCharsetLegacy => "boolean", SmbShareName => "boolean", SmbShareUserName => "boolean", SmbSharePasswd => "boolean", SmbClientFullCmd => "boolean", SmbClientIncrCmd => "boolean", SmbClientRestoreCmd => "boolean", TarShareName => "boolean", TarFullArgs => "boolean", TarIncrArgs => "boolean", TarClientCmd => "boolean", TarClientPath => "boolean", TarClientRestoreCmd => "boolean", RsyncShareName => "boolean", RsyncdClientPort => "boolean", RsyncdUserName => "boolean", RsyncdPasswd => "boolean", RsyncdAuthRequired => "boolean", RsyncCsumCacheVerifyProb => "boolean", RsyncArgs => "boolean", RsyncArgsExtra => "boolean", RsyncRestoreArgs => "boolean", RsyncClientCmd => "boolean", RsyncClientPath => "boolean", RsyncClientRestoreCmd => "boolean", FtpShareName => "boolean", FtpUserName => "boolean", FtpPasswd => "boolean", FtpBlockSize => "boolean", FtpPort => "boolean", FtpTimeout => "boolean", FtpFollowSymlinks => "boolean", FtpRestoreEnabled => "boolean", ArchiveDest => "boolean", ArchiveComp => "boolean", ArchivePar => "boolean", ArchiveSplit => "boolean", ArchiveClientCmd => "boolean", FixedIPNetBiosNameCheck => "boolean", PingMaxMsec => "boolean", NmbLookupCmd => "boolean", NmbLookupFindHostCmd => "boolean", PingCmd => "boolean", ClientTimeout => "boolean", MaxOldPerPCLogFiles => "boolean", CompressLevel => "boolean", ClientNameAlias => "boolean", DumpPreUserCmd => "boolean", DumpPostUserCmd => "boolean", RestorePreUserCmd => "boolean", RestorePostUserCmd => "boolean", ArchivePreUserCmd => "boolean", ArchivePostUserCmd => "boolean", DumpPostShareCmd => "boolean", DumpPreShareCmd => "boolean", UserCmdCheckStatus => "boolean", EMailNotifyMinDays => "boolean", EMailFromUserName => "boolean", EMailAdminUserName => "boolean", EMailUserDestDomain => "boolean", EMailNoBackupEverSubj => "boolean", EMailNoBackupEverMesg => "boolean", EMailNotifyOldBackupDays => "boolean", EMailNoBackupRecentSubj => "boolean", EMailNoBackupRecentMesg => "boolean", EMailNotifyOldOutlookDays => "boolean", EMailOutlookBackupSubj => "boolean", EMailOutlookBackupMesg => "boolean", EMailHeaders => "boolean", }, }, ###################################################################### # Fake config setting for editing the hosts ###################################################################### Hosts => { type => "list", emptyOk => 1, child => { type => "horizHash", order => [qw(host dhcp user moreUsers)], noKeyEdit => 1, child => { host => { type => "string", size => 20 }, dhcp => { type => "boolean" }, user => { type => "string", size => 20 }, moreUsers => { type => "string", size => 30 }, }, }, }, ); 1; BackupPC-3.3.2/lib/BackupPC/Config.pm0000444000076500000240000002605613042250554016067 0ustar craigstaffpackage BackupPC::Config; use warnings; use Data::Dumper; our %ConfigDef; # this currently isn't used (or completed) sub CheckConfigInfo { my($self) = @_; my $errstr = ''; my($attr, $val, $def, $ref); foreach $attr (sort keys %{ $self->{Conf} }) { $val = $self->{Conf}->{$attr}; $ref = ref $val; $def = $ConfigDef{$attr}; if (!defined $def) { $errstr .= "Unknown attribute $attr; "; } elsif ($def->{struct} eq 'SCALAR' && $ref) { $errstr .= "$attr expected to be SCALAR but is $ref; "; } elsif ($def->{struct} =~ /^ARRAY(OFHASH)$/ && $ref && $ref ne 'ARRAY') { $errstr .= "$attr expected to be ARRAY but is $ref; "; } elsif ($def->{struct} =~ /^HASH(OFARRAY)$/ && $ref && $ref ne 'HASH') { $errstr .= "$attr expected to be HASH but is $ref; "; # still working out this logic.. #} elsif (defined $val && !$ref) { # # if we got a scalar but were expecting a reference, fix it # # if($def->{struct} eq 'ARRAY') { # $val = [ $val ]; # } elsif ($def->{struct} eq 'HASH') { # $val = { $val }; # } elsif ($def->{struct} eq 'ARRAYOFHASH') { # $val = [ { $val } ]; # } elsif ($def->{struct} eq 'HASHOFARRAY') { # $val = { [ $val ] }; # } } } return $errstr; } sub TopDir { my($self) = @_; return $self->{TopDir}; } sub BinDir { my($self) = @_; return $self->{BinDir}; } sub Version { my($self) = @_; return $self->{Version}; } sub Conf { my($self) = @_; return %{$self->{Conf}}; } sub ConfigDef { my($self) = @_; return \%ConfigDef; } sub Lang { my($self) = @_; return $self->{Lang}; } sub adminJob { return " admin "; } sub trashJob { return " trashClean "; } sub timeStamp { my($self, $t, $noPad) = @_; my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($t || time); $year += 1900; $mon++; return "$year/$mon/$mday " . sprintf("%02d:%02d:%02d", $hour, $min, $sec) . ($noPad ? "" : " "); } sub ConnectData { # fallback routine in case no database used return 1; } ########################### %ConfigDef = ( ServerHost => {struct => 'SCALAR', type => 'STRING', }, ServerPort => {struct => 'SCALAR', type => 'INT', }, ServerMesgSecret => {struct => 'SCALAR', type => 'STRING', }, MyPath => {struct => 'SCALAR', type => 'STRING', }, UmaskMode => {struct => 'SCALAR', type => 'INT', }, WakeupSchedule => {struct => 'ARRAY', type => 'INT', }, MaxBackups => {struct => 'SCALAR', type => 'INT', }, MaxUserBackups => {struct => 'SCALAR', type => 'INT', }, MaxPendingCmds => {struct => 'SCALAR', type => 'INT', }, MaxOldLogFiles => {struct => 'SCALAR', type => 'INT', }, DfPath => {struct => 'SCALAR', type => 'STRING', }, DfMaxUsagePct => {struct => 'SCALAR', type => 'INT', }, TrashCleanSleepSec => {struct => 'SCALAR', type => 'INT', }, DHCPAddressRanges => {struct => 'ARRAYOFHASH', type => {ipAddrBase => 'STRING', first => 'INT', last => 'INT',}, }, BackupPCUser => {struct => 'SCALAR', type => 'STRING', }, CgiDir => {struct => 'SCALAR', type => 'STRING', }, InstallDir => {struct => 'SCALAR', type => 'STRING', }, BackupPCUserVerify => {struct => 'SCALAR', type => 'BOOLEAN', }, SmbShareName => {struct => 'ARRAY', type => 'STRING', }, SmbShareUserName => {struct => 'SCALAR', type => 'STRING', }, SmbSharePasswd => {struct => 'SCALAR', type => 'STRING', }, TarShareName => {struct => 'ARRAY', type => 'STRING', }, FullPeriod => {struct => 'SCALAR', type => 'FLOAT', }, IncrPeriod => {struct => 'SCALAR', type => 'FLOAT', }, FullKeepCnt => {struct => 'SCALAR', type => 'INT', }, FullKeepCntMin => {struct => 'SCALAR', type => 'INT', }, FullAgeMax => {struct => 'SCALAR', type => 'INT', }, IncrKeepCnt => {struct => 'SCALAR', type => 'INT', }, IncrKeepCntMin => {struct => 'SCALAR', type => 'INT', }, IncrAgeMax => {struct => 'SCALAR', type => 'INT', }, IncrFill => {struct => 'SCALAR', type => 'BOOLEAN', }, RestoreInfoKeepCnt => {struct => 'SCALAR', type => 'INT', }, BackupFilesOnly => {struct => 'HASHOFARRAY', type => 'STRING', }, BackupFilesExclude => {struct => 'HASHOFARRAY', type => 'STRING', }, BlackoutBadPingLimit => {struct => 'SCALAR', type => 'INT', }, BlackoutGoodCnt => {struct => 'SCALAR', type => 'INT', }, BlackoutHourBegin => {struct => 'SCALAR', type => 'FLOAT', }, BlackoutHourEnd => {struct => 'SCALAR', type => 'FLOAT', }, BlackoutWeekDays => {struct => 'ARRAY', type => 'INT', }, XferMethod => {struct => 'SCALAR', type => 'STRING', }, SmbClientPath => {struct => 'SCALAR', type => 'STRING', }, SmbClientArgs => {struct => 'SCALAR', type => 'STRING', }, TarClientCmd => {struct => 'SCALAR', type => 'STRING', }, TarFullArgs => {struct => 'SCALAR', type => 'STRING', }, TarIncrArgs => {struct => 'SCALAR', type => 'STRING', }, TarClientRestoreCmd => {struct => 'SCALAR', type => 'STRING', }, TarClientPath => {struct => 'SCALAR', type => 'STRING', }, SshPath => {struct => 'SCALAR', type => 'STRING', }, NmbLookupPath => {struct => 'SCALAR', type => 'STRING', }, FixedIPNetBiosNameCheck => {struct => 'SCALAR', type => 'BOOLEAN', }, PingPath => {struct => 'SCALAR', type => 'STRING', }, PingArgs => {struct => 'SCALAR', type => 'STRING', }, CompressLevel => {struct => 'SCALAR', type => 'INT', }, PingMaxMsec => {struct => 'SCALAR', type => 'INT', }, SmbClientTimeout => {struct => 'SCALAR', type => 'INT', }, MaxOldPerPCLogFiles => {struct => 'SCALAR', type => 'INT', }, SendmailPath => {struct => 'SCALAR', type => 'STRING', }, EMailNotifyMinDays => {struct => 'SCALAR', type => 'INT', }, EMailFromUserName => {struct => 'SCALAR', type => 'STRING', }, EMailAdminUserName => {struct => 'SCALAR', type => 'STRING', }, EMailNoBackupEverMesg => {struct => 'SCALAR', type => 'MEMO', }, EMailNotifyOldBackupDays => {struct => 'SCALAR', type => 'INT', }, EMailNoBackupRecentMesg => {struct => 'SCALAR', type => 'MEMO', }, EMailNotifyOldOutlookDays => {struct => 'SCALAR', type => 'INT', }, EMailOutlookBackupMesg => {struct => 'SCALAR', type => 'MEMO', }, CgiAdminUserGroup => {struct => 'SCALAR', type => 'STRING', }, CgiAdminUsers => {struct => 'SCALAR', type => 'STRING', }, Language => {struct => 'SCALAR', type => 'STRING', }, CgiUserHomePageCheck => {struct => 'SCALAR', type => 'STRING', }, CgiUserUrlCreate => {struct => 'SCALAR', type => 'STRING', }, CgiDateFormatMMDD => {struct => 'SCALAR', type => 'BOOLEAN', }, CgiNavBarAdminAllHosts => {struct => 'SCALAR', type => 'BOOLEAN', }, CgiHeaders => {struct => 'SCALAR', type => 'STRING', }, CgiImageDir => {struct => 'SCALAR', type => 'STRING', }, CgiImageDirURL => {struct => 'SCALAR', type => 'STRING', }, ); 1; BackupPC-3.3.2/lib/BackupPC/FileZIO.pm0000444000076500000240000003124313042250554016115 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::FileZIO package # # DESCRIPTION # # This library defines a BackupPC::FileZIO class for doing # compressed or normal file I/O. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::FileZIO; use strict; use vars qw( $CompZlibOK ); use Carp; use File::Path; use File::Copy; use Encode; # # For compressed files we have a to careful about running out of memory # when we inflate a deflated file. For example, if a 500MB file of all # zero-bytes is compressed, it will only occupy a few tens of kbytes. If # we read the compressed file in decent-size chunks, a single inflate # will try to allocate 500MB. Not a good idea. # # Instead, we compress the file in chunks of $CompMaxWrite. If a # deflated chunk produces less than $CompMaxRead bytes, then we flush # and continue. This adds a few bytes to the compressed output file, but # only in extreme cases where the compression ratio is very close to # 100%. The result is that, provided we read the compressed file in # chunks of $CompMaxRead or less, the biggest inflated data will be # $CompMaxWrite. # my $CompMaxRead = 131072; # 128K my $CompMaxWrite = 6291456; # 6MB # # We maintain a write buffer for small writes for both compressed and # uncompressed files. This is the size of the write buffer. # my $WriteBufSize = 65536; BEGIN { eval "use Compress::Zlib;"; if ( $@ ) { # # Compress::Zlib doesn't exist. Define some dummy constant # subs so that the code below doesn't barf. # eval { sub Z_OK { return 0; } sub Z_STREAM_END { return 1; } }; $CompZlibOK = 0; } else { $CompZlibOK = 1; } }; sub open { my($class, $fileName, $write, $compLevel) = @_; local(*FH); my($fh); if ( ref(\$fileName) eq "GLOB" ) { $fh = $fileName; } else { if ( $write ) { open(FH, ">", $fileName) || return; } else { open(FH, "<", $fileName) || return; } binmode(FH); $fh = *FH; } $compLevel = 0 if ( !$CompZlibOK ); my $self = bless { fh => $fh, name => $fileName, write => $write, writeZeroCnt => 0, compress => $compLevel, }, $class; if ( $compLevel ) { if ( $write ) { $self->{deflate} = $self->myDeflateInit; } else { $self->{inflate} = $self->myInflateInit; $self->{inflateStart} = 1; } } return $self; } sub compOk { return $CompZlibOK; } # # Request utf8 strings with readLine interface # sub utf8 { my($self, $mode) = @_; $self->{utf8} = $mode; } sub myDeflateInit { my $self = shift; return deflateInit( -Bufsize => 65536, -Level => $self->{compress}, ); } sub myInflateInit { my $self = shift; return inflateInit( -Bufsize => 65536, ); } sub read { my($self, $dataRef, $nRead) = @_; my($n); return if ( $self->{write} ); return sysread($self->{fh}, $$dataRef, $nRead) if ( !$self->{compress} ); while ( !$self->{eof} && $nRead > length($self->{dataOut}) ) { if ( !length($self->{dataIn}) ) { $n = sysread($self->{fh}, $self->{dataIn}, $CompMaxRead); return $n if ( $n < 0 ); $self->{eof} = 1 if ( $n == 0 ); } if ( $self->{inflateStart} && $self->{dataIn} ne "" ) { my $chr = substr($self->{dataIn}, 0, 1); $self->{inflateStart} = 0; if ( $chr eq chr(0xd6) || $chr eq chr(0xd7) ) { # # Flag 0xd6 or 0xd7 means this is a compressed file with # appended md4 block checksums for rsync. Change # the first byte back to 0x78 and proceed. # ##print("Got 0xd6/0xd7 block: normal\n"); substr($self->{dataIn}, 0, 1) = chr(0x78); } elsif ( $chr eq chr(0xb3) ) { # # Flag 0xb3 means this is the start of the rsync # block checksums, so consider this as EOF for # the compressed file. Also seek the file so # it is positioned at the 0xb3. # sysseek($self->{fh}, -length($self->{dataIn}), 1); $self->{eof} = 1; $self->{dataIn} = ""; ##print("Got 0xb3 block: considering eof\n"); last; } else { # # normal case: nothing to do # } } my($data, $err) = $self->{inflate}->inflate($self->{dataIn}); $self->{dataOut} .= $data; if ( $err == Z_STREAM_END ) { #print("R"); $self->{inflate} = $self->myInflateInit; $self->{inflateStart} = 1; } elsif ( $err != Z_OK ) { $$dataRef = ""; return -1; } } if ( $nRead >= length($self->{dataOut}) ) { $n = length($self->{dataOut}); $$dataRef = $self->{dataOut}; $self->{dataOut} = ''; return $n; } else { $$dataRef = substr($self->{dataOut}, 0, $nRead); $self->{dataOut} = substr($self->{dataOut}, $nRead); return $nRead; } } # # Provide a line-at-a-time interface. This splits and buffers the # lines, you cannot mix calls to read() and readLine(). # sub readLine { my($self) = @_; my $str; $self->{readLineBuf} = [] if ( !defined($self->{readLineBuf}) ); while ( !@{$self->{readLineBuf}} ) { $self->read(\$str, $CompMaxRead); if ( $str eq "" ) { $str = $self->{readLineFrag}; $self->{readLineFrag} = ""; $str = decode_utf8($str) if ( $self->{utf8} ); return $str; } @{$self->{readLineBuf}} = split(/\n/, $self->{readLineFrag} . $str); if ( substr($str, -1, 1) ne "\n" ) { $self->{readLineFrag} = pop(@{$self->{readLineBuf}}); } else { $self->{readLineFrag} = ""; } } $str = shift(@{$self->{readLineBuf}}) . "\n"; if ( $self->{utf8} ) { my $strUtf8 = decode_utf8($str, 0); $strUtf8 = $str if ( length($strUtf8) == 0 ); return $strUtf8; } return $str; } sub rewind { my($self) = @_; return if ( $self->{write} ); return sysseek($self->{fh}, 0, 0) if ( !$self->{compress} ); $self->{dataOut} = ''; $self->{dataIn} = ''; $self->{eof} = 0; $self->{inflate} = $self->myInflateInit; $self->{inflateStart} = 1; return sysseek($self->{fh}, 0, 0); } sub writeBuffered { my $self = shift; my($data, $force) = @_; # # Buffer small writes using a buffer size of up to $WriteBufSize. # if ( $force || length($self->{writeBuf}) + length($data) > $WriteBufSize ) { if ( length($self->{writeBuf}) ) { my $wrData = $self->{writeBuf} . $data; return -1 if ( syswrite($self->{fh}, $wrData) != length($wrData) ); $self->{writeBuf} = undef; } else { return if ( length($data) == 0 ); return -1 if ( syswrite($self->{fh}, $data) != length($data) ); } } else { $self->{writeBuf} .= $data; } return 0; } sub write { my($self, $dataRef) = @_; my $n = length($$dataRef); return if ( !$self->{write} ); print(STDERR $$dataRef) if ( $self->{writeTeeStderr} ); return 0 if ( $n == 0 ); if ( !$self->{compress} ) { # # If smbclient gets a read error on the client (due to a file lock) # it will write a dummy file of zeros. We detect this so we can # store the file efficiently as a sparse file. writeZeroCnt is # the number of consecutive 0 bytes at the start of the file. # my $skip = 0; if ( $self->{writeZeroCnt} >= 0 && $$dataRef =~ /^(\0+)/s ) { $skip = length($1); $self->{writeZeroCnt} += $skip; return $n if ( $skip == $n ); } # # We now have some non-zero bytes, so time to seek to the right # place and turn off zero-byte detection. # if ( $self->{writeZeroCnt} > 0 ) { sysseek($self->{fh}, $self->{writeZeroCnt}, 0); $self->{writeZeroCnt} = -1; } elsif ( $self->{writeZeroCnt} == 0 ) { $self->{writeZeroCnt} = -1; } return -1 if ( $self->writeBuffered(substr($$dataRef, $skip)) < 0 ); return $n; } for ( my $i = 0 ; $i < $n ; $i += $CompMaxWrite ) { my $dataIn = substr($$dataRef, $i, $CompMaxWrite); my $dataOut = $self->{deflate}->deflate($dataIn); return -1 if ( $self->writeBuffered($dataOut) < 0 ); $self->{deflateIn} += length($dataIn); $self->{deflateOut} += length($dataOut); if ( $self->{deflateIn} >= $CompMaxWrite ) { if ( $self->{deflateOut} < $CompMaxRead ) { # # Compression is too high: to avoid huge memory requirements # on read we need to flush(). # $dataOut = $self->{deflate}->flush(); #print("F"); $self->{deflate} = $self->myDeflateInit; return -1 if ( $self->writeBuffered($dataOut) < 0 ); } $self->{deflateIn} = $self->{deflateOut} = 0; } } return $n; } sub name { my($self) = @_; return $self->{name}; } sub writeTeeStderr { my($self, $param) = @_; $self->{writeTeeStderr} = $param if ( defined($param) ); return $self->{writeTeeStderr}; } sub close { my($self) = @_; my $err = 0; if ( $self->{write} && $self->{compress} ) { my $data = $self->{deflate}->flush(); $err = 1 if ( $self->writeBuffered($data) < 0 ); } elsif ( $self->{write} && !$self->{compress} ) { if ( $self->{writeZeroCnt} > 0 ) { # # We got a file of all zero bytes. Write a single zero byte # at the end of the file. On most file systems this is an # efficient way to store the file. # $err = 1 if ( sysseek($self->{fh}, $self->{writeZeroCnt} - 1, 0) != $self->{writeZeroCnt} - 1 || syswrite($self->{fh}, "\0") != 1 ); } } $self->writeBuffered(undef, 1); close($self->{fh}); return $err ? -1 : 0; } # # If $compress is >0, copy and compress $srcFile putting the output # in $destFileZ. Otherwise, copy the file to $destFileNoZ, or do # nothing if $destFileNoZ is undef. Finally, if rename is set, then # the source file is removed. # sub compressCopy { my($class, $srcFile, $destFileZ, $destFileNoZ, $compress, $rmSrc) = @_; my(@s) = stat($srcFile); my $atime = $s[8] =~ /(.*)/ && $1; my $mtime = $s[9] =~ /(.*)/ && $1; if ( $CompZlibOK && $compress > 0 ) { my $fh = BackupPC::FileZIO->open($destFileZ, 1, $compress); my $data; if ( defined($fh) && open(LOG, "<", $srcFile) ) { binmode(LOG); while ( sysread(LOG, $data, 65536) > 0 ) { $fh->write(\$data); } close(LOG); $fh->close(); unlink($srcFile) if ( $rmSrc ); utime($atime, $mtime, $destFileZ); return 1; } else { $fh->close() if ( defined($fh) ); return 0; } } return 0 if ( !defined($destFileNoZ) ); if ( $rmSrc ) { return rename($srcFile, $destFileNoZ); } else { return 0 if ( !copy($srcFile, $destFileNoZ) ); utime($atime, $mtime, $destFileNoZ); } } 1; BackupPC-3.3.2/lib/BackupPC/Lang/0000755000076500000240000000000013042250554015176 5ustar craigstaffBackupPC-3.3.2/lib/BackupPC/Lang/cz.pm0000444000076500000240000015011613042250554016152 0ustar craigstaff#!/usr/bin/perl #my %lang; #use strict; #use utf8; # -------------------------------- $Lang{Start_Archive} = "Spustit Archivaci"; $Lang{Stop_Dequeue_Archive} = "Ukonèit/Odstranit z Fronty Archivaci"; $Lang{Start_Full_Backup} = "Spustit Úplné Zálohování"; $Lang{Start_Incr_Backup} = "Spustit Inkremetaèní Zálohování"; $Lang{Stop_Dequeue_Backup} = "Ukonèit/Odstranit z Fronty Zálohování"; $Lang{Restore} = "Obnovit"; $Lang{Type_full} = "úplný"; $Lang{Type_incr} = "inkrementaèní"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Pouze oprávnìní uživatelé mají pøístup k administraènímu nastavení."; $Lang{H_Admin_Options} = "BackupPC Server: Administraèní nastavení"; $Lang{Admin_Options} = "Administraèní nastavení"; $Lang{Admin_Options_Page} = < \${h2("Kontrola Serveru")}
    Znovu nahrát konfiguraci serveru:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Není možné se pøipojit k BackupPC serveru"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < Chyba: \$err.
    Je možné, že BackupPC server není spuštìn nebo je chyba v konfiguraci. Prosím oznamte to systémovému administrátorovi. EOF $Lang{Admin_Start_Server} = < BackupPC server na \$Conf{ServerHost} port \$Conf{ServerPort} není momentálnì spuštìn (možná jste ho ukonèil nebo ještì nespustil).
    Chceste ho spustit? EOF # ----- $Lang{H_BackupPC_Server_Status} = "Status Serveru BackupPC"; $Lang{BackupPC_Server_Status_General_Info}= <
  • PID serveru je \$Info{pid}, na hostu \$Conf{ServerHost}, verze \$Info{Version}, spuštìný \$serverStartTime.
  • Vygenerování stavu : \$now.
  • Nahrání konfigurace : \$configLoadTime.
  • PC bude pøíštì ve frontì : \$nextWakeupTime.
  • Další informace:
    • \$numBgQueue nevyøízených žádostí o zálohu z posledního naplánované probuzení,
    • \$numUserQueue nevyøízených žádostí o zálohu od uživatelù,
    • \$numCmdQueue pending command requests, \$poolInfo
    • Pool file system was recently at \$Info{DUlastValue}% (\$DUlastTime), today\'s max is \$Info{DUDailyMax}% (\$DUmaxTime) and yesterday\'s max was \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Probíhající úlohy")}

    \$jobStr
    Host Typ Uživatel Spuštìno Pøíkaz PID Xfer PID

    \${h2("Selhání, která vyžadují pozornost")}

    \$statusStr
    Host Typ Uživatel Poslední pokus Detaily Èas chyby Poslední chyba (jiná než žádný ping)
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: Výpis Hostù"; $Lang{BackupPC__Archive} = "BackupPC: Archiv"; $Lang{BackupPC_Summary} = <

    • Tento stav byl vygenerován v \$now.
    • Stav úložištì je \$Info{DUlastValue}% (\$DUlastTime), dnešní maximum je \$Info{DUDailyMax}% (\$DUmaxTime) a vèerejší maximum bylo \$Info{DUDailyMaxPrev}%.

    \${h2("Hosté s úspìšnì provedenými zálohami")}

    \$hostCntGood hostù bylo úspìšnì zálohováno, v celkové velikost:

    • \$fullTot úplných záloh v celkové velitosti \${fullSizeTot}GB (pøed kompresí),
    • \$incrTot inkementaèních záloh v celkové velikosti \${incrSizeTot}GB (pøed kompresí).

    \$strGood
    Host Uživatel #Plný Plný Èas (dní) Plný Velikost (GB) Rychlost (MB/s) #Inkr Inkr èas (dní) Poslední Záloha (dní) Stav #Xfer chyb Poslední pokus


    \${h2("Hosté s žádnými provedenými zálohami")}

    \$hostCntNone hostù s žádnými zálohani.

    \$strNone
    Host Uživatel #Plný Plný Èas (dní) Plný Velikost (GB) Rychlost (MB/s) #Inkr Inkr èas (dní) Poslední Záloha (dní) Stav #Xfer chyb Poslední pokus
    EOF $Lang{BackupPC_Archive} = < \$hostCntGood hostù, kteøí byli zálohováni v celkové velikosti \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    Host Uživatel Velikost zálohy

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Umístìní Archivu EOF $Lang{BackupPC_Archive2_compression} = < Komprese None
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Procent paritních dat (0 = vypnuté, 5 = typické) EOF $Lang{BackupPC_Archive2_split} = < Rozdìlit výstup na Megabytes EOF # ----------------------------------- $Lang{Pool_Stat} = <V úložišti je \${poolSize}GB zahrnujíc \$info->{"\${name}FileCnt"} souborù a \$info->{"\${name}DirCnt"} adresáøù (od \$poolTime),
  • Hashování úložištì dává \$info->{"\${name}FileCntRep"} opakujících se souborù s nejdelším øetìzem \$info->{"\${name}FileRepMax"},
  • Noèní úklid úložištì odstranil \$info->{"\${name}FileCntRm"} souborù velikosti \${poolRmSize}GB (kolem \$poolTime), EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: Záloha vyžádána na \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < Odpovìï serveru na: \$reply

    Vra se na domovskou stránku \$host. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Zaèátek zálohy potvrzen na \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Chystáte se spustit \$type zálohu na \$host.

    Opravdu to chcete provést?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Ukonèit potvrzení kopie na \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < Chystáte se ukonèit/vyøadit z fronty zálohování na \$host;
    Prosím, nezaèínejte jiné zálohování hodin.

    Opravdu to chcete provést?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Pouze oprávnìní uživatelé mají pøistup k frontám."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Pouze oprávnìní uživatelé mohou archivovat."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Pøehled front"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Pøehled fronty uživatelù")}

    Následující uživatelé jsou momentálnì ve frontì:

    \$strUser
    Host Èas do Uživatel


    \${h2("Souhrn fronty v pozadí")}

    Následující žádosti v pozadí jsou momentálnì ve frontì:

    \$strBg
    Host Èas do Uživatel


    \${h2("Souhrn fronty pøíkazù")}

    Následující pøíkazy jsou momentálnì ve frontì:

    \$strCmd
    Host Èas do Uživatel Pøíkaz
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: Soubor \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, modifikován \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ pøeskoèeno \$skipped øádkù ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nNení možné otevøít log soubor \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: Historie Log Souboru";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    Soubor Velikost Èas modifikace
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Pøíjemce Odesílatel Èas Pøedmìt
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Prohlížet zálohu \$num pro \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Obnovit nastavení pro \$host"; $Lang{Restore_Options_for__host2} = < Vybral jste následující soubory/adresáøe z èásti \$share, záloha èíslo #\$num:
      \$fileListStr

    Pro obnovení tìchto souborù/adresáøù máte tøi možnosti. Vyberte si, prosím, jednu z následujících možností.

    \${h2("Možnost 1: Pøímá obnova")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    Varování: jakýkoliv existující soubor, který odpovída tìm, které máte vybrány bude smazán!

    \$hiddenStr
    Obnovit souboru do hosta
    Obnovení souborù do èásti
    Obnovit soubory v adresáøi
    (vztahující se k èásti)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Možnost 2: Stáhnout Zip archiv")}

    Mùžete stáhnout Zip archiv obsahující všechny soubory/adresáøe, které jste vybral. Poté mùžete použít aplikaci, napø. WinZip, k zobrazení nebp rozbalení nìkterého z tìchto souborù.

    Varování: v závislosti na tom, které soubory/adresáøe jste vybral, tento archiv mùže být velmi velký. Vytvoøení a pøenos archivu mùže trvat minuty, a budete potøebovat dostatek místa na lokálním disku.

    \$hiddenStr Vytvoøit archiv relativní k \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (jinak bude archiv obsahovat plnou cestu).
    Komprese (0=off, 1=rychlá,...,9=nejlepší)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Možnost 2: Stánout Zip archiv")}

    Archive::Zip není nainstalován, èili nebude možné stáhnout zip archiv. Požádejte systémového administrátora o instalaci Archive::Zip z www.cpan.org.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < Mùžete stáhnout Tar archiv obsahující všechny soubory/adresáøe, které jste vybral. Poté mùžete použít aplikaci, napø. tar nebo WinZip, k zobrazení nebp rozbalení nìkterého z tìchto souborù.

    Varování: v závislosti na tom, které soubory/adresáøe jste vybral, tento archiv mùže být velmi velký. Vytvoøení a pøenos archivu mùže trvat minuty, a budete potøebovat dostatek místa na lokálním disku.

    \$hiddenStr Vytvoø archiv relativní k \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (jinak bude archiv obsahovat plnou cestu).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Potvrzení obnovení na \$host"; $Lang{Are_you_sure} = < Chystáte se zahájit obnovu pøímo do poèítaèe \$In{hostDest}. Následující soubory budou obnoveny do èásti \$In{shareDest}, ze zálohy èíslo \$num:

    \$fileListStr
    Originální soubor/adresáøBude obnoven do

    \$hiddenStr Obravdu to chceš provést?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Obnovit vyžádané na \$hostDest"; $Lang{Reply_from_server_was___reply} = < Odpovìï od serveru: \$reply

    Jít zpìt na domovská stránka \$hostDest. EOF $Lang{BackupPC_Archive_Reply_from_server} = < Odpovìï od serveru: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Pøehled záloh hosta \$host"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("User Actions")}

    \$startIncrStr

    \${h2("Pøehled záloh")}

    Kliknìte na èíslo zálohy pro prohlížení a obnovení zálohy.

    \$str
    Backup# Typ Vyplnìno Úroveò Datum spuštìní Doba trvání/minuty Doba/dny Cesta serveru zálohy

    \$restoreStr



    \${h2("Pøehled Xfer chyb")}

    \$errStr
    Backup# Typ Pohled #Xfer chyby #špatné soubory #špatné èásti #tar chyby


    \${h2("File Size/Count Reuse Summary")}

    Existující soubory jsou ty, které jsou již v úložišti; nové jsou pøidané do úložištì. Prázné soubory a SMB chyby nejsou poèítány.

    \$sizeStr
    Celkovì Existující soubory Nové soubory
    Záloha # Typ #Soubory Velikost/MB MB/sec #Soubory Velikost/MB #Soubory Velikost/MB


    \${h2("Pøehled kompresí")}

    Výkon komprese pro soubory, které jsou již v úložišti a pro novì zkomprimované soubory.

    \$compStr
    Existující soubory Nové soubory
    Záloha # Typ Úroveò komprese Velikost/MB Komprese/MB Komprese Velikost/MB Komprese/MB Komprese


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Pøehled archivù hosta \$host "; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("Uživatelské akce")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Chyba"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Server"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • Prohlížíte zálohu #\$num, která byla spuštìna kolem \$backupTime (\$backupAge dní zpìt), \$filledBackup
    • Zadej adresáø:
    • Klikni na adresáø níže a pokraèuj do nìj,
    • Klikni na soubor níže a obnov ho,
    • Mùžeš vidìt zálohu history aktuálního adresáøe.
    \${h2("Obsah \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Historie záloh adresáøù pro \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "adres"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < Tato obrazovka zobrazuje každou unikátní verzi souboru ze všech záloh:
    • Klikni na èíslo zálohy k návratu do prohlížeèe záloh,
    • Klikni na odkaz adresáøe (\$Lang->{DirHistory_dirLink}) k pøechodu do nìj,
    • Klikni na odkaz verze souboru (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) k jeho stažení,
    • Soubory se stejným obsahem v rùzných zálohách mají stejné èíslo verze,
    • Soubory nebo adresáøe, které nejsou ve vybrané záloze nejsou oznaèeny.
    • Soubory zobrazené se stejným èíslem verze mohou mít rozdílné atributy. Vyber èíslo zálohy k zobrazení atributù souboru.
    \${h2("Historie \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Èíslo zálohy
    Èas zálohy
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Obnovit #\$num detailù pro \$host"; $Lang{Restore___num_details_for__host2} = <
    Èíslo \$Restores[\$i]{num}
    Vyžádal \$RestoreReq{user}
    Èas vyžádání \$reqTime
    Výsledek \$Restores[\$i]{result}
    Chybová zpráva \$Restores[\$i]{errorMsg}
    Zdrojový host \$RestoreReq{hostSrc}
    Èíslo zdrojové zálohy \$RestoreReq{num}
    Zdrojová èást \$RestoreReq{shareSrc}
    Cílový host \$RestoreReq{hostDest}
    Cílová èást \$RestoreReq{shareDest}
    Èas spuštìní \$startTime
    Doba trvání \$duration min
    Poèet souborù \$Restores[\$i]{nFiles}
    Celková velikost \${MB} MB
    Pøenosová rychlost \$MBperSec MB/sec
    TarCreate chyb \$Restores[\$i]{tarCreateErrs}
    Xfer chyb \$Restores[\$i]{xferErrs}
    Xfer log soubor View, Errors

    \${h1("Seznam souborù/adresáøù")}

    \$fileListStr
    Originální soubor/adresáøObnoven do
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Archivovat #\$num detailù pro \$host"; $Lang{Archive___num_details_for__host2 } = <
    Èíslo \$Archives[\$i]{num}
    Vyžádal \$ArchiveReq{user}
    Èas vyžádání \$reqTime
    Odpovìd \$Archives[\$i]{result}
    Chybová zpráva \$Archives[\$i]{errorMsg}
    Èas spustìní \$startTime
    Dpba trvání \$duration min
    Xfer log soubor View, Errors

    \${h1("Seznam hostù")}

    \$HostListStr
    HostÈíslo kopie
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Souhrn emailù"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new failed: zkontroluj apache error_log\n"; $Lang{Wrong_user__my_userid_is___} = "Špatný uživatel: moje userid je \$>, místo \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Pouze oprávnìní uživatelé jsou oprávnìni prohlížet souhrny PC."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Pouze oprávnìní uživatelé mohou ukonèit nebo spustit zálohování na" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Špatné èíslo \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "Nepodaøilo se otevøít \$file: problém konfigurace?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Pouze oprávnìní uživatelé mají pøístup k log a konfiguraèním souborùm."; $Lang{Only_privileged_users_can_view_log_files} = "Pouze oprávnìní uživatelé mají pøístup k log souborùm."; $Lang{Only_privileged_users_can_view_email_summaries} = "Pouze oprávnìní uživatelé mají pøístup k souhrnu emailù."; $Lang{Only_privileged_users_can_browse_backup_files} = "Pouze oprávnìní uživatelé mohou prohlížet soubory záloh" . " pro host \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Prázdné jméno hosta."; $Lang{Directory___EscHTML} = "Adresáø \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " je prázdný"; $Lang{Can_t_browse_bad_directory_name2} = "Není možné prohlížet - špatný název adresáøe" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Pouze oprávnìní uživatelé mohou obnovovat soubory zálohy" . " pro hosta \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Špatné jméno hosta \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "Nevybral jste žádný soubor; prosím jdìte Zpìt k" . " výbìru souborù."; $Lang{You_haven_t_selected_any_hosts} = "Nevybral jste žádného hosta; prosím jdìte Zpìt k" . " výbìru hostù."; $Lang{Nice_try__but_you_can_t_put} = "Nelze umístit \'..\' do názvu souboru"; $Lang{Host__doesn_t_exist} = "Host \${EscHTML(\$In{hostDest})} neexistuje"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "Nemáte oprávnìní k obnovì na" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Nelze otevøít nebo vytvoøit " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Pouze oprávnìní uživatelé mohou obnovovat soubory zálohy" . " pro hosta \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Prázdné jméno hosta"; $Lang{Unknown_host_or_user} = "Neznámý host nebo uživatel \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Pouze oprávnìní uživatelé mají pøístup k informacím o" . " hostu \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Pouze oprávnìní uživatelé mají pøístup k informacím o archivaci."; $Lang{Only_privileged_users_can_view_restore_information} = "Pouze oprávnìní uživatelé mají pøístup k informacím o obnovì."; $Lang{Restore_number__num_for_host__does_not_exist} = "Èíslo obnovení \$num pro hosta \${EscHTML(\$host)}" . " neexsituje."; $Lang{Archive_number__num_for_host__does_not_exist} = "Èíslo archivu \$num pro hosta \${EscHTML(\$host)}" . " neexsituje."; $Lang{Can_t_find_IP_address_for} = "Nelze nalézt IP adresu pro \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < Dokud nebude vidìt \$host na vybrané DHCP adrese, mùžete pouze spustit žádost z pøímo klientského zaøízení. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "Záloha vyžádána z DHCP \$host (\$In{hostIP}) uživatelem" . " \$User z \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Záloha vyžádána z \$host uživatelem \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Záloha ukonèena/vyøazena z fronty z \$host uživatelem \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Obnova vyžádána na hosta \$hostDest, obnova #\$num," . " uživatelem \$User z \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Archivace vyžádána uživatelem \$User z \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "Stav"; $Lang{PC_Summary} = "Souhrn hostù"; $Lang{LOG_file} = "LOG soubor"; $Lang{LOG_files} = "LOG soubory"; $Lang{Old_LOGs} = "Staré LOGy"; $Lang{Email_summary} = "Souhrn emailù"; $Lang{Config_file} = "Konfiguraèní soubor"; # $Lang{Hosts_file} = "Hosts soubor"; $Lang{Current_queues} = "Aktuální fronty"; $Lang{Documentation} = "Dokumentace"; #$Lang{Host_or_User_name} = "Jméno uživatele nebo hosta:"; $Lang{Go} = "Jdi"; $Lang{Hosts} = "Hosts"; $Lang{Select_a_host} = "Vyber hosta..."; $Lang{There_have_been_no_archives} = "

    Nebyli žádné archivy

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    Toto PC nebylo nikdy zálohováno!!

    \n"; $Lang{This_PC_is_used_by} = "
  • Toto PC je používáno uživatelem \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Rozbalování chyb)"; $Lang{XferLOG} = "XferLOG"; $Lang{Errors} = "Chyby"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <Poslední email odeslán uživately \${UserLink(\$user)} byl v \$mailTime, pøedmìt "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <Pøíkaz \$cmd je aktuálnì vykonáván pro \$host, spuštìn v \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <Host \$host èeká ve frontì na pozadí (bude brzy zálohován). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <Host \$host èeká ve frontì uživatelù (bude brzy zálohován). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <Pøíkaz pro \$host èeká ve frontì pøíkazù (bude brzy spuštìn). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <Poslední stav \"\$Lang->{\$StatusHost{state}}\"\$reason v èase \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <Poslední chyba je \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <Pingy na \$host selhaly \$StatusHost{deadCnt} za sebou. EOF # ----- $Lang{Prior_to_that__pings} = "Pøedchozí pingy"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr na \$host byli úspìšné \$StatusHost{aliveCnt} za sebou. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Protože \$host byl na síti alespoò \$Conf{BlackoutGoodCnt} za sebou, nebude zálohován z \$blackoutStr. EOF $Lang{__time0_to__time1_on__days} = "\$t0 to \$t1 on \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Zálohy byli odloženy na \$hours hodin (zmìn toto èíslo). EOF $Lang{tryIP} = " a \$StatusHost{dhcpHostIP}"; # $Lang{Host_Inhost} = "Host \$In{host}"; $Lang{checkAll} = <  Select all EOF $Lang{checkAllHosts} = <  Select all EOF $Lang{fileHeader} = < Jméno Typ Mód # Velikost Datum zmìny EOF $Lang{Home} = "Doma"; $Lang{Browse} = "Prohlížení záloh"; $Lang{Last_bad_XferLOG} = "Poslední špatný XferLOG"; $Lang{Last_bad_XferLOG_errors_only} = "Poslední špatný XferLOG (chyb pouze)"; $Lang{This_display_is_merged_with_backup} = < Toto zobrazení je slouèeno se zálohou #\$numF. EOF $Lang{Visit_this_directory_in_backup} = < Vyberte zálohu, kterou si pøejete zobrazit: EOF $Lang{Restore_Summary} = < Klikni na obnovení pro více detailù. \$restoreStr
    Obnovení # Výsledek Datum spuštení Doba trvání/minuty #souborù MB #tar chyb #xferErrs

    EOF $Lang{Archive_Summary} = < Klikni na èíslo archivu pro více detailù. \$ArchiveStr
    Archiv# Výsledek Datum spuštení Doba trvání/minuty

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Documentace"; $Lang{No} = "ne"; $Lang{Yes} = "ano"; $Lang{The_directory_is_empty} = <Adresáø \$dirDisplay je prázdný EOF #$Lang{on} = "zapnout"; $Lang{off} = "vypnout"; $Lang{backupType_full} = "plný"; $Lang{backupType_incr} = "inkr"; $Lang{backupType_partial} = "èásteèný"; $Lang{failed} = "neúspìšný"; $Lang{success} = "úspìšný"; $Lang{and} = "a"; # ------ # Hosts states and reasons $Lang{Status_idle} = "neèinný"; $Lang{Status_backup_starting} = "záloha se spouští"; $Lang{Status_backup_in_progress} = "záloha probíhá"; $Lang{Status_restore_starting} = "obnovení se spouští"; $Lang{Status_restore_in_progress} = "obnovení probíhá"; $Lang{Status_link_pending} = "link èeká"; $Lang{Status_link_running} = "link bìží"; $Lang{Reason_backup_done} = "hotovo"; $Lang{Reason_restore_done} = "obnovení dokonèeno"; $Lang{Reason_archive_done} = "archivace dokonèena"; $Lang{Reason_nothing_to_do} = "neèinný"; $Lang{Reason_backup_failed} = "zálohování selhalo"; $Lang{Reason_restore_failed} = "obnovení selhalo"; $Lang{Reason_archive_failed} = "archivace selhala"; $Lang{Reason_no_ping} = "žádný ping"; $Lang{Reason_backup_canceled_by_user} = "zálohování zrušeno uživatelem"; $Lang{Reason_restore_canceled_by_user} = "obnovení zrušeno uživatelem"; $Lang{Reason_archive_canceled_by_user} = "archivace zrušena uživatelem"; $Lang{Disabled_OnlyManualBackups} = "automatické zálohování zakázáno"; $Lang{Disabled_AllBackupsDisabled} = "zakázáno"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: žadné zálohy hosta \$host se nezdaøili"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Pøedmìt: $subj $headers Dear $userName, Vaše PC ($host) nebylo nikdy úspìšnì zálohováno naším zálohovacím softwarem. Zálohování PC by mìlo být spuštìno automaticky, když je Vaše PC pøipojeno do sítì. Mel by jste kontaktovat Vaši podporu pokud: - Vaše PC bylo pravidelnì pøipojováno do sítì, zøejmì je nìjaký probém v nastavení nebo konfiguraci, který zabraòuje zálohování. - Nechcete Vaše PC zálohovat a chcete pøestat dostávat tyto zprávy. Ujistìte se, že je Vaše PC pøipojeno do sítì, až budete pøíštì v kanceláøi. S pozdravem, BackupPC Genie http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: žádné nové zálohy pro \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Pøedmìt: $subj $headers Drahý $userName, Vaše PC ($host) nebylo úspìšnì zálohovýno již $days dní. Vaše PC bylo korektnì zálohováno $numBackups krát od $firstTime do dne pøed $days dny. Zálohování PC by se mìlo spustit automaticky, když je Vaše PC pøipojeno do sítì. Pokud bylo Vaše PC pøipojeno do sítì více než nìkolik hodin v prùbìhu posledních $days dní, mìl by jste kontaktovat Vaši podporu k zjištìní, proè zálohování nefunguje. Pokud jste mimo kanceláø, nemùžete udìlat nic jiného než zkopírovat kritické soubory na jiná media. Mìl by jste mít na pamìti, že všechny soubory vytvoøené nebo zmìnìné v posledních $days dnech (i s všemi novými emaily a pøílohami) nebudou moci býti obnoveny, pokud se disk ve Vašem poèítaèi poškodí. S pozdravem, BackupPC Genie http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Soubory programu Outlook na \$host je nutné zálohovat"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Pøedmìt: $subj $headers Drahý $userName, Soubory programu Outlook na Vašem PC mají $howLong. Tyto soubory obsahují všechny Vaše emaily, pøílohy, kontakty a informace v kalendáøi. Vaše PC bylo naposled korektnì zálohováno $numBackups krát od $firstTime do $lastTime. Nicménì Outlook zamkne všechny svoje soubory když je spuštìn a znemožòuje jejich zálohování. Doporuèujeme Vám zálohovat soubory Outlooku, když jste pøipojen do sítì tak, že ukonèíte program Outlook a všechny ostatní aplikace a ve vašem prohlížeèi otevøete tuto adresu: $CgiURL?host=$host Vyberte "Spustit inkrementaèní zálohování" dvakrát ke spuštení nového zálohování. Mùžete vybrat "Návrat na $host page" a poté stiknout "obnovit" ke zjištìní stavu zálohování. Dokonèení mùže trvat nìkolik minut. S pozdravem, BackupPC Genie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "nebylo zálohováno úspìšnì"; $Lang{howLong_not_been_backed_up_for_days_days} = "nebylo zálohováno \$days dní"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPC Server"; $Lang{RSS_Doc_Description} = "RSS kanál BackupPC"; $Lang{RSS_Host_Summary} = < Poznámka: oznaète Pøepsat, pokud chcete modifikovat hodnotu specifickou pro tohoto hosta.

    EOF $Lang{CfgEdit_Button_Save} = "Uložit"; $Lang{CfgEdit_Button_Insert} = "Vložit"; $Lang{CfgEdit_Button_Delete} = "Smazat"; $Lang{CfgEdit_Button_Add} = "Pøidat"; $Lang{CfgEdit_Button_Override} = "Pøepsat"; $Lang{CfgEdit_Button_New_Key} = "Nový klíè"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "Chyba: Neuloženo z dùvody chyb"; $Lang{CfgEdit_Error__must_be_an_integer} = "Chyba: \$var musí být celé èíslo"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Chyba: \$var musí být reálné èíslo"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Chyba: vstup \$var \$k musí být celé èíslo"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Chyba: vstup \$var \$k musí být reálné èíslo"; $Lang{CfgEdit_Error__must_be_executable_program} = "Chyba: \$var musí být správná cesta"; $Lang{CfgEdit_Error__must_be_valid_option} = "Chyba: \$var musí být správná možnost"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "Kopie hosta \$copyHost neexistuje; vytváøím nový název hosta \$fullHost. Smažte tohota hosta, pokud to není to, co jste chtìl."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User zkopíroval konfiguraci z hosta \$fromHost do \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User smazal \$p z \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User pøidal \$p do \$conf, nastavil na \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User zmìnil \$p v \$conf do \$valueNew z \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User smazal hosta \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User host \$host zmìnil \$key z \$valueOld na \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User pøidal host \$host: \$value\n"; #end of lang_cz.pm BackupPC-3.3.2/lib/BackupPC/Lang/de.pm0000444000076500000240000016066213042250554016135 0ustar craigstaff#!/usr/bin/perl # # by Ralph Passgang (13.11.2006 for V3.0.0) # by Ralph Passgang (30.06.2006 for V3.0.0) # by Ralph Passgang (07.06.2004 for V2.1.0beta3) # by Ralph Passgang (06.05.2004 for V2.1.0beta2) # by Manfred Herrmann (11.03.2004 for V2.1.0beta0) # by Manfred Herrmann (V1.1) (some typo errors + 3 new strings) # CVS-> Revision ??? # #my %lang; #use strict; # -------------------------------- $Lang{Start_Archive} = "Archivierung starten"; $Lang{Stop_Dequeue_Archive} = "Archivierung stoppen"; $Lang{Start_Full_Backup} = "Starte vollständiges Backup"; $Lang{Start_Incr_Backup} = "Starte inkrementelles Backup"; $Lang{Stop_Dequeue_Backup} = "Backup Stoppen/Aussetzen"; $Lang{Restore} = "Wiederherstellen"; $Lang{Type_full} = "voll"; $Lang{Type_incr} = "inkrementell"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Nur privilegierte Nutzer können die Administrationsoptionen einsehen."; $Lang{H_Admin_Options} = "BackupPC: Server Administrationsoptionen"; $Lang{Admin_Options} = "Admin Optionen"; $Lang{Admin_Options_Page} = < \${h2("Server Steuerung")}

    Server Konfiguration neu laden:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Kann keine Verbindung zu dem BackupPC Server herstellen!"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < Der Fehler war: \$err.
    Möglicherweise ist der BackupPC Server Prozess nicht gestartet oder es besteht ein Konfigurationsfehler. Bitte teilen Sie diese Fehlermeldung dem Systemadministrator mit. EOF $Lang{Admin_Start_Server} = < Der BackupPC Server auf \$Conf{ServerHost} Port \$Conf{ServerPort} ist momentan nicht aktiv (möglicherweise wurde er gestoppt, oder noch nicht gestartet).
    Möchten Sie den Server starten? EOF # ----- $Lang{H_BackupPC_Server_Status} = "BackupPC Serverstatus"; $Lang{BackupPC_Server_Status_General_Info}= <
  • Die Server Prozess ID (PID) ist \$Info{pid}, auf Computer \$Conf{ServerHost}, Version \$Info{Version}, gestartet am \$serverStartTime.
  • Dieser Status wurde am \$now generiert.
  • Die Konfiguration wurde am \$configLoadTime neu geladen.
  • Computer werden am \$nextWakeupTime auf neue Aufträge geprüft.
  • Weitere Informationen:
    • \$numBgQueue wartende Backup Aufträge der letzten Prüfung,
    • \$numUserQueue wartende Aufträge von Benutzern,
    • \$numCmdQueue wartende Kommando Aufträge. \$poolInfo
    • Das Pool Filesystem (Backup-Speicherplatz) ist zu \$Info{DUlastValue}% (\$DUlastTime) voll, das Maximum heute ist \$Info{DUDailyMax}% (\$DUmaxTime) und das Maximum gestern war \$Info{DUDailyMaxPrev}%. (Hinweis: Sollten ca. 70% überschritten werden, so ist evtl. bald eine Erweiterung des Backupspeichers erforderlich. Ist weitere Planung nötig?)
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Zur Zeit aktive Aufträge")}

    \$jobStr
    Computer Typ Benutzer Startzeit Kommando PID Transport PID

    \${h2("Fehler, die näher analysiert werden müssen!")}

    \$statusStr
    Computer Typ Benutzer letzter Versuch Details Fehlerzeit Letzter Fehler (ausser "kein ping")
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: Computerübersicht"; $Lang{BackupPC__Archive} = "BackupPC: Archivierung"; $Lang{BackupPC_Summary}=<

    • Dieser Status wurde am \$now generiert.
    • Das Pool Filesystem (Backup-Speicherplatz) ist zu \$Info{DUlastValue}% (\$DUlastTime) voll, das Maximum heute ist \$Info{DUDailyMax}% (\$DUmaxTime) und das Maximum gestern war \$Info{DUDailyMaxPrev}%. (Hinweis: Sollten ca. 70% überschritten werden, so ist evtl. bald eine Erweiterung des Backupspeichers erforderlich. Ist weitere Planung nötig?)

    \${h2("Computer mit erfolgreichen Backups")}

    Es gibt \$hostCntGood Computer die erfolgreich gesichert wurden, mit insgesamt:

    • \$fullTot Volle Backups, Gesamtgröße \${fullSizeTot}GB (vor Pooling und Komprimierung),
    • \$incrTot Inkrementelle Backups, Gesamtgröße \${incrSizeTot}GB (vor Pooling und Komprimierung).

    \$strGood
    Computer Benutzer #Voll Alter (Tage) Größe (GB) MB/sek #Inkr Alter (Tage) Letzes Backup (Tage) Status #Xfer Fehler Letzte Aktion


    \${h2("Computer ohne Backups")}

    Es gibt \$hostCntNone Computer ohne Backups !!!

    \$strNone
    Computer Benutzer #Voll Alter (Tage) Größe (GB) MB/sek #Inkr Alter (Tage) Letztes Backup (Tage) Status #Xfer Fehler Letzter Versuch
    EOF $Lang{BackupPC_Archive} = < Es gibt \$hostCntGood Computer die gesichert wurden, mit insgesamt \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    Computer Benutzer Backup Größe

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Archivierungsort/Gerät EOF $Lang{BackupPC_Archive2_compression} = < Kompression None
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Prozentsatz Paritätsdaten (0 = keine, 5 = Standard) EOF $Lang{BackupPC_Archive2_split} = < Aufteilen in Megabytes EOF # ----------------------------------- $Lang{Pool_Stat} = <Der Pool hat eine Größe von \${poolSize}GB und enthält \$info->{"\${name}FileCnt"} Dateien und \$info->{"\${name}DirCnt"} Verzeichnisse (Stand \$poolTime).
  • Das "Pool hashing" ergibt \$info->{"\${name}FileCntRep"} wiederholte Dateien mit der längsten Verkettung von \$info->{"\${name}FileRepMax"}.
  • Die nächtliche Bereinigung entfernte \$info->{"\${name}FileCntRm"} Dateien mit einer Größe von \${poolRmSize}GB (um ca. \$poolTime). EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: Backupauftrag für \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < Die Antwort des Servers war: \$reply

    Gehe zurück zur \$host Hauptseite. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Starte Backup von \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Sie starten ein \$type Backup für \$host.

    Möchten Sie das wirklich tun?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Beende Backup von \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < Sie werden Backups abbrechen bzw. Aufträge löschen für Computer \$host;
    Zusätzlich bitte keine Backups starten für die Dauer von Stunden.

    Möchten Sie das wirklich tun?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Nur berechtigte Benutzer können die Warteschlangen einsehen."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Nur berechtigte Benutzer könnnen archivieren."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Warteschlange Übersicht"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Übersicht Benutzeraufträge in der Warteschlange")}

    Die folgenden Benutzeraufträge sind eingereiht:

    \$strUser
    Computer Uhrzeit Benutzer


    \${h2("Übersicht Hintergrundaufträge in der Warteschlange")}

    Die folgenden Hintergrundaufträge sind eingereiht:

    \$strBg
    Computer Uhrzeit Benutzer


    \${h2("Übersicht Kommandoaufträge in der Warteschlange")}

    Die folgenden Kommandoaufträge sind eingereiht:

    \$strCmd
    Computer Uhrzeit Benutzer Kommando
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: Datei \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, verändert am \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ überspringe \$skipped Zeilen ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nKann LOG Datei nicht öffnen \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: LOG Datei Historie";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    Datei Größe letzte Änderung
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Empfänger Computer Zeitpunkt Betreff
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Durchsuchen des Backups \$num für Computer \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Wiederherstellungsoptionen für \$host"; $Lang{Restore_Options_for__host2} = < Sie haben die folgenden Dateien/Verzeichnisse aus der Freigabe \$share des Backups mit der Nummer #\$num selektiert:
      \$fileListStr

    Sie haben drei verschiedene Möglichkeiten zur Wiederherstellung (Restore) der Dateien/Verzeichnisse. Bitte wählen Sie eine der folgenden Möglichkeiten:.

    \${h2("Möglichkeit 1: Direkte Wiederherstellung")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost wiederherzustellen. Alternativ können Sie einen anderen Computer und/oder Freigabe als Ziel angeben.

    Warnung: alle aktuell existierenden Dateien/Verzeichnisse, die bereits vorhanden sind, werden überschrieben! (Tip: Alternativ eine spezielle Freigabe erstellen mit Schreibrecht für den Backup-Benutzer und die wiederhergestellten Dateien/Verzeichnisse durch Stichproben prüfen, ob die beabsichtigte Wiederherstellung korrekt ist.)

    \$hiddenStr
    Restore auf Computer
    Restore auf Freigabe
    Restore in Unterverzeichnis
    (relativ zur Freigabe)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Möglichkeit 2: Download als Zip Archiv")}

    Sie können eine ZIP Archivdatei downloaden, die alle selektierten Dateien/Verzeichnisse enthält. Mit einer lokalen Anwendung (z.B. WinZIP, WinXP-ZIP-Ordner...) können Sie dann beliebige Dateien entpacken.

    Warnung: Abhängig von der Anzahl und Größe der selektierten Dateien/Verzeichnisse kann die ZIP Archiv Datei extrem groß bzw. zu groß werden. Der Download kann sehr lange dauern und der Speicherplatz auf Ihrem PC muß ausreichen. Selektieren Sie evtl. die Dateien/Verzeichnisse erneut und lassen sehr große und unnötige Dateien weg.

    \$hiddenStr Archiv relativ zu Pfad \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (andernfalls enthält die Archiv Datei vollständige Pfade).
    Kompression (0=aus, 1=schnelle,...,9=höchste)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Möglichkeit 2: Download als Zip Archiv")}

    Archive::Zip ist nicht installiert. Der Download als Zip Archiv Datei ist daher nicht möglich. Bitte lassen Sie bei Bedarf von Ihrem Administrator die Perl-Erweiterung Archive::Zip von www.cpan.org installieren. Vielen Dank!

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < Sie können eine Tar Archivdatei downloaden, die alle selektierten Dateien/Verzeichnisse enthält. Mit einer lokalen Anwendung (z.B. tar, WinZIP...) können Sie dann beliebige Dateien entpacken.

    Warnung: Abhängig von der Anzahl und Größe der selektierten Dateien/Verzeichnisse kann die Tar-Archiv Datei extrem groß bzw. zu groß werden. Der Download kann sehr lange dauern und der Speicherplatz auf Ihrem PC muß ausreichen. Selektieren Sie evtl. die Dateien/Verzeichnisse erneut und lassen sehr große und unnötige Dateien weg.

    \$hiddenStr Archiv relativ zu Pfad \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (andernfalls enthält die Archiv Datei vollständige Pfade).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Bestätigung für die Wiederherstellung auf \$host"; $Lang{Are_you_sure} = < Sie starten eine direkte Wiederherstellung auf den Computer \$In{hostDest}. Die folgenden Dateien werden auf die Freigabe \$In{shareDest} wiederhergestellt, von dem Backup mit der Nummer \$num:

    \$fileListStr
    Original Datei/Verzeichnis:Wird wiederhergestellt nach:

    \$hiddenStr Wollen Sie das wirklich tun?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Wiederherstellung beauftragt auf Computer \$hostDest"; $Lang{Reply_from_server_was___reply} = < Die Antwort des Servers war: \$reply

    Zurück zur \$hostDest Hauptseite. EOF $Lang{BackupPC_Archive_Reply_from_server} = < Die Antwort des Server war: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupServer: Computer \$host Backupübersicht"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("Benutzeraktionen")}

    \$startIncrStr

    \${h2("Backupübersicht")}

    Klicken Sie auf die Backupnummer um die Dateien zu durchsuchen und bei Bedarf wiederherzustellen.

    \$str
    Backup# Typ gefüllt Level Start Zeitpunkt Dauer/min Alter/Tage Serverbackuppfad

    \$restoreStr



    \${h2("Xfer Fehler Übersicht - bitte kontrollieren")}

    \$errStr
    Backup# Typ Anzeigen #Xfer Fehler #Dateifehler #Freigabefehler #tar Fehler


    \${h2("Datei Größe/Anzahl Wiederverwendungsübersicht")}

    "Bestehende Dateien" bedeutet bereits im Pool vorhanden. "Neue Dateien" bedeutet neu zum Pool hinzugefügt. Leere Dateien und eventuelle Dateifehler sind nicht in den Summen enthalten.

    \$sizeStr
    Gesamt bestehende Dateien neue Dateien
    Backup# Typ #Dateien Größe/MB MB/sec #Dateien Größe/MB #Dateien Größe/MB


    \${h2("Kompressions Übersicht")}

    Kompressionsergebnisse für bereits im Backup-Pool vorhandene und für neu komprimierte Dateien.

    \$compStr
    vorhandene Dateien neue Dateien
    Backup# Typ Komp Level Größe/MB Komp/MB Komp Größe/MB Komp/MB Komp


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Host \$host Archivübersicht"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("Benutzeraktionen")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupServer: Fehler"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Server"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • Sie browsen das Backup #\$num, erstellt am \$backupTime (vor \$backupAge Tagen), \$filledBackup
    • Verzeichnis eingeben:
    • Klicken Sie auf ein Verzeichnis um dieses zu durchsuchen.
    • Klicken Sie auf eine Datei um diese per Download wiederherzustellen.
    • Einsehen der Backup Historie des aktuellen Verzeichnisses.
    \${h2("Inhalt von \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Verzeichnishistorie für \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "Verzeichnis"; $Lang{DirHistory_fileLink} = "V"; $Lang{DirHistory_for__host} = < Diese Ansicht zeigt alle unterschiedlichen Versionen der Dateien in den Datensicherungen:
    • Klicken Sie auf eine Datensicherungsnummer für die Datensicherungsübersicht.
    • Wählen Sie hier auf einen Verzeichnis Namen: (\$Lang->{DirHistory_dirLink}) um Verzeichnisse anzuzeigen.
    • Klicken Sie auf eine Dateiversion (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) für einen Download der Datei.
    • Dateien mit dem gleichen Inhalt in verschiedenen Datensicherungen haben die gleiche Versionsnummer.
    • Dateien oder Verzeichnisse, die in einer Datensicherung nicht vorhanden sind, haben dort keinen Eintrag.
    • Dateien mit der gleichen Version können unterschiedliche Attribute haben. Wählen Sie die Datensicherungsnummer um die Attribute anzuzeigen.
    \${h2("Historie von \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Datensicherungnummer
    Sicherungszeitpunkt
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Restore #\$num Details für Computer \$host"; $Lang{Restore___num_details_for__host2} = <
    Nummer \$Restores[\$i]{num}
    beauftragt von \$RestoreReq{user}
    Auftrag Zeitpunkt \$reqTime
    Ergebnis \$Restores[\$i]{result}
    Fehlermeldung \$Restores[\$i]{errorMsg}
    Quelle Computer \$RestoreReq{hostSrc}
    Quelle Backup Nr. \$RestoreReq{num}
    Quelle Freigabe \$RestoreReq{shareSrc}
    Ziel Computer \$RestoreReq{hostDest}
    Ziel Freigabe \$RestoreReq{shareDest}
    Start Zeitpunkt \$startTime
    Dauer \$duration min
    Anzahl Dateien \$Restores[\$i]{nFiles}
    Größe gesamt \${MB} MB
    Transferrate \$MBperSec MB/sec
    TarCreate Fehler \$Restores[\$i]{tarCreateErrs}
    Xfer Fehler \$Restores[\$i]{xferErrs}
    Xfer LOG Datei Anzeigen, Fehler

    \${h1("Datei/Verzeichnis Liste")}

    \$fileListStr
    Original Datei/Verzeichniswiederhergestellt nach
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Archiv #\$num Details für \$host"; $Lang{Archive___num_details_for__host2 } = <
    Nummer \$Archives[\$i]{num}
    beauftragt von \$ArchiveReq{user}
    Auftrag Zeitpunkt \$reqTime
    Ergebnis \$Archives[\$i]{result}
    Fehlermeldung \$Archives[\$i]{errorMsg}
    Start Zeitpunkt \$startTime
    Dauer \$duration min
    Xfer LOG Datei Anzeigen, Fehler

    \${h1("Computerliste")}

    \$HostListStr
    ComputerDatensicherungsnummer
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Emailübersicht"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new failed: Überprüfen Sie das Apache error_log\n"; $Lang{Wrong_user__my_userid_is___} = "Falscher Benutzer: Meine userid ist \$>, anstelle \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Nur berechtigte Benutzer können die Computer Übersicht einsehen."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Nur berechtigte Benutzer können Backups starten und stoppen für" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "ungültige Nummer \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "kann Datei nicht öffnen \$file: Konfigurationsproblem?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Nur berechtigte Benutzer können Log oder Config Dateien einsehen."; $Lang{Only_privileged_users_can_view_log_files} = "Nur berechtigte Benutzer können LOG Dateien einsehen."; $Lang{Only_privileged_users_can_view_email_summaries} = "Nur berechtigte Benutzer können die Email Übersicht einsehen."; $Lang{Only_privileged_users_can_browse_backup_files} = "Nur berechtigte Benutzer können Backup Dateien durchsuchen" . " für computer \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Kein Hostname."; $Lang{Directory___EscHTML} = "Verzeichnis \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " ist leer"; $Lang{Can_t_browse_bad_directory_name2} = "Kann fehlerhaften Verzeichnisnamen nicht durchsuchen" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Nur berechtigte Benutzer können Dateien wiederherstellen" . " für Computer \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Falscher Computer Name \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "Sie haben keine Dateien selektiert; bitte gehen Sie zurück um" . " Dateien zu selektieren."; $Lang{You_haven_t_selected_any_hosts} = "Sie haben keinen Computer gewählt, bitte zurück gehen um einen auszuwählen."; $Lang{Nice_try__but_you_can_t_put} = "Sie dürfen \'..\' nicht in Dateinamen verwenden"; $Lang{Host__doesn_t_exist} = "Computer \${EscHTML(\$In{hostDest})} existiert nicht"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "Sie haben keine Berechtigung zum Restore auf Computer" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Kann Datei nicht öffnen oder erstellen " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Nur berechtigte Benutzer dürfen Backup und Restore von Dateien" . " für Computer \${EscHTML(\$host)} durchführen."; $Lang{Empty_host_name} = "leerer Computer Name"; $Lang{Unknown_host_or_user} = "Unbekannter Computer oder Benutzer \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Nur berechtigte Benutzer können Informationen sehen über" . " Computer \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Nur berechtigte Benutzer können Archiv Informationen einsehen."; $Lang{Only_privileged_users_can_view_restore_information} = "Nur berechtigte Benutzer können Restore Informationen einsehen."; $Lang{Restore_number__num_for_host__does_not_exist} = "Restore Nummer \$num für Computer \${EscHTML(\$host)} existiert" . " nicht."; $Lang{Archive_number__num_for_host__does_not_exist} = "Archiv Nummer \$num für Computer \${EscHTML(\$host)} existiert" . " nicht."; $Lang{Can_t_find_IP_address_for} = "Kann IP-Adresse für \${EscHTML(\$host)} nicht finden"; $Lang{host_is_a_DHCP_host} = < Solange bis ich \$host mit einer DHCP-Adresse sehe, können Sie diesen Auftrag nur vom diesem Client Computer aus starten. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "Backup angefordert für DHCP Computer \$host (\$In{hostIP}) durch" . " \$User von \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Backup angefordert für \$host durch \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Backup gestoppt/gelöscht für \$host durch \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Restore beauftragt nach Computer \$hostDest, von Backup #\$num," . " durch User \$User von Client \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Archivierung beauftragt durch \$User von \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "Status"; $Lang{PC_Summary} = "Computerübersicht"; $Lang{LOG_file} = "LOG Datei"; $Lang{LOG_files} = "LOG Dateien"; $Lang{Old_LOGs} = "Alte LOG Dateien"; $Lang{Email_summary} = "Emailübersicht"; $Lang{Config_file} = "Konfigurationsdatei"; # $Lang{Hosts_file} = "Hosts Datei"; $Lang{Current_queues} = "Warteschlangen"; $Lang{Documentation} = "Dokumentation"; #$Lang{Host_or_User_name} = "Computer oder Benutzer Name:"; $Lang{Go} = "gehe zu"; $Lang{Hosts} = "Computer"; $Lang{Select_a_host} = "Computer auswählen..."; $Lang{There_have_been_no_archives} = "

    Es existieren keine Archive

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    Dieser Computer wurde nie gesichert!

    \n"; $Lang{This_PC_is_used_by} = "
  • Dieser Computer wird betreut von \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(nur Fehler anzeigen)"; $Lang{XferLOG} = "XferLOG"; $Lang{Errors} = "Fehler"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <Letzte eMail gesendet an \${UserLink(\$user)} am \$mailTime, Titel "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <Das Kommando \$cmd wird gerade für Computer \$host ausgeführt, gestartet am \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <Computer \$host ist in die Hintergrundwarteschlange eingereiht (Backup wird bald gestartet). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <Computer \$host ist in die Benutzerwarteschlange eingereiht (Backup wird bald gestartet). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <Ein Kommando für Computer \$host ist in der Kommandowarteschlange (wird bald ausgeführt). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <Letzter Status ist \"\$Lang->{\$StatusHost{state}}\"\$reason vom \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <Letzter Fehler ist \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <Pings zu Computer \$host sind \$StatusHost{deadCnt} mal fehlgeschlagen. EOF # ----- $Lang{Prior_to_that__pings} = "vorher, Pings"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr zu Computer \$host waren \$StatusHost{aliveCnt} mal fortlaufend erfolgreich. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Da Computer \$host mindestens \$Conf{BlackoutGoodCnt} mal fortlaufend erreichbar war, wird er in der Zeit von \$blackoutStr nicht gesichert. (Die Sicherung erfolgt automatisch außerhalb der konfigurierten Betriebszeit) EOF $Lang{__time0_to__time1_on__days} = "\$t0 bis \$t1 am \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Backups sind für die nächsten \$hours Stunden deaktiviert. (diese Zeit ändern). EOF $Lang{tryIP} = " und \$StatusHost{dhcpHostIP}"; #$Lang{Host_Inhost} = "Computer \$In{host}"; $Lang{checkAll} = <  alles auswählen EOF $Lang{checkAllHosts} = <  alle auswählen EOF $Lang{fileHeader} = < Name Typ Rechte Backup# Größe letzte Änderung EOF $Lang{Home} = "Hauptseite"; $Lang{Browse} = "Datensicherungen anzeigen"; $Lang{Last_bad_XferLOG} = "Letztes fehlerhafte XferLOG"; $Lang{Last_bad_XferLOG_errors_only} = "Letztes fehlerhafte XferLOG (nur Fehler)"; $Lang{This_display_is_merged_with_backup} = < Diese Liste ist mit Backup #\$numF verbunden. EOF $Lang{Visit_this_directory_in_backup} = < Wählen Sie die anzuzeigende Datensicherung: EOF $Lang{Restore_Summary} = < Klicken Sie auf die Restore Nummer (Restore#) für mehr Details. \$restoreStr
    Restore# Ergebnis Start Zeitpunkt Dauer/min #Dateien Größe/MB #tar Fehler #Xfer Fehler

    EOF $Lang{Archive_Summary} = < Klicken Sie auf die Archiv Nummer um die Details anzuzeigen. \$ArchiveStr
    Archiv# Ergebnis Start Zeitpunkt Dauer/min.

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Dokumentation"; $Lang{No} = "nein"; $Lang{Yes} = "ja"; $Lang{The_directory_is_empty} = <Das Verzeichnis \$dirDisplay ist leer. EOF #$Lang{on} = "an"; $Lang{off} = "aus"; $Lang{backupType_full} = "voll"; $Lang{backupType_incr} = "inkrementell"; $Lang{backupType_partial} = "unvollständig"; $Lang{failed} = "fehler"; $Lang{success} = "erfolgreich"; $Lang{and} = "und"; # ------ # Hosts states and reasons $Lang{Status_idle} = "wartet"; $Lang{Status_backup_starting} = "Backup startet"; $Lang{Status_backup_in_progress} = "Backup läuft"; $Lang{Status_restore_starting} = "Restore startet"; $Lang{Status_restore_in_progress} = "Restore läuft"; $Lang{Status_link_pending} = "Link steht an"; $Lang{Status_link_running} = "Link läuft"; $Lang{Reason_backup_done} = "Backup durchgeführt"; $Lang{Reason_restore_done} = "Restore durchgeführt"; $Lang{Reason_archive_done} = "Archivierung durchgeführt"; $Lang{Reason_nothing_to_do} = "kein Auftrag"; $Lang{Reason_backup_failed} = "Backup Fehler"; $Lang{Reason_restore_failed} = "Restore Fehler"; $Lang{Reason_archive_failed} = "Archivierung Fehler"; $Lang{Reason_no_ping} = "nicht erreichbar"; $Lang{Reason_backup_canceled_by_user} = "Abbruch durch Benutzer"; $Lang{Reason_restore_canceled_by_user} = "Abbruch durch Benutzer"; $Lang{Reason_archive_canceled_by_user} = "Archivierung abgebrochen durch Benutzer"; $Lang{Disabled_OnlyManualBackups} = "autom. deaktiviert"; $Lang{Disabled_AllBackupsDisabled} = "deaktiviert"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: keine Backups von \$host waren erfolgreich"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Hallo $userName, Ihr Computer ($host) wurde durch den Backup Server noch nie erfolgreich gesichert. Backups sollten automatisch erfolgen, wenn Ihr Computer am Netzwerk angeschlossen ist. Sie sollten Ihren Backup-Betreuer oder den IT-Dienstleister kontaktieren, wenn: - Ihr Computer regelmäßig am Netzwerk angeschlossen ist. Dann handelt es sich um ein Installations- bzw. Konfigurationsproblem, was die Durchführung von automatischen Backups verhindert. - Wenn Sie kein automatisches Backup des Computers brauchen und diese e-mail nicht mehr erhalten möchten. Andernfalls sollten Sie sicherstellen, daß Ihr Computer regelmäßig korrekt am Netzwerk angeschlossen wird. Mit freundlichen Grüßen, Ihr BackupPC Server http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: keine neuen Backups für Computer \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Hallo $userName, Ihr Computer ($host) wurde seit $days Tagen nicht mehr erfolgreich gesichert. Ihr Computer wurde von vor $firstTime Tagen bis vor $days Tagen $numBackups mal erfolgreich gesichert. Backups sollten automatisch erfolgen, wenn Ihr Computer am Netzwerk angeschlossen ist. Wenn Ihr Computer in den letzten $days Tagen mehr als ein paar Stunden am Netzwerk angeschlossen war, sollten Sie Ihren Backup-Betreuer oder den IT-Dienstleister kontaktieren um die Ursache zu ermitteln und zu beheben. Andernfalls, wenn Sie z.B. lange Zeit nicht im Büro sind, können Sie höchstens manuell Ihre Dateien sichern (evtl. kopieren auf eine externe Festplatte). Bitte denken Sie daran, dass alle in den letzten $days Tagen geänderten Dateien (z.B. auch Emails und Anhänge oder Datenbankeinträge) verloren gehen falls Ihre Festplatte ausfällt oder Dateien durch versehentliches Löschen oder Virenbefall unbrauchbar werden. Mit freundlichen Grüßen, Ihr BackupPC Server http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupServer: Outlook-Dateien auf Computer \$host - Sicherung erforderlich"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Hallo $userName, die Outlook Dateien auf Ihrem Computer wurden $howLong Tage nicht gesichert. Diese Dateien enthalten Ihre Emails, Anhänge, Adressen und Kalender. Ihr Computer wurde zwar $numBackups mal seit $firstTime Tagen bis vor $lastTime Tagen gesichert. Allerdings sperrt Outlook den Zugriff auf diese Dateien. Es wird folgendes Vorgehen empfohlen: 1. Der Computer muss an das BackupServer Netzwerk angeschlossen sein. 2. Beenden Sie das Outlook Programm. 3. Starten Sie ein inkrementelles Backup mit dem Internet-Browser hier: $CgiURL?host=$host Name und Passwort eingeben und dann 2 mal nacheinander auf "Starte inkrementelles Backup" klicken Klicken Sie auf "Gehe zurück zur ...Hauptseite" und beobachten Sie den Status des Backupvorgangs (Browser von Zeit zu Zeit aktualisieren). Das sollte je nach Dateigröße nur eine kurze Zeit dauern. Mit freundlichen Grüßen, Ihr BackupPC Server http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "Backup nicht erfolgreich"; $Lang{howLong_not_been_backed_up_for_days_days} = "Kein Backup seit \$days Tagen"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPC Server"; $Lang{RSS_Doc_Description} = "RSS Feed für BackupPC"; $Lang{RSS_Host_Summary} = < Beachte: Wähle Überschreiben, wenn du einen computerspezifischen Wert verändern willst

    EOF $Lang{CfgEdit_Button_Save} = "Speichern"; $Lang{CfgEdit_Button_Insert} = "Einfügen"; $Lang{CfgEdit_Button_Delete} = "Löschen"; $Lang{CfgEdit_Button_Add} = "Hinzufügen"; $Lang{CfgEdit_Button_Override} = "Überschreiben"; $Lang{CfgEdit_Button_New_Key} = "Neuer Schlüssel"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "Wegen Fehlern nicht gesichert"; $Lang{CfgEdit_Error__must_be_an_integer} = "Error: \$var muss eine Zahl sein"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Error: \$var muss eine ganze Zahl sein"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Error: \$var Eintrag \$k muss eine Zahl sein"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Error: \$var Eintrag \$k muss eine ganze Zahl sein"; $Lang{CfgEdit_Error__must_be_executable_program} = "Error: \$var muss ein gültiger ausführbarer Pfad sein"; $Lang{CfgEdit_Error__must_be_valid_option} = "Error: \$var muss eine gültige Option sein"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "Ursprungs Host \$copyHost existiert nicht; Erstelle den vollen Hostnamen \$fullHost. Lösche den Host wenn das nicht war, was du wolltest."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User hat die Konfig von host \$fromHost zu \$host kopiert\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User hat \$p von \$conf gelöscht\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User hat \$p zu \$conf hinzugefügt und den Wert \$value gegeben\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User änderte \$p in \$conf zu \$valueNew von \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User hat den Host \$host gelöscht\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User Host \$host hat den Schlüssel \$key von \$valueOld zu \$valueNew geändert\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User hat den Host \$host: \$value hinzugefügt\n"; #end of lang_de.pm BackupPC-3.3.2/lib/BackupPC/Lang/en.pm0000444000076500000240000014741513042250554016150 0ustar craigstaff#!/usr/bin/perl #my %lang; #use strict; # -------------------------------- $Lang{Start_Archive} = "Start Archive"; $Lang{Stop_Dequeue_Archive} = "Stop/Dequeue Archive"; $Lang{Start_Full_Backup} = "Start Full Backup"; $Lang{Start_Incr_Backup} = "Start Incr Backup"; $Lang{Stop_Dequeue_Backup} = "Stop/Dequeue Backup"; $Lang{Restore} = "Restore"; $Lang{Type_full} = "full"; $Lang{Type_incr} = "incremental"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Only privileged users can view admin options."; $Lang{H_Admin_Options} = "BackupPC Server: Admin Options"; $Lang{Admin_Options} = "Admin Options"; $Lang{Admin_Options_Page} = < \${h2("Server Control")}

    Reload the server configuration:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Unable to connect to BackupPC server"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < The error was: \$err.
    Perhaps the BackupPC server is not running or there is a configuration error. Please report this to your Sys Admin. EOF $Lang{Admin_Start_Server} = < The BackupPC server at \$Conf{ServerHost} port \$Conf{ServerPort} is not currently running (maybe you just stopped it, or haven't yet started it).
    Do you want to start it? EOF # ----- $Lang{H_BackupPC_Server_Status} = "BackupPC Server Status"; $Lang{BackupPC_Server_Status_General_Info}= <
  • The servers PID is \$Info{pid}, on host \$Conf{ServerHost}, version \$Info{Version}, started at \$serverStartTime.
  • This status was generated at \$now.
  • The configuration was last loaded at \$configLoadTime.
  • PCs will be next queued at \$nextWakeupTime.
  • Other info:
    • \$numBgQueue pending backup requests from last scheduled wakeup,
    • \$numUserQueue pending user backup requests,
    • \$numCmdQueue pending command requests, \$poolInfo
    • Pool file system was recently at \$Info{DUlastValue}% (\$DUlastTime), today\'s max is \$Info{DUDailyMax}% (\$DUmaxTime) and yesterday\'s max was \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Currently Running Jobs")}

    \$jobStr
    Host Type User Start Time Command PID Xfer PID

    \${h2("Failures that need attention")}

    \$statusStr
    Host Type User Last Try Details Error Time Last error (other than no ping)
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: Host Summary"; $Lang{BackupPC__Archive} = "BackupPC: Archive"; $Lang{BackupPC_Summary} = <

    • This status was generated at \$now.
    • Pool file system was recently at \$Info{DUlastValue}% (\$DUlastTime), today\'s max is \$Info{DUDailyMax}% (\$DUmaxTime) and yesterday\'s max was \$Info{DUDailyMaxPrev}%.

    \${h2("Hosts with good Backups")}

    There are \$hostCntGood hosts that have been backed up, for a total of:

    • \$fullTot full backups of total size \${fullSizeTot}GB (prior to pooling and compression),
    • \$incrTot incr backups of total size \${incrSizeTot}GB (prior to pooling and compression).

    \$strGood
    Host User #Full Full Age (days) Full Size (GB) Speed (MB/s) #Incr Incr Age (days) Last Backup (days) State #Xfer errs Last attempt


    \${h2("Hosts with no Backups")}

    There are \$hostCntNone hosts with no backups.

    \$strNone
    Host User #Full Full Age (days) Full Size (GB) Speed (MB/s) #Incr Incr Age/days Last Backup (days) State #Xfer errs Last attempt
    EOF $Lang{BackupPC_Archive} = < There are \$hostCntGood hosts that have been backed up for a total size of \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    Host User Backup Size

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Archive Location/Device EOF $Lang{BackupPC_Archive2_compression} = < Compression None
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Percentage of Parity Data (0 = disable, 5 = typical) EOF $Lang{BackupPC_Archive2_split} = < Split output into Megabytes EOF # ----------------------------------- $Lang{Pool_Stat} = <Pool is \${poolSize}GB comprising \$info->{"\${name}FileCnt"} files and \$info->{"\${name}DirCnt"} directories (as of \$poolTime),
  • Pool hashing gives \$info->{"\${name}FileCntRep"} repeated files with longest chain \$info->{"\${name}FileRepMax"},
  • Nightly cleanup removed \$info->{"\${name}FileCntRm"} files of size \${poolRmSize}GB (around \$poolTime), EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: Backup Requested on \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < Reply from server was: \$reply

    Go back to \$host home page. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Start Backup Confirm on \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < You are about to start a \$type backup on \$host.

    Do you really want to do this?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Stop Backup Confirm on \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < You are about to stop/dequeue backups on \$host;
    Also, please don\'t start another backup for hours.

    Do you really want to do this?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Only privileged users can view queues."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Only privileged users can Archive."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Queue Summary"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("User Queue Summary")}

    The following user requests are currently queued:

    \$strUser
    Host Req Time User


    \${h2("Background Queue Summary")}

    The following background requests are currently queued:

    \$strBg
    Host Req Time User


    \${h2("Command Queue Summary")}

    The following command requests are currently queued:

    \$strCmd
    Host Req Time User Command
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: File \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, modified \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ skipped \$skipped lines ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nCan\'t open log file \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: Log File History";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    File Size Modification time
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Recipient Host Time Subject
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Browse backup \$num for \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Restore Options for \$host"; $Lang{Restore_Options_for__host2} = < You have selected the following files/directories from share \$share, backup number #\$num:
      \$fileListStr

    You have three choices for restoring these files/directories. Please select one of the following options.

    \${h2("Option 1: Direct Restore")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    Warning: any existing files that match the ones you have selected will be overwritten!

    \$hiddenStr
    Restore the files to host
    Restore the files to share
    Restore the files below dir
    (relative to share)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Option 2: Download Zip archive")}

    You can download a Zip archive containing all the files/directories you have selected. You can then use a local application, such as WinZip, to view or extract any of the files.

    Warning: depending upon which files/directories you have selected, this archive might be very very large. It might take many minutes to create and transfer the archive, and you will need enough local disk space to store it.

    \$hiddenStr Make archive relative to \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (otherwise archive will contain full paths).
    Compression (0=off, 1=fast,...,9=best)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Option 2: Download Zip archive")}

    Archive::Zip is not installed so you will not be able to download a zip archive. Please ask your system adminstrator to install Archive::Zip from www.cpan.org.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < You can download a Tar archive containing all the files/directories you have selected. You can then use a local application, such as tar or WinZip to view or extract any of the files.

    Warning: depending upon which files/directories you have selected, this archive might be very very large. It might take many minutes to create and transfer the archive, and you will need enough local disk space to store it.

    \$hiddenStr Make archive relative to \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (otherwise archive will contain full paths).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Restore Confirm on \$host"; $Lang{Are_you_sure} = < You are about to start a restore directly to the machine \$In{hostDest}. The following files will be restored to share \$In{shareDest}, from backup number \$num:

    \$fileListStr
    Original file/dirWill be restored to

    \$hiddenStr Do you really want to do this?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Restore Requested on \$hostDest"; $Lang{Reply_from_server_was___reply} = < Reply from server was: \$reply

    Go back to \$hostDest home page. EOF $Lang{BackupPC_Archive_Reply_from_server} = < Reply from server was: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Host \$host Backup Summary"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("User Actions")}

    \$startIncrStr

    \${h2("Backup Summary")}

    Click on the backup number to browse and restore backup files.

    \$str
    Backup# Type Filled Level Start Date Duration/mins Age/days Server Backup Path

    \$restoreStr



    \${h2("Xfer Error Summary")}

    \$errStr
    Backup# Type View #Xfer errs #bad files #bad share #tar errs


    \${h2("File Size/Count Reuse Summary")}

    Existing files are those already in the pool; new files are those added to the pool. Empty files and SMB errors aren\'t counted in the reuse and new counts.

    \$sizeStr
    Totals Existing Files New Files
    Backup# Type #Files Size/MB MB/sec #Files Size/MB #Files Size/MB


    \${h2("Compression Summary")}

    Compression performance for files already in the pool and newly compressed files.

    \$compStr
    Existing Files New Files
    Backup# Type Comp Level Size/MB Comp/MB Comp Size/MB Comp/MB Comp


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Host \$host Archive Summary"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("User Actions")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Error"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Server"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • You are browsing backup #\$num, which started around \$backupTime (\$backupAge days ago), \$filledBackup
    • Enter directory:
    • Click on a directory below to navigate into that directory,
    • Click on a file below to restore that file,
    • You can view the backup history of the current directory.
    \${h2("Contents of \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Directory backup history for \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "dir"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < This display shows each unique version of files across all the backups:
    • Click on a backup number to return to the backup browser,
    • Click on a directory link (\$Lang->{DirHistory_dirLink}) to navigate into that directory,
    • Click on a file version link (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) to download that file,
    • Files with the same contents between different backups have the same version number,
    • Files or directories not present in a particular backup have an empty box.
    • Files shown with the same version might have different attributes. Select the backup number to see the file attributes.
    \${h2("History of \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Backup number
    Backup time
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Restore #\$num details for \$host"; $Lang{Restore___num_details_for__host2} = <
    Number \$Restores[\$i]{num}
    Requested by \$RestoreReq{user}
    Request time \$reqTime
    Result \$Restores[\$i]{result}
    Error Message \$Restores[\$i]{errorMsg}
    Source host \$RestoreReq{hostSrc}
    Source backup num \$RestoreReq{num}
    Source share \$RestoreReq{shareSrc}
    Destination host \$RestoreReq{hostDest}
    Destination share \$RestoreReq{shareDest}
    Start time \$startTime
    Duration \$duration min
    Number of files \$Restores[\$i]{nFiles}
    Total size \${MB} MB
    Transfer rate \$MBperSec MB/sec
    TarCreate errors \$Restores[\$i]{tarCreateErrs}
    Xfer errors \$Restores[\$i]{xferErrs}
    Xfer log file View, Errors

    \${h1("File/Directory list")}

    \$fileListStr
    Original file/dirRestored to
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Archive #\$num details for \$host"; $Lang{Archive___num_details_for__host2 } = <
    Number \$Archives[\$i]{num}
    Requested by \$ArchiveReq{user}
    Request time \$reqTime
    Result \$Archives[\$i]{result}
    Error Message \$Archives[\$i]{errorMsg}
    Start time \$startTime
    Duration \$duration min
    Xfer log file View, Errors

    \${h1("Host list")}

    \$HostListStr
    HostBackup Number
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Email Summary"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new failed: check apache error_log\n"; $Lang{Wrong_user__my_userid_is___} = "Wrong user: my userid is \$>, instead of \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Only privileged users can view PC summaries."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Only privileged users can stop or start backups on" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Invalid number \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "Unable to open \$file: configuration problem?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Only privileged users can view log or config files."; $Lang{Only_privileged_users_can_view_log_files} = "Only privileged users can view log files."; $Lang{Only_privileged_users_can_view_email_summaries} = "Only privileged users can view email summaries."; $Lang{Only_privileged_users_can_browse_backup_files} = "Only privileged users can browse backup files" . " for host \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Empty host name."; $Lang{Directory___EscHTML} = "Directory \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " is empty"; $Lang{Can_t_browse_bad_directory_name2} = "Can\'t browse bad directory name" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Only privileged users can restore backup files" . " for host \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Bad host name \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "You haven\'t selected any files; please go Back to" . " select some files."; $Lang{You_haven_t_selected_any_hosts} = "You haven\'t selected any hosts; please go Back to" . " select some hosts."; $Lang{Nice_try__but_you_can_t_put} = "Nice try, but you can\'t put \'..\' in any of the file names"; $Lang{Host__doesn_t_exist} = "Host \${EscHTML(\$In{hostDest})} doesn\'t exist"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "You don\'t have permission to restore onto host" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Can\'t open/create " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Only privileged users can restore backup files" . " for host \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Empty host name"; $Lang{Unknown_host_or_user} = "Unknown host or user \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Only privileged users can view information about" . " host \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Only privileged users can view archive information."; $Lang{Only_privileged_users_can_view_restore_information} = "Only privileged users can view restore information."; $Lang{Restore_number__num_for_host__does_not_exist} = "Restore number \$num for host \${EscHTML(\$host)} does" . " not exist."; $Lang{Archive_number__num_for_host__does_not_exist} = "Archive number \$num for host \${EscHTML(\$host)} does" . " not exist."; $Lang{Can_t_find_IP_address_for} = "Can\'t find IP address for \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < Until I see \$host at a particular DHCP address, you can only start this request from the client machine itself. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "Backup requested on DHCP \$host (\$In{hostIP}) by" . " \$User from \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Backup requested on \$host by \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Backup stopped/dequeued on \$host by \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Restore requested to host \$hostDest, backup #\$num," . " by \$User from \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Archive requested by \$User from \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "Status"; $Lang{PC_Summary} = "Host Summary"; $Lang{LOG_file} = "LOG file"; $Lang{LOG_files} = "LOG files"; $Lang{Old_LOGs} = "Old LOGs"; $Lang{Email_summary} = "Email summary"; $Lang{Config_file} = "Config file"; # $Lang{Hosts_file} = "Hosts file"; $Lang{Current_queues} = "Current queues"; $Lang{Documentation} = "Documentation"; #$Lang{Host_or_User_name} = "Host or User name:"; $Lang{Go} = "Go"; $Lang{Hosts} = "Hosts"; $Lang{Select_a_host} = "Select a host..."; $Lang{There_have_been_no_archives} = "

    There have been no archives

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    This PC has never been backed up!!

    \n"; $Lang{This_PC_is_used_by} = "
  • This PC is used by \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Extracting only Errors)"; $Lang{XferLOG} = "XferLOG"; $Lang{Errors} = "Errors"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <Last email sent to \${UserLink(\$user)} was at \$mailTime, subject "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <The command \$cmd is currently running for \$host, started \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <Host \$host is queued on the background queue (will be backed up soon). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <Host \$host is queued on the user queue (will be backed up soon). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <A command for \$host is on the command queue (will run soon). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <Last status is state \"\$Lang->{\$StatusHost{state}}\"\$reason as of \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <Last error is \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <Pings to \$host have failed \$StatusHost{deadCnt} consecutive times. EOF # ----- $Lang{Prior_to_that__pings} = "Prior to that, pings"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr to \$host have succeeded \$StatusHost{aliveCnt} consecutive times. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Because \$host has been on the network at least \$Conf{BlackoutGoodCnt} consecutive times, it will not be backed up from \$blackoutStr. EOF $Lang{__time0_to__time1_on__days} = "\$t0 to \$t1 on \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Backups are deferred for \$hours hours (change this number). EOF $Lang{tryIP} = " and \$StatusHost{dhcpHostIP}"; # $Lang{Host_Inhost} = "Host \$In{host}"; $Lang{checkAll} = <  Select all EOF $Lang{checkAllHosts} = <  Select all EOF $Lang{fileHeader} = < Name Type Mode # Size Date modified EOF $Lang{Home} = "Home"; $Lang{Browse} = "Browse backups"; $Lang{Last_bad_XferLOG} = "Last bad XferLOG"; $Lang{Last_bad_XferLOG_errors_only} = "Last bad XferLOG (errors only)"; $Lang{This_display_is_merged_with_backup} = < This display is merged with backup #\$numF. EOF $Lang{Visit_this_directory_in_backup} = < Select the backup you wish to view: EOF $Lang{Restore_Summary} = < Click on the restore number for more details. \$restoreStr
    Restore# Result Start Date Dur/mins #files MB #tar errs #xferErrs

    EOF $Lang{Archive_Summary} = < Click on the archive number for more details. \$ArchiveStr
    Archive# Result Start Date Dur/mins

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Documentation"; $Lang{No} = "no"; $Lang{Yes} = "yes"; $Lang{The_directory_is_empty} = <The directory \$dirDisplay is empty EOF #$Lang{on} = "on"; $Lang{off} = "off"; $Lang{backupType_full} = "full"; $Lang{backupType_incr} = "incr"; $Lang{backupType_partial} = "partial"; $Lang{failed} = "failed"; $Lang{success} = "success"; $Lang{and} = "and"; # ------ # Hosts states and reasons $Lang{Status_idle} = "idle"; $Lang{Status_backup_starting} = "backup starting"; $Lang{Status_backup_in_progress} = "backup in progress"; $Lang{Status_restore_starting} = "restore starting"; $Lang{Status_restore_in_progress} = "restore in progress"; $Lang{Status_link_pending} = "link pending"; $Lang{Status_link_running} = "link running"; $Lang{Reason_backup_done} = "done"; $Lang{Reason_restore_done} = "restore done"; $Lang{Reason_archive_done} = "archive done"; $Lang{Reason_nothing_to_do} = "idle"; $Lang{Reason_backup_failed} = "backup failed"; $Lang{Reason_restore_failed} = "restore failed"; $Lang{Reason_archive_failed} = "archive failed"; $Lang{Reason_no_ping} = "no ping"; $Lang{Reason_backup_canceled_by_user} = "backup canceled by user"; $Lang{Reason_restore_canceled_by_user} = "restore canceled by user"; $Lang{Reason_archive_canceled_by_user} = "archive canceled by user"; $Lang{Disabled_OnlyManualBackups} = "auto disabled"; $Lang{Disabled_AllBackupsDisabled} = "disabled"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: no backups of \$host have succeeded"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, Your PC ($host) has never been successfully backed up by our PC backup software. PC backups should occur automatically when your PC is connected to the network. You should contact computer support if: - Your PC has been regularly connected to the network, meaning there is some configuration or setup problem preventing backups from occurring. - You don't want your PC backed up and you want these email messages to stop. Otherwise, please make sure your PC is connected to the network next time you are in the office. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: no recent backups on \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, Your PC ($host) has not been successfully backed up for $days days. Your PC has been correctly backed up $numBackups times from $firstTime to $days days ago. PC backups should occur automatically when your PC is connected to the network. If your PC has been connected for more than a few hours to the network during the last $days days you should contact IS to find out why backups are not working. Otherwise, if you are out of the office, there's not much you can do, other than manually copying especially critical files to other media. You should be aware that any files you have created or changed in the last $days days (including all new email and attachments) cannot be restored if your PC disk crashes. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Outlook files on \$host need to be backed up"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, The Outlook files on your PC have $howLong. These files contain all your email, attachments, contact and calendar information. Your PC has been correctly backed up $numBackups times from $firstTime to $lastTime days ago. However, Outlook locks all its files when it is running, preventing these files from being backed up. It is recommended you backup the Outlook files when you are connected to the network by exiting Outlook and all other applications, and, using just your browser, go to this link: $CgiURL?host=$host Select "Start Incr Backup" twice to start a new incremental backup. You can select "Return to $host page" and then hit "reload" to check the status of the backup. It should take just a few minutes to complete. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "not been backed up successfully"; $Lang{howLong_not_been_backed_up_for_days_days} = "not been backed up for \$days days"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPC Server"; $Lang{RSS_Doc_Description} = "RSS feed for BackupPC"; $Lang{RSS_Host_Summary} = < Note: Check Override if you want to modify a value specific to this host.

    EOF $Lang{CfgEdit_Button_Save} = "Save"; $Lang{CfgEdit_Button_Insert} = "Insert"; $Lang{CfgEdit_Button_Delete} = "Delete"; $Lang{CfgEdit_Button_Add} = "Add"; $Lang{CfgEdit_Button_Override} = "Override"; $Lang{CfgEdit_Button_New_Key} = "New Key"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "Error: No save due to errors"; $Lang{CfgEdit_Error__must_be_an_integer} = "Error: \$var must be an integer"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Error: \$var must be a real-valued number"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Error: \$var entry \$k must be an integer"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Error: \$var entry \$k must be a real-valued number"; $Lang{CfgEdit_Error__must_be_executable_program} = "Error: \$var must be a valid executable path"; $Lang{CfgEdit_Error__must_be_valid_option} = "Error: \$var must be a valid option"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "Copy host \$copyHost doesn't exist; creating full host name \$fullHost. Delete this host if that is not what you wanted."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User copied config from host \$fromHost to \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User deleted \$p from \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User added \$p to \$conf, set to \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User changed \$p in \$conf to \$valueNew from \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User deleted host \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User host \$host changed \$key from \$valueOld to \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User added host \$host: \$value\n"; #end of lang_en.pm BackupPC-3.3.2/lib/BackupPC/Lang/es.pm0000444000076500000240000016607713042250554016162 0ustar craigstaff#!/usr/bin/perl #my %lang; #use strict; # -------------------------------- $Lang{Start_Archive} = "Iniciar archivado"; $Lang{Stop_Dequeue_Archive} = "Detener/quitar de cola el archivado"; $Lang{Start_Full_Backup} = "Iniciar copia de seguridad completa"; $Lang{Start_Incr_Backup} = "Iniciar copia de seguridad incremental"; $Lang{Stop_Dequeue_Backup} = "Detener/quitar de cola la copia de seguridad"; $Lang{Restore} = "Restaurar"; $Lang{Type_full} = "completo"; $Lang{Type_incr} = "incremental"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Sólo los superusuarios pueden ver las opciones de administración."; $Lang{H_Admin_Options} = "Servidor BackupPC: Opciones de Administración"; $Lang{Admin_Options} = "Opciones de Admin"; $Lang{Admin_Options_Page} = < \${h2("Control del Servidor")}

    Actualizar configuración del servidor:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Imposible conectar al servidor BackupPC"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < El error fué: \$err.
    Quizá el servidor BackupPC no está activo o hay un error de configuración. Por favor informe a su administrador de sistemas. EOF $Lang{Admin_Start_Server} = < El servidor BackupPC en \$Conf{ServerHost} port \$Conf{ServerPort} no está en funcionamiento ahora (puede haberlo detenido o no haberlo arrancado aún).
    ¿Quiere inicializarlo? EOF # ----- $Lang{H_BackupPC_Server_Status} = "Estado del Servidor BackupPC"; $Lang{BackupPC_Server_Status_General_Info}= <
  • El PID del servidor es \$Info{pid}, en el host \$Conf{ServerHost}, version \$Info{Version}, iniciado el \$serverStartTime.
  • Esta información de estado se ha generado el \$now.
  • La última configuración ha sido cargada a las \$configLoadTime
  • La cola de PC's se activará de nuevo el \$nextWakeupTime.
  • Información adicional:
    • \$numBgQueue solicitudes pendientes de copia de seguridad desde la última activación programada,
    • \$numUserQueue solicitudes pendientes de copia de seguridad de usuarios,
    • \$numCmdQueue solicitudes de comandos pendientes , \$poolInfo
    • El sistema de archivos estaba recientemente al \$Info{DUlastValue}% (\$DUlastTime), el máximo de hoy es \$Info{DUDailyMax}% (\$DUmaxTime) y el máximo de ayer era \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Trabajos en Ejecución")}

    \$jobStr
    Host Tipo Usuario Hora de Inicio Comando PID Transfer. PID

    \${h2("Fallas que Requieren Atención")}

    \$statusStr
    Host Tipo Usuario Ultimo Intento Detalles Hora del error Ultimo error (diferente a no ping)
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: Resumen del Servidor"; $Lang{BackupPC__Archive} = "BackupPC: Archivo"; $Lang{BackupPC_Summary}=<

    • Este status ha sido generado el \$now.
    • El sistema de archivos estaba recientemente al \$Info{DUlastValue}% (\$DUlastTime), el máximo de hoy es \$Info{DUDailyMax}% (\$DUmaxTime) y el máximo de ayer era \$Info{DUDailyMaxPrev}%.

    \${h2("Hosts con Buenas Copias de Seguridad")}

    Hay \$hostCntGood hosts que tienen copia de seguridad, de un total de :

    • \$fullTot copias de seguridad completas con tamaño total de \${fullSizeTot} GB (antes de agrupar y comprimir),
    • \$incrTot copias de seguridad incrementales con tamaño total de \${incrSizeTot} GB (antes de agrupar y comprimir).

    \$strGood
    Host Usuario # Completo Completo Antiguedad (días) Completo Tamaño (GB) Velocidad (MB/s) # Incr Incr Antiguedad (Días) Ultimo Backup (días) Estado Xfer errores Ultimo Intento


    \${h2("Hosts Sin Copias de Seguridad")}

    Hay \$hostCntNone hosts sin copias de seguridad.

    \$strNone
    Host Usuario # Completo Completo Antiguedad (días) Completo Tamaño (GB) Velocidad (MB/s) # Incr Incr Antiguedad (días) Ultimo Backup (días) Estado Xfer errores Ultimo Intento
    EOF $Lang{BackupPC_Archive} = < Hay \$hostCntGood hosts que tienen copia de seguridad con un tamaño total de \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    Host Usuario Tamño de Backup

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Ubicación de archivo/Dispositivo EOF $Lang{BackupPC_Archive2_compression} = < Compression None
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Porcentaje de datos de paridad (0 = deshabilitado, 5 = normal) EOF $Lang{BackupPC_Archive2_split} = < Dividir resultado en Megabytes EOF # ----------------------------------- $Lang{Pool_Stat} = <El grupo tiene \${poolSize}GB incluyendo \$info->{"\${name}FileCnt"} archivos y \$info->{"\${name}DirCnt"} directorios (as of \$poolTime),
  • El procesamiento del grupo da \$info->{"\${name}FileCntRep"} archivos repetidos cuya cadena más larga es \$info->{"\${name}FileRepMax"},
  • El proceso de limpieza nocturna ha eliminado \$info->{"\${name}FileCntRm"} archivos de tamaño \${poolRmSize}GB (around \$poolTime), EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: Copia de Seguridad Solicitada en \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < La respuesta del servidor fué: \$reply

    Volver a \$host home page. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Confirme inicio de copia de seguridad en \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Está a punto de iniciar una copia de seguridad \$type en \$host.

    ¿Realmente quiere hacer esto?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Confirme Detener la Copia de Seguridad en \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < Está a punto de detener/quitar de la cola las copias de seguridad en \$host;
    Asimismo, por favor no empiece otra copia de seguridad durante horas.

    ¿Realmente quiere hacer esto?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Sólo los administradores pueden ver las colas."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Sólo los administradores pueden archivar."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Resumen de la Cola"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Resumen de la Cola de Usuarios")}

    Las siguientes solicitudes de usuarios están actualmente en cola:

    \$strUser
    Host Hora Solicitud Usuario


    \${h2("Resumen de Cola en Segundo Plano")}

    Las siguientes solicitudes en segundo plano están actualmente en cola:

    \$strBg
    Host Hora Solicitud Usuario


    \${h2("Resumen de Cola de Comandos")}

    Los siguientes comandos están actualmente en cola:

    \$strCmd
    Host Hora Solicitud Usuario Comando
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: Archivo de Eventos \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, modificado \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ saltadas \$skipped lineas ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nNo puedo abrir el archivo de eventos \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: Historial de Archivos de Eventos";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    Archivo Tamaño Hora Modificación
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Destinatario Host Hora Asunto
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Explorar Copia de Seguridad \$num de \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Opciones de Restauración para \$host"; $Lang{Restore_Options_for__host2} = < Ha seleccionado los siguientes archivos/directorios de la unidad \$share, copia número #\$num:
      \$fileListStr

    Tiene tres opciones para restaurar estos archivos/directorios. Por favor, seleccione una de las siguientes opciones.

    \${h2("Opción 1: Restauración Directa")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    !Atención!: !Cualquier archivo existente con el mismo nombre que los que ha seleccionado será sobreescrito!

    \$hiddenStr
    Restaurar los archivos al host
    Restaurar los archivos a la unidad
    Restaurar los archivos bajo el directorio
    (relativo a la unidad)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Opción 2: Descargar archivo Zip")}

    Puede descargar un archivo comprimido (.zip) conteniendo todos los archivos y directorios que ha seleccionado. Después puede hacer uso de una aplicación local, como WinZip, para ver o extraer cualquiera de los archivos.

    !Atención!: Dependiendo de que archivos/carpetas haya seleccionado, este archivo puede ser muy grande. Podría tardar muchos minutos en crear y transferir el archivo. Además necesitará suficiente espacio el el disco local para almacenarlo.

    \$hiddenStr Hacer archivo relativo a \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (en caso contrario el archivo contendrá las rutas completas).
    Compresión (0=desactivada, 1=rápida,...,9=mejor)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Opción 2: Descargar archivo Zip")}

    El programa Archive::Zip no está instalado, de modo que no podrá descargar un archivo comprimido zip. Por favor, solicite a su administrador de sistemas que instale Archive::Zip de www.cpan.org.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < Puede descargar un archivo comprimido (.Tar) conteniendo todos los archivos y directorios que ha seleccionado. Después puede hacer uso de una aplicación local, como Tar o WinZip,para ver o extraer cualquiera de los archivos.

    !Atención!: Dependiendo de que archivos/carpetas haya seleccionado, este archivo puede ser muy grande. Podría tardar muchos minutos crear y transferir el archivo. Además necesitará suficiente espacio el el disco local para almacenarlo.

    \$hiddenStr Hacer el archivo relativo a \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (en caso contrario el archivo contendrá las rutas completas).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Confirme restauración en \$host"; $Lang{Are_you_sure} = < Está a punto de iniciar una restauración directamente a la máquina \$In{hostDest}. Los siguientes archivos serán restaurados en la unidad \$In{shareDest}, de la copia de seguridad número \$num:

    \$fileListStr
    Archivo/Dir Original Será restaurado a

    \$hiddenStr ¿Realmente quiere hacer esto?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Restauración solicitada en \$hostDest"; $Lang{Reply_from_server_was___reply} = < La respuesta del servidor fué: \$reply

    volver a \$hostDest home page. EOF $Lang{BackupPC_Archive_Reply_from_server} = < La respuesta del servidor fué: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Host \$host Resumen de Copia de Seguridad"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("Acciones del Usuario")}

    \$startIncrStr

    \${h2("Resumen de Copia de Seguridad")}

    Haga click en el número de copia de seguridad para revisar y restaurar archivos.

    \$str
    Copia N° Tipo Completo Nivel Fecha Inicio Duración/min Antiguedad/días Ruta a la Copia en Servidor

    \$restoreStr



    \${h2("Resumen de Errores de Transferencia")}

    \$errStr
    Copia N° Tipo Ver N° Xfer errs N° err. archivos N° err. unidades N° err. tar


    \${h2("Resumen de Total/Tamaño de Archivos Reutilizados")}

    Los archivos existentes son aquellos que ya están en el lote; los nuevos son aquellos que se han añadido al lote. Los archivos vacíos y los errores SMB no cuentan en las cifras de reutilizados ni en la de nuevos.

    \$sizeStr
    Totales Archivos Existentes Archivos Nuevos
    Copia N° Tipo N° Archivos Tamaño/MB MB/sg N° Archivos Tamaño/MB N° Archivos Tamaño/MB


    \${h2("Resumen de Compresión")}

    Efectividad de compresión para los archivos ya existentes en el lote y los archivos nuevos comprimidos.

    \$compStr
    Archivos Existentes Archivos Nuevos
    Copia N° Tipo Nivel Comp Tamaño/MB Comp/MB Comp Tamaño/MB Comp/MB Comp


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Host \$host Archive Summary"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("Acciones de usuario")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Error"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Servidor"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • Está revisando la copia de seguridad N°\$num, que comenzó hacia las \$backupTime (hace \$backupAge días), \$filledBackup
    • Introduzca el directorio:
    • Haga click en uno de los directorios de abajo para revisar sus contenidos,
    • Haga click en un archivo para restaurarlo,
    • Puede ver la history de la copia de seguridad del directorio actual.
    \${h2("Contenido de \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Histórico de copia de seguridad del directorio en \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "dir"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < Esta pantalla muestra cada versión única de archivos de entre todas las copias de seguridad:
    • Haga click en un número de copia de seguridad para volver al explorador de copias de seguridad,
    • Haga click en un vínculo de directorio (\$Lang->{DirHistory_dirLink}) para navegar en ese directorio,
    • Haga click en un vínculo de versión de archivo (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) para descargar ese archivo,
    • Los archivos con diferentes contenidos entre distintas copias de seguridad tienen el mismo número de versión,
    • Los archivos o directorios que no existen en una copia concreta tienen una celda vacía.
    • Los archivos mostrados con la misma versión pueden tener diferentes atributos. Seleccione el número de copia de seguridad para ver los atributos del archivo.
    \${h2("Historia de \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Número de Backup
    Hora de Backup
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Detalles de la restauración N°\$num de \$host"; $Lang{Restore___num_details_for__host2} = <
    Número \$Restores[\$i]{num}
    Solicitado por \$RestoreReq{user}
    Hora Petición \$reqTime
    Resultado \$Restores[\$i]{result}
    Mensaje de Error \$Restores[\$i]{errorMsg}
    Host Origen \$RestoreReq{hostSrc}
    N° copia origen \$RestoreReq{num}
    Unidad origen \$RestoreReq{shareSrc}
    Host destino \$RestoreReq{hostDest}
    Unidad destino \$RestoreReq{shareDest}
    Hora comienzo \$startTime
    Duración \$duration min
    Número de archivos \$Restores[\$i]{nFiles}
    Tamaño total \${MB} MB
    Tasa de transferencia \$MBperSec MB/sec
    Errores creación Tar \$Restores[\$i]{tarCreateErrs}
    Errores de transferencia \$Restores[\$i]{xferErrs}
    Archivo eventos de transferencia View, Errors

    \${h1("Lista de Archivos/Directorios")}

    \$fileListStr
    Dir/archivo originalRestaurado a
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Copia de seguridad #\$num .Detalles de \$host"; $Lang{Archive___num_details_for__host2 } = <
    Número \$Archives[\$i]{num}
    Solicitado por \$ArchiveReq{user}
    Hora petición \$reqTime
    Resultado \$Archives[\$i]{result}
    Mensaje de error \$Archives[\$i]{errorMsg}
    Hora comienzo \$startTime
    Duración \$duration min
    Archivo eventos Xfer View, Errors

    \${h1("Host list")}

    \$HostListStr
    HostCopia de seguridad número
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Resumen de Correos"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->nuevo ha fallado: revise el error_log de apache\n"; $Lang{Wrong_user__my_userid_is___} = "Usuario erróneo: mi userid es \$>, en lugar de \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Sólo los usuarios autorizados pueden ver los resúmenes de PC's."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Sólo los usuarios autorizados pueden iniciar o detener las copias de seguridad en" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Número no válido \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "No puedo abrir \$file: ¿problema de configuración?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Sólo los usuarios autorizados pueden ver los archivos de eventos o de configuración."; $Lang{Only_privileged_users_can_view_log_files} = "Sólo los usuarios autorizados pueden ver los archivos de eventos."; $Lang{Only_privileged_users_can_view_email_summaries} = "Sólo los usuarios autorizados pueden ver resúmenes de correo."; $Lang{Only_privileged_users_can_browse_backup_files} = "Sólo los usuarios autorizados pueden revisar los archivos de las copias de seguridad" . " para el Host \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Nombre de Host vacío."; $Lang{Directory___EscHTML} = "El directorio \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " está vacío"; $Lang{Can_t_browse_bad_directory_name2} = "No puedo mostrar un nombre de directorio erróneo" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Sólo los usuarios autorizados pueden restaurar copias de seguridad" . " para el host \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Nombre de Host erróneo \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "No ha seleccionado nigún archivo; por favor, vuelva a" . " seleccione algunos archivos."; $Lang{You_haven_t_selected_any_hosts} = "No ha seleccionado ningún host; por favor vuelva a" . " seleccione algunos hosts."; $Lang{Nice_try__but_you_can_t_put} = "Buen intento, pero no puede usar \'..\' en los nombres de archivo"; $Lang{Host__doesn_t_exist} = "El Host \${EscHTML(\$In{hostDest})} no existe"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "No tiene autorización para restaurar en el host" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "No puedo abrir/crear " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Sólo los usuarios autorizados pueden restaurar copias de seguridad" . " del host \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Nombre de host vacío"; $Lang{Unknown_host_or_user} = "Host o usuario desconocido \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Sólo los usuarios autorizados pueden ver información del" . " host \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Sólo los usuarios autorizados pueden ver información de archivo."; $Lang{Only_privileged_users_can_view_restore_information} = "Sólo los usuarios autorizados pueden ver información de restauración."; $Lang{Restore_number__num_for_host__does_not_exist} = "El número de restauración \$num del host \${EscHTML(\$host)} " . " no existe."; $Lang{Archive_number__num_for_host__does_not_exist} = "La copia de seguridad \$num del host \${EscHTML(\$host)} " . " no existe."; $Lang{Can_t_find_IP_address_for} = "No puedo encontrar la dirección IP de \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < Hasta que vea \$host en una dirección DHCP concreta, sólo puede iniciar este proceso desde la propia máquina cliente. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "Copia de seguridad solicitada en DHCP \$host (\$In{hostIP}) por" . " \$User desde \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Copia de seguridad solicitada en \$host por \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Copia de seguridad detenida/desprogramada en \$host por \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Restauración solicitada para el host \$hostDest, copia de seguridad #\$num," . " por \$User desde \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Archivo solicitado por \$User desde \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "Estado"; $Lang{PC_Summary} = "Resumen de Hosts"; $Lang{LOG_file} = "Archivo de eventos"; $Lang{LOG_files} = "Archivos de eventos"; $Lang{Old_LOGs} = "Eventos antiguos"; $Lang{Email_summary} = "Resumen correo"; $Lang{Config_file} = "Archivo configuración"; # $Lang{Hosts_file} = "Archivo Hosts"; $Lang{Current_queues} = "Colas actuales"; $Lang{Documentation} = "Documentación"; #$Lang{Host_or_User_name} = "Host o usuario:"; $Lang{Go} = "Aceptar"; $Lang{Hosts} = "Hosts"; $Lang{Select_a_host} = "Seleccione un host..."; $Lang{There_have_been_no_archives} = "

    No ha habido archivos

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    !Nunca se ha hecho copia de seguridad de este Host!

    \n"; $Lang{This_PC_is_used_by} = "
  • Este Host es utilizado por \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Extrayendo sólo Errores)"; $Lang{XferLOG} = "TransfREG"; $Lang{Errors} = "Errores"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <El último mensaje enviado a \${UserLink(\$user)} fué a las \$mailTime, asunto "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <El comando \$cmd está ejecutandose para \$host, comenzado a \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <El host \$host está en cola en la cola en segundo plano (pronto tendrá copia de seguridad). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <Host \$host está en cola en la cola de usuarios (pronto tendrá copia de seguridad). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <Un comando para \$host está en la cola de comandos (se ejecutará pronto). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <El último estado fué \"\$Lang->{\$StatusHost{state}}\"\$reason a las \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <El último error fué \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <Los pings a \$host han fallado \$StatusHost{deadCnt} veces consecutivas. EOF # ----- $Lang{Prior_to_that__pings} = "Antes de eso, pings"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr a \$host han tenido éxito \$StatusHost{aliveCnt} veces consecutivas. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Dado que \$host ha estado en la red al menos \$Conf{BlackoutGoodCnt} veces consecutivas, no se le realizará copia de seguridad desde \$blackoutStr. EOF $Lang{__time0_to__time1_on__days} = "\$t0 hasta \$t1 en \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Las copias de seguridad se retrasarán durante \$hours horas (Cambie este número). EOF $Lang{tryIP} = " y \$StatusHost{dhcpHostIP}"; #$Lang{Host_Inhost} = "Host \$In{host}"; $Lang{checkAll} = <  Seleccionar todo EOF $Lang{checkAllHosts} = <  Seleccionar todo EOF $Lang{fileHeader} = < Nombre Tipo Modo N° Tamaño Hora Mod. EOF $Lang{Home} = "Inicio"; $Lang{Browse} = "Explorar copias de seguridad"; $Lang{Last_bad_XferLOG} = "Ultimo error en eventos de transferencia"; $Lang{Last_bad_XferLOG_errors_only} = "Ultimo error en eventos de transferencia (errores sólo)"; $Lang{This_display_is_merged_with_backup} = < Esta pantalla está unida a la copia de seguridad N°\$numF. EOF $Lang{Visit_this_directory_in_backup} = < Seleccione la copia de seguridad que desea ver: EOF $Lang{Restore_Summary} = < Haga click en el número de restauración para ver sus detalles. \$restoreStr
    Restauración N° Resultado Fecha Inicio Dur/mins N° Archivos MB N° Err. Tar N° Err. Transf.#xferErrs

    EOF $Lang{Archive_Summary} = < Hacer Click en el número de Archivo para más detalles. \$ArchiveStr
    Archive# Resultado Hora inicio Duración/mins

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Documentacion"; $Lang{No} = "no"; $Lang{Yes} = "si"; $Lang{The_directory_is_empty} = <El directorio \$dirDisplay está vacío EOF #$Lang{on} = "activo"; $Lang{off} = "inactivo"; $Lang{backupType_full} = "completo"; $Lang{backupType_incr} = "incremental"; $Lang{backupType_partial} = "parcial"; $Lang{failed} = "fallido"; $Lang{success} = "éxito"; $Lang{and} = "y"; # ------ # Hosts states and reasons $Lang{Status_idle} = "inactivo"; $Lang{Status_backup_starting} = "comenzando copia de seguridad"; $Lang{Status_backup_in_progress} = "copia de seguridad ejecutándose"; $Lang{Status_restore_starting} = "comenzando restauración"; $Lang{Status_restore_in_progress} = "restauración ejecutándose"; $Lang{Status_link_pending} = "conexión pendiente"; $Lang{Status_link_running} = "conexión en curso"; $Lang{Reason_backup_done} = "copia de seguridad realizada"; $Lang{Reason_restore_done} = "restauración realizada"; $Lang{Reason_archive_done} = "archivado realizado"; $Lang{Reason_nothing_to_do} = "nada por hacer"; $Lang{Reason_backup_failed} = "copia de seguridad fallida"; $Lang{Reason_restore_failed} = "restauración fallida"; $Lang{Reason_archive_failed} = "ha fallado el archivado"; $Lang{Reason_no_ping} = "no hay ping"; $Lang{Reason_backup_canceled_by_user} = "copia cancelada por el usuario"; $Lang{Reason_restore_canceled_by_user} = "restauración cancelada por el usuario"; $Lang{Reason_archive_canceled_by_user} = "archivado cancelado por el usuario"; $Lang{Disabled_OnlyManualBackups} = "auto deshabilitado"; $Lang{Disabled_AllBackupsDisabled} = "deshabilitado"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: ningúna copia de \$host ha tenido éxito"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Estimado $userName, Su PC ($host) nunca ha completado una copia de seguridad mediante nuestro programa de copias de seguridad. Las copias de seguridad deberían ejecutarse automáticamente cuando su PC se conecta a la red. Debería contactar con su soporte técnico si: - Su ordenador ha estado conectado a la red con regularidad. Esto implicaría que existe algún problema de instalación o configuración que impide que se realicen las copias de seguridad. - No desea realizar copias de seguridad y no quiere recibir más mensajes como éste. De no ser así, asegúrese de que su PC está conectado a la red la próxima vez que esté en la oficina. Saludos: Agente BackupPC http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: no hay copias de seguridad recientes de \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Estimado $userName, No se ha podido completar ninguna copia de seguridad de su PC ($host) durante $days días. Su PC ha realizado copias de seguridad correctas $numBackups veces desde $firstTime hasta hace $days días. Las copias de seguridad deberían efectuarse automáticamente cuando su PC está conectado a la red. Si su PC ha estado conectado durante algunas horas a la red durante los últimos $days días debería contactar con su soporte técnico para ver porqué las copias de seguridad no funcionan adecuadamente. Por otro lado, si está fuera de la oficina, no hay mucho que se pueda hacer al respecto salvo copiar manualmente los archivos especialmente críticos a otro soporte físico. Debería estar al corriente de que cualquier archivo que haya creado o modificado en los últimos $days días (incluyendo todo el correo nuevo y archivos adjuntos) no pueden ser restaurados si su disco se avería. Saludos: Agente BackupPC http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Los archivos de Outlook de \$host necesitan ser copiados"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Estimado $userName, The Outlook files on your PC have $howLong. These files contain all your email, attachments, contact and calendar information. Your PC has been correctly backed up $numBackups times from $firstTime to $lastTime days ago. However, Outlook locks all its files when it is running, preventing these files from being backed up. It is recommended you backup the Outlook files when you are connected to the network by exiting Outlook and all other applications, and, using just your browser, go to this link: $CgiURL?host=$host Select "Start Incr Backup" twice to start a new incremental backup. You can select "Return to $host page" and then hit "reload" to check the status of the backup. It should take just a few minutes to complete. Regards, ---------------------------------------------------------------- Los archivos de Outlook de su PC tienen $howLong. Estos archivos contienen todo su correo, adjuntos, contactos e información de su agenda. Los archivos de su PC han sido correctamente salvaguardados $numBackups veces desde $firstTime hasta hace $lastTime días. Sin embargo, Outlook bloquea todos sus archivos mientras funciona, impidiendo que pueda hacerse una copia de seguridad de los mismos. Se le recomienda hacer copia de seguridad de los archivos de Outlook cuando esté conectado a la red cerrando Outlook y el resto de aplicaciones y utilizando su navegador de internet haga click en este vínculo: $CgiURL?host=$host Seleccione "Iniciar copia de seguridad incremental" dos veces para iniciar una neva copia de seguridad incremental. Puede seleccionar "Volver a la página de $host " y luego de click en "refrescar" para verificar el estado del proceso de copia de seguridad. Debería llevarle sólo unos pocos minutos completar el proceso. Saludos: Agente BackupPC http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "no se le ha realizado una copia de seguridad con éxito"; $Lang{howLong_not_been_backed_up_for_days_days} = "no se le ha realizado una copia de seguridad durante \$days días"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "Servidor BackupPC"; $Lang{RSS_Doc_Description} = "RSS feed para BackupPC"; $Lang{RSS_Host_Summary} = < Nota: Marque 'Reemplazar' si desea modificar un valor espec’fico a este host.

    EOF $Lang{CfgEdit_Button_Save} = "Grabar"; $Lang{CfgEdit_Button_Insert} = "Insertar"; $Lang{CfgEdit_Button_Delete} = "Borrar"; $Lang{CfgEdit_Button_Add} = "Aumentar"; $Lang{CfgEdit_Button_Override} = "Reemplazar"; $Lang{CfgEdit_Button_New_Key} = "Nueva Llave"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "ENG Error: No grabó debido a errores"; $Lang{CfgEdit_Error__must_be_an_integer} = "Error: \$var debe ser un entero"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Error: \$var debe ser un número de valor real"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Error: \$var ingreso \$k debe ser un entero"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Error: \$var ingreso \$k debe ser un número de valor real"; $Lang{CfgEdit_Error__must_be_executable_program} = "Error: \$var debe ser una ruta de acceso ejecutable válida"; $Lang{CfgEdit_Error__must_be_valid_option} = "Error: \$var debe ser una opción válida"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "Copia del host \$copyHost no existe; creando nombre completo del host \$fullHost. Elimine este host si eso no es lo que deseaba."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User copió configuración del host \$fromHost a \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User borró \$p de \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User aumentó \$p a \$conf, con el valor \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User cambió \$p en \$conf a \$valueNew de \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User borró host \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User host \$host cambió \$key de \$valueOld a \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User aumentó host \$host: \$value\n"; #end of es.pm backuppc ver 3.3.0 - luis bustamante olivera - luisbustamante@yahoo.com BackupPC-3.3.2/lib/BackupPC/Lang/fr.pm0000444000076500000240000016121013042250554016142 0ustar craigstaff#!/usr/bin/perl #my %Lang; #use strict; # -------------------------------- $Lang{Start_Archive} = "Démarrer l'archivage"; $Lang{Stop_Dequeue_Archive} = "Arrêt/Mise en attente de l'archivage"; $Lang{Start_Full_Backup} = "Démarrer la sauvegarde complète"; $Lang{Start_Incr_Backup} = "Démarrer la sauvegarde incrémentielle"; $Lang{Stop_Dequeue_Backup} = "Arrêter/annuler la sauvegarde"; $Lang{Restore} = "Restaurer"; $Lang{Type_full} = "complète"; $Lang{Type_incr} = "incrémentielle"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Seuls les utilisateurs privilégiés peuvent voir les options d'administration."; $Lang{H_Admin_Options} = "BackupPC: Options d'administration"; $Lang{Admin_Options} = "Options d'administration"; $Lang{Admin_Options_Page} = < \${h2("Contrôle du serveur")}

    Recharger la configuration:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Impossible de se connecter au serveur BackupPC"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < L'erreur est: \$err.
    Il est possible que le serveur BackupPC ne fonctionne pas actuellement ou qu'il y ait une erreur de configuration. Veuillez contacter votre administrateur système. EOF $Lang{Admin_Start_Server} = < Le serveur BackupPC sur \$Conf{ServerHost}, port \$Conf{ServerPort} n'est pas en fonction (vous l'avez peut-être arrêté, ou vous ne l'avez pas encore démarré).
    Voulez-vous le démarrer ? EOF # ----- $Lang{H_BackupPC_Server_Status} = "État du serveur BackupPC"; $Lang{BackupPC_Server_Status_General_Info}= <
  • Le PID du serveur est \$Info{pid}, sur l\'hôte \$Conf{ServerHost}, version \$Info{Version}, démarré le \$serverStartTime.
  • Ce rapport a été généré le \$now.
  • La configuration a été chargée pour la dernière fois à \$configLoadTime.
  • La prochaine file d\'attente sera remplie à \$nextWakeupTime.
  • Autres infos:
    • \$numBgQueue demandes de sauvegardes en attente depuis le dernier réveil automatique,
    • \$numUserQueue requêtes de sauvegardes utilisateur en attente,
    • \$numCmdQueue requêtes de commandes en attente, \$poolInfo
    • L\'espace de stockage a été récemment rempli à \$Info{DUlastValue}% (\$DUlastTime), le maximum aujourd\'hui a été de \$Info{DUDailyMax}% (\$DUmaxTime) et hier le maximum était de \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Travaux en cours d'exécution")}

    \$jobStr
    Hôte Type Utilisateur Date de départ Commande PID PID du transfert

    \${h2("Échecs qui demandent de l'attention")}

    \$statusStr
    Hôte Type Utilisateur Dernier essai Détails Date d\'erreur Dernière erreur (autre que pas de ping)
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: Bilan des machines"; $Lang{BackupPC__Archive} = "BackupPC: Archivage"; $Lang{BackupPC_Summary}=<

    • Ce statut a été généré le \$now.
    • L\'espace de stockage a été récemment rempli à \$Info{DUlastValue}% (\$DUlastTime), le maximum aujourd\'hui a été de \$Info{DUDailyMax}% (\$DUmaxTime) et hier le maximum était de \$Info{DUDailyMaxPrev}%.

    \${h2("Hôtes avec de bonnes sauvegardes")}

    Il y a \$hostCntGood hôtes ayant été sauvegardés, pour un total de :

    • \$fullTot sauvegardes complètes de tailles cumulées de \${fullSizeTot} Go (précédant la mise en commun et la compression),
    • \$incrTot sauvegardes incrémentielles de tailles cumulées de \${incrSizeTot} Go (précédant la mise en commun et la compression).

    \$strGood
    Hôte Utilisateur Nb complètes Complètes Âge (jours) Complètes Taille (Go) Vitesse (Mo/s) Nb incrémentielles Incrémentielles Âge (jours) Dernière sauvegarde (jours) État actuel Nb erreurs transfert Dernière tentative


    \${h2("Hôtes sans sauvegardes")}

    Il y a \$hostCntNone hôtes sans sauvegardes.

    \$strNone
    Hôte Utilisateur Nb complètes Complètes Âge (jours) Complètes Taille (Go) Vitesse (Mo/s) Nb incrémentielles Incrémentielles Âge (jours) Dernière sauvegarde (jours) État actuel Nb erreurs transfert Dernière tentative
    EOF $Lang{BackupPC_Archive}=< Il y a \$hostCntGood hôtes qui ont été sauvegardés, représentant \${fullSizeTot} Go

    \$strGood \$checkAllHosts
    Host Utilisateur Taille

    EOF $Lang{BackupPC_Archive2}=< \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Dispositif/Localisation de l'archive EOF $Lang{BackupPC_Archive2_compression} = < Compression Aucune
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Pourcentage des données de parité (0 = désactivé, 5 = typique) EOF $Lang{BackupPC_Archive2_split} = < Scinder le fichier en fichiers de Mo EOF # ----------------------------------- $Lang{Pool_Stat} = <La mise en commun est constituée de \$info->{"\${name}FileCnt"} fichiers et \$info->{"\${name}DirCnt"} répertoires représentant \${poolSize} Go (depuis le \$poolTime),
  • Le hachage de mise en commun des fichiers donne \$info->{"\${name}FileCntRep"} fichiers répétés avec comme plus longue chaîne \$info->{"\${name}FileRepMax"},
  • Le nettoyage nocturne a effacé \$info->{"\${name}FileCntRm"} fichiers, soit \${poolRmSize} Go (vers \$poolTime), EOF # ----------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: Sauvegarde demandée sur \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < La réponse du serveur a été : \$reply

    Retourner à la page d\'accueil de \$host. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Confirmation du démarrage de la sauvegarde de \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Vous allez bientôt démarrer une sauvegarde \$type depuis \$host.

    Voulez-vous vraiment le faire ?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Confirmer l\'arrêt de la sauvegarde sur \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < Vous êtes sur le point d\'arrêter/supprimer de la file les sauvegardes de \$host;
    En outre, prière de ne pas démarrer d\'autre sauvegarde pendant heures.

    Voulez-vous vraiment le faire ?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Seuls les utilisateurs privilégiés peuvent voir les files."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Seuls les utilisateurs privilégiés peuvent archiver."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Résumé de la file"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Résumé des files des utilisateurs")}

    Les demandes utilisateurs suivantes sont actuellement en attente :

    \$strUser
    Hôte Temps Requis Utilisateur


    \${h2("Résumé de la file en arrière plan")}

    Les demandes en arrière plan suivantes sont actuellement en attente :

    \$strBg
    Hôte Temps requis Utilisateur


    \${h2("Résumé de la file d\'attente des commandes")}

    Les demandes de commande suivantes sont actuellement en attente :

    \$strCmd
    Hôtes Temps Requis Utilisateur Commande
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: Fichier \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, modifié le \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ \$skipped lignes sautées ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nNe peut pas ouvrir le fichier journal \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: Historique du fichier journal";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    Fichier Taille Date de modification
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Destinataire Hôte Date Sujet
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Navigation dans la sauvegarde \$num de \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Options de restauration sur \$host"; $Lang{Restore_Options_for__host2} = < Vous avez sélectionné les fichiers/répertoires suivants depuis le partage \$share, sauvegarde numéro \$num:
      \$fileListStr

    Vous avez trois choix pour restaurer ces fichiers/répertoires. Veuillez sélectionner une des options suivantes.

    \${h2("Option 1: Restauration directe")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    Attention: tous les fichiers correspondant à ceux que vous avez sélectionnés vont être écrasés !

    \$hiddenStr
    Restaure les fichiers vers l'hôte
    Restaurer les fichiers vers le partage
    Restaurer les fichiers du répertoire
    (relatif au partage)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Option 2: Télécharger une archive Zip")}

    Vous pouvez télécharger une archive compressée (.zip) contenant tous les fichiers/répertoires que vous avez sélectionnés. Vous pouvez utiliser une application locale, comme Winzip, pour voir ou extraire n\'importe quel fichier.

    Attention: en fonction des fichiers/répertoires que vous avez sélectionnés, cette archive peut devenir très très volumineuse. Cela peut prendre plusieurs minutes pour créer et transférer cette archive, et vous aurez besoin d\'assez d\'espace disque pour la stocker.

    \$hiddenStr Faire l\'archive relative à \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (Autrement l\'archive contiendra les chemins complets).
    Compression (0=désactivée, 1=rapide,...,9=meilleure)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Option 2: Télécharger une archive Zip")}

    Vous ne pouvez pas télécharger d'archive zip, car Archive::Zip n\'est pas installé. Veuillez demander à votre administrateur système d\'installer Archive::Zip depuis www.cpan.org.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < Vous pouvez télécharger une archive Tar contenant tous les fichiers/répertoires que vous avez sélectionnés. Vous pourrez alors utiliser une application locale, comme tar ou winzip pour voir ou extraire n\'importe quel fichier.

    Attention: en fonction des fichiers/répertoires que vous avez sélectionnés, cette archive peut devenir très très volumineuse. Cela peut prendre plusieurs minutes pour créer et transférer l\'archive, et vous aurez besoin d\'assez d\'espace disque local pour la stocker.

    \$hiddenStr Faire l\'archive relative à \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (Autrement l\'archive contiendra des chemins absolus).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Confirmation de restauration sur \$host"; $Lang{Are_you_sure} = < Vous êtes sur le point de démarrer une restauration directement sur la machine \$In{hostDest}. Les fichiers suivants vont être restaurés dans le partage \$In{shareDest}, depuis la sauvegarde numéro \$num:

    \$fileListStr
    Fichier/Répertoire originalVa être restauré à

    \$hiddenStr Voulez-vous vraiment le faire ?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Restauration demandée sur \$hostDest"; $Lang{Reply_from_server_was___reply} = < La réponse du serveur est : \$reply

    Retourner à la page d\'accueil de \$hostDest . EOF $Lang{BackupPC_Archive_Reply_from_server} = < La réponse du serveur est : \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Résumé de la sauvegarde de l\'hôte \$host "; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("Actions de l\'utilisateur")}

    \$startIncrStr

    \${h2("Résumé de la sauvegarde")}

    Cliquer sur le numéro de l\'archive pour naviguer et restaurer les fichiers de sauvegarde.

    \$str
    Sauvegarde n° Type Fusionnée Niveau Date de démarrage Durée (min) Âge (jours) Chemin d\'accès de la sauvegarde sur le serveur

    \$restoreStr



    \${h2("Résumé des erreurs de transfert")}

    \$errStr
    Sauvegarde n° Type Voir Nb erreurs transfert Nb mauvais fichiers Nb mauvais partages Nb erreurs tar


    \${h2("Récapitulatif de la taille des fichier et du nombre de réutilisations")}

    Les fichiers existants sont ceux qui sont déjà sur le serveur; Les nouveaux fichiers sont ceux qui ont été ajoutés au serveur. Les fichiers vides et les erreurs de SMB ne sont pas comptabilisés dans les fichiers nouveaux ou réutilisés.

    \$sizeStr
    Totaux Fichiers existants Nouveaux fichiers
    Sauvegarde n° Type Nb de Fichiers Taille (Mo) Mo/s Nb de Fichiers Taille (Mo) Nb de Fichiers Taille (Mo)


    \${h2("Résumé de la compression")}

    Performance de la compression pour les fichiers déjà sur le serveur et récemment compressés.

    \$compStr
    Fichiers existants Nouveaux fichiers
    Nb de sauvegardes Type Niveau de Compression Taille (Mo) Taille compressée (Mo) Compression Taille (Mo) Taille compressée (Mo) Compression


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Résumé de l'archivage pour l'hôte \$host"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("User Actions")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Erreur"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Serveur"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • Vous naviguez dans la sauvegarde n°\$num, qui a commencé vers \$backupTime (il y a \$backupAge jours), \$filledBackup
    • Entrez le répertoire:
    • Cliquer sur un répertoire ci-dessous pour y naviguer,
    • Cliquer sur un fichier ci-dessous pour le restaurer,
    • Vous pouvez voir l'historique des différentes sauvegardes du répertoire courant.
    \${h2("Contenu de \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Historique des sauvegardes du répertoire courant pour \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "rep"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < Cette page montre toutes les version disponibles des fichiers sauvegardés pour le répertoire courant :
    • Cliquez sur un numéro de sauvegarde pour revenir à la navigation de sauvegarde,
    • Cliquez sur un répertoire (\$Lang->{DirHistory_dirLink}) pour naviguer dans celui-ci.
    • Cliquez sur une version d'un fichier (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) pour le télécharger.
    • Les fichiers avec des contenus identiques pour plusieurs sauvegardes ont le même numéro de version.
    • Les fichiers qui ne sont pas présents sur une sauvegarde en particulier sont représentés par une boîte vide.
    • Les fichiers montrés avec la même version peuvent avoir des attributs différents. Choisissez le numéro de sauvegarde pour voir les attributs de fichiers.
    \${h2("Historique de \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Numéro de sauvegarde
    Date
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Détails de la restauration n°\$num pour \$host"; $Lang{Restore___num_details_for__host2} = <
    Numéro \$Restores[\$i]{num}
    Demandée par \$RestoreReq{user}
    Demandée à \$reqTime
    Résultat \$Restores[\$i]{result}
    Message d'erreur \$Restores[\$i]{errorMsg}
    Hôte source \$RestoreReq{hostSrc}
    N° de sauvegarde \$RestoreReq{num}
    Partition source \$RestoreReq{shareSrc}
    Hôte de destination \$RestoreReq{hostDest}
    Partition de destination \$RestoreReq{shareDest}
    Début \$startTime
    Durée \$duration min
    Nombre de fichiers \$Restores[\$i]{nFiles}
    Taille totale \${MB} Mo
    Taux de transfert \$MBperSec Mo/s
    Erreurs de TarCreate \$Restores[\$i]{tarCreateErrs}
    Erreurs de transfert \$Restores[\$i]{xferErrs}
    Journal de transfert Visionner, Erreurs

    \${h1("Liste des Fichiers/Répertoires")}

    \$fileListStr
    Fichier/répertoire originalRestauré vers
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Détails de l'archivage n°\$num pour \$host"; $Lang{Archive___num_details_for__host2 } = <
    Numéro \$Archives[\$i]{num}
    Demandé par \$ArchiveReq{user}
    Heure de demande \$reqTime
    Résultat \$Archives[\$i]{result}
    Message d'erreur \$Archives[\$i]{errorMsg}
    Heure de début \$startTime
    Durée \$duration min
    Journal de transfert Voir, Erreurs

    \${h1("Liste de hôtes")}

    \$HostListStr
    HostNuméro de sauvegarde
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Résumé du courriel"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new a échoué: regardez le fichier error_log d\'apache\n"; $Lang{Wrong_user__my_userid_is___} = "Mauvais utilisateur: mon userid est \$>, à la place de \$uid " . "(\$Conf{BackupPCUser})\n"; #$Lang{Only_privileged_users_can_view_PC_summaries} = "Seuls les utilisateurs privilégiés peuvent voir les résumés des machines."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Seuls les utilisateurs privilégiés peuvent arrêter ou démarrer des sauvegardes sur " . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Numéro invalide \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "Impossible d\'ouvrir \$file : problème de configuration ?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Seuls les utilisateurs privilégiés peuvent voir les fichiers de journal ou les fichiers de configuration."; $Lang{Only_privileged_users_can_view_log_files} = "Seuls les utilisateurs privilégiés peuvent voir les fichiers de journal."; $Lang{Only_privileged_users_can_view_email_summaries} = "Seuls les utilisateurs privilégiés peuvent voir les compte-rendus des courriels."; $Lang{Only_privileged_users_can_browse_backup_files} = "Seuls les utilisateurs privilégiés peuvent parcourir les fichiers de sauvegarde" . " pour l'hôte \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Nom d\'hôte vide."; $Lang{Directory___EscHTML} = "Le répertoire \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " est vide"; $Lang{Can_t_browse_bad_directory_name2} = "Ne peut pas parcourir " . " \${EscHTML(\$relDir)} : mauvais nom de répertoire"; $Lang{Only_privileged_users_can_restore_backup_files} = "Seuls les utilisateurs privilégiés peuvent restaurer " . " des fichiers de sauvegarde pour l\'hôte \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Mauvais nom d\'hôte \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "Vous n\'avez sélectionné aucun fichier ; " . "vous pouvez revenir en arrière pour sélectionner des fichiers."; $Lang{You_haven_t_selected_any_hosts} = "Vous n\'avez sélectionné aucun hôte ; veuillez retourner à la page précédente pour" . " faire la sélection d\'un hôte."; $Lang{Nice_try__but_you_can_t_put} = "Bien tenté, mais vous ne pouvez pas mettre \'..\' dans un nom de fichier."; $Lang{Host__doesn_t_exist} = "L'hôte \${EscHTML(\$In{hostDest})} n\'existe pas."; $Lang{You_don_t_have_permission_to_restore_onto_host} = "Vous n\'avez pas la permission de restaurer sur l\'hôte" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Ne peut pas ouvrir/créer " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Seuls les utilisateurs privilégiés peuvent restaurer" . " des fichiers de sauvegarde pour l\'hôte \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Nom d\'hôte vide"; $Lang{Unknown_host_or_user} = "\${EscHTML(\$host)}, hôte ou utilisateur inconnu."; $Lang{Only_privileged_users_can_view_information_about} = "Seuls les utilisateurs privilégiés peuvent accéder aux " . " informations sur l\'hôte \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Seuls les utilisateurs privilégiés peuvent voir les informations d'archivage."; $Lang{Only_privileged_users_can_view_restore_information} = "Seuls les utilisateurs privilégiés peuvent restaurer des informations."; $Lang{Restore_number__num_for_host__does_not_exist} = "La restauration numéro \$num de l\'hôte \${EscHTML(\$host)} n\'existe pas"; $Lang{Archive_number__num_for_host__does_not_exist} = "L\'archive n°\$num pour l\'hôte \${EscHTML(\$host)} n\'existe pas."; $Lang{Can_t_find_IP_address_for} = "Ne peut pas trouver d\'adresse IP pour \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < Tant que je ne verrai pas \$host à une adresse DHCP particulière, vous ne pourrez démarrer cette requête que depuis la machine elle même. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "Demande de sauvegarde sur l\'hôte \$host (\$In{hostIP}) par" . " \$User depuis \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Sauvegarde demandée sur \$host par \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Sauvegarde arrêtée/déprogrammée pour \$host par \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Restauration demandée pour l\'hôte \$hostDest, " . "sauvegarde n°\$num, par \$User depuis \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Archivage demandé par \$User de \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "État"; $Lang{PC_Summary} = "Bilan des machines"; $Lang{LOG_file} = "Fichier journal"; $Lang{LOG_files} = "Fichiers journaux"; $Lang{Old_LOGs} = "Vieux journaux"; $Lang{Email_summary} = "Résumé des courriels"; $Lang{Config_file} = "Fichier de configuration"; # $Lang{Hosts_file} = "Fichiers des hôtes"; $Lang{Current_queues} = "Files actuelles"; $Lang{Documentation} = "Documentation"; #$Lang{Host_or_User_name} = "Hôte ou Nom d\'utilisateur:"; $Lang{Go} = "Chercher"; $Lang{Hosts} = "Hôtes"; $Lang{Select_a_host} = "Choisissez un hôte..."; $Lang{There_have_been_no_archives} = "

    Il n'y a pas d'archives

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    Cette machine n'a jamais été sauvegardée !!

    \n"; $Lang{This_PC_is_used_by} = "
  • Cette machine est utilisée par \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Extraction des erreurs seulement)"; $Lang{XferLOG} = "JournalXfer"; $Lang{Errors} = "Erreurs"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <Le dernier courriel envoyé à \${UserLink(\$user)} le \$mailTime, avait comme sujet "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <La commande \$cmd s\'exécute actuellement sur \$host, démarrée le \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <L\'hôte \$host se trouve dans la liste d\'attente d\'arrière plan (il sera sauvegardé bientôt). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <L\'hôte \$host se trouve dans la liste d\'attente utilisateur (il sera sauvegardé bientôt). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <Une commande pour l\'hôte \$host est dans la liste d\'attente des commandes (sera lancée bientôt). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <L\'état courant est \"\$Lang->{\$StatusHost{state}}\"\$reason depuis \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <La dernière erreur est \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <Les pings vers \$host ont échoué \$StatusHost{deadCnt} fois consécutives. EOF # ----- $Lang{Prior_to_that__pings} = "Avant cela, les pings"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <Les \$priorStr vers \$host ont réussi \$StatusHost{aliveCnt} fois consécutives. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <\$host a été présent sur le réseau au moins \$Conf{BlackoutGoodCnt} fois consécutives, il ne sera donc pas sauvegardé de \$blackoutStr. EOF $Lang{__time0_to__time1_on__days} = "\$t0 à \$t1 pendant \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Les sauvegardes sont reportées pour \$hours heures (changer ce nombre). EOF $Lang{tryIP} = " et \$StatusHost{dhcpHostIP}"; # $Lang{Host_Inhost} = "Hôte \$In{host}"; $Lang{checkAll} = <  Tout sélectionner EOF $Lang{checkAllHosts} = <  Tout sélectionner EOF $Lang{fileHeader} = < Nom Type Mode n° Taille Date de modification EOF $Lang{Home} = "Accueil"; $Lang{Browse} = "Explorer les sauvegardes"; $Lang{Last_bad_XferLOG} = "Bilan des derniers transferts échoués"; $Lang{Last_bad_XferLOG_errors_only} = "Bilan des derniers transferts échoués (erreurs seulement)"; $Lang{This_display_is_merged_with_backup} = < Cet affichage est fusionné avec la sauvegarde n°\$numF, la plus récente copie intégrale. EOF $Lang{Visit_this_directory_in_backup} = < Choisissez la sauvegarde que vous désirez voir : EOF $Lang{Restore_Summary} = < Cliquer sur le numéro de restauration pour plus de détails. \$restoreStr
    Sauvegarde n° Résultat Date de départ Durée (min) Nb fichiers Taille (Mo) Nb errs tar Nb errs trans

    EOF $Lang{Archive_Summary} = < Cliquez sur le numéro de l'archive pour plus de détails. \$ArchiveStr
    No. Archive Résultat Date début Durée (min)

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Documentation"; $Lang{No} = "non"; $Lang{Yes} = "oui"; $Lang{The_directory_is_empty} = <Le répertoire \$dirDisplay est vide EOF #$Lang{on} = "actif"; $Lang{off} = "inactif"; $Lang{backupType_full} = "complète"; $Lang{backupType_incr} = "incrémentielle"; $Lang{backupType_partial} = "partielle"; $Lang{failed} = "échec"; $Lang{success} = "succès"; $Lang{and} = "et"; # ------ # Hosts states and reasons $Lang{Status_idle} = "inactif"; $Lang{Status_backup_starting} = "début de la sauvegarde"; $Lang{Status_backup_in_progress} = "sauvegarde en cours"; $Lang{Status_restore_starting} = "début de la restauration"; $Lang{Status_restore_in_progress} = "restauration en cours"; $Lang{Status_link_pending} = "en attente de l'édition de liens"; $Lang{Status_link_running} = "édition de liens en cours"; $Lang{Reason_backup_done} = "sauvegarde terminée"; $Lang{Reason_restore_done} = "restauration terminée"; $Lang{Reason_archive_done} = "archivage terminé"; $Lang{Reason_nothing_to_do} = "rien à faire"; $Lang{Reason_backup_failed} = "la sauvegarde a échoué"; $Lang{Reason_restore_failed} = "la restauration a échoué"; $Lang{Reason_archive_failed} = "l'archivage a échoué"; $Lang{Reason_no_ping} = "pas de ping"; $Lang{Reason_backup_canceled_by_user} = "sauvegarde annulée par l'utilisateur"; $Lang{Reason_restore_canceled_by_user} = "restauration annulée par l'utilisateur"; $Lang{Reason_archive_canceled_by_user} = "archivage annulé par l'utilisateur"; $Lang{Disabled_OnlyManualBackups} = "auto désactivé"; $Lang{Disabled_AllBackupsDisabled} = "désactivé"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: aucune sauvegarde de \$host n'a réussi"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers $userName, Notre logiciel de copies de sécurité n'a jamais réussi à effectuer la sauvegarde de votre ordinateur ($host). Les sauvegardes devraient normalement survenir lorsque votre ordinateur est connecté au réseau. Vous devriez contacter le responsable informatique si : - Votre ordinateur est régulièrement connecté au réseau, ce qui signifie qu'il y aurait un problème de configuration empêchant les sauvegardes de s'effectuer. - Vous ne voulez pas qu'il y ait de sauvegardes de votre ordinateur ni ne voulez recevoir d'autres messages comme celui-ci. Dans le cas contraire, veuillez vous assurer dès que possible que votre ordinateur est correctement connecté au réseau. Merci de votre attention, BackupPC Génie http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: aucune sauvegarde récente de \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers $userName, Aucune sauvegarde de votre ordinateur n'a été effectuée depuis $days jours. $numBackups sauvegardes ont étés effectuées du $firstTime jusqu'à il y a $days jours. Les sauvegardes devraient normalement survenir lorsque votre ordinateur est connecté au réseau. Si votre ordinateur a effectivement été connecté au réseau plus de quelques heures durant les derniers $days jours, vous devriez contacter votre responsable informatique pour savoir pourquoi les sauvegardes ne s'effectuent pas correctement. Autrement, si vous êtes en dehors du bureau, il n'y a pas d'autre chose que vous pouvez faire, à part faire des copies de vos fichiers importants sur d'autres medias. Vous devez réaliser que tout fichier crée ou modifié durant les $days derniers jours (incluant les courriels et les fichiers attachés) ne pourra pas être restauré si un problème survient avec votre ordinateur. Merci de votre attention, BackupPC Génie http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Les fichiers de Outlook sur \$host doivent être sauvegardés"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers $userName, Les fichiers Outlook sur votre ordinateur n'ont $howLong. Ces fichiers contiennent tous vos courriels, fichiers attachés, carnets d'adresses et calendriers. $numBackups sauvegardes ont étés effectuées du $firstTime au $lastTime. Par contre, Outlook bloque ses fichiers lorsqu'il est ouvert, ce qui empêche de les sauvegarder. Il est recommandé d'effectuer une sauvegarde de vos fichiers Outlook quand vous serez connecté au réseau en quittant Outlook et toute autre application, et en visitant ce lien avec votre navigateur web: $CgiURL?host=$host Choisissez "Démarrer la sauvegarde incrémentielle" deux fois afin d'effectuer une nouvelle sauvegarde. Vous pouvez ensuite choisir "Retourner à la page de $host" et appuyer sur "Recharger" dans votre navigateur avec de vérifier le bon fonctionnement de la sauvegarde. La sauvegarde devrait prendre quelques minutes à s'effectuer. Merci de votre attention, BackupPC Génie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "jamais été sauvegardés"; $Lang{howLong_not_been_backed_up_for_days_days} = "pas été sauvegardés depuis \$days jours"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPC Server"; $Lang{RSS_Doc_Description} = "RSS feed for BackupPC"; $Lang{RSS_Host_Summary} = < Note: Cochez Écraser pour modifier une valeur spécifique à cette machine.

    EOF $Lang{CfgEdit_Button_Save} = "Sauvegarder"; $Lang{CfgEdit_Button_Insert} = "Insérer"; $Lang{CfgEdit_Button_Delete} = "Détruire"; $Lang{CfgEdit_Button_Add} = "Ajouter"; $Lang{CfgEdit_Button_Override} = "Écraser"; $Lang{CfgEdit_Button_New_Key} = "Nouvelle clé"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "Erreur: Pas de sauvegarde à cause d'erreurs."; $Lang{CfgEdit_Error__must_be_an_integer} = "Erreur: \$var doit être un nombre entier"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Erreur: \$var doit être un nombre réel"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Erreur: l'entrée \$k de \$var doit être un nombre entier"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Erreur: l'entrée \$k de \$var doit être un nombre réel"; $Lang{CfgEdit_Error__must_be_executable_program} = "Erreur: \$var doit être un chemin exécutable"; $Lang{CfgEdit_Error__must_be_valid_option} = "Erreur: \$var doit être une option valide"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "La machine \$copyHost ne peut être copiée, car elle n'existe pas ; création d'une machine nommée \$fullHost. Détruisez cette machine si ce n'est pas ce que vous vouliez."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User a copié la config de \$fromHost à \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User a détruit \$p de \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User a ajouté \$p à \$conf en fixant sa valeur à \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User a changé \$p dans \$conf de \$valueOld à \$valueNew\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User a détruit la machine \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User a changé \$key de \$valueOld à \$valueNew sur \$host\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User a jouté la machine \$host: \$value\n"; #end of lang_fr.pm BackupPC-3.3.2/lib/BackupPC/Lang/it.pm0000444000076500000240000015770213042250554016162 0ustar craigstaff#!/usr/bin/perl # # Italian i18n file # # (C) Lorenzo Cappelletti 2004 # Added translations and corrections: # Giuseppe Iuculano 2006 # Vittorio Macchi 2006 # # -------------------------------- $Lang{Start_Archive} = "Avvia archivio"; $Lang{Stop_Dequeue_Archive} = "Arresta/disaccoda archivio"; $Lang{Start_Full_Backup} = "Avvia backup completo"; $Lang{Start_Incr_Backup} = "Avvia backup incrementale"; $Lang{Stop_Dequeue_Backup} = "Arresta/disaccoda backup"; $Lang{Restore} = "Ripristina"; $Lang{Type_full} = "completo"; $Lang{Type_incr} = "incrementale"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Solo gli utenti privilegiati possono visualizzare le opzioni di amministrazione."; $Lang{H_Admin_Options} = "Server BackupPC: opzioni di amministrazione"; $Lang{Admin_Options} = "Opzioni di amministrazione"; $Lang{Admin_Options_Page} = < \${h2("Controllo server")}

    Ricarica la configurazione del server:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Impossibile connettersi al server BackupPC"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < L'errore è: \$err.
    Forse il server BackupPC non è in esecuzione o c'è un errore nella configurazione. Contattare l'amministratore di sistema. EOF $Lang{Admin_Start_Server} = < Il server BackupPC presso \$Conf{ServerHost} sulla porta \$Conf{ServerPort} non è attualmente in esecuzione (forse è stato arrestato oppure non è stato ancora avviato).
    Si desidera avviarlo? EOF # ----- $Lang{H_BackupPC_Server_Status} = "Stato server BackupPC"; $Lang{BackupPC_Server_Status_General_Info}= <
  • Il PID del server è \$Info{pid} sull\'host \$Conf{ServerHost}, versione \$Info{Version}, avviato il \$serverStartTime.
  • Questo rapporto di stato è stato generato il \$now.
  • La configurazione è stata caricata l'ultima volte il \$configLoadTime.
  • Il prossimo accodamento dei PC sarà effettuato il \$nextWakeupTime.
  • Altre informazioni:
    • \$numBgQueue richieste pendenti di backup dall\'ultimo risveglio programmato
    • \$numUserQueue richieste pendenti di backup da parte degli utenti
    • \$numCmdQueue richieste pendenti di comandi \$poolInfo
    • Recentemente il sistema dei file di pool è stato al \$Info{DUlastValue}% (\$DUlastTime). Il massimo di oggi è del \$Info{DUDailyMax}% (\$DUmaxTime), mentre quello di ieri era del \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Processi attualmente in esecuzione")}

    \$jobStr
    Host Tipo Utente Data inizio Comando PID PID Xfer

    \${h2("Fallimenti che richiedono attenzione")}

    \$statusStr
    Host Tipo Utente Ultimo tentativo Dettagli Data errore Ultimo errore (diverso da ping)
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: prospetto host"; $Lang{BackupPC__Archive} = "BackupPC: Archive"; $Lang{BackupPC_Summary} = <

    • Questo rapporto di stato è stato generato il \$now.
    • Recentemente il sistema dei file di pool è stato al \$Info{DUlastValue}% (\$DUlastTime). Il massimo di oggi è del \$Info{DUDailyMax}% (\$DUmaxTime), mentre quello di ieri era del \$Info{DUDailyMaxPrev}%.

    \${h2("Host con backup validi")}

    Ci sono \$hostCntGood host sottoposti a backup per un totale di:

    • \$fullTot backup completi per una dimensione totale di \${fullSizeTot}GB (prima del processo di pooling e compressione),
    • \$incrTot backup incrementali per una dimensione totale di \${incrSizeTot}GB (prima del processo di pooling e compressione).

    \$strGood
    Host Utente Completi Età completi (giorni) Dimensione completi (GB) Velocità (MB/s) Incrementali Età incrementali (giorni) Ultimo Backup (giorni) Stato Numero errori trasferimento Ultimo tentativo


    \${h2("Host senza backup")}

    Ci sono \$hostCntNone host senza alcun backup.

    \$strNone
    Host Utente Completi Età completi (giorni) Dimensione completi (GB) Velocità (MB/s) Incrementali Età incrementali (giorni) Ultimo Backup (giorni) Stato Numero errori trasferimento Ultimo tentativo
    EOF $Lang{BackupPC_Archive} = < È stato effettuato il backup di \$hostCntGood host per una dimensione totale di \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    Host Utente Dimensione backup

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Localizzazione archivio/dispositivi EOF $Lang{BackupPC_Archive2_compression} = < Compressione nessuna
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Percentuale di dati di parità (0 = disabiltata, 5 = valori tipici) EOF $Lang{BackupPC_Archive2_split} = < Suddividi output in Megabyte EOF # ----------------------------------- $Lang{Pool_Stat} = <Il pool di \${poolSize}GB comprende \$info->{"\${name}FileCnt"} file e \$info->{"\${name}DirCnt"} directory (al \$poolTime),
  • L\'hash del pool dà \$info->{"\${name}FileCntRep"} file ripetuti con la catena più lunga di \$info->{"\${name}FileRepMax"},
  • La pulizia notturna ha rimosso \$info->{"\${name}FileCntRm"} file per una dimensione di \${poolRmSize}GB (circa il \$poolTime), EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: richiesta di backup per \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < La risposta del server è stata: \$reply

    Ritorna alla homepage di \$host. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: conferma avvio backup per \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Si sta per avviare un backup \$type per \$host.

    Avviare veramente?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Conferma di arresto backup per \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < Si sta per arrestare/disaccodare i backup per \$host;
    Also, please don\'t start another backup for hours.

    Arrestare veramente?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Solo gli utenti con privilegi possono visualizzare la coda."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Solo gli utenti privilegiati possono archiviare."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Prospetto coda"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Prospetto code utenti")}

    Sono state accodate le seguenti richieste degli utenti:

    \$strUser
    Host Data richiesta Utente


    \${h2("Prospetto code in background")}

    Sono attualmente in coda le seguenti richieste di background:

    \$strBg
    Host Data richiesta Utente


    \${h2("Prospetto coda comandi")}

    Sono attualmente in coda le seguenti richieste di comandi:

    \$strCmd
    Host Data richiesta Utente Comando
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: file \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file modificato il \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ saltate \$skipped righe ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nImpossibile aprire il file di log \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: cronologia file di log";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    File Dimensione Data modifica
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Destinatario Host Data Oggetto
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Sfoglia backup \$num per \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Opzioni di ripristino per \$host"; $Lang{Restore_Options_for__host2} = < Sono state selezionate i seguenti file/directory dalla condivisione \$share, backup numero \$num:
      \$fileListStr

    Sono disponibili tre scelte per il ripristino di questi file/directory. Selezionare una delle opzioni seguenti.

    \${h2("Opzione 1: ripristino diretto")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    Attenzione: ogni file esistente che corrisponde ai file selezionati sarà sovrascritto!

    \$hiddenStr
    Ripristino dei file sull\'host
    Ripristino dei file sulla condivisione
    Ripristino dei file al di sotto della directory (relativa alla condivisione)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Opzione 2: scaricamento archivio zip")}

    È possibile scaricare un archivio zip contenente tutti i file/directory selezionati. Sarà poi possibile usare un applicativo locale, come WinZip, per visualizzare o estrarre un file qualsiasi.

    Attenzione: a seconda dei file/directory selezionati, l\'archivio potrebbe essere molto grande. La creazione ed il trasferimento dell\'archivio potrebbe richiedere diversi minuti e sarà necessario disporre di abbastanza spazio sul proprio disco rigido locale per poterlo contenere.

    \$hiddenStr Creare l\'archivio relativamente a \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (altrimenti l\'archivio conterrà percorsi completi).
    Compressione (0=off, 1=fast,...,9=best)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Opzione 2: scaricamento archivio zip")}

    Archive::Zip non è installato e non è quindi possibile scaricare un archivio zip. Contattare l\'amministratore di sistema per installare Archive::Zip da www.cpan.org.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < È possibile scaricare un archivio tar contenente tutti i file/directory selezionati. Sarà poi possibile usare un applicativo locale, come tar o WinZip, per visualizzare o estrarre un file qualsiasi.

    Attenzione: a seconda dei file/directory selezionati, l\'archivio potrebbe essere molto grande. La creazione ed il trasferimento dell\'archivio potrebbe richiedere diversi minuti e sarà necessario disporre di abbastanza spazio sul proprio disco rigido locale per poterlo contenere.

    \$hiddenStr Creare l\'archivio relativamente a \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (altrimenti l\'archivio conterrà percorsi completi).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Conferma ripristino su \$host"; $Lang{Are_you_sure} = < Si sta per avviare il ripristino diretto sulla macchina \$In{hostDest}. I file seguenti saranno ripristinati sulla condivisione \$In{shareDest} dal backup numero \$num:

    \$fileListStr
    File/directory originaleRipristinato su

    \$hiddenStr Avviare veramente?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: ripristino richiesto per \$hostDest"; $Lang{Reply_from_server_was___reply} = < La risposta del server è stata: \$reply

    Ritorna alla homepage di \$hostDest. EOF $Lang{BackupPC_Archive_Reply_from_server} = < La risposta del server è stata: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: prospetto backup host \$host"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("Azioni utente")}

    \$startIncrStr
    \${h2("Prospetto backup")}

    Cliccare sul numero di backup per sfogliare e ripristinare i file di backup.

    \$str
    Numero backup Tipo Completo Livello Data avvio Durata (minuti) Età (giorni) Percorso backup server

    \$restoreStr



    \${h2("Prospetto errori trasferimento")}

    \$errStr
    Numero backup Tipo Vedere Numero errori trasferimento Numero file con problemi Numero condivisioni con problemi Numero errori tar


    \${h2("Prospetto dimensioni file/contatore riutilizzo")}

    I file esistenti sono quelli già presenti nel pool; i file nuovi sono quelli aggiunti al pool. I file vuoti e gli errori SMB non sono conteggiati nei contatori di riutilizzo e file nuovi.

    \$sizeStr
    Totali File esistenti File nuovi
    Numero backup Tipo Numero file Dimensione (MB) Velocità (MB/s) Numero file Dimensione (MB) Numero file Dimensione (MB)


    \${h2("Prospetto compressione")}

    Prestazione della compressione per file già nel pool e per quelli nuovi.

    \$compStr
    File esistenti File nuovi
    Numero backup Tipo Livello compressione Dimensione (MB) Compresso (MB) Tasso compressione Dimensione (MB) Compresso (MB) Tasso compressione


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: prospetto archivi host \$host"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("Azioni utente")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Errore"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Server"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • Si sta sfogliando il backup numero \$num effettuato il \$backupTime (\$backupAge giorni fa), \$filledBackup
    • Entra directory:
    • Fare clic su una directory per aprirla
    • Fare clic su un file per ripristinarlo
    • È possibile visualizzare la cronologia dei backup della directory corrente
    \${h2("Contents of \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Cronologia backup directory per \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "dir"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < Questa videata mostra tutte le versioni uniche disponibili nei diversi backup:
    • Fare clic su un numero di backup per ritornare al navigatore di backup
    • Fare clic sul collegamento ad una directory (\$Lang->{DirHistory_dirLink}) per navigare nella directory stessa
    • Fare clic sul collegamento ad un file (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) per scaricare quel file
    • I file con lo stesso contenuto fra backup diversi hanno lo stesso numero di versione
    • I file o directory non disponibili in uno specifico backup presentano una casella vuota
    • I file visualizzati con la stessa versione possono avere attributi diversi. Selezionare il numero di backup per visualizzare gli attributi del file.
    \${h2("Cronologia di \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Numero backup
    Data backup
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: dettagli ripristino numero \$num per \$host"; $Lang{Restore___num_details_for__host2} = <
    Numero \$Restores[\$i]{num}
    Richiesto da \$RestoreReq{user}
    Data richiesta \$reqTime
    Risultato \$Restores[\$i]{result}
    Messaggio d\'errore \$Restores[\$i]{errorMsg}
    Host sorgente \$RestoreReq{hostSrc}
    Numero backup sorgente \$RestoreReq{num}
    Condivisione sorgente \$RestoreReq{shareSrc}
    Host destinazione \$RestoreReq{hostDest}
    Condivisione destinazione \$RestoreReq{shareDest}
    Data avvio \$startTime
    Durata \$duration min
    Numero file \$Restores[\$i]{nFiles}
    Dimensione totale \${MB}MB
    Tasso trasferimento \${MBperSec}MB/s
    Errori creazione tar \$Restores[\$i]{tarCreateErrs}
    Errori trasferimento \$Restores[\$i]{xferErrs}
    File log trasferimento Vedi, Errori

    \${h1("Elenco file/directory")}

    \$fileListStr
    File/directory originaliRipristinato su
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Dettagli archivio n. \$num per \$host"; $Lang{Archive___num_details_for__host2 } = <
    Numero \$Archives[\$i]{num}
    Richiesto da \$ArchiveReq{user}
    Data richiesta \$reqTime
    Risultato \$Archives[\$i]{result}
    Messaggio d\'errore \$Archives[\$i]{errorMsg}
    Data inizio \$startTime
    Durata \$duration\'
    Xfer log file Visualizza, Errori

    \${h1("Elenco host")}

    \$HostListStr
    HostNumero backup
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Prospetto email"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new fallita: controllare il file error_log di Apache\n"; $Lang{Wrong_user__my_userid_is___} = "Utente errato: il mio ID utente è \$> invece di \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Solo gli utenti privilegiati possono visualizzare i prospetti dei PC."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Solo gli utenti privilegiati possono arrestare o avviare un backup su" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Numero non valido: \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "Impossibile aprire il file \$file: problema di configurazione?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Solo gli utenti privilegiati possono visualizzare i file di log o di configurazione."; $Lang{Only_privileged_users_can_view_log_files} = "Solo gli utenti privilegiati possono visualizzare i file di log."; $Lang{Only_privileged_users_can_view_email_summaries} = "Solo gli utenti privilegiati possono visualizzare il prospetto delle email."; $Lang{Only_privileged_users_can_browse_backup_files} = "Solo gli utenti privilegiati possono sfogliare i file di backup" . " per l\'host \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Nome host vuoto."; $Lang{Directory___EscHTML} = "La directory \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " è vuota"; $Lang{Can_t_browse_bad_directory_name2} = "Impossibile sfogliare la director. Nome non valido:" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Solo gli utenti privilegiati possono ripristinare dei file di backup" . " per l\'host \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Nome host non valido \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "Non è stato selezionato alcun file. Andare indietro per" . " per selezionare un file."; $Lang{You_haven_t_selected_any_hosts} = "Non è stato selezionato alcun host. Andare indietro" . " per selezionarne uno."; $Lang{Nice_try__but_you_can_t_put} = "Bella mossa, man non è possibile mettere \'..\' in nessun nome di file"; $Lang{Host__doesn_t_exist} = "L\'host \${EscHTML(\$In{hostDest})} non esiste"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "Non si possiedono i permessi per ripristinare sull\'host" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Impossibile creare/aprire " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Solo gli utenti privilegiati possono ripristinare i file" . " per l\'host \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Nome host vuoto"; $Lang{Unknown_host_or_user} = "Host o utente sconosciuti \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Solo gli utenti privilegiati possono visualizzare le informazioni" . " sull\'host \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Solo gli utenti privilegiati possono visualizzare le informazioni sugli archivi."; $Lang{Only_privileged_users_can_view_restore_information} = "Solo gli utenti privilegiati possono visualizzare le informazioni di ripristino."; $Lang{Restore_number__num_for_host__does_not_exist} = "Il numero di ripristino \$num per l\'host \${EscHTML(\$host)}" . " non esiste."; $Lang{Archive_number__num_for_host__does_not_exist} = "L'archivio numero \$num per l'host \${EscHTML(\$host)}" . " non esiste."; $Lang{Can_t_find_IP_address_for} = "Impossibile trovare l\'indirizzo IP per \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < Finché non vedo \$host ad un indirizzo DHCP preciso, sarà possibile avviare questa richiesta solo da quello stesso client. EOF ######################## # ok you can do it then ######################## $Lang{Backup_requested_on_DHCP__host} = "Richiesta di backup su DHCP \$host (\$In{hostIP}) da parte di" . " \$User da \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Richiesta di backup per \$host da \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Backup arrestato/disaccodato per \$host da \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Richiesta di ripristino per l\'host \$hostDest, backup numero \$num," . " da parte di \$User da \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Archivio richiesto da parte di \$User da \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "Stato"; $Lang{PC_Summary} = "Prospetto host"; $Lang{LOG_file} = "File log"; $Lang{LOG_files} = "File log"; $Lang{Old_LOGs} = "Vecchi log"; $Lang{Email_summary} = "Prospetto email"; $Lang{Config_file} = "File configurazione"; # $Lang{Hosts_file} = "File host"; $Lang{Current_queues} = "Code correnti"; $Lang{Documentation} = "Documentazione"; #$Lang{Host_or_User_name} = "Host o nome utente:"; $Lang{Go} = "Vai"; $Lang{Hosts} = "Host"; $Lang{Select_a_host} = "Selezionare un host..."; $Lang{There_have_been_no_archives} = "

    Non ci sono state archiviazioni

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    Non è mai stato eseguito un backup per questo PC!!!

    \n"; $Lang{This_PC_is_used_by} = "
  • Questo PC è usato da \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Estrazione dei soli errori)"; $Lang{XferLOG} = "TransLOG"; $Lang{Errors} = "Errori"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <L\'ultima email inviata a \${UserLink(\$user)} è stata spedita il \$mailTime con oggetto "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <Il comando \$cmd, avviato il \$startTime, è attualmente in esecuzione per \$host. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <L\'host \$host è accodato nella coda di background (il backup comincerà a breve). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <L\'host \$host è accodato nella coda utente (il backup comincerà a breve). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <È già presente un comando per \$host nella coda dei comandi (sarà eseguito a breve). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <L\'ultimo stato è \"\$Lang->{\$StatusHost{state}}\"\$reason del \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <L\'ultimo errore è \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <I ping verso \$host sono falliti per \$StatusHost{deadCnt} volte consecutive. EOF # ----- $Lang{Prior_to_that__pings} = "Prima di questo, i ping"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr verso \$host hanno avuto successo per \$StatusHost{aliveCnt} volte consecutive. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Poiché \$host è rimasto in rete per almeno \$Conf{BlackoutGoodCnt} volte consecutive, il backup non sarà effettuato dalle \$blackoutStr. EOF $Lang{__time0_to__time1_on__days} = "\$t0 alle \$t1 di \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <I backup sono stati posticipati per \$hours ore (modifica questo numero). EOF $Lang{tryIP} = " e \$StatusHost{dhcpHostIP}"; #$Lang{Host_Inhost} = "Host \$In{host}"; $Lang{checkAll} = <  Seleziona tutto EOF $Lang{checkAllHosts} = <  Seleziona tutto EOF $Lang{fileHeader} = < Nome Tipo Modo Numero Dimensione Data modifica EOF $Lang{Home} = "Casa"; $Lang{Browse} = "Naviga backup"; $Lang{Last_bad_XferLOG} = "Ultimo TransLOG fallito"; $Lang{Last_bad_XferLOG_errors_only} = "Ultimo TransLOG fallito (solo errori)"; $Lang{This_display_is_merged_with_backup} = < Questa visualizzazione è fusa con il backup numero \$numF. EOF $Lang{Visit_this_directory_in_backup} = < Selezionare il backup che si desidera visualizzare: EOF $Lang{Restore_Summary} = < Fare clic sul numero del ripristino per maggiori dettagli. \$restoreStr
    Numero ripristino Risultato Data avvio Durata (minuti) Numero file Dimensione (MB) Numero errori tar Numero errori trasferimento

    EOF $Lang{Archive_Summary} = < Fare clic sul numero di archivio per maggiori dettagli. \$ArchiveStr
    Numero archivio Risultato Data avvio Durata minuti

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Documentazione"; $Lang{No} = "no"; $Lang{Yes} = "sì"; $Lang{The_directory_is_empty} = <La directory \$dirDisplay è vuota EOF #$Lang{on} = "acceso"; $Lang{off} = "spento"; $Lang{backupType_full} = "comp."; $Lang{backupType_incr} = "incr."; $Lang{backupType_partial} = "parziale"; $Lang{failed} = "fallito"; $Lang{success} = "eseguito"; $Lang{and} = "e"; # ------ # Hosts states and reasons $Lang{Status_idle} = "inattivo"; $Lang{Status_backup_starting} = "avvio backup"; $Lang{Status_backup_in_progress} = "backup in esecuzione"; $Lang{Status_restore_starting} = "avvio ripristino"; $Lang{Status_restore_in_progress} = "restore in esecuzione"; $Lang{Status_link_pending} = "collegamenti pendenti"; $Lang{Status_link_running} = "collegamenti in esecuzione"; $Lang{Reason_backup_done} = "backup eseguito"; $Lang{Reason_restore_done} = "restore eseguito"; $Lang{Reason_archive_done} = "archivio eseguito"; $Lang{Reason_nothing_to_do} = "nulla da fare"; $Lang{Reason_backup_failed} = "backup fallito"; $Lang{Reason_restore_failed} = "restore fallito"; $Lang{Reason_archive_failed} = "archivio fallito"; $Lang{Reason_no_ping} = "no ping"; $Lang{Reason_backup_canceled_by_user} = "backup annullato dall\'utente"; $Lang{Reason_restore_canceled_by_user} = "ripristino annullato dall\'utente"; $Lang{Reason_archive_canceled_by_user} = "archivio annullato dall\'utente"; $Lang{Disabled_OnlyManualBackups} = "auto disabilitato"; $Lang{Disabled_AllBackupsDisabled} = "disabilitato"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: nessun backup riuscito per \$host"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain Subject: $subj $headers Ciao $userName, Il nostro software di backup non e` ancora riuscito ad effettuare un backup del tuo PC ($host). I backup dei PC dovrebbero essere eseguiti automaticamente quando il tuo PC e` connesso alla rete. E` necessario richiedere il supporto tecnico nel caso in cui: - il tuo PC sia stato connesso regolarmente alla rete, nel qual caso potrebbe sussistere un problema di configurazione o impostazione che impedisce l'esecuzione del backup; - non si desideri che sia eseguito il backup del proprio PC e che questo messaggio non sia piu` inviato. In caso contrario, assicurati che il tuo PC sia connesso alla rete la prossima volta che sei in ufficio. Ciao. BackupPC Genie http://backuppc.sourceforge.net/ EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: non ci sono backup recenti per \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain Subject: $subj $headers Ciao $userName, non e` stato effettuato correttamente il backup del tuo PC ($host) per $days giorni. Dal $firstTime fino a $days fa sono stati eseguiti con successo $numBackups backup. I backup dei PC dovrebbero avvenire automaticamente quando il tuo PC e` connesso alla rete. Se il tuo PC e` rimasto connesso alla rete solo per qualche ora durante gli ultimi $days giorni, dovresti contattare l'amministratore di sistema per capire perche' i backup non sono stati effettuati. In caso contrario, se sei fuori ufficio, non c'e` molto che tu possa fare, se non copiare manualmente i file particolarmente critici su un altro dispositivo. Tieni presente che qualsiasi file creato o modificato negli ultimi $days giorni (compresi i nuovi messaggi di posta elettronica e gli allegati) non possono essere ripristinato se il tuo PC si guasta. Ciao. BackupPC Genie http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: i file di Outlook su \$host richiedono un backup"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain Subject: $subj $headers Ciao $userName, Il backup dei file di Outlook presenti sul tuo PC $howLong. Questi file contengono tutti i tuoi messaggi di posta elettronica, gli allegati, i contatti e gli appuntamenti. Il backup del tuo PC e` stato effettuato correttamente $numBackups volte, a partire dal $firstTime fino a $lastTime giorni fa. Outlook, pero`, blocca tutti i suoi file quando e` in esecuzione, impedendo di fatto il backup dei suoi file. Ti consiglio di effettuare il backup dei file di Outlook quando sei collegato alla rete. E` sufficiente uscire da Outlook e da tutte le altre applicazioni e, semplicemente usando il tuo programma di navigazione, andare alla seguente pagina: $CgiURL?host=$host Seleziona "Avvia backup incrementale" due volte per avviare un nuovo backup incrementale. E` possibile selezionare "Ritorna alla pagina di $host" e quindi cliccare su "ricarica" per controllare lo stato del backup. Il backup dovrebbe essere pronto entro pochi minuti. Ciao. BackupPC Genie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "non e` riuscito"; $Lang{howLong_not_been_backed_up_for_days_days} = "risale a \$days giorni fa"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPC Server"; $Lang{RSS_Doc_Description} = "RSS feed for BackupPC"; $Lang{RSS_Host_Summary} = < Nota: Se vuoi modificare un valore specifico per questo host, seleziona Sovrascrivi.

    EOF $Lang{CfgEdit_Button_Save} = "Salva"; $Lang{CfgEdit_Button_Insert} = "Inserisci"; $Lang{CfgEdit_Button_Delete} = "Cancella"; $Lang{CfgEdit_Button_Add} = "Aggiungi"; $Lang{CfgEdit_Button_Override} = "Sovrascrivi"; $Lang{CfgEdit_Button_New_Key} = "Nuova chiave"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "Errore: Non salvo poiché sono presenti errori"; $Lang{CfgEdit_Error__must_be_an_integer} = "Errore: \$var deve essere un numero intero"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Errore: \$var deve esser un numero con un valore reale"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Errore: \$var entry \$k deve essere un numero intero"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Errore: \$var entry \$k deve esser un numero con un valore reale"; $Lang{CfgEdit_Error__must_be_executable_program} = "Errore: \$var deve essere un percorso valido"; $Lang{CfgEdit_Error__must_be_valid_option} = "Errore: \$var deve essere una opzione valida"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "L\'host da copiare \$copyHost non esiste; creo dunque l\'host \$fullHost da zero. Se non è quello che desideravi, cancella questo host."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User ha copiato la configurazione di \$fromHost su \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User ha cancellato \$p da \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User ha aggiunto \$p a \$conf, settandolo a \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User ha cambiato \$p su \$conf in \$valueNew da \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User ha cancellato l\'host \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User host \$host ha cambiato \$key da \$valueOld in \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User ha aggiunto l\'host \$host: \$value\n"; BackupPC-3.3.2/lib/BackupPC/Lang/ja.pm0000444000076500000240000016132313042250554016132 0ustar craigstaff#!/usr/bin/perl #my %lang; #use strict; use utf8; # -------------------------------- $Lang{Start_Archive} = "アーカイブ開始"; $Lang{Stop_Dequeue_Archive} = "ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–åœæ­¢/デキュー"; $Lang{Start_Full_Backup} = "フルãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—é–‹å§‹"; $Lang{Start_Incr_Backup} = "増分ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—é–‹å§‹"; $Lang{Stop_Dequeue_Backup} = "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—åœæ­¢/デキュー"; $Lang{Restore} = "リストア"; $Lang{Type_full} = "フル"; $Lang{Type_incr} = "インクリメンタル"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "管ç†è€…ã‚ªãƒ—ã‚·ãƒ§ãƒ³ã¯æ¨©é™ãŒã‚るユーザã®ã¿è¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"; $Lang{H_Admin_Options} = "BackupPCサーãƒ: 管ç†è€…オプション"; $Lang{Admin_Options} = "管ç†è€…オプション"; $Lang{Admin_Options_Page} = < \${h2("サーãƒç®¡ç†")}

    サーãƒè¨­å®šã®å†èª­è¾¼:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "BackupPCサーãƒã¸æŽ¥ç¶šã§ãã¾ã›ã‚“"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < エラー内容: \$err.
    BackupPCãŒèµ·å‹•ã—ã¦ã„ãªã„ã‹ã€è¨­å®šã‚¨ãƒ©ãƒ¼ãŒã‚ã‚‹ã¨æ€ã‚れã¾ã™ã€‚ 本件をシステム管ç†è€…ã¸å ±å‘Šã—ã¦ãã ã•ã„。 EOF $Lang{Admin_Start_Server} = < ホスト\$Conf{ServerHost} ãƒãƒ¼ãƒˆ\$Conf{ServerPort} ã®BackupPCサーãƒã¯èµ·å‹•ã—ã¦ã„ã¾ã›ã‚“(ã¡ã‚‡ã†ã©åœæ­¢ã—ã¦ã„ã‚‹ã‹ã€ã¾ã èµ·å‹•ã—ã¦ã„ãªã„ã¨æ€ã‚れã¾ã™)。
    é–‹å§‹ã—ã¾ã™ã‹ï¼Ÿ EOF # ----- $Lang{H_BackupPC_Server_Status} = "BackupPCサーãƒã®çŠ¶æ…‹"; $Lang{BackupPC_Server_Status_General_Info}= <
  • サーãƒã®PID㯠\$Info{pid} ã§ã™ã€‚ \$Conf{ServerHost} ホスト上ã§å‹•作ã—ã¦ã„ã¾ã™ã€‚ ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯ \$Info{Version}〠\$serverStartTime ã«é–‹å§‹ã—ã¦ã„ã¾ã™ã€‚
  • ã“ã®ã‚µãƒ¼ãƒçŠ¶æ…‹ã¯ \$now ç¾åœ¨ã®ã‚‚ã®ã§ã™ã€‚
  • 最後ã«è¨­å®šãŒèª­ã¿è¾¼ã¾ã‚ŒãŸã®ã¯ \$configLoadTime ã§ã™ã€‚
  • 次ã®ã‚­ãƒ¥ãƒ¼ã‚¤ãƒ³ã‚°ã¯ \$nextWakeupTime ã®äºˆå®šã§ã™ã€‚
  • ä»–ã®æƒ…å ±:
    • 最後ã«ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã•れãŸèµ·å‹•ã‹ã‚‰ä¿ç•™ä¸­ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—è¦æ±‚: \$numBgQueue
    • ä¿ç•™ä¸­ã®ãƒ¦ãƒ¼ã‚¶ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—è¦æ±‚: \$numUserQueue
    • ä¿ç•™ä¸­ã®ã‚³ãƒžãƒ³ãƒ‰è¦æ±‚: \$numCmdQueue \$poolInfo
    • プールファイルシステム㯠\$Info{DUlastValue}% (\$DUlastTime ç¾åœ¨)ã€ä»Šæ—¥ã®æœ€å¤§å€¤ã¯ \$Info{DUDailyMax}% (\$DUmaxTime)ã€ æ˜¨æ—¥ã®æœ€å¤§å€¤ã¯ \$Info{DUDailyMaxPrev}%。
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("ç¾åœ¨å®Ÿè¡Œä¸­ã®ã‚¸ãƒ§ãƒ–")}

    \$jobStr
    ホスト 種別 ユーザ 開始時間 コマンド PID è»¢é€ PID

    \${h2("注æ„ã™ã‚‹å¿…è¦ãŒã‚る失敗")}

    \$statusStr
    ホスト 種別 ユーザ 最終試行 詳細 エラー時刻 最終エラー (無応答以外)
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: ホストサマリ"; $Lang{BackupPC__Archive} = "BackupPC: アーカイブ"; $Lang{BackupPC_Summary} = <

    • ã“ã®è¡¨ç¤ºå†…容㯠\$now ã«æ›´æ–°ã•れãŸã‚‚ã®ã§ã™ã€‚
    • プールファイルシステム㯠\$Info{DUlastValue}% (\$DUlastTime ç¾åœ¨)ã€ä»Šæ—¥ã®æœ€å¤§å€¤ã¯ \$Info{DUDailyMax}% (\$DUmaxTime)ã€ æ˜¨æ—¥ã®æœ€å¤§å€¤ã¯ \$Info{DUDailyMaxPrev}%。

    \${h2("ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ãŒå­˜åœ¨ã™ã‚‹ãƒ›ã‚¹ãƒˆ")}

    \$hostCntGood 個ã®ãƒ›ã‚¹ãƒˆã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ãŒå­˜åœ¨ã—ã¾ã™ã€‚

    • \$fullTot 個ã®ãƒ•ルãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã®åˆè¨ˆã‚µã‚¤ã‚º \${fullSizeTot}GB (以å‰ã®ãƒ—ーリングã¨åœ§ç¸®)
    • \$incrTot 個ã®å¢—分ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã®åˆè¨ˆã‚µã‚¤ã‚º \${incrSizeTot}GB (以å‰ã®ãƒ—ーリングã¨åœ§ç¸®)

    \$strGood
    ホスト ユーザ フル フル世代 (日数) フルサイズ (GB) 速度(MB/s) 増分 å¢—åˆ†çµŒéŽ (日数) 最終ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ— (日数) 状態 #転é€ã‚¨ãƒ©ãƒ¼ 最終試行


    \${h2("ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ãŒå­˜åœ¨ã—ãªã„ホスト")}

    \$hostCntNone 個ã®ãƒ›ã‚¹ãƒˆã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ãŒå­˜åœ¨ã—ã¾ã›ã‚“。

    \$strNone
    ホスト ユーザ #フル フル世代(æ—¥) フルサイズ(GB) 速度(MB/s) #増分 増分(æ—¥) 最終ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—(æ—¥) 状態 #転é€ã‚¨ãƒ©ãƒ¼ 最終試行
    EOF $Lang{BackupPC_Archive} = < \$hostCntGood ホストã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—済ã®åˆè¨ˆã‚µã‚¤ã‚º \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    ホスト ユーザ ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—サイズ

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Archive Location/Device EOF $Lang{BackupPC_Archive2_compression} = < 圧縮 ãªã—
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < パリティデータã®å‰²åˆ (0 = 無効, 5 = 一般) EOF $Lang{BackupPC_Archive2_split} = < 出力を次ã¸åˆ†é›¢ Mãƒã‚¤ãƒˆ EOF # ----------------------------------- $Lang{Pool_Stat} = <プール㯠\$info->{"\${name}FileCnt"}ファイル㨠\$info->{"\${name}DirCnt"}ディレクトリ(\$poolTime 時点)ã‚’å«ã‚€ã€\${poolSize}GBã®ã‚µã‚¤ã‚ºãŒã‚りã¾ã™ã€‚
  • プールã®ãƒãƒƒã‚·ãƒ³ã‚°ã¯æœ€é•· \$info->{"\${name}FileRepMax"} ã® \$info->{"\${name}FileCntRep"} ã®ç¹°ã‚Šè¿”ã™ãƒ•ァイルをæä¾›ã—ã¾ã™ã€‚
  • \$poolTime ã®å¤œé–“ã®ã‚¯ãƒªãƒ¼ãƒ³ã‚¢ãƒƒãƒ—ã«ã‚ˆã£ã¦åˆè¨ˆ\${poolRmSize}GBã® \$info->{"\${name}FileCntRm"} 個ã®ãƒ•ァイル㌠削除ã•れã¾ã—ãŸã€‚ EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: \$host ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—è¦æ±‚"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < サーãƒã‹ã‚‰ã®è¿”ä¿¡: \$reply

    \$host ãƒ›ãƒ¼ãƒ ãƒšãƒ¼ã‚¸ã¸æˆ»ã‚‹. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—é–‹å§‹ã®ç¢ºèª \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < \$host ã® \$type ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã‚’é–‹å§‹ã—ã¾ã™ã€‚

    本当ã«å®Ÿè¡Œã—ã¦ã‚ˆã„ã§ã™ã‹ï¼Ÿ
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—åœæ­¢ã®ç¢ºèª \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < \$host ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã®ä¸­æ­¢/デキューをã—ã¾ã™ã€‚
    ãªãŠã€ 時間ã€ä»–ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã‚’é–‹å§‹ã—ãªã„ã§ãã ã•ã„。

    本当ã«å®Ÿè¡Œã—ã¦ã‚ˆã„ã§ã™ã‹ï¼Ÿ

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "権é™ãŒã‚るユーザã®ã¿ã‚­ãƒ¥ãƒ¼ã‚’見るã“ã¨ãŒã§ãã¾ã™ã€‚"; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "権é™ãŒã‚るユーザã®ã¿ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: キューサマリ"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("ユーザキューサマリ")}

    ç¾åœ¨ã‚­ãƒ¥ãƒ¼ã‚¤ãƒ³ã‚°ã•れã¦ã„ã‚‹ãƒ¦ãƒ¼ã‚¶è¦æ±‚ã¯æ¬¡ã®ã¨ãŠã‚Šã§ã™ã€‚

    \$strUser
    ホスト è¦æ±‚時間 ユーザ


    \${h2("ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã‚­ãƒ¥ãƒ¼ã‚µãƒžãƒª")}

    ç¾åœ¨ã‚­ãƒ¥ãƒ¼ã‚¤ãƒ³ã‚°ã•れã¦ã„ã‚‹ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰è¦æ±‚ã¯æ¬¡ã®ã¨ãŠã‚Šã§ã™ã€‚

    \$strBg
    ホスト è¦æ±‚時間 ユーザ


    \${h2("コマンドキューサマリ")}

    ç¾åœ¨ã‚­ãƒ¥ãƒ¼ã‚¤ãƒ³ã‚°ã•れã¦ã„ã‚‹ã‚³ãƒžãƒ³ãƒ‰è¦æ±‚ã¯æ¬¡ã®ã¨ãŠã‚Šã§ã™ã€‚

    \$strCmd
    ホスト è¦æ±‚時間 ユーザ コマンド
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: ファイル \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$fileã®å†…容 \$mtimeStr æ›´æ–° \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ \$skipped 行スキップã—ã¾ã—ãŸã€‚ ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \n\$file ログファイルを開ãã“ã¨ãŒã§ãã¾ã›ã‚“。\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: ログファイルã®å±¥æ­´";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    ファイル サイズ 更新時間
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    å—信者 ホスト 日時 ä»¶å
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: \$host \$num ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã®é–²è¦§"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: \$host リストアオプション"; $Lang{Restore_Options_for__host2} = < 共有 \$share ã‹ã‚‰æ¬¡ã®ãƒ•ァイル/ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’é¸æŠžã—ã¦ã„ã¾ã™ã€‚ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ç•ªå· #\$num:
      \$fileListStr

    ã“れらファイル/ディレクトリã®ãƒªã‚¹ãƒˆã‚¢æ–¹æ³•を3ã¤ã®ä¸­ã‹ã‚‰é¸ã¶ã“ã¨ãŒã§ãã¾ã™ã€‚ 次ã®ä¸­ã‹ã‚‰é¸æŠžã—ã¦ãã ã•ã„。

    \${h2("オプション1: ダイレクトリストア")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHostã¸ç›´æŽ¥ã“れらã®ãƒ•ァイルをリストアã—ã¾ã™ã€‚

    警告: 既存ã®ãƒ•ァイルã¯é¸æŠžã—ãŸã“れらã®ãƒ•ァイルã§ä¸Šæ›¸ãã•れã¾ã™ã€‚

    \$hiddenStr
    ホストã¸ãƒ•ァイルをリストア
    共有ã¸ãƒ•ァイルをリストア
    次ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¸ãƒ•ァイルをリストア
    (共有ã¸ã®ç›¸å¯¾)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("オプション2: Zipアーカイブã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰")}

    é¸æŠžã—ãŸãƒ•ァイル/ディレクトリをã™ã¹ã¦å«ã‚“ã ZIPアーカイブをダウンロードã—ã¾ã™ã€‚ WinZipã®ã‚ˆã†ãªãƒ­ãƒ¼ã‚«ãƒ«ã‚¢ãƒ—リケーションã§é–²è¦§ã—ãŸã‚Šå±•é–‹ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

    警告: depending upon which files/directories you have selected, this archive might be very very large. It might take many minutes to create and transfer the archive, and you will need enough local disk space to store it.

    \$hiddenStr Make archive relative to \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (otherwise archive will contain full paths).
    圧縮 (0=ãªã—, 1=高速,...,9=最高)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("オプション2: ZIPアーカイブã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰")}

    Archive::Zip ã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ãªã„ã®ã§ZIPアーカイブをダウンロードã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。 www.cpan.orgã‹ã‚‰Archive::Zipをインストールã™ã‚‹ã“ã¨ã«ã¤ã„ã¦ã€ã‚·ã‚¹ãƒ†ãƒ ç®¡ç†è€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < You can download a Tar archive containing all the files/directories you have selected. You can then use a local application, such as tar or WinZip to view or extract any of the files.

    Warning: depending upon which files/directories you have selected, this archive might be very very large. It might take many minutes to create and transfer the archive, and you will need enough local disk space to store it.

    \$hiddenStr Make archive relative to \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (otherwise archive will contain full paths).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: \$host リストアã®ç¢ºèª"; $Lang{Are_you_sure} = < You are about to start a restore directly to the machine \$In{hostDest}. The following files will be restored to share \$In{shareDest}, from ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ç•ªå· \$num:

    \$fileListStr
    å…ƒã®ãƒ•ァイル/ディレクトリ次ã®å ´æ‰€ã«ãƒªã‚¹ãƒˆã‚¢ã•れã¾ã™ã€‚

    \$hiddenStr 本当ã«å®Ÿè¡Œã—ã¦ã‚ˆã„ã§ã™ã‹ï¼Ÿ
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: \$hostDest ã¸ãƒªã‚¹ãƒˆã‚¢"; $Lang{Reply_from_server_was___reply} = < サーãƒã‹ã‚‰ã®å¿œç­”: \$reply

    \$hostDest ãƒ›ãƒ¼ãƒ ãƒšãƒ¼ã‚¸ã«æˆ»ã‚‹ EOF $Lang{BackupPC_Archive_Reply_from_server} = < サーãƒã‹ã‚‰ã®å¿œç­”: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: ホスト \$host ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—サマリ"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("ãƒ¦ãƒ¼ã‚¶ã®æ“作")}

    \$startIncrStr

    \${h2("ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—サマリ")}

    閲覧・ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ファイルã®ãƒªã‚¹ãƒˆã‚¢ã‚’行ã„ãŸã„ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—番å·ã‚’クリックã—ã¦ãã ã•ã„。

    \$str
    ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ç•ªå· ç¨®åˆ¥ フィルド レベル 開始日時 é–“éš”(分) 経éŽ(æ—¥) サーãƒãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—パス

    \$restoreStr



    \${h2("転é€ã‚¨ãƒ©ãƒ¼ã‚µãƒžãƒª")}

    \$errStr
    ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ç•ªå· ç¨®åˆ¥ ビュー #転é€ã‚¨ãƒ©ãƒ¼ #badファイル #bad共有 #tarエラー


    \${h2("ファイルサイズ/カウント å†åˆ©ç”¨ã‚µãƒžãƒª")}

    存在ã™ã‚‹ãƒ•ァイルã¯ãƒ—ール内ã«ã™ã§ã«ã‚りã¾ã™ã€‚æ¬¡ã®æ–°ã—ã„ファイルã¯ãƒ—ールã¸è¿½åŠ ã•れã¾ã™ã€‚ 空ファイルã¨SMBエラーã¯å†åˆ©ç”¨ã«ã¯ã‚«ã‚¦ãƒ³ãƒˆã•れã¾ã›ã‚“。

    \$sizeStr
    トータル 既存ファイル 新ファイル
    ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ç•ªå· ç¨®åˆ¥ #ファイル サイズ(MB) 速度(MB/sec) #ファイル サイズ(MB) #ファイル サイズ(MB)


    \${h2("圧縮サマリ")}

    ã™ã§ã«ãƒ—ールã«å…¥ã£ã¦ã„ã‚‹ã‚‚ã®ã¨æ–°ã—ã圧縮ã•れãŸãƒ•ァイルã®åœ§ç¸®ãƒ‘フォーマンス

    \$compStr
    既存ファイル 新ファイル
    ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ç•ªå· ç¨®åˆ¥ 圧縮レベル サイズ(MB) 圧縮(MB) 圧縮 サイズ(MB) 圧縮(MB) 圧縮


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: ホスト \$host アーカイブサマリ"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("ユーザæ“作")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: エラー"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "サーãƒ"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • \$backupTime ã«é–‹å§‹ã—ãŸãƒãƒƒã‚¯ã‚¢ãƒƒãƒ— #\$num (\$backupAge æ—¥å‰) を閲覧ã—ã¦ã„ã¾ã™ã€‚ \$filledBackup
    • ディレクトリを入力ã—ã¦ãã ã•ã„:
    • 移動ã—ãŸã„ディレクトリを左下ã‹ã‚‰é¸æŠžã—ã¦ãã ã•ã„
    • リストアã™ã‚‹ãƒ•ァイルをå³ä¸‹ã‹ã‚‰é¸æŠžã—ã¦ãã ã•ã„
    • ç¾åœ¨ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—履歴を見るã“ã¨ãŒã§ãã¾ã™ã€‚
    \${h2("\$dirDisplay ã®å†…容")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: \$host ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—履歴ディレクトリ"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "dir"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < å…¨ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã‚’ã¾ãŸã„ã§ãƒ•ァイルã®ãれãžã‚Œã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’表示ã—ã¾ã™ã€‚
    • ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—番å·ã‚’クリックã™ã‚‹ã“ã¨ã§ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—閲覧画é¢ã«æˆ»ã‚Šã¾ã™ã€‚
    • ディレクトリリンク(\$Lang->{DirHistory_dirLink})をクリックã™ã‚‹ã“ã¨ã§ã€ ãã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªå†…ã«ç§»å‹•ã§ãã¾ã™ã€‚
    • ファイルã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãƒªãƒ³ã‚¯(\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...)をクリックã™ã‚‹ã“ã¨ã§ã€ãã®ãƒ•ァイルをダウンロードã§ãã¾ã™ã€‚
    • ç•°ãªã‚‹ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—é–“ã®åŒã˜å†…容ã®ãƒ•ァイルã¯åŒã˜ãƒãƒ¼ã‚¸ãƒ§ãƒ³ç•ªå·ã«ãªã‚Šã¾ã™ã€‚
    • ãã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã«å­˜åœ¨ã—ãªã„ファイルやディレクトリã«ã¤ã„ã¦ã¯ç©ºæ¬„ã«ãªã‚Šã¾ã™ã€‚
    • åŒã˜ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ•ァイルã§ã‚‚ç•°ãªã‚‹å±žæ€§ã‚’æŒã£ã¦ã„ã‚‹å ´åˆãŒã‚りã¾ã™ã€‚ ファイルã®å±žæ€§ã¯ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—番å·ã‚’é¸æŠžã™ã‚‹ã¨è¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚
    \${h2("\$dirDisplay ã®å±¥æ­´")}
    \$backupNumStr\$backupTimeStr \$fileStr
    ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—番å·
    ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—日時
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: リストア #\$num 詳細 \$host"; $Lang{Restore___num_details_for__host2} = <
    ç•ªå· \$Restores[\$i]{num}
    è¦æ±‚å…ƒ \$RestoreReq{user}
    è¦æ±‚時間 \$reqTime
    çµæžœ \$Restores[\$i]{result}
    エラーメッセージ \$Restores[\$i]{errorMsg}
    元ホスト \$RestoreReq{hostSrc}
    å…ƒãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ç•ªå· \$RestoreReq{num}
    元共有 \$RestoreReq{shareSrc}
    リストア先 \$RestoreReq{hostDest}
    共有先 \$RestoreReq{shareDest}
    開始日時 \$startTime
    é–“éš” \$duration min
    ファイル数 \$Restores[\$i]{nFiles}
    åˆè¨ˆã‚µã‚¤ã‚º \${MB} MB
    転é€çއ \$MBperSec MB/sec
    Tar作æˆã‚¨ãƒ©ãƒ¼ \$Restores[\$i]{tarCreateErrs}
    転é€ã‚¨ãƒ©ãƒ¼ \$Restores[\$i]{xferErrs}
    転é€ãƒ­ã‚°ãƒ•ァイル ビュー, エラー

    \${h1("ファイル/ディレクトリ一覧")}

    \$fileListStr
    å…ƒã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª/ファイルリストア
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: アーカイブ #\$num 詳細 \$host"; $Lang{Archive___num_details_for__host2 } = <
    ç•ªå· \$Archives[\$i]{num}
    è¦æ±‚å…ƒ \$ArchiveReq{user}
    è¦æ±‚時間 \$reqTime
    çµæžœ \$Archives[\$i]{result}
    エラーメッセージ \$Archives[\$i]{errorMsg}
    開始日時 \$startTime
    é–“éš” \$duration min
    転é€ãƒ­ã‚°ãƒ•ァイル ビュー, エラー

    \${h1("ホスト一覧")}

    \$HostListStr
    ホストãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—番å·
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: メールサマリ"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new failed: check apache error_log\n"; $Lang{Wrong_user__my_userid_is___} = "Wrong user: my userid is \$>, instead of \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Only privileged users can view PC summaries."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Only privileged users can stop or start backups on" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "ç•ªå· \${EscHTML(\$In{num})} ãŒä¸æ­£ã§ã™ã€‚"; $Lang{Unable_to_open__file__configuration_problem} = "Unable to open \$file: configuration problem?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Only privileged users can view log or config files."; $Lang{Only_privileged_users_can_view_log_files} = "Only privileged users can view log files."; $Lang{Only_privileged_users_can_view_email_summaries} = "Only privileged users can view email summaries."; $Lang{Only_privileged_users_can_browse_backup_files} = "Only privileged users can browse backup files" . " ホスト \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "ホストåãŒç©ºã§ã™ã€‚"; $Lang{Directory___EscHTML} = "ディレクトリ \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " ã¯ç©ºã§ã™ã€‚"; $Lang{Can_t_browse_bad_directory_name2} = "Can\'t browse bad directory name" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Only privileged users can restore backup files" . " for host \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "\${EscHTML(\$host)} ã¯ãƒ›ã‚¹ãƒˆåãŒèª¤ã£ã¦ã„ã¾ã™ã€‚"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "ä½•ã‚‚ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ã„ã¾ã›ã‚“。戻ã£ã¦" . "ã„ãã¤ã‹ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„。"; $Lang{You_haven_t_selected_any_hosts} = "ä½•ã‚‚ãƒ›ã‚¹ãƒˆã‚’é¸æŠžã—ã¦ã„ã¾ã›ã‚“。戻ã£ã¦" . "ã„ãã¤ã‹ã®ãƒ›ã‚¹ãƒˆã‚’é¸æŠžã—ã¦ãã ã•ã„。"; $Lang{Nice_try__but_you_can_t_put} = "Nice try, but you can\'t put \'..\' in any of the file names"; $Lang{Host__doesn_t_exist} = "Host \${EscHTML(\$In{hostDest})} doesn\'t exist"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "You don\'t have permission to restore onto host" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Can\'t open/create " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Only privileged users can restore backup files" . " for host \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "ホストåãŒç©ºã§ã™ã€‚"; $Lang{Unknown_host_or_user} = "Unknown host or user \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Only privileged users can view information about" . " host \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Only privileged users can view archive information."; $Lang{Only_privileged_users_can_view_restore_information} = "Only privileged users can view restore information."; $Lang{Restore_number__num_for_host__does_not_exist} = "Restore number \$num for host \${EscHTML(\$host)} does" . " not exist."; $Lang{Archive_number__num_for_host__does_not_exist} = "Archive number \$num for host \${EscHTML(\$host)} does" . " not exist."; $Lang{Can_t_find_IP_address_for} = "Can\'t find IP address for \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < Until I see \$host at a particular DHCP address, you can only start this request from the client machine itself. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—è¦æ±‚ on DHCP \$host (\$In{hostIP}) by" . " \$User from \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "\$User ã«ã‚ˆã‚‹ \$host ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—è¦æ±‚"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "\$User ã«ã‚ˆã‚‹ \$host ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—中止/デキュー"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "ホスト\$hostDest ã®ãƒªã‚¹ãƒˆã‚¢è¦æ±‚ ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ— #\$num," . " by \$User from \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "\$ENV{REMOTE_ADDR} ã‹ã‚‰ \$User ã«ã‚ˆã£ã¦ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ã®è¦æ±‚ãŒã‚りã¾ã—ãŸã€‚"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "状態"; $Lang{PC_Summary} = "ホストサマリ"; $Lang{LOG_file} = "ログファイル"; $Lang{LOG_files} = "全ログファイル"; $Lang{Old_LOGs} = "旧ログ"; $Lang{Email_summary} = "メールサマリ"; $Lang{Config_file} = "設定ファイル"; # $Lang{Hosts_file} = "ホストファイル"; $Lang{Current_queues} = "ç¾åœ¨ã®ã‚­ãƒ¥ãƒ¼"; $Lang{Documentation} = "文章"; #$Lang{Host_or_User_name} = "ホストã¾ãŸã¯ãƒ¦ãƒ¼ã‚¶å:"; $Lang{Go} = "実行"; $Lang{Hosts} = "ホスト"; $Lang{Select_a_host} = "ãƒ›ã‚¹ãƒˆã‚’é¸æŠž"; $Lang{There_have_been_no_archives} = "

    アーカイブã¯ã‚りã¾ã›ã‚“

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    ã“ã®PCã¯ã¾ã ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã•れãŸã“ã¨ãŒã‚りã¾ã›ã‚“!!

    \n"; $Lang{This_PC_is_used_by} = "
  • ã“ã®PC㯠\${UserLink(\$user)} ã«ã‚ˆã£ã¦ä½¿ç”¨ã•れã¦ã„ã¾ã™"; $Lang{Extracting_only_Errors} = "(エラーã ã‘抽出)"; $Lang{XferLOG} = "転é€ãƒ­ã‚°"; $Lang{Errors} = "エラー"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <最後ã®ãƒ¡ãƒ¼ãƒ«ã¯ \$mailTime ã«ä»¶å"\$subj"ã§\${UserLink(\$user)}å®›ã«é€ã‚Šã¾ã—ãŸã€‚ EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <\$startTime ã«é–‹å§‹ã•れãŸã‚³ãƒžãƒ³ãƒ‰ \$cmd ã¯ç¾åœ¨ \$host ã§ã¯å®Ÿè¡Œã•れã¦ã„ã¾ã›ã‚“。 EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <ホスト \$host ã¯ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã‚­ãƒ¥ãƒ¼ã«ã‚­ãƒ¥ãƒ¼ã‚¤ãƒ³ã‚°ã•れã¾ã—ãŸ(ã‚‚ã†å°‘ã—ã§ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã•れã¾ã™)。 EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <ホスト \$host ã¯ãƒ¦ãƒ¼ã‚¶ã‚­ãƒ¥ãƒ¼ã«ã‚­ãƒ¥ãƒ¼ã‚¤ãƒ³ã‚°ã•れã¾ã—ãŸ(ã‚‚ã†å°‘ã—ã§ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã•れã¾ã™)。 EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <\$host ã¸ã®ã‚³ãƒžãƒ³ãƒ‰ã¯ã‚­ãƒ¥ãƒ¼ã‚¤ãƒ³ã‚°ã•れã¾ã—ãŸ(ã‚‚ã†å°‘ã—ã§å®Ÿè¡Œã•れã¾ã™)。 EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <最終状態 \"\$Lang->{\$StatusHost{state}}\"\$reason \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <最終エラー \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <\$StatusHost{deadCnt}回連続㧠\$host ã¯ç„¡å¿œç­”ã§ã™ã€‚ EOF # ----- $Lang{Prior_to_that__pings} = "pingã®ä»¥å‰ã¯"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$host ã¸ã®\$priorStr 㯠\$StatusHost{aliveCnt}å›žé€£ç¶šã§æˆåŠŸã—ã¦ã„ã¾ã™ã€‚ EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <\$host ã¯å°‘ãªãã¨ã‚‚ \$Conf{BlackoutGoodCnt} 回連続ã—ã¦ã€ \$blackoutStr ã‹ã‚‰ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã•れã¦ã„ã¾ã›ã‚“。 EOF $Lang{__time0_to__time1_on__days} = "\$days ã® \$t0 〜 \$t1"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Backups are deferred for \$hours hours (change this number). EOF $Lang{tryIP} = " 㨠\$StatusHost{dhcpHostIP}"; # $Lang{Host_Inhost} = "ホスト \$In{host}"; $Lang{checkAll} = <  å…¨é¸æŠž EOF $Lang{checkAllHosts} = <  å…¨é¸æŠž EOF $Lang{fileHeader} = < åå‰ ç¨®åˆ¥ モード # サイズ 更新日時 EOF $Lang{Home} = "ホーム"; $Lang{Browse} = "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã®é–²è¦§"; $Lang{Last_bad_XferLOG} = "最終失敗転é€ãƒ­ã‚°"; $Lang{Last_bad_XferLOG_errors_only} = "最終失敗転é€ãƒ­ã‚°(エラーã®ã¿)"; $Lang{This_display_is_merged_with_backup} = <ã“ã®è¡¨ç¤ºã¯ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ— #\$numF ã¨ãƒžãƒ¼ã‚¸ã•れã¦ã„ã¾ã™ã€‚ EOF $Lang{Visit_this_directory_in_backup} = < 閲覧ã—ãŸã„ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã‚’é¸æŠžã—ã¦ãã ã•ã„: EOF $Lang{Restore_Summary} = < 詳細を閲覧ã—ãŸã„リストア番å·ã‚’クリックã—ã¦ãã ã•ã„。 \$restoreStr
    ãƒªã‚¹ãƒˆã‚¢ç•ªå· çµæžœ 開始日時 é–“éš”(分) ファイル数 サイズ(MB) #tarエラー #転é€ã‚¨ãƒ©ãƒ¼

    EOF $Lang{Archive_Summary} = < アーカイブ番å·ã‚’クリックã™ã‚‹ã¨è©³ç´°ãŒç¢ºèªã§ãã¾ã™ã€‚ \$ArchiveStr
    ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–ç•ªå· çµæžœ 開始日時 é–“éš”(分)

    EOF $Lang{BackupPC__Documentation} = "BackupPC: 文章"; $Lang{No} = "ã„ã„ãˆ"; $Lang{Yes} = "ã¯ã„"; $Lang{The_directory_is_empty} = <\$dirDisplay ディレクトリã¯ç©ºã§ã™ã€‚ EOF #$Lang{on} = "オン"; $Lang{off} = "オフ"; $Lang{backupType_full} = "フル"; $Lang{backupType_incr} = "増分"; $Lang{backupType_partial} = "部分"; $Lang{failed} = "失敗"; $Lang{success} = "æˆåŠŸ"; $Lang{and} = "ã¨"; # ------ # Hosts states and reasons $Lang{Status_idle} = "待機"; $Lang{Status_backup_starting} = "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—é–‹å§‹"; $Lang{Status_backup_in_progress} = "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—中"; $Lang{Status_restore_starting} = "リストア開始"; $Lang{Status_restore_in_progress} = "リストア中"; $Lang{Status_link_pending} = "リンクä¿ç•™ä¸­"; $Lang{Status_link_running} = "リンク実行中"; $Lang{Reason_backup_done} = "完了"; $Lang{Reason_restore_done} = "リストア完了"; $Lang{Reason_archive_done} = "アーカイブ完了"; $Lang{Reason_nothing_to_do} = "待機"; $Lang{Reason_backup_failed} = "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—失敗"; $Lang{Reason_restore_failed} = "リストア失敗"; $Lang{Reason_archive_failed} = "アーカイブ失敗"; $Lang{Reason_no_ping} = "無応答"; $Lang{Reason_backup_canceled_by_user} = "ユーザã«ã‚ˆã‚‹ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—å–æ¶ˆ"; $Lang{Reason_restore_canceled_by_user} = "ユーザã«ã‚ˆã‚‹ãƒªã‚¹ãƒˆã‚¢å–消"; $Lang{Reason_archive_canceled_by_user} = "ユーザã«ã‚ˆã‚‹ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–å–æ¶ˆ"; $Lang{Disabled_OnlyManualBackups} = "自動無効化"; $Lang{Disabled_AllBackupsDisabled} = "無効化"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: \$host ã®æˆåŠŸã—ãŸãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ãŒå­˜åœ¨ã—ã¾ã›ã‚“。"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, Your PC ($host) has never been successfully backed up by our PC backup software. PC backups should occur automatically when your PC is connected to the network. You should contact computer support if: - Your PC has been regularly connected to the network, meaning there is some configuration or setup problem preventing backups from occurring. - You don't want your PC backed up and you want these email messages to stop. Otherwise, please make sure your PC is connected to the network next time you are in the office. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: no recent backups on \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, Your PC ($host) has not been successfully backed up for $days days. Your PC has been correctly backed up $numBackups times from $firstTime to $days days ago. PC backups should occur automatically when your PC is connected to the network. If your PC has been connected for more than a few hours to the network during the last $days days you should contact IS to find out why backups are not working. Otherwise, if you are out of the office, there's not much you can do, other than manually copying especially critical files to other media. You should be aware that any files you have created or changed in the last $days days (including all new email and attachments) cannot be restored if your PC disk crashes. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Outlook files on \$host need to be backed up"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, The Outlook files on your PC have $howLong. These files contain all your email, attachments, contact and calendar information. Your PC has been correctly backed up $numBackups times from $firstTime to $lastTime days ago. However, Outlook locks all its files when it is running, preventing these files from being backed up. It is recommended you backup the Outlook files when you are connected to the network by exiting Outlook and all other applications, and, using just your browser, go to this link: $CgiURL?host=$host Select "Start Incr Backup" twice to start a new incremental backup. You can select "Return to $host page" and then hit "reload" to check the status of the backup. It should take just a few minutes to complete. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "not been backed up successfully"; $Lang{howLong_not_been_backed_up_for_days_days} = "not been backed up for \$days days"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPCサーãƒ"; $Lang{RSS_Doc_Description} = "RSS feed for BackupPC"; $Lang{RSS_Host_Summary} = < 備考: ã“ã®ãƒ›ã‚¹ãƒˆç‰¹æœ‰ã®å€¤ã«æ›´æ–°ã—ãŸã„å ´åˆã¯ã€ã€Œä¸Šæ›¸ãã€ã‚’ãƒã‚§ãƒƒã‚¯ã—ã¦ãã ã•ã„。

    EOF $Lang{CfgEdit_Button_Save} = "ä¿å­˜"; $Lang{CfgEdit_Button_Insert} = "挿入"; $Lang{CfgEdit_Button_Delete} = "削除"; $Lang{CfgEdit_Button_Add} = "追加"; $Lang{CfgEdit_Button_Override} = "上書ã"; $Lang{CfgEdit_Button_New_Key} = "æ–°é …ç›®"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "エラー: エラーã®ãŸã‚ã«ä¿å­˜ã•れã¦ã¾ã›ã‚“"; $Lang{CfgEdit_Error__must_be_an_integer} = "エラー: \$var ã¯æ•´æ•°ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "エラー: \$var ã¯å®Ÿåœ¨ã™ã‚‹ç•ªå·ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "エラー: \$var エントリー \$k ã¯æ•´æ•°ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "エラー: \$var エントリー \$k ã¯å®Ÿåœ¨ã™ã‚‹ç•ªå·ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™"; $Lang{CfgEdit_Error__must_be_executable_program} = "エラー: \$var ã¯æœ‰åйãªå®Ÿè¡Œå¯èƒ½ãªãƒ‘スã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™"; $Lang{CfgEdit_Error__must_be_valid_option} = "エラー: \$var ã¯æœ‰åйãªã‚ªãƒ—ションã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "\$copyHost ã®ã‚³ãƒ”ーãŒå­˜åœ¨ã—ã¾ã›ã‚“。 creating full host name \$fullHost. Delete this host if that is not what you wanted."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User copied config from host \$fromHost to \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User deleted \$p from \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User added \$p to \$conf, set to \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User changed \$p in \$conf to \$valueNew from \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User deleted host \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User host \$host changed \$key from \$valueOld to \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User added host \$host: \$value\n"; #end of lang_ja.pm BackupPC-3.3.2/lib/BackupPC/Lang/nl.pm0000444000076500000240000016030313042250554016146 0ustar craigstaff#!/usr/bin/perl #my %lang; #use strict; #File: nl.pm version 1.5 # -------------------------------- $Lang{Start_Archive} = "Start Archivering"; $Lang{Stop_Dequeue_Archive} = "Stop/Annuleer Archivering"; $Lang{Start_Full_Backup} = "Start volledige backup"; $Lang{Start_Incr_Backup} = "Start incrementele backup"; $Lang{Stop_Dequeue_Backup} = "Stop/Annuleer backup"; $Lang{Restore} = "Herstellen"; $Lang{Type_full} = "volledig"; $Lang{Type_incr} = "incrementeel"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Alleen gebruikers met bijzondere rechten kunnen admin.-opties bekijken."; $Lang{H_Admin_Options} = "BackupPC Server: Admin Opties"; $Lang{Admin_Options} = "Admin Opties"; $Lang{Admin_Options_Page} = < \${h2("Besturing van de server")}

    Herlaad de configuratie van de server:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Verbinding met de BackupPC server is mislukt"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < De foutmelding was: \$err.
    Mogelijk draait de BackupPC server niet of is er een configuratiefout. Gelieve dit te melden aan uw systeembeheerder. EOF $Lang{Admin_Start_Server} = < De BackupPC-server op \$Conf{ServerHost} poort \$Conf{ServerPort} werkt momenteel niet (misschien hebt u hem juist gestopt, of nog niet gestart).
    Wilt u de server nu starten? EOF # ----- $Lang{H_BackupPC_Server_Status} = "Overzicht BackupPC Server"; $Lang{BackupPC_Server_Status_General_Info}= <
  • De PID (procesidentificatie) van de server is \$Info{pid}, op machine \$Conf{ServerHost}, versie \$Info{Version}, gestart op \$serverStartTime.
  • Dit overzicht werd gemaakt op \$now.
  • De configuratie werd het laatst ingelezen op \$configLoadTime.
  • Volgende backupsessie start op \$nextWakeupTime.
  • Andere informatie:
    • \$numBgQueue wachtende backupaanvragen sinds laatste geplande wakeup,
    • \$numUserQueue wachtende backupaanvragen van gebruikers,
    • \$numCmdQueue wachtende opdrachten, \$poolInfo
    • Het backup filesystem werd recentelijk aangevuld voor \$Info{DUlastValue}% op (\$DUlastTime), het maximum van vandaag is \$Info{DUDailyMax}% (\$DUmaxTime) en het maximum van gisteren was \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Momenteel lopende jobs")}

    \$jobStr
    Machine Type Gebruiker Starttijd Opdracht PID PID vd overdracht

    \${h2("Opgetreden fouten die aandacht vragen")}

    \$statusStr
    Machine Type Gebruiker Laatste poging Details Fouttijd Laatste fout (verschillend van 'geen ping')
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: Overzicht machines"; $Lang{BackupPC__Archive} = "BackupPC: Archivering"; $Lang{BackupPC_Summary}=<

    • Dit overzicht dateert van \$now.
    • Het backup filesystem werd recentelijk aangevuld voor \$Info{DUlastValue}% op (\$DUlastTime), het maximum van vandaag is \$Info{DUDailyMax}% (\$DUmaxTime) en het maximum van gisteren was \$Info{DUDailyMaxPrev}%.

    \${h2("Machine(s) met geslaagde backups")}

    Er zijn \$hostCntGood hosts gebackupt, wat een totaal geeft van:

    • \$fullTot volledige backups met een totale grootte van \${fullSizeTot}GB (voor samenvoegen),
    • \$incrTot oplopende backups met een totale grootte van \${incrSizeTot}GB (voor samenvoegen).

    \$strGood
    Machine Gebruiker Aantal Voll. Voll.Lftd (dagen) Voll.Grootte (GB) Snelheid (MB/sec) Aantal Incr. Incr.Lftd (dagen) Vorige Backup (dagen) Status Aantal fouten Laatste poging


    \${h2("Hosts zonder backups")}

    Er zijn \$hostCntNone hosts zonder backup.

    \$strNone
    Machine Gebruiker Aantal Voll. Voll.Lftd (dagen) Voll.Grootte (GB) Snelheid (MB/sec) Aantal Incr. Incr.Lftd (dagen) Vorige Backup (dagen) Status Aantal fouten Laatste poging
    EOF $Lang{BackupPC_Archive} = < Er zijn \$hostCntGood machines gebackupt die een totale grootte vertegenwoordigen van \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    Machine Gebruiker Backupgrootte

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Plaats van archivering /device EOF $Lang{BackupPC_Archive2_compression} = < Compressie Geen
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Pariteitspercentage (0 = geen, 5 = standaard) EOF $Lang{BackupPC_Archive2_split} = < Opdelen (splitsen) in Megabytes EOF # ----------------------------------- $Lang{Pool_Stat} = <Gebruikte backupschijfruimte is \${poolSize}GB groot en bevat \$info->{"\${name}FileCnt"} bestanden en \$info->{"\${name}DirCnt"} mappen (op \$poolTime),
  • Schijfruimte bevat \$info->{"\${name}FileCntRep"} bestanden met identieke hashcodes (langste reeks is \$info->{"\${name}FileRepMax"},
  • Nachtelijke opruiming verwijderde \$info->{"\${name}FileCntRm"} bestanden met een grootte van \${poolRmSize}GB (ongeveer \$poolTime), EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: backup aangevraagd van \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < Antwoord van server was: \$reply

    Terug naar \$host hoofdpagina. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Bevestiging start van de backup van \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Met deze actie start u een \$type backup van machine \$host.

    Wilt u dat nu doen?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Bevestiging de annulering van de backup van \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < Met deze actie annuleert u de backup van pc \$host of haalt u de opdracht uit de wachtrij;
    Start bovendien geen andere backup gedurende uur/uren.

    Wilt u dit nu bevestigen?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Enkel gebruikers met bijzondere rechten kunnen de wachtrij bekijken."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Enkel gebruikers met bijzondere rechten kunnen archiveren."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: overzicht wachtrij"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Overzicht Wachtrij: Gebruikers")}

    Deze aanvragen van gebruikers staan momenteel in de wachtrij:

    \$strUser
    Machine Aanvraagtijd Gebruiker


    \${h2("Overzicht Wachtrij: in achtergrond")}

    Deze aanvragen voor backups in de achtergrond staan momenteel in de wachtrij:

    \$strBg
    Machine Aanvraagtijd Gebruiker


    \${h2("Overzicht Wachtrij: Opdrachten")}

    Deze aanvragen via opdracht staan momenteel in de wachtrij:

    \$strCmd
    Machine Aanvraagtijd Gebruiker Opdracht
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: Bestand \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, gewijzigd \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ \$skipped regels overgeslagen ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nKan het logbestand \$file niet openen \n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: Geschiedenis Logbestand";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    Bestand Grootte Laatste wijziging
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Bestemming Machine Tijd Onderwerp
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Overzicht backup nummer \$num van pc \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Opties voor het herstellen van bestanden van machine \$host"; $Lang{Restore_Options_for__host2} = < U hebt de volgende bestanden/mappen geselecteerd uit \$share, backup nummer #\$num:
      \$fileListStr

    Er zijn drie mogelijkheden om deze bestanden/mappen terug te herstellen. Gelieve een van de onderstaande mogelijkheden te kiezen.

    \${h2("Optie 1: Rechtstreeks herstellen")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    Waarschuwing: bestaande bestanden met dezelfde naam zullen overschreven worden!

    \$hiddenStr
    Zet de bestanden terug naar de pc
    Plaats de bestanden terug in de map (share)
    Plaats de bestanden onder de map
    (relatief tov share)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Optie 2: Download een Zip-bestand")}

    U kan een Zip-bestand downloaden dat al de bestanden/mappen bevat die u hebt geselecteerd. U kan dan een applicatie op uw pc, zoals WinZip, gebruiken om de bestanden te bekijken of uit te pakken.

    Waarschuwing: Afhankelijk van welke bestanden u geselecteerd hebt, kan dit zip-bestand zeer zeer groot zijn. Het kan meerdere minuten duren om dit bestand aan te maken en het te downloaden. Uw pc moet ook over voldoende harde schijfruimte beschikken om het bestand te kunnen bevatten.

    \$hiddenStr Maak het zip-archief relatief aan \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (in het andere geval zal het archiefbestand volledige padnamen bevatten).
    Compressie (0=uit, 1=snel,...,9=hoogst)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Optie 2: Download Zip-bestand")}

    Archive::Zip is niet geïnstalleerd op de backupPC-server en het is dus niet mogelijk om een Zip-bestand te downloaden. Gelieve aan uw systeembeheerder te vragen om Archive::Zip te downloaden van www.cpan.org en te installeren.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < U kan een tar-bestand downloaden dat alle bestanden/mappen bevat die u geselecteerd hebt. U kan dan een applicatie op uw pc, zoals tar of WinZip, gebruiken om de bestanden te bekijken of uit te pakken.

    Waarschuwing: Afhankelijk van welke bestanden/mappen u geselecteerd hebt kan dit bestand zeer, zeer groot worden. Het kan verscheidene minuten duren om het tar-bestand de maken en te downloaden. Uw pc dient over voldoende vrije schijfruimte te beschikken om het bestand op te slaan.

    \$hiddenStr Maak het tar-archief relatief aan \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (anders zal het tar-archief volledige padnamen bevatten).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Bevestig herstellen voor machine \$host"; $Lang{Are_you_sure} = < U hebt gevraagd om bestanden rechtstreeks terug te zetten op de machine \$In{hostDest}. De volgende bestanden zullen hersteld worden in share \$In{shareDest}, uit backup nummer \$num:

    \$fileListStr
    Oorspronkelijk bestand/mapzal hersteld worden in

    \$hiddenStr Is dit wat u wilt doen? Gelieve te bevestigen.
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Herstellen aangevraagd van machine \$hostDest"; $Lang{Reply_from_server_was___reply} = < Het antwoord van de server was: \$reply

    Ga terug naar \$hostDest homepagina. EOF $Lang{BackupPC_Archive_Reply_from_server} = < Het antwoord van de server was: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Overzicht backup van machine \$host"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("Acties door de gebruiker")}

    \$startIncrStr

    \${h2("Backup overzicht")}

    Klik op het backupnummer om de inhoud te bekijken of om bestanden te herstellen.

    \$str
    backup nr. Type Aangevuld Niveau Startdatum Duurtijd in min. Lftd. in dagen Plaats op de server

    \$restoreStr



    \${h2("Overzicht van fouten tijdens overdracht")}

    \$errStr
    backup nr. Type Bekijken Aantal fouten Aantal foutieve bestanden Aantal foutieve \'shares\' Aantal tar-fouten


    \${h2("Overzicht bestandsgrootte en hergebruik")}

    Bestaande bestanden zijn bestanden die reeds aanwezig waren op de backupschijf. Nieuwe bestanden zijn bestanden die aan de schijf zijn toegevoegd. Lege bestanden en SMB-fouten worden niet geteld in de aantallen \'hergebruik\' en \'nieuw\'.

    \$sizeStr
    Totalen Bestaande bestanden Nieuwe bestanden
    Backup nr. Type Aantal best. Grootte in MB MB/sec Aantal best. Grootte in MB Aantal best. Grootte in MB


    \${h2("Overzicht compressie")}

    Compressie van bestanden die reeds op schijf stonden en van nieuw gecomprimeerde bestanden.

    \$compStr
    Bestaande bestanden Nieuwe bestanden
    backup nr. Type Comp.niveau Grootte in MB Comp.in MB Comp. Grootte in MB Comp.in MB Comp.


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Overzicht archivering machine \$host"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("Acties van de gebruiker")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Fout"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Server"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • U bekijkt nu backup nummer \$num, die gestart werd rond \$backupTime (\$backupAge dagen geleden), \$filledBackup
    • Ga naar map:
    • Klik op een map hieronder om de inhoud van die map te bekijken,
    • Klik op een bestand hieronder om dat bestand terug te zetten.
    • U kan de backupgeschiedenis bekijken van de huidige map.
    \${h2("Inhoud van \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Geschiedenis van een map van backup van \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "map"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < Deze geschiedenis toont elke unieke versie van de bestanden over alle backups heen:
    • Klik op een backupnummer om terug te keren naar het overzicht van de backup,
    • Klik op een map-link (\$Lang->{DirHistory_dirLink}) om door die map te bladeren,
    • Klik op de versie-link van een bestand (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) om dat bestand te downloaden,
    • Bestanden met dezelfde inhoud maar in verschillende backups hebben hetzelfde versienummer,
    • Bestanden of mappen die in een bepaalde backup niet aanwezig zijn hebben een lege cel.
    • Bestanden met hetzelfde versienummer kunnen wel verschillende attributen (eigenaar,lees- of schrijfrechten) hebben.Selecteer het backupnummer om de attributen van het bestand te bekijken.
    \${h2("Geschiedenis van \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    backup nummer
    backup moment
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Details van herstel nr. #\$num van machine \$host"; $Lang{Restore___num_details_for__host2} = <
    Nummer \$Restores[\$i]{num}
    Aangevraagd door \$RestoreReq{user}
    Aanvraagtijd \$reqTime
    Resultaat \$Restores[\$i]{result}
    Foutmelding \$Restores[\$i]{errorMsg}
    Bronmachine \$RestoreReq{hostSrc}
    Bron backupnr. \$RestoreReq{num}
    Bron share \$RestoreReq{shareSrc}
    Bestemmingsmachine \$RestoreReq{hostDest}
    Bestemmingsshare \$RestoreReq{shareDest}
    Starttijd \$startTime
    Duur \$duration min
    Aantal bestanden \$Restores[\$i]{nFiles}
    Totale grootte \${MB} MB
    Overdrachtssnelheid \$MBperSec MB/sec
    TarCreate fouten \$Restores[\$i]{tarCreateErrs}
    Overdrachtsfouten \$Restores[\$i]{xferErrs}
    Logbestand overdracht Bekijken, Fouten

    \${h1("Lijst bestanden/mappen")}

    \$fileListStr
    Oorspronkelijk bestand/maphersteld naar
    EOF # ----------------------------------- $Lang{Archive___num_details_for__host} = "BackupPC: Details van archivering nr. \$num van \$host"; $Lang{Archive___num_details_for__host2 } = <
    Nummer \$Archives[\$i]{num}
    Aangevraagd door \$ArchiveReq{user}
    Aanvraagtijd \$reqTime
    Resultaat \$Archives[\$i]{result}
    Foutmelding \$Archives[\$i]{errorMsg}
    Starttijd \$startTime
    Duur \$duration min
    Logbestand overdracht Bekijken, Fouten

    \${h1("Machinelijst")}

    \$HostListStr
    Machinebackup nr.
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Overzicht E-mail"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new mislukt: controleer de apache error_log\n"; $Lang{Wrong_user__my_userid_is___} = "Foutieve gebruiker: mijn userid is \$>, in plaats van \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Enkel gebruikers met bijzondere rechten kunnen PC-overzichten bekijken."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Enkel gebruikers met bijzondere rechten kunnen backups stoppen of starten van machine" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Ongeldig of onjuist nummer \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "Ik kan \$file niet openen: misschien problemen met de configuratie?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Enkel gebruikers met bijzondere rechten kunnen log- of configuratiebestanden bekijken."; $Lang{Only_privileged_users_can_view_log_files} = "Enkel gebruikers met bijzondere rechten kunnen logbestanden bekijken."; $Lang{Only_privileged_users_can_view_email_summaries} = "Enkel gebruikers met bijzondere rechten kunnen het e-mailoverzicht bekijken."; $Lang{Only_privileged_users_can_browse_backup_files} = "Enkel gebruikers met bijzondere rechten kunnen de backup " . "van machine \${EscHTML(\$In{host})} bekijken."; $Lang{Empty_host_name} = "Geen of lege machinenaam."; $Lang{Directory___EscHTML} = "Map \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " is leeg"; $Lang{Can_t_browse_bad_directory_name2} = "Kan niet bladeren door foutieve mapnaam" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Enkel gebruikers met bijzondere rechten kunnen backups" . " van machine \${EscHTML(\$In{host})} terugzetten."; $Lang{Bad_host_name} = "Foutieve of ongeldige machinenaam \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "U hebt geen enkel bestand geselecteerd. Gelieve terug te gaan en" . " selecteer een of meerdere bestanden."; $Lang{You_haven_t_selected_any_hosts} = "U hebt geen machine geselecteerd. Gelieve terug te gaan om" . " een machine te selecteren."; $Lang{Nice_try__but_you_can_t_put} = "Leuk geprobeerd, maar u kan geen \'..\' in de bestandsnamen plaatsen"; $Lang{Host__doesn_t_exist} = "Machine \${EscHTML(\$In{hostDest})} bestaat niet."; $Lang{You_don_t_have_permission_to_restore_onto_host} = "U beschikt niet over de juiste rechten om bestanden te herstellen naar machine " . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Ik kan " . "\${EscHTML(\"\$openPath\")} niet openen of aanmaken"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Alleen gebruikers met bijzondere rechten kunnen bestanden herstellen" . " naar machine \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Lege machinenaam"; $Lang{Unknown_host_or_user} = "Onbekende machine of gebruiker \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Enkel gebruikers met bijzondere rechten kunnen informatie over" . " machine \${EscHTML(\$host)} bekijken." ; $Lang{Only_privileged_users_can_view_archive_information} = "Enkel gebruikers met bijzondere rechten kunnen archiveringsinformatie bekijken."; $Lang{Only_privileged_users_can_view_restore_information} = "Enkel gebruikers met bijzondere rechten kunnen herstelinformatie bekijken."; $Lang{Restore_number__num_for_host__does_not_exist} = "Herstel nr.\$num van machine \${EscHTML(\$host)}" . " bestaat niet."; $Lang{Archive_number__num_for_host__does_not_exist} = "Archiveringsnr. \$num van machine \${EscHTML(\$host)}" . " bestaat niet."; $Lang{Can_t_find_IP_address_for} = "Ik kan het IP-adres van \${EscHTML(\$host)} niet vinden."; $Lang{host_is_a_DHCP_host} = < In afwachting dat ik machine \$host op een bepaald DHCP-adres terugvind, kan u deze aanvraag enkel doen vanaf die machine zelf. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "backup aangevraagd van DHCP \$host (\$In{hostIP}) door" . " \$User vanaf \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "backup aangevraagd van \$host door \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "backup geannuleerd van \$host door \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Herstel aangevraagd voor machine \$hostDest, backup nr.\$num," . " door \$User vanaf \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Archivering aangevraagd door \$User vanaf \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "Status"; $Lang{PC_Summary} = "Overzicht machine"; $Lang{LOG_file} = "LOG-bestand"; $Lang{LOG_files} = "LOG-bestanden"; $Lang{Old_LOGs} = "Oude LOGs"; $Lang{Email_summary} = "E-mailoverzicht"; $Lang{Config_file} = "Configuratiebest."; # $Lang{Hosts_file} = "Hosts-bestand"; $Lang{Current_queues} = "Huidige wachtrij"; $Lang{Documentation} = "Documentatie"; #$Lang{Host_or_User_name} = "Machine of gebruikersnaam:"; $Lang{Go} = "Start"; $Lang{Hosts} = "Machines"; $Lang{Select_a_host} = "Selecteer een machine..."; $Lang{There_have_been_no_archives} = "

    Er waren (nog) geen archiveringen

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    Deze PC werd (nog) nooit gebackupt !!

    \n"; $Lang{This_PC_is_used_by} = "
  • Deze PC wordt gebruikt door \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Enkel de foutmeldingen)"; $Lang{XferLOG} = "OverdrachtsLOG"; $Lang{Errors} = "Foutmeldingen"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <Meest recente e-mail die gezonden werd naar \${UserLink(\$user)} was op \$mailTime, onderwerp: "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <De opdracht \$cmd loopt momenteel voor machine \$host sedert \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <Machine \$host staat klaar in de wachtrij \'achtergrond\' (backup zal weldra starten). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <Machine \$host staat in de gebruikers-wachtrij (backup zal weldra starten). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <Een opdracht voor machine \$host staat in de opdrachtenwachtrij (opdracht zal weldra starten). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <Meest recente status is \"\$Lang->{\$StatusHost{state}}\"\$reason sedert \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <Meest recente foutmelding was \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <Pings naar machine \$host zijn \$StatusHost{deadCnt} opeenvolgende keren mislukt. EOF # ----- $Lang{Prior_to_that__pings} = "Daarvoor, pings"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr naar machine \$host zijn \$StatusHost{aliveCnt} opeenvolgende keren geslaagd. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Omdat machine \$host op het netwerk was gedurende minstens \$Conf{BlackoutGoodCnt} opeenvolgende keren, zal hij niet gebackupt worden van \$blackoutStr EOF $Lang{__time0_to__time1_on__days} = "\$t0 tot \$t1 op \$days."; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <backups zijn \$hours uren uitgesteld (Wijzig dit aantal). EOF $Lang{tryIP} = " en \$StatusHost{dhcpHostIP}"; # $Lang{Host_Inhost} = "Machine \$In{host}"; $Lang{checkAll} = <  Selecteer alles EOF $Lang{checkAllHosts} = <  Selecteer alles EOF $Lang{fileHeader} = < Naam Type Mode Nr. Grootte Wijziging EOF $Lang{Home} = "Home"; $Lang{Browse} = "Bekijken backups"; $Lang{Last_bad_XferLOG} = "Laaste overdr.LOG met fouten"; $Lang{Last_bad_XferLOG_errors_only} = "Laaste overdr.LOG (enkel foutmeldingen)"; $Lang{This_display_is_merged_with_backup} = < Dit overzicht is samengevoegd met backup #\$numF. EOF $Lang{Visit_this_directory_in_backup} = < Selecteer de backup die u wil bekijken: EOF $Lang{Restore_Summary} = < Klik op het nummer voor meer details. \$restoreStr
    Herstel nr. Resultaat Startdatum Duur(min.) Aantal best. MB Aantal tar-fouten Aantal Overdr.fouten

    EOF $Lang{Archive_Summary} = < Klik op het archiveringsnummer voor meer details. \$ArchiveStr
    Archiveringsnr. Resultaat Startdatum Duur/min

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Documentatie"; $Lang{No} = "nee"; $Lang{Yes} = "ja"; $Lang{The_directory_is_empty} = <De map/directory \$dirDisplay is leeg EOF #$Lang{on} = "aan"; $Lang{off} = "uit"; $Lang{backupType_full} = "volledig"; $Lang{backupType_incr} = "incrementeel"; $Lang{backupType_partial} = "gedeeltelijk"; $Lang{failed} = "mislukt"; $Lang{success} = "succesvol"; $Lang{and} = "en"; # ------ # Hosts states and reasons $Lang{Status_idle} = "inactief"; $Lang{Status_backup_starting} = "backup start"; $Lang{Status_backup_in_progress} = "backup bezig"; $Lang{Status_restore_starting} = "herstel start"; $Lang{Status_restore_in_progress} = "herstel bezig"; $Lang{Status_link_pending} = "wacht op linken"; $Lang{Status_link_running} = "linken is bezig"; $Lang{Reason_backup_done} = "backup voltooid"; $Lang{Reason_restore_done} = "herstel voltooid"; $Lang{Reason_archive_done} = "archivering voltooid"; $Lang{Reason_nothing_to_do} = "niets te doen"; $Lang{Reason_backup_failed} = "backup mislukt"; $Lang{Reason_restore_failed} = "herstel mislukt"; $Lang{Reason_archive_failed} = "archivering mislukt"; $Lang{Reason_no_ping} = "geen ping"; $Lang{Reason_backup_canceled_by_user} = "backup geannuleerd door gebruiker"; $Lang{Reason_restore_canceled_by_user} = "herstellen geannuleerd door gebruiker"; $Lang{Reason_archive_canceled_by_user} = "archivering geannuleerd door gebruiker"; $Lang{Disabled_OnlyManualBackups} = "auto uitgeschakeld"; $Lang{Disabled_AllBackupsDisabled} = "uitgeschakeld"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: Er werd (nog) geen backup gemaakt van pc \$host"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Beste $userName, Uw pc ($host) is tot op heden nog nooit succesvol gebackupt door onze PC backup software. PC backups zouden automatisch moeten gebeuren als uw pc verbonden is met het netwerk. U kan best contact opnemen met de systeembeheerder als: - Uw pc regelmatig en normaal verbonden was met het netwerk. Mogelijk is er immers een configuratie of setupfout waardoor backups niet mogelijk waren/zijn. - U helemaal geen backup wenst van deze pc en u wil dat er hierover geen e-mail meer gezonden worden In andere gevallen dient u er voor te zorgen dat uw pc zo spoedig mogelijk verbonden wordt met het netwerk. In geval van twijfel of voor hulp kan u contact opnemen met de systeembeheerder. Met vriendelijke groeten, BackupPC Genie http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: er zijn recentelijk geen backups (meer) gemaakt van pc \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Beste $userName, Er is reeds gedurende $days dagen geen backup meer gemaakt van uw pc ($host). Er zijn ondertussen van uw pc $numBackups backups gemaakt sinds $firstTime dagen geleden. De laatste backup dateert van $days dagen geleden. PC backups zouden automatisch moeten gebeuren als uw pc verbonden is met het netwerk. Als uw pc gedurende geruime tijd (meer dan een paar uur) verbonden was met het netwerk gedurende de laatste $days dagen, kan u het beste contact opnemen van uw systeembeheerder. Vraag hem of haar om uit te zoeken waarom er geen backups meer gemaakt worden van uw pc. Anderzijds, als deze pc of notebook zich momenteel niet in het netwerk bevindt dan kan u hieraan weinig anders doen behalve van belangrijke bestanden handmatig een kopie te maken op een ander medium (CD, diskette, tape, andere pc,...) U dient te weten dat *geen enkel bestand* dat u aanmaakte of wijzigde in de laatste $days dagen hersteld zal kunnen worden in geval de harde schijf van uw pc zou crashen. Hierin zijn nieuwe e-mail en bijlagen inbegrepen. Met vriendelijke groeten, BackupPC Genie http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Outlookbestanden op pc \$host moeten gebackupt worden"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Beste $userName, De Outlookbestanden van uw pc zijn $howlong. Deze bestanden bevatten al uw e-mail, bijlagen, contactadressen en agenda. Uw pc werd reeds $numBackups keer succesvol gebackupt sinds $firstTime tot $lastTime dagen geleden. Helaas, wanneer Outlook geopend is, worden al de bijhorende bestanden ontoegankelijk gemaakt voor andere programma's, inclusief het programma backupPC. Hierdoor kon van deze bestanden geen backup gemaakt worden. Als u nu verbonden bent met het netwerk, wordt U aangeraden om een backup te maken van de Outlookbestanden. Dat kan op volgende manier: - Sluit Outlook - Sluit bij voorkeur ook alle andere toepassingen - open uw browser en ga naar deze link: $CgiURL?host=$host - Kies dan voor "Start incrementele backup" tweemaal om zo een incrementele backup te starten. U kan klikken op de link "Terug naar $host pagina" en vervolgens op "vernieuwen" om de status van de backup te bekijken. Het zou slechts enkele ogenblikken mogen vragen vooraleer de backup volledig is. Met vriendelijke groeten, BackupPC Genie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "(nog) niet succesvol gebackupt"; $Lang{howLong_not_been_backed_up_for_days_days} = "reeds sedert \$days dagen niet gebackupt"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPC Server"; $Lang{RSS_Doc_Description} = "RSS feed for BackupPC"; $Lang{RSS_Host_Summary} = < NB: Selecteer 'Overschrijven' als je een waarde wil wijzigen specifiek voor deze machine.

    EOF $Lang{CfgEdit_Button_Save} = "Bewaren"; $Lang{CfgEdit_Button_Insert} = "Invoegen"; $Lang{CfgEdit_Button_Delete} = "Verwijderen"; $Lang{CfgEdit_Button_Add} = "Toevoegen"; $Lang{CfgEdit_Button_Override} = "Overschrijven"; $Lang{CfgEdit_Button_New_Key} = "Nieuwe sleutel"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "Fout: niet bewaard ten gevolge van fouten"; $Lang{CfgEdit_Error__must_be_an_integer} = "Fout: \$var moet een geheel getal zijn"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Fout: \$var moet een reële waarde (nummer) zijn"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Fout: \$var ingave \$k moet een geheel getal zijn"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Fout: \$var ingave \$k moet een reële waarde (nummer) zijn"; $Lang{CfgEdit_Error__must_be_executable_program} = "Fout: \$var moet een geldig uitvoerbaar pad zijn"; $Lang{CfgEdit_Error__must_be_valid_option} = "Fout: \$var is geen geldige optie"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "Te kopiëren machine \$copyHost bestaat niet; Machine \$fullHost wordt aangemaakt. Verwijder deze machine indien dit niet is wat je wil."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User kopieerde de instellingen van machine \$fromHost naar \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User verwijderde \$p van \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User voegde \$p toe aan \$conf, met waarde \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User wijzigde \$p in \$conf van \$valueOld naar \$valueNew \n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User verwijderde machine \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User machine \$host wijzigde \$key van \$valueOld naar \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User voegde machine \$host toe: \$value\n"; #end of lang_nl.pm BackupPC-3.3.2/lib/BackupPC/Lang/pl.pm0000444000076500000240000015366413042250554016164 0ustar craigstaff#!/usr/bin/perl #my %lang; #use strict; use utf8; # -------------------------------- $Lang{Start_Archive} = "Zacznij ArchiwizacjÄ™"; $Lang{Stop_Dequeue_Archive} = "Zatrzymaj/Odkolejkuj ArchiwizacjÄ™"; $Lang{Start_Full_Backup} = "Zacznij PeÅ‚nÄ… KopiÄ™ BezpieczeÅ„stwa"; $Lang{Start_Incr_Backup} = "Zacznij InkrementacyjnÄ… KopiÄ™ BezpieczeÅ„stwa"; $Lang{Stop_Dequeue_Backup} = "Zatrzymaj/Odkolejkuj KopiÄ™ BezpieczeÅ„stwa"; $Lang{Restore} = "Przywróć"; $Lang{Type_full} = "peÅ‚ny"; $Lang{Type_incr} = "inkrementacyjny"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Tylko uprzywilejowani użytkownicy mogÄ… oglÄ…dać opcje administracyjne"; $Lang{H_Admin_Options} = "Serwer BackupPC: Opcje Administracyjne"; $Lang{Admin_Options} = "Opcje Administracyjne"; $Lang{Admin_Options_Page} = < \${h2("Kontrola Serwera")}

    Wczytaj ponownie konfiguracjÄ™ serwera:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Nie można połączyć się z serwerem BackupPC"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < Błąd to: \$err.
    Możliwe ,że serwer BackupPC nie jest uruchomiony albo że występuje błąd w konfiguracji. Proszę powiadomić o tym swojego Administratora. EOF $Lang{Admin_Start_Server} = < Serwer BackupPC na \$Conf{ServerHost} porcie \$Conf{ServerPort} nie działa (może tylko go wyłączyłeś, albo po prostu nie wlaczyłeś).
    Czy chcesz go włączyć? EOF # ----- $Lang{H_BackupPC_Server_Status} = "Status Serwera BackupPC"; $Lang{BackupPC_Server_Status_General_Info}= <
  • PID serwera to \$Info{pid}, na hoÅ›cie \$Conf{ServerHost}, wersja \$Info{Version}, włączony \$serverStartTime.
  • WYgenerowanie statusu : \$now.
  • Ostatnie Å‚adowanie konfiguracji : \$configLoadTime.
  • NastÄ™pne kolejkowanie : \$nextWakeupTime.
  • Inne Informacje:
    • \$numBgQueue oczekujÄ…cych żądaÅ„ kopii bezpieczeÅ„stwa od czasu ostatniego zaplanowanego dziaÅ‚ania,
    • \$numUserQueue oczekujacych żądaÅ„ kopii bezpieczeÅ„stwa od uzytkowników,
    • \$numCmdQueue oczekujÄ…cych poleceÅ„ do wykonania, \$poolInfo
    • Ostatni obszar systemu plików to \$Info{DUlastValue}% (\$DUlastTime), dzisiejsza maksymalna wartość to \$Info{DUDailyMax}% (\$DUmaxTime) a wczorajszy byÅ‚ \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Aktualnie Działające Prace")}

    \$jobStr
    Host Typ Użytkownik Początek Polecenie PID Xfer PID

    \${h2("Błędy które wymagają uwagi")}

    \$statusStr
    Host Typ Użytkownik Ostatnia próba Detale Czas Ostatni błąd (inny niż brak połączenia(pingu))
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: WyciÄ…g Hostow"; $Lang{BackupPC__Archive} = "BackupPC: Archiwum"; $Lang{BackupPC_Summary} = <

    • Ten status zostaÅ‚ wygenerowany o \$now.
    • Ostatni obszar systemu plików to \$Info{DUlastValue}% (\$DUlastTime), dzisiejsza maksymalna wartość to \$Info{DUDailyMax}% (\$DUmaxTime) a wczorajszy byÅ‚ \$Info{DUDailyMaxPrev}%.

    \${h2("Hosty z bezbłędnie wykonaną kopią bezpieczeństwa ")}

    Jest \$hostCntGood hostów które zostaly zabezpieczone, na całkowita liczbę:

    • \$fullTot peÅ‚nych kopi bezpieczeÅ„stwa na peÅ‚nÄ… sumÄ™ \${fullSizeTot}GB (przed kompresjÄ…),
    • \$incrTot inkrementalnych kopi bezpieczeÅ„stwa na peÅ‚nÄ… sume \${incrSizeTot}GB (przed kompresjÄ…).

    \$strGood
    Host Użytwkonik #Pełny Pełny Wiek (dni) Pełny Rozmiar (GB) Prędkość (MB/s) #Inkr Wiek Inkr (dni) Ostatnia kopia bezpieczeństwa (dni) Status #Xfer błędó Ostatnia próba


    \${h2("Hosty bez wykonanej kopii bezpieczeństwa")}

    Jest \$hostCntNone hostów bez kopii bezpieczeństwa.

    \$strNone
    Host Użytkonik #Pełny Pełny Wiek (dni Pełny Rozmiar (GB) Prędkość (MB/s) #Inkr Wiek Inkr (dni) Ostatnia kopia bezpieczeństwa (dni) Status #Xfer błędó Ostatnia próba
    EOF $Lang{BackupPC_Archive} = < Jest \$hostCntGood hostów które mają kopie bezpieczeństwa na sumę \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    Host Uzytkownik Rozmiar Kopii Bezpieczeństwa

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Lokalizacja Archiwum EOF $Lang{BackupPC_Archive2_compression} = < Kompresja None
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Procent parytetowanych danych (0 = wyłączone, 5 = typowe) EOF $Lang{BackupPC_Archive2_split} = < Rozdziel wyjście na Megabytes EOF # ----------------------------------- $Lang{Pool_Stat} = <Pula to \${poolSize}GB zawiera \$info->{"\${name}FileCnt"} plików oraz \$info->{"\${name}DirCnt"} katalogów (zajęło \$poolTime),
  • Hashowanie puli daje \$info->{"\${name}FileCntRep"} powtarzajÄ…cych siÄ™ plików z najdÅ‚uższym Å‚ancuchem \$info->{"\${name}FileRepMax"},
  • Nocne czyszczenie usunęło \$info->{"\${name}FileCntRm"} plików o rozmiarze \${poolRmSize}GB (zajęło \$poolTime), EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: Kopia rzÄ…dana na \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < Odpowiedź serwera to : \$reply

    Wróć do strony domowej \$host. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Potwierdzony start kopii na \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Zamierzasz zaczać kopie \$type na \$host.

    Czy napewno chcesz tego ?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Zatrzymaj potwierdzoną kopie na \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < Zamierzasz zatrzymać wykonywanie kopii na \$host;
    Prosze nie zaczynac nowej kopii przez godzin.

    Czy naprawdÄ™ tego chcesz ?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Tylko uprzywilejowani użytwkonicy mogą przeglądać kolejki"; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "ylko uprzywilejowani użytwkonicy mogą archiwizować."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Podsumowanie kolejki"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Podsumowanie kolejki uzytkownika")}

    Następujący użytkonicy są w kolejce:

    \$strUser
    Host Czas do Użytkownik


    \${h2("Podsumowanie kolejki w tle")}

    Następujące kolejki będące w tle czekają na wykonanie :

    \$strBg
    Host Czas do uzytkownik


    \${h2("Podsumowanie kolejki poleceń")}

    Następujące kolejki poleceń czekają na wykonanie :

    \$strCmd
    Host Czas do Użytkownik Polecenie
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: Plik \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, zmodyfikowne \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ pominięto \$skipped linii ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nNie można otworzyc dziennika \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: Historia Dziennika";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    Plik Rozmiar Czas Modyfikacji
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Adresat Nadawca Czas Temat
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Przeglądaj \$num dla \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Przywróć opcje dla \$host"; $Lang{Restore_Options_for__host2} = < Zaznaczyłeś następujące pliki/katalogi z udziału \$share, kopia numer #\$num:
      \$fileListStr

    Masz do wyboru trzy możliwośći przywrócenia tych plików/katalogów. Proszę wybrać jedna z nich.

    \${h2("Opcja Pierwsza: Bezposrednie przywrócenie")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    Uwaga: jakikolwiek plik pasujący do tych ktore masz zaznaczone będzie nadpisany !

    \$hiddenStr
    Przywrócenie plików na host
    Przywrócenie plików do udziału
    Przywróć pliki poniżej
    (podobne do udziału)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Opcja Druga: ÅšciÄ…gnij Archiwum Zip")}

    Możesz ściągnąc archiwum Zip zawieające wszystkie pliki/katalogi które zaznaczyłeś. Możesz wtedy użyć lokalnej aplikacji, Takiej jak 7Zip, do przeglądania czy wypakowania danych.

    Uwaga: zależnie od wybranych plików/katalogów , to archiwum może być bardzo duże. Może zajać dużo czasu do stworzenia i przesłania go, także będziesz potrzebował odpowiedniej ilości miejsca na dysku do przechowania.

    \$hiddenStr Stworzyć archiwum powiązane z \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (inaczej bedzie zawierac pełne scieżki do plików).
    Kompresja (0=off, 1=fast,...,9=best)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Opcja Druga: ÅšciÄ…gnij Archiwum Zip")}

    Archive::Zip nie jest zainstalowane więc nie możesz ściągnąć archiwum Zip. Proszę poprosić swojego Administratora aby zainstalował Archive::Zip z www.cpan.org.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < Możesz ściągnąc archiwum Tar zawieające wszystkie pliki/katalogi które zaznaczyłeś. Możesz wtedy użyć lokalnej aplikacji, Takiej jak 7Zip, do przeglądania czy wypakowania danych.

    Uwaga: zależnie od wybranych plików/katalogów , to archiwum może być bardzo duże. Może zajać dużo czasu do stworzenia i przesłania go, także będziesz potrzebował odpowiedniej ilości miejsca na dysku do przechowania.

    \$hiddenStr Stworzyć archiwum powiązane z\${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (inaczej bedzie zawierac pełne scieżki do plików).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Potwiedź przywrócenie na \$host"; $Lang{Are_you_sure} = < Zaczynasz przywracanie bezpośrednio na maszynę \$In{hostDest}. Następujące pliki zostaną przywrócene na udział \$In{shareDest}, z kopii numer \$num:

    \$fileListStr
    Orginalny plik/katalogBędzie przywrócony na

    \$hiddenStr Czy napewno chcesz tego ?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Rządanie przywrócenie na \$hostDest"; $Lang{Reply_from_server_was___reply} = < Odpowiedź serwera : \$reply

    Wróć stronę domową \$hostDest. EOF $Lang{BackupPC_Archive_Reply_from_server} = < Odpowiedź serwera : \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Podsumowanie kopii bezpieczeństwa hosta \$host"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("Działania użytwkonika")}

    \$startIncrStr

    \${h2("Podsumowanie Kopii Bezpieczeństwa")}

    Kliknij na numer kopii aby przeglądać i przywracać wybrane pliki/katalogi.

    \$str
    Backup# Typ Wypełniony Poziom Początek Czas trwania w min. Wiek/dni Ścieżka serwera kopii

    \$restoreStr



    \${h2("Podsumowanie błędów Xfer")}

    \$errStr
    Backup# Typ Widok #Xfer błędó #bad plików #bad udziałów #tar błędów


    \${h2("Ilość/wielkość użytych ponownie plików")}

    Istniejące pliki to te będące aktualnie w puli; nowe pliki to te dodane do puli. Puste pliki i błędy SMB nie są liczone.

    \$sizeStr
    ÅÄ…cznie IstniejÄ…cych plików Nowych plików
    Kopia nr Typ Plików Rozmiar/MB MB/sek Plików Rozmiar/MB Plików Rozmiar/MB


    \${h2("Podsumowanie Kompresji")}

    Wydajność kompresji dla plików będących w puli oraz tych świeżo skompresowanych.

    \$compStr
    IstniejÄ…ce Pliki Nowe Pliki
    Kopia nr Typ Poziom Kompresji Rozmiar/MB Kompresja/MB Kompresja Rozmiar/MB Kompresja/MB Kompresja


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Podsumowanie Archiwizacji hosta \$host"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("Działania Użytkownika")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Błąd"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Serwer"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • Przegladasz kopie nr #\$num, która zaczeÅ‚a siÄ™ okoÅ‚o \$backupTime (\$backupAge dni temu), \$filledBackup
    • Wpisz adres:
    • Wpisz adres aby przejść do niego,
    • Kliknij plik aby go przywrócić,
    • Możesz zobaczyć kopie history obecnego adresu.
    \${h2("Zawartość \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Histria kopii dla \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "adres"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < Przedstawienie każdej unikalnej wersji każdego pliku we wszystkich kopiach:
    • Kliknij na numerze kopii aby przejść do przegladania tejże kopii,
    • KLiknij na adres (\$Lang->{DirHistory_dirLink}) aby przejść do niego,
    • Kliknij na wersje pliku (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) aby śćiagnać ten plik,
    • Pliki z tÄ… samÄ… zawartoÅ›ciÄ… pomiÄ™dzy różnymi kopiami majÄ… ten sam numer wersji,
    • Pliki lub adresy ,które nie sÄ… dostÄ™pne w okreÅ›lonej kopii nie sÄ… zaznaczone.
    • Pliki pokazane z tÄ… samÄ… wersjÄ… mogÄ… mieć inny atrybut. Wybierz numer kopii aby zobaczyć atrybuty plików.
    \${h2("Historia \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Numer kopii
    Czas trwania kopii
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Przywróć #\$num detali dla \$host"; $Lang{Restore___num_details_for__host2} = <
    Numer \$Restores[\$i]{num}
    Żądane przez \$RestoreReq{user}
    Czas żądania \$reqTime
    Wynik \$Restores[\$i]{result}
    Wiadomość błędu \$Restores[\$i]{errorMsg}
    Host źródłowy \$RestoreReq{hostSrc}
    Źródło kopii nr \$RestoreReq{num}
    Źródło udziału \$RestoreReq{shareSrc}
    Host docelowy \$RestoreReq{hostDest}
    Udział docelowy \$RestoreReq{shareDest}
    Czas rozpoczęcia \$startTime
    Czas trwania \$duration min
    Ilość plików \$Restores[\$i]{nFiles}
    Całkowity rozmiar \${MB} MB
    Szybkość transferu \$MBperSec MB/sec
    Błędy TarCreate \$Restores[\$i]{tarCreateErrs}
    Błędy Xfer \$Restores[\$i]{xferErrs}
    Plik dziennika Xfer Widok, Błędy

    \${h1("Lista plików/katalogów")}

    \$fileListStr
    Orginalny plik/katalogPrzywrócony na
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Detale Archiwum nr #\$num dla \$host"; $Lang{Archive___num_details_for__host2 } = <
    Numer \$Archives[\$i]{num}
    Żądane przez \$ArchiveReq{user}
    Czas żądania \$reqTime
    Wynik \$Archives[\$i]{result}
    Wiadomość błędu \$Archives[\$i]{errorMsg}
    Czas rozpoczęcia \$startTime
    Czas trwania \$duration min
    Plik dziennika Xfer Widok, Błędy

    \${h1("Lista Hostów")}

    \$HostListStr
    HostNumer Kopii
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Podsumowanie emailów"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new failed: sprawdź apache error_log\n"; $Lang{Wrong_user__my_userid_is___} = "Zly użytkownik: mój userid to \$>, a nie \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Tylko uprzywilejowani użytkownicy mogą przegladać podsumowania."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Tylko uprzywilejowani użytkownicy mogą dokonywać kopii na" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Zły numer \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "Niemozna otworzyć \$file: problem z konfiguracja ?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Tylko uprzywilejowani użytkownicy mogą przeglądac logi/pliki konf."; $Lang{Only_privileged_users_can_view_log_files} = "Tylko uprzywilejowani użytkownicy mogą przeglądać logi."; $Lang{Only_privileged_users_can_view_email_summaries} = "Tylko uprzywilejowani użytkownicy mogą przeglądać podsumowania emaili."; $Lang{Only_privileged_users_can_browse_backup_files} = "Tylko uprzywilejowani użytkownicy mogą przeglądać pliki kopii" . " for host \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Pusta nazwa hosta."; $Lang{Directory___EscHTML} = "Adres \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " jest pusty"; $Lang{Can_t_browse_bad_directory_name2} = "Nie można przeglądać - zła nazwa" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Tylko uprzywilejowani użytkownicy mogą przywracać pliki kopii" . " dla hosta \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Zła nazwa hosta \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "Nie zaznaczyłeś zadnych plików; proszę cofnąć sie do" . " zaznaczanych plików."; $Lang{You_haven_t_selected_any_hosts} = "Nie zaznaczyłeś zadnego hosta; proszę cofnij sie" . " i zaznacz odpowiednie hosty."; $Lang{Nice_try__but_you_can_t_put} = "Nieźle , ale nie możesz umieścic \'..\' w nazwie pliku"; $Lang{Host__doesn_t_exist} = "Host \${EscHTML(\$In{hostDest})} nie istnieje"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "Nie masz uprawnień do przywracania danych na host" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Nie można otworzyć/stworzyć" . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Tylko uprzywilejowani użytkownicy mogą przywracać pliki kopii" . " dla hosta \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Pusta nazwa hosta"; $Lang{Unknown_host_or_user} = "Nieznany host albo uzytwkonik \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Tylko uprzywilejowani użytkownicy mogą przeglądać informacje o" . " host \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Tylko uprzywilejowani użytkownicy mogą przeglądać informacje o archiwum."; $Lang{Only_privileged_users_can_view_restore_information} = "Tylko uprzywilejowani użytkownicy mogą przeglądać przywracać informacje."; $Lang{Restore_number__num_for_host__does_not_exist} = "Punkt przywracania nr \$num dla hosta \${EscHTML(\$host)} nie" . " istnieje."; $Lang{Archive_number__num_for_host__does_not_exist} = "Archiwum numer \$num dla hosta \${EscHTML(\$host)} nie" . " istnieje."; $Lang{Can_t_find_IP_address_for} = "Nie moge znaleść adresu IP dla \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < Dopuki \$host jest adresem DHCP, możesz rozpocząć to źądanie bezpośrednio z tejże maszyny. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "Kopia zaźądana na hoscie DHCP \$host (\$In{hostIP}) przez" . " \$User z \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Kopia zażądana na \$host przez \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Kopia przerwana na \$host przez \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Przywrócenie na host \$hostDest, kopii nr #\$num," . " przez \$User z \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Archiwum żądane przez \$User z \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "Status"; $Lang{PC_Summary} = "Podsumowanie hostów"; $Lang{LOG_file} = "Plik Log"; $Lang{LOG_files} = "Pliki Log"; $Lang{Old_LOGs} = "Stare Logi"; $Lang{Email_summary} = "Podsumowanie emaili"; $Lang{Config_file} = "Plik Konfiguracyjny"; # $Lang{Hosts_file} = "Plik Hostów"; $Lang{Current_queues} = "Aktualne kolejki"; $Lang{Documentation} = "Dokumentacja"; #$Lang{Host_or_User_name} = "Host lub nazwa użytkownika:"; $Lang{Go} = "Idź"; $Lang{Hosts} = "Hosty"; $Lang{Select_a_host} = "Wybierz host..."; $Lang{There_have_been_no_archives} = "

    Nie było żadnej archiwizacji

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    Ten PC nie byl nikty backupowany!!

    \n"; $Lang{This_PC_is_used_by} = "
  • Ten PC jest używany przez \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Błędy wypakowywania)"; $Lang{XferLOG} = "XferLOG"; $Lang{Errors} = "Błędy"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <Ostatni email wysÅ‚any do \${UserLink(\$user)} byl o \$mailTime, subject "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <Polecenie \$cmd jest aktualnie wykonywane dla \$host, rozpoczÄ™te o \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <Host \$host jest zakolejkowany (kopia zostanie wykonana niedÅ‚ugo). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <Host \$host jest zakolejkowany w kolejce użytkownika (kopia zostanie wykonana niedÅ‚ugo). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <Polecenie dla \$host jest w kolejce poleceÅ„ (ruszy niedÅ‚ugo). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <Ostatni status \"\$Lang->{\$StatusHost{state}}\"\$reason od \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <Ostatni błąd to \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <Pingowanie \$host niepowidÅ‚o siÄ™ \$StatusHost{deadCnt} razy. EOF # ----- $Lang{Prior_to_that__pings} = "Poprzednio, "; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr pingów do \$host zakoÅ„czyÅ‚o siÄ™ sukcesem \$StatusHost{aliveCnt} razy. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Ponieważ \$host jest w sieci od co najmniej \$Conf{BlackoutGoodCnt} razy, nie zostanie utworzona kopia bezpieczeÅ„stwa \$blackoutStr. EOF $Lang{__time0_to__time1_on__days} = "\$t0 to \$t1 on \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Kopie zostaÅ‚y odÅ‚ożone na \$hours godzin (zmieÅ„ ten numer). EOF $Lang{tryIP} = " i \$StatusHost{dhcpHostIP}"; # $Lang{Host_Inhost} = "Host \$In{host}"; $Lang{checkAll} = <  Select all EOF $Lang{checkAllHosts} = <  Select all EOF $Lang{fileHeader} = < Nazwa Typ Tryb nr# Rozmiar Data modyfikacji EOF $Lang{Home} = "Dom"; $Lang{Browse} = "przeglÄ…daj kopie"; $Lang{Last_bad_XferLOG} = "Ostatni zÅ‚y XferLOG"; $Lang{Last_bad_XferLOG_errors_only} = "Ostatni zÅ‚y XferLOG (tylko bÅ‚edy)"; $Lang{This_display_is_merged_with_backup} = < ten display zostal zÅ‚oÅ„czony z kopiÄ… nr #\$numF. EOF $Lang{Visit_this_directory_in_backup} = < Wybierz kopiÄ™ którÄ… chcesz przeglÄ…dać: EOF $Lang{Restore_Summary} = < Kliknij na numer przywrócenia dla informacji. \$restoreStr
    Nr przywrócenia# Wynik Data początku Trwanie/min Ilość plików MB Ilość błędów tar Ilość błędów xferErrs

    EOF $Lang{Archive_Summary} = < Kliknij na numerze archiwum dla informacji \$ArchiveStr
    Nr Archiwum wynik Data poczÄ…tku Trwanie/min

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Dokumentacja"; $Lang{No} = "nie"; $Lang{Yes} = "tak"; $Lang{The_directory_is_empty} = <Ten katalog jest \$dirDisplay pusty EOF #$Lang{on} = "wł"; $Lang{off} = "wył"; $Lang{backupType_full} = "pełen"; $Lang{backupType_incr} = "inkr"; $Lang{backupType_partial} = "cząstwkowy"; $Lang{failed} = "nieudany"; $Lang{success} = "udany"; $Lang{and} = "oraz"; # ------ # Hosts states and reasons $Lang{Status_idle} = "bezczynny"; $Lang{Status_backup_starting} = "kopia w drodze"; $Lang{Status_backup_in_progress} = "kopia w trakcie tworzenia"; $Lang{Status_restore_starting} = "przywracanie w drodze"; $Lang{Status_restore_in_progress} = "przywracanie w trakcie tworzenia"; $Lang{Status_link_pending} = "link wtrakcie"; $Lang{Status_link_running} = "link działa"; $Lang{Reason_backup_done} = "zrobione"; $Lang{Reason_restore_done} = "przywracanie zrobione"; $Lang{Reason_archive_done} = "archiwum zrobione"; $Lang{Reason_nothing_to_do} = "bezczynny"; $Lang{Reason_backup_failed} = "kopia nieudana"; $Lang{Reason_restore_failed} = "przywracanie nieudane"; $Lang{Reason_archive_failed} = "archiwizacja nieudana"; $Lang{Reason_no_ping} = "nie ma pingu"; $Lang{Reason_backup_canceled_by_user} = "kopia przerwana przez użytwkonika"; $Lang{Reason_restore_canceled_by_user} = "przywracanie przerwane przez użytkownika"; $Lang{Reason_archive_canceled_by_user} = "archiwum przerwane przez użytwkonika"; $Lang{Disabled_OnlyManualBackups} = "automat wyłączony"; $Lang{Disabled_AllBackupsDisabled} = "wyłączony"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: żadna kopia \$host niepowiodła się"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; Do: $user$domain cc: Temat: $subj $headers Drogi $userName, Twoj PC ($host) nigdy nie został zabespieczony przez nasz program tworzenia kopii bezpieczeństwa. Backup powinien nastąpic automatycznie kiedy twoj PC zostanie podłączony do sieci. Powinieneś skontaktować się z pomocą techniczną jeżeli: - Twój PC jest cały czas podłączony , co oznacza ze wysteuje problem z konfiguracją uniemożliwiający tworzenie kopii. - Nie chcesz aby kopie były wykonywane i nie chcesz tych wiadomośći. Inaczej, proszę sprawdzić czy twój PC jest podłączony do sieci nastepnym razem kiedy bedziesz przy nim. Pozdrawiam , Czarodziej BackupPC http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: żadnych nowych kopii na \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; Do: $user$domain cc: Temat: $subj $headers Drogi $userName, Twój PC ($host) nie był pomyślnie zarchiwizowany przez $days dni. Twój PC był poprawnie zarchiwizowany $numBackups razy, od $firstTime do $days temu. Wykonywanie kopii bezpieczeństwa powinno nastąpić automatycznie po podłączeniu do śieci. Jeżeli twoj PC był podłączony więcej niż kilka godzin do sieci w czasie ostatnich $days dni powinieneś skontaktować sie z pomocą techniczą czemu twoje kopie nie działają. Inaczej , jeżeli jestes poza miejscem pracy nie możesz zrobić więcej niz skopiować samemu najważniejsze dane na odpowiedni nośnik. Musisz wiedzieć ze wszystkie pliki które stworzyłeś lub zmieniłeś przez ostatnie $days dni (włącznie z nowymi emailami i załącznikami) nie będą przywrócone jeżeli dysk ulegnie awarii. Pozdrowienia, Czarodziej BackupPC http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Outlook files on \$host need to be backed up"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, The Outlook files on your PC have $howLong. These files contain all your email, attachments, contact and calendar information. Your PC has been correctly backed up $numBackups times from $firstTime to $lastTime days ago. However, Outlook locks all its files when it is running, preventing these files from being backed up. It is recommended you backup the Outlook files when you are connected to the network by exiting Outlook and all other applications, and, using just your browser, go to this link: $CgiURL?host=$host Select "Start Incr Backup" twice to start a new incremental backup. You can select "Return to $host page" and then hit "reload" to check the status of the backup. It should take just a few minutes to complete. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "utworzenie kopii nie zostało zakonczone pomyślnie"; $Lang{howLong_not_been_backed_up_for_days_days} = "Kopia nie była tworzona od \$days dni"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "Serwer BackupPC"; $Lang{RSS_Doc_Description} = "Kanał RSS dla BackupPC"; $Lang{RSS_Host_Summary} = < Notka: Sprawdź opcję "Nadpisz" jeżeli chcesz zmienić wartość specificzną dla tego hosta.

    EOF $Lang{CfgEdit_Button_Save} = "Zapisz"; $Lang{CfgEdit_Button_Insert} = "Wstaw"; $Lang{CfgEdit_Button_Delete} = "Kasuj"; $Lang{CfgEdit_Button_Add} = "Dodaj"; $Lang{CfgEdit_Button_Override} = "Nadpisz"; $Lang{CfgEdit_Button_New_Key} = "Nowy Klucz"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "Błąd: Nie zapisano z powodu błędów"; $Lang{CfgEdit_Error__must_be_an_integer} = "Błąd: \$var musi być liczbÄ… caÅ‚kowitÄ…"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Błąd: \$var musi być liczbÄ… rzeczywistÄ…"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Błąd: \$var wpis \$k musi być liczbÄ… caÅ‚kowitÄ…"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Błąd: \$var wpis \$k musi być liczbÄ… rzeczywistÄ…"; $Lang{CfgEdit_Error__must_be_executable_program} = "Błąd: \$var musi być poprawnÄ… Å›cieżkÄ… do programu wykonywalnego"; $Lang{CfgEdit_Error__must_be_valid_option} = "Błąd: \$var musi być poprawnÄ… opcjÄ…"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "Kopiowany host \$copyHost nie istnieje; tworzÄ™ nowÄ… nazwÄ™ \$fullHost. Skasuj jÄ… jeżeli to nie to co chciaÅ‚eÅ›."; $Lang{CfgEdit_Log_Copy_host_config} = "Skopiowano konfiguracjÄ™ \$User z \$fromHost do \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User skasowany \$p z \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User dodany \$p do \$conf, ustawiono \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User zmieniony \$p w \$conf na \$valueNew z \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User skasowaÅ‚ host \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User z hosta \$host zmieniÅ‚ \$key z \$valueOld na \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User dodaÅ‚ host \$host: \$value\n"; #end of lang_en.pm BackupPC-3.3.2/lib/BackupPC/Lang/pt_br.pm0000444000076500000240000015505613042250554016654 0ustar craigstaff#!/usr/bin/perl # # By Reginaldo Ferreira (23.07.2004 for V2.1.10) # # Edited by Rodrigo Real (22.06.2006) #my %lang; #use strict; # -------------------------------- $Lang{Start_Archive} = "Iniciar backup"; $Lang{Stop_Dequeue_Archive} = "Parar/Cancelar backup"; $Lang{Start_Full_Backup} = "Iniciar Backup Completo"; $Lang{Start_Incr_Backup} = "Iniciar Backup Incremental"; $Lang{Stop_Dequeue_Backup} = "Parar/Cancelar Backup"; $Lang{Restore} = "Restaurar"; $Lang{Type_full} = "completo"; $Lang{Type_incr} = "incremental"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Somente superusuarios podem ver as opções de administração."; $Lang{H_Admin_Options} = "Servidor BackupPC: Opções de administração"; $Lang{Admin_Options} = "Opções de administração"; $Lang{Admin_Options_Page} = < \${h2("Controle do Servidor")}

    Atualizar configurações do servidor:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Impossível conectar ao servidor BackupPC"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < O erro foi: \$err.
    Talvez o servidor BackupPC não esteja ativo ou há um erro de configuração. Por favor informe o administrador do sistema. EOF $Lang{Admin_Start_Server} = < O servidor BackupPC em \$Conf{ServerHost} port \$Conf{ServerPort} não está iniciando (pode ter parado ou não ainda não iniciado).
    Deseja inicia-lo agora? EOF # ----- $Lang{H_BackupPC_Server_Status} = "Estado do Servidor BackupPC"; $Lang{BackupPC_Server_Status_General_Info}= <
  • O PID do servidor é \$Info{pid}, no host \$Conf{ServerHost}, versão \$Info{Version}, iniciado em \$serverStartTime.
  • Esta informação de estado foi gerada em \$now.
  • A última configuração foi carregada às \$configLoadTime
  • A fila de PCs se ativará novamente em \$nextWakeupTime.
  • Informações adicionais:
    • \$numBgQueue solicitações de backup pendentes desde a última ativação programada,
    • \$numUserQueue solicitações de backup de usuarios,
    • \$numCmdQueue solicitações de comandos pendentes, \$poolInfo
    • O sistema de arquivos estava recentemente em \$Info{DUlastValue}% (\$DUlastTime), o máximo de hoje é \$Info{DUDailyMax}% (\$DUmaxTime) e o máximo de ontem foi \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Trabalhos em Execução")}

    \$jobStr
    Host Tipo Usuário Hora de Início Comando PID Transfer. PID

    \${h2("Falhas que Precisam de Atenção")}

    \$statusStr
    Host Tipo Usuário Última Tentativa Detalhes Hora do erro Último erro (ping não incluido)
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: Resumo do Servidor"; $Lang{BackupPC__Archive} = "BackupPC: Archive"; $Lang{BackupPC_Summary}=<

    • Este status foi generado em \$now.
    • O sistema de arquivos estava recentemente em \$Info{DUlastValue}% (\$DUlastTime), o m?ximo de hoje ? \$Info{DUDailyMax}% (\$DUmaxTime) e o m?ximo de ontem foi \$Info{DUDailyMaxPrev}%.

    \${h2("Hosts com Backups Completos")}

    Existem \$hostCntGood hosts com backup, de um total de :

    • \$fullTot backups com tamanho total de \${fullSizeTot} GB (antes de agrupar e comprimir),
    • \$incrTot backups incrementais com tamanho total de \${incrSizeTot} GB (antes de agrupar e comprimir).

    \$strGood
    Host Usuario #Completo Completo Antig. (Dias) Completo Tamanho (GB) Velocidade (MB/sec) #Incrementais Incrementais Antig (Dias) ENG Last Backup (days) Estado Nº Xfer errs Última Tentativa


    \${h2("Hosts Sem Backups")}

    Existem \$hostCntNone hosts sem backups.

    \$strNone
    Host Usuario #Completo Completo Antig. (Dias) Completo Tamanho (GB) Velocidade (MB/sec) #Incrementais Incrementais Antig (Dias) ENG Last Backup (days) Estado Nº Xfer errs Última tentativa
    EOF $Lang{BackupPC_Archive} = < Existem \$hostCntGood hosts que possuem backup com tamanho total de \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    Host Usuário Tamanho Backup

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < Archive Localização/Dispositivo EOF $Lang{BackupPC_Archive2_compression} = < Compression None
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Porcentagem de dados de paridade (0 = desabilitado, 5 = normal) EOF $Lang{BackupPC_Archive2_split} = < Dividir resultado em Megabytes EOF # ----------------------------------- $Lang{Pool_Stat} = <O pool de \${poolSize}GB compreende \$info->{"\${name}FileCnt"} arquivos e \$info->{"\${name}DirCnt"} diretórios (as of \$poolTime),
  • O processamento do pool é de \$info->{"\${name}FileCntRep"} arquivos repetidos cuja cadeia maior é \$info->{"\${name}FileRepMax"},
  • O processo de limpeza noturna eliminou \$info->{"\${name}FileCntRm"} arquivos de \${poolRmSize}GB (around \$poolTime), EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: Solicitação de Backup por \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < A resposta do servidor foi: \$reply

    Voltar a \$host home page. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Confirme inicio do backup em \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Iniciando Backup \$type em \$host.

    Tem certeza desta ação?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Confirmação de Parada do Backup \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < Você está certo de parar/sair da fila de backup em \$host;
    Assim mesmo, por favor não impessa outro backup durante horas.

    Tem certeza de que quer fazer isto?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Somente administradores podem ver as filas."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Somente administradores podem arquivar."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Resumo da Fila de Backup"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Resumo da Fila de Usuários")}

    As seguintes solicitações de usuários estão atualmente em fila:

    \$strUser
    Host Hora Sol. Usuário


    \${h2("Resumo da Fila em Segundo Plano")}

    As seguintes solicitações em segundo plano estão atualmente em fila:

    \$strBg
    Host Hora Sol. Usuário


    \${h2("Resumo da Fila de Comandos")}

    Os seguintes comandos estão atualmente em fila:

    \$strCmd
    Host Hora Sol. Usuário Comando
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: LOG de Registro \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, modificado \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ saltadas \$skipped linhas ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nNão pode-se abrir o LOG de registro \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: Histórico dos Logs de Registro";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    File Tamanho Hora Modificação
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Destinatário Host Hora Assunto
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Explorar Backup \$num de \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Opções de restauração para \$host"; $Lang{Restore_Options_for__host2} = < Foi selecionado os seguintes arquivos/diretórios da unidade \$share, cópia número #\$num:
      \$fileListStr

    Existem três opções para restaurar estes arquivos/diretórios. Por favor, selecione uma das seguintes opções.

    \${h2("Opção 1: Restauração Direta")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    Atenção!: Qualquer arquivo existente com o mesmo nome que o que está selecionado será sobrescrito!

    \$hiddenStr
    Restaurar os arquivos no host
    Restaurar os arquivos na unidade
    Restaurar os arquivos abaixo no diretório
    (relativo a unidade)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Opção 2: Criar arquivo Zip")}

    Pode-se criar um arquivo comprimido (.zip) contendo todos os arquivos e diretórios que foram selecionados. Depois pode-se utilizar uma aplicação local, como WinZip, para ver ou extrair os arquivos.

    Atenção!: Dependendo de quais arquivos/pastas tenham sido selecionados, este arquivo pode ser muito grande. Poderia demorar muitos minutos para criar e transferir o arquivo. Também necessitará suficiente espaçio em disco local para armazená-lo.

    \$hiddenStr Fazer arquivo relativo a \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (caso contrário o arquivo conterá os caminhos completos).
    Compressão (0=desativada, 1=rápida,...,9=máxima)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Opção 2: Criar arquivo Zip")}

    O programa Archive::Zip não está instalado, de modo que nã poderá criar um arquivo comprimido zip. Por favor, solicite ao seu administrador de sistemas que instale Archive::Zip de www.cpan.org.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < Pode-se criar um arquivo comprimido (.Tar) contendo todos os arquivos e diretórios que foram selecionados. Após pode-se utilizar uma aplicação local, como Tar ou WinZip, para ver ou extrair os arquivos gerados.

    Atenção!: Dependendo de quais arquivos/pastas foram selecionados, este arquivo pode ser muito grande. Poderia levar muitos minutos para criar e transferir o arquivo. Também necessitará suficiente espaço no disco local para armazená-lo.

    \$hiddenStr Criar um arquivo relativo a \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (caso contrário o arquivo conterá os caminhos completos).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Confirme restauração em \$host"; $Lang{Are_you_sure} = < Está prestes a començar uma restauração diretamente na máquina \$In{hostDest}. Os seguintes arquivos serão restaurados na unidade \$In{shareDest}, a partir do Backup número \$num:

    \$fileListStr
    Arquivo/Dir Original Será restaurado em

    \$hiddenStr Tem certeza?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Restauração solicitada em \$hostDest"; $Lang{Reply_from_server_was___reply} = < A resposta do servidor foi: \$reply

    voltar a \$hostDest home page. EOF $Lang{BackupPC_Archive_Reply_from_server} = < A resposta do servidor foi: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Host \$host Resumo do Backup"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("Ações do Usuário")}

    \$startIncrStr

    \${h2("Resumo do Backup")}

    Clique no número do Backup para revisar e restaurar arquivos.

    \$str
    Cópia Nº Tipo Completo ENG Level Data Início Duração/min Idade/dias Rota da Cópia no Servidor

    \$restoreStr



    \${h2("Resumo dos Erros de Transferência")}

    \$errStr
    Copia Nº Tipo Ver Nº Xfer errs Nº erros arquivos Nº erros unidades Nº erros tar


    \${h2("Resumo do Total/Tamanho dos Arquivos Reutilizados")}

    Os arquivos existentes são aqueles que já estão no lote; os novos são aqueles que serão adicionados ao lote. Os arquivos vazios e os erros de SMB não contam nos valores de reutilizados nem nos de novos.

    \$sizeStr
    Totais Arquivos Existentes Arquivos Novos
    Cópia Nº Tipo Nº Arquivos Tamanho/MB MB/seg Nº Arquivos Tamanho/MB Nº Arquivos Tamanho/MB


    \${h2("Resumo da Compressão")}

    Performance de compresão para os arquivos já existentes no lote e nos arquivos novos comprimidos.

    \$compStr
    Arquivos Existentes Arquivos Novos
    Cópia Nº Tipo Nível Compr Tamanho/MB Compr/MB Compr Tamanho/MB Compr/MB Compr


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Host \$host Archive Summary"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("Ações do usuário")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Erro"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Servidor"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • Revisando o Backup Nº\$num, que iniciou às \$backupTime (faz \$backupAge dias), \$filledBackup
    • Indique o diretório:
    • Clique em um dos diretórios abaixo para revisar seus conteúdos,
    • Clique em um arquivo para restaurá-lo,
    • Ver o Backup history do diretório atual.
    \${h2("Conteúdo do \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Histórico do Backup do diretório em \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "dir"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < Este quadro mostra cada versão única disponível nos diversos backups:
    • Clique no número do backup para voltar ao explorador de backups,
    • Clique no atalho do diretório (\$Lang->{DirHistory_dirLink}) para navegar por esse diretório,
    • Clique no atalho da versão do arquivo (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) para baixar esse arquivo,
    • Os arquivos com conteúdos diferentes entre cópias distintas de backup tem o mesmo número de verssão,
    • Os arquivos ou diretórios inexistentes em um determinado backup tem uma caixa vazia.
    • Os arquivos mostrados com a mesma versão podem ter diferentes atributos. Selecione o número do backup para ver os atributos do arquivo.
    \${h2("Histórico de \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Backup numero
    Backup time
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Detalhes da restauração Nº\$num de \$host"; $Lang{Restore___num_details_for__host2} = <
    Número \$Restores[\$i]{num}
    Solicitado por \$RestoreReq{user}
    Hora da Solicitação \$reqTime
    Resultado \$Restores[\$i]{result}
    Mensagem de Erro \$Restores[\$i]{errorMsg}
    Host Origem \$RestoreReq{hostSrc}
    Nº cópia origem \$RestoreReq{num}
    Unidade origem \$RestoreReq{shareSrc}
    Host destino \$RestoreReq{hostDest}
    Unidade destino \$RestoreReq{shareDest}
    Hora início \$startTime
    Duração \$duration min
    Número de arquivos \$Restores[\$i]{nFiles}
    Tamanho total \${MB} MB
    Taxa de transferência \$MBperSec MB/sec
    Erros de criação Tar \$Restores[\$i]{tarCreateErrs}
    Erros de transferência \$Restores[\$i]{xferErrs}
    Arquivo registro de transferência View, Errors

    \${h1("Lista de Arquivos/Diretórios")}

    \$fileListStr
    Dir/arquivo originalRestaurado a
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Archive #\$num Detalhes de \$host"; $Lang{Archive___num_details_for__host2 } = <
    Número \$Archives[\$i]{num}
    Solicitado por \$ArchiveReq{user}
    Hora da solicitação \$reqTime
    Resultado \$Archives[\$i]{result}
    Mensagem de erro \$Archives[\$i]{errorMsg}
    Hora início \$startTime
    Duração \$duration min
    Arquivo registro Xfer View, Errors

    \${h1("Host list")}

    \$HostListStr
    HostBackup número
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Resumo de Emails"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->nova falha: revise o error_log do apache\n"; $Lang{Wrong_user__my_userid_is___} = "Usuário inválido: meu userid é \$>, no lugar de \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Somente os usuários autorizados podem ver os resumos de PCs."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Somente os usuários autorizados podem iniciar ou parar as cópias" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Número inválido \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "Não pode abrir \$file: problema de configuração?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Somente os usuários autorizados podem ver registros ou arquivos de configuração."; $Lang{Only_privileged_users_can_view_log_files} = "Somente os usuários autorizados podem ver arquivos de registro."; $Lang{Only_privileged_users_can_view_email_summaries} = "Somente os usuários autorizados podem ver resumos de email."; $Lang{Only_privileged_users_can_browse_backup_files} = "Somente os usuários autorizados podem revisar os arquivos de backup" . " for host \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Número de host vazio."; $Lang{Directory___EscHTML} = "O diretório \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " está vazio"; $Lang{Can_t_browse_bad_directory_name2} = "Não pode mostrar um nome de diretório inválido" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Somente os usuários autorizados podem restaurar backups" . " para o host \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Nome de host inválido \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "Não foi selecionado nenhum arquivo; por favor, volte e" . " selecione alguns arquivos."; $Lang{You_haven_t_selected_any_hosts} = "Não foi selecionado nenhum host; por favor volte e" . " selecione algum host."; $Lang{Nice_try__but_you_can_t_put} = "Boa tentativa, mas não pode usar \'..\' nos nomes de arquivo"; $Lang{Host__doesn_t_exist} = "O Host \${EscHTML(\$In{hostDest})} não existe"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "Sem autorização para restaurar neste host" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Impossível abrir/criar " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Somente os usuários autorizados podem restaurar backups" . " do host \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Nome de host vazio"; $Lang{Unknown_host_or_user} = "Usuário ou host inválido \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Somente os usuários autorizados podem ver informações do" . " host \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Somente os administradores podem ver informações de arquivo."; $Lang{Only_privileged_users_can_view_restore_information} = "Somente os usuários autorizados podem ver informações de restauração."; $Lang{Restore_number__num_for_host__does_not_exist} = "O número de restauração \$num del host \${EscHTML(\$host)} " . " não existe."; $Lang{Archive_number__num_for_host__does_not_exist} = "O backup \$num do host \${EscHTML(\$host)} " . " não existe."; $Lang{Can_t_find_IP_address_for} = "Impossível encontrar o endereço do IP de \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < Até que tenha \$host um endereço num DHCP válido, se pode començar este processo a partir da própria máquina cliente. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "Solicitação de backup em DHCP \$host (\$In{hostIP}) por" . " \$User desde \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Solicitação de backup em \$host por \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Backup parado/desprogramado em \$host por \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Restauração solicitada para o host \$hostDest, backup #\$num," . " por \$User desde \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Arquivo solicitado por \$User desde \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "Estado"; $Lang{PC_Summary} = "Resumo PC"; $Lang{LOG_file} = "Arquivo de Log"; $Lang{LOG_files} = "Arquivos de Log"; $Lang{Old_LOGs} = "Logs antigos"; $Lang{Email_summary} = "Resumo Email"; $Lang{Config_file} = "Arquivo configuração"; # $Lang{Hosts_file} = "Arquivo Hosts"; $Lang{Current_queues} = "Filas atuais"; $Lang{Documentation} = "Documentação"; #$Lang{Host_or_User_name} = "Host ou usuário:"; $Lang{Go} = "Aceitar"; $Lang{Hosts} = "Hosts"; $Lang{Select_a_host} = "Selecione um host..."; $Lang{There_have_been_no_archives} = "

    Não existem arquivos

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    Nunca foi feito backup deste PC!

    \n"; $Lang{This_PC_is_used_by} = "
  • Este PC é utilizado por \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Extraindo somente Erros)"; $Lang{XferLOG} = "TransfLOG"; $Lang{Errors} = "Erros"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <Última mensagem enviada a \${UserLink(\$user)} foi às \$mailTime, assunto "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <O comando \$cmd está executando para \$host, iniciado às \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <O host \$host está em fila para ser processado em segundo plano (logo o backup estará pronto!). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <Host \$host está para ser processado na fila de usuarios (logo o backup estará pronto!). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <Uma execução para \$host estar na fila de execuções (iniciará a seguir). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <O último estado foi \"\$Lang->{\$StatusHost{state}}\"\$reason às \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <O último erro foi \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <Os pings para \$host falharam \$StatusHost{deadCnt} vezes consecutivas. EOF # ----- $Lang{Prior_to_that__pings} = "Antes destes, pings"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr a \$host obtiveram êxito \$StatusHost{aliveCnt} vezes consecutivas. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Dado que \$host tem estado em uso na rede pelo menos \$Conf{BlackoutGoodCnt} vezes consecutivas, não se realizará backup das \$blackoutStr. EOF $Lang{__time0_to__time1_on__days} = "\$t0 até \$t1 em \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Os backups atrazaram-se durante \$hours hours (Troque este número). EOF $Lang{tryIP} = " y \$StatusHost{dhcpHostIP}"; #$Lang{Host_Inhost} = "Host \$In{host}"; $Lang{checkAll} = <  Selecionar tudo EOF $Lang{checkAllHosts} = <  Selecionar tudo EOF $Lang{fileHeader} = < Nome Tipo Modo Nº Tamanho Hora Mod. EOF $Lang{Home} = "Principal"; $Lang{Browse} = "Explorar backups"; $Lang{Last_bad_XferLOG} = "Último erro no Log de Transferência"; $Lang{Last_bad_XferLOG_errors_only} = "Último erro no Log de transferência (erros somente)"; $Lang{This_display_is_merged_with_backup} = < Este quadro pertence ao backup Nº\$numF. EOF $Lang{Visit_this_directory_in_backup} = < Selecione o backup que desseja ver: EOF $Lang{Restore_Summary} = < Clique no número da restauração para ver seus detalhes. \$restoreStr
    Restauração Nº Resultado Data Inicio Dur/mins Nº Arquivos MB Nº Err. Tar Nº Err. Transf.#xferErrs

    EOF $Lang{Archive_Summary} = < Clique no número do arquivo para mais detalhes. \$ArchiveStr
    Archive# Resultado Hora início Dur/min

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Documentação"; $Lang{No} = "não"; $Lang{Yes} = "sim"; $Lang{The_directory_is_empty} = <O diretório \$dirDisplay está vazio EOF #$Lang{on} = "ativo"; $Lang{off} = "inativo"; $Lang{backupType_full} = "completo"; $Lang{backupType_incr} = "incremental"; $Lang{backupType_partial} = "parcial"; $Lang{failed} = "falhado"; $Lang{success} = "sucesso"; $Lang{and} = "e"; # ------ # Hosts states and reasons $Lang{Status_idle} = "inativo"; $Lang{Status_backup_starting} = "iniciando backup"; $Lang{Status_backup_in_progress} = "backup em execução"; $Lang{Status_restore_starting} = "iniciando restauração"; $Lang{Status_restore_in_progress} = "restauração em execução"; $Lang{Status_link_pending} = "conexão pendente"; $Lang{Status_link_running} = "conexão em curso"; $Lang{Reason_backup_done} = "backup realizado"; $Lang{Reason_restore_done} = "restauração realizada"; $Lang{Reason_archive_done} = "arquivamento realizado"; $Lang{Reason_nothing_to_do} = "nada a fazer"; $Lang{Reason_backup_failed} = "falha no backup"; $Lang{Reason_restore_failed} = "falha na restauração"; $Lang{Reason_archive_failed} = "falha no arquivamento"; $Lang{Reason_no_ping} = "sem ping"; $Lang{Reason_backup_canceled_by_user} = "backup cancelado pelo usuário"; $Lang{Reason_restore_canceled_by_user} = "restauração cancelada pelo usuário"; $Lang{Reason_archive_canceled_by_user} = "arquivamento cancelado pelo usuário"; $Lang{Disabled_OnlyManualBackups} = "ENG auto disabled"; $Lang{Disabled_AllBackupsDisabled} = "ENG disabled"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: nenhum backup de \$host foi terminado com êxito"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Caro $userName, Em seu PC ($host) nenhum backup foi completado por nosso programa de backup. Os backups deveriam ser executados automaticamente quando seu PC se conecta a rede. Contate seu suporte técnico se: - Seu computador está conectado a rede com regularidade. Isto significa que existe algum problema de instalação ou configuração que impessa a realização dos backups. - Não deseja realizar backups e não quer receber mais mensagens como esta. Caso contrário, assegure-se de que seu PC está conectado à rede na próxima vez que estiver utilizando-o. Saudações: Agente BackupPC http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: não existem backups recentes de \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Caro $userName, Não foi completado nenhum backup completo de seu PC ($host) durante $days dias. Seu PC tem realizado backups corretos $numBackups vezes desde $firstTime até $days dias. Os backups deveriam efetuar-se automaticamente quando seu PC estiver conectado a rede. Se seu PC tem estado conectado durante algumas horas a rede durante os últimos $days dias deveria contactar com seu suporte técnico para ver porque os backups não funcionam adequadamente. Por outro lado, se você não o está utilizando, não há muito o que fazer a não ser copiar manualmente os arquivos mais críticos para outro suporte físico. Deve-se estar ciente de que qualquer arquivo que tenha sido criado ou modificado nos últimos $days dias (incluindo todos os emails novos e arquivos anexos) não podem ser restaurados se seu disco danificar-se. Saudações: Agente BackupPC http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Oss arquivos do Outlook de \$host necessitam ser copiados"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Caro $userName, Os arquivos de Outlook de seu PC tem $howLong. Estes arquivos contém todo seus emails, anexos, contatos e informações de sua agenda. Seu PC tem sido corretamente salvaguardado $numBackups vezes desde $firstTime até $lastTime dias. Sem fechá-lo, Outlook bloqueia todos seus arquivos quando estão em execução, impidindo de se fazer backup dos mesmo. Recomendamos fazer cópia de segurança dos arquivos do Outlook quando estiver conectado a rede fechando o Outlook e o resto das aplicações e utilizando seu navegador de internet. Clique neste link: $CgiURL?host=$host Selecione "Começar backup incremental" duas vezes para começar um novo backup incremental. Pode-se selecionar "Voltar a página de $host " e clicar em "refazer" para ver o estado do processo de backup. Este processo deve durar somente alguns minutos para completar. Saudações: Agente BackupPC http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "não foi realizado nenhum backup com êxito"; $Lang{howLong_not_been_backed_up_for_days_days} = "não foi realizado nenhum backup durante \$days dias"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "Servidor BackupPC"; $Lang{RSS_Doc_Description} = "RSS feed do BackupPC"; $Lang{RSS_Host_Summary} = < Note: Marque Override se você quiser modificar um valor especificamente neste host.

    EOF $Lang{CfgEdit_Button_Save} = "Salvar"; $Lang{CfgEdit_Button_Insert} = "Inserir"; $Lang{CfgEdit_Button_Delete} = "Excluir"; $Lang{CfgEdit_Button_Add} = "Adicionar"; $Lang{CfgEdit_Button_Override} = "Sobrepor"; $Lang{CfgEdit_Button_New_Key} = "New Key"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "ENG Error: No save due to errors"; $Lang{CfgEdit_Error__must_be_an_integer} = "Erro: \$var precisa ser um inteiro"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Erro: \$var precisa ser um número com valor-real"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Erro: \$var inserida \$k precisa ser um inteiro"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Erro: \$var inserida \$k precisa ser um número com valor-real"; $Lang{CfgEdit_Error__must_be_executable_program} = "Erro: \$var precisa ser um caminho executável válido"; $Lang{CfgEdit_Error__must_be_valid_option} = "Erro: \$var precisa ser uma opção válida"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "Copia host \$copyHost não existe; criando nome de host completo \$fullHost. Exclua este hosts se náo for o que você deseja."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User configuração copiada do host \$fromHost para \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User excluido \$p do \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User adicionado \$p para \$conf, marcado para \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User alterado \$p em \$conf para \$valueNew de \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User excluido host \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User host \$host alterado \$key de \$valueOld para \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User adicionado host \$host: \$value\n"; #end of lang_pt_BR.pm BackupPC-3.3.2/lib/BackupPC/Lang/ru.pm0000444000076500000240000017441313042250554016172 0ustar craigstaff#!/usr/bin/perl # # by Sergei Butakov (2011-05-1x - 2011-05-2x for V3.2.1) # #my %lang; #use strict; use utf8; # -------------------------------- $Lang{Start_Archive} = "Ðачать Ðрхивирование"; $Lang{Stop_Dequeue_Archive} = "ОÑтановить/Убрать из Очереди"; $Lang{Start_Full_Backup} = "Ðачать Полн. Копирование"; $Lang{Start_Incr_Backup} = "Ðачать Инкр. Копирование"; $Lang{Stop_Dequeue_Backup} = "ОÑтановить/Убрать из Очереди"; $Lang{Restore} = "ВоÑÑтановить"; $Lang{Type_full} = "полн."; $Lang{Type_incr} = "инкр."; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Only privileged users can view admin options."; $Lang{H_Admin_Options} = "BackupPC Server: Admin Options"; $Lang{Admin_Options} = "ÐдминиÑтрирование"; $Lang{Admin_Options_Page} = < \${h2("Управление Сервером")}

    Перезагрузить наÑтройки Ñервера:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Ðе могу подключитьÑÑ Ðº Ñерверу BackupPC"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < The error was: \$err.
    Perhaps the BackupPC server is not running or there is a configuration error. Please report this to your Sys Admin. EOF $Lang{Admin_Start_Server} = < The BackupPC server at \$Conf{ServerHost} port \$Conf{ServerPort} is not currently running (maybe you just stopped it, or haven't yet started it).
    Do you want to start it? EOF # ----- $Lang{H_BackupPC_Server_Status} = "СоÑтоÑние Сервера BackupPC"; $Lang{BackupPC_Server_Status_General_Info}= <
  • PID Ñервера \$Info{pid}, верÑÐ¸Ñ \$Info{Version}, запущен \$serverStartTime на узле \$Conf{ServerHost}.
  • Данный отчёт был Ñформирован \$now.
  • ÐаÑтройки поÑледний раз загружалиÑÑŒ \$configLoadTime.
  • Ð’ Ñледующий раз ПК будут поÑтавлены в очередь запроÑов \$nextWakeupTime.
  • ÐŸÑ€Ð¾Ñ‡Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ:
    • \$numBgQueue запроÑов в очереди на резервирование (Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° поÑледнего запуÑка планировщика);
    • \$numUserQueue запроÑов в пользовательÑкой очереди на резервирование;
    • \$numCmdQueue запроÑов в очереди на выполнение команд; \$poolInfo
    • Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема пула занÑта на \$Info{DUlastValue}% (\$DUlastTime), ÑегоднÑшний макÑимум \$Info{DUDailyMax}% (\$DUmaxTime), вчерашний макÑимум \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Работы, выполнÑемые в данный момент времени")}

    \$jobStr
    Узел Тип Пользователь Ð’Ñ€ÐµÐ¼Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° Команда PID Xfer PID

    \${h2("Сбои, нуждающиеÑÑ Ð²Ð½Ð¸Ð¼Ð°Ð½Ð¸Ñ")}

    \$statusStr
    Узел Тип Пользователь ПоÑледнÑÑ Ð¿Ð¾Ð¿Ñ‹Ñ‚ÐºÐ° Детали Ð’Ñ€ÐµÐ¼Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¸ ПоÑледнÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° (не ÑÑ‡Ð¸Ñ‚Ð°Ñ Ð¾Ñ‚ÑутÑтвие \'пинга\')
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "Сводка по Узлам"; $Lang{BackupPC__Archive} = "Ðрхивирование"; $Lang{BackupPC_Summary} = <

    • Данный отчёт был Ñформирован \$now.
    • Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема пула занÑта на \$Info{DUlastValue}% (\$DUlastTime), ÑегоднÑшний макÑимум \$Info{DUDailyMax}% (\$DUmaxTime), вчерашний макÑимум \$Info{DUDailyMaxPrev}%.

    \${h2("Узлы, имеющие резервные копии")}

    Ð’Ñего \$hostCntGood узлов, которые Ñодержат:

    • \$fullTot полных резервных копий общим размером \${fullSizeTot}GB (до Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¸ ÑжатиÑ);
    • \$incrTot инкрементальных резервных копий общим размером \${incrSizeTot}GB (до Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¸ ÑжатиÑ).

    \$strGood
    Узел Поль-ль Кол-во ПОЛÐ. копий ПОЛÐ. возраÑÑ‚ (дни) ПОЛÐ. размер (ГБ) СкороÑть (МБ/Ñ) Кол-во ИÐКР. копий ИÐКР. возраÑÑ‚ (дни) ПоÑл. копир-ие (дни) СоÑтоÑние ТранÑп. ошибок ПоÑледнее дейÑтвие


    \${h2("Узлы, не имеющие резервные копии")}

    Ð’Ñего \$hostCntNone узлов, не имеющих резервных копий.

    \$strNone
    Узел Поль-ль Кол-во ПОЛÐ. копий ПОЛÐ. возраÑÑ‚ (дни) ПОЛÐ. размер (ГБ) СкороÑть (МБ/Ñ) Кол-во ИÐКР. копий ИÐКР. возраÑÑ‚ (дни) ПоÑледн. копир-ие (дни) СоÑтоÑние ТранÑп. ошибок ПоÑледнее дейÑтвие
    EOF $Lang{BackupPC_Archive} = < Ð’Ñего \$hostCntGood узлов, чьи резервные копии занимают в общем \${fullSizeTot} ГБ.

    \$strGood \$checkAllHosts
    Узел Пользователь Размер Копии

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < РаÑположение/УÑтройÑтво Ðрхива EOF $Lang{BackupPC_Archive2_compression} = < Сжатие Ðет
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < Percentage of Parity Data (0 = disable, 5 = typical) EOF $Lang{BackupPC_Archive2_split} = < Разделить на чаÑти по МБ EOF # ----------------------------------- $Lang{Pool_Stat} = <Пул занимает \${poolSize}GB, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ \$info->{"\${name}FileCnt"} файлов и \$info->{"\${name}DirCnt"} каталогов (по данным на \$poolTime);
  • При хешировании пула произошло \$info->{"\${name}FileCntRep"} коллизии, макÑимальное количеÑтво файлов в одной коллизии - \$info->{"\${name}FileRepMax"};
  • Во Ð²Ñ€ÐµÐ¼Ñ Ð½Ð¾Ñ‡Ð½Ð¾Ð¹ очиÑтки было удалено \$info->{"\${name}FileCntRm"} файлов общим размером \${poolRmSize}GB (в районе \$poolTime); EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: Backup Requested on \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < Ответ Ñ Ñервера: \$reply

    ВернутьÑÑ Ð½Ð° Главную Ñтраницу узла \$host. EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Start Backup Confirm on \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Резервное \$type копирование узла \$host.

    Ð’Ñ‹ уверены, что хотите Ñделать Ñто?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Stop Backup Confirm on \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < ОÑтановка/удаление из очереди узла \$host.
    Также, не начинать другое резервное копирование в течение чаÑов.

    Ð’Ñ‹ уверены, что хотите Ñделать Ñто?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Only privileged users can view queues."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Only privileged users can Archive."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Queue Summary"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Очередь ПользовательÑких Задач")}

    Следующие запроÑÑ‹ находÑÑ‚ÑÑ Ð² очереди:

    \$strUser
    Узел Ð’Ñ€ÐµÐ¼Ñ Ð—Ð°Ð¿Ñ€Ð¾Ñа Пользователь


    \${h2("Очередь Фоновых Задач")}

    Следующие фоновые запроÑÑ‹ находÑÑ‚ÑÑ Ð² очереди:

    \$strBg
    Узел Ð’Ñ€ÐµÐ¼Ñ Ð—Ð°Ð¿Ñ€Ð¾Ñа Пользователь


    \${h2("Очередь Команд")}

    Следующие команды находÑÑ‚ÑÑ Ð² очереди:

    \$strCmd
    Узел Ð’Ñ€ÐµÐ¼Ñ Ð—Ð°Ð¿Ñ€Ð¾Ñа Пользователь Команда
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: File \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, Ñ Ð¿Ð¾Ñледними изменениÑми от \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ пропущено \$skipped Ñтрок ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nÐе могу открыть журнальный файл \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: Log File History";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    Файл Размер Ð’Ñ€ÐµÐ¼Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Получатель Узел Ð’Ñ€ÐµÐ¼Ñ Ð¢ÐµÐ¼Ð°
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: Browse backup \$num for \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Restore Options for \$host"; $Lang{Restore_Options_for__host2} = < Ð’Ñ‹ выбрали Ñледующие файлы/каталоги из реÑурÑа \$share, номер копии â„– \$num:
      \$fileListStr

    Выберите один из трёх ÑпоÑобов воÑÑтановлениÑ.

    \${h2("СпоÑоб 1: ПрÑмое ВоÑÑтановление")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    Внимание: вÑе ÑущеÑтвующие файлы, Ñовпадающие Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ð¼Ð¸, будут перепиÑаны!

    \$hiddenStr
    ВоÑÑтановить на узел
    ВоÑÑтановить на реÑурÑ
    ВоÑÑтановить в каталог
    (отноÑительно реÑурÑа)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("СпоÑоб 2: Загрузка Zip-архива")}

    Ð’Ñ‹ можете загрузить Zip-архив, Ñодержащий вÑе выбранные файлы и каталоги. ПоÑле чего, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ðµ приложение, такое как WinZip, можно проÑмотреть или разархивировать любые файлы.

    Внимание: в завиÑимоÑти от выбранных Вами файлов/каталогов, Ñтот архив может быть очень очень большим. Ðа Ñоздание и передачу такого архива может уйти много времени, и Вам понадобитÑÑ Ð´Ð¾Ñтаточно много меÑта на локальном диÑке Ð´Ð»Ñ ÐµÐ³Ð¾ хранениÑ.

    \$hiddenStr Создать архив отноÑительно \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (в противном Ñлучае файлы в архиве будут иметь полные пути).
    Степень ÑÐ¶Ð°Ñ‚Ð¸Ñ (0=нет, 1=ÑÐ°Ð¼Ð°Ñ Ð±Ñ‹ÑтраÑ, ..., 9=ÑÐ°Ð¼Ð°Ñ Ð±Ð¾Ð»ÑŒÑˆÐ°Ñ)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Option 2: Download Zip archive")}

    Archive::Zip is not installed so you will not be able to download a zip archive. Please ask your system adminstrator to install Archive::Zip from www.cpan.org.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < Ð’Ñ‹ можете загрузить Tar-архив, Ñодержащий вÑе выбранные файлы и каталоги. ПоÑле чего, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ðµ приложение, такое как tar или WinZip, можно проÑмотреть или разархивировать любые файлы.

    Внимание: в завиÑимоÑти от выбранных Вами файлов/каталогов, Ñтот архив может быть очень очень большим. Ðа Ñоздание и передачу такого архива может уйти много времени, и Вам понадобитÑÑ Ð´Ð¾Ñтаточно много меÑта на локальном диÑке Ð´Ð»Ñ ÐµÐ³Ð¾ хранениÑ.

    \$hiddenStr Создать архив отноÑительно \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (в противном Ñлучае файлы в архиве будут иметь полные пути).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: Restore Confirm on \$host"; $Lang{Are_you_sure} = < Следующие файлы будут воÑÑтановлены напрÑмую на узел \$In{hostDest} в реÑÑƒÑ€Ñ \$In{shareDest}, из резервной копии â„– \$num:

    \$fileListStr
    Оригинальный файл/каталогБудет воÑÑтановлен как

    \$hiddenStr Вы уверены?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Restore Requested on \$hostDest"; $Lang{Reply_from_server_was___reply} = < Ответ Ñ Ñервера: \$reply

    ВернутьÑÑ Ð½Ð° Главную Ñтраницу узла \$hostDest. EOF $Lang{BackupPC_Archive_Reply_from_server} = < Ответ Ñ Ñервера: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Host \$host Backup Summary"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("ПользовательÑкие ДейÑтвиÑ")}

    \$startIncrStr

    \${h2("Сводка Резервного КопированиÑ")}

    Щёлкните по номеру Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра и воÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñкопированных файлов.

    \$str
    â„– Тип Полный Уровень Дата Ðачала ДлительноÑть(мин) ВозраÑÑ‚(дни) Локальный Путь Копии

    \$restoreStr



    \${h2("Сводка Ошибок при Копировании")}

    \$errStr
    â„– Тип Журнал ТранÑп. ошибок Плохих файлов РеÑурÑ. проблем tar ошибок


    \${h2("Сводка по Файлам")}

    СущеÑтвующие файлы - файлы, уже находÑщиеÑÑ Ð² пуле. Ðовые Ñто те, которые добавлены к пулу. ПуÑтые файлы не учитываютÑÑ. Empty files and SMB errors aren\'t counted in the reuse and new counts.

    \$sizeStr
    Ð’Ñего СущеÑтвующие Файлы Ðовые Файлы
    â„– Тип Файлов Размер(МБ) МБ/Ñ Ð¤Ð°Ð¹Ð»Ð¾Ð² Размер(МБ) Файлов Размер(МБ)


    \${h2("Сводка по Сжатию")}

    Степень ÑÐ¶Ð°Ñ‚Ð¸Ñ ÑущеÑтвующих и новых файлов.

    \$compStr
    СущеÑтвующие Файлы Ðовые Файлы
    â„– Тип Уровень Ð¡Ð¶Ð°Ñ‚Ð¸Ñ Ð Ð°Ð·Ð¼ÐµÑ€(МБ) Сжатый(МБ) Степень Ñж. Размер(МБ) Сжатый(МБ) Степень Ñж.


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Host \$host Archive Summary"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("ПользовательÑкие ДейÑтвиÑ")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Error"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Сервер"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • ÐšÐ¾Ð¿Ð¸Ñ â„– \$num, Ñоздание которой было начато примерно \$backupTime (\$backupAge дней назад), \$filledBackup
    • Введите каталог:
    • Щёлкните на каталог, чтобы увидеть его Ñодержимое.
    • Щёлкните на файл, чтобы воÑÑтановить его.
    • ИÑÑ‚Ð¾Ñ€Ð¸Ñ ÐºÐ¾Ð¿Ð¸Ð¹ текущего каталога.
    \${h2("Содержание каталога \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: Directory backup history for \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "dir"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < ЗдеÑÑŒ показаны вÑе уникальные верÑии файлов, находÑщиеÑÑ Ð²Ð¾ вÑех резервных копиÑÑ…:
    • Щёлкните по номеру копии Ð´Ð»Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‚Ð° к проÑмотру копии;
    • Щёлкните по ÑÑылке на каталог (\$Lang->{DirHistory_dirLink}) Ð´Ð»Ñ Ð·Ð°Ñ…Ð¾Ð´Ð° в Ñтот каталог;
    • Щёлкните по верÑии файла (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Ñтого файла;
    • Файлы из разных резервных копий, Ñодержащие одно и то же, имеют один и тот же номер верÑии;
    • Файлы и каталоги, отÑутÑтвующие в конкретной копии, показаны пуÑтым прÑмоугольником;
    • Файлы одной верÑии могут отличатьÑÑ Ñ€Ð°Ð·Ð½Ñ‹Ð¼Ð¸ атрибутами файловой ÑиÑтемы. Выберите номер копии, чтобы поÑмотреть Ñти атрибуты.
    \${h2("ИÑÑ‚Ð¾Ñ€Ð¸Ñ \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Ðомер копии
    Ð’Ñ€ÐµÐ¼Ñ ÐºÐ¾Ð¿Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Restore #\$num details for \$host"; $Lang{Restore___num_details_for__host2} = <
    Ðомер \$Restores[\$i]{num}
    ЗапроÑил \$RestoreReq{user}
    Ð’Ñ€ÐµÐ¼Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа \$reqTime
    Результат \$Restores[\$i]{result}
    ТекÑÑ‚ ошибки \$Restores[\$i]{errorMsg}
    ИÑходÑщий узел \$RestoreReq{hostSrc}
    Ðомер иÑходÑщей копии \$RestoreReq{num}
    ИÑходÑщий реÑÑƒÑ€Ñ \$RestoreReq{shareSrc}
    Узел Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ \$RestoreReq{hostDest}
    РеÑÑƒÑ€Ñ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ \$RestoreReq{shareDest}
    Ð’Ñ€ÐµÐ¼Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° \$startTime
    ПродолжительноÑть \$duration мин
    КоличеÑтво файлов \$Restores[\$i]{nFiles}
    Общий размер \${MB} МБ
    СкороÑть передачи \$MBperSec МБ/Ñ
    Ошибок при Ñоздании Tar \$Restores[\$i]{tarCreateErrs}
    Ошибок при передаче \$Restores[\$i]{xferErrs}
    Журнал ВеÑÑŒ, Только ошибки

    \${h1("СпиÑок Файлов/Каталогов")}

    \$fileListStr
    Оригинальный файл/каталогВоÑÑтановлен как
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Archive #\$num details for \$host"; $Lang{Archive___num_details_for__host2 } = <
    Ðомер \$Archives[\$i]{num}
    ЗапроÑил \$ArchiveReq{user}
    Ð’Ñ€ÐµÐ¼Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа \$reqTime
    Результат \$Archives[\$i]{result}
    Сообщение об ошибке \$Archives[\$i]{errorMsg}
    Ð’Ñ€ÐµÐ¼Ñ Ð·Ð°Ð¿ÑƒÑка \$startTime
    ПродолжительноÑть \$duration min
    Журнал передачи данных ПроÑмотреть, Только ошибки

    \${h1("СпиÑок узлов")}

    \$HostListStr
    УзелÐомер резервной копии
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Email Summary"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new failed: check apache error_log\n"; $Lang{Wrong_user__my_userid_is___} = "Wrong user: my userid is \$>, instead of \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Only privileged users can view PC summaries."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Only privileged users can stop or start backups on" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Invalid number \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "Unable to open \$file: configuration problem?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Only privileged users can view log or config files."; $Lang{Only_privileged_users_can_view_log_files} = "Only privileged users can view log files."; $Lang{Only_privileged_users_can_view_email_summaries} = "Only privileged users can view email summaries."; $Lang{Only_privileged_users_can_browse_backup_files} = "Only privileged users can browse backup files" . " for host \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Empty host name."; $Lang{Directory___EscHTML} = "Directory \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " is empty"; $Lang{Can_t_browse_bad_directory_name2} = "Ðе могу проÑмотреть каталог Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ñ‹Ð¼ названием" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Only privileged users can restore backup files" . " for host \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Bad host name \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "Ð’Ñ‹ не выбрали ни один файл."; $Lang{You_haven_t_selected_any_hosts} = "You haven\'t selected any hosts; please go Back to" . " select some hosts."; $Lang{Nice_try__but_you_can_t_put} = "Nice try, but you can\'t put \'..\' in any of the file names"; $Lang{Host__doesn_t_exist} = "Host \${EscHTML(\$In{hostDest})} doesn\'t exist"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "You don\'t have permission to restore onto host" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "Can\'t open/create " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Only privileged users can restore backup files" . " for host \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Empty host name"; $Lang{Unknown_host_or_user} = "ÐеизвеÑтный узел или пользователь \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Only privileged users can view information about" . " host \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Only privileged users can view archive information."; $Lang{Only_privileged_users_can_view_restore_information} = "Only privileged users can view restore information."; $Lang{Restore_number__num_for_host__does_not_exist} = "Restore number \$num for host \${EscHTML(\$host)} does" . " not exist."; $Lang{Archive_number__num_for_host__does_not_exist} = "Archive number \$num for host \${EscHTML(\$host)} does" . " not exist."; $Lang{Can_t_find_IP_address_for} = "Can\'t find IP address for \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < Until I see \$host at a particular DHCP address, you can only start this request from the client machine itself. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "Резервное копирование DHCP узла \$host (\$In{hostIP}) запроÑил" . " \$User Ñ \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Резервное копирование узла \$host запроÑил \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Резервное копирование Ð´Ð»Ñ ÑƒÐ·Ð»Ð° \$host оÑтановил/убрал из очереди \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "ВоÑÑтановление на узел \$hostDest, копию â„– \$num," . " запроÑил \$User Ñ \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Ðрхивирование запроÑил \$User Ñ \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "СоÑтоÑние"; $Lang{PC_Summary} = "Сводка по Узлам"; $Lang{LOG_file} = "Журнал"; $Lang{LOG_files} = "Старые журналы"; $Lang{Old_LOGs} = "Старые журналы"; $Lang{Email_summary} = "Сводка по ПиÑьмам"; $Lang{Config_file} = "Config file"; # $Lang{Hosts_file} = "Hosts file"; $Lang{Current_queues} = "Сводка по ОчередÑм"; $Lang{Documentation} = "РуководÑтво"; #$Lang{Host_or_User_name} = "Host or User name:"; $Lang{Go} = "Ðайти"; $Lang{Hosts} = "Узлы"; $Lang{Select_a_host} = "Выбрать узел ..."; $Lang{There_have_been_no_archives} = "

    Ðрхивы отÑутÑтвуют

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    Данный ПК ни разу не резервировалÑÑ!!

    \n"; $Lang{This_PC_is_used_by} = "
  • Данный ПК иÑпользует \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Выбраны только ошибки)"; $Lang{XferLOG} = "ВеÑÑŒ"; $Lang{Errors} = "Только Ошибки"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <ПоÑледнее пиÑьмо было отправлено \$mailTime, Ñ Ñ‚ÐµÐ¼Ð¾Ð¹ "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <Команда \$cmd выполнÑетÑÑ Ð´Ð»Ñ ÑƒÐ·Ð»Ð° \$host, запущена \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <Узел \$host поÑтавлен в фоновую очередь (Ñкоро будет запущено резервное копирование). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <Узел \$host поÑтавлен в пользовательÑкую очередь (Ñкоро будет запущено резервное копирование). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <Команда Ð´Ð»Ñ ÑƒÐ·Ð»Ð° \$host поÑтавлена в очередь команд (Ñкоро будет запущена). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <СоÑтоÑние \"\$Lang->{\$StatusHost{state}}\"\$reason на \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <ПоÑледнÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ°: \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <"Пропинговать" узел \$host не удалоÑÑŒ \$StatusHost{deadCnt} раз(а) подрÑд. EOF # ----- $Lang{Prior_to_that__pings} = "Prior to that, pings"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr to \$host были уÑпешны \$StatusHost{aliveCnt} раз(а) подрÑд. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Because \$host has been on the network at least \$Conf{BlackoutGoodCnt} consecutive times, it will not be backed up from \$blackoutStr. EOF $Lang{__time0_to__time1_on__days} = "\$t0 to \$t1 on \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Backups are deferred for \$hours hours (change this number). EOF $Lang{tryIP} = " and \$StatusHost{dhcpHostIP}"; # $Lang{Host_Inhost} = "Host \$In{host}"; $Lang{checkAll} = <  Ð’ыбрать вÑÑ‘ EOF $Lang{checkAllHosts} = <  Ð’ыбрать вÑÑ‘ EOF $Lang{fileHeader} = < Ðазвание Тип Права â„– Размер Дата Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ EOF $Lang{Home} = "ГлавнаÑ"; $Lang{Browse} = "ПроÑмотр резервной копии"; $Lang{Last_bad_XferLOG} = "ПоÑледний журнал Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°Ð¼Ð¸"; $Lang{Last_bad_XferLOG_errors_only} = "ПоÑледний журнал Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°Ð¼Ð¸ (только Ð¾ÑˆÐ¸Ð±ÐºÐ¸)"; $Lang{This_display_is_merged_with_backup} = < Данное отображение объединено Ñ ÐºÐ¾Ð¿Ð¸ÐµÐ¹ â„– \$numF. EOF $Lang{Visit_this_directory_in_backup} = < Выберите номер копии Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра: EOF $Lang{Restore_Summary} = < Щёлкните по номеру Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ детального проÑмотра. \$restoreStr
    â„– Результат Дата начала ДлительноÑть(мин) Кол-во файлов Размер(МБ) tar ошибок ТранÑп. ошибок

    EOF $Lang{Archive_Summary} = < Щёлкните по номеру архива Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ детального проÑмотра. \$ArchiveStr
    â„– Результат Ð’Ñ€ÐµÐ¼Ñ Ð—Ð°Ð¿ÑƒÑка ПродолжительноÑть(мин)

    EOF $Lang{BackupPC__Documentation} = "BackupPC: Documentation"; $Lang{No} = "нет"; $Lang{Yes} = "да"; $Lang{The_directory_is_empty} = <The directory \$dirDisplay is empty EOF #$Lang{on} = "on"; $Lang{off} = "откл."; $Lang{backupType_full} = "полн."; $Lang{backupType_incr} = "инкр."; $Lang{backupType_partial} = "чаÑтичный"; $Lang{failed} = "неудачно"; $Lang{success} = "уÑпешно"; $Lang{and} = "и"; # ------ # Hosts states and reasons $Lang{Status_idle} = "бездейÑтвует"; $Lang{Status_backup_starting} = "началоÑÑŒ копирование"; $Lang{Status_backup_in_progress} = "в процеÑÑе копированиÑ"; $Lang{Status_restore_starting} = "началоÑÑŒ воÑÑтановление"; $Lang{Status_restore_in_progress} = "в процеÑÑе воÑÑтановлениÑ"; $Lang{Status_link_pending} = "link pending"; $Lang{Status_link_running} = "link running"; $Lang{Reason_backup_done} = "копирование закончено"; $Lang{Reason_restore_done} = "воÑÑтановление закончено"; $Lang{Reason_archive_done} = "архивирование закончено"; $Lang{Reason_nothing_to_do} = "без работы"; $Lang{Reason_backup_failed} = "копирование не удалоÑÑŒ"; $Lang{Reason_restore_failed} = "воÑÑтановление не удалоÑÑŒ"; $Lang{Reason_archive_failed} = "архивирование не удалоÑÑŒ"; $Lang{Reason_no_ping} = "не \'пингуетÑÑ\'"; $Lang{Reason_backup_canceled_by_user} = "копирование прервано пользователем"; $Lang{Reason_restore_canceled_by_user} = "воÑÑтановление прервано пользователем"; $Lang{Reason_archive_canceled_by_user} = "воÑÑтановление прервано пользователем"; $Lang{Disabled_OnlyManualBackups} = "автозапрет"; $Lang{Disabled_AllBackupsDisabled} = "запрещено"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: no backups of \$host have succeeded"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, Your PC ($host) has never been successfully backed up by our PC backup software. PC backups should occur automatically when your PC is connected to the network. You should contact computer support if: - Your PC has been regularly connected to the network, meaning there is some configuration or setup problem preventing backups from occurring. - You don't want your PC backed up and you want these email messages to stop. Otherwise, please make sure your PC is connected to the network next time you are in the office. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: no recent backups on \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, Your PC ($host) has not been successfully backed up for $days days. Your PC has been correctly backed up $numBackups times from $firstTime to $days days ago. PC backups should occur automatically when your PC is connected to the network. If your PC has been connected for more than a few hours to the network during the last $days days you should contact IS to find out why backups are not working. Otherwise, if you are out of the office, there's not much you can do, other than manually copying especially critical files to other media. You should be aware that any files you have created or changed in the last $days days (including all new email and attachments) cannot be restored if your PC disk crashes. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Outlook files on \$host need to be backed up"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, The Outlook files on your PC have $howLong. These files contain all your email, attachments, contact and calendar information. Your PC has been correctly backed up $numBackups times from $firstTime to $lastTime days ago. However, Outlook locks all its files when it is running, preventing these files from being backed up. It is recommended you backup the Outlook files when you are connected to the network by exiting Outlook and all other applications, and, using just your browser, go to this link: $CgiURL?host=$host Select "Start Incr Backup" twice to start a new incremental backup. You can select "Return to $host page" and then hit "reload" to check the status of the backup. It should take just a few minutes to complete. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "not been backed up successfully"; $Lang{howLong_not_been_backed_up_for_days_days} = "not been backed up for \$days days"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPC Server"; $Lang{RSS_Doc_Description} = "RSS feed for BackupPC"; $Lang{RSS_Host_Summary} = < Примечание: ПоÑтавьте галочку Ñ€Ñдом Ñ "Заменить", еÑли хотите изменить значение параметра индивидуально Ð´Ð»Ñ Ñтого узла.

    EOF $Lang{CfgEdit_Button_Save} = "Сохранить"; $Lang{CfgEdit_Button_Insert} = "Ð’Ñтавить"; $Lang{CfgEdit_Button_Delete} = "Удалить"; $Lang{CfgEdit_Button_Add} = "Добавить"; $Lang{CfgEdit_Button_Override} = "Заменить"; $Lang{CfgEdit_Button_New_Key} = "New Key"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "Ошибка: не Ñохранено из-за Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¾ÑˆÐ¸Ð±Ð¾Ðº"; $Lang{CfgEdit_Error__must_be_an_integer} = "Ошибка: \$var должно быть целым чиÑлом"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Ошибка: \$var должно быть дейÑтвительным чиÑлом"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Ошибка: \$var Ñлемент \$k должен быть целым чиÑлом"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Ошибка: \$var Ñлемент \$k должен быть дейÑтвительным чиÑлом"; $Lang{CfgEdit_Error__must_be_executable_program} = "Ошибка: \$var должно быть дейÑтвительным путём иÑполнÑемой программы"; $Lang{CfgEdit_Error__must_be_valid_option} = "Ошибка: \$var должно быть допуÑтимой опцией"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "Копируемый узел \$copyHost отÑутÑтвует, ÑоздаётÑÑ ÑƒÐ·ÐµÐ» \$fullHost. Удалите Ñтот узел, еÑли Ñто не то, что Вам требуетÑÑ."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User copied config from host \$fromHost to \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User deleted \$p from \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User added \$p to \$conf, set to \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User changed \$p in \$conf to \$valueNew from \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User deleted host \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User host \$host changed \$key from \$valueOld to \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User added host \$host: \$value\n"; #end of ru.pm BackupPC-3.3.2/lib/BackupPC/Lang/uk.pm0000444000076500000240000017474013042250554016166 0ustar craigstaff#!/usr/bin/perl # By Serhiy Yakimchuck yakim@yakim.org.ua 02 sept 2012 vor version 3.2.1 #my %lang; #use strict; use utf8; # -------------------------------- $Lang{Start_Archive} = "ЗапуÑтити архівуваннÑ"; $Lang{Stop_Dequeue_Archive} = "Припинити/видалити з черги"; $Lang{Start_Full_Backup} = " Зробити повний архів"; $Lang{Start_Incr_Backup} = "Зробити інкрементальний архів"; $Lang{Stop_Dequeue_Backup} = "Припилити/видалити з черги"; $Lang{Restore} = "Відновити"; $Lang{Type_full} = "повний"; $Lang{Type_incr} = "інкрементальний"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "Тільки привілейовані кориÑтувачі можуть бачити адмінÑькі налаштуваннÑ."; $Lang{H_Admin_Options} = "BackupPC Сервер: ÐдмінÑькі налаштуваннÑ"; $Lang{Admin_Options} = "ÐдмінÑькі налаштуваннÑ"; $Lang{Admin_Options_Page} = < \${h2("ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñервером")}

    Перечитати Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñервера:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Ðеможливо зв'ÑзатиÑÑ Ð· Ñервером BackupPC"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = < The error was: \$err.
    Perhaps the BackupPC server is not running or there is a configuration error. Please report this to your Sys Admin. EOF $Lang{Admin_Start_Server} = < The BackupPC server at \$Conf{ServerHost} port \$Conf{ServerPort} is not currently running (maybe you just stopped it, or haven't yet started it).
    Do you want to start it? EOF # ----- $Lang{H_BackupPC_Server_Status} = "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ñервера BackupPC"; $Lang{BackupPC_Server_Status_General_Info}= <
  • PID Ñервера - \$Info{pid}, на комп'ютері \$Conf{ServerHost}, верÑÑ–Ñ \$Info{Version}, запущений \$serverStartTime.
  • Цей ÑÑ‚Ð°Ñ‚ÑƒÑ Ð±ÑƒÐ² згенерований \$now.
  • ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð¾Ñтанній раз була завантажена \$configLoadTime.
  • ÐаÑтупного разу комп\'ютери будуть поÑтавлені до черги \$nextWakeupTime.
  • Інша інформаціÑ:
    • \$numBgQueue запитів в черзі на резервне ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð· чаÑу оÑтаннього запуÑку планувальника,
    • \$numUserQueue запитів в черзі кориÑтувачів на резервне копіюваннÑ,
    • \$numCmdQueue запитів в черзі на Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´, \$poolInfo
    • Файлова ÑиÑтема пула зайнÑта на \$Info{DUlastValue}% (\$DUlastTime), ÑьогоднÑшній макÑимум \$Info{DUDailyMax}% (\$DUmaxTime) вчорашній макÑимум \$Info{DUDailyMaxPrev}%.
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("Запущені зараз завданнÑ")}

    \$jobStr
    ХоÑÑ‚ Тип КориÑтувач Ñ‡Ð°Ñ Ð·Ð°Ð¿ÑƒÑку Команда PID Xfer PID

    \${h2("Помилки, що потребують уваги")}

    \$statusStr
    ХоÑÑ‚ Тип КориÑтувач ОÑÑ‚Ð°Ð½Ð½Ñ Ñпроба Деталі Ð§Ð°Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ¸ ОÑÑ‚Ð°Ð½Ð½Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° (крім відÑутноÑті пінга)
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ð¾ хоÑтах"; $Lang{BackupPC__Archive} = "BackupPC: Ðрхів"; $Lang{BackupPC_Summary} = <

    • Цей ÑÑ‚Ð°Ñ‚ÑƒÑ Ð±ÑƒÐ»Ð¾ згенеровано \$now.
    • Файлова ÑиÑтема пула зайнÑта на \$Info{DUlastValue}% (\$DUlastTime), ÑьогоднÑшній макÑимум \$Info{DUDailyMax}% (\$DUmaxTime) вчорашній макÑимум \$Info{DUDailyMaxPrev}%.

    \${h2("ХоÑти, що мають резервні копії")}

    Загалом \$hostCntGood хоÑтів, що міÑÑ‚Ñть:

    • \$fullTot загальний розмір повних резервних копій \${fullSizeTot}GB (до об\'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ‚Ð° ÑтиÑканнÑ),
    • \$incrTot загальний розмір інкрементальних резервних копій \${incrSizeTot}GB (до об\'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ‚Ð° ÑтиÑканнÑ).

    \$strGood
    ХоÑÑ‚ КориÑтувач #Кіль-ть повн. копій Вік повн. копій (дні) Повний розмір (GB) ШвидкіÑть (MB/s) #Кіль-ть інкр. копій Вік інкр. копій (дні) ОÑÑ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð¿Ñ–Ñ (days) Стан #Xfer помилки ОÑÑ‚Ð°Ð½Ð½Ñ Ð´Ñ–Ñ


    \${h2("ХоÑти без резервних копій")}

    Загалом \$hostCntNone хоÑтів без резервних копій.

    \$strNone
    ХоÑÑ‚ КориÑтувач #Кіль-ть повн. копій Вік повн. копій (дні) Повний розмір (GB) ШвидкіÑть (MB/s) #Кіль-ть інкр. копій Вік інкр. копій (дні) ОÑÑ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð¿Ñ–Ñ (days) Стан #Xfer помилки ОÑÑ‚Ð°Ð½Ð½Ñ Ð´Ñ–Ñ
    EOF $Lang{BackupPC_Archive} = < Ð’Ñього \$hostCntGood хоÑтів, що мають повну резервну копію загальним розміром \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    Host КориÑтувач Розмір копії

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < МіÑцезнаходженнÑ/ПриÑтрій архіву EOF $Lang{BackupPC_Archive2_compression} = < СтиÑÐºÐ°Ð½Ð½Ñ None
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < ВідÑоток даних парноÑті (0 = disable, 5 = typical) EOF $Lang{BackupPC_Archive2_split} = < Розділити на чаÑтини Megabytes EOF # ----------------------------------- $Lang{Pool_Stat} = <Пул займає \${poolSize}GB в тому чиÑлі \$info->{"\${name}FileCnt"} файлів та \$info->{"\${name}DirCnt"} тек (на \$poolTime),
  • Під Ñ‡Ð°Ñ Ñ…ÐµÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÑƒÐ»Ñƒ виÑвлено \$info->{"\${name}FileCntRep"} файлів що повторюютьÑÑ Ð· найбільшою кількіÑтю повторень \$info->{"\${name}FileRepMax"},
  • Під Ñ‡Ð°Ñ Ð½Ñ–Ñ‡Ð½Ð¾Ð³Ð¾ Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð¾ \$info->{"\${name}FileCntRm"} файлів загальним розміром \${poolRmSize}GB (близько \$poolTime), EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: Запит на резервне ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð· \$host"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < Відповідь з Ñервера: \$reply

    ПовернутиÑÑ Ð½Ð° Ñторінку хоÑта \$host . EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: Start Backup Confirm on \$host"; # -------------------------------- $Lang{Are_you_sure_start} = < Резервне \$type ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð½Ð° \$host.

    Ви дійÑно хочете це зробити?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: Stop Backup Confirm on \$host"; # -------------------------------- $Lang{Are_you_sure_stop} = < Ви зупинÑєте/видалÑєте з черги резервне ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð½Ð° \$host;
    Also, please don\'t start another backup for hours.

    Ви дійÑно хочете це зробити?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Тільки привілейовані кориÑтувачі можуть переглÑдати черги."; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "Тільки привілейовані кориÑтувачі можуть Ñтворювати резервну копію."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ð¾ чергам завдань"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("Черга завдань кориÑтувачів")}

    ÐаÑтупні запити кориÑтувачів поÑтавлені до черги:

    \$strUser
    ХоÑÑ‚ Ð§Ð°Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ КориÑтувач


    \${h2("Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ð¾ фоновій черзі")}

    ÐаÑтупні фонові запити були поÑтавлені до черги:

    \$strBg
    ХоÑÑ‚ Ð§Ð°Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ КориÑтувач


    \${h2("Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ð¾ черзі команд")}

    ÐаÑтупні команди були поÑтавлені до черги:

    \$strCmd
    ХоÑÑ‚ Ð§Ð°Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ КориÑтувач Команда
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: Файли \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, було змінено \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ пропущено \$skipped Ñ€Ñдків ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \nÐе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл логу \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð»Ð¾Ð³-файлу";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    Файл Розмір Ð§Ð°Ñ Ð·Ð¼Ñ–Ð½Ð¸
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    Отримувач ХоÑÑ‚ Ð§Ð°Ñ Ð¢ÐµÐ¼Ð°
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: ПродивитиÑÑ Ñ€ÐµÐ·ÐµÑ€Ð²Ð½Ñƒ копію \$num Ð´Ð»Ñ \$host"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ \$host"; $Lang{Restore_Options_for__host2} = < You have selected the following files/directories from Ви позначили лаÑтупні файли/теки з реÑурÑу \$share, номер резервної копіїr #\$num:
      \$fileListStr

    У Ð’Ð°Ñ Ñ” три варіанта Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ñ€Ð°Ð½Ð¸Ñ… файлів/тек. Виберіть, будь лаÑка, один з них:

    \${h2("Варіант 1: ПрÑме відновленнÑ")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost.

    Увага!: Ð’ÑÑ– Ñ–Ñнуючі файли будуть перезапиÑані!

    \$hiddenStr
    Restore the files to host
    Restore the files to share
    Restore the files below dir
    (relative to share)
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("Варіант 2: Звантажити Zip архів")}

    Ви можете звантажити zip-архів, що буде міÑтити вÑÑ– обрані файли/теки. Ви можете викориÑтати локальну програму, наприклад WinZip, Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾ щоб продивитиÑÑ Ð°Ð±Ð¾ відновити будь-Ñкі файли.

    Увага: в залежноÑті від того, Ñкі файли/теки Ви позначили залежить розмір архіву. ÐŸÑ€Ð¾Ñ†ÐµÑ Ð¹Ð¾Ð³Ð¾ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð° Ð·Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ðµ зайнÑти деÑкий чаÑ. Ви маєте бути впевнені, що Вам виÑтачить міÑÑ†Ñ Ð½Ð° локальному диÑку Ð´Ð»Ñ Ð¹Ð¾Ð³Ð¾ збереженнÑ.

    \$hiddenStr Створити архів відноÑно \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (інакше архів буде міÑтити повні шлÑхи).
    Compression (0=off, 1=fast,...,9=best)
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("Option 2: Download Zip archive")}

    Archive::Zip не вÑтановлено тому Ви не можете звантажити zip-архів. ПопроÑіть ÑиÑтемного адмініÑтратора вÑтановити Archive::Zip з www.cpan.org.

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < Ви можете звантажити tar-архів, що буде міÑтити вÑÑ– обрані файли/теки. Ви можете викориÑтати локальну програму, наприклад WinZip або tar, Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾ щоб продивитиÑÑ Ð°Ð±Ð¾ відновити будь-Ñкі файли.

    Увага: в залежноÑті від того, Ñкі файли/теки Ви позначили залежить розмір архіву. ÐŸÑ€Ð¾Ñ†ÐµÑ Ð¹Ð¾Ð³Ð¾ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð° Ð·Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ðµ зайнÑти деÑкий чаÑ. Ви маєте бути впевнені, що Вам виÑтачить міÑÑ†Ñ Ð½Ð° локальному диÑку Ð´Ð»Ñ Ð¹Ð¾Ð³Ð¾ збереженнÑ.

    \$hiddenStr Створити архів відноÑно \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} (інакше архів буде міÑтити повні шлÑхи).
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð° \$host"; $Lang{Are_you_sure} = < Ви починаєте прÑме Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð° комп\'ютер \$In{hostDest}. ÐаÑтупні файли будуть відновлені до реÑурÑу \$In{shareDest}, з архівної копії номер \$num:

    \$fileListStr
    Original file/dirWill be restored to

    \$hiddenStr Ви дійÑно хочете це зробити?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: Запит на Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾ \$hostDest"; $Lang{Reply_from_server_was___reply} = < Відповідь Ñервера була: \$reply

    ПовернітьÑÑ Ð´Ð¾ \$hostDest home page. EOF $Lang{BackupPC_Archive_Reply_from_server} = < Відповідь Ñервера була: \$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ð¾ резервному копіюванню хоÑта \$host "; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("Дії кориÑтувача")}

    \$startIncrStr

    \${h2("Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ резервні копії")}

    ÐатиÑніть на номер резервної копії Ð´Ð»Ñ Ð¾Ð³Ð»Ñду та Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð² з неї.

    \$str
    Рез. КопіÑ# тип Повний Рівень Дата початку ТриваліÑть/хв Вік/днів Серверний шлÑÑ… копії

    \$restoreStr



    \${h2("Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ помилки Xfer")}

    \$errStr
    Backup# Тип Журнал #Xfer помилок #паганих файлів #паганих реÑурÑів #tar помилок


    \${h2("Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ð¾ файлах")}

    ІÑнуючі файли, це ті, Ñкі вже були в пулі; нові файли це ті, що тільки-но додаютьÑÑ Ð´Ð¾ пулу. ПуÑті файли та SMB-помилки не враховуютьÑÑ

    \$sizeStr
    Totals Existing Files New Files
    Рез. копіÑ# Тип #Файли Розмір/MB MB/Ñ #Файли Розмір/MB #Файли Розмір/MB


    \${h2("Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ÑтиÑканнÑ")}

    Рівень ÑтиÑÐºÐ°Ð½Ð½Ñ Ð½Ð¾Ð²Ð¸Ñ… та Ñ–Ñнуючих файлів.

    \$compStr
    ІÑнуючі файли Ðові файли
    Рез. копіÑ# Тип Рівень ÑтиÑк. Розмір/MB СтиÑк./MB СтиÑк Розмір/MB СтиÑк/MB СтиÑк


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Host \$host Archive Summary"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("Дії кориÑтувачів")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: Помилки"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "Сервер"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • You are browsing backup #\$num, which started around \$backupTime (\$backupAge days ago), \$filledBackup
    • Увійти в текуy:
    • ÐатиÑніть на теку нижче Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ в неї,
    • ÐатиÑніть на файл нижче Ð´Ð»Ñ Ð¹Ð¾Ð³Ð¾ відновленнÑ,
    • Ви можете переглÑнути резервну копію history of the current directory.
    \${h2("Contents of \$dirDisplay")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ñ€ÐµÐ·ÐµÑ€Ð²Ð½Ð¸Ñ… копій тек Ð´Ð»Ñ \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "dir"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < Тут відображені вÑÑ– унікальні верÑÑ–Ñ— файлів в уÑÑ–Ñ… Резервних копіÑÑ…:
    • ÐатиÑніть на номер резервної копії Ð´Ð»Ñ Ð¿Ð¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ Ð´Ð¾ переглÑду рез. копій,
    • ÐатиÑніть на поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° теку (\$Lang->{DirHistory_dirLink}) Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð´Ñƒ в цю теку,
    • ÐатиÑніть на поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° верÑÑ–ÑŽ файла (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) Ð´Ð»Ñ Ð¹Ð¾Ð³Ð¾ звантаженнÑe,
    • Фали з однаковим вміÑтом мають однакову верÑÑ–ÑŽ в уÑÑ–Ñ… резервних копіÑÑ…,
    • Файли та теки, що відÑутні в поточній резервній копії позначені порожнім прÑмокутником.
    • Файли однієї верÑÑ–Ñ— можуть відрізнÑтиÑÑ Ð°Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ð°Ð¼Ð¸ файлової ÑиÑтеми. Виберіть номмер резервної копії Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду атрибутів.
    \${h2("ІÑÑ‚Ð¾Ñ€Ñ–Ñ \$dirDisplay")}
    \$backupNumStr\$backupTimeStr \$fileStr
    Backup number
    Backup time
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: Подробиці Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ #\$num Ð´Ð»Ñ \$host"; $Lang{Restore___num_details_for__host2} = <
    Ðомер \$Restores[\$i]{num}
    Запит від \$RestoreReq{user}
    Ð§Ð°Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ \$reqTime
    Результат \$Restores[\$i]{result}
    ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилки \$Restores[\$i]{errorMsg}
    ХоÑÑ‚ джерело \$RestoreReq{hostSrc}
    Ðомер вихідн. рез. копії \$RestoreReq{num}
    Вихідний реÑÑƒÑ€Ñ \$RestoreReq{shareSrc}
    ХоÑÑ‚ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ \$RestoreReq{hostDest}
    РеÑÑƒÑ€Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ \$RestoreReq{shareDest}
    Ð§Ð°Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ \$startTime
    ТриваліÑть \$duration min
    Кільк. файлів \$Restores[\$i]{nFiles}
    Загальний розмір \${MB} MB
    ШвидкіÑть передачі \$MBperSec MB/sec
    TarCreate помилки \$Restores[\$i]{tarCreateErrs}
    Xfer помилки \$Restores[\$i]{xferErrs}
    Xfer log file View, Errors

    \${h1("Файл/СпиÑок тек")}

    \$fileListStr
    Original file/dirRestored to
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: Деталі архіву #\$num Ð´Ð»Ñ \$host"; $Lang{Archive___num_details_for__host2 } = <
    Ðомер \$Archives[\$i]{num}
    Запит від \$ArchiveReq{user}
    Ð§Ð°Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ \$reqTime
    Результат \$Archives[\$i]{result}
    Помилки \$Archives[\$i]{errorMsg}
    Ð§Ð°Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ \$startTime
    ТриваліÑть \$duration min
    Xfer log файл View, Errors

    \${h1("Host list")}

    \$HostListStr
    ХоÑÑ‚Ðомер резерв. копії
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ð¾ Email"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new failed: check apache error_log\n"; $Lang{Wrong_user__my_userid_is___} = "Ðеправильний кориÑтувач: мій userid \$>, а не \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Only privileged users can view PC summaries."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Тільки привілейований кориÑтувач може запуÑтити резевне ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð½Ð°" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "Ðеправильнмй номер \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "Ðеможливо відкрити \$file: проблеми з конфігурацією?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "Тільки привілейований кориÑтувач може переглÑдати log чи файл конфігурації."; $Lang{Only_privileged_users_can_view_log_files} = "Тільки привілейований кориÑтувач може переглÑдати log файли."; $Lang{Only_privileged_users_can_view_email_summaries} = "Тільки привілейований кориÑтувач може переглÑдати інформацію про email."; $Lang{Only_privileged_users_can_browse_backup_files} = "Тільки привілейований кориÑтувач може переглÑдати файли резервних копій" . " Ð´Ð»Ñ Ñ…Ð¾Ñту \${EscHTML(\$In{host})}."; $Lang{Empty_host_name} = "Порожнє ім\'Ñ Ñ…Ð¾Ñта."; $Lang{Directory___EscHTML} = "Тека \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " порожнÑ"; $Lang{Can_t_browse_bad_directory_name2} = "Ðе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ неправильну назву теки" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "Тільки привілейований кориÑтувач може відновлювати файли з резервних копій" . " Ð´Ð»Ñ Ñ…Ð¾Ñта \${EscHTML(\$In{host})}."; $Lang{Bad_host_name} = "Ðеправильне ім\'Ñ Ñ…Ð¾Ñта \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "Ви не обрали жодного файла; Будь лаÑка повернітьÑÑ Ð½Ð°Ð·Ð°Ð´" . " та оберіть ÑкийÑÑŒ файл."; $Lang{You_haven_t_selected_any_hosts} = "Ви не обрали жодного хоÑта; будь лаÑка повернітьÑÑ Ð½Ð°Ð·Ð°Ð´" . " та оберіть ÑкийÑÑŒ хоÑÑ‚."; $Lang{Nice_try__but_you_can_t_put} = "Добра Ñпроба, але ви не можете вÑтавити \'..\' у будь Ñке ім\'Ñ Ñ„Ð°Ð¹Ð»Ñƒ"; $Lang{Host__doesn_t_exist} = "ХоÑÑ‚ \${EscHTML(\$In{hostDest})} не Ñ–Ñнує"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "У Ð’Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” прав Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð° хоÑÑ‚" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "не можливо відкрити/Ñтворити " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "Тільки привілейований кориÑтувач може відновити резервну копію" . " Ð´Ð»Ñ Ñ…Ð¾Ñта \${EscHTML(\$host)}."; $Lang{Empty_host_name} = "Порожнє ім\'Ñ Ñ…Ð¾Ñта"; $Lang{Unknown_host_or_user} = "Ðевідомий хоÑÑ‚ або кориÑтувач \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Тільки привілейований кориÑтувач може переглÑдати інформацію про" . " хоÑÑ‚ \${EscHTML(\$host)}." ; $Lang{Only_privileged_users_can_view_archive_information} = "Тільки привілейований кориÑтувач може переглÑдати інформацію про архівуваннÑ."; $Lang{Only_privileged_users_can_view_restore_information} = "Тільки привілейований кориÑтувач може переглÑдати інформацію про відновленнÑ."; $Lang{Restore_number__num_for_host__does_not_exist} = "Ðомер Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ \$num Ð´Ð»Ñ Ñ…Ð¾Ñту \${EscHTML(\$host)} не" . " Ñ–Ñнує."; $Lang{Archive_number__num_for_host__does_not_exist} = "Ðомер архіву \$num Ð´Ð»Ñ Ñ…Ð¾Ñту \${EscHTML(\$host)} не" . " Ñ–Ñнує."; $Lang{Can_t_find_IP_address_for} = "Ðеможливо знайти IP адреÑу Ð´Ð»Ñ \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = < ÐаÑкільки Ñ Ð±Ð°Ñ‡Ñƒ \$host має приватну DHCP адреÑу, Ви маєте можливіÑть запуÑтити запит з клієнтÑького комп\'ютера ÑамоÑтійно. EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "Запит на резерв. ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð½Ð° DHCP \$host (\$In{hostIP}) від" . " \$User з \$ENV{REMOTE_ADDR}"; $Lang{Backup_requested_on__host_by__User} = "Запит на резерв. ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð· \$host від \$User"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "Резерв. ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð·ÑƒÐ¿Ð¸Ð½ÐµÐ½Ð¾/виключено з черги на \$host від \$User"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "Запит на Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð° \$hostDest, резерв. ÐºÐ¾Ð¿Ñ–Ñ #\$num," . " від \$User на \$ENV{REMOTE_ADDR}"; $Lang{Archive_requested} = "Запит на архів від \$User на \$ENV{REMOTE_ADDR}"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "СтатуÑ"; $Lang{PC_Summary} = "Зведена Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ð¾ хоÑтам"; $Lang{LOG_file} = "LOG файл"; $Lang{LOG_files} = "LOG файли"; $Lang{Old_LOGs} = "Старі LOGи"; $Lang{Email_summary} = "Поштові налаштуваннÑ"; $Lang{Config_file} = "Файл конфігурації"; # $Lang{Hosts_file} = "Hosts file"; $Lang{Current_queues} = "Поточні черги"; $Lang{Documentation} = "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ (англ)"; #$Lang{Host_or_User_name} = "Host or User name:"; $Lang{Go} = "Перейти"; $Lang{Hosts} = "ХоÑти"; $Lang{Select_a_host} = "Виберіть хоÑÑ‚..."; $Lang{There_have_been_no_archives} = "

    Ðемає жодної резервної копії

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    З цього PC ніколи не ÑтворювалаÑÑŒ резервна копіÑ!!

    \n"; $Lang{This_PC_is_used_by} = "
  • Цей PC викориÑтовуєтьÑÑ \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "(Показати лише помилки)"; $Lang{XferLOG} = "XferLOG"; $Lang{Errors} = "Помилки"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <ОÑтанній email було відіÑлано доo \${UserLink(\$user)} Ñ‡Ð°Ñ \$mailTime, тема "\$subj". EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <Команда \$cmd зараз виконуєтьÑÑ Ð´Ð»Ñ Ñ…Ð¾Ñта \$host, started \$startTime. EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <ХоÑÑ‚ \$host поÑтавлений у фонову чергу (повернетьÑÑ Ð·Ð° першої можливоÑті). EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <ХоÑÑ‚ \$host поÑтавлений у чергу кориÑтувача (повернетьÑÑ Ð·Ð° першої можливоÑті). EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <Команда Ð´Ð»Ñ \$host поÑтавлена в чергу команд (Ñкоро буде запущена). EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <Last status is state \"\$Lang->{\$StatusHost{state}}\"\$reason as of \$startTime. EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <ОÑÑ‚Ð°Ð½Ð½Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° \"\${EscHTML(\$StatusHost{error})}\". EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <Ping до \$host не пройшов \$StatusHost{deadCnt} декілька разів підрÑд. EOF # ----- $Lang{Prior_to_that__pings} = "До цього, pings"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr to \$host have succeeded \$StatusHost{aliveCnt} consecutive times. EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Because \$host has been on the network at least \$Conf{BlackoutGoodCnt} consecutive times, it will not be backed up from \$blackoutStr. EOF $Lang{__time0_to__time1_on__days} = "\$t0 to \$t1 on \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Резервне ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð²Ñ–Ð´ÐºÐ»Ð°Ð´ÐµÐ½Ðµ на \$hours годин (change this number). EOF $Lang{tryIP} = " and \$StatusHost{dhcpHostIP}"; # $Lang{Host_Inhost} = "Host \$In{host}"; $Lang{checkAll} = <  Select all EOF $Lang{checkAllHosts} = <  Select all EOF $Lang{fileHeader} = < Name Тип Режим # Розмір Дата зміни EOF $Lang{Home} = "Додому"; $Lang{Browse} = "ПродивитиÑÑ Ñ€ÐµÐ·ÐµÑ€Ð². копії"; $Lang{Last_bad_XferLOG} = "Last bad XferLOG"; $Lang{Last_bad_XferLOG_errors_only} = "Last bad XferLOG (errors only)"; $Lang{This_display_is_merged_with_backup} = < This display is merged with backup #\$numF. EOF $Lang{Visit_this_directory_in_backup} = < Оберіть резервну копію, Ñку Ви хочете продивитиÑÑ: EOF $Lang{Restore_Summary} = < ÐатиÑніть на номер Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду деталей. \$restoreStr
    ВідновленнÑ# Результат Дата початку Тривал/хи #файлів MB #tar помилок #xfer помилок

    EOF $Lang{Archive_Summary} = < ÐатиÑніть на номер архіву Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду деталей. \$ArchiveStr
    Ðрхів# Результат Дата початку Трив/хв

    EOF $Lang{BackupPC__Documentation} = "BackupPC: ДокументаціÑ"; $Lang{No} = "ні"; $Lang{Yes} = "так"; $Lang{The_directory_is_empty} = <The directory \$dirDisplay is empty EOF #$Lang{on} = "on"; $Lang{off} = "off"; $Lang{backupType_full} = "повний"; $Lang{backupType_incr} = "інкрементальний"; $Lang{backupType_partial} = "чаÑтковий"; $Lang{failed} = "неуÑпішно"; $Lang{success} = "уÑпішно"; $Lang{and} = "та"; # ------ # Hosts states and reasons $Lang{Status_idle} = "бездіÑ"; $Lang{Status_backup_starting} = "Резерв. ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿ÑƒÑ‰ÐµÐ½Ð¾"; $Lang{Status_backup_in_progress} = "Резерв. ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð² процеÑÑ–"; $Lang{Status_restore_starting} = "Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ð¿ÑƒÑ‰ÐµÐ½Ð¾"; $Lang{Status_restore_in_progress} = "Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð² процеÑÑ–"; $Lang{Status_link_pending} = "link pending"; $Lang{Status_link_running} = "link running"; $Lang{Reason_backup_done} = "зроблено"; $Lang{Reason_restore_done} = "Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ñ€Ð¾Ð±Ð»ÐµÐ½Ð¾"; $Lang{Reason_archive_done} = "Ð°Ñ€Ñ…Ñ–Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ñ€Ð¾Ð±Ð»ÐµÐ½Ð¾"; $Lang{Reason_nothing_to_do} = "бездіÑ"; $Lang{Reason_backup_failed} = "резерв. ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð½ÐµÑƒÑпішне"; $Lang{Reason_restore_failed} = "Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½ÐµÑƒÑпішне"; $Lang{Reason_archive_failed} = "Ð°Ñ€Ñ…Ñ–Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÑƒÑпішне"; $Lang{Reason_no_ping} = "no ping"; $Lang{Reason_backup_canceled_by_user} = "резерв. ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð°Ð½Ðµ кориÑтувачем"; $Lang{Reason_restore_canceled_by_user} = "Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð°Ð½Ðµ кориÑтувачем"; $Lang{Reason_archive_canceled_by_user} = "Ð°Ñ€Ñ…Ñ–Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð°Ð½Ðµ кориÑтувачем"; $Lang{Disabled_OnlyManualBackups} = "auto відключене"; $Lang{Disabled_AllBackupsDisabled} = "відключене"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: жлдного резерв. ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð½Ð° \$host не було уÑпішним"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, Your PC ($host) has never been successfully backed up by our PC backup software. PC backups should occur automatically when your PC is connected to the network. You should contact computer support if: - Your PC has been regularly connected to the network, meaning there is some configuration or setup problem preventing backups from occurring. - You don't want your PC backed up and you want these email messages to stop. Otherwise, please make sure your PC is connected to the network next time you are in the office. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: no recent backups on \$host"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, Your PC ($host) has not been successfully backed up for $days days. Your PC has been correctly backed up $numBackups times from $firstTime to $days days ago. PC backups should occur automatically when your PC is connected to the network. If your PC has been connected for more than a few hours to the network during the last $days days you should contact IS to find out why backups are not working. Otherwise, if you are out of the office, there's not much you can do, other than manually copying especially critical files to other media. You should be aware that any files you have created or changed in the last $days days (including all new email and attachments) cannot be restored if your PC disk crashes. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: Outlook files on \$host need to be backed up"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers Dear $userName, The Outlook files on your PC have $howLong. These files contain all your email, attachments, contact and calendar information. Your PC has been correctly backed up $numBackups times from $firstTime to $lastTime days ago. However, Outlook locks all its files when it is running, preventing these files from being backed up. It is recommended you backup the Outlook files when you are connected to the network by exiting Outlook and all other applications, and, using just your browser, go to this link: $CgiURL?host=$host Select "Start Incr Backup" twice to start a new incremental backup. You can select "Return to $host page" and then hit "reload" to check the status of the backup. It should take just a few minutes to complete. Regards, BackupPC Genie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "not been backed up successfully"; $Lang{howLong_not_been_backed_up_for_days_days} = "not been backed up for \$days days"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPC Server"; $Lang{RSS_Doc_Description} = "RSS feed for BackupPC"; $Lang{RSS_Host_Summary} = < Note: Check Override if you want to modify a value specific to this host.

    EOF $Lang{CfgEdit_Button_Save} = "Зберегти"; $Lang{CfgEdit_Button_Insert} = "Ð’Ñтавити"; $Lang{CfgEdit_Button_Delete} = "Видалити"; $Lang{CfgEdit_Button_Add} = "Додати"; $Lang{CfgEdit_Button_Override} = "ПерезапиÑати"; $Lang{CfgEdit_Button_New_Key} = "Ðове значеннÑ"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "Error: No save due to errors"; $Lang{CfgEdit_Error__must_be_an_integer} = "Error: \$var must be an integer"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "Error: \$var must be a real-valued number"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "Error: \$var entry \$k must be an integer"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "Error: \$var entry \$k must be a real-valued number"; $Lang{CfgEdit_Error__must_be_executable_program} = "Error: \$var must be a valid executable path"; $Lang{CfgEdit_Error__must_be_valid_option} = "Error: \$var must be a valid option"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "Copy host \$copyHost doesn't exist; creating full host name \$fullHost. Delete this host if that is not what you wanted."; $Lang{CfgEdit_Log_Copy_host_config} = "\$User copied config from host \$fromHost to \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "\$User deleted \$p from \$conf\n"; $Lang{CfgEdit_Log_Add_param_value} = "\$User added \$p to \$conf, set to \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "\$User changed \$p in \$conf to \$valueNew from \$valueOld\n"; $Lang{CfgEdit_Log_Host_Delete} = "\$User deleted host \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "\$User host \$host changed \$key from \$valueOld to \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "\$User added host \$host: \$value\n"; #end of lang_en.pm BackupPC-3.3.2/lib/BackupPC/Lang/zh_CN.pm0000444000076500000240000015564013042250554016546 0ustar craigstaff#!/usr/bin/perl #my %lang; #use strict; use utf8; # -------------------------------- $Lang{Start_Archive} = "开始备档"; $Lang{Stop_Dequeue_Archive} = "中止ï¼å–消备档"; $Lang{Start_Full_Backup} = "开始完全备份"; $Lang{Start_Incr_Backup} = "开始增é‡å¤‡ä»½"; $Lang{Stop_Dequeue_Backup} = "中止ï¼å–消备份"; $Lang{Restore} = "æ¢å¤"; $Lang{Type_full} = "完全"; $Lang{Type_incr} = "增é‡"; # ----- $Lang{Only_privileged_users_can_view_admin_options} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æŸ¥çœ‹ç®¡ç†é€‰é¡¹ã€‚"; $Lang{H_Admin_Options} = "BackupPC æœåŠ¡å™¨ï¼šç®¡ç†é€‰é¡¹"; $Lang{Admin_Options} = "管ç†é€‰é¡¹"; $Lang{Admin_Options_Page} = < \${h2("æœåŠ¡å™¨æŽ§åˆ¶")}

    æ›´æ–°æœåС噍é…置:
    EOF $Lang{Unable_to_connect_to_BackupPC_server} = "无法连接到 BackupPC æœåС噍"; $Lang{Unable_to_connect_to_BackupPC_server_error_message} = <
    EOF $Lang{Admin_Start_Server} = < BackupPC æœåС噍 \$Conf{ServerHost} ç«¯å£ \$Conf{ServerPort} 此刻没有è¿è¡Œï¼ˆå¯èƒ½åˆšè¢«åœæ­¢ï¼Œæˆ–者还没被å¯åŠ¨ï¼‰ã€‚
    你想现在å¯åŠ¨å®ƒå—? EOF # ----- $Lang{H_BackupPC_Server_Status} = "BackupPC æœåŠ¡å™¨çŠ¶æ€"; $Lang{BackupPC_Server_Status_General_Info}= <
  • æœåŠ¡å™¨è¿›ç¨‹å·æ˜¯ \$Info{pid},è¿è¡Œåœ¨ä¸»æœº \$Conf{ServerHost} 上, ç‰ˆæœ¬å· \$Info{Version},开始è¿è¡ŒäºŽ \$serverStartTime。
  • æ­¤çŠ¶æ€æŠ¥å‘Šç”ŸæˆäºŽ \$now。
  • æœåС噍é…置最近一次加载于 \$configLoadTime。
  • æœåŠ¡å™¨ä»»åŠ¡é˜Ÿåˆ—ä¸‹æ¬¡å¯åŠ¨æ—¶é—´æ˜¯ \$nextWakeupTime。
  • 其它信æ¯ï¼š
    • \$numBgQueue 个自上次é—留备份请求,
    • \$numUserQueue 个待处ç†ç”¨æˆ·å¤‡ä»½è¯·æ±‚,
    • \$numCmdQueue 个待处ç†å‘½ä»¤è¯·æ±‚, \$poolInfo
    • 备份池文件系统ç£ç›˜ç©ºé—´å ç”¨çŽ‡æ˜¯ \$Info{DUlastValue}% (统计于 \$DUlastTime),今天的最大å ç”¨çŽ‡æ˜¯ \$Info{DUDailyMax}%(统计于 \$DUmaxTime), 昨天的最大å ç”¨çŽ‡æ˜¯ \$Info{DUDailyMaxPrev}%。
    EOF $Lang{BackupPC_Server_Status} = < \$generalInfo \${h2("正在è¿è¡Œçš„任务")}

    \$jobStr
    客户机 类型 用户 开始时间 命令 è¿›ç¨‹å· ä¼ è¾“è¿›ç¨‹å·

    \${h2("需è¦å…³æ³¨çš„错误")}

    \$statusStr
    客户机 类型 用户 最åŽä¸€æ¬¡å°è¯• 详情 错误时间 最åŽä¸€æ¬¡é”™è¯¯ï¼ˆ PING 失败除外)
    EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: 客户机报告"; $Lang{BackupPC__Archive} = "BackupPC: 备档"; $Lang{BackupPC_Summary} = <

    • æ­¤çŠ¶æ€æŠ¥å‘Šç”ŸæˆäºŽ \$now。
    • 备份池文件系统ç£ç›˜ç©ºé—´å ç”¨çŽ‡æ˜¯ \$Info{DUlastValue}% (统计于 \$DUlastTime),今天的最大å ç”¨çŽ‡æ˜¯ \$Info{DUDailyMax}%(统计于 \$DUmaxTime), 昨天的最大å ç”¨çŽ‡æ˜¯ \$Info{DUDailyMaxPrev}%。

    \${h2("å·²æˆåŠŸå®Œæˆå¤‡ä»½çš„客户机")}

    有 \$hostCntGood å°å®¢æˆ·æœºå·²å®Œæˆå¤‡ä»½ï¼Œæ€»æ•°æ˜¯ï¼š

    • \$fullTot ä¸ªå®Œå…¨å¤‡ä»½ï¼Œæ€»å¤§å°æ˜¯ \${fullSizeTot}GB (被压缩å‰å€¼ï¼‰ï¼Œ
    • \$incrTot 个增é‡å¤‡ä»½ï¼Œæ€»å¤§å°æ˜¯ \${incrSizeTot}GB (被压缩å‰å€¼ï¼‰ã€‚

    \$strGood
    客户机 用户 完全备份个数 最åŽä¸€æ¬¡å®Œå…¨å¤‡ä»½ (天å‰) å®Œå…¨å¤‡ä»½å¤§å° (GB) 完全备份速度 (MB/s) 增é‡å¤‡ä»½ä¸ªæ•° 最åŽä¸€æ¬¡å¢žé‡å¤‡ä»½ (天å‰) 最åŽä¸€æ¬¡å¤‡ä»½ (天å‰) 当å‰çŠ¶æ€ ä¼ è¾“é”™è¯¯æ•°ç›® 最åŽä¸€æ¬¡å¤‡ä»½ç»“æžœ


    \${h2("未备份过的客户机")}

    有 \$hostCntNone å°å®¢æˆ·æœºä»Žæœªè¢«å¤‡ä»½è¿‡ã€‚

    \$strNone
    客户机 用户 完全备份个数 最åŽä¸€æ¬¡å®Œå…¨å¤‡ä»½ (天å‰) å®Œå…¨å¤‡ä»½å¤§å° (GB) 完全备份速度 (MB/s) 增é‡å¤‡ä»½ä¸ªæ•° 最åŽä¸€æ¬¡å¢žé‡å¤‡ä»½ (天å‰) 最åŽä¸€æ¬¡å¤‡ä»½ (天å‰) 当å‰çŠ¶æ€ ä¼ è¾“é”™è¯¯æ•°ç›® 最åŽä¸€æ¬¡å¤‡ä»½ç»“æžœ
    EOF $Lang{BackupPC_Archive} = < 一共有 \$hostCntGood å°å®¢æˆ·æœºå·²ç»è¢«å¤‡ä»½ï¼Œæ€»å¤‡ä»½å¤§å°ä¸º \${fullSizeTot}GB

    \$strGood \$checkAllHosts
    客户机 用户 备份大å°

    EOF $Lang{BackupPC_Archive2} = < \$HostListStr

    \$hiddenStr \$paramStr
    EOF $Lang{BackupPC_Archive2_location} = < 备档目的地ï¼å¤–设 EOF $Lang{BackupPC_Archive2_compression} = < 压缩 æ— 
    gzip
    bzip2 EOF $Lang{BackupPC_Archive2_parity} = < 奇嶿 ¡éªŒæ•°æ®æ¯”例 (0 = ä¸å¯ç”¨ï¼Œ5 = 典型设置) EOF $Lang{BackupPC_Archive2_split} = < 将输出分开为 兆字节 EOF # ----------------------------------- $Lang{Pool_Stat} = <备份æœåŠ¡å™¨æ–‡ä»¶æ± å¤§å°æ˜¯ \${poolSize}GB åŒ…å« \$info->{"\${name}FileCnt"} 个文件 å’Œ \$info->{"\${name}DirCnt"} 个文件夹ï¼ç›®å½•(截至 \$poolTime)。文件池大å°åŸºæœ¬å°±æ˜¯æ‰€æœ‰å¤‡ä»½æ•°æ®å ç”¨çš„实际ç£ç›˜ç©ºé—´ã€‚
  • æœåŠ¡å™¨æ–‡ä»¶æ± æ•£åˆ—æ“作(Hashing)å‘现 \$info->{"\${name}FileCntRep"} 个文件具有é‡å¤æ•£åˆ—值,其中 \$info->{"\${name}FileRepMax"} ä¸ªæ–‡ä»¶å…·æœ‰ç›¸åŒæ•£åˆ—å€¼ã€‚ç›¸åŒæ•£åˆ—值并䏿„味ç€ç›¸åŒæ–‡ä»¶ã€‚散列æ“作被用æ¥èŠ‚çœç›¸åŒæ–‡ä»¶æ‰€å ç”¨çš„ç£ç›˜ç©ºé—´ã€‚
  • æ¯æ—¥ä¾‹è¡Œæ¸…ç†è¿‡æœŸæ•°æ®æ“作删除了 \$info->{"\${name}FileCntRm"} 个文件共 \${poolRmSize}GB (æ“作于 \$poolTime )。 EOF # -------------------------------- $Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: 客户机 \$host 有备份请求"; # -------------------------------- $Lang{REPLY_FROM_SERVER} = < æœåŠ¡å™¨ç­”å¤æ˜¯ï¼š\$reply

    返回 \$host 主页。 EOF # -------------------------------- $Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: 客户机 \$host 开始备份确认"; # -------------------------------- $Lang{Are_you_sure_start} = < ä½ å³å°†åœ¨å®¢æˆ·æœº \$host 上开始 \$type 备份。

    你能确定å—?
    EOF # -------------------------------- $Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: 客户机 \$host åœæ­¢å¤‡ä»½ç¡®è®¤"; # -------------------------------- $Lang{Are_you_sure_stop} = < ä½ å³å°†åœ¨å®¢æˆ·æœº \$host ä¸Šåœæ­¢ï¼å–消备份æ“作;
    å¦‚æžœç¡®å®šå–æ¶ˆå¤‡ä»½æ“作,请从现在起 å°æ—¶å†…ä¸è¦å†å¯åЍå¦ä¸€å¤‡ä»½æ“作。

    你能确定å—?

    EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æŸ¥çœ‹ä»»åŠ¡è¯·æ±‚é˜Ÿåˆ—ã€‚"; # -------------------------------- $Lang{Only_privileged_users_can_archive} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æ‰§è¡Œå¤‡æ¡£æ“作。"; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: 队列报告"; # -------------------------------- $Lang{Backup_Queue_Summary} = <
    \${h2("用户队列报告")}

    下列用户请求排在队列中:

    \$strUser
    客户机 请求时间 用户


    \${h2("åŽå°è¯·æ±‚队列报告")}

    下列åŽå°è¯·æ±‚排在队列中:

    \$strBg
    客户机 请求时间 用户


    \${h2("命令队列报告")}

    下列命令请求排在队列中:

    \$strCmd
    客户机 请求时间 用户 命令
    EOF # -------------------------------- $Lang{Backup_PC__Log_File__file} = "BackupPC: 日志文件 \$file"; $Lang{Log_File__file__comment} = < EOF # -------------------------------- $Lang{Contents_of_log_file} = <\$file, 修改时间 \$mtimeStr \$comment EOF # -------------------------------- $Lang{skipped__skipped_lines} = "[ 略过 \$skipped 行 ]\n"; # -------------------------------- $Lang{_pre___Can_t_open_log_file__file} = "
    \n无法打开日志文件 \$file\n";
    
    # --------------------------------
    $Lang{BackupPC__Log_File_History} = "BackupPC: 日志文件历å²";
    $Lang{Log_File_History__hdr} = <
    
    \$str
    
    文件 å¤§å° ä¿®æ”¹æ—¶é—´
    EOF # ------------------------------- $Lang{Recent_Email_Summary} = < \$str
    收信人 客户机 时间 标题
    EOF # ------------------------------ $Lang{Browse_backup__num_for__host} = "BackupPC: æµè§ˆå®¢æˆ·æœº \$host 备份åºåˆ—å· \$num"; # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: 客户机 \$host æ¢å¤é€‰é¡¹"; $Lang{Restore_Options_for__host2} = < 你从备份åºåˆ— #\$numï¼Œå· \$share 中选择了以下文件ï¼ç›®å½•:
      \$fileListStr

    你有三ç§é€‰æ‹©æ¥æ¢å¤è¿™äº›æ–‡ä»¶ï¼ç›®å½•。 è¯·ä»Žä¸‹åˆ—ä¸‰ç§æ–¹æ³•中选择其一。

    \${h2("方法 1:直接æ¢å¤")}

    EOF $Lang{Restore_Options_for__host_Option1} = <\$directHost 上。

    警告: 客户机上现存的文件,如果和被æ¢å¤çš„æ–‡ä»¶å…·æœ‰ç›¸åŒæ–‡ä»¶å并且ä½äºŽç›¸åŒè·¯å¾„,其内容将会被替æ¢ï¼

    \$hiddenStr
    æ¢å¤åˆ°å®¢æˆ·æœº
    æ¢å¤åˆ°å·
    æ¢å¤åˆ°æ­¤ç›®å½•中
    (ä½äºŽä¸Šè¿°å·ä¸‹ï¼‰
    EOF $Lang{Restore_Options_for__host_Option1_disabled} = < \${h2("方法 2:下载 Zip 备档")}

    ä½ å¯ä»¥å°†æ‰€æœ‰ä½ é€‰æ‹©çš„æ–‡ä»¶å’Œç›®å½•下载进一个 Zip 备档。然åŽå†ç”¨ä¸€ä¸ªæœ¬åœ°åº”用, 例如 WinZipï¼Œæ¥æµè§ˆæˆ–æå–其中的任何文件。

    警告: å–决于你选择的文件ï¼ç›®å½•,此备档å¯èƒ½ä¼šå ç”¨å¾ˆå¤§å­˜å‚¨ç©ºé—´ã€‚ å¯èƒ½éœ€è¦è‹¥å¹²åˆ†é’Ÿæˆ–æ›´é•¿æ—¶é—´æ¥ç”Ÿæˆå’Œä¼ è¾“此备档,并且还需è¦è¶³å¤Ÿå¤§çš„æœ¬åœ°ç£ç›˜ç©ºé—´ã€‚

    \$hiddenStr 备档中所有文件具有相对路径,在 \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} 目录内 (å¦åˆ™å¤‡æ¡£ä¸­æ–‡ä»¶å…·æœ‰å®Œæ•´è·¯å¾„)。
    选择压缩比(0ï¼ä¸åŽ‹ç¼©ï¼Œ1ï¼æœ€ä½Žä½†é€Ÿåº¦å¿«ï¼Œ...,9ï¼æœ€é«˜ä½†é€Ÿåº¦æ…¢ï¼‰
    Code page (e.g. cp866)

    EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive2} = < \${h2("方法 2:下载 Zip 备档")}

    å› æœåŠ¡å™¨æ²¡æœ‰å®‰è£… Perl 组件 Archive::Zip,Zip 备档无法被生æˆã€‚ 请è”系系统管ç†å‘˜å®‰è£… Archive::Zipï¼Œä¸‹è½½åœ°å€ www.cpan.org。

    EOF # ------------------------------ $Lang{Option_3__Download_Zip_archive} = < ä½ å¯ä»¥å°†æ‰€æœ‰ä½ é€‰æ‹©çš„æ–‡ä»¶å’Œç›®å½•下载进一个 Tar 备档。然åŽå†ç”¨ä¸€ä¸ªæœ¬åœ°åº”用, 例如 tar 或 WinZipï¼Œæ¥æµè§ˆæˆ–æå–其中的任何文件。

    警告: å–决于你选择的文件ï¼ç›®å½•,此备档å¯èƒ½ä¼šå ç”¨å¾ˆå¤§å­˜å‚¨ç©ºé—´ã€‚ å¯èƒ½éœ€è¦è‹¥å¹²åˆ†é’Ÿæˆ–æ›´é•¿æ—¶é—´æ¥ç”Ÿæˆå’Œä¼ è¾“此备档,并且还需è¦è¶³å¤Ÿå¤§çš„æœ¬åœ°ç£ç›˜ç©ºé—´ã€‚

    \$hiddenStr 备档中所有文件具有相对路径,在 \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} 目录内 (å¦åˆ™å¤‡æ¡£ä¸­æ–‡ä»¶å…·æœ‰å®Œæ•´è·¯å¾„)。
    EOF # ------------------------------ $Lang{Restore_Confirm_on__host} = "BackupPC: 客户机 \$host 开始æ¢å¤ç¡®è®¤"; $Lang{Are_you_sure} = < ä½ å³å°†å¼€å§‹æ¢å¤æ•°æ®ç›´æŽ¥åˆ°å®¢æˆ·æœº \$In{hostDest} 上。 å‚¨å­˜åœ¨å¤‡ä»½å· \$num 中的下列文件将被æ¢å¤åˆ°å· \$In{shareDest} 内:

    \$fileListStr
    原始文件ï¼ç›®å½•将被æ¢å¤åˆ°

    \$hiddenStr 你确定å—?
    EOF # -------------------------- $Lang{Restore_Requested_on__hostDest} = "BackupPC: 客户机 \$hostDest 有æ¢å¤è¯·æ±‚"; $Lang{Reply_from_server_was___reply} = < æœåŠ¡å™¨ç­”å¤æ˜¯ï¼š\$reply

    返回 \$hostDest 主页。 EOF $Lang{BackupPC_Archive_Reply_from_server} = < æœåŠ¡å™¨ç­”å¤æ˜¯ï¼š\$reply EOF # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: 客户机 \$host 备份报告"; $Lang{Host__host_Backup_Summary2} = < \$warnStr

      \$statusStr

    \${h2("用户æ“作")}

    \$startIncrStr

    \${h2("备份报告")}

    点击备份åºåˆ—å·æµè§ˆå’Œæ¢å¤æ–‡ä»¶ã€‚

    \$str
    备份åºåˆ—å·ï¼ƒ 类型 完整 备份级别 开始时间 耗时(分钟) è·ç¦»çŽ°åœ¨ï¼ˆå¤©å‰ï¼‰ æœåŠ¡å™¨ä¸Šå¤‡ä»½è·¯å¾„

    \$restoreStr



    \${h2("传输错误报告")}

    \$errStr
    备份åºåˆ—å·ï¼ƒ 类型 查看 传输错误数目 æŸå文件数目 æŸåæ–‡ä»¶ç³»ç»Ÿå·æ•°ç›® æŸå Tar 文件数目


    \${h2("文件大å°ï¼æ•°ç›®ç»Ÿè®¡")}

    "原有文件"是指原先已存在备份池中的文件;"新增文件"是指备份新写入池中的文件。 空文件ä¸è¢«ç»Ÿè®¡åœ¨å†…。

    \$sizeStr
    åˆè®¡ 原有文件 新增文件
    备份åºåˆ—å·ï¼ƒ 类型 文件数目 大å°(MB) 备份速度(MB/sec) 文件数目 大å°(MB) 文件数目 大å°(MB)


    \${h2("压缩报告")}

    备份池中原有文件和新增文件的压缩性能报告。

    \$compStr
    原有文件 新增文件
    备份åºåˆ—å·ï¼ƒ 类型 压缩级别 压缩å‰(MB) 压缩åŽ(MB) 压缩比 压缩å‰(MB) 压缩åŽ(MB) 压缩比


    EOF $Lang{Host__host_Archive_Summary} = "BackupPC: 客户机 \$host 备档报告"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    \${h2("用户æ“作")}

    \$ArchiveStr EOF # ------------------------- $Lang{Error} = "BackupPC: 错误"; $Lang{Error____head} = <\$mesg

    EOF # ------------------------- $Lang{NavSectionTitle_} = "æœåС噍"; # ------------------------- $Lang{Backup_browse_for__host} = <
    • 你正在æµè§ˆå¤‡ä»½ #\$num,该备份开始于 \$backupTime (\$backupAge 天å‰ï¼‰ã€‚ \$filledBackup
    • 进入目录:
    • 点击目录å进入相应目录。
    • ç‚¹å‡»æ–‡ä»¶åæ¢å¤ç›¸åº”文件。
    • 查看当å‰ç›®å½•的备份历å²ã€‚
    \${h2("\$dirDisplay 的内容")}

    \$dirStr

    \$fileHeader \$topCheckAll \$fileStr \$checkAll

    EOF # ------------------------------ $Lang{DirHistory_backup_for__host} = "BackupPC: 客户机 \$host 目录备份历å²"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # $Lang{DirHistory_dirLink} = "目录"; $Lang{DirHistory_fileLink} = "v"; $Lang{DirHistory_for__host} = < 本页显示文件在所有备份中的ä¸åŒç‰ˆæœ¬ï¼š
    • 点击备份åºåˆ—å·è¿”回相应备份æµè§ˆä¸»é¡µï¼Œ
    • 点击目录链接标记 (\$Lang->{DirHistory_dirLink}) 进入相应目录,
    • 点击文件版本链接标记 (\$Lang->{DirHistory_fileLink}0, \$Lang->{DirHistory_fileLink}1, ...) 下载相应文件,
    • 如果一个文件的内容在多个备份中相åŒï¼Œæ–‡ä»¶åœ¨å¤šä¸ªå¤‡ä»½ä¸­å…·æœ‰ç›¸åŒç‰ˆæœ¬å·ï¼Œ
    • 如果一个文件或目录在æŸä¸ªå¤‡ä»½ä¸­ä¸å­˜åœ¨ï¼Œä¸‹è¡¨ä¸­ç”¨ç©ºç™½è¡¨ç¤ºï¼Œ
    • 具有相åŒç‰ˆæœ¬å·çš„æ–‡ä»¶å¯èƒ½åœ¨ä¸åŒå¤‡ä»½ä¸­æœ‰ä¸åŒçš„æ–‡ä»¶å±žæ€§ã€‚å¯ä»¥ç‚¹å‡»å¤‡ä»½åºåˆ—å·æ¥æŸ¥çœ‹æ–‡ä»¶åœ¨ç›¸åº”备份中的属性。
    \${h2("\$dirDisplay 的历å²")}
    \$backupNumStr\$backupTimeStr \$fileStr
    备份åºåˆ—å·
    备份时间
    EOF # ------------------------------ $Lang{Restore___num_details_for__host} = "BackupPC: 客户机 \$host æ¢å¤ #\$num 详情"; $Lang{Restore___num_details_for__host2} = <
    æ¢å¤åºåˆ—å· \$Restores[\$i]{num}
    请求方 \$RestoreReq{user}
    请求时间 \$reqTime
    结果 \$Restores[\$i]{result}
    é”™è¯¯ä¿¡æ¯ \$Restores[\$i]{errorMsg}
    æºå®¢æˆ·æœº \$RestoreReq{hostSrc}
    æºå¤‡ä»½åºåˆ—å· \$RestoreReq{num}
    æºæ–‡ä»¶å· \$RestoreReq{shareSrc}
    目的客户机 \$RestoreReq{hostDest}
    ç›®çš„æ–‡ä»¶å· \$RestoreReq{shareDest}
    æ¢å¤å¼€å§‹æ—¶é—´ \$startTime
    耗时 \$duration 分钟
    文件个数 \$Restores[\$i]{nFiles}
    æ–‡ä»¶æ€»å¤§å° \${MB} MB
    传输速率 \$MBperSec MB/sec
    Tar 生æˆè¿‡ç¨‹é”™è¯¯ä¸ªæ•° \$Restores[\$i]{tarCreateErrs}
    传输过程错误个数 \$Restores[\$i]{xferErrs}
    传输日志文件 查看日志, 查看错误

    \${h1("文件ï¼ç›®å½•列表")}

    \$fileListStr
    原始文件ï¼ç›®å½•æ¢å¤è‡³
    EOF # ------------------------------ $Lang{Archive___num_details_for__host} = "BackupPC: 客户机 \$host 备档 #\$num 详情"; $Lang{Archive___num_details_for__host2 } = <
    备档åºåˆ—å· \$Archives[\$i]{num}
    请求方 \$ArchiveReq{user}
    请求方 \$reqTime
    结果 \$Archives[\$i]{result}
    é”™è¯¯ä¿¡æ¯ \$Archives[\$i]{errorMsg}
    开始时间 \$startTime
    耗时 \$duration 分钟
    传输日志文件 查看日志, 查看错误

    \${h1("客户机列表")}

    \$HostListStr
    客户机备份åºåˆ—å·
    EOF # ----------------------------------- $Lang{Email_Summary} = "BackupPC: 电å­é‚®ä»¶æŠ¥å‘Š"; # ----------------------------------- # !! ERROR messages !! # ----------------------------------- $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new 步骤失败:请检查 Apache æœåŠ¡å™¨æ—¥å¿—\n"; $Lang{Wrong_user__my_userid_is___} = "错误用户:我的用户 ID 是 \$>, 䏿˜¯ \$uid" . "(\$Conf{BackupPCUser})\n"; # $Lang{Only_privileged_users_can_view_PC_summaries} = "Only privileged users can view PC summaries."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æ‰§è¡Œå¤‡ä»½çš„å¼€å§‹æˆ–åœæ­¢æ“作于客户机" . " \${EscHTML(\$host)}."; $Lang{Invalid_number__num} = "无效数字 \${EscHTML(\$In{num})}"; $Lang{Unable_to_open__file__configuration_problem} = "无法打开文件 \$file:é…置有误?"; $Lang{Only_privileged_users_can_view_log_or_config_files} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æŸ¥çœ‹æ—¥å¿—或é…置文件。"; $Lang{Only_privileged_users_can_view_log_files} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æŸ¥çœ‹æ—¥å¿—文件。"; $Lang{Only_privileged_users_can_view_email_summaries} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æŸ¥çœ‹ç”µå­é‚®ä»¶æŠ¥å‘Šã€‚"; $Lang{Only_privileged_users_can_browse_backup_files} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æµè§ˆ" . "客户机 \${EscHTML(\$In{host})} 的备份文件。"; $Lang{Empty_host_name} = "空客户机å。"; $Lang{Directory___EscHTML} = "目录 \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" . " 为空"; $Lang{Can_t_browse_bad_directory_name2} = "无法æµè§ˆéžæ³•目录å" . " \${EscHTML(\$relDir)}"; $Lang{Only_privileged_users_can_restore_backup_files} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æ¢å¤" . "客户机 \${EscHTML(\$In{host})} 的备份文件。"; $Lang{Bad_host_name} = "错误客户机å \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "你还没有选择任何文件;请返回上一页" . "选择文件。"; $Lang{You_haven_t_selected_any_hosts} = "你还没有选择任何客户机;请返回上一页" . "选择客户机。"; $Lang{Nice_try__but_you_can_t_put} = "对ä¸èµ·ï¼Œæ–‡ä»¶å内ä¸èƒ½åŒ…å« \'..\'"; $Lang{Host__doesn_t_exist} = "客户机 \${EscHTML(\$In{hostDest})} ä¸å­˜åœ¨"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "你没有æƒé™æ¢å¤å®¢æˆ·æœº" . " \${EscHTML(\$In{hostDest})}"; $Lang{Can_t_open_create__openPath} = "无法打开ï¼åˆ›å»º " . "\${EscHTML(\"\$openPath\")}"; $Lang{Only_privileged_users_can_restore_backup_files2} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æ¢å¤" . "客户机 \${EscHTML(\$host)} 的备份文件。"; $Lang{Empty_host_name} = "空客户机å"; $Lang{Unknown_host_or_user} = "未知客户机或用户 \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æŸ¥çœ‹" . "客户机 \${EscHTML(\$host)} 的信æ¯ã€‚" ; $Lang{Only_privileged_users_can_view_archive_information} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æŸ¥çœ‹å¤‡æ¡£ä¿¡æ¯ã€‚"; $Lang{Only_privileged_users_can_view_restore_information} = "åªæœ‰ç‰¹æƒç”¨æˆ·å¯ä»¥æŸ¥çœ‹æ¢å¤ä¿¡æ¯ã€‚"; $Lang{Restore_number__num_for_host__does_not_exist} = "客户机 \${EscHTML(\$host)} æ¢å¤åºåˆ—å· \$num " . "ä¸å­˜åœ¨ã€‚"; $Lang{Archive_number__num_for_host__does_not_exist} = "客户机 \${EscHTML(\$host)} 备档åºåˆ—å· \$num " . "ä¸å­˜åœ¨ã€‚"; $Lang{Can_t_find_IP_address_for} = "客户机 \${EscHTML(\$host)} çš„ IP åœ°å€æ— æ³•找到"; $Lang{host_is_a_DHCP_host} = < 除éžèŽ·å¾—å®¢æˆ·æœº \$host çš„åŠ¨æ€ IP 地å€ï¼Œå¦åˆ™åªèƒ½ä»Žå®¢æˆ·ä¸»æœºä¸Šå‘出此任务请求。 EOF # ------------------------------------ # !! Server Mesg !! # ------------------------------------ $Lang{Backup_requested_on_DHCP__host} = "用户 \$User 从 \$ENV{REMOTE_ADDR} å‘èµ·è¯·æ±‚å¤‡ä»½ä½¿ç”¨åŠ¨æ€ IP 的客户机 \$host (\$In{hostIP})"; $Lang{Backup_requested_on__host_by__User} = "用户 \$User å‘起请求备份客户机 \$host"; $Lang{Backup_stopped_dequeued_on__host_by__User} = "用户 \$User åœæ­¢ï¼å–消了对客户机 \$host 的备份"; $Lang{Restore_requested_to_host__hostDest__backup___num} = "用户 \$User 从 \$ENV{REMOTE_ADDR} å‘起请求æ¢å¤å®¢æˆ·æœº \$hostDest,使用备份åºåˆ—å· #\$num"; $Lang{Archive_requested} = "用户 \$User 从 \$ENV{REMOTE_ADDR} å‘起备档请求"; # ------------------------------------------------- # ------- Stuff that was forgotten ---------------- # ------------------------------------------------- $Lang{Status} = "状æ€"; $Lang{PC_Summary} = "客户机报告"; $Lang{LOG_file} = "日志文件"; $Lang{LOG_files} = "日志文件列表"; $Lang{Old_LOGs} = "旧日志"; $Lang{Email_summary} = "电å­é‚®ä»¶æŠ¥å‘Š"; $Lang{Config_file} = "é…置文件"; # $Lang{Hosts_file} = "Hosts file"; $Lang{Current_queues} = "当å‰é˜Ÿåˆ—"; $Lang{Documentation} = "文档资料"; #$Lang{Host_or_User_name} = "Host or User name:"; $Lang{Go} = "确定"; $Lang{Hosts} = "客户机"; $Lang{Select_a_host} = "选择客户机å..."; $Lang{There_have_been_no_archives} = "

    è¿™å°æœºå™¨è¿˜ä»Žæ¥æ²¡æœ‰æ‰§è¡Œè¿‡å¤‡æ¡£æ“作ï¼

    \n"; $Lang{This_PC_has_never_been_backed_up} = "

    è¿™å°æœºå™¨è¿˜ä»Žæ¥æ²¡æœ‰è¢«å¤‡ä»½è¿‡ï¼ï¼

    \n"; $Lang{This_PC_is_used_by} = "
  • è¿™å°æœºå™¨çš„用户包括 \${UserLink(\$user)}"; $Lang{Extracting_only_Errors} = "ï¼ˆåªæå–错误信æ¯ï¼‰"; $Lang{XferLOG} = "传输日志"; $Lang{Errors} = "错误"; # ------------ $Lang{Last_email_sent_to__was_at___subject} = <给用户 \${UserLink(\$user)} 的最近一å°é‚®ä»¶é€å‡ºäºŽ \$mailTime,标题是"\$subj"。 EOF # ------------ $Lang{The_command_cmd_is_currently_running_for_started} = <命令 \$cmd 正在为客户机 \$host è¿è¡Œï¼Œå¼€å§‹äºŽ \$startTime。 EOF # ----------- $Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <客户机 \$host 已在åŽå°é˜Ÿåˆ—中等待(å³å°†è¢«å¤‡ä»½ï¼‰ã€‚ EOF # ---------- $Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <客户机 \$host 已在用户队列中等待(å³å°†è¢«å¤‡ä»½ï¼‰ã€‚ EOF # --------- $Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <针对客户机 \$host 的一æ¡å‘½ä»¤å·²åœ¨å‘½ä»¤é˜Ÿåˆ—中等待(å³å°†è¢«æ‰§è¡Œï¼‰ã€‚ EOF # -------- $Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <最åŽçŠ¶æ€æ˜¯ \"\$Lang->{\$StatusHost{state}}\"\$reason,当时时间 \$startTime。 EOF # -------- $Lang{Last_error_is____EscHTML_StatusHost_error} = <最åŽé”™è¯¯ä¿¡æ¯æ˜¯ \"\${EscHTML(\$StatusHost{error})}\"。 EOF # ------ $Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <试图与客户机 \$host è”系(Ping æ“作)已连续失败 \$StatusHost{deadCnt} 次。 EOF # ----- $Lang{Prior_to_that__pings} = "å…ˆå‰ï¼ŒPing"; # ----- $Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr 客户机 \$host 已连续æˆåŠŸ \$StatusHost{aliveCnt} 次。 EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <因为客户机 \$host å·²ç»åœ¨ç½‘络上至少连续 \$Conf{BlackoutGoodCnt} 次, 在下列时段 \$blackoutStr,它将ä¸è¿›è¡Œå¤‡ä»½æ“作。 EOF $Lang{__time0_to__time1_on__days} = "\$t0 to \$t1 在 \$days"; $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <备份被推迟 \$hours å°æ—¶ (æ”¹å˜æ—¶é—´)。 EOF $Lang{tryIP} = " å’Œ \$StatusHost{dhcpHostIP}"; # $Lang{Host_Inhost} = "Host \$In{host}"; $Lang{checkAll} = <  å…¨é€‰ EOF $Lang{checkAllHosts} = <  å…¨é€‰ EOF $Lang{fileHeader} = < 文件ï¼ç›®å½•å 类型 读写æƒé™ 备份åºåˆ—å· å¤§å° ä¿®æ”¹æ—¥æœŸ EOF $Lang{Home} = "主页"; $Lang{Browse} = "æµè§ˆå¤‡ä»½"; $Lang{Last_bad_XferLOG} = "最近出错传输日志"; $Lang{Last_bad_XferLOG_errors_only} = "最近出错传输日志(åªå«é”™è¯¯ï¼‰"; $Lang{This_display_is_merged_with_backup} = < 本页显示的是与备份åºåˆ— #\$numF åˆæˆçš„结果。 EOF $Lang{Visit_this_directory_in_backup} = < 选择你想查看的备份: EOF $Lang{Restore_Summary} = < 点击æ¢å¤åºåˆ—å·èŽ·å–详情。 \$restoreStr
    æ¢å¤åºåˆ—å· ç»“æžœ 开始时间 耗时(分钟) 文件个数 大å°(MB) Tar 错误个数 传输错误个数

    EOF $Lang{Archive_Summary} = < 点击备档åºåˆ—å·èŽ·å–详情。 \$ArchiveStr
    备档åºåˆ—å· ç»“æžœ 开始时间 耗时(分钟)

    EOF $Lang{BackupPC__Documentation} = "BackupPC: 文档资料"; $Lang{No} = "å¦"; $Lang{Yes} = "是"; $Lang{The_directory_is_empty} = <目录 \$dirDisplay 是空目录 EOF #$Lang{on} = "å¼€"; $Lang{off} = "å…³"; $Lang{backupType_full} = "完全"; $Lang{backupType_incr} = "增é‡"; $Lang{backupType_partial} = "部分"; $Lang{failed} = "失败"; $Lang{success} = "æˆåŠŸ"; $Lang{and} = "å’Œ"; # ------ # Hosts states and reasons $Lang{Status_idle} = "空闲"; $Lang{Status_backup_starting} = "备份已开始"; $Lang{Status_backup_in_progress} = "备份进行中"; $Lang{Status_restore_starting} = "æ¢å¤å·²å¼€å§‹"; $Lang{Status_restore_in_progress} = "æ¢å¤è¿›è¡Œä¸­"; $Lang{Status_link_pending} = "文件链接待建立"; $Lang{Status_link_running} = "文件链接建立中"; $Lang{Reason_backup_done} = "完æˆ"; $Lang{Reason_restore_done} = "æ¢å¤å®Œæˆ"; $Lang{Reason_archive_done} = "备档完æˆ"; $Lang{Reason_nothing_to_do} = "空闲"; $Lang{Reason_backup_failed} = "备份失败"; $Lang{Reason_restore_failed} = "æ¢å¤å¤±è´¥"; $Lang{Reason_archive_failed} = "备档失败"; $Lang{Reason_no_ping} = "网络连接中断(no ping)"; $Lang{Reason_backup_canceled_by_user} = "å¤‡ä»½è¢«ç”¨æˆ·å–æ¶ˆ"; $Lang{Reason_restore_canceled_by_user} = "æ¢å¤è¢«ç”¨æˆ·å–消"; $Lang{Reason_archive_canceled_by_user} = "å¤‡æ¡£è¢«ç”¨æˆ·å–æ¶ˆ"; $Lang{Disabled_OnlyManualBackups} = "自动备份被关闭"; $Lang{Disabled_AllBackupsDisabled} = "关闭"; # --------- # Email messages # No backup ever $Lang{EMailNoBackupEverSubj} = "BackupPC: 客户机 \$host 从未被æˆåŠŸå¤‡ä»½è¿‡"; $Lang{EMailNoBackupEverMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers 尊敬的用户 $userName, 您的电脑 ($host) è¿˜ä»Žæ¥æ²¡æœ‰è¢«æˆ‘们的备份系统æˆåŠŸå¤‡ä»½è¿‡ã€‚ 正常情况下,当您的电脑与网络连接时电脑备份会自动进行。 如果您属于下é¢ä¸¤ç§æƒ…况,请与系统管ç†å‘˜è”系: ï¼ æ‚¨çš„ç”µè„‘ç»å¸¸æ˜¯è¿žåœ¨ç½‘络上的。这æ„味ç€å¯èƒ½æ˜¯æŸäº›é…ç½® æ–¹é¢çš„问题导致备份无法进行。 ï¼ æ‚¨ä¸å¸Œæœ›æ‚¨çš„ç”µè„‘è¢«å¤‡ä»½ï¼Œå¹¶ä¸”ä¸æ„¿å†æ”¶åˆ°è¿™äº›ç”µå­é‚®ä»¶ã€‚ å¦‚æžœä¸æ˜¯ä»¥ä¸Šè¿™äº›æƒ…况,请确认您的电脑是被连接在网络上的。 此致敬礼, BackupPC Genie http://backuppc.sourceforge.net EOF # No recent backup $Lang{EMailNoBackupRecentSubj} = "BackupPC: 客户机 \$host 最近未被备份过"; $Lang{EMailNoBackupRecentMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers 尊敬的用户 $userName, 您的电脑 ($host) å·²ç»æœ‰ $days 天没有被æˆåŠŸå¤‡ä»½è¿‡äº†ã€‚æ‚¨çš„ç”µè„‘ 第一次被备份是在 $firstTime 天å‰ï¼Œç›´è‡³ $days 天å‰å·²ç»è¢«å¤‡ä»½è¿‡ $numBackups 次。 正常情况下,当您的电脑与网络连接时电脑备份会自动进行。 在最近 $days 天内,如果您的电脑已ç»ä¸Žç½‘ç»œè¿žæŽ¥äº†è‹¥å¹²å°æ—¶ï¼Œ 请与系统管ç†å‘˜è”系以判断为什么备份没有进行。 除此之外,如果您ä¸åœ¨åŠžå…¬å®¤ï¼Œæ‚¨åªèƒ½æ‰‹åŠ¨æ‹·è´é‡è¦æ–‡ä»¶åˆ°å…¶å®ƒå­˜å‚¨ä»‹è´¨ä¸Šã€‚ 应该æé†’您的是,如果您的电脑ç£ç›˜æŸå,您在最近 $days 天内创建或修改 的文件,包括新收到的电å­é‚®ä»¶å’Œé™„件,将无法被æ¢å¤ã€‚ 此致敬礼, BackupPC Genie http://backuppc.sourceforge.net EOF # Old Outlook files $Lang{EMailOutlookBackupSubj} = "BackupPC: 客户机 \$host 上的微软 Outlook 文件需è¦å¤‡ä»½"; $Lang{EMailOutlookBackupMesg} = <<'EOF'; To: $user$domain cc: Subject: $subj $headers 尊敬的用户 $userName, 您的电脑上的 Outlook 文件 $howLong。 这些文件包括所有您的电å­é‚®ä»¶ï¼Œé™„ä»¶ï¼Œé€šè®¯å½•åŠæ—¥ç¨‹è¡¨ä¿¡æ¯ã€‚ 您的电脑第一次被备份是在 $firstTime 天å‰ï¼Œç›´è‡³ $lastTime 天å‰å·²ç»è¢« 备份过 $numBackups 次。但是,Outlook 在è¿è¡Œæ—¶é”使‰€æœ‰æ‰€å±žæ–‡ä»¶ï¼Œ 导致这些文件无法被备份。 建议您ä¾ä»¥ä¸‹æ–¹å¼å¤‡ä»½ Outlook 文件: 1。首先确认电脑是连接在网路上; 2。退出 Outlook åŠæ‰€æœ‰å…¶å®ƒåº”用; 3。使用网页æµè§ˆå™¨è®¿é—®æ­¤é“¾æŽ¥ï¼š $CgiURL?host=$host 选择 “开始增é‡å¤‡ä»½â€ï¼Œå¯åŠ¨å¢žé‡å¤‡ä»½æ“作;然åŽé€‰æ‹© “返回 $host 主页†并用æµè§ˆå™¨çš„ â€œåˆ·æ–°â€ åŠŸèƒ½æ¥æ£€æŸ¥è¯¥å¤‡ä»½æ“作的状æ€ã€‚ 此致敬礼, BackupPC Genie http://backuppc.sourceforge.net EOF $Lang{howLong_not_been_backed_up} = "还从未被æˆåŠŸå¤‡ä»½è¿‡"; $Lang{howLong_not_been_backed_up_for_days_days} = "å·²ç»æœ‰ \$days 天未被备份过"; ####################################################################### # RSS strings ####################################################################### $Lang{RSS_Doc_Title} = "BackupPC æœåС噍"; $Lang{RSS_Doc_Description} = "RSS feed for BackupPC"; $Lang{RSS_Host_Summary} = < 注æ„:适用于所有客户机的全局性默认é…置,其相应 “替æ¢â€ æ—的方框是ä¸è¢«é€‰æ‹©çš„。如果è¦ä¿®æ”¹æœ¬å®¢æˆ·æœºçš„æŸé¡¹è®¾ç½®ï¼Œè¯·ç‚¹å‡» “替æ¢â€ æ—的方框。如果该设置已ç»å¤„于被修改状æ€ï¼Œåˆ™ä¿®æ”¹åŽä¸éœ€ç‚¹å‡» “替æ¢â€ æ—的方框。如果è¦å°†å…¶è¿˜åŽŸä½¿ç”¨é»˜è®¤é…置,则需点击 “替æ¢â€ æ—的方框,使其处于未被修改状æ€ã€‚

    EOF $Lang{CfgEdit_Button_Save} = "ä¿å­˜"; $Lang{CfgEdit_Button_Insert} = "æ’å…¥"; $Lang{CfgEdit_Button_Delete} = "删除"; $Lang{CfgEdit_Button_Add} = "添加"; $Lang{CfgEdit_Button_Override} = "替æ¢"; $Lang{CfgEdit_Button_New_Key} = "文件å·å"; $Lang{CfgEdit_Button_New_Share} = "New ShareName or '*'"; $Lang{CfgEdit_Error_No_Save} = "错误:有误,无法ä¿å­˜"; $Lang{CfgEdit_Error__must_be_an_integer} = "错误:\$var 必须是整数"; $Lang{CfgEdit_Error__must_be_real_valued_number} = "错误:\$var 必须是实数,ä¸èƒ½æ˜¯æµ®ç‚¹æ•°"; $Lang{CfgEdit_Error__entry__must_be_an_integer} = "错误:\$var 内容 \$k 必须是整数"; $Lang{CfgEdit_Error__entry__must_be_real_valued_number} = "错误:\$var 内容 \$k 必须是实数,ä¸èƒ½æ˜¯æµ®ç‚¹æ•°"; $Lang{CfgEdit_Error__must_be_executable_program} = "错误:\$var å¿…é¡»æ˜¯å¯æ‰§è¡Œç¨‹åº"; $Lang{CfgEdit_Error__must_be_valid_option} = "错误:\$var å¿…é¡»æ˜¯åˆæ³•选项"; $Lang{CfgEdit_Error_Copy_host_does_not_exist} = "客户机 \$copyHost ä¸å­˜åœ¨ï¼›ç”Ÿæˆå…¨è®¡ç®—机å \$fullHostã€‚å¦‚æžœæ­¤å®¢æˆ·æœºä¸æ˜¯ä½ æƒ³è¦çš„,请将它删除。"; $Lang{CfgEdit_Log_Copy_host_config} = "用户 \$User æ‹·è´äº†å®¢æˆ·æœº \$fromHost çš„é…置到客户机 \$host\n"; $Lang{CfgEdit_Log_Delete_param} = "用户 \$User 从é…ç½® \$conf 中删除了 \$p\n"; $Lang{CfgEdit_Log_Add_param_value} = "用户 \$User 添加了 \$p 到é…ç½® \$conf 中,值设为 \$value\n"; $Lang{CfgEdit_Log_Change_param_value} = "用户 \$User å°†é…ç½® \$conf 中的 \$p 从 \$valueOld 更改为 \$valueNew\n"; $Lang{CfgEdit_Log_Host_Delete} = "用户 \$User 删除了客户机 \$host\n"; $Lang{CfgEdit_Log_Host_Change} = "用户 \$User 将客户机 \$host 上的 \$key 从 \$valueOld 更改为 \$valueNew\n"; $Lang{CfgEdit_Log_Host_Add} = "用户 \$User 添加了客户机 \$host: \$value\n"; #end of lang_zh_CN.pm BackupPC-3.3.2/lib/BackupPC/Lib.pm0000444000076500000240000012263513042250554015370 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Lib package # # DESCRIPTION # # This library defines a BackupPC::Lib class and a variety of utility # functions used by BackupPC. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Lib; use strict; use vars qw(%Conf %Lang); use BackupPC::Storage; use Fcntl ':mode'; use Carp; use File::Path; use File::Compare; use Socket; use Cwd; use Digest::MD5; use Config; use Encode qw/from_to encode_utf8/; use vars qw( $IODirentOk $IODirentLoaded ); use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT_OK = qw( BPC_DT_UNKNOWN BPC_DT_FIFO BPC_DT_CHR BPC_DT_DIR BPC_DT_BLK BPC_DT_REG BPC_DT_LNK BPC_DT_SOCK ); @EXPORT = qw( ); %EXPORT_TAGS = ('BPC_DT_ALL' => [@EXPORT, @EXPORT_OK]); BEGIN { eval "use IO::Dirent qw( readdirent );"; $IODirentLoaded = 1 if ( !$@ ); }; # # The need to match the constants in IO::Dirent # use constant BPC_DT_UNKNOWN => 0; use constant BPC_DT_FIFO => 1; ## named pipe (fifo) use constant BPC_DT_CHR => 2; ## character special use constant BPC_DT_DIR => 4; ## directory use constant BPC_DT_BLK => 6; ## block special use constant BPC_DT_REG => 8; ## regular use constant BPC_DT_LNK => 10; ## symbolic link use constant BPC_DT_SOCK => 12; ## socket sub new { my $class = shift; my($topDir, $installDir, $confDir, $noUserCheck) = @_; # # Whether to use filesystem hierarchy standard for file layout. # If set, text config files are below /etc/BackupPC. # my $useFHS = 0; my $paths; # # Set defaults for $topDir and $installDir. # $topDir = '__TOPDIR__' if ( $topDir eq "" ); $installDir = '__INSTALLDIR__' if ( $installDir eq "" ); # # Pick some initial defaults. For FHS the only critical # path is the ConfDir, since we get everything else out # of the main config file. # if ( $useFHS ) { $paths = { useFHS => $useFHS, TopDir => $topDir, InstallDir => $installDir, ConfDir => $confDir eq "" ? '__CONFDIR__' : $confDir, LogDir => '/var/log/BackupPC', }; } else { $paths = { useFHS => $useFHS, TopDir => $topDir, InstallDir => $installDir, ConfDir => $confDir eq "" ? "$topDir/conf" : $confDir, LogDir => "$topDir/log", }; } my $bpc = bless { %$paths, Version => '3.3.2', }, $class; $bpc->{storage} = BackupPC::Storage->new($paths); # # Clean up %ENV and setup other variables. # delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; if ( defined(my $error = $bpc->ConfigRead()) ) { print(STDERR $error, "\n"); return; } # # Update the paths based on the config file # foreach my $dir ( qw(TopDir ConfDir InstallDir LogDir) ) { next if ( $bpc->{Conf}{$dir} eq "" ); $paths->{$dir} = $bpc->{$dir} = $bpc->{Conf}{$dir}; } $bpc->{storage}->setPaths($paths); $bpc->{PoolDir} = "$bpc->{TopDir}/pool"; $bpc->{CPoolDir} = "$bpc->{TopDir}/cpool"; # # Verify we are running as the correct user # if ( !$noUserCheck && $bpc->{Conf}{BackupPCUserVerify} && $> != (my $uid = (getpwnam($bpc->{Conf}{BackupPCUser}))[2]) ) { print(STDERR "$0: Wrong user: my userid is $>, instead of $uid" . " ($bpc->{Conf}{BackupPCUser})\n"); print(STDERR "Please su $bpc->{Conf}{BackupPCUser} first\n"); return; } return $bpc; } sub TopDir { my($bpc) = @_; return $bpc->{TopDir}; } sub BinDir { my($bpc) = @_; return "$bpc->{InstallDir}/bin"; } sub LogDir { my($bpc) = @_; return $bpc->{LogDir}; } sub ConfDir { my($bpc) = @_; return $bpc->{ConfDir}; } sub LibDir { my($bpc) = @_; return "$bpc->{InstallDir}/lib"; } sub InstallDir { my($bpc) = @_; return $bpc->{InstallDir}; } sub useFHS { my($bpc) = @_; return $bpc->{useFHS}; } sub Version { my($bpc) = @_; return $bpc->{Version}; } sub Conf { my($bpc) = @_; return %{$bpc->{Conf}}; } sub Lang { my($bpc) = @_; return $bpc->{Lang}; } sub adminJob { my($bpc, $num) = @_; return " admin " if ( !$num ); return " admin$num "; } sub isAdminJob { my($bpc, $str) = @_; return $str =~ /^ admin/; } sub trashJob { return " trashClean "; } sub ConfValue { my($bpc, $param) = @_; return $bpc->{Conf}{$param}; } sub verbose { my($bpc, $param) = @_; $bpc->{verbose} = $param if ( defined($param) ); return $bpc->{verbose}; } sub sigName2num { my($bpc, $sig) = @_; if ( !defined($bpc->{SigName2Num}) ) { my $i = 0; foreach my $name ( split(' ', $Config{sig_name}) ) { $bpc->{SigName2Num}{$name} = $i; $i++; } } return $bpc->{SigName2Num}{$sig}; } # # Generate an ISO 8601 format timeStamp (but without the "T"). # See http://www.w3.org/TR/NOTE-datetime and # http://www.cl.cam.ac.uk/~mgk25/iso-time.html # sub timeStamp { my($bpc, $t, $noPad) = @_; my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($t || time); return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec) . ($noPad ? "" : " "); } sub BackupInfoRead { my($bpc, $host) = @_; return $bpc->{storage}->BackupInfoRead($host); } sub BackupInfoWrite { my($bpc, $host, @Backups) = @_; return $bpc->{storage}->BackupInfoWrite($host, @Backups); } sub RestoreInfoRead { my($bpc, $host) = @_; return $bpc->{storage}->RestoreInfoRead($host); } sub RestoreInfoWrite { my($bpc, $host, @Restores) = @_; return $bpc->{storage}->RestoreInfoWrite($host, @Restores); } sub ArchiveInfoRead { my($bpc, $host) = @_; return $bpc->{storage}->ArchiveInfoRead($host); } sub ArchiveInfoWrite { my($bpc, $host, @Archives) = @_; return $bpc->{storage}->ArchiveInfoWrite($host, @Archives); } sub ConfigDataRead { my($bpc, $host) = @_; return $bpc->{storage}->ConfigDataRead($host); } sub ConfigDataWrite { my($bpc, $host, $conf) = @_; return $bpc->{storage}->ConfigDataWrite($host, $conf); } sub ConfigRead { my($bpc, $host) = @_; my($ret); # # Read main config file # my($mesg, $config) = $bpc->{storage}->ConfigDataRead(); return $mesg if ( defined($mesg) ); $bpc->{Conf} = $config; # # Read host config file # if ( $host ne "" ) { ($mesg, $config) = $bpc->{storage}->ConfigDataRead($host, $config); return $mesg if ( defined($mesg) ); $bpc->{Conf} = $config; } # # Load optional perl modules # if ( defined($bpc->{Conf}{PerlModuleLoad}) ) { # # Load any user-specified perl modules. This is for # optional user-defined extensions. # $bpc->{Conf}{PerlModuleLoad} = [$bpc->{Conf}{PerlModuleLoad}] if ( ref($bpc->{Conf}{PerlModuleLoad}) ne "ARRAY" ); foreach my $module ( @{$bpc->{Conf}{PerlModuleLoad}} ) { eval("use $module;"); } } # # Load language file # return "No language setting" if ( !defined($bpc->{Conf}{Language}) ); my $langFile = "$bpc->{InstallDir}/lib/BackupPC/Lang/$bpc->{Conf}{Language}.pm"; if ( !defined($ret = do $langFile) && ($! || $@) ) { $mesg = "Couldn't open language file $langFile: $!" if ( $! ); $mesg = "Couldn't execute language file $langFile: $@" if ( $@ ); $mesg =~ s/[\n\r]+//; return $mesg; } $bpc->{Lang} = \%Lang; # # Make sure IncrLevels is defined # $bpc->{Conf}{IncrLevels} = [1] if ( !defined($bpc->{Conf}{IncrLevels}) ); return; } # # Return the mtime of the config file # sub ConfigMTime { my($bpc) = @_; return $bpc->{storage}->ConfigMTime(); } # # Returns information from the host file in $bpc->{TopDir}/conf/hosts. # With no argument a ref to a hash of hosts is returned. Each # hash contains fields as specified in the hosts file. With an # argument a ref to a single hash is returned with information # for just that host. # sub HostInfoRead { my($bpc, $host) = @_; return $bpc->{storage}->HostInfoRead($host); } sub HostInfoWrite { my($bpc, $host) = @_; return $bpc->{storage}->HostInfoWrite($host); } # # Return the mtime of the hosts file # sub HostsMTime { my($bpc) = @_; return $bpc->{storage}->HostsMTime(); } # # Read a directory and return the entries in sorted inode order. # This relies on the IO::Dirent module being installed. If not, # the inode data is empty and the default directory order is # returned. # # The returned data is a list of hashes with entries {name, type, inode, nlink}. # The returned data includes "." and "..". # # $need is a hash of file attributes we need: type, inode, or nlink. # If set, these parameters are added to the returned hash. # # To support browsing pre-3.0.0 backups where the charset encoding # is typically iso-8859-1, the charsetLegacy option can be set in # $need to convert the path from utf8 and convert the names to utf8. # # If IO::Dirent is successful if will get type and inode for free. # Otherwise, a stat is done on each file, which is more expensive. # sub dirRead { my($bpc, $path, $need) = @_; my(@entries, $addInode); from_to($path, "utf8", $need->{charsetLegacy}) if ( $need->{charsetLegacy} ne "" ); return if ( !opendir(my $fh, $path) ); if ( $IODirentLoaded && !$IODirentOk ) { # # Make sure the IO::Dirent really works - some installs # on certain file systems (eg: XFS) don't return a valid type. # and some fail to return valid inode numbers. # # Also create a temporary file to make sure the inode matches. # my $tempTestFile = ".TestFileDirent.$$"; my $fullTempTestFile = $bpc->{TopDir} . "/$tempTestFile"; if ( open(my $fh, ">", $fullTempTestFile) ) { close($fh); } if ( opendir(my $fh, $bpc->{TopDir}) ) { foreach my $e ( readdirent($fh) ) { if ( $e->{name} eq "." && $e->{type} == BPC_DT_DIR && $e->{inode} == (stat($bpc->{TopDir}))[1] ) { $IODirentOk |= 0x1; } if ( $e->{name} eq $tempTestFile && $e->{type} == BPC_DT_REG && $e->{inode} == (stat($fullTempTestFile))[1] ) { $IODirentOk |= 0x2; } } closedir($fh); } unlink($fullTempTestFile) if ( -f $fullTempTestFile ); # # if it isn't ok then don't check again. # if ( $IODirentOk != 0x3 ) { $IODirentLoaded = 0; $IODirentOk = 0; } } if ( $IODirentOk ) { @entries = sort({ $a->{inode} <=> $b->{inode} } readdirent($fh)); map { $_->{type} = 0 + $_->{type} } @entries; # make type numeric } else { @entries = map { { name => $_} } readdir($fh); } closedir($fh); if ( defined($need) ) { for ( my $i = 0 ; $i < @entries ; $i++ ) { next if ( (!$need->{inode} || defined($entries[$i]{inode})) && (!$need->{type} || defined($entries[$i]{type})) && (!$need->{nlink} || defined($entries[$i]{nlink})) ); my @s = stat("$path/$entries[$i]{name}"); $entries[$i]{nlink} = $s[3] if ( $need->{nlink} ); if ( $need->{inode} && !defined($entries[$i]{inode}) ) { $addInode = 1; $entries[$i]{inode} = $s[1]; } if ( $need->{type} && !defined($entries[$i]{type}) ) { my $mode = S_IFMT($s[2]); $entries[$i]{type} = BPC_DT_FIFO if ( S_ISFIFO($mode) ); $entries[$i]{type} = BPC_DT_CHR if ( S_ISCHR($mode) ); $entries[$i]{type} = BPC_DT_DIR if ( S_ISDIR($mode) ); $entries[$i]{type} = BPC_DT_BLK if ( S_ISBLK($mode) ); $entries[$i]{type} = BPC_DT_REG if ( S_ISREG($mode) ); $entries[$i]{type} = BPC_DT_LNK if ( S_ISLNK($mode) ); $entries[$i]{type} = BPC_DT_SOCK if ( S_ISSOCK($mode) ); } } } # # Sort the entries if inodes were added (the IO::Dirent case already # sorted above) # @entries = sort({ $a->{inode} <=> $b->{inode} } @entries) if ( $addInode ); # # for browing pre-3.0.0 backups, map iso-8859-1 to utf8 if requested # if ( $need->{charsetLegacy} ne "" ) { for ( my $i = 0 ; $i < @entries ; $i++ ) { from_to($entries[$i]{name}, $need->{charsetLegacy}, "utf8"); } } return \@entries; } # # Same as dirRead, but only returns the names (which will be sorted in # inode order if IO::Dirent is installed) # sub dirReadNames { my($bpc, $path, $need) = @_; my $entries = $bpc->dirRead($path, $need); return if ( !defined($entries) ); my @names = map { $_->{name} } @$entries; return \@names; } sub find { my($bpc, $param, $dir, $dontDoCwd) = @_; return if ( !chdir($dir) ); my $entries = $bpc->dirRead(".", {inode => 1, type => 1}); #print Dumper($entries); foreach my $f ( @$entries ) { next if ( $f->{name} eq ".." || $f->{name} eq "." && $dontDoCwd ); $param->{wanted}($f->{name}, "$dir/$f->{name}"); next if ( $f->{type} != BPC_DT_DIR || $f->{name} eq "." ); chdir($f->{name}); $bpc->find($param, "$dir/$f->{name}", 1); return if ( !chdir("..") ); } } # # Stripped down from File::Path. In particular we don't print # many warnings and we try three times to delete each directory # and file -- for some reason the original File::Path rmtree # didn't always completely remove a directory tree on a NetApp. # # Warning: this routine changes the cwd. # sub RmTreeQuiet { my($bpc, $pwd, $roots) = @_; my(@files, $root); if ( defined($roots) && length($roots) ) { $roots = [$roots] unless ref $roots; } else { print(STDERR "RmTreeQuiet: No root path(s) specified\n"); } chdir($pwd); foreach $root (@{$roots}) { $root = $1 if ( $root =~ m{(.*?)/*$} ); # # Try first to simply unlink the file: this avoids an # extra stat for every file. If it fails (which it # will for directories), check if it is a directory and # then recurse. # if ( !unlink($root) ) { if ( -d $root ) { my $d = $bpc->dirReadNames($root); if ( !defined($d) ) { print(STDERR "Can't read $pwd/$root: $!\n"); } else { @files = grep $_ !~ /^\.{1,2}$/, @$d; $bpc->RmTreeQuiet("$pwd/$root", \@files); chdir($pwd); rmdir($root) || rmdir($root); } } else { unlink($root) || unlink($root); } } } } # # Move a directory or file away for later deletion # sub RmTreeDefer { my($bpc, $trashDir, $file) = @_; my($i, $f); return if ( !-e $file ); if ( !-d $trashDir ) { eval { mkpath($trashDir, 0, 0777) }; if ( $@ ) { # # There's no good place to send this error - use stderr # print(STDERR "RmTreeDefer: can't create directory $trashDir"); } } for ( $i = 0 ; $i < 1000 ; $i++ ) { $f = sprintf("%s/%d_%d_%d", $trashDir, time, $$, $i); next if ( -e $f ); return if ( rename($file, $f) ); } # shouldn't get here, but might if you tried to call this # across file systems.... just remove the tree right now. if ( $file =~ /(.*)\/([^\/]*)/ ) { my($d) = $1; my($f) = $2; my($cwd) = Cwd::fastcwd(); $cwd = $1 if ( $cwd =~ /(.*)/ ); $bpc->RmTreeQuiet($d, $f); chdir($cwd) if ( $cwd ); } } # # Empty the trash directory. Returns 0 if it did nothing, 1 if it # did something, -1 if it failed to remove all the files. # sub RmTreeTrashEmpty { my($bpc, $trashDir) = @_; my(@files); my($cwd) = Cwd::fastcwd(); $cwd = $1 if ( $cwd =~ /(.*)/ ); return if ( !-d $trashDir ); my $d = $bpc->dirReadNames($trashDir) or carp "Can't read $trashDir: $!"; @files = grep $_ !~ /^\.{1,2}$/, @$d; return 0 if ( !@files ); $bpc->RmTreeQuiet($trashDir, \@files); foreach my $f ( @files ) { return -1 if ( -e $f ); } chdir($cwd) if ( $cwd ); return 1; } # # Open a connection to the server. Returns an error string on failure. # Returns undef on success. # sub ServerConnect { my($bpc, $host, $port, $justConnect) = @_; local(*FH); return if ( defined($bpc->{ServerFD}) ); # # First try the unix-domain socket # my $sockFile = "$bpc->{LogDir}/BackupPC.sock"; socket(*FH, PF_UNIX, SOCK_STREAM, 0) || return "unix socket: $!"; if ( !connect(*FH, sockaddr_un($sockFile)) ) { my $err = "unix connect: $!"; close(*FH); if ( $port > 0 ) { my $proto = getprotobyname('tcp'); my $iaddr = inet_aton($host) || return "unknown host $host"; my $paddr = sockaddr_in($port, $iaddr); socket(*FH, PF_INET, SOCK_STREAM, $proto) || return "inet socket: $!"; connect(*FH, $paddr) || return "inet connect: $!"; } else { return $err; } } my($oldFH) = select(*FH); $| = 1; select($oldFH); $bpc->{ServerFD} = *FH; return if ( $justConnect ); # # Read the seed that we need for our MD5 message digest. See # ServerMesg below. # sysread($bpc->{ServerFD}, $bpc->{ServerSeed}, 1024); $bpc->{ServerMesgCnt} = 0; return; } # # Check that the server connection is still ok # sub ServerOK { my($bpc) = @_; return 0 if ( !defined($bpc->{ServerFD}) ); vec(my $FDread, fileno($bpc->{ServerFD}), 1) = 1; my $ein = $FDread; return 0 if ( select(my $rout = $FDread, undef, $ein, 0.0) < 0 ); return 1 if ( !vec($rout, fileno($bpc->{ServerFD}), 1) ); } # # Disconnect from the server # sub ServerDisconnect { my($bpc) = @_; return if ( !defined($bpc->{ServerFD}) ); close($bpc->{ServerFD}); delete($bpc->{ServerFD}); } # # Sends a message to the server and returns with the reply. # # To avoid possible attacks via the TCP socket interface, every client # message is protected by an MD5 digest. The MD5 digest includes four # items: # - a seed that is sent to us when we first connect # - a sequence number that increments for each message # - a shared secret that is stored in $Conf{ServerMesgSecret} # - the message itself. # The message is sent in plain text preceded by the MD5 digest. A # snooper can see the plain-text seed sent by BackupPC and plain-text # message, but cannot construct a valid MD5 digest since the secret in # $Conf{ServerMesgSecret} is unknown. A replay attack is not possible # since the seed changes on a per-connection and per-message basis. # sub ServerMesg { my($bpc, $mesg) = @_; return if ( !defined(my $fh = $bpc->{ServerFD}) ); $mesg =~ s/\n/\\n/g; $mesg =~ s/\r/\\r/g; my $md5 = Digest::MD5->new; $mesg = encode_utf8($mesg); $md5->add($bpc->{ServerSeed} . $bpc->{ServerMesgCnt} . $bpc->{Conf}{ServerMesgSecret} . $mesg); print($fh $md5->b64digest . " $mesg\n"); $bpc->{ServerMesgCnt}++; return <$fh>; } # # Do initialization for child processes # sub ChildInit { my($bpc) = @_; close(STDERR); open(STDERR, ">&STDOUT"); select(STDERR); $| = 1; select(STDOUT); $| = 1; $ENV{PATH} = $bpc->{Conf}{MyPath}; } # # Compute the MD5 digest of a file. For efficiency we don't # use the whole file for big files: # - for files <= 256K we use the file size and the whole file. # - for files <= 1M we use the file size, the first 128K and # the last 128K. # - for files > 1M, we use the file size, the first 128K and # the 8th 128K (ie: the 128K up to 1MB). # See the documentation for a discussion of the tradeoffs in # how much data we use and how many collisions we get. # # Returns the MD5 digest (a hex string) and the file size. # sub File2MD5 { my($bpc, $md5, $name) = @_; my($data, $fileSize); local(*N); $fileSize = (stat($name))[7]; return ("", -1) if ( !-f _ ); $name = $1 if ( $name =~ /(.*)/ ); return ("", 0) if ( $fileSize == 0 ); return ("", -1) if ( !open(N, $name) ); binmode(N); $md5->reset(); $md5->add($fileSize); if ( $fileSize > 262144 ) { # # read the first and last 131072 bytes of the file, # up to 1MB. # my $seekPosn = ($fileSize > 1048576 ? 1048576 : $fileSize) - 131072; $md5->add($data) if ( sysread(N, $data, 131072) ); $md5->add($data) if ( sysseek(N, $seekPosn, 0) && sysread(N, $data, 131072) ); } else { # # read the whole file # $md5->add($data) if ( sysread(N, $data, $fileSize) ); } close(N); return ($md5->hexdigest, $fileSize); } # # Compute the MD5 digest of a buffer (string). For efficiency we don't # use the whole string for big strings: # - for files <= 256K we use the file size and the whole file. # - for files <= 1M we use the file size, the first 128K and # the last 128K. # - for files > 1M, we use the file size, the first 128K and # the 8th 128K (ie: the 128K up to 1MB). # See the documentation for a discussion of the tradeoffs in # how much data we use and how many collisions we get. # # Returns the MD5 digest (a hex string). # sub Buffer2MD5 { my($bpc, $md5, $fileSize, $dataRef) = @_; $md5->reset(); $md5->add($fileSize); if ( $fileSize > 262144 ) { # # add the first and last 131072 bytes of the string, # up to 1MB. # my $seekPosn = ($fileSize > 1048576 ? 1048576 : $fileSize) - 131072; $md5->add(substr($$dataRef, 0, 131072)); $md5->add(substr($$dataRef, $seekPosn, 131072)); } else { # # add the whole string # $md5->add($$dataRef); } return $md5->hexdigest; } # # Given an MD5 digest $d and a compress flag, return the full # path in the pool. # sub MD52Path { my($bpc, $d, $compress, $poolDir) = @_; return if ( $d !~ m{(.)(.)(.)(.*)} ); $poolDir = ($compress ? $bpc->{CPoolDir} : $bpc->{PoolDir}) if ( !defined($poolDir) ); return "$poolDir/$1/$2/$3/$1$2$3$4"; } # # For each file, check if the file exists in $bpc->{TopDir}/pool. # If so, remove the file and make a hardlink to the file in # the pool. Otherwise, if the newFile flag is set, make a # hardlink in the pool to the new file. # # Returns 0 if a link should be made to a new file (ie: when the file # is a new file but the newFile flag is 0). # Returns 1 if a link to an existing file is made, # Returns 2 if a link to a new file is made (only if $newFile is set) # Returns negative on error. # sub MakeFileLink { my($bpc, $name, $d, $newFile, $compress) = @_; my($i, $rawFile); return -1 if ( !-f $name ); for ( $i = -1 ; ; $i++ ) { return -2 if ( !defined($rawFile = $bpc->MD52Path($d, $compress)) ); $rawFile .= "_$i" if ( $i >= 0 ); if ( -f $rawFile ) { if ( (stat(_))[3] < $bpc->{Conf}{HardLinkMax} && !compare($name, $rawFile) ) { unlink($name); return -3 if ( !link($rawFile, $name) ); return 1; } } elsif ( $newFile && -f $name && (stat($name))[3] == 1 ) { my($newDir); ($newDir = $rawFile) =~ s{(.*)/.*}{$1}; if ( !-d $newDir ) { eval { mkpath($newDir, 0, 0777) }; return -5 if ( $@ ); } return -4 if ( !link($name, $rawFile) ); return 2; } else { return 0; } } } # # Tests if we can create a hardlink from a file in directory # $newDir to a file in directory $targetDir. A temporary # file in $targetDir is created and an attempt to create a # hardlink of the same name in $newDir is made. The temporary # files are removed. # # Like link(), returns true on success and false on failure. # sub HardlinkTest { my($bpc, $targetDir, $newDir) = @_; my($targetFile, $newFile, $fd); for ( my $i = 0 ; ; $i++ ) { $targetFile = "$targetDir/.TestFileLink.$$.$i"; $newFile = "$newDir/.TestFileLink.$$.$i"; last if ( !-e $targetFile && !-e $newFile ); } return 0 if ( !open($fd, ">", $targetFile) ); close($fd); my $ret = link($targetFile, $newFile); unlink($targetFile); unlink($newFile); return $ret; } sub CheckHostAlive { my($bpc, $host) = @_; my($s, $pingCmd, $ret); # # Return success if the ping cmd is undefined or empty. # if ( $bpc->{Conf}{PingCmd} eq "" ) { print(STDERR "CheckHostAlive: return ok because \$Conf{PingCmd}" . " is empty\n") if ( $bpc->{verbose} ); return 0; } my $args = { pingPath => $bpc->{Conf}{PingPath}, host => $host, }; $pingCmd = $bpc->cmdVarSubstitute($bpc->{Conf}{PingCmd}, $args); # # Do a first ping in case the PC needs to wakeup # $s = $bpc->cmdSystemOrEval($pingCmd, undef, $args); if ( $? ) { print(STDERR "CheckHostAlive: first ping failed ($?, $!)\n") if ( $bpc->{verbose} ); return -1; } # # Do a second ping and get the round-trip time in msec # $s = $bpc->cmdSystemOrEval($pingCmd, undef, $args); if ( $? ) { print(STDERR "CheckHostAlive: second ping failed ($?, $!)\n") if ( $bpc->{verbose} ); return -1; } if ( $s =~ /rtt\s*min\/avg\/max\/mdev\s*=\s*[\d.]+\/([\d.]+)\/[\d.]+\/[\d.]+\s*(ms|usec)/i ) { $ret = $1; $ret /= 1000 if ( lc($2) eq "usec" ); } elsif ( $s =~ /time=([\d.]+)\s*(ms|usec)/i ) { $ret = $1; $ret /= 1000 if ( lc($2) eq "usec" ); } else { print(STDERR "CheckHostAlive: can't extract round-trip time" . " (not fatal)\n") if ( $bpc->{verbose} ); $ret = 0; } print(STDERR "CheckHostAlive: returning $ret\n") if ( $bpc->{verbose} ); return $ret; } sub CheckFileSystemUsage { my($bpc) = @_; my($topDir) = $bpc->{TopDir}; my($s, $dfCmd); return 0 if ( $bpc->{Conf}{DfCmd} eq "" ); my $args = { dfPath => $bpc->{Conf}{DfPath}, topDir => $bpc->{TopDir}, }; $dfCmd = $bpc->cmdVarSubstitute($bpc->{Conf}{DfCmd}, $args); $s = $bpc->cmdSystemOrEval($dfCmd, undef, $args); return 0 if ( $? || $s !~ /(\d+)%/s ); return $1; } # # Given an IP address, return the host name and user name via # NetBios. # sub NetBiosInfoGet { my($bpc, $host) = @_; my($netBiosHostName, $netBiosUserName); my($s, $nmbCmd); # # Skip NetBios check if NmbLookupCmd is emtpy # if ( $bpc->{Conf}{NmbLookupCmd} eq "" ) { print(STDERR "NetBiosInfoGet: return $host because \$Conf{NmbLookupCmd}" . " is empty\n") if ( $bpc->{verbose} ); return ($host, undef); } my $args = { nmbLookupPath => $bpc->{Conf}{NmbLookupPath}, host => $host, }; $nmbCmd = $bpc->cmdVarSubstitute($bpc->{Conf}{NmbLookupCmd}, $args); foreach ( split(/[\n\r]+/, $bpc->cmdSystemOrEval($nmbCmd, undef, $args)) ) { # # skip and other non entries # next if ( /<\w{2}> - /i ); next if ( !/^\s*([\w\s-]+?)\s*<(\w{2})\> - .*/i ); $netBiosHostName ||= $1 if ( $2 eq "00" ); # host is first 00 $netBiosUserName = $1 if ( $2 eq "03" ); # user is last 03 } if ( !defined($netBiosHostName) ) { print(STDERR "NetBiosInfoGet: failed: can't parse return string\n") if ( $bpc->{verbose} ); return; } $netBiosHostName = lc($netBiosHostName); $netBiosUserName = lc($netBiosUserName); print(STDERR "NetBiosInfoGet: success, returning host $netBiosHostName," . " user $netBiosUserName\n") if ( $bpc->{verbose} ); return ($netBiosHostName, $netBiosUserName); } # # Given a NetBios name lookup the IP address via NetBios. # In the case of a host returning multiple interfaces we # return the first IP address that matches the subnet mask. # If none match the subnet mask (or nmblookup doesn't print # the subnet mask) then just the first IP address is returned. # sub NetBiosHostIPFind { my($bpc, $host) = @_; my($netBiosHostName, $netBiosUserName); my($s, $nmbCmd, $subnet, $ipAddr, $firstIpAddr); # # Skip NetBios lookup if NmbLookupFindHostCmd is emtpy # if ( $bpc->{Conf}{NmbLookupFindHostCmd} eq "" ) { print(STDERR "NetBiosHostIPFind: return $host because" . " \$Conf{NmbLookupFindHostCmd} is empty\n") if ( $bpc->{verbose} ); return $host; } my $args = { nmbLookupPath => $bpc->{Conf}{NmbLookupPath}, host => $host, }; $nmbCmd = $bpc->cmdVarSubstitute($bpc->{Conf}{NmbLookupFindHostCmd}, $args); foreach my $resp ( split(/[\n\r]+/, $bpc->cmdSystemOrEval($nmbCmd, undef, $args) ) ) { if ( $resp =~ /querying\s+\Q$host\E\s+on\s+(\d+\.\d+\.\d+\.\d+)/i ) { $subnet = $1; $subnet = $1 if ( $subnet =~ /^(.*?)(\.255)+$/ ); } elsif ( $resp =~ /^\s*(\d+\.\d+\.\d+\.\d+)\s+\Q$host/ ) { my $ip = $1; $firstIpAddr = $ip if ( !defined($firstIpAddr) ); $ipAddr = $ip if ( !defined($ipAddr) && $ip =~ /^\Q$subnet/ ); } } $ipAddr = $firstIpAddr if ( !defined($ipAddr) ); if ( defined($ipAddr) ) { print(STDERR "NetBiosHostIPFind: found IP address $ipAddr for" . " host $host\n") if ( $bpc->{verbose} ); return $ipAddr; } else { print(STDERR "NetBiosHostIPFind: couldn't find IP address for" . " host $host\n") if ( $bpc->{verbose} ); return; } } sub fileNameEltMangle { my($bpc, $name) = @_; return "" if ( $name eq "" ); $name =~ s{([%/\n\r])}{sprintf("%%%02x", ord($1))}eg; return "f$name"; } # # We store files with every name preceded by "f". This # avoids possible name conflicts with other information # we store in the same directories (eg: attribute info). # The process of turning a normal path into one with each # node prefixed with "f" is called mangling. # sub fileNameMangle { my($bpc, $name) = @_; $name =~ s{/([^/]+)}{"/" . $bpc->fileNameEltMangle($1)}eg; $name =~ s{^([^/]+)}{$bpc->fileNameEltMangle($1)}eg; return $name; } # # This undoes FileNameMangle # sub fileNameUnmangle { my($bpc, $name) = @_; $name =~ s{/f}{/}g; $name =~ s{^f}{}; $name =~ s{%(..)}{chr(hex($1))}eg; return $name; } # # Escape shell meta-characters with backslashes. # This should be applied to each argument seperately, not an # entire shell command. # sub shellEscape { my($bpc, $cmd) = @_; $cmd =~ s/([][;&()<>{}|^\n\r\t *\$\\'"`?])/\\$1/g; return $cmd; } # # For printing exec commands (which don't use a shell) so they look like # a valid shell command this function should be called with the exec # args. The shell command string is returned. # sub execCmd2ShellCmd { my($bpc, @args) = @_; my $str; foreach my $a ( @args ) { $str .= " " if ( $str ne "" ); $str .= $bpc->shellEscape($a); } return $str; } # # Do a URI-style escape to protect/encode special characters # sub uriEsc { my($bpc, $s) = @_; $s =~ s{([^\w.\/-])}{sprintf("%%%02X", ord($1));}eg; return $s; } # # Do a URI-style unescape to restore special characters # sub uriUnesc { my($bpc, $s) = @_; $s =~ s{%(..)}{chr(hex($1))}eg; return $s; } # # Do variable substitution prior to execution of a command. # sub cmdVarSubstitute { my($bpc, $template, $vars) = @_; my(@cmd); # # Return without any substitution if the first entry starts with "&", # indicating this is perl code. # if ( (ref($template) eq "ARRAY" ? $template->[0] : $template) =~ /^\&/ ) { return ref($template) eq "ARRAY" ? $template : [$template]; } if ( ref($template) ne "ARRAY" ) { # # Split at white space, except if escaped by \ # $template = [split(/(?{$1}) && ref($vars->{$1}) ne "ARRAY" ? ($2 eq "+" ? $bpc->shellEscape($vars->{$1}) : $vars->{$1}) : "\${$1}$2" }eg; # # Now replicate any array arguments; this just works for just one # array var in each argument. # if ( $arg =~ m[(.*)\$\{(\w+)}(\+?)(.*)] && ref($vars->{$2}) eq "ARRAY" ) { my $pre = $1; my $var = $2; my $esc = $3; my $post = $4; foreach my $v ( @{$vars->{$var}} ) { $v = $bpc->shellEscape($v) if ( $esc eq "+" ); push(@cmd, "$pre$v$post"); } } else { push(@cmd, $arg); } } return \@cmd; } # # Exec or eval a command. $cmd is either a string on an array ref. # # @args are optional arguments for the eval() case; they are not used # for exec(). # sub cmdExecOrEval { my($bpc, $cmd, @args) = @_; if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) { $cmd = join(" ", @$cmd) if ( ref($cmd) eq "ARRAY" ); print(STDERR "cmdExecOrEval: about to eval perl code $cmd\n") if ( $bpc->{verbose} ); eval($cmd); print(STDERR "Perl code fragment for exec shouldn't return!!\n"); exit(1); } else { $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" ); print(STDERR "cmdExecOrEval: about to exec ", $bpc->execCmd2ShellCmd(@$cmd), "\n") if ( $bpc->{verbose} ); alarm(0); $cmd = [map { m/(.*)/ } @$cmd]; # untaint # # force list-form of exec(), ie: no shell even for 1 arg # exec { $cmd->[0] } @$cmd; print(STDERR "Exec failed for @$cmd\n"); exit(1); } } # # System or eval a command. $cmd is either a string on an array ref. # $stdoutCB is a callback for output generated by the command. If it # is undef then output is returned. If it is a code ref then the function # is called with each piece of output as an argument. If it is a scalar # ref the output is appended to this variable. # # @args are optional arguments for the eval() case; they are not used # for system(). # # Also, $? should be set when the CHILD pipe is closed. # sub cmdSystemOrEvalLong { my($bpc, $cmd, $stdoutCB, $ignoreStderr, $pidHandlerCB, @args) = @_; my($pid, $out, $allOut); local(*CHILD); $? = 0; if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) { $cmd = join(" ", @$cmd) if ( ref($cmd) eq "ARRAY" ); print(STDERR "cmdSystemOrEval: about to eval perl code $cmd\n") if ( $bpc->{verbose} ); $out = eval($cmd); $$stdoutCB .= $out if ( ref($stdoutCB) eq 'SCALAR' ); &$stdoutCB($out) if ( ref($stdoutCB) eq 'CODE' ); print(STDERR "cmdSystemOrEval: finished: got output $out\n") if ( $bpc->{verbose} ); return $out if ( !defined($stdoutCB) ); return; } else { $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" ); print(STDERR "cmdSystemOrEval: about to system ", $bpc->execCmd2ShellCmd(@$cmd), "\n") if ( $bpc->{verbose} ); if ( !defined($pid = open(CHILD, "-|")) ) { my $err = "Can't fork to run @$cmd\n"; $? = 1; $$stdoutCB .= $err if ( ref($stdoutCB) eq 'SCALAR' ); &$stdoutCB($err) if ( ref($stdoutCB) eq 'CODE' ); return $err if ( !defined($stdoutCB) ); return; } binmode(CHILD); if ( !$pid ) { # # This is the child # close(STDERR); if ( $ignoreStderr ) { open(STDERR, ">", "/dev/null"); } else { open(STDERR, ">&STDOUT"); } alarm(0); $cmd = [map { m/(.*)/ } @$cmd]; # untaint # # force list-form of exec(), ie: no shell even for 1 arg # exec { $cmd->[0] } @$cmd; print(STDERR "Exec of @$cmd failed\n"); exit(1); } # # Notify caller of child's pid # &$pidHandlerCB($pid) if ( ref($pidHandlerCB) eq "CODE" ); # # The parent gathers the output from the child # while ( ) { $$stdoutCB .= $_ if ( ref($stdoutCB) eq 'SCALAR' ); &$stdoutCB($_) if ( ref($stdoutCB) eq 'CODE' ); $out .= $_ if ( !defined($stdoutCB) ); $allOut .= $_ if ( $bpc->{verbose} ); } $? = 0; close(CHILD); } print(STDERR "cmdSystemOrEval: finished: got output $allOut\n") if ( $bpc->{verbose} ); return $out; } # # The shorter version that sets $ignoreStderr = 0, ie: merges stdout # and stderr together. # sub cmdSystemOrEval { my($bpc, $cmd, $stdoutCB, @args) = @_; return $bpc->cmdSystemOrEvalLong($cmd, $stdoutCB, 0, undef, @args); } # # Promotes $conf->{BackupFilesOnly}, $conf->{BackupFilesExclude} # to hashes and $conf->{$shareName} to an array. # sub backupFileConfFix { my($bpc, $conf, $shareName) = @_; $conf->{$shareName} = [ $conf->{$shareName} ] if ( ref($conf->{$shareName}) ne "ARRAY" ); foreach my $param ( qw(BackupFilesOnly BackupFilesExclude) ) { next if ( !defined($conf->{$param}) ); if ( ref($conf->{$param}) eq "HASH" ) { # # A "*" entry means wildcard - it is the default for # all shares. Replicate the "*" entry for all shares, # but still allow override of specific entries. # next if ( !defined($conf->{$param}{"*"}) ); $conf->{$param} = { map({ $_ => $conf->{$param}{"*"} } @{$conf->{$shareName}}), %{$conf->{$param}} }; } else { $conf->{$param} = [ $conf->{$param} ] if ( ref($conf->{$param}) ne "ARRAY" ); $conf->{$param} = { map { $_ => $conf->{$param} } @{$conf->{$shareName}} }; } } } # # This is sort() compare function, used below. # # New client LOG names are LOG.MMYYYY. Old style names are # LOG, LOG.0, LOG.1 etc. Sort them so new names are # first, and newest to oldest. # sub compareLOGName { my $na = $1 if ( $a =~ /LOG\.(\d+)(\.z)?$/ ); my $nb = $1 if ( $b =~ /LOG\.(\d+)(\.z)?$/ ); $na = -1 if ( !defined($na) ); $nb = -1 if ( !defined($nb) ); if ( length($na) >= 5 && length($nb) >= 5 ) { # # Both new style: format is MMYYYY. Bigger dates are # more recent. # my $ma = $2 * 12 + $1 if ( $na =~ /(\d+)(\d{4})/ ); my $mb = $2 * 12 + $1 if ( $nb =~ /(\d+)(\d{4})/ ); return $mb - $ma; } elsif ( length($na) >= 5 && length($nb) < 5 ) { return -1; } elsif ( length($na) < 5 && length($nb) >= 5 ) { return 1; } else { # # Both old style. Smaller numbers are more recent. # return $na - $nb; } } # # Returns list of paths to a clients's (or main) LOG files, # most recent first. # sub sortedPCLogFiles { my($bpc, $host) = @_; my(@files, $dir); if ( $host ne "" ) { $dir = "$bpc->{TopDir}/pc/$host"; } else { $dir = "$bpc->{LogDir}"; } if ( opendir(DIR, $dir) ) { foreach my $file ( readdir(DIR) ) { next if ( !-f "$dir/$file" ); next if ( $file ne "LOG" && $file !~ /^LOG\.\d/ ); push(@files, "$dir/$file"); } closedir(DIR); } return sort compareLOGName @files; } # # converts a glob-style pattern into a perl regular expression. # sub glob2re { my ( $bpc, $glob ) = @_; my ( $char, $subst ); # $escapeChars escapes characters with no special glob meaning but # have meaning in regexps. my $escapeChars = [ '.', '/', ]; # $charMap is where we implement the special meaning of glob # patterns and translate them to regexps. my $charMap = { '?' => '[^/]', '*' => '[^/]*', }; # multiple forward slashes are equivalent to one slash. We should # never have to use this. $glob =~ s/\/+/\//; foreach $char (@$escapeChars) { $glob =~ s/\Q$char\E/\\$char/g; } while ( ( $char, $subst ) = each(%$charMap) ) { $glob =~ s/(? # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::PoolWrite; use strict; use File::Path; use Digest::MD5; use BackupPC::FileZIO; sub new { my($class, $bpc, $fileName, $fileSize, $compress) = @_; my $self = bless { fileName => $fileName, fileSize => $fileSize, bpc => $bpc, compress => $compress, nWrite => 0, digest => undef, files => [], fileCnt => -1, fhOut => undef, errors => [], data => "", eof => undef, }, $class; $self->{hardLinkMax} = $bpc->ConfValue("HardLinkMax"); # # Always unlink any current file in case it is already linked # unlink($fileName) if ( -f $fileName ); if ( $fileName =~ m{(.*)/.+} && !-d $1 ) { my $newDir = $1; eval { mkpath($newDir, 0, 0777) }; if ( $@ ) { push(@{$self->{errors}}, "Unable to create directory $newDir for $self->{fileName}"); } } return $self; } my $BufSize = 1048576; # 1MB or 2^20 my $MaxFiles = 20; # max number of compare files open at one time sub write { my($a, $dataRef) = @_; return if ( $a->{eof} ); $a->{data} .= $$dataRef if ( defined($dataRef) ); return if ( length($a->{data}) < $BufSize && defined($dataRef) ); # # Correct the fileSize if it is wrong (rsync might transfer # a file whose length is different to the length sent with the # file list if the file changes between the file list sending # and the file sending). Here we only catch the case where # we haven't computed the digest (ie: we have written no more # than $BufSize). We catch the big file case below. # if ( !defined($dataRef) && !defined($a->{digest}) && $a->{fileSize} != length($a->{data}) ) { #my $newSize = length($a->{data}); #print("Fixing file size from $a->{fileSize} to $newSize\n"); $a->{fileSize} = length($a->{data}); } if ( !defined($a->{digest}) && length($a->{data}) > 0 ) { # # build a list of all the candidate matching files # my $md5 = Digest::MD5->new; $a->{fileSize} = length($a->{data}) if ( $a->{fileSize} < length($a->{data}) ); $a->{digest} = $a->{bpc}->Buffer2MD5($md5, $a->{fileSize}, \$a->{data}); if ( !defined($a->{base} = $a->{bpc}->MD52Path($a->{digest}, $a->{compress})) ) { push(@{$a->{errors}}, "Unable to get path from '$a->{digest}'" . " for $a->{fileName}"); } else { while ( @{$a->{files}} < $MaxFiles ) { my $fh; my $fileName = $a->{fileCnt} < 0 ? $a->{base} : "$a->{base}_$a->{fileCnt}"; last if ( !-f $fileName ); # # Don't attempt to match pool files that already # have too many hardlinks. Also, don't match pool # files with only one link since starting in # BackupPC v3.0, BackupPC_nightly could be running # in parallel (and removing those files). This doesn't # eliminate all possible race conditions, but just # reduces the odds. Other design steps eliminate # the remaining race conditions of linking vs # removing. # if ( (stat(_))[3] >= $a->{hardLinkMax} || (stat(_))[3] <= 1 || !defined($fh = BackupPC::FileZIO->open($fileName, 0, $a->{compress})) ) { $a->{fileCnt}++; next; } push(@{$a->{files}}, { name => $fileName, fh => $fh, }); $a->{fileCnt}++; } } # # if there are no candidate files then we must write # the new file to disk # if ( !@{$a->{files}} ) { $a->{fhOut} = BackupPC::FileZIO->open($a->{fileName}, 1, $a->{compress}); if ( !defined($a->{fhOut}) ) { push(@{$a->{errors}}, "Unable to open $a->{fileName}" . " for writing"); } } } my $dataLen = length($a->{data}); if ( !defined($a->{fhOut}) && length($a->{data}) > 0 ) { # # See if the new chunk of data continues to match the # candidate files. # for ( my $i = 0 ; $i < @{$a->{files}} ; $i++ ) { my($d, $match); my $fileName = $a->{fileCnt} < 0 ? $a->{base} : "$a->{base}_$a->{fileCnt}"; if ( $dataLen > 0 ) { # verify next $dataLen bytes from candidate file my $n = $a->{files}[$i]->{fh}->read(\$d, $dataLen); next if ( $n == $dataLen && $d eq $a->{data} ); } else { # verify candidate file is at EOF my $n = $a->{files}[$i]->{fh}->read(\$d, 100); next if ( $n == 0 ); } #print(" File $a->{files}[$i]->{name} doesn't match\n"); # # this candidate file didn't match. Replace it # with a new candidate file. We have to qualify # any new candidate file by making sure that its # first $a->{nWrite} bytes match, plus the next $dataLen # bytes match $a->{data}. # while ( -f $fileName ) { my $fh; if ( (stat(_))[3] >= $a->{hardLinkMax} || !defined($fh = BackupPC::FileZIO->open($fileName, 0, $a->{compress})) ) { $a->{fileCnt}++; #print(" Discarding $fileName (open failed)\n"); $fileName = "$a->{base}_$a->{fileCnt}"; next; } if ( !$a->{files}[$i]->{fh}->rewind() ) { push(@{$a->{errors}}, "Unable to rewind $a->{files}[$i]->{name}" . " for compare"); } $match = $a->filePartialCompare($a->{files}[$i]->{fh}, $fh, $a->{nWrite}, $dataLen, \$a->{data}); if ( $match ) { $a->{files}[$i]->{fh}->close(); $a->{files}[$i]->{fh} = $fh, $a->{files}[$i]->{name} = $fileName; #print(" Found new candidate $fileName\n"); $a->{fileCnt}++; last; } else { #print(" Discarding $fileName (no match)\n"); } $fh->close(); $a->{fileCnt}++; $fileName = "$a->{base}_$a->{fileCnt}"; } if ( !$match ) { # # We couldn't find another candidate file # if ( @{$a->{files}} == 1 ) { #print(" Exhausted matches, now writing\n"); $a->{fhOut} = BackupPC::FileZIO->open($a->{fileName}, 1, $a->{compress}); if ( !defined($a->{fhOut}) ) { push(@{$a->{errors}}, "Unable to open $a->{fileName}" . " for writing"); } else { if ( !$a->{files}[$i]->{fh}->rewind() ) { push(@{$a->{errors}}, "Unable to rewind" . " $a->{files}[$i]->{name} for copy"); } $a->filePartialCopy($a->{files}[$i]->{fh}, $a->{fhOut}, $a->{nWrite}); } } $a->{files}[$i]->{fh}->close(); splice(@{$a->{files}}, $i, 1); $i--; } } } if ( defined($a->{fhOut}) && $dataLen > 0 ) { # # if we are in writing mode then just write the data # my $n = $a->{fhOut}->write(\$a->{data}); if ( $n != $dataLen ) { push(@{$a->{errors}}, "Unable to write $dataLen bytes to" . " $a->{fileName} (got $n)"); } } $a->{nWrite} += $dataLen; $a->{data} = ""; return if ( defined($dataRef) ); # # We are at EOF, so finish up # $a->{eof} = 1; # # Make sure the fileSize was correct. See above for comments about # rsync. # if ( $a->{nWrite} != $a->{fileSize} ) { # # Oops, fileSize was wrong, so our MD5 digest was wrong and our # effort to match files likely failed. This is ugly, but our # only choice at this point is to re-write the entire file with # the correct length. We need to rename the file, open it for # reading, and then re-write the file with the correct length. # #print("Doing big file fixup ($a->{fileSize} != $a->{nWrite})\n"); my($fh, $fileName); $a->{fileSize} = $a->{nWrite}; if ( defined($a->{fhOut}) ) { if ( $a->{fileName} =~ /(.*)\// ) { $fileName = $1; } else { $fileName = "."; } # # Find a unique target temporary file name # my $i = 0; while ( -f "$fileName/t$$.$i" ) { $i++; } $fileName = "$fileName/t$$.$i"; $a->{fhOut}->close(); if ( !rename($a->{fileName}, $fileName) || !defined($fh = BackupPC::FileZIO->open($fileName, 0, $a->{compress})) ) { push(@{$a->{errors}}, "Can't rename $a->{fileName} -> $fileName" . " or open during size fixup"); } #print("Using temporary name $fileName\n"); } elsif ( defined($a->{files}) && defined($a->{files}[0]) ) { # # We haven't written anything yet, so just use the # compare file to copy from. # $fh = $a->{files}[0]->{fh}; $fh->rewind; #print("Using compare file $a->{files}[0]->{name}\n"); } if ( defined($fh) ) { my $poolWrite = BackupPC::PoolWrite->new($a->{bpc}, $a->{fileName}, $a->{fileSize}, $a->{compress}); my $nRead = 0; while ( $nRead < $a->{fileSize} ) { my $thisRead = $a->{fileSize} - $nRead < $BufSize ? $a->{fileSize} - $nRead : $BufSize; my $data; my $n = $fh->read(\$data, $thisRead); if ( $n != $thisRead ) { push(@{$a->{errors}}, "Unable to read $thisRead bytes during resize" . " from temp $fileName (got $n)"); last; } $poolWrite->write(\$data); $nRead += $thisRead; } $fh->close; unlink($fileName) if ( defined($fileName) ); if ( @{$a->{errors}} ) { $poolWrite->close; return (0, $a->{digest}, -s $a->{fileName}, $a->{errors}); } else { return $poolWrite->close; } } } if ( $a->{fileSize} == 0 ) { # # Simply create an empty file # local(*OUT); if ( !open(OUT, ">", $a->{fileName}) ) { push(@{$a->{errors}}, "Can't open $a->{fileName} for empty" . " output"); } else { close(OUT); } # # Close the compare files # foreach my $f ( @{$a->{files}} ) { $f->{fh}->close(); } return (1, $a->{digest}, -s $a->{fileName}, $a->{errors}); } elsif ( defined($a->{fhOut}) ) { $a->{fhOut}->close(); # # Close the compare files # foreach my $f ( @{$a->{files}} ) { $f->{fh}->close(); } return (0, $a->{digest}, -s $a->{fileName}, $a->{errors}); } else { if ( @{$a->{files}} == 0 ) { push(@{$a->{errors}}, "Botch, no matches on $a->{fileName}" . " ($a->{digest})"); } elsif ( @{$a->{files}} > 1 ) { # # This is no longer a real error because $Conf{HardLinkMax} # could be hit, thereby creating identical pool files # #my $str = "Unexpected multiple matches on" # . " $a->{fileName} ($a->{digest})\n"; #for ( my $i = 0 ; $i < @{$a->{files}} ; $i++ ) { # $str .= " -> $a->{files}[$i]->{name}\n"; #} #push(@{$a->{errors}}, $str); } for ( my $i = 0 ; $i < @{$a->{files}} ; $i++ ) { if ( link($a->{files}[$i]->{name}, $a->{fileName}) ) { #print(" Linked $a->{fileName} to $a->{files}[$i]->{name}\n"); # # Close the compare files # foreach my $f ( @{$a->{files}} ) { $f->{fh}->close(); } return (1, $a->{digest}, -s $a->{fileName}, $a->{errors}); } } # # We were unable to link to the pool. Either we're at the # hardlink max, or the pool file got deleted. Recover by # writing the matching file, since we still have an open # handle. # for ( my $i = 0 ; $i < @{$a->{files}} ; $i++ ) { if ( !$a->{files}[$i]->{fh}->rewind() ) { push(@{$a->{errors}}, "Unable to rewind $a->{files}[$i]->{name}" . " for copy after link fail"); next; } $a->{fhOut} = BackupPC::FileZIO->open($a->{fileName}, 1, $a->{compress}); if ( !defined($a->{fhOut}) ) { push(@{$a->{errors}}, "Unable to open $a->{fileName}" . " for writing after link fail"); } else { $a->filePartialCopy($a->{files}[$i]->{fh}, $a->{fhOut}, $a->{nWrite}); $a->{fhOut}->close; } last; } # # Close the compare files # foreach my $f ( @{$a->{files}} ) { $f->{fh}->close(); } return (0, $a->{digest}, -s $a->{fileName}, $a->{errors}); } } # # Finish writing: pass undef dataRef to write so it can do all # the work. Returns a 4 element array: # # (existingFlag, digestString, outputFileLength, errorList) # sub close { my($a) = @_; return $a->write(undef); } # # Abort a pool write # sub abort { my($a) = @_; if ( defined($a->{fhOut}) ) { $a->{fhOut}->close(); unlink($a->{fileName}); } foreach my $f ( @{$a->{files}} ) { $f->{fh}->close(); } $a->{files} = []; } # # Copy $nBytes from files $fhIn to $fhOut. # sub filePartialCopy { my($a, $fhIn, $fhOut, $nBytes) = @_; my($nRead); while ( $nRead < $nBytes ) { my $thisRead = $nBytes - $nRead < $BufSize ? $nBytes - $nRead : $BufSize; my $data; my $n = $fhIn->read(\$data, $thisRead); if ( $n != $thisRead ) { push(@{$a->{errors}}, "Unable to read $thisRead bytes from " . $fhIn->name . " (got $n)"); return; } $n = $fhOut->write(\$data, $thisRead); if ( $n != $thisRead ) { push(@{$a->{errors}}, "Unable to write $thisRead bytes to " . $fhOut->name . " (got $n)"); return; } $nRead += $thisRead; } } # # Compare $nBytes from files $fh0 and $fh1, and also compare additional # $extra bytes from $fh1 to $$extraData. # sub filePartialCompare { my($a, $fh0, $fh1, $nBytes, $extra, $extraData) = @_; my($nRead, $n); my($data0, $data1); while ( $nRead < $nBytes ) { my $thisRead = $nBytes - $nRead < $BufSize ? $nBytes - $nRead : $BufSize; $n = $fh0->read(\$data0, $thisRead); if ( $n != $thisRead ) { push(@{$a->{errors}}, "Unable to read $thisRead bytes from " . $fh0->name . " (got $n)"); return; } $n = $fh1->read(\$data1, $thisRead); return 0 if ( $n < $thisRead || $data0 ne $data1 ); $nRead += $thisRead; } if ( $extra > 0 ) { # verify additional bytes $n = $fh1->read(\$data1, $extra); return 0 if ( $n != $extra || $data1 ne $$extraData ); } else { # verify EOF $n = $fh1->read(\$data1, 100); return 0 if ( $n != 0 ); } return 1; } # # LinkOrCopy() does a hardlink from oldFile to newFile. # # If that fails (because there are too many links on oldFile) # then oldFile is copied to newFile, and the pool stats are # returned to be added to the new file list. That allows # BackupPC_link to try again, and to create a new pool file # if necessary. # sub LinkOrCopy { my($bpc, $oldFile, $oldFileComp, $newFile, $newFileComp) = @_; my($nRead, $data); unlink($newFile) if ( -f $newFile ); # # Try to link if hardlink limit is ok, and compression types # are the same # return (1, undef) if ( (stat($oldFile))[3] < $bpc->{Conf}{HardLinkMax} && !$oldFileComp == !$newFileComp && link($oldFile, $newFile) ); # # There are too many links on oldFile, or compression # type if different, so now we have to copy it. # # We need to compute the file size, which is expensive # since we need to read the file twice. That's probably # ok since the hardlink limit is rarely hit. # my $readFd = BackupPC::FileZIO->open($oldFile, 0, $oldFileComp); if ( !defined($readFd) ) { return (0, undef, undef, undef, ["LinkOrCopy: can't open $oldFile"]); } while ( $readFd->read(\$data, $BufSize) > 0 ) { $nRead += length($data); } $readFd->rewind(); my $poolWrite = BackupPC::PoolWrite->new($bpc, $newFile, $nRead, $newFileComp); while ( $readFd->read(\$data, $BufSize) > 0 ) { $poolWrite->write(\$data); } my($exists, $digest, $outSize, $errs) = $poolWrite->close; return ($exists, $digest, $nRead, $outSize, $errs); } 1; BackupPC-3.3.2/lib/BackupPC/Storage/0000755000076500000240000000000013042250554015721 5ustar craigstaffBackupPC-3.3.2/lib/BackupPC/Storage/Text.pm0000444000076500000240000003420113042250554017201 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Storage::Text package # # DESCRIPTION # # This library defines a BackupPC::Storage::Text class that implements # BackupPC's persistent state storage (config, host info, backup # and restore info) using text files. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2004-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Storage::Text; use strict; use vars qw(%Conf); use Data::Dumper; use File::Path; use Fcntl qw/:flock/; sub new { my $class = shift; my($flds, $paths) = @_; my $s = bless { %$flds, %$paths, }, $class; return $s; } sub setPaths { my $class = shift; my($paths) = @_; foreach my $v ( keys(%$paths) ) { $class->{$v} = $paths->{$v}; } } sub BackupInfoRead { my($s, $host) = @_; local(*BK_INFO, *LOCK); my(@Backups); flock(LOCK, LOCK_EX) if open(LOCK, "$s->{TopDir}/pc/$host/LOCK"); if ( open(BK_INFO, "$s->{TopDir}/pc/$host/backups") ) { binmode(BK_INFO); while ( ) { s/[\n\r]+//; next if ( !/^(\d+\t(incr|full|partial).*)/ ); $_ = $1; @{$Backups[@Backups]}{@{$s->{BackupFields}}} = split(/\t/); } close(BK_INFO); } close(LOCK); # # Default the version field. Prior to 3.0.0 the xferMethod # field is empty, so we use that to figure out the version. # for ( my $i = 0 ; $i < @Backups ; $i++ ) { next if ( $Backups[$i]{version} ne "" ); if ( $Backups[$i]{xferMethod} eq "" ) { $Backups[$i]{version} = "2.1.2"; } else { $Backups[$i]{version} = "3.0.0"; } } return @Backups; } sub BackupInfoWrite { my($s, $host, @Backups) = @_; my($i, $contents, $fileOk); # # Generate the file contents # for ( $i = 0 ; $i < @Backups ; $i++ ) { my %b = %{$Backups[$i]}; $contents .= join("\t", @b{@{$s->{BackupFields}}}) . "\n"; } # # Write the file # return $s->TextFileWrite("$s->{TopDir}/pc/$host/backups", $contents); } sub RestoreInfoRead { my($s, $host) = @_; local(*RESTORE_INFO, *LOCK); my(@Restores); flock(LOCK, LOCK_EX) if open(LOCK, "$s->{TopDir}/pc/$host/LOCK"); if ( open(RESTORE_INFO, "$s->{TopDir}/pc/$host/restores") ) { binmode(RESTORE_INFO); while ( ) { s/[\n\r]+//; next if ( !/^(\d+.*)/ ); $_ = $1; @{$Restores[@Restores]}{@{$s->{RestoreFields}}} = split(/\t/); } close(RESTORE_INFO); } close(LOCK); return @Restores; } sub RestoreInfoWrite { my($s, $host, @Restores) = @_; local(*RESTORE_INFO, *LOCK); my($i, $contents, $fileOk); # # Generate the file contents # for ( $i = 0 ; $i < @Restores ; $i++ ) { my %b = %{$Restores[$i]}; $contents .= join("\t", @b{@{$s->{RestoreFields}}}) . "\n"; } # # Write the file # return $s->TextFileWrite("$s->{TopDir}/pc/$host/restores", $contents); } sub ArchiveInfoRead { my($s, $host) = @_; local(*ARCHIVE_INFO, *LOCK); my(@Archives); flock(LOCK, LOCK_EX) if open(LOCK, "$s->{TopDir}/pc/$host/LOCK"); if ( open(ARCHIVE_INFO, "$s->{TopDir}/pc/$host/archives") ) { binmode(ARCHIVE_INFO); while ( ) { s/[\n\r]+//; next if ( !/^(\d+.*)/ ); $_ = $1; @{$Archives[@Archives]}{@{$s->{ArchiveFields}}} = split(/\t/); } close(ARCHIVE_INFO); } close(LOCK); return @Archives; } sub ArchiveInfoWrite { my($s, $host, @Archives) = @_; local(*ARCHIVE_INFO, *LOCK); my($i, $contents, $fileOk); # # Generate the file contents # for ( $i = 0 ; $i < @Archives ; $i++ ) { my %b = %{$Archives[$i]}; $contents .= join("\t", @b{@{$s->{ArchiveFields}}}) . "\n"; } # # Write the file # return $s->TextFileWrite("$s->{TopDir}/pc/$host/archives", $contents); } # # Write a text file as safely as possible. We write to # a new file, verify the file, and the rename the file. # The previous version of the file is renamed with a # .old extension. # sub TextFileWrite { my($s, $file, $contents) = @_; local(*FD, *LOCK); my($fileOk); (my $dir = $file) =~ s{(.+)/(.+)}{$1}; if ( !-d $dir ) { eval { mkpath($dir, 0, 0775) }; return "TextFileWrite: can't create directory $dir" if ( $@ ); } if ( open(FD, ">", "$file.new") ) { binmode(FD); print FD $contents; close(FD); # # verify the file # if ( open(FD, "<", "$file.new") ) { binmode(FD); if ( join("", ) ne $contents ) { return "TextFileWrite: Failed to verify $file.new"; } else { $fileOk = 1; } close(FD); } } if ( $fileOk ) { my $lock; if ( open(LOCK, "$dir/LOCK") || open(LOCK, ">", "$dir/LOCK") ) { $lock = 1; flock(LOCK, LOCK_EX); } if ( -s "$file" ) { unlink("$file.old") if ( -f "$file.old" ); rename("$file", "$file.old") if ( -f "$file" ); } else { unlink("$file") if ( -f "$file" ); } rename("$file.new", "$file") if ( -f "$file.new" ); close(LOCK) if ( $lock ); } else { return "TextFileWrite: Failed to write $file.new"; } return; } sub ConfigPath { my($s, $host) = @_; return "$s->{ConfDir}/config.pl" if ( !defined($host) ); if ( $s->{useFHS} ) { return "$s->{ConfDir}/pc/$host.pl"; } else { return "$s->{TopDir}/pc/$host/config.pl" if ( -f "$s->{TopDir}/pc/$host/config.pl" ); return "$s->{ConfDir}/$host.pl" if ( $host ne "config" && -f "$s->{ConfDir}/$host.pl" ); return "$s->{ConfDir}/pc/$host.pl"; } } sub ConfigDataRead { my($s, $host, $prevConfig) = @_; my($ret, $mesg, $config, @configs); # # TODO: add lock # my $conf = $prevConfig || {}; my $configPath = $s->ConfigPath($host); push(@configs, $configPath) if ( -f $configPath ); foreach $config ( @configs ) { %Conf = %$conf; if ( !defined($ret = do $config) && ($! || $@) ) { $mesg = "Couldn't open $config: $!" if ( $! ); $mesg = "Couldn't execute $config: $@" if ( $@ ); $mesg =~ s/[\n\r]+//; return ($mesg, $conf); } %$conf = %Conf; } # # Promote BackupFilesOnly and BackupFilesExclude to hashes # foreach my $param ( qw(BackupFilesOnly BackupFilesExclude) ) { next if ( !defined($conf->{$param}) || ref($conf->{$param}) eq "HASH" ); $conf->{$param} = [ $conf->{$param} ] if ( ref($conf->{$param}) ne "ARRAY" ); $conf->{$param} = { "*" => $conf->{$param} }; } # # Handle backward compatibility with defunct BlackoutHourBegin, # BlackoutHourEnd, and BlackoutWeekDays parameters. # if ( defined($conf->{BlackoutHourBegin}) ) { push(@{$conf->{BlackoutPeriods}}, { hourBegin => $conf->{BlackoutHourBegin}, hourEnd => $conf->{BlackoutHourEnd}, weekDays => $conf->{BlackoutWeekDays}, } ); delete($conf->{BlackoutHourBegin}); delete($conf->{BlackoutHourEnd}); delete($conf->{BlackoutWeekDays}); } return (undef, $conf); } sub ConfigDataWrite { my($s, $host, $newConf) = @_; my $configPath = $s->ConfigPath($host); my($err, $contents) = $s->ConfigFileMerge("$configPath", $newConf); if ( defined($err) ) { return $err; } else { # # Write the file # return $s->TextFileWrite($configPath, $contents); } } sub ConfigFileMerge { my($s, $inFile, $newConf) = @_; local(*C); my($contents, $skipExpr, $fakeVar); my $done = {}; if ( -f $inFile ) { # # Match existing settings in current config file # open(C, $inFile) || return ("ConfigFileMerge: can't open/read $inFile", undef); binmode(C); while ( ) { if ( /^\s*\$Conf\{([^}]*)\}\s*=(.*)/ ) { my $var = $1; $skipExpr = "\$fakeVar = $2\n"; if ( exists($newConf->{$var}) ) { my $d = Data::Dumper->new([$newConf->{$var}], [*value]); $d->Indent(1); $d->Terse(1); my $value = $d->Dump; $value =~ s/(.*)\n/$1;\n/s; $contents .= "\$Conf{$var} = " . $value; $done->{$var} = 1; } } elsif ( defined($skipExpr) ) { $skipExpr .= $_; } else { $contents .= $_; } if ( defined($skipExpr) && ($skipExpr =~ /^\$fakeVar = *<{$var} ); my $d = Data::Dumper->new([$newConf->{$var}], [*value]); $d->Indent(1); $d->Terse(1); my $value = $d->Dump; $value =~ s/(.*)\n/$1;\n/s; $contents .= "\$Conf{$var} = " . $value; $done->{$var} = 1; } return (undef, $contents); } # # Return the mtime of the config file # sub ConfigMTime { my($s) = @_; return (stat($s->ConfigPath()))[9]; } # # Returns information from the host file in $s->{ConfDir}/hosts. # With no argument a ref to a hash of hosts is returned. Each # hash contains fields as specified in the hosts file. With an # argument a ref to a single hash is returned with information # for just that host. # sub HostInfoRead { my($s, $host) = @_; my(%hosts, @hdr, @fld); local(*HOST_INFO, *LOCK); flock(LOCK, LOCK_EX) if open(LOCK, "$s->{ConfDir}/LOCK"); if ( !open(HOST_INFO, "$s->{ConfDir}/hosts") ) { print(STDERR "Can't open $s->{ConfDir}/hosts\n"); close(LOCK); return {}; } binmode(HOST_INFO); while ( ) { s/[\n\r]+//; s/#.*//; s/\s+$//; next if ( /^\s*$/ || !/^([\w\.\\-]+\s+.*)/ ); # # Split on white space, except if preceded by \ # using zero-width negative look-behind assertion # (always wanted to use one of those). # @fld = split(/(?{ConfDir}/hosts. # With no argument a ref to a hash of hosts is returned. Each # hash contains fields as specified in the hosts file. With an # argument a ref to a single hash is returned with information # for just that host. # sub HostInfoWrite { my($s, $hosts) = @_; my($gotHdr, @fld, $hostText, $contents); local(*HOST_INFO); if ( !open(HOST_INFO, "$s->{ConfDir}/hosts") ) { return "Can't open $s->{ConfDir}/hosts"; } foreach my $host ( keys(%$hosts) ) { my $name = "$hosts->{$host}{host}"; my $rest = "\t$hosts->{$host}{dhcp}" . "\t$hosts->{$host}{user}" . "\t$hosts->{$host}{moreUsers}"; $name =~ s/ /\\ /g; $rest =~ s/ //g; $hostText->{$host} = $name . $rest; } binmode(HOST_INFO); while ( ) { s/[\n\r]+//; if ( /^\s*$/ || /^\s*#/ ) { $contents .= $_ . "\n"; next; } if ( !$gotHdr ) { $contents .= $_ . "\n"; $gotHdr = 1; next; } @fld = split(/(?{$fld[0]}) ) { $contents .= $hostText->{$fld[0]} . "\n"; delete($hostText->{$fld[0]}); } } foreach my $host ( sort(keys(%$hostText)) ) { $contents .= $hostText->{$host} . "\n"; delete($hostText->{$host}); } close(HOST_INFO); # # Write and verify the new host file # return $s->TextFileWrite("$s->{ConfDir}/hosts", $contents); } # # Return the mtime of the hosts file # sub HostsMTime { my($s) = @_; return (stat("$s->{ConfDir}/hosts"))[9]; } 1; BackupPC-3.3.2/lib/BackupPC/Storage.pm0000444000076500000240000000531413042250554016260 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Storage package # # DESCRIPTION # # This library defines a BackupPC::Storage class for reading/writing # data like config, host info, backup and restore info. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2004-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Storage; use strict; use BackupPC::Storage::Text; use Data::Dumper; sub new { my $class = shift; my($paths) = @_; my $flds = { BackupFields => [qw( num type startTime endTime nFiles size nFilesExist sizeExist nFilesNew sizeNew xferErrs xferBadFile xferBadShare tarErrs compress sizeExistComp sizeNewComp noFill fillFromNum mangle xferMethod level charset version )], RestoreFields => [qw( num startTime endTime result errorMsg nFiles size tarCreateErrs xferErrs )], ArchiveFields => [qw( num startTime endTime result errorMsg )], }; return BackupPC::Storage::Text->new($flds, $paths, @_); } # # Writes per-backup information into the pc/nnn/backupInfo # file to allow later recovery of the pc/backups file in # cases when it is corrupted. # sub backupInfoWrite { my($class, $pcDir, $bkupNum, $bkupInfo, $force) = @_; return if ( !$force && -f "$pcDir/$bkupNum/backupInfo" ); my($dump) = Data::Dumper->new( [ $bkupInfo], [qw(*backupInfo)]); $dump->Indent(1); if ( open(BKUPINFO, ">", "$pcDir/$bkupNum/backupInfo") ) { print(BKUPINFO $dump->Dump); close(BKUPINFO); } } 1; BackupPC-3.3.2/lib/BackupPC/View.pm0000444000076500000240000004642013042250554015571 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::View package # # DESCRIPTION # # This library defines a BackupPC::View class for merging of # incremental backups and file attributes. This provides the # caller with a single view of a merged backup, without worrying # about which backup contributes which files. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2002-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::View; use strict; use File::Path; use BackupPC::Lib; use BackupPC::Attrib qw(:all); use BackupPC::FileZIO; use Data::Dumper; use Encode qw/from_to/; sub new { my($class, $bpc, $host, $backups, $options) = @_; my $m = bless { bpc => $bpc, # BackupPC::Lib object host => $host, # host name backups => $backups, # all backups for this host num => -1, # backup number idx => -1, # index into backups for backup # we are viewing dirPath => undef, # path to current directory dirAttr => undef, # attributes of current directory dirOpts => $options, # $options is a hash of file attributes we need: # type, inode, or nlink. If set, these parameters # are added to the returned hash. # See BackupPC::Lib::dirRead(). }, $class; $m->{topDir} = $m->{bpc}->TopDir(); return $m; } sub dirCache { my($m, $backupNum, $share, $dir) = @_; my($i, $level); #print STDERR "dirCache($backupNum, $share, $dir)\n"; $dir = "/$dir" if ( $dir !~ m{^/} ); $dir =~ s{/+$}{}; return if ( $m->{num} == $backupNum && $m->{share} eq $share && defined($m->{dir}) && $m->{dir} eq $dir ); $m->backupNumCache($backupNum) if ( $m->{num} != $backupNum ); return if ( $m->{idx} < 0 ); $m->{files} = {}; $level = $m->{backups}[$m->{idx}]{level} + 1; # # Remember the requested share and dir # $m->{share} = $share; $m->{dir} = $dir; # # merge backups, starting at the requested one, and working # backwards until we get to level 0. # $m->{mergeNums} = []; for ( $i = $m->{idx} ; $level > 0 && $i >= 0 ; $i-- ) { #print(STDERR "Do $i ($m->{backups}[$i]{noFill},$m->{backups}[$i]{level})\n"); # # skip backups with the same or higher level # next if ( $m->{backups}[$i]{level} >= $level ); $level = $m->{backups}[$i]{level}; $backupNum = $m->{backups}[$i]{num}; push(@{$m->{mergeNums}}, $backupNum); my $mangle = $m->{backups}[$i]{mangle}; my $compress = $m->{backups}[$i]{compress}; my $path = "$m->{topDir}/pc/$m->{host}/$backupNum/"; my $legacyCharset = $m->{backups}[$i]{version} < 3.0; my $sharePathM; if ( $mangle ) { $sharePathM = $m->{bpc}->fileNameEltMangle($share) . $m->{bpc}->fileNameMangle($dir); } else { $sharePathM = $share . $dir; } $path .= $sharePathM; #print(STDERR "Opening $path (share=$share, mangle=$mangle)\n"); my $dirOpts = { %{$m->{dirOpts} || {} } }; my $attribOpts = { compress => $compress }; if ( $legacyCharset ) { $dirOpts->{charsetLegacy} = $attribOpts->{charsetLegacy} = $m->{bpc}->{Conf}{ClientCharsetLegacy} || "iso-8859-1"; } my $dirInfo = $m->{bpc}->dirRead($path, $dirOpts); if ( !defined($dirInfo) ) { if ( $i == $m->{idx} ) { # # Oops, directory doesn't exist. # $m->{files} = undef; return; } next; } my $attr; if ( $mangle ) { $attr = BackupPC::Attrib->new($attribOpts); if ( !$attr->read($path) ) { $m->{error} = "Can't read attribute file in $path: " . $attr->errStr(); $attr = undef; } } foreach my $entry ( @$dirInfo ) { my $file = $1 if ( $entry->{name} =~ /(.*)/s ); my $fileUM = $file; $fileUM = $m->{bpc}->fileNameUnmangle($fileUM) if ( $mangle ); #print(STDERR "Doing $fileUM\n"); # # skip special files # next if ( defined($m->{files}{$fileUM}) || $file eq ".." || $file eq "." || $file eq "backupInfo" || $mangle && $file eq "attrib" ); if ( defined($attr) && defined(my $a = $attr->get($fileUM)) ) { $m->{files}{$fileUM} = $a; # # skip directories in earlier backups (each backup always # has the complete directory tree). # next if ( $i < $m->{idx} && $a->{type} == BPC_FTYPE_DIR ); $attr->set($fileUM, undef); } else { # # Very expensive in the non-attribute case when compresseion # is on. We have to stat the file and read compressed files # to determine their size. # my $realPath = "$path/$file"; from_to($realPath, "utf8", $attribOpts->{charsetLegacy}) if ( $attribOpts->{charsetLegacy} ne "" ); my @s = stat($realPath); next if ( $i < $m->{idx} && -d _ ); $m->{files}{$fileUM} = { type => -d _ ? BPC_FTYPE_DIR : BPC_FTYPE_FILE, mode => $s[2], uid => $s[4], gid => $s[5], size => -f _ ? $s[7] : 0, mtime => $s[9], }; if ( $compress && -f _ ) { # # Compute the correct size by reading the whole file # my $f = BackupPC::FileZIO->open($realPath, 0, $compress); if ( !defined($f) ) { $m->{error} = "Can't open $realPath"; } else { my($data, $size); while ( $f->read(\$data, 65636 * 8) > 0 ) { $size += length($data); } $f->close; $m->{files}{$fileUM}{size} = $size; } } } ($m->{files}{$fileUM}{relPath} = "$dir/$fileUM") =~ s{//+}{/}g; ($m->{files}{$fileUM}{sharePathM} = "$sharePathM/$file") =~ s{//+}{/}g; ($m->{files}{$fileUM}{fullPath} = "$path/$file") =~ s{//+}{/}g; from_to($m->{files}{$fileUM}{fullPath}, "utf8", $attribOpts->{charsetLegacy}) if ( $attribOpts->{charsetLegacy} ne "" ); $m->{files}{$fileUM}{backupNum} = $backupNum; $m->{files}{$fileUM}{compress} = $compress; $m->{files}{$fileUM}{nlink} = $entry->{nlink} if ( $m->{dirOpts}{nlink} ); $m->{files}{$fileUM}{inode} = $entry->{inode} if ( $m->{dirOpts}{inode} ); } # # Also include deleted files # if ( defined($attr) ) { my $a = $attr->get; foreach my $fileUM ( keys(%$a) ) { next if ( $a->{$fileUM}{type} != BPC_FTYPE_DELETED ); my $file = $fileUM; $file = $m->{bpc}->fileNameMangle($fileUM) if ( $mangle ); $m->{files}{$fileUM} = $a->{$fileUM}; $m->{files}{$fileUM}{relPath} = "$dir/$fileUM"; $m->{files}{$fileUM}{sharePathM} = "$sharePathM/$file"; $m->{files}{$fileUM}{fullPath} = "$path/$file"; from_to($m->{files}{$fileUM}{fullPath}, "utf8", $attribOpts->{charsetLegacy}) if ( $attribOpts->{charsetLegacy} ne "" ); $m->{files}{$fileUM}{backupNum} = $backupNum; $m->{files}{$fileUM}{compress} = $compress; $m->{files}{$fileUM}{nlink} = 0; $m->{files}{$fileUM}{inode} = 0; } } } # # Prune deleted files # foreach my $file ( keys(%{$m->{files}}) ) { next if ( $m->{files}{$file}{type} != BPC_FTYPE_DELETED ); delete($m->{files}{$file}); } #print STDERR "Returning:\n", Dumper($m->{files}); } # # Return list of shares for this backup # sub shareList { my($m, $backupNum) = @_; my @shareList; $m->backupNumCache($backupNum) if ( $m->{num} != $backupNum ); return if ( $m->{idx} < 0 ); my $mangle = $m->{backups}[$m->{idx}]{mangle}; my $path = "$m->{topDir}/pc/$m->{host}/$backupNum/"; return if ( !opendir(DIR, $path) ); my @dir = readdir(DIR); closedir(DIR); foreach my $file ( @dir ) { $file = $1 if ( $file =~ /(.*)/s ); next if ( $file eq "attrib" && $mangle || $file eq "." || $file eq ".." || $file eq "backupInfo" ); my $fileUM = $file; $fileUM = $m->{bpc}->fileNameUnmangle($fileUM) if ( $mangle ); push(@shareList, $fileUM); } $m->{dir} = undef; return @shareList; } sub backupNumCache { my($m, $backupNum) = @_; if ( $m->{num} != $backupNum ) { my $i; for ( $i = 0 ; $i < @{$m->{backups}} ; $i++ ) { last if ( $m->{backups}[$i]{num} == $backupNum ); } if ( $i >= @{$m->{backups}} ) { $m->{idx} = -1; return; } $m->{num} = $backupNum; $m->{idx} = $i; } } # # Return the attributes of a specific file # sub fileAttrib { my($m, $backupNum, $share, $path) = @_; #print(STDERR "fileAttrib($backupNum, $share, $path)\n"); if ( $path =~ s{(.*)/+(.+)}{$1}s ) { my $file = $2; $m->dirCache($backupNum, $share, $path); return $m->{files}{$file}; } else { #print STDERR "Got empty $path\n"; $m->dirCache($backupNum, "", ""); my $attr = $m->{files}{$share}; return if ( !defined($attr) ); $attr->{relPath} = "/"; return $attr; } } # # Return the contents of a directory # sub dirAttrib { my($m, $backupNum, $share, $dir) = @_; $m->dirCache($backupNum, $share, $dir); return $m->{files}; } # # Return a listref of backup numbers that are merged to create this view # sub mergeNums { my($m) = @_; return $m->{mergeNums}; } # # Return a list of backup indexes for which the directory exists # sub backupList { my($m, $share, $dir) = @_; my($i, @backupList); $dir = "/$dir" if ( $dir !~ m{^/} ); $dir =~ s{/+$}{}; for ( $i = 0 ; $i < @{$m->{backups}} ; $i++ ) { my $backupNum = $m->{backups}[$i]{num}; my $mangle = $m->{backups}[$i]{mangle}; my $path = "$m->{topDir}/pc/$m->{host}/$backupNum/"; my $sharePathM; if ( $mangle ) { $sharePathM = $m->{bpc}->fileNameEltMangle($share) . $m->{bpc}->fileNameMangle($dir); } else { $sharePathM = $share . $dir; } $path .= $sharePathM; next if ( !-d $path ); push(@backupList, $i); } return @backupList; } # # Return the history of all backups for a particular directory # sub dirHistory { my($m, $share, $dir) = @_; my($i, $level); my $files = {}; $dir = "/$dir" if ( $dir !~ m{^/} ); $dir =~ s{/+$}{}; # # merge backups, starting at the first one, and working # forward. # for ( $i = 0 ; $i < @{$m->{backups}} ; $i++ ) { $level = $m->{backups}[$i]{level}; my $backupNum = $m->{backups}[$i]{num}; my $mangle = $m->{backups}[$i]{mangle}; my $compress = $m->{backups}[$i]{compress}; my $path = "$m->{topDir}/pc/$m->{host}/$backupNum/"; my $legacyCharset = $m->{backups}[$i]{version} < 3.0; my $sharePathM; if ( $mangle ) { $sharePathM = $m->{bpc}->fileNameEltMangle($share) . $m->{bpc}->fileNameMangle($dir); } else { $sharePathM = $share . $dir; } $path .= $sharePathM; #print(STDERR "Opening $path (share=$share)\n"); my $dirOpts = { %{$m->{dirOpts} || {} } }; my $attribOpts = { compress => $compress }; if ( $legacyCharset ) { $dirOpts->{charsetLegacy} = $attribOpts->{charsetLegacy} = $m->{bpc}->{Conf}{ClientCharsetLegacy} || "iso-8859-1"; } my $dirInfo = $m->{bpc}->dirRead($path, $dirOpts); if ( !defined($dirInfo) ) { # # Oops, directory doesn't exist. # next; } my $attr; if ( $mangle ) { $attr = BackupPC::Attrib->new($attribOpts); if ( !$attr->read($path) ) { $m->{error} = "Can't read attribute file in $path"; $attr = undef; } } foreach my $entry ( @$dirInfo ) { my $file = $1 if ( $entry->{name} =~ /(.*)/s ); my $fileUM = $file; $fileUM = $m->{bpc}->fileNameUnmangle($fileUM) if ( $mangle ); #print(STDERR "Doing $fileUM\n"); # # skip special files # next if ( $file eq ".." || $file eq "." || $mangle && $file eq "attrib" || defined($files->{$fileUM}[$i]) ); my $realPath = "$path/$file"; from_to($realPath, "utf8", $attribOpts->{charsetLegacy}) if ( $attribOpts->{charsetLegacy} ne "" ); my @s = stat($realPath); if ( defined($attr) && defined(my $a = $attr->get($fileUM)) ) { $files->{$fileUM}[$i] = $a; $attr->set($fileUM, undef); } else { # # Very expensive in the non-attribute case when compresseion # is on. We have to stat the file and read compressed files # to determine their size. # $files->{$fileUM}[$i] = { type => -d _ ? BPC_FTYPE_DIR : BPC_FTYPE_FILE, mode => $s[2], uid => $s[4], gid => $s[5], size => -f _ ? $s[7] : 0, mtime => $s[9], }; if ( $compress && -f _ ) { # # Compute the correct size by reading the whole file # my $f = BackupPC::FileZIO->open("$realPath", 0, $compress); if ( !defined($f) ) { $m->{error} = "Can't open $path/$file"; } else { my($data, $size); while ( $f->read(\$data, 65636 * 8) > 0 ) { $size += length($data); } $f->close; $files->{$fileUM}[$i]{size} = $size; } } } ($files->{$fileUM}[$i]{relPath} = "$dir/$fileUM") =~ s{//+}{/}g; ($files->{$fileUM}[$i]{sharePathM} = "$sharePathM/$file") =~ s{//+}{/}g; ($files->{$fileUM}[$i]{fullPath} = "$path/$file") =~ s{//+}{/}g; $files->{$fileUM}[$i]{backupNum} = $backupNum; $files->{$fileUM}[$i]{compress} = $compress; $files->{$fileUM}[$i]{nlink} = $entry->{nlink} if ( $m->{dirOpts}{nlink} ); $files->{$fileUM}[$i]{inode} = $entry->{inode} if ( $m->{dirOpts}{inode} ); } # # Flag deleted files # if ( defined($attr) ) { my $a = $attr->get; foreach my $fileUM ( keys(%$a) ) { next if ( $a->{$fileUM}{type} != BPC_FTYPE_DELETED ); $files->{$fileUM}[$i]{type} = BPC_FTYPE_DELETED; } } # # Merge old backups. Don't merge directories from old # backups because every backup has an accurate directory # tree. # for ( my $k = $i - 1 ; $level > 0 && $k >= 0 ; $k-- ) { next if ( $m->{backups}[$k]{level} >= $level ); $level = $m->{backups}[$k]{level}; foreach my $fileUM ( keys(%$files) ) { next if ( !defined($files->{$fileUM}[$k]) || defined($files->{$fileUM}[$i]) || $files->{$fileUM}[$k]{type} == BPC_FTYPE_DIR ); $files->{$fileUM}[$i] = $files->{$fileUM}[$k]; } } } # # Remove deleted files # for ( $i = 0 ; $i < @{$m->{backups}} ; $i++ ) { foreach my $fileUM ( keys(%$files) ) { next if ( !defined($files->{$fileUM}[$i]) || $files->{$fileUM}[$i]{type} != BPC_FTYPE_DELETED ); $files->{$fileUM}[$i] = undef; } } #print STDERR "Returning:\n", Dumper($files); return $files; } # # Do a recursive find starting at the given path (either a file # or directory). The callback function $callback is called on each # file and directory. The function arguments are the attrs hashref, # and additional callback arguments. The search is depth-first if # depth is set. Returns -1 if $path does not exist. # sub find { my($m, $backupNum, $share, $path, $depth, $callback, @callbackArgs) = @_; #print(STDERR "find: got $backupNum, $share, $path\n"); # # First call the callback on the given $path # my $attr = $m->fileAttrib($backupNum, $share, $path); return -1 if ( !defined($attr) ); &$callback($attr, @callbackArgs); return if ( $attr->{type} != BPC_FTYPE_DIR ); # # Now recurse into subdirectories # $m->findRecurse($backupNum, $share, $path, $depth, $callback, @callbackArgs); } # # Same as find(), except the callback is not called on the current # $path, only on the contents of $path. So if $path is a file then # no callback or recursion occurs. # sub findRecurse { my($m, $backupNum, $share, $path, $depth, $callback, @callbackArgs) = @_; my $attr = $m->dirAttrib($backupNum, $share, $path); return if ( !defined($attr) ); foreach my $file ( sort(keys(%$attr)) ) { &$callback($attr->{$file}, @callbackArgs); next if ( !$depth || $attr->{$file}{type} != BPC_FTYPE_DIR ); # # For depth-first, recurse as we hit each directory # $m->findRecurse($backupNum, $share, "$path/$file", $depth, $callback, @callbackArgs); } if ( !$depth ) { # # For non-depth, recurse directories after we finish current dir # foreach my $file ( keys(%{$attr}) ) { next if ( $attr->{$file}{type} != BPC_FTYPE_DIR ); $m->findRecurse($backupNum, $share, "$path/$file", $depth, $callback, @callbackArgs); } } } 1; BackupPC-3.3.2/lib/BackupPC/Xfer/0000755000076500000240000000000013042250554015221 5ustar craigstaffBackupPC-3.3.2/lib/BackupPC/Xfer/Archive.pm0000444000076500000240000000604313042250554017141 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Xfer::Archive package # # DESCRIPTION # # This library defines a BackupPC::Xfer::Archive class for managing # archives to media. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Xfer::Archive; use strict; use base qw(BackupPC::Xfer::Protocol); sub start { return "Archive Started"; } sub run { my($t) = @_; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my(@HostList, @BackupList, $archiveClientCmd, $archiveClientCmd2, $logMsg); $archiveClientCmd = $conf->{ArchiveClientCmd}; $t->{xferOK} = 1; @HostList = $t->{HostList}; @BackupList = $t->{BackupList}; my $i = 0; my $tarCreatePath = "$conf->{InstallDir}/bin/BackupPC_tarCreate"; while (${@HostList[0]}[$i]) { # # Merge variables into @archiveClientCmd # my $errStr; my $cmdargs = { archiveloc => $t->{archiveloc}, parfile => $t->{parfile}, compression => $t->{compression}, compext => $t->{compext}, splitsize => $t->{splitsize}, host => ${@HostList[0]}[$i], backupnumber => ${@BackupList[0]}[$i], Installdir => $conf->{InstallDir}, tarCreatePath => $tarCreatePath, splitpath => $conf->{SplitPath}, parpath => $conf->{ParPath}, }; $archiveClientCmd2 = $bpc->cmdVarSubstitute($archiveClientCmd, $cmdargs); $t->{XferLOG}->write(\"Executing: @$archiveClientCmd2\n"); $bpc->cmdSystemOrEvalLong($archiveClientCmd2, sub { $errStr = $_[0]; $t->{XferLOG}->write(\$_[0]); }, 0, $t->{pidHandler}); if ( $? ) { ($t->{_errStr} = $errStr) =~ s/[\n\r]+//; return; } $i++; } $t->{XferLOG}->write(\"Completed Archive\n"); return "Completed Archive"; } 1; BackupPC-3.3.2/lib/BackupPC/Xfer/Ftp.pm0000444000076500000240000006506213042250554016317 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Xfer::Ftp package # # DESCRIPTION # # This library defines a BackupPC::Xfer::Ftp class for transferring # data from a FTP client. # # AUTHOR # Paul Mantz # # COPYRIGHT # (C) 2008, Zmanda Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307 USA # # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Xfer::Ftp; use strict; use BackupPC::View; use BackupPC::Attrib qw(:all); use Encode qw/from_to encode/; use File::Listing qw/parse_dir/; use File::Path; use Data::Dumper; use base qw(BackupPC::Xfer::Protocol); use vars qw( $FTPLibOK $FTPLibErr $ARCLibOK ); use constant S_IFMT => 0170000; BEGIN { $FTPLibOK = 1; $ARCLibOK = 0; # # clear eval error variable # my @FTPLibs = qw( Net::FTP Net::FTP::RetrHandle ); foreach my $module ( @FTPLibs ) { undef $@; eval "use $module;"; if ( $@ ) { $FTPLibOK = 0; $FTPLibErr = "module $module doesn't exist: $@"; last; } } eval "use Net::FTP::AutoReconnect;"; $ARCLibOK = (defined($@)) ? 1 : 0; }; ############################################################################## # Constructor ############################################################################## # # usage: # $xfer = new BackupPC::Xfer::Ftp( $bpc, %args ); # # new() is your default class constructor. it also calls the # constructor for Protocol as well. # sub new { my ( $class, $bpc, $args ) = @_; $args ||= {}; my $t = BackupPC::Xfer::Protocol->new( $bpc, { ftp => undef, stats => { errorCnt => 0, TotalFileCnt => 0, TotalFileSize => 0, ExistFileCnt => 0, ExistFileSize => 0, ExistFileCompSize => 0, }, %$args, } ); return bless( $t, $class ); } ############################################################################## # Methods ############################################################################## # # usage: # $xfer->start(); # # start() is called to configure and initiate a dump or restore, # depending on the configured options. # sub start { my ($t) = @_; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my ( @fileList, $logMsg, $incrDate, $args, $dumpText ); # # initialize the statistics returned by getStats() # foreach ( qw/byteCnt fileCnt xferErrCnt xferBadShareCnt xferBadFileCnt xferOK hostAbort hostError lastOutputLine/ ) { $t->{$_} = 0; } # # Net::FTP::RetrHandle is necessary. # if ( !$FTPLibOK ) { $t->{_errStr} = "Error: FTP transfer selected but module" . " Net::FTP::RetrHandle is not installed."; $t->{xferErrCnt}++; return; } # # standardize the file include/exclude settings if necessary # unless ( $t->{type} eq 'restore' ) { $bpc->backupFileConfFix( $conf, "FtpShareName" ); $t->loadInclExclRegexps("FtpShareName"); } # # Convert the encoding type of the names if at all possible # from_to( $args->{shareName}, "utf8", $conf->{ClientCharset} ) if ( $conf->{ClientCharset} ne "" ); # # Collect FTP configuration arguments and translate them for # passing to the FTP module. # unless ( $args = $t->getFTPArgs() ) { return; } # # Create the Net::FTP::AutoReconnect or Net::FTP object. # undef $@; eval { $t->{ftp} = ($ARCLibOK) ? Net::FTP::AutoReconnect->new(%$args) : Net::FTP->new(%$args); }; if ($@) { $t->{_errStr} = "Can't open connection to $args->{Host}: $!"; $t->{xferErrCnt}++; return; } $t->logWrite("Connected to $args->{Host}\n", 2); # # Log in to the ftp server and set appropriate path information. # undef $@; eval { $t->{ftp}->login( $conf->{FtpUserName}, $conf->{FtpPasswd} ); }; if ( $@ ) { $t->{_errStr} = "Can't login to $args->{Host}: $!"; $t->{xferErrCnt}++; return; } $t->logWrite("Login successful to $conf->{FtpUserName}\@$args->{Host}\n", 2); undef $@; eval { $t->{ftp}->binary(); }; if ($@) { $t->{_errStr} = "Can't enable binary transfer mode to $args->{Host}: $!"; $t->{xferErrCnt}++; return; } $t->logWrite("Binary command successful\n", 2); undef $@; eval { $t->{shareName} =~ m/^\.?$/ || $t->{ftp}->cwd( $t->{shareName} ); }; if ($@) { $t->{_errStr} = "Can't change working directory to $t->{shareName}: $!"; $t->{xferErrCnt}++; return; } $t->logWrite("Set cwd to $t->{shareName}\n", 2); undef $@; eval { $t->{sharePath} = $t->{ftp}->pwd(); }; if ($@) { $t->{_errStr} = "Can't retrieve full working directory of $t->{shareName}: $!"; $t->{xferErrCnt}++; return; } $t->logWrite("Pwd returned as $t->{sharePath}\n", 2); # # log the beginning of action based on type # if ( $t->{type} eq 'restore' ) { $logMsg = "ftp restore for host $t->{host} started on directory " . "$t->{shareName}\n"; } elsif ( $t->{type} eq 'full' ) { $logMsg = "ftp full backup for host $t->{host} started on directory " . "$t->{shareName}\n"; } elsif ( $t->{type} eq 'incr' ) { $incrDate = $bpc->timeStamp( $t->{incrBaseTime} - 3600, 1 ); $logMsg = "ftp incremental backup for $t->{host} started back to " . "$incrDate (backup #$t->{incrBaseBkupNum}) for directory " . "$t->{shareName}\n"; } $t->logWrite($logMsg, 1); # # call the recursive function based on the type of action # if ( $t->{type} eq 'restore' ) { $t->restore(); $logMsg = "Restore of $t->{host} " . ($t->{xferOK} ? "complete" : "failed"); } elsif ( $t->{type} eq 'incr' ) { $t->backup(); $logMsg = "Incremental backup of $t->{host} " . ($t->{xferOK} ? "complete" : "failed"); } elsif ( $t->{type} eq 'full' ) { $t->backup(); $logMsg = "Full backup of $t->{host} " . ($t->{xferOK} ? "complete" : "failed"); } delete $t->{_errStr}; return $logMsg; } # # # sub run { my ($t) = @_; my $stats = $t->{stats}; my ( $tarErrs, $nFilesExist, $sizeExist, $sizeExistCom, $nFilesTotal, $sizeTotal ); # # TODO: replace the $stats array with variables at the top level, # ones returned by $getStats. They should be identical. # $tarErrs = 0; $nFilesExist = $stats->{ExistFileCnt}; $sizeExist = $stats->{ExistFileSize}; $sizeExistCom = $stats->{ExistFileCompSize}; $nFilesTotal = $stats->{TotalFileCnt}; $sizeTotal = $stats->{TotalFileSize}; if ( $t->{type} eq "restore" ) { return ( $t->{fileCnt}, $t->{byteCnt}, 0, 0 ); } else { return ( $tarErrs, $nFilesExist, $sizeExist, $sizeExistCom, $nFilesTotal, $sizeTotal ); } } # # usage: # $t->restore(); # # TODO: finish or scuttle this function. It is not necessary for a # release. # sub restore { my $t = @_; my $bpc = $t->{bpc}; my $fileList = $t->{fileList}; my ($path, $fileName, $fileAttr, $fileType ); #print STDERR "BackupPC::Xfer::Ftp->restore()"; # # Prepare the view object # $t->{view} = BackupPC::View->new( $bpc, $t->{bkupSrcHost}, $t->{backups} ); my $view = $t->{view}; SCAN: foreach my $f ( @$fileList ) { #print STDERR "restoring $f...\n"; $f =~ /(.*)\/([^\/]*)/; $path = $1; $fileName = $2; $view->dirCache($path); $fileAttr = $view->fileAttrib($fileName); $fileType = fileType2Text( $fileAttr->{type} ); if ( $fileType eq "dir") { $t->restoreDir($fileName, $fileAttr); } elsif ( $fileType eq "file" ) { $t->restoreFile($fileName, $fileAttr); } elsif ( $fileType eq "symlink" ) { # # ignore # } else { # # ignore # } } # end SCAN } sub restoreDir { my ( $t, $dirName, $dirAttr ) = @_; my $ftp = $t->{ftp}; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my $view = $t->{view}; my $TopDir = $bpc->TopDir(); my $path = "$dirAttr->{relPath}/$dirName"; my $dirList = $view->dirAttrib( -1, $t->{shareName}, $path ); my ( $fileName, $fileAttr, $fileType ); #print STDERR "BackupPC::Xfer::Ftp->restore($dirName)\n"; # # Create the remote directory # undef $@; eval { $ftp->mkdir( $path, 1 ); }; if ($@) { $t->logFileAction( "fail", $dirName, $dirAttr ); return; } SCAN: while ( ($fileName, $fileAttr ) = each %$dirList ) { $fileType = fileType2Text( $fileAttr->{type} ); if ( $fileType eq "dir" ) { if ( $t->restoreDir( $fileName, $fileAttr ) ) { $t->logWrite( "restored: $path/$fileName\n", 5 ); } else { $t->logWrite( "restore failed: $path/$fileName\n", 3 ); } } elsif ( $fileType eq "file" ) { $t->restoreFile( $fileName, $fileAttr ); } elsif ( $fileType eq "hardlink" ) { # # Hardlinks cannot be restored. however, if we have the # target file in the pool, we can restore that. # $t->restoreFile( $fileName, $fileAttr ); next SCAN; } elsif ( $fileType eq "symlink" ) { # # Symlinks cannot be restored # next SCAN; } else { # # Ignore all other types (devices, doors, etc) # next SCAN; } } } sub restoreFile { my ($t, $fileName, $fileAttr ) = @_; my $conf = $t->{conf}; my $ftp = $t->{ftp}; my $poolFile = $fileAttr->{fullPath}; my $fileDest = ( $conf->{ClientCharset} ne "" ) ? from_to( "$fileAttr->{relPath}/$fileName", "utf8", $conf->{ClientCharset} ) : "$fileAttr->{relPath}/$fileName"; #print STDERR "BackupPC::Xfer::Ftp->restoreFile($fileName)\n"; undef $@; eval { if ( $ftp->put( $poolFile, $fileDest ) ) { $t->logFileAction( "restore", $fileName, $fileAttr ); } else { $t->logFileAction( "fail", $fileName, $fileAttr ); } }; if ($@) { $t->logFileAction( "fail", $fileName, $fileAttr ); } } # # usage: # $t->backup($path); # # $t->backup() is a recursive function that takes a path as an # argument, and performs a backup on that folder consistent with the # configuration parameters. $path is considered rooted at # $t->{shareName}, so no $ftp->cwd() command is necessary. # sub backup { my ($t) = @_; my $ftp = $t->{ftp}; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my $TopDir = $bpc->TopDir(); my $OutDir = "$TopDir/pc/$t->{client}/new/" . $bpc->fileNameEltMangle( $t->{shareName} ); # # Prepare the view object # $t->{view} = BackupPC::View->new( $bpc, $t->{client}, $t->{backups} ); # # Prepare backup folder # unless ( eval { mkpath( $OutDir, 0, 0755 ); } ) { $t->{_errStr} = "can't create OutDir: $OutDir"; $t->{xferErrCnt}++; return; } $t->logWrite("Created output directory $OutDir\n", 3); # # determine the filetype of the shareName and back it up # appropriately. For now, assume that $t->{shareName} is a # directory. # my $f = { relPath => "", fullName => $t->{shareName}, }; if ( $t->handleDir( $f, $OutDir ) ) { $t->{xferOK} = 1; return 1; } else { $t->{xferBadShareCnt}++; return; } } #################################################################################### # FTP-specific functions #################################################################################### # # This is an encapulation of the logic necessary to grab the arguments # from %Conf and throw it in a hash pointer to be passed to the # Net::FTP object. # sub getFTPArgs { my ($t) = @_; my $conf = $t->{conf}; return { Host => $conf->{ClientNameAlias} || $t->{hostIP} || $t->{host}, Firewall => undef, # not used FirewallType => undef, # not used BlockSize => $conf->{FtpBlockSize} || 10240, Port => $conf->{FtpPort} || 21, Timeout => defined($conf->{FtpTimeout}) ? $conf->{FtpTimeout} : 120, Debug => $t->{logLevel} >= 10 ? 1 : 0, Passive => defined($conf->{FtpPassive}) ? $conf->{FtpPassive} : 1, Hash => undef, # do not touch }; } # # usage: # $dirList = $t->remotels($path); # # remotels() returns a reference to a list of hash references that # describe the contents of each file in the directory of the path # specified. # # In the future, I would like to make this function return objects in # Attrib format. That would be very optimal, and I could probably # release the code to CPAN. # sub remotels { my ( $t, $path ) = @_; my $ftp = $t->{ftp}; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my ( $dirContents, $remoteDir, $f ); $remoteDir = []; undef $@; $t->logWrite("remotels: about to list $path\n", 4); eval { $dirContents = ( $path =~ /^\.?$/ ) ? $ftp->dir() : $ftp->dir("$path/"); }; if ($@) { $t->{xferErrCnt}++; $t->logWrite("remotels: can't retrieve remote directory contents of $path: $!\n", 1); return "can't retrieve remote directory contents of $path: $!"; } if ( $t->{logLevel} >= 4 ) { my $str = join("\n", @$dirContents); $t->logWrite("remotels: got dir() result:\n$str\n", 4); } foreach my $info ( @{parse_dir($dirContents)} ) { $f = { name => $info->[0], type => $info->[1], size => $info->[2], mtime => $info->[3], mode => $info->[4], }; $t->logWrite("remotels: adding name $f->{name}, type $f->{type}, size $f->{size}, mode $f->{mode}\n", 4); $f->{utf8name} = $f->{name}; from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" ) if ( $conf->{ClientCharset} ne "" ); $f->{fullName} = "$t->{sharePath}/$path/$f->{name}"; $f->{fullName} =~ s/\/+/\//g; $f->{relPath} = ($path eq "") ? $f->{name} : "$path/$f->{name}"; $f->{relPath} =~ s/\/+/\//g; push( @$remoteDir, $f ); } return $remoteDir; } # # ignoreFileCheck() looks at the attributes of the arguments and the # backup types, and determines if the file should be skipped in this # backup. # sub ignoreFileCheck { my ( $t, $f, $attrib ) = @_; if ( $f->{name} =~ /^\.\.?$/ ) { return 1; } return ( !$t->checkIncludeExclude( $f->{fullName} ) ); } # # handleSymlink() backs up a symlink. # sub handleSymlink { my ( $t, $f, $OutDir, $attrib ) = @_; my $conf = $t->{conf}; my $ftp = $t->{ftp}; my ( $target, $targetDesc ); my $attribInfo = { type => BPC_FTYPE_SYMLINK, mode => $f->{mode}, uid => undef, # unsupported gid => undef, # unsupported size => 0, mtime => $f->{mtime}, }; # # If we are following symlinks, back them up as the type of file # they point to. Otherwise, backup the symlink. # if ( $conf->{FtpFollowSymlinks} ) { # # handle nested symlinks by recurring on the target until a # file or directory is found. # $f->{type} =~ /^l (.*)/; $target = $1; undef $@; eval { if ( $targetDesc = $ftp->dir("$target/") ) { $t->handleSymDir( $f, $OutDir, $attrib, $targetDesc ); } elsif ( $targetDesc = $ftp->dir($target) ) { if ( $targetDesc->[4] eq 'file' ) { $t->handleSymFile( $f, $OutDir, $attrib ); } elsif ( $targetDesc->[4] =~ /l (.*)/ ) { $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); return; } } else { $t->( "fail", $f ); return; } }; if ($@) { $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); return; } } else { # # If we are not following symlinks, record them normally. # $attrib->set( $f->{utf8name}, $attribInfo ); $t->logFileAction("create", $f->{utf8name}, $attribInfo); } return 1; } sub handleSymDir { my ($t, $fSym, $OutDir, $attrib, $targetDesc) = @_; return 1; } sub handleSymFile { my ( $t, $fSym, $OutDir, $attrib, $targetDesc ) = @_; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my $f = { name => $fSym->{name}, type => $targetDesc->[1], size => $targetDesc->[2], mtime => $targetDesc->[3], mode => $targetDesc->[4] }; $f->{utf8name} = $fSym->{name}; from_to( $f->{utf8name}, $conf->{ClientCharset}, "utf8" ) if ( $conf->{ClientCharset} ne "" ); $f->{relPath} = $fSym->{relPath}; $f->{fullName} = "$t->{shareName}/$fSym->{relPath}/$fSym->{name}"; $f->{fullName} =~ s/\/+/\//g; # # since FTP servers follow symlinks, we can just do this: # return $t->handleFile( $f, $OutDir, $attrib ); } # # handleDir() backs up a directory, and initiates a backup of its # contents. # sub handleDir { my ( $t, $dir, $OutDir ) = @_; my $ftp = $t->{ftp}; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my $view = $t->{view}; my $stats = $t->{stats}; my ( $exists, $digest, $outSize, $errs ); my ( $poolWrite, $poolFile, $attribInfo ); my ( $localDir, $remoteDir, $attrib, %expectedFiles ); if ( exists($dir->{utf8name})) { $OutDir .= "/" . $bpc->fileNameMangle( $dir->{utf8name} ); } unless ( -d $OutDir ) { eval { mkpath( $OutDir, 0, 0755 ) }; if ( $@ ) { $t->logFileAction( "fail", $dir->{utf8name}, $dir ); return; } else { $t->logFileAction( "create", $dir->{utf8name}, $dir ); } } $t->logWrite("handleDir: dir->relPath = $dir->{relPath}, OutDir = $OutDir\n", 4); $attrib = BackupPC::Attrib->new( { compress => $t->{compress} } ); $remoteDir = $t->remotels( $dir->{relPath} ); if ( ref($remoteDir) ne 'ARRAY' ) { $t->logWrite("handleDir failed: $remoteDir\n", 1); $t->logFileAction( "fail", $dir->{utf8name}, $dir ); return; } if ( $t->{type} eq "incr" ) { $localDir = $view->dirAttrib( $t->{incrBaseBkupNum}, $t->{shareName}, $dir->{relPath} ); %expectedFiles = map { $_ => 0 } sort keys %$localDir } # # take care of each file in the directory # SCAN: foreach my $f ( @{$remoteDir} ) { next SCAN if $t->ignoreFileCheck( $f, $attrib ); # # handle based on filetype # if ( $f->{type} eq 'f' ) { $t->handleFile( $f, $OutDir, $attrib ); } elsif ( $f->{type} eq 'd' ) { $attribInfo = { type => BPC_FTYPE_DIR, mode => $f->{mode}, uid => undef, # unsupported gid => undef, # unsupported size => $f->{size}, mtime => $f->{mtime}, }; #print STDERR "$f->{utf8name}: ". Dumper($attribInfo); if ( $t->handleDir($f, $OutDir) ) { $attrib->set( $f->{utf8name}, $attribInfo); } } elsif ( $f->{type} =~ /^l (.*)/ ) { $t->handleSymlink( $f, $OutDir, $attrib ); } else { # # do nothing # } # # Mark file as seen in expected files hash # $expectedFiles{ $f->{utf8name} }++ if ( $t->{type} eq "incr" ); } # end foreach (@{$remoteDir}) # # If the backup type is incremental, mark the files that are not # present on the server as deleted. # if ( $t->{type} eq "incr" ) { while ( my ($f, $seen) = each %expectedFiles ) { $attrib->set( $f, { type => BPC_FTYPE_DELETED } ) unless ($seen); } } # # print the directory attributes, now that the directory is done. # my $fileName = $attrib->fileName($OutDir); my $data = $attrib->writeData(); $poolWrite = BackupPC::PoolWrite->new( $bpc, $fileName, length($data), $t->{compress} ); $poolWrite->write( \$data ); ( $exists, $digest, $outSize, $errs ) = $poolWrite->close(); # # Explicit success # return 1; } # # handleFile() backs up a file. # sub handleFile { my ( $t, $f, $OutDir, $attrib ) = @_; my $bpc = $t->{bpc}; my $ftp = $t->{ftp}; my $view = $t->{view}; my $stats = $t->{stats}; my $newFilesFH = $t->{newFilesFH}; my ( $poolFile, $poolWrite, $data, $localSize ); my ( $exists, $digest, $outSize, $errs ); my ( $oldAttrib ); local *FTP; # # If this is an incremental backup and the file exists in a # previous backup unchanged, write the attribInfo for the file # accordingly. # if ( $t->{type} eq "incr" ) { return 1 if $t->incrFileExistCheck( $f, $attrib ); } my $attribInfo = { %$f, type => BPC_FTYPE_FILE, uid => undef, # unsupported gid => undef, # unsupported }; delete $attribInfo->{utf8name}; # unused value # # If this is a full backup or the file has changed on the host, # back it up. # undef $@; eval { tie ( *FTP, 'Net::FTP::RetrHandle', $ftp, $f->{fullName} ); }; if ( !*FTP || $@ ) { $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); $t->{xferBadFileCnt}++; $stats->{errCnt}++; return; } $poolFile = $OutDir . "/" . $bpc->fileNameMangle( $f->{name} ); $poolWrite = BackupPC::PoolWrite->new( $bpc, $poolFile, $f->{size}, $t->{compress} ); $localSize = 0; undef $@; eval { while () { $localSize += length($_); $poolWrite->write( \$_ ); } }; ( $exists, $digest, $outSize, $errs ) = $poolWrite->close(); if ( !*FTP || $@ || @$errs ) { $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); $t->logWrite("Unlinking($poolFile) because of error on close\n", 3); unlink($poolFile); $t->{xferBadFileCnt}++; $stats->{errCnt} += scalar @$errs; return; } # # this should never happen # if ( $localSize != $f->{size} ) { $t->logFileAction( "fail", $f->{utf8name}, $attribInfo ); $t->logWrite("Unlinking($poolFile) because of size mismatch ($localSize vs $f->{size})\n", 3); unlink($poolFile); $stats->{xferBadFileCnt}++; $stats->{errCnt}++; return; } # # Perform logging # $attrib->set( $f->{utf8name}, $attribInfo ); $t->logFileAction( $exists ? "pool" : "create", $f->{utf8name}, $attribInfo ); my $relPoolFile = $bpc->fileNameEltMangle( $t->{shareName} ) . "/" . $bpc->fileNameMangle($attribInfo->{relPath}); print $newFilesFH "$digest $f->{size} $relPoolFile\n" unless $exists; # # Cumulate the stats # $stats->{TotalFileCnt}++; $stats->{ExistFileCnt}++; $stats->{ExistFileCompSize} += -s $poolFile; $stats->{ExistFileSize} += $f->{size}; $stats->{TotalFileSize} += $f->{size}; $t->{byteCnt} += $localSize; $t->{fileCnt}++; } # # this function checks if the file has been modified on disk, and if # it has, returns. Otherwise, it updates the attrib values. # sub incrFileExistCheck { my ($t, $f, $attrib) = @_; my $view = $t->{view}; my $oldAttribInfo = $view->fileAttrib( $t->{incrBaseBkupNum}, $t->{shareName}, "/" . $f->{relPath} ); ##$t->logWrite( "Old attrib:\n" . Dumper($oldAttribInfo), 1 ); ##$t->logWrite( "New attrib:\n" . Dumper($f), 1 ); ##$t->logWrite( sprintf("%s: mtime %d vs %d, size %d vs %d\n", $f->{fullName}, ## $oldAttribInfo->{mtime}, $f->{mtime}, ## $oldAttribInfo->{size}, $f->{size}), 1); return ( $oldAttribInfo->{mtime} == $f->{mtime} && $oldAttribInfo->{size} == $f->{size} ); } # # Generate a log file message for a completed file. Taken from # BackupPC_tarExtract. $f should be an attrib object. # sub logFileAction { my ( $t, $action, $name, $attrib ) = @_; my $owner = "$attrib->{uid}/$attrib->{gid}"; my $type = ( ( "", "p", "c", "", "d", "", "b", "", "", "", "l", "", "s" ) ) [ ( $attrib->{mode} & S_IFMT ) >> 12 ]; $name = "." if ( $name eq "" ); $owner = "-/-" if ( $owner eq "/" ); my $fileAction = sprintf( " %-6s %1s%4o %9s %11.0f %s\n", $action, $type, $attrib->{mode} & 07777, $owner, $attrib->{size}, $attrib->{relPath} ); return $t->logWrite( $fileAction, 1 ); } 1; BackupPC-3.3.2/lib/BackupPC/Xfer/Protocol.pm0000444000076500000240000002300413042250554017355 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Xfer::Protocol package # # DESCRIPTION # # This library defines a BackupPC::Xfer::Protocol class which # defines standard methods for the transfer protocols in BackupPC. # # AUTHOR # Paul Mantz # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Xfer::Protocol; use strict; use Data::Dumper; use Encode qw/from_to encode/; use BackupPC::Attrib qw(:all); # # usage: # $t = BackupPC::Xfer::Protocol->new($args); # # new() is the constructor. There's nothing special going on here. # sub new { my($class, $bpc, $args) = @_; $args ||= {}; my $t = bless { bpc => $bpc, conf => $bpc->{Conf}, host => "", hostIP => "", shareName => "", pipeRH => undef, pipeWH => undef, badFiles => [], logLevel => $bpc->{Conf}{XferLogLevel}, # # Various stats # byteCnt => 0, fileCnt => 0, xferErrCnt => 0, xferBadShareCnt => 0, xferBadFileCnt => 0, xferOK => 0, # # User's args # %$args, }, $class; return $t; } # # usage: # $t->args($args); # # args() can be used to send additional argument to the Xfer object # via a hash reference. # sub args { my($t, $args) = @_; foreach my $arg ( keys(%$args) ) { $t->{$arg} = $args->{$arg}; } } # # usage: # $t->start(); # # start() executes the actual data transfer. Must be implemented by # the derived class. # sub start { my($t) = @_; $t->{_errStr} = "start() not implemented by ".ref($t); return; } # # # sub run { my($t) = @_; $t->{_errStr} = "run() not implemented by ".ref($t); return; } # # usage: # $t->readOutput(); # # This function is only used when $t->useTar() == 1. # sub readOutput { my($t) = @_; $t->{_errStr} = "readOutput() not implemented by " . ref($t); return; } # # usage: # $t->abort($reason); # # Aborts the current job. # sub abort { my($t, $reason) = @_; my @xferPid = $t->xferPid; $t->{abort} = 1; $t->{abortReason} = $reason; if ( @xferPid ) { kill($t->{bpc}->sigName2num("INT"), @xferPid); } } # # usage: # $t->subSelectMask # # This function sets a mask for files when ($t->useTar == 1). # sub setSelectMask { my($t) = @_; $t->{_errStr} = "readOutput() not implemented by " . ref($t); } # # usage: # $t->errStr(); # sub errStr { my($t) = @_; return $t->{_errStr}; } # # usage: # $pid = $t->xferPid(); # # xferPid() returns the process id of the child forked process. # sub xferPid { my($t) = @_; return ($t->{xferPid}); } # # usage: # $t->logMsg($msg); # sub logMsg { my ($t, $msg) = @_; push(@{$t->{_logMsg}}, $msg); } # # usage: # $t->logMsgGet(); # sub logMsgGet { my($t) = @_; return shift(@{$t->{_logMsg}}); } # # usage: # $t->getStats(); # # This function returns xfer statistics. It Returns a hash ref giving # various status information about the transfer. # sub getStats { my ($t) = @_; return { map { $_ => $t->{$_} } qw(byteCnt fileCnt xferErrCnt xferBadShareCnt xferBadFileCnt xferOK hostAbort hostError lastOutputLine) }; } sub getBadFiles { my ($t) = @_; return @{$t->{badFiles}}; } # # useTar function. In order to work correctly, the protocol in # question should overwrite the function if it needs to return true. # sub useTar { return 0; } ############################################################################## # Logging Functions ############################################################################## # # usage: # $t->logWrite($msg [, $level]) # # This function writes to XferLOG. # sub logWrite { my($t, $msg, $level) = @_; my $XferLOG = $t->{XferLOG}; $level = 3 if ( !defined($level) ); return ( $XferLOG->write(\$msg) ) if ( $level <= $t->{logLevel} ); } ############################################################################## # File Inclusion/Exclusion ############################################################################## # # loadInclExclRegexps() places the appropriate file include/exclude regexps # sub loadInclExclRegexps { my ( $t, $shareType ) = @_; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my @BackupFilesOnly = (); my @BackupFilesExclude = (); my ($shareName, $shareNameRE); $shareName = $t->{shareName}; $shareName =~ s/\/*$//; # remove trailing slashes $shareName = "/" if ( $shareName eq "" ); $t->{shareName} = $shareName; $t->{shareNameRE} = $bpc->glob2re($shareName); # # load all relevant values into @BackupFilesOnly # if ( ref( $conf->{BackupFilesOnly} ) eq "HASH" ) { foreach my $share ( ( '*', $shareName ) ) { push @BackupFilesOnly, @{ $conf->{BackupFilesOnly}{$share} } if ( defined( $conf->{BackupFilesOnly}{$share} ) ); } } elsif ( ref( $conf->{BackupFilesOnly} ) eq "ARRAY" ) { push( @BackupFilesOnly, @{ $conf->{BackupFilesOnly} } ); } elsif ( !defined( $conf->{BackupFilesOnly} ) ) { # # do nothing # } else { # # not a legitimate entry for $conf->{BackupFilesOnly} # $t->{_errStr} = "Incorrect syntax in BackupFilesOnly for host $t->{Host}"; return; } # # load all relevant values into @BackupFilesExclude # if ( ref( $conf->{BackupFilesExclude} ) eq "HASH" ) { foreach my $share ( ( '*', $shareName ) ) { push( @BackupFilesExclude, map { ( $_ =~ /^\// ) ? ( $t->{shareNameRE} . $bpc->glob2re($_) ) : ( '.*\/' . $bpc->glob2re($_) . '(?=\/.*)?' ) } @{ $conf->{BackupFilesExclude}{$share} } ) if ( defined( $conf->{BackupFilesExclude}{$share} ) ) ; } } elsif ( ref( $conf->{BackupFilesExclude} ) eq "ARRAY" ) { push( @BackupFilesExclude, map { ( $_ =~ /\// ) ? ( $bpc->glob2re($_) ) : ( '.*\/' . $bpc->glob2re($_) . '(?<=\/.*)?' ) } @{ $conf->{BackupFilesExclude} } ); } elsif ( !defined( $conf->{BackupFilesOnly} ) ) { # # do nothing here # } else { # # not a legitimate entry for $conf->{BackupFilesExclude} # $t->{_errStr} = "Incorrect syntax in BackupFilesExclude for host $t->{Host}"; return; } # # load the regular expressions into the xfer object # $t->{BackupFilesOnly} = ( @BackupFilesOnly > 0 ) ? \@BackupFilesOnly : undef; $t->{BackupFilesExclude} = ( @BackupFilesExclude > 0 ) ? \@BackupFilesExclude : undef; return 1; } sub checkIncludeExclude { my ($t, $file) = @_; return ( $t->checkIncludeMatch($file) && !$t->checkExcludeMatch($file) ); } sub checkIncludeMatch { my ($t, $file) = @_; my $shareName = $t->{shareName}; my $includes = $t->{BackupFilesOnly} || return 1; my $match = ""; foreach my $include ( @{$includes} ) { # # construct regexp elsewhere to avoid syntactical evil # $match = '^' . quotemeta( $shareName . $include ) . '(?=\/.*)?'; # # return true if the include folder is a parent of the file, # or the folder itself. # return 1 if ( $file =~ /$match/ ); $match = '^' . quotemeta($file) . '(?=\/.*)?'; # # return true if the file is a parent of the include folder, # or the folder itself. # return 1 if ( "$shareName$include" =~ /$match/ ); } return 0; } sub checkExcludeMatch { my ($t, $file) = @_; my $shareName = $t->{shareName}; my $excludes = $t->{BackupFilesExclude} || return 0; my $match = ""; foreach my $exclude ( @{$excludes} ) { # # construct regexp elsewhere to avoid syntactical evil # $match = '^' . quotemeta( $shareName . $exclude ) . '(?=\/.*)?'; # # return true if the exclude folder is a parent of the file, # or the folder itself. # return 1 if ( $file =~ /$match/ ); $match = '^' . quotemeta($file) . '(?=\/.*)?'; # # return true if the file is a parent of the exclude folder, # or the folder itself. # return 1 if ( "$shareName$exclude" =~ /$match/ ); } return 0; } 1; BackupPC-3.3.2/lib/BackupPC/Xfer/Rsync.pm0000444000076500000240000004177713042250554016673 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Xfer::Rsync package # # DESCRIPTION # # This library defines a BackupPC::Xfer::Rsync class for managing # the rsync-based transport of backup data from the client. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2002-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Xfer::Rsync; use strict; use BackupPC::View; use BackupPC::Xfer::RsyncFileIO; use Encode qw/from_to encode/; use base qw(BackupPC::Xfer::Protocol); use vars qw( $RsyncLibOK $RsyncLibErr ); BEGIN { eval "use File::RsyncP;"; if ( $@ ) { # # Rsync module doesn't exist. # $RsyncLibOK = 0; $RsyncLibErr = "File::RsyncP module doesn't exist"; } else { # # Note: also update configure.pl when this version number is changed! # if ( $File::RsyncP::VERSION < 0.68 ) { $RsyncLibOK = 0; $RsyncLibErr = "File::RsyncP module version" . " ($File::RsyncP::VERSION) too old: need >= 0.68"; } else { $RsyncLibOK = 1; } } }; sub new { my($class, $bpc, $args) = @_; return if ( !$RsyncLibOK ); my $t = BackupPC::Xfer::Protocol->new($bpc, $args); return bless($t, $class); } sub start { my($t) = @_; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my(@fileList, $rsyncClientCmd, $rsyncArgs, $logMsg, $incrDate, $argList, $fioArgs); # # We add a slash to the share name we pass to rsync # ($t->{shareNameSlash} = "$t->{shareName}/") =~ s{//+$}{/}; if ( $t->{type} eq "restore" ) { $rsyncClientCmd = $conf->{RsyncClientRestoreCmd}; $rsyncArgs = $conf->{RsyncRestoreArgs}; # # Merge variables into $rsyncArgs # $rsyncArgs = $bpc->cmdVarSubstitute($rsyncArgs, { host => $t->{host}, hostIP => $t->{hostIP}, client => $t->{client}, confDir => $conf->{ConfDir}, }); my $remoteDir = "$t->{shareName}/$t->{pathHdrDest}"; $remoteDir =~ s{//+}{/}g; from_to($remoteDir, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); $argList = ['--server', @$rsyncArgs, '.', $remoteDir]; $fioArgs = { client => $t->{bkupSrcHost}, share => $t->{bkupSrcShare}, viewNum => $t->{bkupSrcNum}, fileList => $t->{fileList}, }; $logMsg = "restore started below directory $t->{shareName}" . " to host $t->{host}"; } else { # # Turn $conf->{BackupFilesOnly} and $conf->{BackupFilesExclude} # into a hash of arrays of files, and $conf->{RsyncShareName} # to an array # $bpc->backupFileConfFix($conf, "RsyncShareName"); if ( defined($conf->{BackupFilesOnly}{$t->{shareName}}) ) { my(@inc, @exc, %incDone, %excDone); foreach my $file2 ( @{$conf->{BackupFilesOnly}{$t->{shareName}}} ) { # # If the user wants to just include /home/craig, then # we need to do create include/exclude pairs at # each level: # --include /home --exclude /* # --include /home/craig --exclude /home/* # # It's more complex if the user wants to include multiple # deep paths. For example, if they want /home/craig and # /var/log, then we need this mouthfull: # --include /home --include /var --exclude /* # --include /home/craig --exclude /home/* # --include /var/log --exclude /var/* # # To make this easier we do all the includes first and all # of the excludes at the end (hopefully they commute). # my $file = $file2; $file =~ s{/$}{}; $file = "/$file"; $file =~ s{//+}{/}g; if ( $file eq "/" ) { # # This is a special case: if the user specifies # "/" then just include it and don't exclude "/*". # push(@inc, $file) if ( !$incDone{$file} ); next; } my $f = ""; while ( $file =~ m{^/([^/]*)(.*)} ) { my $elt = $1; $file = $2; if ( $file eq "/" ) { # # preserve a tailing slash # $file = ""; $elt = "$elt/"; } push(@exc, "$f/*") if ( !$excDone{"$f/*"} ); $excDone{"$f/*"} = 1; $f = "$f/$elt"; push(@inc, $f) if ( !$incDone{$f} ); $incDone{$f} = 1; } } foreach my $file ( @inc ) { $file = encode($conf->{ClientCharset}, $file) if ( $conf->{ClientCharset} ne "" ); push(@fileList, "--include=$file"); } foreach my $file ( @exc ) { $file = encode($conf->{ClientCharset}, $file) if ( $conf->{ClientCharset} ne "" ); push(@fileList, "--exclude=$file"); } } if ( defined($conf->{BackupFilesExclude}{$t->{shareName}}) ) { foreach my $file2 ( @{$conf->{BackupFilesExclude}{$t->{shareName}}} ) { # # just append additional exclude lists onto the end # my $file = $file2; $file = encode($conf->{ClientCharset}, $file) if ( $conf->{ClientCharset} ne "" ); push(@fileList, "--exclude=$file"); } } if ( $t->{type} eq "full" ) { if ( $t->{partialNum} ) { $logMsg = "full backup started for directory $t->{shareName};" . " updating partial #$t->{partialNum}"; } else { $logMsg = "full backup started for directory $t->{shareName}"; if ( $t->{incrBaseBkupNum} ne "" ) { $logMsg .= " (baseline backup #$t->{incrBaseBkupNum})"; } } } else { $incrDate = $bpc->timeStamp($t->{incrBaseTime}, 1); $logMsg = "incr backup started back to $incrDate" . " (backup #$t->{incrBaseBkupNum}) for directory" . " $t->{shareName}"; } # # A full dump is implemented with --ignore-times: this causes all # files to be checksummed, even if the attributes are the same. # That way all the file contents are checked, but you get all # the efficiencies of rsync: only files deltas need to be # transferred, even though it is a full dump. # $rsyncArgs = $conf->{RsyncArgs}; # # Add any additional rsync args # $rsyncArgs = [@$rsyncArgs, @{$conf->{RsyncArgsExtra}}] if ( ref($conf->{RsyncArgsExtra}) eq 'ARRAY' ); # # Merge variables into $rsyncArgs # $rsyncArgs = $bpc->cmdVarSubstitute($rsyncArgs, { host => $t->{host}, hostIP => $t->{hostIP}, client => $t->{client}, confDir => $conf->{ConfDir}, }); $rsyncArgs = [@$rsyncArgs, @fileList] if ( @fileList ); $rsyncArgs = [@$rsyncArgs, "--ignore-times"] if ( $t->{type} eq "full" ); $rsyncClientCmd = $conf->{RsyncClientCmd}; my $shareNameSlash = $t->{shareNameSlash}; from_to($shareNameSlash, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); $argList = ['--server', '--sender', @$rsyncArgs, '.', $shareNameSlash]; eval { $argList = File::RsyncP->excludeStrip($argList); }; $fioArgs = { client => $t->{client}, share => $t->{shareName}, viewNum => $t->{incrBaseBkupNum}, partialNum => $t->{partialNum}, }; } # # Merge variables into $rsyncClientCmd # my $args = { host => $t->{host}, hostIP => $t->{hostIP}, client => $t->{client}, shareName => $t->{shareName}, shareNameSlash => $t->{shareNameSlash}, rsyncPath => $conf->{RsyncClientPath}, sshPath => $conf->{SshPath}, argList => $argList, }; from_to($args->{shareName}, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); from_to($args->{shareNameSlash}, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); $rsyncClientCmd = $bpc->cmdVarSubstitute($rsyncClientCmd, $args); # # Create the Rsync object, and tell it to use our own File::RsyncP::FileIO # module, which handles all the special BackupPC file storage # (compression, mangling, hardlinks, special files, attributes etc). # $t->{rsyncClientCmd} = $rsyncClientCmd; $t->{rs} = File::RsyncP->new({ logLevel => $t->{logLevel} || $conf->{RsyncLogLevel}, rsyncCmd => sub { $bpc->verbose(0); $bpc->cmdExecOrEval($rsyncClientCmd, $args); }, rsyncCmdType => "full", rsyncArgs => $rsyncArgs, timeout => $conf->{ClientTimeout}, doPartial => defined($t->{partialNum}) ? 1 : undef, logHandler => sub { my($str) = @_; $str .= "\n"; $t->{XferLOG}->write(\$str); if ( $str =~ /^Remote\[1\]: read errors mapping "(.*)"/ ) { # # Files with read errors (eg: region locked files # on WinXX) are filled with 0 by rsync. Remember # them and delete them later. # my $badFile = $1; $badFile =~ s/^\/+//; push(@{$t->{badFiles}}, { share => $t->{shareName}, file => $badFile }); } }, pidHandler => sub { $t->{pidHandler}(@_); }, completionPercent => sub { $t->{completionPercent}(@_); }, clientCharset => $conf->{ClientCharset}, fio => BackupPC::Xfer::RsyncFileIO->new({ xfer => $t, bpc => $t->{bpc}, conf => $t->{conf}, backups => $t->{backups}, logLevel => $t->{logLevel} || $conf->{RsyncLogLevel}, logHandler => sub { my($str) = @_; $str .= "\n"; $t->{XferLOG}->write(\$str); }, cacheCheckProb => $conf->{RsyncCsumCacheVerifyProb}, clientCharset => $conf->{ClientCharset}, %$fioArgs, }), }); delete($t->{_errStr}); return $logMsg; } sub run { my($t) = @_; my $rs = $t->{rs}; my $conf = $t->{conf}; my($remoteSend, $remoteDir, $remoteDirDaemon); alarm($conf->{ClientTimeout}); if ( $t->{type} eq "restore" ) { $remoteSend = 0; ($remoteDir = "$t->{shareName}/$t->{pathHdrDest}") =~ s{//+}{/}g; ($remoteDirDaemon = "$t->{shareName}/$t->{pathHdrDest}") =~ s{//+}{/}g; $remoteDirDaemon = $t->{shareNameSlash} if ( $t->{pathHdrDest} eq "" || $t->{pathHdrDest} eq "/" ); } else { $remoteSend = 1; $remoteDir = $t->{shareNameSlash}; $remoteDirDaemon = "."; } from_to($remoteDir, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); from_to($remoteDirDaemon, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); if ( $t->{XferMethod} eq "rsync" ) { # # Run rsync command # my $str = "Running: " . $t->{bpc}->execCmd2ShellCmd(@{$t->{rsyncClientCmd}}) . "\n"; from_to($str, $conf->{ClientCharset}, "utf8") if ( $conf->{ClientCharset} ne "" ); $t->{XferLOG}->write(\$str); $rs->remoteStart($remoteSend, $remoteDir); } else { # # Connect to the rsync server # if ( defined(my $err = $rs->serverConnect($t->{hostIP}, $conf->{RsyncdClientPort})) ) { $t->{hostError} = $err; my $str = "Error connecting to rsync daemon at $t->{hostIP}" . ":$conf->{RsyncdClientPort}: $err\n"; $t->{XferLOG}->write(\$str); return; } # # Pass module name, and follow it with a slash if it already # contains a slash; otherwise just keep the plain module name. # my $module = $t->{shareName}; $module = $t->{shareNameSlash} if ( $module =~ /\// ); from_to($module, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); if ( defined(my $err = $rs->serverService($module, $conf->{RsyncdUserName}, $conf->{RsyncdPasswd}, $conf->{RsyncdAuthRequired})) ) { my $str = "Error connecting to module $module at $t->{hostIP}" . ":$conf->{RsyncdClientPort}: $err\n"; $t->{XferLOG}->write(\$str); $t->{hostError} = $err; return; } # # This is a hack. To avoid wide chars we encode the arguments # to utf8 byte streams, then to the client's local charset. # The second conversion should really go in File::RsyncP, since # it shouldn't be applied to in-line include/exclude arguments. # for ( my $i = 0 ; $i < @{$rs->{rsyncArgs}} ; $i++ ) { $rs->{rsyncArgs}[$i] = encode('utf8', $rs->{rsyncArgs}[$i]); from_to($rs->{rsyncArgs}[$i], 'utf8', $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); } ##my $str = "RsyncArgsBefore: " . join(" ", @{$rs->{rsyncArgs}}) . "\n"; ##$t->{XferLOG}->write(\$str); $rs->serverStart($remoteSend, $remoteDirDaemon); ##$str = "RsyncArgsAfter: " . join(" ", @{$rs->{rsyncArgs}}) . "\n"; ##$t->{XferLOG}->write(\$str); } my $shareNameSlash = $t->{shareNameSlash}; from_to($shareNameSlash, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); my $error = $rs->go($shareNameSlash); $rs->serverClose(); # # TODO: generate sensible stats # # $rs->{stats}{totalWritten} # $rs->{stats}{totalSize} # my $stats = $rs->statsFinal; if ( !defined($error) && defined($stats) ) { $t->{xferOK} = 1; } else { $t->{xferOK} = 0; } $t->{xferErrCnt} = $stats->{remoteErrCnt} + $stats->{childStats}{errorCnt} + $stats->{parentStats}{errorCnt}; $t->{byteCnt} = $stats->{childStats}{TotalFileSize} + $stats->{parentStats}{TotalFileSize}; $t->{fileCnt} = $stats->{childStats}{TotalFileCnt} + $stats->{parentStats}{TotalFileCnt}; my $str = "Done: $t->{fileCnt} files, $t->{byteCnt} bytes\n"; $t->{XferLOG}->write(\$str); # # TODO: get error count, and call fio to get stats... # $t->{hostError} = $error if ( defined($error) ); if ( $t->{type} eq "restore" ) { return ( $t->{fileCnt}, $t->{byteCnt}, 0, 0 ); } else { return ( 0, $stats->{childStats}{ExistFileCnt} + $stats->{parentStats}{ExistFileCnt}, $stats->{childStats}{ExistFileSize} + $stats->{parentStats}{ExistFileSize}, $stats->{childStats}{ExistFileCompSize} + $stats->{parentStats}{ExistFileCompSize}, $stats->{childStats}{TotalFileCnt} + $stats->{parentStats}{TotalFileCnt}, $stats->{childStats}{TotalFileSize} + $stats->{parentStats}{TotalFileSize}, ); } } sub abort { my($t, $reason) = @_; my $rs = $t->{rs}; $rs->abort($reason); return 1; } sub errStr { my($t) = @_; return $RsyncLibErr if ( !defined($t) || ref($t) ne "HASH" ); return $t->{_errStr}; } sub xferPid { my($t) = @_; return (); } 1; BackupPC-3.3.2/lib/BackupPC/Xfer/RsyncDigest.pm0000444000076500000240000003636713042250554020032 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Xfer::RsyncDigest package # # DESCRIPTION # # This library defines a BackupPC::Xfer::RsyncDigest class for computing # and caching rsync checksums. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Xfer::RsyncDigest; use strict; use BackupPC::FileZIO; use vars qw( $RsyncLibOK ); use Carp; use Fcntl; require Exporter; use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS ); my $Log = \&logHandler; # # Magic value for checksum seed. We only cache block and file digests # when the checksum seed matches this value. # use constant RSYNC_CSUMSEED_CACHE => 32761; @ISA = qw(Exporter); @EXPORT = qw( ); @EXPORT_OK = qw( RSYNC_CSUMSEED_CACHE ); %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ], ); BEGIN { eval "use File::RsyncP;"; if ( $@ ) { # # File::RsyncP doesn't exist. Define some dummy constant # subs so that the code below doesn't barf. # $RsyncLibOK = 0; } else { $RsyncLibOK = 1; } }; # # Return the rsync block size based on the file size. # We also make sure the block size plus 4 (ie: cheeksumSeed) # is not a multiple of 64 - otherwise the cached checksums # will not be the same for protocol versions <= 26 and > 26. # sub blockSize { my($class, $fileSize, $defaultBlkSize) = @_; my $blkSize = int($fileSize / 10000); $blkSize = $defaultBlkSize if ( $blkSize < $defaultBlkSize ); $blkSize = 16384 if ( $blkSize > 16384 ); $blkSize += 4 if ( (($blkSize + 4) % 64) == 0 ); return $blkSize; } sub fileDigestIsCached { my($class, $file) = @_; my $data; sysopen(my $fh, $file, O_RDONLY) || return -1; binmode($fh); return -2 if ( sysread($fh, $data, 1) != 1 ); close($fh); return $data eq chr(0xd7) ? 1 : 0; } # # Compute and add rsync block and file digests to the given file. # # Empty files don't get cached checksums. # # If verify is set then existing cached checksums are checked. # If verify == 2 then only a verify is done; no fixes are applied. # # Returns 0 on success. Returns 1 on good verify and 2 on bad verify. # Returns a variety of negative values on error. # sub digestAdd { my($class, $file, $blockSize, $checksumSeed, $verify, $protocol_version) = @_; my $retValue = 0; # # Don't cache checksums if the checksumSeed is not RSYNC_CSUMSEED_CACHE # or if the file is empty. # return -100 if ( $checksumSeed != RSYNC_CSUMSEED_CACHE || !-s $file ); if ( $blockSize == 0 ) { &$Log("digestAdd: bad blockSize ($file, $blockSize, $checksumSeed)"); $blockSize = 2048; } my $nBlks = int(65536 * 16 / $blockSize) + 1; my($data, $blockDigest, $fileDigest); return -101 if ( !$RsyncLibOK ); my $digest = File::RsyncP::Digest->new; $digest->protocol($protocol_version) if ( defined($protocol_version) ); $digest->add(pack("V", $checksumSeed)) if ( $checksumSeed ); return -102 if ( !defined(my $fh = BackupPC::FileZIO->open($file, 0, 1)) ); my $fileSize; while ( 1 ) { $fh->read(\$data, $nBlks * $blockSize); $fileSize += length($data); last if ( $data eq "" ); $blockDigest .= $digest->blockDigest($data, $blockSize, 16, $checksumSeed); $digest->add($data); } $fileDigest = $digest->digest2; my $eofPosn = sysseek($fh->{fh}, 0, 1); $fh->close; my $rsyncData = $blockDigest . $fileDigest; my $metaData = pack("VVVV", $blockSize, $checksumSeed, length($blockDigest) / 20, 0x5fe3c289, # magic number ); my $data2 = chr(0xb3) . $rsyncData . $metaData; # printf("appending %d+%d bytes to %s at offset %d\n", # length($rsyncData), # length($metaData), # $file, # $eofPosn); sysopen(my $fh2, $file, O_RDWR) || return -103; binmode($fh2); return -104 if ( sysread($fh2, $data, 1) != 1 ); if ( $data ne chr(0x78) && $data ne chr(0xd6) && $data ne chr(0xd7) ) { &$Log(sprintf("digestAdd: $file has unexpected first char 0x%x", ord($data))); return -105; } return -106 if ( sysseek($fh2, $eofPosn, 0) != $eofPosn ); if ( $verify ) { my $data3; # # Verify the cached checksums # return -107 if ( $data ne chr(0xd7) ); return -108 if ( sysread($fh2, $data3, length($data2) + 1) < 0 ); if ( $data2 eq $data3 ) { return 1; } # # Checksums don't agree - fall through so we rewrite the data # &$Log(sprintf("digestAdd: %s verify failed; redoing checksums; len = %d,%d; eofPosn = %d, fileSize = %d", $file, length($data2), length($data3), $eofPosn, $fileSize)); #&$Log(sprintf("dataNew = %s", unpack("H*", $data2))); #&$Log(sprintf("dataFile = %s", unpack("H*", $data3))); return -109 if ( sysseek($fh2, $eofPosn, 0) != $eofPosn ); $retValue = 2; return $retValue if ( $verify == 2 ); } return -110 if ( syswrite($fh2, $data2) != length($data2) ); if ( $verify ) { # # Make sure there is no extraneous data on the end of # the file. Seek to the end and truncate if it doesn't # match our expected length. # return -111 if ( !defined(sysseek($fh2, 0, 2)) ); if ( sysseek($fh2, 0, 1) != $eofPosn + length($data2) ) { if ( !truncate($fh2, $eofPosn + length($data2)) ) { &$Log(sprintf("digestAdd: $file truncate from %d to %d failed", sysseek($fh2, 0, 1), $eofPosn + length($data2))); return -112; } else { &$Log(sprintf("digestAdd: %s truncated from %d to %d", $file, sysseek($fh2, 0, 1), $eofPosn + length($data2))); } } } return -113 if ( !defined(sysseek($fh2, 0, 0)) ); return -114 if ( syswrite($fh2, chr(0xd7)) != 1 ); close($fh2); return $retValue; } # # Return rsync checksums for the given file. We read the cached checksums # if they exist and the block size and checksum seed match. Otherwise # we compute the checksums from the file contents. # # The doCache flag can take three ranges: # # - doCache < 0: don't generate/use cached checksums # - doCache == 0: don't generate, but do use cached checksums if available # - doCache > 0: generate (if necessary) and use cached checksums # # Note: caching is only enabled when compression is on and the # checksum seed is RSYNC_CSUMSEED_CACHE (32761). # # Returns 0 on success. Returns a variety of negative values on error. # sub digestStart { my($class, $fileName, $fileSize, $blockSize, $defBlkSize, $checksumSeed, $needMD4, $compress, $doCache, $protocol_version) = @_; return -1 if ( !$RsyncLibOK ); my $data; my $dg = bless { name => $fileName, needMD4 => $needMD4, digest => File::RsyncP::Digest->new, protocol_version => $protocol_version, }, $class; $dg->{digest}->protocol($dg->{protocol_version}) if ( defined($dg->{protocol_version}) ); if ( $fileSize > 0 && $compress && $doCache >= 0 ) { open(my $fh, "<", $fileName) || return -2; binmode($fh); return -3 if ( sysread($fh, $data, 4096) < 1 ); my $ret; if ( (vec($data, 0, 8) == 0x78 || vec($data, 0, 8) == 0xd6) && $doCache > 0 && $checksumSeed == RSYNC_CSUMSEED_CACHE ) { # # RSYNC_CSUMSEED_CACHE (32761) is the magic number that # rsync uses for checksumSeed with the --fixed-csum option. # # We now add the cached checksum data to the file. There # is a possible race condition here since two BackupPC_dump # processes might call this function at the same time # on the same file. But this should be ok since both # processes will write the same data, and the order # in which they write it doesn't matter. # close($fh); $ret = $dg->digestAdd($fileName, $blockSize || BackupPC::Xfer::RsyncDigest->blockSize( $fileSize, $defBlkSize), $checksumSeed, 0, $dg->{protocol_version}); if ( $ret < 0 ) { &$Log("digestAdd($fileName) failed ($ret)"); } # # now re-open the file and re-read the first byte # open($fh, "<", $fileName) || return -4; binmode($fh); return -5 if ( read($fh, $data, 1) != 1 ); } if ( $ret >= 0 && vec($data, 0, 8) == 0xd7 ) { # # Looks like this file has cached checksums # Read the last 48 bytes: that's 2 file MD4s (32 bytes) # plus 4 words of meta data # my $cacheInfo; if ( length($data) >= 4096 ) { return -6 if ( !defined(sysseek($fh, -4096, 2)) ); return -7 if ( sysread($fh, $data, 4096) != 4096 ); } $cacheInfo = substr($data, -48); ($dg->{md4DigestOld}, $dg->{md4Digest}, $dg->{blockSize}, $dg->{checksumSeed}, $dg->{nBlocks}, $dg->{magic}) = unpack("a16 a16 V V V V", $cacheInfo); if ( $dg->{magic} == 0x5fe3c289 && $dg->{checksumSeed} == $checksumSeed && ($blockSize == 0 || $dg->{blockSize} == $blockSize) ) { $dg->{fh} = $fh; $dg->{cached} = 1; if ( length($data) >= $dg->{nBlocks} * 20 + 48 ) { # # We have all the data already - just remember it # $dg->{digestData} = substr($data, length($data) - $dg->{nBlocks} * 20 - 48, $dg->{nBlocks} * 20); } else { # # position the file at the start of the rsync block checksums # (4 (adler) + 16 (md4) bytes each) # return -8 if ( !defined(sysseek($fh, -$dg->{nBlocks} * 20 - 48, 2)) ); } } else { # # cached checksums are not valid, so we close the # file and treat it as uncached. # $dg->{cachedInvalid} = 1; close($fh); } } } if ( !$dg->{cached} ) { # # This file doesn't have cached checksums, or the checksumSeed # or blocksize doesn't match. Open the file and prepare to # compute the checksums. # $blockSize = BackupPC::Xfer::RsyncDigest->blockSize($fileSize, $defBlkSize) if ( $blockSize == 0 ); $dg->{checksumSeed} = $checksumSeed; $dg->{blockSize} = $blockSize; $dg->{fh} = BackupPC::FileZIO->open($fileName, 0, $compress); return -9 if ( !defined($dg->{fh}) ); if ( $needMD4) { $dg->{csumDigest} = File::RsyncP::Digest->new; $dg->{csumDigest}->protocol($dg->{protocol_version}) if ( defined($dg->{protocol_version}) ); $dg->{csumDigest}->add(pack("V", $dg->{checksumSeed})); } } return (undef, $dg, $dg->{blockSize}); } sub digestGet { my($dg, $num, $csumLen, $noPad) = @_; my($fileData); my $blockSize = $dg->{blockSize}; if ( $dg->{cached} ) { my $thisNum = $num; $thisNum = $dg->{nBlocks} if ( $thisNum > $dg->{nBlocks} ); if ( defined($dg->{digestData}) ) { $fileData = substr($dg->{digestData}, 0, 20 * $thisNum); $dg->{digestData} = substr($dg->{digestData}, 20 * $thisNum); } else { sysread($dg->{fh}, $fileData, 20 * $thisNum); } $dg->{nBlocks} -= $thisNum; if ( $thisNum < $num && !$noPad) { # # unexpected shortfall of data; pad with zero digest # $fileData .= pack("c", 0) x (20 * ($num - $thisNum)); } return $dg->{digest}->blockDigestExtract($fileData, $csumLen); } else { if ( $dg->{fh}->read(\$fileData, $blockSize * $num) <= 0 ) { # # unexpected shortfall of data; pad with zeros # $fileData = pack("c", 0) x ($blockSize * $num) if ( !$noPad ); } $dg->{csumDigest}->add($fileData) if ( $dg->{needMD4} ); return $dg->{digest}->blockDigest($fileData, $blockSize, $csumLen, $dg->{checksumSeed}); } } sub digestEnd { my($dg, $skipMD4) = @_; my($fileData); if ( $dg->{cached} ) { close($dg->{fh}); if ( $dg->{needMD4} ) { if ( $dg->{protocol_version} <= 26 ) { return $dg->{md4DigestOld}; } else { return $dg->{md4Digest}; } } } else { # # make sure we read the entire file for the file MD4 digest # if ( $dg->{needMD4} && !$skipMD4 ) { my $fileData; while ( $dg->{fh}->read(\$fileData, 65536) > 0 ) { $dg->{csumDigest}->add($fileData); } } $dg->{fh}->close(); return $dg->{csumDigest}->digest if ( $dg->{needMD4} ); } } sub isCached { my($dg) = @_; return wantarray ? ($dg->{cached}, $dg->{cachedInvalid}) : $dg->{cached}; } sub blockSizeCurr { my($dg) = @_; return $dg->{blockSize}; } # # Default log handler # sub logHandler { my($str) = @_; print(STDERR $str, "\n"); } # # Set log handler to a new subroutine. # sub logHandlerSet { my($dg, $sub) = @_; $Log = $sub; } 1; BackupPC-3.3.2/lib/BackupPC/Xfer/RsyncFileIO.pm0000444000076500000240000013473413042250554017717 0ustar craigstaff#============================================================= -*-perl-*- # # Rsync package # # DESCRIPTION # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2002-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Xfer::RsyncFileIO; use strict; use File::Path; use Encode qw/from_to/; use BackupPC::Attrib qw(:all); use BackupPC::View; use BackupPC::Xfer::RsyncDigest qw(:all); use BackupPC::PoolWrite; use constant S_HLINK_TARGET => 0400000; # this file is hardlink target use constant S_IFMT => 0170000; # type of file use constant S_IFDIR => 0040000; # directory use constant S_IFCHR => 0020000; # character special use constant S_IFBLK => 0060000; # block special use constant S_IFREG => 0100000; # regular use constant S_IFLNK => 0120000; # symbolic link use constant S_IFSOCK => 0140000; # socket use constant S_IFIFO => 0010000; # fifo use vars qw( $RsyncLibOK ); BEGIN { eval "use File::RsyncP::Digest"; if ( $@ ) { # # Rsync module doesn't exist. # $RsyncLibOK = 0; } else { $RsyncLibOK = 1; } }; sub new { my($class, $options) = @_; return if ( !$RsyncLibOK ); $options ||= {}; my $fio = bless { blockSize => 700, logLevel => 0, digest => File::RsyncP::Digest->new(), checksumSeed => 0, attrib => {}, logHandler => \&logHandler, stats => { errorCnt => 0, TotalFileCnt => 0, TotalFileSize => 0, ExistFileCnt => 0, ExistFileSize => 0, ExistFileCompSize => 0, }, %$options, }, $class; $fio->{digest}->protocol($fio->{protocol_version}); $fio->{shareM} = $fio->{bpc}->fileNameEltMangle($fio->{share}); $fio->{outDir} = "$fio->{xfer}{outDir}/new/"; $fio->{outDirSh} = "$fio->{outDir}/$fio->{shareM}/"; $fio->{view} = BackupPC::View->new($fio->{bpc}, $fio->{client}, $fio->{backups}); $fio->{full} = $fio->{xfer}{type} eq "full" ? 1 : 0; $fio->{newFilesFH} = $fio->{xfer}{newFilesFH}; $fio->{partialNum} = undef if ( !$fio->{full} ); return $fio; } # # We publish our version to File::RsyncP. This is so File::RsyncP # can provide backward compatibility to older FileIO code. # # Versions: # # undef or 1: protocol version 26, no hardlinks # 2: protocol version 28, supports hardlinks # sub version { return 2; } sub blockSize { my($fio, $value) = @_; $fio->{blockSize} = $value if ( defined($value) ); return $fio->{blockSize}; } sub protocol_version { my($fio, $value) = @_; if ( defined($value) ) { $fio->{protocol_version} = $value; $fio->{digest}->protocol($fio->{protocol_version}); } return $fio->{protocol_version}; } sub preserve_hard_links { my($fio, $value) = @_; $fio->{preserve_hard_links} = $value if ( defined($value) ); return $fio->{preserve_hard_links}; } sub logHandlerSet { my($fio, $sub) = @_; $fio->{logHandler} = $sub; BackupPC::Xfer::RsyncDigest->logHandlerSet($sub); } # # Setup rsync checksum computation for the given file. # sub csumStart { my($fio, $f, $needMD4, $defBlkSize, $phase) = @_; $defBlkSize ||= $fio->{blockSize}; my $attr = $fio->attribGet($f, 1); $fio->{file} = $f; $fio->csumEnd if ( defined($fio->{csum}) ); return -1 if ( $attr->{type} != BPC_FTYPE_FILE ); # # Rsync uses short checksums on the first phase. If the whole-file # checksum fails, then the file is repeated with full checksums. # So on phase 2 we verify the checksums if they are cached. # if ( ($phase > 0 || rand(1) < $fio->{cacheCheckProb}) && $attr->{compress} && $fio->{checksumSeed} == RSYNC_CSUMSEED_CACHE ) { my($err, $d, $blkSize) = BackupPC::Xfer::RsyncDigest->digestStart( $attr->{fullPath}, $attr->{size}, 0, $defBlkSize, $fio->{checksumSeed}, 0, $attr->{compress}, 0, $fio->{protocol_version}); if ( $err ) { $fio->log("Can't get rsync digests from $attr->{fullPath}" . " (err=$err, name=$f->{name})"); $fio->{stats}{errorCnt}++; return -1; } my($isCached, $isInvalid) = $d->isCached; if ( $fio->{logLevel} >= 5 ) { $fio->log("$attr->{fullPath} verify; cached = $isCached," . " invalid = $isInvalid, phase = $phase"); } if ( $isCached || $isInvalid ) { my $ret = BackupPC::Xfer::RsyncDigest->digestAdd( $attr->{fullPath}, $blkSize, $fio->{checksumSeed}, 1, # verify $fio->{protocol_version} ); if ( $ret != 1 ) { $fio->log("Bad cached digest for $attr->{fullPath} ($ret);" . " fixed"); $fio->{stats}{errorCnt}++; } else { $fio->log("$f->{name}: verified cached digest") if ( $fio->{logLevel} >= 2 ); } } $d->digestEnd; } (my $err, $fio->{csum}, my $blkSize) = BackupPC::Xfer::RsyncDigest->digestStart($attr->{fullPath}, $attr->{size}, 0, $defBlkSize, $fio->{checksumSeed}, $needMD4, $attr->{compress}, 1, $fio->{protocol_version}); if ( $err ) { $fio->log("Can't get rsync digests from $attr->{fullPath}" . " (err=$err, name=$f->{name})"); $fio->{stats}{errorCnt}++; return -1; } if ( $fio->{logLevel} >= 5 ) { my($isCached, $invalid) = $fio->{csum}->isCached; $fio->log("$attr->{fullPath} cache = $isCached," . " invalid = $invalid, phase = $phase"); } return $blkSize; } sub csumGet { my($fio, $num, $csumLen, $blockSize) = @_; my($fileData); $num ||= 100; $csumLen ||= 16; return if ( !defined($fio->{csum}) ); return $fio->{csum}->digestGet($num, $csumLen); } sub csumEnd { my($fio) = @_; return if ( !defined($fio->{csum}) ); return $fio->{csum}->digestEnd(); } sub readStart { my($fio, $f) = @_; my $attr = $fio->attribGet($f, 1); $fio->{file} = $f; $fio->readEnd if ( defined($fio->{fh}) ); if ( !defined($fio->{fh} = BackupPC::FileZIO->open($attr->{fullPath}, 0, $attr->{compress})) ) { $fio->log("Can't open $attr->{fullPath} (name=$f->{name})"); $fio->{stats}{errorCnt}++; return; } $fio->log("$f->{name}: opened for read") if ( $fio->{logLevel} >= 4 ); } sub read { my($fio, $num) = @_; my $fileData; $num ||= 32768; return if ( !defined($fio->{fh}) ); if ( $fio->{fh}->read(\$fileData, $num) <= 0 ) { return $fio->readEnd; } $fio->log(sprintf("read returns %d bytes", length($fileData))) if ( $fio->{logLevel} >= 8 ); return \$fileData; } sub readEnd { my($fio) = @_; return if ( !defined($fio->{fh}) ); $fio->{fh}->close; $fio->log("closing $fio->{file}{name})") if ( $fio->{logLevel} >= 8 ); delete($fio->{fh}); return; } sub checksumSeed { my($fio, $checksumSeed) = @_; $fio->{checksumSeed} = $checksumSeed; $fio->log("Checksum caching enabled (checksumSeed = $checksumSeed)") if ( $fio->{logLevel} >= 1 && $checksumSeed == RSYNC_CSUMSEED_CACHE ); $fio->log("Checksum seed is $checksumSeed") if ( $fio->{logLevel} >= 2 && $checksumSeed != RSYNC_CSUMSEED_CACHE ); } sub dirs { my($fio, $localDir, $remoteDir) = @_; $fio->{localDir} = $localDir; $fio->{remoteDir} = $remoteDir; } sub viewCacheDir { my($fio, $share, $dir) = @_; my $shareM; #$fio->log("viewCacheDir($share, $dir)"); if ( !defined($share) ) { $share = $fio->{share}; $shareM = $fio->{shareM}; } else { $shareM = $fio->{bpc}->fileNameEltMangle($share); } $shareM = "$shareM/$dir" if ( $dir ne "" ); return if ( defined($fio->{viewCache}{$shareM}) ); # # purge old cache entries (ie: those that don't match the # first part of $dir). # foreach my $d ( keys(%{$fio->{viewCache}}) ) { delete($fio->{viewCache}{$d}) if ( $shareM !~ m{^\Q$d/} ); } # # fetch new directory attributes # $fio->{viewCache}{$shareM} = $fio->{view}->dirAttrib($fio->{viewNum}, $share, $dir); # # also cache partial backup attrib data too # if ( defined($fio->{partialNum}) ) { foreach my $d ( keys(%{$fio->{partialCache}}) ) { delete($fio->{partialCache}{$d}) if ( $shareM !~ m{^\Q$d/} ); } $fio->{partialCache}{$shareM} = $fio->{view}->dirAttrib($fio->{partialNum}, $share, $dir); } } sub attribGetWhere { my($fio, $f, $noCache, $fname) = @_; my($dir, $share, $shareM, $partial, $attr); if ( !defined($fname) ) { $fname = $f->{name}; $fname = "$fio->{xfer}{pathHdrSrc}/$fname" if ( defined($fio->{xfer}{pathHdrSrc}) ); } $fname =~ s{//+}{/}g; if ( $fname =~ m{(.*)/(.*)}s ) { $shareM = $fio->{shareM}; $dir = $1; $fname = $2; } elsif ( $fname ne "." ) { $shareM = $fio->{shareM}; $dir = ""; } else { $share = ""; $shareM = ""; $dir = ""; $fname = $fio->{share}; } $shareM .= "/$dir" if ( $dir ne "" ); if ( $noCache ) { $share = $fio->{share} if ( !defined($share) ); my $dirAttr = $fio->{view}->dirAttrib($fio->{viewNum}, $share, $dir); $attr = $dirAttr->{$fname}; } else { $fio->viewCacheDir($share, $dir); if ( defined($attr = $fio->{viewCache}{$shareM}{$fname}) ) { $partial = 0; } elsif ( defined($attr = $fio->{partialCache}{$shareM}{$fname}) ) { $partial = 1; } else { return; } if ( $attr->{mode} & S_HLINK_TARGET ) { $attr->{hlink_self} = 1; $attr->{mode} &= ~S_HLINK_TARGET; } } return ($attr, $partial); } sub attribGet { my($fio, $f, $doHardLink) = @_; my($attr) = $fio->attribGetWhere($f); if ( $doHardLink && $attr->{type} == BPC_FTYPE_HARDLINK ) { $fio->log("$attr->{fullPath}: opening for hardlink read" . " (name = $f->{name})") if ( $fio->{logLevel} >= 4 ); my $fh = BackupPC::FileZIO->open($attr->{fullPath}, 0, $attr->{compress}); my $target; if ( defined($fh) ) { $fh->read(\$target, 65536); $fh->close; $target =~ s/^\.?\/+//; } else { $fio->log("$attr->{fullPath}: can't open for hardlink read"); $fio->{stats}{errorCnt}++; $attr->{type} = BPC_FTYPE_FILE; return $attr; } $target = "/$target" if ( $target !~ /^\// ); $fio->log("$attr->{fullPath}: redirecting to $target") if ( $fio->{logLevel} >= 4 ); $target =~ s{^/+}{}; ($attr) = $fio->attribGetWhere($f, 1, $target); $fio->log(" ... now got $attr->{fullPath}") if ( $fio->{logLevel} >= 4 ); } return $attr; } sub mode2type { my($fio, $f) = @_; my $mode = $f->{mode}; if ( ($mode & S_IFMT) == S_IFREG ) { if ( defined($f->{hlink}) && !$f->{hlink_self} ) { return BPC_FTYPE_HARDLINK; } else { return BPC_FTYPE_FILE; } } elsif ( ($mode & S_IFMT) == S_IFDIR ) { return BPC_FTYPE_DIR; } elsif ( ($mode & S_IFMT) == S_IFLNK ) { return BPC_FTYPE_SYMLINK; } elsif ( ($mode & S_IFMT) == S_IFCHR ) { return BPC_FTYPE_CHARDEV; } elsif ( ($mode & S_IFMT) == S_IFBLK ) { return BPC_FTYPE_BLOCKDEV; } elsif ( ($mode & S_IFMT) == S_IFIFO ) { return BPC_FTYPE_FIFO; } elsif ( ($mode & S_IFMT) == S_IFSOCK ) { return BPC_FTYPE_SOCKET; } else { return BPC_FTYPE_UNKNOWN; } } # # Set the attributes for a file. Returns non-zero on error. # sub attribSet { my($fio, $f, $placeHolder) = @_; my($dir, $file); return if ( $placeHolder && $fio->{phase} > 0 ); if ( $f->{name} =~ m{(.*)/(.*)}s ) { $file = $2; $dir = "$fio->{shareM}/" . $1; } elsif ( $f->{name} eq "." ) { $dir = ""; $file = $fio->{share}; } else { $dir = $fio->{shareM}; $file = $f->{name}; } if ( $dir ne "" && (!defined($fio->{attribLastDir}) || $fio->{attribLastDir} ne $dir) ) { # # Flush any directories that don't match the first part # of the new directory. Don't flush the top-level directory # (ie: $dir eq "") since the "." might get sorted in the middle # of other top-level directories or files. # foreach my $d ( keys(%{$fio->{attrib}}) ) { next if ( $d eq "" || "$dir/" =~ m{^\Q$d/} ); $fio->attribWrite($d); } $fio->{attribLastDir} = $dir; } if ( !exists($fio->{attrib}{$dir}) ) { $fio->log("attribSet: dir=$dir not found") if ( $fio->{logLevel} >= 4 ); $fio->{attrib}{$dir} = BackupPC::Attrib->new({ compress => $fio->{xfer}{compress}, }); my $dirM = $dir; $dirM = $1 . "/" . $fio->{bpc}->fileNameMangle($2) if ( $dirM =~ m{(.*?)/(.*)}s ); my $path = $fio->{outDir} . $dirM; if ( -f $fio->{attrib}{$dir}->fileName($path) ) { if ( !$fio->{attrib}{$dir}->read($path) ) { $fio->log(sprintf("Unable to read attribute file %s", $fio->{attrib}{$dir}->fileName($path))); } else { $fio->log(sprintf("attribRead file %s", $fio->{attrib}{$dir}->fileName($path))) if ( $fio->{logLevel} >= 4 ); } } } else { $fio->log("attribSet: dir=$dir exists") if ( $fio->{logLevel} >= 4 ); } $fio->log("attribSet(dir=$dir, file=$file, size=$f->{size}, placeholder=$placeHolder)") if ( $fio->{logLevel} >= 4 ); my $mode = $f->{mode}; $mode |= S_HLINK_TARGET if ( $f->{hlink_self} ); $fio->{attrib}{$dir}->set($file, { type => $fio->mode2type($f), mode => $mode, uid => $f->{uid}, gid => $f->{gid}, size => $placeHolder ? -1 : $f->{size}, mtime => $f->{mtime}, }); return; } sub attribWrite { my($fio, $d) = @_; my($poolWrite); if ( !defined($d) ) { # # flush all entries (in reverse order) # foreach $d ( sort({$b cmp $a} keys(%{$fio->{attrib}})) ) { $fio->attribWrite($d); } return; } return if ( !defined($fio->{attrib}{$d}) ); # # Set deleted files in the attributes. Any file in the view # that doesn't have attributes is flagged as deleted for # incremental dumps. All files sent by rsync have attributes # temporarily set so we can do deletion detection. We also # prune these temporary attributes. # if ( $d ne "" ) { my $dir; my $share; $dir = $1 if ( $d =~ m{.+?/(.*)}s ); $fio->viewCacheDir(undef, $dir); ##print("attribWrite $d,$dir\n"); ##$Data::Dumper::Indent = 1; ##$fio->log("attribWrite $d,$dir"); ##$fio->log("viewCacheLogKeys = ", keys(%{$fio->{viewCache}})); ##$fio->log("attribKeys = ", keys(%{$fio->{attrib}})); ##print "viewCache = ", Dumper($fio->{attrib}); ##print "attrib = ", Dumper($fio->{attrib}); if ( defined($fio->{viewCache}{$d}) ) { foreach my $f ( keys(%{$fio->{viewCache}{$d}}) ) { my $name = $f; $name = "$1/$name" if ( $d =~ m{.*?/(.*)}s ); if ( defined(my $a = $fio->{attrib}{$d}->get($f)) ) { # # delete temporary attributes (skipped files) # if ( $a->{size} < 0 ) { $fio->{attrib}{$d}->set($f, undef); $fio->logFileAction("skip", { %{$fio->{viewCache}{$d}{$f}}, name => $name, }) if ( $fio->{logLevel} >= 2 && $a->{type} == BPC_FTYPE_FILE ); } } elsif ( $fio->{phase} == 0 && !$fio->{full} ) { ##print("Delete file $f\n"); $fio->logFileAction("delete", { %{$fio->{viewCache}{$d}{$f}}, name => $name, }) if ( $fio->{logLevel} >= 1 ); $fio->{attrib}{$d}->set($f, { type => BPC_FTYPE_DELETED, mode => 0, uid => 0, gid => 0, size => 0, mtime => 0, }); } } } } if ( $fio->{attrib}{$d}->fileCount || $fio->{phase} > 0 ) { my $data = $fio->{attrib}{$d}->writeData; my $dirM = $d; $dirM = $1 . "/" . $fio->{bpc}->fileNameMangle($2) if ( $dirM =~ m{(.*?)/(.*)}s ); my $fileName = $fio->{attrib}{$d}->fileName("$fio->{outDir}$dirM"); $fio->log("attribWrite(dir=$d) -> $fileName") if ( $fio->{logLevel} >= 4 ); my $poolWrite = BackupPC::PoolWrite->new($fio->{bpc}, $fileName, length($data), $fio->{xfer}{compress}); $poolWrite->write(\$data); $fio->processClose($poolWrite, $fio->{attrib}{$d}->fileName($dirM), length($data), 0); } delete($fio->{attrib}{$d}); } sub processClose { my($fio, $poolWrite, $fileName, $origSize, $doStats) = @_; my($exists, $digest, $outSize, $errs) = $poolWrite->close; $fileName =~ s{^/+}{}; if ( defined($errs) && @$errs ) { $fio->log(@$errs); $fio->{stats}{errorCnt} += @$errs; } if ( $doStats ) { $fio->{stats}{TotalFileCnt}++; $fio->{stats}{TotalFileSize} += $origSize; } if ( $exists ) { if ( $doStats ) { $fio->{stats}{ExistFileCnt}++; $fio->{stats}{ExistFileSize} += $origSize; $fio->{stats}{ExistFileCompSize} += $outSize; } } elsif ( $outSize > 0 ) { my $fh = $fio->{newFilesFH}; print($fh "$digest $origSize $fileName\n") if ( defined($fh) ); } return $exists && $origSize > 0; } sub statsGet { my($fio) = @_; return $fio->{stats}; } # # Make a given directory. Returns non-zero on error. # sub makePath { my($fio, $f) = @_; my $name = $1 if ( $f->{name} =~ /(.*)/s ); my $path; if ( $name eq "." ) { $path = $fio->{outDirSh}; } else { $path = $fio->{outDirSh} . $fio->{bpc}->fileNameMangle($name); } $fio->logFileAction("create", $f) if ( $fio->{logLevel} >= 1 ); $fio->log("makePath($path, 0777)") if ( $fio->{logLevel} >= 5 ); $path = $1 if ( $path =~ /(.*)/s ); eval { File::Path::mkpath($path, 0, 0777) } if ( !-d $path ); return $fio->attribSet($f) if ( -d $path ); $fio->log("Can't create directory $path"); $fio->{stats}{errorCnt}++; return -1; } # # Make a special file. Returns non-zero on error. # sub makeSpecial { my($fio, $f) = @_; my $name = $1 if ( $f->{name} =~ /(.*)/s ); my $fNameM = $fio->{bpc}->fileNameMangle($name); my $path = $fio->{outDirSh} . $fNameM; my $attr = $fio->attribGet($f); my $str = ""; my $type = $fio->mode2type($f); $fio->log("makeSpecial($path, $type, $f->{mode})") if ( $fio->{logLevel} >= 5 ); if ( $type == BPC_FTYPE_CHARDEV || $type == BPC_FTYPE_BLOCKDEV ) { my($major, $minor, $fh, $fileData); if ( defined($f->{rdev_major}) ) { $major = $f->{rdev_major}; $minor = $f->{rdev_minor}; } else { $major = $f->{rdev} >> 8; $minor = $f->{rdev} & 0xff; } $str = "$major,$minor"; } elsif ( ($f->{mode} & S_IFMT) == S_IFLNK ) { $str = $f->{link}; } elsif ( ($f->{mode} & S_IFMT) == S_IFREG ) { # # this is a hardlink # if ( !defined($f->{hlink}) ) { $fio->log("Error: makeSpecial($path, $type, $f->{mode}) called" . " on a regular non-hardlink file"); return 1; } $str = $f->{hlink}; } # # Now see if the file is different, or this is a full, in which # case we create the new file. # my($fh, $fileData); if ( $fio->{full} || !defined($attr) || $attr->{type} != $type || $attr->{mtime} != $f->{mtime} || $attr->{size} != $f->{size} || $attr->{uid} != $f->{uid} || $attr->{gid} != $f->{gid} || $attr->{mode} != $f->{mode} || $attr->{hlink_self} != $f->{hlink_self} || !defined($fh = BackupPC::FileZIO->open($attr->{fullPath}, 0, $attr->{compress})) || $fh->read(\$fileData, length($str) + 1) != length($str) || $fileData ne $str ) { $fh->close if ( defined($fh) ); $fh = BackupPC::PoolWrite->new($fio->{bpc}, $path, length($str), $fio->{xfer}{compress}); $fh->write(\$str); my $exist = $fio->processClose($fh, "$fio->{shareM}/$fNameM", length($str), 1); $fio->logFileAction($exist ? "pool" : "create", $f) if ( $fio->{logLevel} >= 1 ); return $fio->attribSet($f); } else { $fio->logFileAction("skip", $f) if ( $fio->{logLevel} >= 2 ); } $fh->close if ( defined($fh) ); } # # Make a hardlink. Returns non-zero on error. # This actually gets called twice for each hardlink. # Once as the file list is processed, and again at # the end. BackupPC does them as it goes (since it is # just saving the hardlink info and not actually making # hardlinks). # sub makeHardLink { my($fio, $f, $end) = @_; return if ( $end ); return $fio->makeSpecial($f) if ( !$f->{hlink_self} ); } sub unlink { my($fio, $path) = @_; $fio->log("Unexpected call BackupPC::Xfer::RsyncFileIO->unlink($path)"); } # # Default log handler # sub logHandler { my($str) = @_; print(STDERR $str, "\n"); } # # Handle one or more log messages # sub log { my($fio, @logStr) = @_; foreach my $str ( @logStr ) { next if ( $str eq "" ); $fio->{logHandler}($str); } } # # Generate a log file message for a completed file # sub logFileAction { my($fio, $action, $f) = @_; my $owner = "$f->{uid}/$f->{gid}"; my $type = (("", "p", "c", "", "d", "", "b", "", "", "", "l", "", "s")) [($f->{mode} & S_IFMT) >> 12]; my $name = $f->{name}; if ( ($f->{mode} & S_IFMT) == S_IFLNK ) { $name .= " -> $f->{link}"; } elsif ( ($f->{mode} & S_IFMT) == S_IFREG && defined($f->{hlink}) && !$f->{hlink_self} ) { $name .= " -> $f->{hlink}"; } $name =~ s/\n/\\n/g; $fio->log(sprintf(" %-6s %1s%4o %9s %11.0f %s", $action, $type, $f->{mode} & 07777, $owner, $f->{size}, $name)); } # # If there is a partial and we are doing a full, we do an incremental # against the partial and a full against the rest. This subroutine # is how we tell File::RsyncP which files to ignore attributes on # (ie: against the partial dump we do consider the attributes, but # otherwise we ignore attributes). # sub ignoreAttrOnFile { my($fio, $f) = @_; return if ( !defined($fio->{partialNum}) ); my($attr, $isPartial) = $fio->attribGetWhere($f); $fio->log("$f->{name}: just checking attributes from partial") if ( $isPartial && $fio->{logLevel} >= 5 ); return !$isPartial; } # # This is called by File::RsyncP when a file is skipped because the # attributes match. # sub attrSkippedFile { my($fio, $f, $attr) = @_; # # Unless this is a partial, this is normal so ignore it. # return if ( !defined($fio->{partialNum}) ); $fio->log("$f->{name}: skipped in partial; adding link") if ( $fio->{logLevel} >= 5 ); $fio->{rxLocalAttr} = $attr; $fio->{rxFile} = $f; $fio->{rxSize} = $attr->{size}; delete($fio->{rxInFd}); delete($fio->{rxOutFd}); delete($fio->{rxDigest}); delete($fio->{rxInData}); return $fio->fileDeltaRxDone(); } # # Start receive of file deltas for a particular file. # sub fileDeltaRxStart { my($fio, $f, $cnt, $size, $remainder) = @_; $fio->{rxFile} = $f; # remote file attributes $fio->{rxLocalAttr} = $fio->attribGet($f); # local file attributes $fio->{rxBlkCnt} = $cnt; # how many blocks we will receive $fio->{rxBlkSize} = $size; # block size $fio->{rxRemainder} = $remainder; # size of the last block $fio->{rxMatchBlk} = 0; # current start of match $fio->{rxMatchNext} = 0; # current next block of match $fio->{rxSize} = 0; # size of received file my $rxSize = $cnt > 0 ? ($cnt - 1) * $size + $remainder : 0; if ( $fio->{rxFile}{size} != $rxSize ) { $fio->{rxMatchBlk} = undef; # size different, so no file match $fio->log("$fio->{rxFile}{name}: size doesn't match" . " ($fio->{rxFile}{size} vs $rxSize)") if ( $fio->{logLevel} >= 5 ); } # # If compression was off and now on, or on and now off, then # don't do an exact match. # if ( defined($fio->{rxLocalAttr}) && !$fio->{rxLocalAttr}{compress} != !$fio->{xfer}{compress} ) { $fio->{rxMatchBlk} = undef; # compression changed, so no file match $fio->log("$fio->{rxFile}{name}: compression changed, so no match" . " ($fio->{rxLocalAttr}{compress} vs $fio->{xfer}{compress})") if ( $fio->{logLevel} >= 4 ); } # # If the local file is a hardlink then no match # if ( defined($fio->{rxLocalAttr}) && $fio->{rxLocalAttr}{type} == BPC_FTYPE_HARDLINK ) { $fio->{rxMatchBlk} = undef; $fio->log("$fio->{rxFile}{name}: no match on hardlinks") if ( $fio->{logLevel} >= 4 ); my $fCopy; # need to copy since hardlink attribGet overwrites the name %{$fCopy} = %$f; $fio->{rxHLinkAttr} = $fio->attribGet($fCopy, 1); # hardlink attributes } else { delete($fio->{rxHLinkAttr}); } delete($fio->{rxInFd}); delete($fio->{rxOutFd}); delete($fio->{rxDigest}); delete($fio->{rxInData}); } # # Process the next file delta for the current file. Returns 0 if ok, # -1 if not. Must be called with either a block number, $blk, or new data, # $newData, (not both) defined. # sub fileDeltaRxNext { my($fio, $blk, $newData) = @_; if ( defined($blk) ) { if ( defined($fio->{rxMatchBlk}) && $fio->{rxMatchNext} == $blk ) { # # got the next block in order; just keep track. # $fio->{rxMatchNext}++; return; } } my $newDataLen = length($newData); $fio->log("$fio->{rxFile}{name}: blk=$blk, newData=$newDataLen, rxMatchBlk=$fio->{rxMatchBlk}, rxMatchNext=$fio->{rxMatchNext}") if ( $fio->{logLevel} >= 8 ); if ( !defined($fio->{rxOutFd}) ) { # # maybe the file has no changes # if ( $fio->{rxMatchNext} == $fio->{rxBlkCnt} && !defined($blk) && !defined($newData) ) { #$fio->log("$fio->{rxFile}{name}: file is unchanged"); # if ( $fio->{logLevel} >= 8 ); return; } # # need to open an output file where we will build the # new version. # $fio->{rxFile}{name} =~ /(.*)/s; my $rxOutFileRel = "$fio->{shareM}/" . $fio->{bpc}->fileNameMangle($1); my $rxOutFile = $fio->{outDir} . $rxOutFileRel; $fio->{rxOutFd} = BackupPC::PoolWrite->new($fio->{bpc}, $rxOutFile, $fio->{rxFile}{size}, $fio->{xfer}{compress}); $fio->log("$fio->{rxFile}{name}: opening output file $rxOutFile") if ( $fio->{logLevel} >= 9 ); $fio->{rxOutFile} = $rxOutFile; $fio->{rxOutFileRel} = $rxOutFileRel; $fio->{rxDigest} = File::RsyncP::Digest->new(); $fio->{rxDigest}->protocol($fio->{protocol_version}); $fio->{rxDigest}->add(pack("V", $fio->{checksumSeed})); } if ( defined($fio->{rxMatchBlk}) && $fio->{rxMatchBlk} != $fio->{rxMatchNext} ) { # # Need to copy the sequence of blocks that matched. If the file # is compressed we need to make a copy of the uncompressed file, # since the compressed file is not seekable. Future optimizations # could include only creating an uncompressed copy if the matching # blocks were not monotonic, and to only do this if there are # matching blocks (eg, maybe the entire file is new). # my $attr = $fio->{rxLocalAttr}; my $fh; if ( !defined($fio->{rxInFd}) && !defined($fio->{rxInData}) ) { my $inPath = $attr->{fullPath}; $inPath = $fio->{rxHLinkAttr}{fullPath} if ( defined($fio->{rxHLinkAttr}) ); if ( $attr->{compress} ) { if ( !defined($fh = BackupPC::FileZIO->open( $inPath, 0, $attr->{compress})) ) { $fio->log("Can't open $inPath"); $fio->{stats}{errorCnt}++; return -1; } if ( $attr->{size} < 16 * 1024 * 1024 ) { # # Cache the entire old file if it is less than 16MB # my $data; $fio->{rxInData} = ""; while ( $fh->read(\$data, 16 * 1024 * 1024) > 0 ) { $fio->{rxInData} .= $data; } $fio->log("$attr->{fullPath}: cached all $attr->{size}" . " bytes") if ( $fio->{logLevel} >= 9 ); } else { # # Create and write a temporary output file # unlink("$fio->{outDirSh}RStmp") if ( -f "$fio->{outDirSh}RStmp" ); if ( open(F, "+>", "$fio->{outDirSh}RStmp") ) { my $data; my $byteCnt = 0; binmode(F); while ( $fh->read(\$data, 1024 * 1024) > 0 ) { if ( syswrite(F, $data) != length($data) ) { $fio->log(sprintf("Can't write len=%d to %s", length($data) , "$fio->{outDirSh}RStmp")); $fh->close; $fio->{stats}{errorCnt}++; return -1; } $byteCnt += length($data); } $fio->{rxInFd} = *F; $fio->{rxInName} = "$fio->{outDirSh}RStmp"; sysseek($fio->{rxInFd}, 0, 0); $fio->log("$attr->{fullPath}: copied $byteCnt," . "$attr->{size} bytes to $fio->{rxInName}") if ( $fio->{logLevel} >= 9 ); } else { $fio->log("Unable to open $fio->{outDirSh}RStmp"); $fh->close; $fio->{stats}{errorCnt}++; return -1; } } $fh->close; } else { if ( open(F, "<", $inPath) ) { binmode(F); $fio->{rxInFd} = *F; $fio->{rxInName} = $attr->{fullPath}; } else { $fio->log("Unable to open $inPath"); $fio->{stats}{errorCnt}++; return -1; } } } my $lastBlk = $fio->{rxMatchNext} - 1; $fio->log("$fio->{rxFile}{name}: writing blocks $fio->{rxMatchBlk}.." . "$lastBlk") if ( $fio->{logLevel} >= 9 ); my $seekPosn = $fio->{rxMatchBlk} * $fio->{rxBlkSize}; if ( defined($fio->{rxInFd}) && !sysseek($fio->{rxInFd}, $seekPosn, 0) ) { $fio->log("Unable to seek $fio->{rxInName} to $seekPosn"); $fio->{stats}{errorCnt}++; return -1; } my $cnt = $fio->{rxMatchNext} - $fio->{rxMatchBlk}; my($thisCnt, $len, $data); for ( my $i = 0 ; $i < $cnt ; $i += $thisCnt ) { $thisCnt = $cnt - $i; $thisCnt = 512 if ( $thisCnt > 512 ); if ( $fio->{rxMatchBlk} + $i + $thisCnt == $fio->{rxBlkCnt} ) { $len = ($thisCnt - 1) * $fio->{rxBlkSize} + $fio->{rxRemainder}; } else { $len = $thisCnt * $fio->{rxBlkSize}; } if ( defined($fio->{rxInData}) ) { $data = substr($fio->{rxInData}, $seekPosn, $len); $seekPosn += $len; } else { my $got = sysread($fio->{rxInFd}, $data, $len); if ( $got != $len ) { my $inFileSize = -s $fio->{rxInName}; $fio->log("Unable to read $len bytes from $fio->{rxInName}" . " got=$got, seekPosn=$seekPosn" . " ($i,$thisCnt,$fio->{rxBlkCnt},$inFileSize" . ",$attr->{size})"); $fio->{stats}{errorCnt}++; return -1; } $seekPosn += $len; } $fio->{rxOutFd}->write(\$data); $fio->{rxDigest}->add($data); $fio->{rxSize} += length($data); } $fio->{rxMatchBlk} = undef; } if ( defined($blk) ) { # # Remember the new block number # $fio->{rxMatchBlk} = $blk; $fio->{rxMatchNext} = $blk + 1; } if ( defined($newData) ) { # # Write the new chunk # my $len = length($newData); $fio->log("$fio->{rxFile}{name}: writing $len bytes new data") if ( $fio->{logLevel} >= 9 ); $fio->{rxOutFd}->write(\$newData); $fio->{rxDigest}->add($newData); $fio->{rxSize} += length($newData); } } # # Finish up the current receive file. Returns undef if ok, -1 if not. # Returns 1 if the md4 digest doesn't match. # sub fileDeltaRxDone { my($fio, $md4, $phase) = @_; my $name = $1 if ( $fio->{rxFile}{name} =~ /(.*)/s ); my $ret; close($fio->{rxInFd}) if ( defined($fio->{rxInFd}) ); unlink("$fio->{outDirSh}RStmp") if ( -f "$fio->{outDirSh}RStmp" ); $fio->{phase} = $phase; # # Check the final md4 digest # if ( defined($md4) ) { my $newDigest; if ( !defined($fio->{rxDigest}) ) { # # File was exact match, but we still need to verify the # MD4 checksum. Compute the md4 digest (or fetch the # cached one.) # if ( defined(my $attr = $fio->{rxLocalAttr}) ) { # # block size doesn't matter: we're only going to # fetch the md4 file digest, not the block digests. # my($err, $csum, $blkSize) = BackupPC::Xfer::RsyncDigest->digestStart( $attr->{fullPath}, $attr->{size}, 0, 2048, $fio->{checksumSeed}, 1, $attr->{compress}, 1, $fio->{protocol_version}); if ( $err ) { $fio->log("Can't open $attr->{fullPath} for MD4" . " check (err=$err, $name)"); $fio->{stats}{errorCnt}++; } else { if ( $fio->{logLevel} >= 5 ) { my($isCached, $invalid) = $csum->isCached; $fio->log("MD4 $attr->{fullPath} cache = $isCached," . " invalid = $invalid"); } $newDigest = $csum->digestEnd; } $fio->{rxSize} = $attr->{size}; } else { # # Empty file; just create an empty file digest # $fio->{rxDigest} = File::RsyncP::Digest->new(); $fio->{rxDigest}->protocol($fio->{protocol_version}); $fio->{rxDigest}->add(pack("V", $fio->{checksumSeed})); $newDigest = $fio->{rxDigest}->digest; } $fio->log("$name got exact match") if ( $fio->{logLevel} >= 5 ); } else { $newDigest = $fio->{rxDigest}->digest; } if ( $fio->{logLevel} >= 3 ) { my $md4Str = unpack("H*", $md4); my $newStr = unpack("H*", $newDigest); $fio->log("$name got digests $md4Str vs $newStr") } if ( $md4 ne $newDigest ) { if ( $phase > 0 ) { $fio->log("$name: fatal error: md4 doesn't match on retry;" . " file removed"); $fio->{stats}{errorCnt}++; } else { $fio->log("$name: md4 doesn't match: will retry in phase 1;" . " file removed"); } if ( defined($fio->{rxOutFd}) ) { $fio->{rxOutFd}->close; unlink($fio->{rxOutFile}); } delete($fio->{rxFile}); delete($fio->{rxOutFile}); return 1; } } # # One special case is an empty file: if the file size is # zero we need to open the output file to create it. # if ( $fio->{rxSize} == 0 ) { my $rxOutFileRel = "$fio->{shareM}/" . $fio->{bpc}->fileNameMangle($name); my $rxOutFile = $fio->{outDir} . $rxOutFileRel; $fio->{rxOutFd} = BackupPC::PoolWrite->new($fio->{bpc}, $rxOutFile, $fio->{rxSize}, $fio->{xfer}{compress}); } if ( !defined($fio->{rxOutFd}) ) { # # No output file, meaning original was an exact match. # $fio->log("$name: nothing to do") if ( $fio->{logLevel} >= 5 ); my $attr = $fio->{rxLocalAttr}; my $f = $fio->{rxFile}; $fio->logFileAction("same", $f) if ( $fio->{logLevel} >= 1 ); if ( $fio->{full} || $attr->{type} != $f->{type} || $attr->{mtime} != $f->{mtime} || $attr->{size} != $f->{size} || $attr->{uid} != $f->{uid} || $attr->{gid} != $f->{gid} || $attr->{mode} != $f->{mode} || $attr->{hlink_self} != $f->{hlink_self} ) { # # In the full case, or if the attributes are different, # we need to make a link from the previous file and # set the attributes. # my $rxOutFile = $fio->{outDirSh} . $fio->{bpc}->fileNameMangle($name); my($exists, $digest, $origSize, $outSize, $errs) = BackupPC::PoolWrite::LinkOrCopy( $fio->{bpc}, $attr->{fullPath}, $attr->{compress}, $rxOutFile, $fio->{xfer}{compress}); # # Cumulate the stats # $fio->{stats}{TotalFileCnt}++; $fio->{stats}{TotalFileSize} += $fio->{rxSize}; $fio->{stats}{ExistFileCnt}++; $fio->{stats}{ExistFileSize} += $fio->{rxSize}; $fio->{stats}{ExistFileCompSize} += -s $rxOutFile; $fio->{rxFile}{size} = $fio->{rxSize}; $ret = $fio->attribSet($fio->{rxFile}); $fio->log(@$errs) if ( defined($errs) && @$errs ); if ( !$exists && $outSize > 0 ) { # # the hard link failed, most likely because the target # file has too many links. We have copied the file # instead, so add this to the new file list. # my $rxOutFileRel = "$fio->{shareM}/" . $fio->{bpc}->fileNameMangle($name); $rxOutFileRel =~ s{^/+}{}; my $fh = $fio->{newFilesFH}; print($fh "$digest $origSize $rxOutFileRel\n") if ( defined($fh) ); } } } else { my $exist = $fio->processClose($fio->{rxOutFd}, $fio->{rxOutFileRel}, $fio->{rxSize}, 1); $fio->logFileAction($exist ? "pool" : "create", $fio->{rxFile}) if ( $fio->{logLevel} >= 1 ); $fio->{rxFile}{size} = $fio->{rxSize}; $ret = $fio->attribSet($fio->{rxFile}); } delete($fio->{rxDigest}); delete($fio->{rxInData}); delete($fio->{rxFile}); delete($fio->{rxOutFile}); return $ret; } # # Callback function for BackupPC::View->find. Note the order of the # first two arguments. # sub fileListEltSend { my($a, $fio, $fList, $outputFunc) = @_; my $name = $a->{relPath}; my $n = $name; my $type = $a->{type}; my $extraAttribs = {}; if ( $a->{mode} & S_HLINK_TARGET ) { $a->{hlink_self} = 1; $a->{mode} &= ~S_HLINK_TARGET; } $n =~ s/^\Q$fio->{xfer}{pathHdrSrc}//; $fio->log("Sending $name (remote=$n) type = $type") if ( $fio->{logLevel} >= 1 ); if ( $type == BPC_FTYPE_CHARDEV || $type == BPC_FTYPE_BLOCKDEV || $type == BPC_FTYPE_SYMLINK ) { my $fh = BackupPC::FileZIO->open($a->{fullPath}, 0, $a->{compress}); my($str, $rdSize); if ( defined($fh) ) { $rdSize = $fh->read(\$str, $a->{size} + 1024); if ( $type == BPC_FTYPE_SYMLINK ) { # # Reconstruct symbolic link # $extraAttribs = { link => $str }; if ( $rdSize != $a->{size} ) { # ERROR $fio->log("$name: can't read exactly $a->{size} bytes"); $fio->{stats}{errorCnt}++; } } elsif ( $str =~ /(\d*),(\d*)/ ) { # # Reconstruct char or block special major/minor device num # # Note: char/block devices have $a->{size} = 0, so we # can't do an error check on $rdSize. # $extraAttribs = { rdev => $1 * 256 + $2, rdev_major => $1, rdev_minor => $2, }; } else { $fio->log("$name: unexpected special file contents $str"); $fio->{stats}{errorCnt}++; } $fh->close; } else { # ERROR $fio->log("$name: can't open"); $fio->{stats}{errorCnt}++; } } elsif ( $fio->{preserve_hard_links} && ($type == BPC_FTYPE_HARDLINK || $type == BPC_FTYPE_FILE) && ($type == BPC_FTYPE_HARDLINK || $fio->{protocol_version} < 27 || $a->{hlink_self}) ) { # # Fill in fake inode information so that the remote rsync # can correctly create hardlinks. # $name =~ s/^\.?\/+//; my($target, $inode); if ( $type == BPC_FTYPE_HARDLINK ) { my $fh = BackupPC::FileZIO->open($a->{fullPath}, 0, $a->{compress}); if ( defined($fh) ) { $fh->read(\$target, 65536); $fh->close; $target =~ s/^\.?\/+//; if ( defined($fio->{hlinkFile2Num}{$target}) ) { $inode = $fio->{hlinkFile2Num}{$target}; } else { $inode = $fio->{fileListCnt}; $fio->{hlinkFile2Num}{$target} = $inode; } } else { $fio->log("$a->{fullPath}: can't open for hardlink"); $fio->{stats}{errorCnt}++; } } elsif ( $a->{hlink_self} ) { if ( defined($fio->{hlinkFile2Num}{$name}) ) { $inode = $fio->{hlinkFile2Num}{$name}; } else { $inode = $fio->{fileListCnt}; $fio->{hlinkFile2Num}{$name} = $inode; } } $inode = $fio->{fileListCnt} if ( !defined($inode) ); $fio->log("$name: setting inode to $inode"); $extraAttribs = { %$extraAttribs, dev => 0, inode => $inode, }; } my $f = { name => $n, mode => $a->{mode} & ~S_HLINK_TARGET, uid => $a->{uid}, gid => $a->{gid}, mtime => $a->{mtime}, size => $a->{size}, %$extraAttribs, }; my $logName = $f->{name}; from_to($f->{name}, "utf8", $fio->{clientCharset}) if ( $fio->{clientCharset} ne "" ); $fList->encode($f); $logName = "$fio->{xfer}{pathHdrDest}/$logName"; $logName =~ s{//+}{/}g; $f->{name} = $logName; $fio->logFileAction("restore", $f) if ( $fio->{logLevel} >= 1 ); &$outputFunc($fList->encodeData); # # Cumulate stats # $fio->{fileListCnt}++; if ( $type != BPC_FTYPE_DIR ) { $fio->{stats}{TotalFileCnt}++; $fio->{stats}{TotalFileSize} += $a->{size}; } } sub fileListSend { my($fio, $flist, $outputFunc) = @_; # # Populate the file list with the files requested by the user. # Since some might be directories so we call BackupPC::View::find. # $fio->log("fileListSend: sending file list: " . join(" ", @{$fio->{fileList}})) if ( $fio->{logLevel} >= 4 ); $fio->{fileListCnt} = 0; $fio->{hlinkFile2Num} = {}; foreach my $name ( @{$fio->{fileList}} ) { $fio->{view}->find($fio->{xfer}{bkupSrcNum}, $fio->{xfer}{bkupSrcShare}, $name, 1, \&fileListEltSend, $fio, $flist, $outputFunc); } } sub finish { my($fio, $isChild) = @_; # # If we are aborting early, remove the last file since # it was not complete # if ( $isChild && defined($fio->{rxFile}) ) { unlink("$fio->{outDirSh}RStmp") if ( -f "$fio->{outDirSh}RStmp" ); if ( defined($fio->{rxFile}) ) { unlink($fio->{rxOutFile}); $fio->log("finish: removing in-process file $fio->{rxFile}{name}"); } } # # Flush the attributes if this is the child # $fio->attribWrite(undef) if ( $isChild ); } #sub is_tainted #{ # return ! eval { # join('',@_), kill 0; # 1; # }; #} 1; BackupPC-3.3.2/lib/BackupPC/Xfer/Smb.pm0000444000076500000240000003115213042250554016300 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Xfer::Smb package # # DESCRIPTION # # This library defines a BackupPC::Xfer::Smb class for managing # the SMB (smbclient) transport of backup data from the client. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Xfer::Smb; use strict; use Encode qw/from_to encode/; use base qw(BackupPC::Xfer::Protocol); sub useTar { return 1; } sub start { my($t) = @_; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my $I_option = $t->{hostIP} eq $t->{host} ? [] : ['-I', $t->{hostIP}]; my(@fileList, $X_option, $smbClientCmd, $logMsg); my($timeStampFile); local(*SMB); # # First propagate the PASSWD setting # $ENV{PASSWD} = $ENV{BPC_SMB_PASSWD} if ( defined($ENV{BPC_SMB_PASSWD}) ); $ENV{PASSWD} = $conf->{SmbSharePasswd} if ( defined($conf->{SmbSharePasswd}) ); if ( !defined($ENV{PASSWD}) ) { $t->{_errStr} = "passwd not set for smbclient"; return; } if ( !defined($conf->{SmbClientPath}) || !-x $conf->{SmbClientPath} ) { $t->{_errStr} = '$Conf{SmbClientPath} is not a valid executable'; return; } if ( $t->{type} eq "restore" ) { $smbClientCmd = $conf->{SmbClientRestoreCmd}; $logMsg = "restore started for share $t->{shareName}"; } else { # # Turn $conf->{BackupFilesOnly} and $conf->{BackupFilesExclude} # into a hash of arrays of files, and $conf->{SmbShareName} # to an array # $bpc->backupFileConfFix($conf, "SmbShareName"); $t->{fileIncludeHash} = {}; if ( defined($conf->{BackupFilesOnly}{$t->{shareName}}) ) { foreach my $file ( @{$conf->{BackupFilesOnly}{$t->{shareName}}} ) { $file = encode($conf->{ClientCharset}, $file) if ( $conf->{ClientCharset} ne "" ); push(@fileList, $file); $t->{fileIncludeHash}{$file} = 1; } } elsif ( defined($conf->{BackupFilesExclude}{$t->{shareName}}) ) { foreach my $file ( @{$conf->{BackupFilesExclude}{$t->{shareName}}} ) { $file = encode($conf->{ClientCharset}, $file) if ( $conf->{ClientCharset} ne "" ); push(@fileList, $file); } # # Allow simple wildcards in exclude list by specifying "r" option. # $X_option = "rX"; } if ( $t->{type} eq "full" ) { $smbClientCmd = $conf->{SmbClientFullCmd}; $logMsg = "full backup started for share $t->{shareName}"; } else { $timeStampFile = "$t->{outDir}/timeStamp.level0"; open(LEV0, ">", $timeStampFile) && close(LEV0); utime($t->{incrBaseTime} - 3600, $t->{incrBaseTime} - 3600, $timeStampFile); $smbClientCmd = $conf->{SmbClientIncrCmd}; $logMsg = "incr backup started back to " . $bpc->timeStamp($t->{incrBaseTime} - 3600, 0) . " (backup #$t->{incrBaseBkupNum}) for share" . " $t->{shareName}"; } } my $args = { smbClientPath => $conf->{SmbClientPath}, host => $t->{host}, hostIP => $t->{hostIP}, client => $t->{client}, shareName => $t->{shareName}, userName => $conf->{SmbShareUserName}, fileList => \@fileList, I_option => $I_option, X_option => $X_option, timeStampFile => $timeStampFile, }; from_to($args->{shareName}, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); $smbClientCmd = $bpc->cmdVarSubstitute($smbClientCmd, $args); if ( !defined($t->{xferPid} = open(SMB, "-|")) ) { $t->{_errStr} = "Can't fork to run smbclient"; return; } $t->{pipeSMB} = *SMB; if ( !$t->{xferPid} ) { # # This is the smbclient child. # setpgrp 0,0; if ( $t->{type} eq "restore" ) { # # For restores close the write end of the pipe, # clone STDIN from RH, and STDERR to STDOUT # close($t->{pipeWH}); close(STDERR); open(STDERR, ">&STDOUT"); close(STDIN); open(STDIN, "<&$t->{pipeRH}"); } else { # # For backups close the read end of the pipe, # clone STDOUT to WH, STDERR to STDOUT # close($t->{pipeRH}); close(STDERR); open(STDERR, ">&STDOUT"); open(STDOUT, ">&$t->{pipeWH}"); } # # Run smbclient. # alarm(0); $bpc->cmdExecOrEval($smbClientCmd, $args); # should not be reached, but just in case... $t->{_errStr} = "Can't exec $conf->{SmbClientPath}"; return; } my $str = "Running: " . $bpc->execCmd2ShellCmd(@$smbClientCmd) . "\n"; from_to($str, $conf->{ClientCharset}, "utf8") if ( $conf->{ClientCharset} ne "" ); $t->{XferLOG}->write(\$str); alarm($conf->{ClientTimeout}); $t->{_errStr} = undef; return $logMsg; } sub readOutput { my($t, $FDreadRef, $rout) = @_; my $conf = $t->{conf}; if ( vec($rout, fileno($t->{pipeSMB}), 1) ) { my $mesg; if ( sysread($t->{pipeSMB}, $mesg, 8192) <= 0 ) { vec($$FDreadRef, fileno($t->{pipeSMB}), 1) = 0; close($t->{pipeSMB}); } else { $t->{smbOut} .= $mesg; } } while ( $t->{smbOut} =~ /(.*?)[\n\r]+(.*)/s ) { $_ = $1; $t->{smbOut} = $2; # # ignore the log file time stamps from smbclient introduced # in version 3.0.0 - don't even write them to the log file. # if ( m{^\[\d+/\d+/\d+ +\d+:\d+:\d+.*\] +(client/cli|lib/util_unistr).*\(\d+\)} ) { $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 5 ); next; } # # refresh our inactivity alarm # alarm($conf->{ClientTimeout}) if ( !$t->{abort} ); $t->{lastOutputLine} = $_ if ( !/^$/ ); from_to($_, $conf->{ClientCharset}, "utf8") if ( $conf->{ClientCharset} ne "" ); # # This section is highly dependent on the version of smbclient. # If you upgrade Samba, make sure that these regexp are still valid. # # MAKSYM 14082016: The next regex will never match on Samba-4.3, as # smbclient doesn't produce output required; keeping it for older Sambas if ( /^\s*(-?\d+) \(\s*\d+[.,]\d kb\/s\) (.*)$/ ) { my $sambaFileSize = $1; my $pcFileName = $2; (my $fileName = $pcFileName) =~ s/\\/\//g; $sambaFileSize += 1024 * 1024 * 4096 if ( $sambaFileSize < 0 ); $fileName =~ s/^\/*//; $t->{byteCnt} += $sambaFileSize; $t->{fileCnt}++; $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 2 ); } elsif ( /restore tar file (.*) of size (\d+) bytes/ ) { $t->{byteCnt} += $2; $t->{fileCnt}++; $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 ); } elsif ( /^\s*tar: dumped (\d+) files/) { # MAKSYM 14082016: Updating file count to the likely number $t->{xferOK} = 1; $t->{fileCnt} = $1; $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^\s*tar:\d+\s*Total bytes received: (\d+)/) { # MAKSYM 14082016: Updating byte count to the likely number $t->{xferOK} = 1; $t->{byteCnt} = $1; $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^\s*tar: restored \d+ files/ ) { $t->{xferOK} = 1; $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^\s*read_socket_with_timeout: timeout read. /i ) { $t->{hostAbort} = 1; $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^code 0 listing / || /^\s*code 0 opening / || /^\s*abandoning restore/i || /^\s*Error: Looping in FIND_NEXT/i || /^\s*SUCCESS - 0/i || /^\s*Call timed out: server did not respond/i || /^\s*tree connect failed: ERRDOS - ERRnoaccess \(Access denied\.\)/ || /^\s*tree connect failed: NT_STATUS_BAD_NETWORK_NAME/ || /^\s*NT_STATUS_INSUFF_SERVER_RESOURCES listing / ) { if ( $t->{hostError} eq "" ) { $t->{XferLOG}->write(\"This backup will fail because: $_\n"); $t->{hostError} = $_; } $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^\s*NT_STATUS_ACCESS_DENIED listing (.*)/ || /^\s*ERRDOS - ERRnoaccess \(Access denied\.\) listing (.*)/ ) { $t->{xferErrCnt}++; my $badDir = $1; $badDir =~ s{\\}{/}g; $badDir =~ s{/+}{/}g; $badDir =~ s{/\*$}{}; if ( $t->{hostError} eq "" && ($badDir eq "" || $t->{fileIncludeHash}{$badDir}) ) { $t->{XferLOG}->write(\"This backup will fail because: $_\n"); $t->{hostError} ||= $_; } $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^\s*directory \\/i ) { $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 2 ); } elsif ( /smb: \\>/ || /^\s*tar:\d+/ # MAKSYM 14082016: ignoring 2 more Samba-4.3 specific lines || /^\s*WARNING:/i || /^\s*added interface/i || /^\s*tarmode is now/i || /^\s*Total bytes written/i || /^\s*Domain=/i || /^\([\d\.]* kb\/s\) \(average [\d\.]* kb\/s\)$/i || /^\s*Getting files newer than/i || /^\s*restore directory \\/i || /^\s*Output is \/dev\/null/i || /^\s*Timezone is/i || /^\s*tar_re_search set/i || /^\s*creating lame (up|low)case table/i ) { # ignore these messages $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 ); } else { $t->{xferErrCnt}++; $t->{xferBadShareCnt}++ if ( /^ERRDOS - ERRbadshare/ ); $t->{xferBadFileCnt}++ if ( /^ERRDOS - ERRbadfile/ ); if ( $t->{xferErrCnt} > 50000 ) { $t->logMsg( "Too many smbtar errors ($t->{xferErrCnt})... giving up"); $t->{hostError} = "Too many smbtar errors ($t->{xferErrCnt})"; return; } if ( /^Error reading file (.*)\. Got 0 bytes/ ) { # # This happens when a Windoze application has # locked the file. This is a particular problem # with MS-Outlook. smbclient has already written # the tar header to stdout, so all it can do is to # write a dummy file with the correct size, but all # zeros. BackupPC_tarExtract stores these # zero-content files efficiently as a sparse file, # or if compression is on the file will be small # anyhow. After the dump is done we simply delete # the file (it is no use) and try to link it to same # file in any recent backup. # my $badFile = $1; $badFile =~ s{\\}{/}g; $badFile =~ s{^/}{}; push(@{$t->{badFiles}}, { share => $t->{shareName}, file => $badFile }); } $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 ); } } return 1; } sub setSelectMask { my($t, $FDreadRef) = @_; vec($$FDreadRef, fileno($t->{pipeSMB}), 1) = 1; } 1; BackupPC-3.3.2/lib/BackupPC/Xfer/Tar.pm0000444000076500000240000002017113042250554016304 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Xfer::Tar package # # DESCRIPTION # # This library defines a BackupPC::Xfer::Tar class for managing # the tar-based transport of backup data from the client. # # AUTHOR # Craig Barratt # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Xfer::Tar; use strict; use Encode qw/from_to encode/; use base qw(BackupPC::Xfer::Protocol); sub useTar { return 1; } sub start { my($t) = @_; my $bpc = $t->{bpc}; my $conf = $t->{conf}; my(@fileList, $tarClientCmd, $logMsg, $incrDate); local(*TAR); if ( $t->{type} eq "restore" ) { $tarClientCmd = $conf->{TarClientRestoreCmd}; $logMsg = "restore started below directory $t->{shareName}"; # # restores are considered to work unless we see they fail # (opposite to backups...) # $t->{xferOK} = 1; } else { # # Turn $conf->{BackupFilesOnly} and $conf->{BackupFilesExclude} # into a hash of arrays of files, and $conf->{TarShareName} # to an array # $bpc->backupFileConfFix($conf, "TarShareName"); if ( defined($conf->{BackupFilesExclude}{$t->{shareName}}) ) { foreach my $file2 ( @{$conf->{BackupFilesExclude}{$t->{shareName}}} ) { my $file = $file2; $file = "./$2" if ( $file =~ m{^(\./+|/+)(.*)}s ); $file = encode($conf->{ClientCharset}, $file) if ( $conf->{ClientCharset} ne "" ); push(@fileList, "--exclude=$file"); } } if ( defined($conf->{BackupFilesOnly}{$t->{shareName}}) ) { foreach my $file2 ( @{$conf->{BackupFilesOnly}{$t->{shareName}}} ) { my $file = $file2; $file = $2 if ( $file =~ m{^(\./+|/+)(.*)}s ); $file = "./$file"; $file = encode($conf->{ClientCharset}, $file) if ( $conf->{ClientCharset} ne "" ); push(@fileList, $file); } } else { push(@fileList, "."); } if ( ref($conf->{TarClientCmd}) eq "ARRAY" ) { $tarClientCmd = $conf->{TarClientCmd}; } else { $tarClientCmd = [split(/ +/, $conf->{TarClientCmd})]; } my $args; if ( $t->{type} eq "full" ) { $args = $conf->{TarFullArgs}; $logMsg = "full backup started for directory $t->{shareName}"; } else { $incrDate = $bpc->timeStamp($t->{incrBaseTime} - 3600, 1); $args = $conf->{TarIncrArgs}; $logMsg = "incr backup started back to $incrDate" . " (backup #$t->{incrBaseBkupNum}) for directory" . " $t->{shareName}"; } push(@$tarClientCmd, split(/ +/, $args)); } # # Merge variables into @tarClientCmd # my $args = { host => $t->{host}, hostIP => $t->{hostIP}, client => $t->{client}, incrDate => $incrDate, shareName => $t->{shareName}, fileList => \@fileList, tarPath => $conf->{TarClientPath}, sshPath => $conf->{SshPath}, }; from_to($args->{shareName}, "utf8", $conf->{ClientCharset}) if ( $conf->{ClientCharset} ne "" ); $tarClientCmd = $bpc->cmdVarSubstitute($tarClientCmd, $args); if ( !defined($t->{xferPid} = open(TAR, "-|")) ) { $t->{_errStr} = "Can't fork to run tar"; return; } $t->{pipeTar} = *TAR; if ( !$t->{xferPid} ) { # # This is the tar child. # setpgrp 0,0; if ( $t->{type} eq "restore" ) { # # For restores, close the write end of the pipe, # clone STDIN to RH # close($t->{pipeWH}); close(STDERR); open(STDERR, ">&STDOUT"); close(STDIN); open(STDIN, "<&$t->{pipeRH}"); } else { # # For backups, close the read end of the pipe, # clone STDOUT to WH, and STDERR to STDOUT # close($t->{pipeRH}); close(STDERR); open(STDERR, ">&STDOUT"); open(STDOUT, ">&$t->{pipeWH}"); } # # Run the tar command # alarm(0); $bpc->cmdExecOrEval($tarClientCmd, $args); # should not be reached, but just in case... $t->{_errStr} = "Can't exec @$tarClientCmd"; return; } my $str = "Running: " . $bpc->execCmd2ShellCmd(@$tarClientCmd) . "\n"; from_to($str, $conf->{ClientCharset}, "utf8") if ( $conf->{ClientCharset} ne "" ); $t->{XferLOG}->write(\"Running: @$tarClientCmd\n"); alarm($conf->{ClientTimeout}); $t->{_errStr} = undef; return $logMsg; } sub readOutput { my($t, $FDreadRef, $rout) = @_; my $conf = $t->{conf}; if ( vec($rout, fileno($t->{pipeTar}), 1) ) { my $mesg; if ( sysread($t->{pipeTar}, $mesg, 8192) <= 0 ) { vec($$FDreadRef, fileno($t->{pipeTar}), 1) = 0; if ( !close($t->{pipeTar}) && $? != 256 ) { # # Tar 1.16 uses exit status 1 (256) when some files # changed during archive creation. We allow this # as a benign error and consider the archive ok # $t->{tarOut} .= "Tar exited with error $? ($!) status\n"; $t->{xferOK} = 0 if ( !$t->{tarBadExitOk} ); } } else { $t->{tarOut} .= $mesg; } } my $logFileThres = $t->{type} eq "restore" ? 1 : 2; while ( $t->{tarOut} =~ /(.*?)[\n\r]+(.*)/s ) { $_ = $1; $t->{tarOut} = $2; from_to($_, $conf->{ClientCharset}, "utf8") if ( $conf->{ClientCharset} ne "" ); # # refresh our inactivity alarm # alarm($conf->{ClientTimeout}) if ( !$t->{abort} ); $t->{lastOutputLine} = $_ if ( !/^$/ ); if ( /^Total bytes (written|read): / ) { $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 ); $t->{xferOK} = 1; } elsif ( /^\./ ) { $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= $logFileThres ); $t->{fileCnt}++; } else { # # Ignore annoying log message on incremental for tar 1.15.x # if ( !/: file is unchanged; not dumped$/ && !/: socket ignored$/ ) { $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); $t->{xferErrCnt}++; } # # If tar encounters a minor error, it will exit with a non-zero # status. We still consider that ok. Remember if tar prints # this message indicating a non-fatal error. # $t->{tarBadExitOk} = 1 if ( $t->{xferOK} && /Error exit delayed from previous / ); # # Also remember files that had read errors # if ( /: \.\/(.*): Read error at byte / ) { my $badFile = $1; push(@{$t->{badFiles}}, { share => $t->{shareName}, file => $badFile }); } } } return 1; } sub setSelectMask { my($t, $FDreadRef) = @_; vec($$FDreadRef, fileno($t->{pipeTar}), 1) = 1; } 1; BackupPC-3.3.2/lib/BackupPC/Xfer.pm0000444000076500000240000001171613042250554015563 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Xfer package # # DESCRIPTION # # This library defines a Factory for invoking transfer protocols in # a polymorphic manner. This libary allows for easier expansion of # supported protocols. # # AUTHOR # Paul Mantz # # COPYRIGHT # Copyright (C) 2001-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Xfer; use strict; use Encode qw/from_to encode/; use BackupPC::Xfer::Archive; use BackupPC::Xfer::Ftp; use BackupPC::Xfer::Protocol; use BackupPC::Xfer::Rsync; use BackupPC::Xfer::Smb; use BackupPC::Xfer::Tar; use vars qw( $errStr ); sub create { my($protocol, $bpc, $args) = @_; my $xfer; $errStr = undef; if ( $protocol eq 'archive' ) { $xfer = BackupPC::Xfer::Archive->new( $bpc, $args ); $errStr = BackupPC::Xfer::Archive::errStr() if ( !defined($xfer) ); return $xfer; } elsif ( $protocol eq 'ftp' ) { $xfer = BackupPC::Xfer::Ftp->new( $bpc, $args ); $errStr = BackupPC::Xfer::Ftp::errStr() if ( !defined($xfer) ); return $xfer; } elsif ( $protocol eq 'rsync' || $protocol eq 'rsyncd' ) { $xfer = BackupPC::Xfer::Rsync->new( $bpc, $args ); $errStr = BackupPC::Xfer::Rsync::errStr() if ( !defined($xfer) ); return $xfer; } elsif ( $protocol eq 'smb' ) { $xfer = BackupPC::Xfer::Smb->new( $bpc, $args ); $errStr = BackupPC::Xfer::Smb::errStr() if ( !defined($xfer) ); return $xfer; } elsif ( $protocol eq 'tar' ) { $xfer = BackupPC::Xfer::Tar->new( $bpc, $args ); $errStr = BackupPC::Xfer::Tar::errStr() if ( !defined($xfer) ); return $xfer; } elsif ( $protocol eq 'protocol') { $xfer = BackupPC::Xfer::Protocol->new( $bpc, $args ); $errStr = BackupPC::Xfer::Protocol::errStr() if ( !defined($xfer) ); return $xfer; } else { $xfer = undef; $errStr = "$protocol is not a supported protocol."; return $xfer; } } # # getShareNames() loads the correct shares dependent on the # transfer type. # sub getShareNames { my($conf) = @_; my $ShareNames; if ( $conf->{XferMethod} eq "tar" ) { $ShareNames = $conf->{TarShareName}; } elsif ( $conf->{XferMethod} eq "ftp" ) { $ShareNames = $conf->{FtpShareName}; } elsif ( $conf->{XferMethod} eq "rsync" || $conf->{XferMethod} eq "rsyncd" ) { $ShareNames = $conf->{RsyncShareName}; } elsif ( $conf->{XferMethod} eq "smb" ) { $ShareNames = $conf->{SmbShareName}; } else { # # default to smb shares # $ShareNames = $conf->{SmbShareName}; } $ShareNames = [$ShareNames] unless ref($ShareNames) eq "ARRAY"; return $ShareNames; } sub getRestoreCmd { my($conf) = @_; my $restoreCmd; if ( $conf->{XferMethod} eq "archive" ) { $restoreCmd = undef; } elsif ( $conf->{XferMethod} eq "ftp" ) { $restoreCmd = undef; } elsif ( $conf->{XferMethod} eq "rsync" || $conf->{XferMethod} eq "rsyncd" ) { $restoreCmd = $conf->{RsyncRestoreArgs}; } elsif ( $conf->{XferMethod} eq "tar" ) { $restoreCmd = $conf->{TarClientRestoreCmd}; } elsif ( $conf->{XferMethod} eq "smb" ) { $restoreCmd = $conf->{SmbClientRestoreCmd}; } else { # # protocol unrecognized # $restoreCmd = undef; } return $restoreCmd; } sub restoreEnabled { my($conf) = @_; my $restoreCmd; if ( $conf->{XferMethod} eq "archive" ) { return; } elsif ( $conf->{XferMethod} eq "ftp" ) { return; } elsif ( $conf->{XferMethod} eq "rsync" || $conf->{XferMethod} eq "rsyncd" || $conf->{XferMethod} eq "tar" || $conf->{XferMethod} eq "smb" ) { $restoreCmd = getRestoreCmd( $conf ); return !!( ref $restoreCmd eq "ARRAY" ? @$restoreCmd : $restoreCmd ne "" ); } else { return; } } sub errStr { return $errStr; } 1; BackupPC-3.3.2/lib/BackupPC/Zip/0000755000076500000240000000000013042250554015057 5ustar craigstaffBackupPC-3.3.2/lib/BackupPC/Zip/FileMember.pm0000444000076500000240000001102213042250554017416 0ustar craigstaff#============================================================= -*-perl-*- # # BackupPC::Zip::FileMember # # DESCRIPTION # # This library defines a BackupPC::Zip::FileMember class that subclass # the Archive::Zip::FileMember class. This allows BackupPC_zipCreate # to create zip files by reading and uncomressing BackupPC's pool # files on the fly. This avoids the need to uncompress the files # ahead of time and either store them in memory or on disk. # # AUTHOR # Craig Barratt # Based on Archive::Zip::FileMember, Copyright (c) 2000 Ned Konz. # # COPYRIGHT # Copyright (C) 2002-2017 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #======================================================================== # # Version 3.3.2, released 25 Jan 2017. # # See http://backuppc.sourceforge.net. # #======================================================================== package BackupPC::Zip::FileMember; use vars qw( @ISA ); @ISA = qw ( Archive::Zip::FileMember ); BEGIN { use Archive::Zip qw( :CONSTANTS :ERROR_CODES :UTILITY_METHODS ) } # Given a file name, set up for eventual writing. sub newFromFileNamed # BackupPC::Zip::FileMember { my $class = shift; my $fileName = shift; my $newName = shift || $fileName; my $size = shift; my $compress = shift; return undef unless ( stat($fileName) && -r _ && !-d _ ); my $self = $class->new(@_); $self->fileName($newName); $self->{'externalFileName'} = $fileName; $self->{'compressionMethod'} = COMPRESSION_STORED; $self->{'compressedSize'} = $self->{'uncompressedSize'} = $size; $self->{'fileCompressLevel'} = $compress; $self->desiredCompressionMethod( ( $self->compressedSize() > 0 ) ? COMPRESSION_DEFLATED : COMPRESSION_STORED ); $self->isTextFile( -T _ ); return $self; } sub rewindData # BackupPC::Zip::FileMember { my $self = shift; my $status = $self->SUPER::rewindData(@_); return $status unless $status == AZ_OK; return AZ_IO_ERROR unless $self->fh(); $self->fh()->rewind(); return AZ_OK; } sub fh # BackupPC::Zip::FileMember { my $self = shift; $self->_openFile() if !defined( $self->{'bpcfh'} ); return $self->{'bpcfh'}; } # opens my file handle from my file name sub _openFile # BackupPC::Zip::FileMember { my $self = shift; my ( $fh ) = BackupPC::FileZIO->open($self->externalFileName(), 0, $self->{'fileCompressLevel'}); if ( !defined($fh) ) { _ioError( "Can't open", $self->externalFileName() ); return undef; } $self->{'bpcfh'} = $fh; return $fh; } # Closes my file handle sub _closeFile # BackupPC::Zip::FileMember { my $self = shift; $self->{'bpcfh'}->close() if ( defined($self->{'bpcfh'}) ); $self->{'bpcfh'} = undef; } # Make sure I close my file handle sub endRead # BackupPC::Zip::FileMember { my $self = shift; $self->_closeFile(); return $self->SUPER::endRead(@_); } # Return bytes read. Note that first parameter is a ref to a buffer. # my $data; # my ($bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize ); sub _readRawChunk # BackupPC::Zip::FileMember { my ( $self, $dataRef, $chunkSize ) = @_; return ( 0, AZ_OK ) unless $chunkSize; my $bytesRead = $self->fh()->read( $dataRef, $chunkSize ) or return ( 0, _ioError("reading data") ); return ( $bytesRead, AZ_OK ); } sub extractToFileNamed # BackupPC::Zip::FileMember { die("BackupPC::Zip::FileMember::extractToFileNamed not supported\n"); } # # There is a bug in Archive::Zip 1.30 that causes BackupPC_zipCreate # to fail when compression is on and it is writing to an unseekable # output file (eg: pipe or socket); see: # # https://rt.cpan.org/Public/Bug/Display.html?id=54827 # # We overload the bitFlag function here to avoid the bug. # sub bitFlag { my $self = shift; return $self->{bitFlag}; } BackupPC-3.3.2/lib/Net/0000755000076500000240000000000013042250554013413 5ustar craigstaffBackupPC-3.3.2/lib/Net/FTP/0000755000076500000240000000000013042250554014044 5ustar craigstaffBackupPC-3.3.2/lib/Net/FTP/AutoReconnect.pm0000444000076500000240000002301513042250554017152 0ustar craigstaffpackage Net::FTP::AutoReconnect; our $VERSION = '0.2'; use warnings; use strict; use Net::FTP; =head1 NAME Net::FTP::AutoReconnect - FTP client class with automatic reconnect on failure =head1 SYNOPSIS C is a wrapper module around C. For many commands, if anything goes wrong on the first try, it tries to disconnect and reconnect to the server, restore the state to the same as it was when the command was executed, then execute it again. The state includes login credentials, authorize credentials, transfer mode (ASCII or binary), current working directory, and any restart, passive, or port commands sent. =head1 DESCRIPTION The goal of this method is to hide some implementation details of FTP server systems from the programmer. In particular, many FTP systems will automatically disconnect a user after a relatively short idle time or after a transfer is aborted. In this case, C will simply reconnect, send the commands necessary to return your session to its previous state, then resend the command. If that fails, it will return the error. It makes no effort to determine what sorts of errors are likely to succeed when they're retried. Partly that's because it's hard to know; if you're retreiving a file from an FTP site with several mirrors and the file is not found, for example, maybe on the next try you'll connect to a different server and find it. But mostly it's from laziness; if you have some good ideas about how to determine when to retry and when not to bother, by all means send patches. This module contains an instance of C, which it passes most method calls along to. These methods also record their state: C, C, C, C, C, C, C, C,C, C, C. Directory changing commands execute a C afterwards and store their new working directory. These methods are automatically retried: C, C, C, C, C, C, C, C, C

    , C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C. These methods are tried just once: C, C, C, C, C, C, C, C, C, C, C. From C: C, C, C, C. C doesn't actually send any FTP commands (they're sent along with the command they apply to), which is why it's not restarted. Any other commands are unimplemented (or possibly misdocumented); if I missed one you'd like, please send a patch. =head2 CONSTRUCTOR =head3 new All parameters are passed along verbatim to C, as well as stored in case we have to reconnect. =cut ; sub new { my $self = {}; my $class = shift; bless $self,$class; $self->{newargs} = \@_; $self->reconnect(); $self; } =head2 METHODS Most of the methods are those of L. One additional method is available: =head3 reconnect() Abandon the current FTP connection and create a new one, restoring all the state we can. =cut ; sub reconnect { my $self = shift; warn "Reconnecting!\n" if ($ENV{DEBUG}); $self->{ftp} = Net::FTP->new(@{$self->{newargs}}) or die "Couldn't create new FTP object\n"; if ($self->{login}) { $self->{ftp}->login(@{$self->{login}}); } if ($self->{authorize}) { $self->{ftp}->authorize(@{$self->{authorize}}); } if ($self->{mode}) { if ($self->{mode} eq 'ascii') { $self->{ftp}->ascii(); } else { $self->{ftp}->binary(); } } if ($self->{cwd}) { $self->{ftp}->cwd($self->{cwd}); } if ($self->{hash}) { $self->{ftp}->hash(@{$self->{hash}}); } if ($self->{restart}) { $self->{ftp}->restart(@{$self->{restart}}); } if ($self->{alloc}) { $self->{ftp}->restart(@{$self->{alloc}}); } if ($self->{pasv}) { $self->{ftp}->pasv(@{$self->{pasv}}); } if ($self->{port}) { $self->{ftp}->port(@{$self->{port}}); } } sub _auto_reconnect { my $self = shift; my($code)=@_; my $ret = $code->(); if (!defined($ret)) { $self->reconnect(); $ret = $code->(); } $ret; } sub _after_pcmd { my $self = shift; my($r) = @_; if ($r) { # succeeded delete $self->{port}; delete $self->{pasv}; delete $self->{restart}; delete $self->{alloc}; } $r; } sub login { my $self = shift; $self->{login} = \@_; $self->{ftp}->login(@_); } sub authorize { my $self = shift; $self->{authorize} = \@_; $self->{ftp}->authorize(@_); } sub site { my $self = shift; $self->{ftp}->site(@_); } sub ascii { my $self = shift; $self->{mode} = 'ascii'; $self->_auto_reconnect(sub { $self->{ftp}->ascii() }); } sub binary { my $self = shift; $self->{mode} = 'binary'; $self->_auto_reconnect(sub { $self->{ftp}->binary() }); } sub rename { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->rename(@a) }); } sub delete { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->delete(@a) }); } sub cwd { my $self = shift; my @a = @_; my $ret = $self->_auto_reconnect(sub { $self->{ftp}->cwd(@a) }); if (defined($ret)) { $self->{cwd} = $self->{ftp}->pwd() or die "Couldn't get directory after cwd\n"; } $ret; } sub cdup { my $self = shift; my @a = @_; my $ret = $self->_auto_reconnect(sub { $self->{ftp}->cdup(@a) }); if (defined($ret)) { $self->{cwd} = $self->{ftp}->pwd() or die "Couldn't get directory after cdup\n"; } $ret; } sub pwd { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->pwd(@a) }); } sub rmdir { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->rmdir(@a) }); } sub mkdir { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->mkdir(@a) }); } sub ls { my $self = shift; my @a = @_; my $ret = $self->_auto_reconnect(sub { $self->{ftp}->ls(@a) }); return $ret ? (wantarray ? @$ret : $ret) : undef; } sub dir { my $self = shift; my @a = @_; my $ret = $self->_auto_reconnect(sub { $self->{ftp}->dir(@a) }); return $ret ? (wantarray ? @$ret : $ret) : undef; } sub restart { my $self = shift; my @a = @_; $self->{restart} = \@a; $self->{ftp}->restart(@_); } sub retr { my $self = shift; my @a = @_; $self->_after_pcmd($self->_auto_reconnect(sub { $self->{ftp}->retr(@a) })); } sub get { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->get(@a) }); } sub mdtm { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->mdtm(@a) }); } sub size { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->size(@a) }); } sub abort { my $self = shift; $self->{ftp}->abort(); } sub quit { my $self = shift; $self->{ftp}->quit(); } sub hash { my $self = shift; my @a = @_; $self->{hash} = \@a; $self->{ftp}->hash(@_); } sub alloc { my $self = shift; my @a = @_; $self->{alloc} = \@a; $self->_auto_reconnect(sub { $self->{ftp}->alloc(@a) }); } sub put { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->put(@a) }); } sub put_unique { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->put_unique(@a) }); } sub append { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->append(@a) }); } sub unique_name { my $self = shift; $self->{ftp}->unique_name(@_); } sub supported { my $self = shift; my @a = @_; $self->_auto_reconnect(sub { $self->{ftp}->supported(@a) }); } sub port { my $self = shift; my @a = @_; $self->{port} = \@a; $self->_auto_reconnect(sub { $self->{ftp}->port(@a) }); } sub pasv { my $self = shift; my @a = @_; $self->{pasv} = \@a; $self->_auto_reconnect(sub { $self->{ftp}->pasv(@a) }); } sub nlst { my $self = shift; my @a = @_; $self->_after_pcmd($self->_auto_reconnect(sub { $self->{ftp}->nlst(@a) })); } sub stou { my $self = shift; my @a = @_; $self->_after_pcmd($self->_auto_reconnect(sub { $self->{ftp}->stou(@a) })); } sub appe { my $self = shift; my @a = @_; $self->_after_pcmd($self->_auto_reconnect(sub { $self->{ftp}->appe(@a) })); } sub list { my $self = shift; my @a = @_; $self->_after_pcmd($self->_auto_reconnect(sub { $self->{ftp}->list(@a) })); } sub pasv_xfer { my $self = shift; $self->{ftp}->pasv_xfer(@_); } sub pasv_xfer_unique { my $self = shift; $self->{ftp}->pasv_xfer_unique(@_); } sub pasv_wait { my $self = shift; $self->{ftp}->pasv_wait(@_); } sub message { my $self = shift; $self->{ftp}->message(@_); } sub code { my $self = shift; $self->{ftp}->code(@_); } sub ok { my $self = shift; $self->{ftp}->ok(@_); } sub status { my $self = shift; $self->{ftp}->status(@_); } =head1 AUTHOR Scott Gifford =head1 BUGS We should really be smarter about when to retry. We shouldn't be hardwired to use C, but any FTP-compatible class; that would allow all modules similar to this one to be chained together. Much of this is only lightly tested; it's hard to find an FTP server unreliable enough to test all aspects of it. It's mostly been tested with a server that dicsonnects after an aborted transfer, and the module seems to work OK. =head1 SEE ALSO L. =head1 COPYRIGHT Copyright (c) 2006 Scott Gifford. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; BackupPC-3.3.2/lib/Net/FTP/RetrHandle.pm0000444000076500000240000003363013042250554016435 0ustar craigstaffpackage Net::FTP::RetrHandle; our $VERSION = '0.2'; use warnings; use strict; use constant DEFAULT_MAX_SKIPSIZE => 1024 * 1024 * 2; use constant DEFAULT_BLOCKSIZE => 10240; # Net::FTP's default use base 'IO::Seekable'; # We don't use base 'IO::Handle'; it currently confuses Archive::Zip. use Carp; use Scalar::Util; =head1 NAME Net::FTP::RetrHandle - Tied or IO::Handle-compatible interface to a file retrieved by FTP =head1 SYNOPSIS Provides a file reading interface for reading all or parts of files located on a remote FTP server, including emulation of C and support for downloading only the parts of the file requested. =head1 DESCRIPTION Support for skipping the beginning of the file is implemented with the FTP C command, which starts a retrieval at any point in the file. Support for skipping the end of the file is implemented with the FTP C command, which stops the transfer. With these two commands and some careful tracking of the current file position, we're able to reliably emulate a C pair, and get only the parts of the file that are actually read. This was originally designed for use with L; it's reliable enough that the table of contents and individual files can be extracted from a remote ZIP archive without downloading the whole thing. See L below. An interface compatible with L is provided, along with a C-based interface. Remember that an FTP server can only do one thing at a time, so make sure to C your connection before asking the FTP server to do nything else. =head1 CONSTRUCTOR =head2 new ( $ftp, $filename, options... ) Creates a new L-compatible object to fetch all or parts of C<$filename> using the FTP connection C<$ftp>. Available options: =over 4 =item MaxSkipSize => $size If we need to move forward in a file or close the connection, sometimes it's faster to just read the bytes we don't need than to abort the connection and restart. This setting tells how many unnecessary bytes we're willing to read rather than abort. An appropriate setting depends on the speed of transferring files and the speed of reconnecting to the server. =item BlockSize => $size When doing buffered reads, how many bytes to read at once. The default is the same as the default for L, so it's generally best to leave it alone. =item AlreadyBinary => $bool If set to a true value, we assume the server is already in binary mode, and don't try to set it. =back =cut use constant USAGE => "Usage: Net::FTP::RetrHandle\->new(ftp => \$ftp_obj, filename => \$filename)\n"; sub new { my $class = shift; my $ftp = shift or croak USAGE; my $filename = shift or croak USAGE; my $self = { MaxSkipSize => DEFAULT_MAX_SKIPSIZE, BlockSize => DEFAULT_BLOCKSIZE, @_, ftp => $ftp, filename => $filename, pos => 0, nextpos => 0}; $self->{size} = $self->{ftp}->size($self->{filename}) or return undef; $self->{ftp}->binary() unless ($self->{AlreadyBinary}); bless $self,$class; } =head1 METHODS Most of the methods implemented behave exactly like those from L. These methods are implemented: C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C. =cut ; sub opened { 1; } sub seek { my $self = shift; my $pos = shift || 0; my $whence = shift || 0; warn " SEEK: self=$self, pos=$pos, whence=$whence\n" if ($ENV{DEBUG}); my $curpos = $self->tell(); my $newpos = _newpos($self->tell(),$self->{size},$pos,$whence); my $ret; if ($newpos == $curpos) { return $curpos; } elsif (defined($self->{_buf}) and ($newpos > $curpos) and ($newpos < ($curpos + length($self->{_buf})))) { # Just seeking within the buffer (or not at all) substr($self->{_buf},0,$newpos - $curpos,''); $ret = $newpos; } else { $ret = $self->sysseek($newpos,0); $self->{_buf} = ''; } return $ret; } sub _newpos { my($curpos,$size,$pos,$whence)=@_; if ($whence == 0) # seek_set { return $pos; } elsif ($whence == 1) # seek_cur { return $curpos + $pos; } elsif ($whence == 2) # seek_end { return $size + $pos; } else { die "Invalid value $whence for whence!"; } } sub sysseek { my $self = shift; my $pos = shift || 0; my $whence = shift || 0; warn "SYSSEEK: self=$self, pos=$pos, whence=$whence\n" if ($ENV{DEBUG}); my $newpos = _newpos($self->{nextpos},$self->{size},$pos,$whence); $self->{eof}=undef; return $self->{nextpos}=$newpos; } sub tell { my $self = shift; return $self->{nextpos} - (defined($self->{_buf}) ? length($self->{_buf}) : 0); } # WARNING: ASCII mode probably breaks seek. sub binmode { my $self = shift; my $mode = shift || ':raw'; return if (defined($self->{curmode}) && ($self->{curmode} eq $mode)); if (defined($mode) and $mode eq ':crlf') { $self->_finish_connection(); $self->{ftp}->ascii() or return $self->seterr(); } else { $self->_finish_connection(); $self->{ftp}->binary() or return $self->seterr(); } $self->{curmode} = $mode; } sub _min { return $_[0] < $_[1] ? $_[0] : $_[1]; } sub _max { return $_[0] > $_[1] ? $_[0] : $_[1]; } sub read { my $self = shift; # return $self->sysread(@_); my(undef,$len,$offset)=@_; $offset ||= 0; warn "READ(buf,$len,$offset)\n" if ($ENV{DEBUG}); if (!defined($self->{_buf}) || length($self->{_buf}) <= 0) { $self->sysread($self->{_buf},_max($len,$self->{BlockSize})) or return 0; } elsif (length($self->{_buf}) < $len) { $self->sysread($self->{_buf},_max($len-length($self->{_buf}),$self->{BlockSize}),length($self->{_buf})); } my $ret = _min($len,length($self->{_buf})); if (!defined($_[0])) { $_[0] = '' } substr($_[0],$offset) = substr($self->{_buf},0,$len,''); $self->{read_count}++; return $ret; } sub sysread { my $self = shift; if ($self->{eof}) { return 0; } my(undef,$len,$offset) = @_; $offset ||= 0; warn "SYSREAD(buf,$len,$offset)\n" if ($ENV{DEBUG}); if ($self->{nextpos} >= $self->{size}) { $self->{eof} = 1; $self->{pos} = $self->{nextpos}; return 0; } if ($self->{pos} != $self->{nextpos}) { # They seeked. if ($self->{ftp_running}) { warn "Seek detected, nextpos=$self->{nextpos}, pos=$self->{pos}, MaxSkipSize=$self->{MaxSkipSize}\n" if ($ENV{DEBUG}); if ($self->{nextpos} > $self->{pos} and ($self->{nextpos} - $self->{pos}) < $self->{MaxSkipSize}) { my $br = $self->{nextpos}-$self->{pos}; warn "Reading $br bytes to skip ahead\n" if ($ENV{DEBUG}); my $junkbuff; while ($br > 0) { warn "Trying to read $br more bytes\n" if ($ENV{DEBUG}); my $b = $self->{ftp_data}->read($junkbuff,$br); if ($b == 0) { $self->_at_eof(); return 0; } elsif (!defined($b) || $b < 0) { return $self->seterr(); } else { $br -= $b; } } $self->{pos}=$self->{nextpos}; } else { warn "Aborting connection to move to new position\n" if ($ENV{DEBUG}); $self->_finish_connection(); } } } if (!$self->{ftp_running}) { $self->{ftp}->restart($self->{nextpos}); $self->{ftp_data} = $self->{ftp}->retr($self->{filename}) or return $self->seterr(); $self->{ftp_running} = 1; $self->{pos}=$self->{nextpos}; } my $tmpbuf; my $rb = $self->{ftp_data}->read($tmpbuf,$len); if ($rb == 0) { $self->_at_eof(); return 0; } elsif (!defined($rb) || $rb < 0) { return $self->seterr(); } if (!defined($_[0])) { $_[0] = '' } substr($_[0],$offset) = $tmpbuf; $self->{pos} += $rb; $self->{nextpos} += $rb; $self->{sysread_count}++; $rb; } sub _at_eof { my $self = shift; $self->{eof}=1; $self->_finish_connection(); # $self->{ftp_data}->_close(); $self->{ftp_running} = $self->{ftp_data} = undef; } sub _finish_connection { my $self = shift; warn "_finish_connection\n" if ($ENV{DEBUG}); return unless ($self->{ftp_running}); if ($self->{size} - $self->{pos} < $self->{MaxSkipSize}) { warn "Skipping " . ($self->{size}-$self->{pos}) . " bytes\n" if ($ENV{DEBUG}); my $junkbuff; my $br; while(($br = $self->{ftp_data}->read($junkbuff,8192))) { # Read until EOF or error } defined($br) or $self->seterr(); } warn "Shutting down existing FTP DATA session...\n" if ($ENV{DEBUG}); my $closeret; { eval { $closeret = $self->{ftp_data}->close(); }; # Work around a timeout bug in Net::FTP if ($@ && $@ =~ /^Timeout /) { warn "Timeout closing connection, retrying...\n" if ($ENV{DEBUG}); select(undef,undef,undef,1); redo; } } $self->{ftp_running} = $self->{ftp_data} = undef; return $closeret ? 1 : $self->seterr(); } sub write { die "Only reading currently supported"; } sub close { my $self = shift; return $self->{ftp_data} ? $self->_finish_connection() : 1; } sub eof { my $self = shift; if ($self->{eof}) { return 1; } my $c = $self->getc; if (!defined($c)) { return 1; } $self->ungetc(ord($c)); return undef; } sub getc { my $self = shift; my $c; my $rb = $self->read($c,1); if ($rb < 1) { return undef; } return $c; } sub ungetc { my $self = shift; # Note that $c is the ordinal value of a character, not the # character itself (for some reason) my($c)=@_; $self->{_buf} = chr($c) . $self->{_buf}; } sub getline { my $self = shift; if (!defined($/)) { my $buf; while($self->read($buf,$self->{BlockSize},length($buf)) > 0) { # Keep going } return $buf; } elsif (ref($/) && looks_like_number ${$/} ) { my $buf; $self->read($buf,${$/}) or return undef; return $buf; } my $rs; if ($/ eq '') { $rs = "\n\n"; } else { $rs = $/; } my $eol; if (!defined($self->{_buf})) { $self->{_buf} = '' } while (($eol=index($self->{_buf},$rs)) < $[) { if ($self->{eof}) { # return what's left if (length($self->{_buf}) == 0) { return undef; } else { return substr($self->{_buf},0,length($self->{_buf}),''); } } else { $self->sysread($self->{_buf},$self->{BlockSize},length($self->{_buf})); } } # OK, we should have a match. my $tmpbuf = substr($self->{_buf},0,$eol+length($rs),''); while ($/ eq '' and substr($self->{_buf},0,1) eq "\n") { substr($self->{_buf},0,1)=''; } return $tmpbuf; } sub getlines { my $self = shift; my @lines; my $line; while (defined($line = $self->getline())) { push(@lines,$line); } @lines; } sub error { return undef; } sub seterr { my $self = shift; $self->{_error} = 1; return undef; } sub clearerr { my $self = shift; $self->{_error} = undef; return 0; } sub getpos { my $self = shift; return $self->tell(); } sub setpos { my $self = shift; return $self->seek(@_); } sub DESTROY { my $self = shift; if (UNIVERSAL::isa($self,'GLOB')) { $self = tied *$self or die "$self not tied?..."; } if ($self->{ftp_data}) { $self->_finish_connection(); } warn "sysread called ".$self->{sysread_count}." times.\n" if ($ENV{DEBUG}); } =head1 TIED INTERFACE Instead of a L-compatible interface, you can use a C-based interface to use the standard Perl I/O operators. You can use it like this: use Net::FTP::RetrHandle; # Create FTP object in $ftp # Store filename in $filename tie *FH, 'Net::FTP::RetrHandle', $ftp, $filename or die "Error in tie!\n"; =cut ; sub TIEHANDLE { my $class = shift; my $obj = $class->new(@_); $obj; } sub READ { my $self = shift; $self->read(@_); } sub READLINE { my $self = shift; return wantarray ? $self->getlines(@_) : $self->getline(@_); } sub GETC { my $self = shift; return $self->getc(@_); } sub SEEK { my $self = shift; return $self->seek(@_); } sub SYSSEEK { my $self = shift; return $self->sysseek(@_); } sub TELL { my $self = shift; return $self->tell(); } sub CLOSE { my $self = shift; return $self->close(@_); } sub EOF { my $self = shift; return $self->eof(@_); } sub UNTIE { tied($_[0])->close(@_); } =head1 EXAMPLE Here's an example of listing a Zip file without downloading the whole thing: #!/usr/bin/perl use warnings; use strict; use Net::FTP; use Net::FTP::AutoReconnect; use Net::FTP::RetrHandle; use Archive::Zip; my $ftp = Net::FTP::AutoReconnect->new("ftp.info-zip.com", Debug => $ENV{DEBUG}) or die "connect error\n"; $ftp->login('anonymous','example@example.com') or die "login error\n"; $ftp->cwd('/pub/infozip/UNIX/LINUX') or die "cwd error\n"; my $fh = Net::FTP::RetrHandle->new($ftp,'unz551x-glibc.zip') or die "Couldn't get handle to remote file\n"; my $zip = Archive::Zip->new($fh) or die "Couldn't create Zip object\n"; foreach my $fn ($zip->memberNames()) { print "unz551-glibc.zip: $fn\n"; } =head1 AUTHOR Scott Gifford =head1 BUGS The distinction between tied filehandles and C-compatible filehandles should be blurrier. It seems like other file handle objects you can freely mix method calls and traditional Perl operations, but I can't figure out how to do it. Many FTP servers don't like frequent connection aborts. If that's the case, try L, which will hide much of that from you. If the filehandle is tied and created with C, C doesn't work with older versions of Perl. No idea why. =head1 SEE ALSO L, L, L. =head1 COPYRIGHT Copyright (c) 2006 Scott Gifford. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; BackupPC-3.3.2/LICENSE0000444000076500000240000003556413042250554013137 0ustar craigstaffGNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS BackupPC-3.3.2/README0000444000076500000240000001357113042250554013004 0ustar craigstaff BackupPC Version 3.3.2 25 Jan 2017 Copyright (C) 2001-2017 Craig Barratt. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License. See the LICENSE file. QUICK START: ----------- The latest version of BackupPC can be fetched from: https://github.com/backuppc/backuppc/releases If you use rsync you will need to install the per module File::RsyncP from SourceForge or www.cpan.org, plus at least rsync 2.5.6 on the client machines. A pre-built windows rsync client with an installer is available at: https://sourceforge.net/projects/backuppc/files/cygwin-rsyncd/ If you will use SMB for WinXX clients, you will need smbclient and nmblookup from the Samba distribution. Version >= 2.2.0 of Samba is recommended. See www.samba.org for source and binaries. To install BackupPC run these commands as root: tar zxf BackupPC-3.3.2.tar.gz cd BackupPC-3.3.2 perl configure.pl This will automatically determine some system information and prompt you for install paths. Do "perldoc configure.pl" to see the various options that configure.pl provides. INTRODUCTION: ------------ BackupPC is a high-performance, enterprise-grade system for backing up Linux, WinXX, and MacOS PCs and laptops to a server's disk. BackupPC is highly configurable and easy to install and maintain. Given the ever decreasing cost of disks and raid systems, it is now practical and cost effective to backup a large number of machines onto a server's local disk or network storage. This is what BackupPC does. For some sites, this might be the complete backup solution. For other sites, additional permanent archives could be created by periodically backing up the server to tape. A variety of Open Source systems are available for doing backup to tape. BackupPC is written in Perl and extracts backup data via SMB (using Samba), rsync, or tar over ssh/rsh/nfs. It is robust, reliable, well documented and freely available as Open Source on SourceForge. FEATURES: -------- - A clever pooling scheme minimizes disk storage and disk IO. Identical files across multiple backups of the same or different PCs are stored only once resulting in substantial savings in disk storage. - One example of disk use: 95 latops with each full backup averaging 3.6GB each, and each incremental averaging about 0.3GB. Storing three weekly full backups and six incremental backups per laptop is around 1200GB of raw data, but because of pooling and compression only 150GB is needed. - No client-side software is needed. The standard smb protocol is used to extract backup data on WinXX clients. On *nix clients, either rsync or tar over ssh/rsh/nfs is used to backup the data. Various alternatives are possible: rsync can also be used with WinXX by running rsyncd/cygwin. Similarly, smb could be used to backup *nix file systems if they are exported as smb shares. - A powerful http/cgi user interface allows administrators to view log files, configuration, current status and allows users to initiate and cancel backups and browse and restore files from backups. - Flexible restore options. Single files can be downloaded from any backup directly from the CGI interface. Zip or Tar archives for selected files or directories from any backup can also be downloaded from the CGI interface. Finally, direct restore to the client machine (using SMB, rsync or tar) for selected files or directories is also supported from the CGI interface. - Supports mobile environments where laptops are only intermittently connected to the network and have dynamic IP addresses (DHCP). - Flexible configuration parameters allow multiple backups to be performed in parallel, specification of which shares to backup, which directories to backup or not backup, various schedules for full and incremental backups, schedules for email reminders to users and so on. Configuration parameters can be set system-wide or also on a per-PC basis. - Users are sent periodic email reminders if their PC has not recently been backed up. Email content, timing and policies are configurable. - Tested on Linux and Solaris hosts, and Linux, Win95, Win98, Win2000 and WinXP clients. - Detailed documentation. - Open Source hosted by SourceForge and freely available under GPL. RESOURCES: --------- Complete documentation is available in this release in doc/BackupPC.pod or doc/BackupPC.html. You can read doc/BackupPC.pod with perldoc and doc/BackupPC.html with any browser. You can also see the documentation and general information at: http://backuppc.sourceforge.net The source code is available on Github at: https://github.com/backuppc and releases are available on github: https://github.com/backuppc/backuppc/releases or SourceForge: https://sourceforge.net/projects/backuppc/files You are encouraged to subscribe to any of the mail lists available on sourceforge.net: http://lists.sourceforge.net/lists/listinfo/backuppc-announce http://lists.sourceforge.net/lists/listinfo/backuppc-users http://lists.sourceforge.net/lists/listinfo/backuppc-devel The backuppc-announce list is moderated and is used only for important announcements (eg: new versions). It is low traffic. You only need to subscribe to one of users and announce: backuppc-users also receives any messages on backuppc-announce. The backuppc-devel list is only for developers who are working on BackupPC. Do not post questions or support requests there. But detailed technical discussions should happen on this list. To post a message to the backuppc-users list, send an email to backuppc-users@lists.sourceforge.net Do not send subscription requests to this address!