lua: add reference counting for strings - this will need A LOT of testing, but it...
[openwrt.git] / package / uci / patches / 110-plugin_support.patch
1 --- a/Makefile
2 +++ b/Makefile
3 @@ -7,7 +7,7 @@ DEBUG_TYPECAST=0
4  
5  include Makefile.inc
6  
7 -LIBS=-lc
8 +LIBS=-lc -ldl
9  SHLIB_FILE=libuci.$(SHLIB_EXT).$(VERSION)
10  
11  define add_feature
12 @@ -23,6 +23,7 @@ ucimap.o: ucimap.c uci.h uci_config.h uc
13  
14  uci_config.h: FORCE
15         @rm -f "$@.tmp"
16 +       @echo "#define UCI_PREFIX \"$(prefix)\"" > "$@.tmp"
17         $(call add_feature,PLUGIN_SUPPORT)
18         $(call add_feature,DEBUG)
19         $(call add_feature,DEBUG_TYPECAST)
20 @@ -33,10 +34,10 @@ uci_config.h: FORCE
21         fi
22  
23  uci: cli.o libuci.$(SHLIB_EXT)
24 -       $(CC) -o $@ $< -L. -luci
25 +       $(CC) -o $@ $< -L. -luci $(LIBS)
26  
27  uci-static: cli.o libuci.a
28 -       $(CC) $(CFLAGS) -o $@ $^
29 +       $(CC) $(CFLAGS) -o $@ $^ $(LIBS)
30  
31  libuci-static.o: libuci.c $(LIBUCI_DEPS)
32         $(CC) $(CFLAGS) -c -o $@ $<
33 --- a/cli.c
34 +++ b/cli.c
35 @@ -27,6 +27,7 @@ static enum {
36         CLI_FLAG_NOCOMMIT = (1 << 2),
37         CLI_FLAG_BATCH =    (1 << 3),
38         CLI_FLAG_SHOW_EXT = (1 << 4),
39 +       CLI_FLAG_NOPLUGINS= (1 << 5),
40  } flags;
41  
42  static FILE *input;
43 @@ -136,6 +137,7 @@ static void uci_usage(void)
44                 "\t-c <path>  set the search path for config files (default: /etc/config)\n"
45                 "\t-d <str>   set the delimiter for list values in uci show\n"
46                 "\t-f <file>  use <file> as input instead of stdin\n"
47 +               "\t-L         do not load any plugins\n"
48                 "\t-m         when importing, merge data into an existing package\n"
49                 "\t-n         name unnamed sections on export (default)\n"
50                 "\t-N         don't name unnamed sections\n"
51 @@ -603,7 +605,7 @@ int main(int argc, char **argv)
52                 return 1;
53         }
54  
55 -       while((c = getopt(argc, argv, "c:d:f:mnNp:P:sSqX")) != -1) {
56 +       while((c = getopt(argc, argv, "c:d:f:LmnNp:P:sSqX")) != -1) {
57                 switch(c) {
58                         case 'c':
59                                 uci_set_confdir(ctx, optarg);
60 @@ -618,6 +620,9 @@ int main(int argc, char **argv)
61                                         return 1;
62                                 }
63                                 break;
64 +                       case 'L':
65 +                               flags |= CLI_FLAG_NOPLUGINS;
66 +                               break;
67                         case 'm':
68                                 flags |= CLI_FLAG_MERGE;
69                                 break;
70 @@ -662,6 +667,10 @@ int main(int argc, char **argv)
71                 uci_usage();
72                 return 0;
73         }
74 +
75 +       if (!(flags & CLI_FLAG_NOPLUGINS))
76 +               uci_load_plugins(ctx, NULL);
77 +
78         ret = uci_cmd(argc - 1, argv + 1);
79         if (input != stdin)
80                 fclose(input);
81 --- a/history.c
82 +++ b/history.c
83 @@ -406,6 +406,17 @@ int uci_save(struct uci_context *ctx, st
84         if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
85                 UCI_THROW(ctx, UCI_ERR_MEM);
86  
87 +       uci_foreach_element(&ctx->hooks, tmp) {
88 +               struct uci_hook *hook = uci_to_hook(tmp);
89 +
90 +               if (!hook->ops->set)
91 +                       continue;
92 +
93 +               uci_foreach_element(&p->history, e) {
94 +                       hook->ops->set(hook->ops, p, uci_to_history(e));
95 +               }
96 +       }
97 +
98         ctx->err = 0;
99         UCI_TRAP_SAVE(ctx, done);
100         f = uci_open_stream(ctx, filename, SEEK_END, true, true);
101 --- a/libuci.c
102 +++ b/libuci.c
103 @@ -22,6 +22,8 @@
104  #include <string.h>
105  #include <stdlib.h>
106  #include <stdio.h>
107 +#include <dlfcn.h>
108 +#include <glob.h>
109  #include "uci.h"
110  
111  static const char *uci_confdir = UCI_CONFDIR;
112 @@ -39,6 +41,7 @@ static const char *uci_errstr[] = {
113  };
114  
115  static void uci_cleanup(struct uci_context *ctx);
116 +static void uci_unload_plugin(struct uci_context *ctx, struct uci_plugin *p);
117  
118  #include "uci_internal.h"
119  #include "util.c"
120 @@ -56,6 +59,8 @@ struct uci_context *uci_alloc_context(vo
121         uci_list_init(&ctx->root);
122         uci_list_init(&ctx->history_path);
123         uci_list_init(&ctx->backends);
124 +       uci_list_init(&ctx->hooks);
125 +       uci_list_init(&ctx->plugins);
126         ctx->flags = UCI_FLAG_STRICT | UCI_FLAG_SAVED_HISTORY;
127  
128         ctx->confdir = (char *) uci_confdir;
129 @@ -86,6 +91,9 @@ void uci_free_context(struct uci_context
130                 uci_free_element(e);
131         }
132         UCI_TRAP_RESTORE(ctx);
133 +       uci_foreach_element_safe(&ctx->root, tmp, e) {
134 +               uci_unload_plugin(ctx, uci_to_plugin(e));
135 +       }
136         free(ctx);
137  
138  ignore:
139 @@ -209,9 +217,16 @@ int uci_commit(struct uci_context *ctx, 
140  int uci_load(struct uci_context *ctx, const char *name, struct uci_package **package)
141  {
142         struct uci_package *p;
143 +       struct uci_element *e;
144 +
145         UCI_HANDLE_ERR(ctx);
146         UCI_ASSERT(ctx, ctx->backend && ctx->backend->load);
147         p = ctx->backend->load(ctx, name);
148 +       uci_foreach_element(&ctx->hooks, e) {
149 +               struct uci_hook *h = uci_to_hook(e);
150 +               if (h->ops->load)
151 +                       h->ops->load(h->ops, p);
152 +       }
153         if (package)
154                 *package = p;
155  
156 @@ -280,3 +295,94 @@ int uci_set_backend(struct uci_context *
157         ctx->backend = uci_to_backend(e);
158         return 0;
159  }
160 +
161 +int uci_add_hook(struct uci_context *ctx, const struct uci_hook_ops *ops)
162 +{
163 +       struct uci_element *e;
164 +       struct uci_hook *h;
165 +
166 +       UCI_HANDLE_ERR(ctx);
167 +
168 +       /* check for duplicate elements */
169 +       uci_foreach_element(&ctx->hooks, e) {
170 +               h = uci_to_hook(e);
171 +               if (h->ops == ops)
172 +                       return UCI_ERR_INVAL;
173 +       }
174 +
175 +       h = uci_alloc_element(ctx, hook, "", 0);
176 +       h->ops = ops;
177 +       uci_list_init(&h->e.list);
178 +       uci_list_add(&ctx->hooks, &h->e.list);
179 +
180 +       return 0;
181 +}
182 +
183 +int uci_remove_hook(struct uci_context *ctx, const struct uci_hook_ops *ops)
184 +{
185 +       struct uci_element *e;
186 +
187 +       uci_foreach_element(&ctx->hooks, e) {
188 +               struct uci_hook *h = uci_to_hook(e);
189 +               if (h->ops == ops) {
190 +                       uci_list_del(&e->list);
191 +                       return 0;
192 +               }
193 +       }
194 +       return UCI_ERR_NOTFOUND;
195 +}
196 +
197 +int uci_load_plugin(struct uci_context *ctx, const char *filename)
198 +{
199 +       struct uci_plugin *p;
200 +       const struct uci_plugin_ops *ops;
201 +       void *dlh;
202 +
203 +       UCI_HANDLE_ERR(ctx);
204 +       dlh = dlopen(filename, RTLD_GLOBAL|RTLD_NOW);
205 +       if (!dlh)
206 +               UCI_THROW(ctx, UCI_ERR_NOTFOUND);
207 +
208 +       ops = dlsym(dlh, "uci_plugin");
209 +       if (!ops || !ops->attach || (ops->attach(ctx) != 0)) {
210 +               if (!ops)
211 +                       fprintf(stderr, "No ops\n");
212 +               else if (!ops->attach)
213 +                       fprintf(stderr, "No attach\n");
214 +               else
215 +                       fprintf(stderr, "Other weirdness\n");
216 +               dlclose(dlh);
217 +               UCI_THROW(ctx, UCI_ERR_INVAL);
218 +       }
219 +
220 +       p = uci_alloc_element(ctx, plugin, filename, 0);
221 +       p->dlh = dlh;
222 +       p->ops = ops;
223 +       uci_list_add(&ctx->plugins, &p->e.list);
224 +
225 +       return 0;
226 +}
227 +
228 +static void uci_unload_plugin(struct uci_context *ctx, struct uci_plugin *p)
229 +{
230 +       if (p->ops->detach)
231 +               p->ops->detach(ctx);
232 +       dlclose(p->dlh);
233 +       uci_free_element(&p->e);
234 +}
235 +
236 +int uci_load_plugins(struct uci_context *ctx, const char *pattern)
237 +{
238 +       glob_t gl;
239 +       int i;
240 +
241 +       if (!pattern)
242 +               pattern = UCI_PREFIX "/lib/uci_*.so";
243 +
244 +       memset(&gl, 0, sizeof(gl));
245 +       glob(pattern, 0, NULL, &gl);
246 +       for (i = 0; i < gl.gl_pathc; i++)
247 +               uci_load_plugin(ctx, gl.gl_pathv[i]);
248 +
249 +       return 0;
250 +}
251 --- a/uci.h
252 +++ b/uci.h
253 @@ -56,6 +56,8 @@ struct uci_list
254  };
255  
256  struct uci_ptr;
257 +struct uci_plugin;
258 +struct uci_hook_ops;
259  struct uci_element;
260  struct uci_package;
261  struct uci_section;
262 @@ -275,6 +277,43 @@ extern int uci_set_backend(struct uci_co
263   */
264  extern bool uci_validate_text(const char *str);
265  
266 +
267 +/**
268 + * uci_add_hook: add a uci hook
269 + * @ctx: uci context
270 + * @ops: uci hook ops
271 + *
272 + * NB: allocated and freed by the caller
273 + */
274 +extern int uci_add_hook(struct uci_context *ctx, const struct uci_hook_ops *ops);
275 +
276 +/**
277 + * uci_remove_hook: remove a uci hook
278 + * @ctx: uci context
279 + * @ops: uci hook ops
280 + */
281 +extern int uci_remove_hook(struct uci_context *ctx, const struct uci_hook_ops *ops);
282 +
283 +/**
284 + * uci_load_plugin: load an uci plugin
285 + * @ctx: uci context
286 + * @filename: path to the uci plugin
287 + *
288 + * NB: plugin will be unloaded automatically when the context is freed
289 + */
290 +int uci_load_plugin(struct uci_context *ctx, const char *filename);
291 +
292 +/**
293 + * uci_load_plugins: load all uci plugins from a directory
294 + * @ctx: uci context
295 + * @pattern: pattern of uci plugin files (optional)
296 + *
297 + * if pattern is NULL, then uci_load_plugins will call uci_load_plugin
298 + * for uci_*.so in <prefix>/lib/
299 + */
300 +int uci_load_plugins(struct uci_context *ctx, const char *pattern);
301 +
302 +
303  /* UCI data structures */
304  enum uci_type {
305         UCI_TYPE_UNSPEC = 0,
306 @@ -285,6 +324,8 @@ enum uci_type {
307         UCI_TYPE_PATH = 5,
308         UCI_TYPE_BACKEND = 6,
309         UCI_TYPE_ITEM = 7,
310 +       UCI_TYPE_HOOK = 8,
311 +       UCI_TYPE_PLUGIN = 9,
312  };
313  
314  enum uci_option_type {
315 @@ -346,6 +387,9 @@ struct uci_context
316         bool internal, nested;
317         char *buf;
318         int bufsz;
319 +
320 +       struct uci_list hooks;
321 +       struct uci_list plugins;
322  };
323  
324  struct uci_package
325 @@ -420,6 +464,31 @@ struct uci_ptr
326         const char *value;
327  };
328  
329 +struct uci_hook_ops
330 +{
331 +       void (*load)(const struct uci_hook_ops *ops, struct uci_package *p);
332 +       void (*set)(const struct uci_hook_ops *ops, struct uci_package *p, struct uci_history *e);
333 +};
334 +
335 +struct uci_hook
336 +{
337 +       struct uci_element e;
338 +       const struct uci_hook_ops *ops;
339 +};
340 +
341 +struct uci_plugin_ops
342 +{
343 +       int (*attach)(struct uci_context *ctx);
344 +       void (*detach)(struct uci_context *ctx);
345 +};
346 +
347 +struct uci_plugin
348 +{
349 +       struct uci_element e;
350 +       const struct uci_plugin_ops *ops;
351 +       void *dlh;
352 +};
353 +
354  
355  /* linked list handling */
356  #ifndef offsetof
357 @@ -490,6 +559,8 @@ struct uci_ptr
358  #define uci_type_package UCI_TYPE_PACKAGE
359  #define uci_type_section UCI_TYPE_SECTION
360  #define uci_type_option UCI_TYPE_OPTION
361 +#define uci_type_hook UCI_TYPE_HOOK
362 +#define uci_type_plugin UCI_TYPE_PLUGIN
363  
364  /* element typecasting */
365  #ifdef UCI_DEBUG_TYPECAST
366 @@ -499,6 +570,8 @@ static const char *uci_typestr[] = {
367         [uci_type_package] = "package",
368         [uci_type_section] = "section",
369         [uci_type_option] = "option",
370 +       [uci_type_hook] = "hook",
371 +       [uci_type_plugin] = "plugin",
372  };
373  
374  static void uci_typecast_error(int from, int to)
375 @@ -520,6 +593,8 @@ BUILD_CAST(history)
376  BUILD_CAST(package)
377  BUILD_CAST(section)
378  BUILD_CAST(option)
379 +BUILD_CAST(hook)
380 +BUILD_CAST(plugin)
381  
382  #else
383  #define uci_to_backend(ptr) container_of(ptr, struct uci_backend, e)
384 @@ -527,6 +602,8 @@ BUILD_CAST(option)
385  #define uci_to_package(ptr) container_of(ptr, struct uci_package, e)
386  #define uci_to_section(ptr) container_of(ptr, struct uci_section, e)
387  #define uci_to_option(ptr)  container_of(ptr, struct uci_option, e)
388 +#define uci_to_hook(ptr)    container_of(ptr, struct uci_hook, e)
389 +#define uci_to_plugin(ptr)  container_of(ptr, struct uci_plugin, e)
390  #endif
391  
392  /**
393 --- a/lua/uci.c
394 +++ b/lua/uci.c
395 @@ -765,6 +765,20 @@ uci_lua_add_history(lua_State *L)
396  }
397  
398  static int
399 +uci_lua_load_plugins(lua_State *L)
400 +{
401 +       struct uci_context *ctx;
402 +       int ret, offset = 0;
403 +       const char *str = NULL;
404 +
405 +       ctx = find_context(L, &offset);
406 +       if (lua_isstring(L, -1))
407 +               str = lua_tostring(L, -1);
408 +       ret = uci_load_plugins(ctx, str);
409 +       return uci_push_status(L, ctx, false);
410 +}
411 +
412 +static int
413  uci_lua_set_savedir(lua_State *L)
414  {
415         struct uci_context *ctx;
416 @@ -831,6 +845,7 @@ static const luaL_Reg uci[] = {
417         { "changes", uci_lua_changes },
418         { "foreach", uci_lua_foreach },
419         { "add_history", uci_lua_add_history },
420 +       { "load_plugins", uci_lua_load_plugins },
421         { "get_confdir", uci_lua_get_confdir },
422         { "set_confdir", uci_lua_set_confdir },
423         { "get_savedir", uci_lua_get_savedir },