diff -ru heartbeat-0.4.8.orig/ldirectord/README heartbeat-0.4.8/ldirectord/README
--- heartbeat-0.4.8.orig/ldirectord/README	Sun Apr 16 14:25:27 2000
+++ heartbeat-0.4.8/ldirectord/README	Thu Jul  6 03:55:19 2000
@@ -1,9 +1,15 @@
-The Linux Director Daemon (ldirectord) is written by Jacob Rief
-<jacob.rief@tis.at>
+The Linux Director Daemon (ldirectord) was written by Jacob Rief
+<jacob.rief@tiscover.com>
 
 The ldirectord is a stand-alone daemon to monitor services of real
-servers, currently http and https service. It is simple to install and
-works with the heartbeat code (http://www.linux-ha.org/).
+servers, currently http, https and ftp service. It is simple to
+install and works with the heartbeat code (http://www.linux-ha.org/).
 
-See 'perldoc ldirectord' for all the information about ldirectord.
+See 'ldirectord -h' for all the information about ldirectord.
+See linux-ha/doc/ldirectord for more information.
+
+If You have to check only for some kind of the possible services
+You must not install all of the Perl libraries LWP::UserAgent, 
+Net::SSLeay, Net::FTP, simply comment out the functions You dont
+need, check_http, check_https or check_ftp.
 
diff -ru heartbeat-0.4.8.orig/ldirectord/ldirectord heartbeat-0.4.8/ldirectord/ldirectord
--- heartbeat-0.4.8.orig/ldirectord/ldirectord	Thu Jul  6 23:21:41 2000
+++ heartbeat-0.4.8/ldirectord/ldirectord	Mon Oct  9 17:00:42 2000
@@ -1,14 +1,14 @@
 #!/usr/bin/perl
-# Linux Director Daemon - run "perldoc ldirectord" for details 
+# Linux Director Daemon - run "perldoc ldirectord" for details
 # THIS IS BETA. checkout version 1.6 for a stable version.
-# $Id: ldirectord,v 1.13 2000/07/06 10:06:01 jacob Exp $
+# $Id: ldirectord,v 1.20 2000/10/09 21:00:42 horms Exp $
 # © 2000, Jacob Rief <jacob.rief@tis.at>
 # This is GPL software. You should own a few hundred copies
 # of the GPL by now. if not, get one at http://www.fsf.org
 
 =head1 NAME
 
-ldirectord - Linux Director Daemon 
+ldirectord - Linux Director Daemon
 
 Daemon to monitor remote services and control Linux Virtual Server
 
@@ -43,11 +43,11 @@
 
 I<configuration>:
 This is the name for the configuration as specified in the file
-B</etc/ha.d/conf/>I<configuration> 
+B</etc/ha.d/conf/>I<configuration>
 
-B<-d> Don't start as daemon. Useful for debugging. 
+B<-d> Don't start as daemon. Useful for debugging.
 
-B<-h> Help. Print user manual of ldirectord. 
+B<-h> Help. Print user manual of ldirectord.
 
 B<start> the daemon for the specified configuration.
 
@@ -64,7 +64,7 @@
 B<status> of the running daemon for the specified configuration.
 
 
-=head1 SYNTAX 
+=head1 SYNTAX
 
 =head2 Description how to write configuration files
 
@@ -74,11 +74,12 @@
 firewall-mark is an integer greater than zero. The configuration of marking
 packets is controled using the C<-m> option to B<ipchains>(8).  All real
 services and flags for a virtual service must follow this line immediately
-and be indentet.
+and be indented.
 
-B<timeout = >I<n>
+B<checktimeout = >I<n>
 
-defines the number of second until a real server is declared dead
+Defines the number of second until a real server is declared dead. Default
+is 5 seconds. If mentioned in virtual server section - overrides global value.
 
 B<checkinterval = >I<n>
 
@@ -108,6 +109,9 @@
 servers are down. Typically this would be 127.0.0.1 with
 an emergency page.
 
+This directive may also appear within a virtual server, in which 
+case it will overide the global fallback server, if set.
+
 
 B<logfile = ">I</path/to/logfile>B<">
 
@@ -116,41 +120,63 @@
 =head2 These commands must follow a B<virtual> entry and must
 be indented (minimum 4 spaces or one tab)
 
-B<real => I<x.y.z.w:p> B<gate>|B<masq>|B<ipip> [I<weight>] [B<">I<request>B<", ">I<receive>B<">]
+B<real => I<w.x.y.z[-a.b.c.d]:p> B<gate>|B<masq>|B<ipip> [I<weight>] [B<">I<request>B<", ">I<receive>B<">]
 
-Defines a real service by IP-address and port. The second argument
-defines the forwarding method, must be B<gate>, B<ipip> or B<masq>.
-The thrid argument is optional and defines the weight for that real server.
-The last two arguments are optional. They define a request-receive pair to 
-be used to check if a server is alive. They override the request-receive
-pair in the virtual server section. These two strings must be quoted.
-If the request string starts with I<http://...> the IP-address of the real
-server is overridden, otherwise the IP-address of the real server is used.
-This may be used to send a request over a transparent proxy.
+Defines a real service by IP-address and port. Optionally a range of IP
+addresses may be given, in which case each IP address in the range will be
+treated as a real server using the given port. The second argument defines
+the forwarding method, must be B<gate>, B<ipip> or B<masq>.  The thrid
+argument is optional and defines the weight for that real server. The last
+two arguments are optional. They define a request-receive pair to be used
+to check if a server is alive. They override the request-receive pair in
+the virtual server section. These two strings must be quoted. If the
+request string starts with I<http://...> the IP-address of the real server
+is overridden, otherwise the IP-address of the real server is used. This
+may be used to send a request over a transparent proxy.
 
 =head2 More than one of these entries may be inside a virtual section:
 
+B<checktype = >I<negotiate>|I<connect>|I<off>
+
+Type of check to perform. Negotiate means to send request and expect
+receive strings. Connect means raw tcp/ip connection. Default is
+I<negotiate>.
+
 B<service = http>|B<https>|B<ftp>|B<none>
 
 The type of service to monitor. None denotes a service that will not be
 monitored. If the port specfied for the virtual server is 80 the default
-service is B<http>.  If the port specified is 443 the default service is B<https>.
-If the port specified is 21 the default service is B<ftp>. Otherwise the default
-service is B<none>.
+service is B<http>.  If the port specified is 443 the default service is
+B<https>.  If the port specified is 21 the default service is B<ftp>.
+Otherwise the default service is B<none>.
+
+B<checkport = >I<n>
+
+Number of port to monitor. Sometimes check port differs from service port.
+Default is port specified for the real server.
 
 B<request = ">I<uri to requested object>B<">
 
-This object will be requested each checkinterval seconds on each real server.
-The string must be inside quotes. Note that this string may be overridden
-by an optional per real-server based request-string.
+This object will be requested each checkinterval seconds on each real
+server.  The string must be inside quotes. Note that this string may be
+overridden by an optional per real-server based request-string.
 
 B<receive = ">I<string to compare>B<">
 
 If the requested result contains this I<string to compare>, the real server
-is declared alive. The string must be inside quotes. Note that this string may
-be overridden by an optional per real-server based receive-string.
-This string is ignored for ftp services. Ftp service only checks if the
-file exists.
+is declared alive. The string must be inside quotes. Note that this string
+may be overridden by an optional per real-server based receive-string.
+
+B<login = ">I<username>B<">
+
+Username to use to login to FTP server. Default is anonymous.
+
+
+B<passwd = ">I<password>B<">
+
+Password to use to login to FTP server. Default is ldirectord\@<hostname>,
+where hostname is the environment variable HOSTNAME evaluated at run time.
+
 
 B<scheduler = rr>|B<wrr>|B<lc>|B<wlc>
 
@@ -175,6 +201,8 @@
 
 B</var/run/ldirectord.>I<configuration>B<.pid>
 
+B</etc/>I<services>
+
 =head1 SEE ALSO
 
 L<ipvsadm>, L<heartbeat>
@@ -184,19 +212,15 @@
 
 Jacob Rief <jacob.rief@tiscover.com>
 
-Simon Horm <horms@vergenet.net>
+Horms <horms@vergenet.net>, <horms@valinux.com>
+
+Skliarouk Peter <skliaroukp@bigfoot.com>
 
 =cut
 
 # default values
-$TIMEOUT = 10;
+$CHECKTIMEOUT = 5;
 $CHECKINTERVAL = 10;
-if(-f "/sbin/ipvsadm") {
-  $IPVSADM="/sbin/ipvsadm";
-}
-else {
-  $IPVSADM="/usr/sbin/ipvsadm";
-}
 $LDIRLOG="/var/log/ldirectord.log";
 $RUNPID="/var/run/ldirectord";
 $AUTOCHECK="no";
@@ -204,16 +228,35 @@
 @VIRTUAL;
 @REAL;
 @OLDVIRTUAL;
+$LD_TERM_CALLED=0;
 $checksum;
 $initializing;
 
 use Getopt::Std;
+use English;
+#use Time::HiRes qw( gettimeofday tv_interval );
+use Socket;
+use Sys::Hostname;
 #use LWP::Parallel::UserAgent;
+use POSIX qw(setsid);
+
+# command line options
 getopts("dh");
 
+$DEBUG = 3 if (defined $opt_d);
+if ($DEBUG>0 and -f "./ipvsadm") {
+	$IPVSADM="./ipvsadm";
+} else {
+	if(-f "/sbin/ipvsadm") {
+		$IPVSADM="/sbin/ipvsadm";
+	} else {
+		$IPVSADM="/usr/sbin/ipvsadm";
+	}
+}
+
 # main code
 if ($opt_h) {
-	system "/usr/bin/perldoc /etc/ha.d/resource.d/ldirectord";
+	&system_wrapper("/usr/bin/perldoc /etc/ha.d/resource.d/ldirectord");
 } else {
 	$initializing = 1;
 	init();
@@ -222,15 +265,26 @@
 	$initializing = 0;
 	ld_main();
 }
-system("/bin/rm -f $RUNPID.$CONFIG.pid");
-exit 0;
+&ld_rm_file("$RUNPID.$CONFIG.pid");
+&ld_exit(0, "Reached end of \"main\"");
 
 
 # functions
 sub init
 {
-	# install signal handler
-	$SIG{'TERM'} = \&ld_term;
+	# install signal handlers
+
+	# Catch all signals  (this covers TERM)
+	for $i (keys %SIG){
+	  $SIG{"$i"} = \&ld_term;
+	}
+
+	#except CHLD, USR1 and USR2
+	$SIG{'CHLD'} = "DEFAULT";
+	$SIG{'USR1'} = "DEFAULT";
+	$SIG{'USR2'} = "DEFAULT";
+
+	#HUP is actually used
 	$SIG{'HUP'} = \&ld_hup;
 
 	if ( !defined $ARGV[0] || !defined $ARGV[1] || ($ARGV[1] ne "start" && $ARGV[1] ne "stop"
@@ -258,7 +312,7 @@
 			# Kill old daemon
 			if ($CMD eq "stop") {
 				kill 15, $oldpid;
-				exit 0;
+				ld_exit(0, "Exiting from ldirectord stop");
 			} elsif ($CMD eq "restart") {
 				kill 15, $oldpid;
 				while (-f "$RUNPID.$CONFIG.pid") {
@@ -266,17 +320,16 @@
 				}
 			} elsif ($CMD eq "reload") {
 				kill 1, $oldpid;
-				exit 0;
+				ld_exit(0, "Exiting from ldirectord reload");
 			} elsif ($CMD eq "status") {
 				print "ldirectord for $CONFIG is running with pid: $oldpid\n";
-				exit 0;
+				ld_exit(0, "Exiting from ldirectord status");
 			} else {
 				init_error("ldirectord for $CONFIG is already running with pid: $oldpid");
 			}
 		} else {
 			if ($CMD eq "status") {
 				print "ldirectord is not running for $CONFIG\n";
-				exit 0;
 			} elsif ($CMD ne "start") {
 				init_error("ldirectord is not running for $CONFIG");
 			}
@@ -287,20 +340,12 @@
 
 	# Run as daemon
 	if (!defined $opt_d) {
-		if ($pid = fork()) {
-			# the parent goes away
-			open(FILE, ">$RUNPID.$CONFIG.pid") || init_error("Can not open $RUNPID.$CONFIG.pid");
-			print FILE "$pid\n";
-			close(FILE);
-			exit 0;
-		} elsif (!defined $pid) {
-			init_error("ldirector could not fork: $!");
-		}
+		&ld_daemon();
+		open(FILE, ">$RUNPID.$CONFIG.pid") || init_error("Can not open $RUNPID.$CONFIG.pid");
+		print FILE "$$\n";
+		close(FILE);
 	}
-	open(LOGFILE, ">>$LDIRLOG");
-	my $now = localtime()."|$CONFIG";
-	print LOGFILE "[$now] Starting Linux Director Daemon\n";
-	close(LOGFILE);
+	&ld_log("Starting Linux Director Daemon");
 }
 
 
@@ -309,34 +354,38 @@
 	my $msg = shift;
 	chomp($msg);
 	if (defined $opt_d) {
-		open(LOGFILE, ">>$LDIRLOG");
-		print LOGFILE "$msg\n";
-		close(LOGFILE);
+		&ld_log("$msg");
 	} else {
 		print STDERR "$msg\n";
 	}
-	exit 1;
+	ld_exit(1, "Initialisation Error");
 }
 
 
+# ld_term
+# If we get a sinal then log it and quit
+
 sub ld_term
 {
+        my ($signal)=(@_);
+	if($LD_TERM_CALLED){ 
+	  $SIG{'__DIE__'} = "IGNORE"; 
+	  $SIG{"$signal"} = "IGNORE"; 
+	  die("Exit Handler Repeatedly Called\n"); 
+	}
+	$LD_TERM_CALLED=1;
+
+	&ld_rm_file("$RUNPID.$CONFIG.pid");
 	ld_stop();
-	open(LOGFILE, ">>$LDIRLOG");
-	my $now = localtime()."|$CONFIG";
-	print LOGFILE "[$now] Linux Director Daemon terminated\n";
-	close(LOGFILE);
-	&system_wrapper("/bin/rm -f $RUNPID.$CONFIG.pid");
-	exit 0;
+	&ld_log("Linux Director Daemon terminated on signal: $signal");
+	&ld_exit(0, "Linux Director Daemon terminated on signal: $signal");
 }
 
 
 sub ld_hup
 {
-	open(LOGFILE, ">>$LDIRLOG");
-	my $now = localtime()."|$CONFIG";
-	print LOGFILE "[$now] Reloading Linux Director Daemon config\n";
-	close(LOGFILE);
+        my ($signal)=(@_);
+	&ld_log("Reloading Linux Director Daemon config on signal: $signal");
 	@OLDVIRTUAL = @VIRTUAL;
 	eval {
 		read_config();
@@ -355,29 +404,42 @@
 	undef @VIRTUAL;
 	undef $CALLBACK;
 	undef $checksum;
-	open(CFGFILE, "</etc/ha.d/conf/$CONFIG") || config_error(0, "can not open file /etc/ha.d/conf/$CONFIG");
+        if ( $DEBUG > 1 ) {
+		$CONFIGFILE = $CONFIG;
+	} else {
+		$CONFIGFILE = "/etc/ha.d/conf/$CONFIG";
+	}
+	open(CFGFILE, "<$CONFIGFILE") || config_error(0, "can not open file $CONFIGFILE");
 	my $line = 0;
 	while(<CFGFILE>) {
 		$line++;
+		outer_loop:
 		if ($_ =~ /^virtual\s*=\s*(.*)/) {
-			$1 =~ /(\d+\.\d+\.\d+\.\d+:\d+|\d+)/
+			$1 =~ /^(((\d+\.\d+\.\d+\.\d+):(\d+))|(\d+))$/
 			    or config_error($line, "invalid address for virtual server");
 			my (%vsrv, @rsrv);
-			$vsrv{server} = $1;
-			if ($vsrv{server} =~ /(\d+\.\d+\.\d+\.\d+):(\d+)/) {
+			if (defined($2)) {
+			        $vsrv{server} = $3;
+			        $vsrv{port} = $4;
+				$vsrv{checktype} = "negotiate";
 			        $vsrv{protocol} = "tcp";
-				if ($2 eq "80") {
+#				( $vsrv{port} =~ /(\d+)/ ) or config_error($line, "invalid port number");
+				( $vsrv{port}>0 && $vsrv{port}<65536 ) or config_error($line, "port number must be in range 1..65536");
+				if ($vsrv{port} eq "80") {
 					$vsrv{service} = "http";
-				} elsif ($2 eq "443") {
+				} elsif ($vsrv{port} eq "443") {
 					$vsrv{service} = "https";
-				} elsif ($2 eq "21") {
+				} elsif ($vsrv{port} eq "21") {
 					$vsrv{service} = "ftp";
 				} else {
 					$vsrv{service} = "none";
 				}
 			} else {
+			        $vsrv{fwm} = $5;
+				$vsrv{checktype} = "off";
 			        $vsrv{protocol} = "fwm";
 				$vsrv{service} = "none";
+				$vsrv{port} = "0";
 			}
 			$vsrv{real} = \@rsrv;
 			$vsrv{status} = 0;
@@ -386,44 +448,24 @@
 			$vsrv{receive} = "";
 			$vsrv{login} = "anonymous";
 			$vsrv{passwd} = "ldirectord\@$ENV{HOSTNAME}";
+			$vsrv{checktimeout} = "0";
 			push(@VIRTUAL, \%vsrv);
 			while(<CFGFILE>) {
 				$line++;
 				$_ =~ s/\t/    /g;
-				last if !($_ =~ /^ {4,}(.*)/);
+				unless ($_ =~ /^ {4,}(.*)/) {
+				  #Arggh a goto :(
+				  goto outer_loop;
+				}
 				my $rcmd = $1;
 				next if ($rcmd =~ /^#/);
 				if ($rcmd =~ /^real\s*=\s*(.*)/) {
-					$1 =~ /(\d+\.\d+\.\d+\.\d+:\d+)\s+(.*)/ 
+					$1 =~ /(\d+\.\d+\.\d+\.\d+)(-(\d+\.\d+\.\d+\.\d+))?:(\d+)\s+(.*)/ 
 					    or config_error($line, "invalid address for real server");
-					my $rmt = $1;
-					$2 =~ /(\w+)(.*)/ && ($1 eq "gate" || $1 eq "masq" || $1 eq "ipip")
-					    or config_error($line, "forward method must be gate, masq or ipip");
-					my $fwd = $1;
-					if ($2 =~ /\s+(\d+)\s+(.*)/) {
-						my $weight = $1;
-						if ($2 =~ /\"(.*)\",\s*\"(.*)\"/) {
-							push(@rsrv, {"server"=>$rmt, "forward"=>$fwd, "weight"=>$weight, "request"=>$1, "receive"=>$2});
-						} else {
-							push(@rsrv, {"server"=>$rmt, "forward"=>$fwd, "weight"=>$weight});
-						}
-					} elsif ($2 =~ /\s+\"(.*)\",\s*\"(.*)\"/) {
-						push(@rsrv, {"server"=>$rmt, "forward"=>$fwd, "request"=>$1, "receive"=>$2});
+					if ( defined ($2) ) {
+						add_real_server_range($line, \%vsrv, \@rsrv, $1, $3, $4, $5);
 					} else {
-						push(@rsrv, {"server"=>$rmt, "forward"=>$fwd});
-					}
-					my $realsrv=0;
-					foreach $r (@REAL){
-						if($r->{"real"} eq $vsrv{protocol}.":".$rmt){
-							my $ref=$r->{"virtual"};
-							push(@$ref, $vsrv{"protocol"}.":".$vsrv{"server"});
-							$realsrv=1;
-							last;
-						}
-					}
-					if($realsrv==0){
-						push(@REAL, { "real"=>$vsrv{protocol}.":".$rmt, 
-                                                              "virtual"=>[ $vsrv{protocol}.":".$vsrv{"server"} ] });
+						add_real_server($line, \%vsrv, \@rsrv, $1, $4, $5);
 					}
 				} elsif ($rcmd =~ /^request\s*=\s*\"(.*)\"/) {
 					$1 =~ /(.+)/ or config_error($line, "no request string specified");
@@ -431,6 +473,18 @@
 				} elsif ($rcmd =~ /^receive\s*=\s*\"(.*)\"/) {
 					$1 =~ /(.+)/ or config_error($line, "invalid receive string");
 					$vsrv{receive} = $1;
+				} elsif ($rcmd =~ /^checktype\s*=\s*(.*)/){
+                                        lc($1);
+                                        $1 =~ /(\w+)/ && ($1 eq "connect" || $1 eq "negotiate" || $1 eq "off")
+                                            or config_error($line, "checktype must be connect or negotiate or off");
+                                        $vsrv{checktype} = $1;
+				} elsif ($rcmd =~ /^checktimeout\s*=\s*(.*)/){
+                                        $1 =~ /(\d+)/ && $1 or config_error($line, "invalid check timeout");
+                                        $vsrv{checktimeout} = $1;
+				} elsif ($rcmd =~ /^checkport\s*=\s*(.*)/){
+					$1 =~ /(\d+)/ or config_error($line, "invalid port");
+					( $1 > 0 && $1 < 65536 ) or config_error($line, "checkport must be in range 1..65536");
+					$vsrv{checkport} = $1;
 				} elsif ($rcmd =~ /^login\s*=\s*\"(.*)\"/) {
 					$1 =~ /(.+)/ or config_error($line, "invalid login string");
 					$vsrv{login} = $1;
@@ -440,9 +494,9 @@
 				} elsif ($rcmd =~ /^load\s*=\s*\"(.*)\"/) {
 					$1 =~ /(\w+)/ or config_error($line, "invalid string for load testing");
 					$vsrv{load} = $1;
-				} elsif ($rcmd =~ /^scheduler\s*=\s*(.*)/) {
 					lc($1);
-					$1 =~ /(\w+)/ && ($1 eq "rr" || $1 eq "wrr" || $1 eq "lc" || $1 eq "wlc") 
+				} elsif ($rcmd =~ /^scheduler\s*=\s*(.*)/) {
+					$1 =~ /(\w+)/ && ($1 eq "rr" || $1 eq "wrr" || $1 eq "lc" || $1 eq "wlc")
 					    or config_error($line, "scheduler must be rr, wrr, lc or wlc");
 					$vsrv{scheduler} = $1;
 				} elsif ($rcmd =~ /^persistent\s*=\s*(.*)/) {
@@ -476,15 +530,20 @@
 				} elsif ($rcmd =~ /^sitename\s*=\s*(.*)/) {
 					$1 =~ /(\w+)/ or config_error($line, "invalid sitename");
 					$vsrv{sitename} = $1;
+				} elsif ($rcmd =~ /^fallback\s*=\s*(.*)/) {    # Allow specification of a virtual-specific fallback host
+					my $tmp = $1;
+					($tmp =~ /(\d+\.\d+\.\d+\.\d+:\d+)/ || $tmp =~ /(\d+\.\d+\.\d+\.\d+)/) && $1
+			    			or config_error($line, "invalid address for fallback server");
+					$vsrv{fallback} = $tmp
 				} else {
 					config_error($line, "Unknown command $_");
 				}
 			}
 		}
 		next if ($_ =~ /^\s*$/ || $_ =~ /^\s*#/);
-		if ($_ =~ /^timeout\s*=\s*(.*)/) {
-			$1 =~ /(\d+)/ && $1 or config_error($line, "invalid timeout value");
-			$TIMEOUT = $1;
+		if ($_ =~ /^checktimeout\s*=\s*(.*)/) {
+			($1 =~ /(\d+)/ && $1 && $1>0) or config_error($line, "invalid timeout value");
+			$CHECKTIMEOUT = $1;
 		} elsif ($_ =~ /^checkinterval\s*=\s*(.*)/) {
 			$1 =~ /(\d+)/ && $1 or config_error($line, "invalid checkinterval value");
 			$CHECKINTERVAL = $1;
@@ -500,14 +559,16 @@
 		} elsif ($_ =~ /^callback\s*=\s*\"(.*)\"/) {
 			$CALLBACK = $1;
 		} elsif ($_ =~ /^logfile\s*=\s*\"(.*)\"/) {
-			if (open(LOGFILE, ">>$1")) {
-				print LOGFILE "Reading file conf/$CONFIG\n";
-				close(LOGFILE);
-				$LDIRLOG = $1;
-			} else {
+			my $tmpLDIRLOG = $LDIRLOG;
+			$LDIRLOG = $1;
+			if (&ld_log("Reading file conf/$CONFIG")) {
 				config_error($line, "unable to open logfile: $1");
+				$LDIRLOG=$tmpLDIRLOG;
 			}
 		} else {
+			if ($_ =~ /^timeout\s*=\s*(.*)/) {
+				config_error($line, "timeout directive deprciated in favour of checktimeout");
+			}
 			config_error($line, "Unknown command $_");
 		}
 	}
@@ -515,6 +576,68 @@
 	return(0);
 }
 
+sub add_real_server_range
+{
+	my ($line, $vsrv, $rsrv, $first, $last, $port, $flags) = (@_);
+
+        my (@tmp, $first_i, $last_i, $i);
+       
+	if ( ($first_i=ip_to_decimal($first)) <0 ) {
+		config_error($line, "Invalid IP address: $first");
+	}
+	if ( ($last_i=ip_to_decimal($last)) <0 ) {
+		config_error($line, "Invalid IP address: $last");
+	}
+
+	if ($first_i>$last_i) {
+		config_error($line, "Invalid Range: $first-$last: First value must be less than or equal to the second value");
+	}
+
+	# A for loop didn't seem to want to work
+	$i=$first_i;
+	while ( $i le $last_i ) {
+		add_real_server($line, $vsrv, $rsrv, decimal_to_ip($i), $port, $flags);
+		$i++;
+	}
+}
+
+sub add_real_server
+{
+	my ($line, $vsrv, $rsrv, $rmts, $rmtp, $flags) = (@_);
+
+	my $ref;
+	my $realsrv=0;
+
+	$flags =~ /(\w+)(.*)/ && ($1 eq "gate" || $1 eq "masq" || $1 eq "ipip")
+	    or config_error($line, "forward method must be gate, masq or ipip");
+	my $fwd = $1;
+	if ($2 =~ /\s+(\d+)(\s+(.*))?/) {
+		my $weight = $1;
+		if ($3 =~ /\"(.*)\",\s*\"(.*)\"/) {
+			push(@$rsrv, {"server"=>$rmts, "port"=>$rmtp, "forward"=>$fwd, "weight"=>$weight, "request"=>$1, "receive"=>$2});
+		} else {
+			push(@$rsrv, {"server"=>$rmts, "port"=>$rmtp, "forward"=>$fwd, "weight"=>$weight});
+		}
+	} elsif ($2 =~ /\s+\"(.*)\",\s*\"(.*)\"/) {
+		push(@$rsrv, {"server"=>$rmts, "port"=>$rmtp, "forward"=>$fwd, "request"=>$1, "receive"=>$2});
+	} else {
+		push(@$rsrv, {"server"=>$rmts, "port"=>$rmtp, "forward"=>$fwd});
+	}
+
+        my $real    = $vsrv->{"protocol"}.":".$rmts.":".$rmtp;
+	my $virtual = $vsrv->{"protocol"}.":".&get_virtual($vsrv);
+	foreach $r (@REAL){
+		if($r->{"real"} eq $real){
+			my $ref=$r->{"virtual"};
+			push(@$ref, $virtual);
+			$realsrv=1;
+			last;
+		}
+	}
+	if($realsrv==0){
+		push(@REAL, { "real"=>$real, "virtual"=>[ $virtual ] });
+	}
+}
 
 sub config_error
 {
@@ -526,17 +649,15 @@
 			print STDERR "Error: $msg\n";
 		}
 	} else {
-		open(LOGFILE, ">>$LDIRLOG");
 		if ($line>0) {
-			print LOGFILE "Error reading file conf/$CONFIG at line $line: $msg\n";
+			&ld_log("Error reading file conf/$CONFIG at line $line: $msg");
 		} else {
-			print LOGFILE "Error: $msg\n";
+			 &ld_log("Error: $msg\n");
 		}
-		close(LOGFILE);
 	}
 	if ($initializing) {
-		&system_wrapper("/bin/rm -f $RUNPID.$CONFIG.pid");
-		exit 2;
+		&ld_rm_file("$RUNPID.$CONFIG.pid");
+		ld_exit(2, "config_error: Confituration Error");
 	} else {
 		die;
 	}
@@ -553,7 +674,7 @@
 		} elsif ($$v{protocol} eq "fwm") {
 			$$v{proto} = "-f";
 		}
-		$$v{flags} = "$$v{proto} $$v{server} ";
+		$$v{flags} = "$$v{proto} " .  &get_virtual($v) . " ";
 		$$v{flags} .= "-s $$v{scheduler} " if defined ($$v{scheduler});
 		$$v{flags} .= "-p $$v{persistent} " if defined ($$v{persistent});
 		my $real = $$v{real};
@@ -579,24 +700,24 @@
 				if ($$r{request} =~ /$$v{service}:\/\//) {
 					$$r{url} = "$$r{request}";
 				} else {
-					$$r{url} = "$$v{service}:\/\/$$r{server}\/$uri";
+					$$r{url} = "$$v{service}:\/\/$$r{server}:$$r{port}\/$uri";
 				}
 			} else {
 				my $uri = $$v{request};
 				$uri =~ s/^\///g;
-				$$r{url} = "$$v{service}:\/\/$$r{server}\/$uri";
+				$$r{url} = "$$v{service}:\/\/$$r{server}:$$r{port}\/$uri";
                 		$$r{request} = $$v{request} unless defined $$r{request};
                 		$$r{receive} = $$v{receive};
 			}
 		}
 		$$v{status} = -1;
+		$$v{checktimeout} = $CHECKTIMEOUT if ($$v{checktimeout}<=0);
 	}
 }
 
 
 sub ld_start
 {
-	open(LOGFILE, ">>$LDIRLOG");
 	# read status of current ipvsadm -L -n
 	open(IPVS, "$IPVSADM -L -n |");
 	$_ = <IPVS>; $_ = <IPVS>; $_ = <IPVS>;
@@ -604,12 +725,10 @@
 	my $real_service;
 	while (<IPVS>) {
 		if ($_ =~ /(\w+)\s+(\d+\.\d+\.\d+\.\d+\:\d+|\d+)\s+(\w+)\s+persistent\s+(\d+)/) {
-			my $prot = lc $1;
-			$real_service = "$2 " . lc $1;
+			$real_service = "$2 ".lc($1);
 			$oldsrv{"$real_service"} = {"real"=>{}, "scheduler"=>$3, "persistent"=>$4};
 		} elsif ($_ =~ /(\w+)\s+(\d+\.\d+\.\d+\.\d+\:\d+|\d+)\s+(\w+)/) {
-			my $prot = lc $1;
-			$real_service = "$2 " . lc $1;
+			$real_service = "$2 ".lc($1);
 			$oldsrv{"$real_service"} = {"real"=>{}, "scheduler"=>$3};
 		} else {
 			next;
@@ -629,80 +748,73 @@
 	}
 	close(IPVS);
 
-	# modify service, if changed 
-	my $now = localtime()."|$CONFIG";
+	# modify service, if changed
+
 	foreach $nv (@VIRTUAL) {
 		my $nreal = $$nv{real};
 		$$nv{status} = 0;
-		if (exists($oldsrv{"$$nv{server} $$nv{protocol}"})) {
+		my $real_service = &get_virtual($nv) . " "  . $$nv{protocol};
+		if (exists($oldsrv{"$real_service"})) {
 			# service exists, modify it
 			&system_wrapper("$IPVSADM -E $$nv{flags}");
-			print LOGFILE "[$now] Changing virtual server: $$nv{server}\n";
-			my $ov = $oldsrv{"$$nv{server} $$nv{protocol}"};
+			&ld_log("Changing virtual server: " . &get_virtual($nv));
+			my $ov = $oldsrv{&get_virtual($nv) . " " . $$nv{protocol}};
 			my $or = $$ov{real};
 			foreach $nr (@$nreal) {
-				if (exists($$or{"$$nr{server}"})) {
-					&system_wrapper("$IPVSADM -e $$nv{proto} $$nv{server} -R $$nr{server} $$nr{forw} $$nr{wght}");
+				if (exists($$or{"$$nr{server}:$$nr{port}"})) {
+					&system_wrapper("$IPVSADM -e $$nv{proto} " . &get_virtual($nv) . " -R $$nr{server}:$$nr{port} $$nr{forw} $$nr{wght}");
 					$$nr{status} = 1;
 					$$nv{status}++;
-					print LOGFILE "[$now] Changing real server: $$nr{server} ($$nv{status}*$$nv{server})\n";
-					delete($$or{"$$nr{server}"});
+					 &ld_log("Changing real server: $$nr{server}:$$nr{port} (" . &get_virtual($nv) . ")\n");
+					delete($$or{"$$nr{server}:$$nr{port}"});
 				} else {
 					$$nr{status} = 0;
-				}		
+				}
 			}
 			# remove remaining entries for real servers
 			foreach $k (keys %$or) {
-				&system_wrapper("$IPVSADM -d $$nv{proto} $$nv{server} -R $k");
-				print LOGFILE "[$now] Removing real server: $$nr{server} ($$nv{status}*$$nv{server})\n";
+				&system_wrapper("$IPVSADM -d $$nv{proto} " . &get_virtual($nv) . " -R $k");
+				print ("Removing real server: $$nr{server}:$$nr{port} ($$nv{status}*" . &get_virtual($nv) . ")\n");
 			}
-			delete $oldsrv{"$$nv{server} $$nv{protocol}"};
+			delete $oldsrv{&get_virtual($nv) . " " . $$nv{protocol}};
 		} else {
 			# no such service, create a new one
 			&system_wrapper("$IPVSADM -A $$nv{flags}");
 			foreach $nr (@$nreal) {
 				$$nr{status} = 0;
 			}
-			print LOGFILE "[$now] Adding virtual server: $$nv{server}\n";
+			&ld_log("Adding virtual server: " . &get_virtual($nv) . "\n");
 		}
 
-		if (defined $FALLBACK && $$nv{status}==0) {
-			# turn on fallback service
-			&system_wrapper("$IPVSADM -a $$nv{proto} $$nv{server} -R $FALLBACK");
-			print LOGFILE "[$now] Starting fallback server for: $$nv{server}\n";
-		}
+		&fallback_on($nv);
 	}
 
 	# remove remaining entries for virtual servers
 	foreach $nv (@OLDVIRTUAL) {
-		if (exists($oldsrv{"$$nv{server} $$nv{protocol}"})) {
+		if (exists($oldsrv{&get_virtual($nv) . " " . $$nv{protocol}})) {
 			# service still exists, remove it
-			&system_wrapper("$IPVSADM -D $$nv{proto} $$nv{server}");
-			print LOGFILE "[$now] Removing virtual server: $$nv{server}\n";
+			&system_wrapper("$IPVSADM -D $$nv{proto} " . &get_virtual($nv));
+			&ld_log("Removing virtual server: " . &get_virtual($nv) . "\n");
 		}
 	}
-	close(LOGFILE);
 }
 
 
 sub ld_stop
 {
-	open(LOGFILE, ">>$LDIRLOG");
-	my $now = localtime()."|$CONFIG";
 	foreach $v (@VIRTUAL) {
 		my $real = $$v{real};
 		foreach $r (@$real) {
 			if ($$r{status}>0) {
-				&system_wrapper("$IPVSADM -d $$v{proto} $$v{server} -R $$r{server}");
+				&system_wrapper("$IPVSADM -d $$v{proto} " . &get_virtual($v) . " -R $$r{server}:$$r{port}");
 				$$r{status} = 0;
 				$$v{status}--;
-				print LOGFILE "[$now] Removing real server: $$r{server} ($$v{status}*$$v{server})\n";
+				&ld_log("Removing real server: $$r{server}:$$r{port} ($$v{status}*" . &get_virtual($v) );
 			}
 		}
-		&system_wrapper("$IPVSADM -D $$v{proto} $$v{server}");
-		print LOGFILE "[$now] Removing virtual server: $$v{server}\n";
+		&system_wrapper("$IPVSADM -D $$v{proto} " .  &get_virtual($v));
+		&ld_log("Removing virtual server: " .  &get_virtual($v));
 	}
-	close(LOGFILE);
 }
 
 
@@ -710,7 +822,6 @@
 {
 	# Main failover checking code
 	while (1) {
-		open(LOGFILE, ">>$LDIRLOG");
 		my @real_checked;
 		foreach $v (@VIRTUAL) {
 			my $real = $$v{real};
@@ -723,25 +834,30 @@
 			# $ua->max_hosts($#$real+1);
 			# $ua->max_req($#$real+1);
 			foreach $r (@$real) {
-				unless(grep(/^$$v{protocol}:$$r{server}$/, @real_checked)){
-					if ($$v{service} eq "http") {
-						check_http($v, $r);
-						# my $req = new HTTP::Request(GET=>"$$r{url}");
-						# $ua->register($req, \&http_received);
-					} elsif ($$v{service} eq "https") {
-						check_https($v, $r);
-					} elsif ($$v{service} eq "ftp") {
-						check_ftp($v, $r);
-					} else {
-						check_server($v, $r);
+				unless(grep(/^$$v{protocol}:$$r{server}:$$r{port}$/, @real_checked)){
+					if ($$v{checktype} eq "negotiate") {
+						print "DEBUG2: Checking negotiate: real server=$$r{server}:$$r{port} (virtual: " .  &get_virtual($v) . ")\n" if ($DEBUG>=2);
+						if ($$v{service} eq "http") {
+							check_http($v, $r);
+							# my $req = new HTTP::Request(GET=>"$$r{url}");
+							# $ua->register($req, \&http_received);
+						} elsif ($$v{service} eq "https") {
+							check_https($v, $r);
+						} elsif ($$v{service} eq "ftp") {
+							check_ftp($v, $r);
+						} else {
+							check_none($v, $r);
+						}
+					} elsif ($$v{checktype} eq "connect") {
+						print "DEBUG2: Checking connect: server=$$r{server}\n" if ($DEBUG >=2);
+						check_connect($v, $r);
 					}
-					push(@real_checked, "$$v{protocol}:$$r{server}");
+					push(@real_checked, "$$v{protocol}:$$r{server}:$$r{port}");
 				}
 			}
-			# $ua->wait($TIMEOUT);
+			# $ua->wait($$v{checktimeout});
 		}
 		check_cfgfile();
-		close(LOGFILE);
 		sleep $CHECKINTERVAL;
 	}
 }
@@ -781,10 +897,10 @@
 	my ($v, $r) = @_;
 	my $ua = new LWP::UserAgent;
 	$ua->agent("LinuxDirector/0.1".$ua->agent);
-	$ua->timeout($TIMEOUT);
+	$ua->timeout($$v{checktimeout});
 	my $req = new HTTP::Request(GET=>"$$r{url}");
 	my $res = $ua->request($req);
-	my $recstr = $$v{receive};
+	my $recstr = $$r{receive};
 	if ($res->is_success && (!($recstr =~ /.+/) || $res->content =~ /$recstr/)) {
 		service_set($v, $r, "up");
 	} else {
@@ -798,9 +914,8 @@
 	use Net::SSLeay;
 	my ($v, $r) = @_;
 	$uri = $$v{request};
-	$$r{server} =~ /(\d+\.\d+\.\d+\.\d+):(\d+)/;
-	my ($page, $result, %headers) = &Net::SSLeay::get_https($1, $2, $uri);
-	my $recstr = $$v{receive};
+	my ($page, $result, %headers) = &Net::SSLeay::get_https($$r{server}, $$r{port}, $uri);
+	my $recstr = $$r{receive};
 	if (!($recstr =~ /.+/) || $page =~ /$recstr/) {
 		service_set($v, $r, "up");
 	} else {
@@ -809,98 +924,241 @@
 }
 
 
+sub check_connect
+{
+	my ($v, $r) = @_;
+	undef $EVAL_ERROR, $result;
+
+	my $port=(defined $$v{checkport}?$$v{checkport}:$$r{port});
+	eval {
+		local $SIG{'__DIE__'} = "DEFAULT";
+		local $SIG{'ALRM'} = sub { die "Timeout Alarm" };
+		print "DEBUG4: Timeout is $$v{checktimeout}\n" if ($DEBUG>= 4);
+		alarm $$v{checktimeout};
+		$result = OpenSocket( $$r{server}, $port );
+		if ($result == 0) {
+			# Failure to open the socket
+			die("Couldn't open socket");
+		} else {
+			print "DEBUG3: Connected to $1 (port $port)\n" if ($DEBUG>=3);
+		}
+	};
+	alarm 0; # Cancel the alarm
+	if ($EVAL_ERROR) {
+		service_set($v, $r, "down");
+		print "DEBUG3: Deactivating service $$r{server}:$$r{port} because $EVAL_ERROR\n" if ($DEBUG>=3);
+	} else {
+		service_set($v, $r, "up");
+		print "DEBUG3: Activating service $$r{server}:$$r{port}\n" if ($DEBUG>=3);
+	}
+}
+
+
 sub check_ftp
 {
 	use Net::FTP;
 	my ($v, $r) = @_;
-	my $TMPFILE = "/tmp/ldirectord.recvftp";
-	my $ftp = Net::FTP->new("$$v{server}", Timeout=>$TIMEOUT);
+	my $ftp;
+        local *READ_FH;
+	local *WRITE_FH;
+
+	pipe(\*READ_FH, \*WRITE_FH);
+	select(\*WRITE_FH); $| = 1; select(STDERR);
+
+	unless ($ftp = Net::FTP->new("$$r{server}:$$r{port}", Timeout=>$$v{checktimeout})) {
+		service_set($v, $r, "down");
+		return;
+	}
 	$ftp->login($$v{login}, $$v{passwd});
 	$ftp->cwd("/");
 	$ftp->binary();
-	my $localfile = $ftp->get("$$r{request}", $TMPFILE);
+	$ftp->get("$$r{request}", \*WRITE_FH);
 	$ftp->quit();
-	if ($localfile eq $TMPFILE) {
-		service_set($v, $r, "up");
-	} else {
-		service_set($v, $r, "down");
+	close(\*WRITE_FH);
+	while(<READ_FH>) {
+		if (/$$r{receive}/) {
+	 		service_set($v, $r, "up");
+			close(\*READ_FH);
+			return;
+		}
 	}
+	close(\*READ_FH);
+
+	service_set($v, $r, "down");
 }
 
 
-sub check_server
-# dummy function for unknown services
+# check_none
+# Dummy function to check service if service type is none.
+# Just activates the real server
+
+sub check_none
 {
 	my ($v, $r) = @_;
 	service_set($v, $r, "up");
 }
 
 
+# service_set
+# Used to bring up and down real servers.
+# This is the function you should call if you want to bring a real server up or down.
+# This function is safe to call regrdless of the current state of a real server.
+# Do _not_ call _service_up or _service_down directly.
+# pre: v: virtual that the real service belongs to
+#         Only used to determine the protocol of the service
+#      r: real server to take down
+#      state: up or down
+#             up to bring the real service up
+#             down to bring the real service up
+# post: The real server is brough up or down for each virtual service
+#       it belongs to.
+# return: none
+
 sub service_set()
 {
 	my ($v, $r, $state) = @_;
 
 	my ($real, $virtual, $virt);
+
+        # Return if the real server is already in the desired state
+	return if ($$r{status}!=0 and $state=~/up/i);
+	return if ($$r{status}!=1 and $state=~/down/i);
+
+        # Find the real server in @REAL
 	foreach $real (@REAL) {
-		if($real->{"real"} eq "$$v{protocol}:$$r{server}"){
+		if($real->{"real"} eq "$$v{protocol}:$$r{server}:$$r{port}"){
 			$virtual = $real->{"virtual"};
 			last;
 		}
 	}
-	return unless(defined($virtual));
-	return if($$r{status}!=0 and $state=~/up/i);
-	return if($$r{status}!=1 and $state=~/down/i);
+	return unless (defined($virtual));
+	
+	# Check each virtual service for the real server and make
+	# changes as neccessary
 	foreach $v (@VIRTUAL){
-		if(grep(/^$$v{protocol}:$$v{server}$/, @$virtual)){
-			if($state=~/up/i){
+		my $qry=$$v{protocol} . ":" . &get_virtual($v);
+		if (grep(/^$qry$/, @$virtual)) {
+			if ($state=~/up/i) {
 				$$r{status}=0;
 				_service_up($v, $r);
-			}
-			elsif($state=~/down/i){
+				$DEBUG >=2 && print "DEBUG2: Enabling server=$$r{server}\n";
+			} elsif ($state=~/down/i) {
 				$$r{status}=1;
 				_service_down($v, $r);
+				$DEBUG >=2 && print "DEBUG2: Disabling server=$$r{server}\n";
 			}
 		}
 	}
 }
 
-      
+
+# _service_up
+# Bring a real service up if it is down
+# Should be called by set_service only
+# I.e. If you want to change the state of a real server call set_service.
+#      If you call this function directly then ldirectord will lose track 
+#      of the state of real servers.
+# pre: v: reference to virtual service to with the real server belongs
+#      r: refernece to the real server to take down
+# post: real service is taken up from the respective virtual service
+#       if it is inactive
+# return: none   
+
 sub _service_up
 {
 	my ($v, $r) = @_;
 	if ($$r{status}==0) {
-		&system_wrapper("$IPVSADM -a $$v{proto} $$v{server} -R $$r{server} $$r{forw} $$r{wght}");
+		&system_wrapper("$IPVSADM -a $$v{proto}  " . &get_virtual($v) . " -R $$r{server}:$$r{port} $$r{forw} $$r{wght}");
 		$$r{status} = 1;
 		$$v{status}++;
-		my $now = localtime()."|$CONFIG";
-		print LOGFILE "[$now] Adding real server: $$r{server} ($$v{status}*$$v{server})\n";
-		if ($$v{status}==1 && defined $FALLBACK) {
-			# turn off fallback service
-			&system_wrapper("$IPVSADM -d $$v{proto} $$v{server} -R $FALLBACK");
-			print LOGFILE "[$now] Turning off fallback server for: $$v{server}\n";
-		}
+		&ld_log("Adding real server: $$r{server}:$$r{port} (" .  &get_virtual($v) . ")");
+		&fallback_off($v);
 	}
 }
 
 
+# _service_down
+# Bring a real service down if it is up
+# Should be called by set_service only
+# I.e. if you want to change the state of a real server call set_service.
+#      If you call this function directly then ldirectord will lose track 
+#      of the state of real servers.
+# pre: v: reference to virtual service to with the real server belongs
+#      r: refernece to the real server to take down
+# post: real service is taken down from the respective virtual service
+#       if it is active
+# return: none   
+
 sub _service_down
 {
 	my ($v, $r) = @_;
 	if ($$r{status}==1) {
-		&system_wrapper("$IPVSADM -d $$v{proto} $$v{server} -R $$r{server}");
+		&system_wrapper("$IPVSADM -d $$v{proto} " .  &get_virtual($v) . " -R $$r{server}:$$r{port}");
 		$$r{status} = 0;
 		$$v{status}--;
-		my $now = localtime()."|$CONFIG";
-		print LOGFILE "[$now] Removing real server: $$r{server} ($$v{status}*$$v{server})\n";
-		if ($$v{status}==0 && defined $FALLBACK) {
-			# turn on fallback service
-			&system_wrapper("$IPVSADM -a $$v{proto} $$v{server} -R $FALLBACK");
-			print LOGFILE "[$now] Starting fallback server for: $$v{server}\n";
-		}
+		&ld_log("Removing real server: $$r{server}:$$r{port} (" .  &get_virtual($v) . ")");
+		&fallback_on($v);
+	}
+}
+
+
+# fallback_on
+# Turn on the fallback server for a virtual service if it is inactive
+# pre: virtaual: virtual to turn fallback service on for
+# post: fallback server is turned on if it was inactive
+# return: none
+
+sub fallback_on {
+	my ($virtual) = (@_);
+
+	my $fallback=&fallback_find($virtual);
+	if (defined $fallback && $$virtual{status}==0) {
+		# turn on fallback service
+		&system_wrapper("$IPVSADM -a $$virtual{proto} " . &get_virtual($virtual) . " -R $fallback");
+		&ld_log("Starting fallback server for: " . &get_virtual($virtual) . " ($fallback)");
 	}
 }
 
 
+# fallback_off
+# Turn off the fallback server for a virtual service if it is active
+# pre: virtaual: virtual to turn fallback service off for
+# post: fallback server is turned off if it was active
+# return: none
+
+sub fallback_off {
+	my ($virtual) = (@_);
+
+	my $fallback=&fallback_find($virtual);
+	if ($$v{status}==1 && defined $fallback) {
+		# turn off fallback service
+		&system_wrapper("$IPVSADM -d $$virtual{proto} " . &get_virtual($virtual) . " -R $fallback");
+		&ld_log("Turning off fallback server for: " . &get_virtual($virtual) . " ($fallback)");
+	}
+}
+
+
+# fallback_find
+# Determine the fallback for a virtual service
+# pre: virtual: reference to a virtual service
+# post: none
+# return: $virtual->{"fallback"} if defined
+#         else $FALLBACK if defined
+#         else undef
+
+sub fallback_find {
+	my ($virtual) = (@_);
+
+	if( defined $virtual->{"fallback"} ) {
+		return($virtual->{"fallback"});
+	} elsif ( defined($FALLBACK) ) {
+		return($FALLBACK);
+	}
+
+	return;
+}
+  
+
 sub check_cfgfile
 {
 	return unless ($AUTOCHECK eq "yes" || defined $CALLBACK);
@@ -914,10 +1172,9 @@
 	}
 	close(CFGFILE);
 	if (defined $checksum && $chs!=$checksum) {
-		my $now = localtime()."|$CONFIG";
-		print LOGFILE "[$now] Configuration file has changed on disk\n";
+		&ld_log("Configuration file '/etc/ha.d/conf/$CONFIG' has changed on disk");
 		if ($AUTOCHECK eq "yes") {
-			print LOGFILE "[$now] Rereading Linux Director Daemon config\n";
+			&ld_log(" - reread new configuration");
 			@OLDVIRTUAL = @VIRTUAL;
 			eval {
 				read_config();
@@ -928,29 +1185,340 @@
 				@VIRTUAL = @OLDVIRTUAL;
 			}
 			undef @OLDVIRTUAL;
+		} else {
+			&ld_log(" - ignore new configuration\n");
 		}
 		if (-x $CALLBACK) {
-			my $rc = system("$CALLBACK $CONFIG");
-			print LOGFILE "[$now] Callback: $CALLBACK returned code: $rc\n";
+			&system_wrapper("$CALLBACK $CONFIG");
 		}
 	}
 	$checksum = $chs;
 }
 
 
+# ld_log
+# Log a message to a file.
+# File is opened and closed again as a primative means to
+# make log rotation work
+# pre: message: Message to write
+# post: message and simetsamp is written to log file
+# return: 0 on success
+#         1 on error
+
+sub ld_log 
+{
+	my ($message) = (@_);
+
+        my $now = localtime()."|$CONFIG";
+
+        if ($DEBUG >=2) {
+	  print "DEBUG$DEBUG: $message\n" || return(1);
+	}
+
+        open(LOGFILE, ">>$LDIRLOG") || return(1);
+	chomp $message;
+        print LOGFILE "[$now] $message\n" || return(1);
+        close(LOGFILE) || return(1);;
+
+	return(0);
+}
+
+
 # system_wrapper
 # Wrapper arround system command to log errors
 # pre: LIST: arguments to pass to system()
 # post: system is called and if it returns non-zero a failure message is logged
 # return: none
-#
-# Added by Horms <horms@vergenet.net>, 11th January 2000
 
 sub system_wrapper
 {
 	my (@args)=(@_);
 
-	my $now = localtime() . "|$CONFIG";
-	system(@_) == 0 or print LOGFILE  "[$now] system(@args) failed\n"
+	system(@_) == 0 or &ld_log("system(@args) failed");
+}
+
+
+# ld_rm_file
+# Remove a file, symink, or anything that isn't a directory
+# and exists
+# pre: filename: file to delete
+# post: If filename does not exist or is a directory an
+#       error state is reached
+#       Else filename is delete
+#       If $DEBUG >=2 errors are logged
+# return: 0 on success
+#         -1 on error
+
+sub ld_rm_file
+{
+	my ($filename)=(@_);
+	
+	my ($status);
+
+	if(-d "$filename"){ 
+	        if ($DEBUG >=2) {
+			&ld_log("ld_rm_file: $filename is a directory, skipping");
+		}
+		return(-1); 
+	}
+	if(! -e "$filename"){ 
+	        if ($DEBUG >=2) {
+			&ld_log("ld_rm_file: $filename doesn't exist, skipping");
+		}
+		return(-1); 
+	}
+	$status = unlink($filename);
+	if($tatus!=1 && $DEBUG >=2){
+		&ld_log("ld_rm_file: Error deleting: $filename: $!");
+	}
+	return(($status==1)?0:-1)
+}
+
+
+# See if a number is an octet, that is >=0 and <=255
+# pre: alleged_octet: the octect to test
+# post: 1 if the alleged_octet is an octet
+#       0 otherwise
+
+sub is_octet 
+{
+	  my ($alleged_octet)=(@_);
+
+	  if($alleged_octet<0){ return 0; }
+	  if($alleged_octet>255){ return 0; }
+
+	  return(1);
+}
+
+
+# is_ip
+# Check that a given string is an IP address
+# pre: alleged_ip: string representing ip address
+# post: 1 if alleged_ip is a valid ip address
+#       0 otherwise
+
+sub is_ip 
+{
+	  my ($alleged_ip)=(@_);
+
+	  #If we don't have four, . delimited numbers then we have no hope
+	  unless($alleged_ip=~m/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { return 0; }
+
+	  #Each octet mist be >=0 and <=255
+	  unless(&is_octet($1)){ return 0; }
+	  unless(&is_octet($2)){ return 0; }
+	  unless(&is_octet($3)){ return 0; }
+	  unless(&is_octet($4)){ return 0; }
+
+	  return(1);
+}
+
+
+# ip_to_decimal
+# Turn an IP address given as a dotted quad into a decimal
+# pre: ip_address: string representing IP address
+# post: -1 if an error occurs
+#       decimal representation of IP address otherwise
+
+sub ip_to_decimal 
+{
+	  my ($ip_address)=(@_);
+
+	  unless(&is_ip($ip_address)){ return(-1); }
+	  unless($ip_address=~m/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/){ return(-1); }
+
+	  return(((((($1<<8)+$2)<<8)+$3)<<8)+$4);
+}
+
+# decimal_to_ip
+# Turn an IP address given as a dotted quad into a decimal
+# pre: ip_address: string representing IP address
+# post: -1 if an error occurs
+#       decimal representation of IP address otherwise
+
+sub decimal_to_ip 
+{
+	my ($ip_address)=(@_);
+
+	my $result="";
+
+	return(sprintf(
+		"%d.%d.%d.%d",
+		($ip_address>>24)&255,
+		($ip_address>>16)&255,
+		($ip_address>>8)&255,
+		$ip_address&255
+	));
+}
+
+# get_virtual
+# Get the service for a virtual
+# Will be of the form IP:port for a UDP or TCP service
+# Will be of the form fwmark for a Firewall Mark service
+sub get_virtual
+{
+	my ($nv)=(@_);
+
+	if($nv->{"protocol"} eq "fwm"){
+	  return $nv->{"fwm"};
+	}
+	else {
+          return $nv->{"server"} . ":" . $nv->{"port"};
+	}
+}
+
+
+
+# ld_exit
+# Exit and log a message
+# pre: exit_status: Integer exit status to exit with
+#                   0 wiil be used if parameter is omitted
+#      message: Message to log when exiting. May be omitted
+# post: If exit_status is non-zero or $DEBUG>2 then
+#       message logged.
+#       Programme exits with exit_status
+# return: does not return
+
+sub ld_exit
+{
+	my($exit_status, $message)=(@_);
+	unless(defined($exit_status)){ $exit_status=0; }
+	unless(defined($exit_status)){ $message=""; }
+
+	if($exit_status!=0 or $DEBUG>2){
+	  &ld_log("Exiting with exit_status $exit_status: $message");
+	}
+	exit($exit_status);
+}
+
+
+# Added by Peter Skliarouk 
+# Used if checktype = connect
+sub OpenSocket ($ $)
+{
+	# Make a Berkeley socket connection between this program and a TCP port
+	# on another (or this) host. Port must be number.
+	local($OtherHostIP, $Port) = @_;
+	local($OurHostname, $sockaddr, $name, $aliases, $proto, $type, $len, $ThisAddr, $this, $that);
+
+	$OurHostname = &hostname;
+
+	($name, $aliases, $proto) = getprotobyname('tcp');
+	($name, $aliases, $type, $len, $ThisAddr) = gethostbyname($OurHostname);
+	($name, $aliases, $type, $len, $OtherHostAddr) = gethostbyname($OtherHostIP);
+
+	$sockaddr = 'S n a4 x8';    # Format for packed network address
+	$this = pack($sockaddr, &AF_INET, 0, $ThisAddr);
+	$that = pack($sockaddr, &AF_INET, $Port, $OtherHostAddr);
+
+	$result = socket(S, &PF_INET, &SOCK_STREAM, $proto) || return undef;
+	$result = bind(S, $this) || return undef;
+	$result = connect(S, $that) || return undef;
+
+	select(S); $| = 1; select(STDOUT);      # set S to be un-buffered
+	return 1;                               # success
+}
+
+
+# daemon
+# Close and fork to become a daemon.
+#
+# Notes from unix programmer faq
+# http://www.landfield.com/faqs/unix-faq/programmer/faq/
+#
+# Almost none of this is necessary (or advisable) if your daemon is being
+# started by `inetd'.  In that case, stdin, stdout and stderr are all set up
+# for you to refer to the network connection, and the `fork()'s and session
+# manipulation should *not* be done (to avoid confusing `inetd').  Only the
+# `chdir()' and `umask()' steps remain as useful.
+#
+# Gratuitously over documented, because it can be
+#
+# Writen by Horms, horms@vergenet.net for an unrelated project while
+# working for Zip World, http://www.zipworld.com.au/, 1997-1999.
+
+sub ld_daemon {
+	# `fork()' so the parent can exit, this returns control to the command
+	# line or shell invoking your program.  This step is required so that
+	# the new process is guaranteed not to be a process group leader. The
+	# next step, `setsid()', fails if you're a process group leader.
+	&ld_daemon_become_child();
+
+	# setsid()' to become a process group and session group leader. Since a
+	# controlling terminal is associated with a session, and this new
+	# session has not yet acquired a controlling terminal our process now
+	# has no controlling terminal, which is a Good Thing for daemons.
+	if(POSIX::setsid()<0){
+		&ld_exit(-1, "ld_daemon: Could not setsid");
+	}
+
+	# fork()' again so the parent, (the session group leader), can exit.
+	# This means that we, as a non-session group leader, can never regain a
+	# controlling terminal.
+	&ld_daemon_become_child();
+
+	# `chdir("/")' to ensure that our process doesn't keep any directory in
+	# use. Failure to do this could make it so that an administrator
+	# couldn't unmount a filesystem, because it was our current directory.
+	if(chdir("/")<0){
+		&ld_exit(-1, "ld_daemon: Could not chdir");
+	}
+
+	# `umask(0)' so that we have complete control over the permissions of
+	# anything we write. We don't know what umask we may have inherited.
+	umask(0);
+
+	# `close()' fds 0, 1, and 2. This releases the standard in, out, and
+	# error we inherited from our parent process. We have no way of knowing
+	# where these fds might have been redirected to. Note that many daemons
+	# use `sysconf()' to determine the limit `_SC_OPEN_MAX'.  `_SC_OPEN_MAX'
+	# tells you the maximun open files/process. Then in a loop, the daemon
+	# can close all possible file descriptors. You have to decide if you
+	# need to do this or not.  If you think that there might be
+	# file-descriptors open you should close them, since there's a limit on
+	# number of concurrent file descriptors.
+	close(STDIN);
+	close(STDOUT);
+	close(STDERR);
+
+	# Establish new open descriptors for stdin, stdout and stderr. Even if
+	# you don't plan to use them, it is still a good idea to have them open.
+	# The precise handling of these is a matter of taste; if you have a
+	# logfile, for example, you might wish to open it as stdout or stderr,
+	# and open `/dev/null' as stdin; alternatively, you could open
+	# `/dev/console' as stderr and/or stdout, and `/dev/null' as stdin, or
+	# any other combination that makes sense for your particular daemon.
+	if(open(STDIN, "</dev/null")<0){
+		&ld_exit(-1, "ld_daemon: Could not open /dev/null");
+	}
+	if(open(STDOUT, ">>/dev/console")<0){
+		&ld_exit(-1, "ld_daemon: Could not open /dev/console");
+	}
+	if(open(STDERR, ">>/dev/console")<0){
+		&ld_exit(-1, "ld_daemon: Could not open /dev/console");
+	}
+}
+
+# ld_daemon_become_child
+# Fork, kill parent and return child process
+# pre: none
+# post: process forkes and parent exits
+#       All preocess exit with exit status -1 if an error occurs
+# return: parent: exits
+#         child: none  (this is the process that returns)
+# Writen by Horms, horms@vergenet.net for an unrelated project while
+# working for Zip World, http://www.zipworld.com.au/, 1997-1999.
+
+sub ld_daemon_become_child {
+	my($status);
+
+	$status=fork();
+
+	if($status<0){
+		&ld_exit(-1, "ld_daemon_become_child: Could not fork: $!");
+	}
+	if($status>0){
+		&ld_exit(0, "ld_daemon_become_child: Parent exiting as it should");
+	}
 }
 
diff -ru heartbeat-0.4.8.orig/ldirectord/ldirectord.cf heartbeat-0.4.8/ldirectord/ldirectord.cf
--- heartbeat-0.4.8.orig/ldirectord/ldirectord.cf	Thu Jul  6 23:21:41 2000
+++ heartbeat-0.4.8/ldirectord/ldirectord.cf	Thu Sep 14 18:59:37 2000
@@ -1,6 +1,5 @@
 #
-# Sample ldirectord configuration file to configure a vitual service on TCP
-# port 80 for a single IP address with 3 real servers using direct routing.
+# Sample ldirectord configuration file to configure various virtual services.
 #
 # Ldirectord will connect to each real server once per second and request
 # /index.html. If the data returned by the server does not contain the
@@ -11,13 +10,17 @@
 #
 # Prepared: April 2000
 
-timeout=3
+# Global Directives
+checktimeout=3
 checkinterval=1
 fallback=127.0.0.1:80
+
+# A sample virual with a fallback that will override the gobal setting
 virtual=192.168.6.240:80
         real=192.168.6.2:80 gate
         real=192.168.6.3:80 gate
         real=192.168.6.6:80 gate
+	fallback=127.0.0.1:80
         service=http
         request="index.html"
         receive="Test Page"
@@ -27,7 +30,6 @@
 
 # Sample configuration for a fwmark based service For an explination of
 # fwmark see the ipvsadm(8) man page
-
 #virtual=1
 #        real=192.168.6.2:80 gate
 #        real=192.168.6.3:80 gate
@@ -37,3 +39,47 @@
 #        receive="Test Page"
 #        scheduler=rr
 #        #persistent=600
+#        protocol=fwm
+#        checktype=connect
+
+# Sample configuration for a service using a range of real servers
+# and a single real server for a virtual service
+#virtual=192.168.6.240:80
+#        real=192.168.6.2-192.168.6.30:80 gate
+#        real=192.168.6.32:80 gate
+#        service=http
+#        request="index.html"
+#        receive="Test Page"
+#        scheduler=rr
+#        protocol=tcp
+
+
+#Sample configuration for an ftp virtual service.
+#Fallback setting overides global
+#virtual=192.168.6.240:21
+#        real=192.168.16.3:21 masq
+#        real=192.168.16.5:21 masq
+#        fallback=127.0.0.1:21
+#        service=ftp
+#        scheduler=wlc
+#        login="anonymous"
+#	 passwd="ldirectord@localhost"
+#	 request="welcome.msg"
+#	 receive="test"
+#        persistent=600
+#        protocol=tcp
+
+#Sample configuration for an unsuported protocol
+#The real servers will just be brought up without checking for availability
+#Fallback setting overides global
+#virtual=192.168.6.240:23
+#        real=192.168.16.3:23 masq 
+#        real=192.168.16.5:23 masq
+#        fallback=127.0.0.1:21
+#        service=none
+#        scheduler=wlc
+#        request="welcome.msg"
+#        receive="test"         
+#        persistent=600
+#        protocol=tcp
+
diff -ru heartbeat-0.4.8.orig/ldirectord/ldirectord.sh heartbeat-0.4.8/ldirectord/ldirectord.sh
--- heartbeat-0.4.8.orig/ldirectord/ldirectord.sh	Thu Jul  6 23:21:41 2000
+++ heartbeat-0.4.8/ldirectord/ldirectord.sh	Tue Aug  1 08:30:27 2000
@@ -15,7 +15,11 @@
 #
 
 # Source function library.
-. /etc/rc.d/init.d/functions
+if
+  [ -f /etc/rc.d/init.d/functions ]
+then
+  . /etc/rc.d/init.d/functions
+fi
 
 [ -f /etc/ha.d/conf/ldirectord.cf ] || exit 0
 
@@ -29,7 +33,7 @@
         action "Starting ldirectord" /sbin/ldirectord  ldirectord.cf start
 	;;
   stop)
-        action "Stoping ldirectord" /sbin/ldirectord  ldirectord.cf stop
+        action "Stopping ldirectord" /sbin/ldirectord  ldirectord.cf stop
 	;;
   restart)
 	$0 stop
