Esempio n. 1
0
/**
 * @internal
 * Check if two keys have the same name.
 * If one of the keys is cascading only the cascading names are compared.
 *
 * @param  key   key
 * @param  check check
 * @retval 1 if keys have the same name
 * @retval 0 otherwise
 */
static int checkKeyIsSame (Key * key, Key * check)
{
	int result = 0;
	if (keyGetNamespace (check) == KEY_NS_CASCADING || keyGetNamespace (key) == KEY_NS_CASCADING)
	{
		const char * cascadingCheck = strrchr (keyName (check), '/');
		const char * cascadingKey = strrchr (keyName (key), '/');
		if (cascadingCheck != NULL && cascadingKey != NULL)
		{
			result = elektraStrCmp (cascadingKey, cascadingCheck) == 0;
		}
		else
		{
			if (cascadingCheck == NULL)
			{
				ELEKTRA_LOG_WARNING ("invalid key given: '%s' is not a valid key", cascadingCheck);
			}
			if (cascadingKey == NULL)
			{
				ELEKTRA_LOG_WARNING ("invalid key given: '%s' is not a valid key", cascadingKey);
			}
		}
	}
	else
	{
		result = elektraStrCmp (keyName (check), keyName (key)) == 0;
	}
	return result;
}
Esempio n. 2
0
// keyRel2 helper, returns how many levels check is below key, or 0 if check isn't below
int keyGetLevelsBelow (const Key * key, const Key * check)
{
	if (!keyIsBelow (key, check)) return 0;
	if (keyGetNamespace (key) != keyGetNamespace (check)) return 0;
	Key * toCheck = keyDup (check);
	int levels = 0;
	while (strcmp (keyName (key), keyName (toCheck)))
	{
		keySetBaseName (toCheck, 0);
		if (keyName (toCheck)[0] == '\0') keySetName (toCheck, "/");
		++levels;
	}
	keyDel (toCheck);
	return levels;
}
Esempio n. 3
0
static KeySet * prepareGlobalKS (KeySet * ks, Key * parentKey)
{
	ksRewind (ks);
	Key * cutKey = keyNew ("/", KEY_CASCADING_NAME, KEY_END);
	keyAddName (cutKey, strchr (keyName (parentKey), '/'));
	KeySet * cutKS = ksCut (ks, cutKey);
	Key * specCutKey = keyNew ("spec", KEY_END);
	KeySet * specCut = ksCut (cutKS, specCutKey);
	ksRewind (specCut);
	Key * cur;
	while ((cur = ksNext (specCut)) != NULL)
	{
		if (keyGetNamespace (cur) == KEY_NS_CASCADING)
		{
			ksAppendKey (cutKS, cur);
			keyDel (ksLookup (specCut, cur, KDB_O_POP));
		}
	}
	ksAppend (ks, specCut);
	ksDel (specCut);
	keyDel (specCutKey);
	keyDel (cutKey);
	ksRewind (cutKS);
	return cutKS;
}
Esempio n. 4
0
static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey)
{
	resolverHandles * pks = elektraPluginGetData (handle);
	ELEKTRA_ASSERT (pks != NULL, "Unable to retrieve plugin data for handle %p with parentKey %s", (void *) handle,
			keyName (parentKey));

	switch (keyGetNamespace (parentKey))
	{
	case KEY_NS_SPEC:
		return &pks->spec;
	case KEY_NS_DIR:
		return &pks->dir;
	case KEY_NS_USER:
		return &pks->user;
	case KEY_NS_SYSTEM:
		return &pks->system;
	case KEY_NS_PROC:
	case KEY_NS_EMPTY:
	case KEY_NS_NONE:
	case KEY_NS_META:
	case KEY_NS_CASCADING:
		return 0;
	}

	return 0;
}
Esempio n. 5
0
void printNamespace(Key const* k)
{
//! [namespace]
switch (keyGetNamespace(k))
{
case KEY_NS_SPEC:
	printf ("spec namespace\n");
	break;
case KEY_NS_PROC:
	printf ("proc namespace\n");
	break;
case KEY_NS_DIR:
	printf ("dir namespace\n");
	break;
case KEY_NS_USER:
	printf ("user namespace\n");
	break;
case KEY_NS_SYSTEM:
	printf ("system namespace\n");
	break;
case KEY_NS_EMPTY:
	printf ("empty name\n");
	break;
case KEY_NS_NONE:
	printf ("no key\n");
	break;
case KEY_NS_META:
	printf ("meta key\n");
	break;
case KEY_NS_CASCADING:
	printf ("cascading key\n");
	break;
}
//! [namespace]
}
Esempio n. 6
0
/**
 * Also update sizes after kdbSet() to recognize multiple kdbSet() attempts.
 *
 * @warning cant use the same code with elektraSplitGet because there is
 * no default split part for kdbSet().
 */
int elektraSplitUpdateSize (Split * split)
{
	/* Iterate everything */
	for (size_t i = 0; i < split->size; ++i)
	{
		switch (keyGetNamespace (split->parents[i]))
		{
		case KEY_NS_SPEC:
			split->handles[i]->specsize = ksGetSize (split->keysets[i]);
			break;
		case KEY_NS_DIR:
			split->handles[i]->dirsize = ksGetSize (split->keysets[i]);
			break;
		case KEY_NS_USER:
			split->handles[i]->usersize = ksGetSize (split->keysets[i]);
			break;
		case KEY_NS_SYSTEM:
			split->handles[i]->systemsize = ksGetSize (split->keysets[i]);
			break;
		case KEY_NS_PROC:
		case KEY_NS_EMPTY:
		case KEY_NS_NONE:
		case KEY_NS_META:
		case KEY_NS_CASCADING:
			return -1;
		}
	}
	return 1;
}
Esempio n. 7
0
/**
 * @brief Filter out keys not in the correct keyset
 *
 * @param split the split where to do it
 * @param i for which split
 * @param warningKey the key
 * @param handle where to do backend lookups
 *
 * @retval -1 on error (no backend, wrong namespace)
 * @retval 0 otherwise
 */
static int elektraSplitPostprocess (Split * split, int i, Key * warningKey, KDB * handle)
{
	Key * cur = 0;
	Backend * curHandle = 0;
	ksRewind (split->keysets[i]);
	while ((cur = ksNext (split->keysets[i])) != 0)
	{
		curHandle = elektraMountGetBackend (handle, cur);
		if (!curHandle) return -1;

		keyClearSync (cur);

		if (curHandle != split->handles[i])
		{
			elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is hidden by other mountpoint");
		}
		else
			switch (keyGetNamespace (cur))
			{
			case KEY_NS_SPEC:
				if (!keyIsSpec (split->parents[i]))
					elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not spec");
				break;
			case KEY_NS_DIR:
				if (!keyIsDir (split->parents[i]))
					elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not dir");
				break;
			case KEY_NS_USER:
				if (!keyIsUser (split->parents[i]))
					elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not user");
				break;
			case KEY_NS_SYSTEM:
				if (!keyIsSystem (split->parents[i]))
					elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it is not system");
				break;
			case KEY_NS_PROC:
				elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has a proc key name");
				break;
			case KEY_NS_EMPTY:
				elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has an empty name");
				break;
			case KEY_NS_META:
				elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has a meta name");
				break;
			case KEY_NS_CASCADING:
				elektraDropCurrentKey (split->keysets[i], warningKey, curHandle, "it has a cascading name");
				break;
			case KEY_NS_NONE:
				ELEKTRA_ASSERT (0 && "wrong key namespace, should not be none");
				return -1;
			}
	}
	return 0;
}
Esempio n. 8
0
static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey)
{
	resolverHandles * pks = elektraPluginGetData (handle);
	switch (keyGetNamespace (parentKey))
	{
	case KEY_NS_SPEC:
		return &pks->spec;
	case KEY_NS_DIR:
		return &pks->dir;
	case KEY_NS_USER:
		return &pks->user;
	case KEY_NS_SYSTEM:
		return &pks->system;
	case KEY_NS_PROC:
	case KEY_NS_EMPTY:
	case KEY_NS_NONE:
	case KEY_NS_META:
	case KEY_NS_CASCADING:
		break;
	}
	ELEKTRA_ASSERT (0, "namespace %d not valid for resolving", keyGetNamespace (parentKey));
	return 0;
}
Esempio n. 9
0
static int elektraResolveFilename (Key * parentKey, ElektraResolveTempfile tmpFile)
{
	int rc = 0;
	void * handle = elektraInvokeOpen ("resolver", 0, 0);
	if (!handle)
	{
		rc = -1;
		goto RESOLVE_FAILED;
	}
	ElektraResolved * resolved = NULL;
	typedef ElektraResolved * (*resolveFileFunc) (elektraNamespace, const char *, ElektraResolveTempfile, Key *);
	resolveFileFunc resolveFunc = *(resolveFileFunc *) elektraInvokeGetFunction (handle, "filename");

	if (!resolveFunc)
	{
		rc = -1;
		goto RESOLVE_FAILED;
	}

	typedef void (*freeHandleFunc) (ElektraResolved *);
	freeHandleFunc freeHandle = *(freeHandleFunc *) elektraInvokeGetFunction (handle, "freeHandle");

	if (!freeHandle)
	{
		rc = -1;
		goto RESOLVE_FAILED;
	}

	resolved = resolveFunc (keyGetNamespace (parentKey), keyString (parentKey), tmpFile, parentKey);

	if (!resolved)
	{
		rc = -1;
		goto RESOLVE_FAILED;
	}
	else
	{
		keySetString (parentKey, resolved->fullPath);
		freeHandle (resolved);
	}

RESOLVE_FAILED:
	elektraInvokeClose (handle, 0);
	return rc;
}
Esempio n. 10
0
/**
 * Determines if the backend is already inserted or not.
 *
 * @warning If no parent Key is given, the default/root backends won't
 * be searched.
 *
 * @param split the split object to work with
 * @param backend the backend to search for
 * @param parent the key to check for domains in default/root backends.
 * @return pos of backend if it already exist
 * @retval -1 if it does not exist
 * @ingroup split
 */
ssize_t elektraSplitSearchBackend (Split * split, Backend * backend, Key * parent)
{
	for (size_t i = 0; i < split->size; ++i)
	{
		if (backend == split->handles[i])
		{
			if (test_bit (split->syncbits[i], SPLIT_FLAG_CASCADING))
			{
				switch (keyGetNamespace (parent))
				{
				case KEY_NS_SPEC:
					if (keyIsSpec (split->parents[i])) return i;
					break;
				case KEY_NS_DIR:
					if (keyIsDir (split->parents[i])) return i;
					break;
				case KEY_NS_USER:
					if (keyIsUser (split->parents[i])) return i;
					break;
				case KEY_NS_SYSTEM:
					if (keyIsSystem (split->parents[i])) return i;
					break;
				case KEY_NS_PROC:
					return -1;
				case KEY_NS_EMPTY:
					return -1;
				case KEY_NS_NONE:
					return -1;
				case KEY_NS_META:
					return -1;
				case KEY_NS_CASCADING:
					return -1;
				}
				continue;
			}
			/* We already have this backend, so leave */
			return i;
		}
	}
	return -1;
}
Esempio n. 11
0
// keyRel2 helper, turns key into a cascading key ( removes namespace)
Key * keyAsCascading (const Key * key)
{
	if (keyName (key)[0] == '/')
	{
		return keyDup (key);
	}
	else
	{
		elektraNamespace ns = keyGetNamespace (key);
		if (ns == KEY_NS_META || ns == KEY_NS_EMPTY || ns == KEY_NS_NONE)
		{
			// For metakeys or keys without namespace just prefix the keyname with a "/"
			Key * cKey = keyNew ("/", KEY_CASCADING_NAME, KEY_END);
			keyAddName (cKey, keyName (key));
			return cKey;
		}
		else
		{
			// Skip namespace
			const char * name = keyName (key);
			const char * ptr = strchr (name, '/');
			if (!ptr)
			{
				return keyNew ("/", KEY_CASCADING_NAME, KEY_END);
			}
			else
			{
				ssize_t length = keyGetNameSize (key);
				if ((ptr - name) == (length - 1))
				{
					return keyNew ("/", KEY_CASCADING_NAME, KEY_END);
				}
				else
				{
					return keyNew (ptr, KEY_CASCADING_NAME, KEY_END);
				}
			}
		}
	}
}
Esempio n. 12
0
static resolverHandle * elektraGetResolverHandle (Plugin * handle, Key * parentKey)
{
	resolverHandles * pks = elektraPluginGetData (handle);
	switch (keyGetNamespace (parentKey))
	{
	case KEY_NS_SPEC:
		return &pks->spec;
	case KEY_NS_DIR:
		return &pks->dir;
	case KEY_NS_USER:
		return &pks->user;
	case KEY_NS_SYSTEM:
		return &pks->system;
	case KEY_NS_PROC:
	case KEY_NS_EMPTY:
	case KEY_NS_NONE:
	case KEY_NS_META:
	case KEY_NS_CASCADING:
		return 0;
	}

	return 0;
}
Esempio n. 13
0
/** @brief Set keys in an atomic and universal way.
 *
 * @pre kdbGet() must be called before kdbSet():
 *    - initially (after kdbOpen())
 *    - after conflict errors in kdbSet().
 *
 * @pre The @p returned KeySet must be a valid KeySet, e.g. constructed
 *     with ksNew().
 *
 * @pre The @p parentKey Key must be a valid Key, e.g. constructed with
 *     keyNew().
 *
 * If you pass NULL on any parameter kdbSet() will fail immediately without doing anything.
 *
 * With @p parentKey you can give an hint which part of the given keyset
 * is of interest for you. Then you promise to only modify or
 * remove keys below this key. All others would be passed back
 * as they were retrieved by kdbGet().
 *
 * @par Errors
 * If some error occurs:
 * - kdbSet() will leave the KeySet's * internal cursor on the key that generated the error.
 * - Error information will be written into the metadata of
 *   the parent key.
 * - None of the keys are actually committed in this situation, i.e. no
 *   configuration file will be modified.
 *
 * In case of errors you should present the error message to the user and let the user decide what
 * to do. Possible solutions are:
 * - remove the problematic key and use kdbSet() again (for validation or type errors)
 * - change the value of the problematic key and use kdbSet() again (for validation errors)
 * - do a kdbGet() (for conflicts, i.e. error 30) and then
 *   - set the same keyset again (in favour of what was set by this user)
 *   - drop the old keyset (in favour of what was set from another application)
 *   - merge the original, your own and the other keyset
 * - export the configuration into a file (for unresolvable errors)
 * - repeat the same kdbSet might be of limited use if the user does
 *   not explicitly request it, because temporary
 *   errors are rare and its unlikely that they fix themselves
 *   (e.g. disc full, permission problems)
 *
 * @par Optimization
 * Each key is checked with keyNeedSync() before being actually committed.
 * If no key of a backend needs to be synced
 * any affairs to backends are omitted and 0 is returned.
 *
 * @snippet kdbset.c set
 *
 * showElektraErrorDialog() and doElektraMerge() need to be implemented
 * by the user of Elektra. For doElektraMerge a 3-way merge algorithm exists in
 * libelektra-tools.
 *
 * @param handle contains internal information of @link kdbOpen() opened @endlink key database
 * @param ks a KeySet which should contain changed keys, otherwise nothing is done
 * @param parentKey is used to add warnings and set an error
 *         information. Additionally, its name is an hint which keys
 *         should be committed (it is possible that more are changed).
 *           - cascading keys (starting with /) will set the path in all namespaces
 *           - / will commit all keys
 *           - metanames will be rejected (error 104)
 *           - empty/invalid (error 105)
 * @retval 1 on success
 * @retval 0 if nothing had to be done, no changes in KDB
 * @retval -1 on failure, no changes in KDB
 * @see keyNeedSync()
 * @see ksCurrent() contains the error key
 * @see kdbOpen() and kdbGet() that must be called first
 * @see kdbClose() that must be called afterwards
 * @ingroup kdb
 */
int kdbSet (KDB * handle, KeySet * ks, Key * parentKey)
{
	elektraNamespace ns = keyGetNamespace (parentKey);
	if (ns == KEY_NS_NONE)
	{
		return -1;
	}
	Key * oldError = keyNew (keyName (parentKey), KEY_END);
	copyError (oldError, parentKey);

	if (ns == KEY_NS_META)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERRORF (104, parentKey, "metakey with name \"%s\" passed to kdbSet", keyName (parentKey));
		keyDel (oldError);
		return -1;
	}

	if (ns == KEY_NS_EMPTY)
	{
		ELEKTRA_ADD_WARNING (105, parentKey, "invalid key name passed to kdbSet");
	}

	if (!handle || !ks)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERROR (37, parentKey, "handle or ks null pointer");
		keyDel (oldError);
		return -1;
	}

	int errnosave = errno;
	Key * initialParent = keyDup (parentKey);

	ELEKTRA_LOG ("now in new kdbSet (%s) %p %zd", keyName (parentKey), (void *)handle, ksGetSize (ks));

	elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, INIT);
	elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, DEINIT);

	ELEKTRA_LOG ("after presetstorage maxonce(%s) %p %zd", keyName (parentKey), (void *)handle, ksGetSize (ks));

	Split * split = splitNew ();
	Key * errorKey = 0;

	if (splitBuildup (split, handle, parentKey) == -1)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERROR (38, parentKey, "error in splitBuildup");
		goto error;
	}

	// 1.) Search for syncbits
	int syncstate = splitDivide (split, handle, ks);
	if (syncstate == -1)
	{
		clearError (parentKey); // clear previous error to set new one
		ELEKTRA_SET_ERROR (8, parentKey, keyName (ksCurrent (ks)));
		goto error;
	}
	ELEKTRA_ASSERT (syncstate == 0 || syncstate == 1, "syncstate not 0 or 1, but %d", syncstate);

	// 2.) Search for changed sizes
	syncstate |= splitSync (split);
	ELEKTRA_ASSERT (syncstate <= 1, "syncstate not equal or below 1, but %d", syncstate);
	if (syncstate != 1)
	{
		/* No update is needed */
		keySetName (parentKey, keyName (initialParent));
		if (syncstate < 0) clearError (parentKey); // clear previous error to set new one
		if (syncstate == -1)
		{
			ELEKTRA_SET_ERROR (107, parentKey, "Assert failed: invalid namespace");
		}
		else if (syncstate < -1)
		{
			ELEKTRA_SET_ERROR (107, parentKey, keyName (split->parents[-syncstate - 2]));
		}
		keyDel (initialParent);
		splitDel (split);
		errno = errnosave;
		keyDel (oldError);
		return syncstate == 0 ? 0 : -1;
	}
	ELEKTRA_ASSERT (syncstate == 1, "syncstate not 1, but %d", syncstate);

	splitPrepare (split);

	clearError (parentKey); // clear previous error to set new one
	if (elektraSetPrepare (split, parentKey, &errorKey, handle->globalPlugins) == -1)
	{
		goto error;
	}
	else
	{
		// no error, restore old error
		copyError (parentKey, oldError);
	}
	keySetName (parentKey, keyName (initialParent));

	elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, INIT);
	elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, DEINIT);

	elektraSetCommit (split, parentKey);

	elektraGlobalSet (handle, ks, parentKey, COMMIT, INIT);
	elektraGlobalSet (handle, ks, parentKey, COMMIT, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, COMMIT, DEINIT);

	splitUpdateSize (split);

	keySetName (parentKey, keyName (initialParent));

	elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, INIT);
	elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, MAXONCE);
	elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, DEINIT);

	for (size_t i = 0; i < ks->size; ++i)
	{
		// remove all flags from all keys
		clear_bit (ks->array[i]->flags, KEY_FLAG_SYNC);
	}

	keySetName (parentKey, keyName (initialParent));
	keyDel (initialParent);
	splitDel (split);

	keyDel (oldError);
	errno = errnosave;
	return 1;

error:
	keySetName (parentKey, keyName (initialParent));

	elektraGlobalError (handle, ks, parentKey, PREROLLBACK, INIT);
	elektraGlobalError (handle, ks, parentKey, PREROLLBACK, MAXONCE);
	elektraGlobalError (handle, ks, parentKey, PREROLLBACK, DEINIT);

	elektraSetRollback (split, parentKey);

	if (errorKey)
	{
		Key * found = ksLookup (ks, errorKey, 0);
		if (!found)
		{
			ELEKTRA_ADD_WARNING (82, parentKey, keyName (errorKey));
		}
	}

	keySetName (parentKey, keyName (initialParent));

	elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, INIT);
	elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, MAXONCE);
	elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, DEINIT);

	keySetName (parentKey, keyName (initialParent));
	keyDel (initialParent);
	splitDel (split);
	errno = errnosave;
	keyDel (oldError);
	return -1;
}
Esempio n. 14
0
/**
 * @brief checks if a given Key k is in the spec namespace.
 * @retval 0 if the Key k is in the spec namespace.
 * @retval 1 if the Key k is NOT in the spec namespace.
 */
static inline int isSpecNamespace (const Key * k)
{
	return (keyGetNamespace (k) == KEY_NS_SPEC);
}
Esempio n. 15
0
/**
 * @brief Retrieve keys in an atomic and universal way.
 *
 * @pre The @p handle must be passed as returned from kdbOpen().
 *
 * @pre The @p returned KeySet must be a valid KeySet, e.g. constructed
 *     with ksNew().
 *
 * @pre The @p parentKey Key must be a valid Key, e.g. constructed with
 *     keyNew().
 *
 * If you pass NULL on any parameter kdbGet() will fail immediately without doing anything.
 *
 * The @p returned KeySet may already contain some keys, e.g. from previous
 * kdbGet() calls. The new retrieved keys will be appended using
 * ksAppendKey().
 *
 * If not done earlier kdbGet() will fully retrieve all keys under the @p parentKey
 * folder recursively (See Optimization below when it will not be done).
 *
 * @note kdbGet() might retrieve more keys than requested (that are not
 *     below parentKey). These keys must be passed to calls of kdbSet(),
 *     otherwise they will be lost. This stems from the fact that the
 *     user has the only copy of the whole configuration and backends
 *     only write configuration that was passed to them.
 *     For example, if you kdbGet() "system/mountpoint/interest"
 *     you will not only get all keys below system/mountpoint/interest,
 *     but also all keys below system/mountpoint (if system/mountpoint
 *     is a mountpoint as the name suggests, but
 *     system/mountpoint/interest is not a mountpoint).
 *     Make sure to not touch or remove keys outside the keys of interest,
 *     because others may need them!
 *
 * @par Example:
 * This example demonstrates the typical usecase within an application
 * (without error handling).
 *
 * @include kdbget.c
 *
 * When a backend fails kdbGet() will return -1 with all
 * error and warning information in the @p parentKey.
 * The parameter @p returned will not be changed.
 *
 * @par Optimization:
 * In the first run of kdbGet all requested (or more) keys are retrieved. On subsequent
 * calls only the keys are retrieved where something was changed
 * inside the key database. The other keys stay in the
 * KeySet returned as passed.
 *
 * It is your responsibility to save the original keyset if you
 * need it afterwards.
 *
 * If you want to be sure to get a fresh keyset again, you need to open a
 * second handle to the key database using kdbOpen().
 *
 * @param handle contains internal information of @link kdbOpen() opened @endlink key database
 * @param parentKey is used to add warnings and set an error
 *         information. Additionally, its name is a hint which keys
 *         should be retrieved (it is possible that more are retrieved, see Note above).
 *           - cascading keys (starting with /) will retrieve the same path in all namespaces
 *           - / will retrieve all keys
 * @param ks the (pre-initialized) KeySet returned with all keys found
 * 	will not be changed on error or if no update is required
 * @see ksLookup(), ksLookupByName() for powerful
 * 	lookups after the KeySet was retrieved
 * @see kdbOpen() which needs to be called before
 * @see kdbSet() to save the configuration afterwards and kdbClose() to
 * 	finish affairs with the key database.
 * @retval 1 if the keys were retrieved successfully
 * @retval 0 if there was no update - no changes are made to the keyset then
 * @retval -1 on failure - no changes are made to the keyset then
 * @ingroup kdb
 */
int kdbGet (KDB * handle, KeySet * ks, Key * parentKey)
{
	elektraNamespace ns = keyGetNamespace (parentKey);
	if (ns == KEY_NS_NONE)
	{
		return -1;
	}

	Key * oldError = keyNew (keyName (parentKey), KEY_END);
	copyError (oldError, parentKey);

	if (ns == KEY_NS_META)
	{
		clearError (parentKey);
		keyDel (oldError);
		ELEKTRA_SET_ERRORF (104, parentKey, "metakey with name \"%s\" passed to kdbGet", keyName (parentKey));
		return -1;
	}

	if (ns == KEY_NS_EMPTY)
	{
		ELEKTRA_ADD_WARNING (105, parentKey, "invalid key name passed to kdbGet");
	}

	int errnosave = errno;
	Key * initialParent = keyDup (parentKey);

	ELEKTRA_LOG ("now in new kdbGet (%s)", keyName (parentKey));

	Split * split = splitNew ();

	if (!handle || !ks)
	{
		clearError (parentKey);
		ELEKTRA_SET_ERROR (37, parentKey, "handle or ks null pointer");
		goto error;
	}

	elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, INIT);
	elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, MAXONCE);
	elektraGlobalGet (handle, ks, parentKey, PREGETSTORAGE, DEINIT);

	if (splitBuildup (split, handle, parentKey) == -1)
	{
		clearError (parentKey);
		ELEKTRA_SET_ERROR (38, parentKey, "error in splitBuildup");
		goto error;
	}

	// Check if a update is needed at all
	switch (elektraGetCheckUpdateNeeded (split, parentKey))
	{
	case 0: // We don't need an update so let's do nothing
		keySetName (parentKey, keyName (initialParent));
		elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT);
		elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
		elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);
		splitUpdateFileName (split, handle, parentKey);
		keyDel (initialParent);
		splitDel (split);
		errno = errnosave;
		keyDel (oldError);
		return 0;
	case -1:
		goto error;
		// otherwise fall trough
	}

	// Appoint keys (some in the bypass)
	if (splitAppoint (split, handle, ks) == -1)
	{
		clearError (parentKey);
		ELEKTRA_SET_ERROR (38, parentKey, "error in splitAppoint");
		goto error;
	}

	if (handle->globalPlugins[POSTGETSTORAGE][FOREACH] || handle->globalPlugins[POSTGETCLEANUP][FOREACH])
	{
		clearError (parentKey);
		if (elektraGetDoUpdateWithGlobalHooks (NULL, split, NULL, parentKey, initialParent, FIRST) == -1)
		{
			goto error;
		}
		else
		{
			copyError (parentKey, oldError);
		}

		keySetName (parentKey, keyName (initialParent));

		if (splitGet (split, parentKey, handle) == -1)
		{
			ELEKTRA_ADD_WARNING (108, parentKey, keyName (ksCurrent (ks)));
			// continue, because sizes are already updated
		}
		ksClear (ks);
		splitMerge (split, ks);

		clearError (parentKey);
		if (elektraGetDoUpdateWithGlobalHooks (handle, split, ks, parentKey, initialParent, LAST) == -1)
		{
			goto error;
		}
		else
		{
			copyError (parentKey, oldError);
		}
	}
	else
	{

		/* Now do the real updating,
		   but not for bypassed keys in split->size-1 */
		clearError (parentKey);
		if (elektraGetDoUpdate (split, parentKey) == -1)
		{
			goto error;
		}
		else
		{
			copyError (parentKey, oldError);
		}
		/* Now post-process the updated keysets */
		if (splitGet (split, parentKey, handle) == -1)
		{
			ELEKTRA_ADD_WARNING (108, parentKey, keyName (ksCurrent (ks)));
			// continue, because sizes are already updated
		}
		/* We are finished, now just merge everything to returned */
		ksClear (ks);

		splitMerge (split, ks);
	}

	elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, INIT);
	elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
	elektraGlobalGet (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);

	ksRewind (ks);

	keySetName (parentKey, keyName (initialParent));

	splitUpdateFileName (split, handle, parentKey);
	keyDel (initialParent);
	keyDel (oldError);
	splitDel (split);
	errno = errnosave;
	return 1;

error:
	keySetName (parentKey, keyName (initialParent));
	elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, INIT);
	elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, MAXONCE);
	elektraGlobalError (handle, ks, parentKey, POSTGETSTORAGE, DEINIT);

	keySetName (parentKey, keyName (initialParent));
	if (handle) splitUpdateFileName (split, handle, parentKey);
	keyDel (initialParent);
	keyDel (oldError);
	splitDel (split);
	errno = errnosave;
	return -1;
}
Esempio n. 16
0
int keyRel2 (const Key * key, const Key * check, KeyRelType which)
{
	if (!key || !check) return -1;
	if (!key->key || !check->key) return -1;

	Key * cKey = keyAsCascading (key);
	Key * cCheck = keyAsCascading (check);
	Key * cKeyParent = keyDup (cKey);
	keySetBaseName (cKeyParent, 0);
	if (keyName (cKeyParent)[0] == '\0') keySetName (cKeyParent, "/");
	int isBelow = 0;
	int isSilblingNephew = 0;
	isBelow = keyGetLevelsBelow (cKey, cCheck);
	if (!isBelow) isSilblingNephew = keyGetLevelsBelow (cKeyParent, cCheck);
	elektraNamespace keyNamespace = keyGetNamespace (key);
	elektraNamespace checkNamespace = keyGetNamespace (check);
	int retVal = 0;
	int bits = 0;
	for (KeyRelType type = 1; type != 0; type <<= 1)
	{
		if (type & which) ++bits;
	}
	if (bits != 1) return -1;
	switch (which)
	{
	case ELEKTRA_REL_BELOW_SAME_NS:
		if (isBelow && (keyNamespace == checkNamespace)) retVal = isBelow;
		break;
	case ELEKTRA_REL_BELOW_IGNORE_NS:
		if (isBelow) retVal = isBelow;
		break;
	case ELEKTRA_REL_BELOW_CASCADING_NS:
		if (isBelow && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = isBelow;
		break;
	case ELEKTRA_REL_DIRECT_BELOW_SAME_NS:
		if ((isBelow == 1) && (keyNamespace == checkNamespace)) retVal = 1;
		break;
	case ELEKTRA_REL_DIRECT_BELOW_IGNORE_NS:
		if (isBelow == 1) retVal = 1;
		break;
	case ELEKTRA_REL_DIRECT_BELOW_CASCADING_NS:
		if ((isBelow == 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = 1;
		break;
	case ELEKTRA_REL_SILBLING_SAME_NS:
		if ((isSilblingNephew == 1) && (keyNamespace == checkNamespace)) retVal = 1;
		break;
	case ELEKTRA_REL_SILBLING_IGNORE_NS:
		if (isSilblingNephew == 1) retVal = 1;
		break;
	case ELEKTRA_REL_SILBLING_CASCADING_NS:
		if ((isSilblingNephew == 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = 1;
		break;
	case ELEKTRA_REL_NEPHEW_SAME_NS:
		if ((isSilblingNephew > 1) && (keyNamespace == checkNamespace)) retVal = isSilblingNephew - 1;
		break;
	case ELEKTRA_REL_NEPHEW_IGNORE_NS:
		if (isSilblingNephew > 1) retVal = isSilblingNephew - 1;
		break;
	case ELEKTRA_REL_NEPHEW_CASCADING_NS:
		if ((isSilblingNephew > 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING)))
			retVal = isSilblingNephew - 1;
		break;
	default:
		retVal = -1;
		break;
	}
	keyDel (cKey);
	keyDel (cCheck);
	keyDel (cKeyParent);
	return retVal;
}
Esempio n. 17
0
static void test_keyNamespace ()
{
    Key * key;

    printf ("Test namespaces\n");

    succeed_if (keyGetNamespace (0) == KEY_NS_NONE, "null key");

    key = keyNew (0);
    succeed_if (keyGetNamespace (key) == KEY_NS_EMPTY, "empty namespace not empty");
    succeed_if (keyNameIsSystem (keyName (key)) == 0, "empty name is not system");
    succeed_if (keyIsSystem (key) == 0, "empty key is not system");
    succeed_if (keyNameIsUser (keyName (key)) == 0, "empty name is not user");
    succeed_if (keyIsUser (key) == 0, "empty key is not user");
    keyDel (key);

    key = keyNew ("", KEY_END);
    succeed_if (keyGetNamespace (key) == KEY_NS_EMPTY, "empty namespace not empty");
    succeed_if (keyNameIsSystem (keyName (key)) == 0, "empty name is not system");
    succeed_if (keyIsSystem (key) == 0, "empty key is not system");
    succeed_if (keyNameIsUser (keyName (key)) == 0, "empty name is not user");
    succeed_if (keyIsUser (key) == 0, "empty key is not user");
    keyDel (key);

    key = keyNew ("user", KEY_END);
    succeed_if (keyGetNamespace (key) == KEY_NS_USER, "user namespace not KEY_NS_USER");
    succeed_if (keyNameIsSystem (keyName (key)) == 0, "user name is not system");
    succeed_if (keyIsSystem (key) == 0, "user key is not system");
    succeed_if (keyNameIsUser (keyName (key)) == 1, "user name is not user");
    succeed_if (keyIsUser (key) == 1, "user key is not user");
    keyDel (key);

    key = keyNew ("user/key", KEY_END);
    succeed_if (keyGetNamespace (key) == KEY_NS_USER, "user namespace not KEY_NS_USER");
    succeed_if (keyNameIsSystem (keyName (key)) == 0, "user name is not system");
    succeed_if (keyIsSystem (key) == 0, "user key is not system");
    succeed_if (keyNameIsUser (keyName (key)) == 1, "user name is not user");
    succeed_if (keyIsUser (key) == 1, "user key is not user");
    keyDel (key);

    key = keyNew ("user:owner/key", KEY_END);
    succeed_if (keyGetNamespace (key) == KEY_NS_USER, "user namespace not KEY_NS_USER");
    succeed_if (keyNameIsSystem (keyName (key)) == 0, "user name is not system");
    succeed_if (keyIsSystem (key) == 0, "user key is not system");
    succeed_if (keyNameIsUser (keyName (key)) == 1, "user name is not user");
    succeed_if (keyIsUser (key) == 1, "user key is not user");
    keyDel (key);

    key = keyNew ("system", KEY_END);
    succeed_if (keyGetNamespace (key) == KEY_NS_SYSTEM, "system namespace not KEY_NS_SYSTEM");
    succeed_if (keyNameIsSystem (keyName (key)) == 1, "system name is not system");
    succeed_if (keyIsSystem (key) == 1, "system key is not system");
    succeed_if (keyNameIsUser (keyName (key)) == 0, "system name is not system");
    succeed_if (keyIsUser (key) == 0, "system key is not system");
    keyDel (key);

    key = keyNew ("system/key", KEY_END);
    succeed_if (keyGetNamespace (key) == KEY_NS_SYSTEM, "system namespace not KEY_NS_SYSTEM");
    succeed_if (keyNameIsSystem (keyName (key)) == 1, "system name is not system");
    succeed_if (keyIsSystem (key) == 1, "system key is not system");
    succeed_if (keyNameIsUser (keyName (key)) == 0, "system name is not system");
    succeed_if (keyIsUser (key) == 0, "system key is not system");
    keyDel (key);

    key = keyNew ("spec/key", KEY_END);
    succeed_if (keyGetNamespace (key) == KEY_NS_SPEC, "Spec namespace not KEY_NS_SPEC");
    succeed_if (keyNameIsSpec (keyName (key)) == 1, "Spec name is not Spec");
    succeed_if (keyIsSpec (key) == 1, "Spec key is not Spec");
    succeed_if (keyNameIsUser (keyName (key)) == 0, "Spec name is not Spec");
    succeed_if (keyIsUser (key) == 0, "Spec key is not Spec");
    keyDel (key);

    key = keyNew ("/key", KEY_CASCADING_NAME, KEY_END);
    succeed_if (keyGetNamespace (key) == KEY_NS_CASCADING, "not correct namespace");
    keyDel (key);

    key = keyNew ("type", KEY_META_NAME, KEY_END);
    succeed_if (keyGetNamespace (key) == KEY_NS_META, "not correct namespace");
    keyDel (key);
}
Esempio n. 18
0
/** Add sync bits everywhere keys were removed/added.
 *
 * - checks if the size of a previous kdbGet() is unchanged.
 * - checks if in correct state (kdbGet() needs to be executed before)
 *
 * Only elektraSplitDivide() together with this function can really decide
 * if sync is needed or not.
 *
 * @pre split needs to be processed with elektraSplitDivide() before.
 *
 * @retval 0 if kdbSet() is not needed
 * @retval 1 if kdbSet() is needed
 * @retval -1 on wrong keys (also has assert, should not happen)
 * @retval -2 wrong spec state: kdbGet() was not executed before
 * @retval -3 wrong dir state: kdbGet() was not executed before
 * @retval -4 wrong user state: kdbGet() was not executed before
 * @retval -5 wrong system state: kdbGet() was not executed before
 * @pre user/system was split before.
 * @param split the split object to work with
 * @ingroup split
 *
**/
int elektraSplitSync (Split * split)
{
	int needsSync = 0;

	for (size_t i = 0; i < split->size; ++i)
	{
		// first check for wrong states etc.
		switch (keyGetNamespace (split->parents[i]))
		{
		case KEY_NS_SPEC:
			// Check if we are in correct state
			if (split->handles[i]->specsize == -1)
			{
				return -(int)i - 2;
			}
			/* Check for spec keyset for removed keys */
			if (split->handles[i]->specsize != ksGetSize (split->keysets[i]))
			{
				set_bit (split->syncbits[i], SPLIT_FLAG_SYNC);
				needsSync = 1;
			}
			break;
		case KEY_NS_DIR:
			// Check if we are in correct state
			if (split->handles[i]->dirsize == -1)
			{
				return -(int)i - 2;
			}
			/* Check for dir keyset for removed keys */
			if (split->handles[i]->dirsize != ksGetSize (split->keysets[i]))
			{
				set_bit (split->syncbits[i], SPLIT_FLAG_SYNC);
				needsSync = 1;
			}
			break;
		case KEY_NS_USER:
			// Check if we are in correct state
			if (split->handles[i]->usersize == -1)
			{
				return -(int)i - 2;
			}
			/* Check for user keyset for removed keys */
			if (split->handles[i]->usersize != ksGetSize (split->keysets[i]))
			{
				set_bit (split->syncbits[i], SPLIT_FLAG_SYNC);
				needsSync = 1;
			}
			break;
		case KEY_NS_SYSTEM:
			// Check if we are in correct state
			if (split->handles[i]->systemsize == -1)
			{
				return -(int)i - 2;
			}
			/* Check for system keyset for removed keys */
			if (split->handles[i]->systemsize != ksGetSize (split->keysets[i]))
			{
				set_bit (split->syncbits[i], SPLIT_FLAG_SYNC);
				needsSync = 1;
			}
			break;
		case KEY_NS_PROC:
		case KEY_NS_EMPTY:
		case KEY_NS_META:
		case KEY_NS_CASCADING:
		case KEY_NS_NONE:
			ELEKTRA_ASSERT (0 && "Got keys that should not be here");
			return -1;
		}
	}

	return needsSync;
}