--- /dev/null +++ b/trigger/uci_trigger.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include +#include +#include "uci.h" + +// #define DEBUG + +static int refcount = 0; +static lua_State *gL = NULL; +static bool prepared = false; + +struct trigger_set_op { + struct uci_package *p; + struct uci_delta *h; +}; + +static int trigger_set(lua_State *L) +{ + struct trigger_set_op *so = + (struct trigger_set_op *)lua_touserdata(L, 1); + struct uci_package *p = so->p; + struct uci_delta *h = so->h; + struct uci_context *ctx = p->ctx; + + /* ignore non-standard savedirs/configdirs + * in order to not trigger events on uci state changes */ + if (strcmp(ctx->savedir, UCI_SAVEDIR) || + strcmp(ctx->confdir, UCI_CONFDIR)) + return 0; + + if (!prepared) { + lua_getglobal(L, "require"); + lua_pushstring(L, "uci.trigger"); + lua_call(L, 1, 0); + + lua_getglobal(L, "uci"); + lua_getfield(L, -1, "trigger"); + lua_getfield(L, -1, "load_modules"); + lua_call(L, 0, 0); + prepared = true; + } else { + lua_getglobal(L, "uci"); + lua_getfield(L, -1, "trigger"); + } + + lua_getfield(L, -1, "set"); + lua_createtable(L, 4, 0); + + lua_pushstring(L, p->e.name); + lua_rawseti(L, -2, 1); + if (h->section) { + lua_pushstring(L, h->section); + lua_rawseti(L, -2, 2); + } + if (h->e.name) { + lua_pushstring(L, h->e.name); + lua_rawseti(L, -2, 3); + } + if (h->value) { + lua_pushstring(L, h->value); + lua_rawseti(L, -2, 4); + } + lua_call(L, 1, 0); + lua_pop(L, 2); + return 0; +} + +#ifdef DEBUG + +static int report (lua_State *L, int status) { + if (status && !lua_isnil(L, -1)) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) msg = "(error object is not a string)"; + fprintf(stderr, "ERROR: %s\n", msg); + lua_pop(L, 1); + } + return status; +} + +#else + +static inline int report(lua_State *L, int status) { + return status; +} + +#endif + +static void trigger_set_hook(const struct uci_hook_ops *ops, struct uci_package *p, struct uci_delta *h) +{ + struct trigger_set_op so; + + so.p = p; + so.h = h; + report(gL, lua_cpcall(gL, &trigger_set, &so)); +} + +static struct uci_hook_ops hook = { + .set = trigger_set_hook, +}; + +static int trigger_attach(struct uci_context *ctx) +{ + if (!gL) { + gL = luaL_newstate(); + if (!gL) + return -1; + luaL_openlibs(gL); + + refcount++; + } + uci_add_hook(ctx, &hook); + return 0; +} + +static void trigger_detach(struct uci_context *ctx) +{ + if (gL && (--refcount <= 0)) { + lua_close(gL); + gL = NULL; + refcount = 0; + prepared = false; + } +} + +struct uci_plugin_ops uci_plugin = { + .attach = trigger_attach, + .detach = trigger_detach, +}; --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ ADD_EXECUTABLE(ucimap-example ucimap-exa TARGET_LINK_LIBRARIES(ucimap-example uci-static ucimap dl) ADD_SUBDIRECTORY(lua) +ADD_SUBDIRECTORY(trigger) INSTALL(FILES uci.h uci_config.h ucimap.h DESTINATION include/libubox --- /dev/null +++ b/trigger/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(uci C) + +SET(CMAKE_INSTALL_PREFIX /) + +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -I..) + +IF(APPLE) + SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") +ENDIF(APPLE) + +ADD_LIBRARY(uci_trigger MODULE uci_trigger.c) +SET_TARGET_PROPERTIES(uci_trigger PROPERTIES + OUTPUT_NAME uci_trigger + PREFIX "" +) +TARGET_LINK_LIBRARIES(uci_trigger uci) +