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); }
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 }
/** @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; } }
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; }
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; }
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); }
/** * @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; }
/**@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); }
void outputKeySet (KeySet * returned) { ksRewind (returned); while (ksNext (returned)) { printf ("%s\n", keyName (ksCurrent (returned))); } }
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] }
/**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; }
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; }
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; }
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; }
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; }
/** @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; }
/** * @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; }
/** * @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; }