uci: add delete method
[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)
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 uci_foreach_element(&s->options, e)
368 {
369 o = uci_to_option(e);
370 rpc_uci_dump_option(o, o->e.name);
371 }
372
373 blobmsg_close_table(&buf, c);
374 }
375
376 /*
377 * Dump the given uci_package object into the global blobmsg buffer and use
378 * given "name" as key.
379 * Puts a table into the blob buffer and puts each package section member as
380 * value into the table using the section name as key.
381 * Only dumps sections matching the given "type" and "matches", see explaination
382 * of rpc_uci_match_section() for details.
383 */
384 static void
385 rpc_uci_dump_package(struct uci_package *p, const char *name,
386 struct blob_attr *type, struct blob_attr *matches)
387 {
388 void *c;
389 struct uci_element *e;
390
391 c = blobmsg_open_table(&buf, name);
392
393 uci_foreach_element(&p->sections, e)
394 {
395 if (!rpc_uci_match_section(uci_to_section(e), type, matches))
396 continue;
397
398 rpc_uci_dump_section(uci_to_section(e), e->name);
399 }
400
401 blobmsg_close_table(&buf, c);
402 }
403
404
405 static int
406 rpc_uci_get(struct ubus_context *ctx, struct ubus_object *obj,
407 struct ubus_request_data *req, const char *method,
408 struct blob_attr *msg)
409 {
410 struct blob_attr *tb[__RPC_G_MAX];
411 struct uci_package *p = NULL;
412 struct uci_ptr ptr = { 0 };
413
414 blobmsg_parse(rpc_uci_get_policy, __RPC_G_MAX, tb,
415 blob_data(msg), blob_len(msg));
416
417 if (!tb[RPC_G_CONFIG])
418 return UBUS_STATUS_INVALID_ARGUMENT;
419
420 ptr.package = blobmsg_data(tb[RPC_G_CONFIG]);
421 uci_load(cursor, ptr.package, &p);
422
423 if (!p)
424 goto out;
425
426 if (tb[RPC_G_SECTION])
427 {
428 ptr.section = blobmsg_data(tb[RPC_G_SECTION]);
429
430 if (tb[RPC_G_OPTION])
431 ptr.option = blobmsg_data(tb[RPC_G_OPTION]);
432 }
433
434 if (rpc_uci_lookup(&ptr) || !(ptr.flags & UCI_LOOKUP_COMPLETE))
435 goto out;
436
437 blob_buf_init(&buf, 0);
438
439 switch (ptr.last->type)
440 {
441 case UCI_TYPE_PACKAGE:
442 rpc_uci_dump_package(ptr.p, "values", tb[RPC_G_TYPE], tb[RPC_G_MATCH]);
443 break;
444
445 case UCI_TYPE_SECTION:
446 rpc_uci_dump_section(ptr.s, "values");
447 break;
448
449 case UCI_TYPE_OPTION:
450 rpc_uci_dump_option(ptr.o, "value");
451 break;
452
453 default:
454 break;
455 }
456
457 ubus_send_reply(ctx, req, buf.head);
458
459 out:
460 if (p)
461 uci_unload(cursor, p);
462
463 return rpc_uci_status();
464 }
465
466 static int
467 rpc_uci_add(struct ubus_context *ctx, struct ubus_object *obj,
468 struct ubus_request_data *req, const char *method,
469 struct blob_attr *msg)
470 {
471 struct blob_attr *tb[__RPC_A_MAX];
472 struct blob_attr *cur, *elem;
473 struct uci_package *p = NULL;
474 struct uci_section *s;
475 struct uci_ptr ptr = { 0 };
476 int rem, rem2;
477
478 blobmsg_parse(rpc_uci_add_policy, __RPC_A_MAX, tb,
479 blob_data(msg), blob_len(msg));
480
481 if (!tb[RPC_A_CONFIG] || !tb[RPC_A_TYPE])
482 return UBUS_STATUS_INVALID_ARGUMENT;
483
484 ptr.package = blobmsg_data(tb[RPC_A_CONFIG]);
485
486 uci_load(cursor, ptr.package, &p);
487
488 if (!p)
489 goto out;
490
491 /* add named section */
492 if (tb[RPC_A_NAME])
493 {
494 ptr.section = blobmsg_data(tb[RPC_A_NAME]);
495 ptr.value = blobmsg_data(tb[RPC_A_TYPE]);
496 ptr.option = NULL;
497
498 if (rpc_uci_lookup(&ptr) || uci_set(cursor, &ptr))
499 goto out;
500 }
501
502 /* add anon section */
503 else
504 {
505 if (uci_add_section(cursor, p, blobmsg_data(tb[RPC_A_TYPE]), &s) || !s)
506 goto out;
507
508 ptr.section = s->e.name;
509 }
510
511 if (tb[RPC_A_VALUES])
512 {
513 blobmsg_for_each_attr(cur, tb[RPC_A_VALUES], rem)
514 {
515 ptr.o = NULL;
516 ptr.option = blobmsg_name(cur);
517
518 if (rpc_uci_lookup(&ptr) || !ptr.s)
519 continue;
520
521 switch (blobmsg_type(cur))
522 {
523 case BLOBMSG_TYPE_ARRAY:
524 blobmsg_for_each_attr(elem, cur, rem2)
525 if (rpc_uci_format_blob(elem, &ptr.value))
526 uci_add_list(cursor, &ptr);
527 break;
528
529 default:
530 if (rpc_uci_format_blob(cur, &ptr.value))
531 uci_set(cursor, &ptr);
532 break;
533 }
534 }
535 }
536
537 uci_save(cursor, p);
538
539 blob_buf_init(&buf, 0);
540 blobmsg_add_string(&buf, "section", ptr.section);
541 ubus_send_reply(ctx, req, buf.head);
542
543 out:
544 if (p)
545 uci_unload(cursor, p);
546
547 return rpc_uci_status();
548 }
549
550 /*
551 * Turn value from a blob attribute into uci set operation
552 * 1) if the blob is of type array, delete existing option (if any) and
553 * emit uci add_list operations for each element
554 * 2) if the blob is not an array but an option of type list exists,
555 * delete existing list and emit uci set operation for the blob value
556 * 3) in all other cases only emit a set operation if there is no existing
557 * option of if the existing options value differs from the blob value
558 */
559 static void
560 rpc_uci_merge_set(struct blob_attr *opt, struct uci_ptr *ptr)
561 {
562 struct blob_attr *cur;
563 int rem;
564
565 ptr->o = NULL;
566 ptr->option = blobmsg_name(opt);
567
568 if (rpc_uci_lookup(ptr) || !ptr->s)
569 return;
570
571 if (blobmsg_type(opt) == BLOBMSG_TYPE_ARRAY)
572 {
573 if (ptr->o)
574 uci_delete(cursor, ptr);
575
576 blobmsg_for_each_attr(cur, opt, rem)
577 if (rpc_uci_format_blob(cur, &ptr->value))
578 uci_add_list(cursor, ptr);
579 }
580 else if (ptr->o && ptr->o->type == UCI_TYPE_LIST)
581 {
582 uci_delete(cursor, ptr);
583
584 if (rpc_uci_format_blob(opt, &ptr->value))
585 uci_set(cursor, ptr);
586 }
587 else if (rpc_uci_format_blob(opt, &ptr->value))
588 {
589 if (!ptr->o || !ptr->o->v.string || strcmp(ptr->o->v.string, ptr->value))
590 uci_set(cursor, ptr);
591 }
592 }
593
594 static int
595 rpc_uci_set(struct ubus_context *ctx, struct ubus_object *obj,
596 struct ubus_request_data *req, const char *method,
597 struct blob_attr *msg)
598 {
599 struct blob_attr *tb[__RPC_S_MAX];
600 struct blob_attr *cur;
601 struct uci_package *p = NULL;
602 struct uci_element *e;
603 struct uci_ptr ptr = { 0 };
604 int rem;
605
606 blobmsg_parse(rpc_uci_set_policy, __RPC_S_MAX, tb,
607 blob_data(msg), blob_len(msg));
608
609 if (!tb[RPC_S_CONFIG] || !tb[RPC_S_VALUES] ||
610 (!tb[RPC_S_SECTION] && !tb[RPC_S_TYPE] && !tb[RPC_S_MATCH]))
611 return UBUS_STATUS_INVALID_ARGUMENT;
612
613 ptr.package = blobmsg_data(tb[RPC_S_CONFIG]);
614 uci_load(cursor, ptr.package, &p);
615
616 if (!p)
617 goto out;
618
619 if (tb[RPC_S_SECTION])
620 {
621 ptr.section = blobmsg_data(tb[RPC_S_SECTION]);
622 blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
623 rpc_uci_merge_set(cur, &ptr);
624 }
625 else
626 {
627 uci_foreach_element(&p->sections, e)
628 {
629 if (!rpc_uci_match_section(uci_to_section(e),
630 tb[RPC_S_TYPE], tb[RPC_S_MATCH]))
631 continue;
632
633 ptr.s = NULL;
634 ptr.section = e->name;
635
636 blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
637 rpc_uci_merge_set(cur, &ptr);
638 }
639 }
640
641 uci_save(cursor, p);
642
643 out:
644 if (p)
645 uci_unload(cursor, p);
646
647 return rpc_uci_status();
648 }
649
650 /*
651 * Delete option or section from uci specified by given blob attribute pointer
652 * 1) if the blob is of type array, delete any option named after each element
653 * 2) if the blob is of type string, delete the option named after its value
654 * 3) if the blob is NULL, delete entire section
655 */
656 static void
657 rpc_uci_merge_delete(struct blob_attr *opt, struct uci_ptr *ptr)
658 {
659 struct blob_attr *cur;
660 int rem;
661
662 if (rpc_uci_lookup(ptr) || !ptr->s)
663 return;
664
665 if (!opt)
666 {
667 ptr->o = NULL;
668 ptr->option = NULL;
669
670 uci_delete(cursor, ptr);
671 }
672 else if (blobmsg_type(opt) == BLOBMSG_TYPE_ARRAY)
673 {
674 blobmsg_for_each_attr(cur, opt, rem)
675 {
676 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
677 continue;
678
679 ptr->o = NULL;
680 ptr->option = blobmsg_data(cur);
681
682 if (rpc_uci_lookup(ptr) || !ptr->o)
683 continue;
684
685 uci_delete(cursor, ptr);
686 }
687 }
688 else if (blobmsg_type(opt) == BLOBMSG_TYPE_STRING)
689 {
690 ptr->o = NULL;
691 ptr->option = blobmsg_data(opt);
692
693 if (rpc_uci_lookup(ptr) || !ptr->o)
694 return;
695
696 uci_delete(cursor, ptr);
697 }
698 }
699
700 static int
701 rpc_uci_delete(struct ubus_context *ctx, struct ubus_object *obj,
702 struct ubus_request_data *req, const char *method,
703 struct blob_attr *msg)
704 {
705 struct blob_attr *tb[__RPC_D_MAX];
706 struct uci_package *p = NULL;
707 struct uci_element *e, *tmp;
708 struct uci_ptr ptr = { 0 };
709
710 blobmsg_parse(rpc_uci_delete_policy, __RPC_D_MAX, tb,
711 blob_data(msg), blob_len(msg));
712
713 if (!tb[RPC_D_CONFIG] ||
714 (!tb[RPC_D_SECTION] && !tb[RPC_D_TYPE] && !tb[RPC_D_MATCH]))
715 return UBUS_STATUS_INVALID_ARGUMENT;
716
717 ptr.package = blobmsg_data(tb[RPC_D_CONFIG]);
718 uci_load(cursor, ptr.package, &p);
719
720 if (!p)
721 goto out;
722
723 if (tb[RPC_D_SECTION])
724 {
725 ptr.section = blobmsg_data(tb[RPC_D_SECTION]);
726
727 if (tb[RPC_D_OPTIONS])
728 rpc_uci_merge_delete(tb[RPC_D_OPTIONS], &ptr);
729 else
730 rpc_uci_merge_delete(tb[RPC_D_OPTION], &ptr);
731 }
732 else
733 {
734 uci_foreach_element_safe(&p->sections, tmp, e)
735 {
736 if (!rpc_uci_match_section(uci_to_section(e),
737 tb[RPC_D_TYPE], tb[RPC_D_MATCH]))
738 continue;
739
740 ptr.s = NULL;
741 ptr.section = e->name;
742
743 if (tb[RPC_D_OPTIONS])
744 rpc_uci_merge_delete(tb[RPC_D_OPTIONS], &ptr);
745 else
746 rpc_uci_merge_delete(tb[RPC_D_OPTION], &ptr);
747 }
748 }
749
750 uci_save(cursor, p);
751
752 out:
753 if (p)
754 uci_unload(cursor, p);
755
756 return rpc_uci_status();
757 }
758
759 static int
760 rpc_uci_rename(struct ubus_context *ctx, struct ubus_object *obj,
761 struct ubus_request_data *req, const char *method,
762 struct blob_attr *msg)
763 {
764 struct blob_attr *tb[__RPC_R_MAX];
765 struct uci_package *p = NULL;
766 struct uci_ptr ptr = { 0 };
767
768 blobmsg_parse(rpc_uci_rename_policy, __RPC_R_MAX, tb,
769 blob_data(msg), blob_len(msg));
770
771 if (!tb[RPC_R_CONFIG] || !tb[RPC_R_SECTION] || !tb[RPC_R_NAME])
772 return UBUS_STATUS_INVALID_ARGUMENT;
773
774 ptr.package = blobmsg_data(tb[RPC_R_CONFIG]);
775 ptr.section = blobmsg_data(tb[RPC_R_SECTION]);
776 ptr.value = blobmsg_data(tb[RPC_R_NAME]);
777
778 if (tb[RPC_R_OPTION])
779 ptr.option = blobmsg_data(tb[RPC_R_OPTION]);
780
781 uci_load(cursor, ptr.package, &p);
782
783 if (!p)
784 goto out;
785
786 if (uci_lookup_ptr(cursor, &ptr, NULL, true))
787 goto out;
788
789 if ((ptr.option && !ptr.o) || !ptr.s)
790 {
791 cursor->err = UCI_ERR_NOTFOUND;
792 goto out;
793 }
794
795 if (uci_rename(cursor, &ptr))
796 goto out;
797
798 uci_save(cursor, p);
799
800 out:
801 if (p)
802 uci_unload(cursor, p);
803
804 return rpc_uci_status();
805 }
806
807 static int
808 rpc_uci_order(struct ubus_context *ctx, struct ubus_object *obj,
809 struct ubus_request_data *req, const char *method,
810 struct blob_attr *msg)
811 {
812 struct blob_attr *tb[__RPC_O_MAX];
813 struct blob_attr *cur;
814 struct uci_package *p = NULL;
815 struct uci_ptr ptr = { 0 };
816 int rem, i = 1;
817
818 blobmsg_parse(rpc_uci_order_policy, __RPC_O_MAX, tb,
819 blob_data(msg), blob_len(msg));
820
821 if (!tb[RPC_O_CONFIG] || !tb[RPC_O_SECTIONS])
822 return UBUS_STATUS_INVALID_ARGUMENT;
823
824 ptr.package = blobmsg_data(tb[RPC_O_CONFIG]);
825
826 uci_load(cursor, ptr.package, &p);
827
828 if (!p)
829 goto out;
830
831 blobmsg_for_each_attr(cur, tb[RPC_O_SECTIONS], rem)
832 {
833 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
834 continue;
835
836 ptr.s = NULL;
837 ptr.section = blobmsg_data(cur);
838
839 if (uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.s)
840 continue;
841
842 uci_reorder_section(cursor, ptr.s, i++);
843 }
844
845 uci_save(cursor, p);
846
847 out:
848 if (p)
849 uci_unload(cursor, p);
850
851 return rpc_uci_status();
852 }
853
854 static void
855 rpc_uci_dump_change(struct uci_delta *d)
856 {
857 void *c;
858 const char *types[] = {
859 [UCI_CMD_REORDER] = "order",
860 [UCI_CMD_REMOVE] = "remove",
861 [UCI_CMD_RENAME] = "rename",
862 [UCI_CMD_ADD] = "add",
863 [UCI_CMD_LIST_ADD] = "list-add",
864 [UCI_CMD_LIST_DEL] = "list-del",
865 [UCI_CMD_CHANGE] = "set",
866 };
867
868 if (!d->section)
869 return;
870
871 c = blobmsg_open_array(&buf, NULL);
872
873 blobmsg_add_string(&buf, NULL, types[d->cmd]);
874 blobmsg_add_string(&buf, NULL, d->section);
875
876 if (d->e.name)
877 blobmsg_add_string(&buf, NULL, d->e.name);
878
879 if (d->value)
880 {
881 if (d->cmd == UCI_CMD_REORDER)
882 blobmsg_add_u32(&buf, NULL, strtoul(d->value, NULL, 10));
883 else
884 blobmsg_add_string(&buf, NULL, d->value);
885 }
886
887 blobmsg_close_array(&buf, c);
888 }
889
890 static int
891 rpc_uci_changes(struct ubus_context *ctx, struct ubus_object *obj,
892 struct ubus_request_data *req, const char *method,
893 struct blob_attr *msg)
894 {
895 struct blob_attr *tb[__RPC_C_MAX];
896 struct uci_package *p = NULL;
897 struct uci_element *e;
898 void *c;
899
900 blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
901 blob_data(msg), blob_len(msg));
902
903 if (!tb[RPC_C_CONFIG])
904 return UBUS_STATUS_INVALID_ARGUMENT;
905
906 uci_load(cursor, blobmsg_data(tb[RPC_C_CONFIG]), &p);
907
908 if (!p)
909 goto out;
910
911 blob_buf_init(&buf, 0);
912 c = blobmsg_open_array(&buf, "changes");
913
914 uci_foreach_element(&p->saved_delta, e)
915 rpc_uci_dump_change(uci_to_delta(e));
916
917 blobmsg_close_array(&buf, c);
918
919 ubus_send_reply(ctx, req, buf.head);
920
921 out:
922 if (p)
923 uci_unload(cursor, p);
924
925 return rpc_uci_status();
926 }
927
928 static int
929 rpc_uci_revert_commit(struct blob_attr *msg, bool commit)
930 {
931 struct blob_attr *tb[__RPC_C_MAX];
932 struct uci_package *p = NULL;
933 struct uci_ptr ptr = { 0 };
934
935 blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
936 blob_data(msg), blob_len(msg));
937
938 if (!tb[RPC_C_CONFIG])
939 return UBUS_STATUS_INVALID_ARGUMENT;
940
941 ptr.package = blobmsg_data(tb[RPC_C_CONFIG]);
942 uci_load(cursor, ptr.package, &p);
943
944 if (!p || uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.p)
945 goto out;
946
947 if (commit)
948 uci_commit(cursor, &p, false);
949 else
950 uci_revert(cursor, &ptr);
951
952 out:
953 if (p)
954 uci_unload(cursor, p);
955
956 return rpc_uci_status();
957 }
958
959 static int
960 rpc_uci_revert(struct ubus_context *ctx, struct ubus_object *obj,
961 struct ubus_request_data *req, const char *method,
962 struct blob_attr *msg)
963 {
964 return rpc_uci_revert_commit(msg, false);
965 }
966
967 static int
968 rpc_uci_commit(struct ubus_context *ctx, struct ubus_object *obj,
969 struct ubus_request_data *req, const char *method,
970 struct blob_attr *msg)
971 {
972 return rpc_uci_revert_commit(msg, true);
973 }
974
975 static int
976 rpc_uci_configs(struct ubus_context *ctx, struct ubus_object *obj,
977 struct ubus_request_data *req, const char *method,
978 struct blob_attr *msg)
979 {
980 char **configs;
981 void *c;
982 int i;
983
984 if (uci_list_configs(cursor, &configs))
985 goto out;
986
987 blob_buf_init(&buf, 0);
988
989 c = blobmsg_open_array(&buf, "configs");
990
991 for (i = 0; configs[i]; i++)
992 blobmsg_add_string(&buf, NULL, configs[i]);
993
994 blobmsg_close_array(&buf, c);
995
996 ubus_send_reply(ctx, req, buf.head);
997
998 out:
999 return rpc_uci_status();
1000 }
1001
1002
1003 int rpc_uci_api_init(struct ubus_context *ctx)
1004 {
1005 static const struct ubus_method uci_methods[] = {
1006 { .name = "configs", .handler = rpc_uci_configs },
1007 UBUS_METHOD("get", rpc_uci_get, rpc_uci_get_policy),
1008 UBUS_METHOD("add", rpc_uci_add, rpc_uci_add_policy),
1009 UBUS_METHOD("set", rpc_uci_set, rpc_uci_set_policy),
1010 UBUS_METHOD("delete", rpc_uci_delete, rpc_uci_delete_policy),
1011 UBUS_METHOD("rename", rpc_uci_rename, rpc_uci_rename_policy),
1012 UBUS_METHOD("order", rpc_uci_order, rpc_uci_order_policy),
1013 UBUS_METHOD("changes", rpc_uci_changes, rpc_uci_config_policy),
1014 UBUS_METHOD("revert", rpc_uci_revert, rpc_uci_config_policy),
1015 UBUS_METHOD("commit", rpc_uci_commit, rpc_uci_config_policy),
1016 };
1017
1018 static struct ubus_object_type uci_type =
1019 UBUS_OBJECT_TYPE("luci-rpc-uci", uci_methods);
1020
1021 static struct ubus_object obj = {
1022 .name = "uci",
1023 .type = &uci_type,
1024 .methods = uci_methods,
1025 .n_methods = ARRAY_SIZE(uci_methods),
1026 };
1027
1028 cursor = uci_alloc_context();
1029
1030 if (!cursor)
1031 return UBUS_STATUS_UNKNOWN_ERROR;
1032
1033 return ubus_add_object(ctx, &obj);
1034 }