..And fixed. Here's the updated checkhost script, which now uses IO::Socket::INET instead of Net::Telnet *and* properly uses the Bacula ping. It can optionally do wake-on-lan, but some external setup is required.
-- Phil Stracchino Babylon Communications ph...@caerllewys.net p...@co.ordinate.org Landline: +1.603.293.8485 Mobile: +1.603.998.6958
#!/usr/bin/perl # # checkhost version 2.0 by Phil Stracchino # All rights assigned to the Bacula project # Licensed under the GPL v2, or at your option any later version # # Check whether a bacula client is alive and responding on the network. # Optionally, send a wake-on-LAN packet to the client before testing. # # Checkhost does not perform a Bacula connection handshake or verify # that passwords match; it only verifies that the client is reachable # on the network and responding, and that the Bacula client is running # and listening on port 9102. # # Usage: checkhost [-r] [-v] [-i seconds] hostname|ipaddress # Options: # -r, --retry: Try three times to connect (default: try only once) # -i, --interval: Specify time in seconds to wait between retries # (default: 30 seconds) # -v, --verbose: output verbose status messages, for interactive # use (default: operate silently) # -w, --wake: send wake-on-LAN packet before trying to connect # # Wake-on-LAN functionality requires the following schema to be created, by # default, with a correct authentication group set up in .my.cnf for the # group Bacula runs as: # CREATE DATABASE ethers; # CREATE TABLE ethers.ethers (id int primary key auto_increment, # hostname varchar(128), # ethers varchar(128), # index host (hostname)); # The hostname field should contain a short or fully qualified hostname, # while the ethers field should contain a list of hardware MAC addresses # for that host, separated by spaces. use strict; use Getopt::Long; use POSIX; use DBI; use IO::Socket::INET; use Net::Ping; # Return values: # -1 Program error or no host specified # 0 Success, FD found and responding # 1 Client alive but FD not listening # 2 Client not found on network my $ret = -1; my ($etherdb, $ethergroup) = qw(ethers ethers); # substitute your DB name/group here as appropriate, # IF you are using the wake-on-LAN feature my $broadcast = '255.255.255.255'; # set this to your network broadcast address my $my_ip = 0; # set this to the IP of the Director my $fdport = 9102; my ($host, %opts); die "ERROR: You must set the broadcast and my_ip addresses before running this tool" unless ($my_ip); Getopt::Long::Configure ("bundling"); GetOptions(\%opts, 'interval|i=i', 'retry|r', 'verbose|v+', 'wake|w'); $host = shift || die "No host specified!\n"; $ret = check_host($host) if ($host); print "Client $host not found on network\n" if ($ret == 2); print "Returning value $ret\n" if ($opts{verbose} > 1); exit ($ret); sub check_host { my $host = $_[0]; my ($hostname, $p, $retrycount); if ($host =~ /^\d+\.\d+\.\d+\.\d+$/) { my $ip = inet_aton($host); $hostname = gethostbyaddr($ip, AF_INET); print "Host $host has name $hostname\n" if ($opts{verbose}); } else { $hostname = $host; printf("Client $host has address %d.%d.%d.%d\n", unpack('C4', (my @addrs = (gethostbyname($host))[4])[0])) if ($opts{verbose} > 1); } if ($opts{wake}) { foreach my $ether (split(/\s+/, get_ethers_by_host(shift()))) { wake_host_by_hwaddr($ether); } printf("Wake-on-LAN packet sent to $host; sleeping for %s seconds\n", $opts{interval} || 30) if ($opts{verbose}); sleep ($opts{interval} || 30); } $p = Net::Ping->new("icmp"); $p->bind($my_ip); $retrycount = ($opts{retry} ? 3 : 1); while ($retrycount && ($ret != 0)) { if ($p->ping($host, 2)) { print "Host $host is alive\n" if ($opts{verbose}); my ($fdsocket, $buf); $fdsocket = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $fdport, Proto => 'tcp') || die "Socket $host:$fdport: $!"; $fdsocket->send(pack("N/a","Ping")) || die "Send: $!"; $buf = <$fdsocket> || die "Recv: $!"; $fdsocket->close() || die "Cannot close socket: $!"; if ($buf =~ /2000 Ping OK/) { print "Bacula-FD is listening on $host:$fdport\n" if ($opts{verbose}); $ret = 0; } else { print "Bacula-FD is not running on host $host\n"; $ret = 1; } } else { $ret = 2; } $retrycount--; if ($opts{retry} && ($ret != 0)) { printf("\tNot found on try %d", 3 - $retrycount) if ($opts{verbose}); if ($retrycount) { printf("; sleeping for %s seconds before retrying\n", $opts{interval} || 30) if ($opts{verbose}); sleep($opts{interval} || 30); } else { print "\n" if ($opts{verbose}); } } } $p->close(); return ($ret); } sub wake_host_by_hwaddr { my ($packet, $hwaddr, $raddr, $port); foreach (split /:/, $_[0]) { $hwaddr .= chr(hex($_)); } $packet = chr(0xFF) x 6 . $hwaddr x 16; socket((SOCK, AF_INET, SOCK_DGRAM, getprotobyname('udp'))) || die "socket : $!"; setsockopt(SOCK, SOL_SOCKET, SO_BROADCAST, 1) || die "setsockopt : $!"; $port = getservbyname('discard', 'udp'); $raddr = gethostbyname($broadcast); send(SOCK, $packet, 0, pack_sockaddr_in($port, $raddr)) || die "send : $!"; close (SOCK); return; } sub get_ethers_by_host { my ($dbh, $sth, $query, $ethers); $dbh = open_db(); $query = sprintf('SELECT ethers FROM ethers WHERE hostname = \'%s\'', $_[0]); $sth = $dbh->prepare($query); $sth->execute || die "Error:" . $dbh->errstr . "\n"; while (my $ref = $sth->fetchrow_arrayref) { $ethers = $$ref[0]; } $sth->finish(); $dbh->disconnect(); return ($ethers); } sub open_db { my $home = $ENV{'HOME'}; my ($dsn, $dbh, $user, $password); $dsn = "DBI:mysql:database=$etherdb;" . "mysql_read_default_group=$ethergroup;" . "mysql_read_default_file=$home/.my.cnf;" . "mysql_mysql_client_found_rows=TRUE;" . "mysql_mysql_ssl=TRUE"; $dbh = DBI->connect($dsn, undef, undef, {RaiseError => 1, AutoCommit => 0}); return ($dbh); }
_______________________________________________ Bacula-devel mailing list Bacula-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/bacula-devel