APLOG_USE_MODULE(lua);
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *ap_lua_mutex;
+
+void ap_lua_init_mutex(apr_pool_t *pool, server_rec *s)
+{
+ apr_thread_mutex_create(&ap_lua_mutex, APR_THREAD_MUTEX_DEFAULT, pool);
+}
+#endif
+
/* forward dec'l from this file */
#if 0
makeintegerfield(L, AUTHZ_NEUTRAL);
makeintegerfield(L, AUTHZ_GENERAL_ERROR);
makeintegerfield(L, AUTHZ_DENIED_NO_USER);
-
+
/*
makeintegerfield(L, HTTP_CONTINUE);
makeintegerfield(L, HTTP_SWITCHING_PROTOCOLS);
return APR_SUCCESS;
}
+static apr_status_t server_cleanup_lua(void *resource)
+{
+ 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",
return APR_SUCCESS;
}
+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 = spec->bytecode ? apr_pstrdup(pool, spec->bytecode) : 0;
+ copied_spec->cb = spec->cb;
+ copied_spec->cb_arg = NULL;
+ copied_spec->file = spec->file ? apr_pstrdup(pool, spec->file) : 0;
+ 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));
+ 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.
ap_lua_vm_spec *spec, request_rec* r)
{
lua_State *L = NULL;
+ ap_lua_finfo *cache_info;
int tryCache = 0;
- if (apr_pool_userdata_get((void **)&L, spec->file,
- lifecycle_pool) == APR_SUCCESS) {
-
- if(L==NULL) {
+
+ 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);
+ 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_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;
+ }
+ }
+#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);
+ "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,
+ 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 {
- ap_lua_finfo *cache_info;
- char* mkey = apr_psprintf(r->pool, "ap_lua_modified:%s", spec->file);
- if (apr_pool_userdata_get((void **)&cache_info, mkey,
- lifecycle_pool) == APR_SUCCESS) {
+ 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));
}
- 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;
+ }
+ 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->runs++;
+ 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;
}
- else {
- tryCache = 1;
+ cache_info->runs++;
+ if (spec->scope != AP_LUA_SCOPE_SERVER) {
+ apr_pool_userdata_set((void*) cache_info, mkey, NULL, lifecycle_pool);
}
- apr_pool_userdata_set((void*) cache_info, mkey, NULL, lifecycle_pool);
}
if (tryCache == 0 && spec->scope != AP_LUA_SCOPE_ONCE) {
int rc;
const char *lua_response;
r->status = HTTP_INTERNAL_SERVER_ERROR;
r->content_type = "text/html";
-
ap_rputs("<b>Error!</b>\n", r);
ap_rputs("<p>", r);
lua_response = lua_tostring(L, -1);
}
}
+static void ap_lua_release_state(lua_State* L, ap_lua_vm_spec* spec, request_rec* r) {
+ char *hash;
+ apr_reslist_t* reslist = NULL;
+ if (spec->scope == AP_LUA_SCOPE_SERVER) {
+ ap_lua_server_spec* sspec = NULL;
+ lua_settop(L, 0);
+ lua_getfield(L, LUA_REGISTRYINDEX, "Apache2.Lua.server_spec");
+ sspec = (ap_lua_server_spec*) lua_touserdata(L, 1);
+ hash = apr_psprintf(r->pool, "reslist:%s", spec->file);
+ if (apr_pool_userdata_get((void **)&reslist, hash,
+ r->server->process->pool) == APR_SUCCESS) {
+ AP_DEBUG_ASSERT(sspec != NULL);
+ if (reslist != NULL) {
+ apr_reslist_release(reslist, sspec);
+ }
+ }
+ }
+}
+
+#if APR_HAS_THREADS
+extern void ap_lua_init_mutex(apr_pool_t *pool, server_rec *s);
+#endif
+
static ap_lua_vm_spec *create_vm_spec(apr_pool_t **lifecycle_pool,
request_rec *r,
const ap_lua_dir_cfg *cfg,
spec->bytecode = bytecode;
spec->bytecode_len = bytecode_len;
spec->codecache = (cfg->codecache == AP_LUA_CACHE_UNSET) ? AP_LUA_CACHE_STAT : cfg->codecache;
-
+ spec->vm_min = cfg->vm_min ? cfg->vm_min : 1;
+ spec->vm_max = cfg->vm_max ? cfg->vm_max : 1;
+
if (filename) {
char *file;
apr_filepath_merge(&file, server_cfg->root_path,
case AP_LUA_SCOPE_THREAD:
pool = apr_thread_pool_get(r->connection->current_thread);
break;
+ case AP_LUA_SCOPE_SERVER:
+ pool = r->server->process->pool;
+ break;
#endif
default:
ap_assert(0);
/* TODO annotate spec with failure reason */
r->status = HTTP_INTERNAL_SERVER_ERROR;
ap_rputs("Unable to compile VM, see logs", r);
+ ap_lua_release_state(L, spec, r);
return HTTP_INTERNAL_SERVER_ERROR;
}
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, APLOGNO(01474) "got a vm!");
"lua: Unable to find function %s in %s",
"handle",
spec->file);
+ ap_lua_release_state(L, spec, r);
return HTTP_INTERNAL_SERVER_ERROR;
}
ap_lua_run_lua_request(L, r);
if (lua_pcall(L, 1, 0, 0)) {
report_lua_error(L, r);
}
+ ap_lua_release_state(L, spec, r);
}
return OK;
}
"lua: Unable to find function %s in %s",
hook_spec->function_name,
hook_spec->file_name);
+ ap_lua_release_state(L, spec, r);
return HTTP_INTERNAL_SERVER_ERROR;
}
if (lua_pcall(L, 1, 1, 0)) {
report_lua_error(L, r);
+ ap_lua_release_state(L, spec, r);
return HTTP_INTERNAL_SERVER_ERROR;
}
rc = DECLINED;
rc = lua_tointeger(L, -1);
}
if (rc != DECLINED) {
+ ap_lua_release_state(L, spec, r);
return rc;
}
}
ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01477)
"lua: Failed to obtain lua interpreter for %s %s",
function_name, filename);
+ ap_lua_release_state(L, spec, r);
return HTTP_INTERNAL_SERVER_ERROR;
}
"lua: Unable to find function %s in %s",
function_name,
filename);
+ ap_lua_release_state(L, spec, r);
return HTTP_INTERNAL_SERVER_ERROR;
}
if (lua_pcall(L, 1, 1, 0)) {
report_lua_error(L, r);
+ ap_lua_release_state(L, spec, r);
return HTTP_INTERNAL_SERVER_ERROR;
}
rc = DECLINED;
rc = lua_tointeger(L, -1);
}
if (rc != DECLINED) {
+ ap_lua_release_state(L, spec, r);
return rc;
}
}
#endif
cfg->vm_scope = AP_LUA_SCOPE_THREAD;
}
+ else if (strcmp("server", scope) == 0) {
+ unsigned int vmin, vmax;
+#if !APR_HAS_THREADS
+ return apr_psprintf(cmd->pool,
+ "Scope type of '%s' cannot be used because this "
+ "server does not have threading support "
+ "(APR_HAS_THREADS)"
+ scope);
+#endif
+ cfg->vm_scope = AP_LUA_SCOPE_SERVER;
+ vmin = min ? atoi(min) : 1;
+ vmax = max ? atoi(max) : 1;
+ if (vmin == 0) {
+ vmin = 1;
+ }
+ if (vmax < vmin) {
+ vmax = vmin;
+ }
+ cfg->vm_min = vmin;
+ cfg->vm_max = vmax;
+ }
else {
return apr_psprintf(cmd->pool,
"Invalid value for LuaScope, '%s', acceptable "
- "values are: 'once', 'request', 'conn', 'server'"
+ "values are: 'once', 'request', 'conn'"
#if APR_HAS_THREADS
- ", 'thread'"
+ ", 'thread', 'server'"
#endif
,scope);
}
ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02319)
"Unable to find function %s in %s",
prov_spec->function_name, prov_spec->file_name);
+ ap_lua_release_state(L, spec, r);
return AUTHZ_GENERAL_ERROR;
}
ap_lua_run_lua_request(L, r);
if (!lua_checkstack(L, prov_spec->args->nelts)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02315)
"Error: authz provider %s: too many arguments", prov_spec->name);
+ ap_lua_release_state(L, spec, r);
return AUTHZ_GENERAL_ERROR;
}
for (i = 0; i < prov_spec->args->nelts; i++) {
const char *err = lua_tostring(L, -1);
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02316)
"Error executing authz provider %s: %s", prov_spec->name, err);
+ ap_lua_release_state(L, spec, r);
return AUTHZ_GENERAL_ERROR;
}
if (!lua_isnumber(L, -1)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02317)
"Error: authz provider %s did not return integer", prov_spec->name);
+ ap_lua_release_state(L, spec, r);
return AUTHZ_GENERAL_ERROR;
}
result = lua_tointeger(L, -1);
+ ap_lua_release_state(L, spec, r);
switch (result) {
case AUTHZ_DENIED:
case AUTHZ_GRANTED:
cfg->dir = apr_pstrdup(p, dir);
cfg->vm_scope = AP_LUA_SCOPE_UNSET;
cfg->codecache = AP_LUA_CACHE_UNSET;
+ cfg->vm_min = 0;
+ cfg->vm_max = 0;
return cfg;
}
a->vm_scope = (overrides->vm_scope == AP_LUA_SCOPE_UNSET) ? base->vm_scope: overrides->vm_scope;
a->inherit = (overrides->inherit== AP_LUA_INHERIT_UNSET) ? base->inherit : overrides->inherit;
a->codecache = (overrides->codecache== AP_LUA_CACHE_UNSET) ? base->codecache : overrides->codecache;
+
+ a->vm_min = (overrides->vm_min== 0) ? base->vm_min : overrides->vm_min;
+ a->vm_max = (overrides->vm_max== 0) ? base->vm_max : overrides->vm_max;
if (a->inherit == AP_LUA_INHERIT_UNSET || a->inherit == AP_LUA_INHERIT_PARENT_FIRST) {
a->package_paths = apr_array_append(p, base->package_paths, overrides->package_paths);
APR_OPTIONAL_HOOK(ap_lua, lua_request, lua_request_hook, NULL, NULL,
APR_HOOK_REALLY_FIRST);
ap_hook_handler(lua_map_handler, NULL, NULL, AP_LUA_HOOK_FIRST);
+#if APR_HAS_THREADS
+ ap_hook_child_init(ap_lua_init_mutex, NULL, NULL, APR_HOOK_MIDDLE);
+#endif
/* providers */
lua_authz_providers = apr_hash_make(p);
}