From: Felix Fietkau Date: Wed, 30 Jan 2008 04:13:59 +0000 (+0100) Subject: proper commit support, better debugging X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fuci.git;a=commitdiff_plain;h=71a0126dff93c4e7f81b9fe7d3629b4b706bd162 proper commit support, better debugging --- diff --git a/cli.c b/cli.c index 3d25b7c..1c31083 100644 --- a/cli.c +++ b/cli.c @@ -127,6 +127,35 @@ static int uci_do_export(int argc, char **argv) return 0; } +static int uci_do_commit(int argc, char **argv) +{ + char **configs = NULL; + char **p; + + if ((uci_list_configs(ctx, &configs) != UCI_OK) || !configs) { + uci_perror(ctx, appname); + return 1; + } + + for (p = configs; *p; p++) { + if ((argc < 2) || !strcmp(argv[1], *p)) { + struct uci_package *package = NULL; + int ret; + + if (uci_load(ctx, *p, &package) != UCI_OK) { + uci_perror(ctx, appname); + continue; + } + if (uci_commit(ctx, &package) != UCI_OK) + uci_perror(ctx, appname); + + uci_unload(ctx, package); + } + } + return 0; +} + + static int uci_do_cmd(int cmd, int argc, char **argv) { char *package = NULL; @@ -147,11 +176,11 @@ static int uci_do_cmd(int cmd, int argc, char **argv) return 1; } - if (uci_lookup(ctx, &e, p, section, option) != UCI_OK) - return 1; - switch(cmd) { case CMD_GET: + if (uci_lookup(ctx, &e, p, section, option) != UCI_OK) + return 1; + switch(e->type) { case UCI_TYPE_SECTION: value = uci_to_section(e)->type; @@ -201,6 +230,8 @@ static int uci_cmd(int argc, char **argv) return uci_show(argc, argv); if (!strcasecmp(argv[0], "export")) return uci_do_export(argc, argv); + if (!strcasecmp(argv[0], "commit")) + return uci_do_commit(argc, argv); if (!strcasecmp(argv[0], "get")) cmd = CMD_GET; diff --git a/err.h b/err.h index bc83863..358cadf 100644 --- a/err.h +++ b/err.h @@ -41,6 +41,7 @@ * and UCI_TRAP_RESTORE. */ #define UCI_HANDLE_ERR(ctx) do { \ + DPRINTF("ENTER: %s\n", __func__); \ int __val = 0; \ if (!ctx) \ return UCI_ERR_INVAL; \ diff --git a/file.c b/file.c index 47d9c44..980189f 100644 --- a/file.c +++ b/file.c @@ -93,7 +93,7 @@ static void uci_file_cleanup(struct uci_context *ctx) ctx->pctx = NULL; if (pctx->package) - uci_free_package(pctx->package); + uci_free_package(&pctx->package); if (pctx->buf) free(pctx->buf); @@ -502,7 +502,6 @@ int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct u while (!feof(pctx->file)) { uci_getln(ctx, 0); - UCI_TRAP_SAVE(ctx, error); if (pctx->buf[0]) uci_parse_line(ctx, single); @@ -534,33 +533,34 @@ error: * note: when opening for write and seeking to the beginning of * the stream, truncate the file */ -static FILE *uci_open_stream(struct uci_context *ctx, const char *filename, int pos, bool write) +static FILE *uci_open_stream(struct uci_context *ctx, const char *filename, int pos, bool write, bool create) { struct stat statbuf; FILE *file = NULL; int fd, ret; + int mode = (write ? O_RDWR : O_RDONLY); + + if (create) + mode |= O_CREAT; if (!write && ((stat(filename, &statbuf) < 0) || ((statbuf.st_mode & S_IFMT) != S_IFREG))) { UCI_THROW(ctx, UCI_ERR_NOTFOUND); } - fd = open(filename, (write ? O_RDWR | O_CREAT : O_RDONLY), UCI_FILEMODE); + fd = open(filename, mode, UCI_FILEMODE); if (fd <= 0) goto error; if (flock(fd, (write ? LOCK_EX : LOCK_SH)) < 0) goto error; - if (write && (pos == SEEK_SET)) - ret = ftruncate(fd, 0); - else - ret = lseek(fd, 0, pos); + ret = lseek(fd, 0, pos); if (ret < 0) goto error; - file = fdopen(fd, (write ? "w" : "r")); + file = fdopen(fd, (write ? "w+" : "r")); if (file) goto done; @@ -626,19 +626,27 @@ static void uci_parse_history(struct uci_context *ctx, FILE *stream, struct uci_ ctx->pctx = pctx; pctx->file = stream; - rewind(stream); while (!feof(pctx->file)) { uci_getln(ctx, 0); if (!pctx->buf[0]) continue; + + /* + * ignore parse errors in single lines, we want to preserve as much + * history as possible + */ + UCI_TRAP_SAVE(ctx, error); uci_parse_history_line(ctx, p, pctx->buf); + UCI_TRAP_RESTORE(ctx); +error: + continue; } /* no error happened, we can get rid of the parser context now */ uci_file_cleanup(ctx); } -static void uci_load_history(struct uci_context *ctx, struct uci_package *p) +static void uci_load_history(struct uci_context *ctx, struct uci_package *p, bool flush) { char *filename = NULL; FILE *f = NULL; @@ -649,16 +657,22 @@ static void uci_load_history(struct uci_context *ctx, struct uci_package *p) UCI_THROW(ctx, UCI_ERR_MEM); UCI_TRAP_SAVE(ctx, done); - f = uci_open_stream(ctx, filename, SEEK_SET, false); + f = uci_open_stream(ctx, filename, SEEK_SET, flush, false); uci_parse_history(ctx, f, p); UCI_TRAP_RESTORE(ctx); + done: + if (flush && f) { + rewind(f); + ftruncate(fileno(f), 0); + } if (filename) free(filename); uci_close_stream(f); ctx->errno = 0; } + int uci_load(struct uci_context *ctx, const char *name, struct uci_package **package) { char *filename; @@ -690,7 +704,7 @@ int uci_load(struct uci_context *ctx, const char *name, struct uci_package **pac break; } - file = uci_open_stream(ctx, filename, SEEK_SET, false); + file = uci_open_stream(ctx, filename, SEEK_SET, false, false); ctx->errno = 0; UCI_TRAP_SAVE(ctx, done); uci_import(ctx, file, name, package, true); @@ -699,7 +713,7 @@ int uci_load(struct uci_context *ctx, const char *name, struct uci_package **pac if (*package) { (*package)->path = filename; (*package)->confdir = confdir; - uci_load_history(ctx, *package); + uci_load_history(ctx, *package, false); } done: @@ -719,10 +733,11 @@ int uci_save(struct uci_context *ctx, struct uci_package *p) /* * if the config file was outside of the /etc/config path, * don't save the history to a file, update the real file - * directly + * directly. + * does not modify the uci_package pointer */ if (!p->confdir) - return uci_commit(ctx, p); + return uci_commit(ctx, &p); if (uci_list_empty(&p->history)) return 0; @@ -732,7 +747,7 @@ int uci_save(struct uci_context *ctx, struct uci_package *p) ctx->errno = 0; UCI_TRAP_SAVE(ctx, done); - f = uci_open_stream(ctx, filename, SEEK_END, true); + f = uci_open_stream(ctx, filename, SEEK_END, true, true); UCI_TRAP_RESTORE(ctx); uci_foreach_element_safe(&p->history, tmp, e) { @@ -762,21 +777,56 @@ done: return 0; } -int uci_commit(struct uci_context *ctx, struct uci_package *p) +int uci_commit(struct uci_context *ctx, struct uci_package **package) { + struct uci_package *p; FILE *f = NULL; + char *name = NULL; + char *path = NULL; UCI_HANDLE_ERR(ctx); + UCI_ASSERT(ctx, package != NULL); + p = *package; + UCI_ASSERT(ctx, p != NULL); UCI_ASSERT(ctx, p->path != NULL); - f = uci_open_stream(ctx, p->path, SEEK_SET, true); + /* open the config file for writing now, so that it is locked */ + f = uci_open_stream(ctx, p->path, SEEK_SET, true, true); + /* flush unsaved changes and reload from history file */ UCI_TRAP_SAVE(ctx, done); + if (p->confdir) { + name = uci_strdup(ctx, p->e.name); + path = uci_strdup(ctx, p->path); + if (!uci_list_empty(&p->history)) + UCI_INTERNAL(uci_save, ctx, p); + uci_free_package(&p); + uci_file_cleanup(ctx); + UCI_INTERNAL(uci_import, ctx, f, name, &p, true); + + p->path = path; + p->confdir = true; + *package = p; + + /* freed together with the uci_package */ + path = NULL; + + /* check for updated history, just in case */ + uci_load_history(ctx, p, true); + } + + rewind(f); + ftruncate(fileno(f), 0); + uci_export(ctx, f, p, false); UCI_TRAP_RESTORE(ctx); done: + if (name) + free(name); + if (path) + free(path); uci_close_stream(f); if (ctx->errno) UCI_THROW(ctx, ctx->errno); diff --git a/libuci.c b/libuci.c index 0dd81fe..51ce7da 100644 --- a/libuci.c +++ b/libuci.c @@ -59,7 +59,8 @@ void uci_free_context(struct uci_context *ctx) UCI_TRAP_SAVE(ctx, ignore); uci_cleanup(ctx); uci_foreach_element_safe(&ctx->root, tmp, e) { - uci_free_package(uci_to_package(e)); + struct uci_package *p = uci_to_package(e); + uci_free_package(&p); } free(ctx); UCI_TRAP_RESTORE(ctx); diff --git a/list.c b/list.c index 334bc32..b644523 100644 --- a/list.c +++ b/list.c @@ -195,9 +195,10 @@ uci_alloc_package(struct uci_context *ctx, const char *name) } static void -uci_free_package(struct uci_package *p) +uci_free_package(struct uci_package **package) { struct uci_element *e, *tmp; + struct uci_package *p = *package; if(!p) return; @@ -211,6 +212,7 @@ uci_free_package(struct uci_package *p) uci_free_history(uci_to_history(e)); } uci_free_element(&p->e); + *package = NULL; } static struct uci_element *uci_lookup_list(struct uci_context *ctx, struct uci_list *list, const char *name) @@ -410,7 +412,10 @@ int uci_set(struct uci_context *ctx, struct uci_package *p, char *section, char * if the section/option is to be modified and it is not found * create a new element in the appropriate list */ - UCI_INTERNAL(uci_lookup, ctx, &e, p, section, NULL); + e = uci_lookup_list(ctx, &p->sections, section); + if (!e) + goto notfound; + s = uci_to_section(e); if (option) { e = uci_lookup_list(ctx, &s->options, option); @@ -462,7 +467,7 @@ int uci_unload(struct uci_context *ctx, struct uci_package *p) UCI_HANDLE_ERR(ctx); UCI_ASSERT(ctx, p != NULL); - uci_free_package(p); + uci_free_package(&p); return 0; } diff --git a/uci.h b/uci.h index 88d94c9..88b5833 100644 --- a/uci.h +++ b/uci.h @@ -202,9 +202,12 @@ extern int uci_save(struct uci_context *ctx, struct uci_package *p); /** * uci_commit: commit changes to a package * @ctx: uci context - * @p: uci_package struct + * @p: uci_package struct pointer + * + * committing may reload the whole uci_package data, + * the supplied pointer is updated accordingly */ -extern int uci_commit(struct uci_context *ctx, struct uci_package *p); +extern int uci_commit(struct uci_context *ctx, struct uci_package **p); /** * uci_list_configs: List available uci config files