From: Martin Mares Date: Sat, 28 Apr 2012 13:17:38 +0000 (+0200) Subject: Conf: Rewritten configuration parser to support multiple contexts (step 1) X-Git-Tag: v5.99~181 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=0ca8c151f5afe1680e98f3bfbe9d5c752d8a2924;p=libucw.git Conf: Rewritten configuration parser to support multiple contexts (step 1) Some parts remain, most notably the handling of dirty sections in conf-section.c. --- diff --git a/ucw/Makefile b/ucw/Makefile index aa795e76..62373ade 100644 --- a/ucw/Makefile +++ b/ucw/Makefile @@ -13,7 +13,7 @@ LIBUCW_MODS= \ partmap hashfunc \ slists simple-lists bitsig \ log log-stream log-file log-syslog log-conf tbf \ - conf-alloc conf-dump conf-input conf-intr conf-journal conf-parse conf-section \ + conf-context conf-alloc conf-dump conf-input conf-intr conf-journal conf-parse conf-section \ ipaccess \ fastbuf ff-binary ff-string ff-printf ff-unicode ff-stkstring \ fb-file fb-mem fb-temp tempfile fb-mmap fb-limfd fb-buffer fb-grow fb-pool fb-atomic fb-param fb-socket \ diff --git a/ucw/conf-alloc.c b/ucw/conf-alloc.c index 937965dd..f5f80cc8 100644 --- a/ucw/conf-alloc.c +++ b/ucw/conf-alloc.c @@ -2,7 +2,7 @@ * UCW Library -- Configuration files: memory allocation * * (c) 2001--2006 Robert Spalek - * (c) 2003--2006 Martin Mares + * (c) 2003--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -10,26 +10,31 @@ #include #include +#include #include -struct mempool *cf_pool; // current pool for loading new configuration +inline struct mempool * +cf_get_pool(void) +{ + return cf_get_context()->pool; +} void * cf_malloc(uns size) { - return mp_alloc(cf_pool, size); + return mp_alloc(cf_get_pool(), size); } void * cf_malloc_zero(uns size) { - return mp_alloc_zero(cf_pool, size); + return mp_alloc_zero(cf_get_pool(), size); } char * cf_strdup(const char *s) { - return mp_strdup(cf_pool, s); + return mp_strdup(cf_get_pool(), s); } char * @@ -37,7 +42,7 @@ cf_printf(const char *fmt, ...) { va_list args; va_start(args, fmt); - char *res = mp_vprintf(cf_pool, fmt, args); + char *res = mp_vprintf(cf_get_pool(), fmt, args); va_end(args); return res; } diff --git a/ucw/conf-context.c b/ucw/conf-context.c new file mode 100644 index 00000000..dc74b66a --- /dev/null +++ b/ucw/conf-context.c @@ -0,0 +1,83 @@ +/* + * UCW Library -- Configuration files: Contexts + * + * (c) 2012 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#include +#include +#include +#include + +#ifndef CONFIG_UCW_DEFAULT_CONFIG +#define CONFIG_UCW_DEFAULT_CONFIG NULL +#endif + +#ifndef CONFIG_UCW_ENV_VAR_CONFIG +#define CONFIG_UCW_ENV_VAR_CONFIG NULL +#endif + +struct cf_context * +cf_new_context(void) +{ + struct cf_context *cc = xmalloc_zero(sizeof(*cc)); + cc->need_journal = 1; + clist_init(&cc->conf_entries); + return cc; +} + +void +cf_free_context(struct cf_context *cc) +{ + ASSERT(!cc->is_active); + xfree(cc->parser); + xfree(cc); +} + +struct cf_context * +cf_switch_context(struct cf_context *cc) +{ + struct ucwlib_context *uc = ucwlib_thread_context(); + struct cf_context *prev = uc->cf_context; + if (prev) + prev->is_active = 0; + if (cc) + { + ASSERT(!cc->is_active); + cc->is_active = 1; + } + uc->cf_context = cc; + return prev; +} + +struct cf_context * +cf_obtain_context(void) +{ + struct ucwlib_context *uc = ucwlib_thread_context(); + if (unlikely(!uc->cf_context)) + { + struct cf_context *cc = cf_new_context(); + uc->cf_context = cc; + cc->def_file = CONFIG_UCW_DEFAULT_CONFIG; + cc->env_file = CONFIG_UCW_ENV_VAR_CONFIG; + cc->is_active = 1; + } + return uc->cf_context; +} + +void +cf_set_default_file(char *name) +{ + struct cf_context *cc = cf_obtain_context(); + cc->def_file = name; +} + +void +cf_set_env_override(char *name) +{ + struct cf_context *cc = cf_obtain_context(); + cc->env_file = name; +} diff --git a/ucw/conf-dump.c b/ucw/conf-dump.c index 2176f71e..9d7e31c9 100644 --- a/ucw/conf-dump.c +++ b/ucw/conf-dump.c @@ -2,7 +2,7 @@ * UCW Library -- Configuration files: dumping * * (c) 2001--2006 Robert Spalek - * (c) 2003--2006 Martin Mares + * (c) 2003--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -118,6 +118,7 @@ dump_section(struct fastbuf *fb, struct cf_section *sec, int level, void *ptr) void cf_dump_sections(struct fastbuf *fb) { - dump_section(fb, &cf_sections, 0, NULL); + struct cf_context *cc = cf_get_context(); + dump_section(fb, &cc->sections, 0, NULL); } diff --git a/ucw/conf-input.c b/ucw/conf-input.c index 117c5482..fff753ce 100644 --- a/ucw/conf-input.c +++ b/ucw/conf-input.c @@ -2,7 +2,7 @@ * UCW Library -- Configuration files: parsing input streams * * (c) 2001--2006 Robert Spalek - * (c) 2003--2009 Martin Mares + * (c) 2003--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -25,72 +25,77 @@ /* Text file parser */ -static const char *name_parse_fb; -static struct fastbuf *parse_fb; -static uns line_num; - #define MAX_LINE 4096 -static char line_buf[MAX_LINE]; -static char *line = line_buf; #include -static bb_t copy_buf; -static uns copied; #define GBUF_TYPE uns #define GBUF_PREFIX(x) split_##x #include -static split_t word_buf; -static uns words; -static uns ends_by_brace; // the line is ended by "{" + +struct cf_parser_state { + const char *name_parse_fb; + struct fastbuf *parse_fb; + uns line_num; + char *line; + split_t word_buf; + uns words; + uns ends_by_brace; // the line is ended by "{" + bb_t copy_buf; + uns copied; + char line_buf[]; +}; static int -get_line(char **msg) +get_line(struct cf_parser_state *p, char **msg) { - int err = bgets_nodie(parse_fb, line_buf, MAX_LINE); - line_num++; + int err = bgets_nodie(p->parse_fb, p->line_buf, MAX_LINE); + p->line_num++; if (err <= 0) { *msg = err < 0 ? "Line too long" : NULL; return 0; } - line = line_buf; - while (Cblank(*line)) - line++; + p->line = p->line_buf; + while (Cblank(*p->line)) + p->line++; return 1; } static void -append(char *start, char *end) +append(struct cf_parser_state *p, char *start, char *end) { uns len = end - start; - bb_grow(©_buf, copied + len + 1); - memcpy(copy_buf.ptr + copied, start, len); - copied += len + 1; - copy_buf.ptr[copied-1] = 0; + bb_grow(&p->copy_buf, p->copied + len + 1); + memcpy(p->copy_buf.ptr + p->copied, start, len); + p->copied += len + 1; + p->copy_buf.ptr[p->copied-1] = 0; } static char * -get_word(uns is_command_name) +get_word(struct cf_parser_state *p, uns is_command_name) { char *msg; + char *line = p->line; + if (*line == '\'') { line++; while (1) { char *start = line; while (*line && *line != '\'') line++; - append(start, line); + append(p, start, line); if (*line) break; - copy_buf.ptr[copied-1] = '\n'; - if (!get_line(&msg)) + p->copy_buf.ptr[p->copied-1] = '\n'; + if (!get_line(p, &msg)) return msg ? : "Unterminated apostrophe word at the end"; + line = p->line; } line++; } else if (*line == '"') { line++; - uns start_copy = copied; + uns start_copy = p->copied; while (1) { char *start = line; uns escape = 0; @@ -103,23 +108,24 @@ get_word(uns is_command_name) escape = 0; line++; } - append(start, line); + append(p, start, line); if (*line) break; if (!escape) - copy_buf.ptr[copied-1] = '\n'; + p->copy_buf.ptr[p->copied-1] = '\n'; else // merge two lines - copied -= 2; - if (!get_line(&msg)) + p->copied -= 2; + if (!get_line(p, &msg)) return msg ? : "Unterminated quoted word at the end"; + line = p->line; } line++; - char *tmp = stk_str_unesc(copy_buf.ptr + start_copy); + char *tmp = stk_str_unesc(p->copy_buf.ptr + start_copy); uns l = strlen(tmp); - bb_grow(©_buf, start_copy + l + 1); - strcpy(copy_buf.ptr + start_copy, tmp); - copied = start_copy + l + 1; + bb_grow(&p->copy_buf, start_copy + l + 1); + strcpy(p->copy_buf.ptr + start_copy, tmp); + p->copied = start_copy + l + 1; } else { // promised that *line is non-null and non-blank @@ -135,59 +141,60 @@ get_word(uns is_command_name) } if (line == start) // already the first char is control line++; - append(start, line); + append(p, start, line); } while (Cblank(*line)) line++; + p->line = line; return NULL; } static char * -get_token(uns is_command_name, char **err) +get_token(struct cf_parser_state *p, uns is_command_name, char **err) { *err = NULL; while (1) { - if (!*line || *line == '#') { - if (!is_command_name || !get_line(err)) + if (!*p->line || *p->line == '#') { + if (!is_command_name || !get_line(p, err)) return NULL; - } else if (*line == ';') { - *err = get_word(0); + } else if (*p->line == ';') { + *err = get_word(p, 0); if (!is_command_name || *err) return NULL; - } else if (*line == '\\' && !line[1]) { - if (!get_line(err)) { + } else if (*p->line == '\\' && !p->line[1]) { + if (!get_line(p, err)) { if (!*err) *err = "Last line ends by a backslash"; return NULL; } - if (!*line || *line == '#') - msg(L_WARN, "The line %s:%d following a backslash is empty", name_parse_fb ? : "", line_num); + if (!*p->line || *p->line == '#') + msg(L_WARN, "The line %s:%d following a backslash is empty", p->name_parse_fb ? : "", p->line_num); } else { - split_grow(&word_buf, words+1); - uns start = copied; - word_buf.ptr[words++] = copied; - *err = get_word(is_command_name); - return *err ? NULL : copy_buf.ptr + start; + split_grow(&p->word_buf, p->words+1); + uns start = p->copied; + p->word_buf.ptr[p->words++] = p->copied; + *err = get_word(p, is_command_name); + return *err ? NULL : p->copy_buf.ptr + start; } } } static char * -split_command(void) +split_command(struct cf_parser_state *p) { - words = copied = ends_by_brace = 0; + p->words = p->copied = p->ends_by_brace = 0; char *msg, *start_word; - if (!(start_word = get_token(1, &msg))) + if (!(start_word = get_token(p, 1, &msg))) return msg; if (*start_word == '{') // only one opening brace return "Unexpected opening brace"; - while (*line != '}') // stays for the next time + while (*p->line != '}') // stays for the next time { - if (!(start_word = get_token(0, &msg))) + if (!(start_word = get_token(p, 0, &msg))) return msg; if (*start_word == '{') { - words--; // discard the brace - ends_by_brace = 1; + p->words--; // discard the brace + p->ends_by_brace = 1; break; } } @@ -197,32 +204,36 @@ split_command(void) /* Parsing multiple files */ static char * -parse_fastbuf(const char *name_fb, struct fastbuf *fb, uns depth) +parse_fastbuf(struct cf_context *cc, const char *name_fb, struct fastbuf *fb, uns depth) { + struct cf_parser_state *p = cc->parser; + if (!p) + p = cc->parser = xmalloc_zero(sizeof(*p) + MAX_LINE); + p->name_parse_fb = name_fb; + p->parse_fb = fb; + p->line_num = 0; + p->line = p->line_buf; + *p->line = 0; + char *err; - name_parse_fb = name_fb; - parse_fb = fb; - line_num = 0; - line = line_buf; - *line = 0; while (1) { - err = split_command(); + err = split_command(p); if (err) goto error; - if (!words) + if (!p->words) return NULL; - char *name = copy_buf.ptr + word_buf.ptr[0]; - char *pars[words-1]; - for (uns i=1; icopy_buf.ptr + p->word_buf.ptr[0]; + char *pars[p->words-1]; + for (uns i=1; iwords; i++) + pars[i-1] = p->copy_buf.ptr + p->word_buf.ptr[i]; if (!strcasecmp(name, "include")) { - if (words != 2) + if (p->words != 2) err = "Expecting one filename"; else if (depth > 8) err = "Too many nested files"; - else if (*line && *line != '#') // because the contents of line_buf is not re-entrant and will be cleared + else if (*p->line && *p->line != '#') // because the contents of line_buf is not re-entrant and will be cleared err = "The include command must be the last one on a line"; if (err) goto error; @@ -231,13 +242,13 @@ parse_fastbuf(const char *name_fb, struct fastbuf *fb, uns depth) err = cf_printf("Cannot open file %s: %m", pars[0]); goto error; } - uns ll = line_num; - err = parse_fastbuf(stk_strdup(pars[0]), new_fb, depth+1); - line_num = ll; + uns ll = p->line_num; + err = parse_fastbuf(cc, stk_strdup(pars[0]), new_fb, depth+1); + p->line_num = ll; bclose(new_fb); if (err) goto error; - parse_fb = fb; + p->parse_fb = fb; continue; } enum cf_operation op; @@ -265,70 +276,56 @@ parse_fastbuf(const char *name_fb, struct fastbuf *fb, uns depth) goto error; } } - if (ends_by_brace) + if (p->ends_by_brace) op |= OP_OPEN; - err = cf_interpret_line(name, op, words-1, pars); + err = cf_interpret_line(cc, name, op, p->words-1, pars); if (err) goto error; } error: if (name_fb) - msg(L_ERROR, "File %s, line %d: %s", name_fb, line_num, err); - else if (line_num == 1) + msg(L_ERROR, "File %s, line %d: %s", name_fb, p->line_num, err); + else if (p->line_num == 1) msg(L_ERROR, "Manual setting of configuration: %s", err); else - msg(L_ERROR, "Manual setting of configuration, line %d: %s", line_num, err); + msg(L_ERROR, "Manual setting of configuration, line %d: %s", p->line_num, err); return "included from here"; } -#ifndef CONFIG_UCW_DEFAULT_CONFIG -#define CONFIG_UCW_DEFAULT_CONFIG NULL -#endif -char *cf_def_file = CONFIG_UCW_DEFAULT_CONFIG; -static int cf_def_loaded; - -#ifndef CONFIG_UCW_ENV_VAR_CONFIG -#define CONFIG_UCW_ENV_VAR_CONFIG NULL -#endif -char *cf_env_file = CONFIG_UCW_ENV_VAR_CONFIG; - -static uns postpone_commit; // only for cf_getopt() -static uns everything_committed; // after the 1st load, this flag is set on - static int -done_stack(void) +done_stack(struct cf_context *cc) { - if (cf_check_stack()) + if (cf_check_stack(cc)) return 1; - if (cf_commit_all(postpone_commit ? CF_NO_COMMIT : everything_committed ? CF_COMMIT : CF_COMMIT_ALL)) + if (cf_commit_all(cc->postpone_commit ? CF_NO_COMMIT : cc->everything_committed ? CF_COMMIT : CF_COMMIT_ALL)) return 1; - if (!postpone_commit) - everything_committed = 1; + if (!cc->postpone_commit) + cc->everything_committed = 1; return 0; } static int -load_file(const char *file) +load_file(struct cf_context *cc, const char *file) { - cf_init_stack(); + cf_init_stack(cc); struct fastbuf *fb = bopen_try(file, O_RDONLY, 1<<14); if (!fb) { msg(L_ERROR, "Cannot open %s: %m", file); return 1; } - char *err_msg = parse_fastbuf(file, fb, 0); + char *err_msg = parse_fastbuf(cc, file, fb, 0); bclose(fb); - return !!err_msg || done_stack(); + return !!err_msg || done_stack(cc); } static int -load_string(const char *string) +load_string(struct cf_context *cc, const char *string) { - cf_init_stack(); + cf_init_stack(cc); struct fastbuf fb; fbbuf_init_read(&fb, (byte *)string, strlen(string), 0); - char *msg = parse_fastbuf(NULL, &fb, 0); - return !!msg || done_stack(); + char *msg = parse_fastbuf(cc, NULL, &fb, 0); + return !!msg || done_stack(cc); } /* Safe loading and reloading */ @@ -342,61 +339,58 @@ struct conf_entry { /* We remember a list of actions to apply upon reload */ char *arg; }; -static clist conf_entries; - static void -cf_remember_entry(uns type, const char *arg) +cf_remember_entry(struct cf_context *cc, uns type, const char *arg) { - if (!cf_need_journal) + if (!cc->need_journal) return; - if (!postpone_commit) + if (!cc->postpone_commit) return; struct conf_entry *ce = cf_malloc(sizeof(*ce)); ce->type = type; ce->arg = cf_strdup(arg); - clist_add_tail(&conf_entries, &ce->n); + clist_add_tail(&cc->conf_entries, &ce->n); } int cf_reload(const char *file) { + struct cf_context *cc = cf_get_context(); cf_journal_swap(); struct cf_journal_item *oldj = cf_journal_new_transaction(1); - uns ec = everything_committed; - everything_committed = 0; + uns ec = cc->everything_committed; + cc->everything_committed = 0; - if (!conf_entries.head.next) - clist_init(&conf_entries); clist old_entries; - clist_move(&old_entries, &conf_entries); - postpone_commit = 1; + clist_move(&old_entries, &cc->conf_entries); + cc->postpone_commit = 1; int err = 0; if (file) - err = load_file(file); + err = load_file(cc, file); else CLIST_FOR_EACH(struct conf_entry *, ce, old_entries) { if (ce->type == CE_FILE) - err |= load_file(ce->arg); + err |= load_file(cc, ce->arg); else - err |= load_string(ce->arg); + err |= load_string(cc, ce->arg); if (err) break; - cf_remember_entry(ce->type, ce->arg); + cf_remember_entry(cc, ce->type, ce->arg); } - postpone_commit = 0; + cc->postpone_commit = 0; if (!err) - err |= done_stack(); + err |= done_stack(cc); if (!err) { cf_journal_delete(); cf_journal_commit_transaction(1, NULL); } else { - everything_committed = ec; + cc->everything_committed = ec; cf_journal_rollback_transaction(1, oldj); cf_journal_swap(); - clist_move(&conf_entries, &old_entries); + clist_move(&cc->conf_entries, &old_entries); } return err; } @@ -404,12 +398,13 @@ cf_reload(const char *file) int cf_load(const char *file) { + struct cf_context *cc = cf_get_context(); struct cf_journal_item *oldj = cf_journal_new_transaction(1); - int err = load_file(file); + int err = load_file(cc, file); if (!err) { cf_journal_commit_transaction(1, oldj); - cf_remember_entry(CE_FILE, file); - cf_def_loaded = 1; + cf_remember_entry(cc, CE_FILE, file); + cc->def_loaded = 1; } else cf_journal_rollback_transaction(1, oldj); return err; @@ -418,11 +413,12 @@ cf_load(const char *file) int cf_set(const char *string) { + struct cf_context *cc = cf_get_context(); struct cf_journal_item *oldj = cf_journal_new_transaction(0); - int err = load_string(string); + int err = load_string(cc, string); if (!err) { cf_journal_commit_transaction(0, oldj); - cf_remember_entry(CE_STRING, string); + cf_remember_entry(cc, CE_STRING, string); } else cf_journal_rollback_transaction(0, oldj); return err; @@ -431,37 +427,37 @@ cf_set(const char *string) /* Command-line parser */ static void -load_default(void) +load_default(struct cf_context *cc) { - if (cf_def_loaded++) + if (cc->def_loaded++) return; - if (cf_def_file) + if (cc->def_file) { char *env; - if (cf_env_file && (env = getenv(cf_env_file))) + if (cc->env_file && (env = getenv(cc->env_file))) { if (cf_load(env)) die("Cannot load config file %s", env); } - else if (cf_load(cf_def_file)) - die("Cannot load default config %s", cf_def_file); + else if (cf_load(cc->def_file)) + die("Cannot load default config %s", cc->def_file); } else { // We need to create an empty pool and initialize all configuration items struct cf_journal_item *oldj = cf_journal_new_transaction(1); - cf_init_stack(); - done_stack(); + cf_init_stack(cc); + done_stack(cc); cf_journal_commit_transaction(1, oldj); } } static void -final_commit(void) +final_commit(struct cf_context *cc) { - if (postpone_commit) { - postpone_commit = 0; - if (done_stack()) + if (cc->postpone_commit) { + cc->postpone_commit = 0; + if (done_stack(cc)) die("Cannot commit after the initialization"); } } @@ -469,18 +465,17 @@ final_commit(void) int cf_getopt(int argc, char * const argv[], const char *short_opts, const struct option *long_opts, int *long_index) { - clist_init(&conf_entries); - postpone_commit = 1; + struct cf_context *cc = cf_obtain_context(); + cc->postpone_commit = 1; - static int other_options = 0; while (1) { int res = getopt_long (argc, argv, short_opts, long_opts, long_index); if (res == 'S' || res == 'C' || res == 0x64436667) { - if (other_options) + if (cc->other_options) die("The -S and -C options must precede all other arguments"); if (res == 'S') { - load_default(); + load_default(cc); if (cf_set(optarg)) die("Cannot set %s", optarg); } else if (res == 'C') { @@ -489,8 +484,8 @@ cf_getopt(int argc, char * const argv[], const char *short_opts, const struct op } #ifdef CONFIG_UCW_DEBUG else { /* --dumpconfig */ - load_default(); - final_commit(); + load_default(cc); + final_commit(cc); struct fastbuf *b = bfdopen(1, 4096); cf_dump_sections(b); bclose(b); @@ -500,10 +495,10 @@ cf_getopt(int argc, char * const argv[], const char *short_opts, const struct op } else { /* unhandled option or end of options */ if (res != ':' && res != '?') { - load_default(); - final_commit(); + load_default(cc); + final_commit(cc); } - other_options++; + cc->other_options++; return res; } } diff --git a/ucw/conf-internal.h b/ucw/conf-internal.h index 377a9cb2..d8c77d62 100644 --- a/ucw/conf-internal.h +++ b/ucw/conf-internal.h @@ -2,7 +2,7 @@ * UCW Library -- Configuration files: only for internal use of conf-*.c * * (c) 2001--2006 Robert Spalek - * (c) 2003--2006 Martin Mares + * (c) 2003--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -11,6 +11,46 @@ #ifndef _UCW_CONF_INTERNAL_H #define _UCW_CONF_INTERNAL_H +#include + +#define MAX_STACK_SIZE 16 + +struct item_stack { // used by conf-intr.c + struct cf_section *sec; // nested section + void *base_ptr; // because original pointers are often relative + int op; // it is performed when a closing brace is encountered + void *list; // list the operations should be done on + u32 mask; // bit array of selectors searching in a list + struct cf_item *item; // cf_item of the list +}; + +struct cf_context { + struct mempool *pool; + int is_active; + int need_journal; + char *def_file; + char *env_file; + int def_loaded; + struct cf_parser_state *parser; + uns everything_committed; // after the 1st load, this flag is set on + uns postpone_commit; // used internally by cf_getopt() + uns other_options; + clist conf_entries; + struct old_pools *pools; + struct cf_journal_item *journal; + struct item_stack stack[MAX_STACK_SIZE]; + uns stack_level; + uns initialized; + struct cf_section sections; // root section +}; + +/* conf-ctxt.c */ +static inline struct cf_context * +cf_get_context(void) +{ + return ucwlib_thread_context()->cf_context; +} + /* conf-intr.c */ #define OP_MASK 0xff // only get the operation #define OP_OPEN 0x100 // here we only get an opening brace instead of parameters @@ -21,9 +61,9 @@ extern char *cf_op_names[]; extern char *cf_type_names[]; uns cf_type_size(enum cf_type type, struct cf_user_type *utype); -char *cf_interpret_line(char *name, enum cf_operation op, int number, char **pars); -void cf_init_stack(void); -int cf_check_stack(void); +char *cf_interpret_line(struct cf_context *cc, char *name, enum cf_operation op, int number, char **pars); +void cf_init_stack(struct cf_context *cc); +int cf_check_stack(struct cf_context *cc); /* conf-journal.c */ void cf_journal_swap(void); diff --git a/ucw/conf-intr.c b/ucw/conf-intr.c index ba32dd7b..57192e92 100644 --- a/ucw/conf-intr.c +++ b/ucw/conf-intr.c @@ -2,7 +2,7 @@ * UCW Library -- Configuration files: interpreter * * (c) 2001--2006 Robert Spalek - * (c) 2003--2006 Martin Mares + * (c) 2003--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -378,24 +378,13 @@ record_selector(struct cf_item *item, struct cf_section *sec, u32 *mask) return NULL; } -#define MAX_STACK_SIZE 10 -static struct item_stack { - struct cf_section *sec; // nested section - void *base_ptr; // because original pointers are often relative - enum cf_operation op; // it is performed when a closing brace is encountered - void *list; // list the operations should be done on - u32 mask; // bit array of selectors searching in a list - struct cf_item *item; // cf_item of the list -} stack[MAX_STACK_SIZE]; -static uns level; - static char * -opening_brace(struct cf_item *item, void *ptr, enum cf_operation op) +opening_brace(struct cf_context *cc, struct cf_item *item, void *ptr, enum cf_operation op) { - if (level >= MAX_STACK_SIZE-1) + if (cc->stack_level >= MAX_STACK_SIZE-1) return "Too many nested sections"; enum cf_operation pure_op = op & OP_MASK; - stack[++level] = (struct item_stack) { + cc->stack[++cc->stack_level] = (struct item_stack) { .sec = NULL, .base_ptr = NULL, .op = pure_op, @@ -405,27 +394,27 @@ opening_brace(struct cf_item *item, void *ptr, enum cf_operation op) }; if (!item) // unknown is ignored; we just need to trace recursion return NULL; - stack[level].sec = item->u.sec; + cc->stack[cc->stack_level].sec = item->u.sec; if (item->cls == CC_SECTION) { if (pure_op != OP_SET) return "Only SET operation can be used with a section"; - stack[level].base_ptr = ptr; - stack[level].op = OP_EDIT | OP_2ND; // this list operation does nothing + cc->stack[cc->stack_level].base_ptr = ptr; + cc->stack[cc->stack_level].op = OP_EDIT | OP_2ND; // this list operation does nothing } else if (item->cls == CC_LIST) { - stack[level].base_ptr = cf_malloc(item->u.sec->size); - cf_init_section(item->name, item->u.sec, stack[level].base_ptr, 1); - stack[level].list = ptr; - stack[level].item = item; + cc->stack[cc->stack_level].base_ptr = cf_malloc(item->u.sec->size); + cf_init_section(item->name, item->u.sec, cc->stack[cc->stack_level].base_ptr, 1); + cc->stack[cc->stack_level].list = ptr; + cc->stack[cc->stack_level].item = item; if (pure_op == OP_ALL) return "Operation ALL cannot be applied on lists"; else if (pure_op < OP_REMOVE) { - add_to_list(ptr, stack[level].base_ptr, pure_op); - stack[level].op |= OP_2ND; + add_to_list(ptr, cc->stack[cc->stack_level].base_ptr, pure_op); + cc->stack[cc->stack_level].op |= OP_2ND; } else - stack[level].op |= OP_1ST; + cc->stack[cc->stack_level].op |= OP_1ST; } else return "Opening brace can only be used on sections and lists"; @@ -433,13 +422,13 @@ opening_brace(struct cf_item *item, void *ptr, enum cf_operation op) } static char * -closing_brace(struct item_stack *st, enum cf_operation op, int number, char **pars) +closing_brace(struct cf_context *cc, struct item_stack *st, enum cf_operation op, int number, char **pars) { if (st->op == OP_CLOSE) // top-level return "Unmatched } parenthesis"; if (!st->sec) { // dummy run on unknown section if (!(op & OP_OPEN)) - level--; + cc->stack_level--; return NULL; } enum cf_operation pure_op = st->op & OP_MASK; @@ -475,7 +464,7 @@ closing_brace(struct item_stack *st, enum cf_operation op, int number, char **pa } add_to_list(st->list, st->base_ptr, pure_op); } - level--; + cc->stack_level--; if (number) return "No parameters expected after the }"; else if (op & OP_OPEN) @@ -487,14 +476,15 @@ closing_brace(struct item_stack *st, enum cf_operation op, int number, char **pa static struct cf_item * find_item(struct cf_section *curr_sec, const char *name, char **msg, void **ptr) { + struct cf_context *cc = cf_get_context(); *msg = NULL; if (name[0] == '^') // absolute name instead of relative - name++, curr_sec = &cf_sections, *ptr = NULL; + name++, curr_sec = &cc->sections, *ptr = NULL; if (!curr_sec) // don't even search in an unknown section return NULL; while (1) { - if (curr_sec != &cf_sections) + if (curr_sec != &cc->sections) cf_add_dirty(curr_sec, *ptr); char *c = strchr(name, '.'); if (c) @@ -535,21 +525,21 @@ interpret_add(char *name, struct cf_item *item, int number, char **pars, int *ta } char * -cf_interpret_line(char *name, enum cf_operation op, int number, char **pars) +cf_interpret_line(struct cf_context *cc, char *name, enum cf_operation op, int number, char **pars) { char *msg; if ((op & OP_MASK) == OP_CLOSE) - return closing_brace(stack+level, op, number, pars); - void *ptr = stack[level].base_ptr; - struct cf_item *item = find_item(stack[level].sec, name, &msg, &ptr); + return closing_brace(cc, cc->stack+cc->stack_level, op, number, pars); + void *ptr = cc->stack[cc->stack_level].base_ptr; + struct cf_item *item = find_item(cc->stack[cc->stack_level].sec, name, &msg, &ptr); if (msg) return msg; - if (stack[level].op & OP_1ST) - TRY( record_selector(item, stack[level].sec, &stack[level].mask) ); + if (cc->stack[cc->stack_level].op & OP_1ST) + TRY( record_selector(item, cc->stack[cc->stack_level].sec, &cc->stack[cc->stack_level].mask) ); if (op & OP_OPEN) { // the operation will be performed after the closing brace if (number) return "Cannot open a block after a parameter has been passed on a line"; - return opening_brace(item, ptr, op); + return opening_brace(cc, item, ptr, op); } if (!item) // ignored item in an unknown section return NULL; @@ -583,9 +573,10 @@ cf_interpret_line(char *name, enum cf_operation op, int number, char **pars) char * cf_find_item(const char *name, struct cf_item *item) { + struct cf_context *cc = cf_get_context(); char *msg; void *ptr = NULL; - struct cf_item *ci = find_item(&cf_sections, name, &msg, &ptr); + struct cf_item *ci = find_item(&cc->sections, name, &msg, &ptr); if (msg) return msg; if (ci) { @@ -642,17 +633,16 @@ cf_modify_item(struct cf_item *item, enum cf_operation op, int number, char **pa } void -cf_init_stack(void) +cf_init_stack(struct cf_context *cc) { - static uns initialized = 0; - if (!initialized++) { - cf_sections.flags |= SEC_FLAG_UNKNOWN; - cf_sections.size = 0; // size of allocated array used to be stored here - cf_init_section(NULL, &cf_sections, NULL, 0); + if (!cc->initialized++) { + cc->sections.flags |= SEC_FLAG_UNKNOWN; + cc->sections.size = 0; // size of allocated array used to be stored here + cf_init_section(NULL, &cc->sections, NULL, 0); } - level = 0; - stack[0] = (struct item_stack) { - .sec = &cf_sections, + cc->stack_level = 0; + cc->stack[0] = (struct item_stack) { + .sec = &cc->sections, .base_ptr = NULL, .op = OP_CLOSE, .list = NULL, @@ -662,12 +652,11 @@ cf_init_stack(void) } int -cf_check_stack(void) +cf_check_stack(struct cf_context *cc) { - if (level > 0) { + if (cc->stack_level > 0) { msg(L_ERROR, "Unterminated block"); return 1; } return 0; } - diff --git a/ucw/conf-journal.c b/ucw/conf-journal.c index 6134bfd2..75af51ad 100644 --- a/ucw/conf-journal.c +++ b/ucw/conf-journal.c @@ -2,7 +2,7 @@ * UCW Library -- Configuration files: journaling * * (c) 2001--2006 Robert Spalek - * (c) 2003--2006 Martin Mares + * (c) 2003--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -16,38 +16,39 @@ #include -static struct old_pools { +struct old_pools { struct old_pools *prev; struct mempool *pool; -} *pools; // link-list of older cf_pool's +}; // link-list of older cf_pool's -uns cf_need_journal = 1; // some programs do not need journal -static struct cf_journal_item { +struct cf_journal_item { struct cf_journal_item *prev; byte *ptr; uns len; byte copy[0]; -} *journal; +}; void cf_journal_block(void *ptr, uns len) { - if (!cf_need_journal) + struct cf_context *cc = cf_get_context(); + if (!cc->need_journal) return; struct cf_journal_item *ji = cf_malloc(sizeof(struct cf_journal_item) + len); - ji->prev = journal; + ji->prev = cc->journal; ji->ptr = ptr; ji->len = len; memcpy(ji->copy, ptr, len); - journal = ji; + cc->journal = ji; } void cf_journal_swap(void) // swaps the contents of the memory and the journal, and reverses the list { + struct cf_context *cc = cf_get_context(); struct cf_journal_item *curr, *prev, *next; - for (next=NULL, curr=journal; curr; next=curr, curr=prev) + for (next=NULL, curr=cc->journal; curr; next=curr, curr=prev) { prev = curr->prev; curr->prev = next; @@ -58,32 +59,34 @@ cf_journal_swap(void) curr->ptr[i] = x; } } - journal = next; + cc->journal = next; } struct cf_journal_item * cf_journal_new_transaction(uns new_pool) { + struct cf_context *cc = cf_get_context(); if (new_pool) - cf_pool = mp_new(1<<10); - struct cf_journal_item *oldj = journal; - journal = NULL; + cc->pool = mp_new(1<<10); + struct cf_journal_item *oldj = cc->journal; + cc->journal = NULL; return oldj; } void cf_journal_commit_transaction(uns new_pool, struct cf_journal_item *oldj) { + struct cf_context *cc = cf_get_context(); if (new_pool) { struct old_pools *p = cf_malloc(sizeof(struct old_pools)); - p->prev = pools; - p->pool = cf_pool; - pools = p; + p->prev = cc->pools; + p->pool = cc->pool; + cc->pools = p; } if (oldj) { - struct cf_journal_item **j = &journal; + struct cf_journal_item **j = &cc->journal; while (*j) j = &(*j)->prev; *j = oldj; @@ -93,23 +96,25 @@ cf_journal_commit_transaction(uns new_pool, struct cf_journal_item *oldj) void cf_journal_rollback_transaction(uns new_pool, struct cf_journal_item *oldj) { - if (!cf_need_journal) + struct cf_context *cc = cf_get_context(); + if (!cc->need_journal) die("Cannot rollback the configuration, because the journal is disabled."); cf_journal_swap(); - journal = oldj; + cc->journal = oldj; if (new_pool) { - mp_delete(cf_pool); - cf_pool = pools ? pools->pool : NULL; + mp_delete(cc->pool); + cc->pool = cc->pools ? cc->pools->pool : NULL; } } void cf_journal_delete(void) { - for (struct old_pools *p=pools; p; p=pools) + struct cf_context *cc = cf_get_context(); + for (struct old_pools *p=cc->pools; p; p=cc->pools) { - pools = p->prev; + cc->pools = p->prev; mp_delete(p->pool); } } diff --git a/ucw/conf-section.c b/ucw/conf-section.c index 9cac24a5..d006643b 100644 --- a/ucw/conf-section.c +++ b/ucw/conf-section.c @@ -2,7 +2,7 @@ * UCW Library -- Configuration files: sections * * (c) 2001--2006 Robert Spalek - * (c) 2003--2006 Martin Mares + * (c) 2003--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -18,6 +18,8 @@ /* Dirty sections */ +// FIXME!!! + struct dirty_section { struct cf_section *sec; void *ptr; @@ -67,8 +69,6 @@ sort_dirty(void) /* Initialization */ -struct cf_section cf_sections; // root section - struct cf_item * cf_find_subitem(struct cf_section *sec, const char *name) { @@ -106,12 +106,13 @@ inspect_section(struct cf_section *sec) void cf_declare_section(const char *name, struct cf_section *sec, uns allow_unknown) { - if (!cf_sections.cfg) + struct cf_context *cc = cf_get_context(); + if (!cc->sections.cfg) { - cf_sections.size = 50; - cf_sections.cfg = xmalloc_zero(cf_sections.size * sizeof(struct cf_item)); + cc->sections.size = 50; + cc->sections.cfg = xmalloc_zero(cc->sections.size * sizeof(struct cf_item)); } - struct cf_item *ci = cf_find_subitem(&cf_sections, name); + struct cf_item *ci = cf_find_subitem(&cc->sections, name); if (ci->cls) die("Cannot register section %s twice", name); ci->cls = CC_SECTION; @@ -123,11 +124,11 @@ cf_declare_section(const char *name, struct cf_section *sec, uns allow_unknown) if (allow_unknown) sec->flags |= SEC_FLAG_UNKNOWN; ci++; - if (ci - cf_sections.cfg >= (int) cf_sections.size) + if (ci - cc->sections.cfg >= (int) cc->sections.size) { - cf_sections.cfg = xrealloc(cf_sections.cfg, 2*cf_sections.size * sizeof(struct cf_item)); - bzero(cf_sections.cfg + cf_sections.size, cf_sections.size * sizeof(struct cf_item)); - cf_sections.size *= 2; + cc->sections.cfg = xrealloc(cc->sections.cfg, 2*cc->sections.size * sizeof(struct cf_item)); + bzero(cc->sections.cfg + cc->sections.size, cc->sections.size * sizeof(struct cf_item)); + cc->sections.size *= 2; } } @@ -193,10 +194,11 @@ commit_section(struct cf_section *sec, void *ptr, uns commit_all) int cf_commit_all(enum cf_commit_mode cm) { + struct cf_context *cc = cf_get_context(); sort_dirty(); if (cm == CF_NO_COMMIT) return 0; - if (commit_section(&cf_sections, NULL, cm == CF_COMMIT_ALL)) + if (commit_section(&cc->sections, NULL, cm == CF_COMMIT_ALL)) return 1; dirties = 0; return 0; diff --git a/ucw/conf.h b/ucw/conf.h index 50dbddc1..bf106274 100644 --- a/ucw/conf.h +++ b/ucw/conf.h @@ -2,7 +2,7 @@ * UCW Library -- Configuration files * * (c) 2001--2006 Robert Spalek - * (c) 2003--2006 Martin Mares + * (c) 2003--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -11,6 +11,63 @@ #ifndef _UCW_CONF_H #define _UCW_CONF_H +#include + +struct mempool; + +/*** + * [[conf_ctxt]] + * Configuration contexts + * ~~~~~~~~~~~~~~~~~~~~~~ + * + * The state of the configuration parser is stored within a configuration context. + * If you do not create contexts explicitly, the library will create one for you + * and you need not care, as long as you use a single configuration file. + * + * In whole generality, you can define as many context as you wish and switch + * between them. Each thread has its own pointer to the current context, which + * must not be shared with other threads. + ***/ + +/** Create a new configuration context. **/ +struct cf_context *cf_new_context(void); + +/** + * Free a configuration context. The context must not be set as current + * for any thread. + * + * All configuration settings made within the context are rolled back + * (except when journalling is turned off). All memory allocated on behalf + * of the context is freed, which includes memory obtained by calls to + * cf_malloc(). + **/ +void cf_free_context(struct cf_context *cc); + +/** + * Make the given configuration context current and return the previously + * active context. Both the new and the old context may be NULL. + **/ +struct cf_context *cf_switch_context(struct cf_context *cc); + +/** + * Return a pointer to the current context, or create the default context + * if there is no context active. + **/ +struct cf_context *cf_obtain_context(void); + +/** + * Set name of default configuration file. May be NULL if there should be + * no such default. + * FIXME: Explain where it is used + **/ +void cf_set_default_file(char *name); + +/** + * Set name of environment variable used to override the name of the default + * configuration file. May be NULL if there should be no such variable. + **/ +void cf_set_env_override(char *name); + /*** === Data types [[conf_types]] ***/ enum cf_class { /** Class of the configuration item. **/ @@ -306,18 +363,17 @@ struct cf_section { /** A section. **/ * Memory allocation * ~~~~~~~~~~~~~~~~~ * - * Uses <> for efficiency and journal recovery. - * You should use these routines when implementing custom parsers. + * Each configuration context has one or more <>, where all + * data related to the configuration are stored. + * + * The following set of functions allocate from these pools. The allocated memory + * is valid as long as the current configuration (when the configuration file is + * reloaded or rolled back, or the context is deleted, it gets lost). + * + * Memory allocated from within custom parsers should be allocated from the pools. ***/ -struct mempool; -/** - * A <> for configuration parser needs. - * Memory allocated from here is valid as long as the current config is loaded - * (if you allocate some memory and rollback the transaction or you load some - * other configuration, it gets lost). - **/ -extern struct mempool *cf_pool; -void *cf_malloc(uns size); /** Returns @size bytes of memory. Allocates from <>. **/ +struct mempool *cf_get_pool(void); /** Return a pointer to the current configuration pool. **/ +void *cf_malloc(uns size); /** Returns @size bytes of memory allocated from the current configuration pool. **/ void *cf_malloc_zero(uns size); /** Like @cf_malloc(), but zeroes the memory. **/ char *cf_strdup(const char *s); /** Copy a string into @cf_malloc()ed memory. **/ char *cf_printf(const char *fmt, ...) FORMAT_CHECK(printf,1,2); /** printf() into @cf_malloc()ed memory. **/ diff --git a/ucw/threads.h b/ucw/threads.h index de1fa306..3c1f29b7 100644 --- a/ucw/threads.h +++ b/ucw/threads.h @@ -1,7 +1,7 @@ /* * The UCW Library -- Threading Helpers * - * (c) 2006--2011 Martin Mares + * (c) 2006--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -20,6 +20,7 @@ struct ucwlib_context { struct asio_queue *io_queue; // Async I/O queue for fb-direct.c ucw_sighandler_t *signal_handlers; // Signal handlers for sighandler.c struct main_context *main_context; // Current context for mainloop.c + struct cf_context *cf_context; // Current context for configuration parser // Resources and transactions: struct respool *current_respool; // Current resource pool struct mempool *trans_pool; // Transaction mempool