diff --git a/package/gluon-announce/files/lib/gluon/announce/collect.lua b/package/gluon-announce/files/lib/gluon/announce/collect.lua
index 4339207523590e2928ac8f2b9c33a3c66a42d45b..3510fbf02484e9ab9c24a80656fffefb41341dc7 100755
--- a/package/gluon-announce/files/lib/gluon/announce/collect.lua
+++ b/package/gluon-announce/files/lib/gluon/announce/collect.lua
@@ -2,7 +2,6 @@
 
 local announce = require 'gluon.announce'
 local json = require 'luci.jsonc'
-local ltn12 = require 'luci.ltn12'
 
 local announce_dir = '/lib/gluon/announce/' .. arg[1] .. '.d'
 
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/nproc b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/nproc
index 3072f8f8e30003d9131020073c2cbeb848b8dff4..fc94bd5bc44f800e4890745d52be638a36b4d0d2 100644
--- a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/nproc
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/nproc
@@ -1,8 +1,8 @@
 local n = 0
 
-local cpus = util.trim(fs.readfile('/sys/devices/system/cpu/online'))
+local cpus = util.readline(io.open('/sys/devices/system/cpu/online'))
 
-for _, entry in ipairs(cpus:split(',')) do
+for entry in cpus:gmatch('([^,]+)') do
   local x, y = entry:match('(%d+)-(%d+)')
   if x then
     n = n + tonumber(y) - tonumber(x) + 1
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware
index cf50f79fc67b6c1c72a749c07541fc3991d9369e..53f6fbaf72748fdc6c8f1d030d8a6647341d839e 100644
--- a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware
@@ -1,4 +1,4 @@
 return {
-	base = 'gluon-' .. util.trim(fs.readfile('/lib/gluon/gluon-version')),
-	release = util.trim(fs.readfile('/lib/gluon/release')),
+	base = 'gluon-' .. util.readline(io.open('/lib/gluon/gluon-version')),
+	release = util.readline(io.open('/lib/gluon/release')),
 }
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code
index 876fb6d89cba62700ebb1c78a48a908bee3fdc9a..fa7d9e804139040ea479ec5da56ff184a5ae7447 100644
--- a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code
@@ -1,3 +1 @@
-local site = require 'gluon.site_config'
-
-return site.site_code
+return require('gluon.site_config').site_code
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime b/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime
index 845de268074766e91a7c5d8d52085ae0846f286e..6125882045cf1a36388374e11260d008f743fc80 100644
--- a/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime
@@ -1 +1 @@
-return tonumber(fs.readfile('/proc/uptime'):match('^[^ ]+ ([^ ]+)'))
+return tonumber(util.readline(io.open('/proc/uptime')):match('^[^ ]+ ([^ ]+)'))
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg b/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg
index d79973aa7d405cfea3a920416651f68b84a09010..3548ac7a832012bf5439cbbccc20e6a3547fb981 100644
--- a/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg
@@ -1 +1 @@
-return tonumber(fs.readfile('/proc/loadavg'):match('^([^ ]+) '))
+return tonumber(util.readline(io.open('/proc/loadavg')):match('^([^ ]+) '))
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory b/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory
index 7b07a1079e0dfe3442c7d939613e521161c824b1..2e880346ab736d5ec0a4b19f56c2458a3e58840b 100644
--- a/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory
@@ -1,4 +1,4 @@
-local data = fs.readfile('/proc/meminfo')
+local data = io.open('/proc/meminfo'):read('*a')
 
 local fields = {}
 for k, v in data:gmatch('([^\n:]+):%s*(%d+) kB') do
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes b/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes
index 33ecff668324e9b762a8d8fb1d2eb842415520cd..30f0979011af1b7adeea4af6d384731fa6ec4d48 100644
--- a/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes
@@ -1,3 +1,3 @@
-local running, total = fs.readfile('/proc/loadavg'):match('^[^ ]+ [^ ]+ [^ ]+ (%d+)/(%d+)')
+local running, total = util.readline(io.open('/proc/loadavg')):match('^[^ ]+ [^ ]+ [^ ]+ (%d+)/(%d+)')
 
 return { running = tonumber(running), total = tonumber(total) }
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime b/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime
index 0bc45beaeae3bdb55973289a1862b1fad65be0a9..509d1470d882f195c7ad379088e534c7f4ac69e0 100644
--- a/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime
@@ -1 +1 @@
-return tonumber(fs.readfile('/proc/uptime'):match('^([^ ]+) '))
+return tonumber(util.readline(io.open('/proc/uptime')):match('^([^ ]+) '))
diff --git a/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua b/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua
index fdd2479e0872cc4a7e35b5aabadc3ade198297e0..444dbd338c2e502dc8039c4198cf6ddc4b0044a2 100644
--- a/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua
+++ b/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua
@@ -4,7 +4,7 @@ module('gluon.announce', package.seeall)
 
 fs = require 'nixio.fs'
 uci = require('luci.model.uci').cursor()
-util = require 'luci.util'
+util = require 'gluon.util'
 
 local function collect_entry(entry)
 	if fs.stat(entry, 'type') == 'dir' then
diff --git a/package/gluon-core/files/usr/lib/lua/gluon/util.lua b/package/gluon-core/files/usr/lib/lua/gluon/util.lua
index 7d7809b1aff5abce2863b1fd8043b778501825eb..86b868054ff1901a7c49c512a4709d9069d2bc77 100644
--- a/package/gluon-core/files/usr/lib/lua/gluon/util.lua
+++ b/package/gluon-core/files/usr/lib/lua/gluon/util.lua
@@ -38,7 +38,7 @@ local uci = require('luci.model.uci').cursor()
 module 'gluon.util'
 
 function exec(...)
-	return os.execute(escape_args('', ...))
+	return os.execute(escape_args('', 'exec', ...))
 end
 
 -- Removes all lines starting with a prefix from a file, optionally adding a new one
@@ -52,6 +52,12 @@ function replace_prefix(file, prefix, add)
 	os.rename(tmp, file)
 end
 
+function readline(fd)
+	local line = fd:read('*l')
+	fd:close()
+	return line
+end
+
 function lock(file)
 	exec('lock', file)
 end
diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/batadv b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/batadv
index e83f188f4c67ded5c07479da758404a20515b25c..69b7981dd7f545f3559150392e6a4d64a4f8eb2b 100644
--- a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/batadv
+++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/batadv
@@ -5,7 +5,7 @@ function ifname2address(ifname)
   if ifname_address_cache[ifname] ~= nil then
     ifaddress = ifname_address_cache[ifname]
   else
-    ifaddress = util.trim(fs.readfile("/sys/class/net/" .. ifname .. "/address"))
+    ifaddress = util.readline(io.open("/sys/class/net/" .. ifname .. "/address"))
     ifname_address_cache[ifname] = ifaddress
   end
 
diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/wifi b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/wifi
index 26168003ab9b6e50324aec04cc1a4aabf650f89f..bbe5fc966ac20bb16a2d9a90c319b3ed8dd2f306 100644
--- a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/wifi
+++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/wifi
@@ -16,18 +16,17 @@ end
 
 function interfaces()
   local interfaces = {}
-  for _, line in ipairs(util.split(util.exec('batctl if'))) do
-    ifname = line:match('^(.-): active')
-    if ifname ~= nil then
-      pcall(function()
-        local address = util.trim(fs.readfile('/sys/class/net/' .. ifname .. '/address'))
-        local wifitype = iwinfo.type(ifname)
-        if wifitype ~= nil then
-          interfaces[address] = { ifname = ifname, iw = iwinfo[wifitype] }
-        end
-      end)
-    end
+  local popen = io.popen('exec batctl if')
+  for ifname in popen:read('*a'):gmatch('([^%s]+): active') 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
+  popen:close()
 
   return interfaces
 end
diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh/bat0/interfaces b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh/bat0/interfaces
index 1ac22d30cfb4eb76517eeed8650ed1fb90ed39a2..b1d30efe876e692549daf6371e99e50b2e5d4d1e 100644
--- a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh/bat0/interfaces
+++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh/bat0/interfaces
@@ -1,5 +1,3 @@
-local list = util.exec('batctl if')
-
 local wireless = {}
 local tunnel = {}
 local other = {}
@@ -7,21 +5,27 @@ local other = {}
 local function get_address(t, ifname)
   pcall(
     function()
-      table.insert(t, util.trim(fs.readfile('/sys/class/net/' .. ifname .. '/address')))
+      table.insert(t, util.readline(io.open('/sys/class/net/' .. ifname .. '/address')))
     end
   )
 end
 
-local function is_wireless(ifname)
-  local type = fs.stat('/sys/class/net/' .. ifname .. '/wireless', 'type')
+local function file_exists(filename)
+  local f = io.open(filename)
+  if f == nil then
+    return false
+  else
+    f:close()
+    return true
+  end
+end
 
-  return type == 'directory'
+local function is_wireless(ifname)
+  return file_exists('/sys/class/net/' .. ifname .. '/wireless')
 end
 
 local function is_tuntap(ifname)
-  local type = fs.stat('/sys/class/net/' .. ifname .. '/tun_flags', 'type')
-
-  return type == 'regular'
+  return file_exists('/sys/class/net/' .. ifname .. '/tun_flags')
 end
 
 local function nil_table(t)
@@ -32,18 +36,17 @@ local function nil_table(t)
   end
 end
 
-for _, line in ipairs(util.split(list)) do
-  local ifname = line:match('^(.-):')
-  if ifname ~= nil then
-    if is_wireless(ifname) then
-      get_address(wireless, ifname)
-    elseif is_tuntap(ifname) then
-      get_address(tunnel, ifname)
-    else
-      get_address(other, ifname)
-    end
+local popen = io.popen('exec batctl if')
+for ifname in popen:read('*a'):gmatch('([^%s]+): active') 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
+popen:close()
 
 return {
   wireless = nil_table(wireless),
diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh_interfaces b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh_interfaces
index 1fef5e104becb01cd3669d99fdb7b31cbcf55545..c69ca4ef998518e0882d2a4769252e91607f70da 100644
--- a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh_interfaces
+++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh_interfaces
@@ -1,15 +1,13 @@
-local list = util.exec('batctl if')
-
 local interfaces = {}
-for _, line in ipairs(util.split(list)) do
-  local ifname = line:match('^(.-):')
-  if ifname ~= nil then
-    pcall(
-      function()
-	table.insert(interfaces, util.trim(fs.readfile('/sys/class/net/' .. ifname .. '/address')))
-      end
-    )
-  end
+
+local popen = io.popen('exec batctl if')
+for ifname in popen:read('*a'):gmatch('([^%s]+): active') do
+  pcall(
+    function()
+      table.insert(interfaces, util.readline(io.open('/sys/class/net/' .. ifname .. '/address')))
+    end
+  )
 end
+popen:close()
 
 return interfaces
diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/version b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/version
index abb317e9dc1efa75ba982b013a1e3ff2f7254093..e0f556cff123c45c9c085ba37319461325081348 100644
--- a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/version
+++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/version
@@ -1 +1 @@
-return util.trim(fs.readfile('/sys/module/batman_adv/version'))
+return util.readline(io.open('/sys/module/batman_adv/version'))
diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/gateway b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/gateway
index a1be9ac027db12d4f4e422e39b4edf3bd9583378..67623b0c4670bad16ab7f883472b41b07f37bdac 100644
--- a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/gateway
+++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/gateway
@@ -1,5 +1,14 @@
-local gateway = util.trim(util.exec("batctl -m bat0 gateways | awk '/^=>/ { print $2 }'"))
+local gateway = ''
+
+local popen = io.popen("exec batctl -m bat0 gateways")
+for line in popen:lines() do
+  if line:sub(1, 3) == '=> ' then
+    gateway = line:sub(4, 20)
+    break
+  end
+end
+popen:close()
 
 if gateway ~= '' then
-	return gateway
+  return gateway
 end
diff --git a/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/nodeinfo.d/software/fastd b/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/nodeinfo.d/software/fastd
index bbfbda089e4075e65a88925e1aa8be11c9a28e40..7b1ff4bcaf704ab8cfe58256d36994220003c45a 100644
--- a/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/nodeinfo.d/software/fastd
+++ b/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/nodeinfo.d/software/fastd
@@ -1,4 +1,5 @@
-return {
-	enabled = uci:get_bool('fastd', 'mesh_vpn', 'enabled'),
-	version = util.exec('fastd -v'):match('^[^%s]+%s+([^\n]+)'),
+local ret = {
+	enabled = uci:get('fastd', 'mesh_vpn', 'enabled') ~= 0,
+	version = util.readline(io.popen('exec fastd -v')):match('^[^%s]+%s+(.+)'),
 }
+return ret
diff --git a/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/statistics.d/mesh_vpn b/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/statistics.d/mesh_vpn
index 0d6d66f0ba4d9b0a6fd4f5570a7cb78071465317..42efe827fabd9fcc18217565c621b36979933a64 100644
--- a/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/statistics.d/mesh_vpn
+++ b/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/statistics.d/mesh_vpn
@@ -2,7 +2,6 @@ local json = require 'luci.jsonc'
 local ltn12 = require 'luci.ltn12'
 local nixio = require 'nixio'
 local site = require 'gluon.site_config'
-local uci = require('luci.model.uci').cursor()
 
 local fastd_sock = nixio.socket('unix', 'stream')
 local socket_path = uci:get('fastd', 'mesh_vpn', 'status_socket')