2 ######################################################################
4 # Linux Director Daemon - run "perldoc l7directord" for details
6 # 2005-2008 (C) NTT COMWARE
8 # License: GNU General Public License (GPL)
10 # This program is developed on similar lines of ldirectord. It handles
11 # l7vsadm and monitoring of real servers.
13 # The version of ldirectord used as a reference for this l7directord is
14 # ldirectord,v 1.77.2.32 2005/09/21 04:00:41
16 # Note : * The existing code of ldirectord that is not required for
17 # l7directord is also maintained in the program but is
20 # This program is free software; you can redistribute it and/or
21 # modify it under the terms of the GNU General Public License as
22 # published by the Free Software Foundation; either version 2 of the
23 # License, or (at your option) any later version.
25 # This program is distributed in the hope that it will be useful, but
26 # WITHOUT ANY WARRANTY; without even the implied warranty of
27 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 # General Public License for more details.
30 # You should have received a copy of the GNU General Public License
31 # along with this program; if not, write to the Free Software
32 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
34 ######################################################################
37 # 0.5.0-0: Added code related to Sorry server and Max connection
38 # - 2006/11/03 NTT COMWARE
39 # 1.0.0-0: Added code related to weight of real server and QoS
40 # - 2007/10/12 NTT COMWARE
41 # 1.0.1-0: Added the code below.
42 # configuration of realdowncallback, realrecovercallback,
43 # and sessionless module.
44 # - 2007/12/28 NTT COMWARE
45 # 1.0.2-0: Added the code below.
46 # cookie insert with X-Forwarded-For module(cinsert_xf)
47 # - 2008/1/14 Shinya TAKEBAYASHI
48 # 2.0.0-0: Added code related to sslid module.
49 # cinsert_xf module is marged into cinsert module.
50 # Added code related to syntax test of configuration.
51 # Expanded checkcount setting to all service check.
52 # - 2008/03/25 Norihisa NAKAI
53 # 2.1.0-0: Changed helthcheck logic to multi-process.
54 # - 2008/12/17 NTT COMWARE
55 # 2.1.1-0: Fix 'Range iterator outside integer range' in parse_real.
56 # - 2009/01/06 NTT COMWARE
57 # 2.1.2-0: Added code related to some module. See below.
58 # (cpassive, crewrite, pfilter, url, ip)
59 # Add custom healthcheck.
60 # (checktype=custom, customcheck=exec_command)
61 # - 2009/02/14 NTT COMWARE
66 use Getopt::Long qw(:config posix_default);
68 use POSIX qw(:sys_wait_h :signal_h);
69 use Sys::Syslog qw(:DEFAULT setlogsock);
71 use Fatal qw(open close);
74 use Time::HiRes qw(sleep);
78 our $VERSION = '2.1.2-0';
79 our $COPYRIGHT = 'Copyright (C) 2009 NTT COMWARE CORPORATION';
81 # default global config values
83 logfile => '/var/log/l7vs/l7directord.log',
90 negotiatetimeout => 5,
99 # default virtual config values
102 module => { name => 'sessionless', key => q{} },
105 checktype => 'negotiate',
111 sorryserver => { ip => '0.0.0.0', port => 0 },
115 virtualhost => undef,
119 realdowncallback => undef,
120 realrecovercallback => undef,
121 customcheck => undef,
122 sslconfigfile => undef,
124 accesslogfile => undef,
125 socketoption => undef,
126 accesslog_rotate_type => undef,
127 accesslog_rotate_max_backup_index => undef,
128 accesslog_rotate_max_filesize => undef,
129 accesslog_rotate_rotation_timing => undef,
130 accesslog_rotate_rotation_timing_value => undef,
131 other_virtual_key => undef,
134 checkinterval => undef,
135 retryinterval => undef,
136 checktimeout => undef,
137 negotiatetimeout => undef,
142 # default real config values
151 # current config data
152 our %CONFIG = %GLOBAL;
162 # process environment
166 pid_prefix => '/var/run/l7directord',
181 our $DEBUG_LEVEL = 0;
183 # health check process data
184 our %HEALTH_CHECK = ();
186 # real server health flag
188 our $SERVICE_DOWN = 1;
190 # section virtual sub config prefix
191 our $SECTION_VIRTUAL_PREFIX = " ";
196 # Main method of this program.
197 # parse command line and run each command method.
200 start => \&cmd_start,
202 restart => \&cmd_restart,
203 'try-restart' => \&cmd_try_restart,
204 reload => \&cmd_reload,
205 status => \&cmd_status,
206 configtest => \&cmd_configtest,
207 version => \&cmd_version,
209 usage => \&cmd_usage,
212 # change program name for removing `perl' string from `ps' command result.
213 my $ps_name = @ARGV ? $PROGRAM_NAME . " @ARGV"
215 $PROGRAM_NAME = $ps_name;
217 my $cmd_mode = parse_cmd();
218 if ( !defined $cmd_mode || !exists $cmd_func->{$cmd_mode} ) {
221 if ($cmd_mode ne 'help' && $cmd_mode ne 'version' && $cmd_mode ne 'usage') {
226 my $cmd_result = &{ $cmd_func->{$cmd_mode} }();
228 ld_exit( $cmd_result, _message_only('INF0008') );
232 # Parse command line (ARGV)
234 # configtest or help command
235 my $cmd_mode = parse_option();
238 if (!defined $cmd_mode && @ARGV) {
239 $cmd_mode = pop @ARGV;
245 # Parse option strings by Getopt::Long
247 my $cmd_mode = undef;
249 # default option value
255 # parse command line options
256 my $result = GetOptions(
257 'd:3' => \$debug, # debug mode, arg: debug level (default 3)
258 'h|help' => \$help, # show help message
259 't' => \$test, # config syntax test
260 'v|version' => \$version, # show version
265 if (defined $debug) {
266 $DEBUG_LEVEL = $debug;
273 elsif (defined $version) {
274 $cmd_mode = 'version';
276 elsif (defined $test) {
277 $cmd_mode = 'configtest';
288 # Initialize file path settings.
289 sub initial_setting {
290 # search config and l7vsadm
291 $PROC_ENV{l7vsadm} = search_l7vsadm_file();
292 $CONFIG_FILE{path} = search_config_file();
294 # get config file name exclude `.cf' or `.conf'
295 ( $CONFIG_FILE{filename} )
296 = $CONFIG_FILE{path} =~ m{([^/]+?)(?:\.cf|\.conf)?$};
300 = defined $ENV{HOSTNAME} ? $ENV{HOSTNAME}
301 : ( POSIX::uname() )[1]
306 # Search l7directord.cf file from search path.
307 sub search_config_file {
308 my $config_file = undef;
309 my @search_path = qw(
310 /etc/ha.d/conf/l7directord.cf
311 /etc/ha.d/l7directord.cf
316 $config_file = $ARGV[0];
318 init_error( _message_only('ERR0404', $config_file) );
322 for my $file (@search_path) {
324 $config_file = $file;
328 if (!defined $config_file) {
329 init_error( _message_only('ERR0405', $config_file) );
333 return abs_path($config_file);
336 # search_l7vsadm_file
337 # Search l7vsadm file from search path.
338 sub search_l7vsadm_file {
339 my $l7vsadm_file = undef;
340 my @search_path = qw(
346 for my $file (@search_path) {
348 $l7vsadm_file = $file;
352 if (!defined $l7vsadm_file) {
353 init_error( _message_only('ERR0406', $l7vsadm_file) );
356 return abs_path($l7vsadm_file);
361 # Called if command argument is start
362 # return: 0 if success
363 # 1 if old process id is found.
368 ld_log( _message('INF0001', $PROGRAM_NAME) );
372 my $oldpid = read_pid();
374 # already other process is running
376 print {*STDERR} _message_only('INF0103', $oldpid) . "\n";
380 # supervised or debug mode (not daemon)
381 if ($CONFIG{supervised} || $DEBUG_LEVEL > 0) {
382 ld_log( _message( 'INF0002', $VERSION, $PID, $CONFIG_FILE{path} ) );
387 ld_log( _message( 'INF0003', $VERSION, $CONFIG_FILE{path} ) );
390 write_pid( $PROC_STAT{pid} );
391 ld_cmd_children('start');
393 ld_cmd_children('stop');
400 # Send stop signal (TERM)
401 # Called if command argument is stop
402 # return: 0 if success
403 # 2 if old process id is not found.
404 # 3 if signal failed.
406 my ($oldpid, $stalepid) = read_pid();
408 # process is not running
411 my $pid_file = $PROC_ENV{pid_prefix} . q{.} . $CONFIG_FILE{filename} . '.pid';
412 print {*STDERR} _message_only('INF0102', $pid_file, $CONFIG_FILE{path}) . "\n";
414 print {*STDERR} _message_only('INF0104') . "\n";
419 my $signaled = kill 15, $oldpid;
420 if ($signaled != 1) {
421 print {*STDERR} _message('WRN0003', $oldpid);
435 # Called if command argument is restart
436 # return: see cmd_start return
438 # stop and ignore result
442 my $status = cmd_start();
448 # Trying restart process
449 # Called if command argument is try-restart
450 # return: see cmd_start, cmd_stop return
451 sub cmd_try_restart {
453 my $stop_result = cmd_stop();
455 # start only if stop succeed
456 if ($stop_result != 0) {
461 my $status = cmd_start();
467 # Send reload signal (HUP)
468 # Called if command argument is reload
469 # return: 0 if success
470 # 2 if old process id is not found.
471 # 3 if signal failed.
474 my ($oldpid, $stalepid) = read_pid();
477 my $pid_file = $PROC_ENV{pid_prefix} . q{.} . $CONFIG_FILE{filename} . '.pid';
478 print {*STDERR} _message_only( 'INF0102', $pid_file, $CONFIG_FILE{path} ) . "\n";
480 print {*STDERR} _message_only('INF0104') . "\n";
485 my $signaled = kill 1, $oldpid;
486 if ($signaled != 1) {
487 print {*STDERR} _message('WRN0004', $oldpid);
494 # Show process id of running
495 # Called if command argument is status
496 # return: 0 if success
497 # 2 if old process id is not found.
499 my ($oldpid, $stalepid) = read_pid();
502 my $pid_file = $PROC_ENV{pid_prefix} . q{.} . $CONFIG_FILE{filename} . '.pid';
503 print {*STDERR} _message_only('INF0102', $pid_file, $CONFIG_FILE{path}) . "\n";
505 print {*STDERR} _message_only('INF0104') . "\n";
506 ld_cmd_children('status');
511 print {*STDERR} _message_only('INF0101', $CONFIG_FILE{path}, $oldpid) . "\n";
514 ld_cmd_children('status');
520 # Configuration syntax check
521 # Called if command argument is configtest
522 # return: 0 if syntax ok
523 # otherwise, exit by read_config
526 print {*STDOUT} "Syntax OK\n";
531 # Show program version.
532 # Called if command argument is version
535 print {*STDOUT} "l7directord, version $VERSION\n$COPYRIGHT\n";
540 # Show command manual.
541 # Called if command argument is help
544 system_wrapper( '/usr/bin/perldoc ' . $PROC_ENV{l7directord} );
549 # Show command usage.
550 # Called if command argument is unknown or not specified.
554 "Usage: l7directord {start|stop|restart|try-restart|reload|status|configtest}\n"
555 . "Try `l7directord --help' for more information.\n";
560 # Set signal handler function.
562 $SIG{ INT } = \&ld_handler_term;
563 $SIG{ QUIT } = \&ld_handler_term;
564 $SIG{ ILL } = \&ld_handler_term;
565 $SIG{ ABRT } = \&ld_handler_term;
566 $SIG{ FPE } = \&ld_handler_term;
567 $SIG{ SEGV } = \&ld_handler_term;
568 $SIG{ TERM } = \&ld_handler_term;
569 $SIG{ BUS } = \&ld_handler_term;
570 $SIG{ SYS } = \&ld_handler_term;
571 $SIG{ XCPU } = \&ld_handler_term;
572 $SIG{ XFSZ } = \&ld_handler_term;
573 # HUP is actually used
574 $SIG{ HUP } = \&ld_handler_hup;
575 # This used to call a signal handler, that logged a message
576 # However, this typically goes to syslog and if syslog
577 # is playing up a loop will occur.
578 $SIG{ PIPE } = 'IGNORE';
579 # handle perl warn signal
580 $SIG{__WARN__} = \&ld_handler_perl_warn;
583 # ld_handler_perl_warn
584 # Handle Perl warnings for logging file.
585 sub ld_handler_perl_warn {
586 my $warning = join q{, }, @_;
587 $warning =~ s/[\r\n]//g;
588 ld_log( _message('WRN0301', $warning) );
592 # Read pid file and check if pid (l7directord) is still running
595 my $file_pid = undef;
596 my $pid_file = $PROC_ENV{pid_prefix} . q{.} . $CONFIG_FILE{filename} . '.pid';
598 open my $pid_handle, '<', $pid_file;
599 $file_pid = <$pid_handle>;
603 # Check to make sure this isn't a stale pid file
604 my $proc_file = "/proc/$file_pid/cmdline";
605 open my $proc_handle, '<', $proc_file;
606 my $line = <$proc_handle>;
607 if ($line =~ /l7directord/) {
608 $old_pid = $file_pid;
613 return wantarray ? ($old_pid, $file_pid) : $old_pid;
617 # Write pid number to pid file.
621 my $pid_file = $PROC_ENV{pid_prefix} . q{.} . $CONFIG_FILE{filename} . '.pid';
622 if (!defined $pid || $pid !~ /^\d+$/ || $pid < 1) {
623 $pid = defined $pid ? $pid : 'undef';
624 init_error( _message_only('ERR0412', $pid) );
627 open my $pid_handle, '>', $pid_file;
628 print {$pid_handle} $pid . "\n";
632 init_error( _message_only('ERR0409', $pid_file, $EVAL_ERROR) );
639 my $pid_file = $PROC_ENV{pid_prefix} . q{.} . $CONFIG_FILE{filename} . '.pid';
640 ld_rm_file($pid_file);
644 # Handle error during initialization and exit.
648 if ($DEBUG_LEVEL == 0) {
649 print {*STDERR} $msg . "\n";
651 ld_log( _message('ERR0001', $msg) );
653 ld_exit( 4, _message_only('INF0004') );
657 # If we get a sinal then put a halt flag up
658 sub ld_handler_term {
660 $PROC_STAT{halt} = defined $signal ? $signal : 'undef';
664 # If we get a sinal then put a reload flag up
667 $PROC_STAT{reload} = defined $signal ? $signal : 'undef';
671 # Re-read config, and then re-setup l7vsd and child process.
673 my $old_virtual = defined $CONFIG{virtual} ? [ @{ $CONFIG{virtual} } ]
676 my %old_sub_config = defined $CONFIG{execute} ? %{ $CONFIG{execute} }
681 $CONFIG{old_virtual} = $old_virtual;
683 # analyze config and catch format error
690 my $exception = $EVAL_ERROR;
692 ld_log( _message('ERR0122', $exception) );
693 $CONFIG{virtual} = [ @{ $CONFIG{old_virtual} } ];
694 $CONFIG{execute} = \%old_sub_config;
697 my %new_sub_config = defined $CONFIG{execute} ? %{ $CONFIG{execute} }
700 for my $sub_config ( keys %old_sub_config ) {
701 if ( exists $new_sub_config{$sub_config} ) {
702 if ( system_wrapper($PROC_ENV{l7directord} . " $sub_config reload") ) {
703 system_wrapper($PROC_ENV{l7directord} . " $sub_config start");
705 delete $new_sub_config{$sub_config};
706 delete $old_sub_config{$sub_config};
709 ld_cmd_children('stop', \%old_sub_config);
710 ld_cmd_children('start', \%new_sub_config);
714 # Read configuration and parse settings.
717 my $current_global_name = q{};
721 open $config_handle, '<', $CONFIG_FILE{path};
724 config_error( 0, 'ERR0407', $CONFIG_FILE{path} );
727 while (my $config_line = <$config_handle>) {
730 $config_line =~ s/#.*//mg; # remove comment (FIXME optimize regex for "foo='#'")
731 $config_line =~ s/^\t/$SECTION_VIRTUAL_PREFIX/mg; # convert tab to prefix
733 next if ($config_line =~ /^(?:$SECTION_VIRTUAL_PREFIX)?\s*$/);
736 if ($config_line !~ /^$SECTION_VIRTUAL_PREFIX/) {
737 my ($name, $value) = validate_config($line, $config_line);
738 $current_global_name = $name;
739 if ($name eq 'virtual') {
740 my %virtual = %VIRTUAL;
741 $virtual{server} = $value;
742 push @{ $CONFIG{virtual} }, \%virtual;
743 _ld_service_resolve(\%virtual, $value->{port});
745 elsif ($name eq 'execute') {
746 $CONFIG{execute}{$value} = 1;
749 $CONFIG{$name} = $value;
754 if ($current_global_name ne 'virtual') {
755 config_error($line, 'ERR0119', $config_line);
757 my ($name, $value) = validate_config($line, $config_line);
758 if ($name eq 'real' && defined $value) {
759 push @{ $CONFIG{virtual}[-1]{real} }, @$value;
761 elsif (defined $value) {
762 $CONFIG{virtual}[-1]{$name} = $value;
768 close $config_handle;
771 config_error( 0, 'ERR0408', $CONFIG_FILE{path} );
774 ld_openlog( $CONFIG{logfile} ) if !$PROC_STAT{log_opened};
775 check_require_module();
776 undef $CONFIG_FILE{checksum};
777 undef $CONFIG_FILE{stattime};
780 $PROC_STAT{initialized} = 1;
784 # Validation check of configuration.
785 sub validate_config {
786 my ($line, $config) = @_;
787 my ($name, $value) = split /\s*=\s*/, $config, 2;
788 if (defined $value) {
790 $value =~ s/^("|')(.*)\1$/$2/;
793 # section global validate
794 if ($name !~ /^$SECTION_VIRTUAL_PREFIX/) {
795 if (!exists $GLOBAL{$name}) {
796 config_error($line, 'ERR0120', $config);
798 if ($name eq 'virtual') {
799 $value = ld_gethostservbyname($value, 'tcp');
800 if (!defined $value) {
801 config_error($line, 'ERR0114', $config);
804 elsif ( $name eq 'checktimeout'
805 || $name eq 'negotiatetimeout'
806 || $name eq 'checkinterval'
807 || $name eq 'retryinterval'
808 || $name eq 'configinterval'
809 || $name eq 'checkcount' ) {
810 if (!defined $value || $value !~ /^\d+$/ || $value == 0 ) {
811 config_error($line, 'ERR0101', $config);
814 elsif ( $name eq 'autoreload'
815 || $name eq 'quiescent' ) {
816 $value = defined $value && $value =~ /^yes$/i ? 1
817 : defined $value && $value =~ /^no$/i ? 0
820 if (!defined $value) {
821 config_error($line, 'ERR0102', $config);
824 elsif ($name eq 'fallback') {
825 my $fallback = parse_fallback($line, $value, $config);
826 $value = {tcp => $fallback};
828 elsif ($name eq 'callback') {
829 if (!defined $value || !-f $value || !-x $value) {
830 config_error($line, 'ERR0117', $config);
833 elsif ($name eq 'execute') {
834 if (!defined $value || !-f $value) {
835 config_error($line, 'ERR0116', $config);
838 elsif ($name eq 'logfile') {
839 if (!defined $value || ld_openlog($value) ) {
840 config_error($line, 'ERR0118', $config);
843 elsif ($name eq 'supervised') {
847 # section virtual validate
849 $name =~ s/^$SECTION_VIRTUAL_PREFIX\s*//g;
850 if (!exists $VIRTUAL{$name}) {
851 config_error($line, 'ERR0120', $config);
853 if ($name eq 'real') {
854 $value = parse_real($line, $value, $config);
856 elsif ( $name eq 'request'
857 || $name eq 'receive'
860 || $name eq 'database'
861 || $name eq 'customcheck'
862 || $name eq 'virtualhost' ) {
863 if (!defined $value || $value !~ /^.+$/) {
864 config_error($line, 'ERR0103', $config);
867 elsif ($name eq 'checktype') {
868 my $valid_type = qr{custom|connect|negotiate|ping|off|on|\d+};
870 if (!defined $value || $value !~ /^(?:$valid_type)$/) {
871 config_error($line, 'ERR0104', $config);
873 if ($value =~ /^\d+$/ && $value == 0) {
874 config_error($line, 'ERR0104', $config);
877 elsif ( $name eq 'checktimeout'
878 || $name eq 'negotiatetimeout'
879 || $name eq 'checkinterval'
880 || $name eq 'retryinterval'
881 || $name eq 'checkcount'
882 || $name eq 'maxconn' ) {
883 if (!defined $value || $value !~ /^\d+$/ || ($name ne 'maxconn' && $value == 0) ) {
884 config_error($line, 'ERR0101', $config);
887 elsif ($name eq 'checkport') {
888 if (!defined $value || $value !~ /^\d+$/ || $value == 0 || $value > 65535) {
889 config_error($line, 'ERR0108', $config);
892 elsif ($name eq 'scheduler') {
893 my $valid_scheduler = qr{lc|rr|wrr};
895 if (!defined $value || $value !~ /^(?:$valid_scheduler)$/) {
896 config_error($line, 'ERR0105', $config);
899 elsif ($name eq 'protocol') {
901 if (!defined $value || $value !~ /^tcp$/) {
902 config_error($line, 'ERR0109', $config);
905 elsif ($name eq 'service') {
907 my $valid_service = qr{http|https|ldap|ftp|smtp|pop|imap|nntp|dns|mysql|pgsql|sip|none};
908 if (!defined $value || $value !~ /^(?:$valid_service)$/) {
909 config_error($line, 'ERR0106', $config);
912 elsif ($name eq 'httpmethod') {
913 my $valid_method = qr{GET|HEAD};
915 if (!defined $value || $value !~ /^(?:$valid_method)$/) {
916 config_error($line, 'ERR0110', $config);
919 elsif ($name eq 'fallback') {
920 my $fallback = parse_fallback($line, $value, $config);
921 $value = {tcp => $fallback};
923 elsif ( $name eq 'quiescent'
924 || $name eq 'accesslog') {
925 $value = defined $value && $value =~ /^yes$/i ? 1
926 : defined $value && $value =~ /^no$/i ? 0
929 if (!defined $value) {
930 config_error($line, 'ERR0102', $config);
933 elsif ($name eq 'module') {
934 ## V3 Un-offering (url,pfileter).
935 my %key_option = ( url => ['--pattern-match', '--uri-pattern-match', '--host-pattern-match'],
943 if (defined $value) {
945 ($module, $option) = split /\s+/, $value, 2;
947 $module = lc $module;
948 if ( !defined $module || !exists $key_option{$module} ) {
949 config_error($line, 'ERR0111', $config);
951 for my $key_opt ( @{$key_option{$module}} ) {
952 if (defined $option && $option =~ /$key_opt\s+(\S+)/) {
953 $key .= q{ } if $key;
954 $key .= $key_opt . q{ } . $1;
957 if ( !$key && @{$key_option{$module}} ) {
958 # when omit cookie module key option
959 my $key_opt = join q{' or `}, @{$key_option{$module}};
960 config_error($line, 'ERR0112', $module, $key_opt, $config);
962 $value = {name => $module, option => $option, key => $key};
964 elsif ($name eq 'sorryserver') {
965 my $sorry_server = ld_gethostservbyname($value, 'tcp');
966 if (!defined $sorry_server) {
967 config_error($line, 'ERR0114', $config);
969 $value = $sorry_server;
971 elsif ( $name eq 'qosup'
972 || $name eq 'qosdown' ) {
974 if ( !defined $value || ($value ne '0' && $value !~ /^[1-9]\d{0,2}[KMG]$/) ) {
975 config_error($line, 'ERR0113', $config);
978 elsif ( $name eq 'realdowncallback'
979 || $name eq 'realrecovercallback' ) {
980 if (!defined $value || !-f $value || !-x $value) {
981 config_error($line, 'ERR0117', $config);
984 elsif ( $name eq 'socketoption') {
986 # socketoption=OPTION,OPTION,OPTION
987 if (!defined $value) {
988 config_error($line, 'ERR0124', $config);
990 my @option_value = split /,/, $value;
991 # OPTION:deferaccept,nodelay,cork,quickackon|quickackoff
992 for my $option (@option_value) {
993 if($option !~ /deferaccept|nodelay|cork|quickackon|quickackoff/) {
994 config_error($line, 'ERR0124', $config);
998 elsif ($name eq 'sslconfigfile'
999 || $name eq 'accesslogfile') {
1000 if (!defined $value || !-f $value) {
1001 config_error($line, 'ERR0116', $config);
1004 elsif ($name eq 'accesslog_rotate_type') {
1006 my $valid_rotate_type = qr{date|size|datesize};
1007 if (!defined $value || $value !~ /^(?:$valid_rotate_type)$/) {
1008 config_error($line, 'ERR0124', $config);
1011 elsif ($name eq 'accesslog_rotate_max_backup_index') {
1012 if (!defined $value || $value !~ /^\d+$/ || $value <= 0 || $value >= 13) {
1013 config_error($line, 'ERR0126', $config);
1016 elsif ($name eq 'accesslog_rotate_max_filesize') {
1018 if ( !defined $value || ($value ne '0' && $value !~ /^([1-9]\d{0,2}[KMG]|\d)$/) ) {
1019 config_error($line, 'ERR0127', $config);
1022 elsif ($name eq 'accesslog_rotate_rotation_timing') {
1024 my $valid_rotation_timing = qr{year|month|week|date|hour};
1025 if (!defined $value || $value !~ /^(?:$valid_rotation_timing)$/) {
1026 config_error($line, 'ERR0128', $config);
1029 elsif ($name eq 'accesslog_rotate_rotation_timing_value') {
1031 $value =~ s/["']//g;
1032 if (!defined $value ) {
1033 config_error($line, 'ERR0129', $config);
1035 if ($value =~ /^[1-9]\d{0,1}\/[1-9]\d{0,1}\s\d{1,2}:\d{1,2}$/i) {
1036 ## MM/dd hh:mm Check
1039 elsif ($value =~ /^[1-9]\d{0,1}\s\d{1,2}:\d{1,2}$/i) {
1043 elsif ($value =~ /^(sun|mon|tue|wed|thu|fri|sat)\s\d{1,2}:\d{1,2}$/i) {
1044 ## <week> hh:mm Check
1047 elsif ($value =~ /^\d{1,2}:\d{1,2}$/i) {
1051 elsif ($value =~ /^\d{1,2}$/i) {
1055 if ( !defined $check ) {
1056 config_error($line, 'ERR0129', $config);
1061 return ($name, $value);
1064 # check_require_module
1065 # Check service setting and require module.
1066 sub check_require_module {
1067 my %require_module = (
1068 http => [ qw( LWP::UserAgent LWP::Debug ) ],
1069 https => [ qw( LWP::UserAgent LWP::Debug Crypt::SSLeay ) ],
1070 ftp => [ qw( Net::FTP ) ],
1071 smtp => [ qw( Net::SMTP ) ],
1072 pop => [ qw( Net::POP3 ) ],
1073 imap => [ qw( Mail::IMAPClient ) ],
1074 ldap => [ qw( Net::LDAP ) ],
1075 nntp => [ qw( IO::Socket IO::Select ) ],
1076 dns => [ qw( Net::DNS ) ],
1077 mysql => [ qw( DBI DBD::mysql ) ],
1078 pgsql => [ qw( DBI DBD::Pg ) ],
1079 sip => [ qw( IO::Socket::INET IO::Socket::INET6 ) ],
1080 ping => [ qw( Net::Ping ) ],
1081 connect => [ qw( IO::Socket::INET IO::Socket::INET6 ) ],
1084 for my $v ( @{ $CONFIG{virtual} } ) {
1085 next if !defined $v;
1086 next if ( !defined $v->{service} || !defined $v->{checktype} );
1087 my $check_service = q{};
1088 if ( $v->{checktype} eq 'negotiate' && $require_module{ $v->{service} } ) {
1089 $check_service = $v->{service};
1091 elsif ($v->{checktype} eq 'ping' || $v->{checktype} eq 'connect') {
1092 $check_service = $v->{checktype};
1097 for my $module ( @{ $require_module{$check_service} } ) {
1098 my $module_path = $module . '.pm';
1099 $module_path =~ s{::}{/}g;
1101 require $module_path;
1104 config_error(0, 'ERR0123', $module, $check_service);
1110 # _ld_service_resolve
1111 # Set service name from port number
1112 # pre: vsrv: Virtual Service to resolve port
1113 # port: port in the form
1114 # post: If $vsrv->{service} is not set, then set it to "http",
1115 # "https", "ftp", "smtp", "pop", "imap", "ldap", "nntp" or "none"
1116 # if $vsrv->{port} is 80, 443, 21, 25, 110, 143, 389 or
1117 # any other value, respectivley
1119 sub _ld_service_resolve {
1120 my ($vsrv, $port) = @_;
1123 my @p = qw( 80 443 21 25 110 119 143 389 53 3306 5432 5060 );
1124 my @s = qw( http https ftp smtp pop nntp imap ldap dns mysql pgsql sip );
1127 if (defined $vsrv && !defined $vsrv->{service} && defined $port) {
1128 $vsrv->{service} = exists $servname{$port} ? $servname{$port}
1135 # Parse a fallback server
1136 # pre: line: line number fallback server was read from
1137 # fallback: Should be of the form
1138 # ip_address|hostname[:port|:service_name] masq
1139 # config_line: line read from configuration file
1140 # post: fallback is parsed
1141 # return: Reference to hash of the form
1142 # { server => blah, forward => blah }
1143 # Debugging message will be reported and programme will exit
1145 sub parse_fallback {
1146 my ($line, $fallback, $config_line) = @_;
1148 if (!defined $fallback || $fallback !~ /^(\S+)(?:\s+(\S+))?$/) {
1149 config_error($line, 'ERR0114', $config_line);
1151 my ($ip_port, $forward) = ($1, $2);
1152 $ip_port = ld_gethostservbyname($ip_port, 'tcp');
1153 if ( !defined $ip_port ) {
1154 config_error($line, 'ERR0114', $config_line);
1156 if (defined $forward && $forward !~ /^masq$/i) {
1157 config_error($line, 'ERR0107', $config_line);
1160 my %fallback = %REAL;
1161 $fallback{server} = $ip_port;
1162 if (defined $forward) {
1163 $fallback{forward} = $forward;
1170 # Parse a real server
1171 # pre: line: line number real server was read from
1172 # real: Should be of the form
1173 # ip_address|hostname[:port|:service_name] masq
1174 # config_line: line read from configuration file
1175 # post: real is parsed
1176 # return: Reference to array include real server hash reference
1177 # [ {server...}, {server...} ... ]
1178 # Debugging message will be reported and programme will exit
1181 my ($line, $real, $config_line) = @_;
1183 my $ip_host = qr{\d+\.\d+\.\d+\.\d+|[a-z0-9.-]+|\[[a-zA-Z0-9:]+\]};
1184 my $port_service = qr{\d+|[a-z0-9-]+};
1187 ($ip_host) # ip or host
1188 (?:->($ip_host))? # range (optional)
1189 (?::($port_service))? # port or service (optional)
1190 (?:\s+([a-z]+))? # forwarding mode (optional)
1191 (?:\s+(\d+))? # weight (optional)
1193 ([^,\s]+) # "request
1194 \s*[ ,]\s* # separater
1198 config_error($line, 'ERR0114', $config_line);
1200 my ($ip1, $ip2, $port, $forward, $weight, $request, $receive)
1201 = ( $1, $2, $3, $4, $5, $6, $7);
1202 # set forward, weight and request-receive pair.
1204 if (defined $forward) {
1205 $forward = lc $forward;
1206 if ($forward !~ /^masq$/) {
1207 config_error($line, 'ERR0107', $config_line);
1209 $real{forward} = $forward;
1211 if (defined $weight) {
1212 $real{weight} = $weight;
1214 if (defined $request && defined $receive) {
1215 $request =~ s/^\s*("|')(.*)\1\s*/$2/;
1216 $receive =~ s/^\s*("|')(.*)\1\s*/$2/;
1217 $real{request} = $request;
1218 $real{receive} = $receive;
1221 my $resolved_port = undef;
1222 if (defined $port) {
1223 $resolved_port = ld_getservbyname($port);
1224 if (!defined $resolved_port) {
1225 config_error($line, 'ERR0108', $config_line);
1229 my $resolved_ip1 = ld_gethostbyname($ip1);
1230 if (!defined $resolved_ip1) {
1231 config_error($line, 'ERR0114', $config_line);
1234 my $resolved_ip2 = $resolved_ip1;
1236 $resolved_ip2 = ld_gethostbyname($ip2);
1237 if (!defined $resolved_ip2) {
1238 config_error($line, 'ERR0114', $config_line);
1242 my ($ip_version , $int_ip1, $int_ip1_prefix ) = ip_to_int($resolved_ip1);
1243 my ($ip_version2, $int_ip2, $int_ip2_prefix ) = ip_to_int($resolved_ip2);
1245 if ( defined $int_ip1 && defined $int_ip2 ) {
1246 if ($int_ip1 > $int_ip2) {
1247 config_error($line, 'ERR0115', $resolved_ip1, $resolved_ip2, $config_line);
1249 elsif ($int_ip1 eq $int_ip2) {
1250 my %new_real = %real;
1251 $new_real{server}{ip } = $resolved_ip1;
1252 $new_real{server}{port} = $resolved_port;
1253 push @reals, \%new_real;
1256 for (my $int_ip = $int_ip1; $int_ip <= $int_ip2; $int_ip++) {
1257 my %new_real = %real;
1258 $new_real{server}{ip } = int_to_ip($ip_version, $int_ip, $int_ip1_prefix);
1259 $new_real{server}{port} = $resolved_port;
1260 push @reals, \%new_real;
1268 # Handle error during read configuration and validation check
1270 my ($line, $msg_code, @msg_args) = @_;
1272 if ($DEBUG_LEVEL > 0 || $PROC_STAT{initialized} == 0) {
1273 my $msg = _message_only($msg_code, @msg_args);
1274 if (defined $line && $line > 0) {
1275 print {*STDERR} _message_only('ERR0121', $CONFIG_FILE{path}, $line, $msg) . "\n";
1278 print {*STDERR} $msg . "\n";
1283 ld_log( _message('ERR0121', $CONFIG_FILE{path}, $line, q{}) );
1285 ld_log( _message($msg_code, @msg_args) );
1287 if ( $PROC_STAT{initialized} == 0 ) {
1288 ld_exit(5, _message_only('ERR0002') );
1291 die "Configuration error.\n";
1296 # Check configuration value and set default value, overwrite global config value and so on.
1298 if ( defined $CONFIG{virtual} ) {
1299 for my $v ( @{ $CONFIG{virtual} } ) {
1300 next if !defined $v;
1301 if (defined $v->{protocol} && $v->{protocol} eq 'tcp') {
1302 $v->{option}{protocol} = "-t";
1305 if ( defined $v->{option} && defined $v->{option}{protocol} && defined $v->{module} && defined $v->{module}{name} ) {
1306 my $module_option = $v->{module}{name};
1307 if ( defined $v->{module}{option} ) {
1308 $module_option .= q{ } . $v->{module}{option};
1310 $v->{option}{main} = sprintf "%s %s -m %s", $v->{option}{protocol}, get_ip_port($v), $module_option;
1311 $v->{option}{flags} = $v->{option}{main};
1312 if ( defined $v->{scheduler} ) {
1313 $v->{option}{flags} .= ' -s ' . $v->{scheduler};
1315 if ( defined $v->{maxconn} ) {
1316 $v->{option}{flags} .= ' -u ' . $v->{maxconn};
1318 if ( defined $v->{sorryserver} && defined $v->{sorryserver}{ip} && defined $v->{sorryserver}{port} ) {
1319 $v->{option}{flags} .= ' -b ' . $v->{sorryserver}{ip} . ':' . $v->{sorryserver}{port};
1321 if ( defined $v->{qosup} ) {
1322 $v->{option}{flags} .= ' -Q ' . $v->{qosup};
1324 if ( defined $v->{qosdown} ) {
1325 $v->{option}{flags} .= ' -q ' . $v->{qosdown};
1327 if ( defined $v->{sslconfigfile} ) {
1328 $v->{option}{flags} .= ' -S ' . $v->{sslconfigfile};
1329 $v->{other_virtual_key} .= ' ' . $v->{sslconfigfile};
1332 $v->{other_virtual_key} .= ' none';
1334 if ( defined $v->{socketoption} ) {
1335 $v->{option}{flags} .= ' -O ' . $v->{socketoption};
1336 $v->{other_virtual_key} .= ' ' . $v->{socketoption};
1339 $v->{other_virtual_key} .= ' none';
1341 if ( defined $v->{accesslog} ) {
1342 $v->{option}{flags} .= ' -L ' . $v->{accesslog};
1344 if ( defined $v->{accesslogfile} ) {
1345 $v->{option}{flags} .= ' -a ' . $v->{accesslogfile};
1346 $v->{other_virtual_key} .= ' ' . $v->{accesslogfile};
1349 $v->{other_virtual_key} .= ' none';
1351 my $option_key_flag = 0;
1352 if ( defined $v->{accesslog_rotate_type} ) {
1354 .= ' --ac-rotate-type ' . $v->{accesslog_rotate_type};
1355 $v->{other_virtual_key}
1356 .= ' --ac-rotate-type ' . $v->{accesslog_rotate_type};
1357 $option_key_flag = 1;
1359 if ( defined $v->{accesslog_rotate_max_backup_index} ) {
1361 .= ' --ac-rotate-max-backup-index '
1362 . $v->{accesslog_rotate_max_backup_index};
1363 $v->{other_virtual_key}
1364 .= ' --ac-rotate-max-backup-index '
1365 . $v->{accesslog_rotate_max_backup_index};
1366 $option_key_flag = 1;
1368 if ( defined $v->{accesslog_rotate_max_filesize} ) {
1370 .= ' --ac-rotate-max-filesize '
1371 . $v->{accesslog_rotate_max_filesize};
1372 $v->{other_virtual_key}
1373 .= ' --ac-rotate-max-filesize '
1374 . $v->{accesslog_rotate_max_filesize};
1375 $option_key_flag = 1;
1377 if ( defined $v->{accesslog_rotate_rotation_timing} ) {
1379 .= ' --ac-rotate-rotation-timing '
1380 . $v->{accesslog_rotate_rotation_timing};
1381 $v->{other_virtual_key}
1382 .= ' --ac-rotate-rotation-timing '
1383 . $v->{accesslog_rotate_rotation_timing};
1384 $option_key_flag = 1;
1386 if ( defined $v->{accesslog_rotate_rotation_timing_value} ) {
1388 .= ' --ac-rotate-rotation-timing-value '
1389 . $v->{accesslog_rotate_rotation_timing_value};
1390 $v->{other_virtual_key}
1391 .= ' --ac-rotate-rotation-timing-value '
1392 . $v->{accesslog_rotate_rotation_timing_value};
1393 $option_key_flag = 1;
1395 if ( $option_key_flag == 0 ) {
1396 $v->{other_virtual_key} .= ' none ';
1400 if ( !defined $v->{fallback} && defined $CONFIG{fallback} ) {
1401 $v->{fallback} = { %{ $CONFIG{fallback} } };
1403 if ( defined $v->{fallback} ) {
1404 for my $proto ( keys %{ $v->{fallback} } ) {
1405 $v->{fallback}{$proto}{option}{flags} = '-r ' . get_ip_port( $v->{fallback}{$proto} );
1408 if (defined $v->{checktype} && $v->{checktype} =~ /^\d+$/) {
1409 $v->{num_connects} = $v->{checktype};
1410 $v->{checktype} = 'combined';
1413 if ( defined $v->{login} && $v->{login} eq q{} ) {
1414 $v->{login} = defined $v->{service} && $v->{service} eq 'ftp' ? 'anonymous'
1415 : defined $v->{service} && $v->{service} eq 'sip' ? 'l7directord@' . $PROC_ENV{hostname}
1419 if ( defined $v->{passwd} && $v->{passwd} eq q{} ) {
1420 $v->{passwd} = defined $v->{service} && $v->{service} eq 'ftp' ? 'l7directord@' . $PROC_ENV{hostname}
1425 if ( defined $v->{real} ) {
1426 for my $r ( @{ $v->{real} } ) {
1427 next if !defined $r;
1428 if ( defined $r->{forward} ) {
1429 $r->{option}{forward} = get_forward_flag( $r->{forward} );
1431 if ( !defined $r->{weight} || $r->{weight} !~ /^\d+$/ ) {
1435 if ( !defined $r->{server}{port} ) {
1436 $r->{server}{port} = $v->{server}{port};
1439 $r->{option}{flags} = '-r ' . get_ip_port($r);
1442 if ( defined $v->{service} && defined $r->{server} ) {
1443 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
1444 $r->{url} = sprintf "%s://%s:%s/",
1445 $v->{service}, $r->{server}{ip}, $port;
1447 if ( !defined $r->{request} && defined $v->{request} ) {
1448 $r->{request} = $v->{request};
1450 if ( !defined $r->{receive} && defined $v->{receive} ) {
1451 $r->{receive} = $v->{receive};
1453 if ( defined $r->{request} ) {
1454 my $uri = $r->{request};
1455 my $service = $v->{service};
1456 if ( defined $v->{service} && $uri =~ m{^$service://} ) {
1465 # set connect count for combine check
1466 if (defined $v->{checktype} && $v->{checktype} eq 'combined') {
1467 $r->{num_connects} = undef;
1470 $r->{fail_counts} = 0;
1471 $r->{healthchecked} = 0;
1474 if ( !defined $v->{checkcount} || $v->{checkcount} <= 0 ) {
1475 $v->{checkcount} = $CONFIG{checkcount};
1477 if ( !defined $v->{checktimeout} || $v->{checktimeout} <= 0 ) {
1478 $v->{checktimeout} = $CONFIG{checktimeout};
1480 if ( !defined $v->{negotiatetimeout} || $v->{negotiatetimeout} <= 0 ) {
1481 $v->{negotiatetimeout} = $CONFIG{negotiatetimeout};
1483 if ( !defined $v->{checkinterval} || $v->{checkinterval} <= 0 ) {
1484 $v->{checkinterval} = $CONFIG{checkinterval};
1486 if ( !defined $v->{retryinterval} || $v->{retryinterval} <= 0 ) {
1487 $v->{retryinterval} = $CONFIG{retryinterval};
1489 if ( !defined $v->{quiescent} ) {
1490 $v->{quiescent} = $CONFIG{quiescent};
1495 if (defined $CONFIG{fallback}) {
1496 $CONFIG{fallback}{tcp}{option}{flags} = '-r ' . get_ip_port( $CONFIG{fallback}{tcp} );
1500 # Removed persistent and netmask related hash entries from the structure of l7vsadm since it is not used - NTT COMWARE
1502 # Parses the output of "l7vsadm -K -n" and puts into a structure of
1503 # the following from:
1506 # (vip_address:vport) protocol module_name module_key_value => {
1507 # "scheduler" => scheduler,
1509 # rip_address:rport => {
1510 # "forward" => forwarding_mechanism,
1511 # "weight" => weight
1520 # vip_address: IP address of virtual service
1521 # vport: Port of virtual service
1522 # module_name: Depicts the name of the module (For example, pfilter)
1523 # module_key_value: Depicts the module key values (For example, --path-match xxxx)
1524 # scheduler: Scheduler for virtual service
1526 # rip_address: IP address of real server
1527 # rport: Port of real server
1528 # forwarding_mechanism: Forwarding mechanism for real server. This would be only masq.
1529 # weight: Weight of real server
1532 # post: l7vsadm -K -n is parsed
1533 # result: reference to structure detailed above.
1534 sub ld_read_l7vsadm {
1535 my $current_service = {};
1538 if ( !-f $PROC_ENV{l7vsadm} || !-x $PROC_ENV{l7vsadm} ) {
1539 ld_log( _message( 'FTL0101', $PROC_ENV{l7vsadm} ) );
1540 return $current_service;
1542 # read status of current l7vsadm -K -n
1543 # -K indicates Key parameters of the module included.
1544 my $list_command = $PROC_ENV{l7vsadm} . " -K -n";
1545 my $cmd_result = qx{$list_command};
1546 my @list_line = split /\n/, $cmd_result;
1547 my $other_virtual_flag = 'off';
1548 my $other_virtual_count = 0;
1549 my $other_virtual_option = undef;
1552 # [cf] Layer-7 Virtual Server version 2.0.0-0
1553 # [cf] Prot LocalAddress:Port ProtoMod Scheduler Reschedule Protomod_key_string
1554 # [cf] -> RemoteAddress:Port Forward Weight ActiveConn InactConn
1555 shift @list_line; shift @list_line; shift @list_line;
1557 for my $line (@list_line) {
1558 # check virtual service line format
1559 # [cf] TCP 192.168.0.4:12121 sessionless rr
1560 # TCP [2031:130f:876a::130b]:1231 sessionless rr
1561 #### ((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[0-9a-fA-F:])(%.+)?\]:\d{1,5}) \s+ # ip port
1565 (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}) \s+ # ip port
1566 (\w+) \s+ # protocol module
1575 (\[[0-9a-fA-F:]+(?:%.+)?\]:\d{1,5}) \s+ # ip port
1576 (\w+) \s+ # protocol module
1582 my ($proto, $ip_port, $module) = ($1, $2, $3);
1583 # vip_id MUST be same format as get_virtual_id_str
1585 $vip_id = "$proto:$ip_port:$module";
1586 $vip_id =~ s/\s+$//;
1587 $current_service->{$vip_id} = undef;
1588 $other_virtual_flag = 'on';
1589 $other_virtual_option = undef;
1590 $other_virtual_count = 0;
1593 # check real server line format
1594 # [cf] -> 192.168.0.4:7780 Masq 1 10 123456
1595 if ((defined $vip_id && $line =~ /
1598 (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d{1,5}) \s+ # ip port
1601 \d+ \s+ # active connections
1602 \d+ \s* # inactive connections
1606 ||(defined $vip_id && $line =~ /
1609 (\[[0-9a-fA-F:]+(?:%.+)?\]):(\d{1,5}) \s+ # ip port
1612 \d+ \s+ # active connections
1613 \d+ \s* # inactive connections
1617 my ($ip, $port, $forward, $weight) = ($1, $2, $3, $4);
1618 my $ip_port = "$ip:$port";
1620 server => { ip => $ip, port => $port },
1622 forward => $forward,
1624 flags => "-r $ip_port",
1625 forward => get_forward_flag($forward),
1628 $other_virtual_flag = 'off';
1629 $current_service->{$vip_id}{$ip_port} = $real;
1631 elsif ($other_virtual_flag eq 'on'){
1632 ## SSL_config_file value set D->A Command
1633 ## Socket option value set D->A Command
1634 ## Access_log_flag value set E Command
1635 ## Access_log_file value set D->A Command
1636 ## Access_log_rotate option value set D->A Command
1637 if ($other_virtual_count != 2 ) {
1639 $other_virtual_option .= $line;
1640 $current_service->{$vip_id}{other_virtual_option}
1641 = $other_virtual_option;
1643 $other_virtual_count++;
1646 return $current_service;
1649 # ld_operate_virtual
1650 # Operate virtual service on l7vsd by l7vsadm command.
1651 sub ld_operate_virtual {
1652 my ($v, $option, $success_code, $error_code) = @_;
1653 if (!defined $v || !defined $option || !defined $success_code || !defined $error_code) {
1654 ld_log( _message('ERR0501') );
1658 my $command = $PROC_ENV{l7vsadm} . " $option ";
1659 if ($option ne '-D') {
1660 $command .= $v->{option}{flags};
1663 $command .= $v->{option}{main};
1665 $command .= ' 2>&1';
1667 my ($result, $output) = command_wrapper($command);
1669 my $module_key = $v->{module}{name};
1670 if ( defined $v->{module}{key} ) {
1671 $module_key .= q{ } . $v->{module}{key};
1674 ld_log( _message($success_code, get_ip_port($v), $module_key) );
1677 ($output) = split /\n/, $output, 2;
1678 ld_log( _message($error_code, get_ip_port($v), $module_key, $output) );
1683 # Call operate virtual with add option.
1684 sub ld_add_virtual {
1686 ld_operate_virtual($v, '-A', 'INF0201', 'ERR0201');
1690 # Call operate virtual with edit option.
1691 sub ld_edit_virtual {
1693 ld_operate_virtual($v, '-E', 'INF0202', 'ERR0202');
1697 # Call operate virtual with delete option.
1698 sub ld_delete_virtual {
1700 ld_operate_virtual($v, '-D', 'INF0203', 'ERR0203');
1704 # Operate real server on l7vsd by l7vsadm command.
1705 sub ld_operate_real {
1706 my ($v, $r, $weight, $option, $success_code, $error_code) = @_;
1707 if (!defined $v || !defined $r || !defined $option || !defined $success_code || !defined $error_code) {
1708 ld_log( _message('ERR0501') );
1713 = $PROC_ENV{l7vsadm} . " $option " . $v->{option}{main} . q{ } . $r->{option}{flags};
1715 # replace weight value
1716 if (defined $weight) {
1717 $command .= ' -w ' . $weight;
1719 $command .= ' 2>&1';
1721 my ($result, $output) = command_wrapper($command);
1723 my $module_key = $v->{module}{name};
1724 if ( defined $v->{module}{key} ) {
1725 $module_key .= q{ } . $v->{module}{key};
1728 ld_log( _message($success_code, get_ip_port($r), get_ip_port($v), $module_key, $weight) );
1731 ($output) = split /\n/, $output, 2;
1732 ld_log( _message($error_code, get_ip_port($r), get_ip_port($v), $module_key, $output) );
1737 # Call operate real with add option.
1739 my ($v, $r, $weight) = @_;
1740 ld_operate_real($v, $r, $weight, '-a', 'INF0204', 'ERR0204');
1744 # Call operate real with edit option.
1746 my ($v, $r, $weight) = @_;
1747 ld_operate_real($v, $r, $weight, '-e', 'INF0205', 'ERR0205');
1751 # Call operate real with delete option.
1752 sub ld_delete_real {
1754 ld_operate_real($v, $r, undef, '-d', 'INF0206', 'ERR0206');
1758 # Check l7vsd by l7vsadm command and create virtual service on l7vsd.
1760 # read status of current l7vsadm -K -n
1761 my $current_service = ld_read_l7vsadm();
1762 if (!defined $current_service ) {
1763 ld_log( _message('FTL0201') );
1767 my %old_health_check = %HEALTH_CHECK;
1770 # make sure virtual servers are up to date
1771 if ( defined $CONFIG{virtual} ) {
1772 for my $nv ( @{ $CONFIG{virtual} } ) {
1773 my $vip_id = get_virtual_id_str($nv);
1774 if (!defined $vip_id) {
1775 ld_log( _message('ERR0502') );
1779 if ( exists( $current_service->{$vip_id} )
1780 && (defined $current_service->{$vip_id}{other_virtual_option}
1781 && defined $nv->{other_virtual_key}
1782 && $current_service->{$vip_id}{other_virtual_option}
1783 eq $nv->{other_virtual_key} )) {
1784 # service already exists, modify it
1785 ld_edit_virtual($nv);
1788 # no such service, create a new one
1789 ld_delete_virtual($nv);
1790 # no such service, create a new one
1791 ld_add_virtual($nv);
1794 my $or = $current_service->{$vip_id} || {};
1796 # Not delete fallback server from l7vsd if exist
1797 my $fallback = fallback_find($nv);
1798 if (defined $fallback) {
1799 my $fallback_ip_port = get_ip_port( $fallback->{ $nv->{protocol} } );
1800 delete $or->{$fallback_ip_port};
1804 if ( defined $nv->{real} ) {
1806 for my $nr ( @{ $nv->{real} } ) {
1807 delete $or->{ get_ip_port($nr) };
1809 my $health_check_id = get_health_check_id_str($nv, $nr);
1810 if (!defined $health_check_id) {
1811 ld_log( _message('ERR0503') );
1815 # search same health check process
1816 if ( exists $HEALTH_CHECK{$health_check_id} ) {
1817 # same health check process exist
1818 # then check real server and virtual service ($r, $v)
1819 for my $v_r_pair ( @{ $HEALTH_CHECK{$health_check_id}{manage} } ) {
1820 # completely same. check next real server
1821 next CHECK_REAL if ($nv eq $v_r_pair->[0] && $nr eq $v_r_pair->[1]);
1824 # add real server and virtual service to management list
1825 push @{ $HEALTH_CHECK{$health_check_id}{manage} }, [$nv, $nr];
1828 # add to health check process list
1829 $HEALTH_CHECK{$health_check_id}{manage} = [ [$nv, $nr] ];
1834 # remove remaining entries for real servers
1835 for my $remove_real_ip_port (keys %$or) {
1836 ld_delete_real( $nv, $or->{$remove_real_ip_port} );
1837 delete $or->{$remove_real_ip_port};
1840 delete $current_service->{$vip_id};
1844 # terminate old health check process
1845 # TODO should compare old and new, and only if different then re-create process...
1846 for my $id (keys %old_health_check) {
1847 # kill old health check process
1848 if ( defined $old_health_check{$id}{pid} ) {
1849 # TODO cannot kill process during pinging to unreachable host?
1851 local $SIG{ALRM} = sub { die; };
1852 kill 15, $old_health_check{$id}{pid};
1855 waitpid $old_health_check{$id}{pid}, 0;
1860 kill 9, $old_health_check{$id}{pid};
1861 waitpid $old_health_check{$id}{pid}, WNOHANG;
1867 # remove remaining entries for virtual servers
1868 if ( defined $CONFIG{old_virtual} ) {
1869 for my $nv ( @{ $CONFIG{old_virtual} } ) {
1870 my $vip_id = get_virtual_id_str($nv);
1871 if ( exists $current_service->{$vip_id} ) {
1872 # service still exists, remove it
1873 ld_delete_virtual($nv);
1877 delete $CONFIG{old_virtual};
1881 # Run l7directord command to child process.
1882 # Child process is not health check process,
1883 # but sub config (specified by configuration with `execute') process.
1884 sub ld_cmd_children {
1885 my $command_type = shift;
1886 my $execute = shift;
1888 # instantiate other l7directord, if specified
1889 if (!defined $execute) {
1890 if ( defined $CONFIG{execute} ) {
1891 for my $sub_config ( keys %{ $CONFIG{execute} } ) {
1892 if (defined $command_type && defined $sub_config) {
1893 my $command = $PROC_ENV{l7directord} . " $sub_config $command_type";
1894 system_wrapper($command);
1900 for my $sub_config ( keys %$execute ) {
1901 if (defined $command_type && defined $sub_config) {
1902 my $command = $PROC_ENV{l7directord} . " $sub_config $command_type";
1903 system_wrapper($command);
1910 # Remove virtual service for stopping this program.
1912 my $srv = ld_read_l7vsadm();
1913 if (!defined $srv) {
1914 ld_log( _message('FTL0201') );
1917 if ( defined $CONFIG{virtual} ) {
1918 for my $v ( @{ $CONFIG{virtual} } ) {
1919 my $vid = get_virtual_id_str($v);
1920 if (!defined $vid) {
1921 ld_log( _message('ERR0502') );
1924 if ( exists $srv->{$vid} ) {
1925 for my $rid ( keys %{ $srv->{$vid} } ) {
1926 ld_delete_real( $v, $srv->{$vid}{$rid} );
1929 ld_delete_virtual($v);
1935 # Main function of this program.
1936 # Create virtual service and loop below 3 steps.
1937 # 1. Check health check sub process and (re-)create sub process as needed
1938 # 2. Check signal in sleep and start to terminate program or reload config as needed
1939 # 3. Check config file and reload config as needed
1943 # Main failover checking code
1946 # manage real server check process.
1949 my @id_lists = check_child_process();
1950 # if child process is not running
1952 create_check_process(@id_lists);
1954 my $signal = sleep_and_check_signal( $CONFIG{configinterval} );
1955 last MAIN_LOOP if defined $signal && $signal eq 'halt';
1956 last REAL_CHECK if defined $signal && $signal eq 'reload';
1957 last REAL_CHECK if check_cfgfile();
1964 # signal TERM to child process
1965 for my $id (keys %HEALTH_CHECK) {
1966 if ( defined $HEALTH_CHECK{$id}{pid} ) {
1967 # TODO cannot kill process during pinging to unreachable host?
1969 local $SIG{ALRM} = sub { die; };
1970 kill 15, $HEALTH_CHECK{$id}{pid};
1973 waitpid $HEALTH_CHECK{$id}{pid}, 0;
1978 kill 9, $HEALTH_CHECK{$id}{pid};
1979 waitpid $HEALTH_CHECK{$id}{pid}, WNOHANG;
1987 # check_child_process
1988 # Check health check process by signal zero.
1989 # return: Health check id list that (re-)created later.
1990 sub check_child_process {
1991 my @down_process_ids = ();
1992 for my $id (sort keys %HEALTH_CHECK) {
1993 if ( !defined $HEALTH_CHECK{$id}{pid} ) {
1995 ld_log( _message('INF0401', $id) );
1996 push @down_process_ids, $id;
2000 my $signaled = kill 0, $HEALTH_CHECK{$id}{pid};
2001 if ($signaled != 1) {
2002 # maybe killed from outside
2003 ld_log( _message('ERR0603', $HEALTH_CHECK{$id}{pid}, $id) );
2004 push @down_process_ids, $id;
2008 return @down_process_ids;
2011 # create_check_process
2012 # Fork health check sub process.
2013 # And health check sub process run health_check sub function.
2014 sub create_check_process {
2016 for my $health_check_id (@id_lists) {
2019 ld_log( _message('INF0402', $pid, $health_check_id) );
2020 $HEALTH_CHECK{$health_check_id}{pid} = $pid;
2023 $PROC_STAT{parent_pid} = $PROC_STAT{pid};
2024 $PROC_STAT{pid} = $PID;
2025 health_check( $HEALTH_CHECK{$health_check_id}{manage} );
2028 ld_log( _message('ERR0604', $health_check_id) );
2035 # Main function of health check process.
2038 # 2. Status change and reflect to l7vsd as needed.
2039 # 3. Check signal in sleep.
2040 # pre: v_r_list: reference list of virtual service and real server pair
2041 # $v_r_list = [ [$virtual, $real], [$virtual, $real], ... ];
2043 # MUST use POSIX::_exit when terminate sub process.
2045 my $v_r_list = shift;
2046 if (!defined $v_r_list) {
2047 ld_log( _message('ERR0501') );
2048 ld_log( _message('FTL0001') );
2052 # you can use any virtual, real pair in $v_r_list.
2053 my ($v, $r) = @{ $v_r_list->[0] };
2054 if (!defined $v || !defined $r) {
2055 ld_log( _message('FTL0002') );
2059 my $health_check_func = get_check_func($v);
2060 my $current_status = get_status($v_r_list);
2062 my $status = 'STARTING';
2063 my $type = $v->{checktype} eq 'negotiate' ? $v->{service}
2064 : $v->{checktype} eq 'combined' ? $v->{service} . '(combined)'
2067 $PROGRAM_NAME = 'l7directord: ' . $type . ':' . get_ip_port($r) . ':' . $status;
2071 my $service_status = &$health_check_func($v, $r);
2073 if ($service_status == $SERVICE_DOWN) {
2074 if (!defined $current_status || $current_status == $SERVICE_UP) {
2075 $r->{fail_counts}++;
2076 undef $r->{num_connects};
2077 if ($r->{fail_counts} >= $v->{checkcount}) {
2078 ld_log( _message( 'ERR0602', get_ip_port($r) ) );
2079 service_set($v_r_list, 'down');
2080 $current_status = $SERVICE_DOWN;
2082 $r->{fail_counts} = 0;
2085 ld_log( _message( 'WRN1001', get_ip_port($r), $v->{checkcount} - $r->{fail_counts} ) );
2086 $status = sprintf "NG[%d/%d]", $r->{fail_counts}, $v->{checkcount};
2090 if ($service_status == $SERVICE_UP) {
2091 $r->{fail_counts} = 0;
2092 if (!defined $current_status || $current_status == $SERVICE_DOWN) {
2093 ld_log( _message( 'ERR0601', get_ip_port($r) ) );
2094 service_set($v_r_list, 'up');
2095 $current_status = $SERVICE_UP;
2100 $PROGRAM_NAME = 'l7directord: ' . $type . ':' . get_ip_port($r) . ':' . $status;
2102 my $sleeptime = $r->{fail_counts} ? $v->{retryinterval} : $v->{checkinterval};
2103 last if (sleep_and_check_signal($sleeptime, 1) eq 'halt');
2105 my $parent_process = kill 0, $PROC_STAT{parent_pid};
2106 if ($parent_process != 1) {
2107 ld_log( _message( 'FTL0003', $PROC_STAT{parent_pid} ) );
2112 ld_log( _message('INF0007') );
2116 # sleep_and_check_signal
2117 # Check signal flag each 0.1 secound with sleeping specified seconds.
2118 sub sleep_and_check_signal {
2119 my ($sec, $is_child) = @_;
2120 if (!defined $sec || $sec !~ /^\d+$/) {
2121 ld_log( _message('ERR0501') );
2126 while ($sec > $sleeped) {
2127 # non-blocking wait for zombie process
2128 waitpid(-1, WNOHANG); # TODO should move to sigchld handler?
2131 if ( defined $PROC_STAT{halt} ) {
2132 ld_log( _message( 'WRN0001', $CONFIG_FILE{path}, $PROC_STAT{halt} ) );
2137 if ( defined $PROC_STAT{halt} ) {
2138 ld_log( _message( 'WRN0001', $CONFIG_FILE{path}, $PROC_STAT{halt} ) );
2141 if ( defined $PROC_STAT{reload} ) {
2142 ld_log( _message( 'WRN0002', $CONFIG_FILE{path}, $PROC_STAT{reload} ) );
2143 undef $PROC_STAT{reload};
2154 # Determine check function by checktype and service.
2155 sub get_check_func {
2158 ld_log( _message('ERR0501') );
2162 my $type = $v->{checktype};
2163 my $service_func = {
2164 http => \&check_http,
2165 https => \&check_http,
2167 imap => \&check_imap,
2168 smtp => \&check_smtp,
2170 ldap => \&check_ldap,
2171 nntp => \&check_nntp,
2174 mysql => \&check_mysql,
2175 pgsql => \&check_pgsql,
2178 if ( defined $type && ($type eq 'negotiate' || $type eq 'combined') ) {
2179 if (defined $v->{service} && exists $service_func->{ $v->{service} } ) {
2180 my $negotiate_func = $service_func->{ $v->{service} };
2181 if ($type eq 'negotiate') {
2182 return $negotiate_func;
2184 elsif ($type eq 'combined') {
2185 my $combined_func = make_combined_func($negotiate_func);
2186 return $combined_func;
2190 return \&check_none;
2194 if (defined $type && $type eq 'custom') {
2195 my $custom_func = make_custom_func( $v->{customcheck} );
2196 return $custom_func;
2199 if (defined $type && $type eq 'connect') {
2200 if (defined $v->{protocol} && $v->{protocol} eq 'tcp') {
2201 return \&check_connect;
2204 return \&check_ping;
2208 if (defined $type && $type eq 'ping') {
2209 return \&check_ping;
2212 if (defined $type && $type eq 'off') {
2216 if (defined $type && $type eq 'on') {
2220 return \&check_none;
2223 # make_combined_func
2224 # Create combined function.
2225 sub make_combined_func {
2226 my $negotiate_func = shift;
2227 if (!defined $negotiate_func) {
2228 ld_log( _message('ERR0504') );
2229 return \&check_connect;
2233 my $combined_func = sub {
2235 my $timing = $v->{num_connects};
2236 my $connected = $r->{num_connects};
2238 if (!defined $connected ||
2239 (defined $timing && $timing <= $connected) ) {
2240 $r->{num_connects} = 0;
2241 return &$negotiate_func($v, $r);
2244 $r->{num_connects}++;
2245 return check_connect($v, $r);
2249 return $combined_func;
2253 # Create custom check function.
2254 sub make_custom_func {
2255 my $customcheck = shift;
2256 if (!defined $customcheck) {
2257 ld_log( _message('ERR0505') );
2262 my $custom_func = sub {
2264 my $status = get_status([[$v, $r]]);
2265 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2266 my $ip_port = $r->{server}{ip} . ':' . $port;
2269 $customcheck =~ s/_IP_/$r->{server}{ip}/g;
2270 $customcheck =~ s/_PORT_/$port/g;
2274 local $SIG{__DIE__} = 'DEFAULT';
2275 local $SIG{ALRM} = sub { die "custom check timeout\n"; };
2277 alarm $v->{checktimeout};
2278 $res = system_wrapper($customcheck);
2283 ld_log( _message('WRN3301', $v->{checktimeout}, $ip_port) ) if (!defined $status || $status eq $SERVICE_UP);
2284 return $SERVICE_DOWN;
2288 ld_log( _message('WRN3302', $customcheck, $res) ) if (!defined $status || $status eq $SERVICE_UP);
2289 return $SERVICE_DOWN;
2291 ld_log( _message('WRN0215', $ip_port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2295 return $custom_func;
2299 # HTTP service health check.
2300 # Send GET/HEAD request, and check response
2302 require LWP::UserAgent;
2304 if ( $DEBUG_LEVEL > 2 ) {
2305 LWP::Debug::level('+');
2308 my $status = get_status([[$v, $r]]);
2310 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2312 if ( $r->{url} !~ m{^https?://([^:/]+)} ) {
2313 ld_log( _message( 'WRN1101', $r->{url}, $r->{server}{ip}, $port ) ) if (!defined $status || $status eq $SERVICE_UP);
2314 return $SERVICE_DOWN;
2317 my $virtualhost = defined $v->{virtualhost} ? $v->{virtualhost} : $host;
2319 ld_debug(2, "check_http: url=\"$r->{url}\" " . "virtualhost=\"$virtualhost\"");
2321 my $ua = LWP::UserAgent->new( timeout => $v->{negotiatetimeout} );
2322 my $req = new HTTP::Request( $v->{httpmethod}, $r->{url}, [ Host => $virtualhost ] );
2325 # LWP makes ungaurded calls to eval
2326 # which throw a fatal exception if they fail
2327 local $SIG{__DIE__} = 'DEFAULT';
2328 local $SIG{ALRM} = sub { die "Can't connect to $r->{server}{ip}:$port (connect: timeout)\n"; };
2330 alarm $v->{negotiatetimeout};
2331 $res = $ua->request($req);
2337 my $status_line = $res->status_line;
2338 $status_line =~ s/[\r\n]//g;
2340 my $recstr = $r->{receive};
2341 if (!$res->is_success) {
2342 ld_log( _message( 'WRN1102', $status_line, $r->{server}{ip}, $port ) ) if (!defined $status || $status eq $SERVICE_UP);
2343 return $SERVICE_DOWN;
2345 elsif (defined $recstr && $res->as_string !~ /$recstr/) {
2346 ld_log( _message( 'WRN1103', $recstr, $r->{server}{ip}, $port ) ) if (!defined $status || $status eq $SERVICE_UP);
2347 ld_debug(3, "Headers " . $res->headers->as_string);
2348 ld_debug(2, "check_http: $r->{url} is down\n");
2349 return $SERVICE_DOWN;
2352 ld_debug(2, "check_http: $r->{url} is up\n");
2353 ld_log( _message( 'WRN0203', $status_line, $r->{server}{ip}, $port ) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2358 # SMTP service health check.
2359 # Connect SMTP server and check first response
2363 my $status = get_status([[$v, $r]]);
2365 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2367 ld_debug(2, "Checking http: server=$r->{server}{ip} port=$port");
2368 my $debug_flag = $DEBUG_LEVEL ? 1 : 0;
2370 my $smtp = Net::SMTP->new(
2373 Timeout => $v->{negotiatetimeout},
2374 Debug => $debug_flag,
2377 ld_log( _message('WRN1201', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2378 return $SERVICE_DOWN;
2382 ld_log( _message('WRN0204', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2387 # POP3 service health check.
2388 # Connect POP3 server and login if user-pass specified.
2392 my $status = get_status([[$v, $r]]);
2394 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2396 ld_debug(2, "Checking pop server=$r->{server}{ip} port=$port");
2397 my $debug_flag = $DEBUG_LEVEL ? 1 : 0;
2399 my $pop = Net::POP3->new(
2402 Timeout => $v->{negotiatetimeout},
2403 Debug => $debug_flag,
2406 ld_log( _message('WRN1301', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2407 return $SERVICE_DOWN;
2410 if ( defined $v->{login} && defined $v->{passwd} && $v->{login} ne q{} ) {
2411 $pop->user( $v->{login} );
2412 my $num = $pop->pass( $v->{passwd} );
2413 if (!defined $num) {
2414 ld_log( _message('WRN1302', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2416 return $SERVICE_DOWN;
2421 ld_log( _message('WRN0205', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2426 # IMAP service health check.
2427 # Connect IMAP server and login if user-pass specified.
2429 require Mail::IMAPClient;
2431 my $status = get_status([[$v, $r]]);
2433 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2435 ld_debug(2, "Checking imap server=$r->{server}{ip} port=$port");
2436 my $debug_flag = $DEBUG_LEVEL ? 1 : 0;
2440 local $SIG{ALRM} = sub { die "Connection timeout\n"; };
2442 alarm $v->{negotiatetimeout};
2443 $imap = Mail::IMAPClient->new(
2444 Server => $r->{server}{ip},
2446 Timeout => $v->{negotiatetimeout},
2447 Debug => $debug_flag,
2453 ld_log( _message('WRN1403', $v->{negotiatetimeout}, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2454 return $SERVICE_DOWN;
2458 ld_log( _message('WRN1401', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2459 return $SERVICE_DOWN;
2462 if ( defined $v->{login} && defined $v->{passwd} && $v->{login} ne q{} ) {
2463 $imap->User( $v->{login} );
2464 $imap->Password( $v->{passwd} );
2465 my $authres = $imap->login();
2467 ld_log( _message('WRN1402', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2469 return $SERVICE_DOWN;
2474 ld_log( _message('WRN0206', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2479 # LDAP service health check.
2480 # Connect LDAP server and search if base-DN specified by 'request'
2484 my $status = get_status([[$v, $r]]);
2486 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2488 ld_debug(2, "Checking ldap server=$r->{server}{ip} port=$port");
2489 my $debug_flag = $DEBUG_LEVEL ? 15 : 0;
2491 my $ldap = Net::LDAP->new(
2494 timeout => $v->{negotiatetimeout},
2495 debug => $debug_flag,
2498 ld_log( _message('WRN1501', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2499 return $SERVICE_DOWN;
2504 local $SIG{ALRM} = sub { die "Connection timeout\n"; };
2506 alarm $v->{negotiatetimeout};
2507 $mesg = $ldap->bind;
2512 ld_log( _message('WRN1502', $v->{negotiatetimeout}, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2513 return $SERVICE_DOWN;
2516 if ($mesg->is_error) {
2517 ld_log( _message('WRN1503', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2518 return $SERVICE_DOWN;
2521 if ( defined $r->{request} && $r->{request} ne q{} ) {
2522 ld_debug( 4, "Base : " . $r->{request} );
2523 my $result = $ldap->search(
2524 base => $r->{request},
2526 filter => '(objectClass=*)',
2529 if ($result->count != 1) {
2530 ld_log( _message('WRN1504', $result->count, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2532 return $SERVICE_DOWN;
2535 if ( defined $r->{receive} ) {
2536 my $href = $result->as_struct;
2537 my @arrayOfDNs = keys %$href;
2538 my $recstr = $r->{receive};
2539 if ($recstr =~ /.+/ && $arrayOfDNs[0] !~ /$recstr/) {
2540 ld_log( _message('WRN1505', $recstr, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2542 return $SERVICE_DOWN;
2548 ld_log( _message('WRN0207', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2553 # NNTP service health check.
2554 # Connect NNTP server and check response start with '2**'
2559 my $status = get_status([[$v, $r]]);
2561 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2563 ld_debug(2, "Checking nntp server=$r->{server}{ip} port=$port");
2565 my $sock = ld_open_socket( $r->{server}{ip}, $port, $v->{protocol}, $v->{negotiatetimeout} );
2567 ld_log( _message('WRN1601', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2568 return $SERVICE_DOWN;
2571 ld_debug(3, "Connected to $r->{server}{ip} (port $port)");
2572 my $select = IO::Select->new();
2573 $select->add($sock);
2574 if ( !defined $select->can_read( $v->{negotiatetimeout} ) ) {
2575 ld_log( _message('WRN1602', $v->{negotiatetimeout}, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2576 $select->remove($sock);
2578 return $SERVICE_DOWN;
2582 sysread $sock, $buf, 64;
2583 $select->remove($sock);
2585 my ($response) = split /[\r\n]/, $buf;
2587 if ($response !~ /^2/) {
2588 ld_log( _message('WRN1603', $response, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2589 return $SERVICE_DOWN;
2592 ld_log( _message('WRN0208', $response, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2597 # MySQL service health check.
2598 # call check_sql and use MySQL driver
2600 return check_sql(@_, 'mysql', 'database');
2604 # PostgreSQL service health check.
2605 # call check_sql and use PostgreSQL driver
2607 return check_sql(@_, 'Pg', 'dbname');
2611 # DBI service health check.
2612 # Login DB and send query if query specified by 'request', check result row number same as 'receive'
2615 my ($v, $r, $dbd, $dbname) = @_;
2616 my $status = get_status([[$v, $r]]);
2618 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2620 if ( !defined $v->{login} || !defined $v->{passwd} || !defined $v->{database} ||
2621 $v->{login} eq q{} || $v->{database} eq q{} ) {
2622 ld_log( _message('WRN1701', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2623 return $SERVICE_DOWN;
2626 ld_debug(2, "Checking $v->{server}{ip} server=$r->{server}{ip} port=$port\n");
2628 my $mask = POSIX::SigSet->new(SIGALRM);
2629 my $action = POSIX::SigAction->new(
2630 sub { die "Connection timeout\n" },
2633 my $oldaction = POSIX::SigAction->new();
2634 sigaction(SIGALRM, $action, $oldaction);
2638 alarm $v->{negotiatetimeout};
2640 DBI->trace(15) if $DEBUG_LEVEL;
2641 $dbh = DBI->connect( "dbi:$dbd:$dbname=$v->{database};host=$r->{server}{ip};port=$port", $v->{login}, $v->{passwd} );
2644 if (!defined $dbh) {
2646 sigaction(SIGALRM, $oldaction);
2647 ld_log( _message('WRN1702', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2651 local $dbh->{TraceLevel} = $DEBUG_LEVEL ? 15 : 0;
2655 if ( defined $r->{request} && $r->{request} ne q{} ) {
2656 my $sth = $dbh->prepare( $r->{request} );
2657 $rows = $sth->execute;
2664 sigaction(SIGALRM, $oldaction);
2666 if ( defined $r->{request} && $r->{request} ne q{} ) {
2667 ld_debug(4, "Database search returned $rows rows");
2669 ld_log( _message('WRN1703', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2672 # If user defined a receive string (number of rows returned), only do
2673 # the check if the previous fetchall_arrayref succeeded.
2674 if (defined $r->{receive} && $r->{receive} =~ /^\d+$/) {
2675 # Receive string specifies an exact number of rows
2676 if ( $rows ne $r->{receive} ) {
2677 ld_log( _message('WRN1704', $r->{receive}, $rows, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2684 sigaction(SIGALRM, $oldaction);
2686 if ($EVAL_ERROR eq "Connection timeout\n") {
2687 ld_log( _message('WRN1705', $v->{negotiatetimeout}, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2689 return $SERVICE_DOWN;
2692 ld_log( _message('WRN0209', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2697 # Connect service health check.
2698 # Just connect port and close.
2701 my $status = get_status([[$v, $r]]);
2703 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2705 ld_debug(2, "Checking connect: real server=$r->{server}{ip}:$port");
2707 my $sock = ld_open_socket( $r->{server}{ip}, $port, $v->{protocol}, $v->{checktimeout} );
2708 if (!defined $sock) {
2709 ld_log( _message('WRN3201', $ERRNO, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2710 return $SERVICE_DOWN;
2714 ld_debug(3, "Connected to: (port $port)");
2716 ld_log( _message('WRN0210', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2721 # SIP service health check.
2722 # Send SIP OPTIONS request and check 200 response
2725 my $status = get_status([[$v, $r]]);
2727 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2729 ld_debug(2, "Checking sip server=$r->{server}{ip} port=$port");
2731 if ( !defined $v->{login} ) {
2732 ld_log( _message('WRN1801', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2733 return $SERVICE_DOWN;
2736 my $sock = ld_open_socket( $r->{server}{ip}, $port, $v->{protocol}, $v->{negotiatetimeout} );
2737 if (!defined $sock) {
2738 ld_log( _message('WRN1802', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2739 return $SERVICE_DOWN;
2742 my $sip_s_addr = $sock->sockhost;
2743 my $sip_s_port = $sock->sockport;
2745 ld_debug(3, "Connected from $sip_s_addr:$sip_s_port to " . $r->{server} . ":$port");
2747 my $id = $v->{login};
2749 "OPTIONS sip:$id SIP/2.0\r\n"
2750 . "Via: SIP/2.0/UDP $sip_s_addr:$sip_s_port;branch=z9hG4bKhjhs8ass877\r\n"
2751 . "Max-Forwards: 70\r\n"
2752 . "To: <sip:$id>\r\n"
2753 . "From: <sip:$id>;tag=1928301774\r\n"
2754 . "Call-ID: a84b4c76e66710\r\n"
2755 . "CSeq: 63104 OPTIONS\r\n"
2756 . "Contact: <sip:$id>\r\n"
2757 . "Accept: application/sdp\r\n"
2758 . "Content-Length: 0\r\n"
2761 ld_debug(3, "Request:\n$request");
2765 local $SIG{__DIE__} = 'DEFAULT';
2766 local $SIG{ALRM } = sub { die "Connection timeout\n"; };
2767 ld_debug(4, "Timeout is $v->{negotiatetimeout}");
2768 alarm $v->{negotiatetimeout};
2770 print {$sock} $request;
2771 $response = <$sock>;
2775 ld_debug(3, "Response:\n$response");
2777 if ( $response !~ m{^SIP/2\.0 200 OK} ) {
2778 ld_log( _message('WRN1803', $response, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2784 if ($EVAL_ERROR eq "Connection timeout\n") {
2785 ld_log( _message('WRN1804', $v->{negotiatetimeout}, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2787 return $SERVICE_DOWN;
2790 ld_log( _message('WRN0211', $response, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2795 # FTP service health check.
2796 # Login server and get file if 'request' specified, and check file include 'receive' string
2800 my $status = get_status([[$v, $r]]);
2802 my $ip_port = get_ip_port($r, $v->{checkport});
2804 ld_debug(2, "Checking ftp server=$ip_port");
2805 my $debug_flag = $DEBUG_LEVEL ? 1 : 0;
2807 if ( !defined $v->{login} || !defined $v->{passwd} || $v->{login} eq q{} ) {
2808 ld_log( _message('WRN1901', $ip_port) ) if (!defined $status || $status eq $SERVICE_UP);
2809 return $SERVICE_DOWN;
2812 my $ftp = Net::FTP->new(
2814 Timeout => $v->{negotiatetimeout},
2816 Debug => $debug_flag,
2818 if (!defined $ftp) {
2819 ld_log( _message('WRN1902', $ip_port) ) if (!defined $status || $status eq $SERVICE_UP);
2820 return $SERVICE_DOWN;
2822 if ( !$ftp->login( $v->{login}, $v->{passwd} ) ) {
2823 ld_log( _message('WRN1903', $ip_port) ) if (!defined $status || $status eq $SERVICE_UP);
2825 return $SERVICE_DOWN;
2827 if ( !$ftp->cwd('/') ) {
2828 ld_log( _message('WRN1904', $ip_port) ) if (!defined $status || $status eq $SERVICE_UP);
2830 return $SERVICE_DOWN;
2832 if ( $r->{request} ) {
2835 local $SIG{__DIE__} = 'DEFAULT';
2836 local $SIG{ALRM } = sub { die "Connection timeout\n"; };
2837 alarm $v->{negotiatetimeout};
2839 open my $tmp, '+>', undef;
2841 if ( !$ftp->get( $r->{request}, *$tmp ) ) {
2843 ld_log( _message('WRN1905', $r->{request}, $ip_port) ) if (!defined $status || $status eq $SERVICE_UP);
2848 elsif ( $r->{receive} ) {
2851 my $memory = <$tmp>;
2853 if ($memory !~ /$r->{receive}/) {
2856 ld_log( _message('WRN1906', $r->{receive}, $r->{request}, $ip_port) ) if (!defined $status || $status eq $SERVICE_UP);
2864 my $error_message = $EVAL_ERROR;
2865 $error_message =~ s/[\r\n]//g;
2866 if ($error_message eq 'Connection timeout') {
2867 ld_log( _message('WRN1908', $v->{negotiatetimeout}, $ip_port) ) if (!defined $status || $status eq $SERVICE_UP);
2870 ld_log( _message('WRN1907', $error_message, $ip_port) ) if (!defined $status || $status eq $SERVICE_UP);
2872 return $SERVICE_DOWN;
2876 return $SERVICE_DOWN;
2881 ld_log( _message('WRN0212', $ip_port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2886 # DNS service health check.
2887 # Connect server and search 'request' A or PTR record and check result include 'response' string
2890 my $status = get_status([[$v, $r]]);
2892 my $port = defined $v->{checkport} ? $v->{checkport} : $r->{server}{port};
2895 # Net::DNS makes ungaurded calls to eval
2896 # which throw a fatal exception if they fail
2897 local $SIG{__DIE__} = 'DEFAULT';
2900 my $res = Net::DNS::Resolver->new();
2906 if ( !defined $r->{request} || $r->{request} eq q{} || !defined $r->{receive} || $r->{receive} eq q{} ) {
2907 ld_log( _message('WRN2001', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2908 return $SERVICE_DOWN;
2910 ld_debug( 2, qq(Checking dns: request="$r->{request}" receive="$r->{receive}"\n) );
2914 local $SIG{__DIE__} = 'DEFAULT';
2915 local $SIG{ALRM } = sub { die "Connection timeout\n"; };
2916 alarm $v->{negotiatetimeout};
2917 $res->nameservers( $r->{server}{ip} );
2919 $packet = $res->search( $r->{request} );
2924 if ($EVAL_ERROR eq "Connection timeout\n") {
2925 ld_log( _message('WRN2002', $v->{negotiatetimeout}, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2928 ld_log( _message('WRN2003', $EVAL_ERROR, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2930 return $SERVICE_DOWN;
2933 ld_log( _message('WRN2004', $r->{request}, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2934 return $SERVICE_DOWN;
2938 for my $rr ($packet->answer) {
2939 if ( ( $rr->type eq 'A' && $rr->address eq $r->{receive} )
2940 || ( $rr->type eq 'PTR' && $rr->ptrdname eq $r->{receive} ) ) {
2946 ld_log( _message('WRN2005', $r->{receive}, $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_UP);
2947 return $SERVICE_DOWN;
2950 ld_log( _message('WRN0213', $r->{server}{ip}, $port) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2955 # ICMP ping service health check.
2956 # Ping server and check response.
2960 my $status = get_status([[$v, $r]]);
2962 ld_debug( 2, qq(Checking ping: host="$r->{server}{ip}" checktimeout="$v->{checktimeout}"\n) );
2964 if ( is_ip( $r->{server}{ip})) {
2967 my $p = Net::Ping->new('icmp', 1);
2968 if ( !$p->ping( $r->{server}{ip}, $v->{checktimeout} ) ) {
2969 ld_log( _message('WRN3101', $v->{checktimeout}, $r->{server}{ip}) ) if (!defined $status || $status eq $SERVICE_UP);
2970 return $SERVICE_DOWN;
2976 = sprintf "ping6 %s -w %d > /dev/null 2>&1",
2980 if( system_wrapper( $command )) {
2981 ld_log( _message('WRN3101', $v->{checktimeout}, $r->{server}{ip}) ) if (!defined $status || $status eq $SERVICE_UP);
2982 return $SERVICE_DOWN;
2986 ld_log( _message('WRN0214', $r->{server}{ip}) ) if (!defined $status || $status eq $SERVICE_DOWN || $r->{fail_counts} > 0);
2991 # Dummy function to check service if service type is none.
2992 # Just activates the real server
2995 ld_debug(2, "Checking none");
3000 # Check nothing and always return $SERVICE_DOWN
3003 return $SERVICE_DOWN;
3007 # Check nothing and always return $SERVICE_UP
3014 # Used to bring up and down real servers.
3015 # This is the function you should call if you want to bring a real
3016 # server up or down.
3017 # This function is safe to call regrdless of the current state of a
3019 # Do _not_ call _service_up or _service_down directly.
3020 # pre: v_r_list: virtual and real pair list
3021 # [ [$v, $r], [$v, $r] ... ]
3023 # up to bring the real service up
3024 # down to bring the real service up
3025 # post: The real server is brough up or down for each virtual service
3029 my ($v_r_list, $state) = @_;
3031 if (defined $state && $state eq 'up') {
3032 _service_up($v_r_list);
3034 elsif (defined $state && $state eq 'down') {
3035 _service_down($v_r_list);
3040 # Bring a real service up if it is down
3041 # Should be called by service_set only
3042 # I.e. If you want to change the state of a real server call service_set.
3043 # If you call this function directly then l7directord will lose track
3044 # of the state of real servers.
3045 # pre: v_r_list: virtual and real pair list
3046 # [ [$v, $r], [$v, $r] ... ]
3047 # post: real service is taken up from the respective virtual service
3051 my $v_r_list = shift;
3052 if ( !_status_up($v_r_list) ) {
3056 for my $v_r_pair (@$v_r_list) {
3057 my ($v, $r) = @$v_r_pair;
3058 _restore_service($v, $r, 'real');
3064 # Bring a real service down if it is up
3065 # Should be called by service_set only
3066 # I.e. if you want to change the state of a real server call service_set.
3067 # If you call this function directly then l7directord will lose track
3068 # of the state of real servers.
3069 # pre: v_r_list: virtual and real pair list
3070 # [ [$v, $r], [$v, $r] ... ]
3071 # post: real service is taken down from the respective virtual service
3075 my $v_r_list = shift;
3076 if ( !_status_down($v_r_list) ) {
3080 for my $v_r_pair (@$v_r_list) {
3081 my ($v, $r) = @$v_r_pair;
3082 _remove_service($v, $r, 'real');
3088 # Set the status of a server as up
3089 # Should only be called from _service_up or fallback_on
3091 my ($v_r_list, $is_fallback) = @_;
3092 if (!defined $v_r_list) {
3096 if (!$is_fallback) {
3097 my $current_status = get_status($v_r_list);
3098 if (defined $current_status && $current_status eq $SERVICE_UP) {
3102 my $id = get_health_check_id_str( @{ $v_r_list->[0] } );
3104 ld_log( _message('ERR0503') );
3107 $HEALTH_CHECK{$id}{status} = $SERVICE_UP;
3112 my $current_service = ld_read_l7vsadm();
3113 if (!defined $current_service) {
3114 ld_log( _message('FTL0201') );
3117 my $vid = get_virtual_id_str( $v_r_list->[0][0] );
3118 if ( exists $current_service->{$vid} ) {
3120 if ( !defined $current_service->{$vid} ) {
3124 # all real server's weight are zero.
3125 for my $real ( keys %{ $current_service->{$vid} } ) {
3126 # already added fallback server.
3127 if ( $real eq get_ip_port( $v_r_list->[0][1] ) ) {
3130 $weight += $current_service->{$vid}{$real}{weight};
3141 # Set the status of a server as down
3142 # Should only be called from _service_down or _ld_stop
3144 my ($v_r_list, $is_fallback) = (@_);
3145 if (!defined $v_r_list) {
3149 if (!$is_fallback) {
3150 my $current_status = get_status($v_r_list);
3151 if ($current_status && $current_status eq $SERVICE_DOWN) {
3155 my $id = get_health_check_id_str( @{ $v_r_list->[0] } );
3157 ld_log( _message('ERR0503') );
3160 $HEALTH_CHECK{$id}{status} = $SERVICE_DOWN;
3165 my $current_service = ld_read_l7vsadm();
3166 if (!defined $current_service) {
3167 ld_log( _message('FTL0201') );
3170 my $vid = get_virtual_id_str( $v_r_list->[0][0] );
3171 if ( defined $current_service->{$vid} ) {
3173 my $fallback_exist = 0;
3174 # any real server has weight.
3175 for my $real ( keys %{ $current_service->{$vid} } ) {
3176 if ( $real eq get_ip_port( $v_r_list->[0][1] ) ) {
3177 $fallback_exist = 1;
3179 $weight += $current_service->{$vid}{$real}{weight};
3181 if ($fallback_exist && $weight) {
3190 # Get health check server status
3191 # return $SERVICE_UP / $SERVICE_DOWN
3193 my $v_r_list = shift;
3195 my $id = get_health_check_id_str( @{ $v_r_list->[0] } );
3197 ld_log( _message('ERR0503') );
3200 return $HEALTH_CHECK{$id}{status};
3204 # Remove a real server by either making it quiescent or deleteing it
3205 # Should be called by _service_down or fallback_off
3206 # I.e. If you want to change the state of a real server call service_set.
3207 # If you call this function directly then l7directord will lose track
3208 # of the state of real servers.
3209 # If the real server exists (which it should) make it quiescent or
3210 # delete it, depending on the global and per virtual service quiecent flag.
3211 # If it # doesn't exist, just leave it as it will be added by the
3212 # _service_up code as appropriate.
3213 # pre: v: reference to virtual service to with the real server belongs
3214 # rservice: service to restore. Of the form server:port for tcp
3215 # rforw: Forwarding mechanism of service. Should be only "-m"
3216 # rforw is kept as it is, even though not used - NTT COMWARE
3217 # tag: Tag to use for logging. Should be either "real" or "fallback"
3218 # post: real service is taken up from the respective virtual service
3221 sub _remove_service {
3222 my ($v, $r, $tag) = @_;
3223 if (!defined $v || !defined $r) {
3224 ld_log( _message('ERR0501') );
3228 my $vip_id = get_virtual_id_str($v);
3229 if (!defined $vip_id) {
3230 ld_log( _message('ERR0502') );
3233 my $oldsrv = ld_read_l7vsadm();
3234 if (!defined $oldsrv) {
3235 ld_log( _message('FTL0201') );
3239 if ( !exists $oldsrv->{$vip_id} ) {
3240 ld_log( _message( 'ERR0208', get_ip_port($r), get_ip_port($v) ) );
3245 my $is_quiescent = 0;
3246 if (!defined $tag || $tag ne 'fallback') {
3247 if ( defined $v->{quiescent} && $v->{quiescent} ) {
3252 my $or = $oldsrv->{$vip_id}{ get_ip_port($r) };
3253 # already removed server
3254 if (!defined $or && !$is_quiescent) {
3255 my $module_key = $v->{module}{name} . q{ } . $v->{module}{key};
3256 ld_log( _message( 'ERR0210', get_ip_port($r), get_ip_port($v), $module_key ) );
3259 # already quiescent server
3260 if ( defined $or && $is_quiescent && $or->{weight} == 0 &&
3261 $or->{option}{forward} eq $r->{option}{forward} ) {
3262 my $module_key = $v->{module}{name} . q{ } . $v->{module}{key};
3263 ld_log( _message( 'ERR0211', get_ip_port($r), get_ip_port($v), $module_key ) );
3267 if ($is_quiescent) {
3269 ld_edit_real($v, $r, 0);
3272 ld_add_real($v, $r, 0);
3274 if (!defined $tag || $tag eq 'real') {
3275 ld_log( _message( 'INF0303', get_ip_port($r) ) );
3277 elsif ($tag eq 'fallback') {
3278 ld_log( _message( 'INF0304', get_ip_port($r) ) );
3282 ld_delete_real($v, $r);
3283 if (!defined $tag || $tag eq 'real') {
3284 ld_log( _message( 'INF0305', get_ip_port($r) ) );
3286 elsif ($tag eq 'fallback') {
3287 ld_log( _message( 'INF0306', get_ip_port($r) ) );
3291 if ( defined $v->{realdowncallback} && $r->{healthchecked} ) {
3292 system_wrapper( $v->{realdowncallback}, get_ip_port($r) );
3293 ld_log( _message( 'INF0501', $v->{realdowncallback}, get_ip_port($r) ) );
3295 $r->{healthchecked} = 1;
3299 # Make a retore a real server. The opposite of _quiescent_server.
3300 # Should be called by _service_up or fallback_on
3301 # I.e. If you want to change the state of a real server call service_set.
3302 # If you call this function directly then l7directord will lose track
3303 # of the state of real servers.
3304 # If the real server exists (which it should) make it quiescent. If it
3305 # doesn't exist, just leave it as it will be added by the _service_up code
3307 # pre: v: reference to virtual service to with the real server belongs
3308 # r: reference to real server to restore.
3309 # tag: Tag to use for logging. Should be either "real" or "fallback"
3310 # post: real service is taken up from the respective virtual service
3313 sub _restore_service {
3314 my ($v, $r, $tag) = @_;
3315 if (!defined $v || !defined $r) {
3316 ld_log( _message('ERR0501') );
3320 my $vip_id = get_virtual_id_str($v);
3321 if (!defined $vip_id) {
3322 ld_log( _message('ERR0502') );
3325 my $oldsrv = ld_read_l7vsadm();
3326 if (!defined $oldsrv) {
3327 ld_log( _message('FTL0201') );
3331 if ( !exists $oldsrv->{$vip_id} ) {
3332 ld_log( _message( 'ERR0207', get_ip_port($r), get_ip_port($v) ) );
3336 my $or = $oldsrv->{$vip_id}{ get_ip_port($r) };
3337 # already completely same server exist
3339 $or->{weight} eq $r->{weight} &&
3340 $or->{option}{forward} eq $r->{option}{forward} ) {
3341 my $module_key = $v->{module}{name} . q{ } . $v->{module}{key};
3342 ld_log( _message( 'ERR0209', get_ip_port($r), get_ip_port($v), $module_key ) );
3347 ld_edit_real( $v, $r, $r->{weight} );
3350 ld_add_real( $v, $r, $r->{weight} );
3353 if (!defined $tag || $tag eq 'real') {
3354 ld_log( _message( 'INF0301', get_ip_port($r) ) );
3356 elsif ($tag eq 'fallback') {
3357 ld_log( _message( 'INF0302', get_ip_port($r) ) );
3360 if ( defined $v->{realrecovercallback} && $r->{healthchecked} ){
3361 system_wrapper( $v->{realrecovercallback}, get_ip_port($r) );
3362 ld_log( _message( 'INF0502', $v->{realrecovercallback}, get_ip_port($r) ) );
3364 $r->{healthchecked} = 1;
3368 # Turn on the fallback server for a virtual service if it is inactive
3369 # pre: v: virtual to turn fallback service on for
3370 # post: fallback server is turned on if it was inactive
3375 my $fallback = fallback_find($v);
3376 if (defined $fallback) {
3377 my $v_r_list = [ [ $v, $fallback->{tcp} ] ];
3378 if ( _status_up($v_r_list, 'fallback') ) {
3379 _restore_service($v, $fallback->{tcp}, 'fallback');
3385 # Turn off the fallback server for a virtual service if it is active
3386 # pre: v: virtual to turn fallback service off for
3387 # post: fallback server is turned off if it was active
3392 my $fallback = fallback_find($v);
3393 if (defined $fallback) {
3394 my $v_r_list = [ [ $v, $fallback->{tcp} ] ];
3395 if ( _status_down($v_r_list, 'fallback') ) {
3396 _remove_service($v, $fallback->{tcp}, 'fallback');
3402 # Determine the fallback for a virtual service
3403 # pre: v: reference to a virtual service
3405 # return: $v->{fallback} if defined
3410 ld_log( _message('ERR0501') );
3413 return $v->{fallback};
3417 # Check configfile change.
3419 # post: check configfile size, and then check md5 sum
3420 # return: 1 if notice file change
3421 # 0 if not notice or not change
3423 if (!defined $CONFIG_FILE{path}) {
3424 ld_log( _message('FTL0102') );
3428 my $mtime = (stat $CONFIG_FILE{path})[9];
3429 if (!defined $mtime) {
3430 ld_log( _message( 'ERR0410', $CONFIG_FILE{path} ) );
3434 if ( defined $CONFIG_FILE{stattime} && $mtime == $CONFIG_FILE{stattime} ) {
3435 # file mtime is not change
3438 $CONFIG_FILE{stattime} = $mtime;
3440 my $digest = undef;;
3442 require Digest::MD5;
3444 my $ctx = Digest::MD5->new();
3445 open my $config, '<', $CONFIG_FILE{path};
3446 $ctx->addfile($config);
3447 $digest = $ctx->hexdigest;
3451 ld_log( _message( 'ERR0407', $CONFIG_FILE{path} ) );
3455 if (defined $CONFIG_FILE{checksum} && $digest &&
3456 $CONFIG_FILE{checksum} ne $digest ) {
3457 ld_log( _message('WRN0101', $CONFIG_FILE{path}) );
3458 $CONFIG_FILE{checksum} = $digest;
3460 if ( defined $CONFIG{callback} && -x $CONFIG{callback} ) {
3461 system_wrapper( $CONFIG{callback} . q{ } . $CONFIG_FILE{path} );
3462 ld_log( _message( 'INF0503', $CONFIG{callback}, $CONFIG_FILE{path} ) );
3465 if ( $CONFIG{autoreload} ) {
3466 ld_log( _message('WRN0102') );
3470 ld_log( _message('WRN0103') );
3475 $CONFIG_FILE{checksum} = $digest;
3481 # make log rotation work
3483 # post: If logger is a file, it opened and closed again as a test
3484 # If logger is syslog, it is opened so it can be used without
3485 # needing to be opened again.
3486 # Otherwiese, nothing is done.
3487 # return: 0 on success
3490 my $log_config = shift;
3491 if (!defined $log_config) {
3492 ld_log( _message('ERR0501') );
3496 if ( $DEBUG_LEVEL > 0 or $CONFIG{supervised} ) {
3497 # Instantly do nothing
3501 if ( $log_config =~ m{^/}) {
3502 # Open and close the file as a test.
3503 # We open the file each time we want to log to it
3505 open my $log_file, ">>", $log_config;
3509 ld_log( _message('ERR0118', $log_config) );
3514 # Assume $log_config is a logfacility, log to syslog
3516 openlog("l7directord", "pid", $log_config);
3517 # FIXME "closelog" not found
3520 $PROC_STAT{log_opened} = 1;
3526 # pre: message: Message to write
3527 # post: message and timetsamp is written to loged
3528 # If logger is a file, it is opened and closed again as a
3529 # primative means to make log rotation work
3530 # return: 0 on success
3533 my $message = shift;
3534 if (!defined $message) {
3535 ld_log( _message('ERR0501') );
3539 ld_debug(2, $message);
3542 if ( !$PROC_STAT{log_opened} ) {
3546 my $now = localtime();
3547 my $line_header = sprintf "[%s|%d] ", $now, $PROC_STAT{pid};
3548 $message =~ s/^/$line_header/mg;
3550 if ( $CONFIG{supervised} ) {
3551 print {*STDOUT} $message . "\n";
3553 elsif ( $CONFIG{logfile} =~ m{^/} ) {
3555 open my $log_file, '>>', $CONFIG{logfile};
3556 flock $log_file, 2; # LOCK_EX
3557 print {$log_file} $message . "\n";
3561 print {*STDERR} _message_only( 'FTL0103', $CONFIG{logfile}, $message ) . "\n";
3566 # Assume LOGFILE is a logfacility, log to syslog
3567 syslog('info', $message);
3573 # Log a message to a STDOUT.
3574 # pre: priority: priority of message
3575 # message: Message to write
3576 # post: message is written to STDOUT if $DEBUG_LEVEL >= priority
3579 my ($priority, $message) = @_;
3581 if (defined $priority && $priority =~ /^\d+$/ &&
3582 defined $message && $DEBUG_LEVEL >= $priority) {
3584 $message =~ s/^/DEBUG[$priority]: /mg;
3585 print {*STDERR} $message . "\n";
3590 # Wrapper around command(qx) to get output
3591 # pre: command to execute
3592 # post: execute command and if it returns non-zero a failure
3594 # return: return value of command, and output
3595 sub command_wrapper {
3596 my $command = shift;
3598 if ($DEBUG_LEVEL > 2) {
3599 ld_log( _message( 'INF0506', $command) );
3602 $command =~ s/([{}\\])/\\$1/g;
3603 my $output = qx($command);
3604 if ($CHILD_ERROR != 0) {
3605 ld_log( _message('ERR0303', $command, $CHILD_ERROR) );
3607 return ($CHILD_ERROR, $output);
3611 # Wrapper around system() to log errors
3612 # pre: LIST: arguments to pass to system()
3613 # post: system() is called and if it returns non-zero a failure
3615 # return: return value of system()
3616 sub system_wrapper {
3619 if ($DEBUG_LEVEL > 2) {
3620 ld_log( _message( 'INF0504', join(q{ }, @args) ) );
3622 my $status = system(@args);
3623 if ($DEBUG_LEVEL > 2) {
3625 ld_log( _message('ERR0301', join(q{ }, @args), $status) );
3632 # Wrapper around exec() to log errors
3633 # pre: LIST: arguments to pass to exec()
3634 # post: exec() is called and if it returns non-zero a failure
3636 # return: return value of exec() on failure
3637 # does not return on success
3641 if ($DEBUG_LEVEL > 2) {
3642 ld_log( _message( 'INF0505', join(q{ }, @args) ) );
3644 my $status = exec(@args);
3646 ld_log( _message('ERR0302', join(q{ }, @args), $status) );
3652 # Remove a file, symink, or anything that isn't a directory
3654 # pre: filename: file to delete
3655 # post: If filename does not exist or is a directory an
3656 # error state is reached
3657 # Else filename is delete
3658 # If $DEBUG_LEVEL >=2 errors are logged
3659 # return: 0 on success
3662 my $filename = shift;
3663 if (!defined $filename) {
3664 ld_log( _message('ERR0411') );
3668 ld_log( _message('ERR0401', $filename) );
3671 if (!-e $filename) {
3672 ld_log( _message('ERR0402', $filename) );
3675 my $status = unlink $filename;
3677 ld_log( _message('ERR0403', $filename, $ERRNO) );
3684 # See if a number is an octet, that is >=0 and <=255
3685 # pre: alleged_octet: the octect to test
3686 # post: alleged_octect is checked to see if it is valid
3687 # return: 1 if the alleged_octet is an octet
3690 my $alleged_octet = shift;
3691 if (!defined $alleged_octet || $alleged_octet !~ /^\d+$/ || $alleged_octet > 255) {
3692 ld_log( _message('ERR0501') );
3699 # Check that a given string is an IP address
3700 # pre: alleged_ip: string representing ip address
3701 # post: alleged_ip is checked to see if it is valid
3702 # return: 1 if alleged_ip is a valid ip address
3705 my $alleged_ip = shift;
3707 # If we don't have four, . delimited numbers then we have no hope
3708 if (!defined $alleged_ip || $alleged_ip !~ m/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) {
3709 ld_log( _message('ERR0501') );
3713 # Each octet must be >=0 and <=255
3714 is_octet($1) or return 0;
3715 is_octet($2) or return 0;
3716 is_octet($3) or return 0;
3717 is_octet($4) or return 0;
3723 # Check that a given string is an IPv6 address
3724 # pre: alleged_ip6: string representing ip address
3725 # post: alleged_ip6 is checked to see if it is valid
3726 # return: 1 if alleged_ip is a valid ipv6 address
3729 my $alleged_ip = shift;
3730 my @return_array = (0, undef);
3732 # If we don't have four, . delimited numbers then we have no hope
3733 if (!defined $alleged_ip ) {
3734 ld_log( _message('ERR0501') );
3738 ## Change IPv6 Address
3739 $alleged_ip =~ s/[\[\]]//g;
3741 my ($work, $link_local) = split /%/, $alleged_ip;
3743 if ( $alleged_ip =~ /::/ ){
3744 my ($adr_a, $adr_b) = split /::/, $alleged_ip;
3745 my @adr_a = split /:/ , $adr_a;
3746 my @adr_b = split /:/ , $adr_b;
3747 for(scalar @adr_a .. 7 - scalar @adr_b){
3750 @address = (@adr_a, @adr_b);
3753 @address = split /:/, $alleged_ip;
3755 $alleged_ip = join ":", @address;
3756 if ( defined $link_local ){
3757 $alleged_ip .= '%' . $link_local;
3759 if (!defined $alleged_ip ||
3760 $alleged_ip !~ m/^([0-9a-fA-F]{1,4}):
3767 ([0-9a-fA-F]{1,4})(%.+)?$/x) {
3768 ld_log( _message('ERR0501') );
3771 @return_array = (1, @address);
3772 return @return_array;
3777 # Turn an IP address given as a dotted quad into an integer
3778 # pre: ip_address: string representing IP address
3779 # post: post ip_address is converted to an integer
3780 # return: -1 if an error occurs
3781 # integer representation of IP address otherwise
3783 my $ip_address = shift;
3784 my $ip_version = 'ipv4';
3786 my @result2 = undef;
3787 my @return_array = (undef, -1);
3790 if ( is_ip($ip_address) ) {
3791 my ($oct1, $oct2, $oct3, $oct4)
3792 = $ip_address =~ m/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
3793 @result = (($oct1 << 24) + ($oct2 << 16) + ($oct3 << 8) + $oct4);
3796 my ( $ret, @address ) = is_ip6($ip_address);
3798 my ( $hex1, $hex2, $hex3, $hex4, $hex5, $hex6, $hex7, $hex8, $linklocal) = @address;
3799 @result = ((hex($hex5) << 48) + (hex($hex6) << 32) + (hex($hex7) << 16) + hex($hex8));
3800 @result2 = ((hex($hex1) << 48) + (hex($hex2) << 32) + (hex($hex3) << 16) + hex($hex4));
3801 $ip_version = 'ipv6';
3804 return @return_array;
3807 @return_array = ($ip_version, @result, @result2);
3808 return @return_array;
3812 # Turn an IP address given as an integer into a dotted quad
3813 # pre: ip_address: integer representation of IP address
3814 # post: Decimal is converted to a dotted quad
3815 # return: string representing IP address
3817 my ($ip_version, $ip_address,$ip_address2) = @_;
3818 if (!defined $ip_address || $ip_address !~ /^\d+$/ ) {
3819 ##|| !defined $ip_version || $ip_version !~ /ipv[46]/ ) {
3820 ld_log( _message('ERR0501') );
3825 if ($ip_version eq 'ipv6') {
3826 ## IPv6 Address Change
3827 $result = sprintf "%0x:%0x:%0x:%0x:%0x:%0x:%0x:%0x",
3828 ($ip_address2 >> 48) & 0xffff,
3829 ($ip_address2 >> 32) & 0xffff,
3830 ($ip_address2 >> 16) & 0xffff,
3831 ($ip_address2 ) & 0xffff,
3832 ($ip_address >> 48) & 0xffff,
3833 ($ip_address >> 32) & 0xffff,
3834 ($ip_address >> 16) & 0xffff,
3835 ($ip_address ) & 0xffff;
3837 ## ($ip_address >> 112) & 0xffff,
3838 ## ($ip_address >> 96) & 0xffff,
3839 ## ($ip_address >> 80) & 0xffff,
3840 ## ($ip_address >> 64) & 0xffff,
3841 ## ($ip_address >> 48) & 0xffff,
3842 ## ($ip_address >> 32) & 0xffff,
3843 ## ($ip_address >> 16) & 0xffff,
3844 ## ($ip_address ) & 0xffff;
3847 ## IPv4 Address Change
3848 $result = sprintf "%d.%d.%d.%d",
3849 ($ip_address >> 24) & 255,
3850 ($ip_address >> 16) & 255,
3851 ($ip_address >> 8 ) & 255,
3852 ($ip_address ) & 255;
3858 # Get the service for a virtual or a real
3859 # pre: host: virtual or real to get the service for
3861 # return: ip_address:port
3863 my ($host, $checkport) = @_;
3864 my $server = defined $host && defined $host->{server} && defined $host->{server}{ip}
3865 ? $host->{server}{ip } : q{};
3866 my $port = defined $checkport ? $checkport
3867 : defined $host && defined $host->{server} && defined $host->{server}{port}
3868 ? $host->{server}{port} : q{};
3870 my $ip_port = $server ne q{} && $port ne q{} ? "$server:$port" : q{};
3874 # get_health_check_id_str
3875 # Get an id string for a health check process
3876 # pre: r: Real service.
3877 # v: Virtual service
3879 # return: Id string for the health check process
3880 sub get_health_check_id_str {
3882 if ( !defined $v || !defined $r || !defined $r->{server} ) {
3883 ld_log( _message('ERR0501') );
3887 my $ip = defined $r->{server}{ip } ? $r->{server}{ip } : q{};
3888 my $port = defined $v->{checkport } ? $v->{checkport } :
3889 defined $r->{server}{port} ? $r->{server}{port} : q{};
3890 my $checktype = defined $v->{checktype } ? $v->{checktype } : q{};
3891 my $service = defined $v->{service } ? $v->{service } : q{};
3892 my $protocol = defined $v->{protocol } ? $v->{protocol } : q{};
3893 my $num_connects = defined $v->{num_connects} ? $v->{num_connects} : q{};
3894 my $request = defined $r->{request } ? $r->{request } : q{};
3895 my $receive = defined $r->{receive } ? $r->{receive } : q{};
3896 my $httpmethod = defined $v->{httpmethod } ? $v->{httpmethod } : q{};
3897 my $virtualhost = defined $v->{virtualhost } ? $v->{virtualhost } : q{};
3898 my $login = defined $v->{login } ? $v->{login } : q{};
3899 my $password = defined $v->{passwd } ? $v->{passwd } : q{};
3900 my $database = defined $v->{database } ? $v->{database } : q{};
3902 my $customcheck = defined $v->{customcheck } ? $v->{customcheck } : q{};
3903 my $checkinterval = defined $v->{checkinterval } ? $v->{checkinterval } : q{};
3904 my $checkcount = defined $v->{checkcount } ? $v->{checkcount } : q{};
3905 my $checktimeout = defined $v->{checktimeout } ? $v->{checktimeout } : q{};
3906 my $negotiatetimeout = defined $v->{negotiatetimeout } ? $v->{negotiatetimeout } : q{};
3907 my $retryinterval = defined $v->{retryinterval } ? $v->{retryinterval } : q{};
3909 # FIXME SHOULD change separator. (request, receive, login, passwd ,database may include ':')
3910 my $id = "$ip:$port:$checktype:$service:$protocol:$num_connects:$request:$receive:" .
3911 "$httpmethod:$virtualhost:$login:$password:$database:$customcheck:" .
3912 "$checkinterval:$checkcount:$checktimeout:$negotiatetimeout:$retryinterval";
3917 # get_virtual_id_str
3918 # Get an id string for a virtual service
3919 # pre: v: Virtual service
3921 # return: Id string for the virtual service
3922 sub get_virtual_id_str {
3924 if ( !defined $v || !defined $v->{module} ) {
3925 ld_log( _message('ERR0501') );
3929 my $ip_port = get_ip_port($v);
3930 my $protocol = defined $v->{protocol } ? $v->{protocol } : q{};
3931 my $module_name = defined $v->{module}{name} ? $v->{module}{name} : q{};
3932 my $module_key = defined $v->{module}{key } ? $v->{module}{key } : q{};
3934 my $id = "$protocol:$ip_port:$module_name $module_key";
3938 # [cf] id = "tcp:127.0.0.1:80:cinsert --cookie-name 'monkey'"
3942 # Get the l7vsadm flag corresponging to a forwarding mechanism
3943 # pre: forward: Name of forwarding mechanism.
3946 # return: l7vsadm flag corresponding to the forwading mechanism
3947 # " " if $forward is unknown
3948 sub get_forward_flag {
3949 my $forward = shift;
3951 if (defined $forward && $forward =~ /^masq$/i) {
3958 # Exit and log a message
3959 # pre: exit_status: Integer exit status to exit with
3960 # 0 wiil be used if parameter is omitted
3961 # message: Message to log when exiting. May be omitted
3962 # post: If exit_status is non-zero or $DEBUG_LEVEL>2 then
3964 # Programme exits with exit_status
3965 # return: does not return
3967 my ($exit_status, $message) = @_;
3968 if (defined $exit_status && defined $message) {
3969 ld_log( _message('INF0006', $exit_status, $message) );
3975 # Open a socket connection
3976 # pre: remote: IP address as a dotted quad of remote host to connect to
3977 # port: port to connect to
3978 # protocol: Prococol to use. Should be either "tcp" or "udp"
3979 # post: A Socket connection is opened to the remote host
3980 # return: Open socket
3981 sub ld_open_socket {
3982 require IO::Socket::INET6;
3983 my ($remote, $port, $protocol, $timeout) = @_;
3984 my $sock_handle = IO::Socket::INET6->new(
3985 PeerAddr => $remote,
3988 Timeout => $timeout,
3990 return $sock_handle;
3994 # Close and fork to become a daemon.
3996 # Notes from unix programmer faq
3997 # http://www.landfield.com/faqs/unix-faq/programmer/faq/
3999 # Almost none of this is necessary (or advisable) if your daemon is being
4000 # started by `inetd'. In that case, stdin, stdout and stderr are all set up
4001 # for you to refer to the network connection, and the `fork()'s and session
4002 # manipulation should *not* be done (to avoid confusing `inetd'). Only the
4003 # `chdir()' step remains useful.
4005 ld_daemon_become_child();
4007 if (POSIX::setsid() < 0) {
4008 ld_exit( 7, _message_only('ERR0702') );
4011 ld_daemon_become_child();
4013 if (chdir('/') < 0) {
4014 ld_exit( 8, _message_only('ERR0703') );
4021 eval { open *STDIN, '<', '/dev/null'; };
4022 ld_exit(9, _message_only('ERR0704') ) if ($EVAL_ERROR);
4023 eval { open *STDOUT, '>>', '/dev/console'; };
4024 ld_exit(10, _message_only('ERR0705') ) if ($EVAL_ERROR);
4025 eval { open *STDERR, '>>', '/dev/console'; };
4026 ld_exit(10, _message_only('ERR0705') ) if ($EVAL_ERROR);
4029 # ld_daemon_become_child
4030 # Fork, kill parent and return child process
4032 # post: process forkes and parent exits
4033 # All preocess exit with exit status -1 if an error occurs
4034 # return: parent: exits
4035 # child: none (this is the process that returns)
4036 sub ld_daemon_become_child {
4037 my $status = fork();
4038 $PROC_STAT{pid} = $PID;
4041 ld_exit( 6, _message_only('ERR0701', $ERRNO) );
4044 ld_exit( 0, _message_only('INF0005') );
4049 # Wrapper to gethostbyname. Look up the/an IP address of a hostname
4050 # If an IP address is given is it returned
4051 # pre: name: Hostname of IP address to lookup
4052 # post: gethostbyname is called to find an IP address for $name
4053 # This is converted to a string
4054 # return: IP address
4056 sub ld_gethostbyname {
4058 $name = q{} if !defined $name;
4059 my $addrs = ( gethostbyname($name) )[4] or return;
4060 return Socket::inet_ntoa($addrs);
4064 # Wraper for getservbyname. Look up the port for a service name
4065 # If a port is given it is returned.
4066 # pre: name: Port or Service name to look up
4067 # post: if $name is a number
4068 # if 0<=$name<=65536 $name is returned
4069 # else undef is returned
4070 # else getservbyname is called to look up the port for the service
4073 sub ld_getservbyname {
4074 my ($name, $protocol) = @_;
4075 $name = q{} if !defined $name;
4076 $protocol = q{} if !defined $protocol;
4078 if ($name =~ /^\d+$/) {
4079 if ($name > 65535) {
4085 my $port = ( getservbyname($name, $protocol) )[2];
4089 # ld_gethostservbyname
4090 # Wraper for ld_gethostbyname and ld_getservbyname. Given a server of the
4091 # form ip_address|hostname:port|servicename return hash refs of ip_address and port
4092 # pre: hostserv: Servver of the form ip_address|hostname:port|servicename
4093 # protocol: Protocol for service. Should be either "tcp" or "udp"
4094 # post: lookups performed as per ld_getservbyname and ld_gethostbyname
4095 # return: { ip => ip_address, port => port }
4097 sub ld_gethostservbyname {
4098 my ($hostserv, $protocol) = @_;
4102 if (!defined $hostserv || $hostserv !~ /
4104 (\d+\.\d+\.\d+\.\d+|[a-z0-9.-]+) # host or ip
4106 (\d+|[a-z0-9-]+) # serv or port
4109 if ( !defined $hostserv || $hostserv !~ /
4111 (\[[a-z0-9.-:]+\]) # host or ip
4113 (\d+|[a-z0-9-]+) # serv or port
4127 $ip = ld_gethostbyname($ip) or return;
4128 $port = ld_getservbyname($port, $protocol);
4129 return if !defined $port;
4131 return {ip => $ip, port => $port};
4135 # Create message only.
4137 my ($code, @message_args) = @_;
4139 my $message_list = {
4140 # health check process exit
4141 FTL0001 => "health_check argument is invalid. Exit this monitor process with status: 1",
4142 FTL0002 => "health_check argument pair, virtual or real structure is invalid. Exit this monitor process with status: 2",
4143 FTL0003 => "Detected down management process (pid: %s). Exit this monitor process with status: 3",
4145 FTL0101 => "l7vsadm file `%s' is not found or cannot execute.",
4146 FTL0102 => "Config file is not defined. So cannot check configuration change.",
4147 FTL0103 => "Cannot open logfile `%s'. Log message: `%s'",
4148 # command fatal error
4149 FTL0201 => "Result of read from l7vsadm is not defined.",
4152 ERR0001 => "Initialization error: %s",
4153 ERR0002 => "Configuration error and exit.",
4155 ERR0101 => "Invalid value (set natural number) `%s'.",
4156 ERR0102 => "Invalid value (set `yes' or `no') `%s'.",
4157 ERR0103 => "Invalid value (set any word) `%s'.",
4158 ERR0104 => "Invalid value (set `custom', `connect', `negotiate', `ping', `off', `on' "
4159 . "or positive number) `%s'.",
4160 ERR0105 => "Invalid value (set `lc', `rr' or `wrr') `%s'.",
4161 ERR0106 => "Invalid value (set `http', `https', `ftp', `smtp', `pop', `imap', "
4162 . "`ldap', `nntp', `dns', `mysql', `pgsql', `sip', or `none') `%s'.",
4163 ERR0107 => "Invalid value (forwarding mode must be `masq') `%s'.",
4164 ERR0108 => "Invalid port number `%s'.",
4165 ERR0109 => "Invalid protocol (protocol must be `tcp') `%s'.",
4166 ERR0110 => "Invalid HTTP method (set `GET' or `HEAD') `%s'.",
4167 ERR0111 => "Invalid module (set `url', `pfilter', `ip', `sslid' or `sessionless') `%s'.",
4168 # ERR0111 => "Invalid module (set `cinsert', `cpassive', `crewrite', `url', `pfilter', `ip', `sslid' or `sessionless') `%s'.",
4169 ERR0112 => "Invalid module key option (`%s' module must set `%s' option) `%s'.",
4170 ERR0113 => "Invalid QoS value (set 0 or 1-999[KMG]. must specify unit(KMG)) `%s'.",
4171 ERR0114 => "Invalid address `%s'.",
4172 ERR0115 => "Invalid address range (first value(%s) must be less than or equal to the second value(%s)) `%s'.",
4173 ERR0116 => "File not found `%s'.",
4174 ERR0117 => "File not found or cannot execute `%s'.",
4175 ERR0118 => "Unable to open logfile `%s'.",
4176 ERR0119 => "Virtual section not found for `%s'.",
4177 ERR0120 => "Unknown config `%s'.",
4178 ERR0121 => "Configuration error. Reading file `%s' at line %d: %s",
4179 ERR0122 => "Caught exception during re-read config file and re-setup l7vsd. (message: %s) "
4180 . "So config setting will be rollbacked.",
4181 ERR0123 => "`%s' is a required module for checking %s service.",
4182 ERR0124 => "Invalid value `%s'.",
4183 ERR0125 => "Invalid accesslog rotate type (set 'date', 'size' or 'datesize') `%s'.",
4184 ERR0126 => "Invalid accesslog rotate max backup index number `%s'.",
4185 ERR0127 => "Invalid accesslog rotate max filesize value (set 0 or 1-999[KMG]. must specify unit(KMG)) `%s'.",
4186 ERR0128 => "Invalid accesslog rotate rotation timing (set 'year','month','week','date', or 'hour') `%s'.",
4187 ERR0129 => "Invalid accesslog rotate rotation timing value `%s'.",
4188 # operate l7vsd error
4189 ERR0201 => "Failed to add virtual service to l7vsd: `%s %s', output: `%s'",
4190 ERR0202 => "Failed to edit virtual service on l7vsd: `%s %s', output: `%s'",
4191 ERR0203 => "Failed to delete virtual service from l7vsd: `%s %s', output: `%s'",
4192 ERR0204 => "Failed to add server to l7vsd: `%s' ( x `%s %s'), output: `%s'",
4193 ERR0205 => "Failed to edit server on l7vsd: `%s' ( x `%s %s'), output: `%s'",
4194 ERR0206 => "Failed to delete server from l7vsd: `%s' ( x `%s %s'), output: `%s'",
4195 ERR0207 => "Trying add server `%s', but virtual service `%s' is not found.",
4196 ERR0208 => "Trying delete server `%s', but virtual service `%s' is not found.",
4197 ERR0209 => "`%s' was already existed on l7vsd. ( x `%s %s')",
4198 ERR0210 => "`%s' was already deleted on l7vsd. ( x `%s %s')",
4199 ERR0211 => "`%s' was already changed to quiescent state on l7vsd. ( x `%s %s')",
4201 ERR0301 => "Failed to system `%s' with return: %s",
4202 ERR0302 => "Failed to exec `%s' with return: %s",
4203 ERR0303 => "Failed to command `%s' with return: %s",
4205 ERR0401 => "Failed to delete file `%s': `Is a directory'",
4206 ERR0402 => "Failed to delete file `%s': `No such file'",
4207 ERR0403 => "Failed to delete file `%s': `%s'",
4208 ERR0404 => "Config file `%s' is not found.",
4209 ERR0405 => "`l7directord.cf' is not found at default search paths.",
4210 ERR0406 => "`l7vsadm' file is not found at default search paths.",
4211 ERR0407 => "Cannot open config file `%s'.",
4212 ERR0408 => "Cannot close config file `%s'.",
4213 ERR0409 => "Cannot open pid file (%s): %s",
4214 ERR0410 => "Cannot get mtime of configuration file `%s'",
4215 ERR0411 => "No delete file specified.",
4216 ERR0412 => "Invalid pid specified. (pid: %s)",
4218 ERR0501 => "Some method arguments are undefined.",
4219 ERR0502 => "VirtualService ID is undefined.",
4220 ERR0503 => "HealthCheck ID is undefined.",
4221 ERR0504 => "negotiate function is undefined. So use check_connect function.",
4222 ERR0505 => "custom check script is undefined. So use check_off function.",
4223 # health check process
4224 ERR0601 => "Service up detected. (Real server `%s')",
4225 ERR0602 => "Service down detected. (Real server `%s')",
4226 ERR0603 => "Detected down monitor process (pid: %s). Prepare to re-start health check process. (id: `%s')",
4227 ERR0604 => "Failed to fork() on sub process creation. (id: `%s')",
4229 ERR0701 => "Cannot fork for become daemon (errno: `%s') and exit.",
4230 ERR0702 => "Cannot setsid for become daemon and exit.",
4231 ERR0703 => "Cannot chdir for become daemon and exit.",
4232 ERR0704 => "Cannot open /dev/null for become daemon and exit.",
4233 ERR0705 => "Cannot open /dev/console for become daemon and exit.",
4236 WRN0001 => "l7directord `%s' received signal: %s. Terminate process.",
4237 WRN0002 => "l7directord `%s' received signal: %s. Reload configuration.",
4238 WRN0003 => "Signal TERM send error(pid: %d)",
4239 WRN0004 => "Signal HUP send error(pid: %d)",
4241 WRN0101 => "Configuration file `%s' has changed on disk.",
4242 WRN0102 => "Reread new configuration.",
4243 WRN0103 => "Ignore new configuration.",
4245 WRN0203 => "Service check OK. HTTP response is valid. HTTP response status line is `%s' (real - `%s:%s')",
4246 WRN0204 => "Service check OK. Successfully connect SMTP server. (real - `%s:%s')",
4247 WRN0205 => "Service check OK. Successfully connect POP3 server. (real - `%s:%s')",
4248 WRN0206 => "Service check OK. Successfully connect IMAP server. (real - `%s:%s')",
4249 WRN0207 => "Service check OK. Successfully bind LDAP server. (real - `%s:%s')",
4250 WRN0208 => "Service check OK. NNTP response is valid. `%s' (real - `%s:%s')",
4251 WRN0209 => "Service check OK. Database response is valid. (real - `%s:%s')",
4252 WRN0210 => "Service check OK. Successfully connect socket to server. (real - `%s:%s')",
4253 WRN0211 => "Service check OK. SIP response is valid. `%s' (real - `%s:%s')",
4254 WRN0212 => "Service check OK. Successfully login FTP server. (real - `%s')",
4255 WRN0213 => "Service check OK. Successfully lookup DNS. (real - `%s:%s')",
4256 WRN0214 => "Service check OK. Successfully receive ping response. (real - `%s')",
4257 WRN0215 => "Custom check result OK. (real - `%s')",
4259 WRN0301 => "Perl warning: `%s'",
4261 WRN1001 => "Retry service check `%s' %d more time(s).",
4263 WRN1101 => "Service check NG. Check URL `%s' is not valid. (real - `%s:%s')",
4264 WRN1102 => "Service check NG. HTTP response is not ok. Response status line is `%s' (real - `%s:%s')",
4265 WRN1103 => "Service check NG. Check string `%s' is not found in HTTP response. (real - `%s:%s')",
4267 WRN1201 => "Service check NG. Cannot connect SMTP server. (real - `%s:%s')",
4269 WRN1301 => "Service check NG. Cannot connect POP3 server. (real - `%s:%s')",
4270 WRN1302 => "Service check NG. Cannot login POP3 server. (real - `%s:%s')",
4272 WRN1401 => "Service check NG. Cannot connect IMAP server. (real - `%s:%s')",
4273 WRN1402 => "Service check NG. Cannot login IMAP server. (real - `%s:%s')",
4274 WRN1403 => "Service check NG. Connection timeout from IMAP server in %d seconds. (real - `%s:%s')",
4276 WRN1501 => "Service check NG. Cannot connect LDAP server. (real - `%s:%s')",
4277 WRN1502 => "Service check NG. Connection timeout from LDAP server in %d seconds. (real - `%s:%s')",
4278 WRN1503 => "Service check NG. LDAP bind error. (real - `%s:%s')",
4279 WRN1504 => "Service check NG. Exists %d results (not one) on search Base DN. (real - `%s:%s')",
4280 WRN1505 => "Service check NG. Check string `%s' is not found in Base DN search result. (real - `%s:%s')",
4282 WRN1601 => "Service check NG. Cannot connect NNTP server. (real - `%s:%s')",
4283 WRN1602 => "Service check NG. Connection timeout from NNTP server in %d seconds. (real - `%s:%s')",
4284 WRN1603 => "Service check NG. NNTP response is not ok. `%s' (real - `%s:%s')",
4286 WRN1701 => "Service check NG. SQL check must set `database', `login', `passwd' by configuration. (real - `%s:%s')",
4287 WRN1702 => "Service check NG. Cannot connect database or cannot login database. (real - `%s:%s')",
4288 WRN1703 => "Service check NG. Query result has no row. (real - `%s:%s')",
4289 WRN1704 => "Service check NG. Expected %d rows of query results, but got %d rows. (real - `%s:%s')",
4290 WRN1705 => "Service check NG. Connection timeout from database in %d seconds. (real - `%s:%s')",
4292 WRN1801 => "Service check NG. SIP check must set `login' by configuration. (real - `%s:%s')",
4293 WRN1802 => "Service check NG. Cannot connect SIP server. (real - `%s:%s')",
4294 WRN1803 => "Service check NG. SIP response is not ok. `%s' (real - `%s:%s')",
4295 WRN1804 => "Service check NG. Connection timeout from SIP server in %d seconds. (real - `%s:%s')",
4297 WRN1901 => "Service check NG. FTP check must set `login', `passwd' by configuration. (real - `%s')",
4298 WRN1902 => "Service check NG. Cannot connect FTP server. (real - `%s')",
4299 WRN1903 => "Service check NG. Cannot login FTP server. (real - `%s')",
4300 WRN1904 => "Service check NG. Cannot chdir to / of FTP server. (real - `%s')",
4301 WRN1905 => "Service check NG. Cannot get file `%s' (real - `%s')",
4302 WRN1906 => "Service check NG. Check string `%s' is not found in file `%s' (real - `%s')",
4303 WRN1907 => "Service check NG. Exception occur during FTP check `%s' (real - `%s')",
4304 WRN1908 => "Service check NG. Connection timeout from FTP server in %d seconds. (real - `%s')",
4306 WRN2001 => "Service check NG. DNS check must set `request', `receive' by configuration. (real - `%s:%s')",
4307 WRN2002 => "Service check NG. Connection timeout from DNS server in %d seconds. (real - `%s:%s')",
4308 WRN2003 => "Service check NG. Net::DNS exception occur `%s' (real - `%s:%s')",
4309 WRN2004 => "Service check NG. DNS search `%s' not respond. (real - `%s:%s')",
4310 WRN2005 => "Service check NG. Check string `%s' is not found in search result. (real - `%s:%s')",
4312 WRN3101 => "Service check NG. Ping timeout in %d seconds. (real - `%s')",
4314 WRN3201 => "Service check NG. Cannot connect socket to server. (errno: `%s') (real - `%s:%s')",
4316 WRN3301 => "Custom check NG. Check timeout in %d seconds. (real - `%s')",
4317 WRN3302 => "Custom check NG. `%s' returns %d",
4320 INF0001 => "Starting program with command: `%s'",
4321 INF0002 => "Starting l7directord v%s with pid: %d (configuration: `%s')",
4322 INF0003 => "Starting l7directord v%s as daemon. (configuration: `%s')",
4323 INF0004 => "Exit by initialize error.",
4324 INF0005 => "Exit parent process for become daemon",
4325 INF0006 => "Exiting with exit status %d: %s",
4326 INF0007 => "Detected halt flag. Exit this monitor process with status: 0",
4327 INF0008 => "Reached end of `main'",
4329 INF0101 => "l7directord for `%s' is running with pid: %d",
4330 INF0102 => "l7directord stale pid file %s for %s",
4331 INF0103 => "Other l7directord process is running. (pid: %d)",
4332 INF0104 => "l7directord process is not running.",
4334 INF0201 => "Add virtual service to l7vsd: `%s %s'",
4335 INF0202 => "Edit virtual service on l7vsd: `%s %s'",
4336 INF0203 => "Delete virtual service from l7vsd: `%s %s'",
4337 INF0204 => "Add server to l7vsd: `%s' ( x `%s %s') (weight set to %d)",
4338 INF0205 => "Edit server on l7vsd: `%s' ( x `%s %s') (weight set to %d)",
4339 INF0206 => "Delete server from l7vsd: `%s' ( x `%s %s')",
4341 INF0301 => "Added real server. (`%s')",
4342 INF0302 => "Added fallback server. (`%s')",
4343 INF0303 => "Changed real server to quiescent state. (`%s')",
4344 INF0304 => "Changed fallback server to quiescent state. (`%s')",
4345 INF0305 => "Deleted real server. (`%s')",
4346 INF0306 => "Deleted fallback server. (`%s')",
4348 INF0401 => "Prepare to start health check process. (id: `%s')",
4349 INF0402 => "Create health check process with pid: %d. (id `%s')",
4351 INF0501 => "Real server down shell execute: `%s %s'",
4352 INF0502 => "Real server recovery shell execute: `%s %s'",
4353 INF0503 => "Config callback shell execute: `%s %s'",
4354 INF0504 => "Running system: `%s'",
4355 INF0505 => "Running exec: `%s'",
4356 INF0506 => "Running command: `%s'",
4360 = exists $message_list->{$code} ? sprintf $message_list->{$code}, @message_args
4361 : "Unknown message. (code:[$code] args:[" . join(q{, }, @message_args) . '])';
4367 # Create message by _message_only and add code header.
4369 my ($code, @message_args) = @_;
4370 my $message = _message_only($code, @message_args);
4371 $message = "[$code] $message";
4381 l7directord - UltraMonkey-L7 Director Daemon
4383 Daemon to monitor remote services and control UltraMonkey-L7
4388 B<l7directord> [B<-d>] [I<configuration>] {B<start>|B<stop>|B<restart>|B<try-restart>|B<reload>|B<status>|B<configtest>}
4390 B<l7directord> B<-t> [I<configuration>]
4392 B<l7directord> B<-h|--help>
4394 B<l7directord> B<-v|--version>
4398 B<l7directord> is a daemon to monitor and administer real servers in a
4399 cluster of load balanced virtual servers. B<l7directord> is similar to B<ldirectord>
4400 in terms of functionality except that it triggers B<l7vsadm>.
4401 B<l7directord> typically is started from command line but can be included
4402 to start from heartbeat. On startup B<l7directord> reads the file
4403 B</etc/ha.d/conf/>I<configuration>.
4404 After parsing the file, entries for virtual servers are created on the UltraMonkey-L7.
4405 Now at regular intervals the specified real servers are monitored and if
4406 they are considered alive, added to a list for each virtual server. If a
4407 real server fails, it is removed from that list. Only one instance of
4408 B<l7directord> can be started for each configuration, but more instances of
4409 B<l7directord> may be started for different configurations. This helps to
4410 group clusters of services. This can be done by putting an entry inside
4411 B</etc/ha.d/haresources>
4413 I<nodename virtual-ip-address l7directord::configuration>
4415 to start l7directord from heartbeat.
4422 =item I<configuration>:
4424 This is the name for the configuration as specified in the file
4425 B</etc/ha.d/conf/>I<configuration>
4429 Don't start as daemon. Useful for debugging.
4433 Help. Print user manual of l7directord.
4437 Version. Print version of l7directord.
4441 Run syntax tests for configuration files only. The program immediately exits after these syntax parsing tests
4442 with either a return code of 0 (Syntax OK) or return code not equal to 0 (Syntax Error).
4446 Start the daemon for the specified configuration.
4450 Stop the daemon for the specified configuration. This is the same as sending
4451 a TERM signal to the running daemon.
4455 Restart the daemon for the specified configuration. The same as stopping and starting.
4457 =item B<try-restart>
4459 Try to restart the daemon for the specified configuration. If l7directord is already running for the
4460 specified configuration, then the same is stopped and started (Similar to restart).
4461 However, if l7directord is not already running for the specified configuration, then an error message
4462 is thrown and the program exits.
4466 Reload the configuration file. This is only useful for modifications
4467 inside a virtual server entry. It will have no effect on adding or
4468 removing a virtual server block. This is the same as sending a HUP signal to
4473 Show status of the running daemon for the specified configuration.
4477 This is the same as B<-t>.
4484 =head2 Description how to write configuration files
4488 =item B<virtual = >I<(ip_address|hostname:portnumber|servicename)>
4490 Defines a virtual service by IP-address (or hostname) and port (or
4491 servicename). All real services and flags for a virtual
4492 service must follow this line immediately and be indented.
4493 For ldirectord, Firewall-mark settings could be set. But for l7directord
4494 Firewall-mark settings cannot be set.
4496 =item B<checktimeout = >I<n>
4498 Timeout in seconds for connect checks. If the timeout is exceeded then the
4499 real server is declared dead. Default is 5 seconds. If defined in virtual
4500 server section then the global value is overridden.
4502 =item B<negotiatetimeout = >I<n>
4504 Timeout in seconds for negotiate checks. Default is 5 seconds.
4505 If defined in virtual server section then the global value is overridden.
4507 =item B<checkinterval = >I<n>
4509 Defines the number of second between server checks. Default is 10 seconds.
4510 If defined in virtual server section then the global value is overridden.
4512 =item B<retryinterval = >I<n>
4514 Defines the number of second between server checks when server status is NG.
4515 Default is 10 seconds. If defined in virtual server section then the global
4516 value is overridden.
4518 =item B<checkcount = >I<n>
4520 The number of times a check will be attempted before it is considered
4521 to have failed. Note that the checktimeout is additive, so if checkcount
4522 is 3 and checktimeout is 2 seconds and retryinterval is 1 second,
4523 then a total of 8 seconds (2 + 1 + 2 + 1 + 2) worth of timeout will occur
4524 before the check fails. Default is 1. If defined in virtual server section
4525 then the global value is overridden.
4527 =item B<configinterval = >I<n>
4529 Defines the number of second between configuration checks.
4530 Default is 5 seconds.
4532 =item B<autoreload = >[B<yes>|B<no>]
4534 Defines if <l7directord> should continuously check the configuration file
4535 for modification each B<configinterval> seconds. If this is set to B<yes>
4536 and the configuration file changed on disk and its modification time (mtime)
4537 is newer than the previous version, the configuration is automatically reloaded.
4540 =item B<callback = ">I</path/to/callback>B<">
4542 If this directive is defined, B<l7directord> automatically calls
4543 the executable I</path/to/callback> after the configuration
4544 file has changed on disk. This is useful to update the configuration
4545 file through B<scp> on the other heartbeated host. The first argument
4546 to the callback is the name of the configuration.
4548 This directive might also be used to restart B<l7directord> automatically
4549 after the configuration file changed on disk. However, if B<autoreload>
4550 is set to B<yes>, the configuration is reloaded anyway.
4552 =item B<fallback = >I<ip_address|hostname[:portnumber|servicename]> [B<masq>]
4554 the server onto which a web service is redirected if all real
4555 servers are down. Typically this would be 127.0.0.1 with
4558 This directive may also appear within a virtual server, in which
4559 case it will override the global fallback server, if set.
4560 Only a value of B<masq> can be specified here. The default is I<masq>.
4562 =item B<logfile = ">I</path/to/logfile>B<">|syslog_facility
4564 An alternative logfile might be specified with this directive. If the logfile
4565 does not have a leading '/', it is assumed to be a syslog(3) facility name.
4567 The default is to log directly to the file I</var/log/l7vs/l7directord.log>.
4569 =item B<execute = ">I<configuration>B<">
4571 Use this directive to start an instance of l7directord for
4572 the named I<configuration>.
4576 If this directive is specified, the daemon does not go into background mode.
4577 All log-messages are redirected to stdout instead of a logfile.
4578 This is useful to run B<l7directord> supervised from daemontools.
4579 See http://untroubled.org/rpms/daemontools/ or http://cr.yp.to/daemontools.html
4582 =item B<quiescent = >[B<yes>|B<no>]
4584 If B<yes>, then when real or fallback servers are determined
4585 to be down, they are not actually removed from the UltraMonkey-L7,
4586 but set weight to zero.
4587 If B<no>, then the real or fallback servers will be removed
4588 from the UltraMonkey-L7. The default is B<yes>.
4590 This directive may also appear within a virtual server, in which
4591 case it will override the global fallback server, if set.
4596 =head2 Section virtual
4598 The following commands must follow a B<virtual> entry and must be indented
4599 with a minimum of 4 spaces or one tab.
4603 =item B<real => I<ip_address|hostname[-E<gt>ip_address|hostname][:portnumber|servicename>] [B<masq>] [I<n>] [B<">I<request>B<", ">I<receive>B<">]
4605 Defines a real service by IP-address (or hostname) and port (or
4606 servicename). If the port is omitted then a 0 will be used.
4607 Optionally a range of IP addresses (or two hostnames) may be
4608 given, in which case each IP address in the range will be treated as a real
4609 server using the given port. The second argument defines the forwarding
4610 method, it must be B<masq> only. The third argument defines the weight of
4611 each real service. This argument is optional. Default is 1. The last two
4612 arguments are optional too. They define a request-receive pair to be used to
4613 check if a server is alive. They override the request-receive pair in the
4614 virtual server section. These two strings must be quoted. If the request
4615 string starts with I<http://...> the IP-address and port of the real server
4616 is overridden, otherwise the IP-address and port of the real server is used.
4618 =item B<module => I<proto-module module-args [opt-module-args]>
4620 Indicates the module parameter of B<l7directord>. Here B<proto-module>
4621 denotes the protocol module name (For example, pfilter). B<module-args> denotes the
4622 arguments for the protocol module (For example, --pattern-match '*.html*').
4623 B<module-args> is optional only when set B<sessionless>, B<ip> and B<sslid> module to B<proto-module>.
4624 The last argument is optional (For example, --reschedule).
4628 =head2 More than one of these entries may be inside a virtual section:
4632 =item B<maxconn => I<n>
4634 Defines the maximum connection that the virtual service can handle. If the number of
4635 requests cross the maxconn limit, the requests would be redirected to the
4638 =item B<qosup => I<n>[B<K>|B<M>|B<G>]
4640 Defines the bandwidth quota size in bps for up stream. If the number of the
4641 bandwidth is over the qosup limit, a packet to the virtual service will be delayed
4642 until the number of bandwidth become below the qosup limit.
4643 B<K>(kilo), B<M>(mega) and B<G>(giga) unit are available.
4645 =item B<qosdown => I<n>[B<K>|B<M>|B<G>]
4647 Defines the bandwidth quota size in bps for down stream. If the number of the
4648 bandwidth is over the qosdown limit, a packet to the client will be delayed
4649 until the number of bandwidth become below the qosdown limit.
4650 B<K>(kilo), B<M>(mega) and B<G>(giga) unit are available.
4652 =item B<sorryserver =>I<ip_address|hostname[:portnumber|servicename]>
4654 Defines a sorry server by IP-address (or hostname) and port (or
4655 servicename). Firewall-mark settings cannot be set.
4656 If the number of requests to the virtual service cross the maxconn limit, the requests would be
4657 redirected to the sorry server.
4659 =item B<checktype = negotiate>|B<connect>|I<N>|B<ping>|B<custom>|B<off>|B<on>
4661 Type of check to perform. Negotiate sends a request and matches a receive
4662 string. Connect only attempts to make a TCP/IP connection, thus the
4663 request and receive strings may be omitted. If checktype is a number then
4664 negotiate and connect is combined so that after each N connect attempts one
4665 negotiate attempt is performed. This is useful to check often if a service
4666 answers and in much longer intervals a negotiating check is done. Ping
4667 means that ICMP ping will be used to test the availability of real servers.
4668 Ping is also used as the connect check for UDP services. Custom means that
4669 custom command will be used to test the availability of real servers.
4670 Off means no checking will take place and no real or fallback servers will
4671 be activated. On means no checking will take place and real servers will
4672 always be activated. Default is I<negotiate>.
4674 =item B<service = ftp>|B<smtp>|B<http>|B<pop>|B<nntp>|B<imap>|B<ldap>|B<https>|B<dns>|B<mysql>|B<pgsql>|B<sip>|B<none>
4676 The type of service to monitor when using checktype=negotiate. None denotes
4677 a service that will not be monitored. If the port specified for the virtual
4678 server is 21, 25, 53, 80, 110, 119, 143, 389, 443, 3306, 5432 or 5060 then
4679 the default is B<ftp>, B<smtp>, B<dns>, B<http>, B<pop>, B<nntp>, B<imap>,
4680 B<ldap>, B<https>, B<mysql>, B<pgsql> or B<sip> respectively. Otherwise the
4681 default service is B<none>.
4683 =item B<checkport = >I<n>
4685 Number of port to monitor. Sometimes check port differs from service port.
4686 Default is port specified for the real server.
4688 =item B<request = ">I<uri to requested object>B<">
4690 This object will be requested each checkinterval seconds on each real
4691 server. The string must be inside quotes. Note that this string may be
4692 overridden by an optional per real-server based request-string.
4694 For a DNS check this should the name of an A record, or the address
4695 of a PTR record to look up.
4697 For a MySQL or PostgreSQL checks, this should be a SQL query.
4698 The data returned is not checked, only that the
4699 answer is one or more rows. This is a required setting.
4701 =item B<receive = ">I<regexp to compare>B<">
4703 If the requested result contains this I<regexp to compare>, the real server
4704 is declared alive. The regexp must be inside quotes. Keep in mind that
4705 regexps are not plain strings and that you need to escape the special
4706 characters if they should as literals. Note that this regexp may be
4707 overridden by an optional per real-server based receive regexp.
4709 For a DNS check this should be any one the A record's addresses or
4710 any one of the PTR record's names.
4712 For a MySQL check, the receive setting is not used.
4714 =item B<httpmethod = GET>|B<HEAD>
4716 Sets the HTTP method, which should be used to fetch the URI specified in
4717 the request-string. GET is the method used by default if the parameter is
4718 not set. If HEAD is used, the receive-string should be unset.
4720 =item B<virtualhost = ">I<hostname>B<">
4722 Used when using a negotiate check with HTTP or HTTPS. Sets the host header
4723 used in the HTTP request. In the case of HTTPS this generally needs to
4724 match the common name of the SSL certificate. If not set then the host
4725 header will be derived from the request url for the real server if present.
4726 As a last resort the IP address of the real server will be used.
4728 =item B<login = ">I<username>B<">
4730 Username to use to login to FTP, POP, IMAP, MySQL and PostgreSQL servers.
4731 For FTP, the default is anonymous. For POP and IMAP, the default is the
4732 empty string, in which case authentication will not be attempted.
4733 For a MySQL and PostgreSQL, the username must be provided.
4735 For SIP the username is used as both the to and from address
4736 for an OPTIONS query. If unset it defaults to l7directord\@<hostname>,
4737 hostname is derived as per the passwd option below.
4739 =item B<passwd = ">I<password>B<">
4741 Password to use to login to FTP, POP, IMAP, MySQL and PostgreSQL servers.
4742 Default is for FTP is l7directord\@<hostname>, where hostname is the
4743 environment variable HOSTNAME evaluated at run time, or sourced from uname
4744 if unset. The default for all other services is an empty password, in the
4745 case of MySQL and PostgreSQL this means authentication will not be
4748 =item B<database = ">I<databasename>B<">
4750 Database to use for MySQL and PostgreSQL servers, this is the database that
4751 the query (set by B<receive> above) will be performed against. This is a
4754 =item B<scheduler => I<scheduler_name>
4756 Scheduler to be used by UltraMonkey-L7 for load balancing.
4757 The available schedulers are only B<lc> and B<rr>. The default is I<rr>.
4759 =item B<protocol = tcp>
4761 Protocol to be used. B<l7vsadm> supports only B<tcp>.
4762 Since the virtual is specified as an IP address and port, it would be tcp
4763 and will default to tcp.
4765 =item B<realdowncallback = ">I</path/to/realdowncallback>B<">
4767 If this directive is defined, B<l7directord> automatically calls
4768 the executable I</path/to/realdowncallback> after a real server's status
4769 changes to down. The first argument to the realdowncallback is the real
4770 server's IP-address and port (ip_address:portnumber).
4772 =item B<realrecovercallback = ">I</path/to/realrecovercallback>B<">
4774 If this directive is defined, B<l7directord> automatically calls
4775 the executable I</path/to/realrecovercallback> after a real server's status
4776 changes to up. The first argument to the realrecovercallback is the real
4777 server's IP-address and port (ip_address:portnumber).
4779 =item B<customcheck = ">I<custom check command>B<">
4781 If this directive is defined and set B<checktype> to custom, B<l7directord>
4782 exec custom command for real servers health checking. Only if custom command
4783 returns 0, real servers will change to up. Otherwise real servers will change
4784 to down. Custom check command has some macro string. See below.
4790 Change to real server IP address.
4794 Change to real server port number.
4798 =item B<sslconfigfile = ">I</path/to/sslconfigfile>B<">
4800 When communication with Client is SSL, the file name for SSL setting is
4804 =item B<socketoption = ">I<OPTION,OPTION, ...>B<">
4806 An option of the socket used in VirtualService (TCP) is designated.
4807 The setting possible value is described.
4810 In a listener socket of VirtualService, TCP_DEFER_ACCEPT setting.
4813 In a socket for communication with Client in Session and RealServer, TCP_NODELAY setting.
4816 In a socket for communication with Client in Session and RealServer, TCP_CORK setting.
4818 4.quickackon or quickackoff
4819 In a socket for communication with Client in Session and RealServer, TCP_QUICKACK setting.
4822 socketoption=deferaccept nodelay quickackoff
4824 =item B<accesslog = ">I<ACCESSLOG_ROTATE_TYPE>B<">
4826 A presence of access log output is established.
4827 yes: outputs/no: doesn't output.
4829 =item B<accesslog_rotate_type = date>|B<size>|B<datesize>
4831 The rotation type designates "date" "size" "datesize".
4833 =item B<accesslog_rotate_max_backup_index = I<n>
4835 The maximum number of back-up file is designated.
4837 =item B<accesslog_rotate_max_filesize = > I<n>[B<K>|B<M>|B<G>]
4839 The most large size of log is designated.
4840 When "size" and "datesize" are set as accesslog_rotate_type, it becomes indispensable. K(kilo), M(mega) and G(giga) unit are available.
4842 =item B<accesslog_rotate_rotation_timing = year>|B<month>|B<week>|B<date>|B<hour>
4844 It's designated at the timing of a rotation.
4845 When "date" and "datesize" are set as accesslog_rotate_type, it becomes indispensable.
4847 =item B<accesslog_rotate_rotation_timing_value = ">I<rotation_timing_value>B<">
4849 When "year" was designated as accesslog_rotate_rotation_timing.
4850 FORMAT: "MM/dd hh:mm" MM(month) dd(day) hh(hour):mm(minute)
4852 When "month" was designated as accesslog_rotate_rotation_timing.
4853 FORMAT: "dd hh:mm" dd(day) hh(hour):mm(minute)
4855 When "week" was designated as accesslog_rotate_rotation_timing.
4856 FORMAT: "<week> hh:mm" sun|mon|tue|wed|thu|fri|sat(week) hh(hour):mm(minute)"
4858 When "date" was designated as accesslog_rotate_rotation_timing.
4859 FORMAT: "hh:mm" hh(hour):mm(minute)
4861 When "hour" was designated as accesslog_rotate_rotation_timing.
4862 FORMAT: "mm" mm(minute)
4869 B</etc/ha.d/conf/l7directord.cf>
4871 B</var/log/l7vs/l7directord.log>
4873 B</var/run/l7directord.>I<configuration>B<.pid>
4879 L<l7vsadm>, L<heartbeat>