/**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; }
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); }
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); }
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 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); }
int elektraCsvstorageGet(Plugin *handle, KeySet *returned, Key *parentKey) { if (!strcmp(keyName(parentKey), "system/elektra/modules/csvstorage")) { KeySet *contract = ksNew (30, keyNew ("system/elektra/modules/csvstorage", KEY_VALUE, "csvstorage plugin waits for your orders", KEY_END), keyNew ("system/elektra/modules/csvstorage/exports", KEY_END), keyNew ("system/elektra/modules/csvstorage/exports/get", KEY_FUNC, elektraCsvstorageGet, KEY_END), keyNew ("system/elektra/modules/csvstorage/exports/set", KEY_FUNC, elektraCsvstorageSet, KEY_END), #include ELEKTRA_README(csvstorage) keyNew ("system/elektra/modules/csvstorage/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END); ksAppend (returned, contract); ksDel (contract); return 1; /* success */ } KeySet *config = elektraPluginGetConfig(handle); Key *delimKey = ksLookupByName(config, "/delimiter", 0); char delim = ';'; if(delimKey) { const char *delimString = keyString(delimKey); delim = delimString[0]; } Key *readHeaderKey = ksLookupByName(config, "/header", 0); short useHeader = 0; if(readHeaderKey) { const char *printHeaderString = keyString(readHeaderKey); if(!strcmp(printHeaderString, "colname")) { useHeader = 1; } else if(!(strcmp(printHeaderString, "skip"))) { useHeader = -1; } else if(!(strcmp(printHeaderString, "record"))) { useHeader = 0; } else { useHeader = 0; } } unsigned long fixColumnCount = 0; Key *fixColumnCountKey = ksLookupByName(config, "/columns", 0); if(fixColumnCountKey) { if(keyString(fixColumnCountKey)) { fixColumnCount = atol(keyString(fixColumnCountKey)); } } Key *setNamesKey = ksLookupByName(config, "/columns/names", 0); char *colNames = NULL; if(setNamesKey) { if(fixColumnCountKey) { KeySet *namesKS = ksCut(config, setNamesKey); unsigned long nrNames = (unsigned long)ksGetSize(namesKS)-1; if(nrNames == fixColumnCount) { colNames = (char *)elektraMalloc(nrNames*sizeof(char *)); Key *cur; char **ptr = (char **)colNames; while((cur = ksNext(namesKS)) != NULL) { if(!strcmp(keyName(cur), keyName(setNamesKey))) continue; if(!strcmp(keyString(cur), "")) *ptr = NULL; else *ptr = (char *)keyString(cur); ++ptr; } } ksAppend(config, namesKS); ksDel(namesKS); } } int nr_keys; nr_keys = csvRead(returned, parentKey, delim, useHeader, fixColumnCount, (const char **)colNames); if(colNames) elektraFree(colNames); if (nr_keys == -1) return -1; return 1; }
void clear_sync (KeySet *ks) { Key *k; ksRewind(ks); while ((k = ksNext(ks)) != 0) keyClearSync(k); }
/** * @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 */ }
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; }
/**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; }
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() ; }
/** * 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; }
/** * @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; }
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; }