--- /dev/null +++ b/trigger/Makefile @@ -0,0 +1,44 @@ +include ../Makefile.inc +LUA_VERSION=5.1 +PREFIX_SEARCH=/usr /usr/local /opt/local +LUA_PLUGINDIR=$(firstword \ + $(foreach ldir,$(subst ;, ,$(shell lua -e 'print(package.cpath)')), \ + $(if $(findstring lib/lua/,$(ldir)),$(patsubst %/?.so,%,$(ldir))) \ + ) \ +) + +# find lua prefix +LUA_PREFIX=$(firstword \ + $(foreach prefix,$(PREFIX_SEARCH),\ + $(if $(wildcard $(prefix)/include/lua.h),$(prefix)) \ + ) \ +) + +libdir=$(prefix)/libs +luadir=$(if $(LUA_PLUGINDIR),$(LUA_PLUGINDIR),$(libdir)/lua/$(LUA_VERSION)) +luainc=$(shell pkg-config --silence-errors --cflags lua$(LUA_VERSION)) + +CPPFLAGS=-I.. $(if $(luainc),$(luainc), -I$(LUA_PREFIX)/include) +LIBS=-L.. -luci $(shell pkg-config --silence-errors --libs lua$(LUA_VERSION)) + +PLUGIN_LD=$(CC) +ifeq ($(OS),Darwin) + PLUGIN_LDFLAGS=-bundle +else + PLUGIN_LDFLAGS=-shared -Wl,-soname,$(SHLIB_FILE) +endif + +all: uci_trigger.so + +uci_trigger.so: uci_trigger.o + $(PLUGIN_LD) $(PLUGIN_LDFLAGS) -o $@ $^ $(LIBS) + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(FPIC) -c -o $@ $< + +install: + mkdir -p $(DESTDIR)$(luadir) + $(INSTALL) -m0644 uci_trigger.so $(DESTDIR)$(luadir)/ + +clean: + rm -f *.so *.o uci_trigger.so --- /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_history *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_history *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_history *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, +};