Pārlūkot izejas kodu

cfg framework: support for group instances with delayed commits

cfg_set_delayed() and the related functions, commit, diff,... support
the additinal group instances from now.
The group instance must exist before cfg_set_delayed() is called, i.e.
the function does not create new instances.
Miklos Tirpak 15 gadi atpakaļ
vecāks
revīzija
ec5bc6bb85
2 mainītis faili ar 205 papildinājumiem un 45 dzēšanām
  1. 200 42
      cfg/cfg_ctx.c
  2. 5 3
      cfg/cfg_ctx.h

+ 200 - 42
cfg/cfg_ctx.c

@@ -648,11 +648,13 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
 	cfg_group_t	*group;
 	cfg_group_t	*group;
 	cfg_mapping_t	*var;
 	cfg_mapping_t	*var;
 	void		*v;
 	void		*v;
-	char		*temp_handle;
+	unsigned char	*temp_handle;
 	int		temp_handle_created;
 	int		temp_handle_created;
-	cfg_changed_var_t	*changed = NULL;
+	cfg_changed_var_t	*changed = NULL, **changed_p;
 	int		size;
 	int		size;
 	str		s;
 	str		s;
+	cfg_group_inst_t	*group_inst;
+	unsigned char		*var_block;
 
 
 	if (!cfg_shmized)
 	if (!cfg_shmized)
 		/* the cfg has not been shmized yet, there is no
 		/* the cfg has not been shmized yet, there is no
@@ -675,6 +677,19 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
 		goto error0;
 		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 */
 	/* check whether we have to convert the type */
 	if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
 	if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
 		goto error0;
 		goto error0;
@@ -700,31 +715,56 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
 		Only the values within the group are applied,
 		Only the values within the group are applied,
 		other modifications are not visible to the callback.
 		other modifications are not visible to the callback.
 		The local config is the base. */
 		The local config is the base. */
+		if (!group_id) {
+			var_block = *(group->handle);
+		} else {
+			if (!cfg_local) {
+				LOG(L_ERR, "ERROR: cfg_set_delayed(): Local configuration is missing\n");
+				goto error;
+			}
+			group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, group),
+							group->size,
+							*group_id);
+			if (!group_inst) {
+				LOG(L_ERR, "ERROR: cfg_set_delayed(): local group instance %.*s[%u] is not found\n",
+					group_name->len, group_name->s, *group_id);
+				goto error;
+			}
+			var_block = group_inst->vars;
+		}
 
 
 		if (ctx->changed_first) {
 		if (ctx->changed_first) {
-			temp_handle = (char *)pkg_malloc(group->size);
+			temp_handle = (unsigned char *)pkg_malloc(group->size);
 			if (!temp_handle) {
 			if (!temp_handle) {
 				LOG(L_ERR, "ERROR: cfg_set_delayed(): "
 				LOG(L_ERR, "ERROR: cfg_set_delayed(): "
 					"not enough memory\n");
 					"not enough memory\n");
 				goto error;
 				goto error;
 			}
 			}
 			temp_handle_created = 1;
 			temp_handle_created = 1;
-			memcpy(temp_handle, *(group->handle), group->size);
+			memcpy(temp_handle, var_block, group->size);
 
 
 			/* apply the changes */
 			/* apply the changes */
 			for (	changed = ctx->changed_first;
 			for (	changed = ctx->changed_first;
 				changed;
 				changed;
 				changed = changed->next
 				changed = changed->next
 			) {
 			) {
-				if (changed->group != group) continue;
-
-				memcpy(	temp_handle + changed->var->offset,
-					changed->new_val.vraw,
-					cfg_var_size(changed->var));
+				if (changed->group != group)
+					continue;
+				if ((!group_id && !changed->group_id_set) /* default values */
+					|| (group_id && !changed->group_id_set
+						&& !CFG_VAR_TEST(group_inst, changed->var))
+							/* default value is changed which affects the group_instance */
+					|| (group_id && changed->group_id_set
+						&& (*group_id == changed->group_id))
+							/* change within the group instance */
+				)
+					memcpy(	temp_handle + changed->var->offset,
+						changed->new_val.vraw,
+						cfg_var_size(changed->var));
 			}
 			}
 		} else {
 		} else {
 			/* there is not any change */
 			/* there is not any change */
-			temp_handle = *(group->handle);
+			temp_handle = var_block;
 			temp_handle_created = 0;
 			temp_handle_created = 0;
 		}
 		}
 			
 			
@@ -751,6 +791,10 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
 	memset(changed, 0, size);
 	memset(changed, 0, size);
 	changed->group = group;
 	changed->group = group;
 	changed->var = var;
 	changed->var = var;
+	if (group_id) {
+		changed->group_id = *group_id;
+		changed->group_id_set = 1;
+	}
 
 
 	switch (CFG_VAR_TYPE(var)) {
 	switch (CFG_VAR_TYPE(var)) {
 
 
@@ -779,15 +823,27 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
 
 
 	}
 	}
 
 
-	/* Add the new item to the end of the linked list,
-	The commit will go though the list from the first item,
-	so the list is kept in order */
-	if (ctx->changed_first)
-		ctx->changed_last->next = changed;
-	else
-		ctx->changed_first = changed;
-
-	ctx->changed_last = changed;
+	/* Order the changes by group + group_id + original order.
+	 * Hence, the list is still kept in order within the group.
+	 * The changes can be committed faster this way, the group instances
+	 * do not have to be looked-up for each and every variable. */
+	/* Check whether there is any variable in the list which
+	belongs to the same group */
+	for (	changed_p = &ctx->changed_first;
+		*changed_p && ((*changed_p)->group != changed->group);
+		changed_p = &(*changed_p)->next);
+	/* try to find the group instance, and move changed_p to the end of
+	the instance. */
+	for (	;
+		*changed_p
+			&& ((*changed_p)->group == changed->group)
+			&& (!(*changed_p)->group_id_set
+				|| ((*changed_p)->group_id_set && changed->group_id_set
+					&& ((*changed_p)->group_id <= changed->group_id)));
+		changed_p = &(*changed_p)->next);
+	/* Add the new variable before *changed_p */
+	changed->next = *changed_p;
+	*changed_p = changed;
 
 
 	CFG_CTX_UNLOCK(ctx);
 	CFG_CTX_UNLOCK(ctx);
 
 
@@ -817,6 +873,11 @@ int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, unsigned int *group_id, str
 			var_name->len, var_name->s,
 			var_name->len, var_name->s,
 			((str *)val)->len, ((str *)val)->s,
 			((str *)val)->len, ((str *)val)->s,
 			ctx);
 			ctx);
+	if (group_id)
+		LOG(L_INFO, "INFO: cfg_set_delayed(): group id = %u "
+			"[context=%p]\n",
+			*group_id,
+			ctx);
 
 
 	convert_val_cleanup();
 	convert_val_cleanup();
 	return 0;
 	return 0;
@@ -862,7 +923,7 @@ int cfg_commit(cfg_ctx_t *ctx)
 {
 {
 	int	replaced_num = 0;
 	int	replaced_num = 0;
 	cfg_changed_var_t	*changed, *changed2;
 	cfg_changed_var_t	*changed, *changed2;
-	cfg_block_t	*block;
+	cfg_block_t	*block = NULL;
 	void	**replaced = NULL;
 	void	**replaced = NULL;
 	cfg_child_cb_t	*child_cb;
 	cfg_child_cb_t	*child_cb;
 	cfg_child_cb_t	*child_cb_first = NULL;
 	cfg_child_cb_t	*child_cb_first = NULL;
@@ -870,6 +931,8 @@ int cfg_commit(cfg_ctx_t *ctx)
 	int	size;
 	int	size;
 	void	*p;
 	void	*p;
 	str	s, s2;
 	str	s, s2;
+	cfg_group_t	*group;
+	cfg_group_inst_t	*group_inst;
 
 
 	if (!ctx) {
 	if (!ctx) {
 		LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
 		LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
@@ -885,19 +948,30 @@ int cfg_commit(cfg_ctx_t *ctx)
 	/* is there any change? */
 	/* is there any change? */
 	if (!ctx->changed_first) goto done;
 	if (!ctx->changed_first) goto done;
 
 
-	/* count the number of replaced strings,
-	and prepare the linked list of per-child process
-	callbacks, that will be added to the global list */
-	for (	changed = ctx->changed_first;
+	/* Count the number of replaced strings,
+	and replaced group arrays.
+	Prepare the linked list of per-child process
+	callbacks, that will be added to the global list. */
+	for (	changed = ctx->changed_first, group = NULL;
 		changed;
 		changed;
 		changed = changed->next
 		changed = changed->next
 	) {
 	) {
+		/* Each string/str potentially causes an old string to be freed
+		 * unless the variable of an additional group instance is set
+		 * which uses the default value. This case cannot be determined
+		 * without locking *cfg_global, hence, it is better to count these
+		 * strings as well even though the slot might not be used later. */
 		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
 		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
 		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
 		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
 			replaced_num++;
 			replaced_num++;
 
 
+		/* See the above comments for strings */
+		if (group != changed->group) {
+			replaced_num++;
+			group = changed->group;
+		}
 
 
-		if (changed->var->def->on_set_child_cb) {
+		if (!changed->group_id_set && changed->var->def->on_set_child_cb) {
 			s.s = changed->group->name;
 			s.s = changed->group->name;
 			s.len = changed->group->name_len;
 			s.len = changed->group->name_len;
 			s2.s = changed->var->def->name;
 			s2.s = changed->var->def->name;
@@ -921,7 +995,7 @@ int cfg_commit(cfg_ctx_t *ctx)
 		replaced = (void **)shm_malloc(size);
 		replaced = (void **)shm_malloc(size);
 		if (!replaced) {
 		if (!replaced) {
 			LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
 			LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
-			goto error;
+			goto error0;
 		}
 		}
 		memset(replaced, 0 , size);
 		memset(replaced, 0 , size);
 	}
 	}
@@ -931,22 +1005,54 @@ int cfg_commit(cfg_ctx_t *ctx)
 	CFG_WRITER_LOCK();
 	CFG_WRITER_LOCK();
 
 
 	/* clone the memory block, and prepare the modification */
 	/* clone the memory block, and prepare the modification */
-	if (!(block = cfg_clone_global())) {
-		CFG_WRITER_UNLOCK();
+	if (!(block = cfg_clone_global()))
 		goto error;
 		goto error;
-	}
 
 
-	/* apply the modifications to the buffer */
+	/* Apply the modifications to the buffer.
+	Note that the cycle relies on the order of the groups and group instances, i.e.
+	the order is group + group_id + order of commits. */
 	replaced_num = 0;
 	replaced_num = 0;
-	for (	changed = ctx->changed_first;
+	for (	changed = ctx->changed_first, group = NULL; /* group points to the
+							last group array that has been cloned */
 		changed;
 		changed;
 		changed = changed->next
 		changed = changed->next
 	) {
 	) {
-		p = CFG_GROUP_DATA(block, changed->group)
-			+ changed->var->offset;
+		if (!changed->group_id_set) {
+			p = CFG_GROUP_DATA(block, changed->group)
+				+ changed->var->offset;
+			group_inst = NULL; /* fore the look-up of the next group_inst */
+		} else {
+			if (group != changed->group) {
+				/* The group array has not been cloned yet. */
+				group = changed->group;
+				if (!(CFG_GROUP_META(block, group)->array = 
+					cfg_clone_array(CFG_GROUP_META(*cfg_global, group), group))
+				)
+					goto error;
 
 
-		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
-		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
+				replaced[replaced_num] = CFG_GROUP_META(*cfg_global, group)->array;
+				replaced_num++;
+
+				group_inst = NULL; /* fore the look-up of group_inst */
+			}
+			if (!group_inst || (group_inst->id != changed->group_id)) {
+				group_inst = cfg_find_group(CFG_GROUP_META(block, group),
+								group->size,
+								changed->group_id);
+			}
+			if (!group_inst) {
+				LOG(L_ERR, "ERROR: cfg_set_now(): global group instance %.*s[%u] is not found\n",
+					group->name_len, group->name, changed->group_id);
+				goto error;
+			}
+			p = group_inst->vars + changed->var->offset;
+		}
+
+		if (((changed->group_id_set && CFG_VAR_TEST_AND_SET(group_inst, changed->var))
+			|| !changed->group_id_set)
+		&& ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
+			|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) 
+		) {
 			replaced[replaced_num] = *(char **)p;
 			replaced[replaced_num] = *(char **)p;
 			if (replaced[replaced_num])
 			if (replaced[replaced_num])
 				replaced_num++;
 				replaced_num++;
@@ -958,13 +1064,33 @@ int cfg_commit(cfg_ctx_t *ctx)
 		memcpy(	p,
 		memcpy(	p,
 			changed->new_val.vraw,
 			changed->new_val.vraw,
 			cfg_var_size(changed->var));
 			cfg_var_size(changed->var));
+
+		if (!changed->group_id_set) {
+			/* the default value is changed, the copies of this value
+			need to be also updated */
+			if (cfg_update_defaults(CFG_GROUP_META(block, changed->group),
+						changed->group, changed->var, p,
+						(group != changed->group)) /* clone if the array
+									has not been cloned yet */
+                        )
+                                goto error;
+                        if ((group != changed->group)
+				&& (CFG_GROUP_META(block, changed->group)->array != CFG_GROUP_META(*cfg_global, changed->group)->array)
+			) {
+				/* The array has been cloned */
+				group = changed->group;
+
+				replaced[replaced_num] = CFG_GROUP_META(*cfg_global, group)->array;
+				replaced_num++;
+			}
+		}
 	}
 	}
 
 
 	/* replace the global config with the new one */
 	/* replace the global config with the new one */
 	cfg_install_global(block, replaced, child_cb_first, child_cb_last);
 	cfg_install_global(block, replaced, child_cb_first, child_cb_last);
 	CFG_WRITER_UNLOCK();
 	CFG_WRITER_UNLOCK();
 
 
-	/* free the changed list */	
+	/* free the changed list */
 	for (	changed = ctx->changed_first;
 	for (	changed = ctx->changed_first;
 		changed;
 		changed;
 		changed = changed2
 		changed = changed2
@@ -973,7 +1099,6 @@ int cfg_commit(cfg_ctx_t *ctx)
 		shm_free(changed);
 		shm_free(changed);
 	}
 	}
 	ctx->changed_first = NULL;
 	ctx->changed_first = NULL;
-	ctx->changed_last = NULL;
 
 
 done:
 done:
 	LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
 	LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
@@ -984,9 +1109,24 @@ done:
 	return 0;
 	return 0;
 
 
 error:
 error:
-	CFG_CTX_UNLOCK(ctx);
+	if (block) {
+		/* clean the new block from the cloned arrays */
+		for (	group = cfg_group;
+			group;
+			group = group->next
+		)
+			if (CFG_GROUP_META(block, group)->array
+				&& (CFG_GROUP_META(block, group)->array != CFG_GROUP_META(*cfg_global, group)->array)
+			)
+				shm_free(CFG_GROUP_META(block, group)->array);
+		/* the block can be freed outside of the writer lock */
+	}
+	CFG_WRITER_UNLOCK();
+	if (block)
+		shm_free(block);
 
 
 error0:
 error0:
+	CFG_CTX_UNLOCK(ctx);
 
 
 	if (child_cb_first) cfg_child_cb_free(child_cb_first);
 	if (child_cb_first) cfg_child_cb_free(child_cb_first);
 	if (replaced) shm_free(replaced);
 	if (replaced) shm_free(replaced);
@@ -1028,7 +1168,6 @@ int cfg_rollback(cfg_ctx_t *ctx)
 		shm_free(changed);
 		shm_free(changed);
 	}
 	}
 	ctx->changed_first = NULL;
 	ctx->changed_first = NULL;
-	ctx->changed_last = NULL;
 
 
 	CFG_CTX_UNLOCK(ctx);
 	CFG_CTX_UNLOCK(ctx);
 
 
@@ -1181,11 +1320,12 @@ int cfg_diff_init(cfg_ctx_t *ctx,
  * committed yet
  * committed yet
  */
  */
 int cfg_diff_next(void **h,
 int cfg_diff_next(void **h,
-			str *gname, str *vname,
+			str *gname, unsigned int **gid, str *vname,
 			void **old_val, void **new_val,
 			void **old_val, void **new_val,
 			unsigned int *val_type)
 			unsigned int *val_type)
 {
 {
 	cfg_changed_var_t	*changed;
 	cfg_changed_var_t	*changed;
+	cfg_group_inst_t	*group_inst;
 	union cfg_var_value* pval;
 	union cfg_var_value* pval;
 	static str	old_s, new_s;	/* we need the value even
 	static str	old_s, new_s;	/* we need the value even
 					after the function returns */
 					after the function returns */
@@ -1195,14 +1335,32 @@ int cfg_diff_next(void **h,
 
 
 	gname->s = changed->group->name;
 	gname->s = changed->group->name;
 	gname->len = changed->group->name_len;
 	gname->len = changed->group->name_len;
+	*gid = (changed->group_id_set ? &changed->group_id : NULL);
 	vname->s = changed->var->def->name;
 	vname->s = changed->var->def->name;
 	vname->len = changed->var->name_len;
 	vname->len = changed->var->name_len;
 
 
 	/* use the module's handle to access the variable
 	/* use the module's handle to access the variable
 	It means that the variable is read from the local config
 	It means that the variable is read from the local config
 	after forking */
 	after forking */
-	pval = (union cfg_var_value*)
-			(*(changed->group->handle) + changed->var->offset);
+	if (!changed->group_id_set) {
+		pval = (union cfg_var_value*)
+				(*(changed->group->handle) + changed->var->offset);
+	} else {
+		if (!cfg_local) {
+			LOG(L_ERR, "ERROR: cfg_diff_next(): Local configuration is missing\n");
+			return 0;
+		}
+		group_inst = cfg_find_group(CFG_GROUP_META(cfg_local, changed->group),
+						changed->group->size,
+						changed->group_id);
+		if (!group_inst) {
+			LOG(L_ERR, "ERROR: cfg_diff_next(): local group instance %.*s[%u] is not found\n",
+				changed->group->name_len, changed->group->name, changed->group_id);
+			return 0;
+		}
+		pval = (union cfg_var_value*)
+				(group_inst->vars + changed->var->offset);
+	}
 
 
 	switch (CFG_VAR_TYPE(changed->var)) {
 	switch (CFG_VAR_TYPE(changed->var)) {
 	case CFG_VAR_INT:
 	case CFG_VAR_INT:

+ 5 - 3
cfg/cfg_ctx.h

@@ -49,6 +49,9 @@ typedef struct _cfg_changed_var {
 	cfg_mapping_t	*var;
 	cfg_mapping_t	*var;
 	struct _cfg_changed_var	*next;
 	struct _cfg_changed_var	*next;
 
 
+	unsigned int	group_id; /* valid only if group_id_set==1 */
+	unsigned char	group_id_set;
+
 	/* blob that contains the new value */
 	/* blob that contains the new value */
 	union cfg_var_value new_val; /* variable size */
 	union cfg_var_value new_val; /* variable size */
 } cfg_changed_var_t;
 } cfg_changed_var_t;
@@ -61,7 +64,6 @@ typedef struct _cfg_ctx {
 	/* variables that are already changed
 	/* variables that are already changed
 	but have not been committed yet */
 	but have not been committed yet */
 	cfg_changed_var_t	*changed_first;
 	cfg_changed_var_t	*changed_first;
-	cfg_changed_var_t	*changed_last;
 	/* lock protecting the linked-list of
 	/* lock protecting the linked-list of
 	changed variables */
 	changed variables */
 	gen_lock_t		lock;
 	gen_lock_t		lock;
@@ -161,7 +163,7 @@ int cfg_diff_init(cfg_ctx_t *ctx,
  * void *handle;
  * void *handle;
  * if (cfg_diff_init(ctx, &handle)) return -1
  * if (cfg_diff_init(ctx, &handle)) return -1
  * while (cfg_diff_next(&handle
  * while (cfg_diff_next(&handle
- *			&group_name, &var_name,
+ *			&group_name, &group_id, &var_name,
  *			&old_val, &new_val
  *			&old_val, &new_val
  *			&val_type)
  *			&val_type)
  * ) {
  * ) {
@@ -170,7 +172,7 @@ int cfg_diff_init(cfg_ctx_t *ctx,
  * cfg_diff_release(ctx);
  * cfg_diff_release(ctx);
  */
  */
 int cfg_diff_next(void **h,
 int cfg_diff_next(void **h,
-			str *gname, str *vname,
+			str *gname, unsigned int **gid, str *vname,
 			void **old_val, void **new_val,
 			void **old_val, void **new_val,
 			unsigned int *val_type);
 			unsigned int *val_type);