Add lua plugins

personal/stbuehler/wip
Stefan Bühler 13 years ago
parent b4c3e2ba02
commit fe7ddede88
  1. 3
      include/lighttpd/actions_lua.h
  2. 4
      include/lighttpd/config_lua.h
  3. 3
      include/lighttpd/core_lua.h
  4. 8
      include/lighttpd/plugin.h
  5. 3
      include/lighttpd/value_lua.h
  6. 15
      src/main/actions_lua.c
  7. 27
      src/main/config_lua.c
  8. 10
      src/main/core_lua.c
  9. 14
      src/main/plugin.c
  10. 66
      src/main/value_lua.c
  11. 366
      src/modules/mod_lua.c

@ -10,4 +10,7 @@ LI_API int li_lua_push_action(liServer *srv, lua_State *L, liAction *a);
/* create new action from lua function */
LI_API liAction* li_lua_make_action(lua_State *L, int ndx);
/* either acquire a action ref or makes a new action from a lua function */
LI_API liAction* li_lua_get_action_ref(lua_State *L, int ndx);
#endif

@ -7,4 +7,8 @@
LI_API gboolean li_config_lua_load(lua_State *L, liServer *srv, const gchar *filename, liAction **pact, gboolean allow_setup);
LI_API gboolean li_lua_config_publish_str_hash(liServer *srv, lua_State *L, GHashTable *ht, int (*wrapper)(liServer *srv, lua_State *L, gpointer data));
LI_API int li_lua_config_handle_server_action(liServer *srv, lua_State *L, gpointer _sa);
LI_API int li_lua_config_handle_server_setup(liServer *srv, lua_State *L, gpointer _ss);
#endif

@ -55,6 +55,9 @@ LI_API void li_lua_init_vrequest_mt(lua_State *L);
LI_API liVRequest* li_lua_get_vrequest(lua_State *L, int ndx);
LI_API int li_lua_push_vrequest(lua_State *L, liVRequest *vr);
LI_API int li_lua_fixindex(lua_State *L, int ndx);
/* return 1 if value is found in mt (on top of the stack), 0 if it is not found (stack balance = 0)
* table, key on stack at pos 0 and 1 (i.e. __index metho)
*/

@ -58,13 +58,13 @@ struct liPluginOption {
liValueType type;
gpointer default_value;
liPluginParseOptionCB li_parse_option;
liPluginParseOptionCB parse_option;
liPluginFreeOptionCB free_option;
};
struct liPluginAction {
const gchar *name;
liPluginCreateActionCB li_create_action;
liPluginCreateActionCB create_action;
gpointer userdata;
};
@ -90,7 +90,7 @@ struct liServerOption {
*
* Default behaviour (NULL) is to extract the inner value from val
*/
liPluginParseOptionCB li_parse_option;
liPluginParseOptionCB parse_option;
/** the free_option handler has to free all allocated resources;
* it may get called with 0 initialized options, so you have to
@ -112,7 +112,7 @@ struct liServerOption {
struct liServerAction {
liPlugin *p;
liPluginCreateActionCB li_create_action;
liPluginCreateActionCB create_action;
gpointer userdata;
};

@ -10,6 +10,9 @@
*/
LI_API liValue* li_value_from_lua(liServer *srv, lua_State *L);
/* always returns 1, pushes nil on error */
LI_API int li_lua_push_value(lua_State *L, liValue *value);
LI_API GString* li_lua_togstring(lua_State *L, int ndx);
#endif

@ -165,3 +165,18 @@ liAction* li_lua_make_action(lua_State *L, int ndx) {
return li_action_new_function(lua_action_func, lua_action_cleanup, lua_action_free, par);
}
liAction* li_lua_get_action_ref(lua_State *L, int ndx) {
liAction *act;
act = li_lua_get_action(L, ndx);
if (NULL == act) {
if (lua_isfunction(L, ndx)) {
act = li_lua_make_action(L, ndx);
}
} else {
li_action_acquire(act);
}
return act;
}

@ -123,7 +123,7 @@ static void lua_push_publish_hash_metatable(liServer *srv, lua_State *L) {
}
}
static gboolean publish_str_hash(liServer *srv, lua_State *L, GHashTable *ht, LuaWrapper wrapper) {
gboolean li_lua_config_publish_str_hash(liServer *srv, lua_State *L, GHashTable *ht, LuaWrapper wrapper) {
lua_newtable(L); /* { } */
lua_pushlightuserdata(L, ht);
lua_setfield(L, -2, "__ht");
@ -136,7 +136,7 @@ static gboolean publish_str_hash(liServer *srv, lua_State *L, GHashTable *ht, Lu
}
static int handle_server_action(liServer *srv, lua_State *L, gpointer _sa) {
int li_lua_config_handle_server_action(liServer *srv, lua_State *L, gpointer _sa) {
liServerAction *sa = (liServerAction*) _sa;
liValue *val;
liAction *a;
@ -147,7 +147,7 @@ static int handle_server_action(liServer *srv, lua_State *L, gpointer _sa) {
li_lua_unlock(srv);
/* TRACE(srv, "%s", "Creating action"); */
a = sa->li_create_action(srv, sa->p, val, sa->userdata);
a = sa->create_action(srv, sa->p, val, sa->userdata);
li_value_free(val);
li_lua_lock(srv);
@ -160,7 +160,7 @@ static int handle_server_action(liServer *srv, lua_State *L, gpointer _sa) {
return li_lua_push_action(srv, L, a);
}
static int handle_server_setup(liServer *srv, lua_State *L, gpointer _ss) {
int li_lua_config_handle_server_setup(liServer *srv, lua_State *L, gpointer _ss) {
liServerSetup *ss = (liServerSetup*) _ss;
liValue *val;
gboolean res;
@ -204,11 +204,11 @@ gboolean li_config_lua_load(lua_State *L, liServer *srv, const gchar *filename,
DEBUG(srv, "Loaded config script '%s'", filename);
if (allow_setup) {
publish_str_hash(srv, L, srv->setups, handle_server_setup);
li_lua_config_publish_str_hash(srv, L, srv->setups, li_lua_config_handle_server_setup);
lua_setfield(L, LUA_GLOBALSINDEX, "setup");
}
publish_str_hash(srv, L, srv->actions, handle_server_action);
li_lua_config_publish_str_hash(srv, L, srv->actions, li_lua_config_handle_server_action);
lua_setfield(L, LUA_GLOBALSINDEX, "action");
li_lua_push_lvalues_dict(srv, L);
@ -232,19 +232,8 @@ gboolean li_config_lua_load(lua_State *L, liServer *srv, const gchar *filename,
lua_pop(L, 1); /* pop the ret-value */
{
liAction *act;
lua_getfield(L, LUA_GLOBALSINDEX, "actions");
act = li_lua_get_action(L, -1);
if (NULL == act && lua_isfunction(L, -1)) {
act = li_lua_make_action(L, -1);
} else {
li_action_acquire(act);
}
*pact = act;
}
lua_getfield(L, LUA_GLOBALSINDEX, "actions");
*pact = li_lua_get_action_ref(L, -1);
lua_pop(L, 1);
assert(lua_gettop(L) == lua_stack_top);

@ -4,6 +4,16 @@
#include <lualib.h>
#include <lauxlib.h>
/* replace a negative stack index with a positive one,
* so that you don't need to care about push/pop
*/
int li_lua_fixindex(lua_State *L, int ndx) {
int top;
if (ndx < 0 && ndx >= -(top = lua_gettop(L)))
ndx = top + ndx + 1;
return ndx;
}
static int traceback (lua_State *L) {
if (!lua_isstring(L, 1)) /* 'message' not a string? */
return 1; /* keep it intact */

@ -143,7 +143,7 @@ liPlugin *li_plugin_register(liServer *srv, const gchar *name, liPluginInitCB in
}
so = g_slice_new0(liServerOption);
so->type = po->type;
so->li_parse_option = po->li_parse_option;
so->parse_option = po->parse_option;
so->free_option = po->free_option;
so->index = g_hash_table_size(srv->options);
so->module_index = i;
@ -169,7 +169,7 @@ liPlugin *li_plugin_register(liServer *srv, const gchar *name, liPluginInitCB in
return NULL;
}
sa = g_slice_new0(liServerAction);
sa->li_create_action = pa->li_create_action;
sa->create_action = pa->create_action;
sa->p = p;
sa->userdata = pa->userdata;
g_hash_table_insert(srv->actions, (gchar*) pa->name, sa);
@ -223,10 +223,10 @@ gboolean li_parse_option(liServer *srv, const char *name, liValue *val, liOption
return FALSE;
}
if (!sopt->li_parse_option) {
if (!sopt->parse_option) {
mark->value = li_value_extract(val);
} else {
if (!sopt->li_parse_option(srv, sopt->p, sopt->module_index, val, &mark->value)) {
if (!sopt->parse_option(srv, sopt->p, sopt->module_index, val, &mark->value)) {
/* errors should be logged by parse function */
return FALSE;
}
@ -300,7 +300,7 @@ liAction* li_create_action(liServer *srv, const gchar *name, liValue *val) {
return NULL;
}
if (NULL == (a = sa->li_create_action(srv, sa->p, val, sa->userdata))) {
if (NULL == (a = sa->create_action(srv, sa->p, val, sa->userdata))) {
ERROR(srv, "Action '%s' creation failed", name);
return NULL;
}
@ -393,7 +393,7 @@ static gboolean plugin_load_default_option(liServer *srv, liServerOption *sopt)
if (!sopt)
return FALSE;
if (!sopt->li_parse_option) {
if (!sopt->parse_option) {
switch (sopt->type) {
case LI_VALUE_NONE:
break;
@ -409,7 +409,7 @@ static gboolean plugin_load_default_option(liServer *srv, liServerOption *sopt)
oval.ptr = NULL;
}
} else {
if (!sopt->li_parse_option(srv, sopt->p, sopt->module_index, NULL, &oval)) {
if (!sopt->parse_option(srv, sopt->p, sopt->module_index, NULL, &oval)) {
/* errors should be logged by parse function */
return FALSE;
}

@ -2,16 +2,7 @@
#include <lighttpd/value_lua.h>
#include <lighttpd/condition_lua.h>
#include <lighttpd/actions_lua.h>
/* replace a negative stack index with a positive one,
* so that you don't need to care about push/pop
*/
static int lua_fixindex(lua_State *L, int ndx) {
int top;
if (ndx < 0 && ndx >= -(top = lua_gettop(L)))
ndx = top + ndx + 1;
return ndx;
}
#include <lighttpd/core_lua.h>
static liValue* li_value_from_lua_table(liServer *srv, lua_State *L, int ndx) {
liValue *val = NULL, *sub_option;
@ -20,7 +11,7 @@ static liValue* li_value_from_lua_table(liServer *srv, lua_State *L, int ndx) {
int ikey;
GString *skey;
ndx = lua_fixindex(L, ndx);
ndx = li_lua_fixindex(L, ndx);
lua_pushnil(L);
while (lua_next(L, ndx) != 0) {
switch (lua_type(L, -2)) {
@ -162,3 +153,56 @@ GString* li_lua_togstring(lua_State *L, int ndx) {
return str;
}
int li_lua_push_value(lua_State *L, liValue *value) {
switch (value->type) {
case LI_VALUE_NONE:
lua_pushnil(L);
break;
case LI_VALUE_BOOLEAN:
lua_pushboolean(L, value->data.boolean);
break;
case LI_VALUE_NUMBER:
lua_pushinteger(L, value->data.number);
break;
case LI_VALUE_STRING:
lua_pushlstring(L, GSTR_LEN(value->data.string));
break;
case LI_VALUE_LIST: {
GArray *list = value->data.list;
guint i;
lua_newtable(L);
for (i = 0; i < list->len; i++) {
liValue *subval = g_array_index(list, liValue*, i);
li_lua_push_value(L, subval);
lua_rawseti(L, -2, i);
}
} break;
case LI_VALUE_HASH: {
GHashTableIter it;
gpointer pkey, pvalue;
lua_newtable(L);
g_hash_table_iter_init(&it, value->data.hash);
while (g_hash_table_iter_next(&it, &pkey, &pvalue)) {
GString *key = pkey;
liValue *subval = pvalue;
lua_pushlstring(L, GSTR_LEN(key));
li_lua_push_value(L, subval);
lua_rawset(L, -3);
}
} break;
case LI_VALUE_ACTION:
li_action_acquire(value->data.val_action.action);
li_lua_push_action(value->data.val_action.srv, L, value->data.val_action.action);
break;
case LI_VALUE_CONDITION:
li_condition_acquire(value->data.val_cond.cond);
li_lua_push_condition(value->data.val_cond.srv, L, value->data.val_cond.cond);
break;
default: /* ignore error and push nil */
lua_pushnil(L);
break;
}
return 1;
}

@ -31,10 +31,22 @@
#include <lighttpd/core_lua.h>
#include <lighttpd/config_lua.h>
#include <lighttpd/condition_lua.h>
#include <lighttpd/value_lua.h>
#include <lighttpd/actions_lua.h>
#include <lualib.h>
#include <lauxlib.h>
LI_API gboolean mod_lua_init(liModules *mods, liModule *mod);
LI_API gboolean mod_lua_free(liModules *mods, liModule *mod);
typedef struct module_config module_config;
struct module_config {
liPlugin *main_plugin;
GPtrArray *lua_plugins;
};
typedef struct lua_worker_config lua_worker_config;
struct lua_worker_config {
liAction *act;
@ -143,7 +155,7 @@ static liAction* lua_handler_create(liServer *srv, liPlugin* p, liValue *val, gp
liValue *v_filename = NULL, *v_options = NULL;
lua_config *conf;
guint ttl = 0;
UNUSED(srv); UNUSED(p); UNUSED(userdata);
UNUSED(p); UNUSED(userdata);
if (val) {
if (val->type == LI_VALUE_STRING) {
@ -199,6 +211,330 @@ option_failed:
return NULL;
}
/* lua plugins */
typedef struct luaPlugin luaPlugin;
struct luaPlugin {
GArray *actions, *setups;
GString *filename;
};
static int push_args(lua_State *L, liValue *val) {
if (val->type == LI_VALUE_LIST) {
GArray *list = val->data.list;
guint i;
for (i = 0; i < list->len; i++) {
liValue *subval = g_array_index(list, liValue*, i);
li_lua_push_value(L, subval);
}
return list->len;
} else {
return li_lua_push_value(L, val);
}
}
static gboolean lua_plugin_handle_setup(liServer *srv, liPlugin *p, liValue *val, gpointer userdata) {
lua_State *L = srv->L;
int lua_ref = GPOINTER_TO_INT(userdata);
int nargs, errfunc;
gboolean res;
UNUSED(p);
li_lua_lock(srv);
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_ref);
nargs = push_args(L, val);
errfunc = li_lua_push_traceback(L, 0);
if (lua_pcall(L, nargs, 1, errfunc)) {
ERROR(srv, "lua_pcall(): %s", lua_tostring(L, -1));
lua_pop(L, 1);
res = FALSE;
} else {
res = TRUE;
/* accept nil and true; everything else is "false" */
if (!lua_isnil(L, -1) && (!lua_isboolean(L, -1) || !lua_toboolean(L, -1))) {
res = FALSE;
}
lua_pop(L, 1);
}
lua_remove(L, errfunc);
li_lua_unlock(srv);
return res;
}
static liAction* lua_plugin_handle_action(liServer *srv, liPlugin* p, liValue *val, gpointer userdata) {
lua_State *L = srv->L;
int lua_ref = GPOINTER_TO_INT(userdata);
int nargs, errfunc;
liAction *res = NULL;
UNUSED(p);
li_lua_lock(srv);
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_ref);
nargs = push_args(L, val);
errfunc = li_lua_push_traceback(L, nargs);
if (lua_pcall(L, nargs, 1, errfunc)) {
ERROR(srv, "lua_pcall(): %s", lua_tostring(L, -1));
lua_pop(L, 1);
} else {
res = li_lua_get_action_ref(L, -1);
if (res == NULL) {
ERROR(srv, "%s", "lua plugin action-create callback didn't return an action");
}
lua_pop(L, 1);
}
lua_remove(L, errfunc);
li_lua_unlock(srv);
return res;
}
static void lua_plugin_free_data(liServer *srv, luaPlugin *lp) {
lua_State *L = srv->L;
guint i;
if (L) li_lua_lock(srv);
for (i = 0; i < lp->actions->len; i++) {
liPluginAction *pa = &g_array_index(lp->actions, liPluginAction, i);
int lua_ref = GPOINTER_TO_INT(pa->userdata);
if (L) luaL_unref(L, LUA_REGISTRYINDEX, lua_ref);
g_free((gchar*) pa->name);
}
g_array_free(lp->actions, TRUE);
for (i = 0; i < lp->setups->len; i++) {
liPluginSetup *ps = &g_array_index(lp->setups, liPluginSetup, i);
int lua_ref = GPOINTER_TO_INT(ps->userdata);
if (L) luaL_unref(L, LUA_REGISTRYINDEX, lua_ref);
g_free((gchar*) ps->name);
}
g_array_free(lp->setups, TRUE);
if (L) li_lua_unlock(srv);
if (lp->filename)
g_string_free(lp->filename, TRUE);
g_slice_free(luaPlugin, lp);
}
static luaPlugin* lua_plugin_create_data(liServer *srv, lua_State *L) {
luaPlugin *lp;
lp = g_slice_new0(luaPlugin);
lp->actions = g_array_new(TRUE, TRUE, sizeof(liPluginAction));
lp->setups = g_array_new(TRUE, TRUE, sizeof(liPluginSetup));
lua_getfield(L, LUA_GLOBALSINDEX, "actions");
if (lua_istable(L, -1)) {
liPluginAction plug_action;
int ndx;
plug_action.create_action = lua_plugin_handle_action;
ndx = li_lua_fixindex(L, -1);
lua_pushnil(L);
while (lua_next(L, ndx) != 0) {
switch (lua_type(L, -2)) {
case LUA_TSTRING:
plug_action.name = g_strdup(lua_tostring(L, -2));
plug_action.userdata = GINT_TO_POINTER(luaL_ref(L, LUA_REGISTRYINDEX));
g_array_append_val(lp->actions, plug_action);
break;
default:
ERROR(srv, "Unexpted key type in table 'actions': %s (%i) - skipping entry", lua_typename(L, -1), lua_type(L, -1));
lua_pop(L, 1);
break;
}
}
}
lua_pop(L, 1);
lua_getfield(L, LUA_GLOBALSINDEX, "setups");
if (lua_istable(L, -1)) {
liPluginSetup plug_setup;
int ndx;
plug_setup.setup = lua_plugin_handle_setup;
ndx = li_lua_fixindex(L, -1);
lua_pushnil(L);
while (lua_next(L, ndx) != 0) {
switch (lua_type(L, -2)) {
case LUA_TSTRING:
plug_setup.name = g_strdup(lua_tostring(L, -2));
plug_setup.userdata = GINT_TO_POINTER(luaL_ref(L, LUA_REGISTRYINDEX));
g_array_append_val(lp->setups, plug_setup);
break;
default:
ERROR(srv, "Unexpted key type in table 'setups': %s (%i) - skipping entry", lua_typename(L, -1), lua_type(L, -1));
lua_pop(L, 1);
break;
}
}
}
lua_pop(L, 1);
return lp;
}
static const liPluginOption lp_options[] = {
{ NULL, 0, NULL, NULL, NULL }
};
static void lua_plugin_free(liServer *srv, liPlugin *p) {
luaPlugin *lp = p->data;
lua_plugin_free_data(srv, lp);
}
static void lua_plugin_init(liServer *srv, liPlugin *p, gpointer userdata) {
luaPlugin *lp = userdata;
UNUSED(srv);
p->options = lp_options;
p->actions = &g_array_index(lp->actions, liPluginAction, 0);
p->setups = &g_array_index(lp->setups, liPluginSetup, 0);;
p->data = lp;
p->free = lua_plugin_free;
}
static gboolean lua_plugin_load(liServer *srv, liPlugin *p, GString *filename) {
int errfunc;
int lua_stack_top;
lua_State *L = srv->L;
luaPlugin *lp;
module_config *mc = p->data;
liPlugin *newp;
li_lua_lock(srv);
lua_stack_top = lua_gettop(L);
li_lua_new_globals(L);
if (0 != luaL_loadfile(L, filename->str)) {
ERROR(srv, "Loading lua plugin '%s' failed: %s", filename->str, lua_tostring(L, -1));
goto failed_unlock_lua;
}
DEBUG(srv, "Loaded lua plugin '%s'", filename->str);
li_lua_config_publish_str_hash(srv, L, srv->setups, li_lua_config_handle_server_setup);
lua_setfield(L, LUA_GLOBALSINDEX, "setup");
li_lua_config_publish_str_hash(srv, L, srv->actions, li_lua_config_handle_server_action);
lua_setfield(L, LUA_GLOBALSINDEX, "action");
li_lua_push_lvalues_dict(srv, L);
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setfenv(L, -2);
errfunc = li_lua_push_traceback(L, 0);
if (lua_pcall(L, 0, 0, errfunc)) {
ERROR(srv, "lua_pcall(): %s", lua_tostring(L, -1));
goto failed_unlock_lua;
}
lua_remove(L, errfunc);
if (NULL == (lp = lua_plugin_create_data(srv, L))) goto failed_unlock_lua;
if (NULL == (newp = li_plugin_register(srv, filename->str, lua_plugin_init, lp))) {
lua_plugin_free_data(srv, lp);
goto failed_unlock_lua;
}
g_ptr_array_add(mc->lua_plugins, newp);
li_lua_restore_globals(L);
li_lua_unlock(srv);
lp->filename = filename;
return TRUE;
failed_unlock_lua:
lua_pop(L, lua_gettop(L) - lua_stack_top);
li_lua_restore_globals(L);
li_lua_unlock(srv);
g_string_free(filename, TRUE);
return FALSE;
}
static gboolean lua_plugin(liServer *srv, liPlugin *p, liValue *val, gpointer userdata) {
liValue *v_filename = NULL, *v_options = NULL;
UNUSED(userdata);
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.plugin expects at least a filename, or a filename and some options");
return FALSE;
}
if (v_options && v_options->type != LI_VALUE_HASH) {
ERROR(srv, "%s", "lua.plugin expects options in a hash");
return FALSE;
}
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.plugin option '%s' expects positive integer as parameter", lon_ttl.str);
return FALSE;
}
ttl = value->data.number;
} else {
*/
ERROR(srv, "unknown option for lua.plugin '%s'", key->str);
return FALSE;
/*
}
*/
}
}
return lua_plugin_load(srv, p, li_value_extract_ptr(v_filename));
}
static const liPluginOption options[] = {
{ NULL, 0, NULL, NULL, NULL }
};
@ -210,6 +546,8 @@ static const liPluginAction actions[] = {
};
static const liPluginSetup setups[] = {
{ "lua.plugin", lua_plugin, NULL },
{ NULL, NULL, NULL }
};
@ -224,18 +562,38 @@ static void plugin_lua_init(liServer *srv, liPlugin *p, gpointer userdata) {
gboolean mod_lua_init(liModules *mods, liModule *mod) {
liPlugin *p;
UNUSED(mod);
MODULE_VERSION_CHECK(mods);
mod->config = li_plugin_register(mods->main, "mod_lua", plugin_lua_init, NULL);
p = li_plugin_register(mods->main, "mod_lua", plugin_lua_init, NULL);
if (NULL != p) {
module_config *mc = g_slice_new0(module_config);
mc->lua_plugins = g_ptr_array_new();
mc->main_plugin = p;
p->data = mc;
mod->config = mc;
}
return mod->config != NULL;
}
gboolean mod_lua_free(liModules *mods, liModule *mod) {
if (mod->config)
li_plugin_free(mods->main, mod->config);
if (mod->config) {
module_config *mc = mod->config;
guint i;
li_plugin_free(mods->main, mc->main_plugin);
for (i = 0; i < mc->lua_plugins->len; i++) {
li_plugin_free(mods->main, g_ptr_array_index(mc->lua_plugins, i));
}
g_ptr_array_free(mc->lua_plugins, TRUE);
g_slice_free(module_config, mc);
}
return TRUE;
}

Loading…
Cancel
Save