Browse code

Merge remote branch 'origin/tirpi/cfg_framework_multivalue'

* origin/tirpi/cfg_framework_multivalue: (33 commits)
cfg framework: fix uninitialized group_inst pointers
xmlrpc: scan resets the error code
cfg_rpc: group can be specified for cfg.list
cfg_rpc: cfg.set and cfg.set_delayed commands added
ctl: rpc->scan does not immediately send out errors
cfg_rpc: documentation update - group instances
cfg framework: multiple group instances is documented
cfg framework: timer must reset the handles more frequently
cfg framework: cfg_select() and cfg_reset() added
cfg framework: group handle can be moved runtime
cfg framework: group instance support in the script
cfg framework: apply the values in the order they are set
cfg framework: translate_pointer bugfix
cfg framework: apply additional var list bugfix
cfg framework: apply the additional variable list
cfg framework: group instance support before forking
cfg framework: CFG_GROUP_UNKNOWN group type
cfg framework: cfg_commit() log message fixes
cfg_db: updated to the cfg framework changes
cfg_db: updated to the cfg framework changes
...

Miklos Tirpak authored on 27/10/2010 11:03:59
Showing 23 changed files
... ...
@@ -93,6 +93,7 @@
93 93
 #endif
94 94
 #include "switch.h"
95 95
 #include "events.h"
96
+#include "cfg/cfg_struct.h"
96 97
 
97 98
 #include <sys/types.h>
98 99
 #include <sys/socket.h>
... ...
@@ -1492,6 +1493,40 @@ match_cleanup:
1492 1492
 			msg->rpl_send_flags.f|= SND_F_CON_CLOSE;
1493 1493
 			ret=1; /* continue processing */
1494 1494
 			break;
1495
+		case CFG_SELECT_T:
1496
+			if (a->val[0].type != CFG_GROUP_ST) {
1497
+				BUG("unsupported parameter in CFG_SELECT_T: %d\n",
1498
+						a->val[0].type);
1499
+				ret=-1;
1500
+				goto error;
1501
+			}
1502
+			switch(a->val[1].type) {
1503
+				case NUMBER_ST:
1504
+					v=(int)a->val[1].u.number;
1505
+					break;
1506
+				case RVE_ST:
1507
+					if (rval_expr_eval_int(h, msg, &v, (struct rval_expr*)a->val[1].u.data) < 0) {
1508
+						ret=-1;
1509
+						goto error;
1510
+					}
1511
+					break;
1512
+				default:
1513
+					BUG("unsupported group id type in CFG_SELECT_T: %d\n",
1514
+							a->val[1].type);
1515
+					ret=-1;
1516
+					goto error;
1517
+			}
1518
+			ret=(cfg_select((cfg_group_t*)a->val[0].u.data, v) == 0) ? 1 : -1;
1519
+			break;
1520
+		case CFG_RESET_T:
1521
+			if (a->val[0].type != CFG_GROUP_ST) {
1522
+				BUG("unsupported parameter in CFG_RESET_T: %d\n",
1523
+						a->val[0].type);
1524
+				ret=-1;
1525
+				goto error;
1526
+			}
1527
+			ret=(cfg_reset((cfg_group_t*)a->val[0].u.data) == 0) ? 1 : -1;
1528
+			break;
1495 1529
 /*
1496 1530
 		default:
1497 1531
 			LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);
... ...
@@ -251,6 +251,9 @@ WHILE			"while"
251 251
 
252 252
 INCLUDEFILE     "include_file"
253 253
 
254
+CFG_SELECT	"cfg_select"
255
+CFG_RESET	"cfg_reset"
256
+
254 257
 /*ACTION LVALUES*/
255 258
 URIHOST			"uri:host"
256 259
 URIPORT			"uri:port"
... ...
@@ -635,6 +638,9 @@ SUBST       subst
635 635
 
636 636
 <INITIAL>{INCLUDEFILE}  { count(); BEGIN(INCLF); }
637 637
 
638
+<INITIAL>{CFG_SELECT}	{ count(); yylval.strval=yytext; return CFG_SELECT; }
639
+<INITIAL>{CFG_RESET}	{ count(); yylval.strval=yytext; return CFG_RESET; }
640
+
638 641
 <INITIAL>{URIHOST}	{ count(); yylval.strval=yytext; return URIHOST; }
639 642
 <INITIAL>{URIPORT}	{ count(); yylval.strval=yytext; return URIPORT; }
640 643
 
... ...
@@ -101,7 +101,7 @@
101 101
  * 2010-02-17  added blacklist imask (DST_BLST_*_IMASK) support (andrei)
102 102
 */
103 103
 
104
-%expect 5
104
+%expect 6
105 105
 
106 106
 %{
107 107
 
... ...
@@ -347,6 +347,8 @@ extern char *finame;
347 347
 %token CASE
348 348
 %token DEFAULT
349 349
 %token WHILE
350
+%token CFG_SELECT
351
+%token CFG_RESET
350 352
 %token URIHOST
351 353
 %token URIPORT
352 354
 %token MAX_LEN
... ...
@@ -1632,6 +1634,16 @@ cfg_var:
1632 1632
 	| cfg_var_id DOT cfg_var_id EQUAL error { 
1633 1633
 		yyerror("number or string expected"); 
1634 1634
 	}
1635
+	| cfg_var_id LBRACK NUMBER RBRACK DOT cfg_var_id EQUAL NUMBER {
1636
+		if (cfg_ginst_var_int($1, $3, $6, $8)) {
1637
+			yyerror("variable cannot be added to the group instance");
1638
+		}
1639
+	}
1640
+	| cfg_var_id LBRACK NUMBER RBRACK DOT cfg_var_id EQUAL STRING {
1641
+		if (cfg_ginst_var_string($1, $3, $6, $8)) {
1642
+			yyerror("variable cannot be added to the group instance");
1643
+		}
1644
+	}
1635 1645
 	;
1636 1646
 
1637 1647
 module_stm:
... ...
@@ -3261,6 +3273,19 @@ cmd:
3261 3261
 	| SET_RPL_CLOSE	{
3262 3262
 		$$=mk_action(SET_RPL_CLOSE_T, 0); set_cfg_pos($$);
3263 3263
 	}
3264
+	| CFG_SELECT LPAREN STRING COMMA NUMBER RPAREN {
3265
+		$$=mk_action(CFG_SELECT_T, 2, STRING_ST, $3, NUMBER_ST, (void*)$5); set_cfg_pos($$);
3266
+	}
3267
+	| CFG_SELECT LPAREN STRING COMMA rval_expr RPAREN {
3268
+		$$=mk_action(CFG_SELECT_T, 2, STRING_ST, $3, RVE_ST, $5); set_cfg_pos($$);
3269
+	}
3270
+	| CFG_SELECT error { $$=0; yyerror("missing '(' or ')' ?"); }
3271
+	| CFG_SELECT LPAREN error RPAREN { $$=0; yyerror("bad arguments, string and number expected"); }
3272
+	| CFG_RESET LPAREN STRING RPAREN {
3273
+		$$=mk_action(CFG_RESET_T, 1, STRING_ST, $3); set_cfg_pos($$);
3274
+	}
3275
+	| CFG_RESET error { $$=0; yyerror("missing '(' or ')' ?"); }
3276
+	| CFG_RESET LPAREN error RPAREN { $$=0; yyerror("bad arguments, string expected"); }
3264 3277
 	| ID {mod_func_action = mk_action(MODULE0_T, 2, MODEXP_ST, NULL, NUMBER_ST,
3265 3278
 			0); } LPAREN func_params RPAREN	{
3266 3279
 		mod_func_action->val[0].u.data =
... ...
@@ -43,6 +43,7 @@ int cfg_declare(char *group_name, cfg_def_t *def, void *values, int def_size,
43 43
 {
44 44
 	int	i, num, size, group_name_len;
45 45
 	cfg_mapping_t	*mapping = NULL;
46
+	cfg_group_t	*group;
46 47
 	int types;
47 48
 
48 49
 	/* check the number of the variables */
... ...
@@ -61,6 +62,7 @@ int cfg_declare(char *group_name, cfg_def_t *def, void *values, int def_size,
61 61
 	for (i=0, size=0; i<num; i++) {
62 62
 		mapping[i].def = &(def[i]);
63 63
 		mapping[i].name_len = strlen(def[i].name);
64
+		mapping[i].pos = i;
64 65
 		/* record all the types for sanity checks */
65 66
 		types|=1 << CFG_VAR_MASK(def[i].type);
66 67
 
... ...
@@ -141,18 +143,24 @@ int cfg_declare(char *group_name, cfg_def_t *def, void *values, int def_size,
141 141
 
142 142
 	group_name_len = strlen(group_name);
143 143
 	/* check for duplicates */
144
-	if (cfg_lookup_group(group_name, group_name_len)) {
145
-		LOG(L_ERR, "ERROR: register_cfg_def(): "
146
-			"configuration group has been already declared: %s\n",
147
-			group_name);
148
-		goto error;
144
+	if ((group = cfg_lookup_group(group_name, group_name_len))) {
145
+		if (group->dynamic != CFG_GROUP_UNKNOWN) {
146
+			/* conflict with another module/core group, or with a dynamic group */
147
+			LOG(L_ERR, "ERROR: register_cfg_def(): "
148
+				"configuration group has been already declared: %s\n",
149
+				group_name);
150
+			goto error;
151
+		}
152
+		/* An empty group is found which does not have any variable yet */
153
+		cfg_set_group(group, num, mapping, values, size, handle);
154
+	} else {
155
+		/* create a new group
156
+		I will allocate memory in shm mem for the variables later in a single block,
157
+		when we know the size of all the registered groups. */
158
+		if (!(group = cfg_new_group(group_name, group_name_len, num, mapping, values, size, handle)))
159
+			goto error;
149 160
 	}
150
-
151
-	/* create a new group
152
-	I will allocate memory in shm mem for the variables later in a single block,
153
-	when we know the size of all the registered groups. */
154
-	if (!cfg_new_group(group_name, group_name_len, num, mapping, values, size, handle))
155
-		goto error;
161
+	group->dynamic = CFG_GROUP_STATIC;
156 162
 
157 163
 	/* The cfg variables are ready to use, let us set the handle
158 164
 	before passing the new definitions to the drivers.
... ...
@@ -219,13 +227,63 @@ int cfg_declare_str(char *group_name, char *var_name, char *val, char *descr)
219 219
 	return 0;
220 220
 }
221 221
 
222
+/* Add a varibale to a group instance with integer type.
223
+ * The group instance is created if it does not exist.
224
+ * wrapper function for new_add_var()
225
+ */
226
+int cfg_ginst_var_int(char *group_name, unsigned int group_id, char *var_name,
227
+			int val)
228
+{
229
+	str	gname, vname;
230
+
231
+	gname.s = group_name;
232
+	gname.len = strlen(group_name);
233
+	vname.s = var_name;
234
+	vname.len = strlen(var_name);
235
+
236
+	return new_add_var(&gname, group_id, &vname,
237
+			(void *)(long)val, CFG_VAR_INT);
238
+}
239
+
240
+/* Add a varibale to a group instance with string type.
241
+ * The group instance is created if it does not exist.
242
+ * wrapper function for new_add_var()
243
+ */
244
+int cfg_ginst_var_string(char *group_name, unsigned int group_id, char *var_name,
245
+			char *val)
246
+{
247
+	str	gname, vname;
248
+
249
+	gname.s = group_name;
250
+	gname.len = strlen(group_name);
251
+	vname.s = var_name;
252
+	vname.len = strlen(var_name);
253
+
254
+	return new_add_var(&gname, group_id, &vname,
255
+			(void *)val, CFG_VAR_STRING);
256
+}
257
+
258
+/* Create a new group instance.
259
+ * wrapper function for new_add_var()
260
+ */
261
+int cfg_new_ginst(char *group_name, unsigned int group_id)
262
+{
263
+	str	gname;
264
+
265
+	gname.s = group_name;
266
+	gname.len = strlen(group_name);
267
+
268
+	return new_add_var(&gname, group_id, NULL /* var */,
269
+			NULL /* val */, 0 /* type */);
270
+}
271
+
222 272
 /* returns the handle of a cfg group */
223 273
 void **cfg_get_handle(char *gname)
224 274
 {
225 275
 	cfg_group_t	*group;
226 276
 
227 277
 	group = cfg_lookup_group(gname, strlen(gname));
228
-	if (!group || group->dynamic) return NULL;
278
+	if (!group || (group->dynamic != CFG_GROUP_STATIC)) return NULL;
229 279
 
230 280
 	return group->handle;
231 281
 }
... ...
@@ -87,6 +87,25 @@ int cfg_declare_int(char *group_name, char *var_name,
87 87
 /* declares a single variable with str type */
88 88
 int cfg_declare_str(char *group_name, char *var_name, char *val, char *descr);
89 89
 
90
+/* Add a varibale to a group instance with integer type.
91
+ * The group instance is created if it does not exist.
92
+ * wrapper function for new_add_var()
93
+ */
94
+int cfg_ginst_var_int(char *group_name, unsigned int group_id, char *var_name,
95
+			int val);
96
+
97
+/* Add a varibale to a group instance with string type.
98
+ * The group instance is created if it does not exist.
99
+ * wrapper function for new_add_var()
100
+ */
101
+int cfg_ginst_var_string(char *group_name, unsigned int group_id, char *var_name,
102
+			char *val);
103
+
104
+/* Create a new group instance.
105
+ * wrapper function for new_add_var()
106
+ */
107
+int cfg_new_ginst(char *group_name, unsigned int group_id);
108
+
90 109
 /* returns the handle of a cfg group */
91 110
 void **cfg_get_handle(char *gname);
92 111
 
... ...
@@ -77,7 +77,7 @@ int cfg_register_ctx(cfg_ctx_t **handle, cfg_on_declare on_declare_cb)
77 77
 		) {
78 78
 			/* dynamic groups are not ready, the callback
79 79
 			will be called later when the group is fixed-up */
80
-			if (group->dynamic) continue;
80
+			if (group->dynamic != CFG_GROUP_STATIC) continue;
81 81
 
82 82
 			gname.s = group->name;
83 83
 			gname.len = group->name_len;
... ...
@@ -239,6 +239,66 @@ error:
239 239
 		} \
240 240
 	} while(0)
241 241
 
242
+/* returns the size of the variable */
243
+static int cfg_var_size(cfg_mapping_t *var)
244
+{
245
+	switch (CFG_VAR_TYPE(var)) {
246
+
247
+	case CFG_VAR_INT:
248
+		return sizeof(int);
249
+
250
+	case CFG_VAR_STRING:
251
+		return sizeof(char *);
252
+
253
+	case CFG_VAR_STR:
254
+		return sizeof(str);
255
+
256
+	case CFG_VAR_POINTER:
257
+		return sizeof(void *);
258
+
259
+	default:
260
+		LOG(L_CRIT, "BUG: cfg_var_size(): unknown type: %u\n",
261
+			CFG_VAR_TYPE(var));
262
+		return 0;
263
+	}
264
+}
265
+
266
+/* Update the varibales of the array within the meta structure
267
+ * with the new default value.
268
+ * The array is cloned before a change if clone is set to 1.
269
+ */
270
+static int cfg_update_defaults(cfg_group_meta_t	*meta,
271
+				cfg_group_t *group, cfg_mapping_t *var, char *new_val,
272
+				int clone)
273
+{
274
+	int	i, clone_done=0;
275
+	cfg_group_inst_t *array, *ginst;
276
+
277
+	if (!(array = meta->array))
278
+		return 0;
279
+	for (i = 0; i < meta->num; i++) {
280
+		ginst = (cfg_group_inst_t *)((char *)array
281
+			+ (sizeof(cfg_group_inst_t) + group->size - 1) * i);
282
+
283
+		if (!CFG_VAR_TEST(ginst, var)) {
284
+			/* The variable uses the default value, it needs to be rewritten. */
285
+			if (clone && !clone_done) {
286
+				/* The array needs to be cloned before the modification */
287
+				if (!(array = cfg_clone_array(meta, group)))
288
+					return -1;
289
+				ginst = (cfg_group_inst_t *)translate_pointer((char *)array,
290
+								(char *)meta->array,
291
+								(char *)ginst);
292
+				/* re-link the array to the meta-data */
293
+				meta->array = array;
294
+				clone_done = 1;
295
+			}
296
+			memcpy(ginst->vars + var->offset, new_val, cfg_var_size(var)); 
297
+		}
298
+	}
299
+	return 0;
300
+}
301
+
242 302
 /* sets the value of a variable without the need of commit
243 303
  *
244 304
  * return value:
... ...
@@ -246,17 +306,20 @@ error:
246 246
  *  -1: error
247 247
  *   1: variable has not been found
248 248
  */
249
-int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
249
+int cfg_set_now(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
250 250
 			void *val, unsigned int val_type)
251 251
 {
252
+	int		i;
252 253
 	cfg_group_t	*group;
253 254
 	cfg_mapping_t	*var;
254 255
 	void		*p, *v;
255 256
 	cfg_block_t	*block = NULL;
256 257
 	str		s, s2;
257 258
 	char		*old_string = NULL;
258
-	char		**replaced = NULL;
259
+	void		**replaced = NULL;
259 260
 	cfg_child_cb_t	*child_cb = NULL;
261
+	cfg_group_inst_t	*group_inst = NULL, *new_array = NULL;
262
+	unsigned char		*var_block;
260 263
 
261 264
 	/* verify the context even if we do not need it now
262 265
 	to make sure that a cfg driver has called the function
... ...
@@ -266,6 +329,15 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
266 266
 		return -1;
267 267
 	}
268 268
 
269
+	if (group_id && !cfg_shmized) {
270
+		/* The config group has not been shmized yet,
271
+		but an additional instance of a variable needs to be added to the group.
272
+		Add this instance to the linked list of variables, they
273
+		will be fixed later. */
274
+		return new_add_var(group_name, *group_id, var_name,
275
+				val, val_type);
276
+	}
277
+
269 278
 	/* look-up the group and the variable */
270 279
 	if (cfg_lookup_var(group_name, var_name, &group, &var))
271 280
 		return 1;
... ...
@@ -276,6 +348,19 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
276 276
 		goto error0;
277 277
 	}
278 278
 
279
+	/* The additional variable instances having per-child process callback
280
+	 * with CFG_CB_ONLY_ONCE flag cannot be rewritten.
281
+	 * The reason is that such variables typically set global parameters
282
+	 * as opposed to per-process variables. Hence, it is not possible to set
283
+	 * the group handle temporary to another block, and then reset it back later. */
284
+	if (group_id
285
+		&& var->def->on_set_child_cb
286
+		&& var->def->type & CFG_CB_ONLY_ONCE
287
+	) {
288
+		LOG(L_ERR, "ERROR: cfg_set_now(): This variable does not support muliple values.\n");
289
+		goto error0;
290
+	}
291
+
279 292
 	/* check whether we have to convert the type */
280 293
 	if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
281 294
 		goto error0;
... ...
@@ -294,7 +379,25 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
294 294
 		/* Call the fixup function.
295 295
 		There is no need to set a temporary cfg handle,
296 296
 		becaue a single variable is changed */
297
-		if (var->def->on_change_cb(*(group->handle),
297
+		if (!group_id) {
298
+			var_block = *(group->handle);
299
+		} else {
300
+			if (!cfg_local) {
301
+				LOG(L_ERR, "ERROR: cfg_set_now(): Local configuration is missing\n");
302
+				goto error0;
303
+			}
304
+			group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, group),
305
+							group->size,
306
+							*group_id);
307
+			if (!group_inst) {
308
+				LOG(L_ERR, "ERROR: cfg_set_now(): local group instance %.*s[%u] is not found\n",
309
+					group_name->len, group_name->s, *group_id);
310
+				goto error0;
311
+			}
312
+			var_block = group_inst->vars;
313
+		}
314
+
315
+		if (var->def->on_change_cb(var_block,
298 316
 						group_name,
299 317
 						var_name,
300 318
 						&v) < 0) {
... ...
@@ -304,7 +407,10 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
304 304
 
305 305
 	}
306 306
 
307
-	if (var->def->on_set_child_cb) {
307
+	/* Set the per-child process callback only if the default value is changed.
308
+	 * The callback of other instances will be called when the config is
309
+	 * switched to that instance. */
310
+	if (!group_id && var->def->on_set_child_cb) {
308 311
 		/* get the name of the variable from the internal struct,
309 312
 		because var_name may be freed before the callback needs it */
310 313
 		s.s = group->name;
... ...
@@ -325,16 +431,51 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
325 325
 		while the new one is prepared */
326 326
 		CFG_WRITER_LOCK();
327 327
 
328
+		if (group_id) {
329
+			group_inst = cfg_find_group(CFG_GROUP_META(*cfg_global, group),
330
+							group->size,
331
+							*group_id);
332
+			if (!group_inst) {
333
+				LOG(L_ERR, "ERROR: cfg_set_now(): global group instance %.*s[%u] is not found\n",
334
+					group_name->len, group_name->s, *group_id);
335
+				goto error;
336
+			}
337
+			var_block = group_inst->vars;
338
+		} else {
339
+			group_inst = NULL;
340
+			var_block = CFG_GROUP_DATA(*cfg_global, group);
341
+		}
342
+
328 343
 		if (var->def->type & CFG_ATOMIC) {
329 344
 			/* atomic change is allowed, we can rewrite the value
330 345
 			directly in the global config */
331
-			p = (*cfg_global)->vars+group->offset+var->offset;
346
+			p = var_block + var->offset;
332 347
 
333 348
 		} else {
334 349
 			/* clone the memory block, and prepare the modification */
335 350
 			if (!(block = cfg_clone_global())) goto error;
336 351
 
337
-			p = block->vars+group->offset+var->offset;
352
+			if (group_inst) {
353
+				/* The additional array of the group needs to be also cloned.
354
+				 * When any of the variables within this array is changed, then
355
+				 * the complete config block and this array is replaced. */
356
+				if (!(new_array = cfg_clone_array(CFG_GROUP_META(*cfg_global, group), group)))
357
+					goto error;
358
+				group_inst = (cfg_group_inst_t *)translate_pointer((char *)new_array,
359
+					(char *)CFG_GROUP_META(*cfg_global, group)->array,
360
+					(char *)group_inst);
361
+				var_block = group_inst->vars;
362
+				CFG_GROUP_META(block, group)->array = new_array;
363
+			} else {
364
+				/* The additional array may need to be replaced depending
365
+				 * on whether or not there is any variable in the array set
366
+				 * to the default value which is changed now. If this is the case,
367
+				 * then the array will be replaced later when the variables are
368
+				 * updated.
369
+				 */
370
+				var_block = CFG_GROUP_DATA(block, group);
371
+			}
372
+			p = var_block + var->offset;
338 373
 		}
339 374
 	} else {
340 375
 		/* we are allowed to rewrite the value on-the-fly
... ...
@@ -371,18 +512,43 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
371 371
 		break;
372 372
 
373 373
 	}
374
+	if (group_inst && !CFG_VAR_TEST_AND_SET(group_inst, var))
375
+		old_string = NULL; /* the string is the same as the default one,
376
+					it cannot be freed */
374 377
 
375 378
 	if (cfg_shmized) {
376
-		if (old_string) {
379
+		if (!group_inst) {
380
+			/* the default value is changed, the copies of this value
381
+			need to be also updated */
382
+			if (cfg_update_defaults(CFG_GROUP_META(block ? block : *cfg_global, group),
383
+						group, var, p,
384
+						block ? 1 : 0) /* clone if needed */
385
+			)
386
+				goto error;
387
+			if (block && (CFG_GROUP_META(block, group)->array != CFG_GROUP_META(*cfg_global, group)->array))
388
+				new_array = CFG_GROUP_META(block, group)->array;
389
+		}
390
+
391
+		if (old_string || new_array) {
377 392
 			/* prepare the array of the replaced strings,
393
+			and replaced group instances,
378 394
 			they will be freed when the old block is freed */
379
-			replaced = (char **)shm_malloc(sizeof(char *)*2);
395
+			replaced = (void **)shm_malloc(sizeof(void *)
396
+					* ((old_string?1:0) + (new_array?1:0) + 1));
380 397
 			if (!replaced) {
381 398
 				LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
382 399
 				goto error;
383 400
 			}
384
-			replaced[0] = old_string;
385
-			replaced[1] = NULL;
401
+			i = 0;
402
+			if (old_string) {
403
+				replaced[i] = old_string;
404
+				i++;
405
+			}
406
+			if (new_array) {	
407
+				replaced[i] = CFG_GROUP_META(*cfg_global, group)->array;
408
+				i++;
409
+			}
410
+			replaced[i] = NULL;
386 411
 		}
387 412
 		/* replace the global config with the new one */
388 413
 		if (block) cfg_install_global(block, replaced, child_cb, child_cb);
... ...
@@ -423,6 +589,9 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
423 423
 			group_name->len, group_name->s,
424 424
 			var_name->len, var_name->s,
425 425
 			((str *)val)->len, ((str *)val)->s);
426
+	if (group_id)
427
+		LOG(L_INFO, "INFO: cfg_set_now(): group id = %u\n",
428
+			*group_id);
426 429
 
427 430
 	convert_val_cleanup();
428 431
 	return 0;
... ...
@@ -430,7 +599,9 @@ int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
430 430
 error:
431 431
 	if (cfg_shmized) CFG_WRITER_UNLOCK();
432 432
 	if (block) cfg_block_free(block);
433
+	if (new_array) shm_free(new_array);
433 434
 	if (child_cb) cfg_child_cb_free(child_cb);
435
+	if (replaced) shm_free(replaced);
434 436
 
435 437
 error0:
436 438
 	LOG(L_ERR, "ERROR: cfg_set_now(): failed to set the variable: %.*s.%.*s\n",
... ...
@@ -443,45 +614,27 @@ error0:
443 443
 }
444 444
 
445 445
 /* wrapper function for cfg_set_now */
446
-int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
446
+int cfg_set_now_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
447
+			int val)
447 448
 {
448
-	return cfg_set_now(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
449
+	return cfg_set_now(ctx, group_name, group_id, var_name,
450
+				(void *)(long)val, CFG_VAR_INT);
449 451
 }
450 452
 
451 453
 /* wrapper function for cfg_set_now */
452
-int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
454
+int cfg_set_now_string(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
455
+			char *val)
453 456
 {
454
-	return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
457
+	return cfg_set_now(ctx, group_name, group_id, var_name,
458
+				(void *)val, CFG_VAR_STRING);
455 459
 }
456 460
 
457 461
 /* wrapper function for cfg_set_now */
458
-int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
462
+int cfg_set_now_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
463
+			str *val)
459 464
 {
460
-	return cfg_set_now(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
461
-}
462
-
463
-/* returns the size of the variable */
464
-static int cfg_var_size(cfg_mapping_t *var)
465
-{
466
-	switch (CFG_VAR_TYPE(var)) {
467
-
468
-	case CFG_VAR_INT:
469
-		return sizeof(int);
470
-
471
-	case CFG_VAR_STRING:
472
-		return sizeof(char *);
473
-
474
-	case CFG_VAR_STR:
475
-		return sizeof(str);
476
-
477
-	case CFG_VAR_POINTER:
478
-		return sizeof(void *);
479
-
480
-	default:
481
-		LOG(L_CRIT, "BUG: cfg_var_size(): unknown type: %u\n",
482
-			CFG_VAR_TYPE(var));
483
-		return 0;
484
-	}
465
+	return cfg_set_now(ctx, group_name, group_id, var_name,
466
+				(void *)val, CFG_VAR_STR);
485 467
 }
486 468
 
487 469
 /* sets the value of a variable but does not commit the change
... ...
@@ -491,22 +644,24 @@ static int cfg_var_size(cfg_mapping_t *var)
491 491
  *  -1: error
492 492
  *   1: variable has not been found
493 493
  */
494
-int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
494
+int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
495 495
 			void *val, unsigned int val_type)
496 496
 {
497 497
 	cfg_group_t	*group;
498 498
 	cfg_mapping_t	*var;
499 499
 	void		*v;
500
-	char		*temp_handle;
500
+	unsigned char	*temp_handle;
501 501
 	int		temp_handle_created;
502
-	cfg_changed_var_t	*changed = NULL;
502
+	cfg_changed_var_t	*changed = NULL, **changed_p;
503 503
 	int		size;
504 504
 	str		s;
505
+	cfg_group_inst_t	*group_inst = NULL;
506
+	unsigned char		*var_block;
505 507
 
506 508
 	if (!cfg_shmized)
507 509
 		/* the cfg has not been shmized yet, there is no
508 510
 		point in registering the change and committing it later */
509
-		return cfg_set_now(ctx, group_name, var_name,
511
+		return cfg_set_now(ctx, group_name, group_id, var_name,
510 512
 					val, val_type);
511 513
 
512 514
 	if (!ctx) {
... ...
@@ -524,6 +679,19 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
524 524
 		goto error0;
525 525
 	}
526 526
 
527
+	/* The additional variable instances having per-child process callback
528
+	 * with CFG_CB_ONLY_ONCE flag cannot be rewritten.
529
+	 * The reason is that such variables typically set global parameters
530
+	 * as opposed to per-process variables. Hence, it is not possible to set
531
+	 * the group handle temporary to another block, and then reset it back later. */
532
+	if (group_id
533
+		&& var->def->on_set_child_cb
534
+		&& var->def->type & CFG_CB_ONLY_ONCE
535
+	) {
536
+		LOG(L_ERR, "ERROR: cfg_set_delayed(): This variable does not support muliple values.\n");
537
+		goto error0;
538
+	}
539
+
527 540
 	/* check whether we have to convert the type */
528 541
 	if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
529 542
 		goto error0;
... ...
@@ -549,31 +717,56 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
549 549
 		Only the values within the group are applied,
550 550
 		other modifications are not visible to the callback.
551 551
 		The local config is the base. */
552
+		if (!group_id) {
553
+			var_block = *(group->handle);
554
+		} else {
555
+			if (!cfg_local) {
556
+				LOG(L_ERR, "ERROR: cfg_set_delayed(): Local configuration is missing\n");
557
+				goto error;
558
+			}
559
+			group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, group),
560
+							group->size,
561
+							*group_id);
562
+			if (!group_inst) {
563
+				LOG(L_ERR, "ERROR: cfg_set_delayed(): local group instance %.*s[%u] is not found\n",
564
+					group_name->len, group_name->s, *group_id);
565
+				goto error;
566
+			}
567
+			var_block = group_inst->vars;
568
+		}
552 569
 
553 570
 		if (ctx->changed_first) {
554
-			temp_handle = (char *)pkg_malloc(group->size);
571
+			temp_handle = (unsigned char *)pkg_malloc(group->size);
555 572
 			if (!temp_handle) {
556 573
 				LOG(L_ERR, "ERROR: cfg_set_delayed(): "
557 574
 					"not enough memory\n");
558 575
 				goto error;
559 576
 			}
560 577
 			temp_handle_created = 1;
561
-			memcpy(temp_handle, *(group->handle), group->size);
578
+			memcpy(temp_handle, var_block, group->size);
562 579
 
563 580
 			/* apply the changes */
564 581
 			for (	changed = ctx->changed_first;
565 582
 				changed;
566 583
 				changed = changed->next
567 584
 			) {
568
-				if (changed->group != group) continue;
569
-
570
-				memcpy(	temp_handle + changed->var->offset,
571
-					changed->new_val.vraw,
572
-					cfg_var_size(changed->var));
585
+				if (changed->group != group)
586
+					continue;
587
+				if ((!group_id && !changed->group_id_set) /* default values */
588
+					|| (group_id && !changed->group_id_set
589
+						&& !CFG_VAR_TEST(group_inst, changed->var))
590
+							/* default value is changed which affects the group_instance */
591
+					|| (group_id && changed->group_id_set
592
+						&& (*group_id == changed->group_id))
593
+							/* change within the group instance */
594
+				)
595
+					memcpy(	temp_handle + changed->var->offset,
596
+						changed->new_val.vraw,
597
+						cfg_var_size(changed->var));
573 598
 			}
574 599
 		} else {
575 600
 			/* there is not any change */
576
-			temp_handle = *(group->handle);
601
+			temp_handle = var_block;
577 602
 			temp_handle_created = 0;
578 603
 		}
579 604
 			
... ...
@@ -600,6 +793,10 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
600 600
 	memset(changed, 0, size);
601 601
 	changed->group = group;
602 602
 	changed->var = var;
603
+	if (group_id) {
604
+		changed->group_id = *group_id;
605
+		changed->group_id_set = 1;
606
+	}
603 607
 
604 608
 	switch (CFG_VAR_TYPE(var)) {
605 609
 
... ...
@@ -628,15 +825,27 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
628 628
 
629 629
 	}
630 630
 
631
-	/* Add the new item to the end of the linked list,
632
-	The commit will go though the list from the first item,
633
-	so the list is kept in order */
634
-	if (ctx->changed_first)
635
-		ctx->changed_last->next = changed;
636
-	else
637
-		ctx->changed_first = changed;
638
-
639
-	ctx->changed_last = changed;
631
+	/* Order the changes by group + group_id + original order.
632
+	 * Hence, the list is still kept in order within the group.
633
+	 * The changes can be committed faster this way, the group instances
634
+	 * do not have to be looked-up for each and every variable. */
635
+	/* Check whether there is any variable in the list which
636
+	belongs to the same group */
637
+	for (	changed_p = &ctx->changed_first;
638
+		*changed_p && ((*changed_p)->group != changed->group);
639
+		changed_p = &(*changed_p)->next);
640
+	/* try to find the group instance, and move changed_p to the end of
641
+	the instance. */
642
+	for (	;
643
+		*changed_p
644
+			&& ((*changed_p)->group == changed->group)
645
+			&& (!(*changed_p)->group_id_set
646
+				|| ((*changed_p)->group_id_set && changed->group_id_set
647
+					&& ((*changed_p)->group_id <= changed->group_id)));
648
+		changed_p = &(*changed_p)->next);
649
+	/* Add the new variable before *changed_p */
650
+	changed->next = *changed_p;
651
+	*changed_p = changed;
640 652
 
641 653
 	CFG_CTX_UNLOCK(ctx);
642 654
 
... ...
@@ -666,6 +875,11 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
666 666
 			var_name->len, var_name->s,
667 667
 			((str *)val)->len, ((str *)val)->s,
668 668
 			ctx);
669
+	if (group_id)
670
+		LOG(L_INFO, "INFO: cfg_set_delayed(): group id = %u "
671
+			"[context=%p]\n",
672
+			*group_id,
673
+			ctx);
669 674
 
670 675
 	convert_val_cleanup();
671 676
 	return 0;
... ...
@@ -683,21 +897,27 @@ error0:
683 683
 }
684 684
 
685 685
 /* wrapper function for cfg_set_delayed */
686
-int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, str *var_name, int val)
686
+int cfg_set_delayed_int(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
687
+				int val)
687 688
 {
688
-	return cfg_set_delayed(ctx, group_name, var_name, (void *)(long)val, CFG_VAR_INT);
689
+	return cfg_set_delayed(ctx, group_name, group_id, var_name,
690
+				(void *)(long)val, CFG_VAR_INT);
689 691
 }
690 692
 
691 693
 /* wrapper function for cfg_set_delayed */
692
-int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, str *var_name, char *val)
694
+int cfg_set_delayed_string(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
695
+				char *val)
693 696
 {
694
-	return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STRING);
697
+	return cfg_set_delayed(ctx, group_name, group_id, var_name,
698
+				(void *)val, CFG_VAR_STRING);
695 699
 }
696 700
 
697 701
 /* wrapper function for cfg_set_delayed */
698
-int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, str *var_name, str *val)
702
+int cfg_set_delayed_str(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
703
+				str *val)
699 704
 {
700
-	return cfg_set_delayed(ctx, group_name, var_name, (void *)val, CFG_VAR_STR);
705
+	return cfg_set_delayed(ctx, group_name, group_id, var_name,
706
+				(void *)val, CFG_VAR_STR);
701 707
 }
702 708
 
703 709
 /* commits the previously prepared changes within the context */
... ...
@@ -705,14 +925,16 @@ int cfg_commit(cfg_ctx_t *ctx)
705 705
 {
706 706
 	int	replaced_num = 0;
707 707
 	cfg_changed_var_t	*changed, *changed2;
708
-	cfg_block_t	*block;
709
-	char	**replaced = NULL;
708
+	cfg_block_t	*block = NULL;
709
+	void	**replaced = NULL;
710 710
 	cfg_child_cb_t	*child_cb;
711 711
 	cfg_child_cb_t	*child_cb_first = NULL;
712 712
 	cfg_child_cb_t	*child_cb_last = NULL;
713 713
 	int	size;
714 714
 	void	*p;
715 715
 	str	s, s2;
716
+	cfg_group_t	*group;
717
+	cfg_group_inst_t	*group_inst = NULL;
716 718
 
717 719
 	if (!ctx) {
718 720
 		LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
... ...
@@ -728,19 +950,30 @@ int cfg_commit(cfg_ctx_t *ctx)
728 728
 	/* is there any change? */
729 729
 	if (!ctx->changed_first) goto done;
730 730
 
731
-	/* count the number of replaced strings,
732
-	and prepare the linked list of per-child process
733
-	callbacks, that will be added to the global list */
734
-	for (	changed = ctx->changed_first;
731
+	/* Count the number of replaced strings,
732
+	and replaced group arrays.
733
+	Prepare the linked list of per-child process
734
+	callbacks, that will be added to the global list. */
735
+	for (	changed = ctx->changed_first, group = NULL;
735 736
 		changed;
736 737
 		changed = changed->next
737 738
 	) {
739
+		/* Each string/str potentially causes an old string to be freed
740
+		 * unless the variable of an additional group instance is set
741
+		 * which uses the default value. This case cannot be determined
742
+		 * without locking *cfg_global, hence, it is better to count these
743
+		 * strings as well even though the slot might not be used later. */
738 744
 		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
739 745
 		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
740 746
 			replaced_num++;
741 747
 
748
+		/* See the above comments for strings */
749
+		if (group != changed->group) {
750
+			replaced_num++;
751
+			group = changed->group;
752
+		}
742 753
 
743
-		if (changed->var->def->on_set_child_cb) {
754
+		if (!changed->group_id_set && changed->var->def->on_set_child_cb) {
744 755
 			s.s = changed->group->name;
745 756
 			s.len = changed->group->name_len;
746 757
 			s2.s = changed->var->def->name;
... ...
@@ -760,11 +993,11 @@ int cfg_commit(cfg_ctx_t *ctx)
760 760
 
761 761
 	if (replaced_num) {
762 762
 		/* allocate memory for the replaced string array */
763
-		size = sizeof(char *)*(replaced_num + 1);
764
-		replaced = (char **)shm_malloc(size);
763
+		size = sizeof(void *)*(replaced_num + 1);
764
+		replaced = (void **)shm_malloc(size);
765 765
 		if (!replaced) {
766 766
 			LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
767
-			goto error;
767
+			goto error0;
768 768
 		}
769 769
 		memset(replaced, 0 , size);
770 770
 	}
... ...
@@ -774,23 +1007,57 @@ int cfg_commit(cfg_ctx_t *ctx)
774 774
 	CFG_WRITER_LOCK();
775 775
 
776 776
 	/* clone the memory block, and prepare the modification */
777
-	if (!(block = cfg_clone_global())) {
778
-		CFG_WRITER_UNLOCK();
777
+	if (!(block = cfg_clone_global()))
779 778
 		goto error;
780
-	}
781 779
 
782
-	/* apply the modifications to the buffer */
780
+	/* Apply the modifications to the buffer.
781
+	Note that the cycle relies on the order of the groups and group instances, i.e.
782
+	the order is group + group_id + order of commits. */
783 783
 	replaced_num = 0;
784
-	for (	changed = ctx->changed_first;
784
+	for (	changed = ctx->changed_first, group = NULL; /* group points to the
785
+							last group array that has been cloned */
785 786
 		changed;
786 787
 		changed = changed->next
787 788
 	) {
788
-		p = block->vars
789
-			+ changed->group->offset
790
-			+ changed->var->offset;
789
+		if (!changed->group_id_set) {
790
+			p = CFG_GROUP_DATA(block, changed->group)
791
+				+ changed->var->offset;
792
+			group_inst = NULL; /* force the look-up of the next group_inst */
793
+		} else {
794
+			if (group != changed->group) {
795
+				/* The group array has not been cloned yet. */
796
+				group = changed->group;
797
+				if (!(CFG_GROUP_META(block, group)->array = 
798
+					cfg_clone_array(CFG_GROUP_META(*cfg_global, group), group))
799
+				) {
800
+					LOG(L_ERR, "ERROR: cfg_commit(): group array cannot be cloned for %.*s[%u]\n",
801
+						group->name_len, group->name, changed->group_id);
802
+					goto error;
803
+				}
804
+
805
+				replaced[replaced_num] = CFG_GROUP_META(*cfg_global, group)->array;
806
+				replaced_num++;
791 807
 
792
-		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
793
-		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
808
+				group_inst = NULL; /* fore the look-up of group_inst */
809
+			}
810
+			if (!group_inst || (group_inst->id != changed->group_id)) {
811
+				group_inst = cfg_find_group(CFG_GROUP_META(block, group),
812
+								group->size,
813
+								changed->group_id);
814
+			}
815
+			if (!group_inst) {
816
+				LOG(L_ERR, "ERROR: cfg_commit(): global group instance %.*s[%u] is not found\n",
817
+					group->name_len, group->name, changed->group_id);
818
+				goto error;
819
+			}
820
+			p = group_inst->vars + changed->var->offset;
821
+		}
822
+
823
+		if (((changed->group_id_set && CFG_VAR_TEST_AND_SET(group_inst, changed->var))
824
+			|| !changed->group_id_set)
825
+		&& ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
826
+			|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) 
827
+		) {
794 828
 			replaced[replaced_num] = *(char **)p;
795 829
 			if (replaced[replaced_num])
796 830
 				replaced_num++;
... ...
@@ -802,13 +1069,33 @@ int cfg_commit(cfg_ctx_t *ctx)
802 802
 		memcpy(	p,
803 803
 			changed->new_val.vraw,
804 804
 			cfg_var_size(changed->var));
805
+
806
+		if (!changed->group_id_set) {
807
+			/* the default value is changed, the copies of this value
808
+			need to be also updated */
809
+			if (cfg_update_defaults(CFG_GROUP_META(block, changed->group),
810
+						changed->group, changed->var, p,
811
+						(group != changed->group)) /* clone if the array
812
+									has not been cloned yet */
813
+                        )
814
+                                goto error;
815
+                        if ((group != changed->group)
816
+				&& (CFG_GROUP_META(block, changed->group)->array != CFG_GROUP_META(*cfg_global, changed->group)->array)
817
+			) {
818
+				/* The array has been cloned */
819
+				group = changed->group;
820
+
821
+				replaced[replaced_num] = CFG_GROUP_META(*cfg_global, group)->array;
822
+				replaced_num++;
823
+			}
824
+		}
805 825
 	}
806 826
 
807 827
 	/* replace the global config with the new one */
808 828
 	cfg_install_global(block, replaced, child_cb_first, child_cb_last);
809 829
 	CFG_WRITER_UNLOCK();
810 830
 
811
-	/* free the changed list */	
831
+	/* free the changed list */
812 832
 	for (	changed = ctx->changed_first;
813 833
 		changed;
814 834
 		changed = changed2
... ...
@@ -817,7 +1104,6 @@ int cfg_commit(cfg_ctx_t *ctx)
817 817
 		shm_free(changed);
818 818
 	}
819 819
 	ctx->changed_first = NULL;
820
-	ctx->changed_last = NULL;
821 820
 
822 821
 done:
823 822
 	LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
... ...
@@ -828,9 +1114,24 @@ done:
828 828
 	return 0;
829 829
 
830 830
 error:
831
-	CFG_CTX_UNLOCK(ctx);
831
+	if (block) {
832
+		/* clean the new block from the cloned arrays */
833
+		for (	group = cfg_group;
834
+			group;
835
+			group = group->next
836
+		)
837
+			if (CFG_GROUP_META(block, group)->array
838
+				&& (CFG_GROUP_META(block, group)->array != CFG_GROUP_META(*cfg_global, group)->array)
839
+			)
840
+				shm_free(CFG_GROUP_META(block, group)->array);
841
+		/* the block can be freed outside of the writer lock */
842
+	}
843
+	CFG_WRITER_UNLOCK();
844
+	if (block)
845
+		shm_free(block);
832 846
 
833 847
 error0:
848
+	CFG_CTX_UNLOCK(ctx);
834 849
 
835 850
 	if (child_cb_first) cfg_child_cb_free(child_cb_first);
836 851
 	if (replaced) shm_free(replaced);
... ...
@@ -872,7 +1173,6 @@ int cfg_rollback(cfg_ctx_t *ctx)
872 872
 		shm_free(changed);
873 873
 	}
874 874
 	ctx->changed_first = NULL;
875
-	ctx->changed_last = NULL;
876 875
 
877 876
 	CFG_CTX_UNLOCK(ctx);
878 877
 
... ...
@@ -885,7 +1185,7 @@ int cfg_rollback(cfg_ctx_t *ctx)
885 885
  * -1 - error
886 886
  *  1 - variable exists, but it is not readable
887 887
  */
888
-int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
888
+int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str *var_name,
889 889
 			void **val, unsigned int *val_type)
890 890
 {
891 891
 	cfg_group_t	*group;
... ...
@@ -893,6 +1193,7 @@ int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
893 893
 	void		*p;
894 894
 	static str	s;	/* we need the value even
895 895
 				after the function returns */
896
+	cfg_group_inst_t	*group_inst;
896 897
 
897 898
 	/* verify the context even if we do not need it now
898 899
 	to make sure that a cfg driver has called the function
... ...
@@ -913,10 +1214,27 @@ int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
913 913
 		return 1;
914 914
 	}
915 915
 
916
-	/* use the module's handle to access the variable
917
-	It means that the variable is read from the local config
918
-	after forking */
919
-	p = *(group->handle) + var->offset;
916
+	if (group_id) {
917
+		if (!cfg_local) {
918
+			LOG(L_ERR, "ERROR: cfg_get_by_name(): Local configuration is missing\n");
919
+			return -1;
920
+		}
921
+		group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, group),
922
+						group->size,
923
+						*group_id);
924
+		if (!group_inst) {
925
+			LOG(L_ERR, "ERROR: cfg_get_by_name(): local group instance %.*s[%u] is not found\n",
926
+				group_name->len, group_name->s, *group_id);
927
+			return -1;
928
+		}
929
+		p = group_inst->vars + var->offset;
930
+
931
+	} else {
932
+		/* use the module's handle to access the variable
933
+		It means that the variable is read from the local config
934
+		after forking */
935
+		p = *(group->handle) + var->offset;
936
+	}
920 937
 
921 938
 	switch (CFG_VAR_TYPE(var)) {
922 939
 	case CFG_VAR_INT:
... ...
@@ -1005,13 +1323,18 @@ int cfg_diff_init(cfg_ctx_t *ctx,
1005 1005
 
1006 1006
 /* return the pending changes that have not been
1007 1007
  * committed yet
1008
+ * return value:
1009
+ *	1: valid value is found
1010
+ *	0: no more changed value found
1011
+ *	-1: error occured 
1008 1012
  */
1009 1013
 int cfg_diff_next(void **h,
1010
-			str *gname, str *vname,
1014
+			str *gname, unsigned int **gid, str *vname,
1011 1015
 			void **old_val, void **new_val,
1012 1016
 			unsigned int *val_type)
1013 1017
 {
1014 1018
 	cfg_changed_var_t	*changed;
1019
+	cfg_group_inst_t	*group_inst;
1015 1020
 	union cfg_var_value* pval;
1016 1021
 	static str	old_s, new_s;	/* we need the value even
1017 1022
 					after the function returns */
... ...
@@ -1021,14 +1344,32 @@ int cfg_diff_next(void **h,
1021 1021
 
1022 1022
 	gname->s = changed->group->name;
1023 1023
 	gname->len = changed->group->name_len;
1024
+	*gid = (changed->group_id_set ? &changed->group_id : NULL);
1024 1025
 	vname->s = changed->var->def->name;
1025 1026
 	vname->len = changed->var->name_len;
1026 1027
 
1027 1028
 	/* use the module's handle to access the variable
1028 1029
 	It means that the variable is read from the local config
1029 1030
 	after forking */
1030
-	pval = (union cfg_var_value*)
1031
-			(*(changed->group->handle) + changed->var->offset);
1031
+	if (!changed->group_id_set) {
1032
+		pval = (union cfg_var_value*)
1033
+				(*(changed->group->handle) + changed->var->offset);
1034
+	} else {
1035
+		if (!cfg_local) {
1036
+			LOG(L_ERR, "ERROR: cfg_diff_next(): Local configuration is missing\n");
1037
+			return -1;
1038
+		}
1039
+		group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, changed->group),
1040
+						changed->group->size,
1041
+						changed->group_id);
1042
+		if (!group_inst) {
1043
+			LOG(L_ERR, "ERROR: cfg_diff_next(): local group instance %.*s[%u] is not found\n",
1044
+				changed->group->name_len, changed->group->name, changed->group_id);
1045
+			return -1;
1046
+		}
1047
+		pval = (union cfg_var_value*)
1048
+				(group_inst->vars + changed->var->offset);
1049
+	}
1032 1050
 
1033 1051
 	switch (CFG_VAR_TYPE(changed->var)) {
1034 1052
 	case CFG_VAR_INT:
... ...
@@ -1070,3 +1411,364 @@ void cfg_diff_release(cfg_ctx_t *ctx)
1070 1070
 
1071 1071
 	CFG_CTX_UNLOCK(ctx);
1072 1072
 }
1073
+
1074
+/* Add a new instance to an existing group */
1075
+int cfg_add_group_inst(cfg_ctx_t *ctx, str *group_name, unsigned int group_id)
1076
+{
1077
+	cfg_group_t	*group;
1078
+	cfg_block_t	*block = NULL;
1079
+	void		**replaced = NULL;
1080
+	cfg_group_inst_t	*new_array = NULL, *new_inst;
1081
+
1082
+	/* verify the context even if we do not need it now
1083
+	to make sure that a cfg driver has called the function
1084
+	(very very weak security) */
1085
+	if (!ctx) {
1086
+		LOG(L_ERR, "ERROR: cfg_add_group_inst(): context is undefined\n");
1087
+		return -1;
1088
+	}
1089
+
1090
+	if (!cfg_shmized) {
1091
+		/* Add a new variable without any value to
1092
+		the linked list of additional values. This variable
1093
+		will force a new group instance to be created. */
1094
+		return new_add_var(group_name, group_id,  NULL /* var_name */,
1095
+					NULL /* val */, 0 /* type */);
1096
+	}
1097
+
1098
+	if (!(group = cfg_lookup_group(group_name->s, group_name->len))) {
1099
+		LOG(L_ERR, "ERROR: cfg_add_group_inst(): group not found\n");
1100
+		return -1;
1101
+	}
1102
+
1103
+	/* make sure that nobody else replaces the global config
1104
+	while the new one is prepared */
1105
+	CFG_WRITER_LOCK();
1106
+	if (cfg_find_group(CFG_GROUP_META(*cfg_global, group),
1107
+							group->size,
1108
+							group_id)
1109
+	) {
1110
+		LOG(L_DBG, "DEBUG: cfg_add_group_inst(): the group instance already exists\n");
1111
+		CFG_WRITER_UNLOCK();
1112
+		return 0; /* not an error */
1113
+	}
1114
+
1115
+	/* clone the global memory block because the additional array can be
1116
+	replaced only together with the block. */
1117
+	if (!(block = cfg_clone_global()))
1118
+		goto error;
1119
+
1120
+	/* Extend the array with a new group instance */
1121
+	if (!(new_array = cfg_extend_array(CFG_GROUP_META(*cfg_global, group), group,
1122
+					group_id,
1123
+					&new_inst))
1124
+	)
1125
+		goto error;
1126
+
1127
+	/* fill in the new group instance with the default data */
1128
+	memcpy(	new_inst->vars,
1129
+		CFG_GROUP_DATA(*cfg_global, group),
1130
+		group->size);
1131
+
1132
+	CFG_GROUP_META(block, group)->array = new_array;
1133
+	CFG_GROUP_META(block, group)->num++;
1134
+
1135
+	if (CFG_GROUP_META(*cfg_global, group)->array) {
1136
+		/* prepare the array of the replaced strings,
1137
+		and replaced group instances,
1138
+		they will be freed when the old block is freed */
1139
+		replaced = (void **)shm_malloc(sizeof(void *) * 2);
1140
+		if (!replaced) {
1141
+			LOG(L_ERR, "ERROR: cfg_add_group_inst(): not enough shm memory\n");
1142
+			goto error;
1143
+		}
1144
+		replaced[0] = CFG_GROUP_META(*cfg_global, group)->array;
1145
+		replaced[1] = NULL;
1146
+	}
1147
+	/* replace the global config with the new one */
1148
+	cfg_install_global(block, replaced, NULL, NULL);
1149
+	CFG_WRITER_UNLOCK();
1150
+
1151
+	LOG(L_INFO, "INFO: cfg_add_group_inst(): "
1152
+		"group instance is added: %.*s[%u]\n",
1153
+		group_name->len, group_name->s,
1154
+		group_id);
1155
+
1156
+	return 0;
1157
+error:
1158
+	CFG_WRITER_UNLOCK();
1159
+	if (block) cfg_block_free(block);
1160
+	if (new_array) shm_free(new_array);
1161
+	if (replaced) shm_free(replaced);
1162
+
1163
+	LOG(L_ERR, "ERROR: cfg_add_group_inst(): "
1164
+		"Failed to add the group instance: %.*s[%u]\n",
1165
+		group_name->len, group_name->s,
1166
+		group_id);
1167
+
1168
+	return -1;
1169
+}
1170
+
1171
+/* Delete an instance of a group */
1172
+int cfg_del_group_inst(cfg_ctx_t *ctx, str *group_name, unsigned int group_id)
1173
+{
1174
+	cfg_group_t	*group;
1175
+	cfg_block_t	*block = NULL;
1176
+	void		**replaced = NULL;
1177
+	cfg_group_inst_t	*new_array = NULL, *group_inst;
1178
+
1179
+	/* verify the context even if we do not need it now
1180
+	to make sure that a cfg driver has called the function
1181
+	(very very weak security) */
1182
+	if (!ctx) {
1183
+		LOG(L_ERR, "ERROR: cfg_del_group_inst(): context is undefined\n");
1184
+		return -1;
1185
+	}
1186
+
1187
+	if (!cfg_shmized) {
1188
+		/* It makes no sense to delete a group instance that has not
1189
+		been created yet */
1190
+		return -1;
1191
+	}
1192
+
1193
+	if (!(group = cfg_lookup_group(group_name->s, group_name->len))) {
1194
+		LOG(L_ERR, "ERROR: cfg_del_group_inst(): group not found\n");
1195
+		return -1;
1196
+	}
1197
+
1198
+	/* make sure that nobody else replaces the global config
1199
+	while the new one is prepared */
1200
+	CFG_WRITER_LOCK();
1201
+	if (!(group_inst = cfg_find_group(CFG_GROUP_META(*cfg_global, group),
1202
+							group->size,
1203
+							group_id))
1204
+	) {
1205
+		LOG(L_DBG, "DEBUG: cfg_del_group_inst(): the group instance does not exist\n");
1206
+		goto error;
1207
+	}
1208
+
1209
+	/* clone the global memory block because the additional array can be
1210
+	replaced only together with the block. */
1211
+	if (!(block = cfg_clone_global()))
1212
+		goto error;
1213
+
1214
+	/* Remove the group instance from the array. */
1215
+	if (cfg_collapse_array(CFG_GROUP_META(*cfg_global, group), group,
1216
+					group_inst,
1217
+					&new_array)
1218
+	)
1219
+		goto error;
1220
+
1221
+	CFG_GROUP_META(block, group)->array = new_array;
1222
+	CFG_GROUP_META(block, group)->num--;
1223
+
1224
+	if (CFG_GROUP_META(*cfg_global, group)->array) {
1225
+		/* prepare the array of the replaced strings,
1226
+		and replaced group instances,
1227
+		they will be freed when the old block is freed */
1228
+		replaced = (void **)shm_malloc(sizeof(void *) * 2);
1229
+		if (!replaced) {
1230
+			LOG(L_ERR, "ERROR: cfg_del_group_inst(): not enough shm memory\n");
1231
+			goto error;
1232
+		}
1233
+		replaced[0] = CFG_GROUP_META(*cfg_global, group)->array;
1234
+		replaced[1] = NULL;
1235
+	}
1236
+	/* replace the global config with the new one */
1237
+	cfg_install_global(block, replaced, NULL, NULL);
1238
+	CFG_WRITER_UNLOCK();
1239
+
1240
+	LOG(L_INFO, "INFO: cfg_del_group_inst(): "
1241
+		"group instance is deleted: %.*s[%u]\n",
1242
+		group_name->len, group_name->s,
1243
+		group_id);
1244
+
1245
+	return 0;
1246
+error:
1247
+	CFG_WRITER_UNLOCK();
1248
+	if (block) cfg_block_free(block);
1249
+	if (new_array) shm_free(new_array);
1250
+	if (replaced) shm_free(replaced);
1251
+
1252
+	LOG(L_ERR, "ERROR: cfg_add_group_inst(): "
1253
+		"Failed to delete the group instance: %.*s[%u]\n",
1254
+		group_name->len, group_name->s,
1255
+		group_id);
1256
+
1257
+	return -1;
1258
+}
1259
+
1260
+/* Apply the changes to a group instance as long as the additional variable
1261
+ * belongs to the specified group_id. *add_var_p is moved to the next additional
1262
+ * variable, and all the consumed variables are freed.
1263
+ * This function can be used only during the cfg shmize process.
1264
+ * For internal use only!
1265
+ */
1266
+int cfg_apply_list(cfg_group_inst_t *ginst, cfg_group_t *group,
1267
+			unsigned int group_id, cfg_add_var_t **add_var_p)
1268
+{
1269
+	cfg_add_var_t	*add_var;
1270
+	cfg_mapping_t	*var;
1271
+	void		*val, *v, *p;
1272
+	str		group_name, var_name, s;
1273
+	char		*old_string;
1274
+
1275
+	group_name.s = group->name;
1276
+	group_name.len = group->name_len;
1277
+	while (*add_var_p && ((*add_var_p)->group_id == group_id)) {
1278
+		add_var = *add_var_p;
1279
+
1280
+		if (add_var->type == 0)
1281
+			goto done; /* Nothing needs to be changed,
1282
+				this additional variable only forces a new
1283
+				group instance to be created. */
1284
+		var_name.s = add_var->name;
1285
+		var_name.len = add_var->name_len;
1286
+
1287
+		if (!(var = cfg_lookup_var2(group, add_var->name, add_var->name_len))) {
1288
+			LOG(L_ERR, "ERROR: cfg_apply_list(): Variable is not found: %.*s.%.*s\n",
1289
+				group->name_len, group->name,
1290
+				add_var->name_len, add_var->name);
1291
+			goto error;
1292
+		}
1293
+
1294
+		/* check whether the variable is read-only */
1295
+		if (var->def->type & CFG_READONLY) {
1296
+			LOG(L_ERR, "ERROR: cfg_apply_list(): variable is read-only\n");
1297
+			goto error;
1298
+		}
1299
+
1300
+		/* The additional variable instances having per-child process callback
1301
+		 * with CFG_CB_ONLY_ONCE flag cannot be rewritten.
1302
+		 * The reason is that such variables typically set global parameters
1303
+		 * as opposed to per-process variables. Hence, it is not possible to set
1304
+		 * the group handle temporary to another block, and then reset it back later. */
1305
+		if (var->def->on_set_child_cb
1306
+			&& var->def->type & CFG_CB_ONLY_ONCE
1307
+		) {
1308
+			LOG(L_ERR, "ERROR: cfg_apply_list(): This variable does not support muliple values.\n");
1309
+			goto error;
1310
+		}
1311
+
1312
+		switch(add_var->type) {
1313
+		case CFG_VAR_INT:
1314
+			val = (void *)(long)add_var->val.i;
1315
+			break;
1316
+		case CFG_VAR_STR:
1317
+			val = (str *)&(add_var->val.s);
1318
+			break;
1319
+		case CFG_VAR_STRING:
1320
+			val = (char *)add_var->val.ch;
1321
+			break;
1322
+		default:
1323
+			LOG(L_ERR, "ERROR: cfg_apply_list(): unsupported variable type: %d\n",
1324
+				add_var->type);
1325
+			goto error;
1326
+		}
1327
+		/* check whether we have to convert the type */
1328
+		if (convert_val(add_var->type, val, CFG_INPUT_TYPE(var), &v))
1329
+			goto error;
1330
+
1331
+		if ((CFG_INPUT_TYPE(var) == CFG_INPUT_INT) 
1332
+		&& (var->def->min || var->def->max)) {
1333
+			/* perform a simple min-max check for integers */
1334
+			if (((int)(long)v < var->def->min)
1335
+			|| ((int)(long)v > var->def->max)) {
1336
+				LOG(L_ERR, "ERROR: cfg_apply_list(): integer value is out of range\n");
1337
+				goto error;
1338