#!/usr/bin/perl -w # vim:ts=4 ############################################################################## # rrd-archive-clean.pl v0.2 # S Shipway 2004. Distributed under the GNU GPL # # This script will identify orphaned rrd archives, and archives for targets # that have 'no archive' configured. It will then delete them. # It will not delete expired archives -- rrd-archive will take care fo that. # # Usage: # perl rrd-archive-clean.pl # # Will also read the routers.conf file, and look in the [archive] section, # if it exists. See the example for the options. # # Added options to your MRTG .cfg file: # # routers.cgi*Archive[targetname]: # can take 'daily xxx' for some number xxx, 'monthly yyy' for some number yyy # to specify expiry of daily archives (in days) and monthly (in months). # Default is 31 days, 12 months. # # Steve Shipway, Jan 2004 ############################################################################## use strict; ################# CONFIGURABLE LINES START ############### # default location of routers.conf file my( $conffile ) = "/u01/etc/routers2.conf"; # default number of days after which to expire archived logs my( $expiredaily ) = 31; my( $expiremonthly ) = 12; # 1st of the month are Monthly ################# CONFIGURABLE LINES END ################# my($forreal) = 0; my($printname) = 0; my(@cfgfiles) = (); my($pattern, $thisfile); my($workdir, $rrd, $opt ); my($expd, $expm); my(%targets,$t); my( %config ); my( $NT ) = 0; my( $pathsep ) = "/"; my( $confpath, $cfgfiles ); my( $debug ) = 0; my(%paths) = (); my(%valid) = (); my($apath); my($df)=0; ########################################################################### # readconf: pass it a list of section names sub readconf(@) { my ($inlist, $i, @secs, $sec); @secs = @_; %config = (); # set defaults %config = ( 'routers.cgi-confpath' => ".",); ( open CFH, "<".$conffile ) || do { print "Error: unable to open file $conffile\n"; exit(0); }; $inlist=0; $sec = ""; while( ) { /^\s*#/ && next; /\[(\S*)\]/ && do { $sec = $1; $inlist=0; foreach $i ( @secs ) { if ( $i eq $1 ) { $inlist=1; last; } } next; }; # note final \s* to strip all trailing spaces (which doesnt work # because the * operator is greedy!) if ( $inlist ) { /(\S+)\s*=\s*(\S.*?)\s*$/ and $config{"$sec-$1"}=$2; } } close CFH; # Activate NT compatibility options. # $^O is the OS name, NT usually produces 'MSWin32'. By checking for 'Win' # we should be able to cover most possibilities. if ( (defined $config{'web-NT'} and $config{'web-NT'}=~/[1y]/i) or $^O =~ /Win/ or $^O =~ /DOS/i ) { $pathsep = "\\"; $NT = 1; } # some path corrections: remove trailing path separators on f/s paths foreach ( qw/dbpath confpath graphpath graphurl/ ) { $config{"routers.cgi-$_"} =~ s/[\/\\]$//; } } ########################################################################### ############### MAIN CODE STARTS HERE ####### $|=1; # get parameters print "Reading configuration\n" if($debug); readconf('routers.cgi','web','archive'); $confpath = $config{'routers.cgi-confpath'}; $confpath = $config{'archive-confpath'} if(defined $config{'archive-confpath'}); $cfgfiles = $config{'routers.cgi-cfgfiles'}; $cfgfiles = $config{'archive-cfgfiles'} if(defined $config{'archive-cfgfiles'}); if(! -d $confpath ) { print "Error: MRTG directory $confpath does not exist.\n"; exit 1; } $expiredaily = $config{'archive-expiredaily'} if(defined $config{'archive-expiredaily'}); $expiremonthly = $config{'archive-expiremonthly'} if(defined $config{'archive-expiremonthly'}); # Now we have the defaults, and we know which files to process. # We can optimise our processing of the .cfg files. foreach $pattern ( split " ",$cfgfiles ) { # print "$confpath$pathsep$pattern\n" if($debug); push @cfgfiles, glob( $confpath.$pathsep.$pattern ); } if( @ARGV and $ARGV[0] eq '-F' ) { $forreal = 1; shift @ARGV; } if( @ARGV and $ARGV[0] eq '-n' ) { $printname = 1; shift @ARGV; } if( @ARGV and $ARGV[0] =~ /^-/ ) { print "Option '".$ARGV[0]."' not known.\n"; exit(1); } @cfgfiles = @ARGV if(@ARGV); if(!$forreal) { print "NOTE: Running in test mode only: not deleting things for real.\nUse -F option to actually delete orphaned files.\n"; } print "Default expiry: DAILY($expiredaily), MONTHLY($expiremonthly)\n"; print "Processing configuration files\n" ; foreach $thisfile ( @cfgfiles ) { next if(!-f $thisfile); open CFG,"<$thisfile" or next; print "."; $workdir = $config{'routers.cgi-dbpath'}; # default %targets = ( '_' => { expd => $expiredaily, expm => $expiremonthly }); while ( ) { if( /^\s*Include\s*:\s*(\S+)/i ) { push @cfgfiles,$1; next; } if( /^\s*WorkDir\s*:\s*(\S+)/i ) { $workdir = $1; next; } if( /^\s*Directory\[(\S+)\]\s*:\s*(\S+)/i ) { $t = lc $1; $targets{$t} = {} if(!defined $targets{$t}); $targets{$t}->{directory} = $2; next; } if( /^\s*Target\[(\S+)\]/i ) { $t = lc $1; $targets{$t} = {} if(!defined $targets{$t}); $targets{$t}->{file} = "$t.rrd"; next; } if( /^\s*routers\.cgi\*Archive\[(\S+)\]\s*:\s*(\S.+)/i ) { $t = lc $1; $opt = $2; ($expd, $expm) = ($expiredaily, $expiremonthly); if( $opt =~ /no/i ) { ($expd, $expm) = (0,0); } elsif( $opt =~ /daily/ or $opt =~ /monthly/) { if( $opt =~ /daily\s+(\d+)/i ) { $expd = $1; } if( $opt =~ /monthly\s+(\d+)/i ) { $expm = $1; } } elsif( $opt =~ /(\d+)/i ) { $expd = $1; } $targets{$t}->{expd} = $expd; $targets{$t}->{expm} = $expm; next; } } close CFG; # now process the archiving foreach $t ( keys %targets ) { next if(!defined $targets{$t}->{file}); # skip dummy ones foreach ( keys %{$targets{'_'}} ) { $targets{$t}->{$_} = $targets{'_'}->{$_} if(!defined $targets{$t}->{$_}); } $rrd = $workdir; $rrd .= $pathsep.$targets{$t}->{directory} if(defined $targets{$t}->{directory}); $apath = $rrd.$pathsep."archive"; $paths{$apath} = 1 if(!defined $paths{$apath} and $targets{$t}->{expd}); $rrd .= $pathsep.$targets{$t}->{file}; $targets{$t}->{rrd} = $rrd; $valid{$t} = 1; # keep the root $valid{$t} = 2 if($targets{$t}->{expd}); # keep archives } } print "\n"; # Now, keys(%valid) holds a list of valid targetnames. # keys(%paths) holds a list of archive directories print "Checking the root .rrd files \n"; foreach ( glob($config{'routers.cgi-dbpath'}.$pathsep.'*.rrd') ) { # find the targetname if( /[\\\/]([^\\\/]+)\.rrd$/ ) { $t = $1; if(!$valid{$t}) { # print "\nRemoving $_\n"; if($forreal) { unlink $_; } if($printname) { print "$_\n"; } else { print "!"; } $df++; } } else { print "?"; } } print "\n"; print "Identifying files to delete\n"; foreach $apath ( keys %paths ) { print ">"; foreach ( glob($apath.$pathsep.'*'.$pathsep.'*.rrd') ) { # find the targetname if( /[\\\/]([^\\\/]+)\.rrd$/ ) { # found it $t = $1; if(!$valid{$t} or $valid{$t}<2) { # zap it # print "\nRemoving $_\n"; print "!"; if($forreal) { unlink $_; } $df++; } } else { # we cant identify it, best to keep it then print "?"; } } } print "\n"; print "All finished ($df files "; print "would have been " if(!$forreal); print "deleted).\n" ; exit(0);