Exemplo n.º 1
0
/**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;
}
Exemplo n.º 2
0
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);
}
Exemplo n.º 3
0
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);
}
Exemplo n.º 4
0
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 */
}
Exemplo n.º 5
0
/**
 * @brief encrypt or sign the file specified at parentKey
 * @param pluginConfig holds the plugin configuration
 * @param parentKey holds the path to the file to be encrypted. Will hold an error description in case of failure.
 * @retval 1 on success
 * @retval -1 on error, errorKey holds an error description
 */
static int fcryptEncrypt (KeySet * pluginConfig, Key * parentKey)
{
	Key * k;
	const size_t recipientCount = getRecipientCount (pluginConfig, ELEKTRA_RECIPIENT_KEY);
	const size_t signatureCount = getRecipientCount (pluginConfig, ELEKTRA_SIGNATURE_KEY);

	if (recipientCount == 0 && signatureCount == 0)
	{
		ELEKTRA_SET_ERRORF (
			ELEKTRA_ERROR_NO_GPG_RECIPIENTS, parentKey,
			"Missing GPG recipient key (specified as %s) or GPG signature key (specified as %s) in plugin configuration.",
			ELEKTRA_RECIPIENT_KEY, ELEKTRA_SIGNATURE_KEY);
		return -1;
	}

	int tmpFileFd = -1;
	char * tmpFile = getTemporaryFileName (pluginConfig, keyString (parentKey), &tmpFileFd);
	if (!tmpFile)
	{
		ELEKTRA_SET_ERROR (87, parentKey, "Memory allocation failed");
		return -1;
	}

	const size_t testMode = inTestMode (pluginConfig);
	const size_t textMode = inTextMode (pluginConfig);

	// prepare argument vector for gpg call
	// 7 static arguments (magic number below) are:
	//   1. path to the binary
	//   2. --batch
	//   3. -o
	//   4. path to tmp file
	//   5. yes
	//   6. file to be encrypted
	//   7. NULL terminator
	int argc = 7 + (2 * recipientCount) + (2 * signatureCount) + (2 * testMode) + textMode + (recipientCount > 0 ? 1 : 0) +
		   (signatureCount > 0 ? 1 : 0);
	kdb_unsigned_short_t i = 0;
	char * argv[argc];
	argv[i++] = NULL;
	argv[i++] = "--batch";
	argv[i++] = "-o";
	argv[i++] = tmpFile;
	argv[i++] = "--yes"; // overwrite files if they exist

	// add recipients
	Key * gpgRecipientRoot = ksLookupByName (pluginConfig, ELEKTRA_RECIPIENT_KEY, 0);

	// append root (gpg/key) as gpg recipient
	if (gpgRecipientRoot && strlen (keyString (gpgRecipientRoot)) > 0)
	{
		argv[i++] = "-r";
		// NOTE argv[] values will not be modified, so const can be discarded safely
		argv[i++] = (char *) keyString (gpgRecipientRoot);
	}

	// append keys beneath root (crypto/key/#_) as gpg recipients
	if (gpgRecipientRoot)
	{
		ksRewind (pluginConfig);
		while ((k = ksNext (pluginConfig)) != 0)
		{
			const char * kStringVal = keyString (k);
			if (keyIsBelow (k, gpgRecipientRoot) && strlen (kStringVal) > 0)
			{
				argv[i++] = "-r";
				// NOTE argv[] values will not be modified, so const can be discarded safely
				argv[i++] = (char *) kStringVal;
			}
		}
	}


	// add signature keys
	Key * gpgSignatureRoot = ksLookupByName (pluginConfig, ELEKTRA_SIGNATURE_KEY, 0);

	// append root signature key
	if (gpgSignatureRoot && strlen (keyString (gpgSignatureRoot)) > 0)
	{
		argv[i++] = "-u";
		// NOTE argv[] values will not be modified, so const can be discarded safely
		argv[i++] = (char *) keyString (gpgSignatureRoot);
	}

	// append keys beneath root (fcrypt/sign/#_) as gpg signature keys
	if (gpgSignatureRoot)
	{
		ksRewind (pluginConfig);
		while ((k = ksNext (pluginConfig)) != 0)
		{
			const char * kStringVal = keyString (k);
			if (keyIsBelow (k, gpgSignatureRoot) && strlen (kStringVal) > 0)
			{
				argv[i++] = "-u";
				// NOTE argv[] values will not be modified, so const can be discarded safely
				argv[i++] = (char *) kStringVal;
			}
		}
	}

	// if we are in test mode we add the trust model
	if (testMode > 0)
	{
		argv[i++] = "--trust-model";
		argv[i++] = "always";
	}

	// ASCII armor in text mode
	if (textMode)
	{
		argv[i++] = "--armor";
	}

	// prepare rest of the argument vector
	if (recipientCount > 0)
	{
		// encrypt the file
		argv[i++] = "-e";
	}

	if (signatureCount > 0)
	{
		if (textMode && recipientCount == 0)
		{
			// clear-sign the file
			argv[i++] = "--clearsign";
		}
		else
		{
			// sign the file
			argv[i++] = "-s";
		}
	}

	argv[i++] = (char *) keyString (parentKey);
	argv[i++] = NULL;

	// NOTE the encryption process works like this:
	// gpg2 --batch --yes -o encryptedFile -r keyID -e configFile
	// mv encryptedFile configFile

	return fcryptGpgCallAndCleanup (parentKey, pluginConfig, argv, argc, tmpFileFd, tmpFile);
}
Exemplo n.º 6
0
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;
}
Exemplo n.º 7
0
void clear_sync (KeySet *ks)
{
	Key *k;
	ksRewind(ks);
	while ((k = ksNext(ks)) != 0) keyClearSync(k);
}
Exemplo n.º 8
0
/**
 * @internal
 *
 * Calculates the common parent to all keys in @p ks.
 *
 * This is a c-helper function, you need not implement it in bindings.
 *
 * Given the @p ks KeySet, calculates the parent name for all the keys.
 * So if @p ks contains this keys:
 *
 * @code
 *   system/sw/xorg/Monitors/Monitor1/vrefresh
 *   system/sw/xorg/Monitors/Monitor1/hrefresh
 *   system/sw/xorg/Devices/Device1/driver
 *   system/sw/xorg/Devices/Device1/mode
 * @endcode
 *
 * The common parent is @p system/sw/xorg .
 *
 * On the other hand, if we have this KeySet:
 *
 * @code
 *   system/some/thing
 *   system/other/thing
 *   user/unique/thing
 * @endcode
 *
 * No common parent is possible, so @p returnedCommonParent will contain nothing.
 *
 * @param working the Keyset to work with
 * @param returnedCommonParent a pre-allocated buffer that will receive the
 *        common parent, if found
 * @param maxSize size of the pre-allocated @p returnedCommonParent buffer
 * @return size in bytes of the parent name, or 0 if there is no common parent,
 *         or -1 to indicate an error, then @p errno must be checked.
 */
ssize_t ksGetCommonParentName (const KeySet * working, char * returnedCommonParent, size_t maxSize)
{
	size_t parentSize = 0;
	Key * current = 0;
	cursor_t cinit;
	KeySet * ks;
	ssize_t sMaxSize;

	if (maxSize > SSIZE_MAX) return -1;
	sMaxSize = maxSize;

	cinit = ksGetCursor (working);
	ks = (KeySet *)working;

	if (ksGetSize (ks) < 1) return 0;

	ksRewind (ks);
	current = ksNext (ks);
	if (keyGetNameSize (current) > sMaxSize)
	{
		/*errno=KDB_ERR_TRUNC;*/
		returnedCommonParent[0] = 0;
		return -1;
	}

	strcpy (returnedCommonParent, keyName (current));
	parentSize = elektraStrLen (returnedCommonParent);

	while (*returnedCommonParent)
	{
		ksRewind (ks);
		while ((current = ksNext (ks)) != 0)
		{
			/* Test if a key doesn't match */
			if (memcmp (returnedCommonParent, keyName (current), parentSize - 1)) break;
		}
		if (current)
		{
			/* some key failed to be a child */
			/* parent will be the parent of current parent... */
			char * delim = 0;

			// TODO: does not honor escaped characters
			if ((delim = strrchr (returnedCommonParent, KDB_PATH_SEPARATOR)))
			{
				*delim = 0;
				parentSize = elektraStrLen (returnedCommonParent);
			}
			else
			{
				*returnedCommonParent = 0;
				parentSize = 0;
				break; /* Better don't make comparison with parentSize-1 now */
			}
		}
		else
		{
			/* All keys matched (current==0) */
			/* We have our common parent to return in commonParent */
			ksSetCursor (ks, cinit);
			return parentSize;
		}
	}
	ksSetCursor (ks, cinit);
	return parentSize; /* if reached, will be zero */
}
Exemplo n.º 9
0
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;
}
Exemplo n.º 10
0
/**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;
}
Exemplo n.º 11
0
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() ;
}
Exemplo n.º 12
0
/**
 * 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;
}
Exemplo n.º 13
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;
}
Exemplo n.º 14
0
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;
}