add a reload_config method to the uci object
[project/rpcd.git] / uci.c
1 /*
2  * rpcd - UBUS RPC server
3  *
4  *   Copyright (C) 2013-2014 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 <libgen.h>
20 #include <glob.h>
21
22 #include <libubox/blobmsg.h>
23 #include <libubox/blobmsg_json.h>
24
25 #include <rpcd/uci.h>
26 #include <rpcd/exec.h>
27 #include <rpcd/session.h>
28
29 static struct blob_buf buf;
30 static struct uci_context *cursor;
31 static struct uloop_timeout apply_timer;
32 static struct ubus_context *apply_ctx;
33 static char apply_sid[RPC_SID_LEN + 1];
34
35 enum {
36         RPC_G_CONFIG,
37         RPC_G_SECTION,
38         RPC_G_OPTION,
39         RPC_G_TYPE,
40         RPC_G_MATCH,
41         RPC_G_SESSION,
42         __RPC_G_MAX,
43 };
44
45 static const struct blobmsg_policy rpc_uci_get_policy[__RPC_G_MAX] = {
46         [RPC_G_CONFIG]  = { .name = "config",  .type = BLOBMSG_TYPE_STRING },
47         [RPC_G_SECTION] = { .name = "section", .type = BLOBMSG_TYPE_STRING },
48         [RPC_G_OPTION]  = { .name = "option",  .type = BLOBMSG_TYPE_STRING },
49         [RPC_G_TYPE]    = { .name = "type",    .type = BLOBMSG_TYPE_STRING },
50         [RPC_G_MATCH]   = { .name = "match",   .type = BLOBMSG_TYPE_TABLE  },
51         [RPC_G_SESSION] = { .name = "ubus_rpc_session",
52                                                .type = BLOBMSG_TYPE_STRING },
53 };
54
55 enum {
56         RPC_A_CONFIG,
57         RPC_A_TYPE,
58         RPC_A_NAME,
59         RPC_A_VALUES,
60         RPC_A_SESSION,
61         __RPC_A_MAX,
62 };
63
64 static const struct blobmsg_policy rpc_uci_add_policy[__RPC_A_MAX] = {
65         [RPC_A_CONFIG]  = { .name = "config",  .type = BLOBMSG_TYPE_STRING },
66         [RPC_A_TYPE]    = { .name = "type",    .type = BLOBMSG_TYPE_STRING },
67         [RPC_A_NAME]    = { .name = "name",    .type = BLOBMSG_TYPE_STRING },
68         [RPC_A_VALUES]  = { .name = "values",  .type = BLOBMSG_TYPE_TABLE  },
69         [RPC_A_SESSION] = { .name = "ubus_rpc_session",
70                                                .type = BLOBMSG_TYPE_STRING },
71 };
72
73 enum {
74         RPC_S_CONFIG,
75         RPC_S_SECTION,
76         RPC_S_TYPE,
77         RPC_S_MATCH,
78         RPC_S_VALUES,
79         RPC_S_SESSION,
80         __RPC_S_MAX,
81 };
82
83 static const struct blobmsg_policy rpc_uci_set_policy[__RPC_S_MAX] = {
84         [RPC_S_CONFIG]  = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
85         [RPC_S_SECTION] = { .name = "section",  .type = BLOBMSG_TYPE_STRING },
86         [RPC_S_TYPE]    = { .name = "type",     .type = BLOBMSG_TYPE_STRING },
87         [RPC_S_MATCH]   = { .name = "match",    .type = BLOBMSG_TYPE_TABLE  },
88         [RPC_S_VALUES]  = { .name = "values",   .type = BLOBMSG_TYPE_TABLE  },
89         [RPC_S_SESSION] = { .name = "ubus_rpc_session",
90                                                 .type = BLOBMSG_TYPE_STRING },
91 };
92
93 enum {
94         RPC_D_CONFIG,
95         RPC_D_SECTION,
96         RPC_D_TYPE,
97         RPC_D_MATCH,
98         RPC_D_OPTION,
99         RPC_D_OPTIONS,
100         RPC_D_SESSION,
101         __RPC_D_MAX,
102 };
103
104 static const struct blobmsg_policy rpc_uci_delete_policy[__RPC_D_MAX] = {
105         [RPC_D_CONFIG]  = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
106         [RPC_D_SECTION] = { .name = "section",  .type = BLOBMSG_TYPE_STRING },
107         [RPC_D_TYPE]    = { .name = "type",     .type = BLOBMSG_TYPE_STRING },
108         [RPC_D_MATCH]   = { .name = "match",    .type = BLOBMSG_TYPE_TABLE  },
109         [RPC_D_OPTION]  = { .name = "option",   .type = BLOBMSG_TYPE_STRING },
110         [RPC_D_OPTIONS] = { .name = "options",  .type = BLOBMSG_TYPE_ARRAY  },
111         [RPC_D_SESSION] = { .name = "ubus_rpc_session",
112                                                 .type = BLOBMSG_TYPE_STRING },
113 };
114
115 enum {
116         RPC_R_CONFIG,
117         RPC_R_SECTION,
118         RPC_R_OPTION,
119         RPC_R_NAME,
120         RPC_R_SESSION,
121         __RPC_R_MAX,
122 };
123
124 static const struct blobmsg_policy rpc_uci_rename_policy[__RPC_R_MAX] = {
125         [RPC_R_CONFIG]  = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
126         [RPC_R_SECTION] = { .name = "section",  .type = BLOBMSG_TYPE_STRING },
127         [RPC_R_OPTION]  = { .name = "option",   .type = BLOBMSG_TYPE_STRING },
128         [RPC_R_NAME]    = { .name = "name",     .type = BLOBMSG_TYPE_STRING },
129         [RPC_R_SESSION] = { .name = "ubus_rpc_session",
130                                                 .type = BLOBMSG_TYPE_STRING },
131 };
132
133 enum {
134         RPC_O_CONFIG,
135         RPC_O_SECTIONS,
136         RPC_O_SESSION,
137         __RPC_O_MAX,
138 };
139
140 static const struct blobmsg_policy rpc_uci_order_policy[__RPC_O_MAX] = {
141         [RPC_O_CONFIG]   = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
142         [RPC_O_SECTIONS] = { .name = "sections", .type = BLOBMSG_TYPE_ARRAY  },
143         [RPC_O_SESSION]  = { .name = "ubus_rpc_session",
144                                                  .type = BLOBMSG_TYPE_STRING },
145 };
146
147 enum {
148         RPC_C_CONFIG,
149         RPC_C_SESSION,
150         __RPC_C_MAX,
151 };
152
153 static const struct blobmsg_policy rpc_uci_config_policy[__RPC_C_MAX] = {
154         [RPC_C_CONFIG]   = { .name = "config",  .type = BLOBMSG_TYPE_STRING },
155         [RPC_C_SESSION]  = { .name = "ubus_rpc_session",
156                                                 .type = BLOBMSG_TYPE_STRING },
157 };
158
159 enum {
160         RPC_T_ROLLBACK,
161         RPC_T_TIMEOUT,
162         RPC_T_SESSION,
163         __RPC_T_MAX,
164 };
165
166 static const struct blobmsg_policy rpc_uci_apply_policy[__RPC_T_MAX] = {
167         [RPC_T_ROLLBACK] = { .name = "rollback", .type = BLOBMSG_TYPE_BOOL },
168         [RPC_T_TIMEOUT]  = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
169         [RPC_T_SESSION]  = { .name = "ubus_rpc_session",
170                                                 .type = BLOBMSG_TYPE_STRING },
171 };
172
173 enum {
174         RPC_B_SESSION,
175         __RPC_B_MAX,
176 };
177
178 static const struct blobmsg_policy rpc_uci_rollback_policy[__RPC_B_MAX] = {
179         [RPC_B_SESSION]  = { .name = "ubus_rpc_session",
180                                                 .type = BLOBMSG_TYPE_STRING },
181 };
182
183 /*
184  * Turn uci error state into ubus return code
185  */
186 static int
187 rpc_uci_status(void)
188 {
189         switch (cursor->err)
190         {
191         case UCI_OK:
192                 return UBUS_STATUS_OK;
193
194         case UCI_ERR_INVAL:
195                 return UBUS_STATUS_INVALID_ARGUMENT;
196
197         case UCI_ERR_NOTFOUND:
198                 return UBUS_STATUS_NOT_FOUND;
199
200         default:
201                 return UBUS_STATUS_UNKNOWN_ERROR;
202         }
203 }
204
205 /*
206  * Setup per-session delta save directory. If the passed "sid" blob attribute
207  * pointer is NULL then the precedure was not invoked through the ubus-rpc so
208  * we do not perform session isolation and use the default save directory.
209  */
210 static void
211 rpc_uci_set_savedir(struct blob_attr *sid)
212 {
213         char path[PATH_MAX];
214
215         if (!sid)
216         {
217                 uci_set_savedir(cursor, "/tmp/.uci");
218                 return;
219         }
220
221         snprintf(path, sizeof(path) - 1,
222                  RPC_UCI_SAVEDIR_PREFIX "%s", blobmsg_get_string(sid));
223
224         uci_set_savedir(cursor, path);
225 }
226
227 /*
228  * Test read access to given config. If the passed "sid" blob attribute pointer
229  * is NULL then the precedure was not invoked through the ubus-rpc so we do not
230  * perform access control and always assume true.
231  */
232 static bool
233 rpc_uci_read_access(struct blob_attr *sid, struct blob_attr *config)
234 {
235         rpc_uci_set_savedir(sid);
236
237         if (!sid)
238                 return true;
239
240         return rpc_session_access(blobmsg_data(sid), "uci",
241                                   blobmsg_data(config), "read");
242 }
243
244 /*
245  * Test write access to given config. If the passed "sid" blob attribute pointer
246  * is NULL then the precedure was not invoked through the ubus-rpc so we do not
247  * perform access control and always assume true.
248  */
249 static bool
250 rpc_uci_write_access(struct blob_attr *sid, struct blob_attr *config)
251 {
252         rpc_uci_set_savedir(sid);
253
254         if (!sid)
255                 return true;
256
257         return rpc_session_access(blobmsg_data(sid), "uci",
258                                   blobmsg_data(config), "write");
259 }
260
261 /*
262  * Format applicable blob value as string and place a pointer to the string
263  * buffer in "p". Uses a static string buffer.
264  */
265 static bool
266 rpc_uci_format_blob(struct blob_attr *v, const char **p)
267 {
268         static char buf[21];
269
270         *p = NULL;
271
272         switch (blobmsg_type(v))
273         {
274         case BLOBMSG_TYPE_STRING:
275                 if (blobmsg_data_len(v) > 1)
276                         *p = blobmsg_data(v);
277                 break;
278
279         case BLOBMSG_TYPE_INT64:
280                 snprintf(buf, sizeof(buf), "%"PRIu64, blobmsg_get_u64(v));
281                 *p = buf;
282                 break;
283
284         case BLOBMSG_TYPE_INT32:
285                 snprintf(buf, sizeof(buf), "%u", blobmsg_get_u32(v));
286                 *p = buf;
287                 break;
288
289         case BLOBMSG_TYPE_INT16:
290                 snprintf(buf, sizeof(buf), "%u", blobmsg_get_u16(v));
291                 *p = buf;
292                 break;
293
294         case BLOBMSG_TYPE_INT8:
295                 snprintf(buf, sizeof(buf), "%u", !!blobmsg_get_u8(v));
296                 *p = buf;
297                 break;
298
299         default:
300                 break;
301         }
302
303         return !!*p;
304 }
305
306 /*
307  * Lookup the given uci_ptr and enable extended lookup format if the .section
308  * value of the uci_ptr looks like extended syntax. Uses an internal copy
309  * of the given uci_ptr to perform the lookup as failing extended section
310  * lookup operations in libuci will zero our the uci_ptr struct.
311  * Copies the internal uci_ptr back to given the uci_ptr on success.
312  */
313 static int
314 rpc_uci_lookup(struct uci_ptr *ptr)
315 {
316         int rv;
317         struct uci_ptr lookup = *ptr;
318
319         if (!lookup.s && lookup.section && *lookup.section == '@')
320                 lookup.flags |= UCI_LOOKUP_EXTENDED;
321
322         rv = uci_lookup_ptr(cursor, &lookup, NULL, true);
323
324         if (!rv)
325                 *ptr = lookup;
326
327         return rv;
328 }
329
330 /*
331  * Checks whether the given uci_option object matches the given string value.
332  *  1) If the uci_option is of type list, check whether any of the list elements
333  *     equals to the given string
334  *  2) If the uci_option is of type string, parse it into space separated tokens
335  *     and check if any of the tokens equals to the given string.
336  *  Returns true if a list element or token matched the given string.
337  */
338 static bool
339 rpc_uci_match_option(struct uci_option *o, const char *cmp)
340 {
341         struct uci_element *e;
342         char *s, *p;
343
344         if (o->type == UCI_TYPE_LIST)
345         {
346                 uci_foreach_element(&o->v.list, e)
347                         if (e->name && !strcmp(e->name, cmp))
348                                 return true;
349
350                 return false;
351         }
352
353         if (!o->v.string)
354                 return false;
355
356         s = strdup(o->v.string);
357
358         if (!s)
359                 return false;
360
361         for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
362         {
363                 if (!strcmp(p, cmp))
364                 {
365                         free(s);
366                         return true;
367                 }
368         }
369
370         free(s);
371         return false;
372 }
373
374 /*
375  * Checks whether the given uci_section matches the type and value blob attrs.
376  *  1) Returns false if "type" is given and the section type does not match
377  *     the value specified in the "type" string blob attribute, else continue.
378  *  2) Tests whether any key in the "matches" table blob attribute exists in
379  *     the given uci_section and whether each value is contained in the
380  *     corresponding uci option value (see rpc_uci_match_option()).
381  *  3) A missing or empty "matches" table blob attribute is always considered
382  *     to be a match.
383  * Returns true if "type" matches or is NULL and "matches" matches or is NULL.
384  */
385 static bool
386 rpc_uci_match_section(struct uci_section *s,
387                       struct blob_attr *type, struct blob_attr *matches)
388 {
389         struct uci_element *e;
390         struct blob_attr *cur;
391         const char *cmp;
392         bool match = false;
393         bool empty = true;
394         int rem;
395
396         if (type && strcmp(s->type, blobmsg_data(type)))
397                 return false;
398
399         if (!matches)
400                 return true;
401
402         blobmsg_for_each_attr(cur, matches, rem)
403         {
404                 if (!rpc_uci_format_blob(cur, &cmp))
405                         continue;
406
407                 uci_foreach_element(&s->options, e)
408                 {
409                         if (strcmp(e->name, blobmsg_name(cur)))
410                                 continue;
411
412                         if (!rpc_uci_match_option(uci_to_option(e), cmp))
413                                 return false;
414
415                         match = true;
416                 }
417
418                 empty = false;
419         }
420
421         return (empty || match);
422 }
423
424 /*
425  * Dump the given uci_option value into the global blobmsg buffer and use
426  * given "name" as key.
427  *  1) If the uci_option is of type list, put a table into the blob buffer and
428  *     add each list item as string to it.
429  *  2) If the uci_option is of type string, put its value directly into the blob
430  *     buffer.
431  */
432 static void
433 rpc_uci_dump_option(struct uci_option *o, const char *name)
434 {
435         void *c;
436         struct uci_element *e;
437
438         switch (o->type)
439         {
440         case UCI_TYPE_STRING:
441                 blobmsg_add_string(&buf, name, o->v.string);
442                 break;
443
444         case UCI_TYPE_LIST:
445                 c = blobmsg_open_array(&buf, name);
446
447                 uci_foreach_element(&o->v.list, e)
448                         blobmsg_add_string(&buf, NULL, e->name);
449
450                 blobmsg_close_array(&buf, c);
451                 break;
452
453         default:
454                 break;
455         }
456 }
457
458 /*
459  * Dump the given uci_section object into the global blobmsg buffer and use
460  * given "name" as key.
461  * Puts a table into the blob buffer and puts each section option member value
462  * as value into the table using the option name as key.
463  * Adds three special keys ".anonymous", ".type" and ".name" which specify the
464  * corresponding section properties.
465  */
466 static void
467 rpc_uci_dump_section(struct uci_section *s, const char *name, int index)
468 {
469         void *c;
470         struct uci_option *o;
471         struct uci_element *e;
472
473         c = blobmsg_open_table(&buf, name);
474
475         blobmsg_add_u8(&buf, ".anonymous", s->anonymous);
476         blobmsg_add_string(&buf, ".type", s->type);
477         blobmsg_add_string(&buf, ".name", s->e.name);
478
479         if (index >= 0)
480                 blobmsg_add_u32(&buf, ".index", index);
481
482         uci_foreach_element(&s->options, e)
483         {
484                 o = uci_to_option(e);
485                 rpc_uci_dump_option(o, o->e.name);
486         }
487
488         blobmsg_close_table(&buf, c);
489 }
490
491 /*
492  * Dump the given uci_package object into the global blobmsg buffer and use
493  * given "name" as key.
494  * Puts a table into the blob buffer and puts each package section member as
495  * value into the table using the section name as key.
496  * Only dumps sections matching the given "type" and "matches", see explaination
497  * of rpc_uci_match_section() for details.
498  */
499 static void
500 rpc_uci_dump_package(struct uci_package *p, const char *name,
501                      struct blob_attr *type, struct blob_attr *matches)
502 {
503         void *c;
504         struct uci_element *e;
505         int i = -1;
506
507         c = blobmsg_open_table(&buf, name);
508
509         uci_foreach_element(&p->sections, e)
510         {
511                 i++;
512
513                 if (!rpc_uci_match_section(uci_to_section(e), type, matches))
514                         continue;
515
516                 rpc_uci_dump_section(uci_to_section(e), e->name, i);
517         }
518
519         blobmsg_close_table(&buf, c);
520 }
521
522
523 static int
524 rpc_uci_getcommon(struct ubus_context *ctx, struct ubus_request_data *req,
525                   struct blob_attr *msg, bool use_state)
526 {
527         struct blob_attr *tb[__RPC_G_MAX];
528         struct uci_package *p = NULL;
529         struct uci_ptr ptr = { 0 };
530
531         blobmsg_parse(rpc_uci_get_policy, __RPC_G_MAX, tb,
532                       blob_data(msg), blob_len(msg));
533
534         if (!tb[RPC_G_CONFIG])
535                 return UBUS_STATUS_INVALID_ARGUMENT;
536
537         if (!rpc_uci_read_access(tb[RPC_G_SESSION], tb[RPC_G_CONFIG]))
538                 return UBUS_STATUS_PERMISSION_DENIED;
539
540         ptr.package = blobmsg_data(tb[RPC_G_CONFIG]);
541
542         if (use_state)
543                 uci_set_savedir(cursor, "/var/state");
544
545         if (uci_load(cursor, ptr.package, &p))
546                 return rpc_uci_status();
547
548         if (tb[RPC_G_SECTION])
549         {
550                 ptr.section = blobmsg_data(tb[RPC_G_SECTION]);
551
552                 if (tb[RPC_G_OPTION])
553                         ptr.option = blobmsg_data(tb[RPC_G_OPTION]);
554         }
555
556         if (rpc_uci_lookup(&ptr) || !(ptr.flags & UCI_LOOKUP_COMPLETE))
557                 goto out;
558
559         blob_buf_init(&buf, 0);
560
561         switch (ptr.last->type)
562         {
563         case UCI_TYPE_PACKAGE:
564                 rpc_uci_dump_package(ptr.p, "values", tb[RPC_G_TYPE], tb[RPC_G_MATCH]);
565                 break;
566
567         case UCI_TYPE_SECTION:
568                 rpc_uci_dump_section(ptr.s, "values", -1);
569                 break;
570
571         case UCI_TYPE_OPTION:
572                 rpc_uci_dump_option(ptr.o, "value");
573                 break;
574
575         default:
576                 break;
577         }
578
579         ubus_send_reply(ctx, req, buf.head);
580
581 out:
582         uci_unload(cursor, p);
583
584         return rpc_uci_status();
585 }
586
587 static int
588 rpc_uci_get(struct ubus_context *ctx, struct ubus_object *obj,
589             struct ubus_request_data *req, const char *method,
590             struct blob_attr *msg)
591 {
592         return rpc_uci_getcommon(ctx, req, msg, false);
593 }
594
595 static int
596 rpc_uci_state(struct ubus_context *ctx, struct ubus_object *obj,
597               struct ubus_request_data *req, const char *method,
598               struct blob_attr *msg)
599 {
600         return rpc_uci_getcommon(ctx, req, msg, true);
601 }
602
603 static int
604 rpc_uci_add(struct ubus_context *ctx, struct ubus_object *obj,
605             struct ubus_request_data *req, const char *method,
606             struct blob_attr *msg)
607 {
608         struct blob_attr *tb[__RPC_A_MAX];
609         struct blob_attr *cur, *elem;
610         struct uci_package *p = NULL;
611         struct uci_section *s;
612         struct uci_ptr ptr = { 0 };
613         int rem, rem2;
614
615         blobmsg_parse(rpc_uci_add_policy, __RPC_A_MAX, tb,
616                       blob_data(msg), blob_len(msg));
617
618         if (!tb[RPC_A_CONFIG] || !tb[RPC_A_TYPE])
619                 return UBUS_STATUS_INVALID_ARGUMENT;
620
621         if (!rpc_uci_write_access(tb[RPC_A_SESSION], tb[RPC_A_CONFIG]))
622                 return UBUS_STATUS_PERMISSION_DENIED;
623
624         ptr.package = blobmsg_data(tb[RPC_A_CONFIG]);
625
626         if (uci_load(cursor, ptr.package, &p))
627                 return rpc_uci_status();
628
629         /* add named section */
630         if (tb[RPC_A_NAME])
631         {
632                 ptr.section = blobmsg_data(tb[RPC_A_NAME]);
633                 ptr.value   = blobmsg_data(tb[RPC_A_TYPE]);
634                 ptr.option  = NULL;
635
636                 if (rpc_uci_lookup(&ptr) || uci_set(cursor, &ptr))
637                         goto out;
638         }
639
640         /* add anon section */
641         else
642         {
643                 if (uci_add_section(cursor, p, blobmsg_data(tb[RPC_A_TYPE]), &s) || !s)
644                         goto out;
645
646                 ptr.section = s->e.name;
647         }
648
649         if (tb[RPC_A_VALUES])
650         {
651                 blobmsg_for_each_attr(cur, tb[RPC_A_VALUES], rem)
652                 {
653                         ptr.o = NULL;
654                         ptr.option = blobmsg_name(cur);
655
656                         if (rpc_uci_lookup(&ptr) || !ptr.s)
657                                 continue;
658
659                         switch (blobmsg_type(cur))
660                         {
661                         case BLOBMSG_TYPE_ARRAY:
662                                 blobmsg_for_each_attr(elem, cur, rem2)
663                                         if (rpc_uci_format_blob(elem, &ptr.value))
664                                                 uci_add_list(cursor, &ptr);
665                                 break;
666
667                         default:
668                                 if (rpc_uci_format_blob(cur, &ptr.value))
669                                         uci_set(cursor, &ptr);
670                                 break;
671                         }
672                 }
673         }
674
675         uci_save(cursor, p);
676
677         blob_buf_init(&buf, 0);
678         blobmsg_add_string(&buf, "section", ptr.section);
679         ubus_send_reply(ctx, req, buf.head);
680
681 out:
682         uci_unload(cursor, p);
683
684         return rpc_uci_status();
685 }
686
687 /*
688  * Turn value from a blob attribute into uci set operation
689  *  1) if the blob is of type array, delete existing option (if any) and
690  *     emit uci add_list operations for each element
691  *  2) if the blob is not an array but an option of type list exists,
692  *     delete existing list and emit uci set operation for the blob value
693  *  3) in all other cases only emit a set operation if there is no existing
694  *     option of if the existing options value differs from the blob value
695  */
696 static void
697 rpc_uci_merge_set(struct blob_attr *opt, struct uci_ptr *ptr)
698 {
699         struct blob_attr *cur;
700         int rem;
701
702         ptr->o = NULL;
703         ptr->option = blobmsg_name(opt);
704         ptr->value = NULL;
705
706         if (rpc_uci_lookup(ptr) || !ptr->s)
707                 return;
708
709         if (blobmsg_type(opt) == BLOBMSG_TYPE_ARRAY)
710         {
711                 if (ptr->o)
712                         uci_delete(cursor, ptr);
713
714                 blobmsg_for_each_attr(cur, opt, rem)
715                         if (rpc_uci_format_blob(cur, &ptr->value))
716                                 uci_add_list(cursor, ptr);
717         }
718         else if (ptr->o && ptr->o->type == UCI_TYPE_LIST)
719         {
720                 uci_delete(cursor, ptr);
721
722                 if (rpc_uci_format_blob(opt, &ptr->value))
723                         uci_set(cursor, ptr);
724         }
725         else if (rpc_uci_format_blob(opt, &ptr->value))
726         {
727                 if (!ptr->o || !ptr->o->v.string || strcmp(ptr->o->v.string, ptr->value))
728                         uci_set(cursor, ptr);
729         }
730 }
731
732 static int
733 rpc_uci_set(struct ubus_context *ctx, struct ubus_object *obj,
734             struct ubus_request_data *req, const char *method,
735             struct blob_attr *msg)
736 {
737         struct blob_attr *tb[__RPC_S_MAX];
738         struct blob_attr *cur;
739         struct uci_package *p = NULL;
740         struct uci_element *e;
741         struct uci_ptr ptr = { 0 };
742         int rem;
743
744         blobmsg_parse(rpc_uci_set_policy, __RPC_S_MAX, tb,
745                       blob_data(msg), blob_len(msg));
746
747         if (!tb[RPC_S_CONFIG] || !tb[RPC_S_VALUES] ||
748                 (!tb[RPC_S_SECTION] && !tb[RPC_S_TYPE] && !tb[RPC_S_MATCH]))
749                 return UBUS_STATUS_INVALID_ARGUMENT;
750
751         if (!rpc_uci_write_access(tb[RPC_S_SESSION], tb[RPC_S_CONFIG]))
752                 return UBUS_STATUS_PERMISSION_DENIED;
753
754         ptr.package = blobmsg_data(tb[RPC_S_CONFIG]);
755
756         if (uci_load(cursor, ptr.package, &p))
757                 return rpc_uci_status();
758
759         if (tb[RPC_S_SECTION])
760         {
761                 ptr.section = blobmsg_data(tb[RPC_S_SECTION]);
762                 blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
763                         rpc_uci_merge_set(cur, &ptr);
764         }
765         else
766         {
767                 uci_foreach_element(&p->sections, e)
768                 {
769                         if (!rpc_uci_match_section(uci_to_section(e),
770                                                    tb[RPC_S_TYPE], tb[RPC_S_MATCH]))
771                                 continue;
772
773                         ptr.s = NULL;
774                         ptr.section = e->name;
775
776                         blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
777                                 rpc_uci_merge_set(cur, &ptr);
778                 }
779         }
780
781         uci_save(cursor, p);
782         uci_unload(cursor, p);
783
784         return rpc_uci_status();
785 }
786
787 /*
788  * Delete option or section from uci specified by given blob attribute pointer
789  *  1) if the blob is of type array, delete any option named after each element
790  *  2) if the blob is of type string, delete the option named after its value
791  *  3) if the blob is NULL, delete entire section
792  */
793 static void
794 rpc_uci_merge_delete(struct blob_attr *opt, struct uci_ptr *ptr)
795 {
796         struct blob_attr *cur;
797         int rem;
798
799         if (rpc_uci_lookup(ptr) || !ptr->s)
800                 return;
801
802         if (!opt)
803         {
804                 ptr->o = NULL;
805                 ptr->option = NULL;
806
807                 uci_delete(cursor, ptr);
808         }
809         else if (blobmsg_type(opt) == BLOBMSG_TYPE_ARRAY)
810         {
811                 blobmsg_for_each_attr(cur, opt, rem)
812                 {
813                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
814                                 continue;
815
816                         ptr->o = NULL;
817                         ptr->option = blobmsg_data(cur);
818
819                         if (rpc_uci_lookup(ptr) || !ptr->o)
820                                 continue;
821
822                         uci_delete(cursor, ptr);
823                 }
824         }
825         else if (blobmsg_type(opt) == BLOBMSG_TYPE_STRING)
826         {
827                 ptr->o = NULL;
828                 ptr->option = blobmsg_data(opt);
829
830                 if (rpc_uci_lookup(ptr) || !ptr->o)
831                         return;
832
833                 uci_delete(cursor, ptr);
834         }
835 }
836
837 static int
838 rpc_uci_delete(struct ubus_context *ctx, struct ubus_object *obj,
839                struct ubus_request_data *req, const char *method,
840                struct blob_attr *msg)
841 {
842         struct blob_attr *tb[__RPC_D_MAX];
843         struct uci_package *p = NULL;
844         struct uci_element *e, *tmp;
845         struct uci_ptr ptr = { 0 };
846
847         blobmsg_parse(rpc_uci_delete_policy, __RPC_D_MAX, tb,
848                       blob_data(msg), blob_len(msg));
849
850         if (!tb[RPC_D_CONFIG] ||
851                 (!tb[RPC_D_SECTION] && !tb[RPC_D_TYPE] && !tb[RPC_D_MATCH]))
852                 return UBUS_STATUS_INVALID_ARGUMENT;
853
854         if (!rpc_uci_write_access(tb[RPC_D_SESSION], tb[RPC_D_CONFIG]))
855                 return UBUS_STATUS_PERMISSION_DENIED;
856
857         ptr.package = blobmsg_data(tb[RPC_D_CONFIG]);
858
859         if (uci_load(cursor, ptr.package, &p))
860                 return rpc_uci_status();
861
862         if (tb[RPC_D_SECTION])
863         {
864                 ptr.section = blobmsg_data(tb[RPC_D_SECTION]);
865
866                 if (tb[RPC_D_OPTIONS])
867                         rpc_uci_merge_delete(tb[RPC_D_OPTIONS], &ptr);
868                 else
869                         rpc_uci_merge_delete(tb[RPC_D_OPTION], &ptr);
870         }
871         else
872         {
873                 uci_foreach_element_safe(&p->sections, tmp, e)
874                 {
875                         if (!rpc_uci_match_section(uci_to_section(e),
876                                                    tb[RPC_D_TYPE], tb[RPC_D_MATCH]))
877                                 continue;
878
879                         ptr.s = NULL;
880                         ptr.section = e->name;
881
882                         if (tb[RPC_D_OPTIONS])
883                                 rpc_uci_merge_delete(tb[RPC_D_OPTIONS], &ptr);
884                         else
885                                 rpc_uci_merge_delete(tb[RPC_D_OPTION], &ptr);
886                 }
887         }
888
889         uci_save(cursor, p);
890         uci_unload(cursor, p);
891
892         return rpc_uci_status();
893 }
894
895 static int
896 rpc_uci_rename(struct ubus_context *ctx, struct ubus_object *obj,
897                struct ubus_request_data *req, const char *method,
898                struct blob_attr *msg)
899 {
900         struct blob_attr *tb[__RPC_R_MAX];
901         struct uci_package *p = NULL;
902         struct uci_ptr ptr = { 0 };
903
904         blobmsg_parse(rpc_uci_rename_policy, __RPC_R_MAX, tb,
905                       blob_data(msg), blob_len(msg));
906
907         if (!tb[RPC_R_CONFIG] || !tb[RPC_R_SECTION] || !tb[RPC_R_NAME])
908                 return UBUS_STATUS_INVALID_ARGUMENT;
909
910         if (!rpc_uci_write_access(tb[RPC_R_SESSION], tb[RPC_R_CONFIG]))
911                 return UBUS_STATUS_PERMISSION_DENIED;
912
913         ptr.package = blobmsg_data(tb[RPC_R_CONFIG]);
914         ptr.section = blobmsg_data(tb[RPC_R_SECTION]);
915         ptr.value   = blobmsg_data(tb[RPC_R_NAME]);
916
917         if (tb[RPC_R_OPTION])
918                 ptr.option = blobmsg_data(tb[RPC_R_OPTION]);
919
920         if (uci_load(cursor, ptr.package, &p))
921                 return rpc_uci_status();
922
923         if (uci_lookup_ptr(cursor, &ptr, NULL, true))
924                 goto out;
925
926         if ((ptr.option && !ptr.o) || !ptr.s)
927         {
928                 cursor->err = UCI_ERR_NOTFOUND;
929                 goto out;
930         }
931
932         if (uci_rename(cursor, &ptr))
933                 goto out;
934
935         uci_save(cursor, p);
936
937 out:
938         uci_unload(cursor, p);
939
940         return rpc_uci_status();
941 }
942
943 static int
944 rpc_uci_order(struct ubus_context *ctx, struct ubus_object *obj,
945               struct ubus_request_data *req, const char *method,
946               struct blob_attr *msg)
947 {
948         struct blob_attr *tb[__RPC_O_MAX];
949         struct blob_attr *cur;
950         struct uci_package *p = NULL;
951         struct uci_ptr ptr = { 0 };
952         int rem, i = 1;
953
954         blobmsg_parse(rpc_uci_order_policy, __RPC_O_MAX, tb,
955                       blob_data(msg), blob_len(msg));
956
957         if (!tb[RPC_O_CONFIG] || !tb[RPC_O_SECTIONS])
958                 return UBUS_STATUS_INVALID_ARGUMENT;
959
960         if (!rpc_uci_write_access(tb[RPC_O_SESSION], tb[RPC_O_CONFIG]))
961                 return UBUS_STATUS_PERMISSION_DENIED;
962
963         ptr.package = blobmsg_data(tb[RPC_O_CONFIG]);
964
965         if (uci_load(cursor, ptr.package, &p))
966                 return rpc_uci_status();
967
968         blobmsg_for_each_attr(cur, tb[RPC_O_SECTIONS], rem)
969         {
970                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
971                         continue;
972
973                 ptr.s = NULL;
974                 ptr.section = blobmsg_data(cur);
975
976                 if (uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.s)
977                         continue;
978
979                 uci_reorder_section(cursor, ptr.s, i++);
980         }
981
982         uci_save(cursor, p);
983         uci_unload(cursor, p);
984
985         return rpc_uci_status();
986 }
987
988 static void
989 rpc_uci_dump_change(struct uci_delta *d)
990 {
991         void *c;
992         const char *types[] = {
993                 [UCI_CMD_REORDER]  = "order",
994                 [UCI_CMD_REMOVE]   = "remove",
995                 [UCI_CMD_RENAME]   = "rename",
996                 [UCI_CMD_ADD]      = "add",
997                 [UCI_CMD_LIST_ADD] = "list-add",
998                 [UCI_CMD_LIST_DEL] = "list-del",
999                 [UCI_CMD_CHANGE]   = "set",
1000         };
1001
1002         if (!d->section)
1003                 return;
1004
1005         c = blobmsg_open_array(&buf, NULL);
1006
1007         blobmsg_add_string(&buf, NULL, types[d->cmd]);
1008         blobmsg_add_string(&buf, NULL, d->section);
1009
1010         if (d->e.name)
1011                 blobmsg_add_string(&buf, NULL, d->e.name);
1012
1013         if (d->value)
1014         {
1015                 if (d->cmd == UCI_CMD_REORDER)
1016                         blobmsg_add_u32(&buf, NULL, strtoul(d->value, NULL, 10));
1017                 else
1018                         blobmsg_add_string(&buf, NULL, d->value);
1019         }
1020
1021         blobmsg_close_array(&buf, c);
1022 }
1023
1024 static int
1025 rpc_uci_changes(struct ubus_context *ctx, struct ubus_object *obj,
1026                 struct ubus_request_data *req, const char *method,
1027                 struct blob_attr *msg)
1028 {
1029         struct blob_attr *tb[__RPC_C_MAX];
1030         struct uci_package *p = NULL;
1031         struct uci_element *e;
1032         char **configs;
1033         void *c, *d;
1034         int i;
1035
1036         blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
1037                       blob_data(msg), blob_len(msg));
1038
1039         if (tb[RPC_C_CONFIG])
1040         {
1041                 if (!rpc_uci_read_access(tb[RPC_C_SESSION], tb[RPC_C_CONFIG]))
1042                         return UBUS_STATUS_PERMISSION_DENIED;
1043
1044                 if (uci_load(cursor, blobmsg_data(tb[RPC_C_CONFIG]), &p))
1045                         return rpc_uci_status();
1046
1047                 blob_buf_init(&buf, 0);
1048                 c = blobmsg_open_array(&buf, "changes");
1049
1050                 uci_foreach_element(&p->saved_delta, e)
1051                         rpc_uci_dump_change(uci_to_delta(e));
1052
1053                 blobmsg_close_array(&buf, c);
1054
1055                 uci_unload(cursor, p);
1056
1057                 ubus_send_reply(ctx, req, buf.head);
1058
1059                 return rpc_uci_status();
1060         }
1061
1062         rpc_uci_set_savedir(tb[RPC_C_SESSION]);
1063
1064         if (uci_list_configs(cursor, &configs))
1065                 return rpc_uci_status();
1066
1067         blob_buf_init(&buf, 0);
1068
1069         c = blobmsg_open_table(&buf, "changes");
1070
1071         for (i = 0; configs[i]; i++)
1072         {
1073                 if (tb[RPC_C_SESSION] &&
1074                     !rpc_session_access(blobmsg_data(tb[RPC_C_SESSION]), "uci",
1075                                         configs[i], "read"))
1076                         continue;
1077
1078                 if (uci_load(cursor, configs[i], &p))
1079                         continue;
1080
1081                 if (!uci_list_empty(&p->saved_delta))
1082                 {
1083                         d = blobmsg_open_array(&buf, configs[i]);
1084
1085                         uci_foreach_element(&p->saved_delta, e)
1086                                 rpc_uci_dump_change(uci_to_delta(e));
1087
1088                         blobmsg_close_array(&buf, d);
1089                 }
1090
1091                 uci_unload(cursor, p);
1092         }
1093
1094         blobmsg_close_table(&buf, c);
1095
1096         ubus_send_reply(ctx, req, buf.head);
1097
1098         return 0;
1099 }
1100
1101 static void
1102 rpc_uci_trigger_event(struct ubus_context *ctx, const char *config)
1103 {
1104         char *pkg = strdup(config);
1105         static struct blob_buf b;
1106         uint32_t id;
1107
1108         if (!ubus_lookup_id(ctx, "service", &id)) {
1109                 void *c;
1110
1111                 blob_buf_init(&b, 0);
1112                 blobmsg_add_string(&b, "type", "config.change");
1113                 c = blobmsg_open_table(&b, "data");
1114                 blobmsg_add_string(&b, "package", pkg);
1115                 blobmsg_close_table(&b, c);
1116                 ubus_invoke(ctx, id, "event", b.head, NULL, 0, 1000);
1117         }
1118         free(pkg);
1119 }
1120
1121 static int
1122 rpc_uci_revert_commit(struct ubus_context *ctx, struct blob_attr *msg, bool commit)
1123 {
1124         struct blob_attr *tb[__RPC_C_MAX];
1125         struct uci_package *p = NULL;
1126         struct uci_ptr ptr = { 0 };
1127
1128         if (apply_sid[0])
1129                 return UBUS_STATUS_PERMISSION_DENIED;
1130
1131         blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
1132                       blob_data(msg), blob_len(msg));
1133
1134         if (!tb[RPC_C_CONFIG])
1135                 return UBUS_STATUS_INVALID_ARGUMENT;
1136
1137         if (!rpc_uci_write_access(tb[RPC_C_SESSION], tb[RPC_C_CONFIG]))
1138                 return UBUS_STATUS_PERMISSION_DENIED;
1139
1140         ptr.package = blobmsg_data(tb[RPC_C_CONFIG]);
1141
1142         if (commit)
1143         {
1144                 if (!uci_load(cursor, ptr.package, &p))
1145                 {
1146                         uci_commit(cursor, &p, false);
1147                         uci_unload(cursor, p);
1148                         rpc_uci_trigger_event(ctx, blobmsg_get_string(tb[RPC_C_CONFIG]));
1149                 }
1150         }
1151         else
1152         {
1153                 if (!uci_lookup_ptr(cursor, &ptr, NULL, true) && ptr.p)
1154                 {
1155                         uci_revert(cursor, &ptr);
1156                         uci_unload(cursor, ptr.p);
1157                 }
1158         }
1159
1160         return rpc_uci_status();
1161 }
1162
1163 static int
1164 rpc_uci_revert(struct ubus_context *ctx, struct ubus_object *obj,
1165                struct ubus_request_data *req, const char *method,
1166                struct blob_attr *msg)
1167 {
1168         return rpc_uci_revert_commit(ctx, msg, false);
1169 }
1170
1171 static int
1172 rpc_uci_commit(struct ubus_context *ctx, struct ubus_object *obj,
1173                struct ubus_request_data *req, const char *method,
1174                struct blob_attr *msg)
1175 {
1176         return rpc_uci_revert_commit(ctx, msg, true);
1177 }
1178
1179 static int
1180 rpc_uci_configs(struct ubus_context *ctx, struct ubus_object *obj,
1181                 struct ubus_request_data *req, const char *method,
1182                 struct blob_attr *msg)
1183 {
1184         char **configs;
1185         void *c;
1186         int i;
1187
1188         if (uci_list_configs(cursor, &configs))
1189                 goto out;
1190
1191         blob_buf_init(&buf, 0);
1192
1193         c = blobmsg_open_array(&buf, "configs");
1194
1195         for (i = 0; configs[i]; i++)
1196                 blobmsg_add_string(&buf, NULL, configs[i]);
1197
1198         blobmsg_close_array(&buf, c);
1199
1200         ubus_send_reply(ctx, req, buf.head);
1201
1202 out:
1203         return rpc_uci_status();
1204 }
1205
1206
1207 /*
1208  * Remove given delta save directory (if any).
1209  */
1210 static void
1211 rpc_uci_purge_dir(const char *path)
1212 {
1213         DIR *d;
1214         struct stat s;
1215         struct dirent *e;
1216         char file[PATH_MAX];
1217
1218         if (stat(path, &s) || !S_ISDIR(s.st_mode))
1219                 return;
1220
1221         if ((d = opendir(path)) != NULL)
1222         {
1223                 while ((e = readdir(d)) != NULL)
1224                 {
1225                         snprintf(file, sizeof(file) - 1, "%s/%s", path, e->d_name);
1226
1227                         if (stat(file, &s) || !S_ISREG(s.st_mode))
1228                                 continue;
1229
1230                         unlink(file);
1231                 }
1232
1233                 closedir(d);
1234
1235                 rmdir(path);
1236         }
1237 }
1238
1239 static int
1240 rpc_uci_apply_config(struct ubus_context *ctx, char *config)
1241 {
1242         struct uci_package *p = NULL;
1243
1244         if (!uci_load(cursor, config, &p)) {
1245                 uci_commit(cursor, &p, false);
1246                 uci_unload(cursor, p);
1247         }
1248         rpc_uci_trigger_event(ctx, config);
1249
1250         return 0;
1251 }
1252
1253 static void
1254 rpc_uci_copy_file(const char *src, const char *target, const char *file)
1255 {
1256         char tmp[256];
1257         FILE *in, *out;
1258
1259         snprintf(tmp, sizeof(tmp), "%s%s", src, file);
1260         in = fopen(tmp, "rb");
1261         snprintf(tmp, sizeof(tmp), "%s%s", target, file);
1262         out = fopen(tmp, "wb+");
1263         if (in && out)
1264                 while (!feof(in)) {
1265                         int len = fread(tmp, 1, sizeof(tmp), in);
1266
1267                         if(len > 0)
1268                                 fwrite(tmp, 1, len, out);
1269                 }
1270         if(in)
1271                 fclose(in);
1272         if(out)
1273                 fclose(out);
1274 }
1275
1276 static void
1277 rpc_uci_do_rollback(struct ubus_context *ctx, const char *sid, glob_t *gl)
1278 {
1279         int i;
1280         char tmp[PATH_MAX];
1281
1282         if (sid) {
1283                 snprintf(tmp, sizeof(tmp), RPC_UCI_SAVEDIR_PREFIX "%s/", sid);
1284                 mkdir(tmp, 0700);
1285         }
1286
1287         for (i = 0; i < gl->gl_pathc; i++) {
1288                 char *config = basename(gl->gl_pathv[i]);
1289
1290                 if (*config == '.')
1291                         continue;
1292
1293                 rpc_uci_copy_file(RPC_SNAPSHOT_FILES, RPC_UCI_DIR, config);
1294                 rpc_uci_apply_config(ctx, config);
1295                 if (sid)
1296                         rpc_uci_copy_file(RPC_SNAPSHOT_DELTA, tmp, config);
1297         }
1298
1299         rpc_uci_purge_dir(RPC_SNAPSHOT_FILES);
1300         rpc_uci_purge_dir(RPC_SNAPSHOT_DELTA);
1301
1302         uloop_timeout_cancel(&apply_timer);
1303         memset(apply_sid, 0, sizeof(apply_sid));
1304         apply_ctx = NULL;
1305 }
1306
1307 static void
1308 rpc_uci_apply_timeout(struct uloop_timeout *t)
1309 {
1310         glob_t gl;
1311         char tmp[PATH_MAX];
1312
1313         snprintf(tmp, sizeof(tmp), "%s/*", RPC_SNAPSHOT_FILES);
1314         if (glob(tmp, GLOB_PERIOD, NULL, &gl) < 0)
1315                 return;
1316
1317         rpc_uci_do_rollback(apply_ctx, NULL, &gl);
1318 }
1319
1320 static int
1321 rpc_uci_apply_access(const char *sid, glob_t *gl)
1322 {
1323         struct stat s;
1324         int i, c = 0;
1325
1326         if (gl->gl_pathc < 3)
1327                 return UBUS_STATUS_NO_DATA;
1328
1329         for (i = 0; i < gl->gl_pathc; i++) {
1330                 char *config = basename(gl->gl_pathv[i]);
1331
1332                 if (*config == '.')
1333                         continue;
1334                 if (stat(gl->gl_pathv[i], &s) || !s.st_size)
1335                         continue;
1336                 if (!rpc_session_access(sid, "uci", config, "write"))
1337                         return UBUS_STATUS_PERMISSION_DENIED;
1338                 c++;
1339         }
1340
1341         if (!c)
1342                 return UBUS_STATUS_NO_DATA;
1343
1344         return 0;
1345 }
1346
1347 static int
1348 rpc_uci_apply(struct ubus_context *ctx, struct ubus_object *obj,
1349               struct ubus_request_data *req, const char *method,
1350               struct blob_attr *msg)
1351 {
1352         struct blob_attr *tb[__RPC_T_MAX];
1353         int timeout = RPC_APPLY_TIMEOUT;
1354         char tmp[PATH_MAX];
1355         bool rollback = false;
1356         int ret, i;
1357         char *sid;
1358         glob_t gl;
1359
1360         blobmsg_parse(rpc_uci_apply_policy, __RPC_T_MAX, tb,
1361                       blob_data(msg), blob_len(msg));
1362
1363         if (tb[RPC_T_ROLLBACK])
1364                 rollback = blobmsg_get_bool(tb[RPC_T_ROLLBACK]);
1365
1366         if (apply_sid[0] && rollback)
1367                 return UBUS_STATUS_PERMISSION_DENIED;
1368
1369         if (!tb[RPC_T_SESSION])
1370                 return UBUS_STATUS_INVALID_ARGUMENT;
1371
1372         sid = blobmsg_data(tb[RPC_T_SESSION]);
1373
1374         if (tb[RPC_T_TIMEOUT])
1375                 timeout = blobmsg_get_u32(tb[RPC_T_TIMEOUT]);
1376
1377         rpc_uci_purge_dir(RPC_SNAPSHOT_FILES);
1378         rpc_uci_purge_dir(RPC_SNAPSHOT_DELTA);
1379
1380         if (!apply_sid[0]) {
1381                 mkdir(RPC_SNAPSHOT_FILES, 0700);
1382                 mkdir(RPC_SNAPSHOT_DELTA, 0700);
1383
1384                 snprintf(tmp, sizeof(tmp), RPC_UCI_SAVEDIR_PREFIX "%s/*", sid);
1385                 if (glob(tmp, GLOB_PERIOD, NULL, &gl) < 0)
1386                         return UBUS_STATUS_NOT_FOUND;
1387
1388                 snprintf(tmp, sizeof(tmp), RPC_UCI_SAVEDIR_PREFIX "%s/", sid);
1389
1390                 ret = rpc_uci_apply_access(sid, &gl);
1391                 if (ret) {
1392                         globfree(&gl);
1393                         return ret;
1394                 }
1395
1396                 /* copy SID early because rpc_uci_apply_config() will clobber buf */
1397                 if (rollback)
1398                         strncpy(apply_sid, sid, RPC_SID_LEN);
1399
1400                 for (i = 0; i < gl.gl_pathc; i++) {
1401                         char *config = basename(gl.gl_pathv[i]);
1402                         struct stat s;
1403
1404                         if (*config == '.')
1405                                 continue;
1406
1407                         if (stat(gl.gl_pathv[i], &s) || !s.st_size)
1408                                 continue;
1409
1410                         rpc_uci_copy_file(RPC_UCI_DIR, RPC_SNAPSHOT_FILES, config);
1411                         rpc_uci_copy_file(tmp, RPC_SNAPSHOT_DELTA, config);
1412                         rpc_uci_apply_config(ctx, config);
1413                 }
1414
1415                 globfree(&gl);
1416
1417                 if (rollback) {
1418                         apply_timer.cb = rpc_uci_apply_timeout;
1419                         uloop_timeout_set(&apply_timer, timeout * 1000);
1420                         apply_ctx = ctx;
1421                 }
1422         }
1423
1424         return 0;
1425 }
1426
1427 static int
1428 rpc_uci_confirm(struct ubus_context *ctx, struct ubus_object *obj,
1429                 struct ubus_request_data *req, const char *method,
1430                 struct blob_attr *msg)
1431 {
1432         struct blob_attr *tb[__RPC_B_MAX];
1433         char *sid;
1434
1435         blobmsg_parse(rpc_uci_rollback_policy, __RPC_B_MAX, tb,
1436                       blob_data(msg), blob_len(msg));
1437
1438         if (!tb[RPC_B_SESSION])
1439                 return UBUS_STATUS_INVALID_ARGUMENT;
1440
1441         sid = blobmsg_data(tb[RPC_B_SESSION]);
1442
1443         if (!apply_sid[0])
1444                 return UBUS_STATUS_NO_DATA;
1445
1446         if (strcmp(apply_sid, sid))
1447                 return UBUS_STATUS_PERMISSION_DENIED;
1448
1449         rpc_uci_purge_dir(RPC_SNAPSHOT_FILES);
1450         rpc_uci_purge_dir(RPC_SNAPSHOT_DELTA);
1451
1452         uloop_timeout_cancel(&apply_timer);
1453         memset(apply_sid, 0, sizeof(apply_sid));
1454         apply_ctx = NULL;
1455
1456         return 0;
1457 }
1458
1459 static int
1460 rpc_uci_rollback(struct ubus_context *ctx, struct ubus_object *obj,
1461                  struct ubus_request_data *req, const char *method,
1462                  struct blob_attr *msg)
1463 {
1464         struct blob_attr *tb[__RPC_B_MAX];
1465         char tmp[PATH_MAX];
1466         glob_t gl;
1467         char *sid;
1468
1469         blobmsg_parse(rpc_uci_rollback_policy, __RPC_B_MAX, tb,
1470                       blob_data(msg), blob_len(msg));
1471
1472         if (!apply_sid[0])
1473                 return UBUS_STATUS_NO_DATA;
1474
1475         if (!tb[RPC_B_SESSION])
1476                 return UBUS_STATUS_INVALID_ARGUMENT;
1477
1478         sid = blobmsg_data(tb[RPC_B_SESSION]);
1479
1480         if (strcmp(apply_sid, sid))
1481                 return UBUS_STATUS_PERMISSION_DENIED;
1482
1483         snprintf(tmp, sizeof(tmp), "%s/*", RPC_SNAPSHOT_FILES);
1484         if (glob(tmp, GLOB_PERIOD, NULL, &gl) < 0)
1485                 return UBUS_STATUS_NOT_FOUND;
1486
1487         rpc_uci_do_rollback(ctx, sid, &gl);
1488
1489         globfree(&gl);
1490
1491         return 0;
1492 }
1493
1494 static int
1495 rpc_uci_reload(struct ubus_context *ctx, struct ubus_object *obj,
1496                  struct ubus_request_data *req, const char *method,
1497                  struct blob_attr *msg)
1498 {
1499         char * const cmd[2] = { "/sbin/reload_config", NULL };
1500
1501         if (!fork()) {
1502                 /* wait for the RPC call to complete */
1503                 sleep(2);
1504                 return execv(cmd[0], cmd);
1505         }
1506
1507         return 0;
1508 }
1509
1510 /*
1511  * Session destroy callback to purge associated delta directory.
1512  */
1513 static void
1514 rpc_uci_purge_savedir_cb(struct rpc_session *ses, void *priv)
1515 {
1516         char path[PATH_MAX];
1517
1518         snprintf(path, sizeof(path) - 1, RPC_UCI_SAVEDIR_PREFIX "%s", ses->id);
1519         rpc_uci_purge_dir(path);
1520 }
1521
1522 /*
1523  * Removes all delta directories which match the RPC_UCI_SAVEDIR_PREFIX.
1524  * This is used to clean up garbage when starting rpcd.
1525  */
1526 void rpc_uci_purge_savedirs(void)
1527 {
1528         int i;
1529         glob_t gl;
1530
1531         if (!glob(RPC_UCI_SAVEDIR_PREFIX "*", 0, NULL, &gl))
1532         {
1533                 for (i = 0; i < gl.gl_pathc; i++)
1534                         rpc_uci_purge_dir(gl.gl_pathv[i]);
1535
1536                 globfree(&gl);
1537         }
1538 }
1539
1540 int rpc_uci_api_init(struct ubus_context *ctx)
1541 {
1542         static const struct ubus_method uci_methods[] = {
1543                 { .name = "configs", .handler = rpc_uci_configs },
1544                 UBUS_METHOD("get",      rpc_uci_get,      rpc_uci_get_policy),
1545                 UBUS_METHOD("state",    rpc_uci_state,    rpc_uci_get_policy),
1546                 UBUS_METHOD("add",      rpc_uci_add,      rpc_uci_add_policy),
1547                 UBUS_METHOD("set",      rpc_uci_set,      rpc_uci_set_policy),
1548                 UBUS_METHOD("delete",   rpc_uci_delete,   rpc_uci_delete_policy),
1549                 UBUS_METHOD("rename",   rpc_uci_rename,   rpc_uci_rename_policy),
1550                 UBUS_METHOD("order",    rpc_uci_order,    rpc_uci_order_policy),
1551                 UBUS_METHOD("changes",  rpc_uci_changes,  rpc_uci_config_policy),
1552                 UBUS_METHOD("revert",   rpc_uci_revert,   rpc_uci_config_policy),
1553                 UBUS_METHOD("commit",   rpc_uci_commit,   rpc_uci_config_policy),
1554                 UBUS_METHOD("apply",    rpc_uci_apply,    rpc_uci_apply_policy),
1555                 UBUS_METHOD("confirm",  rpc_uci_confirm,  rpc_uci_rollback_policy),
1556                 UBUS_METHOD("rollback", rpc_uci_rollback, rpc_uci_rollback_policy),
1557                 UBUS_METHOD_NOARG("reload_config", rpc_uci_reload),
1558         };
1559
1560         static struct ubus_object_type uci_type =
1561                 UBUS_OBJECT_TYPE("luci-rpc-uci", uci_methods);
1562
1563         static struct ubus_object obj = {
1564                 .name = "uci",
1565                 .type = &uci_type,
1566                 .methods = uci_methods,
1567                 .n_methods = ARRAY_SIZE(uci_methods),
1568         };
1569
1570         static struct rpc_session_cb cb = {
1571                 .cb = rpc_uci_purge_savedir_cb
1572         };
1573
1574         cursor = uci_alloc_context();
1575
1576         if (!cursor)
1577                 return UBUS_STATUS_UNKNOWN_ERROR;
1578
1579         rpc_session_destroy_cb(&cb);
1580
1581         return ubus_add_object(ctx, &obj);
1582 }