* 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 mkdir -p host
for i in $(MODULES); do cp -a $$i/dist/* host/ -R 2>/dev/null || true; done for i in $(MODULES); do cp -a $$i/dist/* host/ -R 2>/dev/null || true; done
rm -f host/luci rm -f host/luci
ln -s .$(LUCI_INSTALLDIR) host/luci ln -s .$(LUCI_MODULEDIR) host/luci
run: host run: host
libs/sgi-webuci/host/buildconfig.sh `pwd`/host > host/etc/boa/boa.conf 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 LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org> Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0. 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 Luci-Statistics - Statistics for LuCI
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0. Licensed under the Apache License, Version 2.0.

View file

@ -12,7 +12,7 @@ function index()
end end
function action_activate() 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 if mac and luci.http.formvalue("accept") then
os.execute("luci-splash add "..mac.." >/dev/null 2>&1") os.execute("luci-splash add "..mac.." >/dev/null 2>&1")
luci.http.redirect(luci.model.uci.get("freifunk", "community", "homepage")) luci.http.redirect(luci.model.uci.get("freifunk", "community", "homepage"))

View file

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

View file

@ -500,7 +500,7 @@ function Graph.render( self, plugin, plugin_instance )
-- check for a whole graph handler -- check for a whole graph handler
local plugin_def = "luci.statistics.rrdtool.definitions." .. plugin 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 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 -- check for data type handler
local dtype_def = plugin_def .. "." .. dtype 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 if stat and def and type(def.rrdargs) == "function" then

View file

@ -1,4 +1,9 @@
LUAC = luac LUAC = luac
LUAC_OPTIONS = -s 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 clean: luaclean
luasource: 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 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 for i in $$(find dist -name .svn); do rm $$i -rf; done
luacompile: luasource luacompile: luasource

View file

@ -34,8 +34,8 @@ $(LUAPOSIX_DIR)/.patched: $(LUAPOSIX_DIR)/.prepared $(LUAPOSIX_PATCHDIR)/series
compile: $(LUAPOSIX_DIR)/.patched compile: $(LUAPOSIX_DIR)/.patched
$(MAKE) -C $(LUAPOSIX_DIR) CC=$(CC) CFLAGS="$(CFLAGS) $(LUA_CFLAGS)" LDFLAGS="$(LDFLAGS) $(LUA_SHLIBS)" OS="$(OS)" $(MAKE) -C $(LUAPOSIX_DIR) CC=$(CC) CFLAGS="$(CFLAGS) $(LUA_CFLAGS)" LDFLAGS="$(LDFLAGS) $(LUA_SHLIBS)" OS="$(OS)"
mkdir -p dist/usr/lib/lua mkdir -p dist$(LUA_LIBRARYDIR)
cp $(LUAPOSIX_DIR)/posix.so dist/usr/lib/lua/ cp $(LUAPOSIX_DIR)/posix.so dist$(LUA_LIBRARYDIR)
luasource: luasource:
compile-all: compile 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_REV:=$(shell LC_ALL=C svn info ${PKG_SOURCE_URL} | sed -ne's/^Last Changed Rev: //p')
PKG_NAME:=luci PKG_NAME:=luci
PKG_VERSION:=0.6+svn$(PKG_REV) PKG_VERSION:=0.7+svn$(PKG_REV)
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)

View file

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

View file

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

View file

@ -36,7 +36,7 @@ function class(base)
setmetatable(inst, {__index = class}) setmetatable(inst, {__index = class})
if inst.__init__ then if inst.__init__ then
local stat, err = pcall(inst.__init__, inst, ...) local stat, err = copcall(inst.__init__, inst, ...)
if not stat then if not stat then
error(err) error(err)
end end
@ -152,6 +152,12 @@ function pcdata(value)
end 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 -- Resets the scope of f doing a shallow copy of its scope into a new table
function resfenv(f) function resfenv(f)
setfenv(f, clone(getfenv(f))) setfenv(f, clone(getfenv(f)))
@ -255,6 +261,34 @@ function strip_bytecode(dump)
end 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 -- Removes whitespace from beginning and end of a string
function trim(str) function trim(str)
local s = str:gsub("^%s*(.-)%s*$", "%1") local s = str:gsub("^%s*(.-)%s*$", "%1")
@ -355,3 +389,46 @@ end
function vspairs(t) function vspairs(t)
return _sortiter( t, function (a,b) return t[a] < t[b] end ) return _sortiter( t, function (a,b) return t[a] < t[b] end )
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) $(LUA_CFLAGS) $(FPIC) -c -o $@ $<
compile: src/fastindex.o compile: src/fastindex.o
mkdir -p dist$(LUCI_INSTALLDIR) mkdir -p dist$(LUCI_LIBRARYDIR)
$(LINK) $(SHLIB_FLAGS) -o dist$(LUCI_INSTALLDIR)/fastindex.so src/fastindex.o $(LUA_SHLIBS) $(LINK) $(SHLIB_FLAGS) -o dist$(LUCI_LIBRARYDIR)/fastindex.so src/fastindex.o $(LUA_SHLIBS)
clean: clean:
rm -f src/*.o rm -f src/*.o

View file

@ -24,80 +24,49 @@ limitations under the License.
]]-- ]]--
module("luci.sgi.haserl", package.seeall) module("luci.sgi.haserl", package.seeall)
require("luci.fs") require("luci.http")
require("luci.util")
require("luci.dispatcher")
-- Environment Table function run()
luci.http.env = ENV local r = luci.http.Request()
r.env = ENV
r.request = normalize_table(FORM)
-- Enforces user authentification local x = coroutine.create(luci.dispatcher.httpdispatch)
function luci.http.basic_auth(verify_callback, realm) while coroutine.status(x) ~= "dead" do
-- Dummy for Haserl local res, id, data1, data2 = coroutine.resume(x, r)
return true
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 end
-- Returns the main dispatcher URL function normalize_table(table, prefix)
function luci.http.dispatcher() prefix = prefix and prefix .. "." or ""
return luci.http.env.SCRIPT_NAME or "" local new = {}
end
-- Returns the upload dispatcher URL for k,v in pairs(table) do
function luci.http.dispatcher_upload() if type(v) == "table" and #v == 0 then
return luci.http.dispatcher() .. "-upload" luci.util.update(new, normalize_table(v, prefix .. k))
end else
new[prefix .. k] = v
-- 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()
for match in key:gmatch("[%w-_]+") do
c = c[match]
if c == nil then
return default
end end
end end
return c return new
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)
end end

View file

@ -1,4 +1,4 @@
#!/usr/bin/haserl --shell=luac #!/usr/bin/haserl --shell=luac
require("luci.dispatcher") require("luci.sgi.haserl")
luci.dispatcher.indexcache = "/tmp/.luciindex" 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) module("luci.sgi.webuci", package.seeall)
require("luci.http")
require("luci.util")
require("luci.dispatcher")
local status_set = false function run(env, vars)
local r = luci.http.Request()
r.env = env
r.request = vars
-- Initialize the environment local x = coroutine.create(luci.dispatcher.httpdispatch)
function initenv(env, vars)
luci.http.env = env
luci.http.vars = vars
end
-- Enforces user authentification while coroutine.status(x) ~= "dead" do
function luci.http.basic_auth(verify_callback, realm) local res, id, data1, data2 = coroutine.resume(x, r)
local user = luci.http.env.auth_user
local pass = luci.http.env.auth_password
realm = realm or ""
if not user or not verify_callback(user, pass) then if not res then
luci.http.status("401", "Unauthorized") print(env.SERVER_PROTOCOL .. " 500 Internal Server Error")
luci.http.header("WWW-Authenticate", string.format('Basic realm="%s"', realm)) print("Content-Type: text/plain\n")
return false print(id)
else break;
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 "."
for k, v in pairs(luci.http.formvalues()) do
if k:find(prefix, 1, true) == 1 then
vals[k:sub(#prefix + 1)] = v
end
end end
return vals if id == 1 then
end io.write(env.SERVER_PROTOCOL .. " " .. tostring(data1) .. " " .. data2 .. "\n")
elseif id == 2 then
-- Sends a custom HTTP-Header io.write(data1 .. ": " .. data2 .. "\n")
function luci.http.header(key, value) elseif id == 3 then
print(key .. ": " .. value) io.write("\n")
end elseif id == 4 then
io.write(data1)
-- Set Content-Type
function luci.http.prepare_content(type)
if not status_set then
luci.http.status(200, "OK")
end end
print("Content-Type: "..type.."\n") end
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 end

View file

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

View file

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

View file

@ -28,13 +28,141 @@ limitations under the License.
]]-- ]]--
module("luci.http", package.seeall) module("luci.http", package.seeall)
require("luci.util")
context = luci.util.threadlocal()
if ENV and ENV.HASERLVER then
require("luci.sgi.haserl") Request = luci.util.class()
elseif webuci then function Request.__init__(self)
require("luci.sgi.webuci") self.headers = {}
self.request = {}
self.uploads = {}
self.env = {}
self.data = ""
end 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) function build_querystring(table)
local s="?" local s="?"

View file

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

View file

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

View file

@ -4,7 +4,7 @@
<p><%:a_s_flash_upgrade1%></p> <p><%:a_s_flash_upgrade1%></p>
<br /> <br />
<% if sysupgrade and not ret then %> <% 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-section-node">
<div class="cbi-value clear"> <div class="cbi-value clear">
<div class="cbi-value-title left"><%:a_s_flash_fwimage%></div> <div class="cbi-value-title left"><%:a_s_flash_fwimage%></div>

View file

@ -1,5 +1,5 @@
<%+header%> <%+header%>
<h1>404 Not Found</h1> <h1>404 Not Found</h1>
<p>Sorry, the object you requested was not found.</p> <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%> <%+footer%>

View file

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

View file

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

View file

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