#include "lib/mempool.h"
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
/* Memory allocation */
-static struct mempool *cf_pool;
+static struct old_pools {
+ struct old_pools *prev;
+ struct mempool *pool;
+} *pools;
void *
cf_malloc(uns size)
{
- return mp_alloc(cf_pool, size);
+ return mp_alloc(pools->pool, size);
}
void *
cf_malloc_zero(uns size)
{
- return mp_alloc_zero(cf_pool, size);
+ return mp_alloc_zero(pools->pool, size);
}
byte *
cf_strdup(byte *s)
{
- return mp_strdup(cf_pool, s);
+ return mp_strdup(pools->pool, s);
}
byte *
{
va_list args;
va_start(args, fmt);
- byte *res = mp_vprintf(cf_pool, fmt, args);
+ byte *res = mp_vprintf(pools->pool, fmt, args);
va_end(args);
return res;
}
/* Undo journal */
+static struct journal_item {
+ struct journal_item *prev;
+ void *ptr;
+ uns len;
+ byte copy[0];
+} *journal;
+
void
-cf_journal_block(void *ptr UNUSED, uns len UNUSED)
+cf_journal_block(void *ptr, uns len)
+{
+ struct journal_item *ji = cf_malloc(sizeof(struct journal_item) + len);
+ ji->prev = journal;
+ ji->ptr = ptr;
+ ji->len = len;
+ memcpy(ji->copy, ptr, len);
+ journal = ji;
+}
+
+static void
+journal_swap(void)
+ // swaps the contents of the memory and the journal, and reverses the list
+{
+ struct journal_item *curr, *prev, *next;
+ uns max_len = 0;
+ for (curr=journal; curr; curr=curr->prev)
+ max_len = MAX(max_len, curr->len);
+ byte *buf = xmalloc(max_len);
+ for (next=NULL, curr=journal; curr; next=curr, curr=prev)
+ {
+ prev = curr->prev;
+ curr->prev = next;
+ memcpy(buf, curr->copy, curr->len);
+ memcpy(curr->copy, curr->ptr, curr->len);
+ memcpy(curr->ptr, buf, curr->len);
+ }
+ journal = next;
+ xfree(buf);
+}
+
+static struct journal_item *
+journal_new_section(uns new_pool)
+{
+ if (new_pool)
+ {
+ struct old_pools *oldp = pools;
+ struct mempool *mp = mp_new(1<<14);
+ pools = mp_alloc(mp, sizeof(struct old_pools));
+ pools->prev = oldp;
+ pools->pool = mp;
+ }
+ struct journal_item *oldj = journal;
+ journal = NULL;
+ return oldj;
+}
+
+static void
+journal_commit_section(struct journal_item *oldj)
+{
+ struct journal_item **j = &journal;
+ while (*j)
+ j = &(*j)->prev;
+ *j = oldj;
+}
+
+static void
+journal_rollback_section(uns new_pool, struct journal_item *oldj)
+{
+ journal_swap();
+ journal = oldj;
+ if (new_pool)
+ {
+ struct old_pools *oldp = pools;
+ pools = pools->prev;
+ mp_delete(oldp->pool);
+ }
+}
+
+/* Safe loading and reloading */
+
+static byte *load_file(byte *file);
+static byte *load_string(byte *string);
+
+byte *
+cf_reload(byte *file)
+{
+ journal_swap();
+ struct old_pools *oldp = pools;
+ pools = NULL;
+
+ struct journal_item *oldj = journal_new_section(1);
+ byte *msg = load_file(file);
+ if (msg)
+ {
+ journal_rollback_section(1, oldj);
+ pools = oldp;
+ journal_swap();
+ }
+ else
+ for (struct old_pools *p=oldp; p; p=oldp)
+ {
+ oldp = p->prev;
+ mp_delete(p->pool);
+ }
+ return msg;
+}
+
+byte *
+cf_load(byte *file)
+{
+ struct journal_item *oldj = journal_new_section(1);
+ byte *msg = load_file(file);
+ if (!msg)
+ journal_commit_section(oldj);
+ else
+ journal_rollback_section(1, oldj);
+ return msg;
+}
+
+byte *
+cf_set(byte *string)
{
+ struct journal_item *oldj = journal_new_section(0);
+ byte *msg = load_string(string);
+ if (!msg)
+ journal_commit_section(oldj);
+ else
+ journal_rollback_section(0, oldj);
+ return msg;
}
/* Parsers for standard types */
{
cf_journal_block(ptr, sizeof(void*));
*ptr = cf_malloc((number+1) * parsers[type].size) + parsers[type].size;
- * (uns*) (ptr - parsers[type].size) = number;
+ * (uns*) (*ptr - parsers[type].size) = number;
return ((cf_parser*) parsers[type].parser) (number, pars, *ptr);
}
struct cf_item {
enum cf_class cls;
byte *name;
- int number; // length of an array or #parameters of a parser
+ int number; // length of an array or #parameters of a parser (negative means at most)
void *ptr; // pointer to a global variable or an offset in a section
union {
enum cf_type type; // type of a static or dynamic attribute
/* Undo journal for error recovery */
void cf_journal_block(void *ptr, uns len);
+/* Safe reloading and loading of configuration files */
+byte *cf_reload(byte *file);
+byte *cf_load(byte *file);
+byte *cf_set(byte *string);
+
/* Parsers for basic types */
byte *cf_parse_int(uns number, byte **pars, int *ptr);
byte *cf_parse_u64(uns number, byte **pars, u64 *ptr);