Ejemplo n.º 1
0
/**
 * @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;
}
Ejemplo n.º 2
0
/**
 * 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
 *   &lt;?xml?&gt; and schema info inside the &lt;keyset&gt; object will not be generated.
 * - @p option_t::KDB_O_HIER \n
 *   Will generate a &lt;keyset&gt; node containing a @c parent attribute, and
 *   &lt;key&gt; 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 &lt;key&gt; 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;
}
Ejemplo n.º 3
0
/**
 * @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;
}
Ejemplo n.º 4
0
/**
 * 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;
}
Ejemplo n.º 5
0
/**
 * 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;
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
/**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;
}
Ejemplo n.º 8
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);
}
Ejemplo n.º 9
0
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);
}
Ejemplo n.º 10
0
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);
}
Ejemplo n.º 11
0
 * @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);
Ejemplo n.º 12
0
/** 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
}
Ejemplo n.º 13
0
/**
 * 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;
}
Ejemplo n.º 14
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;
}