#!/usr/bin/perl -w # productname: raidcheck # productrelease: V0.50b # # copyright 2003-2004 by kapper.net # reach kapper.net at http://kapper.net/ or via postal mail to: # kapper & partner communications keg, kapper.net gmbh # loeblichgasse 6, A-1090 Vienna, Austria # development-team: mm@kapper.net, hk@kapper.net # # 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 # http://www.gnu.org/licenses/gpl.txt # ---------------------------------------------------- # project started: 30.09.2003 # ---------------------------------------------------- # synopsis: # script writes data from "cat /proc/mdstat" into the file "/var/log/raidstatus". # it also can compare the data with the current output of the above command. # if specified the result of that comparision can be sent via email to the # given email address # # requirements: Mail::Sendmail perl-class (available from http://alma.ch/perl/mail.html or CPAN) # local sendmail/postfix/whatever mailserver to deliver the sent email if required # alternatively you can edit Sendmail.pm to have your smtp-server globally set # # usage: raidcheck.pl # # possible parameters # ------------------- # --read-mdstat | -r # reads data from /proc/mdstat and saves it in /var/log/raidstatus # # --check-mdstat | -c # reads data from /var/log/raidstatus and compares it with /proc/mdstat # if there is a difference, both is printed to STDOUT # --sendto | -s # this is an optional parameter, if used, it requires an email address, # to which a possible difference between /var/log/raidstatus and /proc/mdstat # is sent. # # # # History: # -------- # 05.08.2004 hk first-output now is "current" status in email and printout for # a fast look at things in email-preview, etc # 29.07.2004 hk adopt for fedora-core-series of redhat, release it, short params # 30.01.2004 hk from-email-address is now always similar to to-address # 30.01.2004 hk added "die" option for raidstatus-file-open on check # 20.01.2004 mm added "O_CREAT" to sysopen in "--read-mdstat" case # 06.09.2003 mm ready for usage! # 06.09.2003 mm changed sysopen params when writing, added sendmail class calls # 03.09.2003 mm param checking, problem with some chars and their mysterious apperance # 30.09.2003 mm test with file io and mdstat call, usage, ... # use strict; use Fcntl; # for file io use Mail::Sendmail; # for sendmail class my $CMD = "cat /proc/mdstat"; # cmd to get raid status my $FILENAME = "/var/log/raidstatus"; # in that file raid status is saved when --read-mdstat # if needed please change the above systemcall and filepath/-name. # possible params my $PARAM_READ = "--read-mdstat"; my $PARAM_CHECK = "--check-mdstat"; my $PARAM_SEND = "--sendto"; my $PARAM_READ_SHORT = "-r"; my $PARAM_CHECK_SHORT = "-c"; my $PARAM_SEND_SHORT = "-s"; # constants my $PDEFAULT = 0b0000; my $callparam = $PDEFAULT; # don't ask ;-) my $READ = 0b1000; my $CHECK = 0b0100; my $SEND = 0b0010; my $EQUAL = 123443; my $NOTEQUAL = 394342; #my $FROM = ""; # set to get a specific from-address my $SUBJECT = "raidstatus failed: " . `hostname`; # -------------------------------------------- # compares the 2 submitted strings and returns # above specified constants for equal / notequal sub compare($$) { my $one = $_[0]; my $two = $_[1]; my $rc = $NOTEQUAL; # - - - - - - - - $one =~ s/^\s+//; # eliminates heading "whitespaces" $one =~ s/\s+$//; # eliminates trailing "whitespaces" $two =~ s/^\s+//; # eliminates heading "whitespaces" $two =~ s/\s+$//; # eliminates trailing "whitespaces" if ($one eq $two) # if identical { $rc = $EQUAL; } return ($rc); } # -------------------------------------------------------------------------- # give the user some hints on how to use... sub usage { my $usageinfo = ""; $usageinfo .= "---------------------------------------------------------\n"; $usageinfo .= "usage: $0 \n\n"; $usageinfo .= "possible parameters\n"; $usageinfo .= "-------------------\n"; $usageinfo .= "\t--read-mdstat | -r\n"; $usageinfo .= "\t reads data from /proc/mdstat and saves it in $FILENAME\n\n"; $usageinfo .= "\t--check-mdstat | -c\n"; $usageinfo .= "\t reads data from $FILENAME and compares it with /proc/mdstat\n"; $usageinfo .= "\t if there is a difference, both is printed to STDOUT\n"; $usageinfo .= "\t --sendto | -s \n"; $usageinfo .= "\t this is optional - if used - it requires an email address,\n"; $usageinfo .= "\t a possible difference between $FILENAME and /proc/mdstat\n"; $usageinfo .= "\t is sent to this address using this email also as from-tag.\n\n"; $usageinfo .= "Have fun and may your mirrors never break ;)\n"; $usageinfo .= "copyright 2003-2004 by http://kapper.net/\n\n"; $usageinfo .= "---------------------------------------------------------\n"; die "$usageinfo"; } # ---------------------------------------- # main { my $param = shift @ARGV; # shift gets the next parameter and cuts it off ARGV my $infotxt = ""; my ($mailadr,$cmdreply, $rc,$maxpos,$readdata); my %mail; # sendmail needs this while (defined $param) # as long as there are parameters { if (($param eq $PARAM_READ) || ($param eq $PARAM_READ_SHORT)) { $callparam |= $READ; } elsif (($param eq $PARAM_CHECK) || ($param eq $PARAM_CHECK_SHORT)) { $callparam |= $CHECK; } elsif (($param eq $PARAM_SEND) || ($param eq $PARAM_SEND_SHORT)) { $callparam |= $SEND; $mailadr = shift @ARGV; # next parameter needs to be an email address if (!($mailadr)) { $callparam = $PDEFAULT; # default value ... means error and needs to show usage } else { if (!($mailadr =~m/.+\@.+\..+/)) { $callparam = $PDEFAULT; # default value ... means error and needs to show usage } } } else # no params { $callparam = $PDEFAULT; # default value ... means error and needs to show usage } $param = shift @ARGV; # get next parameter } if ($callparam == ($READ | $SEND)) { $callparam = $PDEFAULT;} # combined not possible if ($callparam == ($SEND)) { $callparam = $PDEFAULT;} # no sendonly option &usage if ($callparam == $PDEFAULT); if (($callparam & $READ) == $READ) { $cmdreply = qx($CMD); # get current mdstat info # O_WRONLY ... in "read" case, mdstat output will be written into file # O_CREAT ... Create the file if it does not exist # O_TRUNC ... truncate the file (esp. necessary if file exists and new mdstat output is # not as long as the "old" one stored in file $rc =sysopen(FH, $FILENAME, O_WRONLY | O_CREAT | O_TRUNC) or die "raidstatus-file cannot be opened\n"; if ($rc) { if (not (syswrite(FH, $cmdreply, length($cmdreply)-1))) { print STDOUT "could not write raidstatus-file\n"; } } close(FH); $callparam = $PDEFAULT; # default value ... means error and needs to show usage } if (($callparam & $CHECK) == $CHECK) { $cmdreply = qx($CMD); $rc =sysopen(FH, $FILENAME, O_RDONLY) or die "raidstatus file not found!\n"; if ($rc) { $maxpos = sysseek(FH, 0, 2); # check file length $rc = sysseek(FH,-$maxpos, 2); # position on file begin $rc = sysread(FH, $readdata, $maxpos); # get whole data if (!($rc)) { print STDOUT "raidstatus could not be read from status-file!\n"; } $rc = compare($cmdreply, $readdata); if ($rc == $NOTEQUAL) { $infotxt = "----------------------------------------\n"; $infotxt .= "Attention! raid-status changed: \n"; $infotxt .= "----------------------------------------\n"; $infotxt .= "current status shows:\n"; $infotxt .= "----------------------------------------\n"; $infotxt .= "$cmdreply"; $infotxt .= "----------------------------------------\n"; $infotxt .= "stored status:\n"; $infotxt .= "----------------------------------------\n"; $infotxt .= "$readdata\n"; $infotxt .= "----------------------------------------\n"; $infotxt .= "please check your raid and take\nappropriate action.\n"; $infotxt .= "raid-status-check sponsored by http://kapper.net/\n"; } } close(FH); if (($callparam & $SEND) == $SEND) { if ($infotxt ne "") { # set mail parameters for sending %mail = ( To => $mailadr, From => $mailadr, Subject => $SUBJECT, Message => $infotxt );# sendmail(%mail) or die $Mail::Sendmail::error; #print "OK. Log says:\n", $Mail::Sendmail::log; } } else { if ($infotxt) # if param = --check-mdstat without email { print STDOUT "\n$infotxt\n"; } } } } # end main