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:
Tim Wilkinson 2022-01-19 05:42:31 -08:00 committed by GitHub
parent fa6c2da4fe
commit 610cde651c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 3007 additions and 1237 deletions

View File

@ -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

680
files/www/cgi-bin/mesh.pl Executable file
View File

@ -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 "&nbsp;&nbsp;&nbsp;\n";
print "<input type=submit name=auto value=Auto title='Automatic page refresh'>\n";
}
print "&nbsp;&nbsp;&nbsp";
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} .= " &nbsp; <small>($nodeiface)</small>"; }
$rows{$host} .= "</td><td colspan=3>&nbsp;</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>&nbsp;<img src='/dot.png'>$localpart</nobr></td>"; }
elsif($aliased) { $rows{$host} .= "<tr><td class=aliased-hosts valign=top title='Aliased Host'><nobr>&nbsp;<img src='/dot.png'>$localpart</nobr></td>"; }
else { $rows{$host} .= "<tr><td class=hidden-hosts valign=top title='Non Propagated Host'><nobr>&nbsp;<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>&nbsp;</td></tr>\n";
print "<tr><th align=left><nobr>Remote Nodes</nobr></th><th>&nbsp;&nbsp;</th><th>ETX</th><th>&nbsp;&nbsp;</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 .= " &nbsp; <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>&nbsp;<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>&nbsp;</td><td valign=top><table>\n";
# show current neighbors
print "<tr><th align=left><nobr>Current Neighbors</nobr></th><th>&nbsp;&nbsp;</th><th>LQ</th><th>NLQ</th><th>TxMbps</th><th>&nbsp;&nbsp;</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 .= " &nbsp; <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>&nbsp;<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>&nbsp;</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>&nbsp;</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>&nbsp;</td><td align='right'>$olsrTotal</td></tr>\n";
print "<tr><td>Nodes</td><td>&nbsp;</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";

View File

@ -1,12 +1,11 @@
#!/usr/bin/perl
=for comment
#!/usr/bin/lua
--[[
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
Copyright (C) 2015 Conrad Lara
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.
@ -32,67 +31,232 @@
Modified versions must be modified to attribute to the original source
and be marked in reasonable ways as differentiate it from the original
version.
version
=cut
--]]
$debug = 0;
require("aredn.http")
require("aredn.hardware")
local html = require("aredn.html")
local aredn_info = require("aredn.info")
BEGIN {push @INC, '/www/cgi-bin'};
use perlfunc;
local node = aredn_info.get_nvram("node")
if not node then
node = "NOCALL"
end
local wifiiface = aredn.hardware.get_iface_name("wifi")
# collect some variables
$node = nvram_get("node");
$node = "NOCALL" if $node eq "";
if not nixio.fs.stat("/tmp/web") then
nixio.fs.mkdir("/tmp/web")
end
read_postdata();
-- scan start
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};
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
# 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>
-- scan end
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
-- 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>";
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";
</style>
</head>
<body><form method=post action=/cgi-bin/scan enctype='multipart/form-data'>
<center>
]])
if(-f "/tmp/web/autoscan")
{
print "<input type=submit name=stop value=Stop title='Abort continuous scan'>\n";
}
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 "&nbsp;&nbsp;&nbsp;\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("&nbsp;&nbsp;&nbsp;")
html.print("<input type=submit name=auto value=Auto title='Begin continuous scan'>")
end
print "&nbsp;&nbsp;&nbsp;\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("&nbsp;&nbsp;&nbsp;")
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>")

98
files/www/cgi-bin/scan.pl Executable file
View File

@ -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 "&nbsp;&nbsp;&nbsp;\n";
print "<input type=submit name=auto value=Auto title='Begin continuous scan'>\n";
}
print "&nbsp;&nbsp;&nbsp;\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";

View File

@ -1,8 +1,9 @@
#!/usr/bin/perl
=for comment
#!/usr/bin/lua
--[[
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
Copyright (C) 2015 Darryl Quinn
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
@ -30,32 +31,57 @@
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
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;
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>
@ -77,17 +103,17 @@ $header = <<EOF;
}
function hide_spinner() {
\$('#content-loading-spinner').dequeue();
\$('#content-loading-spinner').hide();
\$('#content-overlay').dequeue();
\$('#content-overlay').hide();
$('#content-loading-spinner').dequeue();
$('#content-loading-spinner').hide();
$('#content-overlay').dequeue();
$('#content-overlay').hide();
}
\$(document).ajaxComplete(function() {
$(document).ajaxComplete(function() {
hide_spinner();
});
\$(document).ready(function () {
$(document).ready(function () {
var MAXPOINTS=10;
var chart = new CanvasJS.Chart("chartContainer", {
zoomEnabled: true,
@ -150,7 +176,7 @@ $header = <<EOF;
};
var updateArchiveChart = function () {
\$.getJSON("/cgi-bin/api?chart=archive&device=$parms{device}", function (result) {
$.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) {
@ -160,12 +186,12 @@ $header = <<EOF;
};
var updateRealtimeChart = function () {
\$.getJSON("/cgi-bin/api?chart=realtime&realtime=1&device=$parms{device}", function (result) {
$.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);
$('#snr').html(point.snr);
});
};
@ -216,9 +242,6 @@ $header = <<EOF;
</script>
</head>
EOF
$page = <<EOF;
<body>
<div class="overlay" id="content-overlay"></div>
<div id="content-loading-spinner-wrapper">
@ -231,6 +254,7 @@ $page = <<EOF;
</div>
<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>
@ -242,66 +266,40 @@ $page = <<EOF;
<div id="deviceSelector">
<form name="deviceSelector" method="GET" action="/cgi-bin/signal">
Selected Device:&nbsp;<select name="device" onChange="this.form.submit();">
EOF
]]
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>&nbsp;";
$page = $page . "<button id='toneOff' disabled onclick='toneOff();'>Off</button><br /><br />";
$page = $page . "Pitch:&nbsp;<input type='range' id='tonePitch' name='tonePitch' min='5' max='100'></input><br /><br />";
$page = $page . "Volume:&nbsp;<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>&nbsp;")
html.print("<button id='toneOff' disabled onclick='toneOff();'>Off</button><br /><br />")
html.print("Pitch:&nbsp;<input type='range' id='tonePitch' name='tonePitch' min='5' max='100'></input><br /><br />")
html.print("Volume:&nbsp;<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>")

307
files/www/cgi-bin/signal.pl Executable file
View File

@ -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:&nbsp;<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>&nbsp;";
$page = $page . "<button id='toneOff' disabled onclick='toneOff();'>Off</button><br /><br />";
$page = $page . "Pitch:&nbsp;<input type='range' id='tonePitch' name='tonePitch' min='5' max='100'></input><br /><br />";
$page = $page . "Volume:&nbsp;<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;
}

View File

@ -1,12 +1,11 @@
#!/usr/bin/perl
=for comment
#!/usr/bin/lua
--[[
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
Copyright (C) 2015 Conrad Lara
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.
@ -32,267 +31,382 @@
Modified versions must be modified to attribute to the original source
and be marked in reasonable ways as differentiate it from the original
version.
version
=cut
--]]
$debug = 0;
BEGIN {push @INC, '/www/cgi-bin'};
use perlfunc;
use ucifunc;
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")
# 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;
-- helpers start
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 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
$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_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
$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 "&nbsp;&nbsp;&nbsp;";
print "<input type=submit name=refresh value=Refresh title='Refresh this page'>\n";
if($config eq "mesh")
{
print "&nbsp;&nbsp;&nbsp;";
print "<button type=button onClick='window.location=\"mesh\"' title='See what is on the mesh'>Mesh Status</button>\n";
if ( ! $wifi_disable )
{
print "&nbsp;&nbsp;&nbsp;";
print "<button type=button onClick='window.location=\"scan\"' title='See what wireless networks are nearby'>WiFi Scan</button>\n";
}
}
print "&nbsp;&nbsp;&nbsp;";
print "<button type=button onClick='window.location=\"setup\"' title='Configure this node'>Setup</button>\n";
print "&nbsp;&nbsp;&nbsp;";
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 &gt Administration</li>";
print "<li>Obtain the blockknownencryption package from the AREDN&trade; 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>";
}
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
{
$str = "<th align=right><nobr>Primary address</nobr></th><td>$ip <small>$cidr</small><br>";
}
return signal, noise
end
end
# $str .= "<small><nobr>" . get_ip6_addr($wifi_iface) . "</nobr></small></td>";
push @col1, $str;
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
# 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);
}
-- helpers 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 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
{
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;
}
}
local cursor = uci.cursor()
$ip = get_default_gw();
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
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>";
}
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
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>";
}
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
if ( ! $wifi_disable )
{
$str = "<th align=right><nobr>SSID</nobr></th><td>$wifi_ssid";
push @col1, $str . "</td>";
-- post data
$str = "<th align=right><nobr>Channel</nobr></th><td>$wifi_channel";
push @col1, $str . "</td>";
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
$str = "<th align=right><nobr>Bandwidth</nobr></th><td>$wifi_chanbw MHz";
push @col1, $str . "</td>";
}
-- generate page
# right column - system info
http_header()
html.header(node .. " status", true)
html.print("<body><form method='post' action='/cgi-bin/status' enctype='multipart/form-data'>")
html.print("<center>")
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 .= "&nbsp;&nbsp;&nbsp;";
$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;
}
html.alert_banner()
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>";
-- 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>")
$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>";
-- nav buttons
html.print("<nobr>")
html.print("<a href='/help.html' target='_blank'>Help</a>")
html.print("&nbsp;&nbsp;")
html.print("<input type=submit name=refresh value=Refresh title='Refresh this page'>")
if config == "mesh" then
html.print("&nbsp;&nbsp;")
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("&nbsp;&nbsp;")
html.print("<button type=button onClick='window.location=\"scan\"' title='See what wireless networks are nearby'>WiFi Scan</button>")
end
end
html.print("&nbsp;&nbsp")
html.print("<button type=button onClick='window.location=\"setup\"' title='Configure this node'>Setup</button>")
html.print("&nbsp;&nbsp;")
html.print("<select name=\"css\" size=\"1\" onChange=\"form.submit()\" >")
css_options()
html.print("</select>")
html.print("</nobr")
$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>";
html.print("<input type=hidden name=reload value=reload>")
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
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 &gt Administration</li>")
html.print("<li>Obtain the blockknownencryption package from the AREDN&trade; 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
# now print the tables
-- status display
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";
local col1 = {}
local col2 = {}
# end
print "</center>\n";
print "</form>\n";
-- left column - network interface info
show_debug_info();
show_parse_errors();
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
col1[#col1 + 1] = "<th align=right><nobr>Wifi address</nobr></th><td>" .. ip .. " <small>/ " .. cidr .. "</small><br>"
end
page_footer();
print "</body>\n";
print "</html>\n";
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
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
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
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
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 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
-- right column - system info
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] .. "&nbsp;&nbsp;&nbsp;<button type=button onClick='window.location=\"signal?realtime=1\"' title='Display continuous or archived signal strength on a chart'>Charts</button></nobr></td>"
end
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>";
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
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>"
-- now print the tables
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>")
-- end
html.print("</center></form>")
html.footer()
html.print("</body></html>")

298
files/www/cgi-bin/status.pl Executable file
View File

@ -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 "&nbsp;&nbsp;&nbsp;";
print "<input type=submit name=refresh value=Refresh title='Refresh this page'>\n";
if($config eq "mesh")
{
print "&nbsp;&nbsp;&nbsp;";
print "<button type=button onClick='window.location=\"mesh\"' title='See what is on the mesh'>Mesh Status</button>\n";
if ( ! $wifi_disable )
{
print "&nbsp;&nbsp;&nbsp;";
print "<button type=button onClick='window.location=\"scan\"' title='See what wireless networks are nearby'>WiFi Scan</button>\n";
}
}
print "&nbsp;&nbsp;&nbsp;";
print "<button type=button onClick='window.location=\"setup\"' title='Configure this node'>Setup</button>\n";
print "&nbsp;&nbsp;&nbsp;";
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 &gt Administration</li>";
print "<li>Obtain the blockknownencryption package from the AREDN&trade; 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 .= "&nbsp;&nbsp;&nbsp;";
$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";