Browse Source

[core] handle differences between options/actions/setups in plugin.c

personal/stbuehler/wip
Stefan Bühler 8 years ago
parent
commit
061b2a4262
  1. 5
      include/lighttpd/config_lua.h
  2. 75
      include/lighttpd/plugin.h
  3. 2
      src/main/actions_lua.c
  4. 181
      src/main/config_lua.c
  5. 62
      src/main/config_parser.rl
  6. 343
      src/main/plugin.c
  7. 54
      src/main/plugin_core.c
  8. 37
      src/main/server.c
  9. 4
      src/modules/mod_lua.c
  10. 42
      tests/t-mod-lua.py

5
include/lighttpd/config_lua.h

@ -7,8 +7,7 @@
LI_API gboolean li_config_lua_load(liLuaState *LL, liServer *srv, liWorker *wrk, const gchar *filename, liAction **pact, gboolean allow_setup, liValue *args);
LI_API gboolean li_lua_config_publish_str_hash(liServer *srv, liWorker *wrk, lua_State *L, GHashTable *ht, int (*wrapper)(liServer *srv, liWorker *wrk, lua_State *L, gpointer data));
LI_API int li_lua_config_handle_server_action(liServer *srv, liWorker *wrk, lua_State *L, gpointer _sa);
LI_API int li_lua_config_handle_server_setup(liServer *srv, liWorker *wrk, lua_State *L, gpointer _ss);
LI_API void li_lua_push_action_table(liServer *srv, liWorker *wrk, lua_State *L);
LI_API void li_lua_push_setup_table(liServer *srv, liWorker *wrk, lua_State *L);
#endif

75
include/lighttpd/plugin.h

@ -93,71 +93,12 @@ struct liPluginAngel {
liPluginAngelCB angel_cb;
};
/* Internal structures */
struct liServerOption {
liPlugin *p;
/** the value is freed with li_value_free after the parse call, so you
* probably want to extract pointers via li_value_extract_*
* val is zero to get the global default value if nothing is specified
* save result in value
*
* Default behaviour (NULL) is to extract the inner value from val
*/
liPluginParseOptionCB parse_option;
/** if parse_option is NULL, the default_value is used */
gint64 default_value;
size_t index, module_index;
liValueType type;
};
struct liServerOptionPtr {
liPlugin *p;
/** the value is freed with li_value_free after the parse call, so you
* probably want to extract pointers via li_value_extract_*
* val is zero to get the global default value if nothing is specified
* save result in value
*
* Default behaviour (NULL) is to extract the inner value from val
*/
liPluginParseOptionPtrCB parse_option;
/** the free_option handler has to free all allocated resources;
* it may get called with 0 initialized options, so you have to
* check the value.
*/
liPluginFreeOptionPtrCB free_option;
/** if parse_option is NULL, the default_value is used; it is only used
* for the following value types:
* - STRING: used for g_string_new, i.e. a const char*
*/
gpointer default_value;
size_t index, module_index;
liValueType type;
};
struct liServerAction {
liPlugin *p;
liPluginCreateActionCB create_action;
gpointer userdata;
};
struct liServerSetup {
liPlugin *p;
liPluginSetupCB setup;
gpointer userdata;
};
/* Needed by modules to register their plugin(s) */
LI_API liPlugin *li_plugin_register(liServer *srv, const gchar *name, liPluginInitCB init, gpointer userdata);
/* Internal needed functions */
LI_API void li_plugin_free(liServer *srv, liPlugin *p);
LI_API void li_server_plugins_init(liServer *srv);
LI_API void li_server_plugins_free(liServer *srv);
LI_API void li_release_optionptr(liServer *srv, liOptionPtrValue *value);
@ -179,17 +120,9 @@ LI_API void li_plugins_handle_close(liConnection *con);
LI_API void li_plugins_handle_vrclose(liVRequest *vr);
/* Needed for config frontends */
/** For parsing 'somemod.option = "somevalue"', free value after call */
LI_API liAction* li_option_action(liServer *srv, liWorker *wrk, const gchar *name, liValue *val);
/** For parsing 'somemod.action value', e.g. 'rewrite "/url" => "/destination"'
* free value after call
*/
LI_API liAction* li_create_action(liServer *srv, liWorker *wrk, const gchar *name, liValue *val);
/** For setup function, e.g. 'listen "127.0.0.1:8080"'; free value after call */
LI_API gboolean li_call_setup(liServer *srv, const char *name, liValue *val);
/** free val after call */
LI_API gboolean li_plugin_set_default_option(liServer *srv, const gchar* name, liValue *val);
/* "val" gets freed in any case */
LI_API liAction *li_plugin_config_action(liServer *srv, liWorker *wrk, const gchar *name, liValue *val);
LI_API gboolean li_plugin_config_setup(liServer *srv, const char *name, liValue *val);
LI_API void li_plugins_init_lua(liLuaState *LL, liServer *srv, liWorker *wrk);

2
src/main/actions_lua.c

@ -180,7 +180,7 @@ liAction* li_lua_make_action(lua_State *L, int ndx) {
lua_setfield(L, -2, "__index"); /* -1 */
lua_setmetatable(L, -2); /* -1 */
if (NULL != wrk) {
li_lua_config_publish_str_hash(wrk->srv, wrk, L, wrk->srv->actions, li_lua_config_handle_server_action); /* +1 */
li_lua_push_action_table(wrk->srv, wrk, L); /* +1 */
lua_setfield(L, -2, "action"); /* -1 */
}
lua_setfenv(L, -2); /* -1 */

181
src/main/config_lua.c

@ -9,13 +9,13 @@
#include <lualib.h>
#include <lauxlib.h>
typedef int (*LuaWrapper)(liServer *srv, liWorker *wrk, lua_State *L, gpointer data);
typedef int (*LuaWrapper)(liServer *srv, liWorker *wrk, lua_State *L, const char *key);
static liValue* lua_params_to_value(liServer *srv, lua_State *L) {
liValue *val, *subval;
switch (lua_gettop(L)) {
case 0:
case 1:
case 1: /* first parameter is the table the __call method is for */
return NULL;
case 2:
return li_value_from_lua(srv, L);
@ -31,117 +31,72 @@ static liValue* lua_params_to_value(liServer *srv, lua_State *L) {
return NULL;
}
/* Creates a table on the lua stack */
static void lua_push_publish_hash_metatable(liServer *srv, liWorker *wrk, lua_State *L);
static int lua_str_hash_index(lua_State *L) {
static int _lua_dynamic_hash_index_call(lua_State *L) {
liServer *srv;
liWorker *wrk;
GHashTable *ht;
LuaWrapper wrapper;
const char *key;
srv = (liServer*) lua_touserdata(L, lua_upvalueindex(1));
wrk = (liWorker*) lua_touserdata(L, lua_upvalueindex(2));
lua_pushstring(L, "__ht"); lua_rawget(L, 1);
ht = (GHashTable*) lua_touserdata(L, -1); lua_pop(L, 1);
lua_pushstring(L, "__wrapper"); lua_rawget(L, 1);
wrapper = (LuaWrapper)(intptr_t) lua_touserdata(L, -1); lua_pop(L, 1);
if (!lua_isstring(L, 2) || !ht || !wrapper) {
lua_pop(L, lua_gettop(L));
lua_pushstring(L, "lookup failed (lua_str_hash_index)");
lua_error(L);
return 0;
}
/* TRACE(srv, "str hash index: '%s'", luaL_checklstring(L, 2, NULL)); */
wrapper = (LuaWrapper)(intptr_t) lua_touserdata(L, lua_upvalueindex(3));
key = luaL_checklstring(L, lua_upvalueindex(4), NULL);
lua_newtable(L);
lua_pushlightuserdata(L, ht);
lua_setfield(L, -2, "__ht");
lua_pushlightuserdata(L, (void*)(intptr_t)wrapper);
lua_setfield(L, -2, "__wrapper");
lua_pushstring(L, "__key"); lua_rawget(L, 1);
if (lua_isstring(L, -1)) {
lua_pushstring(L, ".");
lua_pushvalue(L, 2);
lua_concat(L, 3);
} else {
lua_pop(L, 1);
lua_pushvalue(L, 2);
}
lua_setfield(L, -2, "__key");
lua_push_publish_hash_metatable(srv, wrk, L);
lua_setmetatable(L, -2);
return 1;
return wrapper(srv, wrk, L, key);
}
static int lua_str_hash_call(lua_State *L) {
liServer *srv;
liWorker *wrk;
GHashTable *ht;
LuaWrapper wrapper;
const char *key;
gpointer d;
srv = (liServer*) lua_touserdata(L, lua_upvalueindex(1));
wrk = (liWorker*) lua_touserdata(L, lua_upvalueindex(2));
lua_pushstring(L, "__ht"); lua_rawget(L, 1);
ht = (GHashTable*) lua_touserdata(L, -1); lua_pop(L, 1);
lua_pushstring(L, "__wrapper"); lua_rawget(L, 1);
wrapper = (LuaWrapper)(intptr_t) lua_touserdata(L, -1); lua_pop(L, 1);
lua_pushstring(L, "__key"); lua_rawget(L, 1);
key = luaL_checklstring(L, -1, NULL);
/* DEBUG(srv, "str hash call: '%s'", key); */
if (key && NULL != (d = g_hash_table_lookup(ht, key))) {
lua_pop(L, 1);
return wrapper(srv, wrk, L, d);
}
/* downside of this dynamic hash: you always get a function back on __index,
* no matter whether the key actually exists
*/
static int _lua_dynamic_hash_index(lua_State *L) {
int key_ndx;
lua_pushvalue(L, lua_upvalueindex(4));
lua_pushvalue(L, 2);
lua_concat(L, 2);
key_ndx = lua_gettop(L);
lua_newtable(L); /* result */
lua_newtable(L); /* meta table */
/* call method */
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushvalue(L, lua_upvalueindex(3));
lua_pushvalue(L, key_ndx);
lua_pushcclosure(L, _lua_dynamic_hash_index_call, 4);
lua_setfield(L, -2, "__call");
/* index for "nested" keys */
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushvalue(L, lua_upvalueindex(3));
lua_pushvalue(L, key_ndx); /* append a "." to the current key for nesting */
lua_pushstring(L, ".");
lua_concat(L, 2);
lua_pushcclosure(L, _lua_dynamic_hash_index, 4);
lua_setfield(L, -2, "__index");
lua_pop(L, lua_gettop(L));
lua_pushstring(L, "lookup '");
lua_pushstring(L, key);
lua_pushstring(L, "' failed (lua_str_hash_call)");
lua_concat(L, 3);
lua_error(L);
return 0;
}
lua_setmetatable(L, -2);
#define LUA_PUBLISH_HASH "GHashTable*"
static void lua_push_publish_hash_metatable(liServer *srv, liWorker *wrk, lua_State *L) {
if (luaL_newmetatable(L, LUA_PUBLISH_HASH)) {
lua_pushlightuserdata(L, srv);
lua_pushlightuserdata(L, wrk);
lua_pushcclosure(L, lua_str_hash_index, 2);
lua_setfield(L, -2, "__index");
lua_pushlightuserdata(L, srv);
lua_pushlightuserdata(L, wrk);
lua_pushcclosure(L, lua_str_hash_call, 2);
lua_setfield(L, -2, "__call");
}
return 1;
}
gboolean li_lua_config_publish_str_hash(liServer *srv, liWorker *wrk, lua_State *L, GHashTable *ht, LuaWrapper wrapper) {
lua_newtable(L); /* { } */
lua_pushlightuserdata(L, ht);
lua_setfield(L, -2, "__ht");
lua_pushlightuserdata(L, (void*)(intptr_t)wrapper);
lua_setfield(L, -2, "__wrapper");
lua_push_publish_hash_metatable(srv, wrk, L);
static void lua_push_dynamic_hash(liServer *srv, liWorker *wrk, lua_State *L, LuaWrapper wrapper) {
lua_newtable(L);
lua_newtable(L); /* meta table */
lua_pushlightuserdata(L, srv);
lua_pushlightuserdata(L, wrk);
lua_pushlightuserdata(L, (void*)(intptr_t) wrapper);
lua_pushstring(L, ""); /* nesting starts at "root" with empty string */
lua_pushcclosure(L, _lua_dynamic_hash_index, 4);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
return TRUE;
}
int li_lua_config_handle_server_action(liServer *srv, liWorker *wrk, lua_State *L, gpointer _sa) {
liServerAction *sa = (liServerAction*) _sa;
liValue *val;
static int li_lua_config_handle_server_action(liServer *srv, liWorker *wrk, lua_State *L, const char *key) {
liAction *a;
liValue *val;
liLuaState *LL = li_lua_state_get(L);
lua_checkstack(L, 16);
@ -149,9 +104,7 @@ int li_lua_config_handle_server_action(liServer *srv, liWorker *wrk, lua_State *
li_lua_unlock(LL);
/* TRACE(srv, "%s", "Creating action"); */
a = sa->create_action(srv, wrk, sa->p, val, sa->userdata);
li_value_free(val);
a = li_plugin_config_action(srv, wrk, key, val);
li_lua_lock(LL);
@ -163,31 +116,38 @@ int li_lua_config_handle_server_action(liServer *srv, liWorker *wrk, lua_State *
return li_lua_push_action(srv, L, a);
}
int li_lua_config_handle_server_setup(liServer *srv, liWorker *wrk, lua_State *L, gpointer _ss) {
liServerSetup *ss = (liServerSetup*) _ss;
void li_lua_push_action_table(liServer *srv, liWorker *wrk, lua_State *L) {
lua_push_dynamic_hash(srv, wrk, L, li_lua_config_handle_server_action);
}
static int li_lua_config_handle_server_setup(liServer *srv, liWorker *wrk, lua_State *L, const char *key) {
gboolean result;
liValue *val;
gboolean res;
liLuaState *LL = li_lua_state_get(L);
UNUSED(wrk);
assert(srv->main_worker == wrk);
lua_checkstack(L, 16);
val = lua_params_to_value(srv, L);
li_lua_unlock(LL);
/* TRACE(srv, "%s", "Calling setup"); */
res = ss->setup(srv, ss->p, val, ss->userdata);
result = li_plugin_config_setup(srv, key, val);
li_lua_lock(LL);
if (!res) {
li_value_free(val);
if (!result) {
lua_pushstring(L, "setup failed");
lua_error(L);
}
li_value_free(val);
return 0;
}
void li_lua_push_setup_table(liServer *srv, liWorker *wrk, lua_State *L) {
assert(srv->main_worker == wrk);
lua_push_dynamic_hash(srv, wrk, L, li_lua_config_handle_server_setup);
}
gboolean li_config_lua_load(liLuaState *LL, liServer *srv, liWorker *wrk, const gchar *filename, liAction **pact, gboolean allow_setup, liValue *args) {
int errfunc;
int lua_stack_top;
@ -209,11 +169,12 @@ gboolean li_config_lua_load(liLuaState *LL, liServer *srv, liWorker *wrk, const
_DEBUG(srv, wrk, NULL, "Loaded config script '%s'", filename);
if (allow_setup) {
li_lua_config_publish_str_hash(srv, wrk, L, srv->setups, li_lua_config_handle_server_setup);
assert(wrk == srv->main_worker);
li_lua_push_setup_table(srv, wrk, L);
lua_setfield(L, LUA_GLOBALSINDEX, "setup");
}
li_lua_config_publish_str_hash(srv, wrk, L, srv->actions, li_lua_config_handle_server_action);
li_lua_push_action_table(srv, wrk, L);
lua_setfield(L, LUA_GLOBALSINDEX, "action");
li_lua_push_lvalues_dict(srv, L);

62
src/main/config_parser.rl

@ -684,26 +684,20 @@ static gboolean config_parser_include(liServer *srv, GList *ctx_stack, gchar *pa
g_array_append_val(al->data.list, a);
}
/* setup action */
else if (ctx->in_setup_block && NULL != g_hash_table_lookup(srv->setups, name->data.string->str)) {
else if (ctx->in_setup_block) {
_printf("%s", "... which is a setup action\n");
if (!li_call_setup(srv, name->data.string->str, val)) {
if (!li_plugin_config_setup(srv, name->data.string->str, val)) {
li_value_free(name);
if (val)
li_value_free(val);
return FALSE;
}
if (val)
li_value_free(val);
}
/* normal action */
else if (!ctx->in_setup_block && NULL != g_hash_table_lookup(srv->actions, name->data.string->str)) {
else {
_printf("%s", "... which is a normal action\n");
al = g_queue_peek_head(ctx->action_list_stack);
a = li_create_action(srv, srv->main_worker, name->data.string->str, val);
li_value_free(val);
a = li_plugin_config_action(srv, srv->main_worker, name->data.string->str, val);
if (a == NULL) {
li_value_free(name);
@ -712,52 +706,6 @@ static gboolean config_parser_include(liServer *srv, GList *ctx_stack, gchar *pa
g_array_append_val(al->data.list, a);
}
/* option assignment */
else if (NULL != g_hash_table_lookup(srv->optionptrs, name->data.string->str) || NULL != g_hash_table_lookup(srv->options, name->data.string->str)) {
_printf("%s", "... which is an option assignment\n");
if (!val) {
WARNING(srv, "action %s takes a parameter", name->data.string->str);
li_value_free(name);
return FALSE;
}
if (ctx->in_setup_block) {
/* in setup { } block, override default values for options */
if (!li_plugin_set_default_option(srv, name->data.string->str, val)) {
ERROR(srv, "failed overriding default value for option \"%s\"", name->data.string->str);
li_value_free(name);
li_value_free(val);
return FALSE;
}
li_value_free(val);
} else {
/* normal assignment */
a = li_option_action(srv, srv->main_worker, name->data.string->str, val);
li_value_free(val);
if (a == NULL) {
li_value_free(name);
return FALSE;
}
al = g_queue_peek_head(ctx->action_list_stack);
g_array_append_val(al->data.list, a);
}
} else {
if (!ctx->in_setup_block && NULL != g_hash_table_lookup(srv->setups, name->data.string->str)) {
WARNING(srv, "action %s can only be called in a setup block", name->data.string->str);
} else if (ctx->in_setup_block && NULL != g_hash_table_lookup(srv->actions, name->data.string->str)) {
WARNING(srv, "action %s can't be called in a setup block", name->data.string->str);
} else {
WARNING(srv, "unknown action %s", name->data.string->str);
}
if (val)
li_value_free(val);
li_value_free(name);
return FALSE;
}
li_value_free(name);
}
@ -1528,7 +1476,7 @@ gboolean li_config_parse(liServer *srv, const gchar *config_path) {
}
/* append fallback "static" action */
a = li_create_action(srv, srv->main_worker, "static", NULL);
a = li_plugin_config_action(srv, srv->main_worker, "static", NULL);
g_array_append_val(srv->mainaction->data.list, a);
g_get_current_time(&end);

343
src/main/plugin.c

@ -1,6 +1,66 @@
#include <lighttpd/base.h>
/* Internal structures */
struct liServerOption {
liPlugin *p;
/** the value is freed with li_value_free after the parse call, so you
* probably want to extract pointers via li_value_extract_*
* val is zero to get the global default value if nothing is specified
* save result in value
*
* Default behaviour (NULL) is to extract the inner value from val
*/
liPluginParseOptionCB parse_option;
/** if parse_option is NULL, the default_value is used */
gint64 default_value;
size_t index, module_index;
liValueType type;
};
struct liServerOptionPtr {
liPlugin *p;
/** the value is freed with li_value_free after the parse call, so you
* probably want to extract pointers via li_value_extract_*
* val is zero to get the global default value if nothing is specified
* save result in value
*
* Default behaviour (NULL) is to extract the inner value from val
*/
liPluginParseOptionPtrCB parse_option;
/** the free_option handler has to free all allocated resources;
* it may get called with 0 initialized options, so you have to
* check the value.
*/
liPluginFreeOptionPtrCB free_option;
/** if parse_option is NULL, the default_value is used; it is only used
* for the following value types:
* - STRING: used for g_string_new, i.e. a const char*
*/
gpointer default_value;
size_t index, module_index;
liValueType type;
};
struct liServerAction {
liPlugin *p;
liPluginCreateActionCB create_action;
gpointer userdata;
};
struct liServerSetup {
liPlugin *p;
liPluginSetupCB setup;
gpointer userdata;
};
static gboolean plugin_load_default_option(liServer *srv, liServerOption *sopt);
static gboolean plugin_load_default_optionptr(liServer *srv, liServerOptionPtr *sopt);
static void li_plugin_free_default_options(liServer *srv, liPlugin *p);
@ -81,9 +141,36 @@ void li_plugin_free(liServer *srv, liPlugin *p) {
g_slice_free(liPlugin, p);
}
static void server_option_free(gpointer _so) {
g_slice_free(liServerOption, _so);
}
static void server_optionptr_free(gpointer _so) {
g_slice_free(liServerOptionPtr, _so);
}
static void server_action_free(gpointer _sa) {
g_slice_free(liServerAction, _sa);
}
static void server_setup_free(gpointer _ss) {
g_slice_free(liServerSetup, _ss);
}
void li_server_plugins_init(liServer *srv) {
srv->plugins = g_hash_table_new(g_str_hash, g_str_equal);
srv->options = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_option_free);
srv->optionptrs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_optionptr_free);
srv->actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_action_free);
srv->setups = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_setup_free);
srv->li_plugins_handle_close = g_array_new(FALSE, TRUE, sizeof(liPlugin*));
srv->li_plugins_handle_vrclose = g_array_new(FALSE, TRUE, sizeof(liPlugin*));
srv->option_def_values = g_array_new(FALSE, TRUE, sizeof(liOptionValue));
srv->optionptr_def_values = g_array_new(FALSE, TRUE, sizeof(liOptionPtrValue*));
}
void li_server_plugins_free(liServer *srv) {
gpointer key, val;
GHashTableIter i;
liServerState s;
s = g_atomic_int_get(&srv->state);
@ -92,23 +179,76 @@ void li_server_plugins_free(liServer *srv) {
return;
}
g_hash_table_iter_init(&i, srv->plugins);
while (g_hash_table_iter_next(&i, &key, &val)) {
liPlugin *p = (liPlugin*) val;
g_array_free(srv->option_def_values, TRUE);
{
guint i;
for (i = 0; i < srv->optionptr_def_values->len; i++) {
li_release_optionptr(srv, g_array_index(srv->optionptr_def_values, liOptionPtrValue*, i));
}
}
g_array_free(srv->optionptr_def_values, TRUE);
{
gpointer key, val;
GHashTableIter i;
g_hash_table_iter_init(&i, srv->plugins);
while (g_hash_table_iter_next(&i, &key, &val)) {
liPlugin *p = (liPlugin*) val;
li_plugin_free_options(srv, p);
li_plugin_free_actions(srv, p);
li_plugin_free_setups(srv, p);
if (p->free)
p->free(srv, p);
li_plugin_free_options(srv, p);
li_plugin_free_actions(srv, p);
li_plugin_free_setups(srv, p);
if (p->free)
p->free(srv, p);
g_slice_free(liPlugin, p);
g_slice_free(liPlugin, p);
}
}
g_hash_table_destroy(srv->plugins);
g_hash_table_destroy(srv->options);
g_hash_table_destroy(srv->optionptrs);
g_hash_table_destroy(srv->actions);
g_hash_table_destroy(srv->setups);
g_array_free(srv->li_plugins_handle_close, TRUE);
g_array_free(srv->li_plugins_handle_vrclose, TRUE);
}
static gboolean check_name_free(liServer *srv, liPlugin *p, const gchar *name, gboolean setup_ns, gboolean action_ns) {
liServerOption *so;
liServerOptionPtr *sop;
liServerAction *sa;
liServerSetup *ss;
if (NULL != (so = (liServerOption*)g_hash_table_lookup(srv->options, name))) {
ERROR(srv, "Name conflict: option '%s' already registered by plugin '%s', unloading '%s'",
name,
NULL != so->p ? so->p->name : "<none>",
p->name);
return FALSE;
}
if (NULL != (sop = (liServerOptionPtr*)g_hash_table_lookup(srv->optionptrs, name))) {
ERROR(srv, "Name conflict: option '%s' already registered by plugin '%s', unloading '%s'",
name,
NULL != sop->p ? sop->p->name : "<none>",
p->name);
return FALSE;
}
if (action_ns && NULL != (sa = (liServerAction*)g_hash_table_lookup(srv->actions, name))) {
ERROR(srv, "Name conflict: action '%s' already registered by plugin '%s', unloading '%s'",
name,
NULL != sa->p ? sa->p->name : "<none>",
p->name);
return FALSE;
}
if (setup_ns && NULL != (ss = (liServerSetup*)g_hash_table_lookup(srv->setups, name))) {
ERROR(srv, "Name conflict: setup '%s' already registered by plugin '%s', unloading '%s'",
name,
NULL != ss->p ? ss->p->name : "<none>",
p->name);
return FALSE;
}
return TRUE;
}
liPlugin *li_plugin_register(liServer *srv, const gchar *name, liPluginInitCB init, gpointer userdata) {
@ -142,26 +282,10 @@ liPlugin *li_plugin_register(liServer *srv, const gchar *name, liPluginInitCB in
if (p->options) {
size_t i;
liServerOption *so;
liServerOptionPtr *sop;
const liPluginOption *po;
for (i = 0; (po = &p->options[i])->name; i++) {
if (NULL != (so = (liServerOption*)g_hash_table_lookup(srv->options, po->name))) {
ERROR(srv, "Option '%s' already registered by plugin '%s', unloading '%s'",
po->name,
so->p ? so->p->name : "<none>",
p->name);
li_plugin_free(srv, p);
return NULL;
}
if (NULL != (sop = (liServerOptionPtr*)g_hash_table_lookup(srv->optionptrs, po->name))) {
ERROR(srv, "Option '%s' already registered by plugin '%s', unloading '%s'",
po->name,
sop->p ? sop->p->name : "<none>",
p->name);
li_plugin_free(srv, p);
return NULL;
}
if (!check_name_free(srv, p, po->name, TRUE, TRUE)) goto fail;
so = g_slice_new0(liServerOption);
so->type = po->type;
so->parse_option = po->parse_option;
@ -176,27 +300,11 @@ liPlugin *li_plugin_register(liServer *srv, const gchar *name, liPluginInitCB in
if (p->optionptrs) {
size_t i;
liServerOption *so_;
liServerOptionPtr *so;
const liPluginOptionPtr *po;
for (i = 0; (po = &p->optionptrs[i])->name; i++) {
if (NULL != (so_ = (liServerOption*)g_hash_table_lookup(srv->options, po->name))) {
ERROR(srv, "Option '%s' already registered by plugin '%s', unloading '%s'",
po->name,
so_->p ? so_->p->name : "<none>",
p->name);
li_plugin_free(srv, p);
return NULL;
}
if (NULL != (so = (liServerOptionPtr*)g_hash_table_lookup(srv->optionptrs, po->name))) {
ERROR(srv, "Option '%s' already registered by plugin '%s', unloading '%s'",
po->name,
so->p ? so->p->name : "<none>",
p->name);
li_plugin_free(srv, p);
return NULL;
}
if (!check_name_free(srv, p, po->name, TRUE, TRUE)) goto fail;
so = g_slice_new0(liServerOptionPtr);
so->type = po->type;
so->parse_option = po->parse_option;
@ -216,14 +324,7 @@ liPlugin *li_plugin_register(liServer *srv, const gchar *name, liPluginInitCB in
const liPluginAction *pa;
for (i = 0; (pa = &p->actions[i])->name; i++) {
if (NULL != (sa = (liServerAction*)g_hash_table_lookup(srv->actions, pa->name))) {
ERROR(srv, "Action '%s' already registered by plugin '%s', unloading '%s'",
pa->name,
sa->p ? sa->p->name : "<none>",
p->name);
li_plugin_free(srv, p);
return NULL;
}
if (!check_name_free(srv, p, pa->name, FALSE, TRUE)) goto fail;
sa = g_slice_new0(liServerAction);
sa->create_action = pa->create_action;
sa->p = p;
@ -238,14 +339,7 @@ liPlugin *li_plugin_register(liServer *srv, const gchar *name, liPluginInitCB in
const liPluginSetup *ps;
for (i = 0; (ps = &p->setups[i])->name; i++) {
if (NULL != (ss = (liServerSetup*)g_hash_table_lookup(srv->setups, ps->name))) {
ERROR(srv, "Setup '%s' already registered by plugin '%s', unloading '%s'",
ps->name,
ss->p ? ss->p->name : "<none>",
p->name);
li_plugin_free(srv, p);
return NULL;
}
if (!check_name_free(srv, p, ps->name, TRUE, FALSE)) goto fail;
ss = g_slice_new0(liServerSetup);
ss->setup = ps->setup;
ss->p = p;
@ -255,6 +349,10 @@ liPlugin *li_plugin_register(liServer *srv, const gchar *name, liPluginInitCB in
}
return p;
fail:
li_plugin_free(srv, p);
return NULL;
}
@ -263,7 +361,7 @@ static liServerOption* find_option(liServer *srv, const char *name) {
}
static gboolean li_parse_option(liServer *srv, liWorker *wrk, liServerOption *sopt, const char *name, liValue *val, liOptionSet *mark) {
if (!srv || !wrk || !name || !mark || !sopt) return FALSE;
assert(NULL != srv && NULL != wrk && NULL != sopt && NULL != name && NULL != val && NULL != mark);
if (sopt->type != val->type && sopt->type != LI_VALUE_NONE) {
ERROR(srv, "Unexpected value type '%s', expected '%s' for option %s",
@ -304,7 +402,7 @@ static gboolean li_parse_optionptr(liServer *srv, liWorker *wrk, liServerOptionP
liOptionPtrValue *oval;
gpointer ptr = NULL;
if (!srv || !wrk || !name || !mark || !sopt) return FALSE;
assert(NULL != srv && NULL != wrk && NULL != sopt && NULL != name && NULL != val && NULL != mark);
if (sopt->type != val->type && sopt->type != LI_VALUE_NONE) {
ERROR(srv, "Unexpected value type '%s', expected '%s' for option %s",
@ -338,8 +436,9 @@ static gboolean li_parse_optionptr(liServer *srv, liWorker *wrk, liServerOptionP
void li_release_optionptr(liServer *srv, liOptionPtrValue *value) {
liServerOptionPtr *sopt;
assert(NULL != srv);
if (!srv || !value) return;
if (NULL == value) return;
assert(g_atomic_int_get(&value->refcount) > 0);
if (!g_atomic_int_dec_and_test(&value->refcount)) return;
@ -380,63 +479,75 @@ void li_release_optionptr(liServer *srv, liOptionPtrValue *value) {
g_slice_free(liOptionPtrValue, value);
}
liAction* li_option_action(liServer *srv, liWorker *wrk, const gchar *name, liValue *val) {
liAction *li_plugin_config_action(liServer *srv, liWorker *wrk, const gchar *name, liValue *val) {
liAction *a = NULL;
liServerAction *sa;
liServerOption *sopt;
liServerOptionPtr *soptptr;
if (NULL != (sopt = find_option(srv, name))) {
if (NULL != (sa = (liServerAction*) g_hash_table_lookup(srv->actions, name))) {
if (NULL == (a = sa->create_action(srv, wrk, sa->p, val, sa->userdata))) {
ERROR(srv, "Action '%s' creation failed", name);
}
} else if (NULL != (sopt = find_option(srv, name))) {
liOptionSet setting;
if (!li_parse_option(srv, wrk, sopt, name, val, &setting)) {
return NULL;
}
if (!li_parse_option(srv, wrk, sopt, name, val, &setting)) goto exit;
return li_action_new_setting(setting);
a = li_action_new_setting(setting);
} else if (NULL != (soptptr = find_optionptr(srv, name))) {
liOptionPtrSet setting;
if (!li_parse_optionptr(srv, wrk, soptptr, name, val, &setting)) {
return NULL;
}
if (!li_parse_optionptr(srv, wrk, soptptr, name, val, &setting)) goto exit;
return li_action_new_settingptr(setting);
a = li_action_new_settingptr(setting);
} else if (NULL != g_hash_table_lookup(srv->setups, name)) {
ERROR(srv, "'%s' can only be called in a setup block", name);
} else {
ERROR(srv, "Unknown option '%s'", name);
return FALSE;
}
}
liAction* li_create_action(liServer *srv, liWorker *wrk, const gchar *name, liValue *val) {
liAction *a;
liServerAction *sa;
if (NULL == (sa = (liServerAction*) g_hash_table_lookup(srv->actions, name))) {
ERROR(srv, "Action '%s' doesn't exist", name);
return NULL;
}
if (NULL == (a = sa->create_action(srv, wrk, sa->p, val, sa->userdata))) {
ERROR(srv, "Action '%s' creation failed", name);
return NULL;
ERROR(srv, "unknown action %s", name);
}
exit:
li_value_free(val);
return a;
}
gboolean li_call_setup(liServer *srv, const char *name, liValue *val) {
gboolean li_plugin_config_setup(liServer *srv, const char *name, liValue *val) {
gboolean result = FALSE;
liServerSetup *ss;
liServerOption *sopt;
liServerOptionPtr *soptptr;
if (NULL == (ss = (liServerSetup*) g_hash_table_lookup(srv->setups, name))) {
ERROR(srv, "Setup function '%s' doesn't exist", name);
return FALSE;
}
if (NULL != (ss = (liServerSetup*) g_hash_table_lookup(srv->setups, name))) {
if (!ss->setup(srv, ss->p, val, ss->userdata)) {
ERROR(srv, "Setup '%s' failed", name);
goto exit;
}
result = TRUE;
} else if (NULL != (sopt = find_option(srv, name))) {
liOptionSet setting;
if (!ss->setup(srv, ss->p, val, ss->userdata)) {
ERROR(srv, "Setup '%s' failed", name);
return FALSE;
if (!li_parse_option(srv, srv->main_worker, sopt, name, val, &setting)) goto exit;
g_array_index(srv->option_def_values, liOptionValue, sopt->index) = setting.value;
result = TRUE;
} else if (NULL != (soptptr = find_optionptr(srv, name))) {
liOptionPtrSet setting;
if (!li_parse_optionptr(srv, srv->main_worker, soptptr, name, val, &setting)) goto exit;
li_release_optionptr(srv, g_array_index(srv->optionptr_def_values, liOptionPtrValue*, soptptr->index));
g_array_index(srv->optionptr_def_values, liOptionPtrValue*, soptptr->index) = setting.value;
result = TRUE;
} else if (NULL != g_hash_table_lookup(srv->setups, name)) {
ERROR(srv, "'%s' can only be called in a setup block", name);
} else {
ERROR(srv, "unknown setup %s", name);
}
return TRUE;
exit:
li_value_free(val);
return result;
}
void li_plugins_prepare_callbacks(liServer *srv) {
@ -472,38 +583,6 @@ void li_plugins_handle_vrclose(liVRequest *vr) {
}
}
gboolean li_plugin_set_default_option(liServer* srv, const gchar* name, liValue* val) {
liServerOption *sopt;
liServerOptionPtr *soptptr;
if (NULL != (sopt = find_option(srv, name))) {
liOptionSet setting;
/* assign new value */
if (!li_parse_option(srv, srv->main_worker, sopt, name, val, &setting)) {
return FALSE;
}
g_array_index(srv->option_def_values, liOptionValue, sopt->index) = setting.value;
} else if (NULL != (soptptr = find_optionptr(srv, name))) {
liOptionPtrSet setting;
/* assign new value */
if (!li_parse_optionptr(srv, srv->main_worker, soptptr, name, val, &setting)) {
return FALSE;
}
li_release_optionptr(srv, g_array_index(srv->optionptr_def_values, liOptionPtrValue*, soptptr->index));
g_array_index(srv->optionptr_def_values, liOptionPtrValue*, soptptr->index) = setting.value;
} else {
ERROR(srv, "unknown option \"%s\"", name);
return FALSE;
}
return TRUE;
}
static gboolean plugin_load_default_option(liServer *srv, liServerOption *sopt) {
liOptionValue oval = {0};

54
src/main/plugin_core.c

@ -101,58 +101,6 @@ static liAction* core_when(liServer *srv, liWorker *wrk, liPlugin* p, liValue *v
return a;
}
static liAction* core_set(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
liValue *val_val, *val_name;
liAction *a;
UNUSED(p); UNUSED(userdata);
if (!val) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (val->type != LI_VALUE_LIST) {
ERROR(srv, "expected list, got %s", li_value_type_string(val->type));
return NULL;
}
if (val->data.list->len != 2) {
ERROR(srv, "expected list with length 2, has length %u", val->data.list->len);
return NULL;
}
val_name = g_array_index(val->data.list, liValue*, 0);
val_val = g_array_index(val->data.list, liValue*, 1);
if (val_name->type != LI_VALUE_STRING) {
ERROR(srv, "expected string as first parameter, got %s", li_value_type_string(val_name->type));
return NULL;
}
a = li_option_action(srv, wrk, val_name->data.string->str, val_val);
return a;
}
static gboolean core_setup_set(liServer *srv, liPlugin* p, liValue *val, gpointer userdata) {
liValue *val_val, *val_name;
UNUSED(p); UNUSED(userdata);
if (!val) {
ERROR(srv, "%s", "need parameter");
return FALSE;
}
if (val->type != LI_VALUE_LIST) {
ERROR(srv, "expected list, got %s", li_value_type_string(val->type));
return FALSE;
}
if (val->data.list->len != 2) {
ERROR(srv, "expected list with length 2, has length %u", val->data.list->len);
return FALSE;
}
val_name = g_array_index(val->data.list, liValue*, 0);
val_val = g_array_index(val->data.list, liValue*, 1);
if (val_name->type != LI_VALUE_STRING) {
ERROR(srv, "expected string as first parameter, got %s", li_value_type_string(val_name->type));
return FALSE;
}
return li_plugin_set_default_option(srv, val_name->data.string->str, val_val);
}
typedef struct docroot_split docroot_split;
struct docroot_split {
GString *hostname;
@ -2105,7 +2053,6 @@ static const liPluginOptionPtr optionptrs[] = {
static const liPluginAction actions[] = {
{ "list", core_list, NULL },
{ "when", core_when, NULL },
{ "set", core_set, NULL },
{ "docroot", core_docroot, NULL },
{ "alias", core_alias, NULL },
@ -2145,7 +2092,6 @@ static const liPluginAction actions[] = {
};
static const liPluginSetup setups[] = {
{ "set_default", core_setup_set, NULL },
{ "listen", core_listen, NULL },
{ "workers", core_workers, NULL },
{ "workers.cpu_affinity", core_workers_cpu_affinity, NULL },

37
src/main/server.c

@ -44,22 +44,6 @@ void li_server_socket_acquire(liServerSocket* sock) {
g_atomic_int_inc(&sock->refcount);
}
static void server_option_free(gpointer _so) {
g_slice_free(liServerOption, _so);
}
static void server_optionptr_free(gpointer _so) {
g_slice_free(liServerOptionPtr, _so);
}
static void server_action_free(gpointer _sa) {
g_slice_free(liServerAction, _sa);
}
static void server_setup_free(gpointer _ss) {
g_slice_free(liServerSetup, _ss);
}
static void server_fetch_db_free(gpointer db) {
li_fetch_database_release((liFetchDatabase*) db);
}
@ -104,19 +88,10 @@ liServer* li_server_new(const gchar *module_dir, gboolean module_resident) {
srv->modules = li_modules_new(srv, module_dir, module_resident);
srv->plugins = g_hash_table_new(g_str_hash, g_str_equal);
srv->options = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_option_free);
srv->optionptrs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_optionptr_free);
srv->actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_action_free);
srv->setups = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_setup_free);
li_server_plugins_init(srv);
srv->prepare_callbacks = g_array_new(FALSE, TRUE, sizeof(liServerPrepareCallbackData));
srv->li_plugins_handle_close = g_array_new(FALSE, TRUE, sizeof(liPlugin*));
srv->li_plugins_handle_vrclose = g_array_new(FALSE, TRUE, sizeof(liPlugin*));
srv->option_def_values = g_array_new(FALSE, TRUE, sizeof(liOptionValue));
srv->optionptr_def_values = g_array_new(FALSE, TRUE, sizeof(liOptionPtrValue*));
srv->mainaction = NULL;
srv->action_mutex = g_mutex_new();
@ -251,17 +226,7 @@ void li_server_free(liServer* srv) {
g_array_free(srv->ts_formats, TRUE);
}
g_array_free(srv->option_def_values, TRUE);
{
guint i;
for (i = 0; i < srv->optionptr_def_values->len; i++) {
li_release_optionptr(srv, g_array_index(srv->optionptr_def_values, liOptionPtrValue*, i));
}
}
g_array_free(srv->optionptr_def_values, TRUE);
li_server_plugins_free(srv);
g_array_free(srv->li_plugins_handle_close, TRUE);
g_array_free(srv->li_plugins_handle_vrclose, TRUE);
if (NULL != srv->prepare_callbacks) {
guint i, len;

4
src/modules/mod_lua.c

@ -492,10 +492,10 @@ static gboolean lua_plugin_load(liServer *srv, liPlugin *p, GString *filename, l
goto failed_unlock_lua;
}
li_lua_config_publish_str_hash(srv, srv->main_worker, L, srv->setups, li_lua_config_handle_server_setup);
li_lua_push_setup_table(srv, srv->main_worker, L);
lua_setfield(L, LUA_GLOBALSINDEX, "setup");
li_lua_config_publish_str_hash(srv, srv->main_worker, L, srv->actions, li_lua_config_handle_server_action);
li_lua_push_action_table(srv, srv->main_worker, L);
lua_setfield(L, LUA_GLOBALSINDEX, "action");
li_lua_push_lvalues_dict(srv, L);

42
tests/t-mod-lua.py

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
from base import *
from requests import *
LUA_TEST_OPTIONS="""
setup["server.tag"]("lighttpd 2.0 with lua")
function changetag(tag)
return action["server.tag"](tag)
end
actions = {
["lua.changetag"] = changetag
}
"""
class TestSetupOption(CurlRequest):
URL = "/"
EXPECT_RESPONSE_HEADERS = [("Server", "lighttpd 2.0 with lua")]
class TestChangeOption(CurlRequest):
URL = "/?change"
EXPECT_RESPONSE_HEADERS = [("Server", "lighttpd 2.0 with modified lua")]
class Test(GroupTest):
group = [
TestSetupOption, TestChangeOption,
]
def Prepare(self):
test_options_lua = self.PrepareFile("lua/test_options.lua", LUA_TEST_OPTIONS)
self.plain_config = """
setup {{ lua.plugin "{test_options_lua}"; }}
""".format(test_options_lua = test_options_lua)
self.config = """
if req.query == "change" {
lua.changetag "lighttpd 2.0 with modified lua";
}
"""
Loading…
Cancel
Save