1/** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17#include "mod_lua.h" 18#include "http_log.h" 19#include "apr_uuid.h" 20#include "lua_config.h" 21#include "apr_file_info.h" 22#include "mod_auth.h" 23 24APLOG_USE_MODULE(lua); 25 26#ifndef AP_LUA_MODULE_EXT 27#if defined(NETWARE) 28#define AP_LUA_MODULE_EXT ".nlm" 29#elif defined(WIN32) 30#define AP_LUA_MODULE_EXT ".dll" 31#elif (defined(__hpux__) || defined(__hpux)) && !defined(__ia64) 32#define AP_LUA_MODULE_EXT ".sl" 33#else 34#define AP_LUA_MODULE_EXT ".so" 35#endif 36#endif 37 38#if APR_HAS_THREADS 39 apr_thread_mutex_t *ap_lua_mutex; 40 41void ap_lua_init_mutex(apr_pool_t *pool, server_rec *s) 42{ 43 apr_thread_mutex_create(&ap_lua_mutex, APR_THREAD_MUTEX_DEFAULT, pool); 44} 45#endif 46 47/* forward dec'l from this file */ 48 49#if 0 50static void pstack_dump(lua_State *L, apr_pool_t *r, int level, 51 const char *msg) 52{ 53 int i; 54 int top = lua_gettop(L); 55 56 ap_log_perror(APLOG_MARK, level, 0, r, "Lua Stack Dump: [%s]", msg); 57 58 for (i = 1; i <= top; i++) { 59 int t = lua_type(L, i); 60 switch (t) { 61 case LUA_TSTRING:{ 62 ap_log_perror(APLOG_MARK, level, 0, r, 63 "%d: '%s'", i, lua_tostring(L, i)); 64 break; 65 } 66 case LUA_TUSERDATA:{ 67 ap_log_perror(APLOG_MARK, level, 0, r, "%d: userdata", i); 68 break; 69 } 70 case LUA_TLIGHTUSERDATA:{ 71 ap_log_perror(APLOG_MARK, level, 0, r, "%d: lightuserdata", 72 i); 73 break; 74 } 75 case LUA_TNIL:{ 76 ap_log_perror(APLOG_MARK, level, 0, r, "%d: NIL", i); 77 break; 78 } 79 case LUA_TNONE:{ 80 ap_log_perror(APLOG_MARK, level, 0, r, "%d: None", i); 81 break; 82 } 83 case LUA_TBOOLEAN:{ 84 ap_log_perror(APLOG_MARK, level, 0, r, 85 "%d: %s", i, lua_toboolean(L, 86 i) ? "true" : 87 "false"); 88 break; 89 } 90 case LUA_TNUMBER:{ 91 ap_log_perror(APLOG_MARK, level, 0, r, 92 "%d: %g", i, lua_tonumber(L, i)); 93 break; 94 } 95 case LUA_TTABLE:{ 96 ap_log_perror(APLOG_MARK, level, 0, r, "%d: <table>", i); 97 break; 98 } 99 case LUA_TTHREAD:{ 100 ap_log_perror(APLOG_MARK, level, 0, r, "%d: <thread>", i); 101 break; 102 } 103 case LUA_TFUNCTION:{ 104 ap_log_perror(APLOG_MARK, level, 0, r, "%d: <function>", i); 105 break; 106 } 107 default:{ 108 ap_log_perror(APLOG_MARK, level, 0, r, 109 "%d: unknown: [%s]", i, lua_typename(L, i)); 110 break; 111 } 112 } 113 } 114} 115#endif 116 117/* BEGIN modules*/ 118 119/* BEGIN apache lmodule */ 120 121#define makeintegerfield(L, n) lua_pushinteger(L, n); lua_setfield(L, -2, #n) 122 123void ap_lua_load_apache2_lmodule(lua_State *L) 124{ 125 lua_getglobal(L, "package"); 126 lua_getfield(L, -1, "loaded"); 127 lua_newtable(L); 128 lua_setfield(L, -2, "apache2"); 129 lua_setglobal(L, "apache2"); 130 lua_pop(L, 1); /* empty stack */ 131 132 lua_getglobal(L, "apache2"); 133 134 lua_pushstring(L, ap_get_server_banner()); 135 lua_setfield(L, -2, "version"); 136 137 makeintegerfield(L, OK); 138 makeintegerfield(L, DECLINED); 139 makeintegerfield(L, DONE); 140 makeintegerfield(L, HTTP_MOVED_TEMPORARILY); 141 makeintegerfield(L, PROXYREQ_NONE); 142 makeintegerfield(L, PROXYREQ_PROXY); 143 makeintegerfield(L, PROXYREQ_REVERSE); 144 makeintegerfield(L, PROXYREQ_RESPONSE); 145 makeintegerfield(L, PROXYREQ_RESPONSE); 146 makeintegerfield(L, AUTHZ_DENIED); 147 makeintegerfield(L, AUTHZ_GRANTED); 148 makeintegerfield(L, AUTHZ_NEUTRAL); 149 makeintegerfield(L, AUTHZ_GENERAL_ERROR); 150 makeintegerfield(L, AUTHZ_DENIED_NO_USER); 151 152 /* 153 makeintegerfield(L, HTTP_CONTINUE); 154 makeintegerfield(L, HTTP_SWITCHING_PROTOCOLS); 155 makeintegerfield(L, HTTP_PROCESSING); 156 makeintegerfield(L, HTTP_OK); 157 makeintegerfield(L, HTTP_CREATED); 158 makeintegerfield(L, HTTP_ACCEPTED); 159 makeintegerfield(L, HTTP_NON_AUTHORITATIVE); 160 makeintegerfield(L, HTTP_NO_CONTENT); 161 makeintegerfield(L, HTTP_RESET_CONTENT); 162 makeintegerfield(L, HTTP_PARTIAL_CONTENT); 163 makeintegerfield(L, HTTP_MULTI_STATUS); 164 makeintegerfield(L, HTTP_ALREADY_REPORTED); 165 makeintegerfield(L, HTTP_IM_USED); 166 makeintegerfield(L, HTTP_MULTIPLE_CHOICES); 167 makeintegerfield(L, HTTP_MOVED_PERMANENTLY); 168 makeintegerfield(L, HTTP_MOVED_TEMPORARILY); 169 makeintegerfield(L, HTTP_SEE_OTHER); 170 makeintegerfield(L, HTTP_NOT_MODIFIED); 171 makeintegerfield(L, HTTP_USE_PROXY); 172 makeintegerfield(L, HTTP_TEMPORARY_REDIRECT); 173 makeintegerfield(L, HTTP_PERMANENT_REDIRECT); 174 makeintegerfield(L, HTTP_BAD_REQUEST); 175 makeintegerfield(L, HTTP_UNAUTHORIZED); 176 makeintegerfield(L, HTTP_PAYMENT_REQUIRED); 177 makeintegerfield(L, HTTP_FORBIDDEN); 178 makeintegerfield(L, HTTP_NOT_FOUND); 179 makeintegerfield(L, HTTP_METHOD_NOT_ALLOWED); 180 makeintegerfield(L, HTTP_NOT_ACCEPTABLE); 181 makeintegerfield(L, HTTP_PROXY_AUTHENTICATION_REQUIRED); 182 makeintegerfield(L, HTTP_REQUEST_TIME_OUT); 183 makeintegerfield(L, HTTP_CONFLICT); 184 makeintegerfield(L, HTTP_GONE); 185 makeintegerfield(L, HTTP_LENGTH_REQUIRED); 186 makeintegerfield(L, HTTP_PRECONDITION_FAILED); 187 makeintegerfield(L, HTTP_REQUEST_ENTITY_TOO_LARGE); 188 makeintegerfield(L, HTTP_REQUEST_URI_TOO_LARGE); 189 makeintegerfield(L, HTTP_UNSUPPORTED_MEDIA_TYPE); 190 makeintegerfield(L, HTTP_RANGE_NOT_SATISFIABLE); 191 makeintegerfield(L, HTTP_EXPECTATION_FAILED); 192 makeintegerfield(L, HTTP_UNPROCESSABLE_ENTITY); 193 makeintegerfield(L, HTTP_LOCKED); 194 makeintegerfield(L, HTTP_FAILED_DEPENDENCY); 195 makeintegerfield(L, HTTP_UPGRADE_REQUIRED); 196 makeintegerfield(L, HTTP_PRECONDITION_REQUIRED); 197 makeintegerfield(L, HTTP_TOO_MANY_REQUESTS); 198 makeintegerfield(L, HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE); 199 makeintegerfield(L, HTTP_INTERNAL_SERVER_ERROR); 200 makeintegerfield(L, HTTP_NOT_IMPLEMENTED); 201 makeintegerfield(L, HTTP_BAD_GATEWAY); 202 makeintegerfield(L, HTTP_SERVICE_UNAVAILABLE); 203 makeintegerfield(L, HTTP_GATEWAY_TIME_OUT); 204 makeintegerfield(L, HTTP_VERSION_NOT_SUPPORTED); 205 makeintegerfield(L, HTTP_VARIANT_ALSO_VARIES); 206 makeintegerfield(L, HTTP_INSUFFICIENT_STORAGE); 207 makeintegerfield(L, HTTP_LOOP_DETECTED); 208 makeintegerfield(L, HTTP_NOT_EXTENDED); 209 makeintegerfield(L, HTTP_NETWORK_AUTHENTICATION_REQUIRED); 210 */ 211} 212 213/* END apache2 lmodule */ 214 215/* END library functions */ 216 217/* callback for cleaning up a lua vm when pool is closed */ 218static apr_status_t cleanup_lua(void *l) 219{ 220 AP_DEBUG_ASSERT(l != NULL); 221 lua_close((lua_State *) l); 222 return APR_SUCCESS; 223} 224 225static apr_status_t server_cleanup_lua(void *resource) 226{ 227 ap_lua_server_spec* spec = (ap_lua_server_spec*) resource; 228 AP_DEBUG_ASSERT(spec != NULL); 229 if (spec->L != NULL) { 230 lua_close((lua_State *) spec->L); 231 } 232 return APR_SUCCESS; 233} 234 235/* 236 munge_path(L, 237 "path", 238 "?.lua", 239 "./?.lua", 240 lifecycle_pool, 241 spec->package_paths, 242 spec->file); 243*/ 244/** 245 * field -> "path" or "cpath" 246 * sub_pat -> "?.lua" 247 * rep_pat -> "./?.lua" 248 * pool -> lifecycle pool for allocations 249 * paths -> things to add 250 * file -> ??? 251 */ 252static void munge_path(lua_State *L, 253 const char *field, 254 const char *sub_pat, 255 const char *rep_pat, 256 apr_pool_t *pool, 257 apr_array_header_t *paths, 258 const char *file) 259{ 260 const char *current; 261 const char *parent_dir; 262 const char *pattern; 263 const char *modified; 264 char *part; 265 266 lua_getglobal(L, "package"); 267 lua_getfield(L, -1, field); 268 269 current = lua_tostring(L, -1); 270 271 parent_dir = ap_make_dirstr_parent(pool, file); 272 273 pattern = apr_pstrcat(pool, parent_dir, sub_pat, NULL); 274 275 luaL_gsub(L, current, rep_pat, pattern); 276 lua_setfield(L, -3, field); 277 lua_getfield(L, -2, field); 278 modified = lua_tostring(L, -1); 279 280 281 lua_pop(L, 2); 282 283 part = apr_pstrcat(pool, modified, ";", apr_array_pstrcat(pool, paths, ';'), 284 NULL); 285 286 lua_pushstring(L, part); 287 lua_setfield(L, -2, field); 288 lua_pop(L, 1); /* pop "package" off the stack */ 289} 290 291#ifdef AP_ENABLE_LUAJIT 292static int loadjitmodule(lua_State *L, apr_pool_t *lifecycle_pool) { 293 lua_getglobal(L, "require"); 294 lua_pushliteral(L, "jit."); 295 lua_pushvalue(L, -3); 296 lua_concat(L, 2); 297 if (lua_pcall(L, 1, 1, 0)) { 298 const char *msg = lua_tostring(L, -1); 299 ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01480) 300 "Failed to init LuaJIT: %s", msg); 301 return 1; 302 } 303 lua_getfield(L, -1, "start"); 304 lua_remove(L, -2); /* drop module table */ 305 return 0; 306} 307 308#endif 309 310static apr_status_t vm_construct(lua_State **vm, void *params, apr_pool_t *lifecycle_pool) 311{ 312 lua_State* L; 313 314 ap_lua_vm_spec *spec = params; 315 316 L = luaL_newstate(); 317#ifdef AP_ENABLE_LUAJIT 318 luaopen_jit(L); 319#endif 320 luaL_openlibs(L); 321 if (spec->package_paths) { 322 munge_path(L, 323 "path", "?.lua", "./?.lua", 324 lifecycle_pool, 325 spec->package_paths, 326 spec->file); 327 } 328 if (spec->package_cpaths) { 329 munge_path(L, 330 "cpath", "?" AP_LUA_MODULE_EXT, "./?" AP_LUA_MODULE_EXT, 331 lifecycle_pool, 332 spec->package_cpaths, 333 spec->file); 334 } 335 336 if (spec->cb) { 337 spec->cb(L, lifecycle_pool, spec->cb_arg); 338 } 339 340 341 if (spec->bytecode && spec->bytecode_len > 0) { 342 luaL_loadbuffer(L, spec->bytecode, spec->bytecode_len, spec->file); 343 lua_pcall(L, 0, LUA_MULTRET, 0); 344 } 345 else { 346 int rc; 347 ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01481) 348 "loading lua file %s", spec->file); 349 rc = luaL_loadfile(L, spec->file); 350 if (rc != 0) { 351 ap_log_perror(APLOG_MARK, APLOG_ERR, 0, lifecycle_pool, APLOGNO(01482) 352 "Error loading %s: %s", spec->file, 353 rc == LUA_ERRMEM ? "memory allocation error" 354 : lua_tostring(L, 0)); 355 return APR_EBADF; 356 } 357 lua_pcall(L, 0, LUA_MULTRET, 0); 358 } 359 360#ifdef AP_ENABLE_LUAJIT 361 loadjitmodule(L, lifecycle_pool); 362#endif 363 lua_pushlightuserdata(L, lifecycle_pool); 364 lua_setfield(L, LUA_REGISTRYINDEX, "Apache2.Wombat.pool"); 365 *vm = L; 366 367 return APR_SUCCESS; 368} 369 370static ap_lua_vm_spec* copy_vm_spec(apr_pool_t* pool, ap_lua_vm_spec* spec) 371{ 372 ap_lua_vm_spec* copied_spec = apr_pcalloc(pool, sizeof(ap_lua_vm_spec)); 373 copied_spec->bytecode_len = spec->bytecode_len; 374 copied_spec->bytecode = apr_pstrdup(pool, spec->bytecode); 375 copied_spec->cb = spec->cb; 376 copied_spec->cb_arg = NULL; 377 copied_spec->file = apr_pstrdup(pool, spec->file); 378 copied_spec->package_cpaths = apr_array_copy(pool, spec->package_cpaths); 379 copied_spec->package_paths = apr_array_copy(pool, spec->package_paths); 380 copied_spec->pool = pool; 381 copied_spec->scope = AP_LUA_SCOPE_SERVER; 382 copied_spec->codecache = spec->codecache; 383 return copied_spec; 384} 385 386static apr_status_t server_vm_construct(lua_State **resource, void *params, apr_pool_t *pool) 387{ 388 lua_State* L; 389 ap_lua_server_spec* spec = apr_pcalloc(pool, sizeof(ap_lua_server_spec)); 390 *resource = NULL; 391 if (vm_construct(&L, params, pool) == APR_SUCCESS) { 392 spec->finfo = apr_pcalloc(pool, sizeof(ap_lua_finfo)); 393 if (L != NULL) { 394 spec->L = L; 395 *resource = (void*) spec; 396 lua_pushlightuserdata(L, spec); 397 lua_setfield(L, LUA_REGISTRYINDEX, "Apache2.Lua.server_spec"); 398 return APR_SUCCESS; 399 } 400 } 401 return APR_EGENERAL; 402} 403 404/** 405 * Function used to create a lua_State instance bound into the web 406 * server in the appropriate scope. 407 */ 408lua_State *ap_lua_get_lua_state(apr_pool_t *lifecycle_pool, 409 ap_lua_vm_spec *spec, request_rec* r) 410{ 411 lua_State *L = NULL; 412 ap_lua_finfo *cache_info = NULL; 413 int tryCache = 0; 414 415 if (spec->scope == AP_LUA_SCOPE_SERVER) { 416 char *hash; 417 apr_reslist_t* reslist = NULL; 418 ap_lua_server_spec* sspec = NULL; 419 hash = apr_psprintf(r->pool, "reslist:%s", spec->file); 420#if APR_HAS_THREADS 421 apr_thread_mutex_lock(ap_lua_mutex); 422#endif 423 if (apr_pool_userdata_get((void **)&reslist, hash, 424 r->server->process->pool) == APR_SUCCESS) { 425 if (reslist != NULL) { 426 if (apr_reslist_acquire(reslist, (void**) &sspec) == APR_SUCCESS) { 427 L = sspec->L; 428 cache_info = sspec->finfo; 429 } 430 } 431 } 432 if (L == NULL) { 433 ap_lua_vm_spec* server_spec = copy_vm_spec(r->server->process->pool, spec); 434 if ( 435 apr_reslist_create(&reslist, spec->vm_min, spec->vm_max, spec->vm_max, 0, 436 (apr_reslist_constructor) server_vm_construct, 437 (apr_reslist_destructor) server_cleanup_lua, 438 server_spec, r->server->process->pool) 439 == APR_SUCCESS && reslist != NULL) { 440 apr_pool_userdata_set(reslist, hash, NULL, 441 r->server->process->pool); 442 if (apr_reslist_acquire(reslist, (void**) &sspec) == APR_SUCCESS) { 443 L = sspec->L; 444 cache_info = sspec->finfo; 445 } 446 else { 447 return NULL; 448 } 449 } 450 } 451#if APR_HAS_THREADS 452 apr_thread_mutex_unlock(ap_lua_mutex); 453#endif 454 } 455 else { 456 if (apr_pool_userdata_get((void **)&L, spec->file, 457 lifecycle_pool) != APR_SUCCESS) { 458 L = NULL; 459 } 460 } 461 if (L == NULL) { 462 ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01483) 463 "creating lua_State with file %s", spec->file); 464 /* not available, so create */ 465 466 if (!vm_construct(&L, spec, lifecycle_pool)) { 467 AP_DEBUG_ASSERT(L != NULL); 468 apr_pool_userdata_set(L, spec->file, cleanup_lua, lifecycle_pool); 469 } 470 } 471 472 if (spec->codecache == AP_LUA_CACHE_FOREVER || (spec->bytecode && spec->bytecode_len > 0)) { 473 tryCache = 1; 474 } 475 else { 476 char* mkey; 477 if (spec->scope != AP_LUA_SCOPE_SERVER) { 478 mkey = apr_psprintf(r->pool, "ap_lua_modified:%s", spec->file); 479 apr_pool_userdata_get((void **)&cache_info, mkey, lifecycle_pool); 480 if (cache_info == NULL) { 481 cache_info = apr_pcalloc(lifecycle_pool, sizeof(ap_lua_finfo)); 482 apr_pool_userdata_set((void*) cache_info, mkey, NULL, lifecycle_pool); 483 } 484 } 485 if (spec->codecache == AP_LUA_CACHE_STAT) { 486 apr_finfo_t lua_finfo; 487 apr_stat(&lua_finfo, spec->file, APR_FINFO_MTIME|APR_FINFO_SIZE, lifecycle_pool); 488 489 /* On first visit, modified will be zero, but that's fine - The file is 490 loaded in the vm_construct function. 491 */ 492 if ((cache_info->modified == lua_finfo.mtime && cache_info->size == lua_finfo.size) 493 || cache_info->modified == 0) { 494 tryCache = 1; 495 } 496 cache_info->modified = lua_finfo.mtime; 497 cache_info->size = lua_finfo.size; 498 } 499 else if (spec->codecache == AP_LUA_CACHE_NEVER) { 500 if (cache_info->runs == 0) 501 tryCache = 1; 502 } 503 cache_info->runs++; 504 } 505 if (tryCache == 0 && spec->scope != AP_LUA_SCOPE_ONCE) { 506 int rc; 507 ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(02332) 508 "(re)loading lua file %s", spec->file); 509 rc = luaL_loadfile(L, spec->file); 510 if (rc != 0) { 511 ap_log_perror(APLOG_MARK, APLOG_ERR, 0, lifecycle_pool, APLOGNO(02333) 512 "Error loading %s: %s", spec->file, 513 rc == LUA_ERRMEM ? "memory allocation error" 514 : lua_tostring(L, 0)); 515 return 0; 516 } 517 lua_pcall(L, 0, LUA_MULTRET, 0); 518 } 519 520 return L; 521} 522