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, bool conf2) 723 { 724 const char *confdir = conf2 ? ctx->conf2dir : ctx->confdir; 725 char *filename; 726 727 UCI_ASSERT(ctx, uci_validate_package(name)); 728 if (!confdir) 729 return NULL; 730 filename = uci_malloc(ctx, strlen(name) + strlen(confdir) + 2); 731 sprintf(filename, "%s/%s", confdir, name); 732 733 return filename; 734 } 735 736 static void uci_file_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite) 737 { 738 struct uci_package *p = *package; 739 FILE *f1, *f2 = NULL; 740 char *volatile name = NULL; 741 char *volatile path = NULL; 742 char *filename = NULL; 743 struct stat statbuf; 744 volatile bool do_rename = false; 745 const char *confdir; 746 int fd, sz; 747 748 if (!p->path && overwrite) 749 p->path = uci_config_path(ctx, p->e.name, p->uses_conf2); 750 if (!p->path) 751 UCI_THROW(ctx, UCI_ERR_INVAL); 752 753 confdir = p->uses_conf2 ? ctx->conf2dir : ctx->confdir; 754 sz = snprintf(NULL, 0, "%s/.%s.uci-XXXXXX", confdir, p->e.name); 755 filename = alloca(sz + 1); 756 snprintf(filename, sz + 1, "%s/.%s.uci-XXXXXX", confdir, p->e.name); 757 758 /* open the config file for writing now, so that it is locked */ 759 f1 = uci_open_stream(ctx, p->path, NULL, SEEK_SET, true, true); 760 761 /* flush unsaved changes and reload from delta file */ 762 UCI_TRAP_SAVE(ctx, done); 763 if (p->has_delta) { 764 if (!overwrite) { 765 name = uci_strdup(ctx, p->e.name); 766 path = uci_strdup(ctx, p->path); 767 /* dump our own changes to the delta file */ 768 if (!uci_list_empty(&p->delta)) 769 UCI_INTERNAL(uci_save, ctx, p); 770 771 /* 772 * other processes might have modified the config 773 * as well. dump and reload 774 */ 775 uci_free_package(&p); 776 uci_cleanup(ctx); 777 UCI_INTERNAL(uci_import, ctx, f1, name, &p, true); 778 779 p->path = path; 780 p->has_delta = true; 781 *package = p; 782 783 /* freed together with the uci_package */ 784 path = NULL; 785 } 786 787 /* flush delta */ 788 if (!uci_load_delta(ctx, p, true)) 789 goto done; 790 } 791 792 fd = mkstemp(filename); 793 if (fd == -1) 794 UCI_THROW(ctx, UCI_ERR_IO); 795 796 if ((flock(fd, LOCK_EX) < 0) && (errno != ENOSYS)) 797 UCI_THROW(ctx, UCI_ERR_IO); 798 799 if (lseek(fd, 0, SEEK_SET) < 0) 800 UCI_THROW(ctx, UCI_ERR_IO); 801 802 f2 = fdopen(fd, "w+"); 803 if (!f2) 804 UCI_THROW(ctx, UCI_ERR_IO); 805 806 uci_export(ctx, f2, p, false); 807 808 fflush(f2); 809 fsync(fileno(f2)); 810 uci_close_stream(f2); 811 812 do_rename = true; 813 814 UCI_TRAP_RESTORE(ctx); 815 816 done: 817 free(name); 818 free(path); 819 uci_close_stream(f1); 820 if (do_rename) { 821 path = realpath(p->path, NULL); 822 if (!path || stat(path, &statbuf) || chmod(filename, statbuf.st_mode) || rename(filename, path)) { 823 unlink(filename); 824 UCI_THROW(ctx, UCI_ERR_IO); 825 } 826 free(path); 827 } 828 if (ctx->err) 829 UCI_THROW(ctx, ctx->err); 830 } 831 832 833 /* 834 * This function returns the filename by returning the string 835 * after the last '/' character. By checking for a non-'\0' 836 * character afterwards, directories are ignored (glob marks 837 * those with a trailing '/' 838 * Filename with '.' are ignored and not parsed. Uci doesn't 839 * permit config files with '.' in the name and cause parsing 840 * problems. 841 */ 842 static inline char *get_filename(char *path) 843 { 844 char *p; 845 846 p = strrchr(path, '/'); 847 p++; 848 if (!*p) 849 return NULL; 850 851 if (strrchr(p, '.')) 852 return NULL; 853 854 return p; 855 } 856 857 static char **uci_list_config_files(struct uci_context *ctx) 858 { 859 char **configs; 860 glob_t globbuf; 861 int size, j, skipped; 862 size_t i; 863 char *buf; 864 char *dir; 865 866 dir = uci_malloc(ctx, strlen(ctx->confdir) + 1 + sizeof("/*")); 867 sprintf(dir, "%s/*", ctx->confdir); 868 if (glob(dir, GLOB_MARK, NULL, &globbuf) != 0) { 869 free(dir); 870 UCI_THROW(ctx, UCI_ERR_NOTFOUND); 871 } 872 873 size = sizeof(char *) * (globbuf.gl_pathc + 1); 874 skipped = 0; 875 for(i = 0; i < globbuf.gl_pathc; i++) { 876 char *p; 877 878 p = get_filename(globbuf.gl_pathv[i]); 879 if (!p) { 880 skipped++; 881 continue; 882 } 883 884 size += strlen(p) + 1; 885 } 886 887 configs = uci_malloc(ctx, size - skipped); 888 buf = (char *) &configs[globbuf.gl_pathc + 1 - skipped]; 889 j = 0; 890 for(i = 0; i < globbuf.gl_pathc; i++) { 891 char *p; 892 893 p = get_filename(globbuf.gl_pathv[i]); 894 if (!p) 895 continue; 896 897 if (!uci_validate_package(p)) 898 continue; 899 900 configs[j++] = buf; 901 strcpy(buf, p); 902 buf += strlen(buf) + 1; 903 } 904 free(dir); 905 globfree(&globbuf); 906 return configs; 907 } 908 909 static struct uci_package *uci_file_load(struct uci_context *ctx, 910 const char *volatile name) 911 { 912 struct uci_package *package = NULL; 913 char *filename; 914 bool confdir; 915 FILE *volatile file = NULL; 916 struct stat st; 917 bool conf2; 918 919 switch (name[0]) { 920 case '.': 921 /* relative path outside of /etc/config */ 922 if (name[1] != '/') 923 UCI_THROW(ctx, UCI_ERR_NOTFOUND); 924 /* fall through */ 925 case '/': 926 /* absolute path outside of /etc/config */ 927 filename = uci_strdup(ctx, name); 928 name = strrchr(name, '/') + 1; 929 confdir = false; 930 conf2 = false; 931 break; 932 default: 933 /* config in /etc/config */ 934 conf2 = true; 935 filename = uci_config_path(ctx, name, conf2); 936 if (!filename || stat(filename, &st) != 0) { 937 conf2 = false; 938 free(filename); 939 filename = uci_config_path(ctx, name, conf2); 940 } 941 confdir = true; 942 break; 943 } 944 945 UCI_TRAP_SAVE(ctx, done); 946 file = uci_open_stream(ctx, filename, NULL, SEEK_SET, false, false); 947 ctx->err = 0; 948 UCI_INTERNAL(uci_import, ctx, file, name, &package, true); 949 UCI_TRAP_RESTORE(ctx); 950 951 if (package) { 952 package->uses_conf2 = conf2; 953 package->path = filename; 954 package->has_delta = confdir; 955 uci_load_delta(ctx, package, false); 956 } 957 958 done: 959 uci_close_stream(file); 960 if (ctx->err) { 961 free(filename); 962 UCI_THROW(ctx, ctx->err); 963 } 964 return package; 965 } 966 967 __private UCI_BACKEND(uci_file_backend, "file", 968 .load = uci_file_load, 969 .commit = uci_file_commit, 970 .list_configs = uci_list_config_files, 971 ); 972
This page was automatically generated by LXR 0.3.1. • OpenWrt