Esempio n. 1
0
/* copies the strings to shared memory */
static int cfg_shmize_strings(cfg_group_t *group)
{
	cfg_mapping_t	*mapping;
	int	i;
	str	s;

	/* We do not know in advance whether the variable will be changed or not,
	and it can happen that we try to free the shm memory area when the variable
	is changed, hence, it must be already in shm mem */
	mapping = group->mapping;
	for (i=0; i<group->num; i++) {
		/* the cfg driver module may have already shmized the variable */
		if (mapping[i].flag & cfg_var_shmized) continue;

		if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STRING) {
			s.s = *(char **)(group->vars + mapping[i].offset);
			if (!s.s) continue;
			s.len = strlen(s.s);

		} else if (CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STR) {
			memcpy(&s, group->vars + mapping[i].offset, sizeof(str));
			if (!s.s) continue;

		} else {
			continue;
		}
		if (cfg_clone_str(&s, &s)) return -1;
		*(char **)(group->vars + mapping[i].offset) = s.s;
		mapping[i].flag |= cfg_var_shmized;
	}

	return 0;
}
Esempio n. 2
0
/* retrieves the value of a variable
 * Return value:
 *  0 - success
 * -1 - error
 *  1 - variable exists, but it is not readable
 */
int cfg_get_by_name(cfg_ctx_t *ctx, str *group_name, str *var_name,
			void **val, unsigned int *val_type)
{
	cfg_group_t	*group;
	cfg_mapping_t	*var;
	void		*p;
	static str	s;	/* we need the value even
				after the function returns */

	/* verify the context even if we do not need it now
	to make sure that a cfg driver has called the function
	(very very weak security) */
	if (!ctx) {
		LOG(L_ERR, "ERROR: cfg_get_by_name(): context is undefined\n");
		return -1;
	}

	/* look-up the group and the variable */
	if (cfg_lookup_var(group_name, var_name, &group, &var))
		return -1;

	if (var->def->on_change_cb) {
		/* The variable cannot be retrieved, because the fixup
		function may have changed it, and it is better to return
		an error than an incorrect value */
		return 1;
	}

	/* use the module's handle to access the variable
	It means that the variable is read from the local config
	after forking */
	p = *(group->handle) + var->offset;

	switch (CFG_VAR_TYPE(var)) {
	case CFG_VAR_INT:
		*val = (void *)(long)*(int *)p;
		break;

	case CFG_VAR_STRING:
		*val = (void *)*(char **)p;
		break;

	case CFG_VAR_STR:
		memcpy(&s, p, sizeof(str));
		*val = (void *)&s;
		break;

	case CFG_VAR_POINTER:
		*val = *(void **)p;
		break;

	}
	*val_type = CFG_VAR_TYPE(var);

	return 0;
}
Esempio n. 3
0
/* return the pending changes that have not been
 * committed yet
 */
int cfg_diff_next(void **h,
			str *gname, str *vname,
			void **old_val, void **new_val,
			unsigned int *val_type)
{
	cfg_changed_var_t	*changed;
	union cfg_var_value* pval;
	static str	old_s, new_s;	/* we need the value even
					after the function returns */

	changed = (cfg_changed_var_t *)(*h);
	if (changed == NULL) return 0;

	gname->s = changed->group->name;
	gname->len = changed->group->name_len;
	vname->s = changed->var->def->name;
	vname->len = changed->var->name_len;

	/* use the module's handle to access the variable
	It means that the variable is read from the local config
	after forking */
	pval = (union cfg_var_value*)
			(*(changed->group->handle) + changed->var->offset);

	switch (CFG_VAR_TYPE(changed->var)) {
	case CFG_VAR_INT:
		*old_val = (void *)(long)pval->vint;
		*new_val = (void *)(long)changed->new_val.vint;
		break;

	case CFG_VAR_STRING:
		*old_val = pval->vp;
		*new_val = changed->new_val.vp;
		break;

	case CFG_VAR_STR:
		old_s=pval->vstr;
		*old_val = (void *)&old_s;
		new_s=changed->new_val.vstr;
		*new_val = (void *)&new_s;
		break;

	case CFG_VAR_POINTER:
		*old_val = pval->vp;
		*new_val = changed->new_val.vp;
		break;

	}
	*val_type = CFG_VAR_TYPE(changed->var);

	(*h) = (void *)changed->next;
	return 1;
}
Esempio n. 4
0
/* read the value of a variable via a group and variable name previously fixed up
 * Returns the type of the variable
 */
unsigned int read_cfg_var(struct cfg_read_handle *read_handle, void **val)
{
	cfg_group_t	*group;
	cfg_mapping_t	*var;
	void		*p;
	static str	s;

	if (!val || !read_handle || !read_handle->group || !read_handle->var)
		return 0;

	group = (cfg_group_t *)(read_handle->group);
	var = (cfg_mapping_t *)(read_handle->var);


	/* use the module's handle to access the variable, so the variables
	are read from the local config */
	p = *(group->handle) + var->offset;
	if (p == NULL)
		return 0;	/* The group is not yet ready.
				 * (Trying to read the value from the
				 * main process that has no local configuration) */

	switch (CFG_VAR_TYPE(var)) {
	case CFG_VAR_INT:
		*val = (void *)(long)*(int *)p;
		break;

	case CFG_VAR_STRING:
		*val = (void *)*(char **)p;
		break;

	case CFG_VAR_STR:
		memcpy(&s, p, sizeof(str));
		*val = (void *)&s;
		break;

	case CFG_VAR_POINTER:
		*val = *(void **)p;
		break;

	}
	return CFG_VAR_TYPE(var);
}
Esempio n. 5
0
/* deallocate the list of groups, and the shmized strings */
static void cfg_destory_groups(unsigned char *block)
{
	cfg_group_t	*group, *group2;
	cfg_mapping_t	*mapping;
	cfg_def_t	*def;
	void		*old_string;
	int		i;

	group = cfg_group;
	while(group) {
		mapping = group->mapping;
		def = mapping ? mapping->def : NULL;

		/* destory the shmized strings in the block */
		if (block && def)
			for (i=0; i<group->num; i++)
				if (((CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STRING) ||
				(CFG_VAR_TYPE(&mapping[i]) == CFG_VAR_STR)) &&
					mapping[i].flag & cfg_var_shmized) {

						old_string = *(char **)(block + group->var_offset + mapping[i].offset);
						if (old_string) shm_free(old_string);
				}

		if (group->dynamic == CFG_GROUP_DYNAMIC) {
			/* the group was dynamically allocated */
			cfg_script_destroy(group);
		} else {
			/* only the mapping was allocated, all the other
			pointers are just set to static variables */
			if (mapping) pkg_free(mapping);
		}
		/* Delete the additional variable list */
		del_add_var_list(group);

		group2 = group->next;
		pkg_free(group);
		group = group2;
	}
}
Esempio n. 6
0
/* drops the not yet committed changes within the context */
int cfg_rollback(cfg_ctx_t *ctx)
{
	cfg_changed_var_t	*changed, *changed2;

	if (!ctx) {
		LOG(L_ERR, "ERROR: cfg_rollback(): context is undefined\n");
		return -1;
	}

	if (!cfg_shmized) return 0; /* nothing to do */

	LOG(L_INFO, "INFO: cfg_rollback(): deleting the config changes "
			"[context=%p]\n",
			ctx);

	/* the ctx must be locked while reading and writing
	the list of changed variables */
	CFG_CTX_LOCK(ctx);

	for (	changed = ctx->changed_first;
		changed;
		changed = changed2
	) {
		changed2 = changed->next;

		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
			if (changed->new_val.vp)
				shm_free(changed->new_val.vp);
		}
		shm_free(changed);
	}
	ctx->changed_first = NULL;
	ctx->changed_last = NULL;

	CFG_CTX_UNLOCK(ctx);

	return 0;
}
Esempio n. 7
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;
	}
}
Esempio n. 8
0
int select_cfg_var(str *res, select_t *s, struct sip_msg *msg)
{
	cfg_group_t	*group;
	cfg_mapping_t	*var;
	void		*p;
	int		i;
	static char	buf[INT2STR_MAX_LEN];

	if (msg == NULL) {
		/* fixup call */

		/* two parameters are mandatory, group name and variable name */
		if (s->n < 3) {
			LM_ERR("At least two parameters are expected\n");
			return -1;
		}

		if ((s->params[1].type != SEL_PARAM_STR)
				|| (s->params[2].type != SEL_PARAM_STR)) {
			LM_ERR("string parameters are expected\n");
			return -1;
		}

		/* look-up the group and the variable */
		if (cfg_lookup_var(&s->params[1].v.s, &s->params[2].v.s, &group, &var)) {
			if (cfg_shmized) {
				LM_ERR("unknown variable: %.*s.%.*s\n",
						s->params[1].v.s.len, s->params[1].v.s.s,
						s->params[2].v.s.len, s->params[2].v.s.s);
				return -1;
			}
			/* The variable was not found, add it to the non-fixed select list.
			 * So we act as if the fixup was successful, and we retry it later */
			if (cfg_new_select(&s->params[1].v.s, &s->params[2].v.s,
						&s->params[1].v.p, &s->params[2].v.p))
				return -1;

			LM_DBG("select fixup is postponed: %.*s.%.*s\n",
					s->params[1].v.s.len, s->params[1].v.s.s,
					s->params[2].v.s.len, s->params[2].v.s.s);

			s->params[1].type = SEL_PARAM_PTR;
			s->params[1].v.p = NULL;

			s->params[2].type = SEL_PARAM_PTR;
			s->params[2].v.p = NULL;

			return 0;
		}

		if (var->def->on_change_cb) {
			/* fixup function is defined -- safer to return an error
			 * than an incorrect value */
			LM_ERR("variable cannot be retrieved\n");
			return -1;
		}

		s->params[1].type = SEL_PARAM_PTR;
		s->params[1].v.p = (void *)group;

		s->params[2].type = SEL_PARAM_PTR;
		s->params[2].v.p = (void *)var;
		return 1;
	}

	group = (cfg_group_t *)s->params[1].v.p;
	var = (cfg_mapping_t *)s->params[2].v.p;

	if (!group || !var) return -1;

	/* use the module's handle to access the variable, so the variables
	 * are read from the local config */
	p = *(group->handle) + var->offset;

	switch (CFG_VAR_TYPE(var)) {
		case CFG_VAR_INT:
			i = *(int *)p;
			res->len = snprintf(buf, sizeof(buf)-1, "%d", i);
			buf[res->len] = '\0';
			res->s = buf;
			break;

		case CFG_VAR_STRING:
			res->s = *(char **)p;
			res->len = (res->s) ? strlen(res->s) : 0;
			break;

		case CFG_VAR_STR:
			if(p) {
				memcpy(res, p, sizeof(str));
			} else {
				res->s = 0;
				res->len = 0;
			}
			break;

		default:
			LM_DBG("unsupported variable type: %d\n",
					CFG_VAR_TYPE(var));
			return -1;
	}
	return 0;
}
Esempio n. 9
0
/* commits the previously prepared changes within the context */
int cfg_commit(cfg_ctx_t *ctx)
{
	int	replaced_num = 0;
	cfg_changed_var_t	*changed, *changed2;
	cfg_block_t	*block;
	char	**replaced = NULL;
	cfg_child_cb_t	*child_cb;
	cfg_child_cb_t	*child_cb_first = NULL;
	cfg_child_cb_t	*child_cb_last = NULL;
	int	size;
	void	*p;
	str	s, s2;

	if (!ctx) {
		LOG(L_ERR, "ERROR: cfg_commit(): context is undefined\n");
		return -1;
	}

	if (!cfg_shmized) return 0; /* nothing to do */

	/* the ctx must be locked while reading and writing
	the list of changed variables */
	CFG_CTX_LOCK(ctx);

	/* is there any change? */
	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;
		changed;
		changed = changed->next
	) {
		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR))
			replaced_num++;


		if (changed->var->def->on_set_child_cb) {
			s.s = changed->group->name;
			s.len = changed->group->name_len;
			s2.s = changed->var->def->name;
			s2.len = changed->var->name_len;
			child_cb = cfg_child_cb_new(&s, &s2,
					changed->var->def->on_set_child_cb,
					changed->var->def->type);
			if (!child_cb) goto error0;

			if (child_cb_last)
				child_cb_last->next = child_cb;
			else
				child_cb_first = child_cb;
			child_cb_last = child_cb;
		}
	}

	if (replaced_num) {
		/* allocate memory for the replaced string array */
		size = sizeof(char *)*(replaced_num + 1);
		replaced = (char **)shm_malloc(size);
		if (!replaced) {
			LOG(L_ERR, "ERROR: cfg_commit(): not enough shm memory\n");
			goto error;
		}
		memset(replaced, 0 , size);
	}

	/* make sure that nobody else replaces the global config
	while the new one is prepared */
	CFG_WRITER_LOCK();

	/* clone the memory block, and prepare the modification */
	if (!(block = cfg_clone_global())) {
		CFG_WRITER_UNLOCK();
		goto error;
	}

	/* apply the modifications to the buffer */
	replaced_num = 0;
	for (	changed = ctx->changed_first;
		changed;
		changed = changed->next
	) {
		p = block->vars
			+ changed->group->offset
			+ changed->var->offset;

		if ((CFG_VAR_TYPE(changed->var) == CFG_VAR_STRING)
		|| (CFG_VAR_TYPE(changed->var) == CFG_VAR_STR)) {
			replaced[replaced_num] = *(char **)p;
			if (replaced[replaced_num])
				replaced_num++;
			/* else do not increase replaced_num, because
			the cfg_block_free() will stop at the first
			NULL value */
		}

		memcpy(	p,
			changed->new_val.vraw,
			cfg_var_size(changed->var));
	}

	/* replace the global config with the new one */
	cfg_install_global(block, replaced, child_cb_first, child_cb_last);
	CFG_WRITER_UNLOCK();

	/* free the changed list */	
	for (	changed = ctx->changed_first;
		changed;
		changed = changed2
	) {
		changed2 = changed->next;
		shm_free(changed);
	}
	ctx->changed_first = NULL;
	ctx->changed_last = NULL;

done:
	LOG(L_INFO, "INFO: cfg_commit(): config changes have been applied "
			"[context=%p]\n",
			ctx);

	CFG_CTX_UNLOCK(ctx);
	return 0;

error:
	CFG_CTX_UNLOCK(ctx);

error0:

	if (child_cb_first) cfg_child_cb_free(child_cb_first);
	if (replaced) shm_free(replaced);

	return -1;
}
Esempio n. 10
0
/* sets the value of a variable but does not commit the change
 *
 * return value:
 *   0: success
 *  -1: error
 *   1: variable has not been found
 */
int cfg_set_delayed(cfg_ctx_t *ctx, str *group_name, str *var_name,
			void *val, unsigned int val_type)
{
	cfg_group_t	*group;
	cfg_mapping_t	*var;
	void		*v;
	char		*temp_handle;
	int		temp_handle_created;
	cfg_changed_var_t	*changed = NULL;
	int		size;
	str		s;

	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,
					val, val_type);

	if (!ctx) {
		LOG(L_ERR, "ERROR: cfg_set_delayed(): context is undefined\n");
		return -1;
	}

	/* look-up the group and the variable */
	if (cfg_lookup_var(group_name, var_name, &group, &var))
		return 1;

	/* check whether the variable is read-only */
	if (var->def->type & CFG_READONLY) {
		LOG(L_ERR, "ERROR: cfg_set_delayed(): variable is read-only\n");
		goto error0;
	}

	/* check whether we have to convert the type */
	if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
		goto error0;

	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_set_delayed(): integer value is out of range\n");
			goto error0;
		}
	}

	/* the ctx must be locked while reading and writing
	the list of changed variables */
	CFG_CTX_LOCK(ctx);

	if (var->def->on_change_cb) {
		/* The fixup function must see also the
		not yet committed values, so a temporary handle
		must be prepared that points to the new config.
		Only the values within the group are applied,
		other modifications are not visible to the callback.
		The local config is the base. */

		if (ctx->changed_first) {
			temp_handle = (char *)pkg_malloc(group->size);
			if (!temp_handle) {
				LOG(L_ERR, "ERROR: cfg_set_delayed(): "
					"not enough memory\n");
				goto error;
			}
			temp_handle_created = 1;
			memcpy(temp_handle, *(group->handle), group->size);

			/* apply the changes */
			for (	changed = ctx->changed_first;
				changed;
				changed = changed->next
			) {
				if (changed->group != group) continue;

				memcpy(	temp_handle + changed->var->offset,
					changed->new_val.vraw,
					cfg_var_size(changed->var));
			}
		} else {
			/* there is not any change */
			temp_handle = *(group->handle);
			temp_handle_created = 0;
		}
			
		if (var->def->on_change_cb(temp_handle,
						group_name,
						var_name,
						&v) < 0) {
			LOG(L_ERR, "ERROR: cfg_set_delayed(): fixup failed\n");
			if (temp_handle_created) pkg_free(temp_handle);
			goto error;
		}
		if (temp_handle_created) pkg_free(temp_handle);

	}

	/* everything went ok, we can add the new value to the list */
	size = sizeof(cfg_changed_var_t) -
			sizeof(((cfg_changed_var_t*)0)->new_val) + cfg_var_size(var);
	changed = (cfg_changed_var_t *)shm_malloc(size);
	if (!changed) {
		LOG(L_ERR, "ERROR: cfg_set_delayed(): not enough shm memory\n");
		goto error;
	}
	memset(changed, 0, size);
	changed->group = group;
	changed->var = var;

	switch (CFG_VAR_TYPE(var)) {

	case CFG_VAR_INT:
		changed->new_val.vint = (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;
		changed->new_val.vp = s.s;
		break;

	case CFG_VAR_STR:
		/* clone the string to shm mem */
		s = *(str *)v;
		if (cfg_clone_str(&s, &s)) goto error;
		changed->new_val.vstr=s;
		break;

	case CFG_VAR_POINTER:
		changed->new_val.vp=v;
		break;

	}

	/* 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;

	CFG_CTX_UNLOCK(ctx);

	if (val_type == CFG_VAR_INT)
		LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
			"is going to be changed to %d "
			"[context=%p]\n",
			group_name->len, group_name->s,
			var_name->len, var_name->s,
			(int)(long)val,
			ctx);

	else if (val_type == CFG_VAR_STRING)
		LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
			"is going to be changed to \"%s\" "
			"[context=%p]\n",
			group_name->len, group_name->s,
			var_name->len, var_name->s,
			(char *)val,
			ctx);

	else /* str type */
		LOG(L_INFO, "INFO: cfg_set_delayed(): %.*s.%.*s "
			"is going to be changed to \"%.*s\" "
			"[context=%p]\n",
			group_name->len, group_name->s,
			var_name->len, var_name->s,
			((str *)val)->len, ((str *)val)->s,
			ctx);

	convert_val_cleanup();
	return 0;

error:
	CFG_CTX_UNLOCK(ctx);
	if (changed) shm_free(changed);
error0:
	LOG(L_ERR, "ERROR: cfg_set_delayed(): failed to set the variable: %.*s.%.*s\n",
			group_name->len, group_name->s,
			var_name->len, var_name->s);

	convert_val_cleanup();
	return -1;
}
Esempio n. 11
0
/* sets the value of a variable without the need of commit
 *
 * return value:
 *   0: success
 *  -1: error
 *   1: variable has not been found
 */
int cfg_set_now(cfg_ctx_t *ctx, str *group_name, str *var_name,
			void *val, unsigned int val_type)
{
	cfg_group_t	*group;
	cfg_mapping_t	*var;
	void		*p, *v;
	cfg_block_t	*block = NULL;
	str		s, s2;
	char		*old_string = NULL;
	char		**replaced = NULL;
	cfg_child_cb_t	*child_cb = NULL;

	/* verify the context even if we do not need it now
	to make sure that a cfg driver has called the function
	(very very weak security) */
	if (!ctx) {
		LOG(L_ERR, "ERROR: cfg_set_now(): context is undefined\n");
		return -1;
	}

	/* look-up the group and the variable */
	if (cfg_lookup_var(group_name, var_name, &group, &var))
		return 1;
		
	/* check whether the variable is read-only */
	if (var->def->type & CFG_READONLY) {
		LOG(L_ERR, "ERROR: cfg_set_now(): variable is read-only\n");
		goto error0;
	}

	/* check whether we have to convert the type */
	if (convert_val(val_type, val, CFG_INPUT_TYPE(var), &v))
		goto error0;
	
	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_set_now(): integer value is out of range\n");
			goto error0;
		}
	}

	if (var->def->on_change_cb) {
		/* 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),
						group_name,
						var_name,
						&v) < 0) {
			LOG(L_ERR, "ERROR: cfg_set_now(): fixup failed\n");
			goto error0;
		}

	}

	if (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;
		s.len = group->name_len;
		s2.s = var->def->name;
		s2.len = var->name_len;
		child_cb = cfg_child_cb_new(&s, &s2,
					var->def->on_set_child_cb,
					var->def->type);
		if (!child_cb) {
			LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
			goto error0;
		}
	}

	if (cfg_shmized) {
		/* make sure that nobody else replaces the global config
		while the new one is prepared */
		CFG_WRITER_LOCK();

		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->offset+var->offset;

		} else {
			/* clone the memory block, and prepare the modification */
			if (!(block = cfg_clone_global())) goto error;

			p = block->vars+group->offset+var->offset;
		}
	} else {
		/* we are allowed to rewrite the value on-the-fly
		The handle either points to group->vars, or to the
		shared memory block (dynamic group) */
		p = *(group->handle) + var->offset;
	}

	/* 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_shmized) {
		if (old_string) {
			/* prepare the array of the replaced strings,
			they will be freed when the old block is freed */
			replaced = (char **)shm_malloc(sizeof(char *)*2);
			if (!replaced) {
				LOG(L_ERR, "ERROR: cfg_set_now(): not enough shm memory\n");
				goto error;
			}
			replaced[0] = old_string;
			replaced[1] = NULL;
		}
		/* replace the global config with the new one */
		if (block) cfg_install_global(block, replaced, child_cb, child_cb);
		CFG_WRITER_UNLOCK();
	} else {
		/* cfg_set() may be called more than once before forking */
		if (old_string && (var->flag & cfg_var_shmized))
			shm_free(old_string);

		/* flag the variable because there is no need
		to shmize it again */
		var->flag |= cfg_var_shmized;

		/* the global config does not have to be replaced,
		but the child callback has to be installed, otherwise the
		child processes will miss the change */
		if (child_cb)
			cfg_install_child_cb(child_cb, child_cb);
	}

	if (val_type == CFG_VAR_INT)
		LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
			"has been changed to %d\n",
			group_name->len, group_name->s,
			var_name->len, var_name->s,
			(int)(long)val);

	else if (val_type == CFG_VAR_STRING)
		LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
			"has been changed to \"%s\"\n",
			group_name->len, group_name->s,
			var_name->len, var_name->s,
			(char *)val);

	else /* str type */
		LOG(L_INFO, "INFO: cfg_set_now(): %.*s.%.*s "
			"has been changed to \"%.*s\"\n",
			group_name->len, group_name->s,
			var_name->len, var_name->s,
			((str *)val)->len, ((str *)val)->s);

	convert_val_cleanup();
	return 0;

error:
	if (cfg_shmized) CFG_WRITER_UNLOCK();
	if (block) cfg_block_free(block);
	if (child_cb) cfg_child_cb_free(child_cb);

error0:
	LOG(L_ERR, "ERROR: cfg_set_now(): failed to set the variable: %.*s.%.*s\n",
			group_name->len, group_name->s,
			var_name->len, var_name->s);


	convert_val_cleanup();
	return -1;
}