[Nagios-users] Citrix Plugin

Tom DE BLENDE tdeblend at gcc.dhl.com
Tue Sep 17 09:16:55 CEST 2002


Grae Noble wrote:
> 
> Theres some interesting plugins for BigBrother that could be modified to work with netsaint. I've modified a few but most require  extensive changes to work.
> 
> http://www.deadcat.net
> 
> >From memory there is one there for citrix. (quite neat too, it will check for the existence of a specific application ).


OK. I've grabbed the Big Brother Citrix plugin from Ed Rolison
(http://www.deadcat.net/cgi-bin/download.pl?section=1&file=bb-citrix.pl).
He managed to write it with the help of tcpdump. I personally think
this is very cool and a job well done. Thanks Ed.

However, the output that NetSaint expects is totally different from
the output BB wants. So I've changed parts of the code so it works
with NetSaint. I then tested it in our environment, and it works OK.

This plugin does not take any command line arguments. Just adjust the
beginning of the script so it reflects your setup. If you use a farm,
then you can use the broadcast address (that's how I use it),
otherwise add the server address(es) you want to check.

Good luck,
Tom De Blende

So here it is:

********************************************************************************************************************************************


#!/usr/bin/perl -w

# Ed Rolison 15/06/02
# ed at nightstalker.net
# If it doesn't work, please let me know, I've only had access to my
environment
# so I'm not 100% sure.
#
# No liability accepted for problems, mistakes, inaccuracies or
nuclear war.
#
# If you want to mess around with this script, then please feel free
to do so.
# however, if you add anything 'funky' then I'd really appreciate
hearing about it.
#
# Oh, and if you do ever make huge amounts of money out of it, cut me
in :)
# version 1.5 ish
#
# Revision history:
# version 1.0 ish. Shell script which used netcat
# version 1.1 ish. extended script to use a non-broadcast method of
querying
# version 1.3 re-written in perl
# version 1.4 updated perl for 'yellow' and 'red' apps + some minor
fixes.
# version 1.5 ish added support for 'long' published apps lists.

# Changed plugin output so it works with NetSaint.
# Tom De Blende 16/09/02
# tom.db at pandora.be

use IO::Socket::INET;
use IO::Select;
use FileHandle;

#configuration section

my $debug = 0;
my $timeout = 1;
my $test_count = 2;
#You might have to change this...
my $date = `date +%s`;
my $buffer_size = 1500; # buffer size used for 'recv' calls.

#a word of warning - at the moment these test _will_ match substrings
#of the applications. So if you have something like 'Desktop' and
'DesktopFull'
#Desktop will match on either. I'll probably fix this in a later
version.

#a 'single' target will be assumed to be a broadcast address for a
cluster.
#that's probably not right, but I've got a local subnet cluster, and
#a remote cluster that I can't broadcast to. So this works well
enough.
#feel free to hack in support for discriminating between
broadcast/standalone though....
 

my (%tests) = 
(
  'netsaint' =>  #this is the name of the 'machine that this test
appears to 
               #be from in the bb display
  {
    testname => 'icaclstr',       #hopefully fairly obvious - which
column in BB
    target => [ "172.16.200.255" ],  
                               #either a list of servers, or a
broadcast address
    red_published_apps =>           # apps which trigger a 'CRITICAL'
when down
      [ "Critical1", "Critical2", "Critical With Spaces 3" ],
    yellow_published_apps =>        #apps which trigger a 'WARNING'
when down.
      [ "Warning1", "Not So Critical", "Warning App 3" ],
 
    longlist => 0, # this is for if you have many published
applications.
                   # if you set it, it won't do any harm, but may slow
the test
                   # down a little. (Since it does a 'recv' twice
instead of 
                   # once and therefore may have to wait for a
timeout).
  },
#  'othersite' =>
#  { 
#    testname => 'icaclstr',
#    target => 
#      [ "server1","server2","server3","server4","server5"],
#    red_published_apps => 
#      [ "Critical 4" ],
#    yellow_published_apps => [ "Critical 5" ],
#    longlist => 1
#  }, 
);

#End user config.

#ica port number. 
my $ica_port = 1604;             #what port ICA runs on. Unlikely to
change.

#definitions of query strings. Change at your own risk :)
#this info was gathered with tcpdump whilst trying to use an ICA
client,
#so I'm not 100% sure of what each value is.
my $app_response_offset = 0x28;
my @bcast_helo = 
  (
0x1e,0x00,0x01,0x30,0x02,0xfd,0xa8,0xe3,0x00,0x02,0xf5,0x95,0x9f,0xf5,0x30,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00
);
my @bcast_query_app = 
  (
0x24,0x00,0x01,0x32,0x02,0xfd,0xa8,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x00,0x02,0x00,0x00,0x00,0x00,0x00,
0x00 );

my @direct_helo = 
  (
0x20,0x00,0x01,0x30,0x02,0xfd,0xa8,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
);

my @direct_query_app = (
0x2c,0x00,0x02,0x32,0x02,0xfd,0xa8,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
);

if ( $debug == 1 )
{
  print "*** Running in test mode\n";
}

#we open 2 UDP ports - one to send the 'search' and the other to query
the app list.
my $UDP_HELO = IO::Socket::INET -> new ( Proto => "udp" ) || die
"Socket failure: $!";
my $UDP_QUERY = IO::Socket::INET -> new ( Proto => "udp" ) || die
"Socket failure: $!";

#select is here to allow us to set timeouts on the connections.
Otherwise they 
#just 'stop' until a server appears.
my $select_helo = IO::Select -> new ($UDP_HELO) || die "Select
failure: $!";
my $select_query = IO::Select -> new ($UDP_QUERY) || die "Select
failure: $!";
my $send_addr ;

#helo needs to be broadcast, but query does not.
$UDP_HELO -> sockopt(SO_BROADCAST, 1 );
#autoflush both.
$UDP_HELO -> autoflush(1);
$UDP_QUERY -> autoflush(1);

my ( $remote_host, $buff, $buff2, $raddr, $rport, $rhost,
@remote_response );
#right, run through the %tests hash above, and test each of the
machines listed.
foreach my $test_target ( keys ( %tests ) )
{
  $color = "clear";
  $line2 = "";
  if ( $debug ) { print "testing $test_target\n" };
  $buff = "";
  $buff2 = "";
  my $this_test = 0;
  #If the first test fails, (as it sometimes does, UDP being
unreliable)
  #then it'll retry up to $test_count times (see at the top).
  while ( $this_test <= $test_count && !$buff ) 
  {
    if ( $debug ) { print "Running test: ", ++$this_test,"\n" };
    #if we have multiple targets, we probe each of them until we get a 
    #response...
    foreach my $destination ( @ { $tests{$test_target}{target} } ) 
    {
      my @query_message = @bcast_helo;
      #if we haven't got a response yet, try this one.
      if ( !$buff ) 
      {
        if ( $debug ) { print "Querying $destination for master
browser\n"; }
        $send_addr = sockaddr_in("$ica_port",
inet_aton("$destination") );
        if ( $#{ $tests{$test_target}{target} } > 1)
        {
          @query_message = @direct_helo;
        }
        $UDP_HELO -> send ( pack ("C"x $#query_message,
@query_message), 0, $send_addr ); 
        if ( $select_helo -> can_read($timeout) )
        {
          $remote_host = $UDP_HELO -> recv($buff, $buffer_size, 0 );
        }
      } # if (!$buff)
    } #foreach destination
  } # if test count loop
  #ok we've looped several times, looking for a response. If we don't
have one 
  #yet, we simply mark the whole lot as being unavailable.
  if ( $buff )
  {
    ($rport, $raddr) = sockaddr_in ( $remote_host );
    $rhost = gethostbyaddr ( $raddr, AF_INET );
    my @tmpbuf = unpack ("C" x length($buff), $buff );
    if ( $debug )
    {
      print "$rhost:$rport responded with: ",length($buff), "
bytes\n";
      foreach (@tmpbuf)
      {
        printf ("%02X ", $_ );
      }
      print "\n";
    } #if debug

    #now we have a response, then we need to figure out the master
browser, and 
    #query it for published applications...

    $master_browser =
"$tmpbuf[32].$tmpbuf[33].$tmpbuf[34].$tmpbuf[35]";
     
    #ok should probably error check this, because it's remotely
possible
    #that a server response might be completely wrong...
      
    $color="green";
    if ( $debug ) { print "Master browser = $master_browser\n" } ;

    $send_addr = sockaddr_in($ica_port, inet_aton("$master_browser")
);
    if ( $#{ $tests{$test_target}{target} } > 1)
    {
       if ( $debug ) { print "using directed query\n" };
       @query_message = @direct_query_app;
     }
     else
     {
       if ( $debug ) { print "using broadcast query\n" };
       @query_message = @bcast_query_app;
     }
   
    #now we send the appropriate query string, to the master browser
we've found.
    $buff = "";
    while ( $this_test <= $test_count && !$buff ) 
    {
      $UDP_QUERY -> send ( pack ("C"x $#query_message,
@query_message), 0, $send_addr ); 
      if ( $select_query -> can_read($timeout) )
      {
        $remote_host = $UDP_QUERY -> recv($buff, $buffer_size, 0 );
      }
      #this is icky, because i _most_ situations, there isn't going to
be
      #any more data... but we have a server with a LONG published
apps list
      #which takes two packets to deliver. Good eh?
      if ( $tests{$test_target}{longlist} && $select_query ->
can_read($timeout) )
      {
        $UDP_QUERY -> recv($buff2, $buffer_size, 0 );
        if ( $buff2 )
          {
             $buff = join ("", $buff, $buff2);
          }
      }
    } #while test_count
    if ($buff)  #eg if we got a response from a couple of retries of
the app query
    {
      ($rport, $raddr) = sockaddr_in ( $remote_host );
      $rhost = gethostbyaddr ( $raddr, AF_INET );
      @tmpbuf = unpack ("C" x length($buff), $buff );
      if ( $debug )
      {
        print "$rhost:$rport responded to app query with:
",length($buff), " bytes\n";
        foreach (@tmpbuf)
        {
          printf ("%02X ",$_ );
        }
        print "\n";
      } #debug

      #now we strip out the icky null chars. This is what makes the
pattern
      #matching on the app list less selective. The problem is that
some
      #serves return an app list in ASCII, and others return it in
unicode...
      #stripping the nulls is the easiest way of converting it to a
common format.
      my @newbuf;
      foreach my $value (@tmpbuf)
      {
        if ( $value > 31 )
        { 
          push(@newbuf, $value);
        }
      }
      #now after trashing the nulls, we need to append one as a string
terminator.
      push(@newbuf, 0 );
      @tmpbuf=@newbuf;

      my $app_list = join("", pack("C" x $#tmpbuf, @tmpbuf ) );
      if ( $debug ) { print "Recieved list of applications:
$app_list\n" };
 
      $line2 = "";
      #yellow first, so a red overrides it...
      foreach $app (@{$tests{$test_target}{yellow_published_apps}} )
      {
        my $app_test = $app_list;
        if ( $app_test =~ /$app/ )
        {
          $line2 = join ( "", $line2 );  
        }
        else
        {
          $line2 = join ( " -", $line2, " $app" );
          $color = "yellow";
        }
      } #foreach
      foreach $app (@{$tests{$test_target}{red_published_apps}} )
      {
        my $app_test = $app_list;
        if ( $app_test =~ /$app/ )
        {
          $line2 = join ( "", $line2 );
        }
        else
        {
          $line2 = join ( " -", $line2, " $app" );
          $color = "red";
        }
      } #foreach
      sleep $timeout; #because otherwise we can get responses from the
WRONG servers. DOH
    } # if ( !$buff)
    else
    {
      $color = "red";
      $line2 = "CRITICAL - No response from master browser.";
      print "$line2";
      exit 2;
    }
    
  } #if !$buff - so we skip this chunk if there was no response from
any of our master
browsers, since
   #there's not point trying to get an app list...   
  else
  {
    $color = "red";
    $line2 = "CRITICAL - No response recieved to discovery
messages.\n";
    print "$line2";
    exit 2;
  }
  if ( $color eq 'green' )
  {
    $line = "OK - All published applications are available.\n";
    print "$line";
    $exitvalue = "0";
  }
  elsif( $color eq 'yellow' )
  {
    $line = "WARNING$line2 unavailable!\n";
    print "$line";
    $exitvalue = "1";
  }
  else
  {
    $line = "CRITICAL$line2 unavailable!\n";
    print "$line";
    $exitvalue = "2";
  }
} #foreach
close $UDP_QUERY;
close $UDP_HELO;
exit $exitvalue

********************************************************************************************************************************************


-------------------------------------------------------
Sponsored by: AMD - Your access to the experts on Hammer Technology! 
Open Source & Linux Developers, register now for the AMD Developer 
Symposium. Code: EX8664 http://www.developwithamd.com/developerlab




More information about the Users mailing list