2 * rpcd - UBUS RPC server
4 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
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.
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.
20 #include <rpcd/session.h>
22 static struct blob_buf buf
;
23 static struct uci_context
*cursor
;
35 static const struct blobmsg_policy rpc_uci_get_policy
[__RPC_G_MAX
] = {
36 [RPC_G_CONFIG
] = { .name
= "config", .type
= BLOBMSG_TYPE_STRING
},
37 [RPC_G_SECTION
] = { .name
= "section", .type
= BLOBMSG_TYPE_STRING
},
38 [RPC_G_OPTION
] = { .name
= "option", .type
= BLOBMSG_TYPE_STRING
},
39 [RPC_G_TYPE
] = { .name
= "type", .type
= BLOBMSG_TYPE_STRING
},
40 [RPC_G_MATCH
] = { .name
= "match", .type
= BLOBMSG_TYPE_TABLE
},
41 [RPC_G_SESSION
] = { .name
= "ubus_rpc_session",
42 .type
= BLOBMSG_TYPE_STRING
},
54 static const struct blobmsg_policy rpc_uci_add_policy
[__RPC_A_MAX
] = {
55 [RPC_A_CONFIG
] = { .name
= "config", .type
= BLOBMSG_TYPE_STRING
},
56 [RPC_A_TYPE
] = { .name
= "type", .type
= BLOBMSG_TYPE_STRING
},
57 [RPC_A_NAME
] = { .name
= "name", .type
= BLOBMSG_TYPE_STRING
},
58 [RPC_A_VALUES
] = { .name
= "values", .type
= BLOBMSG_TYPE_TABLE
},
59 [RPC_A_SESSION
] = { .name
= "ubus_rpc_session",
60 .type
= BLOBMSG_TYPE_STRING
},
73 static const struct blobmsg_policy rpc_uci_set_policy
[__RPC_S_MAX
] = {
74 [RPC_S_CONFIG
] = { .name
= "config", .type
= BLOBMSG_TYPE_STRING
},
75 [RPC_S_SECTION
] = { .name
= "section", .type
= BLOBMSG_TYPE_STRING
},
76 [RPC_S_TYPE
] = { .name
= "type", .type
= BLOBMSG_TYPE_STRING
},
77 [RPC_S_MATCH
] = { .name
= "match", .type
= BLOBMSG_TYPE_TABLE
},
78 [RPC_S_VALUES
] = { .name
= "values", .type
= BLOBMSG_TYPE_TABLE
},
79 [RPC_S_SESSION
] = { .name
= "ubus_rpc_session",
80 .type
= BLOBMSG_TYPE_STRING
},
94 static const struct blobmsg_policy rpc_uci_delete_policy
[__RPC_D_MAX
] = {
95 [RPC_D_CONFIG
] = { .name
= "config", .type
= BLOBMSG_TYPE_STRING
},
96 [RPC_D_SECTION
] = { .name
= "section", .type
= BLOBMSG_TYPE_STRING
},
97 [RPC_D_TYPE
] = { .name
= "type", .type
= BLOBMSG_TYPE_STRING
},
98 [RPC_D_MATCH
] = { .name
= "match", .type
= BLOBMSG_TYPE_TABLE
},
99 [RPC_D_OPTION
] = { .name
= "option", .type
= BLOBMSG_TYPE_STRING
},
100 [RPC_D_OPTIONS
] = { .name
= "options", .type
= BLOBMSG_TYPE_ARRAY
},
101 [RPC_D_SESSION
] = { .name
= "ubus_rpc_session",
102 .type
= BLOBMSG_TYPE_STRING
},
114 static const struct blobmsg_policy rpc_uci_rename_policy
[__RPC_R_MAX
] = {
115 [RPC_R_CONFIG
] = { .name
= "config", .type
= BLOBMSG_TYPE_STRING
},
116 [RPC_R_SECTION
] = { .name
= "section", .type
= BLOBMSG_TYPE_STRING
},
117 [RPC_R_OPTION
] = { .name
= "option", .type
= BLOBMSG_TYPE_STRING
},
118 [RPC_R_NAME
] = { .name
= "name", .type
= BLOBMSG_TYPE_STRING
},
119 [RPC_R_SESSION
] = { .name
= "ubus_rpc_session",
120 .type
= BLOBMSG_TYPE_STRING
},
130 static const struct blobmsg_policy rpc_uci_order_policy
[__RPC_O_MAX
] = {
131 [RPC_O_CONFIG
] = { .name
= "config", .type
= BLOBMSG_TYPE_STRING
},
132 [RPC_O_SECTIONS
] = { .name
= "sections", .type
= BLOBMSG_TYPE_ARRAY
},
133 [RPC_O_SESSION
] = { .name
= "ubus_rpc_session",
134 .type
= BLOBMSG_TYPE_STRING
},
143 static const struct blobmsg_policy rpc_uci_config_policy
[__RPC_C_MAX
] = {
144 [RPC_C_CONFIG
] = { .name
= "config", .type
= BLOBMSG_TYPE_STRING
},
145 [RPC_C_SESSION
] = { .name
= "ubus_rpc_session",
146 .type
= BLOBMSG_TYPE_STRING
},
150 * Turn uci error state into ubus return code
158 return UBUS_STATUS_OK
;
161 return UBUS_STATUS_INVALID_ARGUMENT
;
163 case UCI_ERR_NOTFOUND
:
164 return UBUS_STATUS_NOT_FOUND
;
167 return UBUS_STATUS_UNKNOWN_ERROR
;
172 * Setup per-session delta save directory. If the passed "sid" blob attribute
173 * pointer is NULL then the precedure was not invoked through the ubus-rpc so
174 * we do not perform session isolation and use the default save directory.
177 rpc_uci_set_savedir(struct blob_attr
*sid
)
183 uci_set_savedir(cursor
, "/tmp/.uci");
187 snprintf(path
, sizeof(path
) - 1,
188 "/tmp/.uci-rpc-%s", (char *)blobmsg_data(sid
));
190 uci_set_savedir(cursor
, path
);
194 * Test read access to given config. If the passed "sid" blob attribute pointer
195 * is NULL then the precedure was not invoked through the ubus-rpc so we do not
196 * perform access control and always assume true.
199 rpc_uci_read_access(struct blob_attr
*sid
, struct blob_attr
*config
)
201 rpc_uci_set_savedir(sid
);
206 return rpc_session_access(blobmsg_data(sid
), "uci",
207 blobmsg_data(config
), "read");
211 * Test write access to given config. If the passed "sid" blob attribute pointer
212 * is NULL then the precedure was not invoked through the ubus-rpc so we do not
213 * perform access control and always assume true.
216 rpc_uci_write_access(struct blob_attr
*sid
, struct blob_attr
*config
)
218 rpc_uci_set_savedir(sid
);
223 return rpc_session_access(blobmsg_data(sid
), "uci",
224 blobmsg_data(config
), "write");
228 * Format applicable blob value as string and place a pointer to the string
229 * buffer in "p". Uses a static string buffer.
232 rpc_uci_format_blob(struct blob_attr
*v
, const char **p
)
238 switch (blobmsg_type(v
))
240 case BLOBMSG_TYPE_STRING
:
241 if (blobmsg_data_len(v
) > 1)
242 *p
= blobmsg_data(v
);
245 case BLOBMSG_TYPE_INT64
:
246 snprintf(buf
, sizeof(buf
), "%"PRIu64
, blobmsg_get_u64(v
));
250 case BLOBMSG_TYPE_INT32
:
251 snprintf(buf
, sizeof(buf
), "%u", blobmsg_get_u32(v
));
255 case BLOBMSG_TYPE_INT16
:
256 snprintf(buf
, sizeof(buf
), "%u", blobmsg_get_u16(v
));
260 case BLOBMSG_TYPE_INT8
:
261 snprintf(buf
, sizeof(buf
), "%u", !!blobmsg_get_u8(v
));
273 * Lookup the given uci_ptr and enable extended lookup format if the .section
274 * value of the uci_ptr looks like extended syntax. Uses an internal copy
275 * of the given uci_ptr to perform the lookup as failing extended section
276 * lookup operations in libuci will zero our the uci_ptr struct.
277 * Copies the internal uci_ptr back to given the uci_ptr on success.
280 rpc_uci_lookup(struct uci_ptr
*ptr
)
283 struct uci_ptr lookup
= *ptr
;
285 if (!lookup
.s
&& lookup
.section
&& *lookup
.section
== '@')
286 lookup
.flags
|= UCI_LOOKUP_EXTENDED
;
288 rv
= uci_lookup_ptr(cursor
, &lookup
, NULL
, true);
297 * Checks whether the given uci_option object matches the given string value.
298 * 1) If the uci_option is of type list, check whether any of the list elements
299 * equals to the given string
300 * 2) If the uci_option is of type string, parse it into space separated tokens
301 * and check if any of the tokens equals to the given string.
302 * Returns true if a list element or token matched the given string.
305 rpc_uci_match_option(struct uci_option
*o
, const char *cmp
)
307 struct uci_element
*e
;
310 if (o
->type
== UCI_TYPE_LIST
)
312 uci_foreach_element(&o
->v
.list
, e
)
313 if (e
->name
&& !strcmp(e
->name
, cmp
))
322 s
= strdup(o
->v
.string
);
327 for (p
= strtok(s
, " \t"); p
; p
= strtok(NULL
, " \t"))
341 * Checks whether the given uci_section matches the type and value blob attrs.
342 * 1) Returns false if "type" is given and the section type does not match
343 * the value specified in the "type" string blob attribute, else continue.
344 * 2) Tests whether any key in the "matches" table blob attribute exists in
345 * the given uci_section and whether each value is contained in the
346 * corresponding uci option value (see rpc_uci_match_option()).
347 * 3) A missing or empty "matches" table blob attribute is always considered
349 * Returns true if "type" matches or is NULL and "matches" matches or is NULL.
352 rpc_uci_match_section(struct uci_section
*s
,
353 struct blob_attr
*type
, struct blob_attr
*matches
)
355 struct uci_element
*e
;
356 struct blob_attr
*cur
;
362 if (type
&& strcmp(s
->type
, blobmsg_data(type
)))
368 blobmsg_for_each_attr(cur
, matches
, rem
)
370 if (!rpc_uci_format_blob(cur
, &cmp
))
373 uci_foreach_element(&s
->options
, e
)
375 if (strcmp(e
->name
, blobmsg_name(cur
)))
378 if (!rpc_uci_match_option(uci_to_option(e
), cmp
))
387 return (empty
|| match
);
391 * Dump the given uci_option value into the global blobmsg buffer and use
392 * given "name" as key.
393 * 1) If the uci_option is of type list, put a table into the blob buffer and
394 * add each list item as string to it.
395 * 2) If the uci_option is of type string, put its value directly into the blob
399 rpc_uci_dump_option(struct uci_option
*o
, const char *name
)
402 struct uci_element
*e
;
406 case UCI_TYPE_STRING
:
407 blobmsg_add_string(&buf
, name
, o
->v
.string
);
411 c
= blobmsg_open_array(&buf
, name
);
413 uci_foreach_element(&o
->v
.list
, e
)
414 blobmsg_add_string(&buf
, NULL
, e
->name
);
416 blobmsg_close_array(&buf
, c
);
425 * Dump the given uci_section object into the global blobmsg buffer and use
426 * given "name" as key.
427 * Puts a table into the blob buffer and puts each section option member value
428 * as value into the table using the option name as key.
429 * Adds three special keys ".anonymous", ".type" and ".name" which specify the
430 * corresponding section properties.
433 rpc_uci_dump_section(struct uci_section
*s
, const char *name
, int index
)
436 struct uci_option
*o
;
437 struct uci_element
*e
;
439 c
= blobmsg_open_table(&buf
, name
);
441 blobmsg_add_u8(&buf
, ".anonymous", s
->anonymous
);
442 blobmsg_add_string(&buf
, ".type", s
->type
);
443 blobmsg_add_string(&buf
, ".name", s
->e
.name
);
446 blobmsg_add_u32(&buf
, ".index", index
);
448 uci_foreach_element(&s
->options
, e
)
450 o
= uci_to_option(e
);
451 rpc_uci_dump_option(o
, o
->e
.name
);
454 blobmsg_close_table(&buf
, c
);
458 * Dump the given uci_package object into the global blobmsg buffer and use
459 * given "name" as key.
460 * Puts a table into the blob buffer and puts each package section member as
461 * value into the table using the section name as key.
462 * Only dumps sections matching the given "type" and "matches", see explaination
463 * of rpc_uci_match_section() for details.
466 rpc_uci_dump_package(struct uci_package
*p
, const char *name
,
467 struct blob_attr
*type
, struct blob_attr
*matches
)
470 struct uci_element
*e
;
473 c
= blobmsg_open_table(&buf
, name
);
475 uci_foreach_element(&p
->sections
, e
)
479 if (!rpc_uci_match_section(uci_to_section(e
), type
, matches
))
482 rpc_uci_dump_section(uci_to_section(e
), e
->name
, i
);
485 blobmsg_close_table(&buf
, c
);
490 rpc_uci_get(struct ubus_context
*ctx
, struct ubus_object
*obj
,
491 struct ubus_request_data
*req
, const char *method
,
492 struct blob_attr
*msg
)
494 struct blob_attr
*tb
[__RPC_G_MAX
];
495 struct uci_package
*p
= NULL
;
496 struct uci_ptr ptr
= { 0 };
498 blobmsg_parse(rpc_uci_get_policy
, __RPC_G_MAX
, tb
,
499 blob_data(msg
), blob_len(msg
));
501 if (!tb
[RPC_G_CONFIG
])
502 return UBUS_STATUS_INVALID_ARGUMENT
;
504 if (!rpc_uci_read_access(tb
[RPC_G_SESSION
], tb
[RPC_G_CONFIG
]))
505 return UBUS_STATUS_PERMISSION_DENIED
;
507 ptr
.package
= blobmsg_data(tb
[RPC_G_CONFIG
]);
508 uci_load(cursor
, ptr
.package
, &p
);
513 if (tb
[RPC_G_SECTION
])
515 ptr
.section
= blobmsg_data(tb
[RPC_G_SECTION
]);
517 if (tb
[RPC_G_OPTION
])
518 ptr
.option
= blobmsg_data(tb
[RPC_G_OPTION
]);
521 if (rpc_uci_lookup(&ptr
) || !(ptr
.flags
& UCI_LOOKUP_COMPLETE
))
524 blob_buf_init(&buf
, 0);
526 switch (ptr
.last
->type
)
528 case UCI_TYPE_PACKAGE
:
529 rpc_uci_dump_package(ptr
.p
, "values", tb
[RPC_G_TYPE
], tb
[RPC_G_MATCH
]);
532 case UCI_TYPE_SECTION
:
533 rpc_uci_dump_section(ptr
.s
, "values", -1);
536 case UCI_TYPE_OPTION
:
537 rpc_uci_dump_option(ptr
.o
, "value");
544 ubus_send_reply(ctx
, req
, buf
.head
);
548 uci_unload(cursor
, p
);
550 return rpc_uci_status();
554 rpc_uci_add(struct ubus_context
*ctx
, struct ubus_object
*obj
,
555 struct ubus_request_data
*req
, const char *method
,
556 struct blob_attr
*msg
)
558 struct blob_attr
*tb
[__RPC_A_MAX
];
559 struct blob_attr
*cur
, *elem
;
560 struct uci_package
*p
= NULL
;
561 struct uci_section
*s
;
562 struct uci_ptr ptr
= { 0 };
565 blobmsg_parse(rpc_uci_add_policy
, __RPC_A_MAX
, tb
,
566 blob_data(msg
), blob_len(msg
));
568 if (!tb
[RPC_A_CONFIG
] || !tb
[RPC_A_TYPE
])
569 return UBUS_STATUS_INVALID_ARGUMENT
;
571 if (!rpc_uci_write_access(tb
[RPC_A_SESSION
], tb
[RPC_A_CONFIG
]))
572 return UBUS_STATUS_PERMISSION_DENIED
;
574 ptr
.package
= blobmsg_data(tb
[RPC_A_CONFIG
]);
576 uci_load(cursor
, ptr
.package
, &p
);
581 /* add named section */
584 ptr
.section
= blobmsg_data(tb
[RPC_A_NAME
]);
585 ptr
.value
= blobmsg_data(tb
[RPC_A_TYPE
]);
588 if (rpc_uci_lookup(&ptr
) || uci_set(cursor
, &ptr
))
592 /* add anon section */
595 if (uci_add_section(cursor
, p
, blobmsg_data(tb
[RPC_A_TYPE
]), &s
) || !s
)
598 ptr
.section
= s
->e
.name
;
601 if (tb
[RPC_A_VALUES
])
603 blobmsg_for_each_attr(cur
, tb
[RPC_A_VALUES
], rem
)
606 ptr
.option
= blobmsg_name(cur
);
608 if (rpc_uci_lookup(&ptr
) || !ptr
.s
)
611 switch (blobmsg_type(cur
))
613 case BLOBMSG_TYPE_ARRAY
:
614 blobmsg_for_each_attr(elem
, cur
, rem2
)
615 if (rpc_uci_format_blob(elem
, &ptr
.value
))
616 uci_add_list(cursor
, &ptr
);
620 if (rpc_uci_format_blob(cur
, &ptr
.value
))
621 uci_set(cursor
, &ptr
);
629 blob_buf_init(&buf
, 0);
630 blobmsg_add_string(&buf
, "section", ptr
.section
);
631 ubus_send_reply(ctx
, req
, buf
.head
);
635 uci_unload(cursor
, p
);
637 return rpc_uci_status();
641 * Turn value from a blob attribute into uci set operation
642 * 1) if the blob is of type array, delete existing option (if any) and
643 * emit uci add_list operations for each element
644 * 2) if the blob is not an array but an option of type list exists,
645 * delete existing list and emit uci set operation for the blob value
646 * 3) in all other cases only emit a set operation if there is no existing
647 * option of if the existing options value differs from the blob value
650 rpc_uci_merge_set(struct blob_attr
*opt
, struct uci_ptr
*ptr
)
652 struct blob_attr
*cur
;
656 ptr
->option
= blobmsg_name(opt
);
658 if (rpc_uci_lookup(ptr
) || !ptr
->s
)
661 if (blobmsg_type(opt
) == BLOBMSG_TYPE_ARRAY
)
664 uci_delete(cursor
, ptr
);
666 blobmsg_for_each_attr(cur
, opt
, rem
)
667 if (rpc_uci_format_blob(cur
, &ptr
->value
))
668 uci_add_list(cursor
, ptr
);
670 else if (ptr
->o
&& ptr
->o
->type
== UCI_TYPE_LIST
)
672 uci_delete(cursor
, ptr
);
674 if (rpc_uci_format_blob(opt
, &ptr
->value
))
675 uci_set(cursor
, ptr
);
677 else if (rpc_uci_format_blob(opt
, &ptr
->value
))
679 if (!ptr
->o
|| !ptr
->o
->v
.string
|| strcmp(ptr
->o
->v
.string
, ptr
->value
))
680 uci_set(cursor
, ptr
);
685 rpc_uci_set(struct ubus_context
*ctx
, struct ubus_object
*obj
,
686 struct ubus_request_data
*req
, const char *method
,
687 struct blob_attr
*msg
)
689 struct blob_attr
*tb
[__RPC_S_MAX
];
690 struct blob_attr
*cur
;
691 struct uci_package
*p
= NULL
;
692 struct uci_element
*e
;
693 struct uci_ptr ptr
= { 0 };
696 blobmsg_parse(rpc_uci_set_policy
, __RPC_S_MAX
, tb
,
697 blob_data(msg
), blob_len(msg
));
699 if (!tb
[RPC_S_CONFIG
] || !tb
[RPC_S_VALUES
] ||
700 (!tb
[RPC_S_SECTION
] && !tb
[RPC_S_TYPE
] && !tb
[RPC_S_MATCH
]))
701 return UBUS_STATUS_INVALID_ARGUMENT
;
703 if (!rpc_uci_write_access(tb
[RPC_S_SESSION
], tb
[RPC_S_CONFIG
]))
704 return UBUS_STATUS_PERMISSION_DENIED
;
706 ptr
.package
= blobmsg_data(tb
[RPC_S_CONFIG
]);
707 uci_load(cursor
, ptr
.package
, &p
);
712 if (tb
[RPC_S_SECTION
])
714 ptr
.section
= blobmsg_data(tb
[RPC_S_SECTION
]);
715 blobmsg_for_each_attr(cur
, tb
[RPC_S_VALUES
], rem
)
716 rpc_uci_merge_set(cur
, &ptr
);
720 uci_foreach_element(&p
->sections
, e
)
722 if (!rpc_uci_match_section(uci_to_section(e
),
723 tb
[RPC_S_TYPE
], tb
[RPC_S_MATCH
]))
727 ptr
.section
= e
->name
;
729 blobmsg_for_each_attr(cur
, tb
[RPC_S_VALUES
], rem
)
730 rpc_uci_merge_set(cur
, &ptr
);
738 uci_unload(cursor
, p
);
740 return rpc_uci_status();
744 * Delete option or section from uci specified by given blob attribute pointer
745 * 1) if the blob is of type array, delete any option named after each element
746 * 2) if the blob is of type string, delete the option named after its value
747 * 3) if the blob is NULL, delete entire section
750 rpc_uci_merge_delete(struct blob_attr
*opt
, struct uci_ptr
*ptr
)
752 struct blob_attr
*cur
;
755 if (rpc_uci_lookup(ptr
) || !ptr
->s
)
763 uci_delete(cursor
, ptr
);
765 else if (blobmsg_type(opt
) == BLOBMSG_TYPE_ARRAY
)
767 blobmsg_for_each_attr(cur
, opt
, rem
)
769 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
)
773 ptr
->option
= blobmsg_data(cur
);
775 if (rpc_uci_lookup(ptr
) || !ptr
->o
)
778 uci_delete(cursor
, ptr
);
781 else if (blobmsg_type(opt
) == BLOBMSG_TYPE_STRING
)
784 ptr
->option
= blobmsg_data(opt
);
786 if (rpc_uci_lookup(ptr
) || !ptr
->o
)
789 uci_delete(cursor
, ptr
);
794 rpc_uci_delete(struct ubus_context
*ctx
, struct ubus_object
*obj
,
795 struct ubus_request_data
*req
, const char *method
,
796 struct blob_attr
*msg
)
798 struct blob_attr
*tb
[__RPC_D_MAX
];
799 struct uci_package
*p
= NULL
;
800 struct uci_element
*e
, *tmp
;
801 struct uci_ptr ptr
= { 0 };
803 blobmsg_parse(rpc_uci_delete_policy
, __RPC_D_MAX
, tb
,
804 blob_data(msg
), blob_len(msg
));
806 if (!tb
[RPC_D_CONFIG
] ||
807 (!tb
[RPC_D_SECTION
] && !tb
[RPC_D_TYPE
] && !tb
[RPC_D_MATCH
]))
808 return UBUS_STATUS_INVALID_ARGUMENT
;
810 if (!rpc_uci_write_access(tb
[RPC_D_SESSION
], tb
[RPC_D_CONFIG
]))
811 return UBUS_STATUS_PERMISSION_DENIED
;
813 ptr
.package
= blobmsg_data(tb
[RPC_D_CONFIG
]);
814 uci_load(cursor
, ptr
.package
, &p
);
819 if (tb
[RPC_D_SECTION
])
821 ptr
.section
= blobmsg_data(tb
[RPC_D_SECTION
]);
823 if (tb
[RPC_D_OPTIONS
])
824 rpc_uci_merge_delete(tb
[RPC_D_OPTIONS
], &ptr
);
826 rpc_uci_merge_delete(tb
[RPC_D_OPTION
], &ptr
);
830 uci_foreach_element_safe(&p
->sections
, tmp
, e
)
832 if (!rpc_uci_match_section(uci_to_section(e
),
833 tb
[RPC_D_TYPE
], tb
[RPC_D_MATCH
]))
837 ptr
.section
= e
->name
;
839 if (tb
[RPC_D_OPTIONS
])
840 rpc_uci_merge_delete(tb
[RPC_D_OPTIONS
], &ptr
);
842 rpc_uci_merge_delete(tb
[RPC_D_OPTION
], &ptr
);
850 uci_unload(cursor
, p
);
852 return rpc_uci_status();
856 rpc_uci_rename(struct ubus_context
*ctx
, struct ubus_object
*obj
,
857 struct ubus_request_data
*req
, const char *method
,
858 struct blob_attr
*msg
)
860 struct blob_attr
*tb
[__RPC_R_MAX
];
861 struct uci_package
*p
= NULL
;
862 struct uci_ptr ptr
= { 0 };
864 blobmsg_parse(rpc_uci_rename_policy
, __RPC_R_MAX
, tb
,
865 blob_data(msg
), blob_len(msg
));
867 if (!tb
[RPC_R_CONFIG
] || !tb
[RPC_R_SECTION
] || !tb
[RPC_R_NAME
])
868 return UBUS_STATUS_INVALID_ARGUMENT
;
870 if (!rpc_uci_write_access(tb
[RPC_R_SESSION
], tb
[RPC_R_CONFIG
]))
871 return UBUS_STATUS_PERMISSION_DENIED
;
873 ptr
.package
= blobmsg_data(tb
[RPC_R_CONFIG
]);
874 ptr
.section
= blobmsg_data(tb
[RPC_R_SECTION
]);
875 ptr
.value
= blobmsg_data(tb
[RPC_R_NAME
]);
877 if (tb
[RPC_R_OPTION
])
878 ptr
.option
= blobmsg_data(tb
[RPC_R_OPTION
]);
880 uci_load(cursor
, ptr
.package
, &p
);
885 if (uci_lookup_ptr(cursor
, &ptr
, NULL
, true))
888 if ((ptr
.option
&& !ptr
.o
) || !ptr
.s
)
890 cursor
->err
= UCI_ERR_NOTFOUND
;
894 if (uci_rename(cursor
, &ptr
))
901 uci_unload(cursor
, p
);
903 return rpc_uci_status();
907 rpc_uci_order(struct ubus_context
*ctx
, struct ubus_object
*obj
,
908 struct ubus_request_data
*req
, const char *method
,
909 struct blob_attr
*msg
)
911 struct blob_attr
*tb
[__RPC_O_MAX
];
912 struct blob_attr
*cur
;
913 struct uci_package
*p
= NULL
;
914 struct uci_ptr ptr
= { 0 };
917 blobmsg_parse(rpc_uci_order_policy
, __RPC_O_MAX
, tb
,
918 blob_data(msg
), blob_len(msg
));
920 if (!tb
[RPC_O_CONFIG
] || !tb
[RPC_O_SECTIONS
])
921 return UBUS_STATUS_INVALID_ARGUMENT
;
923 if (!rpc_uci_write_access(tb
[RPC_O_SESSION
], tb
[RPC_O_CONFIG
]))
924 return UBUS_STATUS_PERMISSION_DENIED
;
926 ptr
.package
= blobmsg_data(tb
[RPC_O_CONFIG
]);
928 uci_load(cursor
, ptr
.package
, &p
);
933 blobmsg_for_each_attr(cur
, tb
[RPC_O_SECTIONS
], rem
)
935 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
)
939 ptr
.section
= blobmsg_data(cur
);
941 if (uci_lookup_ptr(cursor
, &ptr
, NULL
, true) || !ptr
.s
)
944 uci_reorder_section(cursor
, ptr
.s
, i
++);
951 uci_unload(cursor
, p
);
953 return rpc_uci_status();
957 rpc_uci_dump_change(struct uci_delta
*d
)
960 const char *types
[] = {
961 [UCI_CMD_REORDER
] = "order",
962 [UCI_CMD_REMOVE
] = "remove",
963 [UCI_CMD_RENAME
] = "rename",
964 [UCI_CMD_ADD
] = "add",
965 [UCI_CMD_LIST_ADD
] = "list-add",
966 [UCI_CMD_LIST_DEL
] = "list-del",
967 [UCI_CMD_CHANGE
] = "set",
973 c
= blobmsg_open_array(&buf
, NULL
);
975 blobmsg_add_string(&buf
, NULL
, types
[d
->cmd
]);
976 blobmsg_add_string(&buf
, NULL
, d
->section
);
979 blobmsg_add_string(&buf
, NULL
, d
->e
.name
);
983 if (d
->cmd
== UCI_CMD_REORDER
)
984 blobmsg_add_u32(&buf
, NULL
, strtoul(d
->value
, NULL
, 10));
986 blobmsg_add_string(&buf
, NULL
, d
->value
);
989 blobmsg_close_array(&buf
, c
);
993 rpc_uci_changes(struct ubus_context
*ctx
, struct ubus_object
*obj
,
994 struct ubus_request_data
*req
, const char *method
,
995 struct blob_attr
*msg
)
997 struct blob_attr
*tb
[__RPC_C_MAX
];
998 struct uci_package
*p
= NULL
;
999 struct uci_element
*e
;
1002 blobmsg_parse(rpc_uci_config_policy
, __RPC_C_MAX
, tb
,
1003 blob_data(msg
), blob_len(msg
));
1005 if (!tb
[RPC_C_CONFIG
])
1006 return UBUS_STATUS_INVALID_ARGUMENT
;
1008 if (!rpc_uci_read_access(tb
[RPC_C_SESSION
], tb
[RPC_C_CONFIG
]))
1009 return UBUS_STATUS_PERMISSION_DENIED
;
1011 uci_load(cursor
, blobmsg_data(tb
[RPC_C_CONFIG
]), &p
);
1016 blob_buf_init(&buf
, 0);
1017 c
= blobmsg_open_array(&buf
, "changes");
1019 uci_foreach_element(&p
->saved_delta
, e
)
1020 rpc_uci_dump_change(uci_to_delta(e
));
1022 blobmsg_close_array(&buf
, c
);
1024 ubus_send_reply(ctx
, req
, buf
.head
);
1028 uci_unload(cursor
, p
);
1030 return rpc_uci_status();
1034 rpc_uci_revert_commit(struct blob_attr
*msg
, bool commit
)
1036 struct blob_attr
*tb
[__RPC_C_MAX
];
1037 struct uci_package
*p
= NULL
;
1038 struct uci_ptr ptr
= { 0 };
1040 blobmsg_parse(rpc_uci_config_policy
, __RPC_C_MAX
, tb
,
1041 blob_data(msg
), blob_len(msg
));
1043 if (!tb
[RPC_C_CONFIG
])
1044 return UBUS_STATUS_INVALID_ARGUMENT
;
1046 if (!rpc_uci_write_access(tb
[RPC_C_SESSION
], tb
[RPC_C_CONFIG
]))
1047 return UBUS_STATUS_PERMISSION_DENIED
;
1049 ptr
.package
= blobmsg_data(tb
[RPC_C_CONFIG
]);
1053 uci_load(cursor
, ptr
.package
, &p
);
1057 uci_commit(cursor
, &p
, false);
1058 uci_unload(cursor
, p
);
1063 if (!uci_lookup_ptr(cursor
, &ptr
, NULL
, true) && ptr
.p
)
1064 uci_revert(cursor
, &ptr
);
1067 return rpc_uci_status();
1071 rpc_uci_revert(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1072 struct ubus_request_data
*req
, const char *method
,
1073 struct blob_attr
*msg
)
1075 return rpc_uci_revert_commit(msg
, false);
1079 rpc_uci_commit(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1080 struct ubus_request_data
*req
, const char *method
,
1081 struct blob_attr
*msg
)
1083 return rpc_uci_revert_commit(msg
, true);
1087 rpc_uci_configs(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1088 struct ubus_request_data
*req
, const char *method
,
1089 struct blob_attr
*msg
)
1095 if (uci_list_configs(cursor
, &configs
))
1098 blob_buf_init(&buf
, 0);
1100 c
= blobmsg_open_array(&buf
, "configs");
1102 for (i
= 0; configs
[i
]; i
++)
1103 blobmsg_add_string(&buf
, NULL
, configs
[i
]);
1105 blobmsg_close_array(&buf
, c
);
1107 ubus_send_reply(ctx
, req
, buf
.head
);
1110 return rpc_uci_status();
1115 * Remove given delta save directory (if any).
1118 rpc_uci_purge_savedir(const char *path
)
1123 char file
[PATH_MAX
];
1125 if (stat(path
, &s
) || !S_ISDIR(s
.st_mode
))
1128 if ((d
= opendir(path
)) != NULL
)
1130 while ((e
= readdir(d
)) != NULL
)
1132 snprintf(file
, sizeof(file
) - 1, "%s/%s", path
, e
->d_name
);
1134 if (stat(file
, &s
) || !S_ISREG(s
.st_mode
))
1147 * Session destroy callback to purge associated delta directory.
1150 rpc_uci_purge_savedir_cb(struct rpc_session
*ses
, void *priv
)
1152 char path
[PATH_MAX
];
1154 snprintf(path
, sizeof(path
) - 1, "/tmp/.uci-rpc-%s", ses
->id
);
1155 rpc_uci_purge_savedir(path
);
1159 * Removes all delta directories which match the /tmp/.uci-rpc-* pattern.
1160 * This is used to clean up garbage when starting rpcd.
1162 void rpc_uci_purge_savedirs(void)
1167 if (!glob("/tmp/.uci-rpc-*", 0, NULL
, &gl
))
1169 for (i
= 0; i
< gl
.gl_pathc
; i
++)
1170 rpc_uci_purge_savedir(gl
.gl_pathv
[i
]);
1176 int rpc_uci_api_init(struct ubus_context
*ctx
)
1178 static const struct ubus_method uci_methods
[] = {
1179 { .name
= "configs", .handler
= rpc_uci_configs
},
1180 UBUS_METHOD("get", rpc_uci_get
, rpc_uci_get_policy
),
1181 UBUS_METHOD("add", rpc_uci_add
, rpc_uci_add_policy
),
1182 UBUS_METHOD("set", rpc_uci_set
, rpc_uci_set_policy
),
1183 UBUS_METHOD("delete", rpc_uci_delete
, rpc_uci_delete_policy
),
1184 UBUS_METHOD("rename", rpc_uci_rename
, rpc_uci_rename_policy
),
1185 UBUS_METHOD("order", rpc_uci_order
, rpc_uci_order_policy
),
1186 UBUS_METHOD("changes", rpc_uci_changes
, rpc_uci_config_policy
),
1187 UBUS_METHOD("revert", rpc_uci_revert
, rpc_uci_config_policy
),
1188 UBUS_METHOD("commit", rpc_uci_commit
, rpc_uci_config_policy
),
1191 static struct ubus_object_type uci_type
=
1192 UBUS_OBJECT_TYPE("luci-rpc-uci", uci_methods
);
1194 static struct ubus_object obj
= {
1197 .methods
= uci_methods
,
1198 .n_methods
= ARRAY_SIZE(uci_methods
),
1201 static struct rpc_session_cb cb
= {
1202 .cb
= rpc_uci_purge_savedir_cb
1205 cursor
= uci_alloc_context();
1208 return UBUS_STATUS_UNKNOWN_ERROR
;
1210 rpc_session_destroy_cb(&cb
);
1212 return ubus_add_object(ctx
, &obj
);