mirror of https://github.com/aredn/aredn.git
Lua userpages (#190)
* Lua Services * Lua versions of user page (non-admin) * Support multiple antenna chains * Improved led detection * Fix logging * Add manager.log files to support tool * Lua versions of user page (non-admin) * Dont count default route * Fix board id for older nodes * Handle missing node.history * Check /tmp/snrlog exists * Per discussion, make the Lua pages the defaults and Perl the backups
This commit is contained in:
parent
fa6c2da4fe
commit
610cde651c
|
@ -95,7 +95,7 @@ function hardware.get_board_id()
|
|||
if not name or name == "" or name == "0x0000" then
|
||||
name = hardware.get_board().model.name
|
||||
end
|
||||
return name
|
||||
return name:chomp()
|
||||
end
|
||||
|
||||
function hardware.get_board_type()
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,680 @@
|
|||
#!/usr/bin/perl
|
||||
=for comment
|
||||
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
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/>.
|
||||
|
||||
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 contained 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.
|
||||
|
||||
=cut
|
||||
|
||||
$debug = 0;
|
||||
|
||||
BEGIN {push @INC, '/www/cgi-bin'};
|
||||
use perlfunc;
|
||||
use ucifunc;
|
||||
|
||||
%rateL = (
|
||||
'1.0M' => '1', # CCP LP
|
||||
'2.0M' => '2', # CCP LP
|
||||
'5.5M' => '5.5', # CCP LP
|
||||
'MCS0' => '6.5', # HT20 LGI
|
||||
'11.0M'=> '11', # CCP LP
|
||||
'MCS1' => '13', # HT20 LGI...
|
||||
'MCS2' => '19.5',
|
||||
'MCS3' => '26',
|
||||
'MCS4' => '39',
|
||||
'MCS5' => '52',
|
||||
'MCS6' => '58.5',
|
||||
'MCS7' => '65',
|
||||
'MCS8' => '13',
|
||||
'MCS9' => '26',
|
||||
'MCS10' => '39',
|
||||
'MCS11' => '52',
|
||||
'MCS12' => '78',
|
||||
'MCS13' => '104',
|
||||
'MCS14' => '117',
|
||||
'MCS15' => '130',
|
||||
);
|
||||
|
||||
%rateS = (
|
||||
'MCS0' => '7.2',
|
||||
'MCS1' => '14.4',
|
||||
'MCS2' => '21.7',
|
||||
'MCS3' => '28.9',
|
||||
'MCS4' => '43.3',
|
||||
'MCS5' => '57.8',
|
||||
'MCS6' => '65',
|
||||
'MCS7' => '72.2',
|
||||
'MCS8' => '14.4',
|
||||
'MCS9' => '28.9',
|
||||
'MCS10' => '43.3',
|
||||
'MCS11' => '57.8',
|
||||
'MCS12' => '86.7',
|
||||
'MCS13' => '115.6',
|
||||
'MCS14' => '130',
|
||||
'MCS15' => '144.4',
|
||||
);
|
||||
|
||||
# Limit displayed nodes and services to the most reachable routes if memory on the node is small
|
||||
%lowMemoryLimits = (
|
||||
memory => `/sbin/uci -q get aredn.\@meshstatus[0].lowmem` || 10000,
|
||||
routes => `/sbin/uci -q get aredn.\@meshstatus[0].lowroutes` || 1000,
|
||||
);
|
||||
|
||||
sub get_available_mem
|
||||
{
|
||||
foreach(`free`)
|
||||
{
|
||||
next unless /^Mem[:]/;
|
||||
my @tmp = split /\s+/, $_;
|
||||
return $tmp[6];
|
||||
}
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
# 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 "" || not -d "/etc/config.mesh";
|
||||
($my_ip) = get_ip4_network(get_interface("wifi"));
|
||||
${wifiif} = `uci -q get 'network.wifi.ifname'`;
|
||||
chomp ${wifiif};
|
||||
$phy = get_wlan2phy("${wifiif}");
|
||||
|
||||
chomp ($chanbw = `cat /sys/kernel/debug/ieee80211/${phy}/ath9k/chanbw`);
|
||||
if ($chanbw eq "0x00000005") {$chanbw = 4;}
|
||||
elsif ($chanbw eq "0x0000000a") {$chanbw = 2;}
|
||||
else {$chanbw = 1;}
|
||||
|
||||
read_postdata();
|
||||
|
||||
system "mkdir -p /tmp/web";
|
||||
system "touch /tmp/web/automesh" if $parms{auto};
|
||||
system "rm -f /tmp/web/automesh" if $parms{stop};
|
||||
|
||||
#get location info if available
|
||||
$lat=&uci_get_indexed_option("aredn","location",0,"lat");
|
||||
$lon=&uci_get_indexed_option("aredn","location",0,"lon");
|
||||
if($lat ne "" and $lon ne "")
|
||||
{
|
||||
$lat_lon = "<center><strong>Location: </strong> $lat $lon</center>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$lat_lon = "<strong>Location Not Available</strong>";
|
||||
}
|
||||
@route30 = `/sbin/ip route list table 30`;
|
||||
$olsrTotal = scalar @route30;
|
||||
$olsrNodes = scalar grep /\//, @route30;
|
||||
$node_desc = `/sbin/uci -q get system.\@system[0].description`; #pull the node description from uci
|
||||
undef @route30;
|
||||
|
||||
# parse the txtinfo output
|
||||
|
||||
chomp($tmperr = `mktemp /tmp/web/nc.XXXXXX`);
|
||||
|
||||
foreach(`echo /rou | nc 127.0.0.1 2006 2>>$tmperr`)
|
||||
{
|
||||
next if /^\D/;
|
||||
my ($ip, $junk, $junk, $etx) = split /\s+/, $_;
|
||||
my ($net, $cidr) = split /\//, $ip;
|
||||
if ( $etx <= 50 ) { $routes{$net}{etx} = $etx; }
|
||||
}
|
||||
if ($olsrTotal > $lowMemoryLimits{routes} && get_available_mem() < $lowMemoryLimits{memory})
|
||||
{
|
||||
my @oroutes = sort { $routes{$a}{etx} <=> $routes{$b}{etx} } keys %routes;
|
||||
foreach ( @oroutes[$lowMemoryLimits{routes} .. $#oroutes] )
|
||||
{
|
||||
delete $routes{$_};
|
||||
}
|
||||
}
|
||||
@arps = {};
|
||||
open my $fh, '<', '/proc/net/arp';
|
||||
foreach (<$fh>) { push @arps, $_ if (/$wifiif/ && !/00:00:00:00:00:00/) }
|
||||
close $fh;
|
||||
foreach(`echo /lin | nc 127.0.0.1 2006 2>>$tmperr`)
|
||||
{
|
||||
next if /^\D/;
|
||||
my ($junk, $ip, $junk, $lq, $nlq) = split /\s+/, $_;
|
||||
$links{$ip} = {
|
||||
lq => $lq,
|
||||
nlq => $nlq,
|
||||
mbps => ""
|
||||
};
|
||||
$neighbor{$ip} = 1;
|
||||
|
||||
my ($mac) = grep /^$ip/, @arps;
|
||||
$mac =~ s/^.*(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w).*$/$1/;
|
||||
chomp $mac;
|
||||
|
||||
open my $fh, '<', "/sys/kernel/debug/ieee80211/${phy}/netdev:${wifiif}/stations/$mac/rc_stats_csv";
|
||||
if ( $mac && $fh )
|
||||
{
|
||||
my @csv = <$fh>;
|
||||
close $fh;
|
||||
#802.11b/n
|
||||
my ($mbps) = grep /^([^,]*,){3}A/, @csv;
|
||||
if ($mbps)
|
||||
{
|
||||
my ($gi, $dummy, $rate, $dummy, $ewma) = $mbps =~ /^[^,]*,([^,]*),([^,]*,){2}([^,]*),([^,]*,){4}([^,]*).*$/ ;
|
||||
$rate =~ s/[ \t]//g;
|
||||
$mbps = $gi eq "SGI" ? $rateS{$rate}*$ewma/100 : $rateL{$rate}*$ewma/100 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
#802.11a/b/g
|
||||
($mbps) = grep /^A/, @csv;
|
||||
if ($mbps)
|
||||
{
|
||||
$mbps =~ /^[^,]*,([^,]*),([^,]*,){4}([^,]*).*$/;
|
||||
$mbps = $1*$3/100;
|
||||
}
|
||||
}
|
||||
$links{$ip}{mbps} = $mbps ? sprintf "%.1f", $mbps / $chanbw : "0.0";
|
||||
}
|
||||
}
|
||||
undef @arps;
|
||||
foreach(`echo /hna | nc 127.0.0.1 2006 2>>$tmperr`)
|
||||
{
|
||||
next if /^\D/;
|
||||
my ($iproute, $ip) = split /\s+/, $_;
|
||||
my ($net, $cidr) = split /\//, $iproute;
|
||||
if ( $net eq "0.0.0.0" ) { $wangateway{$ip} = 1; }
|
||||
}
|
||||
foreach(`echo /mid | nc 127.0.0.1 2006 2>>$tmperr`)
|
||||
{
|
||||
next if /^\D/;
|
||||
my ($ip, $junk) = $_ =~ /^(\S+)\s+(.*)$/;
|
||||
foreach $aip ( split /\s+/, $junk )
|
||||
{
|
||||
$ipalias{$aip} = $ip;
|
||||
$neighbor{$aip} = 1;
|
||||
if ( $links{$aip} ) { $neighbor{$ip} = 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
|
||||
|
||||
open my $fh, '<', '/etc/hosts';
|
||||
foreach (<$fh>)
|
||||
{
|
||||
next unless /^10[.]/;
|
||||
chomp;
|
||||
my ($ip, $name, $tactical) = split /\s+/, $_;
|
||||
next if $name =~ /^(localhost|localnode|localap|dtdlink\..*)$/;
|
||||
if ( $name !~ /\./ ) { $name="${name}.local.mesh"; }
|
||||
if($ip eq $my_ip)
|
||||
{
|
||||
$tactical = "" unless $tactical;
|
||||
$localhosts{$ip}{tactical} = $tactical;
|
||||
$localhosts{$ip}{name} = $name;
|
||||
}
|
||||
else { push @{$localhosts{$my_ip}{hosts}}, $name; }
|
||||
if($tactical eq "#NOPROP") { push @{$localhosts{$my_ip}{noprops}}, $name; }
|
||||
if($tactical eq "#ALIAS") { push @{$localhosts{$my_ip}{aliases}}, $name; }
|
||||
}
|
||||
close $fh;
|
||||
|
||||
# load the olsr hosts file
|
||||
open my $fh, '<', '/var/run/hosts_olsr';
|
||||
foreach (<$fh>)
|
||||
{
|
||||
next unless /^\d/;
|
||||
chomp;
|
||||
my ($ip, $name, undef, $originator, undef, undef) = split /\s+/, $_;
|
||||
next unless $originator;
|
||||
next if $originator eq "myself";
|
||||
# Filter hosts which are unreachable
|
||||
next unless $routes{$ip} || $routes{$originator};
|
||||
|
||||
my $etx = $routes{$ip}{etx} ? $routes{$ip}{etx} : $routes{$originator}{etx};
|
||||
|
||||
if (( $name !~ /\./ ) || ( $name =~ /^mid\.[^\.]*$/ )) { $name="${name}.local.mesh"; }
|
||||
|
||||
if ( $ip eq $originator )
|
||||
{
|
||||
if ($hosts{$ip}{name})
|
||||
{
|
||||
$hosts{$ip}{tactical} = $name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$hosts{$ip}{name} = $name;
|
||||
$hosts{$ip}{etx} = $etx;
|
||||
}
|
||||
}
|
||||
elsif ( $name =~ /^dtdlink\..*$/ )
|
||||
{
|
||||
$dtd{$originator} = 1;
|
||||
if ( $links{$ip} ) { $links{$ip}{dtd} = 1 }
|
||||
}
|
||||
elsif ( $name =~ /^mid\d+\..*$/ )
|
||||
{
|
||||
$midcount{$originator} = $midcount{$originator} ? $midcount{$originator}+1: 1 ;
|
||||
if ( $links{$ip} ) { $links{$ip}{tun} = 1 }
|
||||
}
|
||||
else
|
||||
{
|
||||
push @{$hosts{$originator}{hosts}}, $name;
|
||||
}
|
||||
}
|
||||
close $fh;
|
||||
|
||||
# Discard
|
||||
undef %routes;
|
||||
|
||||
# load the olsr services file
|
||||
open my $fh, '<', '/var/run/services_olsr';
|
||||
foreach (<$fh>)
|
||||
{
|
||||
next unless /^\w/;
|
||||
chomp;
|
||||
my ($url, $junk, $name) = split /\|/, $_;
|
||||
next unless defined $name;
|
||||
my ($protocol, $host, $port, $path) = $url =~ /^(\w+):\/\/([\w\-\.]+):(\d+)\/(.*)/;
|
||||
next unless defined $path;
|
||||
my ($name, $originator) = split /\#/, $name;
|
||||
|
||||
# Filter services for unreachable hosts
|
||||
next unless $hosts{$originator}{name} || $originator eq " my own service";
|
||||
|
||||
$name =~ s/\s+$//;
|
||||
|
||||
if ( $host !~ /\./ ) { $host="${host}.local.mesh"; }
|
||||
|
||||
# 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='${protocol}://${host}:${port}/${path}' target='_blank'>$name</a>" : $name;
|
||||
}
|
||||
close $fh;
|
||||
|
||||
# load the node history
|
||||
open my $fh, '<', '/tmp/node.history';
|
||||
foreach (<$fh>)
|
||||
{
|
||||
chomp;
|
||||
($ip, $age, $host) = split / /, $_;
|
||||
next unless $age;
|
||||
$host = "" unless $host;
|
||||
$host =~ s|/| / |;
|
||||
$history{$ip}{age} = $age;
|
||||
$history{$ip}{host} = $host;
|
||||
}
|
||||
close $fh;
|
||||
|
||||
#delete $hosts{"127.0.0.1"};
|
||||
|
||||
# compress the output if we can
|
||||
if ( $ENV{HTTP_ACCEPT_ENCODING} =~ /gzip/ )
|
||||
{
|
||||
print "Content-type: text/html\r\nCache-Control: no-store\r\nContent-Encoding: gzip\r\n\r\n";
|
||||
open my $zout, "|gzip";
|
||||
select $zout;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "Content-type: text/html\r\nCache-Control: no-store\r\n\r\n";
|
||||
}
|
||||
|
||||
# generate the page
|
||||
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";
|
||||
|
||||
alert_banner();
|
||||
|
||||
# page header
|
||||
print "<h1>$node mesh status</h1>";
|
||||
|
||||
print "$lat_lon"; #display lat lon info
|
||||
print "<table id='node_description_display'><tr><td>$node_desc</td></tr></table>" if $node_desc; #display node description
|
||||
print "<hr><nobr>";
|
||||
|
||||
# nav buttons
|
||||
if(-f "/tmp/web/automesh")
|
||||
{
|
||||
print "<input type=submit name=stop value=Stop title='Abort continuous status'>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
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($txtinfo_err)
|
||||
{
|
||||
print "<br><br><b>Whoops! OLSR is not running, try again later.</b>\n";
|
||||
print "</center></form>";
|
||||
page_footer();
|
||||
print "</body></html>\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
print "</nobr><br><br>\n";
|
||||
|
||||
unless(keys %localhosts || keys %links)
|
||||
{
|
||||
print "No other nodes are available.\n";
|
||||
print "</center></form>";
|
||||
page_footer();
|
||||
print "</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)
|
||||
{
|
||||
my %rows = ();
|
||||
|
||||
foreach $ip (keys %localhosts)
|
||||
{
|
||||
my $host = $localhosts{$ip}{name};
|
||||
my $localpart = $host =~ s/.local.mesh//r;
|
||||
my $tactical = $localhosts{$ip}{tactical} ? " / " . $localhosts{$ip}{tactical} : "";
|
||||
$rows{$host} = sprintf "<tr><td valign=top><nobr>%s</nobr>", $localpart . $tactical;
|
||||
|
||||
if ( $wangateway{$ip} ) { $nodeiface = "wan" ; }
|
||||
if ( $nodeiface ) { $rows{$host} .= " <small>($nodeiface)</small>"; }
|
||||
|
||||
$rows{$host} .= "</td><td colspan=3> </td><td>\n" ;
|
||||
|
||||
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}})
|
||||
{
|
||||
#find non-propagated and aliased hosts and change color
|
||||
my $nopropd = 0;
|
||||
my $aliased = 0;
|
||||
if(grep { /$dmzhost/ } @{$localhosts{$ip}{noprops}}) { $nopropd = 1; }
|
||||
if(grep { /$dmzhost/ } @{$localhosts{$ip}{aliases}}) { $aliased = 1; }
|
||||
$localpart = $dmzhost =~ s/.local.mesh//r;
|
||||
if(!$nopropd && !$aliased) { $rows{$host} .= "<tr><td valign=top><nobr> <img src='/dot.png'>$localpart</nobr></td>"; }
|
||||
elsif($aliased) { $rows{$host} .= "<tr><td class=aliased-hosts valign=top title='Aliased Host'><nobr> <img src='/dot.png'>$localpart</nobr></td>"; }
|
||||
else { $rows{$host} .= "<tr><td class=hidden-hosts valign=top title='Non Propagated Host'><nobr> <img src='/dot.png'>$localpart</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";
|
||||
|
||||
my $row;
|
||||
foreach $ip (sort { $hosts{$a}{etx} <=> $hosts{$b}{etx} } keys %hosts)
|
||||
{
|
||||
next if $neighbor{$ip};
|
||||
my $host = $hosts{$ip}{name};
|
||||
next unless $host;
|
||||
my $localpart = $host =~ s/.local.mesh//r;
|
||||
my $tactical = $hosts{$ip}{tactical} ? " / " . $hosts{$ip}{tactical} : "";
|
||||
my $etx = sprintf "%.2f", $hosts{$ip}{etx};
|
||||
|
||||
$row = sprintf "<tr><td valign=top><nobr><a href='http://%s:8080/'>%s</a>", $host, $localpart . $tactical;
|
||||
|
||||
my $nodeiface;
|
||||
my $mcount = 0 + $midcount{$ip};
|
||||
if ( $dtd{$ip} ) { $mcount -= 1; } # extra mid entry matching and with dtdlink in hosts_olsrd
|
||||
if ( $hosts{$ip}{tactical} ) { $mcount -= 1; } # extra mid entry if tactical name defined
|
||||
if ( $mcount > 0 ) { $nodeiface = "tun*$mcount" ; }
|
||||
if ( $wangateway{$ip} ) { $nodeiface = $nodeiface ? $nodeiface . ",wan" : "wan" ; }
|
||||
|
||||
if ( $nodeiface ) { $row .= " <small>($nodeiface)</small>"; }
|
||||
|
||||
$row .= sprintf "</nobr></td><td></td><td align=right valign=top>%s</td><td></td><td>\n", $etx;
|
||||
foreach(sort keys %{$services{$host}})
|
||||
{
|
||||
$row .= "<nobr>" . $services{$host}{$_} . "</nobr><br>\n";
|
||||
}
|
||||
$row .= "</td></tr>\n";
|
||||
|
||||
# add advertised dmz hosts
|
||||
foreach $dmzhost (@{$hosts{$ip}{hosts}})
|
||||
{
|
||||
my $localpart = $dmzhost =~ s/.local.mesh//r;
|
||||
$row .= "<tr><td valign=top><nobr> <img src='/dot.png'>$localpart</nobr></td>";
|
||||
$row .= "<td colspan=3></td><td>\n";
|
||||
foreach(sort keys %{$services{$dmzhost}})
|
||||
{
|
||||
$row .= "<nobr>" . $services{$dmzhost}{$_} . "</nobr><br>\n";
|
||||
}
|
||||
$row .= "</td></tr>\n";
|
||||
}
|
||||
print $row;
|
||||
}
|
||||
|
||||
undef %neighbor;
|
||||
|
||||
if(!$row)
|
||||
{
|
||||
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>NLQ</th><th>TxMbps</th><th> </th><th align=left>Services</th></tr>\n";
|
||||
print "<tr><td colspan=7><hr></td></tr>\n";
|
||||
|
||||
if(keys %links)
|
||||
{
|
||||
my %rows = ();
|
||||
|
||||
foreach $ip (keys %links)
|
||||
{
|
||||
my $ipmain = exists $ipalias{$ip} ? $ipalias{$ip} : $ip ;
|
||||
my $host = $hosts{$ipmain}{name} ? $hosts{$ipmain}{name} : $ipmain;
|
||||
my $localpart = $host =~ s/.local.mesh//r;
|
||||
my $tactical = $hosts{$ipmain}{tactical} ? " / " . $hosts{$ipmain}{tactical} : "";
|
||||
if ( $rows{$host} ) { $host .= " " ; } # avoid collision 2 links to same host {rf, dtd}
|
||||
|
||||
my $no_space_host=$host;
|
||||
$no_space_host =~ s/\s+$//;
|
||||
my $row = sprintf "<tr><td valign=top><nobr><a href='http://%s:8080/'>%s</a>", $no_space_host, $localpart . $tactical;
|
||||
|
||||
my $nodeiface;
|
||||
if ( $ipmain ne $ip ) # indicate if dtd or tunnel interface to neighbor
|
||||
{
|
||||
if ( $links{$ip}{dtd} ){ $nodeiface="dtd" ; }
|
||||
elsif ( $links{$ip}{tun} ){ $nodeiface="tun" ; }
|
||||
else { $nodeiface="?" ; }
|
||||
}
|
||||
|
||||
if ( $wangateway{$ip} || $wangateway{$ipmain} ) { $nodeiface = $nodeiface ? $nodeiface . ",wan" : "wan" ; }
|
||||
if ( $nodeiface ) { $row .= " <small>($nodeiface)</small>"; }
|
||||
|
||||
$row .= sprintf ("</nobr></td><td></td><td align=right valign=top>%.0f%%</td><td align=right valign=top>%.0f%%</td><td align=right valign=top>%s</td><td></td><td>\n", 100*$links{$ip}{lq}, 100*$links{$ip}{nlq},$links{$ip}{mbps});
|
||||
|
||||
if ( ! exists $neighservices{$host} )
|
||||
{
|
||||
foreach(sort keys %{$services{$host}}) { $row .= "<nobr>" . $services{$host}{$_} . "</nobr><br>\n" }
|
||||
|
||||
$row .= "</td></tr>\n";
|
||||
|
||||
# add advertised dmz hosts
|
||||
foreach $dmzhost (@{$hosts{$ipmain}{hosts}})
|
||||
{
|
||||
my $localpart = $dmzhost =~ s/.local.mesh//r;
|
||||
$row .= "<tr><td valign=top><nobr> <img src='/dot.png'>$localpart</nobr></td><td colspan=5></td><td>\n";
|
||||
foreach(sort keys %{$services{$dmzhost}}) { $row .= $services{$dmzhost}{$_} . "<br>\n" }
|
||||
$row .= "</td></tr>\n";
|
||||
}
|
||||
$neighservices{$host}=1;
|
||||
}
|
||||
|
||||
$rows{$host}=$row;
|
||||
}
|
||||
|
||||
foreach(sort keys %rows) { print $rows{$_} }
|
||||
}
|
||||
else
|
||||
{
|
||||
print "<tr><td>none</td></tr>\n";
|
||||
}
|
||||
|
||||
undef %services;
|
||||
|
||||
# show previous neighbors
|
||||
|
||||
print "<tr><td> </td></tr>\n";
|
||||
print "<tr><th colspan=6 align=left><nobr>Previous Neighbors</nobr></th><th align=left>When</th></tr>\n";
|
||||
print "<tr><td colspan=7><hr></td></tr>\n";
|
||||
%rows = ();
|
||||
($uptime) = `cat /proc/uptime` =~ /^(\d+)/;
|
||||
|
||||
foreach $ip (keys %history)
|
||||
{
|
||||
next if $links{$ip};
|
||||
next if $links{$ipalias{$ip}};
|
||||
$age = sprintf "%010d", $uptime - $history{$ip}{age};
|
||||
$host = $history{$ip}{host} ? $history{$ip}{host} : $ip;
|
||||
$host =~ s/^mid\d+\.// ;
|
||||
$host =~ s/^dtdlink\.// ;
|
||||
$rows{$age} .= sprintf "<tr><td colspan=6><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 "<tr><td> </td></tr>\n";
|
||||
print "<tr><th align='left'>OLSR Entries</th></tr>\n";
|
||||
print "<tr><td colspan=7><hr></td></tr>\n";
|
||||
print "<tr><td>Total</td><td> </td><td align='right'>$olsrTotal</td></tr>\n";
|
||||
print "<tr><td>Nodes</td><td> </td><td align='right'>$olsrNodes</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 "\nlinks\n";
|
||||
foreach(sort keys %links)
|
||||
{
|
||||
print "$_\n";
|
||||
}
|
||||
|
||||
print "</pre>\n";
|
||||
}
|
||||
|
||||
show_debug_info();
|
||||
show_parse_errors();
|
||||
|
||||
page_footer();
|
||||
print "</body>\n";
|
||||
print "</html>\n";
|
||||
|
|
@ -1,98 +1,262 @@
|
|||
#!/usr/bin/perl
|
||||
=for comment
|
||||
#!/usr/bin/lua
|
||||
--[[
|
||||
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
Copyright (C) 2015 Conrad Lara
|
||||
See Contributors file for additional contributors
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
Copyright (C) 2021 Tim Wilkinson
|
||||
Original Perl 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 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.
|
||||
|
||||
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/>.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
Additional Terms:
|
||||
|
||||
Additional Terms:
|
||||
Additional use restrictions exist on the AREDN(TM) trademark and logo.
|
||||
See AREDNLicense.txt for more info.
|
||||
|
||||
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.
|
||||
|
||||
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 contained within.
|
||||
|
||||
You must not misrepresent the origin of the material contained 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
|
||||
|
||||
Modified versions must be modified to attribute to the original source
|
||||
and be marked in reasonable ways as differentiate it from the original
|
||||
version.
|
||||
--]]
|
||||
|
||||
=cut
|
||||
require("aredn.http")
|
||||
require("aredn.hardware")
|
||||
local html = require("aredn.html")
|
||||
local aredn_info = require("aredn.info")
|
||||
|
||||
$debug = 0;
|
||||
local node = aredn_info.get_nvram("node")
|
||||
if not node then
|
||||
node = "NOCALL"
|
||||
end
|
||||
local wifiiface = aredn.hardware.get_iface_name("wifi")
|
||||
|
||||
BEGIN {push @INC, '/www/cgi-bin'};
|
||||
use perlfunc;
|
||||
if not nixio.fs.stat("/tmp/web") then
|
||||
nixio.fs.mkdir("/tmp/web")
|
||||
end
|
||||
|
||||
# collect some variables
|
||||
$node = nvram_get("node");
|
||||
$node = "NOCALL" if $node eq "";
|
||||
-- scan start
|
||||
|
||||
read_postdata();
|
||||
local scanned = {}
|
||||
local f = io.popen("iw dev " .. wifiiface .. " scan passive")
|
||||
if f then
|
||||
local scan
|
||||
for line in f:lines()
|
||||
do
|
||||
local m = line:match("^BSS ([%da-fA-F:]+)")
|
||||
if m then
|
||||
scan = {
|
||||
mac = m,
|
||||
mode = "AP",
|
||||
ssid = "",
|
||||
signal = 0,
|
||||
freq = 0,
|
||||
key = ""
|
||||
}
|
||||
scanned[m] = scan
|
||||
if line:match("joined") then
|
||||
scan.mode = "My Ad-Hoc Network"
|
||||
end
|
||||
end
|
||||
m = line:match("freq: (%d+)")
|
||||
if m then
|
||||
scan.freq = tonumber(m)
|
||||
end
|
||||
m = line:match("SSID: (.+)")
|
||||
if m then
|
||||
scan.ssid = m
|
||||
end
|
||||
m = line:match("signal: ([%d-]+)")
|
||||
if m then
|
||||
scan.signal = tonumber(m)
|
||||
end
|
||||
m = line:match("Group cipher: (.+)")
|
||||
if m then
|
||||
scan.key = m
|
||||
end
|
||||
if line:match("capability: IBSS") and scan.mode == "AP" then
|
||||
scan.mode = "Foreign Ad-Hoc Network"
|
||||
end
|
||||
end
|
||||
if scan then
|
||||
scanned[#scanned + 1] = scan
|
||||
end
|
||||
f:close()
|
||||
end
|
||||
local f = io.popen("iw dev " .. wifiiface .. " station dump")
|
||||
if f then
|
||||
local scan
|
||||
local myssid = aredn_info.getSSID()
|
||||
local myfreq = tonumber(aredn_info.getFreq())
|
||||
for line in f:lines()
|
||||
do
|
||||
local m = line:match("^Station ([%da-fA-F:]+) %(on " .. wifiiface .. "%)")
|
||||
if m then
|
||||
scan = scanned[m]
|
||||
if not scan then
|
||||
scan = {
|
||||
mac = m,
|
||||
mode = "Connected Ad-Hoc Station",
|
||||
ssid = myssid,
|
||||
signal = 0,
|
||||
freq = myfreq,
|
||||
key = ""
|
||||
}
|
||||
scanned[m] = scan
|
||||
else
|
||||
scan.mode = "Connected Ad-Hoc Station"
|
||||
scan.ssid = myssid
|
||||
scan.key = ""
|
||||
scan.freq = myfreq
|
||||
end
|
||||
end
|
||||
m = line:match("signal avg:%s+([%d-]+)")
|
||||
if m then
|
||||
scan.signal = tonumber(m)
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
end
|
||||
|
||||
system "mkdir -p /tmp/web";
|
||||
system "/usr/local/bin/wscan -w > /tmp/web/wscan.next" unless $parms{stop};
|
||||
system "touch /tmp/web/autoscan" if $parms{auto};
|
||||
system "rm -f /tmp/web/autoscan" if $parms{stop};
|
||||
system "mv /tmp/web/wscan.next /tmp/web/wscan" unless $parms{stop};
|
||||
-- scan end
|
||||
|
||||
# generate the page
|
||||
http_header();
|
||||
html_header("$node WiFi scan", 0);
|
||||
print "<meta http-equiv='refresh' content='5;url=/cgi-bin/scan'>\n" if -f "/tmp/web/autoscan";
|
||||
print "<script src=\"/js/sorttable-min.js\"></script>";
|
||||
print "<style>
|
||||
table.sortable thead {
|
||||
background-color:#eee;
|
||||
color:#666666;
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
}
|
||||
</style>";
|
||||
print "</head>\n";
|
||||
print "<body><form method=post action=/cgi-bin/scan enctype='multipart/form-data'>\n";
|
||||
print "<center>\n";
|
||||
alert_banner();
|
||||
print "<h1>$node WiFi scan</h1><hr>\n";
|
||||
if os.getenv("REQUEST_METHOD") == "POST" then
|
||||
require('luci.http')
|
||||
require('luci.sys')
|
||||
local request = luci.http.Request(luci.sys.getenv(),
|
||||
function()
|
||||
local v = io.read(1024)
|
||||
if not v then
|
||||
io.close()
|
||||
end
|
||||
return v
|
||||
end
|
||||
)
|
||||
if request:formvalue("auto") then
|
||||
io.open("/tmp/web/autoscan", "w"):close()
|
||||
end
|
||||
if request:formvalue("stop") then
|
||||
os.remove("/tmp/web/autoscan")
|
||||
end
|
||||
end
|
||||
|
||||
if(-f "/tmp/web/autoscan")
|
||||
{
|
||||
print "<input type=submit name=stop value=Stop title='Abort continuous scan'>\n";
|
||||
}
|
||||
-- generate page
|
||||
http_header()
|
||||
html.header(node .. " WiFi scan", false)
|
||||
local autoscan = nixio.fs.stat("/tmp/web/autoscan");
|
||||
if autoscan then
|
||||
html.print("<meta http-equiv='refresh' content='5;url=/cgi-bin/scan'>")
|
||||
end
|
||||
html.print([[
|
||||
<script src=\"/js/sorttable-min.js\"></script>
|
||||
<style>
|
||||
table.sortable thead {
|
||||
background-color:#eee;
|
||||
color:#666666;
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body><form method=post action=/cgi-bin/scan enctype='multipart/form-data'>
|
||||
<center>
|
||||
]])
|
||||
|
||||
html.alert_banner()
|
||||
html.print("<h1>" .. node .. " WiFi scan</h1><hr>")
|
||||
|
||||
if autoscan then
|
||||
html.print("<input type=submit name=stop value=Stop title='Abort continuous scan'>")
|
||||
else
|
||||
{
|
||||
print "<input type=submit name=refresh value=Refresh title='Refresh this page'>\n";
|
||||
print " \n";
|
||||
print "<input type=submit name=auto value=Auto title='Begin continuous scan'>\n";
|
||||
}
|
||||
html.print("<input type=submit name=refresh value=Refresh title='Refresh this page'>")
|
||||
html.print(" ")
|
||||
html.print("<input type=submit name=auto value=Auto title='Begin continuous scan'>")
|
||||
end
|
||||
|
||||
print " \n";
|
||||
print "<button type=button onClick='window.location=\"status\"' title='Return to status page'>Quit</button><br><br>\n";
|
||||
system "cat /tmp/web/wscan";
|
||||
print "<br>";
|
||||
print "</center>\n";
|
||||
print "</form>\n";
|
||||
html.print(" ")
|
||||
html.print("<button type=button onClick='window.location=\"status\"' title='Return to status page'>Quit</button><br><br>")
|
||||
|
||||
show_debug_info();
|
||||
show_parse_errors();
|
||||
-- display scan
|
||||
html.print("<table class=sortable border=1 cellpadding=5>")
|
||||
html.print("<tr><th>Sig</th><th>Chan</th><th>Enc</th><th>SSID</th><th>Hostname</th><th>MAC/BSSID</th><th>802.11 Mode</th></tr>")
|
||||
|
||||
page_footer();
|
||||
print "</body>\n";
|
||||
print "</html>\n";
|
||||
-- load arp cache
|
||||
local arpcache = {}
|
||||
arptable(function(a)
|
||||
arpcache[a["HW address"]] = a["IP address"]
|
||||
end)
|
||||
|
||||
local scanlist = {}
|
||||
for _, v in pairs(scanned)
|
||||
do
|
||||
if v.signal ~= 0 then
|
||||
scanlist[#scanlist + 1] = v
|
||||
end
|
||||
end
|
||||
table.sort(scanlist, function(a, b) return a.signal > b.signal end)
|
||||
for _, scan in ipairs(scanlist)
|
||||
do
|
||||
-- freq to chan
|
||||
local chan = scan.freq
|
||||
if chan < 256 then
|
||||
elseif chan == 2484 then
|
||||
chan = 14
|
||||
elseif chan == 2407 then
|
||||
chan = 0
|
||||
elseif chan < 2484 then
|
||||
chan = (chan - 2407) / 5
|
||||
elseif chan < 5000 then
|
||||
elseif chan < 5380 then
|
||||
chan = (chan - 5000) / 5
|
||||
elseif chan < 5500 then
|
||||
chan = chan - 2000
|
||||
elseif chan < 6000 then
|
||||
chan = (chan - 5000) / 5
|
||||
end
|
||||
-- ip lookup then host lookup
|
||||
local ip = arpcache[scan.mac]
|
||||
if ip then
|
||||
hostname = ip
|
||||
local f = io.popen("nslookup " .. ip)
|
||||
if f then
|
||||
for line in f:lines()
|
||||
do
|
||||
local m = line:match("name = (.*)%.local%.mesh")
|
||||
if m then
|
||||
hostname = m
|
||||
break
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
end
|
||||
else
|
||||
hostname = "N/A"
|
||||
end
|
||||
if scan.ssid:match("^AREDN-") then
|
||||
html.print("<tr class=\"wscan-row-node\">")
|
||||
else
|
||||
html.print("<tr>")
|
||||
end
|
||||
html.print("<td>" .. scan.signal .. "</td><td>" .. chan .. "</td><td>" .. scan.key .. "</td><td>" .. scan.ssid .. "</td><td align=center>" .. hostname .. "</td><td>" .. scan.mac:upper() .. "</td><td>" .. scan.mode .. "</td>")
|
||||
html.print("</tr>")
|
||||
end
|
||||
|
||||
html.print("</table><br></center></form>")
|
||||
|
||||
html.footer()
|
||||
html.print("</body></html>")
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/perl
|
||||
=for comment
|
||||
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
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/>.
|
||||
|
||||
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 contained 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.
|
||||
|
||||
=cut
|
||||
|
||||
$debug = 0;
|
||||
|
||||
BEGIN {push @INC, '/www/cgi-bin'};
|
||||
use perlfunc;
|
||||
|
||||
# collect some variables
|
||||
$node = nvram_get("node");
|
||||
$node = "NOCALL" if $node eq "";
|
||||
|
||||
read_postdata();
|
||||
|
||||
system "mkdir -p /tmp/web";
|
||||
system "/usr/local/bin/wscan -w > /tmp/web/wscan.next" unless $parms{stop};
|
||||
system "touch /tmp/web/autoscan" if $parms{auto};
|
||||
system "rm -f /tmp/web/autoscan" if $parms{stop};
|
||||
system "mv /tmp/web/wscan.next /tmp/web/wscan" unless $parms{stop};
|
||||
|
||||
# generate the page
|
||||
http_header();
|
||||
html_header("$node WiFi scan", 0);
|
||||
print "<meta http-equiv='refresh' content='5;url=/cgi-bin/scan'>\n" if -f "/tmp/web/autoscan";
|
||||
print "<script src=\"/js/sorttable-min.js\"></script>";
|
||||
print "<style>
|
||||
table.sortable thead {
|
||||
background-color:#eee;
|
||||
color:#666666;
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
}
|
||||
</style>";
|
||||
print "</head>\n";
|
||||
print "<body><form method=post action=/cgi-bin/scan enctype='multipart/form-data'>\n";
|
||||
print "<center>\n";
|
||||
alert_banner();
|
||||
print "<h1>$node WiFi scan</h1><hr>\n";
|
||||
|
||||
if(-f "/tmp/web/autoscan")
|
||||
{
|
||||
print "<input type=submit name=stop value=Stop title='Abort continuous scan'>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "<input type=submit name=refresh value=Refresh title='Refresh this page'>\n";
|
||||
print " \n";
|
||||
print "<input type=submit name=auto value=Auto title='Begin continuous scan'>\n";
|
||||
}
|
||||
|
||||
print " \n";
|
||||
print "<button type=button onClick='window.location=\"status\"' title='Return to status page'>Quit</button><br><br>\n";
|
||||
system "cat /tmp/web/wscan";
|
||||
print "<br>";
|
||||
print "</center>\n";
|
||||
print "</form>\n";
|
||||
|
||||
show_debug_info();
|
||||
show_parse_errors();
|
||||
|
||||
page_footer();
|
||||
print "</body>\n";
|
||||
print "</html>\n";
|
|
@ -1,307 +1,305 @@
|
|||
#!/usr/bin/perl
|
||||
=for comment
|
||||
#!/usr/bin/lua
|
||||
--[[
|
||||
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
Copyright (C) 2015 Darryl Quinn
|
||||
See Contributors file for additional contributors
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
Copyright (C) 2021 Tim Wilkinson
|
||||
Original Perl Copyright (C) 2015 Darryl Quinn
|
||||
See Contributors file for additional contributors
|
||||
|
||||
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 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.
|
||||
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/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional Terms:
|
||||
Additional Terms:
|
||||
|
||||
Additional use restrictions exist on the AREDN(TM) trademark and logo.
|
||||
See AREDNLicense.txt for more info.
|
||||
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.
|
||||
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 contained within.
|
||||
You must not misrepresent the origin of the material contained 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.
|
||||
=cut
|
||||
Modified versions must be modified to attribute to the original source
|
||||
and be marked in reasonable ways as differentiate it from the original
|
||||
version
|
||||
|
||||
$debug = 0;
|
||||
--]]
|
||||
|
||||
BEGIN {push @INC, '/www/cgi-bin'};
|
||||
use perlfunc;
|
||||
require("nixio")
|
||||
require("aredn.http")
|
||||
require("aredn.utils")
|
||||
local html = require("aredn.html")
|
||||
local aredn_info = require("aredn.info")
|
||||
|
||||
chomp ($tzone=`date +%Z`);
|
||||
read_query_string();
|
||||
local node = aredn_info.get_nvram("node")
|
||||
if not node then
|
||||
node = "NOCALL"
|
||||
end
|
||||
local device = ""
|
||||
local tzone = capture("date +%Z"):gsub("\n","")
|
||||
|
||||
# collect some variables
|
||||
$node = nvram_get("node");
|
||||
$node = "NOCALL" unless $node;
|
||||
local dmode = "Realtime"
|
||||
-- query string
|
||||
local query = os.getenv("QUERY_STRING")
|
||||
if query then
|
||||
require('luci.http')
|
||||
local params = luci.http.urldecode_params(query)
|
||||
if params.realtime then
|
||||
dmode = "Realtime"
|
||||
else
|
||||
dmode = "Archived"
|
||||
end
|
||||
device = params.device
|
||||
end
|
||||
|
||||
if($parms{"realtime"} )
|
||||
{
|
||||
$dmode="Realtime";
|
||||
} else {
|
||||
$dmode="Archived";
|
||||
}
|
||||
-- get list of files from /tmp/snrlog
|
||||
local snrfiles = {}
|
||||
local devfound = false
|
||||
if nixio.fs.stat("/tmp/snrlog") then
|
||||
for filename in nixio.fs.dir("/tmp/snrlog")
|
||||
do
|
||||
snrfiles[#snrfiles + 1] = filename
|
||||
if device == filename then
|
||||
devfound = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if not devfound then
|
||||
device = ""
|
||||
end
|
||||
|
||||
http_header();
|
||||
html_header("$node $dmode signal strength", 0);
|
||||
http_header()
|
||||
html.header(node .. " " .. dmode .. " signal strength", false)
|
||||
|
||||
$header = <<EOF;
|
||||
<link href="/loading.css" rel="stylesheet">
|
||||
<script src="/js/jquery-2.1.4.min.js"></script>
|
||||
<script src="/js/jquery.canvasjs.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var dps=[[{"label":"Init","y":[-95,-95]}]];
|
||||
local prelude = [[
|
||||
<link href="/loading.css" rel="stylesheet">
|
||||
<script src="/js/jquery-2.1.4.min.js"></script>
|
||||
<script src="/js/jquery.canvasjs.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var dps=[ [{"label":"Init","y":[-95,-95]}] ];
|
||||
|
||||
// Read a page's GET URL variables and return them as an associative array.
|
||||
function getUrlVars()
|
||||
{
|
||||
var vars = [], hash;
|
||||
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
|
||||
for(var i = 0; i < hashes.length; i++)
|
||||
{
|
||||
hash = hashes[i].split('=');
|
||||
vars.push(hash[0]);
|
||||
vars[hash[0]] = hash[1];
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
function hide_spinner() {
|
||||
\$('#content-loading-spinner').dequeue();
|
||||
\$('#content-loading-spinner').hide();
|
||||
\$('#content-overlay').dequeue();
|
||||
\$('#content-overlay').hide();
|
||||
}
|
||||
|
||||
\$(document).ajaxComplete(function() {
|
||||
hide_spinner();
|
||||
});
|
||||
|
||||
\$(document).ready(function () {
|
||||
var MAXPOINTS=10;
|
||||
var chart = new CanvasJS.Chart("chartContainer", {
|
||||
zoomEnabled: true,
|
||||
zoomType: "xy",
|
||||
exportEnabled: true,
|
||||
backgroundColor: "#E7E7E7",
|
||||
title: {
|
||||
text: "$dmode Signal to Noise"
|
||||
},
|
||||
legend: {
|
||||
horizontalAlign: "right", // left, center ,right
|
||||
verticalAlign: "center", // top, center, bottom
|
||||
},
|
||||
axisX: {
|
||||
title: "Time (in $tzone)",
|
||||
labelFontSize: 12,
|
||||
labelAngle: -45,
|
||||
valueFormatString: "MM/DD HH:mm",
|
||||
},
|
||||
axisY: {
|
||||
title: "dBm",
|
||||
interlacedColor: "#F0F8FF",
|
||||
},
|
||||
toolTip: {
|
||||
contentFormatter: function (e) {
|
||||
var content = " ";
|
||||
content += "At " + e.entries[0].dataPoint.timestamp.substring(11, 19) + "<br />";
|
||||
if (e.entries[0].dataPoint.signal_dbm) {
|
||||
content += "Signal: " + e.entries[0].dataPoint.signal_dbm + "dBm<br/>";
|
||||
} else {
|
||||
content += "Signal: 0<br/>";
|
||||
}
|
||||
content += "Noise: " + e.entries[0].dataPoint.noise_dbm + "dBm<br/>";
|
||||
content += "SNR: " + e.entries[0].dataPoint.snr + "dB<br/>";
|
||||
content += "TX Rate: " + e.entries[0].dataPoint.tx_rate_mbps + "Mbps<br/>";
|
||||
content += "TX MCS: " + e.entries[0].dataPoint.tx_rate_mcs_index + "<br/>";
|
||||
content += "RX Rate: " + e.entries[0].dataPoint.rx_rate_mbps + "Mbps<br/>";
|
||||
content += "RX MCS: " + e.entries[0].dataPoint.rx_rate_mcs_index + "<br/>";
|
||||
return content;
|
||||
}
|
||||
},
|
||||
data: [
|
||||
// Read a page's GET URL variables and return them as an associative array.
|
||||
function getUrlVars()
|
||||
{
|
||||
var vars = [], hash;
|
||||
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
|
||||
for(var i = 0; i < hashes.length; i++)
|
||||
{
|
||||
type: "rangeArea",
|
||||
xValueType: "dateTime",
|
||||
showInLegend: true,
|
||||
legendText: "Signal",
|
||||
dataPoints: dps[0]
|
||||
hash = hashes[i].split('=');
|
||||
vars.push(hash[0]);
|
||||
vars[ hash[0] ] = hash[1];
|
||||
}
|
||||
],
|
||||
}); // --- chart
|
||||
|
||||
var formatDataPoint = function(point) {
|
||||
point.label = point.timestamp.substring(5, 10) + " " + point.timestamp.substring(11, 19);
|
||||
point.y = [point.signal_dbm, point.noise_dbm];
|
||||
point.snr = (point.noise_dbm * -1) - (point.signal_dbm * -1);
|
||||
point.tx_rate_mcs_index = point.tx_rate_mcs_index ? point.tx_rate_mcs_index : "N/A";
|
||||
point.rx_rate_mcs_index = point.rx_rate_mcs_index ? point.rx_rate_mcs_index : "N/A";
|
||||
return point;
|
||||
};
|
||||
|
||||
var updateArchiveChart = function () {
|
||||
\$.getJSON("/cgi-bin/api?chart=archive&device=$parms{device}", function (result) {
|
||||
var points = result.pages.chart.archive.map(formatDataPoint);
|
||||
chart.options.data[0].dataPoints = points;
|
||||
if(result.pages.chart.archive.constructor === Array) {
|
||||
chart.render();
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
var updateRealtimeChart = function () {
|
||||
\$.getJSON("/cgi-bin/api?chart=realtime&realtime=1&device=$parms{device}", function (result) {
|
||||
var point = formatDataPoint(result.pages.chart.realtime[0]);
|
||||
dps[0].push(point);
|
||||
chart.render();
|
||||
toneFreq(point.snr);
|
||||
\$('#snr').html(point.snr);
|
||||
});
|
||||
};
|
||||
|
||||
var dmode = getUrlVars()["realtime"];
|
||||
|
||||
if(dmode) {
|
||||
updateRealtimeChart();
|
||||
setInterval(function() {updateRealtimeChart()}, 1000);
|
||||
} else {
|
||||
updateArchiveChart();
|
||||
setInterval(function() {updateArchiveChart()}, 60000);
|
||||
return vars;
|
||||
}
|
||||
chart.render();
|
||||
}); // --- document.ready
|
||||
|
||||
var audioCtx = new(window.AudioContext || window.webkitAudioContext)();
|
||||
var oscillator = audioCtx.createOscillator();
|
||||
var gainNode = audioCtx.createGain();
|
||||
oscillator.connect(gainNode);
|
||||
oscillator.type = 'sine';
|
||||
gainNode.connect(audioCtx.destination);
|
||||
gainNode.gain.value = 1;
|
||||
function hide_spinner() {
|
||||
$('#content-loading-spinner').dequeue();
|
||||
$('#content-loading-spinner').hide();
|
||||
$('#content-overlay').dequeue();
|
||||
$('#content-overlay').hide();
|
||||
}
|
||||
|
||||
function toneFreq(snr) {
|
||||
var p = document.getElementById("tonePitch").value;
|
||||
oscillator.frequency.value = snr * p;
|
||||
var v = document.getElementById("toneVol").value;
|
||||
gainNode.gain.value = v;
|
||||
}
|
||||
$(document).ajaxComplete(function() {
|
||||
hide_spinner();
|
||||
});
|
||||
|
||||
function toneOn() {
|
||||
oscillator = audioCtx.createOscillator();
|
||||
gainNode = audioCtx.createGain();
|
||||
$(document).ready(function () {
|
||||
var MAXPOINTS=10;
|
||||
var chart = new CanvasJS.Chart("chartContainer", {
|
||||
zoomEnabled: true,
|
||||
zoomType: "xy",
|
||||
exportEnabled: true,
|
||||
backgroundColor: "#E7E7E7",
|
||||
title: {
|
||||
text: "$dmode Signal to Noise"
|
||||
},
|
||||
legend: {
|
||||
horizontalAlign: "right", // left, center ,right
|
||||
verticalAlign: "center", // top, center, bottom
|
||||
},
|
||||
axisX: {
|
||||
title: "Time (in $tzone)",
|
||||
labelFontSize: 12,
|
||||
labelAngle: -45,
|
||||
valueFormatString: "MM/DD HH:mm",
|
||||
},
|
||||
axisY: {
|
||||
title: "dBm",
|
||||
interlacedColor: "#F0F8FF",
|
||||
},
|
||||
toolTip: {
|
||||
contentFormatter: function (e) {
|
||||
var content = " ";
|
||||
content += "At " + e.entries[0].dataPoint.timestamp.substring(11, 19) + "<br />";
|
||||
if (e.entries[0].dataPoint.signal_dbm) {
|
||||
content += "Signal: " + e.entries[0].dataPoint.signal_dbm + "dBm<br/>";
|
||||
} else {
|
||||
content += "Signal: 0<br/>";
|
||||
}
|
||||
content += "Noise: " + e.entries[0].dataPoint.noise_dbm + "dBm<br/>";
|
||||
content += "SNR: " + e.entries[0].dataPoint.snr + "dB<br/>";
|
||||
content += "TX Rate: " + e.entries[0].dataPoint.tx_rate_mbps + "Mbps<br/>";
|
||||
content += "TX MCS: " + e.entries[0].dataPoint.tx_rate_mcs_index + "<br/>";
|
||||
content += "RX Rate: " + e.entries[0].dataPoint.rx_rate_mbps + "Mbps<br/>";
|
||||
content += "RX MCS: " + e.entries[0].dataPoint.rx_rate_mcs_index + "<br/>";
|
||||
return content;
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
type: "rangeArea",
|
||||
xValueType: "dateTime",
|
||||
showInLegend: true,
|
||||
legendText: "Signal",
|
||||
dataPoints: dps[0]
|
||||
}
|
||||
],
|
||||
}); // --- chart
|
||||
|
||||
var formatDataPoint = function(point) {
|
||||
point.label = point.timestamp.substring(5, 10) + " " + point.timestamp.substring(11, 19);
|
||||
point.y = [point.signal_dbm, point.noise_dbm];
|
||||
point.snr = (point.noise_dbm * -1) - (point.signal_dbm * -1);
|
||||
point.tx_rate_mcs_index = point.tx_rate_mcs_index ? point.tx_rate_mcs_index : "N/A";
|
||||
point.rx_rate_mcs_index = point.rx_rate_mcs_index ? point.rx_rate_mcs_index : "N/A";
|
||||
return point;
|
||||
};
|
||||
|
||||
var updateArchiveChart = function () {
|
||||
$.getJSON("/cgi-bin/api?chart=archive&device=$parms{device}", function (result) {
|
||||
var points = result.pages.chart.archive.map(formatDataPoint);
|
||||
chart.options.data[0].dataPoints = points;
|
||||
if(result.pages.chart.archive.constructor === Array) {
|
||||
chart.render();
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
var updateRealtimeChart = function () {
|
||||
$.getJSON("/cgi-bin/api?chart=realtime&realtime=1&device=$parms{device}", function (result) {
|
||||
var point = formatDataPoint(result.pages.chart.realtime[0]);
|
||||
dps[0].push(point);
|
||||
chart.render();
|
||||
toneFreq(point.snr);
|
||||
$('#snr').html(point.snr);
|
||||
});
|
||||
};
|
||||
|
||||
var dmode = getUrlVars()["realtime"];
|
||||
|
||||
if(dmode) {
|
||||
updateRealtimeChart();
|
||||
setInterval(function() {updateRealtimeChart()}, 1000);
|
||||
} else {
|
||||
updateArchiveChart();
|
||||
setInterval(function() {updateArchiveChart()}, 60000);
|
||||
}
|
||||
chart.render();
|
||||
}); // --- document.ready
|
||||
|
||||
var audioCtx = new(window.AudioContext || window.webkitAudioContext)();
|
||||
var oscillator = audioCtx.createOscillator();
|
||||
var gainNode = audioCtx.createGain();
|
||||
oscillator.connect(gainNode);
|
||||
oscillator.type = 'sine';
|
||||
gainNode.connect(audioCtx.destination);
|
||||
gainNode.gain.value = .5;
|
||||
oscillator.start();
|
||||
document.getElementById("toneOff").disabled = false;
|
||||
document.getElementById("toneOn").disabled = true;
|
||||
};
|
||||
gainNode.gain.value = 1;
|
||||
|
||||
function toneOff() {
|
||||
document.getElementById("toneOff").disabled = true;
|
||||
document.getElementById("toneOn").disabled = false;
|
||||
oscillator.stop();
|
||||
};
|
||||
function toneFreq(snr) {
|
||||
var p = document.getElementById("tonePitch").value;
|
||||
oscillator.frequency.value = snr * p;
|
||||
var v = document.getElementById("toneVol").value;
|
||||
gainNode.gain.value = v;
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
EOF
|
||||
function toneOn() {
|
||||
oscillator = audioCtx.createOscillator();
|
||||
gainNode = audioCtx.createGain();
|
||||
oscillator.connect(gainNode);
|
||||
oscillator.type = 'sine';
|
||||
gainNode.connect(audioCtx.destination);
|
||||
gainNode.gain.value = .5;
|
||||
oscillator.start();
|
||||
document.getElementById("toneOff").disabled = false;
|
||||
document.getElementById("toneOn").disabled = true;
|
||||
};
|
||||
|
||||
$page = <<EOF;
|
||||
<body>
|
||||
<div class="overlay" id="content-overlay"></div>
|
||||
<div id="content-loading-spinner-wrapper">
|
||||
<div id="content-loading-spinner">
|
||||
<div class="spinner"></div>
|
||||
<div class="loading-text">
|
||||
Loading . . .
|
||||
function toneOff() {
|
||||
document.getElementById("toneOff").disabled = true;
|
||||
document.getElementById("toneOn").disabled = false;
|
||||
oscillator.stop();
|
||||
};
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="overlay" id="content-overlay"></div>
|
||||
<div id="content-loading-spinner-wrapper">
|
||||
<div id="content-loading-spinner">
|
||||
<div class="spinner"></div>
|
||||
<div class="loading-text">
|
||||
Loading . . .
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
<div class="TopBanner">
|
||||
<div class="LogoDiv"><img src="/AREDN.png" class="AREDNLogo"></img></div>
|
||||
</div>
|
||||
<h1><big>$node</big></h1><hr>
|
||||
<nobr>
|
||||
<button onclick="window.location.href='/cgi-bin/signal'">Archive</button>
|
||||
<button onclick="window.location.href='/cgi-bin/signal?realtime=1'">Realtime</button>
|
||||
<button onclick="window.location.href='/cgi-bin/status'">Quit</button>
|
||||
<br />
|
||||
<div id="deviceSelector">
|
||||
<form name="deviceSelector" method="GET" action="/cgi-bin/signal">
|
||||
Selected Device: <select name="device" onChange="this.form.submit();">
|
||||
EOF
|
||||
<center>
|
||||
<div class="TopBanner">
|
||||
<div style="float: right;font-size: 8px;color: grey;">Lua version</div>
|
||||
<div class="LogoDiv"><img src="/AREDN.png" class="AREDNLogo"></img></div>
|
||||
</div>
|
||||
<h1><big>$node</big></h1><hr>
|
||||
<nobr>
|
||||
<button onclick="window.location.href='/cgi-bin/signal'">Archive</button>
|
||||
<button onclick="window.location.href='/cgi-bin/signal?realtime=1'">Realtime</button>
|
||||
<button onclick="window.location.href='/cgi-bin/status'">Quit</button>
|
||||
<br />
|
||||
<div id="deviceSelector">
|
||||
<form name="deviceSelector" method="GET" action="/cgi-bin/signal">
|
||||
Selected Device: <select name="device" onChange="this.form.submit();">
|
||||
]]
|
||||
html.print(prelude:gsub("$dmode", dmode):gsub("$tzone", tzone):gsub("$parms{device}", device):gsub("$node", node))
|
||||
|
||||
# get a list of files from /tmp/snrlog
|
||||
my @files = `ls -1A /tmp/snrlog`;
|
||||
$parms{device}="" if(!/$parms{device}/ ~~ @files);
|
||||
if dmode == "Realtime" and device == "" then
|
||||
html.print("<option selected value='strongest'>Average signal for all connected stations</option>")
|
||||
end
|
||||
|
||||
# default to "Strongest Signal" for Realtime
|
||||
if(! $parms{device} and $parms{realtime}) {
|
||||
$page = $page . "<option selected value='strongest'>Average signal for all connected stations</option>";
|
||||
}
|
||||
local first_sel = true
|
||||
for _, logfile in ipairs(snrfiles)
|
||||
do
|
||||
local dmac, dname = logfile:match("^([%da-f:]+)-(.*)$")
|
||||
if dname == "" then
|
||||
dname = dmac
|
||||
end
|
||||
if device == logfile or (dmode ~= "Realtime" and first_sel) then
|
||||
html.print("<option selected value='" .. logfile .. "'>" .. dname .."</option>")
|
||||
first_sel = false
|
||||
else
|
||||
html.print("<option value='" .. logfile .."'>" .. dname .. "</option>")
|
||||
end
|
||||
end
|
||||
|
||||
$firstSel=1;
|
||||
# iterate over each file
|
||||
foreach $logfile (@files)
|
||||
{
|
||||
chomp($logfile);
|
||||
my ($dmac, $dname) = $logfile =~ /^(.*?)\-(.*)/;
|
||||
$dname=$dmac if($dname eq '');
|
||||
if($parms{device} eq $logfile or (!$parms{realtime} and $firstSel)) {
|
||||
$page = $page . "<option selected value='$logfile'>$dname</option>\n";
|
||||
$firstSel=0;
|
||||
} else {
|
||||
$page = $page . "<option value='$logfile'>$dname</option>\n";
|
||||
}
|
||||
}
|
||||
|
||||
$page = $page . "</select>\n";
|
||||
$page = $page . "<input type='hidden' name='realtime' value='1'></input>\n" if($parms{realtime} eq "1");
|
||||
$page = $page . <<EOF;
|
||||
</form>
|
||||
</div>
|
||||
<div id="snrValues" style="float: left;">
|
||||
EOF
|
||||
$page = $page . "<div><h3>SNR: <span id='snr'>0</span>dB</h3></div>" if($parms{"realtime"} eq "1");
|
||||
if($parms{"realtime"} eq "1") {
|
||||
$page = $page . "<div style='float: right;'>Sound: <button id='toneOn' onclick='toneOn();'>On</button> ";
|
||||
$page = $page . "<button id='toneOff' disabled onclick='toneOff();'>Off</button><br /><br />";
|
||||
$page = $page . "Pitch: <input type='range' id='tonePitch' name='tonePitch' min='5' max='100'></input><br /><br />";
|
||||
$page = $page . "Volume: <input type='range' id='toneVol' name='toneVol' min='0' max='10'></input><br /><br />";
|
||||
$page = $page . "</div>";
|
||||
}
|
||||
|
||||
$page = $page . <<EOF;
|
||||
</div>
|
||||
<div id="chartContainer" style="width: 80%; height: 60%;"></div>
|
||||
</center>
|
||||
EOF
|
||||
|
||||
print $header;
|
||||
print $page;
|
||||
show_debug_info();
|
||||
print "</body></html>";
|
||||
|
||||
sub DEBUGEXIT()
|
||||
{
|
||||
my ($text) = @_;
|
||||
http_header();
|
||||
html_header("$node setup", 1);
|
||||
print "DEBUG-";
|
||||
print $text;
|
||||
print "</body>";
|
||||
exit;
|
||||
}
|
||||
html.print("</select>")
|
||||
if dmode == "Realtime" then
|
||||
html.print("<input type='hidden' name='realtime' value='1'>")
|
||||
html.print("</form></div><div id='snrValues' style='float: left;'>")
|
||||
html.print("<div><h3>SNR: <span id='snr'>0</span>dB</h3></div>")
|
||||
html.print("<div style='float: right;'>Sound: <button id='toneOn' onclick='toneOn();'>On</button> ")
|
||||
html.print("<button id='toneOff' disabled onclick='toneOff();'>Off</button><br /><br />")
|
||||
html.print("Pitch: <input type='range' id='tonePitch' name='tonePitch' min='5' max='100'></input><br /><br />")
|
||||
html.print("Volume: <input type='range' id='toneVol' name='toneVol' min='0' max='10'></input><br /><br />")
|
||||
html.print("</div>")
|
||||
else
|
||||
html.print("</form></div><div id='snrValues' style='float: left;'>")
|
||||
end
|
||||
html.print("</div><div id='chartContainer' style='width: 80%; height: 60%;'></div></center>")
|
||||
html.print("</body></html>")
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
#!/usr/bin/perl
|
||||
=for comment
|
||||
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
Copyright (C) 2015 Darryl Quinn
|
||||
See Contributors file for additional contributors
|
||||
|
||||
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/>.
|
||||
|
||||
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 contained 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.
|
||||
=cut
|
||||
|
||||
$debug = 0;
|
||||
|
||||
BEGIN {push @INC, '/www/cgi-bin'};
|
||||
use perlfunc;
|
||||
|
||||
chomp ($tzone=`date +%Z`);
|
||||
read_query_string();
|
||||
|
||||
# collect some variables
|
||||
$node = nvram_get("node");
|
||||
$node = "NOCALL" unless $node;
|
||||
|
||||
if($parms{"realtime"} )
|
||||
{
|
||||
$dmode="Realtime";
|
||||
} else {
|
||||
$dmode="Archived";
|
||||
}
|
||||
|
||||
http_header();
|
||||
html_header("$node $dmode signal strength", 0);
|
||||
|
||||
$header = <<EOF;
|
||||
<link href="/loading.css" rel="stylesheet">
|
||||
<script src="/js/jquery-2.1.4.min.js"></script>
|
||||
<script src="/js/jquery.canvasjs.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var dps=[[{"label":"Init","y":[-95,-95]}]];
|
||||
|
||||
// Read a page's GET URL variables and return them as an associative array.
|
||||
function getUrlVars()
|
||||
{
|
||||
var vars = [], hash;
|
||||
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
|
||||
for(var i = 0; i < hashes.length; i++)
|
||||
{
|
||||
hash = hashes[i].split('=');
|
||||
vars.push(hash[0]);
|
||||
vars[hash[0]] = hash[1];
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
function hide_spinner() {
|
||||
\$('#content-loading-spinner').dequeue();
|
||||
\$('#content-loading-spinner').hide();
|
||||
\$('#content-overlay').dequeue();
|
||||
\$('#content-overlay').hide();
|
||||
}
|
||||
|
||||
\$(document).ajaxComplete(function() {
|
||||
hide_spinner();
|
||||
});
|
||||
|
||||
\$(document).ready(function () {
|
||||
var MAXPOINTS=10;
|
||||
var chart = new CanvasJS.Chart("chartContainer", {
|
||||
zoomEnabled: true,
|
||||
zoomType: "xy",
|
||||
exportEnabled: true,
|
||||
backgroundColor: "#E7E7E7",
|
||||
title: {
|
||||
text: "$dmode Signal to Noise"
|
||||
},
|
||||
legend: {
|
||||
horizontalAlign: "right", // left, center ,right
|
||||
verticalAlign: "center", // top, center, bottom
|
||||
},
|
||||
axisX: {
|
||||
title: "Time (in $tzone)",
|
||||
labelFontSize: 12,
|
||||
labelAngle: -45,
|
||||
valueFormatString: "MM/DD HH:mm",
|
||||
},
|
||||
axisY: {
|
||||
title: "dBm",
|
||||
interlacedColor: "#F0F8FF",
|
||||
},
|
||||
toolTip: {
|
||||
contentFormatter: function (e) {
|
||||
var content = " ";
|
||||
content += "At " + e.entries[0].dataPoint.timestamp.substring(11, 19) + "<br />";
|
||||
if (e.entries[0].dataPoint.signal_dbm) {
|
||||
content += "Signal: " + e.entries[0].dataPoint.signal_dbm + "dBm<br/>";
|
||||
} else {
|
||||
content += "Signal: 0<br/>";
|
||||
}
|
||||
content += "Noise: " + e.entries[0].dataPoint.noise_dbm + "dBm<br/>";
|
||||
content += "SNR: " + e.entries[0].dataPoint.snr + "dB<br/>";
|
||||
content += "TX Rate: " + e.entries[0].dataPoint.tx_rate_mbps + "Mbps<br/>";
|
||||
content += "TX MCS: " + e.entries[0].dataPoint.tx_rate_mcs_index + "<br/>";
|
||||
content += "RX Rate: " + e.entries[0].dataPoint.rx_rate_mbps + "Mbps<br/>";
|
||||
content += "RX MCS: " + e.entries[0].dataPoint.rx_rate_mcs_index + "<br/>";
|
||||
return content;
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
type: "rangeArea",
|
||||
xValueType: "dateTime",
|
||||
showInLegend: true,
|
||||
legendText: "Signal",
|
||||
dataPoints: dps[0]
|
||||
}
|
||||
],
|
||||
}); // --- chart
|
||||
|
||||
var formatDataPoint = function(point) {
|
||||
point.label = point.timestamp.substring(5, 10) + " " + point.timestamp.substring(11, 19);
|
||||
point.y = [point.signal_dbm, point.noise_dbm];
|
||||
point.snr = (point.noise_dbm * -1) - (point.signal_dbm * -1);
|
||||
point.tx_rate_mcs_index = point.tx_rate_mcs_index ? point.tx_rate_mcs_index : "N/A";
|
||||
point.rx_rate_mcs_index = point.rx_rate_mcs_index ? point.rx_rate_mcs_index : "N/A";
|
||||
return point;
|
||||
};
|
||||
|
||||
var updateArchiveChart = function () {
|
||||
\$.getJSON("/cgi-bin/api?chart=archive&device=$parms{device}", function (result) {
|
||||
var points = result.pages.chart.archive.map(formatDataPoint);
|
||||
chart.options.data[0].dataPoints = points;
|
||||
if(result.pages.chart.archive.constructor === Array) {
|
||||
chart.render();
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
var updateRealtimeChart = function () {
|
||||
\$.getJSON("/cgi-bin/api?chart=realtime&realtime=1&device=$parms{device}", function (result) {
|
||||
var point = formatDataPoint(result.pages.chart.realtime[0]);
|
||||
dps[0].push(point);
|
||||
chart.render();
|
||||
toneFreq(point.snr);
|
||||
\$('#snr').html(point.snr);
|
||||
});
|
||||
};
|
||||
|
||||
var dmode = getUrlVars()["realtime"];
|
||||
|
||||
if(dmode) {
|
||||
updateRealtimeChart();
|
||||
setInterval(function() {updateRealtimeChart()}, 1000);
|
||||
} else {
|
||||
updateArchiveChart();
|
||||
setInterval(function() {updateArchiveChart()}, 60000);
|
||||
}
|
||||
chart.render();
|
||||
}); // --- document.ready
|
||||
|
||||
var audioCtx = new(window.AudioContext || window.webkitAudioContext)();
|
||||
var oscillator = audioCtx.createOscillator();
|
||||
var gainNode = audioCtx.createGain();
|
||||
oscillator.connect(gainNode);
|
||||
oscillator.type = 'sine';
|
||||
gainNode.connect(audioCtx.destination);
|
||||
gainNode.gain.value = 1;
|
||||
|
||||
function toneFreq(snr) {
|
||||
var p = document.getElementById("tonePitch").value;
|
||||
oscillator.frequency.value = snr * p;
|
||||
var v = document.getElementById("toneVol").value;
|
||||
gainNode.gain.value = v;
|
||||
}
|
||||
|
||||
function toneOn() {
|
||||
oscillator = audioCtx.createOscillator();
|
||||
gainNode = audioCtx.createGain();
|
||||
oscillator.connect(gainNode);
|
||||
oscillator.type = 'sine';
|
||||
gainNode.connect(audioCtx.destination);
|
||||
gainNode.gain.value = .5;
|
||||
oscillator.start();
|
||||
document.getElementById("toneOff").disabled = false;
|
||||
document.getElementById("toneOn").disabled = true;
|
||||
};
|
||||
|
||||
function toneOff() {
|
||||
document.getElementById("toneOff").disabled = true;
|
||||
document.getElementById("toneOn").disabled = false;
|
||||
oscillator.stop();
|
||||
};
|
||||
|
||||
</script>
|
||||
</head>
|
||||
EOF
|
||||
|
||||
$page = <<EOF;
|
||||
<body>
|
||||
<div class="overlay" id="content-overlay"></div>
|
||||
<div id="content-loading-spinner-wrapper">
|
||||
<div id="content-loading-spinner">
|
||||
<div class="spinner"></div>
|
||||
<div class="loading-text">
|
||||
Loading . . .
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
<div class="TopBanner">
|
||||
<div class="LogoDiv"><img src="/AREDN.png" class="AREDNLogo"></img></div>
|
||||
</div>
|
||||
<h1><big>$node</big></h1><hr>
|
||||
<nobr>
|
||||
<button onclick="window.location.href='/cgi-bin/signal'">Archive</button>
|
||||
<button onclick="window.location.href='/cgi-bin/signal?realtime=1'">Realtime</button>
|
||||
<button onclick="window.location.href='/cgi-bin/status'">Quit</button>
|
||||
<br />
|
||||
<div id="deviceSelector">
|
||||
<form name="deviceSelector" method="GET" action="/cgi-bin/signal">
|
||||
Selected Device: <select name="device" onChange="this.form.submit();">
|
||||
EOF
|
||||
|
||||
# get a list of files from /tmp/snrlog
|
||||
my @files = `ls -1A /tmp/snrlog`;
|
||||
$parms{device}="" if(!/$parms{device}/ ~~ @files);
|
||||
|
||||
# default to "Strongest Signal" for Realtime
|
||||
if(! $parms{device} and $parms{realtime}) {
|
||||
$page = $page . "<option selected value='strongest'>Average signal for all connected stations</option>";
|
||||
}
|
||||
|
||||
$firstSel=1;
|
||||
# iterate over each file
|
||||
foreach $logfile (@files)
|
||||
{
|
||||
chomp($logfile);
|
||||
my ($dmac, $dname) = $logfile =~ /^(.*?)\-(.*)/;
|
||||
$dname=$dmac if($dname eq '');
|
||||
if($parms{device} eq $logfile or (!$parms{realtime} and $firstSel)) {
|
||||
$page = $page . "<option selected value='$logfile'>$dname</option>\n";
|
||||
$firstSel=0;
|
||||
} else {
|
||||
$page = $page . "<option value='$logfile'>$dname</option>\n";
|
||||
}
|
||||
}
|
||||
|
||||
$page = $page . "</select>\n";
|
||||
$page = $page . "<input type='hidden' name='realtime' value='1'></input>\n" if($parms{realtime} eq "1");
|
||||
$page = $page . <<EOF;
|
||||
</form>
|
||||
</div>
|
||||
<div id="snrValues" style="float: left;">
|
||||
EOF
|
||||
$page = $page . "<div><h3>SNR: <span id='snr'>0</span>dB</h3></div>" if($parms{"realtime"} eq "1");
|
||||
if($parms{"realtime"} eq "1") {
|
||||
$page = $page . "<div style='float: right;'>Sound: <button id='toneOn' onclick='toneOn();'>On</button> ";
|
||||
$page = $page . "<button id='toneOff' disabled onclick='toneOff();'>Off</button><br /><br />";
|
||||
$page = $page . "Pitch: <input type='range' id='tonePitch' name='tonePitch' min='5' max='100'></input><br /><br />";
|
||||
$page = $page . "Volume: <input type='range' id='toneVol' name='toneVol' min='0' max='10'></input><br /><br />";
|
||||
$page = $page . "</div>";
|
||||
}
|
||||
|
||||
$page = $page . <<EOF;
|
||||
</div>
|
||||
<div id="chartContainer" style="width: 80%; height: 60%;"></div>
|
||||
</center>
|
||||
EOF
|
||||
|
||||
print $header;
|
||||
print $page;
|
||||
show_debug_info();
|
||||
print "</body></html>";
|
||||
|
||||
sub DEBUGEXIT()
|
||||
{
|
||||
my ($text) = @_;
|
||||
http_header();
|
||||
html_header("$node setup", 1);
|
||||
print "DEBUG-";
|
||||
print $text;
|
||||
print "</body>";
|
||||
exit;
|
||||
}
|
|
@ -1,298 +1,412 @@
|
|||
#!/usr/bin/perl
|
||||
=for comment
|
||||
#!/usr/bin/lua
|
||||
--[[
|
||||
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
Copyright (C) 2015 Conrad Lara
|
||||
See Contributors file for additional contributors
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
Copyright (C) 2021 Tim Wilkinson
|
||||
Original Perl 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 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.
|
||||
|
||||
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/>.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
Additional Terms:
|
||||
|
||||
Additional Terms:
|
||||
Additional use restrictions exist on the AREDN(TM) trademark and logo.
|
||||
See AREDNLicense.txt for more info.
|
||||
|
||||
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.
|
||||
|
||||
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 contained within.
|
||||
|
||||
You must not misrepresent the origin of the material contained 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
|
||||
|
||||
Modified versions must be modified to attribute to the original source
|
||||
and be marked in reasonable ways as differentiate it from the original
|
||||
version.
|
||||
--]]
|
||||
|
||||
=cut
|
||||
require("nixio")
|
||||
require("aredn.hardware")
|
||||
require("aredn.http")
|
||||
require("aredn.utils")
|
||||
local html = require("aredn.html")
|
||||
require("uci")
|
||||
local aredn_info = require("aredn.info")
|
||||
local olsr = require("aredn.olsr")
|
||||
|
||||
$debug = 0;
|
||||
BEGIN {push @INC, '/www/cgi-bin'};
|
||||
use perlfunc;
|
||||
use ucifunc;
|
||||
-- helpers start
|
||||
|
||||
# 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.mesh";
|
||||
$wifi_iface = get_interface("wifi");
|
||||
$wifi_iface =~ /wlan(\d+)/;
|
||||
$radio = ( defined $1 )? "radio$1" : "radio0";
|
||||
$wifi_disable = ( $wifi_iface =~ /eth.*$/ )? 1 : 0;
|
||||
function mesh_ip_to_hostnames(ip)
|
||||
if not ip or ip == "" or ip == "none" then
|
||||
return ""
|
||||
end
|
||||
local pattern = "^" .. ip .. "%s+([%w%-]+)"
|
||||
for line in io.lines("/etc/hosts")
|
||||
do
|
||||
local host = line:match(pattern)
|
||||
if host then
|
||||
return host.gsub("%s+", " / ")
|
||||
end
|
||||
end
|
||||
local hosts = ""
|
||||
for line in io.lines("/var/run/hosts_olsr")
|
||||
do
|
||||
local host = line:match(pattern)
|
||||
if host then
|
||||
hosts = hosts .. " / " .. host
|
||||
end
|
||||
end
|
||||
return hosts:sub(4, #hosts)
|
||||
end
|
||||
|
||||
if ( ! $wifi_disable )
|
||||
{
|
||||
($junk, $wifi_channel) = &uci_get_named_option("wireless", "$radio", "channel");
|
||||
if ($wifi_channel >= 76 and $wifi_channel <= 99)
|
||||
{
|
||||
$wifi_channel = ($wifi_channel*5+3000);
|
||||
}
|
||||
($junk, $wifi_chanbw) = &uci_get_named_option("wireless", "$radio", "chanbw");
|
||||
function get_default_gw()
|
||||
-- a node with a wired default gw will route via this
|
||||
local p = io.popen("ip route list table 254")
|
||||
if p then
|
||||
for line in p:lines()
|
||||
do
|
||||
local gw = line:match("^default%svia%s([%d%.]+)")
|
||||
if gw then
|
||||
p:close()
|
||||
return gw
|
||||
end
|
||||
end
|
||||
p:close()
|
||||
end
|
||||
-- table 31 is populated by OLSR
|
||||
p = io.popen("ip route list table 31")
|
||||
if p then
|
||||
for line in p:lines()
|
||||
do
|
||||
local gw = line:match("^default%svia%s([%d%.]+)")
|
||||
if gw then
|
||||
p:close()
|
||||
return gw
|
||||
end
|
||||
end
|
||||
p:close()
|
||||
end
|
||||
return "none"
|
||||
end
|
||||
|
||||
$wifi_ssid = "N/A";
|
||||
@wisections = &uci_get_all_indexed_by_sectiontype("wireless", "wifi-iface");
|
||||
foreach(@wisections) {
|
||||
if ($_->{network} eq "wifi") {
|
||||
$wifi_ssid = $_->{ssid};
|
||||
}
|
||||
}
|
||||
}
|
||||
function get_wifi_signal(wifiif)
|
||||
local signal = -1000
|
||||
local noise = -1000
|
||||
for mac, station in pairs(iwinfo.nl80211.assoclist(wifiif))
|
||||
do
|
||||
if station.signal ~= 0 and station.signal > signal then
|
||||
signal = station.signal
|
||||
end
|
||||
if station.noise ~= 0 and station.noise > noise then
|
||||
noise = station.noise
|
||||
end
|
||||
end
|
||||
if signal == -1000 or noise == -1000 then
|
||||
return "N/A", "N/A"
|
||||
else
|
||||
return signal, noise
|
||||
end
|
||||
end
|
||||
|
||||
$node_desc = `/sbin/uci -q get system.\@system[0].description`; #pull the node description from uci
|
||||
#get location info if available
|
||||
$lat_lon = "<strong>Location Not Available</strong>";
|
||||
$lat=&uci_get_indexed_option("aredn","location",0,"lat");
|
||||
$lon=&uci_get_indexed_option("aredn","location",0,"lon");
|
||||
function css_options()
|
||||
html.print("<option>Select a theme</option>")
|
||||
for file in nixio.fs.glob("/www/*.css")
|
||||
do
|
||||
if file ~= "/www/style.css" then
|
||||
file = file:match("/www/(.*).css")
|
||||
html.print("<option value=\"" .. file .. ".css\">" .. file .. "</option>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if($lat ne "" and $lon ne "") {
|
||||
$lat_lon = "<center><strong>Location: </strong> $lat $lon</center>";
|
||||
}
|
||||
$olsrTotal = `/sbin/ip route list table 30 | wc -l`; #num hosts olsr is keeping track of
|
||||
$olsrNodes = `/sbin/ip route list table 30 | egrep "/" | wc -l`; #num *nodes* on the network (minus other hosts)
|
||||
-- helpers end
|
||||
|
||||
read_postdata();
|
||||
local node = aredn_info.get_nvram("node")
|
||||
if node == "" then
|
||||
node = "NOCALL"
|
||||
end
|
||||
local tactical = aredn_info.get_nvram("tactical")
|
||||
local config = aredn_info.get_nvram("config")
|
||||
if config == "" or nixio.fs.stat("/etc/config.mesh", "type") ~= "dir" then
|
||||
config = "not set"
|
||||
end
|
||||
local wifi_iface = aredn.hardware.get_iface_name("wifi")
|
||||
local wifi_nr = wifi_iface:match("wlan(%d+)")
|
||||
local wifi_disabled = true
|
||||
local radio = "radio0"
|
||||
if wifi_nr then
|
||||
wifi_disabled = false
|
||||
radio = "radio" .. wifi_nr
|
||||
end
|
||||
|
||||
if($parms{css} and -f "/www/$parms{css}" and $parms{css} =~ /\.css$/i) {
|
||||
unlink "/tmp/web/style.css";
|
||||
symlink "/www/$parms{css}","/tmp/web/style.css";
|
||||
}
|
||||
local cursor = uci.cursor()
|
||||
|
||||
# generate the page
|
||||
http_header();
|
||||
html_header("$node status", 1);
|
||||
print "<body><form method='post' action='/cgi-bin/status' enctype='multipart/form-data'>\n";
|
||||
print "<center>\n";
|
||||
local wifi_channel
|
||||
local wifi_chanbw
|
||||
local wifi_ssid
|
||||
if not wifi_disabled then
|
||||
wifi_channel = tonumber(cursor:get("wireless", radio, "channel"))
|
||||
if wifi_channel >= 76 and wifi_channel <= 99 then
|
||||
wifi_channel = wifi_channel * 5 + 3000
|
||||
end
|
||||
wifi_chanbw = cursor:get("wireless", radio, "chanbw")
|
||||
wifi_ssid = "N/A"
|
||||
cursor:foreach("wireless", "wifi-iface",
|
||||
function (section)
|
||||
if section.network == "wifi" then
|
||||
wifi_ssid = section.ssid
|
||||
return false
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
alert_banner();
|
||||
local node_desc = cursor:get("system", "@system[0]", "description")
|
||||
local lat_lon = "<strong>Location Not Available</strong>"
|
||||
local lat = cursor:get("aredn", "@location[0]", "lat")
|
||||
local lon = cursor:get("aredn", "@location[0]", "lon")
|
||||
if lat and lon then
|
||||
lat_lon = string.format("<center><strong>Location: </strong> %s %s</center>", lat, lon)
|
||||
end
|
||||
|
||||
# page header
|
||||
print "<h1><big>$node";
|
||||
print " / $tactical" if $tactical;
|
||||
print "</big></h1>";
|
||||
print "<center>$lat_lon</center>"; #display location info
|
||||
print "<table id='node_description_display'><tr><td>$node_desc</td></tr></table>" if $node_desc;
|
||||
print "<hr>\n";
|
||||
local olsr_routes = olsr.getOLSRRoutes()
|
||||
local olsr_total = 0
|
||||
local olsr_nodes = 0
|
||||
for i, node in ipairs(olsr_routes)
|
||||
do
|
||||
if node.genmask ~= 0 then -- don't count default route
|
||||
olsr_total = olsr_total + 1
|
||||
if node.genmask ~= 32 then
|
||||
olsr_nodes = olsr_nodes + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# nav buttons
|
||||
print "<nobr>\n";
|
||||
-- post data
|
||||
|
||||
#print qq(<button type=button onClick='window.open("/help.html", "_blank")' title='Open a help window'>Help</button>\n);
|
||||
print "<a href='/help.html' target='_blank'>Help</a>\n";
|
||||
if os.getenv("REQUEST_METHOD") == "POST" then
|
||||
require('luci.http')
|
||||
require('luci.sys')
|
||||
local request = luci.http.Request(luci.sys.getenv(),
|
||||
function()
|
||||
local v = io.read(1024)
|
||||
if not v then
|
||||
io.close()
|
||||
end
|
||||
return v
|
||||
end
|
||||
)
|
||||
local css = request:formvalue("css")
|
||||
if css and css:match("%.css$") and nixio.fs.stat("/www/" .. css) then
|
||||
nixio.fs.unlink("/tmp/web/style.css")
|
||||
nixio.fs.symlink("/www/" .. css, "/tmp/web/style.css")
|
||||
end
|
||||
end
|
||||
|
||||
print " ";
|
||||
print "<input type=submit name=refresh value=Refresh title='Refresh this page'>\n";
|
||||
-- generate page
|
||||
|
||||
if($config eq "mesh")
|
||||
{
|
||||
print " ";
|
||||
print "<button type=button onClick='window.location=\"mesh\"' title='See what is on the mesh'>Mesh Status</button>\n";
|
||||
if ( ! $wifi_disable )
|
||||
{
|
||||
print " ";
|
||||
print "<button type=button onClick='window.location=\"scan\"' title='See what wireless networks are nearby'>WiFi Scan</button>\n";
|
||||
}
|
||||
}
|
||||
http_header()
|
||||
html.header(node .. " status", true)
|
||||
html.print("<body><form method='post' action='/cgi-bin/status' enctype='multipart/form-data'>")
|
||||
html.print("<center>")
|
||||
|
||||
print " ";
|
||||
print "<button type=button onClick='window.location=\"setup\"' title='Configure this node'>Setup</button>\n";
|
||||
html.alert_banner()
|
||||
|
||||
print " ";
|
||||
-- page header
|
||||
html.print("<h1><big>" .. node)
|
||||
if tactical ~= "" then
|
||||
html.print(" / " .. tactical)
|
||||
end
|
||||
html.print("</big></h1>")
|
||||
html.print("<center>" .. lat_lon .. "</center>")
|
||||
if node_desc then
|
||||
html.print("<table id='node_description_display'><tr><td>" .. node_desc .. "</td></tr></table>")
|
||||
end
|
||||
html.print("<hr>")
|
||||
|
||||
print "<select name=\"css\" size=\"1\" onChange=\"form.submit()\" >";
|
||||
css_options();
|
||||
print "</select>";
|
||||
-- nav buttons
|
||||
html.print("<nobr>")
|
||||
html.print("<a href='/help.html' target='_blank'>Help</a>")
|
||||
html.print(" ")
|
||||
html.print("<input type=submit name=refresh value=Refresh title='Refresh this page'>")
|
||||
if config == "mesh" then
|
||||
html.print(" ")
|
||||
html.print("<button type=button onClick='window.location=\"mesh\"' title='See what is on the mesh'>Mesh Status</button>")
|
||||
if not wifi_disabled then
|
||||
html.print(" ")
|
||||
html.print("<button type=button onClick='window.location=\"scan\"' title='See what wireless networks are nearby'>WiFi Scan</button>")
|
||||
end
|
||||
end
|
||||
html.print("  ")
|
||||
html.print("<button type=button onClick='window.location=\"setup\"' title='Configure this node'>Setup</button>")
|
||||
html.print(" ")
|
||||
html.print("<select name=\"css\" size=\"1\" onChange=\"form.submit()\" >")
|
||||
css_options()
|
||||
html.print("</select>")
|
||||
html.print("</nobr")
|
||||
|
||||
print "</nobr>";
|
||||
html.print("<input type=hidden name=reload value=reload>")
|
||||
|
||||
print "<input type=hidden name=reload value=reload>\n";
|
||||
if config == "not set" then
|
||||
html.print("<b><br><br>This node is not yet configured.<br>")
|
||||
html.print("Go to the setup page and set your node name and password.<br>")
|
||||
html.print("Click Save Changes, <u>even if you didn't make any changes</u>, then the node will reboot.</b>")
|
||||
html.print("<br><br>")
|
||||
html.print("<div style=\"max-width: 540px\; text-align: left\">")
|
||||
html.print("<p>This device can be configured to either permit or prohibit known encrypted traffic on its RF link. It is up to the user to decide which is appropriate based on how it will be used and the license under which it will be operated. These rules vary by country, frequency, and intended use. You are encouraged to read and understand these rules before going further.</p>")
|
||||
html.print("<p>This device is pre-configured with no restrictions as to the type of data being passed.</p>")
|
||||
html.print("<p>Follow these steps if <span style=\"text-decoration: underline\">you wish to prohibit</span> known encrypted traffic on the RF link. These instructions will disappear, so copy them for your reference:</p>")
|
||||
html.print("<p><ol>")
|
||||
html.print("<li>Setup your node name and password as instructed at the top of this page</li>")
|
||||
html.print("<li>After you Save Changes allow your node to reboot</li>")
|
||||
html.print("<li>Return to the Node Status page and navigate to Setup > Administration</li>")
|
||||
html.print("<li>Obtain the blockknownencryption package from the AREDN™ website OR refresh the Package list (node must be connected to the internet)</li>")
|
||||
html.print("<li>Install the blockknownencryption package by uploading it or choosing it from the package drop-down list</li>")
|
||||
html.print("<li>Wait until the package installs and then reboot your node</li>")
|
||||
html.print("</ol></p>")
|
||||
html.print("</div>")
|
||||
end
|
||||
|
||||
-- status display
|
||||
|
||||
if($config eq "not set")
|
||||
{
|
||||
print "<b><br><br>This node is not yet configured.<br>";
|
||||
print "Go to the setup page and set your node name and password.<br>\n";
|
||||
print "Click Save Changes, <u>even if you didn't make any changes</u>, then the node will reboot.</b>\n";
|
||||
print "<br><br>\n";
|
||||
print "<div style=\"max-width: 540px\; text-align: left\">\n";
|
||||
print "<p>This device can be configured to either permit or prohibit known encrypted traffic on its RF link. It is up to the user to decide which is appropriate based on how it will be used and the license under which it will be operated. These rules vary by country, frequency, and intended use. You are encouraged to read and understand these rules before going further.</p>";
|
||||
print "<p>This device is pre-configured with no restrictions as to the type of data being passed.</p>\n";
|
||||
print "<p>Follow these steps if <span style=\"text-decoration: underline\">you wish to prohibit</span> known encrypted traffic on the RF link. These instructions will disappear, so copy them for your reference:</p>";
|
||||
print "<p><ol>\n";
|
||||
print "<li>Setup your node name and password as instructed at the top of this page</li>";
|
||||
print "<li>After you Save Changes allow your node to reboot</li>";
|
||||
print "<li>Return to the Node Status page and navigate to Setup > Administration</li>";
|
||||
print "<li>Obtain the blockknownencryption package from the AREDN™ website OR refresh the Package list (node must be connected to the internet)</li>";
|
||||
print "<li>Install the blockknownencryption package by uploading it or choosing it from the package drop-down list</li>";
|
||||
print "<li>Wait until the package installs and then reboot your node</li>";
|
||||
print "</ol></p>\n";
|
||||
print "</div>";
|
||||
}
|
||||
local col1 = {}
|
||||
local col2 = {}
|
||||
|
||||
# status display
|
||||
-- left column - network interface info
|
||||
|
||||
@col1 = @col2 = ();
|
||||
$hide_local = 0;
|
||||
$browser_ip = "";
|
||||
|
||||
# left column - network interface info
|
||||
|
||||
# show the Primary/Wifi address
|
||||
($ip, $mask, $bcast, $net, $cidr) = get_ip4_network($wifi_iface);
|
||||
$cidr = "/ $cidr" if $cidr;
|
||||
if (! $wifi_disable )
|
||||
{
|
||||
$str = "<th align=right><nobr>Wifi address</nobr></th><td>$ip <small>$cidr</small><br>";
|
||||
}
|
||||
local ip = cursor:get("network", "wifi", "ipaddr")
|
||||
local cidr = netmask_to_cidr(cursor:get("network", "wifi", "netmask"))
|
||||
if wifi_disabled then
|
||||
col1[#col1 + 1] = "<th align=right><nobr>Primary address</nobr></th><td>" .. ip .. " <small>/ " .. cidr .. "</small><br>"
|
||||
else
|
||||
{
|
||||
$str = "<th align=right><nobr>Primary address</nobr></th><td>$ip <small>$cidr</small><br>";
|
||||
}
|
||||
col1[#col1 + 1] = "<th align=right><nobr>Wifi address</nobr></th><td>" .. ip .. " <small>/ " .. cidr .. "</small><br>"
|
||||
end
|
||||
|
||||
# $str .= "<small><nobr>" . get_ip6_addr($wifi_iface) . "</nobr></small></td>";
|
||||
push @col1, $str;
|
||||
ip = cursor:get("network", "lan", "ipaddr")
|
||||
mask = cursor:get("network", "lan", "netmask")
|
||||
local browser_ip
|
||||
local remote_ip = os.getenv("REMOTE_ADDRESS")
|
||||
if remote_ip then
|
||||
remote_ip = remote_ip.match("::ffff:([%d%.]+)")
|
||||
end
|
||||
local hide_local = false
|
||||
if remote_ip then
|
||||
browser_ip = remote_ip
|
||||
if not validate_same_subnet(remote_ip, ip, mask) then
|
||||
hide_local = true
|
||||
end
|
||||
end
|
||||
|
||||
# find out if the browser is on this node's lan
|
||||
# if not, hide the local network details
|
||||
($ip, $mask, $bcast, $net, $cidr) = get_ip4_network(get_interface("lan"));
|
||||
if($ENV{REMOTE_ADDR} =~ /::ffff:([\d\.]+)/)
|
||||
{
|
||||
$browser_ip = $1;
|
||||
$hide_local = 1 unless validate_same_subnet($browser_ip, $ip, $mask);
|
||||
}
|
||||
if ip:match("^10%.") or not hide_local then
|
||||
cidr = netmask_to_cidr(mask)
|
||||
col1[#col1 + 1] = "<th align=right><nobr>LAN address</nobr></th><td>" .. ip .. " <small>/ " .. cidr .. "</small><br>"
|
||||
end
|
||||
|
||||
if($ip =~ /^10\./ or not $hide_local)
|
||||
{
|
||||
$cidr = "/ $cidr" if $cidr;
|
||||
$str = "<th align=right><nobr>LAN address</nobr></th><td>$ip <small>$cidr</small><br>";
|
||||
# $str .= "<small><nobr>" . get_ip6_addr(get_interface("lan")) . "</nobr></small></td>";
|
||||
push @col1, $str;
|
||||
}
|
||||
local wan_iface = aredn.hardware.get_iface_name("wan")
|
||||
if not hide_local and wan_iface then
|
||||
local ip, bcast, mask = aredn.hardware.get_interface_ip4(wan_iface)
|
||||
if ip then
|
||||
cidr = netmask_to_cidr(mask)
|
||||
col1[#col1 + 1] = "<th align=right><nobr>WAN address</nobr></th><td>" .. ip .. " <small>/ " .. cidr .. "</small><br>"
|
||||
else
|
||||
col1[#col1 + 1] = "<th align=right><nobr>WAN address</nobr></th><td>none</small><br>"
|
||||
end
|
||||
end
|
||||
|
||||
{
|
||||
my $wanintf = get_interface("wan");
|
||||
if(not $hide_local and not system "ifconfig $wanintf >/dev/null 2>&1")
|
||||
{
|
||||
($ip, $mask, $bcast, $net, $cidr) = get_ip4_network("$wanintf");
|
||||
$cidr = "/ $cidr" if $cidr;
|
||||
$cidr = "" unless $cidr;
|
||||
$str = "<th align=right><nobr>WAN address</nobr></th><td>$ip <small>$cidr</small><br>";
|
||||
# $str .= "<small><nobr>" . get_ip6_addr("$wanintf") . "</nobr></small></td>";
|
||||
push @col1, $str;
|
||||
}
|
||||
}
|
||||
ip = get_default_gw()
|
||||
if ip:match("^10%.") or not hide_local then
|
||||
col1[#col1 + 1] = "<th align=right><nobr>default gateway</nobr></th><td>" .. ip
|
||||
if ip:match("^10%.") then
|
||||
col1[#col1] = col1[#col1] .. "<br><nobr>" .. mesh_ip_to_hostnames(ip) .. "</nobr>"
|
||||
end
|
||||
col1[#col1] = col1[#col1] .. "</td>"
|
||||
end
|
||||
|
||||
$ip = get_default_gw();
|
||||
if browser_ip then
|
||||
col1[#col1 + 1] = "<th align=right><nobr>your address</nobr></th><td>" .. browser_ip .. "<br><nobr>" .. mesh_ip_to_hostnames(browser_ip) .. "</nobr></td>"
|
||||
end
|
||||
|
||||
if($ip =~ /^10\./ or not $hide_local)
|
||||
{
|
||||
$str = "<th align=right><nobr>default gateway</nobr></th><td>$ip";
|
||||
$str .= "<br><nobr>" . mesh_ip2hostname($ip) . "</nobr>" if $ip =~ /^10\./;
|
||||
push @col1, $str . "</td>";
|
||||
}
|
||||
if not wifi_disabled then
|
||||
col1[#col1 + 1] = "<th align=right><nobr>SSID</nobr></th><td>" .. wifi_ssid .. "</td>"
|
||||
col1[#col1 + 1] = "<th align=right><nobr>Channel</nobr></th><td>" .. wifi_channel .. "</td>"
|
||||
col1[#col1 + 1] = "<th align=right><nobr>Bandwidth</nobr></th><td>" .. wifi_chanbw .. " MHz</td>"
|
||||
end
|
||||
|
||||
if($browser_ip)
|
||||
{
|
||||
$str = "<th align=right><nobr>your address</nobr></th><td>$browser_ip";
|
||||
$str .= "<br><nobr>" . mesh_ip2hostname($browser_ip) . "</nobr>";# if $ip =~ /^10\./;
|
||||
push @col1, $str . "</td>";
|
||||
}
|
||||
-- right column - system info
|
||||
|
||||
if ( ! $wifi_disable )
|
||||
{
|
||||
$str = "<th align=right><nobr>SSID</nobr></th><td>$wifi_ssid";
|
||||
push @col1, $str . "</td>";
|
||||
if config == "mesh" and not wifi_disabled then
|
||||
col2[#col2 + 1] = "<th align=right valign=middle><nobr>Signal/Noise/Ratio</nobr></th><td valign=middle><nobr>"
|
||||
local s, n = get_wifi_signal(wifi_iface)
|
||||
if s == "N/A" then
|
||||
col2[#col2] = col2[#col2] .. "N/A"
|
||||
else
|
||||
col2[#col2] = col2[#col2] .. "<big><b>" .. s .. " / " .. n .. " / " .. (s - n) .. " dB</b></big>"
|
||||
end
|
||||
col2[#col2] = col2[#col2] .. " <button type=button onClick='window.location=\"signal?realtime=1\"' title='Display continuous or archived signal strength on a chart'>Charts</button></nobr></td>"
|
||||
end
|
||||
|
||||
$str = "<th align=right><nobr>Channel</nobr></th><td>$wifi_channel";
|
||||
push @col1, $str . "</td>";
|
||||
col2[#col2 + 1] = "<th align=right><nobr>firmware version</nobr></th><td>" .. read_all("/etc/mesh-release") .. "</td>";
|
||||
col2[#col2 + 1] = "<th align=right>system time</th><td>" .. os.date("%a %b %e %Y") .. "<br>" .. os.date("%T %Z") .. "</td>";
|
||||
|
||||
$str = "<th align=right><nobr>Bandwidth</nobr></th><td>$wifi_chanbw MHz";
|
||||
push @col1, $str . "</td>";
|
||||
}
|
||||
local sysinfo = nixio.sysinfo()
|
||||
local uptime = string.format("%d:%02d", math.floor(sysinfo.uptime / 3600) % 24, math.floor(sysinfo.uptime / 60) % 60)
|
||||
if sysinfo.uptime >= 172800 then
|
||||
uptime = math.floor(sysinfo.uptime / 86400) .. " days, " .. uptime
|
||||
elseif sysinfo.uptime >= 86400 then
|
||||
uptime = "1 day, " .. uptime
|
||||
end
|
||||
|
||||
# right column - system info
|
||||
col2[#col2 + 1] = "<th align=right>uptime<br>load average</th><td>" .. uptime .. "<br>" .. string.format("%.2f, %.2f, %.2f", sysinfo.loads[1], sysinfo.loads[2], sysinfo.loads[3]) .. "</td>";
|
||||
local vfs = nixio.fs.statvfs("/overlay")
|
||||
local fspace = vfs.bfree * vfs.bsize / 1024
|
||||
if fspace < 100 then
|
||||
fspace = "<blink><b>" .. fspace .. " KB</b></blink>"
|
||||
else
|
||||
fspace = fspace .. " KB"
|
||||
end
|
||||
vfs = nixio.fs.statvfs("/tmp")
|
||||
local tspace = vfs.bfree * vfs.bsize / 1024
|
||||
if tspace < 3000 then
|
||||
tspace = "<blink><b>" .. tspace .. " KB</b></blink>"
|
||||
else
|
||||
tspace = tspace .. " KB"
|
||||
end
|
||||
local rspace = (sysinfo.freeram + sysinfo.bufferram) / 1024
|
||||
if rspace < 500 then
|
||||
rspace = "<blink><b>" .. rspace .. " KB</b></blink>"
|
||||
else
|
||||
rspace = rspace .. " KB"
|
||||
end
|
||||
col2[#col2 + 1] = "<th align=right>free space</th><td><nobr>flash = " .. fspace .. "</nobr><br><nobr>/tmp = " .. tspace .. "</nobr><br><nobr>memory = " .. rspace .. "</nobr></td>"
|
||||
col2[#col2 + 1] = "<th align='right'>OLSR Entries</th><td><nobr>Total = " .. olsr_total .. "<nobr><br><nobr>Nodes = " .. olsr_nodes .. "<nobr></td>"
|
||||
|
||||
if($config eq "mesh" and ! $wifi_disable )
|
||||
{
|
||||
$str = "<th align=right valign=middle><nobr>Signal/Noise/Ratio</nobr></th><td valign=middle><nobr>";
|
||||
($s, $n) = get_wifi_signal($wifi_iface);
|
||||
if($s eq "N/A") { $str .= "N/A" }
|
||||
else { $str .= sprintf "<big><b>%d / %d / %d dB</b></big>", $s, $n, $s - $n }
|
||||
$str .= " ";
|
||||
$str .= "<button type=button onClick='window.location=\"signal?realtime=1\"' title='Display continuous or archived signal strength on a chart'>Charts</button>\n";
|
||||
$str .= "</nobr></td>";
|
||||
push @col2, $str;
|
||||
}
|
||||
-- now print the tables
|
||||
|
||||
push @col2, "<th align=right><nobr>firmware version</nobr></th><td>" . `cat /etc/mesh-release`. "</td>";
|
||||
push @col2, "<th align=right>system time</th><td>" . `date +'%a %b %e %Y<br>%T %Z'` . "</td>";
|
||||
html.print("<br><br><table>")
|
||||
html.print("<tr><td valign=top><table cellpadding=4>")
|
||||
for i, line in ipairs(col1)
|
||||
do
|
||||
html.print("<tr>" .. line .. "</tr>")
|
||||
end
|
||||
html.print("</table></td><td valign=top><table cellpadding=4>")
|
||||
for i, line in ipairs(col2)
|
||||
do
|
||||
html.print("<tr>" .. line .. "</tr>")
|
||||
end
|
||||
html.print("</table></td></tr></table>")
|
||||
|
||||
$uptime = `uptime`;
|
||||
$uptime =~ s/^ ..:..:.. up //;
|
||||
($uptime, $load) = $uptime =~ /(.*), load average: (.*)/;
|
||||
push @col2, "<th align=right>uptime<br>load average</th><td>$uptime<br>$load</td>";
|
||||
-- end
|
||||
html.print("</center></form>")
|
||||
|
||||
$str = "<th align=right>free space</th><td><nobr>flash = ";
|
||||
$space = get_free_space("/overlay");
|
||||
$str .= $space < 100 ? "<blink><b>$space KB</b></blink>" : "$space KB";
|
||||
$str .= "</nobr><br><nobr>/tmp = ";
|
||||
$space = get_free_space("/tmp");
|
||||
$str .= $space < 3000 ? "<blink><b>$space KB</b></blink>" : "$space KB";
|
||||
$str .= "</nobr><br><nobr>memory = ";
|
||||
$space = get_free_mem();
|
||||
$str .= $space < 500 ? "<blink><b>$space KB</b></blink>" : "$space KB";
|
||||
$str .= "</nobr></td>";
|
||||
|
||||
push @col2, $str;
|
||||
push @col2, "<th align='right'>OLSR Entries</th><td><nobr>Total = $olsrTotal<nobr><br><nobr>Nodes = $olsrNodes<nobr></td>"; #display OLSR numbers
|
||||
|
||||
# now print the tables
|
||||
|
||||
print "<br><br><table>\n";
|
||||
print "<tr><td valign=top><table cellpadding=4>\n";
|
||||
foreach(@col1) { print "<tr>$_</tr>\n" }
|
||||
print "</table></td><td valign=top><table cellpadding=4>\n";
|
||||
foreach(@col2) { print "<tr>$_</tr>\n" }
|
||||
print "</table></td></tr></table>\n";
|
||||
|
||||
# end
|
||||
print "</center>\n";
|
||||
print "</form>\n";
|
||||
|
||||
show_debug_info();
|
||||
show_parse_errors();
|
||||
|
||||
page_footer();
|
||||
print "</body>\n";
|
||||
print "</html>\n";
|
||||
html.footer()
|
||||
html.print("</body></html>")
|
||||
|
|
|
@ -0,0 +1,298 @@
|
|||
#!/usr/bin/perl
|
||||
=for comment
|
||||
|
||||
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
||||
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/>.
|
||||
|
||||
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 contained 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.
|
||||
|
||||
=cut
|
||||
|
||||
$debug = 0;
|
||||
BEGIN {push @INC, '/www/cgi-bin'};
|
||||
use perlfunc;
|
||||
use ucifunc;
|
||||
|
||||
# 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.mesh";
|
||||
$wifi_iface = get_interface("wifi");
|
||||
$wifi_iface =~ /wlan(\d+)/;
|
||||
$radio = ( defined $1 )? "radio$1" : "radio0";
|
||||
$wifi_disable = ( $wifi_iface =~ /eth.*$/ )? 1 : 0;
|
||||
|
||||
if ( ! $wifi_disable )
|
||||
{
|
||||
($junk, $wifi_channel) = &uci_get_named_option("wireless", "$radio", "channel");
|
||||
if ($wifi_channel >= 76 and $wifi_channel <= 99)
|
||||
{
|
||||
$wifi_channel = ($wifi_channel*5+3000);
|
||||
}
|
||||
($junk, $wifi_chanbw) = &uci_get_named_option("wireless", "$radio", "chanbw");
|
||||
|
||||
$wifi_ssid = "N/A";
|
||||
@wisections = &uci_get_all_indexed_by_sectiontype("wireless", "wifi-iface");
|
||||
foreach(@wisections) {
|
||||
if ($_->{network} eq "wifi") {
|
||||
$wifi_ssid = $_->{ssid};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$node_desc = `/sbin/uci -q get system.\@system[0].description`; #pull the node description from uci
|
||||
#get location info if available
|
||||
$lat_lon = "<strong>Location Not Available</strong>";
|
||||
$lat=&uci_get_indexed_option("aredn","location",0,"lat");
|
||||
$lon=&uci_get_indexed_option("aredn","location",0,"lon");
|
||||
|
||||
if($lat ne "" and $lon ne "") {
|
||||
$lat_lon = "<center><strong>Location: </strong> $lat $lon</center>";
|
||||
}
|
||||
$olsrTotal = `/sbin/ip route list table 30 | wc -l`; #num hosts olsr is keeping track of
|
||||
$olsrNodes = `/sbin/ip route list table 30 | egrep "/" | wc -l`; #num *nodes* on the network (minus other hosts)
|
||||
|
||||
read_postdata();
|
||||
|
||||
if($parms{css} and -f "/www/$parms{css}" and $parms{css} =~ /\.css$/i) {
|
||||
unlink "/tmp/web/style.css";
|
||||
symlink "/www/$parms{css}","/tmp/web/style.css";
|
||||
}
|
||||
|
||||
# generate the page
|
||||
http_header();
|
||||
html_header("$node status", 1);
|
||||
print "<body><form method='post' action='/cgi-bin/status' enctype='multipart/form-data'>\n";
|
||||
print "<center>\n";
|
||||
|
||||
alert_banner();
|
||||
|
||||
# page header
|
||||
print "<h1><big>$node";
|
||||
print " / $tactical" if $tactical;
|
||||
print "</big></h1>";
|
||||
print "<center>$lat_lon</center>"; #display location info
|
||||
print "<table id='node_description_display'><tr><td>$node_desc</td></tr></table>" if $node_desc;
|
||||
print "<hr>\n";
|
||||
|
||||
# nav buttons
|
||||
print "<nobr>\n";
|
||||
|
||||
#print qq(<button type=button onClick='window.open("/help.html", "_blank")' title='Open a help window'>Help</button>\n);
|
||||
print "<a href='/help.html' target='_blank'>Help</a>\n";
|
||||
|
||||
print " ";
|
||||
print "<input type=submit name=refresh value=Refresh title='Refresh this page'>\n";
|
||||
|
||||
if($config eq "mesh")
|
||||
{
|
||||
print " ";
|
||||
print "<button type=button onClick='window.location=\"mesh\"' title='See what is on the mesh'>Mesh Status</button>\n";
|
||||
if ( ! $wifi_disable )
|
||||
{
|
||||
print " ";
|
||||
print "<button type=button onClick='window.location=\"scan\"' title='See what wireless networks are nearby'>WiFi Scan</button>\n";
|
||||
}
|
||||
}
|
||||
|
||||
print " ";
|
||||
print "<button type=button onClick='window.location=\"setup\"' title='Configure this node'>Setup</button>\n";
|
||||
|
||||
print " ";
|
||||
|
||||
print "<select name=\"css\" size=\"1\" onChange=\"form.submit()\" >";
|
||||
css_options();
|
||||
print "</select>";
|
||||
|
||||
print "</nobr>";
|
||||
|
||||
print "<input type=hidden name=reload value=reload>\n";
|
||||
|
||||
|
||||
if($config eq "not set")
|
||||
{
|
||||
print "<b><br><br>This node is not yet configured.<br>";
|
||||
print "Go to the setup page and set your node name and password.<br>\n";
|
||||
print "Click Save Changes, <u>even if you didn't make any changes</u>, then the node will reboot.</b>\n";
|
||||
print "<br><br>\n";
|
||||
print "<div style=\"max-width: 540px\; text-align: left\">\n";
|
||||
print "<p>This device can be configured to either permit or prohibit known encrypted traffic on its RF link. It is up to the user to decide which is appropriate based on how it will be used and the license under which it will be operated. These rules vary by country, frequency, and intended use. You are encouraged to read and understand these rules before going further.</p>";
|
||||
print "<p>This device is pre-configured with no restrictions as to the type of data being passed.</p>\n";
|
||||
print "<p>Follow these steps if <span style=\"text-decoration: underline\">you wish to prohibit</span> known encrypted traffic on the RF link. These instructions will disappear, so copy them for your reference:</p>";
|
||||
print "<p><ol>\n";
|
||||
print "<li>Setup your node name and password as instructed at the top of this page</li>";
|
||||
print "<li>After you Save Changes allow your node to reboot</li>";
|
||||
print "<li>Return to the Node Status page and navigate to Setup > Administration</li>";
|
||||
print "<li>Obtain the blockknownencryption package from the AREDN™ website OR refresh the Package list (node must be connected to the internet)</li>";
|
||||
print "<li>Install the blockknownencryption package by uploading it or choosing it from the package drop-down list</li>";
|
||||
print "<li>Wait until the package installs and then reboot your node</li>";
|
||||
print "</ol></p>\n";
|
||||
print "</div>";
|
||||
}
|
||||
|
||||
# status display
|
||||
|
||||
@col1 = @col2 = ();
|
||||
$hide_local = 0;
|
||||
$browser_ip = "";
|
||||
|
||||
# left column - network interface info
|
||||
|
||||
# show the Primary/Wifi address
|
||||
($ip, $mask, $bcast, $net, $cidr) = get_ip4_network($wifi_iface);
|
||||
$cidr = "/ $cidr" if $cidr;
|
||||
if (! $wifi_disable )
|
||||
{
|
||||
$str = "<th align=right><nobr>Wifi address</nobr></th><td>$ip <small>$cidr</small><br>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$str = "<th align=right><nobr>Primary address</nobr></th><td>$ip <small>$cidr</small><br>";
|
||||
}
|
||||
|
||||
# $str .= "<small><nobr>" . get_ip6_addr($wifi_iface) . "</nobr></small></td>";
|
||||
push @col1, $str;
|
||||
|
||||
# find out if the browser is on this node's lan
|
||||
# if not, hide the local network details
|
||||
($ip, $mask, $bcast, $net, $cidr) = get_ip4_network(get_interface("lan"));
|
||||
if($ENV{REMOTE_ADDR} =~ /::ffff:([\d\.]+)/)
|
||||
{
|
||||
$browser_ip = $1;
|
||||
$hide_local = 1 unless validate_same_subnet($browser_ip, $ip, $mask);
|
||||
}
|
||||
|
||||
if($ip =~ /^10\./ or not $hide_local)
|
||||
{
|
||||
$cidr = "/ $cidr" if $cidr;
|
||||
$str = "<th align=right><nobr>LAN address</nobr></th><td>$ip <small>$cidr</small><br>";
|
||||
# $str .= "<small><nobr>" . get_ip6_addr(get_interface("lan")) . "</nobr></small></td>";
|
||||
push @col1, $str;
|
||||
}
|
||||
|
||||
{
|
||||
my $wanintf = get_interface("wan");
|
||||
if(not $hide_local and not system "ifconfig $wanintf >/dev/null 2>&1")
|
||||
{
|
||||
($ip, $mask, $bcast, $net, $cidr) = get_ip4_network("$wanintf");
|
||||
$cidr = "/ $cidr" if $cidr;
|
||||
$cidr = "" unless $cidr;
|
||||
$str = "<th align=right><nobr>WAN address</nobr></th><td>$ip <small>$cidr</small><br>";
|
||||
# $str .= "<small><nobr>" . get_ip6_addr("$wanintf") . "</nobr></small></td>";
|
||||
push @col1, $str;
|
||||
}
|
||||
}
|
||||
|
||||
$ip = get_default_gw();
|
||||
|
||||
if($ip =~ /^10\./ or not $hide_local)
|
||||
{
|
||||
$str = "<th align=right><nobr>default gateway</nobr></th><td>$ip";
|
||||
$str .= "<br><nobr>" . mesh_ip2hostname($ip) . "</nobr>" if $ip =~ /^10\./;
|
||||
push @col1, $str . "</td>";
|
||||
}
|
||||
|
||||
if($browser_ip)
|
||||
{
|
||||
$str = "<th align=right><nobr>your address</nobr></th><td>$browser_ip";
|
||||
$str .= "<br><nobr>" . mesh_ip2hostname($browser_ip) . "</nobr>";# if $ip =~ /^10\./;
|
||||
push @col1, $str . "</td>";
|
||||
}
|
||||
|
||||
if ( ! $wifi_disable )
|
||||
{
|
||||
$str = "<th align=right><nobr>SSID</nobr></th><td>$wifi_ssid";
|
||||
push @col1, $str . "</td>";
|
||||
|
||||
$str = "<th align=right><nobr>Channel</nobr></th><td>$wifi_channel";
|
||||
push @col1, $str . "</td>";
|
||||
|
||||
$str = "<th align=right><nobr>Bandwidth</nobr></th><td>$wifi_chanbw MHz";
|
||||
push @col1, $str . "</td>";
|
||||
}
|
||||
|
||||
# right column - system info
|
||||
|
||||
if($config eq "mesh" and ! $wifi_disable )
|
||||
{
|
||||
$str = "<th align=right valign=middle><nobr>Signal/Noise/Ratio</nobr></th><td valign=middle><nobr>";
|
||||
($s, $n) = get_wifi_signal($wifi_iface);
|
||||
if($s eq "N/A") { $str .= "N/A" }
|
||||
else { $str .= sprintf "<big><b>%d / %d / %d dB</b></big>", $s, $n, $s - $n }
|
||||
$str .= " ";
|
||||
$str .= "<button type=button onClick='window.location=\"signal?realtime=1\"' title='Display continuous or archived signal strength on a chart'>Charts</button>\n";
|
||||
$str .= "</nobr></td>";
|
||||
push @col2, $str;
|
||||
}
|
||||
|
||||
push @col2, "<th align=right><nobr>firmware version</nobr></th><td>" . `cat /etc/mesh-release`. "</td>";
|
||||
push @col2, "<th align=right>system time</th><td>" . `date +'%a %b %e %Y<br>%T %Z'` . "</td>";
|
||||
|
||||
$uptime = `uptime`;
|
||||
$uptime =~ s/^ ..:..:.. up //;
|
||||
($uptime, $load) = $uptime =~ /(.*), load average: (.*)/;
|
||||
push @col2, "<th align=right>uptime<br>load average</th><td>$uptime<br>$load</td>";
|
||||
|
||||
$str = "<th align=right>free space</th><td><nobr>flash = ";
|
||||
$space = get_free_space("/overlay");
|
||||
$str .= $space < 100 ? "<blink><b>$space KB</b></blink>" : "$space KB";
|
||||
$str .= "</nobr><br><nobr>/tmp = ";
|
||||
$space = get_free_space("/tmp");
|
||||
$str .= $space < 3000 ? "<blink><b>$space KB</b></blink>" : "$space KB";
|
||||
$str .= "</nobr><br><nobr>memory = ";
|
||||
$space = get_free_mem();
|
||||
$str .= $space < 500 ? "<blink><b>$space KB</b></blink>" : "$space KB";
|
||||
$str .= "</nobr></td>";
|
||||
|
||||
push @col2, $str;
|
||||
push @col2, "<th align='right'>OLSR Entries</th><td><nobr>Total = $olsrTotal<nobr><br><nobr>Nodes = $olsrNodes<nobr></td>"; #display OLSR numbers
|
||||
|
||||
# now print the tables
|
||||
|
||||
print "<br><br><table>\n";
|
||||
print "<tr><td valign=top><table cellpadding=4>\n";
|
||||
foreach(@col1) { print "<tr>$_</tr>\n" }
|
||||
print "</table></td><td valign=top><table cellpadding=4>\n";
|
||||
foreach(@col2) { print "<tr>$_</tr>\n" }
|
||||
print "</table></td></tr></table>\n";
|
||||
|
||||
# end
|
||||
print "</center>\n";
|
||||
print "</form>\n";
|
||||
|
||||
show_debug_info();
|
||||
show_parse_errors();
|
||||
|
||||
page_footer();
|
||||
print "</body>\n";
|
||||
print "</html>\n";
|
Loading…
Reference in New Issue