2013-11-14 23:11:16 -07:00
|
|
|
#!/usr/bin/perl
|
2015-01-18 12:36:49 -07:00
|
|
|
=for commnet
|
|
|
|
|
2015-03-09 17:39:04 -06:00
|
|
|
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
2015-01-18 12:36:49 -07:00
|
|
|
Copyright (C) 2015 Conrad Lara
|
|
|
|
See Contributors file for additional contributors
|
|
|
|
|
|
|
|
Copyright (c) 2013 David Rivenburg et al. BroadBand-HamNet
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation version 3 of the License.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2015-03-09 17:39:04 -06:00
|
|
|
Additional Terms:
|
|
|
|
|
|
|
|
Additional use restrictions exist on the AREDN(TM) trademark and logo.
|
|
|
|
See AREDNLicense.txt for more info.
|
|
|
|
|
|
|
|
Attributions to the AREDN Project must be retained in the source code.
|
|
|
|
If importing this code into a new or existing project attribution
|
|
|
|
to the AREDN project must be added to the source code.
|
|
|
|
|
|
|
|
You must not misrepresent the origin of the material conained within.
|
|
|
|
|
|
|
|
Modified versions must be modified to attribute to the original source
|
|
|
|
and be marked in reasonable ways as differentiate it from the original
|
|
|
|
version.
|
|
|
|
|
2015-01-18 12:36:49 -07:00
|
|
|
=cut
|
2013-11-14 23:11:16 -07:00
|
|
|
|
|
|
|
$debug = 0;
|
|
|
|
|
2013-12-02 13:04:54 -07:00
|
|
|
BEGIN {push @INC, '/www/cgi-bin'};
|
2013-11-14 23:11:16 -07:00
|
|
|
use perlfunc;
|
|
|
|
|
|
|
|
# collect some variables
|
|
|
|
$node = nvram_get("node");
|
|
|
|
$node = "NOCALL" if $node eq "";
|
|
|
|
$tactical = nvram_get("tactical");
|
|
|
|
$config = nvram_get("config");
|
|
|
|
$config = "not set" if $config eq "" or not -d "/etc/config.$config";
|
2013-12-02 13:04:54 -07:00
|
|
|
($my_ip) = get_ip4_network("wlan0");
|
2013-11-14 23:11:16 -07:00
|
|
|
|
|
|
|
read_postdata();
|
|
|
|
|
|
|
|
system "mkdir -p /tmp/web";
|
|
|
|
system "touch /tmp/web/automesh" if $parms{auto};
|
|
|
|
system "rm -f /tmp/web/automesh" if $parms{stop};
|
|
|
|
|
|
|
|
# parse the txtinfo output
|
|
|
|
|
|
|
|
$table = "none";
|
|
|
|
chomp($tmperr = `mktemp /tmp/web/nc.XXXXXX`);
|
|
|
|
|
|
|
|
foreach(`echo /all | nc 127.0.0.1 2006 2>$tmperr`)
|
|
|
|
{
|
|
|
|
if(/^Table: (\w+)/)
|
|
|
|
{
|
|
|
|
$table = $1;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
|
|
|
next if /^\s*$/ or /^\D/;
|
|
|
|
|
|
|
|
if($table eq "Links")
|
|
|
|
{
|
|
|
|
($junk, $ip, $junk, $lq) = split /\s+/, $_;
|
|
|
|
$links{$ip} = $lq;
|
|
|
|
}
|
|
|
|
elsif($table eq "Neighbors")
|
|
|
|
{
|
|
|
|
}
|
|
|
|
elsif($table eq "Topology")
|
|
|
|
{
|
|
|
|
}
|
|
|
|
elsif($table eq "HNA")
|
|
|
|
{
|
|
|
|
}
|
|
|
|
elsif($table eq "MID")
|
|
|
|
{
|
|
|
|
}
|
|
|
|
elsif($table eq "Routes")
|
|
|
|
{
|
|
|
|
($ip, $junk, $junk, $etx) = split /\s+/, $_;
|
|
|
|
($net, $cidr) = split /\//, $ip;
|
|
|
|
$routes{$net}{cidr} = $cidr;
|
|
|
|
$routes{$net}{etx} = $etx;
|
|
|
|
$routes{$net}{value} = ip2decimal($net);
|
|
|
|
$routes{$net}{mask} = 0xffffffff - ((1 << (32 - $cidr)) - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# stat and -s do not work in microperl
|
|
|
|
@parts = split /\s+/, `ls -l $tmperr`;
|
|
|
|
$txtinfo_err = $parts[4];
|
|
|
|
unlink $tmperr;
|
|
|
|
|
|
|
|
# load the local hosts file
|
|
|
|
foreach(`cat /etc/hosts`)
|
|
|
|
{
|
|
|
|
next unless /^10[.]/;
|
|
|
|
chomp;
|
|
|
|
($ip, $name, $tactical) = split /\s+/, $_;
|
2014-06-23 20:42:20 -06:00
|
|
|
next if $name =~ /^(localhost|localnode|localap|dtdlink\..*)$/;
|
2013-11-14 23:11:16 -07:00
|
|
|
if($ip eq $my_ip)
|
|
|
|
{
|
|
|
|
$tactical = "" unless $tactical;
|
|
|
|
$localhosts{$ip}{tactical} = $tactical;
|
|
|
|
$localhosts{$ip}{name} = $name;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
push @{$localhosts{$my_ip}{hosts}}, $name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# load the olsr hosts file
|
|
|
|
foreach(`cat /var/run/hosts_olsr 2>/dev/null`)
|
|
|
|
{
|
|
|
|
next unless /^\d/;
|
|
|
|
chomp;
|
|
|
|
($ip, $name, $junk, $originator, $mid, $midnum) = split /\s+/, $_;
|
|
|
|
next unless $originator;
|
|
|
|
next if $originator eq "myself";
|
2014-06-23 20:42:20 -06:00
|
|
|
if ( $name =~ /^dtdlink\..*$/ )
|
|
|
|
{
|
|
|
|
$hosts{$ip}{name} = $name;
|
|
|
|
next;
|
|
|
|
}
|
2013-11-14 23:11:16 -07:00
|
|
|
|
|
|
|
if(defined $mid and $midnum =~ /^\#(\d+)/)
|
|
|
|
{
|
2014-06-23 20:42:20 -06:00
|
|
|
if ( !exists $hosts{$ip}{name} )
|
|
|
|
{
|
|
|
|
$hosts{$ip}{name} = $name;
|
|
|
|
}
|
2013-11-14 23:11:16 -07:00
|
|
|
$hosts{$ip}{hide} = 1;
|
|
|
|
$hosts{$originator}{mid} = $1;
|
|
|
|
}
|
|
|
|
elsif($ip eq $originator)
|
|
|
|
{
|
|
|
|
if($hosts{$ip}{name}) { $hosts{$ip}{tactical} = $name }
|
|
|
|
else { $hosts{$ip}{name} = $name }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
push @{$hosts{$originator}{hosts}}, $name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# load the olsr services file
|
|
|
|
foreach(`cat /var/run/services_olsr 2>/dev/null`)
|
|
|
|
{
|
|
|
|
next unless /^\w/;
|
|
|
|
chomp;
|
|
|
|
($url, $junk, $name) = split /\|/, $_;
|
|
|
|
next unless defined $name;
|
|
|
|
($host, $port) = $url =~ /^\w+:\/\/([\w\-\.]+):(\d+)/;
|
|
|
|
($name, $originator) = split /\#/, $name;
|
|
|
|
$name =~ s/\s+$//;
|
|
|
|
#$host = $originator eq " my own service" ? $node : $hosts{$originator}{name};
|
|
|
|
|
|
|
|
# attempt to work around olsr never forgetting defunct services
|
|
|
|
# assume that the first entry in the file by this name is the most recent, ignore the rest
|
|
|
|
next if $services{$host}{$name};
|
|
|
|
|
|
|
|
$services{$host}{$name} = $port ? "<a href='$url'>$name</a>" : $name;
|
|
|
|
}
|
|
|
|
|
|
|
|
# load the node history
|
|
|
|
foreach(`cat /tmp/node.history 2>/dev/null`)
|
|
|
|
{
|
|
|
|
chomp;
|
|
|
|
($ip, $age, $host) = split / /, $_;
|
|
|
|
next unless $age;
|
|
|
|
$host = "" unless $host;
|
|
|
|
$host =~ s|/| / |;
|
|
|
|
$history{$ip}{age} = $age;
|
|
|
|
$history{$ip}{host} = $host;
|
|
|
|
}
|
|
|
|
|
|
|
|
#delete $hosts{"127.0.0.1"};
|
|
|
|
|
|
|
|
|
|
|
|
# generate the page
|
|
|
|
http_header();
|
|
|
|
html_header("$node mesh status", 0);
|
|
|
|
print "<meta http-equiv='refresh' content='10;url=/cgi-bin/mesh'>\n" if -f "/tmp/web/automesh";
|
|
|
|
print "</head>\n";
|
|
|
|
print "<body><form method=post action=/cgi-bin/mesh enctype='multipart/form-data'>\n";
|
|
|
|
print "<input type=hidden name=reload value=1>\n";
|
|
|
|
print "<center>\n";
|
|
|
|
|
2013-12-11 23:54:27 -07:00
|
|
|
alert_banner();
|
|
|
|
|
2013-11-14 23:11:16 -07:00
|
|
|
# page header
|
|
|
|
print "<h1>$node mesh status</h1><hr><nobr>";
|
|
|
|
|
|
|
|
# nav buttons
|
|
|
|
if(-f "/tmp/web/automesh")
|
|
|
|
{
|
|
|
|
print "<input type=submit name=stop value=Stop title='Abort continuous status'>\n";
|
|
|
|
}
|
|
|
|
elsif($config eq "mesh")
|
|
|
|
{
|
|
|
|
print "<input type=submit name=refresh value=Refresh title='Refresh this page'>\n";
|
|
|
|
print " \n";
|
|
|
|
print "<input type=submit name=auto value=Auto title='Automatic page refresh'>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
print "  ";
|
|
|
|
print "<button type=button onClick='window.location=\"status\"' title='Return to the status page'>Quit</button>\n";
|
|
|
|
|
|
|
|
if($config ne "mesh")
|
|
|
|
{
|
|
|
|
print "<br><br><b>This page is available only when the router is acting as a Mesh Node.</b>\n";
|
|
|
|
print "</center></form></body></html>\n";
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
elsif($txtinfo_err)
|
|
|
|
{
|
|
|
|
print "<br><br><b>Whoops! OLSR is not running, try again later.</b>\n";
|
|
|
|
print "</center></form></body></html>\n";
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
print "</nobr><br><br>\n";
|
|
|
|
|
|
|
|
unless(keys %localhosts or keys %links)
|
|
|
|
{
|
|
|
|
print "No other nodes are available.\n";
|
|
|
|
print "</center></form></body></html\n";
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
print "<table><tr><td valign=top><table>\n";
|
|
|
|
|
|
|
|
|
|
|
|
# show local hosts
|
|
|
|
|
|
|
|
print "<tr><th colspan=4 align=left><nobr>Local Hosts</nobr></th><th align=left>Services</th></tr>\n";
|
|
|
|
print "<tr><td colspan=5><hr></td></tr>\n";
|
|
|
|
|
|
|
|
if(keys %localhosts)
|
|
|
|
{
|
|
|
|
%rows = ();
|
|
|
|
|
|
|
|
foreach $ip (keys %localhosts)
|
|
|
|
{
|
|
|
|
$host = $localhosts{$ip}{name};
|
|
|
|
$tactical = $localhosts{$ip}{tactical} ? " / " . $localhosts{$ip}{tactical} : "";
|
|
|
|
$rows{$host} = sprintf "<tr><td valign=top><nobr>%s</nobr></td><td colspan=3> </td><td>\n", $host . $tactical;
|
|
|
|
foreach(sort keys %{$services{$host}})
|
|
|
|
{
|
|
|
|
$rows{$host} .= "<nobr>" . $services{$host}{$_} . "</nobr><br>\n";
|
|
|
|
}
|
|
|
|
$rows{$host} .= "</td></tr>\n";
|
|
|
|
|
|
|
|
# add locally advertised dmz hosts
|
|
|
|
foreach $dmzhost (@{$localhosts{$ip}{hosts}})
|
|
|
|
{
|
|
|
|
$rows{$host} .= "<tr><td valign=top><nobr> <img src='/dot.png'>$dmzhost</nobr></td>";
|
|
|
|
$rows{$host} .= "<td colspan=3></td><td>\n";
|
|
|
|
foreach(sort keys %{$services{$dmzhost}})
|
|
|
|
{
|
|
|
|
$rows{$host} .= "<nobr>" . $services{$dmzhost}{$_} . "</nobr><br>\n";
|
|
|
|
}
|
|
|
|
$rows{$host} .= "</td></tr>\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach(sort keys %rows) { print $rows{$_} }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
print "<tr><td>none</td></tr>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# show remote nodes
|
|
|
|
|
|
|
|
print "<tr><td> </td></tr>\n";
|
|
|
|
print "<tr><th align=left><nobr>Remote Nodes</nobr></th><th> </th><th>ETX</th><th> </th><th align=left>Services</th></tr>\n";
|
|
|
|
print "<tr><td colspan=5><hr></td></tr>\n";
|
|
|
|
|
|
|
|
%rows = ();
|
|
|
|
foreach $ip (keys %hosts)
|
|
|
|
{
|
|
|
|
next if $links{$ip};
|
|
|
|
next if exists $hosts{$ip}{hide};
|
|
|
|
$host = $hosts{$ip}{name};
|
|
|
|
$tactical = $hosts{$ip}{tactical} ? " / " . $hosts{$ip}{tactical} : "";
|
|
|
|
$etx = "unknown";
|
|
|
|
|
|
|
|
if($routes{$ip}) # get the etx directly
|
|
|
|
{
|
|
|
|
$etx = sprintf "%.2f", $routes{$ip}{etx};
|
|
|
|
}
|
|
|
|
else # find the subnet it belongs to
|
|
|
|
{
|
|
|
|
foreach $net (keys %routes)
|
|
|
|
{
|
|
|
|
next unless (ip2decimal($ip) & $routes{$net}{mask}) == $routes{$net}{value};
|
|
|
|
$etx = sprintf "%.2f", $routes{$net}{etx};
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
next if $etx eq "unknown";
|
|
|
|
$rows{$host} = sprintf "<tr><td valign=top><nobr><a href='http://%s:8080/'>%s</a>", $host, $host . $tactical;
|
|
|
|
if($midnum = $hosts{$ip}{mid}) # show mid interfaces
|
|
|
|
{
|
|
|
|
$midnum = ($midnum > 1) ? "*$midnum" : "";
|
|
|
|
$rows{$host} .= " <small>(mid$midnum)</small>";
|
|
|
|
}
|
|
|
|
$rows{$host} .= sprintf "</nobr></td><td></td><td align=right valign=top>%s</td><td></td><td>\n", $etx;
|
|
|
|
foreach(sort keys %{$services{$host}})
|
|
|
|
{
|
|
|
|
$rows{$host} .= "<nobr>" . $services{$host}{$_} . "</nobr><br>\n";
|
|
|
|
}
|
|
|
|
$rows{$host} .= "</td></tr>\n";
|
|
|
|
|
|
|
|
# add advertised dmz hosts
|
|
|
|
foreach $dmzhost (@{$hosts{$ip}{hosts}})
|
|
|
|
{
|
|
|
|
$rows{$host} .= "<tr><td valign=top><nobr> <img src='/dot.png'>$dmzhost</nobr></td>";
|
|
|
|
$rows{$host} .= "<td colspan=3></td><td>\n";
|
|
|
|
foreach(sort keys %{$services{$dmzhost}})
|
|
|
|
{
|
|
|
|
$rows{$host} .= "<nobr>" . $services{$dmzhost}{$_} . "</nobr><br>\n";
|
|
|
|
}
|
|
|
|
$rows{$host} .= "</td></tr>\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(keys %rows)
|
|
|
|
{
|
|
|
|
foreach(sort keys %rows) { print $rows{$_} }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
print "<tr><td>none</td></tr>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
print "</table></td><td width=20> </td><td valign=top><table>\n";
|
|
|
|
|
|
|
|
|
|
|
|
# show current neighbors
|
|
|
|
|
|
|
|
print "<tr><th align=left><nobr>Current Neighbors</nobr></th><th> </th><th>LQ</th><th> </th><th align=left>Services</th></tr>\n";
|
|
|
|
print "<tr><td colspan=5><hr></td></tr>\n";
|
|
|
|
|
|
|
|
if(keys %links)
|
|
|
|
{
|
|
|
|
%rows = ();
|
|
|
|
|
|
|
|
foreach $ip (keys %links)
|
|
|
|
{
|
|
|
|
$host = $hosts{$ip}{name} ? $hosts{$ip}{name} : $ip;
|
|
|
|
$tactical = $hosts{$ip}{tactical} ? " / " . $hosts{$ip}{tactical} : "";
|
|
|
|
$rows{$host} = sprintf "<tr><td valign=top><nobr><a href='http://%s:8080/'>%s</a>", $host, $host . $tactical;
|
|
|
|
if($midnum = $hosts{$ip}{mid}) # show mid interfaces
|
|
|
|
{
|
|
|
|
$midnum = ($midnum > 1) ? "*$midnum" : "";
|
|
|
|
$rows{$host} .= " <small>(mid$midnum)</small>";
|
|
|
|
}
|
|
|
|
|
|
|
|
$rows{$host} .= sprintf "</nobr></td><td></td><td align=right valign=top>%.0f%%</td><td></td><td>\n", 100*$links{$ip};
|
|
|
|
foreach(sort keys %{$services{$host}}) { $rows{$host} .= "<nobr>" . $services{$host}{$_} . "</nobr><br>\n" }
|
|
|
|
$rows{$host} .= "</td></tr>\n";
|
|
|
|
|
|
|
|
# add advertised dmz hosts
|
|
|
|
foreach $dmzhost (@{$hosts{$ip}{hosts}})
|
|
|
|
{
|
|
|
|
$rows{$host} .= "<tr><td valign=top><nobr> <img src='/dot.png'>$dmzhost</nobr></td><td colspan=3></td><td>\n";
|
|
|
|
foreach(sort keys %{$services{$dmzhost}}) { $rows{$host} .= $services{$dmzhost}{$_} . "<br>\n" }
|
|
|
|
$rows{$host} .= "</td></tr>\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach(sort keys %rows) { print $rows{$_} }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
print "<tr><td>none</td></tr>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# show previous neighbors
|
|
|
|
|
|
|
|
print "<tr><td> </td></tr>\n";
|
|
|
|
print "<tr><th colspan=4 align=left><nobr>Previous Neighbors</nobr></th><th align=left>When</th></tr>\n";
|
|
|
|
print "<tr><td colspan=5><hr></td></tr>\n";
|
|
|
|
%rows = ();
|
|
|
|
($uptime) = `cat /proc/uptime` =~ /^(\d+)/;
|
|
|
|
|
|
|
|
foreach $ip (keys %history)
|
|
|
|
{
|
|
|
|
next if $links{$ip};
|
|
|
|
$age = sprintf "%010d", $uptime - $history{$ip}{age};
|
|
|
|
$host = $history{$ip}{host} ? $history{$ip}{host} : $ip;
|
|
|
|
$rows{$age} .= sprintf "<tr><td colspan=4><nobr>%s</nobr>", $host;
|
|
|
|
foreach(@{$hosts{$ip}{hosts}}) { $rows{$age} .= "<br><nobr><img src='/dot.png'>$_</nobr>" }
|
|
|
|
$rows{$age} .= "</td><td valign=top><nobr>";
|
|
|
|
if($age < 3600)
|
|
|
|
{
|
|
|
|
$val = int($age/60);
|
|
|
|
$rows{$age} .= $val == 1 ? "1 minute ago" : "$val minutes ago";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$val = sprintf "%.1f", $age/3660;
|
|
|
|
$rows{$age} .= $val eq "1.0" ? "1 hour ago" : "$val hours ago";
|
|
|
|
}
|
|
|
|
$rows{$age} .= "</nobr></td></tr>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
if(keys %rows)
|
|
|
|
{
|
|
|
|
foreach(sort keys %rows) { print $rows{$_} }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
print "<tr><td>none</td></tr>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
print "</table></td></tr></table>\n";
|
|
|
|
|
|
|
|
|
|
|
|
# end
|
|
|
|
print "</center>\n";
|
|
|
|
print "</form>\n";
|
|
|
|
|
|
|
|
if($debug)
|
|
|
|
{
|
|
|
|
print "<pre>\n";
|
|
|
|
print "localhosts\n";
|
|
|
|
foreach $ip (sort keys %localhosts)
|
|
|
|
{
|
|
|
|
printf "%s %s", $ip, $localhosts{$ip}{name};
|
|
|
|
printf "/%s", $localhosts{$ip}{tactical} if $localhosts{$ip}{tactical};
|
|
|
|
foreach(@{$localhosts{$ip}{hosts}}) { print ":$_" }
|
|
|
|
print "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
print "\nhosts\n";
|
|
|
|
foreach $ip (sort keys %hosts)
|
|
|
|
{
|
|
|
|
$hosts{$ip}{name} = "" unless $hosts{$ip}{name};
|
|
|
|
printf "%s %s", $ip, $hosts{$ip}{name};
|
|
|
|
printf "/%s", $hosts{$ip}{tactical} if $hosts{$ip}{tactical};
|
|
|
|
foreach(@{$hosts{$ip}{hosts}}) { print ":$_" }
|
|
|
|
printf(" %d", $hosts{$ip}{mid}) if $hosts{$ip}{mid};
|
|
|
|
print "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
print "\nlinks\n";
|
|
|
|
foreach(sort keys %links)
|
|
|
|
{
|
|
|
|
print "$_\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
print "</pre>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
show_debug_info();
|
|
|
|
show_parse_errors();
|
|
|
|
|
|
|
|
print "</body>\n";
|
|
|
|
print "</html>\n";
|
|
|
|
|