session: expose rpc_session_access() function for testing session ACLs in other rpcd...
[project/rpcd.git] / uci.c
1 /*
2 * luci-rpcd - LuCI UBUS RPC server
3 *
4 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "uci.h"
20
21 static struct blob_buf buf;
22 static struct uci_context *cursor;
23
24 enum {
25 RPC_G_CONFIG,
26 RPC_G_SECTION,
27 RPC_G_OPTION,
28 RPC_G_TYPE,
29 RPC_G_MATCH,
30 __RPC_G_MAX,
31 };
32
33 static const struct blobmsg_policy rpc_uci_get_policy[__RPC_G_MAX] = {
34 [RPC_G_CONFIG] = { .name = "config", .type = BLOBMSG_TYPE_STRING },
35 [RPC_G_SECTION] = { .name = "section", .type = BLOBMSG_TYPE_STRING },
36 [RPC_G_OPTION] = { .name = "option", .type = BLOBMSG_TYPE_STRING },
37 [RPC_G_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
38 [RPC_G_MATCH] = { .name = "match", .type = BLOBMSG_TYPE_TABLE },
39 };
40
41 enum {
42 RPC_A_CONFIG,
43 RPC_A_TYPE,
44 RPC_A_NAME,
45 RPC_A_VALUES,
46 __RPC_A_MAX,
47 };
48
49 static const struct blobmsg_policy rpc_uci_add_policy[__RPC_A_MAX] = {
50 [RPC_A_CONFIG] = { .name = "config", .type = BLOBMSG_TYPE_STRING },
51 [RPC_A_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
52 [RPC_A_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
53 [RPC_A_VALUES] = { .name = "values", .type = BLOBMSG_TYPE_TABLE },
54 };
55
56 enum {
57 RPC_S_CONFIG,
58 RPC_S_SECTION,
59 RPC_S_TYPE,
60 RPC_S_MATCH,
61 RPC_S_VALUES,
62 __RPC_S_MAX,
63 };
64
65 static const struct blobmsg_policy rpc_uci_set_policy[__RPC_S_MAX] = {
66 [RPC_S_CONFIG] = { .name = "config", .type = BLOBMSG_TYPE_STRING },
67 [RPC_S_SECTION] = { .name = "section", .type = BLOBMSG_TYPE_STRING },
68 [RPC_S_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
69 [RPC_S_MATCH] = { .name = "match", .type = BLOBMSG_TYPE_TABLE },
70 [RPC_S_VALUES] = { .name = "values", .type = BLOBMSG_TYPE_TABLE },
71 };
72
73 enum {
74 RPC_D_CONFIG,
75 RPC_D_SECTION,
76 RPC_D_TYPE,
77 RPC_D_MATCH,
78 RPC_D_OPTION,
79 RPC_D_OPTIONS,
80 __RPC_D_MAX,
81 };
82
83 static const struct blobmsg_policy rpc_uci_delete_policy[__RPC_D_MAX] = {
84 [RPC_D_CONFIG] = { .name = "config", .type = BLOBMSG_TYPE_STRING },
85 [RPC_D_SECTION] = { .name = "section", .type = BLOBMSG_TYPE_STRING },
86 [RPC_D_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
87 [RPC_D_MATCH] = { .name = "match", .type = BLOBMSG_TYPE_TABLE },
88 [RPC_D_OPTION] = { .name = "option", .type = BLOBMSG_TYPE_STRING },
89 [RPC_D_OPTIONS] = { .name = "options", .type = BLOBMSG_TYPE_ARRAY },
90 };
91
92 enum {
93 RPC_R_CONFIG,
94 RPC_R_SECTION,
95 RPC_R_OPTION,
96 RPC_R_NAME,
97 __RPC_R_MAX,
98 };
99
100 static const struct blobmsg_policy rpc_uci_rename_policy[__RPC_R_MAX] = {
101 [RPC_R_CONFIG] = { .name = "config", .type = BLOBMSG_TYPE_STRING },
102 [RPC_R_SECTION] = { .name = "section", .type = BLOBMSG_TYPE_STRING },
103 [RPC_R_OPTION] = { .name = "option", .type = BLOBMSG_TYPE_STRING },
104 [RPC_R_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
105 };
106
107 enum {
108 RPC_O_CONFIG,
109 RPC_O_SECTIONS,
110 __RPC_O_MAX,
111 };
112
113 static const struct blobmsg_policy rpc_uci_order_policy[__RPC_O_MAX] = {
114 [RPC_O_CONFIG] = { .name = "config", .type = BLOBMSG_TYPE_STRING },
115 [RPC_O_SECTIONS] = { .name = "sections", .type = BLOBMSG_TYPE_ARRAY },
116 };
117
118 enum {
119 RPC_C_CONFIG,
120 __RPC_C_MAX,
121 };
122
123 static const struct blobmsg_policy rpc_uci_config_policy[__RPC_C_MAX] = {
124 [RPC_C_CONFIG] = { .name = "config", .type = BLOBMSG_TYPE_STRING },
125 };
126
127 /*
128 * Turn uci error state into ubus return code
129 */
130 static int
131 rpc_uci_status(void)
132 {
133 switch (cursor->err)
134 {
135 case UCI_OK:
136 return UBUS_STATUS_OK;
137
138 case UCI_ERR_INVAL:
139 return UBUS_STATUS_INVALID_ARGUMENT;
140
141 case UCI_ERR_NOTFOUND:
142 return UBUS_STATUS_NOT_FOUND;
143
144 default:
145 return UBUS_STATUS_UNKNOWN_ERROR;
146 }
147 }
148
149 /*
150 * Format applicable blob value as string and place a pointer to the string
151 * buffer in "p". Uses a static string buffer.
152 */
153 static bool
154 rpc_uci_format_blob(struct blob_attr *v, const char **p)
155 {
156 static char buf[21];
157
158 *p = NULL;
159
160 switch (blobmsg_type(v))
161 {
162 case BLOBMSG_TYPE_STRING:
163 if (blobmsg_data_len(v) > 1)
164 *p = blobmsg_data(v);
165 break;
166
167 case BLOBMSG_TYPE_INT64:
168 snprintf(buf, sizeof(buf), "%"PRIu64, blobmsg_get_u64(v));
169 *p = buf;
170 break;
171
172 case BLOBMSG_TYPE_INT32:
173 snprintf(buf, sizeof(buf), "%u", blobmsg_get_u32(v));
174 *p = buf;
175 break;
176
177 case BLOBMSG_TYPE_INT16:
178 snprintf(buf, sizeof(buf), "%u", blobmsg_get_u16(v));
179 *p = buf;
180 break;
181
182 case BLOBMSG_TYPE_INT8:
183 snprintf(buf, sizeof(buf), "%u", !!blobmsg_get_u8(v));
184 *p = buf;
185 break;
186
187 default:
188 break;
189 }
190
191 return !!*p;
192 }
193
194 /*
195 * Lookup the given uci_ptr and enable extended lookup format if the .section
196 * value of the uci_ptr looks like extended syntax. Uses an internal copy
197 * of the given uci_ptr to perform the lookup as failing extended section
198 * lookup operations in libuci will zero our the uci_ptr struct.
199 * Copies the internal uci_ptr back to given the uci_ptr on success.
200 */
201 static int
202 rpc_uci_lookup(struct uci_ptr *ptr)
203 {
204 int rv;
205 struct uci_ptr lookup = *ptr;
206
207 if (!lookup.s && lookup.section && *lookup.section == '@')
208 lookup.flags |= UCI_LOOKUP_EXTENDED;
209
210 rv = uci_lookup_ptr(cursor, &lookup, NULL, true);
211
212 if (!rv)
213 *ptr = lookup;
214
215 return rv;
216 }
217
218 /*
219 * Checks whether the given uci_option object matches the given string value.
220 * 1) If the uci_option is of type list, check whether any of the list elements
221 * equals to the given string
222 * 2) If the uci_option is of type string, parse it into space separated tokens
223 * and check if any of the tokens equals to the given string.
224 * Returns true if a list element or token matched the given string.
225 */
226 static bool
227 rpc_uci_match_option(struct uci_option *o, const char *cmp)
228 {
229 struct uci_element *e;
230 char *s, *p;
231
232 if (o->type == UCI_TYPE_LIST)
233 {
234 uci_foreach_element(&o->v.list, e)
235 if (e->name && !strcmp(e->name, cmp))
236 return true;
237
238 return false;
239 }
240
241 if (!o->v.string)
242 return false;
243
244 s = strdup(o->v.string);
245
246 if (!s)
247 return false;
248
249 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
250 {
251 if (!strcmp(p, cmp))
252 {
253 free(s);
254 return true;
255 }
256 }
257
258 free(s);
259 return false;
260 }
261
262 /*
263 * Checks whether the given uci_section matches the type and value blob attrs.
264 * 1) Returns false if "type" is given and the section type does not match
265 * the value specified in the "type" string blob attribute, else continue.
266 * 2) Tests whether any key in the "matches" table blob attribute exists in
267 * the given uci_section and whether each value is contained in the
268 * corresponding uci option value (see rpc_uci_match_option()).
269 * 3) A missing or empty "matches" table blob attribute is always considered
270 * to be a match.
271 * Returns true if "type" matches or is NULL and "matches" matches or is NULL.
272 */
273 static bool
274 rpc_uci_match_section(struct uci_section *s,
275 struct blob_attr *type, struct blob_attr *matches)
276 {
277 struct uci_element *e;
278 struct blob_attr *cur;
279 const char *cmp;
280 bool match = false;
281 bool empty = true;
282 int rem;
283
284 if (type && strcmp(s->type, blobmsg_data(type)))
285 return false;
286
287 if (!matches)
288 return true;
289
290 blobmsg_for_each_attr(cur, matches, rem)
291 {
292 if (!rpc_uci_format_blob(cur, &cmp))
293 continue;
294
295 uci_foreach_element(&s->options, e)
296 {
297 if (strcmp(e->name, blobmsg_name(cur)))
298 continue;
299
300 if (!rpc_uci_match_option(uci_to_option(e), cmp))
301 return false;
302
303 match = true;
304 }
305
306 empty = false;
307 }
308
309 return (empty || match);
310 }
311
312 /*
313 * Dump the given uci_option value into the global blobmsg buffer and use
314 * given "name" as key.
315 * 1) If the uci_option is of type list, put a table into the blob buffer and
316 * add each list item as string to it.
317 * 2) If the uci_option is of type string, put its value directly into the blob
318 * buffer.
319 */
320 static void
321 rpc_uci_dump_option(struct uci_option *o, const char *name)
322 {
323 void *c;
324 struct uci_element *e;
325
326 switch (o->type)
327 {
328 case UCI_TYPE_STRING:
329 blobmsg_add_string(&buf, name, o->v.string);
330 break;
331
332 case UCI_TYPE_LIST:
333 c = blobmsg_open_array(&buf, name);
334
335 uci_foreach_element(&o->v.list, e)
336 blobmsg_add_string(&buf, NULL, e->name);
337
338 blobmsg_close_array(&buf, c);
339 break;
340
341 default:
342 break;
343 }
344 }
345
346 /*
347 * Dump the given uci_section object into the global blobmsg buffer and use
348 * given "name" as key.
349 * Puts a table into the blob buffer and puts each section option member value
350 * as value into the table using the option name as key.
351 * Adds three special keys ".anonymous", ".type" and ".name" which specify the
352 * corresponding section properties.
353 */
354 static void
355 rpc_uci_dump_section(struct uci_section *s, const char *name, int index)
356 {
357 void *c;
358 struct uci_option *o;
359 struct uci_element *e;
360
361 c = blobmsg_open_table(&buf, name);
362
363 blobmsg_add_u8(&buf, ".anonymous", s->anonymous);
364 blobmsg_add_string(&buf, ".type", s->type);
365 blobmsg_add_string(&buf, ".name", s->e.name);
366
367 if (index >= 0)
368 blobmsg_add_u32(&buf, ".index", index);
369
370 uci_foreach_element(&s->options, e)
371 {
372 o = uci_to_option(e);
373 rpc_uci_dump_option(o, o->e.name);
374 }
375
376 blobmsg_close_table(&buf, c);
377 }
378
379 /*
380 * Dump the given uci_package object into the global blobmsg buffer and use
381 * given "name" as key.
382 * Puts a table into the blob buffer and puts each package section member as
383 * value into the table using the section name as key.
384 * Only dumps sections matching the given "type" and "matches", see explaination
385 * of rpc_uci_match_section() for details.
386 */
387 static void
388 rpc_uci_dump_package(struct uci_package *p, const char *name,
389 struct blob_attr *type, struct blob_attr *matches)
390 {
391 void *c;
392 struct uci_element *e;
393 int i = -1;
394
395 c = blobmsg_open_table(&buf, name);
396
397 uci_foreach_element(&p->sections, e)
398 {
399 i++;
400
401 if (!rpc_uci_match_section(uci_to_section(e), type, matches))
402 continue;
403
404 rpc_uci_dump_section(uci_to_section(e), e->name, i);
405 }
406
407 blobmsg_close_table(&buf, c);
408 }
409
410
411 static int
412 rpc_uci_get(struct ubus_context *ctx, struct ubus_object *obj,
413 struct ubus_request_data *req, const char *method,
414 struct blob_attr *msg)
415 {
416 struct blob_attr *tb[__RPC_G_MAX];
417 struct uci_package *p = NULL;
418 struct uci_ptr ptr = { 0 };
419
420 blobmsg_parse(rpc_uci_get_policy, __RPC_G_MAX, tb,
421 blob_data(msg), blob_len(msg));
422
423 if (!tb[RPC_G_CONFIG])
424 return UBUS_STATUS_INVALID_ARGUMENT;
425
426 ptr.package = blobmsg_data(tb[RPC_G_CONFIG]);
427 uci_load(cursor, ptr.package, &p);
428
429 if (!p)
430 goto out;
431
432 if (tb[RPC_G_SECTION])
433 {
434 ptr.section = blobmsg_data(tb[RPC_G_SECTION]);
435
436 if (tb[RPC_G_OPTION])
437 ptr.option = blobmsg_data(tb[RPC_G_OPTION]);
438 }
439
440 if (rpc_uci_lookup(&ptr) || !(ptr.flags & UCI_LOOKUP_COMPLETE))
441 goto out;
442
443 blob_buf_init(&buf, 0);
444
445 switch (ptr.last->type)
446 {
447 case UCI_TYPE_PACKAGE:
448 rpc_uci_dump_package(ptr.p, "values", tb[RPC_G_TYPE], tb[RPC_G_MATCH]);
449 break;
450
451 case UCI_TYPE_SECTION:
452 rpc_uci_dump_section(ptr.s, "values", -1);
453 break;
454
455 case UCI_TYPE_OPTION:
456 rpc_uci_dump_option(ptr.o, "value");
457 break;
458
459 default:
460 break;
461 }
462
463 ubus_send_reply(ctx, req, buf.head);
464
465 out:
466 if (p)
467 uci_unload(cursor, p);
468
469 return rpc_uci_status();
470 }
471
472 static int
473 rpc_uci_add(struct ubus_context *ctx, struct ubus_object *obj,
474 struct ubus_request_data *req, const char *method,
475 struct blob_attr *msg)
476 {
477 struct blob_attr *tb[__RPC_A_MAX];
478 struct blob_attr *cur, *elem;
479 struct uci_package *p = NULL;
480 struct uci_section *s;
481 struct uci_ptr ptr = { 0 };
482 int rem, rem2;
483
484 blobmsg_parse(rpc_uci_add_policy, __RPC_A_MAX, tb,
485 blob_data(msg), blob_len(msg));
486
487 if (!tb[RPC_A_CONFIG] || !tb[RPC_A_TYPE])
488 return UBUS_STATUS_INVALID_ARGUMENT;
489
490 ptr.package = blobmsg_data(tb[RPC_A_CONFIG]);
491
492 uci_load(cursor, ptr.package, &p);
493
494 if (!p)
495 goto out;
496
497 /* add named section */
498 if (tb[RPC_A_NAME])
499 {
500 ptr.section = blobmsg_data(tb[RPC_A_NAME]);
501 ptr.value = blobmsg_data(tb[RPC_A_TYPE]);
502 ptr.option = NULL;
503
504 if (rpc_uci_lookup(&ptr) || uci_set(cursor, &ptr))
505 goto out;
506 }
507
508 /* add anon section */
509 else
510 {
511 if (uci_add_section(cursor, p, blobmsg_data(tb[RPC_A_TYPE]), &s) || !s)
512 goto out;
513
514 ptr.section = s->e.name;
515 }
516
517 if (tb[RPC_A_VALUES])
518 {
519 blobmsg_for_each_attr(cur, tb[RPC_A_VALUES], rem)
520 {
521 ptr.o = NULL;
522 ptr.option = blobmsg_name(cur);
523
524 if (rpc_uci_lookup(&ptr) || !ptr.s)
525 continue;
526
527 switch (blobmsg_type(cur))
528 {
529 case BLOBMSG_TYPE_ARRAY:
530 blobmsg_for_each_attr(elem, cur, rem2)
531 if (rpc_uci_format_blob(elem, &ptr.value))
532 uci_add_list(cursor, &ptr);
533 break;
534
535 default:
536 if (rpc_uci_format_blob(cur, &ptr.value))
537 uci_set(cursor, &ptr);
538 break;
539 }
540 }
541 }
542
543 uci_save(cursor, p);
544
545 blob_buf_init(&buf, 0);
546 blobmsg_add_string(&buf, "section", ptr.section);
547 ubus_send_reply(ctx, req, buf.head);
548
549 out:
550 if (p)
551 uci_unload(cursor, p);
552
553 return rpc_uci_status();
554 }
555
556 /*
557 * Turn value from a blob attribute into uci set operation
558 * 1) if the blob is of type array, delete existing option (if any) and
559 * emit uci add_list operations for each element
560 * 2) if the blob is not an array but an option of type list exists,
561 * delete existing list and emit uci set operation for the blob value
562 * 3) in all other cases only emit a set operation if there is no existing
563 * option of if the existing options value differs from the blob value
564 */
565 static void
566 rpc_uci_merge_set(struct blob_attr *opt, struct uci_ptr *ptr)
567 {
568 struct blob_attr *cur;
569 int rem;
570
571 ptr->o = NULL;
572 ptr->option = blobmsg_name(opt);
573
574 if (rpc_uci_lookup(ptr) || !ptr->s)
575 return;
576
577 if (blobmsg_type(opt) == BLOBMSG_TYPE_ARRAY)
578 {
579 if (ptr->o)
580 uci_delete(cursor, ptr);
581
582 blobmsg_for_each_attr(cur, opt, rem)
583 if (rpc_uci_format_blob(cur, &ptr->value))
584 uci_add_list(cursor, ptr);
585 }
586 else if (ptr->o && ptr->o->type == UCI_TYPE_LIST)
587 {
588 uci_delete(cursor, ptr);
589
590 if (rpc_uci_format_blob(opt, &ptr->value))
591 uci_set(cursor, ptr);
592 }
593 else if (rpc_uci_format_blob(opt, &ptr->value))
594 {
595 if (!ptr->o || !ptr->o->v.string || strcmp(ptr->o->v.string, ptr->value))
596 uci_set(cursor, ptr);
597 }
598 }
599
600 static int
601 rpc_uci_set(struct ubus_context *ctx, struct ubus_object *obj,
602 struct ubus_request_data *req, const char *method,
603 struct blob_attr *msg)
604 {
605 struct blob_attr *tb[__RPC_S_MAX];
606 struct blob_attr *cur;
607 struct uci_package *p = NULL;
608 struct uci_element *e;
609 struct uci_ptr ptr = { 0 };
610 int rem;
611
612 blobmsg_parse(rpc_uci_set_policy, __RPC_S_MAX, tb,
613 blob_data(msg), blob_len(msg));
614
615 if (!tb[RPC_S_CONFIG] || !tb[RPC_S_VALUES] ||
616 (!tb[RPC_S_SECTION] && !tb[RPC_S_TYPE] && !tb[RPC_S_MATCH]))
617 return UBUS_STATUS_INVALID_ARGUMENT;
618
619 ptr.package = blobmsg_data(tb[RPC_S_CONFIG]);
620 uci_load(cursor, ptr.package, &p);
621
622 if (!p)
623 goto out;
624
625 if (tb[RPC_S_SECTION])
626 {
627 ptr.section = blobmsg_data(tb[RPC_S_SECTION]);
628 blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
629 rpc_uci_merge_set(cur, &ptr);
630 }
631 else
632 {
633 uci_foreach_element(&p->sections, e)
634 {
635 if (!rpc_uci_match_section(uci_to_section(e),
636 tb[RPC_S_TYPE], tb[RPC_S_MATCH]))
637 continue;
638
639 ptr.s = NULL;
640 ptr.section = e->name;
641
642 blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
643 rpc_uci_merge_set(cur, &ptr);
644 }
645 }
646
647 uci_save(cursor, p);
648
649 out:
650 if (p)
651 uci_unload(cursor, p);
652
653 return rpc_uci_status();
654 }
655
656 /*
657 * Delete option or section from uci specified by given blob attribute pointer
658 * 1) if the blob is of type array, delete any option named after each element
659 * 2) if the blob is of type string, delete the option named after its value
660 * 3) if the blob is NULL, delete entire section
661 */
662 static void
663 rpc_uci_merge_delete(struct blob_attr *opt, struct uci_ptr *ptr)
664 {
665 struct blob_attr *cur;
666 int rem;
667
668 if (rpc_uci_lookup(ptr) || !ptr->s)
669 return;
670
671 if (!opt)
672 {
673 ptr->o = NULL;
674 ptr->option = NULL;
675
676 uci_delete(cursor, ptr);
677 }
678 else if (blobmsg_type(opt) == BLOBMSG_TYPE_ARRAY)
679 {
680 blobmsg_for_each_attr(cur, opt, rem)
681 {
682 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
683 continue;
684
685 ptr->o = NULL;
686 ptr->option = blobmsg_data(cur);
687
688 if (rpc_uci_lookup(ptr) || !ptr->o)
689 continue;
690
691 uci_delete(cursor, ptr);
692 }
693 }
694 else if (blobmsg_type(opt) == BLOBMSG_TYPE_STRING)
695 {
696 ptr->o = NULL;
697 ptr->option = blobmsg_data(opt);
698
699 if (rpc_uci_lookup(ptr) || !ptr->o)
700 return;
701
702 uci_delete(cursor, ptr);
703 }
704 }
705
706 static int
707 rpc_uci_delete(struct ubus_context *ctx, struct ubus_object *obj,
708 struct ubus_request_data *req, const char *method,
709 struct blob_attr *msg)
710 {
711 struct blob_attr *tb[__RPC_D_MAX];
712 struct uci_package *p = NULL;
713 struct uci_element *e, *tmp;
714 struct uci_ptr ptr = { 0 };
715
716 blobmsg_parse(rpc_uci_delete_policy, __RPC_D_MAX, tb,
717 blob_data(msg), blob_len(msg));
718
719 if (!tb[RPC_D_CONFIG] ||
720 (!tb[RPC_D_SECTION] && !tb[RPC_D_TYPE] && !tb[RPC_D_MATCH]))
721 return UBUS_STATUS_INVALID_ARGUMENT;
722
723 ptr.package = blobmsg_data(tb[RPC_D_CONFIG]);
724 uci_load(cursor, ptr.package, &p);
725
726 if (!p)
727 goto out;
728
729 if (tb[RPC_D_SECTION])
730 {
731 ptr.section = blobmsg_data(tb[RPC_D_SECTION]);
732
733 if (tb[RPC_D_OPTIONS])
734 rpc_uci_merge_delete(tb[RPC_D_OPTIONS], &ptr);
735 else
736 rpc_uci_merge_delete(tb[RPC_D_OPTION], &ptr);
737 }
738 else
739 {
740 uci_foreach_element_safe(&p->sections, tmp, e)
741 {
742 if (!rpc_uci_match_section(uci_to_section(e),
743 tb[RPC_D_TYPE], tb[RPC_D_MATCH]))
744 continue;
745
746 ptr.s = NULL;
747 ptr.section = e->name;
748
749 if (tb[RPC_D_OPTIONS])
750 rpc_uci_merge_delete(tb[RPC_D_OPTIONS], &ptr);
751 else
752 rpc_uci_merge_delete(tb[RPC_D_OPTION], &ptr);
753 }
754 }
755
756 uci_save(cursor, p);
757
758 out:
759 if (p)
760 uci_unload(cursor, p);
761
762 return rpc_uci_status();
763 }
764
765 static int
766 rpc_uci_rename(struct ubus_context *ctx, struct ubus_object *obj,
767 struct ubus_request_data *req, const char *method,
768 struct blob_attr *msg)
769 {
770 struct blob_attr *tb[__RPC_R_MAX];
771 struct uci_package *p = NULL;
772 struct uci_ptr ptr = { 0 };
773
774 blobmsg_parse(rpc_uci_rename_policy, __RPC_R_MAX, tb,
775 blob_data(msg), blob_len(msg));
776
777 if (!tb[RPC_R_CONFIG] || !tb[RPC_R_SECTION] || !tb[RPC_R_NAME])
778 return UBUS_STATUS_INVALID_ARGUMENT;
779
780 ptr.package = blobmsg_data(tb[RPC_R_CONFIG]);
781 ptr.section = blobmsg_data(tb[RPC_R_SECTION]);
782 ptr.value = blobmsg_data(tb[RPC_R_NAME]);
783
784 if (tb[RPC_R_OPTION])
785 ptr.option = blobmsg_data(tb[RPC_R_OPTION]);
786
787 uci_load(cursor, ptr.package, &p);
788
789 if (!p)
790 goto out;
791
792 if (uci_lookup_ptr(cursor, &ptr, NULL, true))
793 goto out;
794
795 if ((ptr.option && !ptr.o) || !ptr.s)
796 {
797 cursor->err = UCI_ERR_NOTFOUND;
798 goto out;
799 }
800
801 if (uci_rename(cursor, &ptr))
802 goto out;
803
804 uci_save(cursor, p);
805
806 out:
807 if (p)
808 uci_unload(cursor, p);
809
810 return rpc_uci_status();
811 }
812
813 static int
814 rpc_uci_order(struct ubus_context *ctx, struct ubus_object *obj,
815 struct ubus_request_data *req, const char *method,
816 struct blob_attr *msg)
817 {
818 struct blob_attr *tb[__RPC_O_MAX];
819 struct blob_attr *cur;
820 struct uci_package *p = NULL;
821 struct uci_ptr ptr = { 0 };
822 int rem, i = 1;
823
824 blobmsg_parse(rpc_uci_order_policy, __RPC_O_MAX, tb,
825 blob_data(msg), blob_len(msg));
826
827 if (!tb[RPC_O_CONFIG] || !tb[RPC_O_SECTIONS])
828 return UBUS_STATUS_INVALID_ARGUMENT;
829
830 ptr.package = blobmsg_data(tb[RPC_O_CONFIG]);
831
832 uci_load(cursor, ptr.package, &p);
833
834 if (!p)
835 goto out;
836
837 blobmsg_for_each_attr(cur, tb[RPC_O_SECTIONS], rem)
838 {
839 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
840 continue;
841
842 ptr.s = NULL;
843 ptr.section = blobmsg_data(cur);
844
845 if (uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.s)
846 continue;
847
848 uci_reorder_section(cursor, ptr.s, i++);
849 }
850
851 uci_save(cursor, p);
852
853 out:
854 if (p)
855 uci_unload(cursor, p);
856
857 return rpc_uci_status();
858 }
859
860 static void
861 rpc_uci_dump_change(struct uci_delta *d)
862 {
863 void *c;
864 const char *types[] = {
865 [UCI_CMD_REORDER] = "order",
866 [UCI_CMD_REMOVE] = "remove",
867 [UCI_CMD_RENAME] = "rename",
868 [UCI_CMD_ADD] = "add",
869 [UCI_CMD_LIST_ADD] = "list-add",
870 [UCI_CMD_LIST_DEL] = "list-del",
871 [UCI_CMD_CHANGE] = "set",
872 };
873
874 if (!d->section)
875 return;
876
877 c = blobmsg_open_array(&buf, NULL);
878
879 blobmsg_add_string(&buf, NULL, types[d->cmd]);
880 blobmsg_add_string(&buf, NULL, d->section);
881
882 if (d->e.name)
883 blobmsg_add_string(&buf, NULL, d->e.name);
884
885 if (d->value)
886 {
887 if (d->cmd == UCI_CMD_REORDER)
888 blobmsg_add_u32(&buf, NULL, strtoul(d->value, NULL, 10));
889 else
890 blobmsg_add_string(&buf, NULL, d->value);
891 }
892
893 blobmsg_close_array(&buf, c);
894 }
895
896 static int
897 rpc_uci_changes(struct ubus_context *ctx, struct ubus_object *obj,
898 struct ubus_request_data *req, const char *method,
899 struct blob_attr *msg)
900 {
901 struct blob_attr *tb[__RPC_C_MAX];
902 struct uci_package *p = NULL;
903 struct uci_element *e;
904 void *c;
905
906 blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
907 blob_data(msg), blob_len(msg));
908
909 if (!tb[RPC_C_CONFIG])
910 return UBUS_STATUS_INVALID_ARGUMENT;
911
912 uci_load(cursor, blobmsg_data(tb[RPC_C_CONFIG]), &p);
913
914 if (!p)
915 goto out;
916
917 blob_buf_init(&buf, 0);
918 c = blobmsg_open_array(&buf, "changes");
919
920 uci_foreach_element(&p->saved_delta, e)
921 rpc_uci_dump_change(uci_to_delta(e));
922
923 blobmsg_close_array(&buf, c);
924
925 ubus_send_reply(ctx, req, buf.head);
926
927 out:
928 if (p)
929 uci_unload(cursor, p);
930
931 return rpc_uci_status();
932 }
933
934 static int
935 rpc_uci_revert_commit(struct blob_attr *msg, bool commit)
936 {
937 struct blob_attr *tb[__RPC_C_MAX];
938 struct uci_package *p = NULL;
939 struct uci_ptr ptr = { 0 };
940
941 blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
942 blob_data(msg), blob_len(msg));
943
944 if (!tb[RPC_C_CONFIG])
945 return UBUS_STATUS_INVALID_ARGUMENT;
946
947 ptr.package = blobmsg_data(tb[RPC_C_CONFIG]);
948 uci_load(cursor, ptr.package, &p);
949
950 if (!p || uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.p)
951 goto out;
952
953 if (commit)
954 uci_commit(cursor, &p, false);
955 else
956 uci_revert(cursor, &ptr);
957
958 out:
959 if (p)
960 uci_unload(cursor, p);
961
962 return rpc_uci_status();
963 }
964
965 static int
966 rpc_uci_revert(struct ubus_context *ctx, struct ubus_object *obj,
967 struct ubus_request_data *req, const char *method,
968 struct blob_attr *msg)
969 {
970 return rpc_uci_revert_commit(msg, false);
971 }
972
973 static int
974 rpc_uci_commit(struct ubus_context *ctx, struct ubus_object *obj,
975 struct ubus_request_data *req, const char *method,
976 struct blob_attr *msg)
977 {
978 return rpc_uci_revert_commit(msg, true);
979 }
980
981 static int
982 rpc_uci_configs(struct ubus_context *ctx, struct ubus_object *obj,
983 struct ubus_request_data *req, const char *method,
984 struct blob_attr *msg)
985 {
986 char **configs;
987 void *c;
988 int i;
989
990 if (uci_list_configs(cursor, &configs))
991 goto out;
992
993 blob_buf_init(&buf, 0);
994
995 c = blobmsg_open_array(&buf, "configs");
996
997 for (i = 0; configs[i]; i++)
998 blobmsg_add_string(&buf, NULL, configs[i]);
999
1000 blobmsg_close_array(&buf, c);
1001
1002 ubus_send_reply(ctx, req, buf.head);
1003
1004 out:
1005 return rpc_uci_status();
1006 }
1007
1008
1009 int rpc_uci_api_init(struct ubus_context *ctx)
1010 {
1011 static const struct ubus_method uci_methods[] = {
1012 { .name = "configs", .handler = rpc_uci_configs },
1013 UBUS_METHOD("get", rpc_uci_get, rpc_uci_get_policy),
1014 UBUS_METHOD("add", rpc_uci_add, rpc_uci_add_policy),
1015 UBUS_METHOD("set", rpc_uci_set, rpc_uci_set_policy),
1016 UBUS_METHOD("delete", rpc_uci_delete, rpc_uci_delete_policy),
1017 UBUS_METHOD("rename", rpc_uci_rename, rpc_uci_rename_policy),
1018 UBUS_METHOD("order", rpc_uci_order, rpc_uci_order_policy),
1019 UBUS_METHOD("changes", rpc_uci_changes, rpc_uci_config_policy),
1020 UBUS_METHOD("revert", rpc_uci_revert, rpc_uci_config_policy),
1021 UBUS_METHOD("commit", rpc_uci_commit, rpc_uci_config_policy),
1022 };
1023
1024 static struct ubus_object_type uci_type =
1025 UBUS_OBJECT_TYPE("luci-rpc-uci", uci_methods);
1026
1027 static struct ubus_object obj = {
1028 .name = "uci",
1029 .type = &uci_type,
1030 .methods = uci_methods,
1031 .n_methods = ARRAY_SIZE(uci_methods),
1032 };
1033
1034 cursor = uci_alloc_context();
1035
1036 if (!cursor)
1037 return UBUS_STATUS_UNKNOWN_ERROR;
1038
1039 return ubus_add_object(ctx, &obj);
1040 }