2
###################################################################################################################
3
# $Id:: check 20 2007-02-23 14:26:44Z fabrice $
5
###################################################################################################################
6
# Authors : Fabrice Dulaunoy <fabrice@dulaunoy.com>
8
# Copyright (C) 2006-2007 Fabrice Dulaunoy <fabrice@dulaunoy.com>
10
# This program is free software; you can redistribute it and/or modify it
11
# under the terms of the GNU General Public License as published by the
12
# Free Software Foundation; either version 2 of the License, or (at your
13
# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
15
# This program is distributed in the hope that it will be useful, but
16
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19
###################################################################################################################
21
###################################################################################################################
33
#my $VERSION = do { my @rev = ( q$Revision: 20 $ =~ /\d+/g ); sprintf "%d." . "%d" x $#rev, @rev };
35
my $VERSION = sprintf "1.%02d", '$Revision: 20 $ ' =~ /(\d+)/;
39
getopts( "vHhc:", \%option );
43
print "Usage: $0 [options ...]\n\n";
44
print "Where options include:\n";
45
print "\t -h \t\t\tthis help (what else ?)\n";
46
print "\t -H \t\t\tshow a sample config file\n";
47
print "\t -v \t\t\tprint version and exit\n";
48
print "\t -c file \t\tuse config file (default /etc/check.conf)\n";
49
print "\n\t This is a small program parsing the config file \n";
50
print "\t and checking one or more condition on one or more servers\n";
51
print "\t these condition could be \n";
52
print "\t\t HTTP return code list (with optinal Host Header and optional Basic Authentication) \n";
53
print "\t\t a regex over a HTTP GET (with optinal Host Header and optional Basic Authentication)\n";
54
print "\t\t a regex over a FTP GET ( with optional Basic Authentication)\n";
55
print "\t\t a TCP open port\n";
56
print "\t the result state is an AND over all tests \n";
57
print "\t this result could be \n";
58
print "\t\t a simple HTTP return state (\"200 OK\" or \"503 Service Unavailable\" \n";
59
print "\t\t a HTML page with a status OK or NOK for each test\n";
60
print "\t\t a HTML page with a staus OK or NOK for each test in a row of a TABLE\n";
61
print "\n\t The natural complement of this tools is the poll_check tool\n";
62
print "\t The result code of this tools is designed to fit the HAPROXY requirement (test over a port not related to the WEB server)\n";
67
print "\t A sample config file could be:\n";
70
###########################################################
71
# listening port ( default 9898 )
74
# on which IP to bind (default 127.0.0.1 ) * = all IP
77
# which client addr is allow ( default 127.0.0.0/8 )
78
#cidr_allow = 0.0.0.0/0
80
# verbosity from 0 to 4 (default 0 = no log )
83
# daemonize (default 0 = no )
86
# content put a HTML content after header
87
# (default 0 = no content 1 = html 2 = table )
90
# reparse the config file at each request ( default 0 = no )
91
# only SIGHUP reread the config file)
94
# pid_file (default /var/run/check.pid )
95
# $$$ = basename of config file
97
pid_file=/var/run/CHECK_$$$.pid
99
# log_file (default /var/log/check.log )
100
# $$$ = basename of config file
102
log_file=/var/log/CHECK_$$$.log
104
# number of servers to keep running (default = 5)
107
# number of servers to have waiting for requests (default = 2)
108
min_spare_servers = 1
110
# maximum number of servers to have waiting for requests (default = 10)
113
# number of servers (default = 50)
117
###########################################################
119
# type could be get , regex or tcp
121
# get = do a http or ftp get and check the result code with
122
# the list, coma separated, provided ( default = 200,201 )
123
# hostheader is optional and send to the server if provided
125
# regex = do a http or ftp get and check the content result
126
# with regex provided
127
# hostheader is optional and send to the server if provided
129
# tcp = test if the tcp port provided is open
131
###########################################################
134
url=http://127.0.0.1:80/apache2-default/index.html
137
hostheader = www.test.com
142
url=http://127.0.0.1:82/apache2-default/index.html
145
hostheader = www.myhost.com
162
url = ftp://USER:PASSWORD@10.2.3.1
165
###########################################################
173
if ( $option{ h } || $option{ H } )
178
if ( $option{ v } ) { print "$VERSION\n"; exit; }
181
use Net::Server::PreFork;
182
@ISA = qw(Net::Server::PreFork);
193
my $min_spare_servers;
194
my $max_spare_servers;
198
my $conf_file = $option{ c } || "/etc/check.conf";
199
my $pwd = $ENV{ PWD };
200
$conf_file =~ s/^\./$pwd/;
201
$conf_file =~ s/^([^\/])/$pwd\/$1/;
202
my $basename = basename( $conf_file, ( '.conf' ) );
203
my $CONF = parse_conf( $conf_file );
207
$SIG{ HUP } = sub { $reparse_one = 1; };
210
my $test_list = $CONF->{ realserver };
211
if ( ref( $test_list ) eq "ARRAY" )
213
@TEST = @{ $test_list };
217
@TEST = ( $test_list );
220
my $server = MyPackage->new(
224
cidr_allow => $cidr_allow,
225
log_level => $log_level,
226
child_communication => 1,
228
log_file => $log_file,
229
pid_file => $pid_file,
230
min_servers => $min_servers,
231
min_spare_servers => $min_spare_servers,
232
max_spare_servers => $max_spare_servers,
233
max_servers => $max_servers,
243
if ( $reparse || $reparse_one )
245
$CONF = parse_conf( $conf_file );
249
my $test_list = $CONF->{ realserver };
251
if ( ref( $test_list ) eq "ARRAY" )
253
@TEST = @{ $test_list };
257
@TEST = ( $test_list );
263
foreach my $test ( @TEST )
267
my $URL = $test->{ url };
268
$uri = URI->new( $URL );
269
$authority = $uri->authority;
271
if ( exists $test->{ type } )
273
if ( $test->{ type } =~ /get/i )
275
my $allow_code = $test->{ code } || '200,201';
277
my $host = $test->{ hostheader } || $authority;
278
my $res = get( $URL, $allow_code, $host );
279
if ( $html_content == 1 )
283
$html_data .= "GET OK $URL<br>\r\n";
287
$html_data .= "GET NOK $URL<br>\r\n";
290
if ( $html_content == 2 )
294
$html_data .= "<tr><td>GET</td><td>OK</td><td>$URL</td></tr>\r\n";
298
$html_data .= "<tr><td>GET</td><td>NOK</td><td>$URL</td></tr>\r\n";
303
if ( $test->{ type } =~ /regex/i )
305
my $regex = $test->{ regex };
307
my $host = $test->{ hostheader } || $authority;
308
my $res = regex( $URL, $regex, $host );
309
if ( $html_content == 1 )
313
$html_data .= "REGEX OK $URL<br>\r\n";
317
$html_data .= "REGEX NOK $URL<br>\r\n";
320
if ( $html_content == 2 )
324
$html_data .= "<tr><td>REGEX</td><td>OK</td><td>$URL</td></tr>\r\n";
328
$html_data .= "<tr><td>REGEX</td><td>NOK</td><td>$URL</td></tr>\r\n";
333
if ( $test->{ type } =~ /tcp/i )
336
my $PORT = $test->{ port } || 80;
337
my $res = TCP( $URL, $PORT );
338
if ( $html_content == 1 )
342
$html_data .= "TCP OK $URL<br>\r\n";
346
$html_data .= "TCP NOK $URL<br>\r\n";
349
if ( $html_content == 2 )
353
$html_data .= "<tr><td>TCP</td><td>OK</td><td>$URL</td></tr>\r\n";
357
$html_data .= "<tr><td>TCP</td><td>NOK</td><td>$URL</td></tr>\r\n";
366
if ( $html_content == 1 )
368
$html_data = "\r\n<html><body>\r\n$html_data</body></html>\r\n";
369
$len = ( length( $html_data ) ) - 2;
371
if ( $html_content == 2 )
373
$html_data = "\r\n<table align='center' border='1' >\r\n$html_data</table>\r\n";
374
$len = ( length( $html_data ) ) - 2;
377
if ( $result != $test_item )
379
my $header = "HTTP/1.0 503 Service Unavailable\r\n";
382
$header .= "Content-Length: $len\r\nContent-Type: text/html; charset=iso-8859-1\r\n";
384
print $header . $html_data;
387
my $header = "HTTP/1.0 200 OK\r\n";
390
$header .= "Content-Length: $len\r\nContent-Type: text/html; charset=iso-8859-1\r\n";
392
print $header. $html_data;
397
##########################################################
398
##########################################################
399
# function to REGEX on a GET from URL
401
# regex to test (with extra parameter like perl e.g. /\bweb\d{2,3}/i )
403
# port (optionnal: default=80)
406
##########################################################
407
##########################################################
414
$regex =~ /\/(.*)\/(.*)/;
418
$options{ 'agent' } = "LB_REGEX_PROBE/$VERSION";
419
$options{ 'timeout' } = 10;
420
my $ua = LWP::UserAgent->new( %options );
421
my $response = $ua->get( $url, "Host" => $host );
422
if ( $response->is_success )
424
my $html = $response->content;
427
if ( $html =~ /$reg/si )
434
if ( $html =~ /$reg/s )
443
##########################################################
444
##########################################################
445
# function to GET an URL (HTTP or FTP) ftp://FTPTest:6ccount4F@brice!@172.29.0.146
447
# allowed code (comma seaparated)
449
# port (optionnal: default=80)
450
# ret: 0 if not the expected vcode
451
# 1 if the expected code is returned
452
##########################################################
453
##########################################################
461
my %codes = map { $_ => $_ } split /,/, $code;
463
$options{ 'agent' } = "LB_HTTP_PROBE/$VERSION";
464
$options{ 'timeout' } = 10;
465
my $ua = LWP::UserAgent->new( %options );
466
my $response = $ua->get( $url, "Host" => $host );
467
if ( $response->is_success )
469
my $rc = $response->{ _rc };
470
if ( defined $codes{ $rc } )
478
##########################################################
479
##########################################################
480
# function to test a port on a host
486
##########################################################
487
##########################################################
490
use IO::Socket::PortState qw(check_ports);
491
my $remote_host = shift;
492
my $remote_port = shift;
495
my %porthash = ( tcp => { $remote_port => { name => 'to_test', } } );
496
check_ports( $remote_host, $timeout, \%porthash );
497
return $porthash{ tcp }{ $remote_port }{ open };
500
##############################################
503
# Out: Ref to a hash with config data
504
##############################################
509
my $conf = new Config::General(
510
-ConfigFile => $file,
511
-ExtendedAccess => 1,
512
-AllowMultiOptions => "yes"
514
my %config = $conf->getall;
515
$port = $config{ port } || 9898;
516
$host = $config{ host } || '127.0.0.1';
517
$reparse = $config{ reparse } || 0;
518
$cidr_allow = $config{ cidr_allow } || '127.0.0.0/8';
519
$log_level = $config{ log_level } || 0;
520
$log_file = $config{ log_file } || "/var/log/check.log";
521
$pid_file = $config{ pid_file } || "/var/run/check.pid";
522
$daemon = $config{ daemon } || 0;
523
$min_servers = $config{ min_servers } || 5;
524
$min_spare_servers = $config{ min_spare_servers } || 2;
525
$max_spare_servers = $config{ max_spare_servers } || 10;
526
$max_servers = $config{ max_servers } || 50;
527
$html_content = $config{ content } || 0;
529
$pid_file =~ s/\$\$\$/$basename/g;
530
$pid_file =~ s/\$\$/$$/g;
531
$log_file =~ s/\$\$\$/$basename/g;
532
$log_file =~ s/\$\$/$$/g;
534
if ( !( keys %{ $config{ realserver } } ) )
536
die "No farm to test\n";