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 \
* UCW Library -- Configuration files: memory allocation
*
* (c) 2001--2006 Robert Spalek <robert@ucw.cz>
- * (c) 2003--2006 Martin Mares <mj@ucw.cz>
+ * (c) 2003--2012 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
#include <ucw/lib.h>
#include <ucw/conf.h>
+#include <ucw/conf-internal.h>
#include <ucw/mempool.h>
-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 *
{
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;
}
--- /dev/null
+/*
+ * UCW Library -- Configuration files: Contexts
+ *
+ * (c) 2012 Martin Mares <mj@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#include <ucw/lib.h>
+#include <ucw/conf.h>
+#include <ucw/conf-internal.h>
+#include <ucw/threads.h>
+
+#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;
+}
* UCW Library -- Configuration files: dumping
*
* (c) 2001--2006 Robert Spalek <robert@ucw.cz>
- * (c) 2003--2006 Martin Mares <mj@ucw.cz>
+ * (c) 2003--2012 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
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);
}
* UCW Library -- Configuration files: parsing input streams
*
* (c) 2001--2006 Robert Spalek <robert@ucw.cz>
- * (c) 2003--2009 Martin Mares <mj@ucw.cz>
+ * (c) 2003--2012 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
/* 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 <ucw/bbuf.h>
-static bb_t copy_buf;
-static uns copied;
#define GBUF_TYPE uns
#define GBUF_PREFIX(x) split_##x
#include <ucw/gbuf.h>
-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;
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
}
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;
}
}
/* 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; i<words; i++)
- pars[i-1] = copy_buf.ptr + word_buf.ptr[i];
+ char *name = p->copy_buf.ptr + p->word_buf.ptr[0];
+ char *pars[p->words-1];
+ for (uns i=1; i<p->words; 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;
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;
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 */
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;
}
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;
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;
/* 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");
}
}
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') {
}
#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);
} 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;
}
}
* UCW Library -- Configuration files: only for internal use of conf-*.c
*
* (c) 2001--2006 Robert Spalek <robert@ucw.cz>
- * (c) 2003--2006 Martin Mares <mj@ucw.cz>
+ * (c) 2003--2012 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
#ifndef _UCW_CONF_INTERNAL_H
#define _UCW_CONF_INTERNAL_H
+#include <ucw/threads.h>
+
+#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
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);
* UCW Library -- Configuration files: interpreter
*
* (c) 2001--2006 Robert Spalek <robert@ucw.cz>
- * (c) 2003--2006 Martin Mares <mj@ucw.cz>
+ * (c) 2003--2012 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
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,
};
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";
}
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;
}
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)
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)
}
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;
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) {
}
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,
}
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;
}
-
* UCW Library -- Configuration files: journaling
*
* (c) 2001--2006 Robert Spalek <robert@ucw.cz>
- * (c) 2003--2006 Martin Mares <mj@ucw.cz>
+ * (c) 2003--2012 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
#include <string.h>
-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;
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;
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);
}
}
* UCW Library -- Configuration files: sections
*
* (c) 2001--2006 Robert Spalek <robert@ucw.cz>
- * (c) 2003--2006 Martin Mares <mj@ucw.cz>
+ * (c) 2003--2012 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
/* Dirty sections */
+// FIXME!!!
+
struct dirty_section {
struct cf_section *sec;
void *ptr;
/* Initialization */
-struct cf_section cf_sections; // root section
-
struct cf_item *
cf_find_subitem(struct cf_section *sec, const char *name)
{
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;
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;
}
}
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;
* UCW Library -- Configuration files
*
* (c) 2001--2006 Robert Spalek <robert@ucw.cz>
- * (c) 2003--2006 Martin Mares <mj@ucw.cz>
+ * (c) 2003--2012 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
#ifndef _UCW_CONF_H
#define _UCW_CONF_H
+#include <ucw/clists.h>
+
+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. **/
* Memory allocation
* ~~~~~~~~~~~~~~~~~
*
- * Uses <<mempool:,memory pools>> for efficiency and journal recovery.
- * You should use these routines when implementing custom parsers.
+ * Each configuration context has one or more <<mempool:,memory pools>>, 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 <<mempool:type_mempool,memory pool>> 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 <<var_cf_pool,`cf_pool`>>. **/
+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. **/
/*
* The UCW Library -- Threading Helpers
*
- * (c) 2006--2011 Martin Mares <mj@ucw.cz>
+ * (c) 2006--2012 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
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