cfg framework: cfg_set_now() supports the group instances
authorMiklos Tirpak <miklos@iptel.org>
Tue, 7 Sep 2010 15:09:07 +0000 (17:09 +0200)
committerMiklos Tirpak <miklos@iptel.org>
Tue, 7 Sep 2010 15:09:07 +0000 (17:09 +0200)
cfg_set_now() can write the variables within both
the default and the additional group instances.
If the additional variable is not explicitely set, then
the default value overwrites it.

Note: the group instance must exist before cfg_set_now() is called,
it is not created dynamicaly.

The function does not work yet before the config is shmized.

cfg/cfg.c
cfg/cfg_ctx.c
cfg/cfg_ctx.h
cfg/cfg_script.c
cfg/cfg_script.h
cfg/cfg_struct.c
cfg/cfg_struct.h

index 0ae9ddb..c718f5a 100644 (file)
--- a/cfg/cfg.c
+++ b/cfg/cfg.c
@@ -61,6 +61,7 @@ int cfg_declare(char *group_name, cfg_def_t *def, void *values, int def_size,
        for (i=0, size=0; i<num; i++) {
                mapping[i].def = &(def[i]);
                mapping[i].name_len = strlen(def[i].name);
+               mapping[i].pos = i;
                /* record all the types for sanity checks */
                types|=1 << CFG_VAR_MASK(def[i].type);
 
index d2ea621..57e6670 100644 (file)
@@ -239,6 +239,63 @@ error:
                } \
        } while(0)
 
+/* returns the size of the variable */
+static int cfg_var_size(cfg_mapping_t *var)
+{
+       switch (CFG_VAR_TYPE(var)) {
+
+       case CFG_VAR_INT:
+               return sizeof(int);
+
+       case CFG_VAR_STRING:
+               return sizeof(char *);
+
+       case CFG_VAR_STR:
+               return sizeof(str);
+
+       case CFG_VAR_POINTER:
+               return sizeof(void *);
+
+       default:
+               LOG(L_CRIT, "BUG: cfg_var_size(): unknown type: %u\n",
+                       CFG_VAR_TYPE(var));
+               return 0;
+       }
+}
+
+/* Update the varibales of the array within the meta structure
+ * with the new default value.
+ * The array is cloned before a change if clone is set to 1.
+ */
+static int cfg_update_defaults(cfg_group_meta_t        *meta,
+                               cfg_group_t *group, cfg_mapping_t *var, char *new_val,
+                               int clone)
+{
+       int     i, clone_done=0;
+       cfg_group_inst_t *array, *ginst;
+
+       array = meta->array;
+       for (i = 0; i < meta->num; i++) {
+               ginst = (cfg_group_inst_t *)((char *)array
+                       + (sizeof(cfg_group_meta_t) + group->size - 1) * i);
+
+               if (!CFG_VAR_TEST(ginst, var)) {
+                       /* The variable uses the default value, it needs to be rewritten. */
+                       if (clone && !clone_done) {
+                               /* The array needs to be cloned before the modification */
+                               if (!(array = cfg_clone_array(meta, group)))
+                                       return -1;
+                               ginst = translate_pointer(array, meta->array, ginst);
+                               /* re-link the array to the meta-data */
+                               meta->array = array;
+                               clone_done = 1;
+                       }
+                       memcpy(ginst->vars + var->offset, new_val, cfg_var_size(var)); 
+               }
+       }
+       return 0;
+}
+
 /* sets the value of a variable without the need of commit
  *
  * return value:
@@ -246,7 +303,7 @@ error:
  *  -1: error
  *   1: variable has not been found
  */
-int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
+int cfg_set_now(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
                        void *val, unsigned int val_type)
 {
        cfg_group_t     *group;
@@ -257,6 +314,8 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
        char            *old_string = NULL;
        char            **replaced = NULL;
        cfg_child_cb_t  *child_cb = NULL;
+       cfg_group_inst_t        *group_inst, *new_array = NULL;
+       unsigned char           *var_block;
 
        /* verify the context even if we do not need it now
        to make sure that a cfg driver has called the function
@@ -266,6 +325,15 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
                return -1;
        }
 
+       if (group_id && !cfg_shmized) {
+               /* The config group has not been shmized yet,
+               but an additional instance of a variable needs to be added to the group.
+               Add this instance to the linked list of variables, they
+               will be fixed later. */
+               /* TODO */
+               return -1;
+       }
+
        /* look-up the group and the variable */
        if (cfg_lookup_var(group_name, var_name, &group, &var))
                return 1;
@@ -276,6 +344,19 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
                goto error0;
        }
 
+       /* The additional variable instances having per-child process callback
+        * with CFG_CB_ONLY_ONCE flag cannot be rewritten.
+        * The reason is that such variables typically set global parameters
+        * as opposed to per-process variables. Hence, it is not possible to set
+        * the group handle temporary to another block, and then reset it back later. */
+       if (group_id
+               && var->def->on_set_child_cb
+               && var->def->type & CFG_CB_ONLY_ONCE
+       ) {
+               LOG(L_ERR, "ERROR: cfg_set_now(): This variable does not support muliple values.\n");
+               goto error0;
+       }
+
        /* check whether we have to convert the type */
        if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
                goto error0;
@@ -294,7 +375,24 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
                /* Call the fixup function.
                There is no need to set a temporary cfg handle,
                becaue a single variable is changed */
-               if (var->def->on_change_cb(*(group->handle),
+               if (!group_id) {
+                       var_block = *(group->handle);
+               } else {
+                       if (!cfg_local) {
+                               LOG(L_ERR, "ERROR: cfg_set_now(): Local configuration is missing\n");
+                               goto error0;
+                       }
+                       group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, group),
+                                                       group->size,
+                                                       *group_id);
+                       if (!group_inst) {
+                               LOG(L_ERR, "ERROR: cfg_set_now(): local group instance is not found\n");
+                               goto error0;
+                       }
+                       var_block = group_inst->vars;
+               }
+
+               if (var->def->on_change_cb(var_block,
                                                group_name,
                                                var_name,
                                                &v) < 0) {
@@ -304,7 +402,10 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
 
        }
 
-       if (var->def->on_set_child_cb) {
+       /* Set the per-child process callback only if the default value is changed.
+        * The callback of other instances will be called when the config is
+        * switched to that instance. */
+       if (!group_id && var->def->on_set_child_cb) {
                /* get the name of the variable from the internal struct,
                because var_name may be freed before the callback needs it */
                s.s = group->name;
@@ -325,16 +426,50 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
                while the new one is prepared */
                CFG_WRITER_LOCK();
 
+               if (group_id) {
+                       group_inst = cfg_find_group(CFG_GROUP_META(*cfg_global, group),
+                                                       group->size,
+                                                       *group_id);
+                       if (!group_inst) {
+                               LOG(L_ERR, "ERROR: cfg_set_now(): global group instance is not found\n");
+                               goto error0;
+                       }
+                       var_block = group_inst->vars;
+               } else {
+                       group_inst = NULL;
+                       var_block = CFG_GROUP_DATA(*cfg_global, group);
+               }
+
                if (var->def->type & CFG_ATOMIC) {
                        /* atomic change is allowed, we can rewrite the value
                        directly in the global config */
-                       p = (*cfg_global)->vars+group->var_offset+var->offset;
+                       p = var_block + var->offset;
 
                } else {
                        /* clone the memory block, and prepare the modification */
                        if (!(block = cfg_clone_global())) goto error;
 
-                       p = block->vars+group->var_offset+var->offset;
+                       if (group_inst) {
+                               /* The additional array of the group needs to be also cloned.
+                                * When any of the variables within this array is changed, then
+                                * the complete config block and this array is replaced. */
+                               if (!(new_array = cfg_clone_array(CFG_GROUP_META(*cfg_global, group), group)))
+                                       goto error;
+                               group_inst = translate_pointer(new_array,
+                                       CFG_GROUP_META(*cfg_global, group)->array,
+                                       group_inst);
+                               var_block = group_inst->vars;
+                               CFG_GROUP_META(block, group)->array = new_array;
+                       } else {
+                               /* The additional array may need to be replaced depending
+                                * on whether or not there is any variable in the array set
+                                * to the default value which is changed now. If this is the case,
+                                * then the array will be replaced later when the variables are
+                                * updated.
+                                */
+                               var_block = CFG_GROUP_DATA(block, group);
+                       }
+                       p = var_block + var->offset;
                }
        } else {
                /* we are allowed to rewrite the value on-the-fly
@@ -371,8 +506,23 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
                break;
 
        }
+       if (group_inst && !CFG_VAR_TEST_AND_SET(group_inst, var))
+               old_string = NULL; /* the string is the same as the default one,
+                                       it cannot be freed */
 
        if (cfg_shmized) {
+               if (!group_inst && CFG_GROUP_META(block, group)->array) {
+                       if (cfg_update_defaults(CFG_GROUP_META(block, group),
+                                               group, var, p,
+                                               ((var->def->type & CFG_ATOMIC) == 0)) /* clone if needed */
+                       ) {
+                               LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
+                               goto error;
+                       }
+                       if (CFG_GROUP_META(block, group)->array != CFG_GROUP_META(*cfg_global, group)->array)
+                               new_array = CFG_GROUP_META(block, group)->array;
+               }
+
                if (old_string) {
                        /* prepare the array of the replaced strings,
                        they will be freed when the old block is freed */
@@ -430,6 +580,7 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
 error:
        if (cfg_shmized) CFG_WRITER_UNLOCK();
        if (block) cfg_block_free(block);
+       if (new_array) shm_free(new_array);
        if (child_cb) cfg_child_cb_free(child_cb);
 
 error0:
@@ -443,45 +594,27 @@ error0:
 }
 
 /* wrapper function for cfg_set_now */
-int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
+int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                       int val)
 {
-       return cfg_set_now(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
+       return cfg_set_now(ctx, group_name, group_id, var_name,
+                               (void *)(long)val, CFG_VAR_INT);
 }
 
 /* wrapper function for cfg_set_now */
-int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
+int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                       char *val)
 {
-       return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
+       return cfg_set_now(ctx, group_name, group_id, var_name,
+                               (void *)val, CFG_VAR_STRING);
 }
 
 /* wrapper function for cfg_set_now */
-int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
-{
-       return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
-}
-
-/* returns the size of the variable */
-static int cfg_var_size(cfg_mapping_t *var)
+int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                       str *val)
 {
-       switch (CFG_VAR_TYPE(var)) {
-
-       case CFG_VAR_INT:
-               return sizeof(int);
-
-       case CFG_VAR_STRING:
-               return sizeof(char *);
-
-       case CFG_VAR_STR:
-               return sizeof(str);
-
-       case CFG_VAR_POINTER:
-               return sizeof(void *);
-
-       default:
-               LOG(L_CRIT, "BUG: cfg_var_size(): unknown type: %u\n",
-                       CFG_VAR_TYPE(var));
-               return 0;
-       }
+       return cfg_set_now(ctx, group_name, group_id, var_name,
+                               (void *)val, CFG_VAR_STR);
 }
 
 /* sets the value of a variable but does not commit the change
@@ -491,7 +624,7 @@ static int cfg_var_size(cfg_mapping_t *var)
  *  -1: error
  *   1: variable has not been found
  */
-int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
+int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
                        void *val, unsigned int val_type)
 {
        cfg_group_t     *group;
@@ -506,7 +639,7 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
        if (!cfg_shmized)
                /* the cfg has not been shmized yet, there is no
                point in registering the change and committing it later */
-               return cfg_set_now(ctx, group_name, var_name,
+               return cfg_set_now(ctx, group_name, group_id, var_name,
                                        val, val_type);
 
        if (!ctx) {
@@ -683,21 +816,27 @@ error0:
 }
 
 /* wrapper function for cfg_set_delayed */
-int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
+int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                               int val)
 {
-       return cfg_set_delayed(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
+       return cfg_set_delayed(ctx, group_name, group_id, var_name,
+                               (void *)(long)val, CFG_VAR_INT);
 }
 
 /* wrapper function for cfg_set_delayed */
-int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
+int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                               char *val)
 {
-       return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
+       return cfg_set_delayed(ctx, group_name, group_id, var_name,
+                               (void *)val, CFG_VAR_STRING);
 }
 
 /* wrapper function for cfg_set_delayed */
-int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
+int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                               str *val)
 {
-       return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
+       return cfg_set_delayed(ctx, group_name, group_id, var_name,
+                               (void *)val, CFG_VAR_STR);
 }
 
 /* commits the previously prepared changes within the context */
@@ -785,8 +924,7 @@ int cfg_commit(cfg_ctx_t *ctx)
                changed;
                changed = changed->next
        ) {
-               p = block->vars
-                       + changed->group->var_offset
+               p = CFG_GROUP_DATA(block, changed->group)
                        + changed->var->offset;
 
                if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
index cdbb9c2..0e2299f 100644 (file)
@@ -84,18 +84,24 @@ int cfg_register_ctx(cfg_ctx_t **handle, cfg_on_declare on_declare_cb);
 void cfg_ctx_destroy(void);
 
 /*! \brief set the value of a variable without the need of explicit commit */
-int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
+int cfg_set_now(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
                        void *val, unsigned int val_type);
-int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val);
-int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val);
-int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val);
+int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                       int val);
+int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                       char *val);
+int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                       str *val);
 
 /* sets the value of a variable but does not commit the change */
-int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
+int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
                        void *val, unsigned int val_type);
-int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val);
-int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val);
-int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val);
+int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                       int val);
+int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                       char *val);
+int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
+                       str *val);
 
 /*! \brief commits the previously prepared changes within the context */
 int cfg_commit(cfg_ctx_t *ctx);
index e57fa2d..8340206 100644 (file)
@@ -181,6 +181,7 @@ int cfg_script_fixup(cfg_group_t *group, unsigned char *block)
 
                mapping[i].def = &(def[i]);
                mapping[i].name_len = script_var->name_len;
+               mapping[i].pos = i;
 
                switch (script_var->type) {
                case CFG_VAR_INT:
index e53236e..1c22bdb 100644 (file)
@@ -29,6 +29,7 @@
 #define _CFG_SCRIPT_H
 
 #include "../str.h"
+#include "cfg_struct.h"
 
 /* structure used for temporary storing the variables
  * which are declared in the script */
index 18a8aca..13605ae 100644 (file)
@@ -216,19 +216,19 @@ int cfg_shmize(void)
                        if (cfg_shmize_strings(group)) goto error;
 
                        /* copy the values to the new block */
-                       memcpy(block->vars+group->var_offset, group->vars, group->size);
+                       memcpy(CFG_GROUP_DATA(block, group), group->vars, group->size);
                } else {
                        /* The group was declared with NULL values,
                         * we have to fix it up.
                         * The fixup function takes care about the values,
                         * it fills up the block */
-                       if (cfg_script_fixup(group, block->vars+group->var_offset)) goto error;
+                       if (cfg_script_fixup(group, CFG_GROUP_DATA(block, group))) goto error;
 
                        /* Notify the drivers about the new config definition.
                         * Temporary set the group handle so that the drivers have a chance to
                         * overwrite the default values. The handle must be reset after this
                         * because the main process does not have a local configuration. */
-                       *(group->handle) = block->vars+group->var_offset;
+                       *(group->handle) = CFG_GROUP_DATA(block, group);
                        cfg_notify_drivers(group->name, group->name_len,
                                        group->mapping->def);
                        *(group->handle) = NULL;
@@ -584,6 +584,48 @@ cfg_block_t *cfg_clone_global(void)
        return block;
 }
 
+/* Clone an array of configuration group instances.
+ * WARNING: unsafe, cfg_writer_lock or cfg_global_lock must be held!
+ */
+cfg_group_inst_t *cfg_clone_array(cfg_group_meta_t *meta, cfg_group_t *group)
+{
+       cfg_group_inst_t        *new_array;
+       int                     size;
+
+       if (!meta->array || !meta->num)
+               return NULL;
+
+       size = (sizeof(cfg_group_inst_t) + group->size - 1) * meta->num;
+       new_array = (cfg_group_inst_t *)shm_malloc(size);
+       if (!new_array) {
+               LOG(L_ERR, "ERROR: cfg_clone_array(): not enough shm memory\n");
+               return NULL;
+       }
+       memcpy(new_array, meta->array, size);
+
+       return new_array;
+}
+
+/* Find the group instance within the meta-data based on the group_id */
+cfg_group_inst_t *cfg_find_group(cfg_group_meta_t *meta, int group_size, unsigned int group_id)
+{
+       int     i;
+       cfg_group_inst_t *ginst;
+
+       if (!meta)
+               return NULL;
+
+       /* For now, search lineray till the end of the array.
+       TODO: improve */
+       for (i = 0; i < meta->num; i++) {
+               ginst = (cfg_group_inst_t *)((char *)meta->array
+                       + (sizeof(cfg_group_meta_t) + group_size - 1) * i);
+               if (ginst->id == group_id)
+                       return ginst;
+       }
+       return NULL;
+}
+
 /* append new callbacks to the end of the child callback list
  *
  * WARNING: the function is unsafe, either hold CFG_LOCK(),
index 456e32b..18ffbc7 100644 (file)
@@ -34,6 +34,7 @@
 #include "../mem/shm_mem.h"
 #include "../locking.h"
 #include "../compiler_opt.h"
+#include "../bit_test.h"
 #include "cfg.h"
 
 /*! \brief Maximum number of variables within a configuration group. */
 /*! \brief indicates that the variable has been already shmized */
 #define cfg_var_shmized        1U
 
+/*! \brief Structure for storing additional values of a variable.
+ * When the config is shmzied, these variables are combined in
+ * an array.
+ */
+typedef struct _cfg_add_var {
+       unsigned int    type;
+       union {
+               str     s;
+               int     i;
+       } val;
+       unsigned int    group_id; /*!< Id of the group instance */
+       struct _cfg_add_var     *next;
+} cfg_add_var_t;
+
 /*! \brief structure used for variable - pointer mapping */
 typedef struct _cfg_mapping {
        cfg_def_t       *def;           /*!< one item of the cfg structure definition */
        int             name_len;       /*!< length of def->name */
 
        /* additional information about the cfg variable */
+       int             pos;    /*!< position of the variable within the group starting from 0 */
        int             offset; /*!< offest within the memory block */
        unsigned int    flag;   /*!< flag indicating the state of the variable */
 } cfg_mapping_t;
@@ -60,6 +76,9 @@ typedef struct _cfg_group {
        char            *vars;          /*!< pointer to the memory block where the values
                                        are stored -- used only before the config is
                                        shmized. */
+       cfg_add_var_t   *add_var;       /*!< Additional instances of the variables.
+                                       This linked list is used only before the config is
+                                       shmized. */
        int             size;           /*!< size of the memory block that has to be
                                        allocated to store the values */
        int             meta_offset;    /*!< offset of the group within the
@@ -81,6 +100,7 @@ typedef struct _cfg_group {
 /*! \brief One instance of the cfg group variables which stores
  * the additional values. These values can overwrite the default values. */
 typedef struct _cfg_group_inst {
+       unsigned int    id;             /*!< identifier of the group instance */
        unsigned int    set[CFG_MAX_VAR_NUM/(sizeof(int)*8)];
                                        /*!< Bitmap indicating whether or not a value is explicitely set
                                        within this instance. If the value is not set,
@@ -147,6 +167,24 @@ extern cfg_child_cb_t      *cfg_child_cb;
 #define CFG_VAR_TYPE(var)      CFG_VAR_MASK((var)->def->type)
 #define CFG_INPUT_TYPE(var)    CFG_INPUT_MASK((var)->def->type)
 
+/* get the meta-data of a group from the block */
+#define CFG_GROUP_META(block, group) \
+       ((cfg_group_meta_t *)((block)->vars + (group)->meta_offset))
+
+/* get the data block of a group from the block */
+#define CFG_GROUP_DATA(block, group) \
+       ((unsigned char *)((block)->vars + (group)->var_offset))
+
+/* Test whether a variable is explicitely set in the group instance,
+ * or it uses the default value */
+#define CFG_VAR_TEST(group_inst, var) \
+       bit_test((var)->pos % (sizeof(int)*8), (group_inst)->set + (var)->pos/(sizeof(int)*8))
+
+/* Test whether a variable is explicitely set in the group instance,
+ * or it uses the default value, and set the flag. */
+#define CFG_VAR_TEST_AND_SET(group_inst, var) \
+       bit_test_and_set((var)->pos % (sizeof(int)*8), (group_inst)->set + (var)->pos/(sizeof(int)*8))
+
 /* initiate the cfg framework */
 int sr_cfg_init(void);
 
@@ -266,7 +304,7 @@ static inline void cfg_update_local(int no_cbs)
                group;
                group = group->next
        )
-               *(group->handle) = cfg_local->vars + group->var_offset;
+               *(group->handle) = CFG_GROUP_DATA(cfg_local, group);
 
        if (unlikely(cfg_child_cb==CFG_NO_CHILD_CBS || no_cbs))
                return;
@@ -328,12 +366,20 @@ cfg_group_t *cfg_lookup_group(char *name, int len);
 int cfg_lookup_var(str *gname, str *vname,
                        cfg_group_t **group, cfg_mapping_t **var);
 
-/* clones the global config block */
+/* clones the global config block
+ * WARNING: unsafe, cfg_writer_lock or cfg_global_lock must be held!
+ */
 cfg_block_t *cfg_clone_global(void);
 
+/* Clone an array of configuration group instances. */
+cfg_group_inst_t *cfg_clone_array(cfg_group_meta_t *meta, cfg_group_t *group);
+
 /* clones a string to shared memory */
 int cfg_clone_str(str *src, str *dst);
 
+/* Find the group instance within the meta-data based on the group_id */
+cfg_group_inst_t *cfg_find_group(cfg_group_meta_t *meta, int group_size, unsigned int group_id);
+
 /* append new callbacks to the end of the child callback list
  *
  * WARNING: the function is unsafe, either hold CFG_LOCK(),