예제 #1
0
파일: kdb.c 프로젝트: KurtMi/libelektra
/**
 * @brief Bootstrap, first phase with fallback
 * @internal
 *
 * @param handle already allocated, but without defaultBackend
 * @param [out] keys for bootstrapping
 * @param errorKey key to add errors too
 *
 * @retval -1 failure: cannot initialize defaultBackend
 * @retval 0 warning: could not get initial config
 * @retval 1 success
 * @retval 2 success in fallback mode
 */
int elektraOpenBootstrap (KDB * handle, KeySet * keys, Key * errorKey)
{
	handle->defaultBackend = backendOpenDefault (handle->modules, KDB_DB_INIT, errorKey);
	if (!handle->defaultBackend) return -1;

	handle->split = splitNew ();
	splitAppend (handle->split, handle->defaultBackend, keyNew (KDB_SYSTEM_ELEKTRA, KEY_END), 2);

	keySetName (errorKey, KDB_SYSTEM_ELEKTRA);
	keySetString (errorKey, "kdbOpen(): get");

	int funret = 1;
	int ret = kdbGet (handle, keys, errorKey);
	int fallbackret = 0;
	if (ret == 0 || ret == -1)
	{
		// could not get KDB_DB_INIT, try KDB_DB_FILE
		// first cleanup:
		ksClear (keys);
		backendClose (handle->defaultBackend, errorKey);
		splitDel (handle->split);

		// then create new setup:
		handle->defaultBackend = backendOpenDefault (handle->modules, KDB_DB_FILE, errorKey);
		if (!handle->defaultBackend)
		{
			elektraRemoveMetaData (errorKey, "error"); // fix errors from kdbGet()
			return -1;
		}
		handle->split = splitNew ();
		splitAppend (handle->split, handle->defaultBackend, keyNew (KDB_SYSTEM_ELEKTRA, KEY_END), 2);

		keySetName (errorKey, KDB_SYSTEM_ELEKTRA);
		keySetString (errorKey, "kdbOpen(): get fallback");
		fallbackret = kdbGet (handle, keys, errorKey);
		keySetName (errorKey, "system/elektra/mountpoints");

		KeySet * cutKeys = ksCut (keys, errorKey);
		if (fallbackret == 1 && ksGetSize (cutKeys) != 0)
		{
			funret = 2;
		}
		ksAppend (keys, cutKeys);
		ksDel (cutKeys);
	}

	if (ret == -1 && fallbackret == -1)
	{
		funret = 0;
	}

	elektraRemoveMetaData (errorKey, "error"); // fix errors from kdbGet()
	return funret;
}
예제 #2
0
static void flushConvertedKeys (Key * target, KeySet * converted, KeySet * orig)
{
    if (ksGetSize (converted) == 0) return;

    ksRewind (converted);
    Key * current;
    Key * appendTarget;

    while ((current = ksNext (converted)))
    {
        appendTarget = target;
        const char * metaName = keyString (keyGetMeta (current, CONVERT_METANAME));

        Key * currentDup = keyDup (current);
        Key * targetDup = keyDup (appendTarget);
        keySetBaseName (currentDup, 0);
        keySetBaseName (targetDup, 0);

        /* the convert key request to be converted to a key
         * on the same level, but the target is below or above
         */
        if (keyGetMeta (current, CONVERT_APPEND_SAMELEVEL) && keyCmp (currentDup, targetDup))
        {
            appendTarget = 0;
        }

        keyDel (currentDup);
        keyDel (targetDup);

        /* no target key was found of the target
         * was discarded for some reason. Revert to the parent
         */
        if (!appendTarget)
        {
            appendTarget = findNearestParent (current, orig);
        }

        elektraKeyAppendMetaLine (appendTarget, metaName, keyString (current));
        removeKeyFromResult (current, target, orig);
    }

    ksClear (converted);
}
예제 #3
0
static void test_getArrayNext (void)
{
	printf ("Test get array next");

	KeySet * array = ksNew (10, keyNew ("user/test/array/#0", KEY_END), keyNew ("user/test/array/#1", KEY_END),

				KS_END);

	Key * nextKey = elektraArrayGetNextKey (array);
	exit_if_fail (array, "The getnext function did not return a proper key");
	succeed_if (!strcmp (keyName (nextKey), "user/test/array/#2"), "The getnext function did not use the correct keyname");

	keyDel (nextKey);
	ksClear (array);
	nextKey = elektraArrayGetNextKey (array);
	succeed_if (!nextKey, "The getnext function did not return NULL on an empty array");
	keyDel (nextKey);

	ksDel (array);
}
예제 #4
0
static void test_ksResize ()
{
    int i;
    KeySet * ks = 0;
    KeySet * copy = ksNew (0, KS_END);
    char name[NAME_SIZE];

    ks = ksNew (20, keyNew ("user/test01", KEY_END), keyNew ("user/test02", KEY_END), keyNew ("user/test03", KEY_END),
                keyNew ("user/test04", KEY_END), keyNew ("user/test05", KEY_END), keyNew ("user/test11", KEY_END),
                keyNew ("user/test12", KEY_END), keyNew ("user/test13", KEY_END), keyNew ("user/test14", KEY_END),
                keyNew ("user/test15", KEY_END), keyNew ("user/test21", KEY_END), keyNew ("user/test22", KEY_END),
                keyNew ("user/test23", KEY_END), keyNew ("user/test24", KEY_END), keyNew ("user/test25", KEY_END),
                keyNew ("user/test31", KEY_END), keyNew ("user/test32", KEY_END), keyNew ("user/test33", KEY_END),
                keyNew ("user/test34", KEY_END), keyNew ("user/test35", KEY_END), KS_END);
    succeed_if (ksGetAlloc (ks) == 20, "20 keys with alloc 20 should work");
    ksDel (ks);

    printf ("Test resize of keyset\n");
    exit_if_fail ((ks = ksNew (0, KS_END)) != 0, "could not create new keyset");
    for (i = 0; i < 100; i++)
    {
        snprintf (name, NAME_SIZE, "user/test%d", i);
        ksAppendKey (ks, keyNew (name, KEY_END));
        if (i >= 63)
        {
            succeed_if (ksGetAlloc (ks) == 127, "allocation size wrong");
        }
        else if (i >= 31)
        {
            succeed_if (ksGetAlloc (ks) == 63, "allocation size wrong");
        }
        else if (i >= 15)
        {
            succeed_if (ksGetAlloc (ks) == 31, "allocation size wrong");
        }
        else if (i >= 0)
        {
            succeed_if (ksGetAlloc (ks) == 15, "allocation size wrong");
        }
    }
    succeed_if (ksGetSize (ks) == 100, "could not append 100 keys");
    succeed_if (ksGetAlloc (ks) == 127, "allocation size wrong");
    for (i = 100; i >= 0; i--)
    {
        keyDel (ksPop (ks));
        if (i >= 64)
        {
            succeed_if (ksGetAlloc (ks) == 127, "allocation size wrong");
        }
        else if (i >= 32)
        {
            succeed_if (ksGetAlloc (ks) == 63, "allocation size wrong");
        }
        else if (i >= 16)
        {
            succeed_if (ksGetAlloc (ks) == 31, "allocation size wrong");
        }
        else if (i >= 0)
        {
            succeed_if (ksGetAlloc (ks) == 15, "allocation size wrong");
        }
    }
    succeed_if (ksGetSize (ks) == 0, "could not pop 100 keys");
    succeed_if (ksGetAlloc (ks) == 15, "allocation size wrong");
    ksDel (ks);

    exit_if_fail ((ks = ksNew (0, KS_END)) != 0, "could not create new keyset");
    ksResize (ks, 100);
    succeed_if (ksGetAlloc (ks) == 100, "allocation size wrong");
    for (i = 0; i < 100; i++)
    {
        snprintf (name, NAME_SIZE, "user/test%d", i);
        ksAppendKey (ks, keyNew (name, KEY_END));
        succeed_if (ksGetAlloc (ks) == 100, "allocation size wrong");
    }
    succeed_if (ksGetSize (ks) == 100, "could not append 100 keys");
    succeed_if (ksGetAlloc (ks) == 100, "allocation size wrong");
    ksDel (ks);

    ks =
#include "data_keyset.c"

        succeed_if (ksGetSize (ks) == 102, "Problem loading keyset with 102 keys");
    succeed_if (ksGetAlloc (ks) == 102, "alloc size wrong");

    ksCopy (copy, ks);
    succeed_if (ksGetSize (copy) == 102, "Problem copy keyset with 102 keys");
    succeed_if (ksGetAlloc (copy) == 127, "alloc of copy size wrong");

    compare_keyset (copy, ks);

    ksClear (copy); // useless, just test for double free
    ksCopy (copy, ks);

    succeed_if (ksGetSize (copy) == 102, "Problem copy keyset with 102 keys");
    succeed_if (ksGetAlloc (copy) == 127, "alloc of copy size wrong");
    compare_keyset (copy, ks);

    ksDel (copy);
    ksDel (ks);
}
예제 #5
0
파일: kdb.c 프로젝트: KurtMi/libelektra
/**
 * @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;
}