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