svcmon.pl 1.0

Dienstag, 29. Mai 2007, 12:53

I love perl. I know, I mentioned it a couple of times before, but I simply love the simplicity, the timtowtdi-attitude (there is more than one way to do it) and the fact that it takes only a couple of lines of code to implement something I need.
Yesterday morning I noticed that the apache service on this host had died a day ago. I didn't notice right away and due to the massive amount of logging all the services on this machine currently produce, it was fairly hard to find out what had gone wrong.
So, to make me aware of something that actually went wrong, I wrote a little script that periodically (called via cron or something similar) checks for the existance of some predefined services.
It doesn't do really check whether the service itself is okay, just whether it accepts a connection in a timely manner. If the state changes (offline-online or online-offline) between to calls of the script, it sends me a message telling me about that.
So, here it is, in case you need something handy for exactly that task - svcmon.pl 1.0:
#!/usr/bin/perl
## svcmon.pl 1.0
## charon@episode-iv.de
use strict;
use warnings;
use IO::Socket;
use Net::SMTP;
use XML::Simple;
my $config=XMLin('config.xml') or die "No config: $!";
my($lastStatus, $currStatus);
if(-e "lastStatus.xml") { $lastStatus=XMLin('lastStatus.xml');
} for(keys(%{$config->{service}})) { my $socket=IO::Socket::INET->new( PeerAddr => $config->{service}->{$_}->{ip},
PeerPort => $config->{service}->{$_}->{port},
Proto => $config->{service}->{$_}->{proto},
Type => SOCK_STREAM
);

if($socket) { $currStatus->{$_}=1;
close($socket) } else { $currStatus->{$_}=0;
}

if($config->{service}->{$_}->{class} eq 'crit') { if($lastStatus->{$_} == 0 && $currStatus->{$_} == 1) { mailWarn("Restored service $_ at ".localtime(time),
"The service $_ is now reachable again!");
} elsif($lastStatus->{$_} == 1 && $currStatus->{$_} == 0) { mailWarn("Failed service $_ at ".localtime(time),
"The service $_ is unreachable!");
} } } open my $outFH, '>:encoding(iso-8859-1)', 'lastStatus.xml' or die "failed to open: $!";
my $lastOut=XMLout($currStatus, RootName => 'lastStatus', OutputFile => $outFH);
close($outFH);
sub mailWarn { my $smtp=Net::SMTP->new('localhost');
$smtp->mail($config->{mailto});
$smtp->to($config->{mailto});
$smtp->data();
$smtp->datasend("From: ".$config->{mailto}."\n");
$smtp->datasend("To: ".$config->{mailto}."\n");
$smtp->datasend("Priority: urgent\n");
$smtp->datasend("X-Priority: 2 (High)\n");
$smtp->datasend("Importance: high\n");
$smtp->datasend("Subject: ".$_[0]."\n");
$smtp->datasend("\n\n");
$smtp->datasend($_[1]);
$smtp->dataend();
$smtp->quit();
}

And here's a simple configuration file for that beast (to be named config.xml):
 mailto="adminemail@host.de">
	 name="Apache" ip="127.0.0.1" port="80" proto="tcp" class="crit" />
	 name="APACHE-SSL" ip="127.0.0.1" port="443" proto="tcp" class="crit" />
	 name="FTP" ip="127.0.0.1" port="21" proto="tcp" class="crit" />
>

If you want to call it from a cron script, you might want to either set a base path or modify the file operations to use absolute paths or the script might barf on you. This is also largely untested, working for me but may blow up your system in ways never seen before. Don't blame me.
Hope it's useful to anyone but me.
- Dennis

Name:
E-Mail:
Homepage:
20+3=? (Tipp: 23)