Merge gluon packages
The gluon packages will be maintained in the gluon main repository in the future.
This commit is contained in:
commit
216c9ce350
32
package/gluon-alfred/Makefile
Normal file
32
package/gluon-alfred/Makefile
Normal file
@ -0,0 +1,32 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-alfred
|
||||
PKG_VERSION:=1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/gluon-alfred
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
DEPENDS:=+gluon-core +gluon-announce +gluon-cron +alfred
|
||||
TITLE:=Configure alfred
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/gluon-alfred/install
|
||||
$(CP) ./files/* $(1)/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-alfred))
|
1
package/gluon-alfred/files/lib/gluon/cron/alfred
Normal file
1
package/gluon-alfred/files/lib/gluon/cron/alfred
Normal file
@ -0,0 +1 @@
|
||||
* * * * * /lib/gluon/announce/collect.lua nodeinfo | gzip | alfred -s 158; /lib/gluon/announce/collect.lua statistics | gzip | alfred -s 159; /lib/gluon/announce/collect.lua neighbours | gzip | alfred -s 160
|
19
package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred
Executable file
19
package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local uci = require 'luci.model.uci'
|
||||
local c = uci.cursor()
|
||||
|
||||
|
||||
c:delete('alfred', 'alfred')
|
||||
c:section('alfred', 'alfred', 'alfred',
|
||||
{
|
||||
interface = 'br-client',
|
||||
mode = 'slave',
|
||||
batmanif = 'bat0',
|
||||
start_vis = '1',
|
||||
run_facters = '0',
|
||||
}
|
||||
)
|
||||
|
||||
c:save('alfred')
|
||||
c:commit('alfred')
|
32
package/gluon-announce/Makefile
Normal file
32
package/gluon-announce/Makefile
Normal file
@ -0,0 +1,32 @@
|
||||
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-json +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))
|
10
package/gluon-announce/files/lib/gluon/announce/collect.lua
Executable file
10
package/gluon-announce/files/lib/gluon/announce/collect.lua
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local announce = require 'gluon.announce'
|
||||
local json = require 'luci.json'
|
||||
local ltn12 = require 'luci.ltn12'
|
||||
|
||||
local announce_dir = '/lib/gluon/announce/' .. arg[1] .. '.d'
|
||||
|
||||
encoder = json.Encoder(announce.collect_dir(announce_dir))
|
||||
ltn12.pump.all(encoder:source(), ltn12.sink.file(io.stdout))
|
@ -0,0 +1 @@
|
||||
return require('gluon.util').node_id()
|
@ -0,0 +1 @@
|
||||
return require('platform_info').get_model()
|
@ -0,0 +1 @@
|
||||
return uci:get_first('system', 'system', 'hostname')
|
@ -0,0 +1 @@
|
||||
return require('gluon.sysconfig').primary_mac
|
@ -0,0 +1 @@
|
||||
return require('gluon.util').node_id()
|
@ -0,0 +1,4 @@
|
||||
return {
|
||||
base = 'gluon-' .. util.trim(fs.readfile('/lib/gluon/gluon-version')),
|
||||
release = util.trim(fs.readfile('/lib/gluon/release')),
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
local site = require 'gluon.site_config'
|
||||
|
||||
return site.site_code
|
@ -0,0 +1 @@
|
||||
return tonumber(fs.readfile('/proc/uptime'):match('^[^ ]+ ([^ ]+)'))
|
@ -0,0 +1 @@
|
||||
return tonumber(fs.readfile('/proc/loadavg'):match('^([^ ]+) '))
|
@ -0,0 +1,13 @@
|
||||
local data = fs.readfile('/proc/meminfo')
|
||||
|
||||
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,
|
||||
}
|
@ -0,0 +1 @@
|
||||
return require('gluon.util').node_id()
|
@ -0,0 +1,3 @@
|
||||
local running, total = fs.readfile('/proc/loadavg'):match('^[^ ]+ [^ ]+ [^ ]+ (%d+)/(%d+)')
|
||||
|
||||
return { running = tonumber(running), total = tonumber(total) }
|
@ -0,0 +1,4 @@
|
||||
local fs = require "nixio.fs"
|
||||
|
||||
local st = fs.statvfs("/")
|
||||
return 1 - st.bfree / st.blocks
|
@ -0,0 +1 @@
|
||||
return tonumber(fs.readfile('/proc/uptime'):match('^([^ ]+) '))
|
33
package/gluon-announce/files/usr/lib/lua/gluon/announce.lua
Executable file
33
package/gluon-announce/files/usr/lib/lua/gluon/announce.lua
Executable file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
module('gluon.announce', package.seeall)
|
||||
|
||||
fs = require 'luci.fs'
|
||||
uci = require('luci.model.uci').cursor()
|
||||
util = require 'luci.util'
|
||||
|
||||
local function collect_entry(entry)
|
||||
if fs.isdirectory(entry) then
|
||||
return collect_dir(entry)
|
||||
else
|
||||
return setfenv(loadfile(entry), _M)()
|
||||
end
|
||||
end
|
||||
|
||||
function collect_dir(dir)
|
||||
local ret = {}
|
||||
|
||||
for _, entry in ipairs(fs.dir(dir)) do
|
||||
if entry:sub(1, 1) ~= '.' then
|
||||
local ok, val = pcall(collect_entry, dir .. '/' .. entry)
|
||||
if ok then
|
||||
ret[entry] = val
|
||||
else
|
||||
io.stderr:write(val, '\n')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
40
package/gluon-announced/Makefile
Normal file
40
package/gluon-announced/Makefile
Normal file
@ -0,0 +1,40 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-announced
|
||||
PKG_VERSION:=1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/gluon-announced
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=announced support
|
||||
DEPENDS:=+gluon-announce
|
||||
endef
|
||||
|
||||
define Package/gluon-announced/description
|
||||
Gluon community wifi mesh firmware framework: announced support
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
|
||||
endef
|
||||
|
||||
define Package/gluon-announced/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-announced $(1)/usr/bin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-announced))
|
@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
. /lib/functions/service.sh
|
||||
|
||||
DEVLIST=/var/run/gluon-announced.devs
|
||||
DAEMON=/usr/bin/gluon-announced
|
||||
|
||||
ifname_to_dev () {
|
||||
json_load "$(ubus call network.interface.$1 status)"
|
||||
json_get_var dev device
|
||||
|
||||
echo "$dev"
|
||||
}
|
||||
|
||||
restart_announced () {
|
||||
SERVICE_USE_PID=1
|
||||
SERVICE_WRITE_PID=1
|
||||
SERVICE_DAEMONIZE=1
|
||||
|
||||
DEVS=$(cat $DEVLIST | while read dev iface;do echo -n " -i $dev";done)
|
||||
|
||||
service_stop $DAEMON
|
||||
service_start $DAEMON -g ff02:0:0:0:0:0:2:1001 -p 1001 -s '/lib/gluon/announce/collect.lua nodeinfo' $DEVS
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
ifdown)
|
||||
sed -i "/$INTERFACE/d" $DEVLIST
|
||||
;;
|
||||
ifup)
|
||||
DEVICE=$(ifname_to_dev $INTERFACE)
|
||||
MESH=$(cat /sys/class/net/$DEVICE/batman_adv/mesh_iface)
|
||||
|
||||
[ $MESH = "bat0" ] || exit 0
|
||||
|
||||
DEVS="$(cat $DEVLIST; echo $DEVICE $INTERFACE)"
|
||||
|
||||
echo "$DEVS" | sort | uniq > $DEVLIST
|
||||
|
||||
restart_announced
|
||||
|
||||
;;
|
||||
esac
|
||||
|
6
package/gluon-announced/src/Makefile
Normal file
6
package/gluon-announced/src/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
all: gluon-announced
|
||||
|
||||
gluon-announced: gluon-announced.c
|
||||
|
||||
clean:
|
||||
rm gluon-announced
|
221
package/gluon-announced/src/gluon-announced.c
Normal file
221
package/gluon-announced/src/gluon-announced.c
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
Copyright (c) 2014, Nils Schneider <nils@nilsschneider.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 <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
|
||||
void usage() {
|
||||
puts("Usage: gluon-announced [-h] -g <group> -p <port> -i <if0> [-i <if1> ..] -s <script>");
|
||||
puts(" -g <ip6> multicast group, e.g. ff02:0:0:0:0:0:2:1001");
|
||||
puts(" -p <int> port number to listen on");
|
||||
puts(" -i <string> interface on which the group is joined");
|
||||
puts(" -s <string> script to be executed for each request");
|
||||
puts(" -h this help\n");
|
||||
}
|
||||
|
||||
/* The maximum size of output returned is limited to 8192 bytes (including
|
||||
* terminating null byte) for now. If this turns out to be problem, a
|
||||
* dynamic buffer should be implemented instead of increasing the
|
||||
* limit.
|
||||
*/
|
||||
#define BUFFER 8192
|
||||
|
||||
char *run_script(size_t *length, const char *script) {
|
||||
FILE *f;
|
||||
char *buffer;
|
||||
|
||||
buffer = calloc(BUFFER, sizeof(char));
|
||||
|
||||
if (buffer == NULL) {
|
||||
fprintf(stderr, "couldn't allocate buffer\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f = popen(script, "r");
|
||||
|
||||
size_t read_bytes = 0;
|
||||
while (1) {
|
||||
ssize_t ret = fread(buffer+read_bytes, sizeof(char), BUFFER-read_bytes, f);
|
||||
|
||||
if (ret <= 0)
|
||||
break;
|
||||
|
||||
read_bytes += ret;
|
||||
}
|
||||
|
||||
int ret = pclose(f);
|
||||
|
||||
if (ret != 0)
|
||||
fprintf(stderr, "script exited with status %d\n", ret);
|
||||
|
||||
*length = read_bytes;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void join_mcast(const int sock, const struct in6_addr addr, const char *iface) {
|
||||
struct ipv6_mreq mreq;
|
||||
|
||||
mreq.ipv6mr_multiaddr = addr;
|
||||
mreq.ipv6mr_interface = if_nametoindex(iface);
|
||||
|
||||
if (mreq.ipv6mr_interface == 0)
|
||||
goto error;
|
||||
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
|
||||
goto error;
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
fprintf(stderr, "Could not join multicast group on %s: ", iface);
|
||||
perror(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
#define REQUESTSIZE 64
|
||||
|
||||
char *recvrequest(const int sock, struct sockaddr *client_addr, socklen_t *clilen) {
|
||||
char request_buffer[REQUESTSIZE];
|
||||
ssize_t read_bytes;
|
||||
|
||||
read_bytes = recvfrom(sock, request_buffer, sizeof(request_buffer), 0, client_addr, clilen);
|
||||
|
||||
if (read_bytes < 0) {
|
||||
perror("recvfrom failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char *request = strndup(request_buffer, read_bytes);
|
||||
|
||||
if (request == NULL)
|
||||
perror("Could not receive request");
|
||||
|
||||
return strsep(&request, "\r\n\t ");
|
||||
}
|
||||
|
||||
void serve(const int sock, const char *script) {
|
||||
char *request;
|
||||
socklen_t clilen;
|
||||
struct sockaddr_in6 client_addr;
|
||||
|
||||
clilen = sizeof(client_addr);
|
||||
|
||||
while (1) {
|
||||
request = recvrequest(sock, (struct sockaddr*)&client_addr, &clilen);
|
||||
|
||||
int cmp = strcmp(request, "nodeinfo");
|
||||
free(request);
|
||||
|
||||
if (cmp != 0)
|
||||
continue;
|
||||
|
||||
char *msg;
|
||||
size_t msg_length;
|
||||
msg = run_script(&msg_length, script);
|
||||
|
||||
if (sendto(sock, msg, msg_length, 0, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0) {
|
||||
perror("sendto failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int sock;
|
||||
struct sockaddr_in6 server_addr = {};
|
||||
char *script = NULL;
|
||||
struct in6_addr mgroup_addr;
|
||||
|
||||
sock = socket(PF_INET6, SOCK_DGRAM, 0);
|
||||
|
||||
if (sock < 0) {
|
||||
perror("creating socket");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
server_addr.sin6_family = AF_INET6;
|
||||
server_addr.sin6_addr = in6addr_any;
|
||||
|
||||
opterr = 0;
|
||||
|
||||
int group_set = 0;
|
||||
|
||||
int c;
|
||||
while ((c = getopt(argc, argv, "p:g:s:i:h")) != -1)
|
||||
switch (c) {
|
||||
case 'p':
|
||||
server_addr.sin6_port = htons(atoi(optarg));
|
||||
break;
|
||||
case 'g':
|
||||
if (!inet_pton(AF_INET6, optarg, &mgroup_addr)) {
|
||||
perror("Invalid multicast group. This message will probably confuse you");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
group_set = 1;
|
||||
break;
|
||||
case 's':
|
||||
script = optarg;
|
||||
|
||||
break;
|
||||
case 'i':
|
||||
if (!group_set) {
|
||||
fprintf(stderr, "Multicast group must be given before interface.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
join_mcast(sock, mgroup_addr, optarg);
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid parameter %c ignored.\n", c);
|
||||
}
|
||||
|
||||
if (script == NULL) {
|
||||
fprintf(stderr, "No script given\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
||||
perror("bind failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
serve(sock, script);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
36
package/gluon-authorized-keys/Makefile
Normal file
36
package/gluon-authorized-keys/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-authorized-keys
|
||||
PKG_VERSION:=2
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(GLUONDIR)/include/package.mk
|
||||
|
||||
define Package/gluon-authorized-keys
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Fill /etc/dropbear/authorized_keys from site.conf
|
||||
DEPENDS:=+gluon-core
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/gluon-authorized-keys/install
|
||||
$(CP) ./files/* $(1)/
|
||||
endef
|
||||
|
||||
define Package/gluon-authorized-keys/postinst
|
||||
#!/bin/sh
|
||||
$(call GluonCheckSite,check_site.lua)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-authorized-keys))
|
1
package/gluon-authorized-keys/check_site.lua
Normal file
1
package/gluon-authorized-keys/check_site.lua
Normal file
@ -0,0 +1 @@
|
||||
need_string_array 'authorized_keys'
|
22
package/gluon-authorized-keys/files/lib/gluon/upgrade/100-authorized-keys
Executable file
22
package/gluon-authorized-keys/files/lib/gluon/upgrade/100-authorized-keys
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local site = require 'gluon.site_config'
|
||||
local file = '/etc/dropbear/authorized_keys'
|
||||
|
||||
local keys = {}
|
||||
|
||||
function load_keys()
|
||||
for line in io.lines(file) do
|
||||
keys[line] = true
|
||||
end
|
||||
end
|
||||
|
||||
pcall(load_keys)
|
||||
|
||||
local f = io.open(file, 'a')
|
||||
for _, key in ipairs(site.authorized_keys) do
|
||||
if not keys[key] then
|
||||
f:write(key .. '\n')
|
||||
end
|
||||
end
|
||||
f:close()
|
42
package/gluon-autoupdater/Makefile
Normal file
42
package/gluon-autoupdater/Makefile
Normal file
@ -0,0 +1,42 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-autoupdater
|
||||
PKG_VERSION:=4
|
||||
PKG_RELEASE:=$(GLUON_BRANCH)
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(GLUONDIR)/include/package.mk
|
||||
|
||||
define Package/gluon-autoupdater
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
DEPENDS:=+gluon-core +gluon-cron +autoupdater
|
||||
TITLE:=Automatically update firmware
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/gluon-autoupdater/install
|
||||
$(CP) ./files/* $(1)/
|
||||
|
||||
if [ '$(GLUON_BRANCH)' ]; then \
|
||||
$(INSTALL_DIR) $(1)/lib/gluon/autoupdater; \
|
||||
echo '$(GLUON_BRANCH)' > $(1)/lib/gluon/autoupdater/default_branch; \
|
||||
fi
|
||||
endef
|
||||
|
||||
define Package/gluon-autoupdater/postinst
|
||||
#!/bin/sh
|
||||
$(call GluonCheckSite,check_site.lua)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-autoupdater))
|
12
package/gluon-autoupdater/check_site.lua
Normal file
12
package/gluon-autoupdater/check_site.lua
Normal file
@ -0,0 +1,12 @@
|
||||
need_string 'autoupdater.branch'
|
||||
|
||||
local function check_branch(k, _)
|
||||
local prefix = string.format('autoupdater.branches[%q].', k)
|
||||
|
||||
need_string(prefix .. 'name')
|
||||
need_string_array(prefix .. 'mirrors')
|
||||
need_number(prefix .. 'good_signatures')
|
||||
need_string_array(prefix .. 'pubkeys')
|
||||
end
|
||||
|
||||
need_table('autoupdater.branches', check_branch)
|
@ -0,0 +1,7 @@
|
||||
local autoupdater = uci:get_all('autoupdater', 'settings')
|
||||
if autoupdater then
|
||||
return {
|
||||
branch = autoupdater['branch'],
|
||||
enabled = uci:get_bool('autoupdater', 'settings', 'enabled'),
|
||||
}
|
||||
end
|
57
package/gluon-autoupdater/files/lib/gluon/upgrade/500-autoupdater
Executable file
57
package/gluon-autoupdater/files/lib/gluon/upgrade/500-autoupdater
Executable file
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local site = require 'gluon.site_config'
|
||||
local uci = require 'luci.model.uci'
|
||||
|
||||
local c = uci.cursor()
|
||||
|
||||
|
||||
for name, config in pairs(site.autoupdater.branches) do
|
||||
c:delete('autoupdater', name)
|
||||
c:section('autoupdater', 'branch', name,
|
||||
{
|
||||
name = config.name,
|
||||
mirror = config.mirrors,
|
||||
good_signatures = config.good_signatures,
|
||||
pubkey = config.pubkeys,
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
if not c:get('autoupdater', 'settings') then
|
||||
local enabled = 0
|
||||
local branch = site.autoupdater.branch
|
||||
|
||||
local f = io.open('/lib/gluon/autoupdater/default_branch')
|
||||
if f then
|
||||
enabled = 1
|
||||
branch = f:read('*line')
|
||||
f:close()
|
||||
end
|
||||
|
||||
c:section('autoupdater', 'autoupdater', 'settings',
|
||||
{
|
||||
enabled = enabled,
|
||||
branch = branch,
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
c:set('autoupdater', 'settings', 'version_file', '/lib/gluon/release')
|
||||
|
||||
c:save('autoupdater')
|
||||
c:commit('autoupdater')
|
||||
|
||||
|
||||
local autoupdater_util = require 'autoupdater.util'
|
||||
autoupdater_util.randomseed()
|
||||
|
||||
|
||||
-- Perform updates at a random time between 04:00 and 05:00, and once an hour
|
||||
-- a fallback update (used after the regular updates haven't
|
||||
local minute = math.random(0, 59)
|
||||
|
||||
local f = io.open('/lib/gluon/cron/autoupdater', 'w')
|
||||
f:write(string.format('%i 4 * * * /usr/sbin/autoupdater\n', minute))
|
||||
f:write(string.format('%i 0-3,5-23 * * * /usr/sbin/autoupdater --fallback\n', minute))
|
||||
f:close()
|
40
package/gluon-config-mode-autoupdater/Makefile
Normal file
40
package/gluon-config-mode-autoupdater/Makefile
Normal file
@ -0,0 +1,40 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-config-mode-autoupdater
|
||||
PKG_VERSION:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(GLUONDIR)/include/package.mk
|
||||
|
||||
PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
|
||||
|
||||
|
||||
define Package/gluon-config-mode-autoupdater
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Let the user know whether the autoupdater is enabled or not.
|
||||
DEPENDS:=+gluon-config-mode-core +gluon-autoupdater
|
||||
endef
|
||||
|
||||
define Package/gluon-config-mode-autoupdater/description
|
||||
Luci based config mode
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call GluonBuildI18N,gluon-config-mode-autoupdater,i18n)
|
||||
endef
|
||||
|
||||
define Package/gluon-config-mode-autoupdater/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(call GluonInstallI18N,gluon-config-mode-autoupdater,$(1))
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-config-mode-autoupdater))
|
@ -0,0 +1,19 @@
|
||||
local cbi = require "luci.cbi"
|
||||
local i18n = require "luci.i18n"
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.section(form)
|
||||
local enabled = uci:get_bool("autoupdater", "settings", "enabled")
|
||||
if enabled then
|
||||
local s = form:section(cbi.SimpleSection, nil,
|
||||
i18n.translate('This node will automatically update its firmware when a new version is available.'))
|
||||
end
|
||||
end
|
||||
|
||||
function M.handle(data)
|
||||
return
|
||||
end
|
||||
|
||||
return M
|
17
package/gluon-config-mode-autoupdater/i18n/de.po
Normal file
17
package/gluon-config-mode-autoupdater/i18n/de.po
Normal file
@ -0,0 +1,17 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"PO-Revision-Date: 2015-03-18 16:03+0100\n"
|
||||
"Last-Translator: Matthias Schiffer <mschiffer@universe-factory.net>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid ""
|
||||
"This node will automatically update its firmware when a new version is "
|
||||
"available."
|
||||
msgstr "Dieser Knoten aktualisiert seine Firmware automatisch, sobald "
|
||||
"eine neue Version vorliegt."
|
@ -0,0 +1,7 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid ""
|
||||
"This node will automatically update its firmware when a new version is "
|
||||
"available."
|
||||
msgstr ""
|
36
package/gluon-config-mode-contact-info/Makefile
Normal file
36
package/gluon-config-mode-contact-info/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-config-mode-contact-info
|
||||
PKG_VERSION:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(GLUONDIR)/include/package.mk
|
||||
|
||||
PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
|
||||
|
||||
|
||||
define Package/gluon-config-mode-contact-info
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Set a custom string that will be distributed in the mesh.
|
||||
DEPENDS:=+gluon-config-mode-core +gluon-node-info
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call GluonBuildI18N,gluon-config-mode-contact-info,i18n)
|
||||
endef
|
||||
|
||||
define Package/gluon-config-mode-contact-info/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(call GluonInstallI18N,gluon-config-mode-contact-info,$(1))
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-config-mode-contact-info))
|
@ -0,0 +1,34 @@
|
||||
local cbi = require "luci.cbi"
|
||||
local i18n = require "luci.i18n"
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.section(form)
|
||||
local s = form:section(cbi.SimpleSection, nil, i18n.translate(
|
||||
'You can provide your contact information here to '
|
||||
.. 'allow others to contact you. Please note that '
|
||||
.. 'this information will be visible <em>publicly</em> '
|
||||
.. 'on the internet together with your node\'s coordinates.'
|
||||
)
|
||||
)
|
||||
|
||||
local o = s:option(cbi.Value, "_contact", i18n.translate("Contact info"))
|
||||
o.default = uci:get_first("gluon-node-info", "owner", "contact", "")
|
||||
o.rmempty = true
|
||||
o.datatype = "string"
|
||||
o.description = i18n.translate("e.g. E-mail or phone number")
|
||||
o.maxlen = 140
|
||||
end
|
||||
|
||||
function M.handle(data)
|
||||
if data._contact ~= nil then
|
||||
uci:set("gluon-node-info", uci:get_first("gluon-node-info", "owner"), "contact", data._contact)
|
||||
else
|
||||
uci:delete("gluon-node-info", uci:get_first("gluon-node-info", "owner"), "contact")
|
||||
end
|
||||
uci:save("gluon-node-info")
|
||||
uci:commit("gluon-node-info")
|
||||
end
|
||||
|
||||
return M
|
27
package/gluon-config-mode-contact-info/i18n/de.po
Normal file
27
package/gluon-config-mode-contact-info/i18n/de.po
Normal file
@ -0,0 +1,27 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"PO-Revision-Date: 2015-03-19 01:32+0100\n"
|
||||
"Last-Translator: Matthias Schiffer <mschiffer@universe-factory.net>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Contact info"
|
||||
msgstr "Kontakt"
|
||||
|
||||
msgid ""
|
||||
"You can provide your contact information here to allow others to contact "
|
||||
"you. Please note that this information will be visible <em>publicly</em> on "
|
||||
"the internet together with your node's coordinates."
|
||||
msgstr ""
|
||||
"Hier kannst du einen <em>öffentlichen</em> Hinweis hinterlegen, um anderen "
|
||||
"zu ermöglichen, Kontakt mit dir aufzunehmen. Bitte beachte, dass "
|
||||
"dieser Hinweis auch öffentlich im Internet, zusammen mit den Koordinaten "
|
||||
"deines Knotens, einsehbar sein wird."
|
||||
|
||||
msgid "e.g. E-mail or phone number"
|
||||
msgstr "z.B. E-Mail oder Telefonnummer"
|
@ -0,0 +1,14 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid "Contact info"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"You can provide your contact information here to allow others to contact "
|
||||
"you. Please note that this information will be visible <em>publicly</em> on "
|
||||
"the internet together with your node's coordinates."
|
||||
msgstr ""
|
||||
|
||||
msgid "e.g. E-mail or phone number"
|
||||
msgstr ""
|
39
package/gluon-config-mode-core/Makefile
Normal file
39
package/gluon-config-mode-core/Makefile
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright (C) 2012 Nils Schneider <nils at nilsschneider.net>
|
||||
# This is free software, licensed under the Apache 2.0 license.
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-config-mode-core
|
||||
PKG_VERSION:=2
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(GLUONDIR)/include/package.mk
|
||||
|
||||
PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
|
||||
|
||||
|
||||
define Package/gluon-config-mode-core
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Luci based config mode for user friendly setup of new mesh nodes
|
||||
DEPENDS:=+gluon-setup-mode +gluon-luci-theme +gluon-lock-password $(GLUON_I18N_PACKAGES)
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call GluonBuildI18N,gluon-config-mode-core,i18n)
|
||||
endef
|
||||
|
||||
define Package/gluon-config-mode-core/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(call GluonInstallI18N,gluon-config-mode-core,$(1))
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-config-mode-core))
|
@ -0,0 +1,3 @@
|
||||
local i18n = require 'luci.i18n'
|
||||
|
||||
return function () luci.template.render_string(i18n.translate('gluon-config-mode:reboot')) end
|
@ -0,0 +1,89 @@
|
||||
--[[
|
||||
Copyright 2013 Nils Schneider <nils@nilsschneider.net>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
$Id$
|
||||
]]--
|
||||
|
||||
module("luci.controller.gluon-config-mode.index", package.seeall)
|
||||
|
||||
function index()
|
||||
local uci_state = luci.model.uci.cursor_state()
|
||||
|
||||
if uci_state:get_first("gluon-setup-mode", "setup_mode", "running", "0") == "1" then
|
||||
local root = node()
|
||||
if not root.target then
|
||||
root.target = alias("gluon-config-mode")
|
||||
root.index = true
|
||||
end
|
||||
|
||||
page = node()
|
||||
page.lock = true
|
||||
page.target = alias("gluon-config-mode")
|
||||
page.subindex = true
|
||||
page.index = false
|
||||
|
||||
page = node("gluon-config-mode")
|
||||
page.title = _("Wizard")
|
||||
page.target = alias("gluon-config-mode", "wizard")
|
||||
page.order = 5
|
||||
page.setuser = "root"
|
||||
page.setgroup = "root"
|
||||
page.index = true
|
||||
|
||||
entry({"gluon-config-mode", "wizard"}, form("gluon-config-mode/wizard")).index = true
|
||||
entry({"gluon-config-mode", "reboot"}, call("action_reboot"))
|
||||
end
|
||||
end
|
||||
|
||||
function action_reboot()
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
uci:set("gluon-setup-mode", uci:get_first("gluon-setup-mode", "setup_mode"), "configured", "1")
|
||||
uci:save("gluon-setup-mode")
|
||||
uci:commit("gluon-setup-mode")
|
||||
|
||||
if nixio.fork() ~= 0 then
|
||||
local fs = require "luci.fs"
|
||||
|
||||
local parts_dir = "/lib/gluon/config-mode/reboot/"
|
||||
local files = fs.dir(parts_dir)
|
||||
|
||||
table.sort(files)
|
||||
|
||||
local parts = {}
|
||||
|
||||
for _, entry in ipairs(files) do
|
||||
if entry:sub(1, 1) ~= '.' then
|
||||
local f = dofile(parts_dir .. '/' .. entry)
|
||||
if f ~= nil then
|
||||
table.insert(parts, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local hostname = uci:get_first("system", "system", "hostname")
|
||||
|
||||
luci.template.render("gluon-config-mode/reboot", { parts=parts
|
||||
, hostname=hostname
|
||||
})
|
||||
else
|
||||
debug.setfenv(io.stdout, debug.getfenv(io.open '/dev/null'))
|
||||
io.stdout:close()
|
||||
|
||||
-- Sleep a little so the browser can fetch everything required to
|
||||
-- display the reboot page, then reboot the device.
|
||||
nixio.nanosleep(2)
|
||||
|
||||
-- Run reboot with popen so it gets its own std filehandles.
|
||||
io.popen("reboot")
|
||||
|
||||
-- Prevent any further execution in this child.
|
||||
os.exit()
|
||||
end
|
||||
end
|
@ -0,0 +1,38 @@
|
||||
local wizard_dir = "/lib/gluon/config-mode/wizard/"
|
||||
local i18n = luci.i18n
|
||||
local uci = luci.model.uci.cursor()
|
||||
local fs = require "luci.fs"
|
||||
local f, s
|
||||
|
||||
local wizard = {}
|
||||
local files = fs.dir(wizard_dir)
|
||||
|
||||
table.sort(files)
|
||||
|
||||
for _, entry in ipairs(files) do
|
||||
if entry:sub(1, 1) ~= '.' then
|
||||
table.insert(wizard, dofile(wizard_dir .. '/' .. entry))
|
||||
end
|
||||
end
|
||||
|
||||
f = SimpleForm("wizard")
|
||||
f.reset = false
|
||||
f.template = "gluon-config-mode/cbi/wizard"
|
||||
|
||||
for _, s in ipairs(wizard) do
|
||||
s.section(f)
|
||||
end
|
||||
|
||||
function f.handle(self, state, data)
|
||||
if state == FORM_VALID then
|
||||
for _, s in ipairs(wizard) do
|
||||
s.handle(data)
|
||||
end
|
||||
|
||||
luci.http.redirect(luci.dispatcher.build_url("gluon-config-mode", "reboot"))
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return f
|
@ -0,0 +1,46 @@
|
||||
<%-
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
local i18n = require 'luci.i18n'
|
||||
local template = require 'luci.template'
|
||||
-%>
|
||||
|
||||
<h2><%:Welcome!%></h2>
|
||||
<p>
|
||||
<%= template.render_string(i18n.translate('gluon-config-mode:welcome'), {hostname=hostname, sysconfig=sysconfig}) %>
|
||||
</p>
|
||||
|
||||
<% if not self.embedded then %>
|
||||
<form method="post" enctype="multipart/form-data" action="<%=REQUEST_URI%>">
|
||||
<div>
|
||||
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
|
||||
<input type="hidden" name="cbi.submit" value="1" />
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="cbi-map" id="cbi-<%=self.config%>">
|
||||
<% if self.title and #self.title > 0 then %><h2><a id="content" name="content"><%=self.title%></a></h2><% end %>
|
||||
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
|
||||
<% self:render_children() %>
|
||||
<br />
|
||||
</div>
|
||||
<%- if self.message then %>
|
||||
<div><%=self.message%></div>
|
||||
<%- end %>
|
||||
<%- if self.errmessage then %>
|
||||
<div class="error"><%=self.errmessage%></div>
|
||||
<%- end %>
|
||||
<% if not self.embedded then %>
|
||||
<div class="cbi-page-actions">
|
||||
<%-
|
||||
if type(self.hidden) == "table" then
|
||||
for k, v in pairs(self.hidden) do
|
||||
-%>
|
||||
<input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
|
||||
<%-
|
||||
end
|
||||
end
|
||||
%>
|
||||
<input class="cbi-button cbi-button-save" type="submit" value="<%:Save & restart%>" />
|
||||
<script type="text/javascript">cbi_d_update();</script>
|
||||
</div>
|
||||
</form>
|
||||
<% end %>
|
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<%=luci.i18n.context.lang%>" lang="<%=luci.i18n.context.lang%>">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title><%=hostname%> is rebooting</title>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="maincontainer">
|
||||
<div id="maincontent">
|
||||
<h2><%:Your node's setup is now complete.%></h2>
|
||||
<% for k, v in ipairs(parts) do v() end %>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
24
package/gluon-config-mode-core/i18n/de.po
Normal file
24
package/gluon-config-mode-core/i18n/de.po
Normal file
@ -0,0 +1,24 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"PO-Revision-Date: 2015-03-19 02:07+0100\n"
|
||||
"Last-Translator: Matthias Schiffer <mschiffer@universe-factory.net>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save & restart"
|
||||
msgstr "Speichern & Neustarten"
|
||||
|
||||
msgid "Welcome!"
|
||||
msgstr "Willkommen!"
|
||||
|
||||
msgid "Wizard"
|
||||
msgstr "Wizard"
|
||||
|
||||
msgid "Your node's setup is now complete."
|
||||
msgstr "Dein Knoten ist nun fertig eingerichtet."
|
@ -0,0 +1,14 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid "Save & restart"
|
||||
msgstr ""
|
||||
|
||||
msgid "Welcome!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wizard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your node's setup is now complete."
|
||||
msgstr ""
|
36
package/gluon-config-mode-geo-location/Makefile
Normal file
36
package/gluon-config-mode-geo-location/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-config-mode-geo-location
|
||||
PKG_VERSION:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(GLUONDIR)/include/package.mk
|
||||
|
||||
PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
|
||||
|
||||
|
||||
define Package/gluon-config-mode-geo-location
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Set geographic location of a node
|
||||
DEPENDS:=+gluon-config-mode-core +gluon-node-info
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call GluonBuildI18N,gluon-config-mode-geo-location,i18n)
|
||||
endef
|
||||
|
||||
define Package/gluon-config-mode-geo-location/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(call GluonInstallI18N,gluon-config-mode-geo-location,$(1))
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-config-mode-geo-location))
|
@ -0,0 +1,60 @@
|
||||
local cbi = require "luci.cbi"
|
||||
local i18n = require "luci.i18n"
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.section(form)
|
||||
local s = form:section(cbi.SimpleSection, nil, i18n.translate(
|
||||
'If you want the location of your node to be displayed on the map, '
|
||||
.. 'you can enter its coordinates here. Specifying the altitude '
|
||||
.. 'is optional and should only be done if a proper value is known.'))
|
||||
|
||||
|
||||
local o
|
||||
|
||||
o = s:option(cbi.Flag, "_location", i18n.translate("Show node on the map"))
|
||||
o.default = uci:get_first("gluon-node-info", "location", "share_location", o.disabled)
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(cbi.Value, "_latitude", i18n.translate("Latitude"))
|
||||
o.default = uci:get_first("gluon-node-info", "location", "latitude")
|
||||
o:depends("_location", "1")
|
||||
o.rmempty = false
|
||||
o.datatype = "float"
|
||||
o.description = i18n.translatef("e.g. %s", "53.873621")
|
||||
|
||||
o = s:option(cbi.Value, "_longitude", i18n.translate("Longitude"))
|
||||
o.default = uci:get_first("gluon-node-info", "location", "longitude")
|
||||
o:depends("_location", "1")
|
||||
o.rmempty = false
|
||||
o.datatype = "float"
|
||||
o.description = i18n.translatef("e.g. %s", "10.689901")
|
||||
|
||||
o = s:option(cbi.Value, "_altitude", i18n.translate("Altitude"))
|
||||
o.default = uci:get_first("gluon-node-info", "location", "altitude")
|
||||
o:depends("_location", "1")
|
||||
o.rmempty = true
|
||||
o.datatype = "float"
|
||||
o.description = i18n.translatef("e.g. %s", "11.51")
|
||||
|
||||
end
|
||||
|
||||
function M.handle(data)
|
||||
local sname = uci:get_first("gluon-node-info", "location")
|
||||
|
||||
uci:set("gluon-node-info", sname, "share_location", data._location)
|
||||
if data._location and data._latitude ~= nil and data._longitude ~= nil then
|
||||
uci:set("gluon-node-info", sname, "latitude", data._latitude)
|
||||
uci:set("gluon-node-info", sname, "longitude", data._longitude)
|
||||
if data._altitude ~= nil then
|
||||
uci:set("gluon-node-info", sname, "altitude", data._altitude)
|
||||
else
|
||||
uci:delete("gluon-node-info", sname, "altitude")
|
||||
end
|
||||
end
|
||||
uci:save("gluon-node-info")
|
||||
uci:commit("gluon-node-info")
|
||||
end
|
||||
|
||||
return M
|
36
package/gluon-config-mode-geo-location/i18n/de.po
Normal file
36
package/gluon-config-mode-geo-location/i18n/de.po
Normal file
@ -0,0 +1,36 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gluon-config-mode-geo-location\n"
|
||||
"PO-Revision-Date: 2015-03-23 02:18+0100\n"
|
||||
"Last-Translator: Martin Weinelt <martin@darmstadt.freifunk.net>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid ""
|
||||
"If you want the location of your node to be displayed on the map, you can "
|
||||
"enter its coordinates here. Specifying the altitude is optional and should "
|
||||
"only be done if a proper value is known."
|
||||
msgstr ""
|
||||
"Um deinen Knoten auf der Karte anzeigen zu können, benötigen wir seine "
|
||||
"Koordinaten. Hier hast du die Möglichkeit, diese zu hinterlegen. Die "
|
||||
"Höhenangabe ist optional und sollte nur gesetzt werden, wenn ein exakter "
|
||||
"Wert bekannt ist."
|
||||
|
||||
msgid "Latitude"
|
||||
msgstr "Breitengrad"
|
||||
|
||||
msgid "Longitude"
|
||||
msgstr "Längengrad"
|
||||
|
||||
msgid "Altitude"
|
||||
msgstr "Höhenmeter über Normalnull"
|
||||
|
||||
msgid "Show node on the map"
|
||||
msgstr "Knoten auf der Karte anzeigen"
|
||||
|
||||
msgid "e.g. %s"
|
||||
msgstr "z.B. %s"
|
@ -0,0 +1,20 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid ""
|
||||
"If you want the location of your node to be displayed on the map, you can "
|
||||
"enter its coordinates here. Specifying the altitude is optional and should "
|
||||
"only be done if a proper value is known."
|
||||
msgstr ""
|
||||
|
||||
msgid "Latitude"
|
||||
msgstr ""
|
||||
|
||||
msgid "Longitude"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show node on the map"
|
||||
msgstr ""
|
||||
|
||||
msgid "e.g. %s"
|
||||
msgstr ""
|
36
package/gluon-config-mode-hostname/Makefile
Normal file
36
package/gluon-config-mode-hostname/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-config-mode-hostname
|
||||
PKG_VERSION:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(GLUONDIR)/include/package.mk
|
||||
|
||||
PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
|
||||
|
||||
|
||||
define Package/gluon-config-mode-hostname
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Set the hostname
|
||||
DEPENDS:=+gluon-config-mode-core
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call GluonBuildI18N,gluon-config-mode-hostname,i18n)
|
||||
endef
|
||||
|
||||
define Package/gluon-config-mode-hostname/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(call GluonInstallI18N,gluon-config-mode-hostname,$(1))
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-config-mode-hostname))
|
@ -0,0 +1,21 @@
|
||||
local cbi = require "luci.cbi"
|
||||
local i18n = require "luci.i18n"
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.section(form)
|
||||
local s = form:section(cbi.SimpleSection, nil, nil)
|
||||
local o = s:option(cbi.Value, "_hostname", i18n.translate("Node name"))
|
||||
o.value = uci:get_first("system", "system", "hostname")
|
||||
o.rmempty = false
|
||||
o.datatype = "hostname"
|
||||
end
|
||||
|
||||
function M.handle(data)
|
||||
uci:set("system", uci:get_first("system", "system"), "hostname", data._hostname)
|
||||
uci:save("system")
|
||||
uci:commit("system")
|
||||
end
|
||||
|
||||
return M
|
14
package/gluon-config-mode-hostname/i18n/de.po
Normal file
14
package/gluon-config-mode-hostname/i18n/de.po
Normal file
@ -0,0 +1,14 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"PO-Revision-Date: 2015-03-19 00:54+0100\n"
|
||||
"Last-Translator: Matthias Schiffer <mschiffer@universe-factory.net>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Node name"
|
||||
msgstr "Name dieses Knotens"
|
@ -0,0 +1,5 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid "Node name"
|
||||
msgstr ""
|
36
package/gluon-config-mode-mesh-vpn/Makefile
Normal file
36
package/gluon-config-mode-mesh-vpn/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-config-mode-mesh-vpn
|
||||
PKG_VERSION:=2
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(GLUONDIR)/include/package.mk
|
||||
|
||||
PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
|
||||
|
||||
|
||||
define Package/gluon-config-mode-mesh-vpn
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Toggle mesh-vpn and bandwidth limit
|
||||
DEPENDS:=+gluon-config-mode-core +gluon-mesh-vpn-fastd +gluon-simple-tc
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call GluonBuildI18N,gluon-config-mode-mesh-vpn,i18n)
|
||||
endef
|
||||
|
||||
define Package/gluon-config-mode-mesh-vpn/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(call GluonInstallI18N,gluon-config-mode-mesh-vpn,$(1))
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-config-mode-mesh-vpn))
|
@ -0,0 +1,29 @@
|
||||
local uci = luci.model.uci.cursor()
|
||||
local meshvpn_enabled = uci:get("fastd", "mesh_vpn", "enabled", "0")
|
||||
|
||||
if meshvpn_enabled ~= "1" then
|
||||
return nil
|
||||
else
|
||||
local i18n = require "luci.i18n"
|
||||
local util = require "luci.util"
|
||||
local site = require 'gluon.site_config'
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
|
||||
local pubkey = util.trim(util.exec("/etc/init.d/fastd show_key " .. "mesh_vpn"))
|
||||
local hostname = uci:get_first("system", "system", "hostname")
|
||||
|
||||
local msg = [[<p>]] .. i18n.translate('gluon-config-mode:pubkey') .. [[</p>
|
||||
<div class="the-key">
|
||||
# <%= hostname %>
|
||||
<br/>
|
||||
<%= pubkey %>
|
||||
</div>]]
|
||||
|
||||
return function ()
|
||||
luci.template.render_string(msg, { pubkey=pubkey
|
||||
, hostname=hostname
|
||||
, site=site
|
||||
, sysconfig=sysconfig
|
||||
})
|
||||
end
|
||||
end
|
@ -0,0 +1,64 @@
|
||||
local cbi = require "luci.cbi"
|
||||
local i18n = require "luci.i18n"
|
||||
local uci = luci.model.uci.cursor()
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.section(form)
|
||||
local msg = i18n.translate('Your internet connection can be used to establish an ' ..
|
||||
'encrypted connection with other nodes. ' ..
|
||||
'Enable this option if there are no other nodes reachable ' ..
|
||||
'over WLAN in your vicinity or you want to make a part of ' ..
|
||||
'your connection\'s bandwidth available for the network. You can limit how ' ..
|
||||
'much bandwidth the node will use at most.')
|
||||
local s = form:section(cbi.SimpleSection, nil, msg)
|
||||
|
||||
local o
|
||||
|
||||
o = s:option(cbi.Flag, "_meshvpn", i18n.translate("Use internet connection (mesh VPN)"))
|
||||
o.default = uci:get_bool("fastd", "mesh_vpn", "enabled") and o.enabled or o.disabled
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(cbi.Flag, "_limit_enabled", i18n.translate("Limit bandwidth"))
|
||||
o:depends("_meshvpn", "1")
|
||||
o.default = uci:get_bool("gluon-simple-tc", "mesh_vpn", "enabled") and o.enabled or o.disabled
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(cbi.Value, "_limit_ingress", i18n.translate("Downstream (kbit/s)"))
|
||||
o:depends("_limit_enabled", "1")
|
||||
o.value = uci:get("gluon-simple-tc", "mesh_vpn", "limit_ingress")
|
||||
o.rmempty = false
|
||||
o.datatype = "integer"
|
||||
|
||||
o = s:option(cbi.Value, "_limit_egress", i18n.translate("Upstream (kbit/s)"))
|
||||
o:depends("_limit_enabled", "1")
|
||||
o.value = uci:get("gluon-simple-tc", "mesh_vpn", "limit_egress")
|
||||
o.rmempty = false
|
||||
o.datatype = "integer"
|
||||
end
|
||||
|
||||
function M.handle(data)
|
||||
uci:set("fastd", "mesh_vpn", "enabled", data._meshvpn)
|
||||
uci:save("fastd")
|
||||
uci:commit("fastd")
|
||||
|
||||
-- checks for nil needed due to o:depends(...)
|
||||
if data._limit_enabled ~= nil then
|
||||
uci:set("gluon-simple-tc", "mesh_vpn", "interface")
|
||||
uci:set("gluon-simple-tc", "mesh_vpn", "enabled", data._limit_enabled)
|
||||
uci:set("gluon-simple-tc", "mesh_vpn", "ifname", "mesh-vpn")
|
||||
|
||||
if data._limit_ingress ~= nil then
|
||||
uci:set("gluon-simple-tc", "mesh_vpn", "limit_ingress", data._limit_ingress)
|
||||
end
|
||||
|
||||
if data._limit_egress ~= nil then
|
||||
uci:set("gluon-simple-tc", "mesh_vpn", "limit_egress", data._limit_egress)
|
||||
end
|
||||
|
||||
uci:commit("gluon-simple-tc")
|
||||
uci:commit("gluon-simple-tc")
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
36
package/gluon-config-mode-mesh-vpn/i18n/de.po
Normal file
36
package/gluon-config-mode-mesh-vpn/i18n/de.po
Normal file
@ -0,0 +1,36 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"PO-Revision-Date: 2015-03-19 22:05+0100\n"
|
||||
"Last-Translator: Matthias Schiffer <mschiffer@universe-factory.net>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Downstream (kbit/s)"
|
||||
msgstr "Downstream (kbit/s)"
|
||||
|
||||
msgid "Limit bandwidth"
|
||||
msgstr "Bandbreite begrenzen"
|
||||
|
||||
msgid "Upstream (kbit/s)"
|
||||
msgstr "Upstream (kbit/s)"
|
||||
|
||||
msgid "Use internet connection (mesh VPN)"
|
||||
msgstr "Internetverbindung nutzen (Mesh-VPN)"
|
||||
|
||||
msgid ""
|
||||
"Your internet connection can be used to establish an encrypted connection "
|
||||
"with other nodes. Enable this option if there are no other nodes reachable "
|
||||
"over WLAN in your vicinity or you want to make a part of your connection's "
|
||||
"bandwidth available for the network. You can limit how much bandwidth the "
|
||||
"node will use at most."
|
||||
msgstr ""
|
||||
"Dein Knoten kann deine Internetverbindung nutzen um darüber eine "
|
||||
"verschlüsselte Verbindung zu anderen Knoten aufzubauen. Die dafür "
|
||||
"genutzte Bandbreite kannst du beschränken. Aktiviere die Option, falls keine "
|
||||
"per WLAN erreichbaren Nachbarknoten in deiner Nähe sind oder du deine "
|
||||
"Internetverbindung für das Mesh-Netzwerk zur Verfügung stellen möchtest."
|
@ -0,0 +1,22 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid "Downstream (kbit/s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Limit bandwidth"
|
||||
msgstr ""
|
||||
|
||||
msgid "Upstream (kbit/s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Use internet connection (mesh VPN)"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Your internet connection can be used to establish an encrypted connection "
|
||||
"with other nodes. Enable this option if there are no other nodes reachable "
|
||||
"over WLAN in your vicinity or you want to make a part of your connection's "
|
||||
"bandwidth available for the network. You can limit how much bandwidth the "
|
||||
"node will use at most."
|
||||
msgstr ""
|
59
package/gluon-core/Makefile
Normal file
59
package/gluon-core/Makefile
Normal file
@ -0,0 +1,59 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-core
|
||||
PKG_VERSION:=3
|
||||
PKG_RELEASE:=$(GLUON_VERSION)
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(GLUONDIR)/include/package.mk
|
||||
|
||||
define Package/gluon-core
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Base files of Gluon
|
||||
DEPENDS:=+gluon-site +lua-platform-info +luci-lib-nixio +odhcp6c +firewall
|
||||
endef
|
||||
|
||||
|
||||
define LangConfig
|
||||
config GLUON_LANG_$(1)
|
||||
bool "$(GLUON_LANG_$(1)) language support"
|
||||
depends on PACKAGE_gluon-core
|
||||
default n
|
||||
|
||||
endef
|
||||
|
||||
|
||||
define Package/gluon-core/config
|
||||
$(foreach lang,$(GLUON_SUPPORTED_LANGS),$(call LangConfig,$(lang)))
|
||||
endef
|
||||
|
||||
|
||||
define Package/gluon-core/description
|
||||
Gluon community wifi mesh firmware framework: core
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/gluon-core/install
|
||||
$(CP) ./files/* $(1)/
|
||||
|
||||
$(INSTALL_DIR) $(1)/lib/gluon
|
||||
echo "$(GLUON_VERSION)" > $(1)/lib/gluon/gluon-version
|
||||
endef
|
||||
|
||||
define Package/gluon-core/postinst
|
||||
#!/bin/sh
|
||||
$(call GluonCheckSite,check_site.lua)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-core))
|
10
package/gluon-core/check_site.lua
Normal file
10
package/gluon-core/check_site.lua
Normal file
@ -0,0 +1,10 @@
|
||||
need_string 'site_code'
|
||||
need_string 'site_name'
|
||||
|
||||
need_string('hostname_prefix', false)
|
||||
need_string 'timezone'
|
||||
|
||||
need_string_array('ntp_servers', false)
|
||||
|
||||
need_string_match('prefix4', '^%d+.%d+.%d+.%d+/%d+$')
|
||||
need_string_match('prefix6', '^[%x:]+/%d+$')
|
5
package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade
Executable file
5
package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
for script in /lib/gluon/upgrade/*; do
|
||||
"$script"
|
||||
done
|
10
package/gluon-core/files/lib/gluon/upgrade/001-upgrade
Executable file
10
package/gluon-core/files/lib/gluon/upgrade/001-upgrade
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local fs = require 'luci.fs'
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
|
||||
|
||||
if fs.isfile('/lib/gluon/version/core') and not sysconfig.gluon_version then
|
||||
-- This isn't an initial upgrade, so set gluon_version
|
||||
sysconfig.gluon_version = ''
|
||||
end
|
42
package/gluon-core/files/lib/gluon/upgrade/010-primary-mac
Executable file
42
package/gluon-core/files/lib/gluon/upgrade/010-primary-mac
Executable file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
|
||||
|
||||
if sysconfig.primary_mac then
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
|
||||
local platform = require 'gluon.platform'
|
||||
|
||||
local fs = require 'luci.fs'
|
||||
local util = require 'luci.util'
|
||||
|
||||
|
||||
local try_files = {
|
||||
'/sys/class/ieee80211/phy0/macaddress',
|
||||
'/sys/class/net/eth0/address',
|
||||
}
|
||||
|
||||
if platform.match('ar71xx', 'generic', {'tl-wdr3600', 'tl-wdr4300'}) then
|
||||
table.insert(try_files, 1, '/sys/class/ieee80211/phy1/macaddress')
|
||||
end
|
||||
|
||||
if platform.match('ar71xx', 'generic', {'unifi-outdoor-plus'}) then
|
||||
table.insert(try_files, 1, '/sys/class/net/eth0/address')
|
||||
end
|
||||
|
||||
if platform.match('ar71xx', 'generic', {'archer-c5', 'archer-c7'}) then
|
||||
table.insert(try_files, 1, '/sys/class/net/eth1/address')
|
||||
end
|
||||
|
||||
|
||||
for _, file in ipairs(try_files) do
|
||||
local addr = fs.readfile(file)
|
||||
|
||||
if addr then
|
||||
sysconfig.primary_mac = util.trim(addr)
|
||||
break
|
||||
end
|
||||
end
|
36
package/gluon-core/files/lib/gluon/upgrade/020-interfaces
Executable file
36
package/gluon-core/files/lib/gluon/upgrade/020-interfaces
Executable file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
local gluon_util = require 'gluon.util'
|
||||
local platform = require 'gluon.platform'
|
||||
|
||||
local uci = require('luci.model.uci').cursor()
|
||||
|
||||
|
||||
if not (sysconfig.lan_ifname or sysconfig.wan_ifname) then
|
||||
local function iface_exists(name)
|
||||
return (gluon_util.exec('ip', 'link', 'show', 'dev', (name:gsub('%..*$', ''))) == 0)
|
||||
end
|
||||
|
||||
|
||||
local lan_ifname = uci:get('network', 'lan', 'ifname')
|
||||
local wan_ifname = uci:get('network', 'wan', 'ifname')
|
||||
|
||||
if platform.match('ar71xx', 'generic', {'cpe510', 'nanostation-m', 'nanostation-m-xw', 'unifi-outdoor-plus'}) then
|
||||
lan_ifname, wan_ifname = wan_ifname, lan_ifname
|
||||
end
|
||||
|
||||
if wan_ifname and iface_exists(wan_ifname) then
|
||||
sysconfig.wan_ifname = wan_ifname
|
||||
sysconfig.lan_ifname = lan_ifname
|
||||
else
|
||||
sysconfig.wan_ifname = lan_ifname
|
||||
end
|
||||
|
||||
|
||||
uci:delete('network', 'lan')
|
||||
uci:delete('network', 'wan')
|
||||
|
||||
uci:save('network')
|
||||
uci:commit('network')
|
||||
end
|
18
package/gluon-core/files/lib/gluon/upgrade/030-system
Executable file
18
package/gluon-core/files/lib/gluon/upgrade/030-system
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
|
||||
-- Initial
|
||||
if not sysconfig.gluon_version then
|
||||
local site = require 'gluon.site_config'
|
||||
local util = require 'gluon.util'
|
||||
local uci = require('luci.model.uci').cursor()
|
||||
|
||||
local system = uci:get_first('system', 'system')
|
||||
|
||||
uci:set('system', system, 'hostname', (site.hostname_prefix or '') .. util.node_id())
|
||||
uci:set('system', system, 'timezone', site.timezone)
|
||||
|
||||
uci:save('system')
|
||||
uci:commit('system')
|
||||
end
|
5
package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq
Executable file
5
package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -e /etc/dnsmasq.conf ]; then
|
||||
sed -i -e '/^conf-dir=\/lib\/gluon\/dnsmasq\.d$/d' -e '/^conf-dir=\/var\/gluon\/dnsmasq\.d$/d' /etc/dnsmasq.conf
|
||||
fi
|
58
package/gluon-core/files/lib/gluon/upgrade/110-network
Executable file
58
package/gluon-core/files/lib/gluon/upgrade/110-network
Executable file
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local uci = require('luci.model.uci').cursor()
|
||||
local sysctl = require 'gluon.sysctl'
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
|
||||
|
||||
uci:section('network', 'interface', 'wan',
|
||||
{
|
||||
ifname = sysconfig.wan_ifname,
|
||||
type = 'bridge',
|
||||
peerdns = 0,
|
||||
auto = 1,
|
||||
}
|
||||
)
|
||||
|
||||
if not uci:get('network', 'wan', 'proto') then
|
||||
uci:set('network', 'wan', 'proto', 'dhcp')
|
||||
end
|
||||
|
||||
|
||||
uci:section('network', 'interface', 'wan6',
|
||||
{
|
||||
ifname = 'br-wan',
|
||||
peerdns = 0,
|
||||
ip6table = 1,
|
||||
}
|
||||
)
|
||||
|
||||
if not uci:get('network', 'wan6', 'proto') then
|
||||
uci:set('network', 'wan6', 'proto', 'dhcpv6')
|
||||
end
|
||||
|
||||
|
||||
uci:section('network', 'rule6', 'wan6_lookup',
|
||||
{
|
||||
mark = '0x01/0x01',
|
||||
lookup = 1,
|
||||
}
|
||||
)
|
||||
|
||||
uci:section('network', 'route6', 'wan6_unreachable',
|
||||
{
|
||||
type = 'unreachable',
|
||||
interface = 'loopback',
|
||||
target = '::/0',
|
||||
gateway = '::',
|
||||
table = 1,
|
||||
metric = 65535,
|
||||
}
|
||||
)
|
||||
|
||||
uci:save('network')
|
||||
uci:commit('network')
|
||||
|
||||
|
||||
sysctl.set('net.ipv6.conf.all.accept_ra', 0)
|
||||
sysctl.set('net.ipv6.conf.default.accept_ra', 0)
|
14
package/gluon-core/files/lib/gluon/upgrade/120-ntp-servers
Executable file
14
package/gluon-core/files/lib/gluon/upgrade/120-ntp-servers
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local site = require 'gluon.site_config'
|
||||
local uci = require 'luci.model.uci'
|
||||
|
||||
if not site.ntp_servers or #site.ntp_servers == 0 then
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
local c = uci.cursor()
|
||||
c:delete('system', 'ntp', 'server')
|
||||
c:set_list('system', 'ntp', 'server', site.ntp_servers)
|
||||
c:save('system')
|
||||
c:commit('system')
|
5
package/gluon-core/files/lib/gluon/upgrade/130-reboot-on-oom
Executable file
5
package/gluon-core/files/lib/gluon/upgrade/130-reboot-on-oom
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local sysctl = require 'gluon.sysctl'
|
||||
|
||||
sysctl.set('vm.panic_on_oom', 1)
|
30
package/gluon-core/files/lib/gluon/upgrade/140-firewall-rules
Executable file
30
package/gluon-core/files/lib/gluon/upgrade/140-firewall-rules
Executable file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local site = require 'gluon.site_config'
|
||||
local uci = require 'luci.model.uci'
|
||||
|
||||
local c = uci.cursor()
|
||||
|
||||
|
||||
local function reject_input_on_wan(zone)
|
||||
if zone.name == 'wan' then
|
||||
c:set('firewall', zone['.name'], 'input', 'REJECT')
|
||||
c:set('firewall', zone['.name'], 'conntrack', '1')
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
c:foreach('firewall', 'zone', reject_input_on_wan)
|
||||
|
||||
c:section('firewall', 'rule', 'wan_ssh',
|
||||
{
|
||||
name = 'wan_ssh',
|
||||
src = 'wan',
|
||||
dest_port = '22',
|
||||
proto = 'tcp',
|
||||
target = 'ACCEPT',
|
||||
}
|
||||
)
|
||||
|
||||
c:save('firewall')
|
||||
c:commit('firewall')
|
12
package/gluon-core/files/lib/gluon/upgrade/200-wireless
Executable file
12
package/gluon-core/files/lib/gluon/upgrade/200-wireless
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
|
||||
-- Initial
|
||||
if not sysconfig.gluon_version then
|
||||
local uci = require('luci.model.uci').cursor()
|
||||
|
||||
uci:delete_all('wireless', 'wifi-iface')
|
||||
uci:save('wireless')
|
||||
uci:commit('wireless')
|
||||
end
|
11
package/gluon-core/files/lib/gluon/upgrade/999-version
Executable file
11
package/gluon-core/files/lib/gluon/upgrade/999-version
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/lua
|
||||
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
|
||||
local fs = require 'luci.fs'
|
||||
local util = require 'luci.util'
|
||||
|
||||
|
||||
-- Save the Gluon version in the sysconfig so we know which version we
|
||||
-- upgraded from after the next upgrade
|
||||
sysconfig.gluon_version = util.trim(fs.readfile('/lib/gluon/gluon-version'))
|
1
package/gluon-core/files/lib/upgrade/keep.d/gluon
Normal file
1
package/gluon-core/files/lib/upgrade/keep.d/gluon
Normal file
@ -0,0 +1 @@
|
||||
/lib/gluon/core/sysconfig/
|
31
package/gluon-core/files/usr/lib/lua/gluon/platform.lua
Normal file
31
package/gluon-core/files/usr/lib/lua/gluon/platform.lua
Normal file
@ -0,0 +1,31 @@
|
||||
local platform_info = require 'platform_info'
|
||||
local util = require 'luci.util'
|
||||
|
||||
local setmetatable = setmetatable
|
||||
|
||||
|
||||
module 'gluon.platform'
|
||||
|
||||
setmetatable(_M,
|
||||
{
|
||||
__index = platform_info,
|
||||
}
|
||||
)
|
||||
|
||||
function match(target, subtarget, boards)
|
||||
if get_target() ~= target then
|
||||
return false
|
||||
end
|
||||
|
||||
if get_subtarget() ~= subtarget then
|
||||
return false
|
||||
end
|
||||
|
||||
if not util.contains(boards, get_board_name()) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
21
package/gluon-core/files/usr/lib/lua/gluon/site_config.lua
Normal file
21
package/gluon-core/files/usr/lib/lua/gluon/site_config.lua
Normal file
@ -0,0 +1,21 @@
|
||||
local config = os.getenv('GLUON_SITE_CONFIG') or '/lib/gluon/site.conf'
|
||||
|
||||
local function loader()
|
||||
coroutine.yield('return ')
|
||||
coroutine.yield(io.open(config):read('*a'))
|
||||
end
|
||||
|
||||
-- setfenv doesn't work with Lua 5.2 anymore, but we're using 5.1
|
||||
local site_config = setfenv(assert(load(coroutine.wrap(loader), 'site.conf')), {})()
|
||||
|
||||
local setmetatable = setmetatable
|
||||
|
||||
module 'gluon.site_config'
|
||||
|
||||
setmetatable(_M,
|
||||
{
|
||||
__index = site_config,
|
||||
}
|
||||
)
|
||||
|
||||
return _M
|
34
package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua
Normal file
34
package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua
Normal file
@ -0,0 +1,34 @@
|
||||
local sysconfigdir = '/lib/gluon/core/sysconfig/'
|
||||
|
||||
local function get(_, name)
|
||||
local f = io.open(sysconfigdir .. name)
|
||||
if f then
|
||||
local ret = f:read('*line')
|
||||
f:close()
|
||||
return (ret or '')
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function set(_, name, val)
|
||||
if val then
|
||||
local f = io.open(sysconfigdir .. name, 'w+')
|
||||
f:write(val)
|
||||
f:close()
|
||||
else
|
||||
os.remove(sysconfigdir .. name)
|
||||
end
|
||||
end
|
||||
|
||||
local setmetatable = setmetatable
|
||||
|
||||
module 'gluon.sysconfig'
|
||||
|
||||
setmetatable(_M,
|
||||
{
|
||||
__index = get,
|
||||
__newindex = set,
|
||||
}
|
||||
)
|
||||
|
||||
return _M
|
8
package/gluon-core/files/usr/lib/lua/gluon/sysctl.lua
Normal file
8
package/gluon-core/files/usr/lib/lua/gluon/sysctl.lua
Normal file
@ -0,0 +1,8 @@
|
||||
local util = require 'gluon.util'
|
||||
|
||||
|
||||
module 'gluon.sysctl'
|
||||
|
||||
function set(name, value)
|
||||
util.replace_prefix('/etc/sysctl.conf', name .. '=', name .. '=' .. value .. '\n')
|
||||
end
|
33
package/gluon-core/files/usr/lib/lua/gluon/users.lua
Normal file
33
package/gluon-core/files/usr/lib/lua/gluon/users.lua
Normal file
@ -0,0 +1,33 @@
|
||||
local util = require 'gluon.util'
|
||||
|
||||
local os = os
|
||||
local string = string
|
||||
|
||||
|
||||
module 'gluon.users'
|
||||
|
||||
function add_user(username, uid, gid)
|
||||
util.lock('/var/lock/passwd')
|
||||
util.replace_prefix('/etc/passwd', username .. ':', string.format('%s:*:%u:%u::/var:/bin/false\n', username, uid, gid))
|
||||
util.replace_prefix('/etc/shadow', username .. ':', string.format('%s:*:0:0:99999:7:::\n', username))
|
||||
util.unlock('/var/lock/passwd')
|
||||
end
|
||||
|
||||
function remove_user(username)
|
||||
util.lock('/var/lock/passwd')
|
||||
util.replace_prefix('/etc/passwd', username .. ':')
|
||||
util.replace_prefix('/etc/shadow', username .. ':')
|
||||
util.unlock('/var/lock/passwd')
|
||||
end
|
||||
|
||||
function add_group(groupname, gid)
|
||||
util.lock('/var/lock/group')
|
||||
util.replace_prefix('/etc/group', groupname .. ':', string.format('%s:x:%u:\n', groupname, gid))
|
||||
util.unlock('/var/lock/group')
|
||||
end
|
||||
|
||||
function remove_group(groupname)
|
||||
util.lock('/var/lock/group')
|
||||
util.replace_prefix('/etc/group', groupname .. ':')
|
||||
util.unlock('/var/lock/group')
|
||||
end
|
79
package/gluon-core/files/usr/lib/lua/gluon/util.lua
Normal file
79
package/gluon-core/files/usr/lib/lua/gluon/util.lua
Normal file
@ -0,0 +1,79 @@
|
||||
-- Writes all lines from the file input to the file output except those starting with prefix
|
||||
-- Doesn't close the output file, but returns the file object
|
||||
local function do_filter_prefix(input, output, prefix)
|
||||
local f = io.open(output, 'w+')
|
||||
local l = prefix:len()
|
||||
|
||||
for line in io.lines(input) do
|
||||
if line:sub(1, l) ~= prefix then
|
||||
f:write(line, '\n')
|
||||
end
|
||||
end
|
||||
|
||||
return f
|
||||
end
|
||||
|
||||
|
||||
local function escape_args(ret, arg0, ...)
|
||||
if not arg0 then
|
||||
return ret
|
||||
end
|
||||
|
||||
return escape_args(ret .. "'" .. string.gsub(arg0, "'", "'\\''") .. "' ", ...)
|
||||
end
|
||||
|
||||
|
||||
local os = os
|
||||
local string = string
|
||||
local tonumber = tonumber
|
||||
|
||||
local nixio = require 'nixio'
|
||||
local sysconfig = require 'gluon.sysconfig'
|
||||
|
||||
|
||||
module 'gluon.util'
|
||||
|
||||
function exec(...)
|
||||
return os.execute(escape_args('', ...))
|
||||
end
|
||||
|
||||
-- Removes all lines starting with a prefix from a file, optionally adding a new one
|
||||
function replace_prefix(file, prefix, add)
|
||||
local tmp = file .. '.tmp'
|
||||
local f = do_filter_prefix(file, tmp, prefix)
|
||||
if add then
|
||||
f:write(add)
|
||||
end
|
||||
f:close()
|
||||
os.rename(tmp, file)
|
||||
end
|
||||
|
||||
function lock(file)
|
||||
exec('lock', file)
|
||||
end
|
||||
|
||||
function unlock(file)
|
||||
exec('lock', '-u', file)
|
||||
end
|
||||
|
||||
function node_id()
|
||||
return string.gsub(sysconfig.primary_mac, ':', '')
|
||||
end
|
||||
|
||||
-- Generates a (hopefully) unique MAC address
|
||||
-- The first parameter defines the function and the second
|
||||
-- parameter an ID to add to the MAC address
|
||||
-- Functions and IDs defined so far:
|
||||
-- (1, 0): WAN (for mesh-on-WAN)
|
||||
-- (1, 1): LAN (for mesh-on-LAN)
|
||||
-- (2, n): client interface for the n'th radio
|
||||
-- (3, n): adhoc interface for n'th radio
|
||||
-- (4, 0): mesh VPN
|
||||
function generate_mac(f, i)
|
||||
local m1, m2, m3, m4, m5, m6 = string.match(sysconfig.primary_mac, '(%x%x):(%x%x):(%x%x):(%x%x):(%x%x):(%x%x)')
|
||||
m1 = nixio.bit.bor(tonumber(m1, 16), 0x02)
|
||||
m2 = (tonumber(m2, 16)+f) % 0x100
|
||||
m3 = (tonumber(m3, 16)+i) % 0x100
|
||||
|
||||
return string.format('%02x:%02x:%02x:%s:%s:%s', m1, m2, m3, m4, m5, m6)
|
||||
end
|
40
package/gluon-cron/Makefile
Normal file
40
package/gluon-cron/Makefile
Normal file
@ -0,0 +1,40 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-cron
|
||||
PKG_VERSION:=1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/gluon-cron
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Cron support
|
||||
DEPENDS:=+gluon-core
|
||||
endef
|
||||
|
||||
define Package/gluon-cron/description
|
||||
Gluon community wifi mesh firmware framework: cron support
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
|
||||
endef
|
||||
|
||||
define Package/gluon-cron/install
|
||||
$(CP) ./files/* $(1)/
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-crond $(1)/usr/sbin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-cron))
|
18
package/gluon-cron/files/etc/init.d/gluon-cron
Executable file
18
package/gluon-cron/files/etc/init.d/gluon-cron
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2013 Project Gluon
|
||||
|
||||
START=50
|
||||
|
||||
SERVICE_USE_PID=1
|
||||
SERVICE_WRITE_PID=1
|
||||
SERVICE_DAEMONIZE=1
|
||||
|
||||
CRONDIR=/lib/gluon/cron
|
||||
|
||||
start () {
|
||||
service_start /usr/sbin/gluon-crond "$CRONDIR"
|
||||
}
|
||||
|
||||
stop() {
|
||||
service_stop /usr/sbin/gluon-crond
|
||||
}
|
0
package/gluon-cron/files/lib/gluon/cron/.keep
Normal file
0
package/gluon-cron/files/lib/gluon/cron/.keep
Normal file
3
package/gluon-cron/src/Makefile
Normal file
3
package/gluon-cron/src/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
all: gluon-crond
|
||||
|
||||
gluon-crond: gluon-crond.c
|
316
package/gluon-cron/src/gluon-crond.c
Normal file
316
package/gluon-cron/src/gluon-crond.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
Copyright (c) 2013, 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 <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <dirent.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
typedef struct job {
|
||||
struct job *next;
|
||||
|
||||
uint64_t minutes;
|
||||
uint32_t hours;
|
||||
uint32_t doms;
|
||||
uint16_t months;
|
||||
uint8_t dows;
|
||||
|
||||
char *command;
|
||||
} job_t;
|
||||
|
||||
|
||||
static const char const *const MONTHS[12] = {
|
||||
"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"
|
||||
};
|
||||
|
||||
static const char const *const WEEKDAYS[7] = {
|
||||
"sun", "mon", "tue", "wed", "thu", "fri", "sat"
|
||||
};
|
||||
|
||||
|
||||
static const char *crondir;
|
||||
|
||||
static job_t *jobs = NULL;
|
||||
|
||||
|
||||
static void usage(void) {
|
||||
fprintf(stderr, "Usage: gluon-crond <crondir>\n");
|
||||
}
|
||||
|
||||
|
||||
static inline uint64_t bit(unsigned b) {
|
||||
return ((uint64_t)1) << b;
|
||||
}
|
||||
|
||||
static int strict_atoi(const char *s) {
|
||||
char *end;
|
||||
int ret = strtol(s, &end, 10);
|
||||
|
||||
if (*end)
|
||||
return -1;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t parse_strings(const char *input, const char *const *strings, size_t n) {
|
||||
size_t i;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (strcasecmp(input, strings[i]) == 0)
|
||||
return bit(i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t parse_times(char *input, int min, int n) {
|
||||
uint64_t ret = 0;
|
||||
int step = 1;
|
||||
|
||||
char *comma = strchr(input, ',');
|
||||
if (comma) {
|
||||
*comma = 0;
|
||||
ret = parse_times(comma+1, min, n);
|
||||
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *slash = strchr(input, '/');
|
||||
if (slash) {
|
||||
*slash = 0;
|
||||
step = strict_atoi(slash+1);
|
||||
|
||||
if (step <= 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int begin, end;
|
||||
char *minus = strchr(input, '-');
|
||||
if (minus) {
|
||||
*minus = 0;
|
||||
begin = strict_atoi(input);
|
||||
end = strict_atoi(minus+1);
|
||||
}
|
||||
else if (strcmp(input, "*") == 0) {
|
||||
begin = min;
|
||||
end = min+n-1;
|
||||
}
|
||||
else {
|
||||
begin = end = strict_atoi(input);
|
||||
}
|
||||
|
||||
if (begin < min || end < min)
|
||||
return 0;
|
||||
|
||||
int i;
|
||||
for (i = begin-min; i <= end-min; i += step)
|
||||
ret |= bit(i % n);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_line(const char *line) {
|
||||
job_t job = {};
|
||||
int ret = -1;
|
||||
char *columns[5];
|
||||
int i;
|
||||
int len;
|
||||
|
||||
int matches = sscanf(line, "%ms %ms %ms %ms %ms %n", &columns[0], &columns[1], &columns[2], &columns[3], &columns[4], &len);
|
||||
if (matches != 5 && matches != 6) {
|
||||
if (matches <= 0)
|
||||
ret = 0;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
job.minutes = parse_times(columns[0], 0, 60);
|
||||
if (!job.minutes)
|
||||
goto end;
|
||||
|
||||
job.hours = parse_times(columns[1], 0, 24);
|
||||
if (!job.hours)
|
||||
goto end;
|
||||
|
||||
job.doms = parse_times(columns[2], 1, 31);
|
||||
if (!job.doms)
|
||||
goto end;
|
||||
|
||||
|
||||
job.months = parse_strings(columns[3], MONTHS, 12);
|
||||
|
||||
if (!job.months)
|
||||
job.months = parse_times(columns[3], 1, 12);
|
||||
if (!job.months)
|
||||
goto end;
|
||||
|
||||
job.dows = parse_strings(columns[4], WEEKDAYS, 7);
|
||||
if (!job.dows)
|
||||
job.dows = parse_times(columns[4], 0, 7);
|
||||
if (!job.dows)
|
||||
goto end;
|
||||
|
||||
job.command = strdup(line+len);
|
||||
|
||||
job_t *jobp = malloc(sizeof(job_t));
|
||||
*jobp = job;
|
||||
|
||||
jobp->next = jobs;
|
||||
jobs = jobp;
|
||||
|
||||
ret = 0;
|
||||
|
||||
end:
|
||||
for (i = 0; i < matches && i < 5; i++)
|
||||
free(columns[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void read_crontab(const char *name) {
|
||||
FILE *file = fopen(name, "r");
|
||||
if (!file) {
|
||||
syslog(LOG_WARNING, "unable to read crontab `%s'", name);
|
||||
return;
|
||||
}
|
||||
|
||||
char line[16384];
|
||||
unsigned lineno = 0;
|
||||
|
||||
while (fgets(line, sizeof(line), file)) {
|
||||
lineno++;
|
||||
|
||||
char *comment = strchr(line, '#');
|
||||
if (comment)
|
||||
*comment = 0;
|
||||
|
||||
if (handle_line(line))
|
||||
syslog(LOG_WARNING, "syntax error in `%s', line %u", name, lineno);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
|
||||
static void read_crondir(void) {
|
||||
DIR *dir;
|
||||
|
||||
if (chdir(crondir) || ((dir = opendir(".")) == NULL)) {
|
||||
fprintf(stderr, "Unable to read crondir `%s'\n", crondir);
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
if (ent->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
read_crontab(ent->d_name);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
|
||||
static void run_job(const job_t *job) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
execl("/bin/sh", "/bin/sh", "-c", job->command, (char*)NULL);
|
||||
syslog(LOG_ERR, "unable to run job: exec failed");
|
||||
_exit(1);
|
||||
}
|
||||
else if (pid < 0) {
|
||||
syslog(LOG_ERR, "unable to run job: fork failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void check_job(const job_t *job, const struct tm *tm) {
|
||||
if (!(job->minutes & bit(tm->tm_min)))
|
||||
return;
|
||||
|
||||
if (!(job->hours & bit(tm->tm_hour)))
|
||||
return;
|
||||
|
||||
if (!(job->doms & bit(tm->tm_mday-1)))
|
||||
return;
|
||||
|
||||
if (!(job->months & bit(tm->tm_mon)))
|
||||
return;
|
||||
|
||||
if (!(job->dows & bit(tm->tm_wday)))
|
||||
return;
|
||||
|
||||
run_job(job);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
usage();
|
||||
|
||||
exit(argc < 2 ? 0 : 1);
|
||||
}
|
||||
|
||||
crondir = argv[1];
|
||||
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
read_crondir();
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm *tm = localtime(&t);
|
||||
int minute = tm->tm_min;
|
||||
|
||||
while (1) {
|
||||
sleep(60 - t%60);
|
||||
|
||||
t = time(NULL);
|
||||
tm = localtime(&t);
|
||||
|
||||
minute = (minute+1)%60;
|
||||
if (tm->tm_min != minute) {
|
||||
/* clock has moved, don't execute jobs */
|
||||
minute = tm->tm_min;
|
||||
continue;
|
||||
}
|
||||
|
||||
job_t *job;
|
||||
for (job = jobs; job; job = job->next)
|
||||
check_job(job, tm);
|
||||
}
|
||||
}
|
40
package/gluon-ebtables-filter-multicast/Makefile
Normal file
40
package/gluon-ebtables-filter-multicast/Makefile
Normal file
@ -0,0 +1,40 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gluon-ebtables-filter-multicast
|
||||
PKG_VERSION:=1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/gluon-ebtables-filter-multicast
|
||||
SECTION:=gluon
|
||||
CATEGORY:=Gluon
|
||||
TITLE:=Ebtables filters for multicast packets
|
||||
DEPENDS:=+gluon-core +gluon-ebtables
|
||||
endef
|
||||
|
||||
define Package/gluon-ebtables-filter-multicast/description
|
||||
Gluon community wifi mesh firmware framework: Ebtables filters for multicast packets
|
||||
|
||||
These filters drop non-essential multicast traffic before it enters the mesh.
|
||||
|
||||
Allowed protocols are: DHCP, DHCPv6, ARP, ICMP, ICMPv6, BitTorrent local peer discovery, BABEL and OSPF
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/gluon-ebtables-filter-multicast/install
|
||||
$(CP) ./files/* $(1)/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,gluon-ebtables-filter-multicast))
|
@ -0,0 +1 @@
|
||||
chain('MULTICAST_OUT', 'DROP')
|
@ -0,0 +1,3 @@
|
||||
rule 'MULTICAST_OUT -p ARP --arp-opcode Reply --arp-ip-src 0.0.0.0 -j DROP'
|
||||
rule 'MULTICAST_OUT -p ARP --arp-opcode Request --arp-ip-dst 0.0.0.0 -j DROP'
|
||||
rule 'MULTICAST_OUT -p ARP -j RETURN'
|
@ -0,0 +1 @@
|
||||
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination-port 6696 -j RETURN'
|
@ -0,0 +1 @@
|
||||
rule 'MULTICAST_OUT -p IPv4 --ip-destination 239.192.152.143 --ip-protocol udp --ip-destination-port 6771 -j RETURN'
|
@ -0,0 +1 @@
|
||||
rule 'MULTICAST_OUT -p IPv4 --ip-protocol udp --ip-destination-port 67 -j RETURN'
|
@ -0,0 +1 @@
|
||||
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j RETURN'
|
@ -0,0 +1 @@
|
||||
rule 'MULTICAST_OUT -p IPv4 --ip-protocol icmp -j RETURN'
|
@ -0,0 +1,2 @@
|
||||
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol 0 -j RETURN' -- hop-by-hop
|
||||
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp -j RETURN'
|
@ -0,0 +1 @@
|
||||
rule 'MULTICAST_OUT -p IPv4 --ip-protocol igmp -j RETURN'
|
@ -0,0 +1,2 @@
|
||||
rule 'MULTICAST_OUT -p IPv4 --ip-protocol ospf -j RETURN'
|
||||
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ospf -j RETURN'
|
@ -0,0 +1 @@
|
||||
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination ff02::9 --ip6-destination-port 521 -j RETURN'
|
@ -0,0 +1,2 @@
|
||||
rule 'FORWARD --logical-out br-client -o bat0 -d Multicast -j MULTICAST_OUT'
|
||||
rule 'OUTPUT --logical-out br-client -o bat0 -d Multicast -j MULTICAST_OUT'
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user