X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=blobdiff_plain;f=file.c;h=47d9c44b08152de07d0ac734192ca89e4c1c482e;hp=9a2e353fe92e77a65893d3536e6c52eab623ca14;hb=a79ac49b066a821b6d99bca846091854e3715a49;hpb=d2c85b2804aaa22e539e266b5ed4cf958d6ceb35 diff --git a/file.c b/file.c index 9a2e353..47d9c44 100644 --- a/file.c +++ b/file.c @@ -97,8 +97,6 @@ static void uci_file_cleanup(struct uci_context *ctx) if (pctx->buf) free(pctx->buf); - if (pctx->file) - fclose(pctx->file); free(pctx); } @@ -238,7 +236,7 @@ done: /* * extract the next argument from the command line */ -static char *next_arg(struct uci_context *ctx, char **str, bool required) +static char *next_arg(struct uci_context *ctx, char **str, bool required, bool name) { char *val; char *ptr; @@ -248,6 +246,8 @@ static char *next_arg(struct uci_context *ctx, char **str, bool required) parse_str(ctx, str, &ptr); if (required && !*val) uci_parse_error(ctx, *str, "insufficient arguments"); + if (name && !uci_validate_name(val)) + uci_parse_error(ctx, val, "invalid character in field"); return val; } @@ -260,7 +260,7 @@ static void assert_eol(struct uci_context *ctx, char **str) { char *tmp; - tmp = next_arg(ctx, str, false); + tmp = next_arg(ctx, str, false, false); if (tmp && *tmp) uci_parse_error(ctx, *str, "too many arguments"); } @@ -309,7 +309,7 @@ static void uci_parse_package(struct uci_context *ctx, char **str, bool single) /* command string null-terminated by strtok */ *str += strlen(*str) + 1; - name = next_arg(ctx, str, true); + name = next_arg(ctx, str, true, true); assert_eol(ctx, str); if (single) return; @@ -336,8 +336,8 @@ static void uci_parse_config(struct uci_context *ctx, char **str) /* command string null-terminated by strtok */ *str += strlen(*str) + 1; - type = next_arg(ctx, str, true); - name = next_arg(ctx, str, false); + type = next_arg(ctx, str, true, true); + name = next_arg(ctx, str, false, true); assert_eol(ctx, str); ctx->pctx->section = uci_alloc_section(ctx->pctx->package, type, name); } @@ -356,8 +356,8 @@ static void uci_parse_option(struct uci_context *ctx, char **str) /* command string null-terminated by strtok */ *str += strlen(*str) + 1; - name = next_arg(ctx, str, true); - value = next_arg(ctx, str, true); + name = next_arg(ctx, str, true, true); + value = next_arg(ctx, str, true, false); assert_eol(ctx, str); uci_alloc_option(ctx->pctx->section, name, value); } @@ -470,15 +470,14 @@ int uci_export(struct uci_context *ctx, FILE *stream, struct uci_package *packag UCI_HANDLE_ERR(ctx); UCI_ASSERT(ctx, stream != NULL); - if (package) { + if (package) uci_export_package(package, stream, header); - goto done; + else { + uci_foreach_element(&ctx->root, e) { + uci_export_package(uci_to_package(e), stream, header); + } } - uci_foreach_element(&ctx->root, e) { - uci_export_package(uci_to_package(e), stream, header); - } -done: return 0; } @@ -503,8 +502,18 @@ 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) @@ -519,51 +528,169 @@ int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct u return 0; } -int uci_load(struct uci_context *ctx, const char *name, struct uci_package **package) +/* + * open a stream and go to the right position + * + * 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) { struct stat statbuf; + FILE *file = NULL; + int fd, ret; + + 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); + 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); + + if (ret < 0) + goto error; + + file = fdopen(fd, (write ? "w" : "r")); + if (file) + goto done; + +error: + UCI_THROW(ctx, UCI_ERR_IO); +done: + return file; +} + +static void uci_close_stream(FILE *stream) +{ + int fd; + + if (!stream) + return; + + fd = fileno(stream); + flock(fd, LOCK_UN); + fclose(stream); +} + +static void uci_parse_history_line(struct uci_context *ctx, struct uci_package *p, char *buf) +{ + bool delete = false; + char *package = NULL; + char *section = NULL; + char *option = NULL; + char *value = NULL; + + if (buf[0] == '-') { + delete = true; + buf++; + } + + UCI_INTERNAL(uci_parse_tuple, ctx, buf, &package, §ion, &option, &value); + if (!package || !section || (!delete && !value)) + goto error; + if (strcmp(package, p->e.name) != 0) + goto error; + if (!uci_validate_name(section)) + goto error; + if (option && !uci_validate_name(option)) + goto error; + + 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); +} + +static void uci_parse_history(struct uci_context *ctx, FILE *stream, struct uci_package *p) +{ + struct uci_parse_context *pctx; + + /* make sure no memory from previous parse attempts is leaked */ + uci_file_cleanup(ctx); + + pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context)); + ctx->pctx = pctx; + pctx->file = stream; + + rewind(stream); + while (!feof(pctx->file)) { + uci_getln(ctx, 0); + if (!pctx->buf[0]) + continue; + uci_parse_history_line(ctx, p, pctx->buf); + } + + /* 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) +{ + char *filename = NULL; + FILE *f = NULL; + + if (!p->confdir) + return; + if ((asprintf(&filename, "%s/%s", UCI_SAVEDIR, p->e.name) < 0) || !filename) + UCI_THROW(ctx, UCI_ERR_MEM); + + UCI_TRAP_SAVE(ctx, done); + f = uci_open_stream(ctx, filename, SEEK_SET, false); + uci_parse_history(ctx, f, p); + UCI_TRAP_RESTORE(ctx); +done: + 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; bool confdir; - FILE *file; - int fd; + FILE *file = NULL; UCI_HANDLE_ERR(ctx); UCI_ASSERT(ctx, name != NULL); switch (name[0]) { case '.': + /* relative path outside of /etc/config */ if (name[1] != '/') UCI_THROW(ctx, UCI_ERR_NOTFOUND); /* fall through */ case '/': - /* absolute/relative path outside of /etc/config */ + /* absolute path outside of /etc/config */ filename = uci_strdup(ctx, name); name = strrchr(name, '/') + 1; confdir = false; break; default: + /* config in /etc/config */ + if (strchr(name, '/')) + UCI_THROW(ctx, UCI_ERR_INVAL); filename = uci_malloc(ctx, strlen(name) + sizeof(UCI_CONFDIR) + 2); sprintf(filename, UCI_CONFDIR "/%s", name); confdir = true; break; } - if ((stat(filename, &statbuf) < 0) || - ((statbuf.st_mode & S_IFMT) != S_IFREG)) { - UCI_THROW(ctx, UCI_ERR_NOTFOUND); - } - - fd = open(filename, O_RDONLY); - if (fd <= 0) - UCI_THROW(ctx, UCI_ERR_IO); - - if (flock(fd, LOCK_SH) < 0) - UCI_THROW(ctx, UCI_ERR_IO); - - file = fdopen(fd, "r"); - if (!file) - UCI_THROW(ctx, UCI_ERR_IO); - + file = uci_open_stream(ctx, filename, SEEK_SET, false); ctx->errno = 0; UCI_TRAP_SAVE(ctx, done); uci_import(ctx, file, name, package, true); @@ -572,50 +699,88 @@ 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); } done: - flock(fd, LOCK_UN); - fclose(file); + uci_close_stream(file); return ctx->errno; } -int uci_commit(struct uci_context *ctx, struct uci_package *p) +int uci_save(struct uci_context *ctx, struct uci_package *p) { FILE *f = NULL; - int fd = 0; - int err = UCI_ERR_IO; + char *filename = NULL; + struct uci_element *e, *tmp; UCI_HANDLE_ERR(ctx); UCI_ASSERT(ctx, p != NULL); - UCI_ASSERT(ctx, p->path != NULL); - fd = open(p->path, O_RDWR); - if (fd < 0) - goto done; + /* + * if the config file was outside of the /etc/config path, + * don't save the history to a file, update the real file + * directly + */ + if (!p->confdir) + return uci_commit(ctx, p); - if (flock(fd, LOCK_EX) < 0) - goto done; + if (uci_list_empty(&p->history)) + return 0; - ftruncate(fd, 0); - f = fdopen(fd, "w"); - if (!f) - goto done; + if ((asprintf(&filename, "%s/%s", UCI_SAVEDIR, p->e.name) < 0) || !filename) + UCI_THROW(ctx, UCI_ERR_MEM); + ctx->errno = 0; UCI_TRAP_SAVE(ctx, done); - uci_export(ctx, f, p, false); + f = uci_open_stream(ctx, filename, SEEK_END, 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); + + if (h->cmd == UCI_CMD_REMOVE) + fprintf(f, "\n"); + else + fprintf(f, "=%s\n", h->value); + uci_free_history(h); + } + done: - if (f) - fclose(f); - else if (fd > 0) - close(fd); + uci_close_stream(f); + if (filename) + free(filename); + if (ctx->errno) + UCI_THROW(ctx, ctx->errno); + + return 0; +} + +int uci_commit(struct uci_context *ctx, struct uci_package *p) +{ + FILE *f = NULL; + + UCI_HANDLE_ERR(ctx); + UCI_ASSERT(ctx, p != NULL); + UCI_ASSERT(ctx, p->path != NULL); + + f = uci_open_stream(ctx, p->path, SEEK_SET, true); + UCI_TRAP_SAVE(ctx, done); + uci_export(ctx, f, p, false); + UCI_TRAP_RESTORE(ctx); + +done: + uci_close_stream(f); if (ctx->errno) UCI_THROW(ctx, ctx->errno); - if (err) - UCI_THROW(ctx, UCI_ERR_IO); + return 0; } @@ -637,15 +802,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++) { @@ -658,11 +825,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; @@ -675,7 +838,8 @@ char **uci_list_configs(struct uci_context *ctx) strcpy(buf, p); buf += strlen(buf) + 1; } - return configs; -} + *list = configs; + return 0; +}