diff options
author | Steven Barth <steven@midlink.org> | 2008-11-30 13:19:45 +0000 |
---|---|---|
committer | Steven Barth <steven@midlink.org> | 2008-11-30 13:19:45 +0000 |
commit | b33943a6e8596c1ddfc1b771a995d3cf21e81cd6 (patch) | |
tree | 6f67cdea044e708a599a06712491b5c60db6f954 /libs/lucittpd | |
parent | a7e7c31f8c659b55c1adb0863a8f2f66d3452d2b (diff) |
Merge LuCIttpd
Diffstat (limited to 'libs/lucittpd')
-rw-r--r-- | libs/lucittpd/Makefile | 18 | ||||
-rw-r--r-- | libs/lucittpd/hostfiles/usr/lib/lucittpd/plugins/httpd.lua | 34 | ||||
-rw-r--r-- | libs/lucittpd/luasrc/ttpd/handler/file.lua | 252 | ||||
-rw-r--r-- | libs/lucittpd/luasrc/ttpd/module.lua | 121 | ||||
-rw-r--r-- | libs/lucittpd/luasrc/ttpd/server.lua | 442 | ||||
-rw-r--r-- | libs/lucittpd/root/usr/lib/lucittpd/plugins/httpd.lua | 35 | ||||
-rw-r--r-- | libs/lucittpd/src/.gitignore | 1 | ||||
-rw-r--r-- | libs/lucittpd/src/Makefile | 20 | ||||
-rw-r--r-- | libs/lucittpd/src/include/lib/list.h | 601 | ||||
-rw-r--r-- | libs/lucittpd/src/include/lib/log.h | 24 | ||||
-rw-r--r-- | libs/lucittpd/src/include/lib/luaplugin.h | 71 | ||||
-rw-r--r-- | libs/lucittpd/src/include/lib/signal.h | 25 | ||||
-rw-r--r-- | libs/lucittpd/src/include/lib/uci.h | 54 | ||||
-rw-r--r-- | libs/lucittpd/src/include/uci.h | 543 | ||||
-rw-r--r-- | libs/lucittpd/src/include/uci_config.h | 3 | ||||
-rw-r--r-- | libs/lucittpd/src/lib/log.c | 45 | ||||
-rw-r--r-- | libs/lucittpd/src/lib/luaplugin.c | 383 | ||||
-rw-r--r-- | libs/lucittpd/src/lib/signal.c | 52 | ||||
-rw-r--r-- | libs/lucittpd/src/lib/uci.c | 206 | ||||
-rw-r--r-- | libs/lucittpd/src/main.c | 333 |
20 files changed, 3263 insertions, 0 deletions
diff --git a/libs/lucittpd/Makefile b/libs/lucittpd/Makefile new file mode 100644 index 000000000..3d1a91177 --- /dev/null +++ b/libs/lucittpd/Makefile @@ -0,0 +1,18 @@ +ifeq ($(CFLAGS),) + MYLDFLAGS ?= -L../../../contrib/uci/dist/usr/lib/ +endif + +include ../../build/module.mk +include ../../build/config.mk +include ../../build/gccconfig.mk + +compile: + make -Csrc \ + CFLAGS="$(CFLAGS) $(FPIC) $(LUA_CFLAGS) $(EXTRA_CFLAGS) $(WFLAGS)" \ + LDFLAGS="$(LDFLAGS) $(LUA_SHLIBS) -luci" \ + MYLDFLAGS="$(MYLDFLAGS)" + mkdir -p dist/usr/bin + cp src/lucittpd dist/usr/bin + +clean: luaclean + make -Csrc clean diff --git a/libs/lucittpd/hostfiles/usr/lib/lucittpd/plugins/httpd.lua b/libs/lucittpd/hostfiles/usr/lib/lucittpd/plugins/httpd.lua new file mode 100644 index 000000000..e40af98fd --- /dev/null +++ b/libs/lucittpd/hostfiles/usr/lib/lucittpd/plugins/httpd.lua @@ -0,0 +1,34 @@ +function initialize() + local lucittpd = require "luci.ttpd.server" + server = lucittpd.Server(lucittpd.VHost()) +end + +function register() + local filehnd = require "luci.ttpd.handler.file" + local filehandler = filehnd.Simple(os.getenv("LUCI_SYSROOT") .. "/www") + server:get_default_vhost():set_default_handler(filehandler) +end + +function accept() + server:process({ + _read = function(...) + local chunk, err = webuci_read(...) + return chunk or (err and error(err, 0)) + end, + + _write = function(...) + local chunk, err = webuci_write(...) + return chunk or (err and error(err, 0)) + end, + + _close = function(...) + local chunk, err = webuci_close(...) + return chunk or (err and error(err, 0)) + end, + + _sendfile = function(...) + local chunk, err = webuci_sendfile(...) + return chunk or (err and error(err, 0)) + end + }) +end
\ No newline at end of file diff --git a/libs/lucittpd/luasrc/ttpd/handler/file.lua b/libs/lucittpd/luasrc/ttpd/handler/file.lua new file mode 100644 index 000000000..e1f707c62 --- /dev/null +++ b/libs/lucittpd/luasrc/ttpd/handler/file.lua @@ -0,0 +1,252 @@ +--[[ + +HTTP server implementation for LuCI - file handler +(c) 2008 Steven Barth <steven@midlink.org> +(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.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$ + +]]-- + +local ipairs, type, tonumber = ipairs, type, tonumber +local io = require "io" +local os = require "os" +local fs = require "luci.fs" +local util = require "luci.util" +local ltn12 = require "luci.ltn12" +local mod = require "luci.ttpd.module" +local srv = require "luci.ttpd.server" +local string = require "string" + +local prot = require "luci.http.protocol" +local date = require "luci.http.protocol.date" +local mime = require "luci.http.protocol.mime" +local cond = require "luci.http.protocol.conditionals" + +module "luci.ttpd.handler.file" + +Simple = util.class(mod.Handler) +Response = mod.Response + +function Simple.__init__(self, docroot, dirlist) + mod.Handler.__init__(self) + self.docroot = docroot + self.dirlist = dirlist and true or false +end + +function Simple.parse_range(self, request, size) + if not request.headers.Range then + return true + end + + local from, to = request.headers.Range:match("bytes=([0-9]*)-([0-9]*)") + if not (from or to) then + return true + end + + from, to = tonumber(from), tonumber(to) + if not (from or to) then + return true + elseif not from then + from, to = size - to, size - 1 + elseif not to then + to = size - 1 + end + + -- Not satisfiable + if from >= size then + return false + end + + -- Normalize + if to >= size then + to = size - 1 + end + + local range = "bytes " .. from .. "-" .. to .. "/" .. size + return from, (1 + to - from), range +end + +function Simple.getfile(self, uri) + local file = self.docroot .. uri:gsub("%.%./+", "") + local stat = fs.stat(file) + + return file, stat +end + +function Simple.handle_get(self, request, sourcein, sinkerr) + local file, stat = self:getfile( prot.urldecode( request.env.PATH_INFO, true ) ) + + if stat then + if stat.type == "regular" then + + -- Generate Entity Tag + local etag = cond.mk_etag( stat ) + + -- Check conditionals + local ok, code, hdrs + + ok, code, hdrs = cond.if_modified_since( request, stat ) + if ok then + ok, code, hdrs = cond.if_match( request, stat ) + if ok then + ok, code, hdrs = cond.if_unmodified_since( request, stat ) + if ok then + ok, code, hdrs = cond.if_none_match( request, stat ) + if ok then + local f, err = io.open(file) + + if f then + local code = 200 + local o, s, r = self:parse_range(request, stat.size) + + if not o then + return self:failure(416, "Invalid Range") + end + + local headers = { + ["Last-Modified"] = date.to_http( stat.mtime ), + ["Content-Type"] = mime.to_mime( file ), + ["ETag"] = etag, + ["Accept-Ranges"] = "bytes", + } + + if o == true then + o = 0 + s = stat.size + else + code = 206 + headers["Content-Range"] = r + end + + headers["Content-Length"] = s + + -- Send Response + return Response(code, headers), + srv.IOResource(f, o, s) + else + return self:failure( 403, err:gsub("^.+: ", "") ) + end + else + return Response( code, hdrs or { } ) + end + else + return Response( code, hdrs or { } ) + end + else + return Response( code, hdrs or { } ) + end + else + return Response( code, hdrs or { } ) + end + + elseif stat.type == "directory" then + + local ruri = request.request_uri:gsub("/$","") + local duri = prot.urldecode( ruri, true ) + local root = self.docroot:gsub("/$","") + + -- check for index files + local index_candidates = { + "index.html", "index.htm", "default.html", "default.htm", + "index.txt", "default.txt" + } + + -- try to find an index file and redirect to it + for i, candidate in ipairs( index_candidates ) do + local istat = fs.stat( + root .. "/" .. duri .. "/" .. candidate + ) + + if istat ~= nil and istat.type == "regular" then + return Response( 302, { + ["Location"] = ruri .. "/" .. candidate + } ) + end + end + + + local html = string.format( + '<?xml version="1.0" encoding="ISO-8859-15"?>\n' .. + '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' .. + '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n' .. + '<html xmlns="http://www.w3.org/1999/xhtml" ' .. + 'xml:lang="en" lang="en">\n' .. + '<head>\n' .. + '<title>Index of %s/</title>\n' .. + '<style type="text/css"><!--\n' .. + 'body { background-color:#fbb034; color:#ffffff } ' .. + 'li { border-bottom:1px dotted #CCCCCC; padding:3px } ' .. + 'small { font-size:60%%; color:#ffffff } ' .. + 'p { margin:0 }' .. + '\n--></style></head><body><h1>Index of %s/</h1><hr /><ul>', + duri, duri + ) + + local entries = fs.dir( file ) + + if type(entries) == "table" then + for i, e in util.spairs( + entries, function(a,b) + if entries[a] == '..' then + return true + elseif entries[b] == '..' then + return false + else + return ( entries[a] < entries[b] ) + end + end + ) do + if e ~= '.' and ( e == '..' or e:sub(1,1) ~= '.' ) then + local estat = fs.stat( file .. "/" .. e ) + + if estat.type == "directory" then + html = html .. string.format( + '<li><p><a href="%s/%s/">%s/</a> ' .. + '<small>(directory)</small><br />' .. + '<small>Changed: %s</small></li>', + ruri, prot.urlencode( e ), e, + date.to_http( estat.mtime ) + ) + else + html = html .. string.format( + '<li><p><a href="%s/%s">%s</a> ' .. + '<small>(%s)</small><br />' .. + '<small>Size: %i Bytes | ' .. + 'Changed: %s</small></li>', + ruri, prot.urlencode( e ), e, + mime.to_mime( e ), + estat.size, date.to_http( estat.mtime ) + ) + end + end + end + + html = html .. '</ul><hr /></body></html>' + + return Response( + 200, { + ["Date"] = date.to_http( os.time() ); + ["Content-Type"] = "text/html; charset=ISO-8859-15"; + } + ), ltn12.source.string(html) + else + return self:failure(403, "Permission denied") + end + else + return self:failure(403, "Unable to transmit " .. stat.type .. " " .. file) + end + else + return self:failure(404, "No such file: " .. file) + end +end + +function Simple.handle_head(self, ...) + return (self:handle_get(...)) +end diff --git a/libs/lucittpd/luasrc/ttpd/module.lua b/libs/lucittpd/luasrc/ttpd/module.lua new file mode 100644 index 000000000..1a7c57473 --- /dev/null +++ b/libs/lucittpd/luasrc/ttpd/module.lua @@ -0,0 +1,121 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2008 Steven Barth <steven@midlink.org> + +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$ +]]-- + +local pcall, ipairs, tonumber, type, next = pcall, ipairs, tonumber, type, next +local util = require "luci.util" +local http = require "luci.http.protocol" +local ltn12 = require "luci.ltn12" +local table = require "table" + +module "luci.ttpd.module" + + +-- Server handler implementation +Handler = util.class() + +-- Constructor +function Handler.__init__(self) + self.handler = {} + self.filters = {} + self.modifiers = {} +end + +-- Add a filter +function Handler.setfilter(self, filter, key) + self.filters[(key) or (#self.filters+1)] = filter +end + +-- Add a modifier +function Handler.setmodifier(self, modifier, key) + self.modifiers[(pos) or (#self.modifiers+1)] = modifier +end + +-- Creates a failure reply +function Handler.failure(self, code, message) + local response = Response(code, { ["Content-Type"] = "text/plain" }) + local sourceout = ltn12.source.string(message) + + return response, sourceout +end + +-- Processes a request +function Handler.process(self, request, sourcein, sinkerr) + local stat, response, sourceout + + -- Detect request Method + local hname = "handle_" .. request.request_method + if self[hname] then + local t = { + processor = self[hname], + handler = self, + request = request, + sourcein = sourcein, + sinkerr = sinkerr + } + + if next(self.modifiers) then + for _, mod in util.kspairs(self.modifiers) do + mod(t) + end + end + + -- Run the handler + stat, response, sourceout = pcall( + t.processor, t.handler, t.request, t.sourcein, t.sinkerr + ) + + -- Check for any errors + if not stat then + response, sourceout = self:failure(500, response) + elseif next(self.filters) then + local t = { + response = response, + sourceout = sourceout, + sinkerr = t.sinkerr + } + + for _, filter in util.kspairs(self.filters) do + filter(t) + end + + response = t.response + sourceout = t.sourceout + end + else + response, sourceout = self:failure(405, http.protocol.statusmsg[405]) + end + + -- Check data + if not util.instanceof(response, Response) then + response, sourceout = self:failure(500, "Core error: Invalid module response!") + end + + return response, sourceout +end + +-- Handler Response +Response = util.class() + +function Response.__init__(self, status, headers) + self.status = tonumber(status) or 200 + self.headers = (type(headers) == "table") and headers or {} +end + +function Response.addheader(self, key, value) + self.headers[key] = value +end + +function Response.setstatus(self, status) + self.status = status +end
\ No newline at end of file diff --git a/libs/lucittpd/luasrc/ttpd/server.lua b/libs/lucittpd/luasrc/ttpd/server.lua new file mode 100644 index 000000000..4cb246af8 --- /dev/null +++ b/libs/lucittpd/luasrc/ttpd/server.lua @@ -0,0 +1,442 @@ +--[[ +LuCIttpd +(c) 2008 Steven Barth <steven@midlink.org> +(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.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$ +]]-- + +local ipairs, pairs = ipairs, pairs +local tostring, tonumber = tostring, tonumber +local pcall, assert = pcall, assert + +local os = require "os" +local io = require "io" +local util = require "luci.util" +local ltn12 = require "luci.ltn12" +local proto = require "luci.http.protocol" +local string = require "string" +local date = require "luci.http.protocol.date" + +module "luci.ttpd.server" + +BUFSIZE = 4096 +VERSION = 0.91 + + +-- File Resource +IOResource = util.class() + +function IOResource.__init__(self, fd, offset, len) + self.fd, self.offset, self.len = fd, offset, len +end + + +VHost = util.class() + +function VHost.__init__(self, handler) + self.handler = handler + self.dhandler = {} +end + +function VHost.process(self, request, sourcein, sinkerr, ...) + local handler = self.handler + + local uri = request.env.REQUEST_URI:match("^([^?]*)") + + -- SCRIPT_NAME + request.env.SCRIPT_NAME = "" + + -- Call URI part + request.env.PATH_INFO = uri + + for k, dhandler in pairs(self.dhandler) do + if k == uri or k.."/" == uri:sub(1, #k+1) then + handler = dhandler + request.env.SCRIPT_NAME = k + request.env.PATH_INFO = uri:sub(#k+1) + break; + end + end + + if handler then + return handler:process(request, sourcein, sinkerr, ...) + end +end + +function VHost.get_default_handler(self) + return self.handler +end + +function VHost.set_default_handler(self, handler) + self.handler = handler +end + +function VHost.get_handlers(self) + return self.dhandler +end + +function VHost.set_handler(self, match, handler) + self.dhandler[match] = handler +end + + + +Server = util.class() + +function Server.__init__(self, host) + self.host = host + self.vhosts = {} + + self.rbuf = "" + self.wbuf = "" +end + +function Server.get_default_vhost(self) + return self.host +end + +function Server.set_default_vhost(self, vhost) + self.host = vhost +end + +function Server.get_vhosts(self) + return self.vhosts +end + +function Server.set_vhost(self, name, vhost) + self.vhosts[name] = vhost +end + +function Server.flush(self) + if #self.wbuf > 0 then + self._write(self.wbuf) + self.wbuf = "" + end +end + +function Server.read(self, len) + while #self.rbuf < len do + self.rbuf = self.rbuf .. self._read(len - #self.rbuf) + end + + local chunk = self.rbuf:sub(1, len) + self.rbuf = self.rbuf:sub(len + 1) + return chunk +end + +function Server.limitsource(self, limit) + limit = limit or 0 + + return function() + if limit < 1 then + return nil + else + local read = (limit > BUFSIZE) and BUFSIZE or limit + limit = limit - read + return self:read(read) + end + end +end + +-- Adapted from Luaposix +function Server.receiveheaders(self) + local line, name, value, err + local headers = {} + -- get first line + line, err = self:readline() + if err then return nil, err end + -- headers go until a blank line is found + while line do + -- get field-name and value + _, _, name, value = line:find("^(.-):%s*(.*)") + if not (name and value) then return nil, "malformed reponse headers" end + name = name:lower() + -- get next line (value might be folded) + line, err = self:readline() + if err then return nil, err end + -- unfold any folded values + while line:find("^%s") do + value = value .. line + line = self:readline() + if err then return nil, err end + end + -- save pair in table + if headers[name] then headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + return headers +end + +function Server.readchunk(self) + -- get chunk size, skip extention + local line, err = self:readline() + if err then return nil, err end + local size = tonumber(line:gsub(";.*", ""), 16) + if not size then return nil, "invalid chunk size" end + -- was it the last chunk? + if size > 0 then + -- if not, get chunk and skip terminating CRLF + local chunk, err, part = self:read(size) + if chunk then self:readline() end + return chunk, err + else + -- if it was, read trailers into headers table + headers, err = self:receiveheaders() + if not headers then return nil, err end + end +end + +function Server.readline(self) + if #self.rbuf < 1 then + self.rbuf = self._read(BUFSIZE) + end + + while true do + local le = self.rbuf:find("\r\n", nil, true) + if le then + if le == 1 then -- EoH + self.rbuf = self.rbuf:sub(le + 2) + return nil + else -- Header + local line = self.rbuf:sub(1, le - 1) + self.rbuf = self.rbuf:sub(le + 2) + return line + end + else + if #self.rbuf >= BUFSIZE then + return nil, "Invalid Request" + end + self.rbuf = self.rbuf .. self._read(BUFSIZE-#self.rbuf) + end + end +end + +function Server.sink(self) + return function(chunk, err) + if err then + return nil, err + elseif chunk then + local stat, err = pcall(self.write, self, chunk) + if stat then + return stat + else + return nil, err + end + else + return true + end + end +end + +function Server.chunksink(self) + return function(chunk, err) + local stat, err = pcall(self.writechunk, self, chunk) + if stat then + return stat + else + return nil, err + end + end +end + +function Server.writechunk(self, chunk, err) + self:flush() + if not chunk then return self._write("0\r\n\r\n") end + local size = string.format("%X\r\n", #chunk) + return self._write(size .. chunk .. "\r\n") +end + +function Server.write(self, chunk) + while #chunk > 0 do + local missing = BUFSIZE - #self.wbuf + self.wbuf = self.wbuf .. chunk:sub(1, missing) + chunk = chunk:sub(missing + 1) + if #self.wbuf == BUFSIZE then + assert(self._write(self.wbuf)) + self.wbuf = "" + end + end +end + +function Server.close(self) + self:flush() + self._close() +end + +function Server.sendfile(self, fd, offset, len) + self:flush() + self._sendfile(fd, offset, len) +end + + +function Server.error(self, code, msg) + hcode = tostring(code) + + self:write( "HTTP/1.0 " .. hcode .. " " .. + proto.statusmsg[code] .. "\r\n" ) + self:write( "Connection: close\r\n" ) + self:write( "Content-Type: text/plain\r\n\r\n" ) + + if msg then + self:write( "HTTP-Error " .. code .. ": " .. msg .. "\r\n" ) + end +end + + +function Server.process(self, functions) + util.update(self, functions) + + local sourcein = ltn12.source.empty() + local sourcehdr = function() return self:readline() or "" end + local sinkerr = ltn12.sink.file( io.stderr ) + local sinkout = self:sink() + + local close = false + local stat, message, err + + repeat + -- parse headers + stat, message, err = pcall(proto.parse_message_header, sourcehdr) + + -- remote socket closed + if not stat and message == 0 then + break + end + + -- remote timeout + if not stat and message == 11 then + --self:error(408) + break + end + + -- any other error + if not stat or not message then + self:error(400, err) + break + end + + -- keep-alive + if message.http_version == 1.1 then + close = (message.env.HTTP_CONNECTION == "close") + else + close = not message.env.HTTP_CONNECTION or message.env.HTTP_CONNECTION == "close" + end + -- Uncomment this to disable keep-alive + -- close = true + + if message.request_method == "get" or message.request_method == "head" then + -- Be happy + + elseif message.request_method == "post" then + -- If we have a HTTP/1.1 client and an Expect: 100-continue header then + -- respond with HTTP 100 Continue message + if message.http_version == 1.1 and message.headers['Expect'] and + message.headers['Expect'] == '100-continue' + then + self:write("HTTP/1.1 100 Continue\r\n\r\n") + end + + if message.headers['Transfer-Encoding'] and + message.headers['Transfer-Encoding'] ~= "identity" then + sourcein = function() return self:readchunk() end + elseif message.env.CONTENT_LENGTH then + sourcein = self:limitsource( + tonumber(message.env.CONTENT_LENGTH) + ) + else + self:error( 411, proto.statusmsg[411] ) + break + end + else + self:error( 405, proto.statusmsg[405] ) + break + + end + + + local host = self.vhosts[message.env.HTTP_HOST] or self.host + if not host then + self:error( 500, "Unable to find matching host" ) + break; + end + + local response, sourceout = host:process( + message, sourcein, sinkerr, + client, io.stderr + ) + if not response then + self:error( 500, "Error processing handler" ) + end + + -- Post process response + if sourceout then + if util.instanceof(sourceout, IOResource) then + if not response.headers["Content-Length"] then + response.headers["Content-Length"] = sourceout.len + end + end + if not response.headers["Content-Length"] then + if message.http_version == 1.1 then + response.headers["Transfer-Encoding"] = "chunked" + sinkout = self:chunksink() + else + close = true + end + end + elseif message.request_method ~= "head" then + response.headers["Content-Length"] = 0 + end + + if close then + response.headers["Connection"] = "close" + end + + response.headers["Date"] = date.to_http(os.time()) + + local header = + message.env.SERVER_PROTOCOL .. " " .. + tostring(response.status) .. " " .. + proto.statusmsg[response.status] .. "\r\n" + + header = header .. "Server: LuCIttpd/" .. tostring(VERSION) .. "\r\n" + + + for k,v in pairs(response.headers) do + header = header .. k .. ": " .. v .. "\r\n" + end + + -- Output + local stat, err = pcall(function() + self:write(header .. "\r\n") + + if sourceout then + if util.instanceof(sourceout, IOResource) then + self:sendfile(sourceout.fd, sourceout.offset, sourceout.len) + else + ltn12.pump.all(sourceout, sinkout) + end + end + + self:flush() + end) + + -- Write errors + if not stat then + if err == 107 then + -- Remote end closed the socket, so do we + elseif err then + io.stderr:write("Error sending data: " .. err .. "\n") + end + break + end + until close + + self:close() +end diff --git a/libs/lucittpd/root/usr/lib/lucittpd/plugins/httpd.lua b/libs/lucittpd/root/usr/lib/lucittpd/plugins/httpd.lua new file mode 100644 index 000000000..2d3a9d8c8 --- /dev/null +++ b/libs/lucittpd/root/usr/lib/lucittpd/plugins/httpd.lua @@ -0,0 +1,35 @@ +function initialize() + local lucittpd = require "luci.ttpd.server" + server = lucittpd.Server(lucittpd.VHost()) +end + +function register() + local filehnd = require "luci.ttpd.handler.file" + local uci = require "luci.model.uci".cursor() + local filehandler = filehnd.Simple((uci:get("lucittpd", "lucittpd", "root") or "/www")) + server:get_default_vhost():set_default_handler(filehandler) +end + +function accept() + server:process({ + _read = function(...) + local chunk, err = webuci_read(...) + return chunk or (err and error(err, 0)) + end, + + _write = function(...) + local chunk, err = webuci_write(...) + return chunk or (err and error(err, 0)) + end, + + _close = function(...) + local chunk, err = webuci_close(...) + return chunk or (err and error(err, 0)) + end, + + _sendfile = function(...) + local chunk, err = webuci_sendfile(...) + return chunk or (err and error(err, 0)) + end + }) +end diff --git a/libs/lucittpd/src/.gitignore b/libs/lucittpd/src/.gitignore new file mode 100644 index 000000000..882c68cb0 --- /dev/null +++ b/libs/lucittpd/src/.gitignore @@ -0,0 +1 @@ +lucittpd diff --git a/libs/lucittpd/src/Makefile b/libs/lucittpd/src/Makefile new file mode 100644 index 000000000..1e70faffe --- /dev/null +++ b/libs/lucittpd/src/Makefile @@ -0,0 +1,20 @@ +PROGS=lucittpd +STRIP?=strip +CFLAGS?= +CFLAGS+=-pedantic -Werror -Wall -std=gnu99 -I/usr/include/lua5.1/ +CPPFLAGS=-I./include +LDFLAGS?= +LDFLAGS+=-luci -llua5.1 +OBJS=main.o lib/log.o lib/signal.o lib/uci.o lib/luaplugin.o + +all: $(PROGS) + +$(PROGS): $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(MYLDFLAGS) $^ -o $@ + #$(CC) $(CFLAGS) -shared -Wl,-soname,gdata $(LDFLAGS) $^ -o $@ + +clean: + rm -f $(PROGS) *.o *.so lib/*.o + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(MYCFLAGS) -c $^ -o $@ diff --git a/libs/lucittpd/src/include/lib/list.h b/libs/lucittpd/src/include/lib/list.h new file mode 100644 index 000000000..2959a061d --- /dev/null +++ b/libs/lucittpd/src/include/lib/list.h @@ -0,0 +1,601 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include <stddef.h> +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#ifndef container_of +#define container_of(ptr, type, member) ( \ + (type *)( (char *)ptr - offsetof(type,member) )) +#endif + + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +static inline void INIT_HLIST_NODE(struct hlist_node *h) +{ + h->next = NULL; + h->pprev = NULL; +} + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = NULL; + n->pprev = NULL; +} + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos; pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos; pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + +#endif diff --git a/libs/lucittpd/src/include/lib/log.h b/libs/lucittpd/src/include/lib/log.h new file mode 100644 index 000000000..1199578d1 --- /dev/null +++ b/libs/lucittpd/src/include/lib/log.h @@ -0,0 +1,24 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Provided by fon.com + * Copyright (C) 2008 John Crispin <blogic@openwrt.org> + */ + +#ifndef _LOG_H__ +#define _LOG_H__ +void log_printf(char *fmt, ...); +void log_start(int daemon); +#endif diff --git a/libs/lucittpd/src/include/lib/luaplugin.h b/libs/lucittpd/src/include/lib/luaplugin.h new file mode 100644 index 000000000..fca6cb7f2 --- /dev/null +++ b/libs/lucittpd/src/include/lib/luaplugin.h @@ -0,0 +1,71 @@ +/* + * luaplugin - fast lua plugin indexing + * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __LUAPLUGIN_H +#define __LUAPLUGIN_H + +#include <sys/time.h> +#include <lualib.h> +#include <lauxlib.h> +#include <stdbool.h> +#include "list.h" + +struct luaplugin_entry { + struct luaplugin_ctx *ctx; + struct list_head list; + time_t timestamp; + int checked; + bool loaded; + bool reload; + char *name; + char *module; + + /* privdata for the caller */ + void *priv; +}; + +struct luaplugin_ctx { + const char *path; + const struct luaplugin_ops *ops; + lua_State *L; + int checked; + struct list_head *last; + struct list_head entries; +}; + +/** luaplugin_init: + * initialize the luaplugin context (allocates a new lua context) + */ +extern int luaplugin_init(struct luaplugin_ctx *ctx, const char *path); + +/** luaplugin_scan: + * rescan the plugin cache + */ +extern void luaplugin_scan(struct luaplugin_ctx *ctx); + +/** luaplugin_call: + * call out to a lua function. + * to be able to use this, you need to push the function name on the lua stack (ctx->L) + * and then narg function arguments afterwards. + * this call pops (narg + 1) arguments from the stack + * returns -ENOENT if the function was not found + */ +extern int luaplugin_call(struct luaplugin_entry *e, int narg); + +/** luaplugin_done: + * drop the luaplugin context (and associated lua context) + * frees all memory allocated by the library + */ +extern void luaplugin_done(struct luaplugin_ctx *ctx); + +#endif diff --git a/libs/lucittpd/src/include/lib/signal.h b/libs/lucittpd/src/include/lib/signal.h new file mode 100644 index 000000000..cfcce0a16 --- /dev/null +++ b/libs/lucittpd/src/include/lib/signal.h @@ -0,0 +1,25 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Provided by fon.com + * Copyright (C) 2008 John Crispin <blogic@openwrt.org> + */ + +#ifndef _SIGNAL_H__ +#define _SIGNAL_H__ + +void setup_signals(void); + +#endif diff --git a/libs/lucittpd/src/include/lib/uci.h b/libs/lucittpd/src/include/lib/uci.h new file mode 100644 index 000000000..4a1a01c09 --- /dev/null +++ b/libs/lucittpd/src/include/lib/uci.h @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2008 John Crispin <blogic@openwrt.org> + */ + +#ifndef _UCI_H__ +#define _UCI_H__ +#include <uci.h> +#include <lib/list.h> + +struct ucilist { + struct list_head list; + char *val; +}; + +struct uci_context* ucix_init(const char *config_file); +struct uci_context* ucix_init_path(const char *path, const char *config_file); +void ucix_cleanup(struct uci_context *ctx); +void ucix_save(struct uci_context *ctx); +void ucix_save_state(struct uci_context *ctx); +const char* ucix_get_option(struct uci_context *ctx, + const char *p, const char *s, const char *o); +int ucix_get_option_list(struct uci_context *ctx, const char *p, + const char *s, const char *o, struct list_head *l); +int ucix_get_option_int(struct uci_context *ctx, + const char *p, const char *s, const char *o, int def); +void ucix_add_section(struct uci_context *ctx, + const char *p, const char *s, const char *t); +void ucix_add_option(struct uci_context *ctx, + const char *p, const char *s, const char *o, const char *t); +void ucix_add_option_int(struct uci_context *ctx, + const char *p, const char *s, const char *o, int t); +void ucix_for_each_section_type(struct uci_context *ctx, + const char *p, const char *t, + void (*cb)(const char*, void*), void *priv); +int ucix_commit(struct uci_context *ctx, const char *p); +void ucix_revert(struct uci_context *ctx, + const char *p, const char *s, const char *o); +void ucix_del(struct uci_context *ctx, const char *p, + const char *s, const char *o); +#endif diff --git a/libs/lucittpd/src/include/uci.h b/libs/lucittpd/src/include/uci.h new file mode 100644 index 000000000..d48ea7cef --- /dev/null +++ b/libs/lucittpd/src/include/uci.h @@ -0,0 +1,543 @@ +/* + * libuci - Library for the Unified Configuration Interface + * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LIBUCI_H +#define __LIBUCI_H + +#include "uci_config.h" + +/* + * you can use these defines to enable debugging behavior for + * apps compiled against libuci: + * + * #define UCI_DEBUG_TYPECAST: + * enable uci_element typecast checking at run time + * + */ + +#include <stdbool.h> +#include <setjmp.h> +#include <stdio.h> + +#define UCI_CONFDIR "/etc/config" +#define UCI_SAVEDIR "/tmp/.uci" +#define UCI_DIRMODE 0700 +#define UCI_FILEMODE 0600 + +enum +{ + UCI_OK = 0, + UCI_ERR_MEM, + UCI_ERR_INVAL, + UCI_ERR_NOTFOUND, + UCI_ERR_IO, + UCI_ERR_PARSE, + UCI_ERR_DUPLICATE, + UCI_ERR_UNKNOWN, + UCI_ERR_LAST +}; + +struct uci_list; +struct uci_list +{ + struct uci_list *next; + struct uci_list *prev; +}; + +struct uci_ptr; +struct uci_element; +struct uci_package; +struct uci_section; +struct uci_option; +struct uci_history; +struct uci_context; +struct uci_backend; +struct uci_parse_context; + + +/** + * uci_alloc_context: Allocate a new uci context + */ +extern struct uci_context *uci_alloc_context(void); + +/** + * uci_free_context: Free the uci context including all of its data + */ +extern void uci_free_context(struct uci_context *ctx); + +/** + * uci_perror: Print the last uci error that occured + * @ctx: uci context + * @str: string to print before the error message + */ +extern void uci_perror(struct uci_context *ctx, const char *str); + +/** + * uci_geterror: Get an error string for the last uci error + * @ctx: uci context + * @dest: target pointer for the string + * @str: prefix for the error message + * + * Note: string must be freed by the caller + */ +extern void uci_get_errorstr(struct uci_context *ctx, char **dest, const char *str); + +/** + * uci_import: Import uci config data from a stream + * @ctx: uci context + * @stream: file stream to import from + * @name: (optional) assume the config has the given name + * @package: (optional) store the last parsed config package in this variable + * @single: ignore the 'package' keyword and parse everything into a single package + * + * the name parameter is for config files that don't explicitly use the 'package <...>' keyword + * if 'package' points to a non-null struct pointer, enable history tracking and merge + */ +extern int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct uci_package **package, bool single); + +/** + * uci_export: Export one or all uci config packages + * @ctx: uci context + * @stream: output stream + * @package: (optional) uci config package to export + * @header: include the package header + */ +extern int uci_export(struct uci_context *ctx, FILE *stream, struct uci_package *package, bool header); + +/** + * uci_load: Parse an uci config file and store it in the uci context + * + * @ctx: uci context + * @name: name of the config file (relative to the config directory) + * @package: store the loaded config package in this variable + */ +extern int uci_load(struct uci_context *ctx, const char *name, struct uci_package **package); + +/** + * uci_unload: Unload a config file from the uci context + * + * @ctx: uci context + * @package: pointer to the uci_package struct + */ +extern int uci_unload(struct uci_context *ctx, struct uci_package *p); + +/** + * uci_lookup_ptr: Split an uci tuple string and look up an element tree + * @ctx: uci context + * @ptr: lookup result struct + * @str: uci tuple string to look up + * @extended: allow extended syntax lookup + * + * if extended is set to true, uci_lookup_ptr supports the following + * extended syntax: + * + * Examples: + * network.@interface[0].ifname ('ifname' option of the first interface section) + * network.@interface[-1] (last interface section) + * Note: uci_lookup_ext will automatically load a config package if necessary + */ +extern int uci_lookup_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *str, bool extended); + +/** + * uci_add_section: Add an unnamed section + * @ctx: uci context + * @p: package to add the section to + * @type: section type + * @res: pointer to store a reference to the new section in + */ +extern int uci_add_section(struct uci_context *ctx, struct uci_package *p, const char *type, struct uci_section **res); + +/** + * uci_set: Set an element's value; create the element if necessary + * @ctx: uci context + * @ptr: uci pointer + * + * The updated/created element is stored in ptr->last + */ +extern int uci_set(struct uci_context *ctx, struct uci_ptr *ptr); + +/** + * uci_add_list: Append a string to an element list + * @ctx: uci context + * @ptr: uci pointer (with value) + * + * Note: if the given option already contains a string value, + * it will be converted to an 1-element-list before appending the next element + */ +extern int uci_add_list(struct uci_context *ctx, struct uci_ptr *ptr); + +/** + * uci_rename: Rename an element + * @ctx: uci context + * @ptr: uci pointer (with value) + */ +extern int uci_rename(struct uci_context *ctx, struct uci_ptr *ptr); + +/** + * uci_delete: Delete a section or option + * @ctx: uci context + * @ptr: uci pointer + */ +extern int uci_delete(struct uci_context *ctx, struct uci_ptr *ptr); + +/** + * uci_save: save change history for a package + * @ctx: uci context + * @p: uci_package struct + */ +extern int uci_save(struct uci_context *ctx, struct uci_package *p); + +/** + * uci_commit: commit changes to a package + * @ctx: uci context + * @p: uci_package struct pointer + * @overwrite: overwrite existing config data and flush history + * + * committing may reload the whole uci_package data, + * the supplied pointer is updated accordingly + */ +extern int uci_commit(struct uci_context *ctx, struct uci_package **p, bool overwrite); + +/** + * uci_list_configs: List available uci config files + * @ctx: uci context + * + * caller is responsible for freeing the allocated memory behind list + */ +extern int uci_list_configs(struct uci_context *ctx, char ***list); + +/** + * uci_set_savedir: override the default history save directory + * @ctx: uci context + * @dir: directory name + */ +extern int uci_set_savedir(struct uci_context *ctx, const char *dir); + +/** + * uci_set_savedir: override the default config storage directory + * @ctx: uci context + * @dir: directory name + */ +extern int uci_set_confdir(struct uci_context *ctx, const char *dir); + +/** + * uci_add_history_path: add a directory to the search path for change history files + * @ctx: uci context + * @dir: directory name + * + * This function allows you to add directories, which contain 'overlays' + * for the active config, that will never be committed. + */ +extern int uci_add_history_path(struct uci_context *ctx, const char *dir); + +/** + * uci_revert: revert all changes to a config item + * @ctx: uci context + * @ptr: uci pointer + */ +extern int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr); + +/** + * uci_parse_argument: parse a shell-style argument, with an arbitrary quoting style + * @ctx: uci context + * @stream: input stream + * @str: pointer to the current line (use NULL for parsing the next line) + * @result: pointer for the result + */ +extern int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, char **result); + +/** + * uci_set_backend: change the default backend + * @ctx: uci context + * @name: name of the backend + * + * The default backend is "file", which uses /etc/config for config storage + */ +extern int uci_set_backend(struct uci_context *ctx, const char *name); + +/** + * uci_validate_text: validate a value string for uci options + * @str: value + * + * this function checks whether a given string is acceptable as value + * for uci options + */ +extern bool uci_validate_text(const char *str); + +/* UCI data structures */ +enum uci_type { + UCI_TYPE_UNSPEC = 0, + UCI_TYPE_HISTORY = 1, + UCI_TYPE_PACKAGE = 2, + UCI_TYPE_SECTION = 3, + UCI_TYPE_OPTION = 4, + UCI_TYPE_PATH = 5, + UCI_TYPE_BACKEND = 6, + UCI_TYPE_ITEM = 7, +}; + +enum uci_option_type { + UCI_TYPE_STRING = 0, + UCI_TYPE_LIST = 1, +}; + +enum uci_flags { + UCI_FLAG_STRICT = (1 << 0), /* strict mode for the parser */ + UCI_FLAG_PERROR = (1 << 1), /* print parser error messages */ + UCI_FLAG_EXPORT_NAME = (1 << 2), /* when exporting, name unnamed sections */ + UCI_FLAG_SAVED_HISTORY = (1 << 3), /* store the saved history in memory as well */ +}; + +struct uci_element +{ + struct uci_list list; + enum uci_type type; + char *name; +}; + +struct uci_backend +{ + struct uci_element e; + char **(*list_configs)(struct uci_context *ctx); + struct uci_package *(*load)(struct uci_context *ctx, const char *name); + void (*commit)(struct uci_context *ctx, struct uci_package **p, bool overwrite); + + /* private: */ + const void *ptr; + void *priv; +}; + +struct uci_context +{ + /* list of config packages */ + struct uci_list root; + + /* parser context, use for error handling only */ + struct uci_parse_context *pctx; + + /* backend for import and export */ + struct uci_backend *backend; + struct uci_list backends; + + /* uci runtime flags */ + enum uci_flags flags; + + char *confdir; + char *savedir; + + /* search path for history files */ + struct uci_list history_path; + + /* private: */ + int err; + const char *func; + jmp_buf trap; + bool internal, nested; + char *buf; + int bufsz; +}; + +struct uci_package +{ + struct uci_element e; + struct uci_list sections; + struct uci_context *ctx; + bool has_history; + char *path; + + /* private: */ + struct uci_backend *backend; + void *priv; + int n_section; + struct uci_list history; + struct uci_list saved_history; +}; + +struct uci_section +{ + struct uci_element e; + struct uci_list options; + struct uci_package *package; + bool anonymous; + char *type; +}; + +struct uci_option +{ + struct uci_element e; + struct uci_section *section; + enum uci_option_type type; + union { + struct uci_list list; + char *string; + } v; +}; + +enum uci_command { + UCI_CMD_ADD, + UCI_CMD_REMOVE, + UCI_CMD_CHANGE, + UCI_CMD_RENAME, + UCI_CMD_LIST_ADD, +}; + +struct uci_history +{ + struct uci_element e; + enum uci_command cmd; + char *section; + char *value; +}; + +struct uci_ptr +{ + enum uci_type target; + enum { + UCI_LOOKUP_DONE = (1 << 0), + UCI_LOOKUP_COMPLETE = (1 << 1), + UCI_LOOKUP_EXTENDED = (1 << 2), + } flags; + + struct uci_package *p; + struct uci_section *s; + struct uci_option *o; + struct uci_element *last; + + const char *package; + const char *section; + const char *option; + const char *value; +}; + + +/* linked list handling */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + */ +#define container_of(ptr, type, member) \ + ((type *) ((char *)ptr - offsetof(type,member))) + + +/** + * uci_list_entry: casts an uci_list pointer to the containing struct. + * @_type: config, section or option + * @_ptr: pointer to the uci_list struct + */ +#define list_to_element(ptr) \ + container_of(ptr, struct uci_element, list) + +/** + * uci_foreach_entry: loop through a list of uci elements + * @_list: pointer to the uci_list struct + * @_ptr: iteration variable, struct uci_element + * + * use like a for loop, e.g: + * uci_foreach(&list, p) { + * ... + * } + */ +#define uci_foreach_element(_list, _ptr) \ + for(_ptr = list_to_element((_list)->next); \ + &_ptr->list != (_list); \ + _ptr = list_to_element(_ptr->list.next)) + +/** + * uci_foreach_entry_safe: like uci_foreach_safe, but safe for deletion + * @_list: pointer to the uci_list struct + * @_tmp: temporary variable, struct uci_element * + * @_ptr: iteration variable, struct uci_element * + * + * use like a for loop, e.g: + * uci_foreach(&list, p) { + * ... + * } + */ +#define uci_foreach_element_safe(_list, _tmp, _ptr) \ + for(_ptr = list_to_element((_list)->next), \ + _tmp = list_to_element(_ptr->list.next); \ + &_ptr->list != (_list); \ + _ptr = _tmp, _tmp = list_to_element(_ptr->list.next)) + +/** + * uci_list_empty: returns true if a list is empty + * @list: list head + */ +#define uci_list_empty(list) ((list)->next == (list)) + +/* wrappers for dynamic type handling */ +#define uci_type_backend UCI_TYPE_BACKEND +#define uci_type_history UCI_TYPE_HISTORY +#define uci_type_package UCI_TYPE_PACKAGE +#define uci_type_section UCI_TYPE_SECTION +#define uci_type_option UCI_TYPE_OPTION + +/* element typecasting */ +#ifdef UCI_DEBUG_TYPECAST +static const char *uci_typestr[] = { + [uci_type_backend] = "backend", + [uci_type_history] = "history", + [uci_type_package] = "package", + [uci_type_section] = "section", + [uci_type_option] = "option", +}; + +static void uci_typecast_error(int from, int to) +{ + fprintf(stderr, "Invalid typecast from '%s' to '%s'\n", uci_typestr[from], uci_typestr[to]); +} + +#define BUILD_CAST(_type) \ + static inline struct uci_ ## _type *uci_to_ ## _type (struct uci_element *e) \ + { \ + if (e->type != uci_type_ ## _type) { \ + uci_typecast_error(e->type, uci_type_ ## _type); \ + } \ + return (struct uci_ ## _type *) e; \ + } + +BUILD_CAST(backend) +BUILD_CAST(history) +BUILD_CAST(package) +BUILD_CAST(section) +BUILD_CAST(option) + +#else +#define uci_to_backend(ptr) container_of(ptr, struct uci_backend, e) +#define uci_to_history(ptr) container_of(ptr, struct uci_history, e) +#define uci_to_package(ptr) container_of(ptr, struct uci_package, e) +#define uci_to_section(ptr) container_of(ptr, struct uci_section, e) +#define uci_to_option(ptr) container_of(ptr, struct uci_option, e) +#endif + +/** + * uci_alloc_element: allocate a generic uci_element, reserve a buffer and typecast + * @ctx: uci context + * @type: {package,section,option} + * @name: string containing the name of the element + * @datasize: additional buffer size to reserve at the end of the struct + */ +#define uci_alloc_element(ctx, type, name, datasize) \ + uci_to_ ## type (uci_alloc_generic(ctx, uci_type_ ## type, name, sizeof(struct uci_ ## type) + datasize)) + +#define uci_dataptr(ptr) \ + (((char *) ptr) + sizeof(*ptr)) + +#endif diff --git a/libs/lucittpd/src/include/uci_config.h b/libs/lucittpd/src/include/uci_config.h new file mode 100644 index 000000000..0dd76cc39 --- /dev/null +++ b/libs/lucittpd/src/include/uci_config.h @@ -0,0 +1,3 @@ +#define UCI_PLUGIN_SUPPORT 1 +#undef UCI_DEBUG +#undef UCI_DEBUG_TYPECAST diff --git a/libs/lucittpd/src/lib/log.c b/libs/lucittpd/src/lib/log.c new file mode 100644 index 000000000..b6ce8c28e --- /dev/null +++ b/libs/lucittpd/src/lib/log.c @@ -0,0 +1,45 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Provided by fon.com + * Copyright (C) 2008 John Crispin <blogic@openwrt.org> + */ + +#include <stdio.h> +#include <syslog.h> +#include <stdarg.h> + +static int daemonize = 0; + +void log_start(int daemon) +{ + daemonize = daemon; + openlog("lucittpd", 0, 0); +} + +void log_printf(char *fmt, ...) +{ + char p[256]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(p, 256, fmt, ap); + va_end(ap); + + if(daemonize) + syslog(10, p); + else + printf(p); +} diff --git a/libs/lucittpd/src/lib/luaplugin.c b/libs/lucittpd/src/lib/luaplugin.c new file mode 100644 index 000000000..6a0e1caad --- /dev/null +++ b/libs/lucittpd/src/lib/luaplugin.c @@ -0,0 +1,383 @@ +/* + * luaplugin - fast lua plugin indexing + * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/cdefs.h> + +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE /* XXX: portability hack for timestamp */ +#endif + +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <limits.h> +#include <glob.h> + +#include <lualib.h> +#include <lauxlib.h> +#include <lib/list.h> +#include <lib/luaplugin.h> + +//#define DEBUG 1 +#ifdef DEBUG +#define DPRINTF(...) fprintf(stderr, __VA_ARGS__) +#else +#define DPRINTF(...) do {} while (0) +#endif + +/** + * list_for_each_offset - iterate over a list, start with the provided pointer + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_offset(pos, head, offset) \ + for (pos = (offset)->next; pos != (offset); \ + pos = ((pos->next == (head)) && ((offset) != (head)) ? (head)->next : pos->next)) + +static char pbuf[PATH_MAX]; +static void load_module(struct luaplugin_ctx *ctx, struct luaplugin_entry *e); + +static struct luaplugin_entry * +find_entry(struct luaplugin_ctx *ctx, const char *name, bool modname) +{ + struct list_head *p; + + if (!ctx->last) + ctx->last = &ctx->entries; + + list_for_each_offset(p, &ctx->entries, ctx->last) { + struct luaplugin_entry *e; + const char *cmp; + + e = container_of(p, struct luaplugin_entry, list); + if (modname) + cmp = e->module; + else + cmp = e->name; + + if (!strcmp(cmp, name)) + return e; + } + return NULL; +} + +static struct luaplugin_entry * +new_entry(struct luaplugin_ctx *ctx, const char *name, const char *modname) +{ + struct luaplugin_entry *e; + char *c; + + e = malloc(sizeof(struct luaplugin_entry)); + if (!e) + goto error; + + memset(e, 0, sizeof(struct luaplugin_entry)); + INIT_LIST_HEAD(&e->list); + e->ctx = ctx; + e->loaded = false; + + e->name = strdup(name); + if (!e->name) + goto error1; + + e->module = strdup(modname); + if (!e->module) + goto error2; + + /* strip filename extension */ + c = strrchr(e->module, '.'); + if (c) + *c = 0; + + /* lua namespace: replace / with . */ + c = e->module; + while ((c = strchr(c, '/')) != NULL) { + *c = '.'; + } + return e; + +error2: + free(e->name); +error1: + free(e); +error: + return NULL; +} + +static const char *module_loader = +"loader = function (newgt, filename)\n" +" setmetatable(newgt, { __index = _G })\n" +" local f = loadfile(filename)\n" +" if (type(f) == \"function\") then\n" +" setfenv(f, newgt)\n" +" f()\n" +" else\n" +" error(f)\n" +" end\n" +"end\n"; + +static void +access_plugin_table (lua_State *L, const char *modname, bool set) +{ + const char *e; + + lua_pushvalue(L, LUA_GLOBALSINDEX); + do { + bool _set = true; + + e = strchr(modname, '.'); + if (e == NULL) { + e = modname + strlen(modname); + _set = set; + } + + lua_pushlstring(L, modname, e - modname); + lua_rawget(L, -2); + if (lua_isnil(L, -1) || + /* no such field or last field */ + (lua_istable(L, -1) && (*e != '.'))) { + lua_pop(L, 1); /* remove this result */ + + if (_set) { + if (*e != '.') + lua_pushvalue(L, -2); /* use table from given index */ + else + lua_createtable(L, 0, 1); /* new table for field */ + } + + lua_pushlstring(L, modname, e - modname); + + if (_set) { + lua_pushvalue(L, -2); + lua_settable(L, -4); /* set new table into field */ + } else { + lua_gettable(L, -2); + } + } + else if (!lua_istable(L, -1)) { /* field has a non-table value? */ + lua_pop(L, 2 + !!set); /* remove table and values */ + return; + } + lua_remove(L, -2); /* remove previous table */ + modname = e + 1; + } while (*e == '.'); + if (set) + lua_pop(L, 2); +} + + +static void +load_module(struct luaplugin_ctx *ctx, struct luaplugin_entry *e) +{ + lua_State *L = ctx->L; + int ret; + + /* grab the loader wrapper function */ + ret = luaL_dostring(L, module_loader); + if (ret) + return; + + lua_getglobal(L, "loader"); + lua_pushnil(L); + lua_setglobal(L, "loader"); + + e->loaded = true; + e->reload = false; + + /* new environment table for function call */ + lua_newtable(L); + + /* register the table globally */ + lua_pushvalue(L, -1); + access_plugin_table(L, e->module, true); + + lua_pushstring(L, e->name); + + if (lua_pcall(L, 2, 0, 0) != 0) { + const char *err = "unknown error"; + + if (lua_isstring(L, -1)) + err = lua_tostring(L, -1); + + fprintf(stderr, err); + } +} + +static void +free_entry(struct luaplugin_ctx *ctx, struct luaplugin_entry *e) +{ + lua_State *L = ctx->L; + + if (e->loaded && L) { + /* allow the gc to free the module */ + lua_pushnil(L); + access_plugin_table(L, e->module, true); + } + list_del(&e->list); + free(e->name); + free(e->module); + free(e); +} + +static void +__luaplugin_scan(struct luaplugin_ctx *ctx, int base_len, int rec) +{ + int gl_flags = GLOB_NOESCAPE | GLOB_NOSORT | GLOB_MARK; + glob_t gl; + int i; + + strncpy(pbuf + base_len, "*.lua", PATH_MAX - base_len); + if (glob(pbuf, gl_flags, NULL, &gl) < 0) { + globfree(&gl); + return; + } + + for (i = 0; i < gl.gl_pathc; i++) { + const char *entry = gl.gl_pathv[i]; + struct luaplugin_entry *e; + struct stat st; + int elen; + + elen = strlen(entry); + + /* should not happen */ + if ((elen <= base_len) || (strncmp(entry, pbuf, base_len) != 0)) { + fprintf(stderr, "[%s] sanity check failed in %s(%d)!\n", __FILE__, __func__, __LINE__); + continue; + } + + /* descend into subdirectories */ + if (entry[elen - 1] == '/') { + strncpy(pbuf + base_len, entry + base_len, PATH_MAX - base_len); + __luaplugin_scan(ctx, base_len, rec + 1); + pbuf[base_len] = '\0'; + continue; + } + + if (stat(gl.gl_pathv[i], &st)) + continue; + + if ((st.st_mode & S_IFMT) != S_IFREG) + continue; + + e = find_entry(ctx, entry + base_len, false); + if (!e) { + e = new_entry(ctx, entry, entry + base_len); + list_add_tail(&e->list, &ctx->entries); + } + if (!e) + continue; + + e->checked = ctx->checked; + e->reload = (e->timestamp < st.st_mtime); + e->timestamp = st.st_mtime; + } + globfree(&gl); +} + +int +luaplugin_call(struct luaplugin_entry *e, int narg) +{ + struct luaplugin_ctx *ctx = e->ctx; + lua_State *L = ctx->L; + const char *func; + int ret; + + func = luaL_checkstring(L, -1 - narg); + + /* grab a reference to the plugin's table */ + access_plugin_table(L, e->module, false); + lua_getfield(L, -1, func); + if (!lua_isfunction(L, -1)) { + lua_pop(L, narg + 1); + ret = -ENOENT; + goto done; + } + + /* replace function name with a ref to the function */ + lua_replace(L, -3 - narg); + + /* pop the table */ + lua_pop(L, 1); + ret = lua_pcall(L, narg, 0, 0); + + if (ret != 0) { + fprintf(stderr, lua_tostring(L, -1)); + } + +done: + return ret; +} + +void +luaplugin_scan(struct luaplugin_ctx *ctx) +{ + struct list_head *tmp, *p; + + sprintf(pbuf, "%s/", ctx->path); + + ctx->checked++; + __luaplugin_scan(ctx, strlen(pbuf), 0); + + /* expire old entries */ + list_for_each_safe(p, tmp, &ctx->entries) { + struct luaplugin_entry *e = container_of(p, struct luaplugin_entry, list); + if (e->checked < ctx->checked) + free_entry(ctx, e); + else if (e->reload) + load_module(ctx, e); + } +} + +int +luaplugin_init(struct luaplugin_ctx *ctx, const char *path) +{ + memset(ctx, 0, sizeof(struct luaplugin_ctx)); + INIT_LIST_HEAD(&ctx->entries); + ctx->path = path; + + ctx->L = luaL_newstate(); + if (!ctx->L) + return -ENOMEM; + + luaL_openlibs(ctx->L); + + /* disable the module functionality, a plugin is restricted to its own environment */ + /* + lua_pushcfunction(ctx->L, luaplugin_module); + lua_setfield(ctx->L, LUA_GLOBALSINDEX, "module"); + */ + + return 0; +} + +void +luaplugin_done(struct luaplugin_ctx *ctx) +{ + struct list_head *p, *tmp; + + lua_close(ctx->L); + ctx->L = NULL; + + list_for_each_safe(p, tmp, &ctx->entries) { + struct luaplugin_entry *e; + e = container_of(p, struct luaplugin_entry, list); + free_entry(ctx, e); + } +} diff --git a/libs/lucittpd/src/lib/signal.c b/libs/lucittpd/src/lib/signal.c new file mode 100644 index 000000000..2b11f47cb --- /dev/null +++ b/libs/lucittpd/src/lib/signal.c @@ -0,0 +1,52 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Provided by fon.com + * Copyright (C) 2008 John Crispin <blogic@openwrt.org> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <lib/log.h> + +void handler_INT(int signo) +{ + log_printf("away we go\n"); + exit(0); +} + +void handler_CHLD(int signo) +{ + while(waitpid(-1, NULL, WNOHANG) > 0); +} + +void setup_signals(void) +{ + struct sigaction s1, s2, s3; + s1.sa_handler = handler_INT; + s1.sa_flags = 0; + sigaction(SIGINT, &s1, NULL); + s2.sa_handler = handler_INT; + s2.sa_flags = 0; + sigaction(SIGTERM, &s2, NULL); + s3.sa_handler = handler_CHLD; + s3.sa_flags = SA_RESTART; + sigaction(SIGCHLD, &s3, NULL); +} diff --git a/libs/lucittpd/src/lib/uci.c b/libs/lucittpd/src/lib/uci.c new file mode 100644 index 000000000..33254ee5e --- /dev/null +++ b/libs/lucittpd/src/lib/uci.c @@ -0,0 +1,206 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2008 John Crispin <blogic@openwrt.org> + */ + +#include <string.h> +#include <stdlib.h> + +#include <uci.h> +#include <lib/list.h> +#include <lib/log.h> +#include <lib/uci.h> + +static struct uci_ptr ptr; +static struct uci_package *p = NULL; + +static inline int ucix_get_ptr(struct uci_context *ctx, const char *p, const char *s, const char *o, const char *t) +{ + memset(&ptr, 0, sizeof(ptr)); + ptr.package = p; + ptr.section = s; + ptr.option = o; + ptr.value = t; + return uci_lookup_ptr(ctx, &ptr, NULL, true); +} + +struct uci_context* ucix_init(const char *config_file) +{ + struct uci_context *ctx = uci_alloc_context(); + uci_add_history_path(ctx, "/var/state"); + uci_set_savedir(ctx, "/var/state/"); + if(uci_load(ctx, config_file, &p) != UCI_OK) + { + log_printf("%s/%s is missing or corrupt\n", ctx->savedir, config_file); + return NULL; + } + return ctx; +} + +struct uci_context* ucix_init_path(const char *path, const char *config_file) +{ + struct uci_context *ctx = uci_alloc_context(); + if(path) + uci_set_confdir(ctx, path); + if(uci_load(ctx, config_file, NULL) != UCI_OK) + { + log_printf("%s/%s is missing or corrupt\n", ctx->savedir, config_file); + return NULL; + } + return ctx; +} + +void ucix_cleanup(struct uci_context *ctx) +{ + uci_free_context(ctx); +} + +void ucix_save(struct uci_context *ctx) +{ + uci_set_savedir(ctx, "/tmp/.uci"); + uci_save(ctx, p); +} + +void ucix_save_state(struct uci_context *ctx) +{ + uci_save(ctx, p); +} + +int ucix_get_option_list(struct uci_context *ctx, const char *p, + const char *s, const char *o, struct list_head *l) +{ + struct uci_element *e = NULL; + if(ucix_get_ptr(ctx, p, s, o, NULL)) + return 1; + if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) + return 1; + e = ptr.last; + switch (e->type) + { + case UCI_TYPE_OPTION: + switch(ptr.o->type) { + case UCI_TYPE_LIST: + uci_foreach_element(&ptr.o->v.list, e) + { + struct ucilist *ul = malloc(sizeof(struct ucilist)); + ul->val = strdup((e->name)?(e->name):("")); + INIT_LIST_HEAD(&ul->list); + list_add(&ul->list, l); + } + break; + default: + break; + } + break; + default: + return 1; + } + + return 0; +} + +const char* ucix_get_option(struct uci_context *ctx, const char *p, const char *s, const char *o) +{ + struct uci_element *e = NULL; + const char *value = NULL; + if(ucix_get_ptr(ctx, p, s, o, NULL)) + return NULL; + if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) + return NULL; + e = ptr.last; + switch (e->type) + { + case UCI_TYPE_SECTION: + value = uci_to_section(e)->type; + break; + case UCI_TYPE_OPTION: + switch(ptr.o->type) { + case UCI_TYPE_STRING: + value = ptr.o->v.string; + break; + default: + value = NULL; + break; + } + break; + default: + return 0; + } + + return value; +} + +int ucix_get_option_int(struct uci_context *ctx, const char *p, const char *s, const char *o, int def) +{ + const char *tmp = ucix_get_option(ctx, p, s, o); + int ret = def; + + if (tmp) + ret = atoi(tmp); + return ret; +} + +void ucix_add_section(struct uci_context *ctx, const char *p, const char *s, const char *t) +{ + if(ucix_get_ptr(ctx, p, s, NULL, t)) + return; + uci_set(ctx, &ptr); +} + +void ucix_add_option(struct uci_context *ctx, const char *p, const char *s, const char *o, const char *t) +{ + if(ucix_get_ptr(ctx, p, s, o, (t)?(t):(""))) + return; + uci_set(ctx, &ptr); +} + +void ucix_add_option_int(struct uci_context *ctx, const char *p, const char *s, const char *o, int t) +{ + char tmp[64]; + snprintf(tmp, 64, "%d", t); + ucix_add_option(ctx, p, s, o, tmp); +} + +void ucix_del(struct uci_context *ctx, const char *p, const char *s, const char *o) +{ + if(!ucix_get_ptr(ctx, p, s, o, NULL)) + uci_delete(ctx, &ptr); +} + +void ucix_revert(struct uci_context *ctx, const char *p, const char *s, const char *o) +{ + if(!ucix_get_ptr(ctx, p, s, o, NULL)) + uci_revert(ctx, &ptr); +} + +void ucix_for_each_section_type(struct uci_context *ctx, + const char *p, const char *t, + void (*cb)(const char*, void*), void *priv) +{ + struct uci_element *e; + if(ucix_get_ptr(ctx, p, NULL, NULL, NULL)) + return; + uci_foreach_element(&ptr.p->sections, e) + if (!strcmp(t, uci_to_section(e)->type)) + cb(e->name, priv); +} + +int ucix_commit(struct uci_context *ctx, const char *p) +{ + if(ucix_get_ptr(ctx, p, NULL, NULL, NULL)) + return 1; + return uci_commit(ctx, &ptr.p, false); +} diff --git a/libs/lucittpd/src/main.c b/libs/lucittpd/src/main.c new file mode 100644 index 000000000..3ee5506a8 --- /dev/null +++ b/libs/lucittpd/src/main.c @@ -0,0 +1,333 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2008 John Crispin <blogic@openwrt.org> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <sys/wait.h> +#include <signal.h> +#include <features.h> + +#include <lib/uci.h> +#include <lib/log.h> +#include <lib/signal.h> +#include <lib/luaplugin.h> + +#ifndef __UCLIBC__ +#include <sys/sendfile.h> +#endif + +#define BACKLOG 10 + +static int port = 0; +static const char *plugin_path = NULL; +static struct luaplugin_ctx ctx; +static struct luaplugin_entry *e; +static struct timeval timeout; + +static void load_config(void) +{ + timeout.tv_usec = 0; + + static struct uci_context* uci = 0; + uci = ucix_init("lucittpd"); + if(uci) + { + plugin_path = ucix_get_option(uci, "lucittpd", "lucittpd", "path"); + port = ucix_get_option_int(uci, "lucittpd", "lucittpd", "port", 80); + timeout.tv_sec = ucix_get_option_int(uci, "lucittpd", "lucittpd", "timeout", 90); + } else { + port = 8080; + timeout.tv_sec = 90; + } + if(!plugin_path) + plugin_path = strdup("/usr/lib/lucittpd/plugins/"); + + // ToDo: Check why below command segfaults in uci_free_context + //ucix_cleanup(uci); +} + +static int webuci_read(lua_State *L) +{ + int len = luaL_checkinteger(L, 1); + if (len <= 0) { + return luaL_argerror(L, 1, "too low"); + } + + char *buffer = malloc(len); + if (!buffer) { + return luaL_error(L, "malloc() failed"); + } + + int sockfd = lua_tointeger(L, lua_upvalueindex(1)); + + len = read(sockfd, buffer, len); + if (len > 0) { + lua_pushlstring(L, buffer, len); + free(buffer); + } else { + free(buffer); + lua_pushnil(L); + lua_pushinteger(L, (len == 0) ? 0 : errno); + return 2; + } + + /* fprintf(stderr, "%s:%s[%d] %d %d\n", __FILE__, __func__, __LINE__, sockfd, len); */ + + return 1; +} + +static int webuci_close(lua_State *L) +{ + int sockfd = lua_tointeger(L, lua_upvalueindex(1)); + int result = shutdown(sockfd, SHUT_RDWR); + close(sockfd); + /*log_printf("%s:%s[%d] %d %d\n", __FILE__, __func__, __LINE__, sockfd, result);*/ + + if (result < 0) { + lua_pushnil(L); + lua_pushinteger(L, errno); + return 2; + } else { + lua_pushboolean(L, 1); + return 1; + } +} + +static int webuci_write(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + + size_t len; + const char *data = lua_tolstring(L, 1, &len); + int sockfd = lua_tointeger(L, lua_upvalueindex(1)); + + len = send(sockfd, data, len, 0); + /*log_printf("%s:%s[%d] %d %d - %s\n", __FILE__, __func__, __LINE__, sockfd, len, data);*/ + if (len < 0) { + lua_pushnil(L); + lua_pushinteger(L, errno); + return 2; + } else { + lua_pushinteger(L, len); + return 1; + } +} + +static int webuci_sendfile(lua_State *L) +{ + FILE **fp = (FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE); + if (*fp == NULL) { + return luaL_error(L, "attempt to use a closed file"); + } + + off_t offset = luaL_checkinteger(L, 2); + size_t size = luaL_checkinteger(L, 3); + + int sockfd = lua_tointeger(L, lua_upvalueindex(1)); + + int cork = 1; + setsockopt(sockfd, SOL_TCP, TCP_CORK, &cork, sizeof(cork)); + +#ifdef __UCLIBC__ + // uclibc is teh sux, it does not implement sendfile correctly + char tmp[1024]; + size_t c, toread = size, oldpos = ftell(*fp); + + fseek(*fp, offset, SEEK_SET); + + while(toread > 0 && (c = fread(tmp, 1, (toread < 1024) ? toread : 1024, *fp)) > 0) + { + size += c; + toread -= c; + write(sockfd, tmp, c); + } + + fseek(*fp, oldpos, SEEK_SET); +#else + size = sendfile(sockfd, fileno(*fp), &offset, size); + /*log_printf("%s:%s[%d] %d %d - %d\n", __FILE__, __func__, __LINE__, sockfd, fileno(*fp), size);*/ +#endif + + cork = 0; + setsockopt(sockfd, SOL_TCP, TCP_CORK, &cork, sizeof(cork)); + + if (size < 1) { + lua_pushnil(L); + lua_pushinteger(L, errno); + } else { + lua_pushinteger(L, size); + lua_pushinteger(L, offset); + } + + return 2; +} + + +static void load_luci(const char *plugindir) +{ + luaplugin_init(&ctx, plugindir); + luaplugin_scan(&ctx); + + list_for_each_entry(e, &ctx.entries, list) + { + lua_pushstring(ctx.L, "initialize"); + luaplugin_call(e, 0); + } + + list_for_each_entry(e, &ctx.entries, list) + { + lua_pushstring(ctx.L, "register"); + luaplugin_call(e, 0); + } + + list_for_each_entry(e, &ctx.entries, list) + { + lua_pushstring(ctx.L, "filter"); + luaplugin_call(e, 0); + } +} + +static void run_luci(int sockfd) +{ + lua_pushinteger(ctx.L, sockfd); + lua_pushcclosure(ctx.L, webuci_read, 1); + lua_setfield(ctx.L, LUA_GLOBALSINDEX, "webuci_read"); + + lua_pushinteger(ctx.L, sockfd); + lua_pushcclosure(ctx.L, webuci_write, 1); + lua_setfield(ctx.L, LUA_GLOBALSINDEX, "webuci_write"); + + lua_pushinteger(ctx.L, sockfd); + lua_pushcclosure(ctx.L, webuci_close, 1); + lua_setfield(ctx.L, LUA_GLOBALSINDEX, "webuci_close"); + + lua_pushinteger(ctx.L, sockfd); + lua_pushcclosure(ctx.L, webuci_sendfile, 1); + lua_setfield(ctx.L, LUA_GLOBALSINDEX, "webuci_sendfile"); + + list_for_each_entry(e, &ctx.entries, list) + { + lua_pushstring(ctx.L, "accept"); + luaplugin_call(e, 0); + } +} + +static void cleanup_luci(void) +{ + luaplugin_done(&ctx); +} + +int main(int argc, char **argv) +{ + int sockfd, new_fd; + struct sockaddr_storage their_addr; + socklen_t sin_size; + int yes = 1; + struct sockaddr_in myaddr; + + log_start(1); + + load_config(); + + setup_signals(); + + /* used by sdk to override plugin dir */ + if(argc != 2) + { + load_luci(plugin_path); + } else { + load_luci(argv[1]); + port = 8080; + } + + myaddr.sin_family = AF_INET; + myaddr.sin_port = htons(port); + //inet_pton(AF_INET, "63.161.169.137", &myaddr.sin_addr.s_addr); + myaddr.sin_addr.s_addr = INADDR_ANY; + + sockfd = socket(PF_INET, SOCK_STREAM, 0); + + if(sockfd == -1) + { + perror("server: socket"); + exit(1); + } + + if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) + { + perror("setsockopt"); + exit(1); + } + + if(bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) == -1) + { + close(sockfd); + perror("server: bind"); + exit(1); + } + + if(listen(sockfd, BACKLOG) == -1) + { + perror("listen"); + exit(1); + } + + /*log_printf("server: waiting for connections...\n");*/ + + while(1) + { + sin_size = sizeof their_addr; + new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); + if(new_fd == -1) + { + perror("accept"); + continue; + } + + /*inet_ntop(their_addr.ss_family, + (void*)&((struct sockaddr_in*)&their_addr)->sin_addr, s, sizeof s); + log_printf("server: got connection from %s\n", s);*/ + + if(!fork()) + { + /* child */ + close(sockfd); + + setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(new_fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + + run_luci(new_fd); + cleanup_luci(); + close(new_fd); + + exit(0); + } + close(new_fd); + } + + return 0; +} |