#!/usr/bin/perl # # DeathClock 0.1 written 021307:1105 by BJP # # DeathClock attempts to predict how much time remains until a given filesystem runs out of storage. # # Usage: deathclock.pl # # # $version=0.1; use Mail::Sendmail; startupRoutine(); mainRoutine(); sub mainRoutine() { while(true) { collectStatusInfo(); determineTimeRemaining(); $counter++; if ($samples==$counter-1) { print"\n"; } if ($samples<=$counter-1) { print "DeathClock: $spaceRemaining MB remaining in $filesystem. Estimated time of death: $timeRemaining.\n"; } else { print "."; } if ($counter-$samples>=$duration && $duration!=0) { print"\n"; exit(); } sleep($sleep); } } sub collectStatusInfo() { @dfStuff=`df -m | grep " $filesystem\$"`; @filesystemState=split /\s+/,@dfStuff[0]; $filesystemSize=$filesystemState[1]; $spaceRemaining=$filesystemState[2]; unshift(@history,$spaceRemaining); if ($counter>$samples) { pop(@history); } } sub determineTimeRemaining() { $timeRemaining=$spaceRemaining/((($history[$samples]-$history[0])/($samples*$sleep))+0.0001); ## 1) ^^^^ A really, REALLY long way of saying y=mx+b and solving for x :) ## 2) The +0.0001 there is to prevent div-by-zero errors when the delta stays flat for the entire sample set. if($timeRemaining<5270400 && $timeRemaining>0) # 2 months { panicCheck(); $wks=int($timeRemaining/604800); $timeRemaining=int($timeRemaining%604800); $day=int($timeRemaining/86400); $timeRemaining=int($timeRemaining%86400); $hrs=int($timeRemaining/3600); $timeRemaining=int($timeRemaining%3600); $min=int($timeRemaining/60); $sec=int($timeRemaining%60); $timeRemaining="".$wks."w ".$day."d ".$hrs."h ".$min."m ".$sec."s"; } else { $timeRemaining="Immortal (>2 months)"; } } sub panicCheck() { if ($timeRemaining<3600 && $timeRemaining>=0) { $panicStateDuration++; } else { $panicStateDuration=0; } if ($panicStateDuration>=$panicThreshold && $panicThreshold!=0) { print "DeathClock: Panic threshold exceeded!\n"; notifyRecipients(); } } sub notifyRecipients() { if ($counter>=$lastPanic+$silencerWaitTime || $firstPass==1) # If we've waited long enough between sending emails.. { print "DeathClock: Sending panic notification to $mailRecipients.\n"; %mail=( To => $mailRecipients, From => $mailSender, Subject => "DeathClock Panic Notification", Message => "DeathClock strongly believes less than 1 hour remains until $filesystem on $whereAmI runs out of space." ); sendmail(%mail) or die("Send failed: $Mail::Sendmail::error\n"); $lastPanic=$counter; $firstPass=0; } else { print "DeathClock: Supressing email notification. Recipient(s) were notified less than 10 minutes ago.\n"; } } sub startupRoutine() { if ($#ARGV!=5) { if ($#ARGV<5) { print"\n\nToo few arguments specified. See below."; } else { print"\n\nToo many arguments specified. See below."; } print "\n\nDeathClock v$version written 021307:1105 by BJP"; print "\n\n Usage: $0 "; print "\n Example: $0 /tmp 10 2 34 18 foo@bar.com"; print "\n English: Monitor /tmp, based on 10 readings, checking every 2 seconds, and do so 34 times. If DeathClock sees less than an hour"; print "\n remains for 18 cycles in a row, it will notify foo@bar.com\n"; print "\nSpecifying 0 for the cycles parameter will run DeathClock indefinitely. Specifying 0 for panic_threshold will disable panic reporting."; print "\nMultiple email recipients can be specified using a comma delimiter, and no spaces between the addresses."; print "\n\n\n"; exit(); } else { # Usage: deathclock.pl print "\n\nDeathClock: Starting up..\n"; $whereAmI=`hostname`; $mailSender=$ENV{"USER"}."\@"."$whereAmI.yourcompanyname.com"; $filesystem=$ARGV[0]; $samples=$ARGV[1]; $sleep=$ARGV[2]; $duration=$ARGV[3]; $panicThreshold=$ARGV[4]; $mailRecipients=$ARGV[5]; @history=(); $queueLength=scalar(@history)+0; $silencerWaitTime=600/$sleep; # hardcoding this at 10 minutes.. $firstPass=1; chomp($whereAmI); $wait=($sleep*$samples)+0; print "DeathClock: Collecting $wait seconds of growth information for $filesystem. Please wait."; } }