• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/uci/file.c

  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 = uci_to_section(ptr.last);
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