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 handling uci config delta 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 <string.h> 29 #include <stdlib.h> 30 31 #include "uci.h" 32 #include "uci_internal.h" 33 34 /* record a change that was done to a package */ 35 void 36 uci_add_delta(struct uci_context *ctx, struct uci_list *list, int cmd, const char *section, const char *option, const char *value) 37 { 38 struct uci_delta *h; 39 int size = strlen(section) + 1; 40 char *ptr; 41 42 if (value) 43 size += strlen(value) + 1; 44 45 h = uci_alloc_element(ctx, delta, option, size); 46 ptr = uci_dataptr(h); 47 h->cmd = cmd; 48 h->section = strcpy(ptr, section); 49 if (value) { 50 ptr += strlen(ptr) + 1; 51 h->value = strcpy(ptr, value); 52 } 53 uci_list_add(list, &h->e.list); 54 } 55 56 void 57 uci_free_delta(struct uci_delta *h) 58 { 59 if (!h) 60 return; 61 if ((h->section != NULL) && 62 (h->section != uci_dataptr(h))) { 63 free(h->section); 64 free(h->value); 65 } 66 uci_free_element(&h->e); 67 } 68 69 static void uci_delta_save(struct uci_context *ctx, FILE *f, 70 const char *name, const struct uci_delta *h) 71 { 72 const struct uci_element *e = &h->e; 73 char prefix[2] = {0, 0}; 74 75 if (h->cmd <= __UCI_CMD_LAST) 76 prefix[0] = uci_command_char[h->cmd]; 77 78 fprintf(f, "%s%s.%s", prefix, name, h->section); 79 if (e->name) 80 fprintf(f, ".%s", e->name); 81 82 if (h->cmd == UCI_CMD_REMOVE && !h->value) 83 fprintf(f, "\n"); 84 else { 85 int i; 86 87 fprintf(f, "='"); 88 for (i = 0; h->value[i]; i++) { 89 unsigned char c = h->value[i]; 90 if (c != '\'') 91 fputc(c, f); 92 else 93 fprintf(f, "'\\''"); 94 } 95 fprintf(f, "'\n"); 96 } 97 } 98 99 int uci_set_savedir(struct uci_context *ctx, const char *dir) 100 { 101 char *sdir; 102 struct uci_element *e, *tmp; 103 volatile bool exists = false; 104 105 UCI_HANDLE_ERR(ctx); 106 UCI_ASSERT(ctx, dir != NULL); 107 108 /* Move dir to the end of ctx->delta_path */ 109 uci_foreach_element_safe(&ctx->delta_path, tmp, e) { 110 if (!strcmp(e->name, dir)) { 111 exists = true; 112 uci_list_del(&e->list); 113 break; 114 } 115 } 116 if (!exists) 117 e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element)); 118 uci_list_add(&ctx->delta_path, &e->list); 119 120 sdir = uci_strdup(ctx, dir); 121 if (ctx->savedir != uci_savedir) 122 free(ctx->savedir); 123 ctx->savedir = sdir; 124 return 0; 125 } 126 127 int uci_add_delta_path(struct uci_context *ctx, const char *dir) 128 { 129 struct uci_element *e; 130 struct uci_list *savedir; 131 132 UCI_HANDLE_ERR(ctx); 133 UCI_ASSERT(ctx, dir != NULL); 134 135 /* Duplicate delta path is not allowed */ 136 uci_foreach_element(&ctx->delta_path, e) { 137 if (!strcmp(e->name, dir)) 138 UCI_THROW(ctx, UCI_ERR_DUPLICATE); 139 } 140 141 e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element)); 142 /* Keep savedir at the end of ctx->delta_path list */ 143 savedir = ctx->delta_path.prev; 144 uci_list_insert(savedir->prev, &e->list); 145 146 return 0; 147 } 148 149 char const uci_command_char[] = { 150 [UCI_CMD_ADD] = '+', 151 [UCI_CMD_REMOVE] = '-', 152 [UCI_CMD_CHANGE] = 0, 153 [UCI_CMD_RENAME] = '@', 154 [UCI_CMD_REORDER] = '^', 155 [UCI_CMD_LIST_ADD] = '|', 156 [UCI_CMD_LIST_DEL] = '~' 157 }; 158 159 static inline int uci_parse_delta_tuple(struct uci_context *ctx, struct uci_ptr *ptr) 160 { 161 struct uci_parse_context *pctx = ctx->pctx; 162 char *str = pctx_cur_str(pctx), *arg; 163 int c; 164 165 UCI_INTERNAL(uci_parse_argument, ctx, ctx->pctx->file, &str, &arg); 166 if (str && *str) { 167 goto error; 168 } 169 for (c = 0; c <= __UCI_CMD_LAST; c++) { 170 if (uci_command_char[c] == *arg) 171 break; 172 } 173 if (c > __UCI_CMD_LAST) 174 c = UCI_CMD_CHANGE; 175 176 if (c != UCI_CMD_CHANGE) 177 arg += 1; 178 179 UCI_INTERNAL(uci_parse_ptr, ctx, ptr, arg); 180 181 if (!ptr->section) 182 goto error; 183 if (ptr->flags & UCI_LOOKUP_EXTENDED) 184 goto error; 185 if (c != UCI_CMD_REMOVE && !ptr->value) { 186 goto error; 187 } 188 189 switch(c) { 190 case UCI_CMD_REORDER: 191 if (!ptr->value || ptr->option) 192 goto error; 193 break; 194 case UCI_CMD_RENAME: 195 if (!ptr->value || !uci_validate_name(ptr->value)) 196 goto error; 197 break; 198 case UCI_CMD_LIST_ADD: 199 if (!ptr->option) 200 goto error; 201 /* fall through */ 202 case UCI_CMD_LIST_DEL: 203 if (!ptr->option) 204 goto error; 205 } 206 207 return c; 208 209 error: 210 UCI_THROW(ctx, UCI_ERR_INVAL); 211 return 0; 212 } 213 214 static void uci_parse_delta_line(struct uci_context *ctx, struct uci_package *p) 215 { 216 struct uci_ptr ptr; 217 int cmd; 218 219 cmd = uci_parse_delta_tuple(ctx, &ptr); 220 if (strcmp(ptr.package, p->e.name) != 0) 221 goto error; 222 223 if (ctx->flags & UCI_FLAG_SAVED_DELTA) 224 uci_add_delta(ctx, &p->saved_delta, cmd, ptr.section, ptr.option, ptr.value); 225 226 switch(cmd) { 227 case UCI_CMD_REORDER: 228 uci_expand_ptr(ctx, &ptr, true); 229 if (!ptr.s) 230 UCI_THROW(ctx, UCI_ERR_NOTFOUND); 231 UCI_INTERNAL(uci_reorder_section, ctx, ptr.s, strtoul(ptr.value, NULL, 10)); 232 break; 233 case UCI_CMD_RENAME: 234 UCI_INTERNAL(uci_rename, ctx, &ptr); 235 break; 236 case UCI_CMD_REMOVE: 237 UCI_INTERNAL(uci_delete, ctx, &ptr); 238 break; 239 case UCI_CMD_LIST_ADD: 240 UCI_INTERNAL(uci_add_list, ctx, &ptr); 241 break; 242 case UCI_CMD_LIST_DEL: 243 UCI_INTERNAL(uci_del_list, ctx, &ptr); 244 break; 245 case UCI_CMD_ADD: 246 UCI_INTERNAL(uci_set, ctx, &ptr); 247 if (!ptr.option && ptr.s) 248 ptr.s->anonymous = true; 249 break; 250 case UCI_CMD_CHANGE: 251 UCI_INTERNAL(uci_set, ctx, &ptr); 252 break; 253 default: 254 break; 255 } 256 return; 257 error: 258 UCI_THROW(ctx, UCI_ERR_PARSE); 259 } 260 261 /* returns the number of changes that were successfully parsed */ 262 static int uci_parse_delta(struct uci_context *ctx, FILE *stream, struct uci_package *p) 263 { 264 struct uci_parse_context *pctx; 265 volatile int changes = 0; 266 267 /* make sure no memory from previous parse attempts is leaked */ 268 uci_cleanup(ctx); 269 270 pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context)); 271 ctx->pctx = pctx; 272 pctx->file = stream; 273 274 while (!feof(pctx->file)) { 275 pctx->pos = 0; 276 uci_getln(ctx, 0); 277 if (!pctx->buf[0]) 278 continue; 279 280 /* 281 * ignore parse errors in single lines, we want to preserve as much 282 * delta as possible 283 */ 284 UCI_TRAP_SAVE(ctx, error); 285 uci_parse_delta_line(ctx, p); 286 UCI_TRAP_RESTORE(ctx); 287 changes++; 288 error: 289 continue; 290 } 291 292 /* no error happened, we can get rid of the parser context now */ 293 uci_cleanup(ctx); 294 return changes; 295 } 296 297 /* returns the number of changes that were successfully parsed */ 298 static int uci_load_delta_file(struct uci_context *ctx, struct uci_package *p, char *filename, FILE *volatile *f, bool flush) 299 { 300 FILE *volatile stream = NULL; 301 volatile int changes = 0; 302 303 UCI_TRAP_SAVE(ctx, done); 304 stream = uci_open_stream(ctx, filename, NULL, SEEK_SET, flush, false); 305 UCI_TRAP_RESTORE(ctx); 306 307 if (p) 308 changes = uci_parse_delta(ctx, stream, p); 309 310 done: 311 if (f) 312 *f = stream; 313 else 314 uci_close_stream(stream); 315 return changes; 316 } 317 318 /* returns the number of changes that were successfully parsed */ 319 __private int uci_load_delta(struct uci_context *ctx, struct uci_package *p, bool flush) 320 { 321 struct uci_element *e; 322 char *filename = NULL; 323 FILE *volatile f = NULL; 324 volatile int changes = 0; 325 326 if (!p->has_delta) 327 return 0; 328 329 uci_foreach_element(&ctx->delta_path, e) { 330 if ((asprintf(&filename, "%s/%s", e->name, p->e.name) < 0) || !filename) 331 UCI_THROW(ctx, UCI_ERR_MEM); 332 333 changes += uci_load_delta_file(ctx, p, filename, NULL, false); 334 free(filename); 335 } 336 337 if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename) 338 UCI_THROW(ctx, UCI_ERR_MEM); 339 uci_load_delta_file(ctx, NULL, filename, &f, flush); 340 341 if (flush && f && (changes > 0)) { 342 if (ftruncate(fileno(f), 0) < 0) { 343 free(filename); 344 uci_close_stream(f); 345 UCI_THROW(ctx, UCI_ERR_IO); 346 } 347 } 348 349 free(filename); 350 uci_close_stream(f); 351 ctx->err = 0; 352 return changes; 353 } 354 355 static void uci_filter_delta(struct uci_context *ctx, const char *name, const char *section, const char *option) 356 { 357 struct uci_parse_context *pctx; 358 struct uci_element *e, *tmp; 359 struct uci_list list; 360 char *filename = NULL; 361 struct uci_ptr ptr; 362 FILE *f = NULL; 363 364 uci_list_init(&list); 365 uci_alloc_parse_context(ctx); 366 pctx = ctx->pctx; 367 368 if ((asprintf(&filename, "%s/%s", ctx->savedir, name) < 0) || !filename) 369 UCI_THROW(ctx, UCI_ERR_MEM); 370 371 UCI_TRAP_SAVE(ctx, done); 372 f = uci_open_stream(ctx, filename, NULL, SEEK_SET, true, false); 373 pctx->file = f; 374 while (!feof(f)) { 375 enum uci_command c; 376 bool match; 377 378 pctx->pos = 0; 379 uci_getln(ctx, 0); 380 if (!pctx->buf[0]) 381 continue; 382 383 c = uci_parse_delta_tuple(ctx, &ptr); 384 match = true; 385 if (section) { 386 if (!ptr.section || (strcmp(section, ptr.section) != 0)) 387 match = false; 388 } 389 if (match && option) { 390 if (!ptr.option || (strcmp(option, ptr.option) != 0)) 391 match = false; 392 } 393 394 if (!match && ptr.section) { 395 uci_add_delta(ctx, &list, c, 396 ptr.section, ptr.option, ptr.value); 397 } 398 } 399 400 /* rebuild the delta file */ 401 rewind(f); 402 if (ftruncate(fileno(f), 0) < 0) 403 UCI_THROW(ctx, UCI_ERR_IO); 404 uci_foreach_element_safe(&list, tmp, e) { 405 struct uci_delta *h = uci_to_delta(e); 406 uci_delta_save(ctx, f, name, h); 407 uci_free_delta(h); 408 } 409 UCI_TRAP_RESTORE(ctx); 410 411 done: 412 free(filename); 413 uci_close_stream(pctx->file); 414 uci_foreach_element_safe(&list, tmp, e) { 415 uci_free_delta(uci_to_delta(e)); 416 } 417 uci_cleanup(ctx); 418 } 419 420 int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr) 421 { 422 char *volatile package = NULL; 423 char *volatile section = NULL; 424 char *volatile option = NULL; 425 426 UCI_HANDLE_ERR(ctx); 427 uci_expand_ptr(ctx, ptr, false); 428 UCI_ASSERT(ctx, ptr->p->has_delta); 429 430 /* 431 * - flush unwritten changes 432 * - save the package name 433 * - unload the package 434 * - filter the delta 435 * - reload the package 436 */ 437 UCI_TRAP_SAVE(ctx, error); 438 UCI_INTERNAL(uci_save, ctx, ptr->p); 439 440 /* NB: need to clone package, section and option names, 441 * as they may get freed on uci_free_package() */ 442 package = uci_strdup(ctx, ptr->p->e.name); 443 if (ptr->section) 444 section = uci_strdup(ctx, ptr->section); 445 if (ptr->option) 446 option = uci_strdup(ctx, ptr->option); 447 448 uci_free_package(&ptr->p); 449 uci_filter_delta(ctx, package, section, option); 450 451 UCI_INTERNAL(uci_load, ctx, package, &ptr->p); 452 UCI_TRAP_RESTORE(ctx); 453 ctx->err = 0; 454 455 error: 456 free(package); 457 free(section); 458 free(option); 459 if (ctx->err) 460 UCI_THROW(ctx, ctx->err); 461 return 0; 462 } 463 464 int uci_save(struct uci_context *ctx, struct uci_package *p) 465 { 466 FILE *volatile f = NULL; 467 char *filename = NULL; 468 struct uci_element *e, *tmp; 469 struct stat statbuf; 470 471 UCI_HANDLE_ERR(ctx); 472 UCI_ASSERT(ctx, p != NULL); 473 474 /* 475 * if the config file was outside of the /etc/config path, 476 * don't save the delta to a file, update the real file 477 * directly. 478 * does not modify the uci_package pointer 479 */ 480 if (!p->has_delta) 481 return uci_commit(ctx, &p, false); 482 483 if (uci_list_empty(&p->delta)) 484 return 0; 485 486 if (stat(ctx->savedir, &statbuf) < 0) { 487 if (stat(ctx->confdir, &statbuf) == 0) { 488 mkdir(ctx->savedir, statbuf.st_mode); 489 } else { 490 mkdir(ctx->savedir, UCI_DIRMODE); 491 } 492 } else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { 493 UCI_THROW(ctx, UCI_ERR_IO); 494 } 495 496 if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename) 497 UCI_THROW(ctx, UCI_ERR_MEM); 498 499 ctx->err = 0; 500 UCI_TRAP_SAVE(ctx, done); 501 f = uci_open_stream(ctx, filename, NULL, SEEK_END, true, true); 502 UCI_TRAP_RESTORE(ctx); 503 504 uci_foreach_element_safe(&p->delta, tmp, e) { 505 struct uci_delta *h = uci_to_delta(e); 506 uci_delta_save(ctx, f, p->e.name, h); 507 uci_free_delta(h); 508 } 509 510 done: 511 uci_close_stream(f); 512 free(filename); 513 if (ctx->err) 514 UCI_THROW(ctx, ctx->err); 515 516 return 0; 517 } 518 519 520
This page was automatically generated by LXR 0.3.1. • OpenWrt