浏览代码

cfg framework: apply the additional variable list

The additional variable list which is linked to the groups
is applied when the config is shmized. During this process,
the array of group instances is created, the values are fixed-up,
the changes are applied to each instance, and the list is freed.
Any change after the config is shmized is directly made in the
memory block as opposed to using the list.
(The list is necessary at the beginning because not even shm_malloc
is available, and the number of group instances is not known.)
Miklos Tirpak 15 年之前
父节点
当前提交
92f85d59c0
共有 4 个文件被更改,包括 286 次插入1 次删除
  1. 175 0
      cfg/cfg_ctx.c
  2. 7 0
      cfg/cfg_ctx.h
  3. 101 1
      cfg/cfg_struct.c
  4. 3 0
      cfg/cfg_struct.h

+ 175 - 0
cfg/cfg_ctx.c

@@ -1595,3 +1595,178 @@ error:
 
 	return -1;
 }
+
+/* Apply the changes to a group instance as long as the additional variable
+ * belongs to the specified group_id. *add_var_p is moved to the next additional
+ * variable, and all the consumed variables are freed.
+ * This function can be used only during the cfg shmize process.
+ * For internal use only!
+ */
+int cfg_apply_list(cfg_group_inst_t *ginst, cfg_group_t *group,
+			unsigned int group_id, cfg_add_var_t **add_var_p)
+{
+	cfg_add_var_t	*add_var;
+	cfg_mapping_t	*var;
+	void		*val, *v, *p;
+	str		group_name, var_name, s;
+	char		*old_string;
+
+	group_name.s = group->name;
+	group_name.len = group->name_len;
+	while (*add_var_p && ((*add_var_p)->group_id == group_id)) {
+		add_var = *add_var_p;
+
+		if (add_var->type == 0)
+			goto done; /* Nothing needs to be changed,
+				this additional variable only forces a new
+				group instance to be created. */
+		var_name.s = add_var->name;
+		var_name.len = add_var->name_len;
+
+		if (!(var = cfg_lookup_var2(group, add_var->name, add_var->name_len))) {
+			LOG(L_ERR, "ERROR: cfg_apply_list(): Variable is not found: %.*s.%.*s\n",
+				group->name_len, group->name,
+				add_var->name_len, add_var->name);
+			goto error;
+		}
+
+		/* check whether the variable is read-only */
+		if (var->def->type & CFG_READONLY) {
+			LOG(L_ERR, "ERROR: cfg_apply_list(): variable is read-only\n");
+			goto error;
+		}
+
+		/* 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 (var->def->on_set_child_cb
+			&& var->def->type & CFG_CB_ONLY_ONCE
+		) {
+			LOG(L_ERR, "ERROR: cfg_apply_list(): This variable does not support muliple values.\n");
+			goto error;
+		}
+
+		switch(add_var->type) {
+		case CFG_VAR_INT:
+			val = (void *)(long)add_var->val.i;
+			break;
+		case CFG_VAR_STR:
+			val = (str *)&(add_var->val.s);
+			break;
+		case CFG_VAR_STRING:
+			val = (char *)add_var->val.ch;
+			break;
+		default:
+			LOG(L_ERR, "ERROR: cfg_apply_list(): unsupported variable type: %d\n",
+				add_var->type);
+			goto error;
+		}
+		/* check whether we have to convert the type */
+		if (convert_val(add_var->type, val, CFG_INPUT_TYPE(var), &v))
+			goto error;
+
+		if ((CFG_INPUT_TYPE(var) == CFG_INPUT_INT) 
+		&& (var->def->min || var->def->max)) {
+			/* perform a simple min-max check for integers */
+			if (((int)(long)v < var->def->min)
+			|| ((int)(long)v > var->def->max)) {
+				LOG(L_ERR, "ERROR: cfg_apply_list(): integer value is out of range\n");
+				goto error;
+			}
+		}
+
+		if (var->def->on_change_cb) {
+			/* Call the fixup function.
+			The handle can point to the variables of the group instance. */
+			if (var->def->on_change_cb(ginst->vars,
+							&group_name,
+							&var_name,
+							&v) < 0) {
+				LOG(L_ERR, "ERROR: cfg_apply_list(): fixup failed\n");
+				goto error;
+			}
+		}
+
+		p = ginst->vars + var->offset;
+		old_string = NULL;
+		/* set the new value */
+		switch (CFG_VAR_TYPE(var)) {
+		case CFG_VAR_INT:
+			*(int *)p = (int)(long)v;
+			break;
+
+		case CFG_VAR_STRING:
+			/* clone the string to shm mem */
+			s.s = v;
+			s.len = (s.s) ? strlen(s.s) : 0;
+			if (cfg_clone_str(&s, &s)) goto error;
+			old_string = *(char **)p;
+			*(char **)p = s.s;
+			break;
+
+		case CFG_VAR_STR:
+			/* clone the string to shm mem */
+			s = *(str *)v;
+			if (cfg_clone_str(&s, &s)) goto error;
+			old_string = *(char **)p;
+			memcpy(p, &s, sizeof(str));
+			break;
+
+		case CFG_VAR_POINTER:
+			*(void **)p = v;
+			break;
+
+		}
+		if (CFG_VAR_TEST_AND_SET(ginst, var) && old_string)
+			shm_free(old_string); /* the string was already in shm memory,
+					it needs to be freed.
+					This can happen when the same variable is set
+					multiple times before forking. */
+
+		if (add_var->type == CFG_VAR_INT)
+			LOG(L_INFO, "INFO: cfg_apply_list(): %.*s[%u].%.*s "
+				"has been set to %d\n",
+				group_name.len, group_name.s,
+				group_id,
+				var_name.len, var_name.s,
+				(int)(long)val);
+
+		else if (add_var->type == CFG_VAR_STRING)
+			LOG(L_INFO, "INFO: cfg_apply_list(): %.*s[%u].%.*s "
+				"has been set to \"%s\"\n",
+				group_name.len, group_name.s,
+				group_id,
+				var_name.len, var_name.s,
+				(char *)val);
+
+		else /* str type */
+			LOG(L_INFO, "INFO: cfg_apply_list(): %.*s[%u].%.*s "
+				"has been set to \"%.*s\"\n",
+				group_name.len, group_name.s,
+				group_id,
+				var_name.len, var_name.s,
+				((str *)val)->len, ((str *)val)->s);
+
+		convert_val_cleanup();
+
+done:
+		*add_var_p = add_var->next;
+
+		if ((add_var->type == CFG_VAR_STR) && add_var->val.s.s)
+			pkg_free(add_var->val.s.s);
+		else if ((add_var->type == CFG_VAR_STRING) && add_var->val.ch)
+			pkg_free(add_var->val.ch);
+		pkg_free(add_var);
+	}
+	return 0;
+
+error:
+	LOG(L_ERR, "ERROR: cfg_apply_list(): Failed to set the value for: %.*s[%u].%.*s\n",
+		group->name_len, group->name,
+		group_id,
+		add_var->name_len, add_var->name);
+	convert_val_cleanup();
+	return -1;
+}

+ 7 - 0
cfg/cfg_ctx.h

@@ -194,4 +194,11 @@ int cfg_add_group_inst(cfg_ctx_t *ctx, str *group_name, unsigned int group_id);
 /* Delete an instance of a group */
 int cfg_del_group_inst(cfg_ctx_t *ctx, str *group_name, unsigned int group_id);
 
+/* Apply the changes to a group instance as long as the additional variable
+ * belongs to the specified group_id. *add_var_p is moved to the next additional
+ * variable, and all the consumed variables are freed.
+ */
+int cfg_apply_list(cfg_group_inst_t *ginst, cfg_group_t *group,
+			unsigned int group_id, cfg_add_var_t **add_var_p);
+
 #endif /* _CFG_CTX_H */

+ 101 - 1
cfg/cfg_struct.c

@@ -59,6 +59,7 @@ cfg_child_cb_t	*cfg_child_cb = NULL;	/* pointer to the previously executed cb */
 
 /* forward declarations */
 static void del_add_var_list(cfg_group_t *group);
+static int apply_add_var_list(cfg_block_t *block, cfg_group_t *group);
 
 /* creates a new cfg group, and adds it to the linked list */
 cfg_group_t *cfg_new_group(char *name, int name_len,
@@ -255,6 +256,11 @@ int cfg_shmize(void)
 			goto error;
 		}
 	}
+	/* Create the additional group instances with applying
+	the temporary list. */
+	if (apply_add_var_list(block, group))
+		goto error;
+
 	/* try to fixup the selects that failed to be fixed-up previously */
 	if (cfg_fixup_selects()) goto error;
 
@@ -587,6 +593,29 @@ int cfg_lookup_var(str *gname, str *vname,
 	return -1;
 }
 
+/* searches a variable definition within a group by its name */
+cfg_mapping_t *cfg_lookup_var2(cfg_group_t *group, char *name, int len)
+{
+	int	i;
+
+	if (!group->mapping) return NULL; /* dynamic group is not ready */
+
+	for (	i = 0;
+		i < group->num;
+		i++
+	) {
+		if ((group->mapping[i].name_len == len)
+		&& (memcmp(group->mapping[i].def->name, name, len)==0)) {
+			return &(group->mapping[i]);
+		}
+	}
+
+	LOG(L_DBG, "DEBUG: cfg_lookup_var2(): variable not found: %.*s.%.*s\n",
+			group->name_len, group->name,
+			len, name);
+	return NULL;
+}
+
 /* clones the global config block
  * WARNING: unsafe, cfg_writer_lock or cfg_global_lock must be held!
  */
@@ -941,7 +970,8 @@ error:
 }
 
 /* delete the additional variable list */
-static void del_add_var_list(cfg_group_t *group) {
+static void del_add_var_list(cfg_group_t *group)
+{
 	cfg_add_var_t	*add_var, *add_var2;
 
 	add_var = group->add_var;
@@ -956,3 +986,73 @@ static void del_add_var_list(cfg_group_t *group) {
 	}
 	group->add_var = NULL;
 }
+
+/* create the array of additional group instances from the linked list */
+static int apply_add_var_list(cfg_block_t *block, cfg_group_t *group)
+{
+	int		i, num, size;
+	unsigned int	group_id;
+	cfg_add_var_t	*add_var;
+	cfg_group_inst_t	*new_array, *ginst;
+
+	/* count the number of group instances */
+	for (	add_var = group->add_var, num = 0, group_id = 0;
+		add_var;
+		add_var = add_var->next
+	) {
+		if (!num || (group_id != add_var->group_id)) {
+			num++;
+			group_id = add_var->group_id;
+		}
+	}
+
+	if (!num)	/* nothing to do */
+		return 0;
+
+	LOG(L_DBG, "DEBUG: apply_add_var_list(): creating the group instance array "
+		"for '%.*s' with %d slots\n",
+		group->name_len, group->name, num);
+	size = (sizeof(cfg_group_inst_t) + group->size - 1) * num;
+	new_array = (cfg_group_inst_t *)shm_malloc(size);
+	if (!new_array) {
+		LOG(L_ERR, "ERROR: apply_add_var_list(): not enough shm memory\n");
+		return -1;
+	}
+	memset(new_array, 0, size);
+
+	for (i = 0; i < num; i++) {
+		/* Go though each group instance, set the default values,
+		and apply the changes */
+
+		if (!group->add_var) {
+			LOG(L_ERR, "BUG: apply_add_var_list(): no more additional variable left\n");
+			goto error;
+		}
+		ginst = (cfg_group_inst_t *)((char*)new_array + (sizeof(cfg_group_inst_t) + group->size - 1) * i);
+		ginst->id = group->add_var->group_id;
+		/* fill in the new group instance with the default data */
+		memcpy(	ginst->vars,
+			CFG_GROUP_DATA(block, group),
+			group->size);
+		/* cfg_apply_list() moves the group->add_var pointer to
+		the beginning of the new group instance. */
+		if (cfg_apply_list(ginst, group, ginst->id, &group->add_var))
+			goto error;
+	}
+
+#ifdef EXTRA_DEBUG
+	if (group->add_var) {
+		LOG(L_ERR, "BUG: apply_add_var_list(): not all the additional variables have been consumed\n");
+		goto error;
+	}
+#endif
+
+	CFG_GROUP_META(block, group)->num = num;
+	CFG_GROUP_META(block, group)->array = new_array;
+	return 0;
+
+error:
+	LOG(L_ERR, "ERROR: apply_add_var_list(): Failed to apply the additional variable list\n");
+	shm_free(new_array);
+	return -1;
+}

+ 3 - 0
cfg/cfg_struct.h

@@ -382,6 +382,9 @@ 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);
 
+/* searches a variable definition within a group by its name */
+cfg_mapping_t *cfg_lookup_var2(cfg_group_t *group, char *name, int len);
+
 /* clones the global config block
  * WARNING: unsafe, cfg_writer_lock or cfg_global_lock must be held!
  */