Commit 5bf83196 authored by Eric - kg6wxc's avatar Eric - kg6wxc
Browse files

New Polling script! It will better catch all sysinfo.json values.

It will also be easier to add more values later.

The new script does not yet find the LAN IP Address, with all the new devices there are several interfaces that could be the LAN interface.
LAN IP info isn't really needed for mapping anyways.

Added new stable firmware to the default user-settings.ini file, please update your file.
Added a new setting in the ini file "errorsInCron" that will have the script send errors when it is running from cron.
The system running the map script will have to have a local MTA (at least) setup, the messages will be received in the local mail of the user the script is running as (this is default behavior for cron).

Fixes #19
Fixes #12 (hopefully)
Fixes #10 (hopefully)

Enjoy!
parent df604384
#!/usr/bin/env php
<?php
/*************************************************************************************
* get-map-info script v4 by kg6wxc\eric satterlee kg6wxc@gmail.com
* March 2019
* Licensed under GPLv3 or later
* This script is the heart of kg6wxcs' mesh map system.
* bug fixes, improvements and corrections are welcomed!
*
* One Script to rule them all!!
*
* see CHANGELOG.md for notes
**************************************************************************************/
/******
* This file is part of the Mesh Mapping System.
* The Mesh Mapping System 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, either version 3 of the License, or
* (at your option) any later version.
*
* The Mesh Mapping System 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 The Mesh Mapping System. If not, see <http://www.gnu.org/licenses/>.
******/
if (PHP_SAPI !== 'cli') {
$file = basename($_SERVER['PHP_SELF']);
exit("<style>html{text-align: center;}p{display: inline;}</style>
<br><strong>This script ($file) should only be run from the
$file = basename($_SERVER['PHP_SELF']);
exit("<style>html{text-align: center;}p{display: inline;}</style>
<br><strong>This script ($file) should only be run from the
<p style='color: red;'>command line</p>!</strong>
<br>exiting...");
}
$mtimeStart = microtime(true);
/*************************************************************************************
* get-map-info script v3 by kg6wxc\eric satterlee kg6wxc@gmail.com
* Licensed under GPLv3 or later
* This script is the heart of kg6wxcs' mesh map system.
* bug fixes, improvements and corrections are welcomed!
*
* One Script to rule them all!!
*
* see CHANGELOG.md for notes
**************************************************************************************/
/******
* This file is part of the Mesh Mapping System.
* The Mesh Mapping System 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, either version 3 of the License, or
* (at your option) any later version.
*
* The Mesh Mapping System 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 The Mesh Mapping System. If not, see <http://www.gnu.org/licenses/>.
******/
//Increase PHP memory limit to 128M (you may need more if you are connected to a "Mega Mesh" :) )
//this should be moved to the ini file maybe
ini_set('memory_limit', '128M');
/***********************************************************************
*DO NOT CHANGE ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU ARE DOING!!!!
************************************************************************/
$INCLUDE_DIR = "..";
//check for users user-settings.ini file and use it if it exists
......@@ -113,6 +112,18 @@ if ($TEST_MODE_NO_SQL) {
$getNodeInfo = 0;
}
//output error messages only in the cron job
//if your system has an MTA installed you should get local emails from
//this script if there are errors, you can turn this on or off in the ini file.
//(not quite done yet)
$errOut = "0";
if(isset($GLOBALS['USER_SETTINGS']['errorsInCron'])) {
$errOut = $GLOBALS['USER_SETTINGS']['errorsInCron'];
}else {
$errOut = "0";
}
//(WiP)checks for some things we need to run
//(currently only really checks for the mysqli php extension
wxc_checkConfigs();
......@@ -130,95 +141,94 @@ $sql_db_tbl_node = $USER_SETTINGS['sql_db_tbl_node'];
$sql_db_tbl_topo = $USER_SETTINGS['sql_db_tbl_topo'];
if ($do_sql) {
//an sql connection that we can reuse...
//$sql_connection = wxc_connectToMySQL();
wxc_connectToMySQL();
//check for new or changed database items (tables, columns,etc)
wxc_checkDB();
//an sql connection that we can reuse...
//$sql_connection = wxc_connectToMySQL();
wxc_connectToMySQL();
//check for new or changed database items (tables, columns,etc)
wxc_checkDB();
}else {
if ($TEST_MODE_NO_SQL) {
wxc_echoWithColor("SQL Server access disabled!", "red");
echo "\n";
}
if ($TEST_MODE_NO_SQL) {
wxc_echoWithColor("SQL Server access disabled!", "red");
echo "\n";
}
}
//This controls when certain parts of the script run.
//We check the DB for the time we last checked and only run if we need to.
//intervals are now controled via the ini file.
//thanks K6GSE!
//intervals are now controled via the ini file thanks to K6GSE!
if ($do_sql) {
//if $do_sql is set to 1, check when we last polled all the known nodes, if it was more than the interval set the variable to 1
$lastRunGetNodeInfo = wxc_scriptGetLastDateTime("NODEINFO", "node_info");
if ($lastRunGetNodeInfo) {
if ($USER_SETTINGS['node_polling_interval'] > 0) {
$intervalNODE = date_diff($lastRunGetNodeInfo, $currentTime);
$intervalNodeInMinutes = $intervalNODE->days * 24 * 60;
$intervalNodeInMinutes += $intervalNODE->h * 60;
$intervalNodeInMinutes += $intervalNODE->i;
if ($intervalNodeInMinutes >= intval($USER_SETTINGS['node_polling_interval'])) {
if ($TEST_MODE_WITH_SQL) {
echo "It has been " . $USER_SETTINGS['node_polling_interval'] . " or more minutes since this script polled all the nodes\n";
echo "Set to poll nodes.\n";
}
$getNodeInfo = 1;
}
}
}else {
//probably never run before, lets get some data!!
echo "Set to poll nodes.\n";
$getNodeInfo = 1;
}
//if $do_sql is set to 1, check when we last got link info, if it was more than the interval set the variable to 1
$lastRunGetLinkInfo = wxc_scriptGetLastDateTime("LINKINFO", "topology");
if($lastRunGetLinkInfo) {
if ($USER_SETTINGS['link_update_interval'] > 0) {
$intervalLINK = date_diff($lastRunGetLinkInfo, $currentTime);
if ($intervalLINK->i >= intval($USER_SETTINGS['link_update_interval'])) {
if ($TEST_MODE_WITH_SQL) {
echo "It has been " . $USER_SETTINGS['link_update_interval'] . " or more minutes since this script got the link info\n";
echo "Set to get network linking info.\n\n";
}
$getLinkInfo = 1;
}
}
}else {
//probably never run before, let's get some data!
echo "Set to get network linking info.\n\n";
$getLinkInfo = 1;
}
//if $do_sql is set to 1, check when we last polled all the known nodes, if it was more than the interval set the variable to 1
$lastRunGetNodeInfo = wxc_scriptGetLastDateTime("NODEINFO", "node_info");
if ($lastRunGetNodeInfo) {
if ($USER_SETTINGS['node_polling_interval'] > 0) {
$intervalNODE = date_diff($lastRunGetNodeInfo, $currentTime);
$intervalNodeInMinutes = $intervalNODE->days * 24 * 60;
$intervalNodeInMinutes += $intervalNODE->h * 60;
$intervalNodeInMinutes += $intervalNODE->i;
if ($intervalNodeInMinutes >= intval($USER_SETTINGS['node_polling_interval'])) {
if ($TEST_MODE_WITH_SQL) {
echo "It has been " . $USER_SETTINGS['node_polling_interval'] . " or more minutes since this script polled all the nodes\n";
echo "Set to poll nodes.\n";
}
$getNodeInfo = 1;
}
}
}else {
//probably never run before, lets get some data!!
echo "Set to poll nodes.\n";
$getNodeInfo = 1;
}
//if $do_sql is set to 1, check when we last got link info, if it was more than the interval set the variable to 1
$lastRunGetLinkInfo = wxc_scriptGetLastDateTime("LINKINFO", "topology");
if($lastRunGetLinkInfo) {
if ($USER_SETTINGS['link_update_interval'] > 0) {
$intervalLINK = date_diff($lastRunGetLinkInfo, $currentTime);
if ($intervalLINK->i >= intval($USER_SETTINGS['link_update_interval'])) {
if ($TEST_MODE_WITH_SQL) {
echo "It has been " . $USER_SETTINGS['link_update_interval'] . " or more minutes since this script got the link info\n";
echo "Set to get network linking info.\n\n";
}
$getLinkInfo = 1;
}
}
}else {
//probably never run before, let's get some data!
echo "Set to get network linking info.\n\n";
$getLinkInfo = 1;
}
}
//check the database to see if we are already polling nodes
//this trys to prevent 2 polling runs at once
//it does nothing in "--test-mode-no-sql"
if ($do_sql) {
$currently_polling_nodes = wxc_getMySql("SELECT script_last_run, currently_running from map_info WHERE id = 'NODEINFO'");
if (is_null($currently_polling_nodes['currently_running'])) {
$currently_polling_nodes['currently_running'] = 0;
$getNodeInfo = 1;
}elseif ($currently_polling_nodes['currently_running'] == 1) {
$getNodeInfo = 0;
}
//hopefully catch a stalled polling run after 3 * node_polling_interval has expired.
//something may have gone wonky and the node polling run never completed and never had a chance
//to unset the "currently_running" bit in the DB,
//this *should* catch that and and just run the node polling again
if ($currently_polling_nodes['currently_running'] == 1 && $intervalNodeInMinutes >= intval($USER_SETTINGS['node_polling_interval']) * 3) {
$currently_polling_nodes['currently_running'] = 0;
$getNodeInfo = 1;
}
$currently_polling_nodes = wxc_getMySql("SELECT script_last_run, currently_running from map_info WHERE id = 'NODEINFO'");
if (is_null($currently_polling_nodes['currently_running'])) {
$currently_polling_nodes['currently_running'] = 0;
$getNodeInfo = 1;
}elseif ($currently_polling_nodes['currently_running'] == 1) {
$getNodeInfo = 0;
}
//hopefully catch a stalled polling run after 3 * node_polling_interval has expired.
//something may have gone wonky and the node polling run never completed and never had a chance
//to unset the "currently_running" bit in the DB,
//this *should* catch that and and just run the node polling again
if ($currently_polling_nodes['currently_running'] == 1 && $intervalNodeInMinutes >= intval($USER_SETTINGS['node_polling_interval']) * 3) {
$currently_polling_nodes['currently_running'] = 0;
$getNodeInfo = 1;
}
}
//check for old outdated node info (intervals will be set in the ini file)
//check for old outdated node info (intervals are set in the ini file)
$do_expire = $USER_SETTINGS['expire_old_nodes'];
if ($do_sql && $do_expire) {
wxc_checkOldNodes();
wxc_checkOldNodes();
}
if ($do_sql) {
wxc_removeIgnoredNodes();
wxc_removeIgnoredNodes();
}
$node = "";
if ($getNodeInfo) {
//this section is what goes out to each node on the mesh and asks for it's info
//this is really the heart of the mapping system, without this (and the sysinfo.json file),
......@@ -232,584 +242,530 @@ if ($getNodeInfo) {
$meshNodes = wxc_netcat($USER_SETTINGS['localnode'], "2004", null, "ipOnly");
if ($meshNodes) {
/* TESTING IDEA */
/*
$ipAddrArray = explode("\n", $meshNodes);
$ipAddrArrayChunks = array_chunk($ipAddrArray, $USER_SETTINGS['numParallelThreads']);
foreach($ipAddrArrayChunks as $chunk => $ipList) {
foreach($ipList as $ipAddr) {
//echo "";
}
}
foreach($ipAddrArray as $ipAddr) {
//echo "";
}
*/
/* END TESTING */
//
//parallel polling will have to at least start here
//going to have to break the IP list up into sections
//or create some kind of "container" that only allow so many to run at a time...
//
foreach (preg_split("/((\r?\n)|(\r\r?))/", $meshNodes) as $line) {
list ($ipAddr) = explode("\n", $line);
//check for nodes that we know will not have the info we are going to request and skip them
if ($do_sql) {
if (wxc_getMySql("SELECT ip FROM hosts_ignore WHERE ip = '$ipAddr'")) {
continue;
}
}
//copy to new var name (I dont feel like editing it all right now)
$nodeName = $ipAddr;
if ($USER_SETTINGS['node_polling_parallel']) {
shell_exec("php $INCLUDE_DIR/scripts/parallel_node_polling.php $ipAddr $do_sql 0 > /dev/null 2>/dev/null &");
//for($count = 1; $count <= 20; $count++) {
// $ipAddrList .= $ipAddr . "\n";
//}
// $parallel_pids = array();
//$parallel_pids[] = trim(shell_exec("php $INCLUDE_DIR/scripts/parallel_node_polling.php $ipAddr $do_sql 0 > /dev/null 2>/dev/null & echo $!"));
// $parallel_pids[] = trim(shell_exec("php $INCLUDE_DIR/scripts/parallel_node_polling.php $ipAddr $do_sql 0 > /dev/null 2>/dev/null & echo $!"));
//$parallel_pids[] = shell_exec("php $INCLUDE_DIR/scripts/parallel_node_polling.php $ipAddr $do_sql 0 > /dev/null & echo $!");
//var_dump($parallel_pids);
}else {
//get the sysinfo.json file from the node being polled.
$sysinfoJson = @file_get_contents("http://$ipAddr:8080/cgi-bin/sysinfo.json?services_local=1"); //get the .json file
list ($ipAddr) = explode("\n", $line);
//check if we got anything back, if not, try to tell why.
if($sysinfoJson === FALSE) {
$error = error_get_last();
wxc_checkErrorMessage($error, $nodeName);
//just skip to the next IP since there was an error
continue;
}else {
//node is there, get all the info we can
//get all the data from the json file and decode it
$result = json_decode($sysinfoJson,true);
//if there's nothing really there just skip to the next IP
if (!$result || empty($result)) {
//$host = wxc_resolveIP($ipAddr);
if($testNodePolling) {
wxc_echoWithColor("The json file is empty for node: " . $ipAddr . " " . wxc_resolveIP($ipAddr) . "\n", "red");
echo "\n";
}
//check for nodes that we know will not have the info we are going to request and skip them
if ($do_sql) {
if (wxc_getMySql("SELECT ip FROM hosts_ignore WHERE ip = '$ipAddr'")) {
continue;
}
//is RF enabled? (default to "on")
$meshRF = "on";
//pull out API version first
//some user-created json files leave out the api_version section
//so lets set a default
if (isset($result['api_version'])) {
$api_version = $result['api_version'];
}else {
//just make it 0.0.0 by default, it will always fail a compare_version then.
$api_version = "0.0.0";
}
//let's see what node we are dealing with
//sometimes this might be blank, catch it
if (version_compare($api_version, "1.5", "=")) {
if (isset($result['node_details']['node'])) {
$node = $result['node_details']['node'];
}else {
$node = $result['node'];
}
if (isset($result['meshrf']['status'])) {
if ($result['meshrf']['status'] == "off") {
$meshRF = "off";
}
}
}else {
$node = $result['node'];
}
//check API version first!
if (version_compare($api_version, "1.5", "=")) {
if (isset($result['location']['lat'])) {
$lat = $result['location']['lat'];
}else {
$lat = $result['lat'];
}
}else {
$lat = $result['lat'];
}
if (version_compare($api_version, "1.5", "=")) {
if (isset($result['location']['lon'])) {
$lon = $result['location']['lon'];
}else {
$lon = $result['lon'];
}
}else {
$lon = $result['lon'];
}
}
//copy to new var name (I dont feel like editing it all right now)
$nodeName = $ipAddr;
if ($USER_SETTINGS['node_polling_parallel']) {
//if it's nothing other than the node name, it's some other device
//or something else entirely...
//people hack things onto the mesh all the time
//
//kg6wxc is *not* guilty of such things... :)
//
//just a few checks for nothing usually catches it.
//it probably wont actually be this way in the end
//newer PHP supports threads
//***NOTE*** this might not be needed any more
if ($node && $lat == "" && $lon == "" && $result['api_version'] == "") {
continue;
}
//disabled for now
//shell_exec("php $INCLUDE_DIR/scripts/parallel_node_polling.php $ipAddr $do_sql 0 > /dev/null 2>/dev/null &");
//this only seems to affect some nodes.
//if lat || lon is blank, make it "0"
//this was sometimes screwing up the SQL writing function
//but not always of course.
if (empty($lat)) {
//if ($result['lat'] == "") {
$lat = 0.0;
}
//else {
// $lat = $result['lat;
//}
if (empty($lon)) {
//if ($result['lon'] == "") {
$lon = 0.0;
}
//else {
// $lon = $result['lon'];
//}
}else {
//save json data to some variables
//probably don't really need to do this, but it is what it is for now...
//get sysinfo.json fron node
//this is the heart of the mapping system
$sysinfoJson = @file_get_contents("http://$ipAddr:8080/cgi-bin/sysinfo.json?services_local=1");
//check for new sysinfo.json API
if (version_compare($api_version, "1.5", ">=")) {
if (isset($result['meshrf']['chanbw'])) {
$chanbw = $result['meshrf']['chanbw'];
}
else {
$chanbw = "0";
}
if (isset($result['meshrf']['status'])) {
if ($result['meshrf']['status'] == "off") {
$meshRF = "off";
$ssid = "NONE";
}
}else {
$ssid = $result['meshrf']['ssid'];
}
if ($meshRF == "off") {
$channel = "NONE";
}else {
$channel = $result['meshrf']['channel'];
}
$board_id = $result['node_details']['board_id'];
$firmware_version = $result['node_details']['firmware_version'];
$model = $result['node_details']['model'];
$firmware_mfg = $result['node_details']['firmware_mfg'];
$tunnel_installed = $result['tunnels']['tunnel_installed'];
$active_tunnel_count = $result['tunnels']['active_tunnel_count'];
if (isset($result['location']['grid_square'])) {
$grid_square = $result['location']['grid_square'];
}
else {
$grid_square = $result['grid_square'];
}
if($sysinfoJson === FALSE) {
$error = error_get_last();
wxc_checkErrorMessage($error, $nodeName);
//just skip to the next IP since there was an error
continue;
}else {
if (isset($result['chanbw'])) {
$chanbw = $result['chanbw'];
}
else {
$chanbw = "0";
}
if (isset($result['ssid'])) {
$ssid = $result['ssid'];
}else {
$ssid = "None";
}
if (isset($result['channel'])) {
$channel = $result['channel'];
}else {
$channel = "N/A";
}
if (isset($result['board_id'])) {
$board_id = $result['board_id'];
}
if (isset($result['firmware_version'])) {
$firmware_version = $result['firmware_version'];
}
if (isset($result['model'])) {
$model = $result['model'];
}
if (isset($result['firmware_mfg'])) {
$firmware_mfg = $result['firmware_mfg'];
}
if (isset($result['tunnel_installed'])) {
$tunnel_installed = $result['tunnel_installed'];
}
if (isset($result['active_tunnel_count'])) {
$active_tunnel_count = $result['active_tunnel_count'];
}
if (isset($result['grid_square'])) {
$grid_square = $result['grid_square'];
}
//get all the data from the json file and decode it
//remove any "funny" characters from the sysinfo.json string *BEFORE* it gets decoded
//these mainly occur when people get fancy with the description field
//use html name codes! do not use the hex codes!
$sysinfoJson = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $sysinfoJson);
$result = json_decode($sysinfoJson,true);
//catch some weird ones just in case...
//If you make a custom json file, try to make sure you conform to the AREDN api_versions.
//if not, this might catch it... maybe... we'll see.
if (isset($result['node_details']['board_id'])) {
$board_id = $result['node_details']['board_id'];
}
if (isset($result['node_details']['firmware_version'])) {
$firmware_version = $result['node_details']['firmware_version'];
}
if (isset($result['node_details']['model'])) {
$model = $result['node_details']['model'];
}
if (isset($result['tunnels']['tunnel_installed'])) {
$tunnel_installed = $result['tunnels']['tunnel_installed'];
}
if (isset($result['tunnels']['active_tunnel_count'])) {
$active_tunnel_count = $result['tunnels']['active_tunnel_count'];
}
if (isset($result['node_details']['firmware_mfg'])) {
$firmware_mfg = $result['node_details']['firmware_mfg'];
}
}
//had to screen scrape the status page for this info before
//which required an additional call to the node
//now it is here! :)
$uptime = "NotAvailable";
$loadavg = "NotAvailable";
if (version_compare($api_version, "1.2", ">=")) {
if (isset($result['sysinfo']['uptime'])) {
$uptime = $result['sysinfo']['uptime'];
}
if (isset($result['sysinfo']['loads'])) {
$loadavg = serialize($result['sysinfo']['loads']); // <-- this is an array that has been serialized!!
}
}
//local service listing are now in the json file!! yay!
//this required an additional call to port 9090 on the node before
$services = "NotAvailable";
if (version_compare($api_version, "1.3", ">=")) {
if (isset($result['services_local'])) {
$services = serialize($result['services_local']); // <-- this is an array that has been serialized!
}
}
//this only seems to affect some nodes.
//if grid_square is blank, make it "none"
//this was sometimes screwing up the SQL writing function
//but not always of course.
if ($grid_square == "") {
$grid_square = "none";
}
//else {
// $grid_square = $result['grid_square'];
//}
//W6BI requested this info to be added, so here it is now. :)
//current ip/mac address info
if ($result['interfaces']) {
foreach($result['interfaces'] as $interface => $infInfo) {
$eth = "eth0";
$wlan = "wlan0";
if ($model == "Ubiquiti Nanostation M XW" || $model == "AirRouter " || $model == "NanoStation M5 XW ") {
//"AirRouter " model name bug caught and fixed by Mark, N2MH 13 March 2017.
$eth = "eth0.0";
}
if ($model == "MikroTik RouterBOARD 952Ui-5ac2nD ") {
$eth = "eth1.0";
}
//!!!THERE IS ONLY 1 Wireless interface available on this device so far!!!
//this will be changed in the near future.
if ($model == "MikroTik RouterBOARD 952Ui-5ac2nD ") {
$wlan = "wlan1";
}
if(is_array($result)) {
//
// This should catch some of those pesky ones
// finally!
//parse json data (this is where the real heart is)
//
if (is_numeric($interface)) {
if ($infInfo['name'] == $eth) {
if (isset($infInfo['ip'])) {
if ($infInfo['ip'] == 'none') {
$lan_ip = "NotAvailable";
//variables and defaults
$meshRF = "on";
$ethInf = "eth0";
$wlanInf = "wlan0";
$node = "";
$wlan_ip = "";
$uptime = "Not Available";
$loadavg = "Not Available";