Beispiel #1
0
void test_nextNotBelow (void)
{
	printf ("Test next not below\n");

	KeySet * ks = getNullKeys ();
	ksRewind (ks);
	Key * k = elektraNextNotBelow (ks);
	succeed_if_equal (keyName (k), "user/tests/yajl/nullkey");
	succeed_if_equal (keyName (ksCurrent (ks)), "user/tests/yajl/nullkey");
	k = elektraNextNotBelow (ks);
	succeed_if_equal (keyName (k), "user/tests/yajl/second_nullkey");
	succeed_if_equal (keyName (ksCurrent (ks)), "user/tests/yajl/second_nullkey");
	k = elektraNextNotBelow (ks);
	succeed_if (k == 0, "not at end of keyset");
	succeed_if (ksCurrent (ks) == 0, "not at end of keyset");
	ksDel (ks);

	ks = getBooleanKeys ();
	ksRewind (ks);
	k = elektraNextNotBelow (ks);
	succeed_if_equal (keyName (k), "user/tests/yajl/boolean_key");
	succeed_if_equal (keyName (ksCurrent (ks)), "user/tests/yajl/boolean_key");
	k = elektraNextNotBelow (ks);
	succeed_if_equal (keyName (k), "user/tests/yajl/second_boolean_key");
	succeed_if_equal (keyName (ksCurrent (ks)), "user/tests/yajl/second_boolean_key");
	k = elektraNextNotBelow (ks);
	succeed_if (k == 0, "not at end of keyset");
	succeed_if (ksCurrent (ks) == 0, "not at end of keyset");
	ksDel (ks);

	ks = getBelowKeys ();
	ksRewind (ks);
	k = elektraNextNotBelow (ks);
	succeed_if_equal (keyName (k), "user/tests/yajl/fancy/path/below/v/y/z");
	succeed_if_equal (keyName (ksCurrent (ks)), "user/tests/yajl/fancy/path/below/v/y/z");
	k = elektraNextNotBelow (ks);
	succeed_if_equal (keyName (k), "user/tests/yajl/fancy/path/below/x/y/z");
	succeed_if_equal (keyName (ksCurrent (ks)), "user/tests/yajl/fancy/path/below/x/y/z");
	k = elektraNextNotBelow (ks);
	succeed_if (k == 0, "not at end of keyset");
	succeed_if (ksCurrent (ks) == 0, "not at end of keyset");
	ksDel (ks);

	ks = getMapKeys ();
	ksRewind (ks);
	k = elektraNextNotBelow (ks);
	succeed_if_equal (keyName (k), "user/tests/yajl/map/nested_map/second_string_key");
	succeed_if_equal (keyName (ksCurrent (ks)), "user/tests/yajl/map/nested_map/second_string_key");
	ksDel (ks);
}
Beispiel #2
0
static void elektraDropCurrentKey (KeySet * ks, Key * warningKey, const Backend * curHandle, const char * msg)
{
	const Key * k = ksCurrent (ks);

	const size_t sizeOfStaticText = 100;
	char * warningMsg = elektraMalloc (keyGetNameSize (curHandle->mountpoint) + keyGetValueSize (curHandle->mountpoint) +
					   keyGetNameSize (k) + strlen (msg) + sizeOfStaticText);
	strcpy (warningMsg, "drop key ");
	const char * name = keyName (k);
	if (name)
	{
		strcat (warningMsg, name);
	}
	else
	{
		strcat (warningMsg, "(no name)");
	}
	strcat (warningMsg, " not belonging to ");
	strcat (warningMsg, keyName (curHandle->mountpoint));
	strcat (warningMsg, " with name ");
	strcat (warningMsg, keyString (curHandle->mountpoint));
	strcat (warningMsg, " because ");
	strcat (warningMsg, msg);
	ELEKTRA_ADD_WARNING (79, warningKey, warningMsg);
	elektraFree (warningMsg);
	cursor_t c = ksGetCursor (ks);
	keyDel (elektraKsPopAtCursor (ks, c));
	ksSetCursor (ks, c);
	elektraKsPrev (ks); // next ksNext() will point correctly again
}
Beispiel #3
0
/**
 @retval 0 if ksCurrent does not hold an array entry
 @retval 1 if the array entry will be used because its the first
 @retval 2 if a new array entry was created
 @retval -1 error in snprintf
 */
static int elektraYajlIncrementArrayEntry (KeySet * ks)
{
	Key * current = ksCurrent (ks);
	const char * baseName = keyBaseName (current);

	if (baseName && *baseName == '#')
	{
		current = keyNew (keyName (current), KEY_END);
		if (!strcmp (baseName, "###empty_array"))
		{
			// get rid of previous key
			keyDel (ksLookup (ks, current, KDB_O_POP));
			// we have a new array entry
			keySetBaseName (current, 0);
			keyAddName (current, "#0");
			ksAppendKey (ks, current);
			return 1;
		}
		else
		{
			// we are in an array
			elektraArrayIncName (current);
			ksAppendKey (ks, current);
			return 2;
		}
	}
	else
	{
		// previous entry indicates this is not an array
		return 0;
	}
}
Beispiel #4
0
static int elektraYajlParseMapKey (void * ctx, const unsigned char * stringVal, yajl_size_type stringLen)
{
	KeySet * ks = (KeySet *)ctx;
	elektraYajlIncrementArrayEntry (ks);

	Key * currentKey = keyNew (keyName (ksCurrent (ks)), KEY_END);
	keySetString (currentKey, 0);

	unsigned char delim = stringVal[stringLen];
	char * stringValue = (char *)stringVal;
	stringValue[stringLen] = '\0';

#ifdef ELEKTRA_YAJL_VERBOSE
	printf ("elektraYajlParseMapKey stringValue: %s currentKey: %s\n", stringValue, keyName (currentKey));
#endif
	if (currentKey && !strcmp (keyBaseName (currentKey), "___empty_map"))
	{
		// remove old key
		keyDel (ksLookup (ks, currentKey, KDB_O_POP));
		// now we know the name of the object
		keySetBaseName (currentKey, stringValue);
	}
	else
	{
		// we entered a new pair (inside the previous object)
		keySetBaseName (currentKey, stringValue);
	}
	ksAppendKey (ks, currentKey);

	// restore old character in buffer
	stringValue[stringLen] = delim;

	return 1;
}
Beispiel #5
0
static int elektraYajlParseEnd (void * ctx)
{
	KeySet * ks = (KeySet *)ctx;
	Key * currentKey = ksCurrent (ks);

	Key * lookupKey = keyNew (keyName (currentKey), KEY_END);
	keySetBaseName (lookupKey, 0); // remove current baseName

	// lets point current to the correct place
	Key * foundKey = ksLookup (ks, lookupKey, 0);

#ifdef ELEKTRA_YAJL_VERBOSE
	if (foundKey)
	{
		printf ("elektraYajlParseEnd %s\n", keyName (foundKey));
	}
	else
	{
		printf ("elektraYajlParseEnd did not find key!\n");
	}
#else
	(void)foundKey; // foundKey is not used, but lookup is needed
#endif

	keyDel (lookupKey);

	return 1;
}
Beispiel #6
0
static void test_ksPopAtCursor()
{
	KeySet *ks = ksNew (
		5,
		keyNew ("user/valid/key1", KEY_END),
		keyNew ("user/valid/key2", KEY_END),
		keyNew ("system/valid/key1", KEY_END),
		keyNew ("system/valid/key2", KEY_END),
		KS_END);
	KeySet *ks_c = ksNew (
		5,
		keyNew ("user/valid/key1", KEY_END),
		keyNew ("user/valid/key2", KEY_END),
		keyNew ("system/valid/key1", KEY_END),
		KS_END);
	ksRewind(ks);
	ksNext(ks);
	ksNext(ks);
	cursor_t c = ksGetCursor(ks);
	keyDel (ksPopAtCursor(ks, c));
	succeed_if(ksCurrent(ks) == 0, "cursor position wrong");

	compare_keyset(ks, ks_c);
	ksDel(ks);
	ksDel(ks_c);
}
Beispiel #7
0
/**
 * @brief sets mountpoint
 *
 * @param backend where the mountpoint should be set
 * @param elektraConfig the config where the mountpoint can be found
 * @param [out] errorKey the name also has the mountpoint set
 *
 * @pre ksCurrent() is root key
 * @post ksCurrent() is root key
 *
 * @retval -1 if no mountpoint is found or memory allocation problem
 * @retval 0 on success
 */
int elektraBackendSetMountpoint(Backend *backend, KeySet *elektraConfig, Key *errorKey)
{
	Key * root = ksCurrent(elektraConfig);
	Key * searchMountpoint = keyDup(root);
	keyAddBaseName(searchMountpoint, "mountpoint");
	Key * foundMountpoint = ksLookup(elektraConfig, searchMountpoint, 0);
	keyDel (searchMountpoint);
	ksLookup(elektraConfig, root, 0); // reset ksCurrent()

	if (!foundMountpoint)
	{
		ELEKTRA_ADD_WARNINGF(14, errorKey,
			"Could not find mountpoint within root %s",
			keyName(root));
		return -1;
	}

	backend->mountpoint = keyNew("",
			KEY_VALUE, keyBaseName(root), KEY_END);
	elektraKeySetName(backend->mountpoint, keyString(foundMountpoint),
			KEY_CASCADING_NAME | KEY_EMPTY_NAME);

	keySetName(errorKey, keyName(backend->mountpoint));

	if (!backend->mountpoint)
	{
		ELEKTRA_ADD_WARNINGF(14, errorKey,
			"Could not create mountpoint with name %s and value %s",
			keyString(foundMountpoint), keyBaseName(root));
		return -1;
	}

	keyIncRef(backend->mountpoint);
	return 0;
}
Beispiel #8
0
/**@return a backend which gives plugin configuration of the module
 * which is currently point to.
 *
 * @param modules the modules to work with
 * @param errorKey the key to issue warnings and errors to
 */
Backend* elektraBackendOpenModules(KeySet *modules, Key *errorKey)
{
	Backend *backend = elektraBackendAllocate();

	cursor_t save = ksGetCursor (modules);
	KeySet *defaultConfig = ksNew(5,
		keyNew("system/module", KEY_VALUE, "1", KEY_END),
		keyNew("user/module", KEY_VALUE, "1", KEY_END),
		KS_END);
	Key *cur = ksCurrent(modules);

	Plugin *plugin = elektraPluginOpen(keyBaseName(cur), modules, defaultConfig, errorKey);
	if (!plugin)
	{
		/* Error already set in plugin */
		elektraFree(backend);
		return 0;
	}

	Key *mp = keyNew ("system/elektra/modules", KEY_VALUE, "modules", KEY_END);
	keyAddBaseName (mp, keyBaseName(cur));

	backend->getplugins[0] = plugin;
	plugin->refcounter = 1;

	backend->mountpoint = mp;
	keyIncRef(backend->mountpoint);

	ksSetCursor (modules, save);

	return backend;
}
int main()
{
    KeySet *out;
    KeySet *ks = ksNew (64,
                        keyNew ("user/a/1", KEY_END),
                        keyNew ("user/a/2", KEY_END),
                        keyNew ("user/a/b/1", KEY_END),
                        keyNew ("user/a/b/2", KEY_END),
                        keyNew ("user/ab/2", KEY_END),
                        keyNew ("user/b/1", KEY_END),
                        keyNew ("user/b/2", KEY_END),
                        KS_END);
    KeySet *values = 0;
    KeySet *values_below_30 = 0;

    global_a = keyNew ("user/a", KEY_END);

    ksForEach (ks, add_string);
    ksForEach (ks, add_comment);

    out = ksNew(0, KS_END);
    ksFilter (out, ks, has_a);
    ksDel (out);

    out = ksNew(0, KS_END);
    ksFilter (out, ks, below_a);
    ksDel (out);

    out = ksNew(0, KS_END);
    ksFilter (out, ks, direct_below_a);
    ksDel (out);

    ksDel (ks);
    keyDel (global_a);
    global_a = 0;

    values = ksNew (64,
                    keyNew ("user/a", KEY_VALUE, "40", KEY_END),
                    keyNew ("user/b", KEY_VALUE, "20", KEY_END),
                    keyNew ("user/c", KEY_VALUE, "80", KEY_END),
                    keyNew ("user/d", KEY_VALUE, "24", KEY_END),
                    keyNew ("user/e", KEY_VALUE, "32", KEY_END),
                    keyNew ("user/f", KEY_VALUE, "12", KEY_END),
                    keyNew ("user/g", KEY_VALUE, "43", KEY_END),
                    KS_END);

    /* add together */
    ksForEach (values, sum_helper);

    values_below_30 = ksNew(0, KS_END);
    ksFilter (values_below_30, values, below_30);
    ksForEach (values_below_30, sum_helper);

    ksForEach (values, find_80);
    ksCurrent (values); /* here is user/c */
    ksLookupByName (values, "user/c", 0); /* should find the same */
    ksDel (values);
    ksDel (values_below_30);
}
Beispiel #10
0
void outputKeySet (KeySet * returned)
{
	ksRewind (returned);
	while (ksNext (returned))
	{
		printf ("%s\n", keyName (ksCurrent (returned)));
	}
}
Beispiel #11
0
int main ()
{
	// clang-format off
//! [set]
KeySet * myConfig = ksNew (0, KS_END);
Key * parentKey = keyNew ("system/sw/MyApp", KEY_END);
KDB * handle = kdbOpen (parentKey);

kdbGet (handle, myConfig, parentKey); // kdbGet needs to be called first!
KeySet * base = ksDup (myConfig);     // save a copy of original keyset

// change the keys within myConfig

KeySet * ours = ksDup (myConfig); // save a copy of our keyset
KeySet * theirs;		  // needed for 3-way merging
int ret = kdbSet (handle, myConfig, parentKey);
while (ret == -1) // as long as we have an error
{
	// We got an error. Warn user.
	Key * problemKey = ksCurrent (myConfig);
	// parentKey has the errorInformation
	// problemKey is the faulty key (may be null)
	int userInput = showElektraErrorDialog (parentKey, problemKey);
	switch (userInput)
	{
	case INPUT_USE_OURS:
		kdbGet (handle, myConfig, parentKey); // refresh key database
		ksDel (myConfig);
		myConfig = ours;
		break;
	case INPUT_DO_MERGE:
		theirs = ksDup (ours);
		kdbGet (handle, theirs, parentKey); // refresh key database
		KeySet * res = doElektraMerge (ours, theirs, base);
		ksDel (theirs);
		myConfig = res;
		break;
	case INPUT_USE_THEIRS:
		// should always work, we just write what we got
		// but to be sure always give the user another way
		// to exit the loop
		kdbGet (handle, myConfig, parentKey); // refresh key database
		break;
		// other cases ...
	}
	ret = kdbSet (handle, myConfig, parentKey);
}

ksDel (ours);
ksDel (base);
ksDel (myConfig); // delete the in-memory configuration

kdbClose (handle, parentKey); // no more affairs with the key database.
keyDel (parentKey);
//! [set]
}
Beispiel #12
0
/**Returns the value of a meta-information which is current.
 *
 * The pointer is NULL if you reached the end or after
 * ksRewind().
 *
 * @note You must not delete or change the returned key,
 *    use keySetMeta() if you want to delete or change it.
 *
 * @param key the key object to work with
 * @return a buffer to the value pointed by @p key's cursor
 * @retval 0 on NULL pointer
 * @see keyNextMeta(), keyRewindMeta()
 *
 * @see ksCurrent() for pedant in iterator interface of KeySet
 * @ingroup keymeta
 **/
const Key * keyCurrentMeta (const Key * key)
{
	Key * ret;
	if (!key) return 0;
	if (!key->meta) return 0;

	ret = ksCurrent (key->meta);

	return ret;
}
Beispiel #13
0
static int elektraYajlParseNull (void * ctx)
{
	KeySet * ks = (KeySet *)ctx;
	elektraYajlIncrementArrayEntry (ks);

	Key * current = ksCurrent (ks);

	keySetBinary (current, NULL, 0);

#ifdef ELEKTRA_YAJL_VERBOSE
	printf ("elektraYajlParseNull\n");
#endif

	return 1;
}
Beispiel #14
0
static int elektraYajlParseStartArray (void * ctx)
{
	KeySet * ks = (KeySet *)ctx;
	elektraYajlIncrementArrayEntry (ks);

	Key * currentKey = ksCurrent (ks);

	Key * newKey = keyNew (keyName (currentKey), KEY_END);
	// add a pseudo element for empty array
	keyAddName (newKey, "###empty_array");
	ksAppendKey (ks, newKey);

#ifdef ELEKTRA_YAJL_VERBOSE
	printf ("elektraYajlParseStartArray with new key %s\n", keyName (newKey));
#endif

	return 1;
}
Beispiel #15
0
static int elektraYajlParseString (void * ctx, const unsigned char * stringVal, yajl_size_type stringLen)
{
	KeySet * ks = (KeySet *)ctx;
	elektraYajlIncrementArrayEntry (ks);

	Key * current = ksCurrent (ks);

	unsigned char delim = stringVal[stringLen];
	char * stringValue = (char *)stringVal;
	stringValue[stringLen] = '\0';

#ifdef ELEKTRA_YAJL_VERBOSE
	printf ("elektraYajlParseString %s %d\n", stringVal, stringLen);
#endif

	keySetString (current, stringValue);

	// restore old character in buffer
	stringValue[stringLen] = delim;
	return 1;
}
Beispiel #16
0
static int elektraYajlParseBoolean (void * ctx, int boolean)
{
	KeySet * ks = (KeySet *)ctx;
	elektraYajlIncrementArrayEntry (ks);

	Key * current = ksCurrent (ks);

	if (boolean == 1)
	{
		keySetString (current, "true");
	}
	else
	{
		keySetString (current, "false");
	}
	keySetMeta (current, "type", "boolean");

#ifdef ELEKTRA_YAJL_VERBOSE
	printf ("elektraYajlParseBoolean %d\n", boolean);
#endif

	return 1;
}
Beispiel #17
0
/** @brief Set keys in an atomic and universal way.
 *
 * @pre kdbGet() must be called before kdbSet():
 *    - initially (after kdbOpen())
 *    - after conflict errors in kdbSet().
 *
 * @pre The @p returned KeySet must be a valid KeySet, e.g. constructed
 *     with ksNew().
 *
 * @pre The @p parentKey Key must be a valid Key, e.g. constructed with
 *     keyNew().
 *
 * If you pass NULL on any parameter kdbSet() will fail immediately without doing anything.
 *
 * With @p parentKey you can give an hint which part of the given keyset
 * is of interest for you. Then you promise to only modify or
 * remove keys below this key. All others would be passed back
 * as they were retrieved by kdbGet().
 *
 * @par Errors
 * If some error occurs:
 * - kdbSet() will leave the KeySet's * internal cursor on the key that generated the error.
 * - Error information will be written into the metadata of
 *   the parent key.
 * - None of the keys are actually committed in this situation, i.e. no
 *   configuration file will be modified.
 *
 * In case of errors you should present the error message to the user and let the user decide what
 * to do. Possible solutions are:
 * - remove the problematic key and use kdbSet() again (for validation or type errors)
 * - change the value of the problematic key and use kdbSet() again (for validation errors)
 * - do a kdbGet() (for conflicts, i.e. error 30) and then
 *   - set the same keyset again (in favour of what was set by this user)
 *   - drop the old keyset (in favour of what was set from another application)
 *   - merge the original, your own and the other keyset
 * - export the configuration into a file (for unresolvable errors)
 * - repeat the same kdbSet might be of limited use if the user does
 *   not explicitly request it, because temporary
 *   errors are rare and its unlikely that they fix themselves
 *   (e.g. disc full, permission problems)
 *
 * @par Optimization
 * Each key is checked with keyNeedSync() before being actually committed.
 * If no key of a backend needs to be synced
 * any affairs to backends are omitted and 0 is returned.
 *
 * @snippet kdbset.c set
 *
 * showElektraErrorDialog() and doElektraMerge() need to be implemented
 * by the user of Elektra. For doElektraMerge a 3-way merge algorithm exists in
 * libelektra-tools.
 *
 * @param handle contains internal information of @link kdbOpen() opened @endlink key database
 * @param ks a KeySet which should contain changed keys, otherwise nothing is done
 * @param parentKey is used to add warnings and set an error
 *         information. Additionally, its name is an hint which keys
 *         should be committed (it is possible that more are changed).
 *           - cascading keys (starting with /) will set the path in all namespaces
 *           - / will commit all keys
 *           - metanames will be rejected (error 104)
 *           - empty/invalid (error 105)
 * @retval 1 on success
 * @retval 0 if nothing had to be done, no changes in KDB
 * @retval -1 on failure, no changes in KDB
 * @see keyNeedSync()
 * @see ksCurrent() contains the error key
 * @see kdbOpen() and kdbGet() that must be called first
 * @see kdbClose() that must be called afterwards
 * @ingroup kdb
 */
int kdbSet (KDB * handle, KeySet * ks, Key * parentKey)
{
	elektraNamespace ns = keyGetNamespace (parentKey);
	if (ns == KEY_NS_NONE)
	{
		return -1;
	}
	Key * oldError = keyNew (keyName (parentKey), KEY_END);
	copyError (oldError, parentKey);

	if (ns == KEY_NS_META)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERRORF (104, parentKey, "metakey with name \"%s\" passed to kdbSet", keyName (parentKey));
		keyDel (oldError);
		return -1;
	}

	if (ns == KEY_NS_EMPTY)
	{
		ELEKTRA_ADD_WARNING (105, parentKey, "invalid key name passed to kdbSet");
	}

	if (!handle || !ks)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERROR (37, parentKey, "handle or ks null pointer");
		keyDel (oldError);
		return -1;
	}

	int errnosave = errno;
	Key * initialParent = keyDup (parentKey);

	ELEKTRA_LOG ("now in new kdbSet (%s) %p %zd", keyName (parentKey), (void *)handle, ksGetSize (ks));

	elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, INIT);
	elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, DEINIT);

	ELEKTRA_LOG ("after presetstorage maxonce(%s) %p %zd", keyName (parentKey), (void *)handle, ksGetSize (ks));

	Split * split = splitNew ();
	Key * errorKey = 0;

	if (splitBuildup (split, handle, parentKey) == -1)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERROR (38, parentKey, "error in splitBuildup");
		goto error;
	}

	// 1.) Search for syncbits
	int syncstate = splitDivide (split, handle, ks);
	if (syncstate == -1)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERROR (8, parentKey, keyName (ksCurrent (ks)));
		goto error;
	}
	ELEKTRA_ASSERT (syncstate == 0 || syncstate == 1, "syncstate not 0 or 1, but %d", syncstate);

	// 2.) Search for changed sizes
	syncstate |= splitSync (split);
	ELEKTRA_ASSERT (syncstate <= 1, "syncstate not equal or below 1, but %d", syncstate);
	if (syncstate != 1)
	{
		/* No update is needed */
		keySetName (parentKey, keyName (initialParent));
		if (syncstate < 0) clearError (parentKey); // clear previous error to set new one
		if (syncstate == -1)
		{
			ELEKTRA_SET_ERROR (107, parentKey, "Assert failed: invalid namespace");
		}
		else if (syncstate < -1)
		{
			ELEKTRA_SET_ERROR (107, parentKey, keyName (split->parents[-syncstate - 2]));
		}
		keyDel (initialParent);
		splitDel (split);
		errno = errnosave;
		keyDel (oldError);
		return syncstate == 0 ? 0 : -1;
	}
	ELEKTRA_ASSERT (syncstate == 1, "syncstate not 1, but %d", syncstate);

	splitPrepare (split);

	clearError (parentKey); // clear previous error to set new one
	if (elektraSetPrepare (split, parentKey, &errorKey, handle->globalPlugins) == -1)
	{
		goto error;
	}
	else
	{
		// no error, restore old error
		copyError (parentKey, oldError);
	}
	keySetName (parentKey, keyName (initialParent));

	elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, INIT);
	elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, DEINIT);

	elektraSetCommit (split, parentKey);

	elektraGlobalSet (handle, ks, parentKey, COMMIT, INIT);
	elektraGlobalSet (handle, ks, parentKey, COMMIT, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, COMMIT, DEINIT);

	splitUpdateSize (split);

	keySetName (parentKey, keyName (initialParent));

	elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, INIT);
	elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, DEINIT);

	for (size_t i = 0; i < ks->size; ++i)
	{
		// remove all flags from all keys
		clear_bit (ks->array[i]->flags, KEY_FLAG_SYNC);
	}

	keySetName (parentKey, keyName (initialParent));
	keyDel (initialParent);
	splitDel (split);

	keyDel (oldError);
	errno = errnosave;
	return 1;

error:
	keySetName (parentKey, keyName (initialParent));

	elektraGlobalError (handle, ks, parentKey, PREROLLBACK, INIT);
	elektraGlobalError (handle, ks, parentKey, PREROLLBACK, MAXONCE);
	elektraGlobalError (handle, ks, parentKey, PREROLLBACK, DEINIT);

	elektraSetRollback (split, parentKey);

	if (errorKey)
	{
		Key * found = ksLookup (ks, errorKey, 0);
		if (!found)
		{
			ELEKTRA_ADD_WARNING (82, parentKey, keyName (errorKey));
		}
	}

	keySetName (parentKey, keyName (initialParent));

	elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, INIT);
	elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, MAXONCE);
	elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, DEINIT);

	keySetName (parentKey, keyName (initialParent));
	keyDel (initialParent);
	splitDel (split);
	errno = errnosave;
	keyDel (oldError);
	return -1;
}
Beispiel #18
0
/**
 * @internal
 * @brief Does all set steps but not commit
 *
 * @param split all information for iteration
 * @param parentKey to add warnings (also passed to plugins for the same reason)
 * @param [out] errorKey may point to which key caused the error or 0 otherwise
 *
 * @retval -1 on error
 * @retval 0 on success
 */
static int elektraSetPrepare (Split * split, Key * parentKey, Key ** errorKey, Plugin * hooks[][NR_GLOBAL_SUBPOSITIONS])
{
	int any_error = 0;
	for (size_t i = 0; i < split->size; i++)
	{
		for (size_t p = 0; p < COMMIT_PLUGIN; ++p)
		{
			int ret = 0; // last return value

			Backend * backend = split->handles[i];
			ksRewind (split->keysets[i]);
			if (backend->setplugins[p] && backend->setplugins[p]->kdbSet)
			{
				if (p != 0)
				{
					keySetString (parentKey, keyString (split->parents[i]));
				}
				else
				{
					keySetString (parentKey, "");
				}
				keySetName (parentKey, keyName (split->parents[i]));
				ret = backend->setplugins[p]->kdbSet (backend->setplugins[p], split->keysets[i], parentKey);

#if VERBOSE && DEBUG
				printf ("Prepare %s with keys %zd in plugin: %zu, split: %zu, ret: %d\n", keyName (parentKey),
					ksGetSize (split->keysets[i]), p, i, ret);
#endif

				if (p == 0)
				{
					if (ret == 0)
					{
						// resolver says that sync is
						// not needed, so we
						// skip other pre-commit
						// plugins
						break;
					}
					keySetString (split->parents[i], keyString (parentKey));
				}
			}

			if (p == 0)
			{
				if (hooks[PRESETSTORAGE][FOREACH])
				{
					ksRewind (split->keysets[i]);
					hooks[PRESETSTORAGE][FOREACH]->kdbSet (hooks[PRESETSTORAGE][FOREACH], split->keysets[i], parentKey);
				}
			}
			else if (p == (STORAGE_PLUGIN - 1))
			{
				if (hooks[PRESETCLEANUP][FOREACH])
				{
					ksRewind (split->keysets[i]);
					hooks[PRESETCLEANUP][FOREACH]->kdbSet (hooks[PRESETCLEANUP][FOREACH], split->keysets[i], parentKey);
				}
			}

			if (ret == -1)
			{
				// do not
				// abort because it might
				// corrupt the KeySet
				// and leads to warnings
				// because of .tmp files not
				// found
				*errorKey = ksCurrent (split->keysets[i]);

				// so better keep going, but of
				// course we will not commit
				any_error = -1;
			}
		}
	}
	return any_error;
}
Beispiel #19
0
/**
 * @brief Retrieve keys in an atomic and universal way.
 *
 * @pre The @p handle must be passed as returned from kdbOpen().
 *
 * @pre The @p returned KeySet must be a valid KeySet, e.g. constructed
 *     with ksNew().
 *
 * @pre The @p parentKey Key must be a valid Key, e.g. constructed with
 *     keyNew().
 *
 * If you pass NULL on any parameter kdbGet() will fail immediately without doing anything.
 *
 * The @p returned KeySet may already contain some keys, e.g. from previous
 * kdbGet() calls. The new retrieved keys will be appended using
 * ksAppendKey().
 *
 * If not done earlier kdbGet() will fully retrieve all keys under the @p parentKey
 * folder recursively (See Optimization below when it will not be done).
 *
 * @note kdbGet() might retrieve more keys than requested (that are not
 *     below parentKey). These keys must be passed to calls of kdbSet(),
 *     otherwise they will be lost. This stems from the fact that the
 *     user has the only copy of the whole configuration and backends
 *     only write configuration that was passed to them.
 *     For example, if you kdbGet() "system/mountpoint/interest"
 *     you will not only get all keys below system/mountpoint/interest,
 *     but also all keys below system/mountpoint (if system/mountpoint
 *     is a mountpoint as the name suggests, but
 *     system/mountpoint/interest is not a mountpoint).
 *     Make sure to not touch or remove keys outside the keys of interest,
 *     because others may need them!
 *
 * @par Example:
 * This example demonstrates the typical usecase within an application
 * (without error handling).
 *
 * @include kdbget.c
 *
 * When a backend fails kdbGet() will return -1 with all
 * error and warning information in the @p parentKey.
 * The parameter @p returned will not be changed.
 *
 * @par Optimization:
 * In the first run of kdbGet all requested (or more) keys are retrieved. On subsequent
 * calls only the keys are retrieved where something was changed
 * inside the key database. The other keys stay in the
 * KeySet returned as passed.
 *
 * It is your responsibility to save the original keyset if you
 * need it afterwards.
 *
 * If you want to be sure to get a fresh keyset again, you need to open a
 * second handle to the key database using kdbOpen().
 *
 * @param handle contains internal information of @link kdbOpen() opened @endlink key database
 * @param parentKey is used to add warnings and set an error
 *         information. Additionally, its name is a hint which keys
 *         should be retrieved (it is possible that more are retrieved, see Note above).
 *           - cascading keys (starting with /) will retrieve the same path in all namespaces
 *           - / will retrieve all keys
 * @param ks the (pre-initialized) KeySet returned with all keys found
 * 	will not be changed on error or if no update is required
 * @see ksLookup(), ksLookupByName() for powerful
 * 	lookups after the KeySet was retrieved
 * @see kdbOpen() which needs to be called before
 * @see kdbSet() to save the configuration afterwards and kdbClose() to
 * 	finish affairs with the key database.
 * @retval 1 if the keys were retrieved successfully
 * @retval 0 if there was no update - no changes are made to the keyset then
 * @retval -1 on failure - no changes are made to the keyset then
 * @ingroup kdb
 */
int kdbGet (KDB * handle, KeySet * ks, Key * parentKey)
{
	elektraNamespace ns = keyGetNamespace (parentKey);
	if (ns == KEY_NS_NONE)
	{
		return -1;
	}

	Key * oldError = keyNew (keyName (parentKey), KEY_END);
	copyError (oldError, parentKey);

	if (ns == KEY_NS_META)
	{
		clearError (parentKey);
		keyDel (oldError);
		ELEKTRA_SET_ERRORF (104, parentKey, "metakey with name \"%s\" passed to kdbGet", keyName (parentKey));
		return -1;
	}

	if (ns == KEY_NS_EMPTY)
	{
		ELEKTRA_ADD_WARNING (105, parentKey, "invalid key name passed to kdbGet");
	}

	int errnosave = errno;
	Key * initialParent = keyDup (parentKey);

	ELEKTRA_LOG ("now in new kdbGet (%s)", keyName (parentKey));

	Split * split = splitNew ();

	if (!handle || !ks)
	{
		clearError (parentKey);
		ELEKTRA_SET_ERROR (37, parentKey, "handle or ks null pointer");
		goto error;
	}

	elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, INIT);
	elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, MAXONCE);
	elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, DEINIT);

	if (splitBuildup (split, handle, parentKey) == -1)
	{
		clearError (parentKey);
		ELEKTRA_SET_ERROR (38, parentKey, "error in splitBuildup");
		goto error;
	}

	// Check if a update is needed at all
	switch (elektraGetCheckUpdateNeeded (split, parentKey))
	{
	case 0: // We don't need an update so let's do nothing
		keySetName (parentKey, keyName (initialParent));
		elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT);
		elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
		elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);
		splitUpdateFileName (split, handle, parentKey);
		keyDel (initialParent);
		splitDel (split);
		errno = errnosave;
		keyDel (oldError);
		return 0;
	case -1:
		goto error;
		// otherwise fall trough
	}

	// Appoint keys (some in the bypass)
	if (splitAppoint (split, handle, ks) == -1)
	{
		clearError (parentKey);
		ELEKTRA_SET_ERROR (38, parentKey, "error in splitAppoint");
		goto error;
	}

	if (handle->globalPlugins[POSTGETSTORAGE][FOREACH] || handle->globalPlugins[POSTGETCLEANUP][FOREACH])
	{
		clearError (parentKey);
		if (elektraGetDoUpdateWithGlobalHooks (NULL, split, NULL, parentKey, initialParent, FIRST) == -1)
		{
			goto error;
		}
		else
		{
			copyError (parentKey, oldError);
		}

		keySetName (parentKey, keyName (initialParent));

		if (splitGet (split, parentKey, handle) == -1)
		{
			ELEKTRA_ADD_WARNING (108, parentKey, keyName (ksCurrent (ks)));
			// continue, because sizes are already updated
		}
		ksClear (ks);
		splitMerge (split, ks);

		clearError (parentKey);
		if (elektraGetDoUpdateWithGlobalHooks (handle, split, ks, parentKey, initialParent, LAST) == -1)
		{
			goto error;
		}
		else
		{
			copyError (parentKey, oldError);
		}
	}
	else
	{

		/* Now do the real updating,
		   but not for bypassed keys in split->size-1 */
		clearError (parentKey);
		if (elektraGetDoUpdate (split, parentKey) == -1)
		{
			goto error;
		}
		else
		{
			copyError (parentKey, oldError);
		}
		/* Now post-process the updated keysets */
		if (splitGet (split, parentKey, handle) == -1)
		{
			ELEKTRA_ADD_WARNING (108, parentKey, keyName (ksCurrent (ks)));
			// continue, because sizes are already updated
		}
		/* We are finished, now just merge everything to returned */
		ksClear (ks);

		splitMerge (split, ks);
	}

	elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT);
	elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
	elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);

	ksRewind (ks);

	keySetName (parentKey, keyName (initialParent));

	splitUpdateFileName (split, handle, parentKey);
	keyDel (initialParent);
	keyDel (oldError);
	splitDel (split);
	errno = errnosave;
	return 1;

error:
	keySetName (parentKey, keyName (initialParent));
	elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, INIT);
	elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
	elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);

	keySetName (parentKey, keyName (initialParent));
	if (handle) splitUpdateFileName (split, handle, parentKey);
	keyDel (initialParent);
	keyDel (oldError);
	splitDel (split);
	errno = errnosave;
	return -1;
}