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, bool *delete, bool *rename)
97 } else if (**buf == '@') {
103 UCI_INTERNAL(uci_parse_tuple, ctx, *buf, package, section, option, value);
105 static void uci_parse_history_line(struct uci_context *ctx, struct uci_package *p, char *buf)
109 char *package = NULL;
110 char *section = NULL;
114 uci_parse_history_tuple(ctx, &buf, &package, §ion, &option, &value, &delete, &rename);
115 if (!package || (strcmp(package, p->e.name) != 0))
117 if (!uci_validate_name(section))
119 if (option && !uci_validate_name(option))
121 if (rename && !uci_validate_str(value, (option || delete)))
124 if (ctx->flags & UCI_FLAG_SAVED_HISTORY) {
127 /* NB: no distinction between CMD_CHANGE and CMD_ADD possible at this point */
129 cmd = UCI_CMD_REMOVE;
131 cmd = UCI_CMD_RENAME;
133 cmd = UCI_CMD_CHANGE;
135 uci_add_history(ctx, &p->saved_history, cmd, section, option, value);
139 UCI_INTERNAL(uci_rename, ctx, p, section, option, value);
141 UCI_INTERNAL(uci_delete, ctx, p, section, option);
143 UCI_INTERNAL(uci_set, ctx, p, section, option, value);
147 UCI_THROW(ctx, UCI_ERR_PARSE);
150 /* returns the number of changes that were successfully parsed */
151 static int uci_parse_history(struct uci_context *ctx, FILE *stream, struct uci_package *p)
153 struct uci_parse_context *pctx;
156 /* make sure no memory from previous parse attempts is leaked */
157 ctx->internal = true;
160 pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context));
164 while (!feof(pctx->file)) {
170 * ignore parse errors in single lines, we want to preserve as much
171 * history as possible
173 UCI_TRAP_SAVE(ctx, error);
174 uci_parse_history_line(ctx, p, pctx->buf);
175 UCI_TRAP_RESTORE(ctx);
181 /* no error happened, we can get rid of the parser context now */
182 ctx->internal = true;
187 /* returns the number of changes that were successfully parsed */
188 static int uci_load_history_file(struct uci_context *ctx, struct uci_package *p, char *filename, FILE **f, bool flush)
193 UCI_TRAP_SAVE(ctx, done);
194 stream = uci_open_stream(ctx, filename, SEEK_SET, flush, false);
196 changes = uci_parse_history(ctx, stream, p);
197 UCI_TRAP_RESTORE(ctx);
202 uci_close_stream(stream);
206 /* returns the number of changes that were successfully parsed */
207 static int uci_load_history(struct uci_context *ctx, struct uci_package *p, bool flush)
209 struct uci_element *e;
210 char *filename = NULL;
217 uci_foreach_element(&ctx->history_path, e) {
218 if ((asprintf(&filename, "%s/%s", e->name, p->e.name) < 0) || !filename)
219 UCI_THROW(ctx, UCI_ERR_MEM);
221 uci_load_history_file(ctx, p, filename, NULL, false);
225 if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
226 UCI_THROW(ctx, UCI_ERR_MEM);
228 changes = uci_load_history_file(ctx, p, filename, &f, flush);
229 if (flush && f && (changes > 0)) {
231 ftruncate(fileno(f), 0);
240 static void uci_filter_history(struct uci_context *ctx, const char *name, char *section, char *option)
242 struct uci_parse_context *pctx;
243 struct uci_element *e, *tmp;
244 struct uci_list list;
245 char *filename = NULL;
252 uci_list_init(&list);
253 uci_alloc_parse_context(ctx);
256 if ((asprintf(&filename, "%s/%s", ctx->savedir, name) < 0) || !filename)
257 UCI_THROW(ctx, UCI_ERR_MEM);
259 UCI_TRAP_SAVE(ctx, done);
260 f = uci_open_stream(ctx, filename, SEEK_SET, true, false);
263 struct uci_element *e;
271 /* NB: need to allocate the element before the call to
272 * uci_parse_history_tuple, otherwise the original string
273 * gets modified before it is saved */
274 e = uci_alloc_generic(ctx, UCI_TYPE_HISTORY, pctx->buf, sizeof(struct uci_element));
275 uci_list_add(&list, &e->list);
277 uci_parse_history_tuple(ctx, &buf, &p, &s, &o, &v, NULL, NULL);
279 if (!s || (strcmp(section, s) != 0))
283 if (!o || (strcmp(option, o) != 0))
286 /* match, drop this element again */
290 /* rebuild the history file */
292 ftruncate(fileno(f), 0);
293 uci_foreach_element_safe(&list, tmp, e) {
294 fprintf(f, "%s\n", e->name);
297 UCI_TRAP_RESTORE(ctx);
303 uci_foreach_element_safe(&list, tmp, e) {
306 ctx->internal = true;
310 int uci_revert(struct uci_context *ctx, struct uci_package **pkg, char *section, char *option)
312 struct uci_package *p;
316 UCI_ASSERT(ctx, pkg != NULL);
318 UCI_ASSERT(ctx, p != NULL);
319 UCI_ASSERT(ctx, p->confdir);
322 * - flush unwritten changes
323 * - save the package name
324 * - unload the package
325 * - filter the history
326 * - reload the package
328 UCI_TRAP_SAVE(ctx, error);
329 UCI_INTERNAL(uci_save, ctx, p);
330 name = uci_strdup(ctx, p->e.name);
333 uci_free_package(&p);
334 uci_filter_history(ctx, name, section, option);
336 UCI_INTERNAL(uci_load, ctx, name, &p);
337 UCI_TRAP_RESTORE(ctx);
344 UCI_THROW(ctx, ctx->errno);
348 int uci_save(struct uci_context *ctx, struct uci_package *p)
351 char *filename = NULL;
352 struct uci_element *e, *tmp;
355 UCI_ASSERT(ctx, p != NULL);
358 * if the config file was outside of the /etc/config path,
359 * don't save the history to a file, update the real file
361 * does not modify the uci_package pointer
364 return uci_commit(ctx, &p, false);
366 if (uci_list_empty(&p->history))
369 if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
370 UCI_THROW(ctx, UCI_ERR_MEM);
373 UCI_TRAP_SAVE(ctx, done);
374 f = uci_open_stream(ctx, filename, SEEK_END, true, true);
375 UCI_TRAP_RESTORE(ctx);
377 uci_foreach_element_safe(&p->history, tmp, e) {
378 struct uci_history *h = uci_to_history(e);
380 if (h->cmd == UCI_CMD_REMOVE)
382 else if (h->cmd == UCI_CMD_RENAME)
385 fprintf(f, "%s.%s", p->e.name, h->section);
387 fprintf(f, ".%s", e->name);
389 if (h->cmd == UCI_CMD_REMOVE)
392 fprintf(f, "=%s\n", h->value);
401 UCI_THROW(ctx, ctx->errno);