81858d45cee55392070fb0d619e79159eec95a46
[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_R_CONFIG,
75         RPC_R_SECTION,
76         RPC_R_OPTION,
77         RPC_R_NAME,
78         __RPC_R_MAX,
79 };
80
81 static const struct blobmsg_policy rpc_uci_rename_policy[__RPC_R_MAX] = {
82         [RPC_R_CONFIG]  = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
83         [RPC_R_SECTION] = { .name = "section",  .type = BLOBMSG_TYPE_STRING },
84         [RPC_R_OPTION]  = { .name = "option",   .type = BLOBMSG_TYPE_STRING },
85         [RPC_R_NAME]    = { .name = "name",     .type = BLOBMSG_TYPE_STRING },
86 };
87
88 enum {
89         RPC_O_CONFIG,
90         RPC_O_SECTIONS,
91         __RPC_O_MAX,
92 };
93
94 static const struct blobmsg_policy rpc_uci_order_policy[__RPC_O_MAX] = {
95         [RPC_O_CONFIG]   = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
96         [RPC_O_SECTIONS] = { .name = "sections", .type = BLOBMSG_TYPE_ARRAY  },
97 };
98
99 enum {
100         RPC_C_CONFIG,
101         __RPC_C_MAX,
102 };
103
104 static const struct blobmsg_policy rpc_uci_config_policy[__RPC_C_MAX] = {
105         [RPC_C_CONFIG]   = { .name = "config",  .type = BLOBMSG_TYPE_STRING },
106 };
107
108 /*
109  * Turn uci error state into ubus return code
110  */
111 static int
112 rpc_uci_status(void)
113 {
114         switch (cursor->err)
115         {
116         case UCI_OK:
117                 return UBUS_STATUS_OK;
118
119         case UCI_ERR_INVAL:
120                 return UBUS_STATUS_INVALID_ARGUMENT;
121
122         case UCI_ERR_NOTFOUND:
123                 return UBUS_STATUS_NOT_FOUND;
124
125         default:
126                 return UBUS_STATUS_UNKNOWN_ERROR;
127         }
128 }
129
130 /*
131  * Format applicable blob value as string and place a pointer to the string
132  * buffer in "p". Uses a static string buffer.
133  */
134 static bool
135 rpc_uci_format_blob(struct blob_attr *v, const char **p)
136 {
137         static char buf[21];
138
139         *p = NULL;
140
141         switch (blobmsg_type(v))
142         {
143         case BLOBMSG_TYPE_STRING:
144                 if (blobmsg_data_len(v) > 1)
145                         *p = blobmsg_data(v);
146                 break;
147
148         case BLOBMSG_TYPE_INT64:
149                 snprintf(buf, sizeof(buf), "%"PRIu64, blobmsg_get_u64(v));
150                 *p = buf;
151                 break;
152
153         case BLOBMSG_TYPE_INT32:
154                 snprintf(buf, sizeof(buf), "%u", blobmsg_get_u32(v));
155                 *p = buf;
156                 break;
157
158         case BLOBMSG_TYPE_INT16:
159                 snprintf(buf, sizeof(buf), "%u", blobmsg_get_u16(v));
160                 *p = buf;
161                 break;
162
163         case BLOBMSG_TYPE_INT8:
164                 snprintf(buf, sizeof(buf), "%u", !!blobmsg_get_u8(v));
165                 *p = buf;
166                 break;
167
168         default:
169                 break;
170         }
171
172         return !!*p;
173 }
174
175 /*
176  * Lookup the given uci_ptr and enable extended lookup format if the .section
177  * value of the uci_ptr looks like extended syntax. Uses an internal copy
178  * of the given uci_ptr to perform the lookup as failing extended section
179  * lookup operations in libuci will zero our the uci_ptr struct.
180  * Copies the internal uci_ptr back to given the uci_ptr on success.
181  */
182 static int
183 rpc_uci_lookup(struct uci_ptr *ptr)
184 {
185         int rv;
186         struct uci_ptr lookup = *ptr;
187
188         if (!lookup.s && lookup.section && *lookup.section == '@')
189                 lookup.flags |= UCI_LOOKUP_EXTENDED;
190
191         rv = uci_lookup_ptr(cursor, &lookup, NULL, true);
192
193         if (!rv)
194                 *ptr = lookup;
195
196         return rv;
197 }
198
199 /*
200  * Checks whether the given uci_option object matches the given string value.
201  *  1) If the uci_option is of type list, check whether any of the list elements
202  *     equals to the given string
203  *  2) If the uci_option is of type string, parse it into space separated tokens
204  *     and check if any of the tokens equals to the given string.
205  *  Returns true if a list element or token matched the given string.
206  */
207 static bool
208 rpc_uci_match_option(struct uci_option *o, const char *cmp)
209 {
210         struct uci_element *e;
211         char *s, *p;
212
213         if (o->type == UCI_TYPE_LIST)
214         {
215                 uci_foreach_element(&o->v.list, e)
216                         if (e->name && !strcmp(e->name, cmp))
217                                 return true;
218
219                 return false;
220         }
221
222         if (!o->v.string)
223                 return false;
224
225         s = strdup(o->v.string);
226
227         if (!s)
228                 return false;
229
230         for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
231         {
232                 if (!strcmp(p, cmp))
233                 {
234                         free(s);
235                         return true;
236                 }
237         }
238
239         free(s);
240         return false;
241 }
242
243 /*
244  * Checks whether the given uci_section matches the type and value blob attrs.
245  *  1) Returns false if "type" is given and the section type does not match
246  *     the value specified in the "type" string blob attribute, else continue.
247  *  2) Tests whether any key in the "matches" table blob attribute exists in
248  *     the given uci_section and whether each value is contained in the
249  *     corresponding uci option value (see rpc_uci_match_option()).
250  *  3) A missing or empty "matches" table blob attribute is always considered
251  *     to be a match.
252  * Returns true if "type" matches or is NULL and "matches" matches or is NULL.
253  */
254 static bool
255 rpc_uci_match_section(struct uci_section *s,
256                       struct blob_attr *type, struct blob_attr *matches)
257 {
258         struct uci_element *e;
259         struct blob_attr *cur;
260         const char *cmp;
261         bool match = false;
262         bool empty = true;
263         int rem;
264
265         if (type && strcmp(s->type, blobmsg_data(type)))
266                 return false;
267
268         if (!matches)
269                 return true;
270
271         blobmsg_for_each_attr(cur, matches, rem)
272         {
273                 if (!rpc_uci_format_blob(cur, &cmp))
274                         continue;
275
276                 uci_foreach_element(&s->options, e)
277                 {
278                         if (strcmp(e->name, blobmsg_name(cur)))
279                                 continue;
280
281                         if (!rpc_uci_match_option(uci_to_option(e), cmp))
282                                 return false;
283
284                         match = true;
285                 }
286
287                 empty = false;
288         }
289
290         return (empty || match);
291 }
292
293 /*
294  * Dump the given uci_option value into the global blobmsg buffer and use
295  * given "name" as key.
296  *  1) If the uci_option is of type list, put a table into the blob buffer and
297  *     add each list item as string to it.
298  *  2) If the uci_option is of type string, put its value directly into the blob
299  *     buffer.
300  */
301 static void
302 rpc_uci_dump_option(struct uci_option *o, const char *name)
303 {
304         void *c;
305         struct uci_element *e;
306
307         switch (o->type)
308         {
309         case UCI_TYPE_STRING:
310                 blobmsg_add_string(&buf, name, o->v.string);
311                 break;
312
313         case UCI_TYPE_LIST:
314                 c = blobmsg_open_array(&buf, name);
315
316                 uci_foreach_element(&o->v.list, e)
317                         blobmsg_add_string(&buf, NULL, e->name);
318
319                 blobmsg_close_array(&buf, c);
320                 break;
321
322         default:
323                 break;
324         }
325 }
326
327 /*
328  * Dump the given uci_section object into the global blobmsg buffer and use
329  * given "name" as key.
330  * Puts a table into the blob buffer and puts each section option member value
331  * as value into the table using the option name as key.
332  * Adds three special keys ".anonymous", ".type" and ".name" which specify the
333  * corresponding section properties.
334  */
335 static void
336 rpc_uci_dump_section(struct uci_section *s, const char *name)
337 {
338         void *c;
339         struct uci_option *o;
340         struct uci_element *e;
341
342         c = blobmsg_open_table(&buf, name);
343
344         blobmsg_add_u8(&buf, ".anonymous", s->anonymous);
345         blobmsg_add_string(&buf, ".type", s->type);
346         blobmsg_add_string(&buf, ".name", s->e.name);
347
348         uci_foreach_element(&s->options, e)
349         {
350                 o = uci_to_option(e);
351                 rpc_uci_dump_option(o, o->e.name);
352         }
353
354         blobmsg_close_table(&buf, c);
355 }
356
357 /*
358  * Dump the given uci_package object into the global blobmsg buffer and use
359  * given "name" as key.
360  * Puts a table into the blob buffer and puts each package section member as
361  * value into the table using the section name as key.
362  * Only dumps sections matching the given "type" and "matches", see explaination
363  * of rpc_uci_match_section() for details.
364  */
365 static void
366 rpc_uci_dump_package(struct uci_package *p, const char *name,
367                      struct blob_attr *type, struct blob_attr *matches)
368 {
369         void *c;
370         struct uci_element *e;
371
372         c = blobmsg_open_table(&buf, name);
373
374         uci_foreach_element(&p->sections, e)
375         {
376                 if (!rpc_uci_match_section(uci_to_section(e), type, matches))
377                         continue;
378
379                 rpc_uci_dump_section(uci_to_section(e), e->name);
380         }
381
382         blobmsg_close_table(&buf, c);
383 }
384
385
386 static int
387 rpc_uci_get(struct ubus_context *ctx, struct ubus_object *obj,
388             struct ubus_request_data *req, const char *method,
389             struct blob_attr *msg)
390 {
391         struct blob_attr *tb[__RPC_G_MAX];
392         struct uci_package *p = NULL;
393         struct uci_ptr ptr = { 0 };
394
395         blobmsg_parse(rpc_uci_get_policy, __RPC_G_MAX, tb,
396                       blob_data(msg), blob_len(msg));
397
398         if (!tb[RPC_G_CONFIG])
399                 return UBUS_STATUS_INVALID_ARGUMENT;
400
401         ptr.package = blobmsg_data(tb[RPC_G_CONFIG]);
402         uci_load(cursor, ptr.package, &p);
403
404         if (!p)
405                 goto out;
406
407         if (tb[RPC_G_SECTION])
408         {
409                 ptr.section = blobmsg_data(tb[RPC_G_SECTION]);
410
411                 if (tb[RPC_G_OPTION])
412                         ptr.option = blobmsg_data(tb[RPC_G_OPTION]);
413         }
414
415         if (rpc_uci_lookup(&ptr) || !(ptr.flags & UCI_LOOKUP_COMPLETE))
416                 goto out;
417
418         blob_buf_init(&buf, 0);
419
420         switch (ptr.last->type)
421         {
422         case UCI_TYPE_PACKAGE:
423                 rpc_uci_dump_package(ptr.p, "values", tb[RPC_G_TYPE], tb[RPC_G_MATCH]);
424                 break;
425
426         case UCI_TYPE_SECTION:
427                 rpc_uci_dump_section(ptr.s, "values");
428                 break;
429
430         case UCI_TYPE_OPTION:
431                 rpc_uci_dump_option(ptr.o, "value");
432                 break;
433
434         default:
435                 break;
436         }
437
438         ubus_send_reply(ctx, req, buf.head);
439
440 out:
441         if (p)
442                 uci_unload(cursor, p);
443
444         return rpc_uci_status();
445 }
446
447 static int
448 rpc_uci_add(struct ubus_context *ctx, struct ubus_object *obj,
449             struct ubus_request_data *req, const char *method,
450             struct blob_attr *msg)
451 {
452         struct blob_attr *tb[__RPC_A_MAX];
453         struct blob_attr *cur, *elem;
454         struct uci_package *p = NULL;
455         struct uci_section *s;
456         struct uci_ptr ptr = { 0 };
457         int rem, rem2;
458
459         blobmsg_parse(rpc_uci_add_policy, __RPC_A_MAX, tb,
460                       blob_data(msg), blob_len(msg));
461
462         if (!tb[RPC_A_CONFIG] || !tb[RPC_A_TYPE])
463                 return UBUS_STATUS_INVALID_ARGUMENT;
464
465         ptr.package = blobmsg_data(tb[RPC_A_CONFIG]);
466
467         uci_load(cursor, ptr.package, &p);
468
469         if (!p)
470                 goto out;
471
472         /* add named section */
473         if (tb[RPC_A_NAME])
474         {
475                 ptr.section = blobmsg_data(tb[RPC_A_NAME]);
476                 ptr.value   = blobmsg_data(tb[RPC_A_TYPE]);
477                 ptr.option  = NULL;
478
479                 if (rpc_uci_lookup(&ptr) || uci_set(cursor, &ptr))
480                         goto out;
481         }
482
483         /* add anon section */
484         else
485         {
486                 if (uci_add_section(cursor, p, blobmsg_data(tb[RPC_A_TYPE]), &s) || !s)
487                         goto out;
488
489                 ptr.section = s->e.name;
490         }
491
492         if (tb[RPC_A_VALUES])
493         {
494                 blobmsg_for_each_attr(cur, tb[RPC_A_VALUES], rem)
495                 {
496                         ptr.o = NULL;
497                         ptr.option = blobmsg_name(cur);
498
499                         if (rpc_uci_lookup(&ptr) || !ptr.s)
500                                 continue;
501
502                         switch (blobmsg_type(cur))
503                         {
504                         case BLOBMSG_TYPE_ARRAY:
505                                 blobmsg_for_each_attr(elem, cur, rem2)
506                                         if (rpc_uci_format_blob(elem, &ptr.value))
507                                                 uci_add_list(cursor, &ptr);
508                                 break;
509
510                         default:
511                                 if (rpc_uci_format_blob(cur, &ptr.value))
512                                         uci_set(cursor, &ptr);
513                                 break;
514                         }
515                 }
516         }
517
518         uci_save(cursor, p);
519
520         blob_buf_init(&buf, 0);
521         blobmsg_add_string(&buf, "section", ptr.section);
522         ubus_send_reply(ctx, req, buf.head);
523
524 out:
525         if (p)
526                 uci_unload(cursor, p);
527
528         return rpc_uci_status();
529 }
530
531 /*
532  * Turn value from a blob attribute into uci set operation
533  *  1) if the blob is of type array, delete existing option (if any) and
534  *     emit uci add_list operations for each element
535  *  2) if the blob is not an array but an option of type list exists,
536  *     delete existing list and emit uci set operation for the blob value
537  *  3) in all other cases only emit a set operation if there is no existing
538  *     option of if the existing options value differs from the blob value
539  */
540 static void
541 rpc_uci_merge_set(struct blob_attr *opt, struct uci_ptr *ptr)
542 {
543         struct blob_attr *cur;
544         int rem;
545
546         ptr->o = NULL;
547         ptr->option = blobmsg_name(opt);
548
549         if (rpc_uci_lookup(ptr) || !ptr->s)
550                 return;
551
552         if (blobmsg_type(opt) == BLOBMSG_TYPE_ARRAY)
553         {
554                 if (ptr->o)
555                         uci_delete(cursor, ptr);
556
557                 blobmsg_for_each_attr(cur, opt, rem)
558                         if (rpc_uci_format_blob(cur, &ptr->value))
559                                 uci_add_list(cursor, ptr);
560         }
561         else if (ptr->o && ptr->o->type == UCI_TYPE_LIST)
562         {
563                 uci_delete(cursor, ptr);
564
565                 if (rpc_uci_format_blob(opt, &ptr->value))
566                         uci_set(cursor, ptr);
567         }
568         else if (rpc_uci_format_blob(opt, &ptr->value))
569         {
570                 if (!ptr->o || !ptr->o->v.string || strcmp(ptr->o->v.string, ptr->value))
571                         uci_set(cursor, ptr);
572         }
573 }
574
575 static int
576 rpc_uci_set(struct ubus_context *ctx, struct ubus_object *obj,
577             struct ubus_request_data *req, const char *method,
578             struct blob_attr *msg)
579 {
580         struct blob_attr *tb[__RPC_S_MAX];
581         struct blob_attr *cur;
582         struct uci_package *p = NULL;
583         struct uci_element *e;
584         struct uci_ptr ptr = { 0 };
585         int rem;
586
587         blobmsg_parse(rpc_uci_set_policy, __RPC_S_MAX, tb,
588                       blob_data(msg), blob_len(msg));
589
590         if (!tb[RPC_S_CONFIG] || !tb[RPC_S_VALUES] ||
591                 (!tb[RPC_S_SECTION] && !tb[RPC_S_TYPE] && !tb[RPC_S_MATCH]))
592                 return UBUS_STATUS_INVALID_ARGUMENT;
593
594         ptr.package = blobmsg_data(tb[RPC_S_CONFIG]);
595         uci_load(cursor, ptr.package, &p);
596
597         if (!p)
598                 goto out;
599
600         if (tb[RPC_S_SECTION])
601         {
602                 ptr.section = blobmsg_data(tb[RPC_S_SECTION]);
603                 blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
604                         rpc_uci_merge_set(cur, &ptr);
605         }
606         else
607         {
608                 uci_foreach_element(&p->sections, e)
609                 {
610                         if (!rpc_uci_match_section(uci_to_section(e),
611                                                    tb[RPC_S_TYPE], tb[RPC_S_MATCH]))
612                                 continue;
613
614                         ptr.s = NULL;
615                         ptr.section = e->name;
616
617                         blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
618                                 rpc_uci_merge_set(cur, &ptr);
619                 }
620         }
621
622         uci_save(cursor, p);
623
624 out:
625         if (p)
626                 uci_unload(cursor, p);
627
628         return rpc_uci_status();
629 }
630
631 static int
632 rpc_uci_rename(struct ubus_context *ctx, struct ubus_object *obj,
633                struct ubus_request_data *req, const char *method,
634                struct blob_attr *msg)
635 {
636         struct blob_attr *tb[__RPC_R_MAX];
637         struct uci_package *p = NULL;
638         struct uci_ptr ptr = { 0 };
639
640         blobmsg_parse(rpc_uci_rename_policy, __RPC_R_MAX, tb,
641                       blob_data(msg), blob_len(msg));
642
643         if (!tb[RPC_R_CONFIG] || !tb[RPC_R_SECTION] || !tb[RPC_R_NAME])
644                 return UBUS_STATUS_INVALID_ARGUMENT;
645
646         ptr.package = blobmsg_data(tb[RPC_R_CONFIG]);
647         ptr.section = blobmsg_data(tb[RPC_R_SECTION]);
648         ptr.value   = blobmsg_data(tb[RPC_R_NAME]);
649
650         if (tb[RPC_R_OPTION])
651                 ptr.option = blobmsg_data(tb[RPC_R_OPTION]);
652
653         uci_load(cursor, ptr.package, &p);
654
655         if (!p)
656                 goto out;
657
658         if (uci_lookup_ptr(cursor, &ptr, NULL, true))
659                 goto out;
660
661         if ((ptr.option && !ptr.o) || !ptr.s)
662         {
663                 cursor->err = UCI_ERR_NOTFOUND;
664                 goto out;
665         }
666
667         if (uci_rename(cursor, &ptr))
668                 goto out;
669
670         uci_save(cursor, p);
671
672 out:
673         if (p)
674                 uci_unload(cursor, p);
675
676         return rpc_uci_status();
677 }
678
679 static int
680 rpc_uci_order(struct ubus_context *ctx, struct ubus_object *obj,
681               struct ubus_request_data *req, const char *method,
682               struct blob_attr *msg)
683 {
684         struct blob_attr *tb[__RPC_O_MAX];
685         struct blob_attr *cur;
686         struct uci_package *p = NULL;
687         struct uci_ptr ptr = { 0 };
688         int rem, i = 1;
689
690         blobmsg_parse(rpc_uci_order_policy, __RPC_O_MAX, tb,
691                       blob_data(msg), blob_len(msg));
692
693         if (!tb[RPC_O_CONFIG] || !tb[RPC_O_SECTIONS])
694                 return UBUS_STATUS_INVALID_ARGUMENT;
695
696         ptr.package = blobmsg_data(tb[RPC_O_CONFIG]);
697
698         uci_load(cursor, ptr.package, &p);
699
700         if (!p)
701                 goto out;
702
703         blobmsg_for_each_attr(cur, tb[RPC_O_SECTIONS], rem)
704         {
705                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
706                         continue;
707
708                 ptr.s = NULL;
709                 ptr.section = blobmsg_data(cur);
710
711                 if (uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.s)
712                         continue;
713
714                 uci_reorder_section(cursor, ptr.s, i++);
715         }
716
717         uci_save(cursor, p);
718
719 out:
720         if (p)
721                 uci_unload(cursor, p);
722
723         return rpc_uci_status();
724 }
725
726 static void
727 rpc_uci_dump_change(struct uci_delta *d)
728 {
729         void *c;
730         const char *types[] = {
731                 [UCI_CMD_REORDER]  = "order",
732                 [UCI_CMD_REMOVE]   = "remove",
733                 [UCI_CMD_RENAME]   = "rename",
734                 [UCI_CMD_ADD]      = "add",
735                 [UCI_CMD_LIST_ADD] = "list-add",
736                 [UCI_CMD_LIST_DEL] = "list-del",
737                 [UCI_CMD_CHANGE]   = "set",
738         };
739
740         if (!d->section)
741                 return;
742
743         c = blobmsg_open_array(&buf, NULL);
744
745         blobmsg_add_string(&buf, NULL, types[d->cmd]);
746         blobmsg_add_string(&buf, NULL, d->section);
747
748         if (d->e.name)
749                 blobmsg_add_string(&buf, NULL, d->e.name);
750
751         if (d->value)
752         {
753                 if (d->cmd == UCI_CMD_REORDER)
754                         blobmsg_add_u32(&buf, NULL, strtoul(d->value, NULL, 10));
755                 else
756                         blobmsg_add_string(&buf, NULL, d->value);
757         }
758
759         blobmsg_close_array(&buf, c);
760 }
761
762 static int
763 rpc_uci_changes(struct ubus_context *ctx, struct ubus_object *obj,
764                 struct ubus_request_data *req, const char *method,
765                 struct blob_attr *msg)
766 {
767         struct blob_attr *tb[__RPC_C_MAX];
768         struct uci_package *p = NULL;
769         struct uci_element *e;
770         void *c;
771
772         blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
773                       blob_data(msg), blob_len(msg));
774
775         if (!tb[RPC_C_CONFIG])
776                 return UBUS_STATUS_INVALID_ARGUMENT;
777
778         uci_load(cursor, blobmsg_data(tb[RPC_C_CONFIG]), &p);
779
780         if (!p)
781                 goto out;
782
783         blob_buf_init(&buf, 0);
784         c = blobmsg_open_array(&buf, "changes");
785
786         uci_foreach_element(&p->saved_delta, e)
787                 rpc_uci_dump_change(uci_to_delta(e));
788
789         blobmsg_close_array(&buf, c);
790
791         ubus_send_reply(ctx, req, buf.head);
792
793 out:
794         if (p)
795                 uci_unload(cursor, p);
796
797         return rpc_uci_status();
798 }
799
800 static int
801 rpc_uci_revert_commit(struct blob_attr *msg, bool commit)
802 {
803         struct blob_attr *tb[__RPC_C_MAX];
804         struct uci_package *p = NULL;
805         struct uci_ptr ptr = { 0 };
806
807         blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
808                       blob_data(msg), blob_len(msg));
809
810         if (!tb[RPC_C_CONFIG])
811                 return UBUS_STATUS_INVALID_ARGUMENT;
812
813         ptr.package = blobmsg_data(tb[RPC_C_CONFIG]);
814         uci_load(cursor, ptr.package, &p);
815
816         if (!p || uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.p)
817                 goto out;
818
819         if (commit)
820                 uci_commit(cursor, &p, false);
821         else
822                 uci_revert(cursor, &ptr);
823
824 out:
825         if (p)
826                 uci_unload(cursor, p);
827
828         return rpc_uci_status();
829 }
830
831 static int
832 rpc_uci_revert(struct ubus_context *ctx, struct ubus_object *obj,
833                struct ubus_request_data *req, const char *method,
834                struct blob_attr *msg)
835 {
836         return rpc_uci_revert_commit(msg, false);
837 }
838
839 static int
840 rpc_uci_commit(struct ubus_context *ctx, struct ubus_object *obj,
841                struct ubus_request_data *req, const char *method,
842                struct blob_attr *msg)
843 {
844         return rpc_uci_revert_commit(msg, true);
845 }
846
847
848 int rpc_uci_api_init(struct ubus_context *ctx)
849 {
850         static const struct ubus_method uci_methods[] = {
851                 UBUS_METHOD("get",     rpc_uci_get,     rpc_uci_get_policy),
852                 UBUS_METHOD("add",     rpc_uci_add,     rpc_uci_add_policy),
853                 UBUS_METHOD("set",     rpc_uci_set,     rpc_uci_set_policy),
854                 UBUS_METHOD("rename",  rpc_uci_rename,  rpc_uci_rename_policy),
855                 UBUS_METHOD("order",   rpc_uci_order,   rpc_uci_order_policy),
856                 UBUS_METHOD("changes", rpc_uci_changes, rpc_uci_config_policy),
857                 UBUS_METHOD("revert",  rpc_uci_revert,  rpc_uci_config_policy),
858                 UBUS_METHOD("commit",  rpc_uci_commit,  rpc_uci_config_policy),
859         };
860
861         static struct ubus_object_type uci_type =
862                 UBUS_OBJECT_TYPE("luci-rpc-uci", uci_methods);
863
864         static struct ubus_object obj = {
865                 .name = "uci",
866                 .type = &uci_type,
867                 .methods = uci_methods,
868                 .n_methods = ARRAY_SIZE(uci_methods),
869         };
870
871         cursor = uci_alloc_context();
872
873         if (!cursor)
874                 return UBUS_STATUS_UNKNOWN_ERROR;
875
876         return ubus_add_object(ctx, &obj);
877 }