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