/** * @internal * * @brief Check if an update is needed at all * * @retval -1 an error occurred * @retval 0 no update needed * @retval number of plugins which need update */ static int elektraGetCheckUpdateNeeded (Split * split, Key * parentKey) { int updateNeededOccurred = 0; for (size_t i = 0; i < split->size; i++) { int ret = -1; Backend * backend = split->handles[i]; clear_bit (split->syncbits[i], 1); if (backend->getplugins[RESOLVER_PLUGIN] && backend->getplugins[RESOLVER_PLUGIN]->kdbGet) { ksRewind (split->keysets[i]); keySetName (parentKey, keyName (split->parents[i])); keySetString (parentKey, ""); ret = backend->getplugins[RESOLVER_PLUGIN]->kdbGet (backend->getplugins[RESOLVER_PLUGIN], split->keysets[i], parentKey); // store resolved filename keySetString (split->parents[i], keyString (parentKey)); // no keys in that backend backendUpdateSize (backend, split->parents[i], 0); } // TODO: set error in else case! switch (ret) { case 1: // Seems like we need to sync that set_bit (split->syncbits[i], SPLIT_FLAG_SYNC); ++updateNeededOccurred; break; case 0: // Nothing to do here break; default: ELEKTRA_ASSERT (0, "resolver did not return 1 0 -1, but %d", ret); case -1: // Ohh, an error occurred, lets stop the // process. return -1; } } return updateNeededOccurred; }
KeySet *getBooleanKeys(void) { KeySet *ks = ksNew(10, keyNew("user/tests/yajl", KEY_END), keyNew("user/tests/yajl/boolean_key", KEY_VALUE, "true", KEY_META, "type", "boolean", KEY_END), keyNew("user/tests/yajl/second_boolean_key", KEY_VALUE, "false", KEY_META, "type", "boolean", KEY_END), KS_END ); ksRewind(ks); return ks; }
static void flushConvertedKeys (Key * target, KeySet * converted, KeySet * orig) { if (ksGetSize (converted) == 0) return; ksRewind (converted); Key * current; while ((current = ksNext (converted))) { Key * 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); }
KeySet *getNullKeys(void) { Key *k1, *k2; KeySet *ks = ksNew(10, keyNew("user/tests/yajl", KEY_END), k1 = keyNew("user/tests/yajl/nullkey", KEY_VALUE, "will be removed", KEY_END), k2 = keyNew("user/tests/yajl/second_nullkey", KEY_VALUE, "will be removed too", KEY_END), KS_END ); keySetBinary(k1, NULL, 0); keySetBinary(k2, NULL, 0); ksRewind(ks); // shouldn't that be default? return ks; }
static void test_ifsetthenkey() { Key *parentKey = keyNew("user/tests/conditionals", KEY_VALUE, "", KEY_END); KeySet *ks = ksNew(5, keyNew("user/tests/conditionals/totest", KEY_VALUE, "", KEY_META, "check/condition", "(totest=='') ? (totest := bla/val1)", KEY_END), keyNew("user/tests/conditionals/bla/val1", KEY_VALUE, "100", KEY_END), keyNew("user/tests/conditionals/bla/val2", KEY_VALUE, "50", KEY_END), keyNew("user/tests/conditionals/bla/val3", KEY_VALUE, "3", KEY_END), KS_END); KeySet *conf = ksNew(0, KS_END); PLUGIN_OPEN("conditionals"); ksRewind(ks); succeed_if(plugin->kdbGet(plugin, ks, parentKey) == 1, "error"); Key *key = ksLookupByName(ks, "user/tests/conditionals/totest", 0); succeed_if(strcmp(keyString(key), "100") == 0, "error setting then value"); ksDel(ks); keyDel(parentKey); PLUGIN_CLOSE(); }
/** * Output all information of a keyset. * * The format is not very strict and only intend to be read * by human eyes for debugging purposes. Don't rely on the * format in your applications. * * Keys are printed line per line with keyOutput(). * * The same options as keyOutput() are taken and passed * to them. * * Additional KDB_O_HEADER will print the number of keys * as first line. * * @param ks the keyset to work with * @param stream the file pointer where to send the stream * @param options * @see keyOutput() * @retval 1 on success * @retval -1 on allocation errors * @ingroup stream */ int ksOutput(const KeySet *ks, FILE *stream, option_t options) { Key *key; KeySet *cks = ksDup (ks); size_t size = 0; ksRewind (cks); if (KDB_O_HEADER & options) { fprintf(stream,"Output keyset of size %d\n", (int)ksGetSize(cks)); } while ( (key = ksNext(cks)) != NULL) { if (options & KDB_O_SHOWINDICES) fprintf(stream, "[%d] ", (int)size); keyOutput (key,stream,options); size ++; } ksDel (cks); return 1; }
/** * Generate a C-Style keyset and stream it. * * This keyset can be used to include as c-code for * applikations using elektra. * * The options takes the same options as kdbGet() * and kdbSet(). * * @param ks the keyset to work with * @param stream the file pointer where to send the stream * @param options which keys not to output * @retval 1 on success * @ingroup stream */ int ksGenerate (const KeySet * ks, FILE * stream, option_t options) { Key * key; KeySet * cks = ksDup (ks); ksRewind (cks); fprintf (stream, "ksNew (%d,\n", (int) ksGetSize (cks)); while ((key = ksNext (cks)) != 0) { if (options & KDB_O_INACTIVE) if (key && keyIsInactive (key)) continue; keyGenerate (key, stream, options); fprintf (stream, ",\n"); } fprintf (stream, "\tKS_END);\n"); ksDel (cks); return 1; }
/**Filter a keyset. * * filter is executed for every key in the keyset result. When it returns 0, * the key will be dropped, when it returns 1 it will be ksAppendKey()ed to result, * when it returns -1 the processing will be stopped. You can use ksCurrent() * on input to see where the problem was. Because of this input is not const, * apart from ksCurrent() the input will not be changed. The keys that have * been in result before will stay untouched. * * @param result is the keyset where keys are added. * @param input is the keyset the filter works on. * @param filter is the function to execute on every key of the keyset to decide if * it should be ksAppendKey()ed to the result. * @return the number of keys added on success * @retval 0 when nothing was done * @retval -1 when filter returned an error (-1), ksCurrent() of input will * be the problematic key. * @see ksForEach() **/ int ksFilter (KeySet *result, KeySet *input, int (*filter) (Key *k)) { int rc = 0; int ret = 0; Key *current; cursor_t cursor = ksGetCursor (input); ksRewind (input); while ((current = ksNext (input)) != 0) { rc = filter (current); if (rc == -1) return -1; else if (rc != 0) { ++ ret; ksAppendKey(result, keyDup (current)); } } ksSetCursor(input, cursor); return ret; }
static void insertNewKeyIntoExistendOrder(Key *key, KeySet *ks) { if (keyGetMeta(ksLookup(ks, key, KDB_O_NONE), "order")) return; ksRewind(ks); Key *curKey; Key *prevKey = NULL; while ((curKey = ksNext(ks)) != NULL) { if (!strcmp(keyName(curKey), keyName(key))) { const char *oldOrder = "#1"; if (keyGetMeta(prevKey, "order")) { oldOrder = keyString(keyGetMeta(prevKey, "order")); } setSubOrderNumber(key, oldOrder); } prevKey = curKey; } }
/** * @brief Pop key at given cursor position * * @param ks the keyset to pop key from * @param c where to pop * * The internal cursor will be rewinded using ksRewind(). You can use * ksGetCursor() and ksSetCursor() jump back to the previous position. * e.g. to pop at current position within ksNext() loop: * @code * cursor_t c = ksGetCursor(ks); * keyDel (ksPopAtCursor(ks, c)); * ksSetCursor(ks, c); * ksPrev(ks); // to have correct key after next ksNext() * @endcode * * @warning do not use, will be superseded by external iterator API * * @return the popped key * @retval 0 if ks is 0 */ Key *ksPopAtCursor(KeySet *ks, cursor_t pos) { if (!ks) return 0; if (pos<0) return 0; if (pos>SSIZE_MAX) return 0; size_t c = pos; if (c>=ks->size) return 0; if (c != ks->size-1) { Key ** found = ks->array+c; Key * k = *found; /* Move the array over the place where key was found * * e.g. c = 2 * size = 6 * * 0 1 2 3 4 5 6 * |--|--|c |--|--|--|size * move to (c/pos is overwritten): * |--|--|--|--|--| * * */ memmove (found, found+1, (ks->size-c-1) * sizeof(Key *)); *(ks->array+ks->size-1) = k; // prepare last element to pop } else { // if c is on last position it is just a ksPop.. // so do nothing.. } ksRewind(ks); return ksPop(ks); }
/** * Builds an array of pointers to the keys in the supplied keyset. * The keys are not copied, calling keyDel may remove them from * the keyset. * * The size of the buffer can be easily allocated via ksGetSize. Example: * @code * KeySet *ks = somekeyset; * Key **keyArray = calloc (ksGetSize(ks), sizeof (Key *)); * elektraKsToMemArray (ks, keyArray); * ... work with the array ... * elektraFree (keyArray); * @endcode * * @param ks the keyset object to work with * @param buffer the buffer to put the result into * @return the number of elements in the array if successful * @return a negative number on null pointers or if an error occurred */ int elektraKsToMemArray (KeySet * ks, Key ** buffer) { if (!ks) return -1; if (!buffer) return -1; /* clear the received buffer */ memset (buffer, 0, ksGetSize (ks) * sizeof (Key *)); cursor_t cursor = ksGetCursor (ks); ksRewind (ks); size_t idx = 0; Key * key; while ((key = ksNext (ks)) != 0) { buffer[idx] = key; ++idx; } ksSetCursor (ks, cursor); return idx; }
/** * @internal * @brief Does the rollback * * @param split all information for iteration * @param parentKey to add warnings (also passed to plugins for the same reason) */ static void elektraSetRollback (Split * split, Key * parentKey) { for (size_t p = 0; p < NR_OF_PLUGINS; ++p) { for (size_t i = 0; i < split->size; i++) { int ret = 0; Backend * backend = split->handles[i]; ksRewind (split->keysets[i]); if (backend->errorplugins[p]) { keySetName (parentKey, keyName (split->parents[i])); ret = backend->errorplugins[p]->kdbError (backend->errorplugins[p], split->keysets[i], parentKey); } if (ret == -1) { ELEKTRA_ADD_WARNING (81, parentKey, keyName (backend->mountpoint)); } } } }
/** * @internal * @brief Does the commit * * @param split all information for iteration * @param parentKey to add warnings (also passed to plugins for the same reason) */ static void elektraSetCommit (Split * split, Key * parentKey) { for (size_t p = COMMIT_PLUGIN; p < NR_OF_PLUGINS; ++p) { for (size_t i = 0; i < split->size; i++) { int ret = 0; Backend * backend = split->handles[i]; if (backend->setplugins[p] && backend->setplugins[p]->kdbSet) { if (p != COMMIT_PLUGIN) { keySetString (parentKey, keyString (split->parents[i])); } keySetName (parentKey, keyName (split->parents[i])); #if DEBUG && VERBOSE printf ("elektraSetCommit: %p # %zu with %s - %s\n", backend, p, keyName (parentKey), keyString (parentKey)); #endif ksRewind (split->keysets[i]); ret = backend->setplugins[p]->kdbSet (backend->setplugins[p], split->keysets[i], parentKey); if (p == COMMIT_PLUGIN) { // name of non-temp file keySetString (split->parents[i], keyString (parentKey)); } } if (ret == -1) { ELEKTRA_ADD_WARNING (80, parentKey, keyName (backend->mountpoint)); } } } }
/** * @brief Read number of total GPG recipient keys from the plugin configuration. * @param config holds the plugin configuration * @param keyName holds the name of the root key to look up * @returns the number of GPG recipient keys. */ static size_t getRecipientCount (KeySet * config, const char * keyName) { Key * k; size_t recipientCount = 0; Key * root = ksLookupByName (config, keyName, 0); if (!root) return 0; // toplevel if (strlen (keyString (root)) > 0) { recipientCount++; } ksRewind (config); while ((k = ksNext (config)) != 0) { if (keyIsBelow (k, root) && strlen (keyString (k)) > 0) { recipientCount++; } } return recipientCount; }
/**Builds a backend out of the configuration supplied * from: * @verbatim system/elektra/mountpoints/<name> @endverbatim * * The root key must be like the above example. You do * not need to rewind the keyset. But every key must be * below the root key. * * The internal consistency will be checked in this * function. If necessary parts are missing, like * no plugins, they cant be loaded or similar 0 * will be returned. * * ksCut() is perfectly suitable for cutting out the * configuration like needed. * * @note The given KeySet will be deleted within the function, * don't use it afterwards. * * @param elektraConfig the configuration to work with. * It is used to build up this backend. * @param modules used to load new modules or get references * to existing one * @param errorKey the key where an error and warnings are added * * @return a pointer to a freshly allocated backend * this could be the requested backend or a so called * "missing backend". * @retval 0 if out of memory * @ingroup backend */ Backend* elektraBackendOpen(KeySet *elektraConfig, KeySet *modules, Key *errorKey) { Key * cur; KeySet *referencePlugins = 0; KeySet *systemConfig = 0; int failure = 0; referencePlugins = ksNew(0, KS_END); ksRewind(elektraConfig); Key * root = ksNext (elektraConfig); Backend *backend = elektraBackendAllocate(); if (elektraBackendSetMountpoint(backend, elektraConfig, errorKey) == -1) { // warning already set failure = 1; } while ((cur = ksNext(elektraConfig)) != 0) { if (keyRel (root, cur) == 1) { // direct below root key KeySet *cut = ksCut (elektraConfig, cur); if (!strcmp(keyBaseName(cur), "config")) { systemConfig = elektraRenameKeys(cut, "system"); ksDel (cut); } else if (!strcmp(keyBaseName(cur), "errorplugins")) { if (elektraProcessPlugins(backend->errorplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for error failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "getplugins")) { if (elektraProcessPlugins(backend->getplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(13, errorKey, "elektraProcessPlugins for get failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "mountpoint")) { ksDel (cut); // already handled by elektraBackendSetMountpoint continue; } else if (!strcmp(keyBaseName(cur), "setplugins")) { if (elektraProcessPlugins(backend->setplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for set failed"); failure = 1; } } else { // no one cares about that config if (!failure) ELEKTRA_ADD_WARNING(16, errorKey, keyBaseName(cur)); ksDel (cut); } } } if (failure) { Backend *tmpBackend = elektraBackendOpenMissing(backend->mountpoint); elektraBackendClose(backend, errorKey); backend = tmpBackend; } ksDel (systemConfig); ksDel (elektraConfig); ksDel (referencePlugins); return backend; }
int elektraIconvGet (Plugin * handle, KeySet * returned, Key * parentKey) { Key * cur; ksRewind (returned); if (!strcmp (keyName (parentKey), "system/elektra/modules/iconv")) { KeySet * pluginConfig = ksNew (30, keyNew ("system/elektra/modules/iconv", KEY_VALUE, "iconv plugin waits for your orders", KEY_END), keyNew ("system/elektra/modules/iconv/exports", KEY_END), keyNew ("system/elektra/modules/iconv/exports/get", KEY_FUNC, elektraIconvGet, KEY_END), keyNew ("system/elektra/modules/iconv/exports/set", KEY_FUNC, elektraIconvSet, KEY_END), #include "readme_iconv.c" keyNew ("system/elektra/modules/iconv/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END); ksAppend (returned, pluginConfig); ksDel (pluginConfig); return 1; } if (!kdbbNeedsUTF8Conversion (handle)) return 0; while ((cur = ksNext (returned)) != 0) { if (keyIsString (cur)) { /* String or similar type of value */ size_t convertedDataSize = keyGetValueSize (cur); char * convertedData = elektraMalloc (convertedDataSize); memcpy (convertedData, keyString (cur), keyGetValueSize (cur)); if (kdbbUTF8Engine (handle, UTF8_FROM, &convertedData, &convertedDataSize)) { ELEKTRA_SET_ERRORF (46, parentKey, "Could not convert string %s, got result %s, encoding settings are from %s to %s", keyString (cur), convertedData, getFrom (handle), getTo (handle)); elektraFree (convertedData); return -1; } keySetString (cur, convertedData); elektraFree (convertedData); } const Key * meta = keyGetMeta (cur, "comment"); if (meta) { /* String or similar type of value */ size_t convertedDataSize = keyGetValueSize (meta); char * convertedData = elektraMalloc (convertedDataSize); memcpy (convertedData, keyString (meta), keyGetValueSize (meta)); if (kdbbUTF8Engine (handle, UTF8_FROM, &convertedData, &convertedDataSize)) { ELEKTRA_SET_ERRORF (46, parentKey, "Could not convert string %s, got result %s, encoding settings are from %s to %s", keyString (meta), convertedData, getFrom (handle), getTo (handle)); elektraFree (convertedData); return -1; } keySetMeta (cur, "comment", convertedData); elektraFree (convertedData); } } return 1; /* success */ }
/** * @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; }
void clear_sync (KeySet *ks) { Key *k; ksRewind(ks); while ((k = ksNext(ks)) != 0) keyClearSync(k); }
/** * @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; }
int elektraSortTopology (KeySet * ks, Key ** array) { if (ks == NULL || array == NULL) return -1; KeySet * done = ksNew (0, KS_END); ksRewind (ks); Key * cur; ssize_t size = ksGetSize (ks); Key * orderCounter = keyNew ("/#", KEY_CASCADING_NAME, KEY_END); elektraArrayIncName (orderCounter); _adjMatrix adjMatrix[size]; int i = 0; int retVal = 1; int depCount = 0; Key ** localArray = elektraMalloc (size * sizeof (Key *)); elektraKsToMemArray (ks, localArray); qsort (localArray, size, sizeof (Key *), topCmpOrder); for (long j = 0; j < size; ++j) { adjMatrix[j].key = localArray[j]; adjMatrix[j].isResolved = 0; adjMatrix[j].deps = elektraCalloc (sizeof (unsigned long) * size); } kdb_octet_t hasOrder = 0; if (keyGetMeta (localArray[0], "order")) hasOrder = 1; unsigned int unresolved = 0; for (int j = 0; j < size; ++j) { cur = localArray[j]; KeySet * deps = elektraMetaArrayToKS (cur, "dep"); keyDel (ksLookupByName (deps, "dep", KDB_O_POP)); Key * tmpDep; switch (ksGetSize (deps)) { case -1: { // key has no dependencies, give it an order number and add it to list of resolved dependencies keySetMeta (cur, "order", keyBaseName (orderCounter)); elektraArrayIncName (orderCounter); ksAppendKey (done, keyDup (cur)); adjMatrix[j].isResolved = 1; ksDel (deps); break; } case 1: { // only 1 dependency: // test if it's reflexive tmpDep = ksHead (deps); if (!strcmp (keyName (cur), keyString (tmpDep))) { keySetMeta (cur, "order", keyBaseName (orderCounter)); elektraArrayIncName (orderCounter); ksAppendKey (done, keyDup (cur)); adjMatrix[j].isResolved = 1; ksDel (deps); break; } // if not, fallthrough to normal dependency handling } default: { int gotUnresolved = 0; while ((tmpDep = ksNext (deps)) != NULL) { if (!isValidKeyName (keyString (tmpDep))) { // invalid keyname -> ERROR retVal = -1; break; } i = getArrayIndex (tmpDep, adjMatrix, size); if (i == -1) { // key doesn't exist yet but has valid name, ignore it. continue; } else if (i == j) { // reflexiv depencency, do nothing } else { if (!adjMatrix[i].isResolved) { // unresolved dependency adjMatrix[j].deps[i] = 1; ++gotUnresolved; // simple cycle detection if (adjMatrix[i].deps[j]) { retVal = 0; break; } } } } if (gotUnresolved) { adjMatrix[j].isResolved = 0; ++unresolved; // cound unresolved dependencies depCount += gotUnresolved; } ksDel (deps); break; } } if (retVal <= 0) break; } if (retVal <= 0) { // error or cycle: goto cleanup goto TopSortCleanup; } // resolve all dependencies that can be resolved immediately for (int j = 0; j < size; ++j) { if (adjMatrix[j].isResolved) depCount -= resolveDep (j, adjMatrix, size); } ssize_t resolved = ksGetSize (done); if (((depCount + resolved) >= size) && (unresolved)) { // more dependencies dependencies than keys: // cycle found ! retVal = 0; goto TopSortCleanup; } if (unresolved) { int found = 1; // we have unresolved dependencies for (int j = 0; j < size + 1; ++j) { // loop until no dependency can be resolved anymore if (j == size) { if (found) { found = 0; j = -1; unresolved = 0; continue; } else break; } if (adjMatrix[j].isResolved) continue; ++unresolved; if (hasOrder) { // resolve by order int ret = resolveDeps (j, adjMatrix, size, done, orderCounter); if (ret == -1) break; j = -1; found = 1; continue; } else { // resolve next possible dependency in keyset if (!hasUnresolvedDependencies (j, adjMatrix, size)) { adjMatrix[j].isResolved = 1; resolveDep (j, adjMatrix, size); keySetMeta (localArray[j], "order", keyBaseName (orderCounter)); elektraArrayIncName (orderCounter); ksAppendKey (done, keyDup (localArray[j])); found = 1; } } } } if (unresolved == 0) { // everything resolved // add dependencies in topological order to array elektraKsToMemArray (ks, array); qsort (array, size, sizeof (Key *), topCmpOrder); retVal = 1; } else { // still unresolved dependencies left: // there must be a cycle somewhere retVal = 0; } TopSortCleanup: ksDel (done); keyDel (orderCounter); elektraFree (localArray); for (ssize_t j = 0; j < size; ++j) { elektraFree (adjMatrix[j].deps); } return retVal; }
/** * Load a plugin. * * The array of plugins must be set to 0. * Its length is NR_OF_PLUGINS. * * systemConfig will only be used, not deleted. * * @param config the config with the information how the * plugins should be put together * @param systemConfig the shared (system) config for the plugins. * Every plugin additional get this config. * * @retval -1 on failure */ int elektraProcessPlugins (Plugin ** plugins, KeySet * modules, KeySet * referencePlugins, KeySet * config, KeySet * systemConfig, Key * errorKey) { Key * root; Key * cur; ksRewind (config); root = ksNext (config); while ((cur = ksNext (config)) != 0) { if (keyRel (root, cur) == 1) { char * pluginName = 0; char * referenceName = 0; int pluginNumber = 0; Key * key; if (elektraProcessPlugin (cur, &pluginNumber, &pluginName, &referenceName, errorKey) == -1) { elektraFree (pluginName); elektraFree (referenceName); ksDel (config); return -1; } if (pluginName) { key = keyDup (cur); keyAddBaseName (key, "config"); KeySet * cutConfig = ksCut (config, key); keyDel (key); KeySet * pluginConfig = elektraRenameKeys (cutConfig, "user"); ksDel (cutConfig); if (!pluginConfig) return -1; ksAppend (pluginConfig, systemConfig); ksRewind (pluginConfig); /* TODO: bug ksAppend invalidates cursor */ /* case 1, we create a new plugin, note that errorKey is not passed here, because it would set error information but we only want a warning instead. */ plugins[pluginNumber] = elektraPluginOpen (pluginName, modules, pluginConfig, errorKey); if (!plugins[pluginNumber]) { ELEKTRA_ADD_WARNING (64, errorKey, pluginName); /* Loading plugin did not work */ elektraFree (pluginName); elektraFree (referenceName); ksDel (config); return -1; } /* case 2, we label it for later use */ if (referenceName) ksAppendKey (referencePlugins, keyNew (referenceName, KEY_BINARY, KEY_SIZE, sizeof (plugins[pluginNumber]), KEY_VALUE, &plugins[pluginNumber], KEY_END)); } else { /* case 3, we use an existing plugin */ Key * lookup = ksLookup (referencePlugins, keyNew (referenceName, KEY_END), KDB_O_DEL); if (!lookup) { ELEKTRA_ADD_WARNING (65, errorKey, referenceName); /* Getting a reference plugin at a previous stage did not work. Note that this check is necessary, because loading the plugin could fail for example at errorplugins and at a later point, for example at setplugins it is tried to refer to that.*/ elektraFree (referenceName); ksDel (config); return -1; } plugins[pluginNumber] = *(Plugin **)keyValue (lookup); ++plugins[pluginNumber]->refcounter; } elektraFree (pluginName); elektraFree (referenceName); } else { ELEKTRA_ADD_WARNING (21, errorKey, keyString (cur)); } } ksDel (config); return 0; }
/** * @internal * * Calculates the common parent to all keys in @p ks. * * This is a c-helper function, you need not implement it in bindings. * * Given the @p ks KeySet, calculates the parent name for all the keys. * So if @p ks contains this keys: * * @code * system/sw/xorg/Monitors/Monitor1/vrefresh * system/sw/xorg/Monitors/Monitor1/hrefresh * system/sw/xorg/Devices/Device1/driver * system/sw/xorg/Devices/Device1/mode * @endcode * * The common parent is @p system/sw/xorg . * * On the other hand, if we have this KeySet: * * @code * system/some/thing * system/other/thing * user/unique/thing * @endcode * * No common parent is possible, so @p returnedCommonParent will contain nothing. * * @param working the Keyset to work with * @param returnedCommonParent a pre-allocated buffer that will receive the * common parent, if found * @param maxSize size of the pre-allocated @p returnedCommonParent buffer * @return size in bytes of the parent name, or 0 if there is no common parent, * or -1 to indicate an error, then @p errno must be checked. */ ssize_t ksGetCommonParentName (const KeySet * working, char * returnedCommonParent, size_t maxSize) { size_t parentSize = 0; Key * current = 0; cursor_t cinit; KeySet * ks; ssize_t sMaxSize; if (maxSize > SSIZE_MAX) return -1; sMaxSize = maxSize; cinit = ksGetCursor (working); ks = (KeySet *)working; if (ksGetSize (ks) < 1) return 0; ksRewind (ks); current = ksNext (ks); if (keyGetNameSize (current) > sMaxSize) { /*errno=KDB_ERR_TRUNC;*/ returnedCommonParent[0] = 0; return -1; } strcpy (returnedCommonParent, keyName (current)); parentSize = elektraStrLen (returnedCommonParent); while (*returnedCommonParent) { ksRewind (ks); while ((current = ksNext (ks)) != 0) { /* Test if a key doesn't match */ if (memcmp (returnedCommonParent, keyName (current), parentSize - 1)) break; } if (current) { /* some key failed to be a child */ /* parent will be the parent of current parent... */ char * delim = 0; // TODO: does not honor escaped characters if ((delim = strrchr (returnedCommonParent, KDB_PATH_SEPARATOR))) { *delim = 0; parentSize = elektraStrLen (returnedCommonParent); } else { *returnedCommonParent = 0; parentSize = 0; break; /* Better don't make comparison with parentSize-1 now */ } } else { /* All keys matched (current==0) */ /* We have our common parent to return in commonParent */ ksSetCursor (ks, cinit); return parentSize; } } ksSetCursor (ks, cinit); return parentSize; /* if reached, will be zero */ }
void test_order(char *fileName) { Key * parentKey = keyNew ("user/tests/augeas-hosts", KEY_VALUE, srcdir_file (fileName), KEY_END); KeySet *conf = ksNew (20, keyNew ("system/lens", KEY_VALUE, "Hosts.lns", KEY_END), KS_END); PLUGIN_OPEN("augeas"); KeySet *ks = ksNew(0, KS_END); succeed_if(plugin->kdbGet (plugin, ks, parentKey) >= 1, "call to kdbGet was not successful"); succeed_if(output_error (parentKey), "error in kdbGet"); succeed_if(output_warnings (parentKey), "warnings in kdbGet"); Key *key; size_t currentIndex = 0; size_t numKeys = ksGetSize (ks); long *usedOrders = malloc (numKeys * sizeof(long)); exit_if_fail(usedOrders, "unable to allocate memory for order array"); /* as 0 is a legit order we have to initialize the array manually */ for (size_t index = 0; index < numKeys; index++) { usedOrders[index] = -1; } ksRewind (ks); while ((key = ksNext (ks)) != 0) { if (strcmp (keyName (key), keyName (parentKey))) { char errorMessage[150]; const Key *orderKey = keyGetMeta (key, "order"); snprintf (errorMessage, 150, "key %s has no order", keyName (key)); succeed_if(orderKey, errorMessage); char *orderString = (char *) keyValue (orderKey); long order; char *end; order = strtol (orderString, &end, 10); snprintf (errorMessage, 150, "key %s has an unparseable order", keyName (key)); succeed_if(*end == 0, errorMessage); snprintf (errorMessage, 150, "key %s has a negative order", keyName (key)); succeed_if(order >= 0, errorMessage); snprintf (errorMessage, 150, "the order %ld exists more than once. Duplicate found in %s.", order, keyName (key)); // TODO: this is in O(n^2) where n is the number of keys for (size_t i = 0; i < currentIndex; i++) { succeed_if(usedOrders[i] != order, errorMessage); } usedOrders[currentIndex] = order; ++currentIndex; } } free (usedOrders); ksDel (ks); keyDel(parentKey); PLUGIN_CLOSE() ; }
static int elektraGetDoUpdateWithGlobalHooks (KDB * handle, Split * split, KeySet * ks, Key * parentKey, Key * initialParent, UpdatePass run) { const int bypassedSplits = 1; int pgs_done = 0; int pgc_done = 0; elektraGlobalGet (handle, ks, parentKey, GETSTORAGE, INIT); elektraGlobalGet (handle, ks, parentKey, GETSTORAGE, MAXONCE); // elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT); for (size_t i = 0; i < split->size - bypassedSplits; i++) { if (!test_bit (split->syncbits[i], SPLIT_FLAG_SYNC)) { // skip it, update is not needed continue; } Backend * backend = split->handles[i]; ksRewind (split->keysets[i]); keySetName (parentKey, keyName (split->parents[i])); keySetString (parentKey, keyString (split->parents[i])); int start, end; if (run == FIRST) { start = 1; end = STORAGE_PLUGIN + 1; } else { start = STORAGE_PLUGIN + 1; end = NR_OF_PLUGINS; } for (int p = start; p < end; ++p) { int ret = 0; if (!pgs_done && (p == (STORAGE_PLUGIN + 1)) && handle->globalPlugins[POSTGETSTORAGE][FOREACH]) { pgs_done = 1; keySetName (parentKey, keyName (initialParent)); ksRewind (ks); handle->globalPlugins[POSTGETSTORAGE][FOREACH]->kdbGet (handle->globalPlugins[POSTGETSTORAGE][FOREACH], ks, parentKey); keySetName (parentKey, keyName (split->parents[i])); } else if (!pgc_done && (p == (NR_OF_PLUGINS - 1)) && handle->globalPlugins[POSTGETCLEANUP][FOREACH]) { pgc_done = 1; keySetName (parentKey, keyName (initialParent)); ksRewind (ks); handle->globalPlugins[POSTGETCLEANUP][FOREACH]->kdbGet (handle->globalPlugins[POSTGETCLEANUP][FOREACH], ks, parentKey); keySetName (parentKey, keyName (split->parents[i])); } if (backend->getplugins[p] && backend->getplugins[p]->kdbGet) { if (p <= STORAGE_PLUGIN) { ret = backend->getplugins[p]->kdbGet (backend->getplugins[p], split->keysets[i], parentKey); } else { KeySet * cutKS = prepareGlobalKS (ks, parentKey); ret = backend->getplugins[p]->kdbGet (backend->getplugins[p], cutKS, parentKey); ksAppend (ks, cutKS); ksDel (cutKS); } } if (ret == -1) { // Ohh, an error occurred, // lets stop the process. elektraGlobalError (handle, ks, parentKey, GETSTORAGE, DEINIT); // elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, DEINIT); return -1; } } } elektraGlobalGet (handle, ks, parentKey, GETSTORAGE, DEINIT); // elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT); return 0; }
/** * @brief encrypt or sign the file specified at parentKey * @param pluginConfig holds the plugin configuration * @param parentKey holds the path to the file to be encrypted. Will hold an error description in case of failure. * @retval 1 on success * @retval -1 on error, errorKey holds an error description */ static int fcryptEncrypt (KeySet * pluginConfig, Key * parentKey) { Key * k; const size_t recipientCount = getRecipientCount (pluginConfig, ELEKTRA_RECIPIENT_KEY); const size_t signatureCount = getRecipientCount (pluginConfig, ELEKTRA_SIGNATURE_KEY); if (recipientCount == 0 && signatureCount == 0) { ELEKTRA_SET_ERRORF ( ELEKTRA_ERROR_NO_GPG_RECIPIENTS, parentKey, "Missing GPG recipient key (specified as %s) or GPG signature key (specified as %s) in plugin configuration.", ELEKTRA_RECIPIENT_KEY, ELEKTRA_SIGNATURE_KEY); return -1; } int tmpFileFd = -1; char * tmpFile = getTemporaryFileName (pluginConfig, keyString (parentKey), &tmpFileFd); if (!tmpFile) { ELEKTRA_SET_ERROR (87, parentKey, "Memory allocation failed"); return -1; } const size_t testMode = inTestMode (pluginConfig); const size_t textMode = inTextMode (pluginConfig); // prepare argument vector for gpg call // 7 static arguments (magic number below) are: // 1. path to the binary // 2. --batch // 3. -o // 4. path to tmp file // 5. yes // 6. file to be encrypted // 7. NULL terminator int argc = 7 + (2 * recipientCount) + (2 * signatureCount) + (2 * testMode) + textMode + (recipientCount > 0 ? 1 : 0) + (signatureCount > 0 ? 1 : 0); kdb_unsigned_short_t i = 0; char * argv[argc]; argv[i++] = NULL; argv[i++] = "--batch"; argv[i++] = "-o"; argv[i++] = tmpFile; argv[i++] = "--yes"; // overwrite files if they exist // add recipients Key * gpgRecipientRoot = ksLookupByName (pluginConfig, ELEKTRA_RECIPIENT_KEY, 0); // append root (gpg/key) as gpg recipient if (gpgRecipientRoot && strlen (keyString (gpgRecipientRoot)) > 0) { argv[i++] = "-r"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) keyString (gpgRecipientRoot); } // append keys beneath root (crypto/key/#_) as gpg recipients if (gpgRecipientRoot) { ksRewind (pluginConfig); while ((k = ksNext (pluginConfig)) != 0) { const char * kStringVal = keyString (k); if (keyIsBelow (k, gpgRecipientRoot) && strlen (kStringVal) > 0) { argv[i++] = "-r"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) kStringVal; } } } // add signature keys Key * gpgSignatureRoot = ksLookupByName (pluginConfig, ELEKTRA_SIGNATURE_KEY, 0); // append root signature key if (gpgSignatureRoot && strlen (keyString (gpgSignatureRoot)) > 0) { argv[i++] = "-u"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) keyString (gpgSignatureRoot); } // append keys beneath root (fcrypt/sign/#_) as gpg signature keys if (gpgSignatureRoot) { ksRewind (pluginConfig); while ((k = ksNext (pluginConfig)) != 0) { const char * kStringVal = keyString (k); if (keyIsBelow (k, gpgSignatureRoot) && strlen (kStringVal) > 0) { argv[i++] = "-u"; // NOTE argv[] values will not be modified, so const can be discarded safely argv[i++] = (char *) kStringVal; } } } // if we are in test mode we add the trust model if (testMode > 0) { argv[i++] = "--trust-model"; argv[i++] = "always"; } // ASCII armor in text mode if (textMode) { argv[i++] = "--armor"; } // prepare rest of the argument vector if (recipientCount > 0) { // encrypt the file argv[i++] = "-e"; } if (signatureCount > 0) { if (textMode && recipientCount == 0) { // clear-sign the file argv[i++] = "--clearsign"; } else { // sign the file argv[i++] = "-s"; } } argv[i++] = (char *) keyString (parentKey); argv[i++] = NULL; // NOTE the encryption process works like this: // gpg2 --batch --yes -o encryptedFile -r keyID -e configFile // mv encryptedFile configFile return fcryptGpgCallAndCleanup (parentKey, pluginConfig, argv, argc, tmpFileFd, tmpFile); }
void test_key () { KeySet * ks; Key * cur; int counter; printf ("Testing Key from xml\n"); ks = ksNew (0, KS_END); exit_if_fail (ksFromXMLfile (ks, srcdir_file ("xmltool/key.xml")) == 0, "ksFromXMLfile(key.xml) failed."); counter = 0; ksRewind (ks); while ((cur = ksNext (ks))) { counter++; /* Prepend key root */ // snprintf(buf, sizeof(buf), "%s/%s", root, keyName(cur)); // keySetName(cur, buf); /* Make tests ... */ // printf ("counter: %d - name: %s - value: %s\n", counter, (char*) keyName(cur), (char*)keyValue(cur)); switch (counter) { case 1: succeed_if (strcmp (keyName (cur), "user/tests/filesys/.HiddenBinaryKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "BinaryValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Binary key with hidden name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 1, "key is inactive"); succeed_if (keyIsBinary (cur) == 1, "key is not binary"); succeed_if (keyGetMode (cur) == 0440, "could not get mode"); break; case 2: succeed_if (strcmp (keyName (cur), "user/tests/filesys/.HiddenDirectoryKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "DirectoryValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Directory key with hidden name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 1, "key is inactive"); succeed_if (keyIsString (cur) == 1, "key is not string"); break; case 3: succeed_if (strcmp (keyName (cur), "user/tests/filesys/.HiddenStringKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "StringValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "String key with hidden name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 1, "key is inactive"); succeed_if (keyIsString (cur) == 1, "key is not string"); succeed_if (keyGetUID (cur) == 0, "could not get uid value"); succeed_if (keyGetGID (cur) == 20, "could not get gid value"); break; case 4: succeed_if (strcmp (keyName (cur), "user/tests/filesys/PerfectBinaryKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "BinaryValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Binary key with standard name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsBinary (cur) == 1, "key is not binary"); succeed_if (keyGetGID (cur) == 40, "could not get gid value"); break; case 5: succeed_if (strcmp (keyName (cur), "user/tests/filesys/PerfectDirectoryKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "DirectoryValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Directory key with standard name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsString (cur) == 1, "key is not string"); break; case 6: succeed_if (strcmp (keyName (cur), "user/tests/filesys/PerfectStringKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "StringValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "String key with\nstandard name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsString (cur) == 1, "key is not string"); succeed_if (keyGetUID (cur) == 20, "could not get uid value"); break; case 7: succeed_if (strcmp (keyName (cur), "user/tests/filesys/Ug.ly:Bin@a€ry Key") == 0, "wrong name"); succeed_if (keyValue (cur) == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Binary key with ugly name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsBinary (cur) == 1, "key is not binary"); succeed_if (keyGetMode (cur) == 230, "could not get mode"); // <key type="binary" basename="Ug.ly:Bin@a€ry Key"><comment>Binary key with ugly name</comment></key> break; case 8: succeed_if (strcmp (keyName (cur), "user/tests/filesys/Ug.ly:Dir@ect€ory Key") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Directory with ugly name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsString (cur) == 1, "key is not string"); // <key type="directory" basename="Ug.ly:Dir@ect€ory Key"><comment>Directory with ugly name</comment></key> break; case 9: succeed_if (strcmp (keyName (cur), "user/tests/filesys/Ug.ly:St@ri€n.g Key") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "With a string value") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "string key with ugly name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsString (cur) == 1, "key is not string"); // <key type="string" basename="Ug.ly:St@ri€n.g Key" value="With a string value"><comment>string key with ugly // name</comment></key> break; } } ksDel (ks); }
void test_keyset () { KeySet * ks; Key * cur; int counter; printf ("Testing KeySet from xml\n"); ks = ksNew (0, KS_END); exit_if_fail (ksFromXMLfile (ks, srcdir_file ("xmltool/keyset.xml")) == 0, "ksFromXMLfile(key.xml) failed."); counter = 0; ksRewind (ks); while ((cur = ksNext (ks))) { counter++; /* Make tests ... */ // printf ("counter: %d - %s\n", counter, keyName(cur)); switch (counter) { // <key type="43" basename="0-27042916" value="0 216905227"><comment>2551516588474823843</comment></key> case 1: succeed_if (strcmp (keyName (cur), "user/tests/filesys/0-27042916") == 0, "name of first key not correct"); succeed_if (strcmp (keyValue (cur), "0 216905227") == 0, "value of first key not correct"); succeed_if (strcmp (keyComment (cur), "2551516588474823843") == 0, "comment of first key not correct"); break; // <key type="253" basename="1-2449524622" value="1 1679328197"><comment>3246436893195629244</comment></key> case 2: succeed_if (strcmp (keyName (cur), "user/tests/filesys/1-2449524622") == 0, "name of 2. key not correct"); succeed_if (strcmp (keyValue (cur), "1 1679328197") == 0, "value of 2. key not correct"); succeed_if (strcmp (keyComment (cur), "3246436893195629244") == 0, "comment of 2. key not correct"); break; // <key type="string" basename="dir-1-0"> case 3: succeed_if (strcmp (keyName (cur), "user/tests/filesys/dir-1-0") == 0, "name of 3. key not correct"); break; // <key type="114" basename="0-294164813" value="0 216245011"><comment>18454108762891828026</comment></key> case 4: succeed_if (strcmp (keyName (cur), "user/tests/filesys/dir-1-0/0-294164813") == 0, "name of 4. key not correct"); succeed_if (strcmp (keyValue (cur), "0 216245011") == 0, "value of 4. key not correct"); succeed_if (strcmp (keyComment (cur), "18454108762891828026") == 0, "comment of 4. key not correct"); break; // <key type="135" basename="1-1479930365" value="1 2732423037"><comment>24597295372375238</comment></key> case 5: succeed_if (strcmp (keyName (cur), "user/tests/filesys/dir-1-0/1-1479930365") == 0, "name of 4. key not correct"); succeed_if (strcmp (keyValue (cur), "1 2732423037") == 0, "value of 4. key not correct"); succeed_if (strcmp (keyComment (cur), "24597295372375238") == 0, "comment of 4. key not correct"); break; // <key type="string" basename="dir-2-0"> case 6: succeed_if (strcmp (keyName (cur), "user/tests/filesys/dir-1-0/dir-2-0") == 0, "name of 3. key not correct"); break; // <key type="144" basename="0-215571059" value="0 264857705"><comment>2188631490667217086</comment></key> case 7: succeed_if (strcmp (keyName (cur), "user/tests/filesys/dir-1-0/dir-2-0/0-215571059") == 0, "name of 4. key not correct"); succeed_if (strcmp (keyValue (cur), "0 264857705") == 0, "value of 4. key not correct"); succeed_if (strcmp (keyComment (cur), "2188631490667217086") == 0, "comment of 4. key not correct"); break; } } ksDel (ks); }
/** * @brief Opens the session with the Key database. * * @pre errorKey must be a valid key, e.g. created with keyNew() * * The method will bootstrap itself the following way. * The first step is to open the default backend. With it * system/elektra/mountpoints will be loaded and all needed * libraries and mountpoints will be determined. * These libraries for backends will be loaded and with it the * @p KDB data structure will be initialized. * * You must always call this method before retrieving or committing any * keys to the database. In the end of the program, * after using the key database, you must not forget to kdbClose(). * * The pointer to the @p KDB structure returned will be initialized * like described above, and it must be passed along on any kdb*() * method your application calls. * * Get a @p KDB handle for every thread using elektra. Don't share the * handle across threads, and also not the pointer accessing it: * * @snippet kdbopen.c open * * You don't need kdbOpen() if you only want to * manipulate plain in-memory Key or KeySet objects. * * @pre errorKey must be a valid key, e.g. created with keyNew() * * @param errorKey the key which holds errors and warnings which were issued * @see kdbGet(), kdbClose() to end all affairs to the key database. * @retval handle on success * @retval NULL on failure * @ingroup kdb */ KDB * kdbOpen (Key * errorKey) { if (!errorKey) { ELEKTRA_LOG ("no error key passed"); return 0; } ELEKTRA_LOG ("called with %s", keyName (errorKey)); int errnosave = errno; KDB * handle = elektraCalloc (sizeof (struct _KDB)); Key * initialParent = keyDup (errorKey); handle->modules = ksNew (0, KS_END); if (elektraModulesInit (handle->modules, errorKey) == -1) { ksDel (handle->modules); elektraFree (handle); ELEKTRA_SET_ERROR (94, errorKey, "elektraModulesInit returned with -1"); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; } KeySet * keys = ksNew (0, KS_END); int inFallback = 0; switch (elektraOpenBootstrap (handle, keys, errorKey)) { case -1: ksDel (handle->modules); elektraFree (handle); ELEKTRA_SET_ERROR (40, errorKey, "could not open default backend"); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; case 0: ELEKTRA_ADD_WARNING (17, errorKey, "Initial kdbGet() failed, you should either fix " KDB_DB_INIT " or the fallback " KDB_DB_FILE); break; case 2: ELEKTRA_LOG ("entered fallback code for bootstrapping"); inFallback = 1; break; } keySetString (errorKey, "kdbOpen(): mountGlobals"); if (mountGlobals (handle, ksDup (keys), handle->modules, errorKey) == -1) { // mountGlobals also sets a warning containing the name of the plugin that failed to load ELEKTRA_ADD_WARNING (139, errorKey, "Mounting global plugins failed"); } keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, "kdbOpen(): backendClose"); backendClose (handle->defaultBackend, errorKey); splitDel (handle->split); handle->defaultBackend = 0; handle->trie = 0; #ifdef HAVE_LOGGER if (inFallback) ELEKTRA_LOG_WARNING ("fallback for bootstrapping: you might want to run `kdb upgrade-bootstrap`"); Key * key; ksRewind (keys); for (key = ksNext (keys); key; key = ksNext (keys)) { ELEKTRA_LOG_DEBUG ("config for createTrie name: %s value: %s", keyName (key), keyString (key)); } #endif handle->split = splitNew (); keySetString (errorKey, "kdbOpen(): mountOpen"); // Open the trie, keys will be deleted within mountOpen if (mountOpen (handle, keys, handle->modules, errorKey) == -1) { ELEKTRA_ADD_WARNING (93, errorKey, "Initial loading of trie did not work"); } keySetString (errorKey, "kdbOpen(): mountDefault"); if (mountDefault (handle, handle->modules, inFallback, errorKey) == -1) { ELEKTRA_SET_ERROR (40, errorKey, "could not reopen and mount default backend"); keySetString (errorKey, "kdbOpen(): close"); kdbClose (handle, errorKey); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; } keySetString (errorKey, "kdbOpen(): mountVersion"); mountVersion (handle, errorKey); keySetString (errorKey, "kdbOpen(): mountModules"); if (mountModules (handle, handle->modules, errorKey) == -1) { ELEKTRA_ADD_WARNING (92, errorKey, "Mounting modules did not work"); } keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return handle; }
/**Builds a backend out of the configuration supplied * from: * @verbatim system/elektra/mountpoints/<name> @endverbatim * * The root key must be like the above example. You do * not need to rewind the keyset. But every key must be * below the root key. * * The internal consistency will be checked in this * function. If necessary parts are missing, like * no plugins, they cant be loaded or similar 0 * will be returned. * * ksCut() is perfectly suitable for cutting out the * configuration like needed. * * @note The given KeySet will be deleted within the function, * don't use it afterwards. * * @param elektraConfig the configuration to work with. * It is used to build up this backend. * @param modules used to load new modules or get references * to existing one * @return a pointer to a freshly allocated backend * this could be the requested backend or a so called * "missing backend". * @retval 0 if out of memory * @ingroup backend */ Backend* elektraBackendOpen(KeySet *elektraConfig, KeySet *modules, Key *errorKey) { Key * cur; Key * root; KeySet *referencePlugins = 0; KeySet *systemConfig = 0; int failure = 0; referencePlugins = ksNew(0, KS_END); ksRewind(elektraConfig); root = ksNext (elektraConfig); Backend *backend = elektraBackendAllocate(); while ((cur = ksNext(elektraConfig)) != 0) { if (keyRel (root, cur) == 1) { // direct below root key KeySet *cut = ksCut (elektraConfig, cur); if (!strcmp(keyBaseName(cur), "config")) { systemConfig = elektraRenameKeys(cut, "system"); ksDel (cut); } else if (!strcmp(keyBaseName(cur), "getplugins")) { if (elektraProcessPlugins(backend->getplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(13, errorKey, "elektraProcessPlugins for get failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "mountpoint")) { backend->mountpoint = keyNew("", KEY_VALUE, keyBaseName(root), KEY_END); elektraKeySetName(backend->mountpoint, keyString(cur), KEY_CASCADING_NAME | KEY_EMPTY_NAME); if (!backend->mountpoint) { if (!failure) ELEKTRA_ADD_WARNINGF(14, errorKey, "Could not create mountpoint with name %s and value %s", keyString(cur), keyBaseName(root)); failure = 1; } keyIncRef(backend->mountpoint); ksDel (cut); } else if (!strcmp(keyBaseName(cur), "setplugins")) { if (elektraProcessPlugins(backend->setplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for set failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "errorplugins")) { if (elektraProcessPlugins(backend->errorplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for error failed"); failure = 1; } } else { // no one cares about that config if (!failure) ELEKTRA_ADD_WARNING(16, errorKey, keyBaseName(cur)); ksDel (cut); } } } if (failure) { Backend *tmpBackend = elektraBackendOpenMissing(backend->mountpoint); elektraBackendClose(backend, errorKey); backend = tmpBackend; } ksDel (systemConfig); ksDel (elektraConfig); ksDel (referencePlugins); return backend; }
static int listParseConfiguration (Placements * placements, KeySet * config) { Key * cur; Key * key = ksLookupByName (config, "/plugins", 0); KeySet * cutKS = ksCut (config, key); ksRewind (cutKS); if (ksGetSize (cutKS) < 2) return 0; int rc = 0; while ((cur = ksNext (cutKS)) != NULL) { if (keyRel (key, cur) != 1) { continue; } if (keyBaseName (cur)[0] == '#') { if (strcmp (lastIndex, keyBaseName (cur)) < 0) { snprintf (lastIndex, ELEKTRA_MAX_ARRAY_SIZE, "%s", keyBaseName (cur)); } } Key * sub; Key * lookup = keyDup (cur); keyAddBaseName (lookup, "placements"); keyAddBaseName (lookup, "set"); sub = ksLookup (cutKS, lookup, 0); if (sub) { const char * setString = keyString (sub); const char * setStrings[] = { "presetstorage", "presetcleanup", "precommit", "postcommit" }; SetPlacements setPlacement = preSetStorage; while (setPlacement != setEnd) { if (strstr (setString, setStrings[setPlacement])) { rc = 1; ksAppendKey (placements->setKS[setPlacement], keyDup (cur)); } ++setPlacement; } } keySetBaseName (lookup, "get"); sub = ksLookup (cutKS, lookup, 0); if (sub) { const char * getString = keyString (sub); const char * getStrings[] = { "pregetstorage", "postgetstorage", "postgetcleanup" }; GetPlacements getPlacement = preGetStorage; while (getPlacement != getEnd) { if (strstr (getString, getStrings[getPlacement])) { rc = 1; ksAppendKey (placements->getKS[getPlacement], keyDup (cur)); } ++getPlacement; } } keySetBaseName (lookup, "error"); sub = ksLookup (cutKS, lookup, 0); if (sub) { const char * errString = keyString (sub); const char * errStrings[] = { "prerollback", "postrollback" }; ErrPlacements errPlacement = preRollback; while (errPlacement != errEnd) { if (strstr (errString, errStrings[errPlacement])) { rc = 1; ksAppendKey (placements->errKS[errPlacement], keyDup (cur)); } ++errPlacement; } } keyDel (lookup); } ksDel (cutKS); return rc; }