summaryrefslogtreecommitdiffstats
path: root/modules/lua/lua_vmprep.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/lua/lua_vmprep.c552
1 files changed, 552 insertions, 0 deletions
diff --git a/modules/lua/lua_vmprep.c b/modules/lua/lua_vmprep.c
new file mode 100644
index 0000000..001897a
--- /dev/null
+++ b/modules/lua/lua_vmprep.c
@@ -0,0 +1,552 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "mod_lua.h"
+#include "http_log.h"
+#include "apr_uuid.h"
+#include "lua_config.h"
+#include "apr_file_info.h"
+#include "mod_auth.h"
+
+APLOG_USE_MODULE(lua);
+
+#ifndef AP_LUA_MODULE_EXT
+#if defined(NETWARE)
+#define AP_LUA_MODULE_EXT ".nlm"
+#elif defined(WIN32)
+#define AP_LUA_MODULE_EXT ".dll"
+#elif (defined(__hpux__) || defined(__hpux)) && !defined(__ia64)
+#define AP_LUA_MODULE_EXT ".sl"
+#else
+#define AP_LUA_MODULE_EXT ".so"
+#endif
+#endif
+
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *ap_lua_mutex;
+#endif
+extern apr_global_mutex_t *lua_ivm_mutex;
+
+void ap_lua_init_mutex(apr_pool_t *pool, server_rec *s)
+{
+ apr_status_t rv;
+
+ /* global IVM mutex */
+ rv = apr_global_mutex_child_init(&lua_ivm_mutex,
+ apr_global_mutex_lockfile(lua_ivm_mutex),
+ pool);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(03016)
+ "mod_lua: Failed to reopen mutex lua-ivm-shm in child");
+ exit(1); /* bah :( */
+ }
+
+ /* Server pool mutex */
+#if APR_HAS_THREADS
+ apr_thread_mutex_create(&ap_lua_mutex, APR_THREAD_MUTEX_DEFAULT, pool);
+#endif
+}
+
+/* forward dec'l from this file */
+
+#if 0
+static void pstack_dump(lua_State *L, apr_pool_t *r, int level,
+ const char *msg)
+{
+ int i;
+ int top = lua_gettop(L);
+
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03211)
+ "Lua Stack Dump: [%s]", msg);
+
+ for (i = 1; i <= top; i++) {
+ int t = lua_type(L, i);
+ switch (t) {
+ case LUA_TSTRING:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03212)
+ "%d: '%s'", i, lua_tostring(L, i));
+ break;
+ }
+ case LUA_TUSERDATA:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03213)
+ "%d: userdata", i);
+ break;
+ }
+ case LUA_TLIGHTUSERDATA:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03214)
+ "%d: lightuserdata", i);
+ break;
+ }
+ case LUA_TNIL:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03215)
+ "%d: NIL", i);
+ break;
+ }
+ case LUA_TNONE:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03216)
+ "%d: None", i);
+ break;
+ }
+ case LUA_TBOOLEAN:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03217)
+ "%d: %s",
+ i, lua_toboolean(L, i) ? "true" : "false");
+ break;
+ }
+ case LUA_TNUMBER:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03218)
+ "%d: %g", i, lua_tonumber(L, i));
+ break;
+ }
+ case LUA_TTABLE:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03219)
+ "%d: <table>", i);
+ break;
+ }
+ case LUA_TTHREAD:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03220)
+ "%d: <thread>", i);
+ break;
+ }
+ case LUA_TFUNCTION:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03221)
+ "%d: <function>", i);
+ break;
+ }
+ default:{
+ ap_log_perror(APLOG_MARK, level, 0, r, APLOGNO(03222)
+ "%d: unknown: [%s]", i, lua_typename(L, i));
+ break;
+ }
+ }
+ }
+}
+#endif
+
+/* BEGIN modules*/
+
+/* BEGIN apache lmodule */
+
+#define makeintegerfield(L, n) lua_pushinteger(L, n); lua_setfield(L, -2, #n)
+
+void ap_lua_load_apache2_lmodule(lua_State *L)
+{
+ lua_getglobal(L, "package");
+ lua_getfield(L, -1, "loaded");
+ lua_newtable(L);
+ lua_setfield(L, -2, "apache2");
+ lua_setglobal(L, "apache2");
+ lua_pop(L, 1); /* empty stack */
+
+ lua_getglobal(L, "apache2");
+
+ lua_pushstring(L, ap_get_server_banner());
+ lua_setfield(L, -2, "version");
+
+ makeintegerfield(L, OK);
+ makeintegerfield(L, DECLINED);
+ makeintegerfield(L, DONE);
+ makeintegerfield(L, HTTP_MOVED_TEMPORARILY);
+ makeintegerfield(L, PROXYREQ_NONE);
+ makeintegerfield(L, PROXYREQ_PROXY);
+ makeintegerfield(L, PROXYREQ_REVERSE);
+ makeintegerfield(L, PROXYREQ_RESPONSE);
+ makeintegerfield(L, PROXYREQ_RESPONSE);
+ makeintegerfield(L, AUTHZ_DENIED);
+ makeintegerfield(L, AUTHZ_GRANTED);
+ makeintegerfield(L, AUTHZ_NEUTRAL);
+ makeintegerfield(L, AUTHZ_GENERAL_ERROR);
+ makeintegerfield(L, AUTHZ_DENIED_NO_USER);
+
+ /*
+ makeintegerfield(L, HTTP_CONTINUE);
+ makeintegerfield(L, HTTP_SWITCHING_PROTOCOLS);
+ makeintegerfield(L, HTTP_PROCESSING);
+ makeintegerfield(L, HTTP_OK);
+ makeintegerfield(L, HTTP_CREATED);
+ makeintegerfield(L, HTTP_ACCEPTED);
+ makeintegerfield(L, HTTP_NON_AUTHORITATIVE);
+ makeintegerfield(L, HTTP_NO_CONTENT);
+ makeintegerfield(L, HTTP_RESET_CONTENT);
+ makeintegerfield(L, HTTP_PARTIAL_CONTENT);
+ makeintegerfield(L, HTTP_MULTI_STATUS);
+ makeintegerfield(L, HTTP_ALREADY_REPORTED);
+ makeintegerfield(L, HTTP_IM_USED);
+ makeintegerfield(L, HTTP_MULTIPLE_CHOICES);
+ makeintegerfield(L, HTTP_MOVED_PERMANENTLY);
+ makeintegerfield(L, HTTP_MOVED_TEMPORARILY);
+ makeintegerfield(L, HTTP_SEE_OTHER);
+ makeintegerfield(L, HTTP_NOT_MODIFIED);
+ makeintegerfield(L, HTTP_USE_PROXY);
+ makeintegerfield(L, HTTP_TEMPORARY_REDIRECT);
+ makeintegerfield(L, HTTP_PERMANENT_REDIRECT);
+ makeintegerfield(L, HTTP_BAD_REQUEST);
+ makeintegerfield(L, HTTP_UNAUTHORIZED);
+ makeintegerfield(L, HTTP_PAYMENT_REQUIRED);
+ makeintegerfield(L, HTTP_FORBIDDEN);
+ makeintegerfield(L, HTTP_NOT_FOUND);
+ makeintegerfield(L, HTTP_METHOD_NOT_ALLOWED);
+ makeintegerfield(L, HTTP_NOT_ACCEPTABLE);
+ makeintegerfield(L, HTTP_PROXY_AUTHENTICATION_REQUIRED);
+ makeintegerfield(L, HTTP_REQUEST_TIME_OUT);
+ makeintegerfield(L, HTTP_CONFLICT);
+ makeintegerfield(L, HTTP_GONE);
+ makeintegerfield(L, HTTP_LENGTH_REQUIRED);
+ makeintegerfield(L, HTTP_PRECONDITION_FAILED);
+ makeintegerfield(L, HTTP_REQUEST_ENTITY_TOO_LARGE);
+ makeintegerfield(L, HTTP_REQUEST_URI_TOO_LARGE);
+ makeintegerfield(L, HTTP_UNSUPPORTED_MEDIA_TYPE);
+ makeintegerfield(L, HTTP_RANGE_NOT_SATISFIABLE);
+ makeintegerfield(L, HTTP_EXPECTATION_FAILED);
+ makeintegerfield(L, HTTP_UNPROCESSABLE_ENTITY);
+ makeintegerfield(L, HTTP_LOCKED);
+ makeintegerfield(L, HTTP_FAILED_DEPENDENCY);
+ makeintegerfield(L, HTTP_UPGRADE_REQUIRED);
+ makeintegerfield(L, HTTP_PRECONDITION_REQUIRED);
+ makeintegerfield(L, HTTP_TOO_MANY_REQUESTS);
+ makeintegerfield(L, HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE);
+ makeintegerfield(L, HTTP_INTERNAL_SERVER_ERROR);
+ makeintegerfield(L, HTTP_NOT_IMPLEMENTED);
+ makeintegerfield(L, HTTP_BAD_GATEWAY);
+ makeintegerfield(L, HTTP_SERVICE_UNAVAILABLE);
+ makeintegerfield(L, HTTP_GATEWAY_TIME_OUT);
+ makeintegerfield(L, HTTP_VERSION_NOT_SUPPORTED);
+ makeintegerfield(L, HTTP_VARIANT_ALSO_VARIES);
+ makeintegerfield(L, HTTP_INSUFFICIENT_STORAGE);
+ makeintegerfield(L, HTTP_LOOP_DETECTED);
+ makeintegerfield(L, HTTP_NOT_EXTENDED);
+ makeintegerfield(L, HTTP_NETWORK_AUTHENTICATION_REQUIRED);
+ */
+}
+
+/* END apache2 lmodule */
+
+/* END library functions */
+
+/* callback for cleaning up a lua vm when pool is closed */
+static apr_status_t cleanup_lua(void *l)
+{
+ AP_DEBUG_ASSERT(l != NULL);
+ lua_close((lua_State *) l);
+ return APR_SUCCESS;
+}
+
+static apr_status_t server_cleanup_lua(void *resource, void *params, apr_pool_t *pool)
+{
+ ap_lua_server_spec* spec = (ap_lua_server_spec*) resource;
+ AP_DEBUG_ASSERT(spec != NULL);
+ if (spec->L != NULL) {
+ lua_close((lua_State *) spec->L);
+ }
+ return APR_SUCCESS;
+}
+
+/*
+ munge_path(L,
+ "path",
+ "?.lua",
+ "./?.lua",
+ lifecycle_pool,
+ spec->package_paths,
+ spec->file);
+*/
+/**
+ * field -> "path" or "cpath"
+ * sub_pat -> "?.lua"
+ * rep_pat -> "./?.lua"
+ * pool -> lifecycle pool for allocations
+ * paths -> things to add
+ * file -> ???
+ */
+static void munge_path(lua_State *L,
+ const char *field,
+ const char *sub_pat,
+ const char *rep_pat,
+ apr_pool_t *pool,
+ apr_array_header_t *paths,
+ const char *file)
+{
+ const char *current;
+ const char *parent_dir;
+ const char *pattern;
+ const char *modified;
+ char *part;
+
+ lua_getglobal(L, "package");
+ lua_getfield(L, -1, field);
+
+ current = lua_tostring(L, -1);
+
+ parent_dir = ap_make_dirstr_parent(pool, file);
+
+ pattern = apr_pstrcat(pool, parent_dir, sub_pat, NULL);
+
+ luaL_gsub(L, current, rep_pat, pattern);
+ lua_setfield(L, -3, field);
+ lua_getfield(L, -2, field);
+ modified = lua_tostring(L, -1);
+
+
+ lua_pop(L, 2);
+
+ part = apr_pstrcat(pool, modified, ";", apr_array_pstrcat(pool, paths, ';'),
+ NULL);
+
+ lua_pushstring(L, part);
+ lua_setfield(L, -2, field);
+ lua_pop(L, 1); /* pop "package" off the stack */
+}
+
+#ifdef AP_ENABLE_LUAJIT
+static int loadjitmodule(lua_State *L, apr_pool_t *lifecycle_pool)
+{
+ lua_getglobal(L, "require");
+ lua_pushliteral(L, "jit.");
+ lua_pushvalue(L, -3);
+ lua_concat(L, 2);
+ if (lua_pcall(L, 1, 1, 0)) {
+ const char *msg = lua_tostring(L, -1);
+ ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01480)
+ "Failed to init LuaJIT: %s", msg);
+ return 1;
+ }
+ lua_getfield(L, -1, "start");
+ lua_remove(L, -2); /* drop module table */
+ return 0;
+}
+
+#endif
+
+static apr_status_t vm_construct(lua_State **vm, void *params, apr_pool_t *lifecycle_pool)
+{
+ lua_State* L;
+
+ ap_lua_vm_spec *spec = params;
+
+ L = luaL_newstate();
+#ifdef AP_ENABLE_LUAJIT
+ luaopen_jit(L);
+#endif
+ luaL_openlibs(L);
+ if (spec->package_paths) {
+ munge_path(L,
+ "path", "?.lua", "./?.lua",
+ lifecycle_pool,
+ spec->package_paths,
+ spec->file);
+ }
+ if (spec->package_cpaths) {
+ munge_path(L,
+ "cpath", "?" AP_LUA_MODULE_EXT, "./?" AP_LUA_MODULE_EXT,
+ lifecycle_pool,
+ spec->package_cpaths,
+ spec->file);
+ }
+
+ if (spec->cb) {
+ spec->cb(L, lifecycle_pool, spec->cb_arg);
+ }
+
+
+ if (spec->bytecode && spec->bytecode_len > 0) {
+ luaL_loadbuffer(L, spec->bytecode, spec->bytecode_len, spec->file);
+ lua_pcall(L, 0, LUA_MULTRET, 0);
+ }
+ else {
+ int rc;
+ ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01481)
+ "loading lua file %s", spec->file);
+ rc = luaL_loadfile(L, spec->file);
+ if (rc != 0) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, 0, lifecycle_pool, APLOGNO(01482)
+ "Error loading %s: %s", spec->file,
+ rc == LUA_ERRMEM ? "memory allocation error"
+ : lua_tostring(L, 0));
+ return APR_EBADF;
+ }
+ if ( lua_pcall(L, 0, LUA_MULTRET, 0) == LUA_ERRRUN ) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, 0, lifecycle_pool, APLOGNO(02613)
+ "Error loading %s: %s", spec->file,
+ lua_tostring(L, -1));
+ return APR_EBADF;
+ }
+ }
+
+#ifdef AP_ENABLE_LUAJIT
+ loadjitmodule(L, lifecycle_pool);
+#endif
+ lua_pushlightuserdata(L, lifecycle_pool);
+ lua_setfield(L, LUA_REGISTRYINDEX, "Apache2.Wombat.pool");
+ *vm = L;
+
+ return APR_SUCCESS;
+}
+
+static ap_lua_vm_spec* copy_vm_spec(apr_pool_t* pool, ap_lua_vm_spec* spec)
+{
+ ap_lua_vm_spec* copied_spec = apr_pcalloc(pool, sizeof(ap_lua_vm_spec));
+ copied_spec->bytecode_len = spec->bytecode_len;
+ copied_spec->bytecode = apr_pstrdup(pool, spec->bytecode);
+ copied_spec->cb = spec->cb;
+ copied_spec->cb_arg = NULL;
+ copied_spec->file = apr_pstrdup(pool, spec->file);
+ copied_spec->package_cpaths = apr_array_copy(pool, spec->package_cpaths);
+ copied_spec->package_paths = apr_array_copy(pool, spec->package_paths);
+ copied_spec->pool = pool;
+ copied_spec->scope = AP_LUA_SCOPE_SERVER;
+ copied_spec->codecache = spec->codecache;
+ return copied_spec;
+}
+
+static apr_status_t server_vm_construct(lua_State **resource, void *params, apr_pool_t *pool)
+{
+ lua_State* L;
+ ap_lua_server_spec* spec = apr_pcalloc(pool, sizeof(ap_lua_server_spec));
+ *resource = NULL;
+ if (vm_construct(&L, params, pool) == APR_SUCCESS) {
+ spec->finfo = apr_pcalloc(pool, sizeof(ap_lua_finfo));
+ if (L != NULL) {
+ spec->L = L;
+ *resource = (void*) spec;
+ lua_pushlightuserdata(L, spec);
+ lua_setfield(L, LUA_REGISTRYINDEX, "Apache2.Lua.server_spec");
+ return APR_SUCCESS;
+ }
+ }
+ return APR_EGENERAL;
+}
+
+/**
+ * Function used to create a lua_State instance bound into the web
+ * server in the appropriate scope.
+ */
+lua_State *ap_lua_get_lua_state(apr_pool_t *lifecycle_pool,
+ ap_lua_vm_spec *spec, request_rec* r)
+{
+ lua_State *L = NULL;
+ ap_lua_finfo *cache_info = NULL;
+ int tryCache = 0;
+
+ if (spec->scope == AP_LUA_SCOPE_SERVER) {
+ char *hash;
+ apr_reslist_t* reslist = NULL;
+ ap_lua_server_spec* sspec = NULL;
+ hash = apr_psprintf(r->pool, "reslist:%s", spec->file);
+#if APR_HAS_THREADS
+ apr_thread_mutex_lock(ap_lua_mutex);
+#endif
+ if (apr_pool_userdata_get((void **)&reslist, hash,
+ r->server->process->pool) == APR_SUCCESS) {
+ if (reslist != NULL) {
+ if (apr_reslist_acquire(reslist, (void**) &sspec) == APR_SUCCESS) {
+ L = sspec->L;
+ cache_info = sspec->finfo;
+ }
+ }
+ }
+ if (L == NULL) {
+ ap_lua_vm_spec* server_spec = copy_vm_spec(r->server->process->pool, spec);
+ if (
+ apr_reslist_create(&reslist, spec->vm_min, spec->vm_max, spec->vm_max, 0,
+ (apr_reslist_constructor) server_vm_construct,
+ (apr_reslist_destructor) server_cleanup_lua,
+ server_spec, r->server->process->pool)
+ == APR_SUCCESS && reslist != NULL) {
+ apr_pool_userdata_set(reslist, hash, NULL,
+ r->server->process->pool);
+ if (apr_reslist_acquire(reslist, (void**) &sspec) == APR_SUCCESS) {
+ L = sspec->L;
+ cache_info = sspec->finfo;
+ }
+ else {
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(ap_lua_mutex);
+#endif
+ return NULL;
+ }
+ }
+ }
+#if APR_HAS_THREADS
+ apr_thread_mutex_unlock(ap_lua_mutex);
+#endif
+ }
+ else {
+ if (apr_pool_userdata_get((void **)&L, spec->file,
+ lifecycle_pool) != APR_SUCCESS) {
+ L = NULL;
+ }
+ }
+ if (L == NULL) {
+ ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01483)
+ "creating lua_State with file %s", spec->file);
+ /* not available, so create */
+
+ if (!vm_construct(&L, spec, lifecycle_pool)) {
+ AP_DEBUG_ASSERT(L != NULL);
+ apr_pool_userdata_set(L, spec->file, cleanup_lua, lifecycle_pool);
+ }
+ }
+
+ if (spec->codecache == AP_LUA_CACHE_FOREVER || (spec->bytecode && spec->bytecode_len > 0)) {
+ tryCache = 1;
+ }
+ else {
+ char* mkey;
+ if (spec->scope != AP_LUA_SCOPE_SERVER) {
+ mkey = apr_psprintf(r->pool, "ap_lua_modified:%s", spec->file);
+ apr_pool_userdata_get((void **)&cache_info, mkey, lifecycle_pool);
+ if (cache_info == NULL) {
+ cache_info = apr_pcalloc(lifecycle_pool, sizeof(ap_lua_finfo));
+ apr_pool_userdata_set((void*) cache_info, mkey, NULL, lifecycle_pool);
+ }
+ }
+ if (spec->codecache == AP_LUA_CACHE_STAT) {
+ apr_finfo_t lua_finfo;
+ apr_stat(&lua_finfo, spec->file, APR_FINFO_MTIME|APR_FINFO_SIZE, lifecycle_pool);
+
+ /* On first visit, modified will be zero, but that's fine - The file is
+ loaded in the vm_construct function.
+ */
+ if ((cache_info->modified == lua_finfo.mtime && cache_info->size == lua_finfo.size)
+ || cache_info->modified == 0) {
+ tryCache = 1;
+ }
+ cache_info->modified = lua_finfo.mtime;
+ cache_info->size = lua_finfo.size;
+ }
+ else if (spec->codecache == AP_LUA_CACHE_NEVER) {
+ if (cache_info->runs == 0)
+ tryCache = 1;
+ }
+ cache_info->runs++;
+ }
+ if (tryCache == 0 && spec->scope != AP_LUA_SCOPE_ONCE) {
+ int rc;
+ ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(02332)
+ "(re)loading lua file %s", spec->file);
+ rc = luaL_loadfile(L, spec->file);
+ if (rc != 0) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, 0, lifecycle_pool, APLOGNO(02333)
+ "Error loading %s: %s", spec->file,
+ rc == LUA_ERRMEM ? "memory allocation error"
+ : lua_tostring(L, 0));
+ return 0;
+ }
+ lua_pcall(L, 0, LUA_MULTRET, 0);
+ }
+
+ return L;
+}