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