#!/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
This comment has been removed by the author.
ReplyDeleteVery thank you for this good script. I use it to determine if my tomcat instance need a restart ;)
ReplyDeletehi... nice work , but i want to know if it can tell us tha status of tomcat like apache stats does ... idle or active
ReplyDeleteLovepreet, 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