Esempio n. 1
0
/* installs a new global config
 *
 * replaced is an array of strings that must be freed together
 * with the previous global config.
 * cb_first and cb_last define a linked list of per-child process
 * callbacks. This list is added to the global linked list.
 */
void cfg_install_global(cfg_block_t *block, void **replaced,
			cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last)
{
	cfg_block_t* old_cfg;
	
	CFG_REF(block);

	if (replaced) {
		/* The replaced array is specified, it has to be linked to the child cb structure.
		 * The last child process processing this structure will free the old strings and the array. */
		if (cb_first) {
			cb_first->replaced = replaced;
		} else {
			/* At least one child cb structure is needed. */
			cb_first = cfg_child_cb_new(NULL, NULL, NULL, 0 /* gname, name, cb, type */);
			if (cb_first) {
				cb_last = cb_first;
				cb_first->replaced = replaced;
			} else {
				LOG(L_ERR, "ERROR: cfg_install_global(): not enough shm memory\n");
				/* Nothing more can be done here, the replaced strings are still needed,
				 * they cannot be freed at this moment.
				 */
			}
		}
	}

	CFG_LOCK();

	old_cfg = *cfg_global;
	*cfg_global = block;

	if (cb_first)
		cfg_install_child_cb(cb_first, cb_last);

	CFG_UNLOCK();
	
	if (old_cfg)
		CFG_UNREF(old_cfg);
}
Esempio n. 2
0
/* installs a new global config
 *
 * replaced is an array of strings that must be freed together
 * with the previous global config.
 * cb_first and cb_last define a linked list of per-child process
 * callbacks. This list is added to the global linked list.
 */
void cfg_install_global(cfg_block_t *block, char **replaced,
			cfg_child_cb_t *cb_first, cfg_child_cb_t *cb_last)
{
	cfg_block_t* old_cfg;
	
	CFG_REF(block);

	CFG_LOCK();

	old_cfg = *cfg_global;
	*cfg_global = block;

	if (cb_first)
		cfg_install_child_cb(cb_first, cb_last);

	CFG_UNLOCK();
	
	if (old_cfg) {
		if (replaced) (old_cfg)->replaced = replaced;
		CFG_UNREF(old_cfg);
	}

}
Esempio n. 3
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;
}