* Rewrote Luci to be coroutine-safe allowing the use of non-forking webservers

* Setting base version to 0.7
This commit is contained in:
Steven Barth 2008-06-14 14:12:12 +00:00
parent 50fd298415
commit 855b7582d3
27 changed files with 416 additions and 276 deletions

View file

@ -32,7 +32,7 @@ hostcopy:
mkdir -p host
for i in $(MODULES); do cp -a $$i/dist/* host/ -R 2>/dev/null || true; done
rm -f host/luci
ln -s .$(LUCI_INSTALLDIR) host/luci
ln -s .$(LUCI_MODULEDIR) host/luci
run: host
libs/sgi-webuci/host/buildconfig.sh `pwd`/host > host/etc/boa/boa.conf

6
NOTICE
View file

@ -1,7 +1,13 @@
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0.
Contains code from:
BinDecHex - Copyright 2007 Tim Kelly/Dialectronics
coxpcall - Copyright 2005 - Kepler Project (www.keplerproject.org)
Luci-Statistics - Statistics for LuCI
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0.

View file

@ -12,7 +12,7 @@ function index()
end
function action_activate()
local mac = luci.sys.net.ip4mac(luci.http.env.REMOTE_ADDR)
local mac = luci.sys.net.ip4mac(luci.http.getenv("REMOTE_ADDR"))
if mac and luci.http.formvalue("accept") then
os.execute("luci-splash add "..mac.." >/dev/null 2>&1")
luci.http.redirect(luci.model.uci.get("freifunk", "community", "homepage"))

View file

@ -149,8 +149,8 @@ function statistics_render()
require("luci.model.uci")
local vars = luci.http.formvalues()
local req = luci.dispatcher.request
local path = luci.dispatcher.dispatched.path
local req = luci.dispatcher.context.request
local path = luci.dispatcher.context.dispatched.path
local uci = luci.model.uci
local spans = luci.util.split( uci.get( "luci_statistics", "collectd_rrdtool", "RRATimespans" ), "%s+", nil, true )
local span = vars.timespan or uci.get( "luci_statistics", "rrdtool", "default_timespan" ) or spans[1]
@ -160,10 +160,10 @@ function statistics_render()
local images = { }
-- find requested plugin and instance
for i, p in ipairs( luci.dispatcher.dispatched.path ) do
if luci.dispatcher.dispatched.path[i] == "graph" then
plugin = luci.dispatcher.dispatched.path[i+1]
instances = { luci.dispatcher.dispatched.path[i+2] }
for i, p in ipairs( luci.dispatcher.context.dispatched.path ) do
if luci.dispatcher.context.dispatched.path[i] == "graph" then
plugin = luci.dispatcher.context.dispatched.path[i+1]
instances = { luci.dispatcher.context.dispatched.path[i+2] }
end
end

View file

@ -500,7 +500,7 @@ function Graph.render( self, plugin, plugin_instance )
-- check for a whole graph handler
local plugin_def = "luci.statistics.rrdtool.definitions." .. plugin
local stat, def = pcall( require, plugin_def )
local stat, def = luci.util.copcall( require, plugin_def )
if stat and def and type(def.rrdargs) == "function" then
@ -539,7 +539,7 @@ function Graph.render( self, plugin, plugin_instance )
-- check for data type handler
local dtype_def = plugin_def .. "." .. dtype
local stat, def = pcall( require, dtype_def )
local stat, def = luci.util.copcall( require, dtype_def )
if stat and def and type(def.rrdargs) == "function" then

View file

@ -1,4 +1,9 @@
LUAC = luac
LUAC_OPTIONS = -s
LUCI_INSTALLDIR = /usr/lib/lua/luci
LUA_MODULEDIR = /usr/lib/lua
LUA_LIBRARYDIR = /usr/lib/lua
LUCI_MODULEDIR = $(LUA_MODULEDIR)/luci
LUCI_LIBRARYDIR = $(LUA_LIBRARYDIR)/luci

View file

@ -12,9 +12,11 @@ compile:
clean: luaclean
luasource:
mkdir -p dist$(LUCI_INSTALLDIR)
mkdir -p dist$(LUA_MODULEDIR)
mkdir -p dist$(LUCI_MODULEDIR)
cp -a root/* dist -R 2>/dev/null || true
cp -a luasrc/* dist$(LUCI_INSTALLDIR) -R 2>/dev/null || true
cp -a luasrc/* dist$(LUCI_MODULEDIR) -R 2>/dev/null || true
cp -a lua/* dist$(LUA_MODULEDIR) -R 2>/dev/null || true
for i in $$(find dist -name .svn); do rm $$i -rf; done
luacompile: luasource

View file

@ -34,8 +34,8 @@ $(LUAPOSIX_DIR)/.patched: $(LUAPOSIX_DIR)/.prepared $(LUAPOSIX_PATCHDIR)/series
compile: $(LUAPOSIX_DIR)/.patched
$(MAKE) -C $(LUAPOSIX_DIR) CC=$(CC) CFLAGS="$(CFLAGS) $(LUA_CFLAGS)" LDFLAGS="$(LDFLAGS) $(LUA_SHLIBS)" OS="$(OS)"
mkdir -p dist/usr/lib/lua
cp $(LUAPOSIX_DIR)/posix.so dist/usr/lib/lua/
mkdir -p dist$(LUA_LIBRARYDIR)
cp $(LUAPOSIX_DIR)/posix.so dist$(LUA_LIBRARYDIR)
luasource:
compile-all: compile

View file

@ -5,7 +5,7 @@ PKG_SOURCE_URL:=https://dev.leipzig.freifunk.net/svn/ff-luci/$(PKG_BRANCH)
PKG_REV:=$(shell LC_ALL=C svn info ${PKG_SOURCE_URL} | sed -ne's/^Last Changed Rev: //p')
PKG_NAME:=luci
PKG_VERSION:=0.6+svn$(PKG_REV)
PKG_VERSION:=0.7+svn$(PKG_REV)
PKG_RELEASE:=1
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)

View file

@ -1,5 +1,5 @@
<%+header%>
<form method="post" action="<%=luci.http.env.REQUEST_URI%>">
<form method="post" action="<%=luci.http.getenv("REQUEST_URI")%>">
<div>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<input type="hidden" name="cbi.submit" value="1" />

View file

@ -25,5 +25,5 @@ limitations under the License.
]]--
module("luci", package.seeall)
__version__ = "0.6"
__version__ = "0.7"
__appname__ = "LuCI"

View file

@ -36,7 +36,7 @@ function class(base)
setmetatable(inst, {__index = class})
if inst.__init__ then
local stat, err = pcall(inst.__init__, inst, ...)
local stat, err = copcall(inst.__init__, inst, ...)
if not stat then
error(err)
end
@ -152,6 +152,12 @@ function pcdata(value)
end
-- Returns an error message to stdout
function perror(obj)
io.stderr:write(tostring(obj) .. "\n")
end
-- Resets the scope of f doing a shallow copy of its scope into a new table
function resfenv(f)
setfenv(f, clone(getfenv(f)))
@ -255,6 +261,34 @@ function strip_bytecode(dump)
end
-- Creates a new threadlocal store
function threadlocal()
local tbl = {}
local function get(self, key)
local c = coroutine.running()
local thread = coxpt[c] or c or 0
if not rawget(self, thread) then
rawset(self, thread, {})
end
return rawget(self, thread)[key]
end
local function set(self, key, value)
local c = coroutine.running()
local thread = coxpt[c] or c or 0
if not rawget(self, thread) then
rawset(self, thread, {})
end
rawget(self, thread)[key] = value
end
setmetatable(tbl, {__index = get, __newindex = set})
return tbl
end
-- Removes whitespace from beginning and end of a string
function trim(str)
local s = str:gsub("^%s*(.-)%s*$", "%1")
@ -355,3 +389,46 @@ end
function vspairs(t)
return _sortiter( t, function (a,b) return t[a] < t[b] end )
end
-- Coroutine safe xpcall and pcall versions modified for Luci
-- original version:
-- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
local performResume, handleReturnValue
local oldpcall, oldxpcall = pcall, xpcall
coxpt = {}
function handleReturnValue(err, co, status, ...)
if not status then
return false, err(debug.traceback(co, (...)), ...)
end
if coroutine.status(co) == 'suspended' then
return performResume(err, co, coroutine.yield(...))
else
return true, ...
end
end
function performResume(err, co, ...)
return handleReturnValue(err, co, coroutine.resume(co, ...))
end
function coxpcall(f, err, ...)
local res, co = oldpcall(coroutine.create, f)
if not res then
local params = {...}
local newf = function() return f(unpack(params)) end
co = coroutine.create(newf)
end
local c = coroutine.running()
coxpt[co] = coxpt[c] or c or 0
return performResume(err, co, ...)
end
local function id(trace, ...)
return ...
end
function copcall(f, ...)
return coxpcall(f, id, ...)
end

View file

@ -6,8 +6,8 @@ include ../../build/gccconfig.mk
$(COMPILE) $(LUA_CFLAGS) $(FPIC) -c -o $@ $<
compile: src/fastindex.o
mkdir -p dist$(LUCI_INSTALLDIR)
$(LINK) $(SHLIB_FLAGS) -o dist$(LUCI_INSTALLDIR)/fastindex.so src/fastindex.o $(LUA_SHLIBS)
mkdir -p dist$(LUCI_LIBRARYDIR)
$(LINK) $(SHLIB_FLAGS) -o dist$(LUCI_LIBRARYDIR)/fastindex.so src/fastindex.o $(LUA_SHLIBS)
clean:
rm -f src/*.o

View file

@ -24,80 +24,49 @@ limitations under the License.
]]--
module("luci.sgi.haserl", package.seeall)
require("luci.fs")
require("luci.http")
require("luci.util")
require("luci.dispatcher")
-- Environment Table
luci.http.env = ENV
-- Enforces user authentification
function luci.http.basic_auth(verify_callback, realm)
-- Dummy for Haserl
return true
end
-- Returns the main dispatcher URL
function luci.http.dispatcher()
return luci.http.env.SCRIPT_NAME or ""
end
-- Returns the upload dispatcher URL
function luci.http.dispatcher_upload()
return luci.http.dispatcher() .. "-upload"
end
-- Returns a table of all COOKIE, GET and POST Parameters
function luci.http.formvalues()
return FORM
end
-- Gets form value from key
function luci.http.formvalue(key, default)
local c = luci.http.formvalues()
function run()
local r = luci.http.Request()
r.env = ENV
r.request = normalize_table(FORM)
for match in key:gmatch("[%w-_]+") do
c = c[match]
if c == nil then
return default
local x = coroutine.create(luci.dispatcher.httpdispatch)
while coroutine.status(x) ~= "dead" do
local res, id, data1, data2 = coroutine.resume(x, r)
if not res then
print("Status: 500 Internal Server Error")
print("Content-Type: text/plain\n")
print(id)
break;
end
if id == 1 then
io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\n")
elseif id == 2 then
io.write(data1 .. ": " .. data2 .. "\n")
elseif id == 3 then
io.write("\n")
elseif id == 4 then
io.write(data1)
end
end
end
function normalize_table(table, prefix)
prefix = prefix and prefix .. "." or ""
local new = {}
for k,v in pairs(table) do
if type(v) == "table" and #v == 0 then
luci.util.update(new, normalize_table(v, prefix .. k))
else
new[prefix .. k] = v
end
end
return c
end
-- Gets a table of values with a certain prefix
function luci.http.formvaluetable(prefix)
return luci.http.formvalue(prefix, {})
end
-- Sends a custom HTTP-Header
function luci.http.header(key, value)
print(key .. ": " .. value)
end
-- Set Content-Type
function luci.http.prepare_content(type)
print("Content-Type: "..type.."\n")
end
-- Asks the browser to redirect to "url"
function luci.http.redirect(url)
luci.http.status(302, "Found")
luci.http.header("Location", url)
print()
end
-- Returns the path of an uploaded file
-- WARNING! File uploads can be easily spoofed! Do additional sanity checks!
function luci.http.upload(name)
local fpath = luci.http.formvalue(name)
local fname = luci.http.formvalue(name .. "_name")
if fpath and fname and luci.fs.isfile(fpath) then
return fpath
end
end
-- Sets HTTP-Status-Header
function luci.http.status(code, message)
print("Status: " .. tostring(code) .. " " .. message)
return new
end

View file

@ -1,4 +1,4 @@
#!/usr/bin/haserl --shell=luac
require("luci.dispatcher")
require("luci.sgi.haserl")
luci.dispatcher.indexcache = "/tmp/.luciindex"
luci.dispatcher.httpdispatch()
luci.sgi.haserl.run()

View file

@ -24,93 +24,36 @@ limitations under the License.
]]--
module("luci.sgi.webuci", package.seeall)
require("luci.http")
require("luci.util")
require("luci.dispatcher")
local status_set = false
-- Initialize the environment
function initenv(env, vars)
luci.http.env = env
luci.http.vars = vars
end
-- Enforces user authentification
function luci.http.basic_auth(verify_callback, realm)
local user = luci.http.env.auth_user
local pass = luci.http.env.auth_password
realm = realm or ""
function run(env, vars)
local r = luci.http.Request()
r.env = env
r.request = vars
if not user or not verify_callback(user, pass) then
luci.http.status("401", "Unauthorized")
luci.http.header("WWW-Authenticate", string.format('Basic realm="%s"', realm))
return false
else
return true
end
end
-- Returns the main dispatcher URL
function luci.http.dispatcher()
return luci.http.env.SCRIPT_NAME or ""
end
-- Returns the upload dispatcher URL
function luci.http.dispatcher_upload()
-- To be implemented
end
-- Returns a table of all COOKIE, GET and POST Parameters
function luci.http.formvalues()
return luci.http.vars
end
-- Gets form value from key
function luci.http.formvalue(key, default)
return luci.http.formvalues()[key] or default
end
-- Gets a table of values with a certain prefix
function luci.http.formvaluetable(prefix)
local vals = {}
prefix = prefix and prefix .. "." or "."
local x = coroutine.create(luci.dispatcher.httpdispatch)
for k, v in pairs(luci.http.formvalues()) do
if k:find(prefix, 1, true) == 1 then
vals[k:sub(#prefix + 1)] = v
while coroutine.status(x) ~= "dead" do
local res, id, data1, data2 = coroutine.resume(x, r)
if not res then
print(env.SERVER_PROTOCOL .. " 500 Internal Server Error")
print("Content-Type: text/plain\n")
print(id)
break;
end
if id == 1 then
io.write(env.SERVER_PROTOCOL .. " " .. tostring(data1) .. " " .. data2 .. "\n")
elseif id == 2 then
io.write(data1 .. ": " .. data2 .. "\n")
elseif id == 3 then
io.write("\n")
elseif id == 4 then
io.write(data1)
end
end
return vals
end
-- Sends a custom HTTP-Header
function luci.http.header(key, value)
print(key .. ": " .. value)
end
-- Set Content-Type
function luci.http.prepare_content(type)
if not status_set then
luci.http.status(200, "OK")
end
print("Content-Type: "..type.."\n")
end
-- Asks the browser to redirect to "url"
function luci.http.redirect(url)
luci.http.status(302, "Found")
luci.http.header("Location", url)
print()
end
-- Returns the path of an uploaded file
-- WARNING! File uploads can be easily spoofed! Do additional sanity checks!
function luci.http.upload(name)
-- To be implemented
end
-- Sets HTTP-Status-Header
function luci.http.status(code, message)
print(luci.http.env.SERVER_PROTOCOL .. " " .. tostring(code) .. " " .. message)
status_set = true
end

View file

@ -60,6 +60,5 @@ function handle_req(context)
env.SERVER_ADDR = context.server_addr
env.SCRIPT_NAME = env.REQUEST_URI:sub(1, #env.REQUEST_URI - #env.PATH_INFO)
luci.sgi.webuci.initenv(env, vars)
luci.dispatcher.httpdispatch()
luci.sgi.webuci.run(env, vars)
end

View file

@ -27,7 +27,7 @@ local uci = require("uci")
local util = require("luci.util")
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
local error, pairs, ipairs, tostring = error, pairs, ipairs, tostring
local table, print = table, print
local table = table
module("luci.model.uci", function(m) setmetatable(m, {__index = uci}) end)

View file

@ -34,21 +34,10 @@ if (os.time() < 1000000000) then
os.execute('date -s '..os.date('%m%d%H%M%Y', luci.fs.mtime(luci.sys.libpath() .. "/dispatcher.lua"))..' > /dev/null 2>&1')
end
-- Local dispatch database
local tree = {nodes={}}
context = luci.util.threadlocal()
-- Index table
local index = {}
-- Global request object
request = {}
-- Active dispatched node
dispatched = nil
-- Status fields
built_index = false
built_tree = false
local index = nil
-- Fastindex
local fi
@ -64,9 +53,9 @@ function error401(message)
message = message or "Unauthorized"
require("luci.template")
if not pcall(luci.template.render, "error401") then
if not luci.util.copcall(luci.template.render, "error401") then
luci.http.prepare_content("text/plain")
print(message)
luci.http.write(message)
end
return false
end
@ -77,9 +66,9 @@ function error404(message)
message = message or "Not Found"
require("luci.template")
if not pcall(luci.template.render, "error404") then
if not luci.util.copcall(luci.template.render, "error404") then
luci.http.prepare_content("text/plain")
print(message)
luci.http.write(message)
end
return false
end
@ -89,31 +78,39 @@ function error500(message)
luci.http.status(500, "Internal Server Error")
require("luci.template")
if not pcall(luci.template.render, "error500", {message=message}) then
if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
luci.http.prepare_content("text/plain")
print(message)
luci.http.write(message)
end
return false
end
-- Creates a request object for dispatching
function httpdispatch()
local pathinfo = luci.http.env.PATH_INFO or ""
function httpdispatch(request)
luci.http.context.request = request
context.request = {}
local pathinfo = request.env.PATH_INFO or ""
for node in pathinfo:gmatch("[^/]+") do
table.insert(request, node)
table.insert(context.request, node)
end
dispatch()
dispatch(context.request)
luci.http.close()
end
-- Dispatches a request
function dispatch()
if not built_tree then
function dispatch(request)
context.path = request
require("luci.i18n")
luci.i18n.setlanguage(require("luci.config").main.lang)
if not context.tree then
createtree()
end
local c = tree
local c = context.tree
local track = {}
for i, s in ipairs(request) do
@ -131,6 +128,7 @@ function dispatch()
local accs = track.sysauth
accs = (type(accs) == "string") and {accs} or accs
--[[
local function sysauth(user, password)
return (luci.util.contains(accs, user)
and luci.sys.user.checkpasswd(user, password))
@ -140,6 +138,7 @@ function dispatch()
error401()
return
end
]]--
end
if track.i18n then
@ -156,22 +155,24 @@ function dispatch()
-- Init template engine
local tpl = require("luci.template")
tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end
tpl.viewns.controller = luci.http.dispatcher()
tpl.viewns.uploadctrl = luci.http.dispatcher_upload() -- DEPRECATED
tpl.viewns.media = luci.config.main.mediaurlbase
tpl.viewns.resource = luci.config.main.resourcebase
tpl.viewns.REQUEST_URI = luci.http.env.SCRIPT_NAME .. (luci.http.env.PATH_INFO or "")
local viewns = {}
tpl.context.viewns = viewns
viewns.write = luci.http.write
viewns.translate = function(...) return require("luci.i18n").translate(...) end
viewns.controller = luci.http.getenv("SCRIPT_NAME")
viewns.media = luci.config.main.mediaurlbase
viewns.resource = luci.config.main.resourcebase
viewns.REQUEST_URI = luci.http.getenv("SCRIPT_NAME") .. (luci.http.getenv("PATH_INFO") or "")
if c and type(c.target) == "function" then
dispatched = c
stat, mod = pcall(require, c.module)
context.dispatched = c
stat, mod = luci.util.copcall(require, c.module)
if stat then
luci.util.updfenv(c.target, mod)
end
stat, err = pcall(c.target)
stat, err = luci.util.copcall(c.target)
if not stat then
error500(err)
end
@ -182,21 +183,20 @@ end
-- Generates the dispatching tree
function createindex()
index = {}
local path = luci.sys.libpath() .. "/controller/"
local suff = ".lua"
if pcall(require, "luci.fastindex") then
if luci.util.copcall(require, "luci.fastindex") then
createindex_fastindex(path, suff)
else
createindex_plain(path, suff)
end
built_index = true
end
-- Uses fastindex to create the dispatching tree
function createindex_fastindex(path, suffix)
function createindex_fastindex(path, suffix)
index = {}
if not fi then
fi = luci.fastindex.new("index")
fi.add(path .. "*" .. suffix)
@ -212,9 +212,7 @@ end
-- Calls the index function of all available controllers
-- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away
function createindex_plain(path, suffix)
if built_index then
return
end
index = {}
local cache = nil
@ -246,7 +244,7 @@ function createindex_plain(path, suffix)
end
if not cache or stime > ctime then
stat, mod = pcall(require, module)
stat, mod = luci.util.copcall(require, module)
if stat and mod and type(mod.index) == "function" then
index[module] = mod.index
@ -263,10 +261,11 @@ end
-- Creates the dispatching tree from the index
function createtree()
if not built_index then
if not index then
createindex()
end
context.tree = {nodes={}}
require("luci.i18n")
-- Load default translation
@ -283,14 +282,12 @@ function createtree()
scope._NAME = k
setfenv(v, scope)
local stat, err = pcall(v)
local stat, err = luci.util.copcall(v)
if not stat then
error500(err)
os.exit(1)
end
end
built_tree = true
end
-- Reassigns a node to another position
@ -302,7 +299,7 @@ function assign(path, clone, title, order)
obj.title = title
obj.order = order
local c = tree
local c = context.tree
for k, v in ipairs(clone) do
if not c.nodes[v] then
c.nodes[v] = {nodes={}}
@ -330,8 +327,7 @@ end
-- Fetch a dispatching node
function node(...)
local c = tree
local c = context.tree
arg.n = nil
if arg[1] then
if type(arg[1]) == "table" then
@ -357,8 +353,7 @@ end
function alias(...)
local req = arg
return function()
request = req
dispatch()
dispatch(req)
end
end
@ -366,19 +361,20 @@ function rewrite(n, ...)
local req = arg
return function()
for i=1,n do
table.remove(request, 1)
table.remove(context.path, 1)
end
for i,r in ipairs(req) do
table.insert(request, i, r)
table.insert(context.path, i, r)
end
dispatch()
end
end
function call(name)
return function() getfenv()[name]() end
function call(name, ...)
local argv = {...}
return function() return getfenv()[name](unpack(argv)) end
end
function template(name)
@ -391,13 +387,13 @@ function cbi(model)
require("luci.template")
return function()
local stat, res = pcall(luci.cbi.load, model)
local stat, res = luci.util.copcall(luci.cbi.load, model)
if not stat then
error500(res)
return true
end
local stat, err = pcall(res.parse, res)
local stat, err = luci.util.copcall(res.parse, res)
if not stat then
error500(err)
return true

View file

@ -28,13 +28,141 @@ limitations under the License.
]]--
module("luci.http", package.seeall)
require("luci.util")
context = luci.util.threadlocal()
if ENV and ENV.HASERLVER then
require("luci.sgi.haserl")
elseif webuci then
require("luci.sgi.webuci")
Request = luci.util.class()
function Request.__init__(self)
self.headers = {}
self.request = {}
self.uploads = {}
self.env = {}
self.data = ""
end
function Request.formvalue(self, name, default)
return self.request[name] or default
end
function Request.formvalues(self)
return self.request
end
function Request.formvaluetable(self, prefix)
local vals = {}
prefix = prefix and prefix .. "." or "."
for k, v in pairs(self.request) do
if k:find(prefix, 1, true) == 1 then
vals[k:sub(#prefix + 1)] = v
end
end
return vals
end
function Request.getenv(self, name)
return self.env[name]
end
function Request.upload(self, name)
return self.uploads[name]
end
function close()
if not context.eoh then
context.eoh = true
coroutine.yield(3)
end
if not context.closed then
context.closed = true
coroutine.yield(5)
end
end
function formvalue(...)
return context.request:formvalue(...)
end
function formvalues(...)
return context.request:formvalues(...)
end
function formvaluetable(...)
return context.request:formvaluetable(...)
end
function getenv(...)
return context.request:getenv(...)
end
function header(key, value)
if not context.status then
status()
end
if not context.headers then
context.headers = {}
end
context.headers[key:lower()] = value
coroutine.yield(2, key, value)
end
function prepare_content(mime)
header("Content-Type", mime)
end
function status(code, message)
code = code or 200
message = message or "OK"
context.status = code
coroutine.yield(1, code, message)
end
function write(content)
if not content or #content == 0 then
return
end
if not context.eoh then
if not context.status then
status()
end
if not context.headers or not context.headers["content-type"] then
header("Content-Type", "text/html; charset=utf-8")
end
context.eoh = true
coroutine.yield(3)
end
coroutine.yield(4, content)
end
function basic_auth(realm, errorpage)
header("Status", "401 Unauthorized")
header("WWW-Authenticate", string.format('Basic realm="%s"', realm or ""))
if errorpage then
errorpage()
end
close()
end
function redirect(url)
header("Status", "302 Found")
header("Location", url)
close()
end
function upload(...)
return context.request:upload(...)
end
function build_querystring(table)
local s="?"

View file

@ -30,6 +30,8 @@ require("luci.sys")
table = {}
i18ndir = luci.sys.libpath() .. "/i18n/"
loaded = {}
context = luci.util.threadlocal()
default = "en"
-- Clears the translation table
function clear()
@ -37,13 +39,17 @@ function clear()
end
-- Loads a translation and copies its data into the global translation table
function load(file, force)
if force or not loaded[file] then
local f = loadfile(i18ndir..file..".lua") or loadfile(i18ndir..file)
function load(file, lang, force)
lang = lang or ""
if force or not loaded[lang] or not loaded[lang][file] then
local f = loadfile(i18ndir .. file .. "." .. lang .. ".lua")
or loadfile(i18ndir .. file .. "." .. lang)
if f then
setfenv(f, table)
table[lang] = table[lang] or {}
setfenv(f, table[lang])
f()
loaded[file] = true
loaded[lang] = loaded[lang] or {}
loaded[lang][file] = true
return true
else
return false
@ -55,13 +61,20 @@ end
-- Same as load but autocompletes the filename with .LANG from config.lang
function loadc(file, force)
load(file .. ".en", force)
return load(file .. "." .. require("luci.config").main.lang, force)
load(file, default, force)
return load(file, context.lang, force)
end
-- Sets the context language
function setlanguage(lang)
context.lang = lang
end
-- Returns the i18n-value defined by "key" or if there is no such: "default"
function translate(key, default)
return table[key] or default
return (table[context.lang] and table[context.lang][key])
or (table[default] and table[default][key])
or default
end
-- Translate shourtcut with sprintf/string.format inclusion

View file

@ -44,9 +44,10 @@ compiler_mode = luci.config.template.compiler_mode or "memory"
-- Define the namespace for template modules
context = luci.util.threadlocal()
viewns = {
write = io.write,
include = function(name) Template(name):render(getfenv(2)) end,
include = function(name) Template(name):render(getfenv(2)) end,
}
-- Compiles a given template into an executable Lua module
@ -113,7 +114,7 @@ end
-- Oldstyle render shortcut
function render(name, scope, ...)
scope = scope or getfenv(2)
local s, t = pcall(Template, name)
local s, t = luci.util.copcall(Template, name)
if not s then
error(t)
else
@ -141,9 +142,10 @@ function Template.__init__(self, name)
self.viewns = {}
-- Copy over from general namespace
for k, v in pairs(viewns) do
self.viewns[k] = v
end
luci.util.update(self.viewns, viewns)
if context.viewns then
luci.util.update(self.viewns, context.viewns)
end
-- If we have a cached template, skip compiling and loading
if self.template then

View file

@ -4,7 +4,7 @@
<p><%:a_s_flash_upgrade1%></p>
<br />
<% if sysupgrade and not ret then %>
<form method="post" action="<%=uploadctrl%><%=luci.http.env.PATH_INFO%>" enctype="multipart/form-data">
<form method="post" action="<%=luci.http.getenv("REQUEST_URI")%>" enctype="multipart/form-data">
<div class="cbi-section-node">
<div class="cbi-value clear">
<div class="cbi-value-title left"><%:a_s_flash_fwimage%></div>

View file

@ -1,5 +1,5 @@
<%+header%>
<h1>404 Not Found</h1>
<p>Sorry, the object you requested was not found.</p>
<tt>Unable to dispatch: <%=luci.http.env.PATH_INFO%></tt>
<tt>Unable to dispatch: <%=luci.http.request.env.PATH_INFO%></tt>
<%+footer%>

View file

@ -22,8 +22,8 @@ function action_index()
luci.http.prepare_content("text/plain")
-- General
print("luciinfo.api=1")
print("luciinfo.version=" .. tostring(require("luci").__version__))
luci.http.write("luciinfo.api=1\n")
luci.http.write("luciinfo.version=" .. tostring(require("luci").__version__) .. "\n")
-- Sysinfo
local s, m, r = luci.sys.sysinfo()
@ -31,14 +31,14 @@ function action_index()
dr = dr and luci.sys.net.hexip4(dr.Gateway) or ""
local l1, l5, l15 = luci.sys.loadavg()
print("sysinfo.system=" .. sanitize(s))
print("sysinfo.cpu=" .. sanitize(m))
print("sysinfo.ram=" .. sanitize(r))
print("sysinfo.hostname=" .. sanitize(luci.sys.hostname()))
print("sysinfo.load1=" .. tostring(l1))
print("sysinfo.load5=" .. tostring(l5))
print("sysinfo.load15=" .. tostring(l15))
print("sysinfo.defaultgw=" .. dr)
luci.http.write("sysinfo.system=" .. sanitize(s) .. "\n")
luci.http.write("sysinfo.cpu=" .. sanitize(m) .. "\n")
luci.http.write("sysinfo.ram=" .. sanitize(r) .. "\n")
luci.http.write("sysinfo.hostname=" .. sanitize(luci.sys.hostname()) .. "\n")
luci.http.write("sysinfo.load1=" .. tostring(l1) .. "\n")
luci.http.write("sysinfo.load5=" .. tostring(l5) .. "\n")
luci.http.write("sysinfo.load15=" .. tostring(l15) .. "\n")
luci.http.write("sysinfo.defaultgw=" .. dr .. "\n")
-- Freifunk
@ -46,7 +46,7 @@ function action_index()
for k, v in pairs(ff) do
for i, j in pairs(v) do
if i:sub(1, 1) ~= "." then
print("freifunk." .. k .. "." .. i .. "=" .. j)
luci.http.write("freifunk." .. k .. "." .. i .. "=" .. j .. "\n")
end
end
end

View file

@ -1,11 +1,11 @@
<%
require("luci.sys")
local load1, load5, load15 = luci.sys.loadavg()
local request = require("luci.dispatcher").request
local request = require("luci.dispatcher").context.path
local category = request[1]
local tree = luci.dispatcher.node()
local cattree = category and luci.dispatcher.node(category)
local node = luci.dispatcher.dispatched
local node = luci.dispatcher.context.dispatched
local c = tree
for i,r in ipairs(request) do

View file

@ -1,11 +1,11 @@
<%
require("luci.sys")
local load1, load5, load15 = luci.sys.loadavg()
local request = require("luci.dispatcher").request
local request = require("luci.dispatcher").context.path
local category = request[1]
local tree = luci.dispatcher.node()
local cattree = category and luci.dispatcher.node(category)
local node = luci.dispatcher.dispatched
local node = luci.dispatcher.context.dispatched
local c = tree
for i,r in ipairs(request) do