156
177
# If using this script standalone, be sure to set the shared lib path and
157
178
# the path to the perldap modules.
159
182
use lib qw(@perlpath@);
161
$usage = "\nusage: $0 -f configuration-file [-h host] [-p port] [-r] [-u refresh-url] [-t refresh-interval]\n\nor : $0 -v\n";
184
my $usage = "\nusage: $0 [-f configuration-file | --configfile configuration-file] " .
185
"[-c connection, --conn connection] [-a alias, --alias alias] [-k color, --color color] " .
186
"[-h host, --host host] [-p port, --port port] [-r, --skip-header] [-s, --text] " .
187
"[-u refresh-url, --url refresh-url] [-t refresh-interval, --interval refresh-interval ] " .
188
"[-W, --prompt]\n\nor : $0 -v | --version\n";
163
use Getopt::Std; # parse command line arguments
190
use Getopt::Long; # parse command line arguments
164
191
use Mozilla::LDAP::Conn; # LDAP module for Perl
165
192
use Mozilla::LDAP::Utils qw(normalizeDN); # LULU, utilities.
166
193
use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API
170
197
# Global variables
172
$product = "Directory Server Replication Monitor";
173
$version = "Version 1.0";
199
my $product = "Directory Server Replication Monitor";
200
my $version = "Version 1.1";
175
202
# ldap servers given or discovered from the replication agreements:
176
# @servers = (host:port=shadowport:binddn:password:cert_db)
203
my @servers; # = (host:port=shadowport:binddn:password:cert_db)
178
206
# entries read from the connection section of the configuration file:
179
# @allconnections = (host:port=shadowport:binddn:password:cert_db)
207
my @allconnections; # = (host:port=shadowport:binddn:password:cert_db)
181
209
# aliases of ldap servers read from the configuration file:
182
# %allaliases{$host:$port}= (alias)
210
my %allaliases; # = {$host:$port} = (alias)
184
216
# replicas discovered on all ldap servers
185
# @allreplicas = (server#:replicaroot:replicatype:serverid:replicadn)
217
my @allreplicas; # = (server#:replicaroot:replicatype:serverid:replicadn)
187
219
# ruvs retrieved from all replicas
188
# @allruvs{replica#:masterid} = (rawcsn:decimalcsn;mon/day/year hh:mi:ss)
220
my %allruvs; # = {replica#:masterid} = (rawcsn:decimalcsn;mon/day/year hh:mi:ss)
190
222
# agreements discovered on all ldap supplier servers:
191
# @allagreements = (supplier_replica#:consumer#:conntype:schedule:status)
223
my @allagreements; # = (supplier_replica#:consumer#:conntype:schedule:status)
192
224
# the array may take another format after the consumer replicas are located:
193
# @allagreements = (supplier_replica#:consumer_replica#:conntype:schedule:status)
225
# @allagreements; # = (supplier_replica#:consumer_replica#:conntype:schedule:status)
227
my %ld; # ldap connection hash
229
my ($opt_f, $opt_h, $opt_p, $opt_u, $opt_t, $opt_r, $opt_s);
230
my (@conns, @alias, @color);
231
my ($section, $interval, $nowraw, $now, $mm, $dd, $tt, $yy, $wday);
232
my ($fn, $rc, $prompt, $last_sidx);
222
271
# print the HTML header
223
272
&print_html_header;
225
# print separator for new replication set
226
print "<hr width=90% size=3><br>\n";
277
# print separator for new replication set
278
print "<hr width=90% size=3><br>\n";
229
exit -1 if &validateArgs < 0;
230
exit if &read_cfg_file ($opt_f) < 0;
232
282
# Start with the given host and port
233
283
# The index names in %ld are defined in Mozilla::LDAP::Utils::ldapArgs()
234
284
&add_server ("$ld{host}:$ld{port}:$ld{bind}:$ld{pswd}:$ld{cert}");
237
while ($serveridx <= $#servers) {
287
while ($serveridx <= $#servers) {
238
288
if (&get_replicas ($serveridx) != 0 && $serveridx == 0) {
239
my ($host, $port, $binddn) = split (/:/, $servers[0]);
289
my ($host, $port, $binddn) = split (/:/, $servers[$serveridx]);
240
290
print("Login to $host:$port as \"$binddn\" failed\n");
273
328
sub read_cfg_file
276
unless (open(CFGFILEHANDLE, $fn)) {
277
print "<p>Error: Can't open \"$fn\": $!.\n";
278
print "<p>If you need help on the configuration file, Please go back and click the Help button.\n";
282
while (<CFGFILEHANDLE>) {
283
next if (/^\s*\#/ || /^\s*$/);
289
if ( $section =~ /conn/i ) {
290
push (@allconnections, $_);
292
elsif ( $section =~ /alias/i ) {
293
m/^\s*(\S.*)\s*=\s*(\S+)/;
294
$allaliases {$2} = $1;
296
elsif ( $section =~ /color/i ) {
297
m/^\s*(-?\d+)\s*=\s*(\S+)/;
298
$allcolors {$1} = $2;
333
# process the command line config params
334
@allconnections = @conns;
336
foreach $tmp (@alias){
337
$tmp =~ m/^\s*(\S.*)\s*=\s*(\S+)/;
338
$allaliases{$2} = $1;
342
foreach $tmp (@color){
343
$tmp =~ m/^\s*(-?\d+)\s*=\s*(\S+)/;
349
unless (open(CFGFILEHANDLE, $fn)) {
351
print "Error: Can't open configuration file\"$fn\": $!.\n";
353
print "<p>Error: Can't open configuration file\"$fn\": $!.\n";
354
print "<p>If you need help on the configuration file, Please go back and click the Help button.\n";
359
while (<CFGFILEHANDLE>) {
360
next if (/^\s*\#/ || /^\s*$/);
366
if ( $section =~ /conn/i ) {
367
push (@allconnections, $_);
369
elsif ( $section =~ /alias/i ) {
370
m/^\s*(\S.*)\s*=\s*(\S+)/;
371
$allaliases {$2} = $1;
373
elsif ( $section =~ /color/i ) {
374
m/^\s*(-?\d+)\s*=\s*(\S+)/;
375
$allcolors {$1} = $2;
379
close (CFGFILEHANDLE);
302
381
if ( ! keys (%allcolors) ) {
303
$allcolors {0} = "#ccffcc"; #apple green
304
$allcolors {5} = "#ffffcc"; #cream yellow
305
$allcolors {60} = "#ffcccc"; #pale pink
382
$allcolors {0} = "#ccffcc"; #apple green
383
$allcolors {5} = "#ffffcc"; #cream yellow
384
$allcolors {60} = "#ffcccc"; #pale pink
307
386
@colorkeys = sort (keys (%allcolors));
308
close (CFGFILEHANDLE);
314
my ($serveridx) = @_;
315
394
my ($conn, $host, $port, $shadowport, $binddn, $bindpwd, $bindcert);
317
396
my ($replica, $replicadn);
318
397
my ($ruv, $replicaroot, $replicatype, $serverid, $masterid, $maxcsn);
319
398
my ($type, $flag, $i);
320
399
my ($myridx, $ridx, $cidx);
400
my ($lastmodifiedat, $agreement);
323
403
# Bind to the server
405
if($#servers < 0 || $serveridx > $#servers + 1){
325
409
($host, $port, $binddn, $bindpwd, $bindcert) = split (/:/, "$servers[$serveridx]", 5);
327
410
($port, $shadowport) = split (/=/, $port);
328
411
$shadowport = $port if !$shadowport;
330
413
$conn = new Mozilla::LDAP::Conn ($host, $shadowport, "$binddn", $bindpwd, $bindcert);
332
414
return -1 if (!$conn);
509
591
sub process_suppliers
511
my ($ridx, $mid, $maxcsn);
593
my ($ridx, $mid, $maxcsn, $ismaster);
516
597
$last_sidx = -1; # global variable for print html page
518
599
for ($ridx = 0; $ridx <= $#allreplicas; $ridx++) {
520
600
# Handle masters and hubs
521
601
if ($allreplicas[$ridx] =~ /:master:(\d+):/i) {
524
604
# Skip replicas without agreements defined yet
525
605
next if (! grep {$_->{ridx} == $ridx} @allagreements);
527
606
$maxcsn = &print_master_header ($ridx, $mid);
528
if ( "$maxcsn" != "none" ) {
607
if ( "$maxcsn" ne "none" ) {
529
608
&print_consumer_header ();
530
609
&print_consumers ($ridx, $mid);
560
643
my ($maxcsnval) = split ( /;/, "$myruv" );
561
644
my ($maxcsn) = &to_string_csn ($maxcsnval);
562
645
my ($sidx, $replicaroot, $replicatype, $serverid) = split (/:/, $allreplicas[$ridx]);
564
if ( $maxcsn == "" ) {
647
if ( $maxcsn eq "" ) {
568
651
# Print the master name
569
if ( $last_sidx != $sidx ) {
652
if ( $last_sidx ne $sidx ) {
570
653
my ($ldapurl) = &get_ldap_url ($sidx, $sidx);
571
654
&print_legend if ( $last_sidx < 0);
572
print "<p><p><hr><p>\n";
573
print "\n<p><center class=page-subtitle><font color=#0099cc>\n";
574
print "Master:  $ldapurl</center>\n";
656
print "Master: $ldapurl\n"
658
print "<p><p><hr><p>\n";
659
print "\n<p><center class=page-subtitle><font color=#0099cc>\n";
660
print "Master:  $ldapurl</center>\n";
575
662
$last_sidx = $sidx;
578
665
# Print the current replica info onthe master
579
print "\n<p><table border=0 cellspacing=1 cellpadding=6 cols=10 width=100% class=bgColor9>\n";
581
print "\n<tr><td colspan=10><center>\n";
582
print "<font class=areatitle>Replica ID: </font>";
583
print "<font class=text28>$serverid</font>\n";
585
print "<font class=areatitle>Replica Root: </font>";
586
print "<font class=text28>$replicaroot</font>\n";
588
print "<font class=areatitle>Max CSN: </font>";
589
print "<font class=text28>$maxcsn</font>\n";
667
print "Replica ID: $serverid\n";
668
print "Replica Root: $replicaroot\n";
669
print "Max CSN: $maxcsn\n";
671
print "\n<p><table border=0 cellspacing=1 cellpadding=6 cols=10 width=100% class=bgColor9>\n";
672
print "\n<tr><td colspan=10><center>\n";
673
print "<font class=areatitle>Replica ID: </font>";
674
print "<font class=text28>$serverid</font>\n";
675
print "<font class=areatitle>Replica Root: </font>";
676
print "<font class=text28>$replicaroot</font>\n";
677
print "<font class=areatitle>Max CSN: </font>";
678
print "<font class=text28>$maxcsn</font>\n";
597
686
my ($myruv) = $allruvs {"$ridx:$mid"};
598
687
my ($maxcsnval) = split ( /;/, "$myruv" );
599
688
my ($maxcsn) = &to_string_csn ($maxcsnval);
600
my ($sidx, $replicaroot, $replicatype, $serverid) = split (/:/, $allreplicas[$ridx]);
689
my ($sidx, $last_sidx, $replicaroot, $replicatype, $serverid) = split (/:/, $allreplicas[$ridx]);
602
691
# Print the master name
603
692
if ( $last_sidx != $sidx ) {
604
693
my ($ldapurl) = &get_ldap_url ($sidx, $sidx);
605
694
&print_legend if ( $last_sidx < 0);
606
print "<p><p><hr><p>\n";
607
print "\n<p><center class=page-subtitle><font color=#0099cc>\n";
608
print "Hub:  $ldapurl</center>\n";
696
print "Hub: $ldapurl\n";
698
print "<p><p><hr><p>\n";
699
print "\n<p><center class=page-subtitle><font color=#0099cc>\n";
700
print "Hub:  $ldapurl</center>\n";
609
702
$last_sidx = $sidx;
612
705
# Print the current replica info onthe master
613
print "\n<p><table border=0 cellspacing=1 cellpadding=6 cols=10 width=100% class=bgColor9>\n";
615
print "\n<tr><td colspan=10><center>\n";
616
print "<font class=areatitle>Replica ID: </font>";
617
print "<font class=text28>$serverid</font>\n";
619
print "<font class=areatitle>Replica Root: </font>";
620
print "<font class=text28>$replicaroot</font>\n";
622
print "<font class=areatitle>Max CSN: </font>";
623
print "<font class=text28>$maxcsn</font>\n";
707
print "Replica ID: $serverid\n";
708
print "Replica Root: $replicaroot\n";
709
print "Max CSN: $maxcsn\n";
711
print "\n<p><table border=0 cellspacing=1 cellpadding=6 cols=10 width=100% class=bgColor9>\n";
712
print "\n<tr><td colspan=10><center>\n";
713
print "<font class=areatitle>Replica ID: </font>";
714
print "<font class=text28>$serverid</font>\n";
715
print "<font class=areatitle>Replica Root: </font>";
716
print "<font class=text28>$replicaroot</font>\n";
717
print "<font class=areatitle>Max CSN: </font>";
718
print "<font class=text28>$maxcsn</font>\n";
628
723
sub print_consumer_header
725
if($opt_s) { return; } # we'll do the text printing in "print_consumers"
630
727
#Print the header of consumer
631
728
print "\n<tr class=bgColor16>\n";
632
729
print "<th nowrap>Receiver</th>\n";
648
745
my ($m_ridx, $mid) = @_;
649
746
my ($ignore, $m_replicaroot) = split (/:/, $allreplicas[$m_ridx]);
650
747
my (@consumers, @ouragreements, @myagreements);
651
my ($s_ridx, $c_ridx, $conntype, $schedule, $status);
652
my ($c_maxcsn_str, $lag, $markcolor);
748
my ($s_ridx, $c_ridx, $s_sidx, $conntype, $schedule, $status);
749
my ($c_maxcsn, $c_maxcsn_str, $c_lastmodified, $c_sidx, $lag, $markcolor);
653
750
my ($c_replicaroot, $c_replicatype);
751
my ($first_entry, $s_ldapurl, $c_ldapurl);
658
755
undef @ouragreements;
756
$c_lastmodified = "";
660
758
# Collect all the consumer replicas for the current master replica
661
759
push (@consumers, $m_ridx);
662
760
foreach (@consumers) {
719
817
$s_ldapurl = &get_ldap_url ($s_sidx, "n/a");
721
819
# Print out the consumer's replica and ruvs
722
print "\n<tr class=bgColor13>\n";
820
if(!$opt_s){ print "\n<tr class=bgColor13>\n"; }
723
821
if ($first_entry) {
724
822
$first_entry = 0;
725
823
$c_ldapurl = &get_ldap_url ($c_sidx, $conntype);
726
print "<td rowspan=$nrows width=5% class=bgColor5>$c_ldapurl<BR>Type: $c_replicatype</td>\n";
727
print "<td rowspan=$nrows width=5% nowrap bgcolor=$markcolor><center>$lag</center></td>\n";
728
print "<td rowspan=$nrows width=15% nowrap>$c_maxcsn_str</td>\n";
729
print "<td rowspan=$nrows width=15% nowrap>$c_lastmodified</td>\n";
731
print "<td width=5% nowrap><center>$s_ldapurl</center></td>\n";
825
print "Receiver: $c_ldapurl\nType: $c_replicatype\n";
826
print "Time Lag: $lag\n";
827
print "Max CSN: $c_maxcsn_str\n";
828
print "Last Modify Time: $c_lastmodified\n";
830
print "<td rowspan=$nrows width=5% class=bgColor5>$c_ldapurl<BR>Type: $c_replicatype</td>\n";
831
print "<td rowspan=$nrows width=5% nowrap bgcolor=$markcolor><center>$lag</center></td>\n";
832
print "<td rowspan=$nrows width=15% nowrap>$c_maxcsn_str</td>\n";
833
print "<td rowspan=$nrows width=15% nowrap>$c_lastmodified</td>\n";
837
print "Supplier: $s_ldapurl\n";
839
print "<td width=5% nowrap><center>$s_ldapurl</center></td>\n";
732
841
my $changecount = $_->{nsds5replicaChangesSentSinceStartup};
733
842
if ( $changecount =~ /^$mid:(\d+)\/(\d+) / || $changecount =~ / $mid:(\d+)\/(\d+) / ) {
734
843
$changecount = "$1 / $2";
753
866
$redfontend = "</font>";
756
print "<td width=20% nowrap>$redfontstart$status$redfontend</td>\n";
757
print "<td nowrap>", &format_z_time($_->{nsds5replicaLastUpdateStart}), "</td>\n";
758
print "<td nowrap>", &format_z_time($_->{nsds5replicaLastUpdateEnd}), "</td>\n";
870
print "Update Status: $status\n";
871
print "Update Started: ", &format_z_time($_->{nsds5replicaLastUpdateStart}), "\n";
872
print "Update Ended: ", &format_z_time($_->{nsds5replicaLastUpdateEnd}), "\n";
874
print "<td width=20% nowrap>$redfontstart$status$redfontend</td>\n";
875
print "<td nowrap>", &format_z_time($_->{nsds5replicaLastUpdateStart}), "</td>\n";
876
print "<td nowrap>", &format_z_time($_->{nsds5replicaLastUpdateEnd}), "</td>\n";
759
878
if ( $schedule =~ /always/i ) {
760
print "<td colspan=2 width=10% nowrap>$schedule</td>\n";
880
print "Schedule: $schedule\n";
882
print "<td colspan=2 width=10% nowrap>$schedule</td>\n";
763
886
my ($ndays, @days);
764
887
$schedule =~ /(\d\d)(\d\d)-(\d\d)(\d\d) (\d+)/;
765
print "<td width=10% nowrap>$1:$2-$3:$4</td>\n";
889
print "Schedule: $1:$2-$3:$4 ";
891
print "<td width=10% nowrap>$1:$2-$3:$4</td>\n";
767
894
$ndays =~ s/(\d)/$1,/g;
768
@days = (Sun,Mon,Tue,Wed,Thu,Fri,Sat)[eval $ndays];
769
print "<td width=10% nowrap>@days</td>\n";
771
print "<td width=3% nowrap class=bgColor5>$conntype</td>\n";
895
@days = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat")[eval $ndays];
899
print "<td width=10% nowrap>@days</td>\n";
903
print "SSL: $conntype\n";
905
print "<td width=3% nowrap class=bgColor5>$conntype</td>\n";
857
992
my (@myconfig, $h, $p, $d, $w, $c);
858
(@myconfig = grep (/^$hostnode($domainpattern)*:$port\D/i, @allconnections)) ||
993
$h = ""; $p = ""; $d = ""; $w = ""; $c = "";
994
(@myconfig = grep (/^$hostnode($domainpattern)*:[0-9]+\D/i, @allconnections)) ||
859
995
(@myconfig = grep (/^$hostnode($domainpattern)*:\*:/i, @allconnections)) ||
860
996
(@myconfig = grep (/^\*:$port\D/, @allconnections)) ||
861
997
(@myconfig = grep (/^\*:\*\D/, @allconnections));
862
998
if ($#myconfig >= 0) {
863
999
($h, $p, $d, $w, $c) = split (/:/, $myconfig[0]);
864
1000
($p, $shadowport) = split (/=/, $p);
865
$p = "" if $p eq "*";
866
$c = "" if $c eq "*";
1001
if(!$p || $p eq "*"){
1004
if(!$c || $c eq "*"){
1007
if(!$w || $w eq "*"){
868
if ($binddn eq "" || $binddn eq "*") {
1011
if (!$binddn || $binddn eq "" || $binddn eq "*") {
869
1012
if ($d eq "" || $d eq "*") {
870
1013
$binddn = "cn=Directory Manager";
970
1160
sub print_html_header
972
# print the HTML header
974
print "Content-type: text/html\n\n";
975
print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\"><html>\n";
976
print "<head><title>Replication Status</title>\n";
977
# print "<link type=text/css rel=stylesheet href=\"master-style.css\">\n";
978
print "<style text/css>\n";
979
print "Body, p, table, td, ul, li {color: #000000; font-family: Arial, Helvetica, sans-serif; font-size: 12px;}\n";
980
print "A {color:blue; text-decoration: none;}\n";
981
print "BODY {font-family: arial, helvetica, sans-serif}\n";
982
print "P {font-family: arial, helvetica, sans-serif}\n";
983
print "TH {font-weight: bold; font-family: arial, helvetica, sans-serif}\n";
984
print "TD {font-family: arial, helvetica, sans-serif}\n";
985
print ".bgColor1 {background-color: #003366;}\n";
986
print ".bgColor4 {background-color: #cccccc;}\n";
987
print ".bgColor5 {background-color: #999999;}\n";
988
print ".bgColor9 {background-color: #336699;}\n";
989
print ".bgColor13 {background-color: #ffffff;}\n";
990
print ".bgColor16 {background-color: #6699cc;}\n";
991
print ".text8 {color: #0099cc; font-size: 11px; font-weight: bold;}\n";
992
print ".text28 {color: #ffcc33; font-size: 12px; font-weight: bold;}\n";
993
print ".areatitle {font-weight: bold; color: #ffffff; font-family: arial, helvetica, sans-serif}\n";
994
print ".page-title {font-weight: bold; font-size: larger; font-family: arial, helvetica, sans-serif}\n";
995
print ".page-subtitle {font-weight: bold; font-family: arial, helvetica, sans-serif}\n";
997
print "</style></head>\n<body class=bgColor4>\n";
1000
print "<meta http-equiv=refresh content=$interval; URL=$opt_u>\n";
1003
print "<table border=0 cellspacing=0 cellpadding=10 width=100% class=bgColor1>\n";
1004
print "<tr><td><font class=text8>$now</font></td>\n";
1005
print "<td align=center class=page-title><font color=#0099CC>";
1006
print "Directory Server Replication Status</font>\n";
1009
print "<br><font class=text8>(This page updates every $interval seconds)</font>\n";
1012
print "</td><td align=right valign=center width=25%><font class=text8>$version";
1013
print "</font></td></table>\n";
1163
# print the HTML header
1165
print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\"><html>\n";
1166
print "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">\n";
1167
print "<head><title>Replication Status</title>\n";
1168
# print "<link type=text/css rel=stylesheet href=\"master-style.css\">\n";
1169
print "<style text/css>\n";
1170
print "Body, p, table, td, ul, li {color: #000000; font-family: Arial, Helvetica, sans-serif; font-size: 12px;}\n";
1171
print "A {color:blue; text-decoration: none;}\n";
1172
print "BODY {font-family: arial, helvetica, sans-serif}\n";
1173
print "P {font-family: arial, helvetica, sans-serif}\n";
1174
print "TH {font-weight: bold; font-family: arial, helvetica, sans-serif}\n";
1175
print "TD {font-family: arial, helvetica, sans-serif}\n";
1176
print ".bgColor1 {background-color: #003366;}\n";
1177
print ".bgColor4 {background-color: #cccccc;}\n";
1178
print ".bgColor5 {background-color: #999999;}\n";
1179
print ".bgColor9 {background-color: #336699;}\n";
1180
print ".bgColor13 {background-color: #ffffff;}\n";
1181
print ".bgColor16 {background-color: #6699cc;}\n";
1182
print ".text8 {color: #0099cc; font-size: 11px; font-weight: bold;}\n";
1183
print ".text28 {color: #ffcc33; font-size: 12px; font-weight: bold;}\n";
1184
print ".areatitle {font-weight: bold; color: #ffffff; font-family: arial, helvetica, sans-serif}\n";
1185
print ".page-title {font-weight: bold; font-size: larger; font-family: arial, helvetica, sans-serif}\n";
1186
print ".page-subtitle {font-weight: bold; font-family: arial, helvetica, sans-serif}\n";
1187
print "</style></head>\n<body class=bgColor4>\n";
1190
print "<meta http-equiv=refresh content=$interval; URL=$opt_u>\n";
1193
print "<table border=0 cellspacing=0 cellpadding=10 width=100% class=bgColor1>\n";
1194
print "<tr><td><font class=text8>$now</font></td>\n";
1195
print "<td align=center class=page-title><font color=#0099CC>";
1196
print "Directory Server Replication Status</font>\n";
1199
print "<br><font class=text8>(This page updates every $interval seconds)</font>\n";
1202
print "</td><td align=right valign=center width=25%><font class=text8>$version";
1203
print "</font></td></table>\n";
1205
print "Directory Server Replication Status ($version)\n\n";
1208
print " - This report updates every $interval seconds\n\n";
1016
1215
sub print_legend
1018
1217
my ($nlegends) = $#colorkeys + 1;
1218
if($opt_s){ return; }
1019
1219
print "\n<center><p><font class=page-subtitle color=#0099cc>Time Lag Legend:</font><p>\n";
1020
1220
print "<table cellpadding=6 cols=$nlegends width=40%>\n<tr>\n";