From: Felix Fietkau Date: Mon, 8 Jun 2009 00:36:09 +0000 (+0200) Subject: add plugin support X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=commitdiff_plain;h=476db477246a1438f3b2535bf37ab7f9130827c8 add plugin support --- diff --git a/Makefile b/Makefile index cbdf08e..c4d8577 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ DEBUG_TYPECAST=0 include Makefile.inc -LIBS=-lc +LIBS=-lc -ldl SHLIB_FILE=libuci.$(SHLIB_EXT).$(VERSION) define add_feature @@ -23,6 +23,7 @@ ucimap.o: ucimap.c uci.h uci_config.h ucimap.h uci_list.h uci_config.h: FORCE @rm -f "$@.tmp" + @echo "#define UCI_PREFIX \"$(prefix)\"" > "$@.tmp" $(call add_feature,PLUGIN_SUPPORT) $(call add_feature,DEBUG) $(call add_feature,DEBUG_TYPECAST) @@ -33,10 +34,10 @@ uci_config.h: FORCE fi uci: cli.o libuci.$(SHLIB_EXT) - $(CC) -o $@ $< -L. -luci + $(CC) -o $@ $< -L. -luci $(LIBS) uci-static: cli.o libuci.a - $(CC) $(CFLAGS) -o $@ $^ + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) libuci-static.o: libuci.c $(LIBUCI_DEPS) $(CC) $(CFLAGS) -c -o $@ $< diff --git a/cli.c b/cli.c index 0b2a76d..7bef7a9 100644 --- a/cli.c +++ b/cli.c @@ -27,6 +27,7 @@ static enum { CLI_FLAG_NOCOMMIT = (1 << 2), CLI_FLAG_BATCH = (1 << 3), CLI_FLAG_SHOW_EXT = (1 << 4), + CLI_FLAG_NOPLUGINS= (1 << 5), } flags; static FILE *input; @@ -138,6 +139,7 @@ static void uci_usage(void) "\t-c set the search path for config files (default: /etc/config)\n" "\t-d set the delimiter for list values in uci show\n" "\t-f use as input instead of stdin\n" + "\t-L do not load any plugins\n" "\t-m when importing, merge data into an existing package\n" "\t-n name unnamed sections on export (default)\n" "\t-N don't name unnamed sections\n" @@ -616,7 +618,7 @@ int main(int argc, char **argv) return 1; } - while((c = getopt(argc, argv, "c:d:f:mnNp:P:sSqX")) != -1) { + while((c = getopt(argc, argv, "c:d:f:LmnNp:P:sSqX")) != -1) { switch(c) { case 'c': uci_set_confdir(ctx, optarg); @@ -631,6 +633,9 @@ int main(int argc, char **argv) return 1; } break; + case 'L': + flags |= CLI_FLAG_NOPLUGINS; + break; case 'm': flags |= CLI_FLAG_MERGE; break; @@ -675,6 +680,10 @@ int main(int argc, char **argv) uci_usage(); return 0; } + + if (!(flags & CLI_FLAG_NOPLUGINS)) + uci_load_plugins(ctx, NULL); + ret = uci_cmd(argc - 1, argv + 1); if (input != stdin) fclose(input); diff --git a/history.c b/history.c index ae36217..c698192 100644 --- a/history.c +++ b/history.c @@ -419,6 +419,17 @@ int uci_save(struct uci_context *ctx, struct uci_package *p) if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename) UCI_THROW(ctx, UCI_ERR_MEM); + uci_foreach_element(&ctx->hooks, tmp) { + struct uci_hook *hook = uci_to_hook(tmp); + + if (!hook->ops->set) + continue; + + uci_foreach_element(&p->history, e) { + hook->ops->set(hook->ops, p, uci_to_history(e)); + } + } + ctx->err = 0; UCI_TRAP_SAVE(ctx, done); f = uci_open_stream(ctx, filename, SEEK_END, true, true); diff --git a/libuci.c b/libuci.c index 947b029..a3dde2e 100644 --- a/libuci.c +++ b/libuci.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "uci.h" static const char *uci_confdir = UCI_CONFDIR; @@ -39,6 +41,7 @@ static const char *uci_errstr[] = { }; static void uci_cleanup(struct uci_context *ctx); +static void uci_unload_plugin(struct uci_context *ctx, struct uci_plugin *p); #include "uci_internal.h" #include "util.c" @@ -56,6 +59,8 @@ struct uci_context *uci_alloc_context(void) uci_list_init(&ctx->root); uci_list_init(&ctx->history_path); uci_list_init(&ctx->backends); + uci_list_init(&ctx->hooks); + uci_list_init(&ctx->plugins); ctx->flags = UCI_FLAG_STRICT | UCI_FLAG_SAVED_HISTORY; ctx->confdir = (char *) uci_confdir; @@ -86,6 +91,9 @@ void uci_free_context(struct uci_context *ctx) uci_free_element(e); } UCI_TRAP_RESTORE(ctx); + uci_foreach_element_safe(&ctx->root, tmp, e) { + uci_unload_plugin(ctx, uci_to_plugin(e)); + } free(ctx); ignore: @@ -209,9 +217,16 @@ int uci_commit(struct uci_context *ctx, struct uci_package **package, bool overw int uci_load(struct uci_context *ctx, const char *name, struct uci_package **package) { struct uci_package *p; + struct uci_element *e; + UCI_HANDLE_ERR(ctx); UCI_ASSERT(ctx, ctx->backend && ctx->backend->load); p = ctx->backend->load(ctx, name); + uci_foreach_element(&ctx->hooks, e) { + struct uci_hook *h = uci_to_hook(e); + if (h->ops->load) + h->ops->load(h->ops, p); + } if (package) *package = p; @@ -280,3 +295,94 @@ int uci_set_backend(struct uci_context *ctx, const char *name) ctx->backend = uci_to_backend(e); return 0; } + +int uci_add_hook(struct uci_context *ctx, const struct uci_hook_ops *ops) +{ + struct uci_element *e; + struct uci_hook *h; + + UCI_HANDLE_ERR(ctx); + + /* check for duplicate elements */ + uci_foreach_element(&ctx->hooks, e) { + h = uci_to_hook(e); + if (h->ops == ops) + return UCI_ERR_INVAL; + } + + h = uci_alloc_element(ctx, hook, "", 0); + h->ops = ops; + uci_list_init(&h->e.list); + uci_list_add(&ctx->hooks, &h->e.list); + + return 0; +} + +int uci_remove_hook(struct uci_context *ctx, const struct uci_hook_ops *ops) +{ + struct uci_element *e; + + uci_foreach_element(&ctx->hooks, e) { + struct uci_hook *h = uci_to_hook(e); + if (h->ops == ops) { + uci_list_del(&e->list); + return 0; + } + } + return UCI_ERR_NOTFOUND; +} + +int uci_load_plugin(struct uci_context *ctx, const char *filename) +{ + struct uci_plugin *p; + const struct uci_plugin_ops *ops; + void *dlh; + + UCI_HANDLE_ERR(ctx); + dlh = dlopen(filename, RTLD_GLOBAL|RTLD_NOW); + if (!dlh) + UCI_THROW(ctx, UCI_ERR_NOTFOUND); + + ops = dlsym(dlh, "uci_plugin"); + if (!ops || !ops->attach || (ops->attach(ctx) != 0)) { + if (!ops) + fprintf(stderr, "No ops\n"); + else if (!ops->attach) + fprintf(stderr, "No attach\n"); + else + fprintf(stderr, "Other weirdness\n"); + dlclose(dlh); + UCI_THROW(ctx, UCI_ERR_INVAL); + } + + p = uci_alloc_element(ctx, plugin, filename, 0); + p->dlh = dlh; + p->ops = ops; + uci_list_add(&ctx->plugins, &p->e.list); + + return 0; +} + +static void uci_unload_plugin(struct uci_context *ctx, struct uci_plugin *p) +{ + if (p->ops->detach) + p->ops->detach(ctx); + dlclose(p->dlh); + uci_free_element(&p->e); +} + +int uci_load_plugins(struct uci_context *ctx, const char *pattern) +{ + glob_t gl; + int i; + + if (!pattern) + pattern = UCI_PREFIX "/lib/uci_*.so"; + + memset(&gl, 0, sizeof(gl)); + glob(pattern, 0, NULL, &gl); + for (i = 0; i < gl.gl_pathc; i++) + uci_load_plugin(ctx, gl.gl_pathv[i]); + + return 0; +} diff --git a/lua/uci.c b/lua/uci.c index 4714bc7..98f1e15 100644 --- a/lua/uci.c +++ b/lua/uci.c @@ -814,6 +814,20 @@ uci_lua_add_history(lua_State *L) } static int +uci_lua_load_plugins(lua_State *L) +{ + struct uci_context *ctx; + int ret, offset = 0; + const char *str = NULL; + + ctx = find_context(L, &offset); + if (lua_isstring(L, -1)) + str = lua_tostring(L, -1); + ret = uci_load_plugins(ctx, str); + return uci_push_status(L, ctx, false); +} + +static int uci_lua_set_savedir(lua_State *L) { struct uci_context *ctx; @@ -881,6 +895,7 @@ static const luaL_Reg uci[] = { { "changes", uci_lua_changes }, { "foreach", uci_lua_foreach }, { "add_history", uci_lua_add_history }, + { "load_plugins", uci_lua_load_plugins }, { "get_confdir", uci_lua_get_confdir }, { "set_confdir", uci_lua_set_confdir }, { "get_savedir", uci_lua_get_savedir }, diff --git a/uci.h b/uci.h index a04da29..74bbdff 100644 --- a/uci.h +++ b/uci.h @@ -56,6 +56,8 @@ struct uci_list }; struct uci_ptr; +struct uci_plugin; +struct uci_hook_ops; struct uci_element; struct uci_package; struct uci_section; @@ -283,6 +285,43 @@ extern int uci_set_backend(struct uci_context *ctx, const char *name); */ extern bool uci_validate_text(const char *str); + +/** + * uci_add_hook: add a uci hook + * @ctx: uci context + * @ops: uci hook ops + * + * NB: allocated and freed by the caller + */ +extern int uci_add_hook(struct uci_context *ctx, const struct uci_hook_ops *ops); + +/** + * uci_remove_hook: remove a uci hook + * @ctx: uci context + * @ops: uci hook ops + */ +extern int uci_remove_hook(struct uci_context *ctx, const struct uci_hook_ops *ops); + +/** + * uci_load_plugin: load an uci plugin + * @ctx: uci context + * @filename: path to the uci plugin + * + * NB: plugin will be unloaded automatically when the context is freed + */ +int uci_load_plugin(struct uci_context *ctx, const char *filename); + +/** + * uci_load_plugins: load all uci plugins from a directory + * @ctx: uci context + * @pattern: pattern of uci plugin files (optional) + * + * if pattern is NULL, then uci_load_plugins will call uci_load_plugin + * for uci_*.so in /lib/ + */ +int uci_load_plugins(struct uci_context *ctx, const char *pattern); + + /* UCI data structures */ enum uci_type { UCI_TYPE_UNSPEC = 0, @@ -293,6 +332,8 @@ enum uci_type { UCI_TYPE_PATH = 5, UCI_TYPE_BACKEND = 6, UCI_TYPE_ITEM = 7, + UCI_TYPE_HOOK = 8, + UCI_TYPE_PLUGIN = 9, }; enum uci_option_type { @@ -354,6 +395,9 @@ struct uci_context bool internal, nested; char *buf; int bufsz; + + struct uci_list hooks; + struct uci_list plugins; }; struct uci_package @@ -429,6 +473,31 @@ struct uci_ptr const char *value; }; +struct uci_hook_ops +{ + void (*load)(const struct uci_hook_ops *ops, struct uci_package *p); + void (*set)(const struct uci_hook_ops *ops, struct uci_package *p, struct uci_history *e); +}; + +struct uci_hook +{ + struct uci_element e; + const struct uci_hook_ops *ops; +}; + +struct uci_plugin_ops +{ + int (*attach)(struct uci_context *ctx); + void (*detach)(struct uci_context *ctx); +}; + +struct uci_plugin +{ + struct uci_element e; + const struct uci_plugin_ops *ops; + void *dlh; +}; + /* linked list handling */ #ifndef offsetof @@ -499,6 +568,8 @@ struct uci_ptr #define uci_type_package UCI_TYPE_PACKAGE #define uci_type_section UCI_TYPE_SECTION #define uci_type_option UCI_TYPE_OPTION +#define uci_type_hook UCI_TYPE_HOOK +#define uci_type_plugin UCI_TYPE_PLUGIN /* element typecasting */ #ifdef UCI_DEBUG_TYPECAST @@ -508,6 +579,8 @@ static const char *uci_typestr[] = { [uci_type_package] = "package", [uci_type_section] = "section", [uci_type_option] = "option", + [uci_type_hook] = "hook", + [uci_type_plugin] = "plugin", }; static void uci_typecast_error(int from, int to) @@ -529,6 +602,8 @@ BUILD_CAST(history) BUILD_CAST(package) BUILD_CAST(section) BUILD_CAST(option) +BUILD_CAST(hook) +BUILD_CAST(plugin) #else #define uci_to_backend(ptr) container_of(ptr, struct uci_backend, e) @@ -536,6 +611,8 @@ BUILD_CAST(option) #define uci_to_package(ptr) container_of(ptr, struct uci_package, e) #define uci_to_section(ptr) container_of(ptr, struct uci_section, e) #define uci_to_option(ptr) container_of(ptr, struct uci_option, e) +#define uci_to_hook(ptr) container_of(ptr, struct uci_hook, e) +#define uci_to_plugin(ptr) container_of(ptr, struct uci_plugin, e) #endif /**