bump library version number
[project/uci.git] / 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 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/file.h>
22 #include <stdbool.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <ctype.h>
27
28 /*
29  * Clean up all extra memory used by the parser and exporter
30  */
31 static void uci_file_cleanup(struct uci_context *ctx)
32 {
33         struct uci_parse_context *pctx;
34
35         if (ctx->buf) {
36                 free(ctx->buf);
37                 ctx->buf = NULL;
38                 ctx->bufsz = 0;
39         }
40
41         pctx = ctx->pctx;
42         if (!pctx)
43                 return;
44
45         ctx->pctx = NULL;
46         if (pctx->package)
47                 uci_free_package(&pctx->package);
48
49         if (pctx->buf)
50                 free(pctx->buf);
51
52         free(pctx);
53 }
54
55
56 /*
57  * verify that the end of the line or command is reached.
58  * throw an error if extra arguments are given on the command line
59  */
60 static void assert_eol(struct uci_context *ctx, char **str)
61 {
62         char *tmp;
63
64         tmp = next_arg(ctx, str, false, false);
65         if (*tmp && (ctx->flags & UCI_FLAG_STRICT))
66                 uci_parse_error(ctx, *str, "too many arguments");
67 }
68
69 /* 
70  * switch to a different config, either triggered by uci_load, or by a
71  * 'package <...>' statement in the import file
72  */
73 static void uci_switch_config(struct uci_context *ctx)
74 {
75         struct uci_parse_context *pctx;
76         struct uci_element *e;
77         const char *name;
78
79         pctx = ctx->pctx;
80         name = pctx->name;
81
82         /* add the last config to main config file list */
83         if (pctx->package) {
84                 uci_list_add(&ctx->root, &pctx->package->e.list);
85
86                 pctx->package = NULL;
87                 pctx->section = NULL;
88         }
89
90         if (!name)
91                 return;
92
93         /* 
94          * if an older config under the same name exists, unload it
95          * ignore errors here, e.g. if the config was not found
96          */
97         e = uci_lookup_list(ctx, &ctx->root, name);
98         if (e)
99                 UCI_THROW(ctx, UCI_ERR_DUPLICATE);
100         pctx->package = uci_alloc_package(ctx, name);
101 }
102
103 /*
104  * parse the 'package' uci command (next config package)
105  */
106 static void uci_parse_package(struct uci_context *ctx, char **str, bool single)
107 {
108         char *name = NULL;
109
110         /* command string null-terminated by strtok */
111         *str += strlen(*str) + 1;
112
113         name = next_arg(ctx, str, true, true);
114         assert_eol(ctx, str);
115         if (single)
116                 return;
117
118         ctx->pctx->name = name;
119         uci_switch_config(ctx);
120 }
121
122 /*
123  * parse the 'config' uci command (open a section)
124  */
125 static void uci_parse_config(struct uci_context *ctx, char **str)
126 {
127         struct uci_parse_context *pctx = ctx->pctx;
128         char *name = NULL;
129         char *type = NULL;
130
131         uci_fixup_section(ctx, ctx->pctx->section);
132         if (!ctx->pctx->package) {
133                 if (!ctx->pctx->name)
134                         uci_parse_error(ctx, *str, "attempting to import a file without a package name");
135
136                 uci_switch_config(ctx);
137         }
138
139         /* command string null-terminated by strtok */
140         *str += strlen(*str) + 1;
141
142         type = next_arg(ctx, str, true, false);
143         if (!uci_validate_str(type, false))
144                 uci_parse_error(ctx, type, "invalid character in field");
145         name = next_arg(ctx, str, false, true);
146         assert_eol(ctx, str);
147
148         if (pctx->merge) {
149                 UCI_TRAP_SAVE(ctx, error);
150                 uci_set(ctx, pctx->package, name, NULL, type, NULL);
151                 UCI_TRAP_RESTORE(ctx);
152                 return;
153 error:
154                 UCI_THROW(ctx, ctx->errno);
155         } else
156                 pctx->section = uci_alloc_section(pctx->package, type, name);
157 }
158
159 /*
160  * parse the 'option' uci command (open a value)
161  */
162 static void uci_parse_option(struct uci_context *ctx, char **str)
163 {
164         struct uci_parse_context *pctx = ctx->pctx;
165         char *name = NULL;
166         char *value = NULL;
167
168         if (!pctx->section)
169                 uci_parse_error(ctx, *str, "option command found before the first section");
170
171         /* command string null-terminated by strtok */
172         *str += strlen(*str) + 1;
173
174         name = next_arg(ctx, str, true, true);
175         value = next_arg(ctx, str, false, false);
176         assert_eol(ctx, str);
177
178         if (pctx->merge) {
179                 UCI_TRAP_SAVE(ctx, error);
180                 uci_set(ctx, pctx->package, pctx->section->e.name, name, value, NULL);
181                 UCI_TRAP_RESTORE(ctx);
182                 return;
183 error:
184                 UCI_THROW(ctx, ctx->errno);
185         } else
186                 uci_alloc_option(pctx->section, name, value);
187 }
188
189
190 /*
191  * parse a complete input line, split up combined commands by ';'
192  */
193 static void uci_parse_line(struct uci_context *ctx, bool single)
194 {
195         struct uci_parse_context *pctx = ctx->pctx;
196         char *word, *brk = NULL;
197
198         for (word = strtok_r(pctx->buf, ";", &brk);
199                  word;
200                  word = strtok_r(NULL, ";", &brk)) {
201
202                 char *pbrk = NULL;
203                 word = strtok_r(word, " \t", &pbrk);
204
205                 if (!word)
206                         continue;
207
208                 switch(word[0]) {
209                         case '#':
210                                 return;
211                         case 'p':
212                                 if ((word[1] == 0) || !strcmp(word + 1, "ackage"))
213                                         uci_parse_package(ctx, &word, single);
214                                 break;
215                         case 'c':
216                                 if ((word[1] == 0) || !strcmp(word + 1, "onfig"))
217                                         uci_parse_config(ctx, &word);
218                                 break;
219                         case 'o':
220                                 if ((word[1] == 0) || !strcmp(word + 1, "ption"))
221                                         uci_parse_option(ctx, &word);
222                                 break;
223                         default:
224                                 uci_parse_error(ctx, word, "unterminated command");
225                                 break;
226                 }
227         }
228 }
229
230 /* max number of characters that escaping adds to the string */
231 #define UCI_QUOTE_ESCAPE        "'\\''"
232
233 /*
234  * escape an uci string for export
235  */
236 static char *uci_escape(struct uci_context *ctx, char *str)
237 {
238         char *s, *p;
239         int pos = 0;
240
241         if (!ctx->buf) {
242                 ctx->bufsz = LINEBUF;
243                 ctx->buf = malloc(LINEBUF);
244         }
245
246         s = str;
247         p = strchr(str, '\'');
248         if (!p)
249                 return str;
250
251         do {
252                 int len = p - s;
253                 if (len > 0) {
254                         if (p + sizeof(UCI_QUOTE_ESCAPE) - str >= ctx->bufsz) {
255                                 ctx->bufsz *= 2;
256                                 ctx->buf = realloc(ctx->buf, ctx->bufsz);
257                                 if (!ctx->buf)
258                                         UCI_THROW(ctx, UCI_ERR_MEM);
259                         }
260                         memcpy(&ctx->buf[pos], s, len);
261                         pos += len;
262                 }
263                 strcpy(&ctx->buf[pos], UCI_QUOTE_ESCAPE);
264                 pos += sizeof(UCI_QUOTE_ESCAPE);
265                 s = p + 1;
266         } while ((p = strchr(s, '\'')));
267
268         return ctx->buf;
269 }
270
271
272 /*
273  * export a single config package to a file stream
274  */
275 static void uci_export_package(struct uci_package *p, FILE *stream, bool header)
276 {
277         struct uci_context *ctx = p->ctx;
278         struct uci_element *s, *o;
279
280         if (header)
281                 fprintf(stream, "package '%s'\n", uci_escape(ctx, p->e.name));
282         uci_foreach_element(&p->sections, s) {
283                 struct uci_section *sec = uci_to_section(s);
284                 fprintf(stream, "\nconfig '%s'", uci_escape(ctx, sec->type));
285                 if (!sec->anonymous || (ctx->flags & UCI_FLAG_EXPORT_NAME))
286                         fprintf(stream, " '%s'", uci_escape(ctx, sec->e.name));
287                 fprintf(stream, "\n");
288                 uci_foreach_element(&sec->options, o) {
289                         struct uci_option *opt = uci_to_option(o);
290                         fprintf(stream, "\toption '%s'", uci_escape(ctx, opt->e.name));
291                         fprintf(stream, " '%s'\n", uci_escape(ctx, opt->value));
292                 }
293         }
294         fprintf(stream, "\n");
295 }
296
297 int uci_export(struct uci_context *ctx, FILE *stream, struct uci_package *package, bool header)
298 {
299         struct uci_element *e;
300
301         UCI_HANDLE_ERR(ctx);
302         UCI_ASSERT(ctx, stream != NULL);
303
304         if (package)
305                 uci_export_package(package, stream, header);
306         else {
307                 uci_foreach_element(&ctx->root, e) {
308                         uci_export_package(uci_to_package(e), stream, header);
309                 }
310         }
311
312         return 0;
313 }
314
315 int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct uci_package **package, bool single)
316 {
317         struct uci_parse_context *pctx;
318         UCI_HANDLE_ERR(ctx);
319
320         /* make sure no memory from previous parse attempts is leaked */
321         uci_file_cleanup(ctx);
322
323         uci_alloc_parse_context(ctx);
324         pctx = ctx->pctx;
325         pctx->file = stream;
326         if (*package && single) {
327                 pctx->package = *package;
328                 pctx->merge = true;
329         }
330
331         /*
332          * If 'name' was supplied, assume that the supplied stream does not contain
333          * the appropriate 'package <name>' string to specify the config name
334          * NB: the config file can still override the package name
335          */
336         if (name) {
337                 UCI_ASSERT(ctx, uci_validate_name(name));
338                 pctx->name = name;
339         }
340
341         while (!feof(pctx->file)) {
342                 uci_getln(ctx, 0);
343                 UCI_TRAP_SAVE(ctx, error);
344                 if (pctx->buf[0])
345                         uci_parse_line(ctx, single);
346                 UCI_TRAP_RESTORE(ctx);
347                 continue;
348 error:
349                 if (ctx->flags & UCI_FLAG_PERROR)
350                         uci_perror(ctx, NULL);
351                 if ((ctx->errno != UCI_ERR_PARSE) ||
352                         (ctx->flags & UCI_FLAG_STRICT))
353                         UCI_THROW(ctx, ctx->errno);
354         }
355
356         uci_fixup_section(ctx, ctx->pctx->section);
357         if (package)
358                 *package = pctx->package;
359         if (pctx->merge)
360                 pctx->package = NULL;
361
362         pctx->name = NULL;
363         uci_switch_config(ctx);
364
365         /* no error happened, we can get rid of the parser context now */
366         uci_file_cleanup(ctx);
367
368         return 0;
369 }
370
371
372 static char *uci_config_path(struct uci_context *ctx, const char *name)
373 {
374         char *filename;
375
376         UCI_ASSERT(ctx, uci_validate_name(name));
377         filename = uci_malloc(ctx, strlen(name) + strlen(ctx->confdir) + 2);
378         sprintf(filename, "%s/%s", ctx->confdir, name);
379
380         return filename;
381 }
382
383 int uci_load(struct uci_context *ctx, const char *name, struct uci_package **package)
384 {
385         char *filename;
386         bool confdir;
387         FILE *file = NULL;
388
389         UCI_HANDLE_ERR(ctx);
390
391         switch (name[0]) {
392         case '.':
393                 /* relative path outside of /etc/config */
394                 if (name[1] != '/')
395                         UCI_THROW(ctx, UCI_ERR_NOTFOUND);
396                 /* fall through */
397         case '/':
398                 /* absolute path outside of /etc/config */
399                 filename = uci_strdup(ctx, name);
400                 name = strrchr(name, '/') + 1;
401                 confdir = false;
402                 break;
403         default:
404                 /* config in /etc/config */
405                 filename = uci_config_path(ctx, name);
406                 confdir = true;
407                 break;
408         }
409
410         file = uci_open_stream(ctx, filename, SEEK_SET, false, false);
411         ctx->errno = 0;
412         UCI_TRAP_SAVE(ctx, done);
413         UCI_INTERNAL(uci_import, ctx, file, name, package, true);
414         UCI_TRAP_RESTORE(ctx);
415
416         if (*package) {
417                 (*package)->path = filename;
418                 (*package)->confdir = confdir;
419                 uci_load_history(ctx, *package, false);
420         }
421
422 done:
423         uci_close_stream(file);
424         return ctx->errno;
425 }
426
427 int uci_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite)
428 {
429         struct uci_package *p;
430         FILE *f = NULL;
431         char *name = NULL;
432         char *path = NULL;
433
434         UCI_HANDLE_ERR(ctx);
435         UCI_ASSERT(ctx, package != NULL);
436         p = *package;
437
438         UCI_ASSERT(ctx, p != NULL);
439         if (!p->path) {
440                 if (overwrite)
441                         p->path = uci_config_path(ctx, p->e.name);
442                 else
443                         UCI_THROW(ctx, UCI_ERR_INVAL);
444         }
445
446
447         /* open the config file for writing now, so that it is locked */
448         f = uci_open_stream(ctx, p->path, SEEK_SET, true, true);
449
450         /* flush unsaved changes and reload from history file */
451         UCI_TRAP_SAVE(ctx, done);
452         if (p->confdir) {
453                 if (!overwrite) {
454                         name = uci_strdup(ctx, p->e.name);
455                         path = uci_strdup(ctx, p->path);
456                         /* dump our own changes to the history file */
457                         if (!uci_list_empty(&p->history))
458                                 UCI_INTERNAL(uci_save, ctx, p);
459
460                         /* 
461                          * other processes might have modified the config 
462                          * as well. dump and reload 
463                          */
464                         uci_free_package(&p);
465                         uci_file_cleanup(ctx);
466                         UCI_INTERNAL(uci_import, ctx, f, name, &p, true);
467
468                         p->path = path;
469                         p->confdir = true;
470                         *package = p;
471
472                         /* freed together with the uci_package */
473                         path = NULL;
474
475                         /* check for updated history, flush */
476                         if (!uci_load_history(ctx, p, true))
477                                 goto done;
478                 } else {
479                         /* flush history */
480                         if (!uci_load_history(ctx, NULL, true))
481                                 goto done;
482                 }
483         }
484
485         rewind(f);
486         ftruncate(fileno(f), 0);
487
488         uci_export(ctx, f, p, false);
489         UCI_TRAP_RESTORE(ctx);
490
491 done:
492         if (name)
493                 free(name);
494         if (path)
495                 free(path);
496         uci_close_stream(f);
497         if (ctx->errno)
498                 UCI_THROW(ctx, ctx->errno);
499
500         return 0;
501 }
502
503
504 /* 
505  * This function returns the filename by returning the string
506  * after the last '/' character. By checking for a non-'\0'
507  * character afterwards, directories are ignored (glob marks
508  * those with a trailing '/'
509  */
510 static inline char *get_filename(char *path)
511 {
512         char *p;
513
514         p = strrchr(path, '/');
515         p++;
516         if (!*p)
517                 return NULL;
518         return p;
519 }
520
521 int uci_list_configs(struct uci_context *ctx, char ***list)
522 {
523         char **configs;
524         glob_t globbuf;
525         int size, i;
526         char *buf;
527         char *dir;
528
529         UCI_HANDLE_ERR(ctx);
530
531         dir = uci_malloc(ctx, strlen(ctx->confdir) + 1 + sizeof("/*"));
532         sprintf(dir, "%s/*", ctx->confdir);
533         if (glob(dir, GLOB_MARK, NULL, &globbuf) != 0)
534                 UCI_THROW(ctx, UCI_ERR_NOTFOUND);
535
536         size = sizeof(char *) * (globbuf.gl_pathc + 1);
537         for(i = 0; i < globbuf.gl_pathc; i++) {
538                 char *p;
539
540                 p = get_filename(globbuf.gl_pathv[i]);
541                 if (!p)
542                         continue;
543
544                 size += strlen(p) + 1;
545         }
546
547         configs = uci_malloc(ctx, size);
548         buf = (char *) &configs[globbuf.gl_pathc + 1];
549         for(i = 0; i < globbuf.gl_pathc; i++) {
550                 char *p;
551
552                 p = get_filename(globbuf.gl_pathv[i]);
553                 if (!p)
554                         continue;
555
556                 configs[i] = buf;
557                 strcpy(buf, p);
558                 buf += strlen(buf) + 1;
559         }
560         *list = configs;
561         free(dir);
562
563         return 0;
564 }
565