2011-11-18

AJP ping/pong: update to ajp_ping.pl.

I wrote a terrible ping script for AJP a while back. Here is an updated version.

#!/usr/bin/perl -w

use warnings;
use strict;

use Socket;

my $host = 'localhost';
my $port = '8009';

# The host and port arguments can be specified by either space or colon
# separating the values.
if (scalar(@ARGV) >= 2) {
    $host = shift @ARGV;
    $port = shift @ARGV;
}
elsif (scalar(@ARGV) == 1) {
    if ($ARGV[0] =~ /:/) {
        ($host, $port) = split /:/, shift @ARGV, 2;
    }
    else {
        $host = shift @ARGV;
    }
}

# If the port has anything other than numbers, assume it is an /etc/services
# name.
if ($port =~ /\D/) {
    $port = getservbyname $port, 'tcp' ;
    die "Bad port: $port" unless $port;
}

my $iaddr = inet_aton($host) or die "No such host: $host";

# Get a printable IP address from the in_addr_t type returned by inet_aton().
my $ip = join('.', unpack('C4', $iaddr));

# Now that the port value is a number, and the host (if it was originally a
# name) has been resolved to an IP address, then print a status header like
# the real ping does.
print "AJP PING $host ($ip) port $port\n";

my $paddr = sockaddr_in($port, $iaddr) or die $!;

# Grab the number for TCP out of /etc/protocols.
my $proto = getprotobyname 'tcp' ;

my $sock;
# PF_INET and SOCK_STREAM are constants imported by the Socket module.  They
# are the same as what is defined in sys/socket.h.
socket $sock, PF_INET, SOCK_STREAM, $proto or die $!;

# This is the ping packet.  For detailed documentation, see
# http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
# I stole the exact byte sequence from
# http://sourceforge.net/project/shownotes.php?group_id=128058&release_id=438456
# instead of fully understanding the packet structure.
my $ping = pack 'C5'    # Format template.
    , 0x12, 0x34        # Magic number for server->container packets.
    , 0x00, 0x01        # 2 byte int length of payload.
    , 0x0A              # Type of packet. 10 = CPing.
;

# If the connection is closed, log a decent message.
$SIG{PIPE} = sub { die $!; };

connect $sock, $paddr or die $!;

syswrite $sock, $ping or die $!;

print 'Sent:     ';
for my $value (unpack 'C5', $ping) {
    printf '%02d ', $value;
}
print "\n";

my $pong;

$pong = 0;

sysread $sock, $pong, 5 or die $!;

print 'Received: ';
for my $value (unpack 'C5', $pong) {
    printf '%02d ', $value;
}
print "\n";

close $sock or warn $!;

# This is the expected pong packet.  That is, this is what Tomcat sends back
# to indicate that it is operating OK.
my $expected = pack 'C5'    # Format template.
    , 0x41, 0x42            # Magic number for container->server packets.
    , 0x00, 0x01            # 2 byte int length of payload.
    , 0x09                  # Type of packet. 9 = CPong reply.
;

print 'Expected: ';
for my $value (unpack 'C5', $expected) {
    printf '%02d ', $value;
}
print "\n";

if ($pong eq $expected) {
    print "Server pong OK.\n";
    exit 0;
}

print "Server pong FAILED.\n";
exit 1;

Here it is running against a good host.

me@mybox:~/ajp_ping
$ ./ajp_ping.pl xxx 12440 ; echo rc = $?
AJP PING xxx (10.1.1.16) port 12440
Sent:     18 52 00 01 10
Received: 65 66 00 01 09
Expected: 65 66 00 01 09
Server pong OK.
rc = 0

Here is a bad response. I went to an HTTP port instead of an AJP one.

me@mybox:~/ajp_ping
$ ./ajp_ping.pl xxx 12040 ; echo rc = $?
AJP PING xxx (10.1.1.16) port 12040
Sent:     18 52 00 01 10
Received: 72 84 84 80 47
Expected: 65 66 00 01 09
Server pong FAILED.
rc = 1

Here is a timeout (using system TCP timeout defaults).

me@mybox:~/ajp_ping
$ time ./ajp_ping.pl yyy 12040 ; echo rc = $?
AJP PING yyy (10.16.1.8) port 12040
Connection timed out at ./ajp_ping.pl line 67.

real    0m21.031s
user    0m0.010s
sys     0m0.018s
rc = 110

Here is an attempt at a connection to a port where nothing is listening.

me@mybox:~/ajp_ping
$ ./ajp_ping.pl xxx 99 ; echo rc = $?
AJP PING xxx (10.1.1.16) port 99
Connection refused at ./ajp_ping.pl line 67.
rc = 111

4 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. Very thank you for this good script. I use it to determine if my tomcat instance need a restart ;)

    ReplyDelete
  3. hi... nice work , but i want to know if it can tell us tha status of tomcat like apache stats does ... idle or active

    ReplyDelete
    Replies
    1. Lovepreet, no it cannot. This script only sends a CPing type of packet and expects a CPong type of packet in return. The concept of idle and active does not apply to such packets.

      Delete