2 * libuci - Library for the Unified Configuration Interface
3 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
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
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.
16 * This file contains the code for handling uci config history files
20 #include <sys/types.h>
29 /* record a change that was done to a package */
31 uci_add_history(struct uci_context *ctx, struct uci_list *list, int cmd, char *section, char *option, char *value)
33 struct uci_history *h;
34 int size = strlen(section) + 1;
38 size += strlen(value) + 1;
40 h = uci_alloc_element(ctx, history, option, size);
43 h->section = strcpy(ptr, section);
45 ptr += strlen(ptr) + 1;
46 h->value = strcpy(ptr, value);
48 uci_list_add(list, &h->e.list);
52 uci_free_history(struct uci_history *h)
56 if ((h->section != NULL) &&
57 (h->section != uci_dataptr(h))) {
61 uci_free_element(&h->e);
65 int uci_set_savedir(struct uci_context *ctx, const char *dir)
70 UCI_ASSERT(ctx, dir != NULL);
72 sdir = uci_strdup(ctx, dir);
73 if (ctx->savedir != uci_savedir)
79 int uci_add_history_path(struct uci_context *ctx, const char *dir)
81 struct uci_element *e;
84 UCI_ASSERT(ctx, dir != NULL);
85 e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element));
86 uci_list_add(&ctx->history_path, &e->list);
91 static inline void uci_parse_history_tuple(struct uci_context *ctx, char **buf, char **package, char **section, char **option, char **value, int *cmd)
93 int c = UCI_CMD_CHANGE;
98 } else if (**buf == '@') {
101 } else if (**buf == '+') {
102 /* UCI_CMD_ADD is used for anonymous sections */
109 UCI_INTERNAL(uci_parse_tuple, ctx, *buf, package, section, option, value);
111 UCI_THROW(ctx, UCI_ERR_PARSE);
115 static void uci_parse_history_line(struct uci_context *ctx, struct uci_package *p, char *buf)
117 struct uci_element *e = NULL;
120 char *package = NULL;
121 char *section = NULL;
126 uci_parse_history_tuple(ctx, &buf, &package, §ion, &option, &value, &cmd);
127 if (!package || (strcmp(package, p->e.name) != 0))
129 if (!uci_validate_name(section))
131 if (option && !uci_validate_name(option))
133 if (rename && !uci_validate_str(value, (option || delete)))
136 if (ctx->flags & UCI_FLAG_SAVED_HISTORY)
137 uci_add_history(ctx, &p->saved_history, cmd, section, option, value);
141 UCI_INTERNAL(uci_rename, ctx, p, section, option, value);
144 UCI_INTERNAL(uci_delete, ctx, p, section, option);
148 UCI_INTERNAL(uci_set, ctx, p, section, option, value, &e);
149 if (!option && e && (cmd == UCI_CMD_ADD))
150 uci_to_section(e)->anonymous = true;
155 UCI_THROW(ctx, UCI_ERR_PARSE);
158 /* returns the number of changes that were successfully parsed */
159 static int uci_parse_history(struct uci_context *ctx, FILE *stream, struct uci_package *p)
161 struct uci_parse_context *pctx;
164 /* make sure no memory from previous parse attempts is leaked */
167 pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context));
171 while (!feof(pctx->file)) {
177 * ignore parse errors in single lines, we want to preserve as much
178 * history as possible
180 UCI_TRAP_SAVE(ctx, error);
181 uci_parse_history_line(ctx, p, pctx->buf);
182 UCI_TRAP_RESTORE(ctx);
188 /* no error happened, we can get rid of the parser context now */
193 /* returns the number of changes that were successfully parsed */
194 static int uci_load_history_file(struct uci_context *ctx, struct uci_package *p, char *filename, FILE **f, bool flush)
199 UCI_TRAP_SAVE(ctx, done);
200 stream = uci_open_stream(ctx, filename, SEEK_SET, flush, false);
202 changes = uci_parse_history(ctx, stream, p);
203 UCI_TRAP_RESTORE(ctx);
208 uci_close_stream(stream);
212 /* returns the number of changes that were successfully parsed */
213 static int uci_load_history(struct uci_context *ctx, struct uci_package *p, bool flush)
215 struct uci_element *e;
216 char *filename = NULL;
223 uci_foreach_element(&ctx->history_path, e) {
224 if ((asprintf(&filename, "%s/%s", e->name, p->e.name) < 0) || !filename)
225 UCI_THROW(ctx, UCI_ERR_MEM);
227 uci_load_history_file(ctx, p, filename, NULL, false);
231 if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
232 UCI_THROW(ctx, UCI_ERR_MEM);
234 changes = uci_load_history_file(ctx, p, filename, &f, flush);
235 if (flush && f && (changes > 0)) {
237 ftruncate(fileno(f), 0);
246 static void uci_filter_history(struct uci_context *ctx, const char *name, char *section, char *option)
248 struct uci_parse_context *pctx;
249 struct uci_element *e, *tmp;
250 struct uci_list list;
251 char *filename = NULL;
258 uci_list_init(&list);
259 uci_alloc_parse_context(ctx);
262 if ((asprintf(&filename, "%s/%s", ctx->savedir, name) < 0) || !filename)
263 UCI_THROW(ctx, UCI_ERR_MEM);
265 UCI_TRAP_SAVE(ctx, done);
266 f = uci_open_stream(ctx, filename, SEEK_SET, true, false);
269 struct uci_element *e;
277 /* NB: need to allocate the element before the call to
278 * uci_parse_history_tuple, otherwise the original string
279 * gets modified before it is saved */
280 e = uci_alloc_generic(ctx, UCI_TYPE_HISTORY, pctx->buf, sizeof(struct uci_element));
281 uci_list_add(&list, &e->list);
283 uci_parse_history_tuple(ctx, &buf, &p, &s, &o, &v, NULL);
285 if (!s || (strcmp(section, s) != 0))
289 if (!o || (strcmp(option, o) != 0))
292 /* match, drop this element again */
296 /* rebuild the history file */
298 ftruncate(fileno(f), 0);
299 uci_foreach_element_safe(&list, tmp, e) {
300 fprintf(f, "%s\n", e->name);
303 UCI_TRAP_RESTORE(ctx);
309 uci_foreach_element_safe(&list, tmp, e) {
315 int uci_revert(struct uci_context *ctx, struct uci_package **pkg, char *section, char *option)
317 struct uci_package *p;
321 UCI_ASSERT(ctx, pkg != NULL);
323 UCI_ASSERT(ctx, p != NULL);
324 UCI_ASSERT(ctx, p->has_history);
327 * - flush unwritten changes
328 * - save the package name
329 * - unload the package
330 * - filter the history
331 * - reload the package
333 UCI_TRAP_SAVE(ctx, error);
334 UCI_INTERNAL(uci_save, ctx, p);
335 name = uci_strdup(ctx, p->e.name);
338 uci_free_package(&p);
339 uci_filter_history(ctx, name, section, option);
341 UCI_INTERNAL(uci_load, ctx, name, &p);
342 UCI_TRAP_RESTORE(ctx);
349 UCI_THROW(ctx, ctx->err);
353 int uci_save(struct uci_context *ctx, struct uci_package *p)
356 char *filename = NULL;
357 struct uci_element *e, *tmp;
361 UCI_ASSERT(ctx, p != NULL);
364 * if the config file was outside of the /etc/config path,
365 * don't save the history to a file, update the real file
367 * does not modify the uci_package pointer
370 return uci_commit(ctx, &p, false);
372 if (uci_list_empty(&p->history))
375 if (stat(ctx->savedir, &statbuf) < 0)
376 mkdir(ctx->savedir, UCI_DIRMODE);
377 else if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
378 UCI_THROW(ctx, UCI_ERR_IO);
380 if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
381 UCI_THROW(ctx, UCI_ERR_MEM);
384 UCI_TRAP_SAVE(ctx, done);
385 f = uci_open_stream(ctx, filename, SEEK_END, true, true);
386 UCI_TRAP_RESTORE(ctx);
388 uci_foreach_element_safe(&p->history, tmp, e) {
389 struct uci_history *h = uci_to_history(e);
405 fprintf(f, "%s.%s", p->e.name, h->section);
407 fprintf(f, ".%s", e->name);
409 if (h->cmd == UCI_CMD_REMOVE)
412 fprintf(f, "=%s\n", h->value);
421 UCI_THROW(ctx, ctx->err);