lua binding: add .name attribute for sections pushed by uci.foreach and uci.get_all
[project/uci.git] / lua / uci.c
1 /*
2  * libuci plugin for Lua
3  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <sys/types.h>
16 #include <sys/time.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <errno.h>
23
24 #include <lauxlib.h>
25 #include <uci.h>
26
27 #define MODNAME        "uci"
28 //#define DEBUG 1
29
30 #ifdef DEBUG
31 #define DPRINTF(...) fprintf(stderr, __VA_ARGS__)
32 #else
33 #define DPRINTF(...) do {} while (0)
34 #endif
35
36 static struct uci_context *ctx = NULL;
37 enum autoload {
38         AUTOLOAD_OFF = 0,
39         AUTOLOAD_ON = 1,
40         AUTOLOAD_FORCE = 2
41 };
42
43 static struct uci_package *
44 find_package(lua_State *L, const char *name, enum autoload al)
45 {
46         struct uci_package *p = NULL;
47         struct uci_element *e;
48
49         uci_foreach_element(&ctx->root, e) {
50                 if (strcmp(e->name, name) != 0)
51                         continue;
52
53                 p = uci_to_package(e);
54                 goto done;
55         }
56
57         if (al == AUTOLOAD_FORCE)
58                 uci_load(ctx, name, &p);
59         else if (al) {
60                 do {
61                         lua_getfield(L, LUA_GLOBALSINDEX, "uci");
62                         lua_getfield(L, -1, "autoload");
63                         if (!lua_isboolean(L, -1))
64                                 break;
65
66                         if (!lua_toboolean(L, -1))
67                                 break;
68
69                         uci_load(ctx, name, &p);
70                 } while (0);
71                 lua_pop(L, 2);
72         }
73
74 done:
75         return p;
76 }
77
78 static void uci_lua_perror(lua_State *L, char *name)
79 {
80         lua_getfield(L, LUA_GLOBALSINDEX, "uci");
81         lua_getfield(L, -1, "warn");
82         if (!lua_isboolean(L, -1))
83                 goto done;
84         if (lua_toboolean(L, -1) != 1)
85                 goto done;
86         uci_perror(ctx, name);
87 done:
88         lua_pop(L, 2);
89 }
90
91 static void uci_push_section(lua_State *L, struct uci_section *s)
92 {
93         struct uci_element *e;
94
95         lua_newtable(L);
96         lua_pushstring(L, s->type);
97         lua_setfield(L, -2, ".type");
98         lua_pushstring(L, s->e.name);
99         lua_setfield(L, -2, ".name");
100
101         uci_foreach_element(&s->options, e) {
102                 struct uci_option *o = uci_to_option(e);
103                 lua_pushstring(L, o->value);
104                 lua_setfield(L, -2, o->e.name);
105         }
106 }
107
108 static void uci_push_package(lua_State *L, struct uci_package *p)
109 {
110         struct uci_element *e;
111         int i = 0;
112
113         lua_newtable(L);
114         uci_foreach_element(&p->sections, e) {
115                 i++;
116                 uci_push_section(L, uci_to_section(e));
117                 lua_setfield(L, -2, e->name);
118         }
119 }
120
121 static int
122 uci_lua_unload(lua_State *L)
123 {
124         struct uci_package *p;
125         const char *s;
126
127         luaL_checkstring(L, 1);
128         s = lua_tostring(L, -1);
129         p = find_package(L, s, AUTOLOAD_OFF);
130         if (p) {
131                 uci_unload(ctx, p);
132                 lua_pushboolean(L, 1);
133         } else {
134                 lua_pushboolean(L, 0);
135         }
136         return 1;
137 }
138
139 static int
140 uci_lua_load(lua_State *L)
141 {
142         struct uci_package *p = NULL;
143         const char *s;
144
145         uci_lua_unload(L);
146         lua_pop(L, 1); /* bool ret value of unload */
147         s = lua_tostring(L, -1);
148
149         if (uci_load(ctx, s, &p)) {
150                 uci_lua_perror(L, "uci.load");
151                 lua_pushboolean(L, 0);
152         } else {
153                 lua_pushboolean(L, 1);
154         }
155
156         return 1;
157 }
158
159
160 static int
161 uci_lua_foreach(lua_State *L)
162 {
163         struct uci_package *p;
164         struct uci_element *e;
165         const char *package, *type;
166         bool ret = false;
167
168         package = luaL_checkstring(L, 1);
169
170         if (lua_isnil(L, 2))
171                 type = NULL;
172         else
173                 type = luaL_checkstring(L, 2);
174
175         if (!lua_isfunction(L, 3) || !package)
176                 luaL_error(L, "Invalid argument");
177
178         p = find_package(L, package, AUTOLOAD_ON);
179         if (!p)
180                 goto done;
181
182         uci_foreach_element(&p->sections, e) {
183                 struct uci_section *s = uci_to_section(e);
184
185                 if (type && (strcmp(s->type, type) != 0))
186                         continue;
187
188                 lua_pushvalue(L, 3); /* iterator function */
189                 uci_push_section(L, s);
190                 if (lua_pcall(L, 1, 0, 0) == 0)
191                         ret = true;
192         }
193
194 done:
195         lua_pushboolean(L, ret);
196         return 1;
197 }
198
199 static int
200 uci_lua_get_any(lua_State *L, bool all)
201 {
202         struct uci_element *e = NULL;
203         struct uci_package *p = NULL;
204         const char *package = NULL;
205         const char *section = NULL;
206         const char *option = NULL;
207         char *s;
208         int err = UCI_ERR_MEM;
209         int n;
210
211         n = lua_gettop(L);
212
213         luaL_checkstring(L, 1);
214         s = strdup(lua_tostring(L, 1));
215         if (!s)
216                 goto error;
217
218         if (n > 1) {
219                 package = luaL_checkstring(L, 1);
220                 section = luaL_checkstring(L, 2);
221                 if (n > 2)
222                         option = luaL_checkstring(L, 3);
223         } else {
224                 if ((err = uci_parse_tuple(ctx, s, (char **) &package, (char **) &section, (char **) &option, NULL)))
225                         goto error;
226         }
227
228         if (!all && (section == NULL)) {
229                 err = UCI_ERR_INVAL;
230                 goto error;
231         }
232
233         p = find_package(L, package, AUTOLOAD_ON);
234         if (!p) {
235                 err = UCI_ERR_NOTFOUND;
236                 goto error;
237         }
238
239         if (section) {
240                 if ((err = uci_lookup(ctx, &e, p, section, option)))
241                         goto error;
242         } else {
243                 e = &p->e;
244         }
245
246         switch(e->type) {
247                 case UCI_TYPE_PACKAGE:
248                         uci_push_package(L, p);
249                         break;
250                 case UCI_TYPE_SECTION:
251                         if (all)
252                                 uci_push_section(L, uci_to_section(e));
253                         else
254                                 lua_pushstring(L, uci_to_section(e)->type);
255                         break;
256                 case UCI_TYPE_OPTION:
257                         lua_pushstring(L, uci_to_option(e)->value);
258                         break;
259                 default:
260                         err = UCI_ERR_INVAL;
261                         goto error;
262         }
263 error:
264         if (s)
265                 free(s);
266
267         switch(err) {
268         default:
269                 ctx->err = err;
270                 uci_lua_perror(L, "uci.get");
271                 /* fall through */
272         case UCI_ERR_NOTFOUND:
273                 lua_pushnil(L);
274                 /* fall through */
275         case 0:
276                 return 1;
277         }
278 }
279
280 static int
281 uci_lua_get(lua_State *L)
282 {
283         return uci_lua_get_any(L, false);
284 }
285
286 static int
287 uci_lua_get_all(lua_State *L)
288 {
289         return uci_lua_get_any(L, true);
290 }
291
292 static int
293 uci_lua_add(lua_State *L)
294 {
295         struct uci_section *s = NULL;
296         struct uci_package *p;
297         const char *package;
298         const char *type;
299         const char *name = NULL;
300
301         do {
302                 package = luaL_checkstring(L, 1);
303                 type = luaL_checkstring(L, 2);
304                 p = find_package(L, package, AUTOLOAD_ON);
305                 if (!p)
306                         break;
307
308                 if (uci_add_section(ctx, p, type, &s) || !s)
309                         break;
310
311                 name = s->e.name;
312         } while (0);
313
314         lua_pushstring(L, name);
315         return 1;
316 }
317
318 static int
319 uci_lua_delete(lua_State *L)
320 {
321         const char *package = NULL;
322         const char *section = NULL;
323         const char *option = NULL;
324         struct uci_package *p;
325         const char *s;
326         int err = UCI_ERR_MEM;
327         int nargs;
328
329         nargs = lua_gettop(L);
330         s = luaL_checkstring(L, 1);
331         switch(nargs) {
332         case 1:
333                 /* Format: uci.delete("p.s[.o]") */
334                 s = strdup(s);
335                 if (!s)
336                         goto error;
337
338                 if ((err = uci_parse_tuple(ctx, (char *) s, (char **) &package, (char **) &section, (char **) &option, NULL)))
339                         goto error;
340                 break;
341         case 3:
342                 /* Format: uci.delete("p", "s", "o") */
343                 option = luaL_checkstring(L, 3);
344                 /* fall through */
345         case 2:
346                 /* Format: uci.delete("p", "s") */
347                 section = luaL_checkstring(L, 2);
348                 package = s;
349                 break;
350         default:
351                 err = UCI_ERR_INVAL;
352                 goto error;
353         }
354
355         p = find_package(L, package, AUTOLOAD_ON);
356         if (!p) {
357                 err = UCI_ERR_NOTFOUND;
358                 goto error;
359         }
360         err = uci_delete(ctx, p, section, option);
361
362 error:
363         if (err)
364                 uci_lua_perror(L, "uci.set");
365         lua_pushboolean(L, (err == 0));
366         return 1;
367 }
368
369 static int
370 uci_lua_set(lua_State *L)
371 {
372         struct uci_package *p;
373         const char *package = NULL;
374         const char *section = NULL;
375         const char *option = NULL;
376         const char *value = NULL;
377         const char *s;
378         int err = UCI_ERR_MEM;
379         int nargs;
380
381         nargs = lua_gettop(L);
382
383         s = luaL_checkstring(L, 1);
384         switch(nargs) {
385         case 1:
386                 /* Format: uci.set("p.s.o=v") or uci.set("p.s=v") */
387                 s = strdup(s);
388                 if (!s)
389                         goto error;
390
391                 if ((err = uci_parse_tuple(ctx, (char *) s, (char **) &package, (char **) &section, (char **) &option, (char **) &value)))
392                         goto error;
393                 break;
394         case 4:
395                 /* Format: uci.set("p", "s", "o", "v") */
396                 option = luaL_checkstring(L, 3);
397                 /* fall through */
398         case 3:
399                 /* Format: uci.set("p", "s", "v") */
400                 package = s;
401                 section = luaL_checkstring(L, 2);
402                 value = luaL_checkstring(L, nargs);
403                 break;
404         default:
405                 err = UCI_ERR_INVAL;
406                 goto error;
407         }
408
409         if ((section == NULL) || (value == NULL)) {
410                 err = UCI_ERR_INVAL;
411                 goto error;
412         }
413
414         p = find_package(L, package, AUTOLOAD_ON);
415         if (!p) {
416                 err = UCI_ERR_NOTFOUND;
417                 goto error;
418         }
419         err = uci_set(ctx, p, section, option, value, NULL);
420
421 error:
422         if (err)
423                 uci_lua_perror(L, "uci.set");
424         lua_pushboolean(L, (err == 0));
425         return 1;
426 }
427
428 enum pkg_cmd {
429         CMD_SAVE,
430         CMD_COMMIT,
431         CMD_REVERT
432 };
433
434 static int
435 uci_lua_package_cmd(lua_State *L, enum pkg_cmd cmd)
436 {
437         struct uci_element *e, *tmp;
438         const char *s = NULL;
439         const char *section = NULL;
440         const char *option = NULL;
441         int failed = 0;
442         int nargs;
443
444         nargs = lua_gettop(L);
445         switch(nargs) {
446         case 3:
447                 if (cmd != CMD_REVERT)
448                         goto err;
449                 luaL_checkstring(L, 1);
450                 option = lua_tostring(L, -1);
451                 lua_pop(L, 1);
452                 /* fall through */
453         case 2:
454                 if (cmd != CMD_REVERT)
455                         goto err;
456                 luaL_checkstring(L, 1);
457                 section = lua_tostring(L, -1);
458                 lua_pop(L, 1);
459                 /* fall through */
460         case 1:
461                 luaL_checkstring(L, 1);
462                 s = lua_tostring(L, -1);
463                 lua_pop(L, 1);
464                 break;
465         case 0:
466                 break;
467         default:
468                 err:
469                 luaL_error(L, "Invalid argument count");
470                 break;
471         }
472
473         uci_foreach_element_safe(&ctx->root, tmp, e) {
474                 struct uci_package *p = uci_to_package(e);
475                 int ret = UCI_ERR_INVAL;
476
477                 if (s && (strcmp(s, e->name) != 0))
478                         continue;
479
480                 switch(cmd) {
481                 case CMD_COMMIT:
482                         ret = uci_commit(ctx, &p, false);
483                         break;
484                 case CMD_SAVE:
485                         ret = uci_save(ctx, p);
486                         break;
487                 case CMD_REVERT:
488                         ret = uci_revert(ctx, &p, section, option);
489                         break;
490                 }
491
492                 if (ret != 0)
493                         failed = 1;
494         }
495
496         lua_pushboolean(L, !failed);
497         return 1;
498 }
499
500 static int
501 uci_lua_save(lua_State *L)
502 {
503         return uci_lua_package_cmd(L, CMD_SAVE);
504 }
505
506 static int
507 uci_lua_commit(lua_State *L)
508 {
509         return uci_lua_package_cmd(L, CMD_COMMIT);
510 }
511
512 static int
513 uci_lua_revert(lua_State *L)
514 {
515         return uci_lua_package_cmd(L, CMD_REVERT);
516 }
517
518 static void
519 uci_lua_add_change(lua_State *L, struct uci_element *e)
520 {
521         struct uci_history *h;
522         const char *name;
523
524         h = uci_to_history(e);
525         if (!h->section)
526                 return;
527
528         lua_getfield(L, -1, h->section);
529         if (lua_isnil(L, -1)) {
530                 lua_pop(L, 1);
531                 lua_newtable(L);
532                 lua_pushvalue(L, -1); /* copy for setfield */
533                 lua_setfield(L, -3, h->section);
534         }
535
536         name = (h->e.name ? h->e.name : ".type");
537         if (h->value)
538                 lua_pushstring(L, h->value);
539         else
540                 lua_pushstring(L, "");
541         lua_setfield(L, -2, name);
542         lua_pop(L, 1);
543 }
544
545 static void
546 uci_lua_changes_pkg(lua_State *L, const char *package)
547 {
548         struct uci_package *p = NULL;
549         struct uci_element *e;
550         bool autoload = false;
551
552         p = find_package(L, package, AUTOLOAD_OFF);
553         if (!p) {
554                 autoload = true;
555                 p = find_package(L, package, AUTOLOAD_FORCE);
556                 if (!p)
557                         return;
558         }
559
560         if (uci_list_empty(&p->history) && uci_list_empty(&p->saved_history))
561                 goto done;
562
563         lua_newtable(L);
564         uci_foreach_element(&p->saved_history, e) {
565                 uci_lua_add_change(L, e);
566         }
567         uci_foreach_element(&p->history, e) {
568                 uci_lua_add_change(L, e);
569         }
570         lua_setfield(L, -2, p->e.name);
571
572 done:
573         if (autoload)
574                 uci_unload(ctx, p);
575 }
576
577 static int
578 uci_lua_changes(lua_State *L)
579 {
580         const char *package = NULL;
581         char **config = NULL;
582         int nargs;
583         int i;
584
585         nargs = lua_gettop(L);
586         switch(nargs) {
587         case 1:
588                 package = luaL_checkstring(L, 1);
589         case 0:
590                 break;
591         default:
592                 luaL_error(L, "invalid argument count");
593         }
594
595         lua_newtable(L);
596         if (package) {
597                 uci_lua_changes_pkg(L, package);
598         } else {
599                 if (uci_list_configs(ctx, &config) != 0)
600                         goto done;
601
602                 for(i = 0; config[i] != NULL; i++) {
603                         uci_lua_changes_pkg(L, config[i]);
604                 }
605         }
606
607 done:
608         return 1;
609 }
610
611 static int
612 uci_lua_set_confdir(lua_State *L)
613 {
614         int ret;
615
616         luaL_checkstring(L, 1);
617         ret = uci_set_confdir(ctx, lua_tostring(L, -1));
618         lua_pushboolean(L, (ret == 0));
619         return 1;
620 }
621
622 static int
623 uci_lua_set_savedir(lua_State *L)
624 {
625         int ret;
626
627         luaL_checkstring(L, 1);
628         ret = uci_set_savedir(ctx, lua_tostring(L, -1));
629         lua_pushboolean(L, (ret == 0));
630
631         return 1;
632 }
633
634 static const luaL_Reg uci[] = {
635         { "load", uci_lua_load },
636         { "unload", uci_lua_unload },
637         { "get", uci_lua_get },
638         { "get_all", uci_lua_get_all },
639         { "add", uci_lua_add },
640         { "set", uci_lua_set },
641         { "save", uci_lua_save },
642         { "delete", uci_lua_delete },
643         { "commit", uci_lua_commit },
644         { "revert", uci_lua_revert },
645         { "changes", uci_lua_changes },
646         { "foreach", uci_lua_foreach },
647         { "set_confdir", uci_lua_set_confdir },
648         { "set_savedir", uci_lua_set_savedir },
649         { NULL, NULL },
650 };
651
652
653 int
654 luaopen_uci(lua_State *L)
655 {
656         ctx = uci_alloc_context();
657         if (!ctx)
658                 luaL_error(L, "Cannot allocate UCI context\n");
659         luaL_register(L, MODNAME, uci);
660
661         /* enable autoload by default */
662         lua_getfield(L, LUA_GLOBALSINDEX, "uci");
663         lua_pushboolean(L, 1);
664         lua_setfield(L, -2, "autoload");
665         lua_pop(L, 1);
666
667         return 0;
668 }