| /** |
| * 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" |
| |
| APLOG_USE_MODULE(lua); |
| |
| /* 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, "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, |
| "%d: '%s'", i, lua_tostring(L, i)); |
| break; |
| } |
| case LUA_TUSERDATA:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, "%d: userdata", i); |
| break; |
| } |
| case LUA_TLIGHTUSERDATA:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, "%d: lightuserdata", |
| i); |
| break; |
| } |
| case LUA_TNIL:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, "%d: NIL", i); |
| break; |
| } |
| case LUA_TNONE:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, "%d: None", i); |
| break; |
| } |
| case LUA_TBOOLEAN:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, |
| "%d: %s", i, lua_toboolean(L, |
| i) ? "true" : |
| "false"); |
| break; |
| } |
| case LUA_TNUMBER:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, |
| "%d: %g", i, lua_tonumber(L, i)); |
| break; |
| } |
| case LUA_TTABLE:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, "%d: <table>", i); |
| break; |
| } |
| case LUA_TTHREAD:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, "%d: <thread>", i); |
| break; |
| } |
| case LUA_TFUNCTION:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, "%d: <function>", i); |
| break; |
| } |
| default:{ |
| ap_log_perror(APLOG_MARK, level, 0, r, |
| "%d: unkown: [%s]", i, lua_typename(L, i)); |
| break; |
| } |
| } |
| } |
| } |
| #endif |
| |
| /* BEGIN modules*/ |
| |
| /* BEGIN apache lmodule */ |
| |
| AP_LUA_DECLARE(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_pushinteger(L, OK); |
| lua_setfield(L, -2, "OK"); |
| |
| lua_pushinteger(L, DECLINED); |
| lua_setfield(L, -2, "DECLINED"); |
| |
| lua_pushinteger(L, DONE); |
| lua_setfield(L, -2, "DONE"); |
| |
| lua_pushstring(L, ap_get_server_banner()); |
| lua_setfield(L, -2, "version"); |
| |
| lua_pushinteger(L, HTTP_MOVED_TEMPORARILY); |
| lua_setfield(L, -2, "HTTP_MOVED_TEMPORARILY"); |
| |
| lua_pushinteger(L, PROXYREQ_NONE); |
| lua_setfield(L, -2, "PROXYREQ_NONE"); |
| |
| lua_pushinteger(L, PROXYREQ_PROXY); |
| lua_setfield(L, -2, "PROXYREQ_PROXY"); |
| |
| lua_pushinteger(L, PROXYREQ_REVERSE); |
| lua_setfield(L, -2, "PROXYREQ_REVERSE"); |
| |
| lua_pushinteger(L, PROXYREQ_RESPONSE); |
| lua_setfield(L, -2, "PROXYREQ_RESPONSE"); |
| |
| /* |
| lua_pushinteger(L, HTTP_CONTINUE); |
| lua_setfield(L, -2, "HTTP_CONTINUE"); |
| lua_pushinteger(L, HTTP_SWITCHING_PROTOCOLS); |
| lua_setfield(L, -2, "HTTP_SWITCHING_PROTOCOLS"); |
| lua_pushinteger(L, HTTP_PROCESSING); |
| lua_setfield(L, -2, "HTTP_PROCESSING"); |
| lua_pushinteger(L, HTTP_OK); |
| lua_setfield(L, -2, "HTTP_OK"); |
| lua_pushinteger(L, HTTP_CREATED); |
| lua_setfield(L, -2, "HTTP_CREATED"); |
| lua_pushinteger(L, HTTP_ACCEPTED); |
| lua_setfield(L, -2, "HTTP_ACCEPTED"); |
| lua_pushinteger(L, HTTP_NON_AUTHORITATIVE); |
| lua_setfield(L, -2, "HTTP_NON_AUTHORITATIVE"); |
| lua_pushinteger(L, HTTP_NO_CONTENT); |
| lua_setfield(L, -2, "HTTP_NO_CONTENT"); |
| lua_pushinteger(L, HTTP_RESET_CONTENT); |
| lua_setfield(L, -2, "HTTP_RESET_CONTENT"); |
| lua_pushinteger(L, HTTP_PARTIAL_CONTENT); |
| lua_setfield(L, -2, "HTTP_PARTIAL_CONTENT"); |
| lua_pushinteger(L, HTTP_MULTI_STATUS); |
| lua_setfield(L, -2, "HTTP_MULTI_STATUS"); |
| lua_pushinteger(L, HTTP_MULTIPLE_CHOICES); |
| lua_setfield(L, -2, "HTTP_MULTIPLE_CHOICES"); |
| lua_pushinteger(L, HTTP_MOVED_PERMANENTLY); |
| lua_setfield(L, -2, "HTTP_MOVED_PERMANENTLY"); |
| lua_pushinteger(L, HTTP_SEE_OTHER); |
| lua_setfield(L, -2, "HTTP_SEE_OTHER"); |
| lua_pushinteger(L, HTTP_NOT_MODIFIED); |
| lua_setfield(L, -2, "HTTP_NOT_MODIFIED"); |
| lua_pushinteger(L, HTTP_USE_PROXY); |
| lua_setfield(L, -2, "HTTP_USE_PROXY"); |
| lua_pushinteger(L, HTTP_TEMPORARY_REDIRECT); |
| lua_setfield(L, -2, "HTTP_TEMPORARY_REDIRECT"); |
| lua_pushinteger(L, HTTP_BAD_REQUEST); |
| lua_setfield(L, -2, "HTTP_BAD_REQUEST"); |
| lua_pushinteger(L, HTTP_UNAUTHORIZED); |
| lua_setfield(L, -2, "HTTP_UNAUTHORIZED"); |
| lua_pushinteger(L, HTTP_PAYMENT_REQUIRED); |
| lua_setfield(L, -2, "HTTP_PAYMENT_REQUIRED"); |
| lua_pushinteger(L, HTTP_FORBIDDEN); |
| lua_setfield(L, -2, "HTTP_FORBIDDEN"); |
| lua_pushinteger(L, HTTP_NOT_FOUND); |
| lua_setfield(L, -2, "HTTP_NOT_FOUND"); |
| lua_pushinteger(L, HTTP_METHOD_NOT_ALLOWED); |
| lua_setfield(L, -2, "HTTP_METHOD_NOT_ALLOWED"); |
| lua_pushinteger(L, HTTP_NOT_ACCEPTABLE); |
| lua_setfield(L, -2, "HTTP_NOT_ACCEPTABLE"); |
| lua_pushinteger(L, HTTP_PROXY_AUTHENTICATION_REQUIRED); |
| lua_setfield(L, -2, "HTTP_PROXY_AUTHENTICATION_REQUIRED"); |
| lua_pushinteger(L, HTTP_REQUEST_TIME_OUT); |
| lua_setfield(L, -2, "HTTP_REQUEST_TIME_OUT"); |
| lua_pushinteger(L, HTTP_CONFLICT); |
| lua_setfield(L, -2, "HTTP_CONFLICT"); |
| lua_pushinteger(L, HTTP_GONE); |
| lua_setfield(L, -2, "HTTP_GONE"); |
| lua_pushinteger(L, HTTP_LENGTH_REQUIRED); |
| lua_setfield(L, -2, "HTTP_LENGTH_REQUIRED"); |
| lua_pushinteger(L, HTTP_PRECONDITION_FAILED); |
| lua_setfield(L, -2, "HTTP_PRECONDITION_FAILED"); |
| lua_pushinteger(L, HTTP_REQUEST_ENTITY_TOO_LARGE); |
| lua_setfield(L, -2, "HTTP_REQUEST_ENTITY_TOO_LARGE"); |
| lua_pushinteger(L, HTTP_REQUEST_URI_TOO_LARGE); |
| lua_setfield(L, -2, "HTTP_REQUEST_URI_TOO_LARGE"); |
| lua_pushinteger(L, HTTP_UNSUPPORTED_MEDIA_TYPE); |
| lua_setfield(L, -2, "HTTP_UNSUPPORTED_MEDIA_TYPE"); |
| lua_pushinteger(L, HTTP_RANGE_NOT_SATISFIABLE); |
| lua_setfield(L, -2, "HTTP_RANGE_NOT_SATISFIABLE"); |
| lua_pushinteger(L, HTTP_EXPECTATION_FAILED); |
| lua_setfield(L, -2, "HTTP_EXPECTATION_FAILED"); |
| lua_pushinteger(L, HTTP_UNPROCESSABLE_ENTITY); |
| lua_setfield(L, -2, "HTTP_UNPROCESSABLE_ENTITY"); |
| lua_pushinteger(L, HTTP_LOCKED); |
| lua_setfield(L, -2, "HTTP_LOCKED"); |
| lua_pushinteger(L, HTTP_FAILED_DEPENDENCY); |
| lua_setfield(L, -2, "HTTP_FAILED_DEPENDENCY"); |
| lua_pushinteger(L, HTTP_UPGRADE_REQUIRED); |
| lua_setfield(L, -2, "HTTP_UPGRADE_REQUIRED"); |
| lua_pushinteger(L, HTTP_INTERNAL_SERVER_ERROR); |
| lua_setfield(L, -2, "HTTP_INTERNAL_SERVER_ERROR"); |
| lua_pushinteger(L, HTTP_NOT_IMPLEMENTED); |
| lua_setfield(L, -2, "HTTP_NOT_IMPLEMENTED"); |
| lua_pushinteger(L, HTTP_BAD_GATEWAY); |
| lua_setfield(L, -2, "HTTP_BAD_GATEWAY"); |
| lua_pushinteger(L, HTTP_SERVICE_UNAVAILABLE); |
| lua_setfield(L, -2, "HTTP_SERVICE_UNAVAILABLE"); |
| lua_pushinteger(L, HTTP_GATEWAY_TIME_OUT); |
| lua_setfield(L, -2, "HTTP_GATEWAY_TIME_OUT"); |
| lua_pushinteger(L, HTTP_VERSION_NOT_SUPPORTED); |
| lua_setfield(L, -2, "HTTP_VERSION_NOT_SUPPORTED"); |
| lua_pushinteger(L, HTTP_VARIANT_ALSO_VARIES); |
| lua_setfield(L, -2, "HTTP_VARIANT_ALSO_VARIES"); |
| lua_pushinteger(L, HTTP_INSUFFICIENT_STORAGE); |
| lua_setfield(L, -2, "HTTP_INSUFFICIENT_STORAGE"); |
| lua_pushinteger(L, HTTP_NOT_EXTENDED); |
| lua_setfield(L, -2, "HTTP_NOT_EXTENDED"); |
| */ |
| } |
| |
| /* 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) |
| { |
| lua_close((lua_State *) 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(void **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", "?.so", "./?.so", 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) { |
| char *err; |
| switch (rc) { |
| case LUA_ERRSYNTAX: |
| err = "syntax error"; |
| break; |
| case LUA_ERRMEM: |
| err = "memory allocation error"; |
| break; |
| case LUA_ERRFILE: |
| err = "error opening or reading file"; |
| break; |
| default: |
| err = "unknown error"; |
| break; |
| } |
| ap_log_perror(APLOG_MARK, APLOG_ERR, 0, lifecycle_pool, APLOGNO(01482) |
| "Loading lua file %s: %s", |
| spec->file, err); |
| return APR_EBADF; |
| } |
| lua_pcall(L, 0, LUA_MULTRET, 0); |
| } |
| |
| #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; |
| } |
| |
| /** |
| * Function used to create a lua_State instance bound into the web |
| * server in the appropriate scope. |
| */ |
| AP_LUA_DECLARE(lua_State*)ap_lua_get_lua_state(apr_pool_t *lifecycle_pool, |
| ap_lua_vm_spec *spec) |
| { |
| lua_State *L = NULL; |
| |
| if (apr_pool_userdata_get((void **)&L, spec->file, |
| lifecycle_pool) == APR_SUCCESS) { |
| |
| 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((void **)&L, spec, lifecycle_pool)) { |
| apr_pool_userdata_set(L, |
| spec->file, |
| cleanup_lua, |
| lifecycle_pool); |
| } |
| } |
| } |
| /*}*/ |
| |
| return L; |
| } |