Convert gluon-announce Lua code to C modules, rename to gluon-respondd

gluon-announced and gluon-announce are merged to gluon-respondd.
This commit is contained in:
Matthias Schiffer 2016-02-02 06:38:03 +01:00
parent 840d07dd48
commit 9004028cb4
68 changed files with 1502 additions and 652 deletions

View File

@ -8,7 +8,7 @@ PACKAGES_OPENWRT_COMMIT=f8a70fc188673d0ae8739b0a3095f7f61335fc10
PACKAGES_OPENWRT_BRANCH=for-15.05
PACKAGES_GLUON_REPO=git://github.com/freifunk-gluon/packages.git
PACKAGES_GLUON_COMMIT=bdb56bba02168a7fdd7c8bbf380ae59a4febac7c
PACKAGES_GLUON_COMMIT=fd06c7d67da69713f0361dadab16393c26cb609b
PACKAGES_ROUTING_REPO=git://github.com/openwrt-routing/packages.git
PACKAGES_ROUTING_COMMIT=ae65d4fe027592652376f8dbd3ff2ef37f5a84bc

View File

@ -11,7 +11,7 @@ include $(INCLUDE_DIR)/package.mk
define Package/gluon-alfred
SECTION:=gluon
CATEGORY:=Gluon
DEPENDS:=+gluon-core +gluon-announced +gluon-neighbour-info +micrond +alfred
DEPENDS:=+gluon-core +gluon-respondd +gluon-neighbour-info +micrond +alfred
TITLE:=Configure alfred
endef

View File

@ -1,32 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-announce
PKG_VERSION:=1
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/gluon-announce
SECTION:=gluon
CATEGORY:=Gluon
DEPENDS:=+gluon-core +luci-lib-jsonc +lua-ethtool-stats
TITLE:=Lua scripts announcing various information
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/gluon-announce/install
$(CP) ./files/* $(1)/
endef
$(eval $(call BuildPackage,gluon-announce))

View File

@ -1 +0,0 @@
return require('gluon.util').node_id()

View File

@ -1 +0,0 @@
return require('platform_info').get_model()

View File

@ -1,14 +0,0 @@
local n = 0
local cpus = util.readline(io.open('/sys/devices/system/cpu/online'))
for entry in cpus:gmatch('([^,]+)') do
local x, y = entry:match('(%d+)-(%d+)')
if x then
n = n + tonumber(y) - tonumber(x) + 1
else
n = n + 1
end
end
return n

View File

@ -1 +0,0 @@
return uci:get_first('system', 'system', 'hostname')

View File

@ -1 +0,0 @@
return require('gluon.sysconfig').primary_mac

View File

@ -1 +0,0 @@
return require('gluon.util').node_id()

View File

@ -1,4 +0,0 @@
return {
base = 'gluon-' .. util.readline(io.open('/lib/gluon/gluon-version')),
release = util.readline(io.open('/lib/gluon/release')),
}

View File

@ -1 +0,0 @@
return require('gluon.site_config').site_code

View File

@ -1 +0,0 @@
return tonumber(util.readline(io.open('/proc/uptime')):match('^[^ ]+ ([^ ]+)'))

View File

@ -1 +0,0 @@
return tonumber(util.readline(io.open('/proc/loadavg')):match('^([^ ]+) '))

View File

@ -1,13 +0,0 @@
local data = io.open('/proc/meminfo'):read('*a')
local fields = {}
for k, v in data:gmatch('([^\n:]+):%s*(%d+) kB') do
fields[k] = tonumber(v)
end
return {
total = fields.MemTotal,
free = fields.MemFree,
buffers = fields.Buffers,
cached = fields.Cached,
}

View File

@ -1 +0,0 @@
return require('gluon.util').node_id()

View File

@ -1,3 +0,0 @@
local running, total = util.readline(io.open('/proc/loadavg')):match('^[^ ]+ [^ ]+ [^ ]+ (%d+)/(%d+)')
return { running = tonumber(running), total = tonumber(total) }

View File

@ -1,4 +0,0 @@
local fs = require "nixio.fs"
local st = fs.statvfs("/")
return 1 - st.bfree / st.blocks

View File

@ -1 +0,0 @@
return tonumber(util.readline(io.open('/proc/uptime')):match('^([^ ]+) '))

View File

@ -1,68 +0,0 @@
#!/usr/bin/lua
module('gluon.announce', package.seeall)
fs = require 'nixio.fs'
util = require 'gluon.util'
model_uci = require 'luci.model.uci'
local collect_dir
local function collect_entry(entry)
if fs.stat(entry, 'type') == 'dir' then
return collect_dir(entry)
else
return loadfile(entry)
end
end
function collect_dir(dir)
local fns = {}
for entry in fs.dir(dir) do
if entry:sub(1, 1) ~= '.' then
collectgarbage()
local fn, err = collect_entry(dir .. '/' .. entry)
if fn then
fns[entry] = fn
else
io.stderr:write(err, '\n')
end
end
end
return function ()
local ret = { [{}] = true }
for k, v in pairs(fns) do
collectgarbage()
local ok, val = pcall(setfenv(v, _M))
if ok then
ret[k] = val
else
io.stderr:write(val, '\n')
end
end
collectgarbage()
return ret
end
end
function collect(dir)
local f = collect_dir(dir)
return function ()
_M.uci = model_uci.cursor()
ret = f()
_M.uci = nil
collectgarbage()
return ret
end
end

View File

@ -1,32 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-announced
PKG_VERSION:=2
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/gluon-announced
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Provides node information to the network
DEPENDS:=+gluon-announce +respondd +lua-deflate
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/gluon-announced/install
$(CP) ./files/* $(1)/
endef
$(eval $(call BuildPackage,gluon-announced))

View File

@ -1,71 +0,0 @@
local announce = require 'gluon.announce'
local deflate = require 'deflate'
local json = require 'luci.jsonc'
local util = require 'luci.util'
local nixio = require 'nixio'
local fs = require 'nixio.fs'
local memoize = {}
nixio.chdir('/lib/gluon/announce/')
for dir in fs.glob('*.d') do
local name = dir:sub(1, -3)
memoize[name] = {
collect = announce.collect(dir),
-- tonumber will return 0 for invalid inputs
cache_time = tonumber(util.trim(fs.readfile(name .. '.cache') or ''))
}
end
local function collect(type, timestamp)
local c = memoize[type]
if not c then
return nil
end
if c.cache_timeout and timestamp < c.cache_timeout then
return c.cache
else
local ret = c.collect()
if c.cache_time then
c.cache = ret
c.cache_timeout = timestamp + c.cache_time
end
return ret
end
end
module('gluon.announced', package.seeall)
function handle_request(query, timestamp)
collectgarbage()
local m = query:match('^GET ([a-z ]+)$')
local ret
if m then
local data = {}
for q in m:gmatch('([a-z]+)') do
local ok, val = pcall(collect, q, timestamp)
if ok then
data[q] = val
end
end
if next(data) then
ret = deflate.compress(json.stringify(data))
end
elseif query:match('^[a-z]+$') then
local ok, data = pcall(collect, query, timestamp)
if ok then
ret = json.stringify(data)
end
end
collectgarbage()
return ret
end

View File

@ -5,29 +5,28 @@ PKG_VERSION:=4
PKG_RELEASE:=$(GLUON_BRANCH)
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
define Package/gluon-autoupdater
SECTION:=gluon
CATEGORY:=Gluon
DEPENDS:=+gluon-core +micrond +autoupdater
DEPENDS:=+gluon-core +libgluonutil +micrond +autoupdater
TITLE:=Automatically update firmware
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-autoupdater/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/autoupdater.so
if [ '$(GLUON_BRANCH)' ]; then \
$(INSTALL_DIR) $(1)/lib/gluon/autoupdater; \
echo '$(GLUON_BRANCH)' > $(1)/lib/gluon/autoupdater/default_branch; \

View File

@ -1,7 +0,0 @@
local autoupdater = uci:get_all('autoupdater', 'settings')
if autoupdater then
return {
branch = autoupdater['branch'],
enabled = uci:get_bool('autoupdater', 'settings', 'enabled'),
}
end

View File

@ -0,0 +1,6 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci

View File

@ -0,0 +1,79 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <string.h>
static struct json_object * get_autoupdater(void) {
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "autoupdater", &p))
goto error;
struct uci_section *s = uci_lookup_section(ctx, p, "settings");
if (!s)
goto error;
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "branch", gluonutil_wrap_string(uci_lookup_option_string(ctx, s, "branch")));
const char *enabled = uci_lookup_option_string(ctx, s, "enabled");
json_object_object_add(ret, "enabled", json_object_new_boolean(enabled && !strcmp(enabled, "1")));
uci_free_context(ctx);
return ret;
error:
uci_free_context(ctx);
return NULL;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *software = json_object_new_object();
json_object_object_add(software, "autoupdater", get_autoupdater());
json_object_object_add(ret, "software", software);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{}
};

View File

@ -12,7 +12,7 @@ define Package/gluon-core
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Base files of Gluon
DEPENDS:=+gluon-site +lua-platform-info +luci-base +luci-lib-jsonc +odhcp6c +firewall
DEPENDS:=+gluon-site +libgluonutil +lua-platform-info +luci-base +luci-lib-jsonc +odhcp6c +firewall
endef

View File

@ -4,6 +4,7 @@ PKG_NAME:=gluon-mesh-batman-adv-core
PKG_VERSION:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
@ -11,21 +12,19 @@ define Package/gluon-mesh-batman-adv-core
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Support for batman-adv meshing (core)
DEPENDS:=+gluon-core +gluon-client-bridge +firewall +libiwinfo-lua +batman-adv-visdata
DEPENDS:=+gluon-core +libgluonutil +gluon-client-bridge +firewall +libiwinfo +batman-adv-visdata
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-mesh-batman-adv-core/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/mesh-batman-adv-core.so
endef
define Package/gluon-mesh-batman-adv-core/postinst

View File

@ -1,39 +0,0 @@
local ifname_address_cache = {}
function ifname2address(ifname)
local ifaddress
if ifname_address_cache[ifname] ~= nil then
ifaddress = ifname_address_cache[ifname]
else
ifaddress = util.readline(io.open("/sys/class/net/" .. ifname .. "/address"))
ifname_address_cache[ifname] = ifaddress
end
return ifaddress
end
function batadv()
local interfaces = {}
local list = io.lines("/tmp/batman-adv-visdata/bat0/originators")
for line in list do
local mac1, lastseen, tq, mac2, ifname =
line:match("^([0-9a-f:]+) +(%d+%.%d+)s +%( *(%d+)%) +([0-9a-f:]+) +%[ *(.-)%]")
if mac1 ~= nil and mac1 == mac2 then
ifaddress = ifname2address(ifname)
if interfaces[ifaddress] == nil then
interfaces[ifaddress] = { neighbours = { [{}] = true } }
end
interfaces[ifaddress].neighbours[mac1] = { tq = tonumber(tq)
, lastseen = tonumber(lastseen)
}
end
end
if next(interfaces) then
return interfaces
end
end
return batadv()

View File

@ -1,40 +0,0 @@
local batman_adv = require 'gluon.batman_adv'
local iwinfo = require 'iwinfo'
function neighbours(iface)
local stations = {}
for k, v in pairs(iface.iw.assoclist(iface.ifname)) do
stations[k:lower()] = { signal = v.signal
, noise = v.noise
, inactive = v.inactive
}
end
if next(stations) then
return stations
end
end
function interfaces()
local interfaces = {}
for ifname in batman_adv.interfaces('bat0') do
pcall(function()
local address = util.readline(io.open('/sys/class/net/' .. ifname .. '/address'))
local wifitype = iwinfo.type(ifname)
if wifitype ~= nil then
interfaces[address] = { ifname = ifname, iw = iwinfo[wifitype] }
end
end)
end
return interfaces
end
local wifi = {}
for address, iface in pairs(interfaces()) do
wifi[address] = { [{}] = true, neighbours = neighbours(iface) }
end
if next(wifi) then
return wifi
end

View File

@ -1,15 +0,0 @@
local ip = require 'luci.ip'
local bit = require 'nixio'.bit
local addresses = {}
for line in io.lines('/proc/net/if_inet6') do
local matches = { line:match('^' .. string.rep('(%x%x%x%x)', 8) .. string.rep(' %x%x', 3) .. ' (%x%x)%s+([^%s]+)$') }
-- exclude wrong interfaces and deprecated as well as tentative addresses
-- (see /include/uapi/linux/if_addr.h in linux source for flags)
if matches[10] == 'br-client' and bit.band(tonumber(matches[9], 16), 0x60) == 0 then
table.insert(addresses, ip.IPv6(string.format('%s:%s:%s:%s:%s:%s:%s:%s', unpack(matches))):string():lower())
end
end
return addresses

View File

@ -1,56 +0,0 @@
local batman_adv = require 'gluon.batman_adv'
local wireless = {}
local tunnel = {}
local other = {}
local function get_address(t, ifname)
pcall(
function()
table.insert(t, util.readline(io.open('/sys/class/net/' .. ifname .. '/address')))
end
)
end
local function file_exists(filename)
local f = io.open(filename)
if f == nil then
return false
else
f:close()
return true
end
end
local function is_wireless(ifname)
return file_exists('/sys/class/net/' .. ifname .. '/wireless')
end
local function is_tuntap(ifname)
return file_exists('/sys/class/net/' .. ifname .. '/tun_flags')
end
local function nil_table(t)
if next(t) ~= nil then
return t
else
return nil
end
end
for ifname in batman_adv.interfaces('bat0') do
if is_wireless(ifname) then
get_address(wireless, ifname)
elseif is_tuntap(ifname) then
get_address(tunnel, ifname)
else
get_address(other, ifname)
end
end
return {
wireless = nil_table(wireless),
tunnel = nil_table(tunnel),
other = nil_table(other),
[{}] = true
}

View File

@ -1,13 +0,0 @@
local batman_adv = require 'gluon.batman_adv'
local interfaces = {}
for ifname in batman_adv.interfaces('bat0') do
pcall(
function()
table.insert(interfaces, util.readline(io.open('/sys/class/net/' .. ifname .. '/address')))
end
)
end
return interfaces

View File

@ -1 +0,0 @@
return util.readline(io.open('/sys/module/batman_adv/version'))

View File

@ -1,55 +0,0 @@
local iwinfo = require 'iwinfo'
local counts = { total = 0
, wifi = 0
, wifi24 = 0
, wifi5 = 0
}
local list = io.lines("/sys/kernel/debug/batman_adv/bat0/transtable_local")
local clients = {}
for line in list do
local mac, _, flags, lastseen = line:match("^ %* ([0-9a-f:]+) *(.- )%[(.-)%] +(%d+%.%d+)")
if mac then
if not flags:match('P') then
counts.total = counts.total + 1
clients[mac:lower()] = true
if flags:match('W') then
counts.wifi = counts.wifi +1
end
end
end
end
function count_iface_stations(iface)
local wifitype = iwinfo.type(iface)
if wifitype == nil then
return
end
local freq = iwinfo[wifitype].frequency(iface)
local key
if freq >= 2400 and freq < 2500 then
key = "wifi24"
elseif freq >= 5000 and freq < 6000 then
key = "wifi5"
else
return
end
for k, v in pairs(iwinfo[wifitype].assoclist(iface)) do
if clients[k:lower()] then
counts[key] = counts[key] + 1
end
end
end
local ifaces = {}
uci:foreach("wireless", "wifi-iface", function(s)
if s.network == "client" and s.mode == "ap" then
count_iface_stations(s.ifname)
end
end)
return counts

View File

@ -1,12 +0,0 @@
local gateway = ''
for line in io.lines('/sys/kernel/debug/batman_adv/bat0/gateways') do
if line:sub(1, 3) == '=> ' then
gateway = line:sub(4, 20)
break
end
end
if gateway ~= '' then
return gateway
end

View File

@ -1,14 +0,0 @@
local ethtool = require 'ethtool_stats'
local fields = ethtool.interface_stats('bat0')
local traffic = {}
for _, class in ipairs({'rx', 'tx', 'forward', 'mgmt_rx', 'mgmt_tx'}) do
traffic[class] = {
bytes = fields[class .. '_bytes'],
packets = fields[class],
}
end
traffic['tx']['dropped'] = fields['tx_dropped']
return traffic

View File

@ -0,0 +1,6 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -liwinfo -luci

View File

@ -0,0 +1,604 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <respondd.h>
#include <iwinfo.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <alloca.h>
#include <glob.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/ethtool.h>
#include <linux/if_addr.h>
#include <linux/sockios.h>
#define _STRINGIFY(s) #s
#define STRINGIFY(s) _STRINGIFY(s)
static struct json_object * get_addresses(void) {
FILE *f = fopen("/proc/net/if_inet6", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
struct json_object *ret = json_object_new_array();
while (getline(&line, &len, f) >= 0) {
/* IF_NAMESIZE would be enough, but adding 1 here is simpler than subtracting 1 in the format string */
char ifname[IF_NAMESIZE+1];
unsigned int flags;
struct in6_addr addr;
char buf[INET6_ADDRSTRLEN];
if (sscanf(line,
"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8
"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8
" %*2x %*2x %*2x %2x %"STRINGIFY(IF_NAMESIZE)"s",
&addr.s6_addr[0], &addr.s6_addr[1], &addr.s6_addr[2], &addr.s6_addr[3],
&addr.s6_addr[4], &addr.s6_addr[5], &addr.s6_addr[6], &addr.s6_addr[7],
&addr.s6_addr[8], &addr.s6_addr[9], &addr.s6_addr[10], &addr.s6_addr[11],
&addr.s6_addr[12], &addr.s6_addr[13], &addr.s6_addr[14], &addr.s6_addr[15],
&flags, ifname) != 18)
continue;
if (strcmp(ifname, "br-client"))
continue;
if (flags & (IFA_F_TENTATIVE|IFA_F_DEPRECATED))
continue;
inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
json_object_array_add(ret, json_object_new_string(buf));
}
fclose(f);
free(line);
return ret;
}
static void add_if_not_empty(struct json_object *obj, const char *key, struct json_object *val) {
if (json_object_array_length(val))
json_object_object_add(obj, key, val);
else
json_object_put(val);
}
static bool interface_file_exists(const char *ifname, const char *name) {
const char *format = "/sys/class/net/%s/%s";
char path[strlen(format) + strlen(ifname) + strlen(name)];
snprintf(path, sizeof(path), format, ifname, name);
return !access(path, F_OK);
}
static void mesh_add_subif(const char *ifname, struct json_object *wireless,
struct json_object *tunnel, struct json_object *other) {
struct json_object *address = gluonutil_wrap_and_free_string(gluonutil_get_interface_address(ifname));
if (interface_file_exists(ifname, "wireless"))
json_object_array_add(wireless, address);
else if (interface_file_exists(ifname, "tun_flags"))
json_object_array_add(tunnel, address);
else
json_object_array_add(other, address);
}
static struct json_object * get_mesh_subifs(const char *ifname) {
struct json_object *wireless = json_object_new_array();
struct json_object *tunnel = json_object_new_array();
struct json_object *other = json_object_new_array();
const char *format = "/sys/class/net/%s/lower_*";
char pattern[strlen(format) + strlen(ifname) - 1];
snprintf(pattern, sizeof(pattern), format, ifname);
size_t pattern_len = strlen(pattern);
glob_t lower;
if (!glob(pattern, GLOB_NOSORT, NULL, &lower)) {
size_t i;
for (i = 0; i < lower.gl_pathc; i++) {
mesh_add_subif(lower.gl_pathv[i] + pattern_len - 1,
wireless, tunnel, other);
}
globfree(&lower);
}
struct json_object *ret = json_object_new_object();
add_if_not_empty(ret, "wireless", wireless);
add_if_not_empty(ret, "tunnel", tunnel);
add_if_not_empty(ret, "other", other);
return ret;
}
static struct json_object * get_mesh(void) {
struct json_object *ret = json_object_new_object();
struct json_object *bat0_interfaces = json_object_new_object();
json_object_object_add(bat0_interfaces, "interfaces", get_mesh_subifs("bat0"));
json_object_object_add(ret, "bat0", bat0_interfaces);
return ret;
}
static struct json_object * get_batman_adv_compat(void) {
FILE *f = fopen("/lib/gluon/mesh-batman-adv-core/compat", "r");
if (!f)
return NULL;
struct json_object *ret = NULL;
int compat;
if (fscanf(f, "%i", &compat) == 1)
ret = json_object_new_int(compat);
fclose(f);
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *network = json_object_new_object();
json_object_object_add(network, "addresses", get_addresses());
json_object_object_add(network, "mesh", get_mesh());
json_object_object_add(ret, "network", network);
struct json_object *software = json_object_new_object();
struct json_object *software_batman_adv = json_object_new_object();
json_object_object_add(software_batman_adv, "version", gluonutil_wrap_and_free_string(gluonutil_read_line("/sys/module/batman_adv/version")));
json_object_object_add(software_batman_adv, "compat", get_batman_adv_compat());
json_object_object_add(software, "batman-adv", software_batman_adv);
json_object_object_add(ret, "software", software);
return ret;
}
static void add_gateway(struct json_object *obj) {
FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/gateways", "r");
if (!f)
return;
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, f) >= 0) {
char addr[18];
if (sscanf(line, "=> %17[0-9a-fA-F:]", addr) != 1)
continue;
json_object_object_add(obj, "gateway", json_object_new_string(addr));
break;
}
free(line);
fclose(f);
}
static inline bool ethtool_ioctl(int fd, struct ifreq *ifr, void *data) {
ifr->ifr_data = data;
return (ioctl(fd, SIOCETHTOOL, ifr) >= 0);
}
static uint32_t ethtool_get_stats_length(int fd, struct ifreq *ifr) {
const size_t sset_info_len = sizeof(struct ethtool_sset_info) + sizeof(uint32_t);
struct ethtool_sset_info *sset_info = alloca(sset_info_len);
memset(sset_info, 0, sset_info_len);
sset_info->cmd = ETHTOOL_GSSET_INFO;
sset_info->sset_mask = 1ull << ETH_SS_STATS;
if (!ethtool_ioctl(fd, ifr, sset_info))
return 0;
return sset_info->sset_mask ? sset_info->data[0] : 0;
}
static struct ethtool_gstrings * ethtool_get_stats_strings(int fd, struct ifreq *ifr) {
uint32_t n_stats = ethtool_get_stats_length(fd, ifr);
if (!n_stats)
return NULL;
struct ethtool_gstrings *strings = calloc(1, sizeof(*strings) + n_stats * ETH_GSTRING_LEN);
strings->cmd = ETHTOOL_GSTRINGS;
strings->string_set = ETH_SS_STATS;
strings->len = n_stats;
if (!ethtool_ioctl(fd, ifr, strings)) {
free(strings);
return NULL;
}
return strings;
}
static struct json_object * get_traffic(void) {
struct ethtool_gstrings *strings = NULL;
struct ethtool_stats *stats = NULL;
struct ifreq ifr = {};
strncpy(ifr.ifr_name, "bat0", IF_NAMESIZE);
struct json_object *ret = NULL;
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return NULL;
strings = ethtool_get_stats_strings(fd, &ifr);
if (!strings)
goto out;
stats = calloc(1, sizeof(struct ethtool_stats) + strings->len * sizeof(uint64_t));
stats->cmd = ETHTOOL_GSTATS;
stats->n_stats = strings->len;
if (!ethtool_ioctl(fd, &ifr, stats))
goto out;
struct json_object *rx = json_object_new_object();
struct json_object *tx = json_object_new_object();
struct json_object *forward = json_object_new_object();
struct json_object *mgmt_rx = json_object_new_object();
struct json_object *mgmt_tx = json_object_new_object();
size_t i;
for (i = 0; i < strings->len; i++) {
if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx", ETH_GSTRING_LEN))
json_object_object_add(rx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx_bytes", ETH_GSTRING_LEN))
json_object_object_add(rx, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx", ETH_GSTRING_LEN))
json_object_object_add(tx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_dropped", ETH_GSTRING_LEN))
json_object_object_add(tx, "dropped", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_bytes", ETH_GSTRING_LEN))
json_object_object_add(tx, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward", ETH_GSTRING_LEN))
json_object_object_add(forward, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward_bytes", ETH_GSTRING_LEN))
json_object_object_add(forward, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx", ETH_GSTRING_LEN))
json_object_object_add(mgmt_rx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx_bytes", ETH_GSTRING_LEN))
json_object_object_add(mgmt_rx, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx", ETH_GSTRING_LEN))
json_object_object_add(mgmt_tx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx_bytes", ETH_GSTRING_LEN))
json_object_object_add(mgmt_tx, "bytes", json_object_new_int64(stats->data[i]));
}
ret = json_object_new_object();
json_object_object_add(ret, "rx", rx);
json_object_object_add(ret, "tx", tx);
json_object_object_add(ret, "forward", forward);
json_object_object_add(ret, "mgmt_rx", mgmt_rx);
json_object_object_add(ret, "mgmt_tx", mgmt_tx);
out:
free(stats);
free(strings);
close(fd);
return ret;
}
static void count_iface_stations(size_t *wifi24, size_t *wifi5, const char *ifname) {
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
if (!iw)
return;
int freq;
if (iw->frequency(ifname, &freq) < 0)
return;
size_t *wifi;
if (freq >= 2400 && freq < 2500)
wifi = wifi24;
else if (freq >= 5000 && freq < 6000)
wifi = wifi5;
else
return;
int len;
char buf[IWINFO_BUFSIZE];
if (iw->assoclist(ifname, buf, &len) < 0)
return;
struct iwinfo_assoclist_entry *entry;
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++)
(*wifi)++;
}
static void count_stations(size_t *wifi24, size_t *wifi5) {
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "wireless", &p))
goto end;
struct uci_element *e;
uci_foreach_element(&p->sections, e) {
struct uci_section *s = uci_to_section(e);
if (strcmp(s->type, "wifi-iface"))
continue;
const char *network = uci_lookup_option_string(ctx, s, "network");
if (!network || strcmp(network, "client"))
continue;
const char *mode = uci_lookup_option_string(ctx, s, "mode");
if (!mode || strcmp(mode, "ap"))
continue;
const char *ifname = uci_lookup_option_string(ctx, s, "ifname");
if (!ifname)
continue;
count_iface_stations(wifi24, wifi5, ifname);
}
end:
uci_free_context(ctx);
}
static struct json_object * get_clients(void) {
size_t total = 0, wifi = 0, wifi24 = 0, wifi5 = 0;
FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/transtable_local", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, f) >= 0) {
char addr[18], flags[16];
if (sscanf(line, " * %17[0-9a-fA-F:] [%15[^]]]", addr, flags) != 2)
continue;
if (strchr(flags, 'P'))
continue;
total++;
if (strchr(flags, 'W'))
wifi++;
}
free(line);
fclose(f);
count_stations(&wifi24, &wifi5);
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "total", json_object_new_int(total));
json_object_object_add(ret, "wifi", json_object_new_int(wifi));
json_object_object_add(ret, "wifi24", json_object_new_int(wifi24));
json_object_object_add(ret, "wifi5", json_object_new_int(wifi5));
return ret;
}
static struct json_object * respondd_provider_statistics(void) {
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "clients", get_clients());
json_object_object_add(ret, "traffic", get_traffic());
add_gateway(ret);
return ret;
}
static struct json_object * ifnames2addrs(struct json_object *interfaces) {
struct json_object *ret = json_object_new_object();
json_object_object_foreach(interfaces, ifname, interface) {
char *ifaddr = gluonutil_get_interface_address(ifname);
if (!ifaddr)
continue;
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "neighbours", json_object_get(interface));
json_object_object_add(ret, ifaddr, obj);
free(ifaddr);
}
json_object_put(interfaces);
return ret;
}
static struct json_object * get_batadv(void) {
FILE *f = fopen("/tmp/batman-adv-visdata/bat0/originators", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
struct json_object *interfaces = json_object_new_object();
while (getline(&line, &len, f) >= 0) {
char mac1[18], mac2[18];
/* IF_NAMESIZE would be enough, but adding 1 here is simpler than subtracting 1 in the format string */
char ifname[IF_NAMESIZE+1];
double lastseen;
int tq;
if (sscanf(line,
"%17[0-9a-fA-F:] %lfs ( %i ) %17[0-9a-fA-F:] [ %"STRINGIFY(IF_NAMESIZE)"[^]] ]",
mac1, &lastseen, &tq, mac2, ifname) != 5)
continue;
if (strcmp(mac1, mac2))
continue;
struct json_object *interface;
if (!json_object_object_get_ex(interfaces, ifname, &interface)) {
interface = json_object_new_object();
json_object_object_add(interfaces, ifname, interface);
}
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "tq", json_object_new_int(tq));
json_object_object_add(obj, "lastseen", json_object_new_double(lastseen));
json_object_object_add(interface, mac1, obj);
}
fclose(f);
free(line);
return ifnames2addrs(interfaces);
}
static struct json_object * get_wifi_neighbours(const char *ifname) {
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
if (!iw)
return NULL;
int len;
char buf[IWINFO_BUFSIZE];
if (iw->assoclist(ifname, buf, &len) < 0)
return NULL;
struct json_object *neighbours = json_object_new_object();
struct iwinfo_assoclist_entry *entry;
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++) {
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "signal", json_object_new_int(entry->signal));
json_object_object_add(obj, "noise", json_object_new_int(entry->noise));
json_object_object_add(obj, "inactive", json_object_new_int(entry->inactive));
char mac[18];
snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
entry->mac[0], entry->mac[1], entry->mac[2],
entry->mac[3], entry->mac[4], entry->mac[5]);
json_object_object_add(neighbours, mac, obj);
}
struct json_object *ret = json_object_new_object();
if (json_object_object_length(neighbours))
json_object_object_add(ret, "neighbours", neighbours);
else
json_object_put(neighbours);
return ret;
}
static struct json_object * get_wifi(void) {
const char *mesh = "bat0";
struct json_object *ret = json_object_new_object();
const char *format = "/sys/class/net/%s/lower_*";
char pattern[strlen(format) + strlen(mesh)];
snprintf(pattern, sizeof(pattern), format, mesh);
size_t pattern_len = strlen(pattern);
glob_t lower;
if (!glob(pattern, GLOB_NOSORT, NULL, &lower)) {
size_t i;
for (i = 0; i < lower.gl_pathc; i++) {
const char *ifname = lower.gl_pathv[i] + pattern_len - 1;
char *ifaddr = gluonutil_get_interface_address(ifname);
if (!ifaddr)
continue;
struct json_object *neighbours = get_wifi_neighbours(ifname);
if (neighbours)
json_object_object_add(ret, ifaddr, neighbours);
free(ifaddr);
}
globfree(&lower);
}
return ret;
}
static struct json_object * respondd_provider_neighbours(void) {
struct json_object *ret = json_object_new_object();
struct json_object *batadv = get_batadv();
if (batadv)
json_object_object_add(ret, "batadv", batadv);
struct json_object *wifi = get_wifi();
if (wifi)
json_object_object_add(ret, "wifi", wifi);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{"statistics", respondd_provider_statistics},
{"neighbours", respondd_provider_neighbours},
{}
};

View File

@ -4,6 +4,7 @@ PKG_NAME:=gluon-mesh-vpn-fastd
PKG_VERSION:=3
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
@ -11,25 +12,19 @@ define Package/gluon-mesh-vpn-fastd
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Support for connecting batman-adv meshes via fastd
DEPENDS:=+gluon-core gluon-mesh-batman-adv +gluon-wan-dnsmasq +fastd +iptables-mod-extra +simple-tc
endef
define Package/gluon-mesh-vpn-fastd/description
Gluon community wifi mesh firmware framework: fastd support
DEPENDS:=+gluon-core +libgluonutil gluon-mesh-batman-adv +gluon-wan-dnsmasq +fastd +iptables-mod-extra +simple-tc
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-mesh-vpn-fastd/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/mesh-vpn-fastd.so
endef
define Package/gluon-mesh-vpn-fastd/postinst

View File

@ -1,5 +0,0 @@
local ret = {
enabled = uci:get('fastd', 'mesh_vpn') and (uci:get('fastd', 'mesh_vpn', 'enabled') == nil or uci:get_bool('fastd', 'mesh_vpn', 'enabled')),
version = util.readline(io.popen('exec fastd -v')):match('^[^%s]+%s+(.+)'),
}
return ret

View File

@ -1,70 +0,0 @@
local json = require 'luci.jsonc'
local ltn12 = require 'luci.ltn12'
local nixio = require 'nixio'
local site = require 'gluon.site_config'
local fastd_sock = nixio.socket('unix', 'stream')
local socket_path = uci:get('fastd', 'mesh_vpn', 'status_socket')
if not fastd_sock:connect(socket_path) then
return nil
end
local decoder = json.new()
ltn12.pump.all(ltn12.source.file(fastd_sock), decoder:sink())
local status = decoder:get()
local peer_groups
local function peer_connection(config)
local peer = status.peers[config.key]
if peer then
if peer.connection then
return {
established = peer.connection.established/1000
}
else
return function()end -- nil
end
end
end
local function peer_group(config)
local ret = {}
if config.peers then
local peers = {}
for peername, peerconfig in pairs(config.peers) do
peers[peername] = peer_connection(peerconfig)
end
if next(peers) then
ret.peers = peers
end
end
ret.groups = peer_groups(config.groups)
if next(ret) then
return ret
end
end
function peer_groups(groups)
if groups then
local ret = {}
for name, group in pairs(groups) do
ret[name] = peer_group(group)
end
if next(ret) then
return ret
end
end
end
return peer_group(site.fastd_mesh_vpn)

View File

@ -0,0 +1,6 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci

View File

@ -0,0 +1,305 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
static struct json_object * get_peer_groups(struct json_object *groups, struct json_object *peers);
static struct json_object * get_fastd_version(void) {
FILE *f = popen("exec fastd -v", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
ssize_t r = getline(&line, &len, f);
pclose(f);
if (r >= 0) {
len = strlen(line); /* The len given by getline is the buffer size, not the string length */
if (len && line[len-1] == '\n')
line[len-1] = 0;
}
else {
free(line);
line = NULL;
}
const char *version = line;
if (strncmp(version, "fastd ", 6) == 0)
version += 6;
struct json_object *ret = gluonutil_wrap_string(version);
free(line);
return ret;
}
static struct json_object * get_fastd(void) {
bool enabled = false;
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "fastd", &p))
goto disabled;
struct uci_section *s = uci_lookup_section(ctx, p, "mesh_vpn");
if (!s)
goto disabled;
const char *enabled_str = uci_lookup_option_string(ctx, s, "enabled");
if (!enabled_str || !strcmp(enabled_str, "1"))
enabled = true;
disabled:
uci_free_context(ctx);
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "version", get_fastd_version());
json_object_object_add(ret, "enabled", json_object_new_boolean(enabled));
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *software = json_object_new_object();
json_object_object_add(software, "fastd", get_fastd());
json_object_object_add(ret, "software", software);
return ret;
}
static const char * get_status_socket(struct uci_context *ctx, struct uci_section *s) {
return uci_lookup_option_string(ctx, s, "status_socket");
}
static struct json_object * read_status(struct uci_context *ctx, struct uci_section *s) {
const char *path = get_status_socket(ctx, s);
size_t addrlen = strlen(path);
/* Allocate enough space for arbitrary-length paths */
char addrbuf[offsetof(struct sockaddr_un, sun_path) + addrlen + 1];
memset(addrbuf, 0, sizeof(addrbuf));
struct sockaddr_un *addr = (struct sockaddr_un *)addrbuf;
addr->sun_family = AF_UNIX;
memcpy(addr->sun_path, path, addrlen+1);
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
return NULL;
if (connect(fd, (struct sockaddr*)addr, sizeof(addrbuf)) < 0) {
close(fd);
return NULL;
}
struct json_object *ret = NULL;
struct json_tokener *tok = json_tokener_new();
do {
char buf[1024];
size_t len = read(fd, buf, sizeof(buf));
if (len <= 0)
break;
ret = json_tokener_parse_ex(tok, buf, len);
} while (!ret && json_tokener_get_error(tok) == json_tokener_continue);
json_tokener_free(tok);
close(fd);
return ret;
}
static struct json_object * get_status(void) {
struct json_object *ret = NULL;
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (!uci_load(ctx, "fastd", &p)) {
struct uci_section *s = uci_lookup_section(ctx, p, "mesh_vpn");
if (s)
ret = read_status(ctx, s);
}
uci_free_context(ctx);
return ret;
}
static bool get_peer_connection(struct json_object **ret, struct json_object *config, struct json_object *peers) {
struct json_object *key_object;
if (!json_object_object_get_ex(config, "key", &key_object))
return false;
const char *key = json_object_get_string(key_object);
if (!key)
return false;
struct json_object *peer, *connection, *established;
if (!json_object_object_get_ex(peers, key, &peer) ||
!json_object_object_get_ex(peer, "connection", &connection))
return false;
if (json_object_object_get_ex(connection, "established", &established)) {
int64_t established_time = json_object_get_int64(established);
*ret = json_object_new_object();
json_object_object_add(*ret, "established", json_object_new_double(established_time/1000.0));
}
else {
*ret = NULL;
}
return true;
}
static struct json_object * get_peer_group(struct json_object *config, struct json_object *peers) {
struct json_object *ret = json_object_new_object();
struct json_object *config_peers;
if (json_object_object_get_ex(config, "peers", &config_peers) &&
json_object_is_type(config_peers, json_type_object)) {
struct json_object *ret_peers = json_object_new_object();
json_object_object_foreach(config_peers, peername, peerconfig) {
struct json_object *obj;
if (get_peer_connection(&obj, peerconfig, peers))
json_object_object_add(ret_peers, peername, obj);
}
if (json_object_object_length(ret_peers))
json_object_object_add(ret, "peers", ret_peers);
else
json_object_put(ret_peers);
}
struct json_object *config_groups;
if (json_object_object_get_ex(config, "groups", &config_groups)) {
struct json_object *obj = get_peer_groups(config_groups, peers);
if (obj)
json_object_object_add(ret, "groups", obj);
}
if (!json_object_object_length(ret)) {
json_object_put(ret);
return NULL;
}
return ret;
}
static struct json_object * get_peer_groups(struct json_object *groups, struct json_object *peers) {
if (!json_object_is_type(groups, json_type_object))
return NULL;
struct json_object *ret = json_object_new_object();
json_object_object_foreach(groups, name, group) {
struct json_object *g = get_peer_group(group, peers);
if (g)
json_object_object_add(ret, name, g);
}
if (!json_object_object_length(ret)) {
json_object_put(ret);
return NULL;
}
return ret;
}
static struct json_object * get_mesh_vpn(void) {
struct json_object *ret = NULL;
struct json_object *status = NULL;
struct json_object *site = NULL;
status = get_status();
if (!status)
goto end;
struct json_object *peers;
if (!json_object_object_get_ex(status, "peers", &peers))
goto end;
site = gluonutil_load_site_config();
if (!site)
goto end;
struct json_object *fastd_mesh_vpn;
if (!json_object_object_get_ex(site, "fastd_mesh_vpn", &fastd_mesh_vpn))
goto end;
ret = get_peer_group(fastd_mesh_vpn, peers);
end:
json_object_put(site);
json_object_put(status);
return ret;
}
static struct json_object * respondd_provider_statistics(void) {
struct json_object *ret = json_object_new_object();
struct json_object *mesh_vpn = get_mesh_vpn();
if (mesh_vpn)
json_object_object_add(ret, "mesh_vpn", mesh_vpn);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{"statistics", respondd_provider_statistics},
{}
};

View File

@ -5,6 +5,7 @@ PKG_VERSION:=1
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
@ -12,25 +13,19 @@ define Package/gluon-node-info
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Add /etc/config/gluon-node-info to uci
DEPENDS:=+gluon-core
endef
define Package/gluon-node-info/description
This packages creates /etc/config/gluon-node-info.
DEPENDS:=+gluon-core +libgluonutil
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-node-info/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/node-info.so
endef
define Package/gluon-node-info/postinst

View File

@ -1,7 +0,0 @@
if uci:get_first('gluon-node-info', 'location', 'share_location', false) then
return {
latitude = tonumber(uci:get_first('gluon-node-info', 'location', 'latitude')),
longitude = tonumber(uci:get_first('gluon-node-info', 'location', 'longitude')),
altitude = tonumber(uci:get_first('gluon-node-info', 'location', 'altitude')),
}
end

View File

@ -1,4 +0,0 @@
local contact = uci:get_first('gluon-node-info', 'owner', 'contact', '')
if contact ~= '' then
return { contact = contact }
end

View File

@ -1,4 +0,0 @@
local role = uci:get_first('gluon-node-info', 'system', 'role', '')
if role ~= '' then
return role
end

View File

@ -0,0 +1,6 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci

View File

@ -0,0 +1,144 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <stdlib.h>
#include <string.h>
static struct uci_section * get_first_section(struct uci_package *p, const char *type) {
struct uci_element *e;
uci_foreach_element(&p->sections, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, type))
return s;
}
return NULL;
}
static const char * get_first_option(struct uci_context *ctx, struct uci_package *p, const char *type, const char *option) {
struct uci_section *s = get_first_section(p, type);
if (s)
return uci_lookup_option_string(ctx, s, option);
else
return NULL;
}
static struct json_object * get_number(struct uci_context *ctx, struct uci_section *s, const char *name) {
const char *val = uci_lookup_option_string(ctx, s, name);
if (!val || !*val)
return NULL;
char *end;
double d = strtod(val, &end);
if (*end)
return NULL;
return json_object_new_double(d);
}
static struct json_object * get_location(struct uci_context *ctx, struct uci_package *p) {
struct uci_section *s = get_first_section(p, "location");
if (!s)
return NULL;
const char *share = uci_lookup_option_string(ctx, s, "share_location");
if (!share || strcmp(share, "1"))
return NULL;
struct json_object *ret = json_object_new_object();
struct json_object *latitude = get_number(ctx, s, "latitude");
if (latitude)
json_object_object_add(ret, "latitude", latitude);
struct json_object *longitude = get_number(ctx, s, "longitude");
if (longitude)
json_object_object_add(ret, "longitude", longitude);
struct json_object *altitude = get_number(ctx, s, "altitude");
if (altitude)
json_object_object_add(ret, "altitude", altitude);
return ret;
}
static struct json_object * get_owner(struct uci_context *ctx, struct uci_package *p) {
const char *contact = get_first_option(ctx, p, "owner", "contact");
if (!contact || !*contact)
return NULL;
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "contact", gluonutil_wrap_string(contact));
return ret;
}
static struct json_object * get_system(struct uci_context *ctx, struct uci_package *p) {
struct json_object *ret = json_object_new_object();
const char *role = get_first_option(ctx, p, "system", "role");
if (role && *role)
json_object_object_add(ret, "role", gluonutil_wrap_string(role));
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (!uci_load(ctx, "gluon-node-info", &p)) {
struct json_object *location = get_location(ctx, p);
if (location)
json_object_object_add(ret, "location", location);
struct json_object *owner = get_owner(ctx, p);
if (owner)
json_object_object_add(ret, "owner", owner);
json_object_object_add(ret, "system", get_system(ctx, p));
}
uci_free_context(ctx);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{}
};

View File

@ -0,0 +1,29 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-respondd
PKG_VERSION:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/gluon-respondd
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Provides node information to the network
DEPENDS:=+gluon-core +libplatforminfo +libgluonutil +respondd
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-respondd/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/respondd.so
endef
$(eval $(call BuildPackage,gluon-respondd))

View File

@ -3,7 +3,7 @@
. /usr/share/libubox/jshn.sh
. /lib/functions/service.sh
DEVLIST=/var/run/gluon-announced.devs
DEVLIST=/var/run/gluon-respondd.devs
DAEMON=/usr/bin/respondd
ifname_to_dev () {
@ -13,7 +13,7 @@ ifname_to_dev () {
echo "$dev"
}
restart_announced () {
restart_respondd () {
SERVICE_USE_PID=1
SERVICE_WRITE_PID=1
SERVICE_DAEMONIZE=1
@ -21,7 +21,7 @@ restart_announced () {
DEVS=$(cat $DEVLIST | while read dev iface; do echo -n " -i $dev"; done)
service_stop $DAEMON
service_start $DAEMON -g ff02::2:1001 -p 1001 -c 'return require("gluon.announced").handle_request' $DEVS
service_start $DAEMON -g ff02::2:1001 -p 1001 -d /lib/gluon/respondd $DEVS
}
case "$ACTION" in
@ -38,8 +38,7 @@ case "$ACTION" in
echo "$DEVS" | sort -u > $DEVLIST
restart_announced
restart_respondd
;;
esac

View File

@ -0,0 +1 @@
10000

View File

@ -0,0 +1 @@
300000

View File

@ -0,0 +1 @@
5000

View File

@ -2,10 +2,12 @@
local uci = require('luci.model.uci').cursor()
-- Allow announced port on WAN to allow resolving neighbours over mesh-on-wan
uci:section('firewall', 'rule', 'wan_announced',
uci:delete('firewall', 'wan_announced')
-- Allow respondd port on WAN to allow resolving neighbours over mesh-on-wan
uci:section('firewall', 'rule', 'wan_respondd',
{
name = 'wan_announced',
name = 'wan_respondd',
src = 'wan',
src_ip = 'fe80::/64',
dest_port = '1001',

View File

@ -0,0 +1,6 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -lplatforminfo

View File

@ -0,0 +1,209 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <libplatforminfo.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <sys/vfs.h>
static struct json_object * gluon_version(void) {
char *version = gluonutil_read_line("/lib/gluon/gluon-version");
if (!version)
return NULL;
char full_version[6 + strlen(version) + 1];
snprintf(full_version, sizeof(full_version), "gluon-%s", version);
free(version);
return json_object_new_string(full_version);
}
static struct json_object * get_site_code(void) {
struct json_object *site = gluonutil_load_site_config();
if (!site)
return NULL;
struct json_object *ret = NULL;
json_object_object_get_ex(site, "site_code", &ret);
if (ret)
json_object_get(ret);
json_object_put(site);
return ret;
}
static struct json_object * get_hostname(void) {
struct utsname utsname;
if (uname(&utsname))
return NULL;
return gluonutil_wrap_string(utsname.nodename);
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "node_id", gluonutil_wrap_and_free_string(gluonutil_get_node_id()));
json_object_object_add(ret, "hostname", get_hostname());
struct json_object *hardware = json_object_new_object();
json_object_object_add(hardware, "model", json_object_new_string(platforminfo_get_model()));
json_object_object_add(hardware, "nproc", json_object_new_int(sysconf(_SC_NPROCESSORS_ONLN)));
json_object_object_add(ret, "hardware", hardware);
struct json_object *network = json_object_new_object();
json_object_object_add(network, "mac", gluonutil_wrap_and_free_string(gluonutil_get_sysconfig("primary_mac")));
json_object_object_add(ret, "network", network);
struct json_object *software = json_object_new_object();
struct json_object *software_firmware = json_object_new_object();
json_object_object_add(software_firmware, "base", gluon_version());
json_object_object_add(software_firmware, "release", gluonutil_wrap_and_free_string(gluonutil_read_line("/lib/gluon/release")));
json_object_object_add(software, "firmware", software_firmware);
json_object_object_add(ret, "software", software);
struct json_object *system = json_object_new_object();
json_object_object_add(system, "site_code", get_site_code());
json_object_object_add(ret, "system", system);
return ret;
}
static void add_uptime(struct json_object *obj) {
FILE *f = fopen("/proc/uptime", "r");
if (!f)
return;
double uptime, idletime;
if (fscanf(f, "%lf %lf", &uptime, &idletime) == 2) {
json_object_object_add(obj, "uptime", json_object_new_double(uptime));
json_object_object_add(obj, "idletime", json_object_new_double(idletime));
}
fclose(f);
}
static void add_loadavg(struct json_object *obj) {
FILE *f = fopen("/proc/loadavg", "r");
if (!f)
return;
double loadavg;
unsigned proc_running, proc_total;
if (fscanf(f, "%lf %*f %*f %u/%u", &loadavg, &proc_running, &proc_total) == 3) {
json_object_object_add(obj, "loadavg", json_object_new_double(loadavg));
struct json_object *processes = json_object_new_object();
json_object_object_add(processes, "running", json_object_new_int(proc_running));
json_object_object_add(processes, "total", json_object_new_int(proc_total));
json_object_object_add(obj, "processes", processes);
}
fclose(f);
}
static struct json_object * get_memory(void) {
FILE *f = fopen("/proc/meminfo", "r");
if (!f)
return NULL;
struct json_object *ret = json_object_new_object();
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, f) >= 0) {
char label[32];
unsigned value;
if (sscanf(line, "%31[^:]: %u", label, &value) != 2)
continue;
if (!strcmp(label, "MemTotal"))
json_object_object_add(ret, "total", json_object_new_int(value));
else if (!strcmp(label, "MemFree"))
json_object_object_add(ret, "free", json_object_new_int(value));
else if (!strcmp(label, "Buffers"))
json_object_object_add(ret, "buffers", json_object_new_int(value));
else if (!strcmp(label, "Cached"))
json_object_object_add(ret, "cached", json_object_new_int(value));
}
free(line);
fclose(f);
return ret;
}
static struct json_object * get_rootfs_usage(void) {
struct statfs s;
if (statfs("/", &s))
return NULL;
return json_object_new_double(1 - (double)s.f_bfree / s.f_blocks);
}
static struct json_object * respondd_provider_statistics(void) {
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "node_id", gluonutil_wrap_and_free_string(gluonutil_get_node_id()));
json_object_object_add(ret, "rootfs_usage", get_rootfs_usage());
json_object_object_add(ret, "memory", get_memory());
add_uptime(ret);
add_loadavg(ret);
return ret;
}
static struct json_object * respondd_provider_neighbours(void) {
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "node_id", gluonutil_wrap_and_free_string(gluonutil_get_node_id()));
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{"statistics", respondd_provider_statistics},
{"neighbours", respondd_provider_neighbours},
{}
};

View File

@ -5,6 +5,7 @@ PKG_VERSION:=1
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(INCLUDE_DIR)/package.mk
@ -12,7 +13,7 @@ define Package/gluon-status-page-api
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=API for gluon-status-page
DEPENDS:=+gluon-core +uhttpd +sse-multiplex +batman-adv-visdata +gluon-neighbour-info +gluon-announced +libiwinfo +libjson-c
DEPENDS:=+gluon-core +uhttpd +sse-multiplex +batman-adv-visdata +gluon-neighbour-info +gluon-respondd +libiwinfo +libjson-c
endef
define Build/Prepare
@ -24,6 +25,10 @@ define Package/gluon-status-page-api/install
$(INSTALL_DIR) $(1)/lib/gluon/status-page/providers
$(INSTALL_BIN) $(PKG_BUILD_DIR)/neighbours-batadv $(1)/lib/gluon/status-page/providers/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/stations $(1)/lib/gluon/status-page/providers/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/status-page-api.so
$(CP) ./files/* $(1)/
endef

View File

@ -1,12 +1,15 @@
CFLAGS += -std=c99 -D_BSD_SOURCE
CFLAGS += $(shell pkg-config --cflags json-c)
LDFLAGS += $(shell pkg-config --libs json-c)
CFLAGS_JSONC = $(shell pkg-config --cflags json-c)
LDFLAGS_JSONC = $(shell pkg-config --libs json-c)
all: neighbours-batadv stations
all: neighbours-batadv stations respondd.so
neighbours-batadv: neighbours-batadv.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS)
$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDFLAGS_JSONC) -Wall -o $@ $^ $(LDLIBS)
stations: stations.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS) -liwinfo
$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDFLAGS_JSONC) -Wall -o $@ $^ $(LDLIBS) -liwinfo
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -o $@ $^ $(LDLIBS)

View File

@ -0,0 +1,47 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <respondd.h>
#include <json-c/json.h>
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *software = json_object_new_object();
struct json_object *software_status_page = json_object_new_object();
json_object_object_add(software_status_page, "api", json_object_new_int(1));
json_object_object_add(software, "status-page", software_status_page);
json_object_object_add(ret, "software", software);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{}
};