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