1 /* 2 * libuci - Library for the Unified Configuration Interface 3 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU Lesser General Public License version 2.1 7 * as published by the Free Software Foundation 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Lesser General Public License for more details. 13 */ 14 15 /* 16 * This file contains the code for parsing uci config files 17 */ 18 19 #define _GNU_SOURCE 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <sys/file.h> 23 #include <stdbool.h> 24 #include <unistd.h> 25 #include <fcntl.h> 26 #include <stdio.h> 27 #include <ctype.h> 28 #include <glob.h> 29 #include <string.h> 30 #include <stdlib.h> 31 #include <errno.h> 32 33 #include "uci.h" 34 #include "uci_internal.h" 35 36 #define LINEBUF 32 37 38 /* 39 * Fetch a new line from the input stream and resize buffer if necessary 40 */ 41 __private void uci_getln(struct uci_context *ctx, size_t offset) 42 { 43 struct uci_parse_context *pctx = ctx->pctx; 44 char *p; 45 size_t ofs; 46 47 if (pctx->buf == NULL) { 48 pctx->buf = uci_malloc(ctx, LINEBUF); 49 pctx->bufsz = LINEBUF; 50 } 51 /* It takes 2 slots for fgets to read 1 char. */ 52 if (offset >= pctx->bufsz - 1) { 53 pctx->bufsz *= 2; 54 pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz); 55 } 56 57 ofs = offset; 58 do { 59 p = &pctx->buf[ofs]; 60 p[0] = 0; 61 62 p = fgets(p, pctx->bufsz - ofs, pctx->file); 63 if (!p || !*p) 64 return; 65 66 ofs += strlen(p); 67 pctx->buf_filled = ofs; 68 if (pctx->buf[ofs - 1] == '\n') { 69 pctx->line++; 70 return; 71 } 72 73 pctx->bufsz *= 2; 74 pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz); 75 } while (1); 76 } 77 78 /* 79 * parse a character escaped by '\' 80 * returns true if the escaped character is to be parsed 81 * returns false if the escaped character is to be ignored 82 */ 83 static bool parse_backslash(struct uci_context *ctx) 84 { 85 struct uci_parse_context *pctx = ctx->pctx; 86 87 /* skip backslash */ 88 pctx->pos += 1; 89 90 /* undecoded backslash at the end of line, fetch the next line */ 91 if (!pctx_cur_char(pctx) || 92 pctx_cur_char(pctx) == '\n' || 93 (pctx_cur_char(pctx) == '\r' && 94 pctx_char(pctx, pctx_pos(pctx) + 1) == '\n' && 95 !pctx_char(pctx, pctx_pos(pctx) + 2))) { 96 uci_getln(ctx, pctx->pos); 97 return false; 98 } 99 100 /* FIXME: decode escaped char, necessary? */ 101 return true; 102 } 103 104 /* 105 * move the string pointer forward until a non-whitespace character or 106 * EOL is reached 107 */ 108 static void skip_whitespace(struct uci_context *ctx) 109 { 110 struct uci_parse_context *pctx = ctx->pctx; 111 112 while (pctx_cur_char(pctx) && isspace(pctx_cur_char(pctx))) 113 pctx->pos += 1; 114 } 115 116 static inline void addc(struct uci_context *ctx, size_t *pos_dest, size_t *pos_src) 117 { 118 struct uci_parse_context *pctx = ctx->pctx; 119 120 pctx_char(pctx, *pos_dest) = pctx_char(pctx, *pos_src); 121 *pos_dest += 1; 122 *pos_src += 1; 123 } 124 125 static int uci_increase_pos(struct uci_parse_context *pctx, size_t add) 126 { 127 if (pctx->pos + add > pctx->buf_filled) 128 return -EINVAL; 129 130 pctx->pos += add; 131 return 0; 132 } 133 134 /* 135 * parse a double quoted string argument from the command line 136 */ 137 static void parse_double_quote(struct uci_context *ctx, size_t *target) 138 { 139 struct uci_parse_context *pctx = ctx->pctx; 140 char c; 141 142 /* skip quote character */ 143 pctx->pos += 1; 144 145 while (1) { 146 c = pctx_cur_char(pctx); 147 switch(c) { 148 case '"': 149 pctx->pos += 1; 150 return; 151 case 0: 152 /* Multi-line str value */ 153 uci_getln(ctx, pctx->pos); 154 if (!pctx_cur_char(pctx)) { 155 uci_parse_error(ctx, "EOF with unterminated \""); 156 } 157 break; 158 case '\\': 159 if (!parse_backslash(ctx)) 160 continue; 161 /* fall through */ 162 default: 163 addc(ctx, target, &pctx->pos); 164 break; 165 } 166 } 167 } 168 169 /* 170 * parse a single quoted string argument from the command line 171 */ 172 static void parse_single_quote(struct uci_context *ctx, size_t *target) 173 { 174 struct uci_parse_context *pctx = ctx->pctx; 175 char c; 176 /* skip quote character */ 177 pctx->pos += 1; 178 179 while (1) { 180 c = pctx_cur_char(pctx); 181 switch(c) { 182 case '\'': 183 pctx->pos += 1; 184 return; 185 case 0: 186 /* Multi-line str value */ 187 uci_getln(ctx, pctx->pos); 188 if (!pctx_cur_char(pctx)) 189 uci_parse_error(ctx, "EOF with unterminated '"); 190 191 break; 192 default: 193 addc(ctx, target, &pctx->pos); 194 } 195 } 196 } 197 198 /* 199 * parse a string from the command line and detect the quoting style 200 */ 201 static void parse_str(struct uci_context *ctx, size_t *target) 202 { 203 struct uci_parse_context *pctx = ctx->pctx; 204 bool next = true; 205 do { 206 switch(pctx_cur_char(pctx)) { 207 case '\'': 208 parse_single_quote(ctx, target); 209 break; 210 case '"': 211 parse_double_quote(ctx, target); 212 break; 213 case '#': 214 pctx_cur_char(pctx) = 0; 215 /* fall through */ 216 case 0: 217 goto done; 218 case ';': 219 next = false; 220 goto done; 221 case '\\': 222 if (!parse_backslash(ctx)) 223 continue; 224 /* fall through */ 225 default: 226 addc(ctx, target, &pctx->pos); 227 break; 228 } 229 } while (pctx_cur_char(pctx) && !isspace(pctx_cur_char(pctx))); 230 done: 231 232 /* 233 * if the string was unquoted and we've stopped at a whitespace 234 * character, skip to the next one, because the whitespace will 235 * be overwritten by a null byte here 236 */ 237 if (pctx_cur_char(pctx) && next) 238 pctx->pos += 1; 239 240 /* terminate the parsed string */ 241 pctx_char(pctx, *target) = 0; 242 } 243 244 /* 245 * extract the next argument from the command line 246 */ 247 static int next_arg(struct uci_context *ctx, bool required, bool name, bool package) 248 { 249 struct uci_parse_context *pctx = ctx->pctx; 250 size_t val, ptr; 251 252 skip_whitespace(ctx); 253 val = ptr = pctx_pos(pctx); 254 if (pctx_cur_char(pctx) == ';') { 255 pctx_cur_char(pctx) = 0; 256 pctx->pos += 1; 257 } else { 258 parse_str(ctx, &ptr); 259 } 260 if (!pctx_char(pctx, val)) { 261 if (required) 262 uci_parse_error(ctx, "insufficient arguments"); 263 goto done; 264 } 265 266 if (name && !uci_validate_str(pctx_str(pctx, val), name, package)) 267 uci_parse_error(ctx, "invalid character in name field"); 268 269 done: 270 return val; 271 } 272 273 int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, char **result) 274 { 275 int ofs_result; 276 277 UCI_HANDLE_ERR(ctx); 278 UCI_ASSERT(ctx, str != NULL); 279 UCI_ASSERT(ctx, result != NULL); 280 281 if (ctx->pctx && (ctx->pctx->file != stream)) 282 uci_cleanup(ctx); 283 284 if (!ctx->pctx) 285 uci_alloc_parse_context(ctx); 286 287 ctx->pctx->file = stream; 288 if (!*str) { 289 ctx->pctx->pos = 0; 290 uci_getln(ctx, 0); 291 } 292 293 ofs_result = next_arg(ctx, false, false, false); 294 *result = pctx_str(ctx->pctx, ofs_result); 295 *str = pctx_cur_str(ctx->pctx); 296 297 return 0; 298 } 299 300 static int 301 uci_fill_ptr(struct uci_context *ctx, struct uci_ptr *ptr, struct uci_element *e) 302 { 303 UCI_ASSERT(ctx, ptr != NULL); 304 UCI_ASSERT(ctx, e != NULL); 305 306 memset(ptr, 0, sizeof(struct uci_ptr)); 307 switch(e->type) { 308 case UCI_TYPE_OPTION: 309 ptr->o = uci_to_option(e); 310 goto fill_option; 311 case UCI_TYPE_SECTION: 312 ptr->s = uci_to_section(e); 313 goto fill_section; 314 case UCI_TYPE_PACKAGE: 315 ptr->p = uci_to_package(e); 316 goto fill_package; 317 default: 318 UCI_THROW(ctx, UCI_ERR_INVAL); 319 } 320 321 fill_option: 322 ptr->option = ptr->o->e.name; 323 ptr->s = ptr->o->section; 324 fill_section: 325 ptr->section = ptr->s->e.name; 326 ptr->p = ptr->s->package; 327 fill_package: 328 ptr->package = ptr->p->e.name; 329 330 ptr->flags |= UCI_LOOKUP_DONE; 331 332 return 0; 333 } 334 335 336 337 /* 338 * verify that the end of the line or command is reached. 339 * throw an error if extra arguments are given on the command line 340 */ 341 static void assert_eol(struct uci_context *ctx) 342 { 343 char *tmp; 344 int ofs_tmp; 345 346 skip_whitespace(ctx); 347 ofs_tmp = next_arg(ctx, false, false, false); 348 tmp = pctx_str(ctx->pctx, ofs_tmp); 349 if (*tmp && (ctx->flags & UCI_FLAG_STRICT)) 350 uci_parse_error(ctx, "too many arguments"); 351 } 352 353 /* 354 * switch to a different config, either triggered by uci_load, or by a 355 * 'package <...>' statement in the import file 356 */ 357 static void uci_switch_config(struct uci_context *ctx) 358 { 359 struct uci_parse_context *pctx; 360 struct uci_element *e; 361 const char *name; 362 363 pctx = ctx->pctx; 364 name = pctx->name; 365 366 /* add the last config to main config file list */ 367 if (pctx->package) { 368 pctx->package->backend = ctx->backend; 369 uci_list_add(&ctx->root, &pctx->package->e.list); 370 371 pctx->package = NULL; 372 pctx->section = NULL; 373 } 374 375 if (!name) 376 return; 377 378 /* 379 * if an older config under the same name exists, unload it 380 * ignore errors here, e.g. if the config was not found 381 */ 382 e = uci_lookup_list(&ctx->root, name); 383 if (e) 384 UCI_THROW(ctx, UCI_ERR_DUPLICATE); 385 pctx->package = uci_alloc_package(ctx, name); 386 } 387 388 /* 389 * parse the 'package' uci command (next config package) 390 */ 391 static void uci_parse_package(struct uci_context *ctx, bool single) 392 { 393 struct uci_parse_context *pctx = ctx->pctx; 394 int ofs_name; 395 char *name; 396 397 /* command string null-terminated by strtok */ 398 if (uci_increase_pos(pctx, strlen(pctx_cur_str(pctx)) + 1)) 399 uci_parse_error(ctx, "package without name"); 400 401 ofs_name = next_arg(ctx, true, true, true); 402 assert_eol(ctx); 403 name = pctx_str(pctx, ofs_name); 404 if (single) 405 return; 406 407 ctx->pctx->name = name; 408 uci_switch_config(ctx); 409 } 410 411 /* 412 * parse the 'config' uci command (open a section) 413 */ 414 static void uci_parse_config(struct uci_context *ctx) 415 { 416 struct uci_parse_context *pctx = ctx->pctx; 417 struct uci_element *e; 418 struct uci_ptr ptr; 419 int ofs_name, ofs_type; 420 char *name; 421 char *type; 422 423 if (!ctx->pctx->package) { 424 if (!ctx->pctx->name) 425 uci_parse_error(ctx, "attempting to import a file without a package name"); 426 427 uci_switch_config(ctx); 428 } 429 430 /* command string null-terminated by strtok */ 431 if (uci_increase_pos(pctx, strlen(pctx_cur_str(pctx)) + 1)) 432 uci_parse_error(ctx, "config without name"); 433 434 ofs_type = next_arg(ctx, true, false, false); 435 type = pctx_str(pctx, ofs_type); 436 if (!uci_validate_type(type)) 437 uci_parse_error(ctx, "invalid character in type field"); 438 439 ofs_name = next_arg(ctx, false, true, false); 440 assert_eol(ctx); 441 type = pctx_str(pctx, ofs_type); 442 name = pctx_str(pctx, ofs_name); 443 444 if (!name || !name[0]) { 445 ctx->internal = !pctx->merge; 446 UCI_NESTED(uci_add_section, ctx, pctx->package, type, &pctx->section); 447 } else { 448 uci_fill_ptr(ctx, &ptr, &pctx->package->e); 449 e = uci_lookup_list(&pctx->package->sections, name); 450 if (e) { 451 ptr.s = uci_to_section(e); 452 453 if ((ctx->flags & UCI_FLAG_STRICT) && strcmp(ptr.s->type, type)) 454 uci_parse_error(ctx, "section of different type overwrites prior section with same name"); 455 } 456 457 ptr.section = name; 458 ptr.value = type; 459 460 ctx->internal = !pctx->merge; 461 UCI_NESTED(uci_set, ctx, &ptr); 462 pctx->section = ptr.s; 463 } 464 } 465 466 /* 467 * parse the 'option' uci command (open a value) 468 */ 469 static void uci_parse_option(struct uci_context *ctx, bool list) 470 { 471 struct uci_parse_context *pctx = ctx->pctx; 472 struct uci_element *e; 473 struct uci_ptr ptr; 474 int ofs_name, ofs_value; 475 char *name = NULL; 476 char *value = NULL; 477 478 if (!pctx->section) 479 uci_parse_error(ctx, "option/list command found before the first section"); 480 481 /* command string null-terminated by strtok */ 482 if (uci_increase_pos(pctx, strlen(pctx_cur_str(pctx)) + 1)) 483 uci_parse_error(ctx, "option without name"); 484 485 ofs_name = next_arg(ctx, true, true, false); 486 ofs_value = next_arg(ctx, false, false, false); 487 assert_eol(ctx); 488 name = pctx_str(pctx, ofs_name); 489 value = pctx_str(pctx, ofs_value); 490 491 uci_fill_ptr(ctx, &ptr, &pctx->section->e); 492 e = uci_lookup_list(&pctx->section->options, name); 493 if (e) 494 ptr.o = uci_to_option(e); 495 ptr.option = name; 496 ptr.value = value; 497 498 ctx->internal = !pctx->merge; 499 if (list) 500 UCI_NESTED(uci_add_list, ctx, &ptr); 501 else 502 UCI_NESTED(uci_set, ctx, &ptr); 503 } 504 505 /* 506 * parse a complete input line, split up combined commands by ';' 507 */ 508 static void uci_parse_line(struct uci_context *ctx, bool single) 509 { 510 struct uci_parse_context *pctx = ctx->pctx; 511 char *word; 512 513 /* Skip whitespace characters at the start of line */ 514 skip_whitespace(ctx); 515 do { 516 word = strtok(pctx_cur_str(pctx), " \t"); 517 if (!word) 518 return; 519 520 switch(word[0]) { 521 case 0: 522 case '#': 523 return; 524 case 'p': 525 if ((word[1] == 0) || !strcmp(word + 1, "ackage")) 526 uci_parse_package(ctx, single); 527 else 528 goto invalid; 529 break; 530 case 'c': 531 if ((word[1] == 0) || !strcmp(word + 1, "onfig")) 532 uci_parse_config(ctx); 533 else 534 goto invalid; 535 break; 536 case 'o': 537 if ((word[1] == 0) || !strcmp(word + 1, "ption")) 538 uci_parse_option(ctx, false); 539 else 540 goto invalid; 541 break; 542 case 'l': 543 if ((word[1] == 0) || !strcmp(word + 1, "ist")) 544 uci_parse_option(ctx, true); 545 else 546 goto invalid; 547 break; 548 default: 549 goto invalid; 550 } 551 continue; 552 invalid: 553 uci_parse_error(ctx, "invalid command"); 554 } while (1); 555 } 556 557 /* max number of characters that escaping adds to the string */ 558 #define UCI_QUOTE_ESCAPE "'\\''" 559 560 /* 561 * escape an uci string for export 562 */ 563 static const char *uci_escape(struct uci_context *ctx, const char *str) 564 { 565 const char *end; 566 int ofs = 0; 567 568 if (!ctx->buf) { 569 ctx->bufsz = LINEBUF; 570 ctx->buf = malloc(LINEBUF); 571 572 if (!ctx->buf) 573 return str; 574 } 575 576 while (1) { 577 int len; 578 579 end = strchr(str, '\''); 580 if (!end) 581 end = str + strlen(str); 582 len = end - str; 583 584 /* make sure that we have enough room in the buffer */ 585 while (ofs + len + (int) sizeof(UCI_QUOTE_ESCAPE) + 1 > ctx->bufsz) { 586 ctx->bufsz *= 2; 587 ctx->buf = uci_realloc(ctx, ctx->buf, ctx->bufsz); 588 } 589 590 /* copy the string until the character before the quote */ 591 memcpy(&ctx->buf[ofs], str, len); 592 ofs += len; 593 594 /* end of string? return the buffer */ 595 if (*end == 0) 596 break; 597 598 memcpy(&ctx->buf[ofs], UCI_QUOTE_ESCAPE, sizeof(UCI_QUOTE_ESCAPE)); 599 ofs += strlen(&ctx->buf[ofs]); 600 str = end + 1; 601 } 602 603 ctx->buf[ofs] = 0; 604 return ctx->buf; 605 } 606 607 /* 608 * export a single config package to a file stream 609 */ 610 static void uci_export_package(struct uci_package *p, FILE *stream, bool header) 611 { 612 struct uci_context *ctx = p->ctx; 613 struct uci_element *s, *o, *i; 614 615 if (header) 616 fprintf(stream, "package %s\n", uci_escape(ctx, p->e.name)); 617 uci_foreach_element(&p->sections, s) { 618 struct uci_section *sec = uci_to_section(s); 619 fprintf(stream, "\nconfig %s", uci_escape(ctx, sec->type)); 620 if (!sec->anonymous || (ctx->flags & UCI_FLAG_EXPORT_NAME)) 621 fprintf(stream, " '%s'", uci_escape(ctx, sec->e.name)); 622 fprintf(stream, "\n"); 623 uci_foreach_element(&sec->options, o) { 624 struct uci_option *opt = uci_to_option(o); 625 switch(opt->type) { 626 case UCI_TYPE_STRING: 627 fprintf(stream, "\toption %s", uci_escape(ctx, opt->e.name)); 628 fprintf(stream, " '%s'\n", uci_escape(ctx, opt->v.string)); 629 break; 630 case UCI_TYPE_LIST: 631 uci_foreach_element(&opt->v.list, i) { 632 fprintf(stream, "\tlist %s", uci_escape(ctx, opt->e.name)); 633 fprintf(stream, " '%s'\n", uci_escape(ctx, i->name)); 634 } 635 break; 636 default: 637 fprintf(stream, "\t# unknown type for option '%s'\n", uci_escape(ctx, opt->e.name)); 638 break; 639 } 640 } 641 } 642 fprintf(stream, "\n"); 643 } 644 645 int uci_export(struct uci_context *ctx, FILE *stream, struct uci_package *package, bool header) 646 { 647 struct uci_element *e; 648 649 UCI_HANDLE_ERR(ctx); 650 UCI_ASSERT(ctx, stream != NULL); 651 652 if (package) 653 uci_export_package(package, stream, header); 654 else { 655 uci_foreach_element(&ctx->root, e) { 656 uci_export_package(uci_to_package(e), stream, header); 657 } 658 } 659 660 return 0; 661 } 662 663 int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct uci_package **package, bool single) 664 { 665 struct uci_parse_context *pctx; 666 UCI_HANDLE_ERR(ctx); 667 668 /* make sure no memory from previous parse attempts is leaked */ 669 uci_cleanup(ctx); 670 671 uci_alloc_parse_context(ctx); 672 pctx = ctx->pctx; 673 pctx->file = stream; 674 if (package && *package && single) { 675 pctx->package = *package; 676 pctx->merge = true; 677 } 678 679 /* 680 * If 'name' was supplied, assume that the supplied stream does not contain 681 * the appropriate 'package <name>' string to specify the config name 682 * NB: the config file can still override the package name 683 */ 684 if (name) { 685 UCI_ASSERT(ctx, uci_validate_package(name)); 686 pctx->name = name; 687 } 688 689 while (!feof(pctx->file)) { 690 pctx->pos = 0; 691 uci_getln(ctx, 0); 692 UCI_TRAP_SAVE(ctx, error); 693 if (pctx->buf[0]) 694 uci_parse_line(ctx, single); 695 UCI_TRAP_RESTORE(ctx); 696 continue; 697 error: 698 if (ctx->flags & UCI_FLAG_PERROR) 699 uci_perror(ctx, NULL); 700 if ((ctx->err != UCI_ERR_PARSE) || 701 (ctx->flags & UCI_FLAG_STRICT)) 702 UCI_THROW(ctx, ctx->err); 703 } 704 705 if (!pctx->package && name) 706 uci_switch_config(ctx); 707 if (package) 708 *package = pctx->package; 709 if (pctx->merge) 710 pctx->package = NULL; 711 712 pctx->name = NULL; 713 uci_switch_config(ctx); 714 715 /* no error happened, we can get rid of the parser context now */ 716 uci_cleanup(ctx); 717 718 return 0; 719 } 720 721 722 static char *uci_config_path(struct uci_context *ctx, const char *name) 723 { 724 char *filename; 725 726 UCI_ASSERT(ctx, uci_validate_package(name)); 727 filename = uci_malloc(ctx, strlen(name) + strlen(ctx->confdir) + 2); 728 sprintf(filename, "%s/%s", ctx->confdir, name); 729 730 return filename; 731 } 732 733 static void uci_file_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite) 734 { 735 struct uci_package *p = *package; 736 FILE *f1, *f2 = NULL; 737 char *volatile name = NULL; 738 char *volatile path = NULL; 739 char *filename = NULL; 740 struct stat statbuf; 741 volatile bool do_rename = false; 742 int fd, sz; 743 744 if (!p->path) { 745 if (overwrite) 746 p->path = uci_config_path(ctx, p->e.name); 747 else 748 UCI_THROW(ctx, UCI_ERR_INVAL); 749 } 750 751 sz = snprintf(NULL, 0, "%s/.%s.uci-XXXXXX", ctx->confdir, p->e.name); 752 filename = alloca(sz + 1); 753 snprintf(filename, sz + 1, "%s/.%s.uci-XXXXXX", ctx->confdir, p->e.name); 754 755 /* open the config file for writing now, so that it is locked */ 756 f1 = uci_open_stream(ctx, p->path, NULL, SEEK_SET, true, true); 757 758 /* flush unsaved changes and reload from delta file */ 759 UCI_TRAP_SAVE(ctx, done); 760 if (p->has_delta) { 761 if (!overwrite) { 762 name = uci_strdup(ctx, p->e.name); 763 path = uci_strdup(ctx, p->path); 764 /* dump our own changes to the delta file */ 765 if (!uci_list_empty(&p->delta)) 766 UCI_INTERNAL(uci_save, ctx, p); 767 768 /* 769 * other processes might have modified the config 770 * as well. dump and reload 771 */ 772 uci_free_package(&p); 773 uci_cleanup(ctx); 774 UCI_INTERNAL(uci_import, ctx, f1, name, &p, true); 775 776 p->path = path; 777 p->has_delta = true; 778 *package = p; 779 780 /* freed together with the uci_package */ 781 path = NULL; 782 } 783 784 /* flush delta */ 785 if (!uci_load_delta(ctx, p, true)) 786 goto done; 787 } 788 789 fd = mkstemp(filename); 790 if (fd == -1) 791 UCI_THROW(ctx, UCI_ERR_IO); 792 793 if ((flock(fd, LOCK_EX) < 0) && (errno != ENOSYS)) 794 UCI_THROW(ctx, UCI_ERR_IO); 795 796 if (lseek(fd, 0, SEEK_SET) < 0) 797 UCI_THROW(ctx, UCI_ERR_IO); 798 799 f2 = fdopen(fd, "w+"); 800 if (!f2) 801 UCI_THROW(ctx, UCI_ERR_IO); 802 803 uci_export(ctx, f2, p, false); 804 805 fflush(f2); 806 fsync(fileno(f2)); 807 uci_close_stream(f2); 808 809 do_rename = true; 810 811 UCI_TRAP_RESTORE(ctx); 812 813 done: 814 free(name); 815 free(path); 816 uci_close_stream(f1); 817 if (do_rename) { 818 path = realpath(p->path, NULL); 819 if (!path || stat(path, &statbuf) || chmod(filename, statbuf.st_mode) || rename(filename, path)) { 820 unlink(filename); 821 UCI_THROW(ctx, UCI_ERR_IO); 822 } 823 free(path); 824 } 825 if (ctx->err) 826 UCI_THROW(ctx, ctx->err); 827 } 828 829 830 /* 831 * This function returns the filename by returning the string 832 * after the last '/' character. By checking for a non-'\0' 833 * character afterwards, directories are ignored (glob marks 834 * those with a trailing '/' 835 */ 836 static inline char *get_filename(char *path) 837 { 838 char *p; 839 840 p = strrchr(path, '/'); 841 p++; 842 if (!*p) 843 return NULL; 844 return p; 845 } 846 847 static char **uci_list_config_files(struct uci_context *ctx) 848 { 849 char **configs; 850 glob_t globbuf; 851 int size, j, skipped; 852 size_t i; 853 char *buf; 854 char *dir; 855 856 dir = uci_malloc(ctx, strlen(ctx->confdir) + 1 + sizeof("/*")); 857 sprintf(dir, "%s/*", ctx->confdir); 858 if (glob(dir, GLOB_MARK, NULL, &globbuf) != 0) { 859 free(dir); 860 UCI_THROW(ctx, UCI_ERR_NOTFOUND); 861 } 862 863 size = sizeof(char *) * (globbuf.gl_pathc + 1); 864 skipped = 0; 865 for(i = 0; i < globbuf.gl_pathc; i++) { 866 char *p; 867 868 p = get_filename(globbuf.gl_pathv[i]); 869 if (!p) { 870 skipped++; 871 continue; 872 } 873 874 size += strlen(p) + 1; 875 } 876 877 configs = uci_malloc(ctx, size - skipped); 878 buf = (char *) &configs[globbuf.gl_pathc + 1 - skipped]; 879 j = 0; 880 for(i = 0; i < globbuf.gl_pathc; i++) { 881 char *p; 882 883 p = get_filename(globbuf.gl_pathv[i]); 884 if (!p) 885 continue; 886 887 if (!uci_validate_package(p)) 888 continue; 889 890 configs[j++] = buf; 891 strcpy(buf, p); 892 buf += strlen(buf) + 1; 893 } 894 free(dir); 895 globfree(&globbuf); 896 return configs; 897 } 898 899 static struct uci_package *uci_file_load(struct uci_context *ctx, 900 const char *volatile name) 901 { 902 struct uci_package *package = NULL; 903 char *filename; 904 bool confdir; 905 FILE *volatile file = NULL; 906 907 switch (name[0]) { 908 case '.': 909 /* relative path outside of /etc/config */ 910 if (name[1] != '/') 911 UCI_THROW(ctx, UCI_ERR_NOTFOUND); 912 /* fall through */ 913 case '/': 914 /* absolute path outside of /etc/config */ 915 filename = uci_strdup(ctx, name); 916 name = strrchr(name, '/') + 1; 917 confdir = false; 918 break; 919 default: 920 /* config in /etc/config */ 921 filename = uci_config_path(ctx, name); 922 confdir = true; 923 break; 924 } 925 926 UCI_TRAP_SAVE(ctx, done); 927 file = uci_open_stream(ctx, filename, NULL, SEEK_SET, false, false); 928 ctx->err = 0; 929 UCI_INTERNAL(uci_import, ctx, file, name, &package, true); 930 UCI_TRAP_RESTORE(ctx); 931 932 if (package) { 933 package->path = filename; 934 package->has_delta = confdir; 935 uci_load_delta(ctx, package, false); 936 } 937 938 done: 939 uci_close_stream(file); 940 if (ctx->err) { 941 free(filename); 942 UCI_THROW(ctx, ctx->err); 943 } 944 return package; 945 } 946 947 __private UCI_BACKEND(uci_file_backend, "file", 948 .load = uci_file_load, 949 .commit = uci_file_commit, 950 .list_configs = uci_list_config_files, 951 ); 952
This page was automatically generated by LXR 0.3.1. • OpenWrt