#!/usr/bin/env lua --[[ Copyright (C) 2013-2017 LibreMesh.org This is free software, licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 Copyright 2017 Marcos Gutierrez Copyright 2017 Nicolas Echaniz ]]-- require "ubus" local json = require 'luci.jsonc' local utils = require("lime.utils") local function shell(command) -- TODO(nicoechaniz): sanitize or evaluate if this is a security risk local handle = io.popen(command) local result = handle:read("*a") handle:close() return result end local conn = ubus.connect() if not conn then error("Failed to connect to ubus") end local function printJson (obj) print(json.stringify(obj)) end local function file_exists(file) -- check if the file exists local f = io.open(file, "rb") if f then f:close() end return f ~= nil end local function list_from_file(file) -- get all lines from a file with one value per line and return a list type table -- return an empty table if the file does not exist if not file_exists(file) then return {} end local list = {} for line in io.lines(file) do table.insert(list, line) end return list end local function _nslookup_working() local shell_output = shell("nslookup google.com | grep Name -A2 | grep Address") return shell_output end local function _get_loss(host, ip_version) local ping_cmd = "ping" if ip_version then if ip_version == 6 then ping_cmd = "ping6" end end local shell_output = shell(ping_cmd.." -q -i 0.1 -c4 -w2 "..host) local loss = "100" if shell_output ~= "" then loss = shell_output:match("(%d*)%% packet loss") end return loss end local function _get_metrics(target) local result = {} local node = target local loss = nil local shell_output = "" if utils.is_installed("lime-proto-bmx6") then loss = _get_loss(node..".mesh", 6) shell_output = shell("netperf -6 -l 10 -H "..node..".mesh| tail -n1| awk '{ print $5 }'") elseif utils.is_installed("lime-proto-babeld") then loss = _get_loss(node, 4) shell_output = shell("netperf -l 10 -H "..node.."| tail -n1| awk '{ print $5 }'") end local bw = 0 if shell_output ~= "" then bw = shell_output:match("[%d.]+") end result.loss = loss result.bandwidth = bw result.status = "ok" return result end local function get_metrics(msg) local result = _get_metrics(msg.target) printJson(result) end local function _get_gateway() local result = {} local gw = nil local internet_path_file = io.open("/etc/last_internet_path", "r") if internet_path_file then local path_content = assert(internet_path_file:read("*a"), nil) internet_path_file:close() path = json.parse(path_content) or nil if utils.tableLength(path) > 0 then return { status="ok", gateway=path[utils.tableLength(path)] } end end return {status="error", error={msg="Not found. No gateway available.", code="1"}} end local function get_gateway(msg) printJson(_get_gateway()) end local function get_last_internet_path(msg) local internet_path_file = io.open("/etc/last_internet_path", "r") if internet_path_file then path_content = assert(internet_path_file:read("*a"), nil) internet_path_file:close() path = json.parse(path_content) or nil local result = {} if path ~= nil then result.path = path result.status = "ok" printJson(result) end else printJson({status="error", error={msg="Not found. No known Internet path.", code="1"}}) end end local function get_path(msg) get_last_internet_path() end local function get_internet_status(msg) local result = {} local lossV4 = _get_loss("4.2.2.2") if lossV4 ~= "100" then result.IPv4 = { working=true } else result.IPv4 = { working=false } end local lossV6 = _get_loss("2600::", 6) if lossV6 ~= "100" then result.IPv6 = { working=true } else result.IPv6 = { working=false } end local lookup_output = _nslookup_working() if lookup_output ~= "" then result.DNS = { working=true } else result.DNS = { working=false } end result.status = "ok" printJson(result) end local function get_station_traffic(msg) local iface = msg.iface local mac = msg.station_mac local result = {} local traffic = shell("iw "..iface.." station get "..mac.." | grep bytes | awk '{ print $3}'") words = {} for w in traffic:gmatch("[^\n]+") do table.insert(words, w) end rx = words[1] tx = words[2] result.station = mac result.rx_bytes = tonumber(rx, 10) result.tx_bytes = tonumber(tx, 10) result.status = "ok" printJson(result) end local methods = { get_metrics = { target = 'value' }, get_gateway = { no_params = 0 }, get_path = { target = 'value' }, get_last_internet_path = { no_params = 0 }, get_internet_status = { no_params = 0 }, get_station_traffic = { iface = 'value', station_mac = 'value' } } if arg[1] == 'list' then printJson(methods) end if arg[1] == 'call' then local msg = io.read() msg = json.parse(msg) if arg[2] == 'get_metrics' then get_metrics(msg) elseif arg[2] == 'get_gateway' then get_gateway(msg) elseif arg[2] == 'get_path' then get_path(msg) elseif arg[2] == 'get_last_internet_path' then get_last_internet_path(msg) elseif arg[2] == 'get_internet_status' then get_internet_status(msg) elseif arg[2] == 'get_station_traffic' then get_station_traffic(msg) else printJson({ error = "Method not found" }) end end