#!/usr/bin/perl -w
# check_disk_space.pl
# Send an email notice out when mounted file systems are past a given percentage.
#
# Perl v5.8.8
# Tested under RHEL4, RHEL5, and RHEL6
#
# When this script is run it will check the status of all mounted file systems
# using the df command and if the percentage is past the given critical set level
# will send out an email notice every time it is run. A set warning percentage
# only send out an email every number_of_days_before_next_warning.
# It keeps track of the number_of_days_before_next_warning using lock files.
#
# The check_disk_space.xml config file should be in the same directory as the program,
# and be given the same name if the program name is changed.
#
#check_disk_space.xml
# < = <, > = >
#-
#
# 90
# 99
# 7
# /home//
# yes
# no
# /bin/df
# /bin/find
# /bin/rm
# /bin/date
# /bin/hostname
# /usr/sbin/sendmail
# user@mail.com
# Disk Check <user@system.com>
# Disk Check
#
# Disk Check
#
#-
#
# The following config toggles whether the host appears in the subject line.
# A value of "yes" will trigger the hostname include.
# no
#
# The following line troggles weather or not lock files are created with a
# hidden dot prefix. A value of "yes" will trigger the hidden file.
# yes
#
# The following line determins where the lock files will be placed. If left
# blank the lock files will be saved in the current directory. Be sure to
# end a directory path with a "/" in the config file.
#/home//
#
# This program doesn't require any special privlege accesses, except read/write
# access to the lockfile directory.
#
# Recommended Crontab Entry:
# # Check precentage of disk used every day and send out a email if critical levels are reached.
# 0 7 * * * cd /home//bin; /home//bin/check_disk_space.pl
#
# Script requirements: install XML Simple
# yum install perl-XML-Simple
#
# History:
# ---------------------------------------------------------------------------
# 2014-02-24 kienenbe Started, works.
#
use XML::Simple;
use File::Basename;
#use Data::Dumper;
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Get the mount points from the df command.
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
sub get_filesystem_data {
my @mount_points;
my $df_line;
my $filesystem;
my $junk;
my $total_size;
my $amount_used;
my $amount_avalible;
my $percent_used;
my @df_output = `$command_df -lh`;
foreach $df_line (@df_output) {
if ($df_line =~ m#\d\%#) {
#print "->$df_line";
# Break out the needed data.
($percent_used,$filesystem) = split(/% /, $df_line);
chomp $filesystem;
($junk,$total_size,$amount_used,$amount_avalible,$percent_used) = split(/\s+/, $percent_used);
# Record the data for the filesystem mount point.
$filesystem_info{"$filesystem"}{"total_size"} = "$total_size";
$filesystem_info{"$filesystem"}{"amount_used"} = "$amount_used";
$filesystem_info{"$filesystem"}{"amount_avalible"} = "$amount_avalible";
$filesystem_info{"$filesystem"}{"percent_used"} = "$percent_used";
}
}
}
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Format the text report output for display.
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# system.mysystem.org
# Fri Feb 21 13:47:03 UTC 2014
#
# ---- Critical file system space at or above 45% ----
#
# Size Used Avail Use% Mounted on
# 1.8T 769G 973G 45% /mnt/backup
# 1.1T 436G 550G 45% /data
#
# ---- Warning file system space at or above 8% ----
#
# Size Used Avail Use% Mounted on
# 485M 38M 422M 9% /boot
sub format_report_output {
$warning_duplicate_check = 0;
$critical_duplicate_check = 0;
my $filename;
# Cycle though each filesystem mount point.
foreach my $filesystem (keys %filesystem_info) {
if ($filesystem_info{"$filesystem"}{"percent_used"} >= $critical_percent_used) {
if ($critical_duplicate_check eq 0) {
# Only print the date and time stamp if this is the first warning check.
if ($warning_duplicate_check eq 0) {
$formated_report .= "\n $Hostname\n $TimeStamp\n";
}
$formated_report .= "\n ---- Critical file system space at or above ${critical_percent_used}% ----\n\n";
$formated_report .= sprintf(" %5s %5s %5s %5s %-60s\n", "Size","Used","Avail","Use%","Mounted on");
$critical_duplicate_check = 1;
}
$formated_report .= sprintf(" %5s %5s %5s %5s %-60s\n", $filesystem_info{$filesystem}{total_size},$filesystem_info{$filesystem}{amount_used},$filesystem_info{$filesystem}{amount_avalible},"$filesystem_info{$filesystem}{percent_used}%",$filesystem);
}
elsif ($filesystem_info{"$filesystem"}{"percent_used"} >= $warning_percent_used) {
# Concert the file system mount point to a file name.
$filename = &convert_filesystem_path_to_name($filesystem);
# Remove warning locks for file system mount points past the number_of_days_before_next_warning
&remove_warning_lockfile($filename);
if (&check_for_warning_lockfile($filename)) {
# Do nothing
}
else {
if ($warning_duplicate_check eq 0) {
# Only print the date and time stamp if this is the first critical check.
if ($critical_duplicate_check eq 0) {
$formated_report .= "\n $Hostname\n $TimeStamp\n";
}
$formated_report .= "\n ---- Warning file system space at or above ${warning_percent_used}% ----\n\n";
$formated_report .= sprintf(" %5s %5s %5s %5s %-60s\n", "Size","Used","Avail","Use%","Mounted on");
$warning_duplicate_check = 1;
}
$formated_report .= sprintf(" %5s %5s %5s %5s %-60s\n", $filesystem_info{$filesystem}{total_size},$filesystem_info{$filesystem}{amount_used},$filesystem_info{$filesystem}{amount_avalible},"$filesystem_info{$filesystem}{percent_used}%",$filesystem);
# Create a warning lock file for the current file system mount point.
&write_warning_lockfile($filename);
}
}
}
}
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Send out the email to the recipients.
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
sub send_email_notice {
# Send full report to recepiants(s)
# Remove complaint about global empty vars such as ${mail_subject_postfix}
no warnings 'uninitialized';
if ( $warning_duplicate_check eq 1 or $critical_duplicate_check eq 1) {
open (MAIL, "|$command_sendmail -t") || die "Can't open the mail binary: ${command_sendmail}!\n";
print MAIL "X-Mailer: ${mail_x_mailer}\n";
print MAIL "Return-Path: root\@${Hostname}\n";
print MAIL "From: ${mail_from} \n";
print MAIL "To: ${mail_to}\n";
if ($Include_Hostname_In_Subject eq "yes") {
print MAIL "Subject: ${mail_subject_prefix}${Hostname}${mail_subject_postfix}\n\n";
}
else {
print MAIL "Subject: ${mail_subject_prefix}${mail_subject_postfix}\n\n";
}
print MAIL "\n${formated_report}\n\n";
close (MAIL);
}
}
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Conver the file system mount point to a lockfile name.
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
sub convert_filesystem_path_to_name {
my $filesystem_path = shift;
my $lockfile_name;
$filesystem_path =~ s#/#_#g;
$filesystem_path =~ s# #_#g;
if ($hide_warning_lockfile eq "yes") {
$lockfile_name = "." . $program_name . "_warning_lock" . $filesystem_path;
}
else {
$lockfile_name = $program_name . "_warning_lock" . $filesystem_path;
}
return ($lockfile_name);
}
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Save the warning lockfile
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Creates the locking file in the format of:
# / are converted to underscores for mount point.
# <.><_warning_lock>
#
# -rwxr-x---. 1 user user 9211 Feb 21 13:46 df.pl*
# -rw-rw-r--. 1 user user 0 Feb 21 13:47 .df_warning_lock_boot
sub write_warning_lockfile {
my $lockfile_name = shift;
my $lockfile_directory = "./";
# Check if the $warning_lock_directory has been set.
# If so use it instead of the current directory.
if ( $warning_lock_directory) {
$lockfile_directory = $warning_lock_directory;
}
# Check for the warning lockfile.
# If it already exists exit.
if ( -e "${lockfile_directory}${lockfile_name}" ) {
return;
}
open (WARNING_LOCKFILE, '>', ${lockfile_directory}.${lockfile_name}) or $formated_report .= "\nUnable to save warning lockfile for ${lockfile_directory}${lockfile_name}\n Warnings will be sent out every time the script runs.\n\n";
close (WARNING_LOCKFILE);
}
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Check for the warning lockfile
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
sub check_for_warning_lockfile {
my $lockfile_name = shift;
my $lockfile_directory = "./";
# Check if the $warning_lock_directory has been set.
# If so use it instead of the current directory.
if ( $warning_lock_directory) {
$lockfile_directory = $warning_lock_directory;
}
# Check for the warning lockfile.
if ( -e "${lockfile_directory}${lockfile_name}" ) {
return 1;
}
else {
return 0;
}
}
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Remove the warning lockfile
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
sub remove_warning_lockfile {
my $lockfile_name = shift;
my $lockfile_directory = "./";
# Check if the $warning_lock_directory has been set.
# If so use it instead of the current directory.
if ( $warning_lock_directory) {
$lockfile_directory = $warning_lock_directory;
}
# If the file doesn't exist just exit.
if (! -e "${lockfile_directory}${lockfile_name}" ) {
return;
}
# Remove the warning lockfile.
`$command_find ${lockfile_directory}${lockfile_name} -mtime +${number_of_days_before_next_warning} -type f -exec $command_rm -f {} \\;`;
}
# Run the main routine.
&Start;
#>>>>>>>>>>>>>
#> Main Start
#>>>>>>>>>>>>>
sub Start {
#declare all major variables used in this subroutine.
local (%filesystem_info, $TimeStamp, $Hostname, $formated_report, $warning_duplicate_check, $critical_duplicate_check, $program_name);
# Obtain configuration from a file with a name inferred from this
# script's name.
($program_name) = split(/\./, basename($0));
my $program_full_name =basename($0);
# if you don not wish to use XML::Simple comment out next line..
$config = XMLin("${program_name}.xml", SuppressEmpty => 'undef');
# if you don not wish to use XML::Simple configure variable directly here.
# if you don't use the XML config file you must place a "\" in front of the
# "@" in your email address so it is not interpreted as an array variable.
# ex: local $mail_to = "vadapt.notice\@gmail.com";
local $command_date = $config->{'command_date'};
local $command_df = $config->{'command_df'};
local $command_find = $config->{'command_find'};
local $command_hostname = $config->{'command_hostname'};
local $command_rm = $config->{'command_rm'};
local $command_sendmail = $config->{'command_sendmail'};
local $warning_percent_used = $config->{'warning_percent_used'};
local $critical_percent_used = $config->{'critical_percent_used'};
local $number_of_days_before_next_warning = $config->{'number_of_days_before_next_warning'};
local $warning_lock_directory = $config->{'warning_lock_directory'};
local $hide_warning_lockfile = $config->{'hide_warning_lockfile'};
local $Include_Hostname_In_Subject = $config->{'Include_Hostname_In_Subject'};
local $mail_to = $config->{'mail_to'};
local $mail_from = $config->{'mail_from'};
local $mail_subject_prefix = $config->{'mail_subject_prefix'};
local $mail_subject_postfix = $config->{'mail_subject_postfix'};
local $mail_x_mailer = $config->{'mail_x_mailer'};
# Get the hostname
local $Hostname = `$command_hostname -f`;
chomp $Hostname;
# Get the date timestamp
$TimeStamp = `$command_date`;
chomp $TimeStamp;
# Do the work
&get_filesystem_data;
&format_report_output;
&send_email_notice;
# uncomment to print report when run.
#print $formated_report;
#End the program
exit;
}