/** * @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; }
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); }
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); }
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); }
/** * @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; }