From d86a5255e4d2d7dc85779b01c62397472420fcfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Sun, 1 Nov 2009 17:23:21 +0100 Subject: [PATCH] Add mod_lua --- src/CMakeLists.txt | 4 + src/modules/Makefile.am | 7 ++ src/modules/mod_lua.c | 242 ++++++++++++++++++++++++++++++++++++++++ src/modules/wscript | 2 + 4 files changed, 255 insertions(+) create mode 100644 src/modules/mod_lua.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 99cfa28..e2e0256 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -312,6 +312,10 @@ IF(HAVE_ZLIB OR HAVE_BZIP) ADD_TARGET_PROPERTIES(mod_deflate COMPILE_FLAGS ${BZIP_CFLAGS} ${ZLIB_CFLAGS}) ENDIF(HAVE_ZLIB OR HAVE_BZIP) +IF(WITH_LUA) + ADD_AND_INSTALL_LIBRARY(mod_lua "modules/mod_lua.c") +ENDIF(WITH_LUA) + IF(HAVE_LIBSSL AND HAVE_LIBCRYPTO) ADD_AND_INSTALL_LIBRARY(mod_openssl "modules/mod_openssl.c") TARGET_LINK_LIBRARIES(mod_openssl ssl) diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index aed18ff..6bac9d4 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -65,6 +65,13 @@ libmod_fortune_la_SOURCES = mod_fortune.c libmod_fortune_la_LDFLAGS = $(common_ldflags) libmod_fortune_la_LIBADD = $(common_libadd) +if USE_LUA +install_libs += libmod_lua.la +libmod_lua_la_SOURCES = mod_lua.c +libmod_lua_la_LDFLAGS = $(common_ldflags) +libmod_lua_la_LIBADD = $(common_libadd) +endif + if USE_OPENSSL install_libs += libmod_openssl.la libmod_openssl_la_SOURCES = mod_openssl.c diff --git a/src/modules/mod_lua.c b/src/modules/mod_lua.c new file mode 100644 index 0000000..1506e23 --- /dev/null +++ b/src/modules/mod_lua.c @@ -0,0 +1,242 @@ +/* + * mod_lua - include lua actions + * + * Description: + * mod_lua + * + * Setups: + * none + * Options: + * none + * Actions: + * lua.handler filename, [ "ttl": 300 ] + * - Basically the same as include_lua, but loads the script in a worker + * specific lua_State, so it doesn't use the server wide lua lock. + * - You can give a ttl, after which the file is checked for modifications + * and reloaded. The default value 0 disables reloading. + * + * Example config: + * lua.handler "/etc/lighttpd/pathrewrite.lua"; + * + * Todo: + * - Support lua plugins (new action/setup callbacks, plugin hooks) + * + * Author: + * Copyright (c) 2009 Stefan Bühler + * License: + * MIT, see COPYING file in the lighttpd 2 tree + */ + +#include + +#include +#include + +LI_API gboolean mod_lua_init(liModules *mods, liModule *mod); +LI_API gboolean mod_lua_free(liModules *mods, liModule *mod); + +typedef struct lua_worker_config lua_worker_config; +struct lua_worker_config { + liAction *act; + time_t ts_loaded; +}; + +typedef struct lua_config lua_config; +struct lua_config { + GString *filename; + guint ttl; + + gint initialized; /* 0: not initialized, 1: initialized, 2: initializing */ + lua_worker_config *worker_config; +}; + +static void lua_config_check_init(liServer *srv, lua_config *conf) { + gint i; + while (1 != (i = g_atomic_int_get(&conf->initialized))) { + if (i != 0) { + ev_sleep(0.01); + } else if (g_atomic_int_compare_and_exchange(&conf->initialized, 0, 2)) { + conf->worker_config = g_slice_alloc0(sizeof(lua_worker_config) * srv->worker_count); + g_atomic_int_set(&conf->initialized, 1); + return; + } + } +} + +static liHandlerResult lua_handle(liVRequest *vr, gpointer param, gpointer *context) { + lua_config *conf = (lua_config*) param; + lua_worker_config *wc; + gboolean timeout = FALSE; + liHandlerResult res; + UNUSED(context); + + lua_config_check_init(vr->wrk->srv, conf); + + wc = &conf->worker_config[vr->wrk->ndx]; + + if (wc->act) timeout = (conf->ttl > 0 && wc->ts_loaded + conf->ttl >= CUR_TS(vr->wrk)); + + if (!wc->act || timeout) { + int err; + struct stat st; + + res = li_stat_cache_get(vr, conf->filename, &st, &err, NULL); + switch (res) { + case LI_HANDLER_ERROR: + VR_ERROR(vr, "lua.handler: couldn't stat file '%s': %s", conf->filename->str, g_strerror(err)); + return LI_HANDLER_ERROR; + case LI_HANDLER_WAIT_FOR_EVENT: + return LI_HANDLER_WAIT_FOR_EVENT; + default: + break; + } + + if (timeout && st.st_mtime <= wc->ts_loaded) { + wc->ts_loaded = CUR_TS(vr->wrk); + goto loaded; + } + + li_action_release(vr->wrk->srv, wc->act); + wc->act = NULL; + if (!li_config_lua_load(vr->wrk->L, vr->wrk->srv, conf->filename->str, &wc->act, FALSE) || !wc->act) { + VR_ERROR(vr, "lua.handler: couldn't load '%s'", conf->filename->str); + return LI_HANDLER_ERROR; + } + } + +loaded: + li_action_enter(vr, wc->act); + + return LI_HANDLER_GO_ON; +} + + +static void lua_config_free(liServer *srv, gpointer param) { + lua_config *conf = (lua_config*) param; + UNUSED(srv); + + if (conf->worker_config) { + lua_worker_config *wc = conf->worker_config; + guint i; + for (i = 0; i < srv->worker_count; i++) { + li_action_release(srv, wc[i].act); + } + g_slice_free1(sizeof(lua_worker_config) * srv->worker_count, wc); + } + + g_slice_free(lua_config, conf); +} + +static lua_config* lua_config_new(GString *filename, guint ttl) { + lua_config *conf = g_slice_new0(lua_config); + conf->filename = filename; + conf->ttl = ttl; + + return conf; +} + +static const GString /* lua option names */ + lon_ttl = { CONST_STR_LEN("ttl"), 0 } +; + +static liAction* lua_handler_create(liServer *srv, liPlugin* p, liValue *val) { + liValue *v_filename = NULL, *v_options = NULL; + lua_config *conf; + guint ttl = 0; + UNUSED(srv); UNUSED(p); + + if (val) { + if (val->type == LI_VALUE_STRING) { + v_filename = val; + } else if (val->type == LI_VALUE_LIST) { + GArray *l = val->data.list; + if (l->len > 0) v_filename = g_array_index(l, liValue*, 0); + if (l->len > 1) v_options = g_array_index(l, liValue*, 1); + } + } + + if (v_filename && v_filename->type != LI_VALUE_STRING) { + v_filename = NULL; + } + + if (!v_filename) { + ERROR(srv, "%s", "lua.handler expects at least a filename, or a filename and some options"); + return NULL; + } + if (v_options && v_options->type != LI_VALUE_HASH) { + ERROR(srv, "%s", "lua.handler expects options in a hash"); + return NULL; + } + + if (v_options) { + GHashTable *ht = v_options->data.hash; + GHashTableIter it; + gpointer pkey, pvalue; + + g_hash_table_iter_init(&it, ht); + while (g_hash_table_iter_next(&it, &pkey, &pvalue)) { + GString *key = pkey; + liValue *value = pvalue; + + if (g_string_equal(key, &lon_ttl)) { + if (value->type != LI_VALUE_NUMBER || value->data.number <= 0) { + ERROR(srv, "lua.handler option '%s' expects positive integer as parameter", lon_ttl.str); + goto option_failed; + } + ttl = value->data.number; + } else { + ERROR(srv, "unknown option for lua.handler '%s'", key->str); + goto option_failed; + } + } + } + + conf = lua_config_new(li_value_extract_ptr(v_filename), ttl); + + return li_action_new_function(lua_handle, NULL, lua_config_free, conf); + +option_failed: + return NULL; +} + + +static const liPluginOption options[] = { + { NULL, 0, NULL, NULL, NULL } +}; + +static const liPluginAction actions[] = { + { "lua.handler", lua_handler_create }, + + { NULL, NULL } +}; + +static const liPluginSetup setups[] = { + { NULL, NULL } +}; + + +static void plugin_lua_init(liServer *srv, liPlugin *p) { + UNUSED(srv); + + p->options = options; + p->actions = actions; + p->setups = setups; +} + + +gboolean mod_lua_init(liModules *mods, liModule *mod) { + UNUSED(mod); + + MODULE_VERSION_CHECK(mods); + + mod->config = li_plugin_register(mods->main, "mod_lua", plugin_lua_init); + + return mod->config != NULL; +} + +gboolean mod_lua_free(liModules *mods, liModule *mod) { + if (mod->config) + li_plugin_free(mods->main, mod->config); + + return TRUE; +} diff --git a/src/modules/wscript b/src/modules/wscript index 71134bc..0b7c447 100644 --- a/src/modules/wscript +++ b/src/modules/wscript @@ -47,6 +47,8 @@ def build(bld): lighty_mod(bld, 'mod_expire', 'mod_expire.c') lighty_mod(bld, 'mod_fastcgi', 'mod_fastcgi.c') lighty_mod(bld, 'mod_fortune', 'mod_fortune.c') + if bld.env['USE_LUA'] == 1: + lighty_mod(bld, 'mod_lua', 'mod_lua.c') if env['USE_OPENSSL'] == 1: uselib = ['ssl','crypto'] lighty_mod(bld, 'mod_openssl', 'mod_openssl.c', uselib)