diff -r 3510b50c6694 -r 822094314703 lua.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lua.c Fri May 04 08:33:21 2012 -0700 @@ -0,0 +1,173 @@ +/* vim: set noet nosta sw=4 ts=4 ft=c : */ +/* +Copyright (c) 2011-2012, Mahlon E. Smith +All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Mahlon E. Smith nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "volta.h" +#include "lua.h" + +/* + * Emit a lua error if a variable is declared without the use of 'local'. + * Since we only start up one lua interpreter, we want to ensure the + * global namespace isn't polluted by random scripts. + * + */ +int +luaV_globalindex( lua_State *lua ) +{ + return luaL_error( lua, "attempt to set global var '%s' (use local!)", lua_tostring(lua, -2) ); +} + + +/* + * Override the regular lua print() function with one that formats the string + * in the same fashion as the rest of volta output -- and goes to stderr instead + * of stdout, which would just confuse squid. + * + */ +int +luaV_print( lua_State *lua ) +{ + lua_Debug info; + int i = 0; + + /* get the file and line number print() was called from. */ + lua_getstack( lua, 1, &info ); + lua_getinfo( lua, "Sl", &info ); + + /* output each argument */ + for( i = 1; i <= lua_gettop( lua ); i++ ) + debug( 2, info.short_src, info.currentline, "%s\n", lua_tostring(lua, i) ); + + return( 1 ); +} + + +/* + * Create the initial lua interpreter, and configure the default environment. + * + */ +lua_State * +luaV_setup( void ) +{ + lua_State *lua = luaL_newstate(); + luaL_openlibs( lua ); /* include lua standard libraries */ + + /* Predeclare the request table. */ + lua_pushstring( lua, "request" ); + lua_createtable( lua, 0, 7 ); + lua_settable( lua, LUA_GLOBALSINDEX ); + + /* Predeclare a table for shared data */ + lua_pushstring( lua, "shared" ); + lua_newtable( lua ); + lua_settable( lua, LUA_GLOBALSINDEX ); + + /* replace the lua print() function with one that calls debug() instead */ + lua_register( lua, "print", luaV_print ); + + /* Restrict additional globals. */ + lua_createtable( lua, 0, 1); + lua_pushcfunction( lua, luaV_globalindex ); + lua_setfield( lua, -2, "__newindex"); + lua_pushboolean( lua, 0 ); + lua_setfield( lua, -2, "__metatable"); + lua_setmetatable( lua, LUA_GLOBALSINDEX ); + + lua_settop( lua, 0 ); /* wipe the stack */ + return( lua ); +} + + +/* + * Convert the request struct into a lua table, and inject it into the interpreter. + * + */ +void +luaV_setup_request( parsed *request ) +{ + lua_getfield( v.lua, LUA_GLOBALSINDEX, "request" ); + lua_pushstring( v.lua, request->scheme ); + lua_setfield( v.lua, 1, "scheme" ); + lua_pushstring( v.lua, request->host ); + lua_setfield( v.lua, 1, "host" ); + lua_pushstring( v.lua, request->path ); + lua_setfield( v.lua, 1, "path" ); + lua_pushstring( v.lua, request->port ); + lua_setfield( v.lua, 1, "port" ); + lua_pushstring( v.lua, request->method ); + lua_setfield( v.lua, 1, "method" ); + lua_pushstring( v.lua, request->client_ip ); + lua_setfield( v.lua, 1, "client_ip" ); + lua_pushstring( v.lua, request->tld ); + lua_setfield( v.lua, 1, "domain" ); + + return; +} + + +/* + * Given a request struct and a path to a lua script (or bytecode), + * execute the script within the global lua interpreter, and return + * a pointer to the string it generated (or NULL). + * + */ +char * +luaV_run( parsed *request, char *path ) +{ + int lua_err = 0; + + /* provide access to the request struct */ + luaV_setup_request( request ); + + /* suck in the lua chunk(s) */ + debug( 4, LOC, "Loading Lua code from '%s'\n", path ); + lua_err = luaL_loadfile( v.lua, path ); + if ( lua_err ) { + debug( 2, LOC, "Unable to run lua rule: %s\n", lua_tostring(v.lua, -1) ); + lua_settop( v.lua, 0 ); + return( NULL ); + } + + /* execute the lua, expecting one value to be returned. */ + lua_err = lua_pcall( v.lua, 0, 1, 0 ); + if ( lua_err ) { + debug( 2, LOC, "Unable to run lua rule: %s\n", lua_tostring(v.lua, -1) ); + lua_settop( v.lua, 0 ); + return( NULL ); + } + + /* get the last element in the stack, which should be the script's return value. */ + char *rewrite = (char *)lua_tostring( v.lua, -1 ); + + debug( 5, LOC, "Lua is currently consuming %dKB of memory\n", lua_gc(v.lua, LUA_GCCOUNT, 0) ); + lua_settop( v.lua, 0 ); /* reset the stack. */ + + return( rewrite ); +} +