2 #============================================================= -*-perl-*-
4 # BackupPC_nightly: Nightly cleanup & statistics script.
8 # BackupPC_nightly performs several administrative tasks:
10 # - monthly aging of per-PC log files (only with -m option)
12 # - pruning files from pool no longer used (ie: those with only one
15 # - sending email to users and administrators (only with -m option)
17 # Usage: BackupPC_nightly [-m] poolRangeStart poolRangeEnd
21 # -m Do monthly aging of per-PC log files and sending of email.
22 # Otherise, BackupPC_nightly just does pool pruning.
24 # The poolRangeStart and poolRangeEnd arguments are integers from 0 to 255.
25 # These specify which parts of the pool to process. There are 256 2nd-level
26 # directories in the pool (0/0, 0/1, ..., f/e, f/f). BackupPC_nightly
27 # processes the given subset of this list (0 means 0/0, 255 means f/f).
28 # Therefore, arguments of 0 255 process the entire pool, 0 127 does
29 # the first half (ie: 0/0 through 7/f), 127 255 does the other half
30 # (eg: 8/0 through f/f) and 0 15 does just the first 1/16 of the pool
31 # (ie: 0/0 through 0/f).
34 # Craig Barratt <cbarratt@users.sourceforge.net>
37 # Copyright (C) 2001-2004 Craig Barratt
39 # This program is free software; you can redistribute it and/or modify
40 # it under the terms of the GNU General Public License as published by
41 # the Free Software Foundation; either version 2 of the License, or
42 # (at your option) any later version.
44 # This program is distributed in the hope that it will be useful,
45 # but WITHOUT ANY WARRANTY; without even the implied warranty of
46 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 # GNU General Public License for more details.
49 # You should have received a copy of the GNU General Public License
50 # along with this program; if not, write to the Free Software
51 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
53 #========================================================================
55 # Version 2.1.0, released 20 Jun 2004.
57 # See http://backuppc.sourceforge.net.
59 #========================================================================
63 use lib "__INSTALLDIR__/lib";
65 use BackupPC::FileZIO;
72 die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) );
73 my $TopDir = $bpc->TopDir();
74 my $BinDir = $bpc->BinDir();
75 my %Conf = $bpc->Conf();
76 my(%Status, %Info, %Jobs, @BgQueue, @UserQueue, @CmdQueue);
81 if ( !getopts("m", \%opts) || @ARGV != 2 ) {
82 print("usage: $0 [-m] poolRangeStart poolRangeEnd\n");
85 if ( $ARGV[0] !~ /^(\d+)$/ || $1 > 255 ) {
86 print("$0: bad poolRangeStart '$ARGV[0]'\n");
89 my $poolRangeStart = $1;
90 if ( $ARGV[1] !~ /^(\d+)$/ || $1 > 255 ) {
91 print("$0: bad poolRangeEnd '$ARGV[1]'\n");
94 my $poolRangeEnd = $1;
97 my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort});
99 print("Can't connect to server ($err)\n");
102 my $reply = $bpc->ServerMesg("status hosts");
103 $reply = $1 if ( $reply =~ /(.*)/s );
107 ###########################################################################
108 # When BackupPC_nightly starts, BackupPC will not run any simultaneous
109 # BackupPC_dump commands. We first do things that contend with
110 # BackupPC_dump, eg: aging per-PC log files etc.
111 ###########################################################################
112 doPerPCLogFileAging() if ( $opts{m} );
114 ###########################################################################
115 # Get statistics on the pool, and remove files that have only one link.
116 ###########################################################################
118 my $fileCnt; # total number of files
119 my $dirCnt; # total number of directories
120 my $blkCnt; # total block size of files
121 my $fileCntRm; # total number of removed files
122 my $blkCntRm; # total block size of removed files
123 my $blkCnt2; # total block size of files with just 2 links
124 # (ie: files that only occur once among all backups)
125 my $fileCntRep; # total number of file names containing "_", ie: files
126 # that have repeated md5 checksums
127 my $fileRepMax; # worse case number of files that have repeated checksums
128 # (ie: max(nnn+1) for all names xxxxxxxxxxxxxxxx_nnn)
129 my $fileLinkMax; # maximum number of hardlinks on a pool file
130 my $fileCntRename; # number of renamed files (to keep file numbering
132 my %FixList; # list of paths that need to be renamed to avoid
134 my @hexChars = qw(0 1 2 3 4 5 6 7 8 9 a b c d e f);
136 for my $pool ( qw(pool cpool) ) {
137 for ( my $i = $poolRangeStart ; $i <= $poolRangeEnd ; $i++ ) {
138 my $dir = "$hexChars[int($i / 16)]/$hexChars[$i % 16]";
139 # print("Doing $pool/$dir\n") if ( ($i % 16) == 0 );
151 find({wanted => \&GetPoolStats}, "$TopDir/$pool/$dir");
152 my $kb = $blkCnt / 2;
153 my $kbRm = $blkCntRm / 2;
154 my $kb2 = $blkCnt2 / 2;
157 # Main BackupPC_nightly counts the top-level directory
159 $dirCnt++ if ( $opts{m} && -d "$TopDir/$pool" && $i == 0 );
162 # Also count the next level directories
164 $dirCnt++ if ( ($i % 16) == 0
165 && -d "$TopDir/$pool/$hexChars[int($i / 16)]" );
168 # Now make sure that files with repeated checksums are still
169 # sequentially numbered
171 foreach my $name ( sort(keys(%FixList)) ) {
172 my $rmCnt = $FixList{$name} + 1;
174 for ( my $old = -1 ; ; $old++ ) {
176 $oldName .= "_$old" if ( $old >= 0 );
177 if ( !-f $oldName ) {
179 # We know we are done when we have missed at least
180 # the number of files that were removed from this
181 # base name, plus a couple just to be sure
183 last if ( $rmCnt-- <= 0 );
187 $newName .= "_$new" if ( $new >= 0 );
189 next if ( $oldName eq $newName );
190 rename($oldName, $newName);
194 print("BackupPC_stats $i = $pool,$fileCnt,$dirCnt,$kb,$kb2,$kbRm,"
195 . "$fileCntRm,$fileCntRep,$fileRepMax,"
196 . "$fileCntRename,$fileLinkMax\n");
200 ###########################################################################
201 # Tell BackupPC that it is now ok to start running BackupPC_dump
202 # commands. We are guaranteed that no BackupPC_link commands will
203 # run since only a single CmdQueue command runs at a time, and
204 # that means we are safe.
205 ###########################################################################
206 printf("BackupPC_nightly lock_off\n");
208 ###########################################################################
210 ###########################################################################
212 print("log BackupPC_nightly now running BackupPC_sendEmail\n");
213 system("$BinDir/BackupPC_sendEmail")
217 # Do per-PC log file aging
219 sub doPerPCLogFileAging
221 my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
223 foreach my $host ( keys(%Status) ) {
224 my $lastLog = $Conf{MaxOldPerPCLogFiles} - 1;
225 unlink("$TopDir/pc/$host/LOG.$lastLog")
226 if ( -f "$TopDir/pc/$host/LOG.$lastLog" );
227 unlink("$TopDir/pc/$host/LOG.$lastLog.z")
228 if ( -f "$TopDir/pc/$host/LOG.$lastLog.z" );
229 for ( my $i = $lastLog - 1 ; $i >= 0 ; $i-- ) {
231 if ( -f "$TopDir/pc/$host/LOG.$i" ) {
232 rename("$TopDir/pc/$host/LOG.$i",
233 "$TopDir/pc/$host/LOG.$j");
234 } elsif ( -f "$TopDir/pc/$host/LOG.$i.z" ) {
235 rename("$TopDir/pc/$host/LOG.$i.z",
236 "$TopDir/pc/$host/LOG.$j.z");
240 # Compress the log file LOG -> LOG.0.z (if enabled).
241 # Otherwise, just rename LOG -> LOG.0.
243 BackupPC::FileZIO->compressCopy("$TopDir/pc/$host/LOG",
244 "$TopDir/pc/$host/LOG.0.z",
245 "$TopDir/pc/$host/LOG.0",
246 $Conf{CompressLevel}, 1);
247 open(LOG, ">", "$TopDir/pc/$host/LOG") && close(LOG);
254 my($nlinks, $nblocks) = (lstat($_))[3, 12];
262 if ( $nlinks == 1 ) {
263 $blkCntRm += $nblocks;
267 # We must keep repeated files numbered sequential (ie: files
268 # that have the same checksum are appended with _0, _1 etc).
269 # There are two cases: we remove the base file xxxx, but xxxx_0
270 # exists, or we remove any file of the form xxxx_nnn. We remember
271 # the base name and fix it up later (not in the middle of find).
274 ($baseName = $File::Find::name) =~ s/_\d+$//;
275 $FixList{$baseName}++;
278 $fileRepMax = $1 + 1 if ( $fileRepMax <= $1 );
283 $blkCnt2 += $nblocks if ( $nlinks == 2 );
284 $fileLinkMax = $nlinks if ( $fileLinkMax < $nlinks );