#!/usr/local/bin/perl # # Bryan's cheesy-ass pinger script for monitoring the status of machines # we all know and love. I fully realize this script is completely worthless # and unnecessary, but you must admit that it's handy having a seemingly # reliable machine like tep12 monitoring the stability of other machines # is pretty darn cool. # # Important note: this script needs to be run as root in order for the ICMP # ping to work. Only root has the access necessary to create an ICMP ping # object. You can, however, use an echo ping instead if you don't want # to or cannot run this as root. See down below for further details. # # Other important notice: hosts to be pinged reside in an external # configuration file, which is specified on the command line. Its contents # should look something like this: # # host1.some.com:0 # host2.some.other.com:0 # host3.yet.another.org:0 # # Basically, one line for each host to be monitored. Don't try putting # comments or blank lines in the file or anything like that. No extraneous # spaces either. At the end of the host name, place a colon, and then the # number zero. The pinger script will interpret a zero as an "unpingable" # host, and replace it with a number one if the host was pingable and then # notify whoever should be notified that it changed. You could enter a one # instead of a zero, but if you put in a zero it'll mail you the first time # the script is run (assuming it can ping those hosts) so you'll see that # the script is working. # Needed includes to make this thing work. use Net::Ping; # Make sure you have this installed; if not, head over to # http://www.cpan.org/ and get the latest version of the Net # library, it should have Net::Ping in it. # Variable initialization. Probably excessive in some cases. # # $timeout --- time (in seconds) it will wait before timing out on a ping. # This variable can be increased or decreased to compensate for # network stupidity. If pinging across a WAN, a longer ping # time is good. In a LAN, short ping times are best. Keep in # mind that pinging hosts across a WAN isn't a reliable way of # keeping in touch with them, since things like ICMP filtering # and general network slowness can give false results. # # $target --- used for indicating which host it is going to ping. This did # not need to be set, I only have it in there out of habit from # programming C code. # # $oldstate --- If this had been C code, I would have made these two # $newstate --- variables into macros instead. But, it's Perl, so I # used variables. They're just values indicating different # elements in the array and what those elements mean. Nothing # special or terribly important. I did it this way for # clarity. # # $notify --- who gets e-mailed when the script detects a change in the # pingability of a host. Set this to reflect the proper # recipient for your implementation. Multiple recipients can be # specified in normal e-mail fashion with a comma seperating # each recipient. And be sure to put a backslash in front of # each @ symbol... Perl 5 needs it there. # # $whosend --- set this to whoever the generated e-mail should be from. # Normally I would say that the recipient should also be used # as the sender. I had to make two variables in case there are # going to be multiple recipients since there can only be one # sender. # # $sendmail --- the full path to the sendmail executable. This can vary # from platform to platform. In the case of SGI and Sun as # well (I think) it's /usr/lib/sendmail but with things like # FreeBSD and Linux it seems to be /usr/sbin/sendmail most of # the time. Be sure to leave the -t option in there. $timeout = 120; $target = 0; $oldstate = 1; $newstate = 2; $notify = "bryan\@badideas.webicommerce.com"; $whosend = "bryan\@badideas.webicommerce.com"; $sendmail = "/usr/sbin/sendmail -t"; # Set this to one if you want debugging mode on. #$debug = 1; # This block reads in the configuration file. The configuration file should # have been specified on the command line. open(CONFFILE, "$ARGV[0]") or die "$0: error during open(): $!\n"; while () { ($hostname[$whichhost][$target], $hostname[$whichhost][$oldstate]) = split(/:/, $_); if ($debug) # If debugging is on, read what it interprets the # previous host statuses to be. { print "$hostname[$whichhost][$target]: host last marked as "; if ($hostname[$whichhost][$oldstate] == 1) {print "up.\n";} else {print "down.\n";} } ++$whichhost; } close(CONFFILE); # This creates a ping object so we can actually do pinging. Again, you # have to be root to use ICMP pings. Change the word "icmp" to "tcp" if # you want to do echo pinging instead, but make sure the host to be echo # pinged is able to echo. This is specified in /etc/inetd.conf and carried # on port 7 for both TCP and UDP. For an easy way to check, telnet to port # 7 on any given host and start typing at it. It should echo what you type # back at you. # # You can also do UDP echo pinging instead of TCP if you want. Just change # the "icmp" to "udp" instead of "tcp" and it should work, assuming UDP # echoing is enabled. $p = Net::Ping->new("icmp", $timeout); # Begin the actual pinging process, set the state according to the result. while ($whichhost) { --$whichhost; if ($debug) {print "Pinging host $hostname[$whichhost][$target].\n";} if ($p->ping($hostname[$whichhost][$target])) { if ($debug) {print "Host is up.\n";} $hostname[$whichhost][$newstate] = 1; if ($hostname[$whichhost][$newstate] != $hostname[$whichhost][$oldstate]) {$statechange = 1;} } else { if ($debug) {print "Host is down.\n";} $hostname[$whichhost][$newstate] = 0; if ($hostname[$whichhost][$newstate] != $hostname[$whichhost][$oldstate]) {$statechange = 1;} } } # Now write back out the new version of the conf file, include # the new status values. open(CONFFILE, ">$ARGV[0]"); while ($whichhost <= $#hostname) { if ($debug) {print "$hostname[$whichhost][$target]:$hostname[$whichhost][$newstate]\n";} print CONFFILE "$hostname[$whichhost][$target]:$hostname[$whichhost][$newstate]\n"; ++$whichhost; } close(CONFFILE); # Notify the individual(s) specified in the $notify variable if the # states of the target hosts have changed. if ($statechange) { open(SENDMAIL, "|$sendmail") || die "$0: unable to open sendmail: $!\n"; select(SENDMAIL); print "To: $notify\n"; print "From: System Monitoring Script <$whosend>\n"; print "Subject: Network host status change.\n\n"; while ($whichhost) { --$whichhost; if ($hostname[$whichhost][$newstate] != $hostname[$whichhost][$oldstate]) { if ($hostname[$whichhost][$newstate]) {print "Host $hostname[$whichhost][$target] is up.\n"} else {print "Host $hostname[$whichhost][$target] is down.\n"} } } print "\n.\n"; select(STDOUT); }