remove uci_parse_tuple, preparation for adding a new api for lookups
[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_option(lua_State *L, struct uci_option *o)
92 {
93         struct uci_element *e;
94         int i = 0;
95
96         switch(o->type) {
97         case UCI_TYPE_STRING:
98                 lua_pushstring(L, o->v.string);
99                 break;
100         case UCI_TYPE_LIST:
101                 lua_newtable(L);
102                 uci_foreach_element(&o->v.list, e) {
103                         i++;
104                         lua_pushstring(L, e->name);
105                         lua_rawseti(L, -2, i);
106                 }
107                 break;
108         default:
109                 lua_pushnil(L);
110                 break;
111         }
112 }
113
114 static void uci_push_section(lua_State *L, struct uci_section *s)
115 {
116         struct uci_element *e;
117
118         lua_newtable(L);
119         lua_pushstring(L, s->type);
120         lua_setfield(L, -2, ".type");
121         lua_pushstring(L, s->e.name);
122         lua_setfield(L, -2, ".name");
123
124         uci_foreach_element(&s->options, e) {
125                 struct uci_option *o = uci_to_option(e);
126                 uci_push_option(L, o);
127                 lua_setfield(L, -2, o->e.name);
128         }
129 }
130
131 static void uci_push_package(lua_State *L, struct uci_package *p)
132 {
133         struct uci_element *e;
134         int i = 0;
135
136         lua_newtable(L);
137         uci_foreach_element(&p->sections, e) {
138                 i++;
139                 uci_push_section(L, uci_to_section(e));
140                 lua_setfield(L, -2, e->name);
141         }
142 }
143
144 static int
145 uci_lua_unload(lua_State *L)
146 {
147         struct uci_package *p;
148         const char *s;
149
150         luaL_checkstring(L, 1);
151         s = lua_tostring(L, -1);
152         p = find_package(L, s, AUTOLOAD_OFF);
153         if (p) {
154                 uci_unload(ctx, p);
155                 lua_pushboolean(L, 1);
156         } else {
157                 lua_pushboolean(L, 0);
158         }
159         return 1;
160 }
161
162 static int
163 uci_lua_load(lua_State *L)
164 {
165         struct uci_package *p = NULL;
166         const char *s;
167
168         uci_lua_unload(L);
169         lua_pop(L, 1); /* bool ret value of unload */
170         s = lua_tostring(L, -1);
171
172         if (uci_load(ctx, s, &p)) {
173                 uci_lua_perror(L, "uci.load");
174                 lua_pushboolean(L, 0);
175         } else {
176                 lua_pushboolean(L, 1);
177         }
178
179         return 1;
180 }
181
182
183 static int
184 uci_lua_foreach(lua_State *L)
185 {
186         struct uci_package *p;
187         struct uci_element *e;
188         const char *package, *type;
189         bool ret = false;
190
191         package = luaL_checkstring(L, 1);
192
193         if (lua_isnil(L, 2))
194                 type = NULL;
195         else
196                 type = luaL_checkstring(L, 2);
197
198         if (!lua_isfunction(L, 3) || !package)
199                 luaL_error(L, "Invalid argument");
200
201         p = find_package(L, package, AUTOLOAD_ON);
202         if (!p)
203                 goto done;
204
205         uci_foreach_element(&p->sections, e) {
206                 struct uci_section *s = uci_to_section(e);
207
208                 if (type && (strcmp(s->type, type) != 0))
209                         continue;
210
211                 lua_pushvalue(L, 3); /* iterator function */
212                 uci_push_section(L, s);
213                 if (lua_pcall(L, 1, 0, 0) == 0)
214                         ret = true;
215         }
216
217 done:
218         lua_pushboolean(L, ret);
219         return 1;
220 }
221
222 static int
223 uci_lua_get_any(lua_State *L, bool all)
224 {
225         struct uci_element *e = NULL;
226         struct uci_package *p = NULL;
227         struct uci_option *o = NULL;
228         const char *package = NULL;
229         const char *section = NULL;
230         const char *option = NULL;
231         char *s;
232         int err = UCI_ERR_MEM;
233         int n;
234
235         n = lua_gettop(L);
236
237         luaL_checkstring(L, 1);
238         s = strdup(lua_tostring(L, 1));
239         if (!s)
240                 goto error;
241
242         if (n > 1) {
243                 package = luaL_checkstring(L, 1);
244                 section = luaL_checkstring(L, 2);
245                 if (n > 2)
246                         option = luaL_checkstring(L, 3);
247         } else {
248                 if ((err = uci_parse_tuple(ctx, s, (char **) &package, (char **) &section, (char **) &option, NULL)))
249                         goto error;
250         }
251
252         if (!all && (section == NULL)) {
253                 err = UCI_ERR_INVAL;
254                 goto error;
255         }
256
257         p = find_package(L, package, AUTOLOAD_ON);
258         if (!p) {
259                 err = UCI_ERR_NOTFOUND;
260                 goto error;
261         }
262
263         if (section) {
264                 if ((err = uci_lookup(ctx, &e, p, section, option)))
265                         goto error;
266         } else {
267                 e = &p->e;
268         }
269
270         switch(e->type) {
271                 case UCI_TYPE_PACKAGE:
272                         uci_push_package(L, p);
273                         break;
274                 case UCI_TYPE_SECTION:
275                         if (all)
276                                 uci_push_section(L, uci_to_section(e));
277                         else
278                                 lua_pushstring(L, uci_to_section(e)->type);
279                         break;
280                 case UCI_TYPE_OPTION:
281                         o = uci_to_option(e);
282                         uci_push_option(L, o);
283                         break;
284                 default:
285                         err = UCI_ERR_INVAL;
286                         goto error;
287         }
288 error:
289         if (s)
290                 free(s);
291
292         switch(err) {
293         default:
294                 ctx->err = err;
295                 uci_lua_perror(L, "uci.get");
296                 /* fall through */
297         case UCI_ERR_NOTFOUND:
298                 lua_pushnil(L);
299                 /* fall through */
300         case 0:
301                 return 1;
302         }
303 }
304
305 static int
306 uci_lua_get(lua_State *L)
307 {
308         return uci_lua_get_any(L, false);
309 }
310
311 static int
312 uci_lua_get_all(lua_State *L)
313 {
314         return uci_lua_get_any(L, true);
315 }
316
317 static int
318 uci_lua_add(lua_State *L)
319 {
320         struct uci_section *s = NULL;
321         struct uci_package *p;
322         const char *package;
323         const char *type;
324         const char *name = NULL;
325
326         do {
327                 package = luaL_checkstring(L, 1);
328                 type = luaL_checkstring(L, 2);
329                 p = find_package(L, package, AUTOLOAD_ON);
330                 if (!p)
331                         break;
332
333                 if (uci_add_section(ctx, p, type, &s) || !s)
334                         break;
335
336                 name = s->e.name;
337         } while (0);
338
339         lua_pushstring(L, name);
340         return 1;
341 }
342
343 static int
344 uci_lua_delete(lua_State *L)
345 {
346         const char *package = NULL;
347         const char *section = NULL;
348         const char *option = NULL;
349         struct uci_package *p;
350         const char *s;
351         int err = UCI_ERR_MEM;
352         int nargs;
353
354         nargs = lua_gettop(L);
355         s = luaL_checkstring(L, 1);
356         switch(nargs) {
357         case 1:
358                 /* Format: uci.delete("p.s[.o]") */
359                 s = strdup(s);
360                 if (!s)
361                         goto error;
362
363                 if ((err = uci_parse_tuple(ctx, (char *) s, (char **) &package, (char **) &section, (char **) &option, NULL)))
364                         goto error;
365                 break;
366         case 3:
367                 /* Format: uci.delete("p", "s", "o") */
368                 option = luaL_checkstring(L, 3);
369                 /* fall through */
370         case 2:
371                 /* Format: uci.delete("p", "s") */
372                 section = luaL_checkstring(L, 2);
373                 package = s;
374                 break;
375         default:
376                 err = UCI_ERR_INVAL;
377                 goto error;
378         }
379
380         p = find_package(L, package, AUTOLOAD_ON);
381         if (!p) {
382                 err = UCI_ERR_NOTFOUND;
383                 goto error;
384         }
385         err = uci_delete(ctx, p, section, option);
386
387 error:
388         if (err)
389                 uci_lua_perror(L, "uci.set");
390         lua_pushboolean(L, (err == 0));
391         return 1;
392 }
393
394 static int
395 uci_lua_set(lua_State *L)
396 {
397         struct uci_package *p;
398         const char *package = NULL;
399         const char *section = NULL;
400         const char *option = NULL;
401         const char *value = NULL;
402         const char *s;
403         int err = UCI_ERR_MEM;
404         int i, nargs;
405         bool istable = false;
406
407         nargs = lua_gettop(L);
408
409         s = luaL_checkstring(L, 1);
410         switch(nargs) {
411         case 1:
412                 /* Format: uci.set("p.s.o=v") or uci.set("p.s=v") */
413                 s = strdup(s);
414                 if (!s)
415                         goto error;
416
417                 if ((err = uci_parse_tuple(ctx, (char *) s, (char **) &package, (char **) &section, (char **) &option, (char **) &value)))
418                         goto error;
419                 break;
420         case 4:
421                 /* Format: uci.set("p", "s", "o", "v") */
422                 option = luaL_checkstring(L, 3);
423                 /* fall through */
424         case 3:
425                 /* Format: uci.set("p", "s", "v") */
426                 package = s;
427                 section = luaL_checkstring(L, 2);
428                 if (lua_istable(L, nargs)) {
429                         if (lua_objlen(L, nargs) < 1)
430                                 luaL_error(L, "Cannot set an uci option to an empty table value");
431                         lua_rawgeti(L, nargs, 1);
432                         value = luaL_checkstring(L, -1);
433                         lua_pop(L, 1);
434                         istable = true;
435                 } else {
436                         value = luaL_checkstring(L, nargs);
437                 }
438                 break;
439         default:
440                 err = UCI_ERR_INVAL;
441                 goto error;
442         }
443
444         if ((section == NULL) || (value == NULL)) {
445                 err = UCI_ERR_INVAL;
446                 goto error;
447         }
448
449         p = find_package(L, package, AUTOLOAD_ON);
450         if (!p) {
451                 err = UCI_ERR_NOTFOUND;
452                 goto error;
453         }
454         err = uci_set(ctx, p, section, option, value, NULL);
455         if (istable) {
456                 for (i = 2; i <= lua_objlen(L, nargs); i++) {
457                         lua_rawgeti(L, nargs, i);
458                         value = luaL_checkstring(L, -1);
459                         err = uci_add_list(ctx, p, section, option, value, NULL);
460                         lua_pop(L, 1);
461                         if (err)
462                                 goto error;
463                 }
464         }
465
466 error:
467         if (err)
468                 uci_lua_perror(L, "uci.set");
469         lua_pushboolean(L, (err == 0));
470         return 1;
471 }
472
473 enum pkg_cmd {
474         CMD_SAVE,
475         CMD_COMMIT,
476         CMD_REVERT
477 };
478
479 static int
480 uci_lua_package_cmd(lua_State *L, enum pkg_cmd cmd)
481 {
482         struct uci_element *e, *tmp;
483         const char *s = NULL;
484         const char *section = NULL;
485         const char *option = NULL;
486         int failed = 0;
487         int nargs;
488
489         nargs = lua_gettop(L);
490         switch(nargs) {
491         case 3:
492                 if (cmd != CMD_REVERT)
493                         goto err;
494                 luaL_checkstring(L, 1);
495                 option = lua_tostring(L, -1);
496                 lua_pop(L, 1);
497                 /* fall through */
498         case 2:
499                 if (cmd != CMD_REVERT)
500                         goto err;
501                 luaL_checkstring(L, 1);
502                 section = lua_tostring(L, -1);
503                 lua_pop(L, 1);
504                 /* fall through */
505         case 1:
506                 luaL_checkstring(L, 1);
507                 s = lua_tostring(L, -1);
508                 lua_pop(L, 1);
509                 break;
510         case 0:
511                 break;
512         default:
513                 err:
514                 luaL_error(L, "Invalid argument count");
515                 break;
516         }
517
518         uci_foreach_element_safe(&ctx->root, tmp, e) {
519                 struct uci_package *p = uci_to_package(e);
520                 int ret = UCI_ERR_INVAL;
521
522                 if (s && (strcmp(s, e->name) != 0))
523                         continue;
524
525                 switch(cmd) {
526                 case CMD_COMMIT:
527                         ret = uci_commit(ctx, &p, false);
528                         break;
529                 case CMD_SAVE:
530                         ret = uci_save(ctx, p);
531                         break;
532                 case CMD_REVERT:
533                         ret = uci_revert(ctx, &p, section, option);
534                         break;
535                 }
536
537                 if (ret != 0)
538                         failed = 1;
539         }
540
541         lua_pushboolean(L, !failed);
542         return 1;
543 }
544
545 static int
546 uci_lua_save(lua_State *L)
547 {
548         return uci_lua_package_cmd(L, CMD_SAVE);
549 }
550
551 static int
552 uci_lua_commit(lua_State *L)
553 {
554         return uci_lua_package_cmd(L, CMD_COMMIT);
555 }
556
557 static int
558 uci_lua_revert(lua_State *L)
559 {
560         return uci_lua_package_cmd(L, CMD_REVERT);
561 }
562
563 static void
564 uci_lua_add_change(lua_State *L, struct uci_element *e)
565 {
566         struct uci_history *h;
567         const char *name;
568
569         h = uci_to_history(e);
570         if (!h->section)
571                 return;
572
573         lua_getfield(L, -1, h->section);
574         if (lua_isnil(L, -1)) {
575                 lua_pop(L, 1);
576                 lua_newtable(L);
577                 lua_pushvalue(L, -1); /* copy for setfield */
578                 lua_setfield(L, -3, h->section);
579         }
580
581         name = (h->e.name ? h->e.name : ".type");
582         if (h->value)
583                 lua_pushstring(L, h->value);
584         else
585                 lua_pushstring(L, "");
586         lua_setfield(L, -2, name);
587         lua_pop(L, 1);
588 }
589
590 static void
591 uci_lua_changes_pkg(lua_State *L, const char *package)
592 {
593         struct uci_package *p = NULL;
594         struct uci_element *e;
595         bool autoload = false;
596
597         p = find_package(L, package, AUTOLOAD_OFF);
598         if (!p) {
599                 autoload = true;
600                 p = find_package(L, package, AUTOLOAD_FORCE);
601                 if (!p)
602                         return;
603         }
604
605         if (uci_list_empty(&p->history) && uci_list_empty(&p->saved_history))
606                 goto done;
607
608         lua_newtable(L);
609         uci_foreach_element(&p->saved_history, e) {
610                 uci_lua_add_change(L, e);
611         }
612         uci_foreach_element(&p->history, e) {
613                 uci_lua_add_change(L, e);
614         }
615         lua_setfield(L, -2, p->e.name);
616
617 done:
618         if (autoload)
619                 uci_unload(ctx, p);
620 }
621
622 static int
623 uci_lua_changes(lua_State *L)
624 {
625         const char *package = NULL;
626         char **config = NULL;
627         int nargs;
628         int i;
629
630         nargs = lua_gettop(L);
631         switch(nargs) {
632         case 1:
633                 package = luaL_checkstring(L, 1);
634         case 0:
635                 break;
636         default:
637                 luaL_error(L, "invalid argument count");
638         }
639
640         lua_newtable(L);
641         if (package) {
642                 uci_lua_changes_pkg(L, package);
643         } else {
644                 if (uci_list_configs(ctx, &config) != 0)
645                         goto done;
646
647                 for(i = 0; config[i] != NULL; i++) {
648                         uci_lua_changes_pkg(L, config[i]);
649                 }
650         }
651
652 done:
653         return 1;
654 }
655
656 static int
657 uci_lua_set_confdir(lua_State *L)
658 {
659         int ret;
660
661         luaL_checkstring(L, 1);
662         ret = uci_set_confdir(ctx, lua_tostring(L, -1));
663         lua_pushboolean(L, (ret == 0));
664         return 1;
665 }
666
667 static int
668 uci_lua_set_savedir(lua_State *L)
669 {
670         int ret;
671
672         luaL_checkstring(L, 1);
673         ret = uci_set_savedir(ctx, lua_tostring(L, -1));
674         lua_pushboolean(L, (ret == 0));
675
676         return 1;
677 }
678
679 static const luaL_Reg uci[] = {
680         { "load", uci_lua_load },
681         { "unload", uci_lua_unload },
682         { "get", uci_lua_get },
683         { "get_all", uci_lua_get_all },
684         { "add", uci_lua_add },
685         { "set", uci_lua_set },
686         { "save", uci_lua_save },
687         { "delete", uci_lua_delete },
688         { "commit", uci_lua_commit },
689         { "revert", uci_lua_revert },
690         { "changes", uci_lua_changes },
691         { "foreach", uci_lua_foreach },
692         { "set_confdir", uci_lua_set_confdir },
693         { "set_savedir", uci_lua_set_savedir },
694         { NULL, NULL },
695 };
696
697
698 int
699 luaopen_uci(lua_State *L)
700 {
701         ctx = uci_alloc_context();
702         if (!ctx)
703                 luaL_error(L, "Cannot allocate UCI context\n");
704         luaL_register(L, MODNAME, uci);
705
706         /* enable autoload by default */
707         lua_getfield(L, LUA_GLOBALSINDEX, "uci");
708         lua_pushboolean(L, 1);
709         lua_setfield(L, -2, "autoload");
710         lua_pop(L, 1);
711
712         return 0;
713 }