lua.c
changeset 22 822094314703
child 32 6dc2d52e4b13
--- /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 <mahlon@martini.nu>
+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 );
+}
+