X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fuci.git;a=blobdiff_plain;f=file.c;h=980189fd1edf7597fa161e3ae2c5603851c07519;hp=0e5992c4de7962b940f36f84449e2c0708fd1686;hb=3d22ffb838d306fc87c3701fa79a2d4198a4720d;hpb=4aa3fd762e2190a4e418581cf24446484d0b7df5 diff --git a/file.c b/file.c index 0e5992c..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,8 +502,17 @@ 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); + UCI_TRAP_RESTORE(ctx); + continue; +error: + if (ctx->flags & UCI_FLAG_PERROR) + uci_perror(ctx, NULL); + if ((ctx->errno != UCI_ERR_PARSE) || + (ctx->flags & UCI_FLAG_STRICT)) + UCI_THROW(ctx, ctx->errno); } if (package) @@ -524,27 +533,34 @@ int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct u * 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)); + 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; @@ -580,7 +596,7 @@ static void uci_parse_history_line(struct uci_context *ctx, struct uci_package * } UCI_INTERNAL(uci_parse_tuple, ctx, buf, &package, §ion, &option, &value); - if (!package || !section || !value) + if (!package || !section || (!delete && !value)) goto error; if (strcmp(package, p->e.name) != 0) goto error; @@ -588,10 +604,13 @@ static void uci_parse_history_line(struct uci_context *ctx, struct uci_package * goto error; if (option && !uci_validate_name(option)) goto error; - if (!delete) - UCI_INTERNAL(uci_set, ctx, package, section, option, value); - return; + if (delete) + UCI_INTERNAL(uci_del, ctx, p, section, option); + else + UCI_INTERNAL(uci_set, ctx, p, section, option, value); + + return; error: UCI_THROW(ctx, UCI_ERR_PARSE); } @@ -607,21 +626,29 @@ 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; + char *filename = NULL; FILE *f = NULL; if (!p->confdir) @@ -630,17 +657,24 @@ 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) { - struct stat statbuf; char *filename; bool confdir; FILE *file = NULL; @@ -670,12 +704,7 @@ int uci_load(struct uci_context *ctx, const char *name, struct uci_package **pac break; } - if ((stat(filename, &statbuf) < 0) || - ((statbuf.st_mode & S_IFMT) != S_IFREG)) { - UCI_THROW(ctx, UCI_ERR_NOTFOUND); - } - - 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); @@ -684,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: @@ -704,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; @@ -717,18 +747,24 @@ 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) { struct uci_history *h = uci_to_history(e); + if (h->cmd == UCI_CMD_REMOVE) fprintf(f, "-"); + fprintf(f, "%s.%s", p->e.name, h->section); if (e->name) fprintf(f, ".%s", e->name); - fprintf(f, "=%s\n", h->value); - uci_list_del(&e->list); + + if (h->cmd == UCI_CMD_REMOVE) + fprintf(f, "\n"); + else + fprintf(f, "=%s\n", h->value); + uci_free_history(h); } done: @@ -741,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); @@ -781,15 +852,17 @@ static inline char *get_filename(char *path) return p; } -char **uci_list_configs(struct uci_context *ctx) +int uci_list_configs(struct uci_context *ctx, char ***list) { char **configs; glob_t globbuf; int size, i; char *buf; + UCI_HANDLE_ERR(ctx); + if (glob(UCI_CONFDIR "/*", GLOB_MARK, NULL, &globbuf) != 0) - return NULL; + UCI_THROW(ctx, UCI_ERR_NOTFOUND); size = sizeof(char *) * (globbuf.gl_pathc + 1); for(i = 0; i < globbuf.gl_pathc; i++) { @@ -802,11 +875,7 @@ char **uci_list_configs(struct uci_context *ctx) size += strlen(p) + 1; } - configs = malloc(size); - if (!configs) - return NULL; - - memset(configs, 0, size); + configs = uci_malloc(ctx, size); buf = (char *) &configs[globbuf.gl_pathc + 1]; for(i = 0; i < globbuf.gl_pathc; i++) { char *p; @@ -819,6 +888,8 @@ char **uci_list_configs(struct uci_context *ctx) strcpy(buf, p); buf += strlen(buf) + 1; } - return configs; + *list = configs; + + return 0; }