/** * @internal * Check if two keys have the same name. * If one of the keys is cascading only the cascading names are compared. * * @param key key * @param check check * @retval 1 if keys have the same name * @retval 0 otherwise */ static int checkKeyIsSame (Key * key, Key * check) { int result = 0; if (keyGetNamespace (check) == KEY_NS_CASCADING || keyGetNamespace (key) == KEY_NS_CASCADING) { const char * cascadingCheck = strrchr (keyName (check), '/'); const char * cascadingKey = strrchr (keyName (key), '/'); if (cascadingCheck != NULL && cascadingKey != NULL) { result = elektraStrCmp (cascadingKey, cascadingCheck) == 0; } else { if (cascadingCheck == NULL) { ELEKTRA_LOG_WARNING ("invalid key given: '%s' is not a valid key", cascadingCheck); } if (cascadingKey == NULL) { ELEKTRA_LOG_WARNING ("invalid key given: '%s' is not a valid key", cascadingKey); } } } else { result = elektraStrCmp (keyName (check), keyName (key)) == 0; } return result; }
// keyRel2 helper, returns how many levels check is below key, or 0 if check isn't below int keyGetLevelsBelow (const Key * key, const Key * check) { if (!keyIsBelow (key, check)) return 0; if (keyGetNamespace (key) != keyGetNamespace (check)) return 0; Key * toCheck = keyDup (check); int levels = 0; while (strcmp (keyName (key), keyName (toCheck))) { keySetBaseName (toCheck, 0); if (keyName (toCheck)[0] == '\0') keySetName (toCheck, "/"); ++levels; } keyDel (toCheck); return levels; }
static KeySet * prepareGlobalKS (KeySet * ks, Key * parentKey) { ksRewind (ks); Key * cutKey = keyNew ("/", KEY_CASCADING_NAME, KEY_END); keyAddName (cutKey, strchr (keyName (parentKey), '/')); KeySet * cutKS = ksCut (ks, cutKey); Key * specCutKey = keyNew ("spec", KEY_END); KeySet * specCut = ksCut (cutKS, specCutKey); ksRewind (specCut); Key * cur; while ((cur = ksNext (specCut)) != NULL) { if (keyGetNamespace (cur) == KEY_NS_CASCADING) { ksAppendKey (cutKS, cur); keyDel (ksLookup (specCut, cur, KDB_O_POP)); } } ksAppend (ks, specCut); ksDel (specCut); keyDel (specCutKey); keyDel (cutKey); ksRewind (cutKS); return cutKS; }
static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey) { resolverHandles * pks = elektraPluginGetData (handle); ELEKTRA_ASSERT (pks != NULL, "Unable to retrieve plugin data for handle %p with parentKey %s", (void *) handle, keyName (parentKey)); switch (keyGetNamespace (parentKey)) { case KEY_NS_SPEC: return &pks->spec; case KEY_NS_DIR: return &pks->dir; case KEY_NS_USER: return &pks->user; case KEY_NS_SYSTEM: return &pks->system; case KEY_NS_PROC: case KEY_NS_EMPTY: case KEY_NS_NONE: case KEY_NS_META: case KEY_NS_CASCADING: return 0; } return 0; }
void printNamespace(Key const* k) { //! [namespace] switch (keyGetNamespace(k)) { case KEY_NS_SPEC: printf ("spec namespace\n"); break; case KEY_NS_PROC: printf ("proc namespace\n"); break; case KEY_NS_DIR: printf ("dir namespace\n"); break; case KEY_NS_USER: printf ("user namespace\n"); break; case KEY_NS_SYSTEM: printf ("system namespace\n"); break; case KEY_NS_EMPTY: printf ("empty name\n"); break; case KEY_NS_NONE: printf ("no key\n"); break; case KEY_NS_META: printf ("meta key\n"); break; case KEY_NS_CASCADING: printf ("cascading key\n"); break; } //! [namespace] }
/** * Also update sizes after kdbSet() to recognize multiple kdbSet() attempts. * * @warning cant use the same code with elektraSplitGet because there is * no default split part for kdbSet(). */ int elektraSplitUpdateSize (Split * split) { /* Iterate everything */ for (size_t i = 0; i < split->size; ++i) { switch (keyGetNamespace (split->parents[i])) { case KEY_NS_SPEC: split->handles[i]->specsize = ksGetSize (split->keysets[i]); break; case KEY_NS_DIR: split->handles[i]->dirsize = ksGetSize (split->keysets[i]); break; case KEY_NS_USER: split->handles[i]->usersize = ksGetSize (split->keysets[i]); break; case KEY_NS_SYSTEM: split->handles[i]->systemsize = ksGetSize (split->keysets[i]); break; case KEY_NS_PROC: case KEY_NS_EMPTY: case KEY_NS_NONE: case KEY_NS_META: case KEY_NS_CASCADING: return -1; } } return 1; }
/** * @brief Filter out keys not in the correct keyset * * @param split the split where to do it * @param i for which split * @param warningKey the key * @param handle where to do backend lookups * * @retval -1 on error (no backend, wrong namespace) * @retval 0 otherwise */ static int elektraSplitPostprocess (Split * split, int i, Key * warningKey, KDB * handle) { Key * cur = 0; Backend * curHandle = 0; ksRewind (split->keysets[i]); while ((cur = ksNext (split->keysets[i])) != 0) { curHandle = elektraMountGetBackend (handle, cur); if (!curHandle) return -1; keyClearSync (cur); if (curHandle != split->handles[i]) { elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is hidden by other mountpoint"); } else switch (keyGetNamespace (cur)) { case KEY_NS_SPEC: if (!keyIsSpec (split->parents[i])) elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not spec"); break; case KEY_NS_DIR: if (!keyIsDir (split->parents[i])) elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not dir"); break; case KEY_NS_USER: if (!keyIsUser (split->parents[i])) elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not user"); break; case KEY_NS_SYSTEM: if (!keyIsSystem (split->parents[i])) elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not system"); break; case KEY_NS_PROC: elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has a proc key name"); break; case KEY_NS_EMPTY: elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has an empty name"); break; case KEY_NS_META: elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has a meta name"); break; case KEY_NS_CASCADING: elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has a cascading name"); break; case KEY_NS_NONE: ELEKTRA_ASSERT (0 && "wrong key namespace, should not be none"); return -1; } } return 0; }
static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey) { resolverHandles * pks = elektraPluginGetData (handle); switch (keyGetNamespace (parentKey)) { case KEY_NS_SPEC: return &pks->spec; case KEY_NS_DIR: return &pks->dir; case KEY_NS_USER: return &pks->user; case KEY_NS_SYSTEM: return &pks->system; case KEY_NS_PROC: case KEY_NS_EMPTY: case KEY_NS_NONE: case KEY_NS_META: case KEY_NS_CASCADING: break; } ELEKTRA_ASSERT (0, "namespace %d not valid for resolving", keyGetNamespace (parentKey)); return 0; }
static int elektraResolveFilename (Key * parentKey, ElektraResolveTempfile tmpFile) { int rc = 0; void * handle = elektraInvokeOpen ("resolver", 0, 0); if (!handle) { rc = -1; goto RESOLVE_FAILED; } ElektraResolved * resolved = NULL; typedef ElektraResolved * (*resolveFileFunc) (elektraNamespace, const char *, ElektraResolveTempfile, Key *); resolveFileFunc resolveFunc = *(resolveFileFunc *) elektraInvokeGetFunction (handle, "filename"); if (!resolveFunc) { rc = -1; goto RESOLVE_FAILED; } typedef void (*freeHandleFunc) (ElektraResolved *); freeHandleFunc freeHandle = *(freeHandleFunc *) elektraInvokeGetFunction (handle, "freeHandle"); if (!freeHandle) { rc = -1; goto RESOLVE_FAILED; } resolved = resolveFunc (keyGetNamespace (parentKey), keyString (parentKey), tmpFile, parentKey); if (!resolved) { rc = -1; goto RESOLVE_FAILED; } else { keySetString (parentKey, resolved->fullPath); freeHandle (resolved); } RESOLVE_FAILED: elektraInvokeClose (handle, 0); return rc; }
/** * Determines if the backend is already inserted or not. * * @warning If no parent Key is given, the default/root backends won't * be searched. * * @param split the split object to work with * @param backend the backend to search for * @param parent the key to check for domains in default/root backends. * @return pos of backend if it already exist * @retval -1 if it does not exist * @ingroup split */ ssize_t elektraSplitSearchBackend (Split * split, Backend * backend, Key * parent) { for (size_t i = 0; i < split->size; ++i) { if (backend == split->handles[i]) { if (test_bit (split->syncbits[i], SPLIT_FLAG_CASCADING)) { switch (keyGetNamespace (parent)) { case KEY_NS_SPEC: if (keyIsSpec (split->parents[i])) return i; break; case KEY_NS_DIR: if (keyIsDir (split->parents[i])) return i; break; case KEY_NS_USER: if (keyIsUser (split->parents[i])) return i; break; case KEY_NS_SYSTEM: if (keyIsSystem (split->parents[i])) return i; break; case KEY_NS_PROC: return -1; case KEY_NS_EMPTY: return -1; case KEY_NS_NONE: return -1; case KEY_NS_META: return -1; case KEY_NS_CASCADING: return -1; } continue; } /* We already have this backend, so leave */ return i; } } return -1; }
// keyRel2 helper, turns key into a cascading key ( removes namespace) Key * keyAsCascading (const Key * key) { if (keyName (key)[0] == '/') { return keyDup (key); } else { elektraNamespace ns = keyGetNamespace (key); if (ns == KEY_NS_META || ns == KEY_NS_EMPTY || ns == KEY_NS_NONE) { // For metakeys or keys without namespace just prefix the keyname with a "/" Key * cKey = keyNew ("/", KEY_CASCADING_NAME, KEY_END); keyAddName (cKey, keyName (key)); return cKey; } else { // Skip namespace const char * name = keyName (key); const char * ptr = strchr (name, '/'); if (!ptr) { return keyNew ("/", KEY_CASCADING_NAME, KEY_END); } else { ssize_t length = keyGetNameSize (key); if ((ptr - name) == (length - 1)) { return keyNew ("/", KEY_CASCADING_NAME, KEY_END); } else { return keyNew (ptr, KEY_CASCADING_NAME, KEY_END); } } } } }
static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey) { resolverHandles * pks = elektraPluginGetData (handle); switch (keyGetNamespace (parentKey)) { case KEY_NS_SPEC: return &pks->spec; case KEY_NS_DIR: return &pks->dir; case KEY_NS_USER: return &pks->user; case KEY_NS_SYSTEM: return &pks->system; case KEY_NS_PROC: case KEY_NS_EMPTY: case KEY_NS_NONE: case KEY_NS_META: case KEY_NS_CASCADING: return 0; } return 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; }
/** * @brief checks if a given Key k is in the spec namespace. * @retval 0 if the Key k is in the spec namespace. * @retval 1 if the Key k is NOT in the spec namespace. */ static inline int isSpecNamespace (const Key * k) { return (keyGetNamespace (k) == KEY_NS_SPEC); }
/** * @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; }
int keyRel2 (const Key * key, const Key * check, KeyRelType which) { if (!key || !check) return -1; if (!key->key || !check->key) return -1; Key * cKey = keyAsCascading (key); Key * cCheck = keyAsCascading (check); Key * cKeyParent = keyDup (cKey); keySetBaseName (cKeyParent, 0); if (keyName (cKeyParent)[0] == '\0') keySetName (cKeyParent, "/"); int isBelow = 0; int isSilblingNephew = 0; isBelow = keyGetLevelsBelow (cKey, cCheck); if (!isBelow) isSilblingNephew = keyGetLevelsBelow (cKeyParent, cCheck); elektraNamespace keyNamespace = keyGetNamespace (key); elektraNamespace checkNamespace = keyGetNamespace (check); int retVal = 0; int bits = 0; for (KeyRelType type = 1; type != 0; type <<= 1) { if (type & which) ++bits; } if (bits != 1) return -1; switch (which) { case ELEKTRA_REL_BELOW_SAME_NS: if (isBelow && (keyNamespace == checkNamespace)) retVal = isBelow; break; case ELEKTRA_REL_BELOW_IGNORE_NS: if (isBelow) retVal = isBelow; break; case ELEKTRA_REL_BELOW_CASCADING_NS: if (isBelow && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = isBelow; break; case ELEKTRA_REL_DIRECT_BELOW_SAME_NS: if ((isBelow == 1) && (keyNamespace == checkNamespace)) retVal = 1; break; case ELEKTRA_REL_DIRECT_BELOW_IGNORE_NS: if (isBelow == 1) retVal = 1; break; case ELEKTRA_REL_DIRECT_BELOW_CASCADING_NS: if ((isBelow == 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = 1; break; case ELEKTRA_REL_SILBLING_SAME_NS: if ((isSilblingNephew == 1) && (keyNamespace == checkNamespace)) retVal = 1; break; case ELEKTRA_REL_SILBLING_IGNORE_NS: if (isSilblingNephew == 1) retVal = 1; break; case ELEKTRA_REL_SILBLING_CASCADING_NS: if ((isSilblingNephew == 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = 1; break; case ELEKTRA_REL_NEPHEW_SAME_NS: if ((isSilblingNephew > 1) && (keyNamespace == checkNamespace)) retVal = isSilblingNephew - 1; break; case ELEKTRA_REL_NEPHEW_IGNORE_NS: if (isSilblingNephew > 1) retVal = isSilblingNephew - 1; break; case ELEKTRA_REL_NEPHEW_CASCADING_NS: if ((isSilblingNephew > 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = isSilblingNephew - 1; break; default: retVal = -1; break; } keyDel (cKey); keyDel (cCheck); keyDel (cKeyParent); return retVal; }
static void test_keyNamespace () { Key * key; printf ("Test namespaces\n"); succeed_if (keyGetNamespace (0) == KEY_NS_NONE, "null key"); key = keyNew (0); succeed_if (keyGetNamespace (key) == KEY_NS_EMPTY, "empty namespace not empty"); succeed_if (keyNameIsSystem (keyName (key)) == 0, "empty name is not system"); succeed_if (keyIsSystem (key) == 0, "empty key is not system"); succeed_if (keyNameIsUser (keyName (key)) == 0, "empty name is not user"); succeed_if (keyIsUser (key) == 0, "empty key is not user"); keyDel (key); key = keyNew ("", KEY_END); succeed_if (keyGetNamespace (key) == KEY_NS_EMPTY, "empty namespace not empty"); succeed_if (keyNameIsSystem (keyName (key)) == 0, "empty name is not system"); succeed_if (keyIsSystem (key) == 0, "empty key is not system"); succeed_if (keyNameIsUser (keyName (key)) == 0, "empty name is not user"); succeed_if (keyIsUser (key) == 0, "empty key is not user"); keyDel (key); key = keyNew ("user", KEY_END); succeed_if (keyGetNamespace (key) == KEY_NS_USER, "user namespace not KEY_NS_USER"); succeed_if (keyNameIsSystem (keyName (key)) == 0, "user name is not system"); succeed_if (keyIsSystem (key) == 0, "user key is not system"); succeed_if (keyNameIsUser (keyName (key)) == 1, "user name is not user"); succeed_if (keyIsUser (key) == 1, "user key is not user"); keyDel (key); key = keyNew ("user/key", KEY_END); succeed_if (keyGetNamespace (key) == KEY_NS_USER, "user namespace not KEY_NS_USER"); succeed_if (keyNameIsSystem (keyName (key)) == 0, "user name is not system"); succeed_if (keyIsSystem (key) == 0, "user key is not system"); succeed_if (keyNameIsUser (keyName (key)) == 1, "user name is not user"); succeed_if (keyIsUser (key) == 1, "user key is not user"); keyDel (key); key = keyNew ("user:owner/key", KEY_END); succeed_if (keyGetNamespace (key) == KEY_NS_USER, "user namespace not KEY_NS_USER"); succeed_if (keyNameIsSystem (keyName (key)) == 0, "user name is not system"); succeed_if (keyIsSystem (key) == 0, "user key is not system"); succeed_if (keyNameIsUser (keyName (key)) == 1, "user name is not user"); succeed_if (keyIsUser (key) == 1, "user key is not user"); keyDel (key); key = keyNew ("system", KEY_END); succeed_if (keyGetNamespace (key) == KEY_NS_SYSTEM, "system namespace not KEY_NS_SYSTEM"); succeed_if (keyNameIsSystem (keyName (key)) == 1, "system name is not system"); succeed_if (keyIsSystem (key) == 1, "system key is not system"); succeed_if (keyNameIsUser (keyName (key)) == 0, "system name is not system"); succeed_if (keyIsUser (key) == 0, "system key is not system"); keyDel (key); key = keyNew ("system/key", KEY_END); succeed_if (keyGetNamespace (key) == KEY_NS_SYSTEM, "system namespace not KEY_NS_SYSTEM"); succeed_if (keyNameIsSystem (keyName (key)) == 1, "system name is not system"); succeed_if (keyIsSystem (key) == 1, "system key is not system"); succeed_if (keyNameIsUser (keyName (key)) == 0, "system name is not system"); succeed_if (keyIsUser (key) == 0, "system key is not system"); keyDel (key); key = keyNew ("spec/key", KEY_END); succeed_if (keyGetNamespace (key) == KEY_NS_SPEC, "Spec namespace not KEY_NS_SPEC"); succeed_if (keyNameIsSpec (keyName (key)) == 1, "Spec name is not Spec"); succeed_if (keyIsSpec (key) == 1, "Spec key is not Spec"); succeed_if (keyNameIsUser (keyName (key)) == 0, "Spec name is not Spec"); succeed_if (keyIsUser (key) == 0, "Spec key is not Spec"); keyDel (key); key = keyNew ("/key", KEY_CASCADING_NAME, KEY_END); succeed_if (keyGetNamespace (key) == KEY_NS_CASCADING, "not correct namespace"); keyDel (key); key = keyNew ("type", KEY_META_NAME, KEY_END); succeed_if (keyGetNamespace (key) == KEY_NS_META, "not correct namespace"); keyDel (key); }
/** Add sync bits everywhere keys were removed/added. * * - checks if the size of a previous kdbGet() is unchanged. * - checks if in correct state (kdbGet() needs to be executed before) * * Only elektraSplitDivide() together with this function can really decide * if sync is needed or not. * * @pre split needs to be processed with elektraSplitDivide() before. * * @retval 0 if kdbSet() is not needed * @retval 1 if kdbSet() is needed * @retval -1 on wrong keys (also has assert, should not happen) * @retval -2 wrong spec state: kdbGet() was not executed before * @retval -3 wrong dir state: kdbGet() was not executed before * @retval -4 wrong user state: kdbGet() was not executed before * @retval -5 wrong system state: kdbGet() was not executed before * @pre user/system was split before. * @param split the split object to work with * @ingroup split * **/ int elektraSplitSync (Split * split) { int needsSync = 0; for (size_t i = 0; i < split->size; ++i) { // first check for wrong states etc. switch (keyGetNamespace (split->parents[i])) { case KEY_NS_SPEC: // Check if we are in correct state if (split->handles[i]->specsize == -1) { return -(int)i - 2; } /* Check for spec keyset for removed keys */ if (split->handles[i]->specsize != ksGetSize (split->keysets[i])) { set_bit (split->syncbits[i], SPLIT_FLAG_SYNC); needsSync = 1; } break; case KEY_NS_DIR: // Check if we are in correct state if (split->handles[i]->dirsize == -1) { return -(int)i - 2; } /* Check for dir keyset for removed keys */ if (split->handles[i]->dirsize != ksGetSize (split->keysets[i])) { set_bit (split->syncbits[i], SPLIT_FLAG_SYNC); needsSync = 1; } break; case KEY_NS_USER: // Check if we are in correct state if (split->handles[i]->usersize == -1) { return -(int)i - 2; } /* Check for user keyset for removed keys */ if (split->handles[i]->usersize != ksGetSize (split->keysets[i])) { set_bit (split->syncbits[i], SPLIT_FLAG_SYNC); needsSync = 1; } break; case KEY_NS_SYSTEM: // Check if we are in correct state if (split->handles[i]->systemsize == -1) { return -(int)i - 2; } /* Check for system keyset for removed keys */ if (split->handles[i]->systemsize != ksGetSize (split->keysets[i])) { set_bit (split->syncbits[i], SPLIT_FLAG_SYNC); needsSync = 1; } break; case KEY_NS_PROC: case KEY_NS_EMPTY: case KEY_NS_META: case KEY_NS_CASCADING: case KEY_NS_NONE: ELEKTRA_ASSERT (0 && "Got keys that should not be here"); return -1; } } return needsSync; }