/** * @internal * Remove plugin at all placements from list plugin configuration and apply it. * * @param list List plugin * @param plugin Plugin to remove * @retval 0 on error * @retval 1 on success */ static int listRemovePlugin (Plugin * list, Plugin * plugin) { ELEKTRA_NOT_NULL (list); ELEKTRA_NOT_NULL (plugin); KeySet * newConfig = ksDup (list->config); Key * configBase = keyNew ("user/plugins", KEY_END); KeySet * array = elektraArrayGet (configBase, newConfig); // Find the plugin with our handle Key * current; ksRewind (array); while ((current = ksNext (array)) != NULL) { Key * handleLookup = keyDup (current); keyAddBaseName (handleLookup, "handle"); Key * handle = ksLookup (newConfig, handleLookup, 0); keyDel (handleLookup); if (handle) { Plugin * handleValue = (*(Plugin **) keyValue (handle)); if (handleValue == plugin) { // Remove plugin configuration KeySet * cut = ksCut (newConfig, current); ksDel (cut); } } } ksDel (array); // Renumber array items KeySet * sourceArray = elektraArrayGet (configBase, newConfig); Key * renumberBase = keyNew ("user/plugins/#", KEY_END); ksRewind (sourceArray); while ((current = ksNext (sourceArray)) != NULL) { // Create new array item base name e.g. "user/plugins/#0" elektraArrayIncName (renumberBase); moveKeysRecursive (keyName (current), keyName (renumberBase), newConfig); } keyDel (configBase); keyDel (renumberBase); ksDel (sourceArray); ksDel (list->config); // Apply new configuration list->config = newConfig; list->kdbOpen (list, NULL); return 1; }
/** * Writes to @p stream an XML version of the @p ks object. * * String generated is of the form: * @verbatim <keyset> <key name=...>...</key> <key name=...>...</key> <key name=...>...</key> </keyset> * @endverbatim * * or if KDB_O_HIER is used, the form will be: * @verbatim <keyset parent="user/smallest/parent/name"> <key basename=...>...</key> <key name=...>...</key> <!-- a key thats not under this keyset's parent --> <key basename=...>...</key> </keyset> * @endverbatim * * KDB_O_HEADER will additionally generate a header like: * @verbatim <?xml version="1.0" encoding="UTF-8"?> <!-- Generated by Elektra API. Total of n keys. --> <keyset xmlns="http://www.libelektra.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.libelektra.org elektra.xsd"> * @endverbatim * * @param ks the KeySet to serialise * @param stream where to write output: a file or stdout * @param options accepted #option_t ORed: * - @p option_t::KDB_O_NUMBERS \n * Do not convert UID and GID into user and group names. * - @p option_t::KDB_O_FULLNAME \n * The @c user keys are exported with their full names (including * user domains) * - @p option_t::KDB_O_CONDENSED \n * Less human readable, more condensed output. * - @p option_t::KDB_O_XMLHEADERS \n * Exclude the correct XML headers in the output. If not used, the * <?xml?> and schema info inside the <keyset> object will not be generated. * - @p option_t::KDB_O_HIER \n * Will generate a <keyset> node containing a @c parent attribute, and * <key> nodes with a @c basename relative to that @c parent. The @c parent * is calculated by taking the smallest key name in the keyset, so it is a * good idea to have only related keys on the keyset. Otherwise, a valid * consistent XML document still will be generated with regular absolute * @c name attribute for the <key> nodes, due to a * clever keyToStreamBasename() implementation. * * @see keyToStream() * @see commandList() for usage example * @return number of bytes written to output, or -1 if some error occurs * @param ks The keyset to output * @param stream the file pointer where to send the stream * @param options see above text */ ssize_t ksToStream(const KeySet *ks, FILE* stream, option_t options) { size_t written=0; Key *key=0; char *codeset = "UTF-8"; KeySet *cks = ksDup (ks); ksRewind (cks); if (options & KDB_O_HEADER) { written+=fprintf(stream,"<?xml version=\"1.0\" encoding=\"%s\"?>", codeset); if (~options & KDB_O_CONDENSED) written+=fprintf(stream, "\n<!-- Generated by Elektra API. Total of %d keys. -->\n",(int)cks->size); if (~options & KDB_O_CONDENSED) written+=fprintf(stream,"<keyset xmlns=\"http://www.libelektra.org\"\n" "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" "\txsi:schemaLocation=\"http://www.libelektra.org elektra.xsd\"\n"); else written+=fprintf(stream,"<keyset xmlns=\"http://www.libelektra.org\"" " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" " xsi:schemaLocation=\"http://www.libelektra.org elektra.xsd\""); } else written+=fprintf(stream,"<keyset"); if (options & KDB_O_HIER) { char commonParent[KDB_MAX_PATH_LENGTH]; ksGetCommonParentName(cks,commonParent,sizeof(commonParent)); if (commonParent[0]) { written+=fprintf(stream," parent=\"%s\">\n", commonParent); ksRewind (cks); while ((key=ksNext (cks)) != 0) written+=keyToStreamBasename(key,stream,commonParent,0,options); } else { written+=fprintf(stream,">\n"); ksRewind (cks); while ((key=ksNext (cks)) != 0) written+=keyToStream(key,stream,options); } } else { /* No KDB_O_HIER*/ written+=fprintf(stream,">\n"); ksRewind (cks); while ((key=ksNext (cks)) != 0) written+=keyToStream(key,stream,options); } written+=fprintf(stream,"</keyset>\n"); ksDel (cks); return written; }
/** * @internal * Add plugin at placement to list plugin configuration and apply it. * * @param list List plugin * @param plugin Plugin to add * @param placement Placement name * @retval 0 on error * @retval 0 on success */ static int listAddPlugin (Plugin * list, Plugin * plugin, char * placement) { ELEKTRA_NOT_NULL (list); ELEKTRA_NOT_NULL (plugin); ELEKTRA_NOT_NULL (placement); KeySet * newConfig = ksDup (list->config); // Find name for next item in plugins array Key * configBase = keyNew ("user/plugins", KEY_END); KeySet * array = elektraArrayGet (configBase, newConfig); Key * pluginItem = elektraArrayGetNextKey (array); ELEKTRA_NOT_NULL (pluginItem); keySetString (pluginItem, plugin->name); keyDel (configBase); // Create key with plugin handle Key * pluginHandle = keyDup (pluginItem); keyAddName (pluginHandle, "handle"); keySetBinary (pluginHandle, &plugin, sizeof (plugin)); // Create key with plugin placement char * placementType = placementToListPositionType (placement); if (placementType == NULL) { keyDel (configBase); keyDel (pluginItem); keyDel (pluginHandle); return 0; } Key * pluginPlacements = keyDup (pluginItem); keyAddName (pluginPlacements, "placements/"); keyAddName (pluginPlacements, placementType); keySetString (pluginPlacements, placement); // Append keys to list plugin configuration ksAppendKey (newConfig, pluginItem); ksAppendKey (newConfig, pluginHandle); ksAppendKey (newConfig, pluginPlacements); ksDel (array); ksDel (list->config); // Apply new configuration list->config = newConfig; list->kdbOpen (list, NULL); return 1; }
/** * 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; }
static CondResult evaluateKey (const Key * meta, const Key * suffixList, Key * parentKey, Key * key, KeySet * ks, Operation op) { CondResult result; result = parseConditionString (meta, suffixList, parentKey, key, ksDup (ks), op); if (result == ERROR) { return ERROR; } else if (result == FALSE && op != ASSIGN) { return ERROR; } else if (result == TRUE && op != ASSIGN) { return TRUE; } else if (result == NOEXPR) { return NOEXPR; } return TRUE; }
/**Do a shallow copy of all metadata from source to dest. * * The key dest will additionally have all metadata * the source had. * Meta data not present in source will not be changed. * Meta data which was present in source and dest will * be overwritten. * * For example the metadata type is copied into the * Key k: * * @snippet keyMeta.c Basic Copy All * * The main purpose of this function is for plugins or * applications which want to add the same metadata to * n keys. When you do that with keySetMeta() it will * take n times the memory for the key. This can be * considerable amount of memory for many keys with * some metadata for each. * * To avoid that problem you can use keyCopyAllMeta() * or keyCopyMeta(): * * @snippet keyMeta.c Shared Meta All * * @post for every metaName present in source: keyGetMeta(source, metaName) == keyGetMeta(dest, metaName) * * @retval 1 if was successfully copied * @retval 0 if source did not have any metadata * @retval -1 on null pointer of dest or source * @retval -1 on memory problems * @param dest the destination where the metadata should be copied too * @param source the key where the metadata should be copied from * @ingroup keymeta */ int keyCopyAllMeta (Key * dest, const Key * source) { if (!source) return -1; if (!dest) return -1; if (dest->flags & KEY_FLAG_RO_META) return -1; if (source->meta) { /*Make sure that dest also does not have metaName*/ if (dest->meta) { ksAppend (dest->meta, source->meta); } else { dest->meta = ksDup (source->meta); } return 1; } return 0; }
static void validateArray (KeySet * ks, Key * arrayKey, Key * specKey) { Key * tmpArrayParent = keyDup (arrayKey); keySetBaseName (tmpArrayParent, 0); Key * arrayParent = ksLookup (ks, tmpArrayParent, KDB_O_NONE); keyDel (tmpArrayParent); if (arrayParent == NULL) return; KeySet * ksCopy = ksDup (ks); KeySet * subKeys = ksCut (ksCopy, arrayParent); Key * cur; long validCount = 0; while ((cur = ksNext (subKeys)) != NULL) { if (!keyIsDirectBelow (arrayParent, cur)) continue; if (keyBaseName (cur)[0] == '#') { if (elektraArrayValidateName (cur) == 1) { ++validCount; keySetMeta (cur, "spec/internal/valid", ""); } else { KeySet * invalidCutKS = ksCut (subKeys, cur); Key * toMark; while ((toMark = ksNext (invalidCutKS)) != NULL) { if (strcmp (keyName (cur), keyName (toMark))) keySetMeta (toMark, "conflict/invalid", ""); elektraMetaArrayAdd (arrayParent, "conflict/invalid/hasmember", keyName (toMark)); } ksDel (invalidCutKS); } } } ksDel (subKeys); ksDel (ksCopy); validateArrayRange (arrayParent, validCount, specKey); }
static void test_BlockresolverWrite (char * fileName, char * compareName) { FILE * fin = fopen (srcdir_file (fileName), "r"); char buffer[1024]; const char * foutname = elektraFilename (); FILE * fout = fopen (foutname, "w"); while (fgets (buffer, sizeof (buffer), fin)) { fputs (buffer, fout); } fclose (fin); fclose (fout); Key * parentKey = keyNew ("system/test/blockresolver-write", KEY_VALUE, foutname, KEY_END); KeySet * conf = ksNew (10, keyNew ("system/path", KEY_VALUE, foutname, KEY_END), keyNew ("system/identifier", KEY_VALUE, "### block config", KEY_END), KS_END); KeySet * modules = ksNew (0, KS_END); KeySet * ks = ksNew (0, KS_END); elektraModulesInit (modules, 0); Plugin * resolver = elektraPluginOpen ("blockresolver", modules, ksDup (conf), 0); succeed_if (resolver->kdbGet (resolver, ks, parentKey) >= 0, "blockresolver->kdbGet failed"); Plugin * storage = elektraPluginOpen ("ini", modules, ksNew (0, KS_END), 0); succeed_if (storage->kdbGet (storage, ks, parentKey) >= 0, "storage->kdbGet failed"); keySetString (ksLookupByName (ks, "system/test/blockresolver-write/section/key", 0), "only the inside has changed"); succeed_if (storage->kdbSet (storage, ks, parentKey) >= 0, "storage->kdbSet failed"); succeed_if (resolver->kdbSet (resolver, ks, parentKey) >= 0, "blockresolver->kdbSet failed"); succeed_if (resolver->kdbSet (resolver, ks, parentKey) >= 0, "blockresolver->kdbSet failed"); succeed_if (compare_line_files (srcdir_file (compareName), foutname), "files do not match as expected"); elektraPluginClose (storage, 0); elektraPluginClose (resolver, 0); ksDel (conf); ksDel (ks); elektraModulesClose (modules, 0); ksDel (modules); keyDel (parentKey); }
static void test_BlockresolverRead (char * fileName) { Key * parentKey = keyNew ("system/test/blockresolver-read", KEY_VALUE, srcdir_file (fileName), KEY_END); KeySet * conf = ksNew (10, keyNew ("system/path", KEY_VALUE, srcdir_file (fileName), KEY_END), keyNew ("system/identifier", KEY_VALUE, "### block config", KEY_END), KS_END); KeySet * modules = ksNew (0, KS_END); KeySet * ks = ksNew (0, KS_END); elektraModulesInit (modules, 0); Plugin * resolver = elektraPluginOpen ("blockresolver", modules, ksDup (conf), 0); succeed_if (resolver->kdbGet (resolver, ks, parentKey) >= 0, "blockresolver->kdbGet failed"); output_warnings (parentKey); output_error (parentKey); Plugin * storage = elektraPluginOpen ("ini", modules, ksNew (0, KS_END), 0); succeed_if (storage->kdbGet (storage, ks, parentKey) >= 0, "storage->kdbGet failed"); succeed_if (!strcmp (keyString (ksLookupByName (ks, "system/test/blockresolver-read/section/key", 0)), "inside block"), "blockresolver failed to resolve requested block"); elektraPluginClose (storage, 0); elektraPluginClose (resolver, 0); ksDel (conf); ksDel (ks); elektraModulesClose (modules, 0); ksDel (modules); keyDel (parentKey); }
* @brief Source for dini plugin * * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) * */ #include "dini.h" #include <kdbhelper.h> int elektraDiniOpen (Plugin * handle, Key * errorKey ELEKTRA_UNUSED) { Dini * dini = elektraMalloc (sizeof (Dini)); dini->dumpConfig = ksDup (elektraPluginGetConfig (handle)); dini->iniConfig = ksDup (elektraPluginGetConfig (handle)); dini->dump = elektraInvokeOpen ("dump", dini->dumpConfig, 0); dini->ini = elektraInvokeOpen ("ini", dini->iniConfig, 0); dini->bin = elektraInvokeOpen ("binary", dini->iniConfig, 0); dini->dumpErrors = keyNew ("", KEY_END); elektraPluginSetData (handle, dini); return dini->ini ? ELEKTRA_PLUGIN_STATUS_SUCCESS : ELEKTRA_PLUGIN_STATUS_ERROR; } int elektraDiniClose (Plugin * handle, Key * errorKey ELEKTRA_UNUSED) { Dini * dini = elektraPluginGetData (handle);
/** Call a plugin's function in a child process * * This will wrap all the required information to execute the given * command in a keyset and send it over to the child process. Then * it waits for the child process's answer and copies the result * back into the original plugin keyset and plugin key. * * Typically called like * @code int elektraPluginSet (Plugin * handle, KeySet * returned, Key * parentKey) { ElektraPluginProcess * pp = elektraPluginGetData (handle); if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_SET, returned, parentKey); // actual plugin functionality to be executed in a child process return ELEKTRA_PLUGIN_STATUS_SUCCESS; } * @endcode * * @param pp the data structure containing the plugin's process information * @param command the plugin command that should be executed, e.g. ELEKTRA_PLUGINPROCESS_GET * @param originalKeySet the original key set that the parent process receives * @param key the original key the parent process receives * @retval ELEKTRA_PLUGIN_STATUS_ERROR if the child process communication failed * @retval the called plugin's return value otherwise * @see elektraPluginProcessIsParent for checking if we are in the parent or child process * @ingroup processplugin **/ int elektraPluginProcessSend (const ElektraPluginProcess * pp, pluginprocess_t command, KeySet * originalKeySet, Key * key) { // Ensure we have a keyset when trying to call GET SET and ERROR if ((command == ELEKTRA_PLUGINPROCESS_GET || command == ELEKTRA_PLUGINPROCESS_SET || command == ELEKTRA_PLUGINPROCESS_ERROR) && originalKeySet == NULL) { ELEKTRA_SET_ERROR (191, key, "originalKeySet has to exist when calling GET SET and ERROR via pluginprocess; but it is NULL"); return ELEKTRA_PLUGIN_STATUS_ERROR; } // Construct the command set that controls the pluginprocess communication KeySet * commandKeySet = ksNew (6, KS_END); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/parent/name", KEY_VALUE, keyName (key), KEY_END)); Key * parentKey = keyDup (key); keySetName (parentKey, "/pluginprocess/parent"); ksAppendKey (commandKeySet, parentKey); char * commandStr = longToStr (command); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/command", KEY_VALUE, commandStr, KEY_END)); elektraFree (commandStr); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/version", KEY_VALUE, "1", KEY_END)); // Some plugin functions don't use keysets, in that case don't send any actual payload, signal via flag KeySet * keySet = originalKeySet != NULL ? ksDup (originalKeySet) : NULL; char * payloadSizeStr = longToStr (ksGetSize (originalKeySet)); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/payload/size", KEY_VALUE, originalKeySet == NULL ? "-1" : payloadSizeStr, KEY_END)); elektraFree (payloadSizeStr); // Serialize, currently statically use dump as our default format, this already writes everything out to the pipe ELEKTRA_LOG ("Parent: Sending data to issue command %u it through pipe %s", command, keyString (pp->parentCommandPipeKey)); elektraInvoke2Args (pp->dump, "set", commandKeySet, pp->parentCommandPipeKey); if (keySet != NULL) { ELEKTRA_LOG ("Parent: Sending the payload keyset with %zd keys through the pipe %s", ksGetSize (keySet), keyString (pp->parentPayloadPipeKey)); elektraInvoke2Args (pp->dump, "set", keySet, pp->parentPayloadPipeKey); } // Deserialize ELEKTRA_LOG_DEBUG ("Parent: Waiting for the result now on pipe %s", keyString (pp->childCommandPipeKey)); elektraInvoke2Args (pp->dump, "get", commandKeySet, pp->childCommandPipeKey); if (keySet != NULL) { // clear the keyset before to avoid memleaks caused by dump char * endPtr; int prevErrno = errno; errno = 0; long payloadSize = strtol (keyString (ksLookupByName (commandKeySet, "/pluginprocess/payload/size", KDB_O_NONE)), &endPtr, 10); // in case the payload size fails to be transferred, that it shouldn't, we simply assume the previous size if (*endPtr != '\0' || errno == ERANGE || payloadSize < 0) payloadSize = ksGetSize (keySet); errno = prevErrno; ksDel (keySet); keySet = ksNew (payloadSize, KS_END); elektraInvoke2Args (pp->dump, "get", keySet, pp->childPayloadPipeKey); ELEKTRA_LOG ("Parent: We received %zd keys in return", ksGetSize (keySet)); } // Bring everything back in order by removing our process-related keys Key * parentDeserializedKey = ksLookupByName (commandKeySet, "/pluginprocess/parent", KDB_O_NONE); Key * resultKey = ksLookupByName (commandKeySet, "/pluginprocess/result", KDB_O_NONE); // Parse the result value char * endPtr; int prevErrno = errno; errno = 0; long lresult = strtol (keyString (resultKey), &endPtr, 10); if (*endPtr != '\0' || errno == ERANGE || lresult > INT_MAX || lresult < INT_MIN) { ELEKTRA_SET_ERRORF (191, key, "Received invalid return code or no KeySet: %s", keyString (resultKey)); lresult = ELEKTRA_PLUGIN_STATUS_ERROR; } else // Copy everything back into the actual keysets { Key * parentKeyInOriginalKeySet = keySet != NULL ? ksLookup (originalKeySet, key, KDB_O_NONE) : NULL; // maybe there are just 2 keys with the same name, can happen in theory, so compare memory int parentKeyExistsInOriginalKeySet = parentKeyInOriginalKeySet == key; // if the child added the parent key to the keyset pop it from the keyset // then reinsert key after we copied the data and delete this serialized copy Key * parentKeyInKeySet = keySet != NULL ? ksLookup (keySet, key, KDB_O_POP) : NULL; int childAddedParentKey = parentKeyInKeySet != NULL; // Unfortunately we can't use keyCopy here as ksAppendKey locks it so it will fail // This is the case if the parent key is also contained in the originalKeySet / has been appended // As an invariant we assume plugins don't change the parent key's name during a plugin call // This would interfere with keyset memberships keySetString (key, keyString (parentDeserializedKey)); // Clear metadata before, we allow children to modify it keyRewindMeta (key); const Key * currentMeta; while ((currentMeta = keyNextMeta (key)) != NULL) { keySetMeta (key, keyName (currentMeta), 0); } keyCopyAllMeta (key, parentDeserializedKey); if (childAddedParentKey) keyCopyAllMeta (key, parentKeyInKeySet); if (keySet != NULL) { // in case originalKeySet contains key this would make it stuck // thus remove it here and re-add it afterwards if (parentKeyExistsInOriginalKeySet) ksLookup (originalKeySet, parentKeyInOriginalKeySet, KDB_O_POP); ksCopy (originalKeySet, keySet); if (parentKeyExistsInOriginalKeySet || childAddedParentKey) ksAppendKey (originalKeySet, key); if (childAddedParentKey) keyDel (parentKeyInKeySet); } } errno = prevErrno; // Command finished, cleanup the remaining memory now ksDel (commandKeySet); if (keySet != NULL) ksDel (keySet); return lresult; // Safe, we had a bound check before, and plugins should return values in the int range }
/** * Copy or Clear a key. * * Most often you may prefer keyDup() which allocates * a new key and returns a duplication of another key. * * But when you need to copy into an existing key, e.g. * because it was passed by a pointer in a function * you can do so: * * @snippet keyCopy.c Basic Usage * * The reference counter will not be changed for * both keys. Affiliation to keysets * are also not affected. * * The meta data will be duplicated for the destination * key. So it will not take much additional space, even * with lots of metadata. * * When you pass a NULL-pointer as source the * data of dest will be cleaned completely * (except reference counter, see keyClear()) and * you get a fresh dest key: * * @snippet keyCopy.c Clear * * If you want to copy everything, except e.g. the value * you can use keyCopy() too: * * @snippet keyCopy.c Copy Without Value * * Restrain from coping everything yourself, because it will lead to * wrong metadata and is not able to copy empty or cascading names: * * @snippet keyCopy.c Individual Copy * * * @param dest the key which will be written to * @param source the key which should be copied * or NULL to clean the destination key * @ingroup key * @retval -1 on failure when a NULL pointer * was passed for dest or a dynamic property could not * be written. The content will be unmodified then. * @retval 0 when dest was cleaned * @retval 1 when source was successfully copied * @see keyDup() to get a duplication of a key */ int keyCopy (Key * dest, const Key * source) { if (!dest) return -1; if (test_bit (dest->flags, KEY_FLAG_RO_NAME) || test_bit (dest->flags, KEY_FLAG_RO_VALUE) || test_bit (dest->flags, KEY_FLAG_RO_META)) { return -1; } if (!source) { keyClear (dest); return 0; } // remember dynamic memory to be removed char * destKey = dest->key; void * destData = dest->data.c; KeySet * destMeta = dest->meta; // duplicate dynamic properties if (source->key) { dest->key = elektraStrNDup (source->key, source->keySize + source->keyUSize); if (!dest->key) goto memerror; } else { dest->key = 0; } if (source->data.v) { dest->data.v = elektraStrNDup (source->data.v, source->dataSize); if (!dest->data.v) goto memerror; } else { dest->data.v = 0; } if (source->meta) { dest->meta = ksDup (source->meta); if (!dest->meta) goto memerror; } else { dest->meta = 0; } // successful, now do the irreversible stuff: we obviously modified dest set_bit (dest->flags, KEY_FLAG_SYNC); // copy sizes accordingly dest->keySize = source->keySize; dest->keyUSize = source->keyUSize; dest->dataSize = source->dataSize; // free old resources of destination elektraFree (destKey); elektraFree (destData); ksDel (destMeta); return 1; memerror: elektraFree (dest->key); elektraFree (dest->data.v); ksDel (dest->meta); dest->key = destKey; dest->data.v = destData; dest->meta = destMeta; return -1; }
/** * @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; }