Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

[new-backend] spec hook #4495

Merged
merged 11 commits into from
Sep 30, 2022
5 changes: 5 additions & 0 deletions doc/news/_preparation_next_release.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ The following section lists news about the [plugins](https://www.libelektra.org/
- <<TODO>>
- <<TODO>>

### spec

- Partial rewrite for spec hook _(Maximilian Irlinger @atmaxinger)_
- <<TODO>>

### <<Plugin3>>

- <<TODO>>
Expand Down
16 changes: 12 additions & 4 deletions src/include/kdbprivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ typedef int (*kdbCommitPtr) (Plugin * handle, KeySet * returned, Key * parentKey

typedef int (*kdbHookGoptsGetPtr) (Plugin * handle, KeySet * returned, Key * parentKey);

typedef int (*kdbHookSpecCopyPtr) (Plugin * handle, KeySet * returned, Key * parentKey, bool isKdbGet);
typedef int (*kdbHookSpecRemovePtr) (Plugin * handle, KeySet * returned, Key * parentKey);

typedef Plugin * (*OpenMapper) (const char *, const char *, KeySet *);
typedef int (*CloseMapper) (Plugin *);

Expand Down Expand Up @@ -321,8 +324,6 @@ struct _KeySet
#endif
};

typedef struct _Hooks Hooks;

/**
* The access point to the key database.
*
Expand Down Expand Up @@ -371,9 +372,16 @@ struct _KDB
{
struct
{
struct _Plugin* plugin;
kdbHookGoptsGetPtr kdbHookGoptsGet;
struct _Plugin * plugin;
kdbHookGoptsGetPtr get;
} gopts;

struct
{
struct _Plugin * plugin;
kdbHookSpecCopyPtr copy;
kdbHookSpecRemovePtr remove;
} spec;
} hooks;
};

Expand Down
45 changes: 44 additions & 1 deletion src/libs/elektra/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ void freeHooks (KDB * kdb, Key * errorKey)
if (kdb->hooks.gopts.plugin != NULL)
{
elektraPluginClose (kdb->hooks.gopts.plugin, errorKey);
kdb->hooks.gopts.plugin = NULL;
kdb->hooks.gopts.get = NULL;
}

if (kdb->hooks.spec.plugin != NULL)
{
elektraPluginClose (kdb->hooks.spec.plugin, errorKey);
kdb->hooks.spec.plugin = NULL;
kdb->hooks.spec.copy = NULL;
kdb->hooks.spec.remove = NULL;
}
}

Expand All @@ -43,7 +53,27 @@ static int initHooksGopts (KDB * kdb, Plugin * plugin, Key * errorKey)

kdb->hooks.gopts.plugin = plugin;

if ((kdb->hooks.gopts.kdbHookGoptsGet = (kdbHookGoptsGetPtr) getFunction (plugin, "hook/gopts/get", errorKey)) == NULL)
if ((kdb->hooks.gopts.get = (kdbHookGoptsGetPtr) getFunction (plugin, "hook/gopts/get", errorKey)) == NULL)
{
return -1;
}

return 0;
}

static int initHooksSpec (KDB * kdb, Plugin * plugin, Key * errorKey)
{
if (!plugin)
{
return -1;
}

kdb->hooks.spec.plugin = plugin;

kdb->hooks.spec.copy = (kdbHookSpecCopyPtr) getFunction (plugin, "hook/spec/copy", errorKey);
kdb->hooks.spec.remove = (kdbHookSpecRemovePtr) getFunction (plugin, "hook/spec/remove", errorKey);

if (kdb->hooks.spec.copy == NULL || kdb->hooks.spec.remove == NULL)
{
return -1;
}
Expand Down Expand Up @@ -138,6 +168,13 @@ static bool isGoptsEnabledByContract (const KeySet * contract)
return isEnabled;
}

static bool isSpecEnabledByConfig (const KeySet * config)
{
// TODO: check for system:/elektra/hook/spec/enabled or system:/elektra/hook/spec/disabled or something else ... TBD
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to solve this before merging this PR. Can be added later at any time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

// See this discussion: https://github.com/ElektraInitiative/libelektra/pull/4471#discussion_r974129215
return true;
}

/**
* Initializes the hooks stored in the passed KDB handle.
* If the handle already contains initialized hooks, they will be reinitialized, including unloading and loading of their plugins.
Expand Down Expand Up @@ -169,6 +206,12 @@ int initHooks (KDB * kdb, const KeySet * config, KeySet * modules, const KeySet
goto error;
}

if (isSpecEnabledByConfig (config) &&
initHooksSpec (kdb, loadPlugin ("spec", kdb->global, modules, contract, errorKey), errorKey) != 0)
{
goto error;
}

if (!existingError)
{
// remove dummy error again
Expand Down
53 changes: 42 additions & 11 deletions src/libs/elektra/kdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1865,28 +1865,30 @@ int kdbGet (KDB * handle, KeySet * ks, Key * parentKey)
goto error;
}

/* TODO (kodebach): implement actual steps with new global plugins
// Step 14: run spec plugin
if (!specGet (dataKs, cascadingParent))
{
clear_bit (parentKey->flags, KEY_LOCK_NAME | KEY_LOCK_VALUE);
goto error;
}
clear_bit (parentKey->flags, KEY_LOCK_NAME | KEY_LOCK_VALUE);
*/

// Step 13: run gopts (if enabled)
keyCopy (parentKey, initialParent, KEY_CP_NAME);
keySetNamespace (parentKey, KEY_NS_CASCADING);

if (goptsActive && !handle->hooks.gopts.kdbHookGoptsGet (handle->hooks.gopts.plugin, dataKs, parentKey))
if (goptsActive && !handle->hooks.gopts.get (handle->hooks.gopts.plugin, dataKs, parentKey))
{
clear_bit (parentKey->flags, KEY_LOCK_NAME | KEY_LOCK_VALUE);
goto error;
}

keySetNamespace (parentKey, keyGetNamespace (initialParent));

// Step 14: run spec plugin
if (handle->hooks.spec.plugin && handle->hooks.spec.copy (handle->hooks.spec.plugin, dataKs, parentKey, true) == -1)
{
clear_bit (parentKey->flags, KEY_LOCK_NAME | KEY_LOCK_VALUE);
goto error;
}
clear_bit (parentKey->flags, KEY_LOCK_NAME | KEY_LOCK_VALUE);

// TODO (atmaxinger): should we have a default:/ backend?
Key * defaultCutpoint = keyNew ("default:/", KEY_END);
KeySet * defaults = ksCut (dataKs, defaultCutpoint);

// Step 15: split dataKs for poststorage phase
// FIXME (kodebach): handle proc:/ keys
if (!backendsDivide (backends, dataKs))
Expand Down Expand Up @@ -1917,6 +1919,11 @@ int kdbGet (KDB * handle, KeySet * ks, Key * parentKey)
// Step 18: merge data into ks and return
backendsMerge (backends, ks);

// TODO (atmaxinger): should we have a default:/ backend?
ksAppend (ks, defaults);
ksDel (defaults);
keyDel (defaultCutpoint);

// Step 19: update cache
// FIXME (kodebach): implement cache

Expand Down Expand Up @@ -2320,6 +2327,11 @@ int kdbSet (KDB * handle, KeySet * ks, Key * parentKey)
goto error;
}

if (handle->hooks.spec.plugin && handle->hooks.spec.copy (handle->hooks.spec.plugin, ks, parentKey, false) == -1)
{
goto error;
}

// TODO (kodebach) [opt]: merge steps 4-6
// By merging steps 4-6, we MAY be able to avoid copying keys from unchanged and read-only backends.

Expand Down Expand Up @@ -2407,6 +2419,25 @@ int kdbSet (KDB * handle, KeySet * ks, Key * parentKey)
}
*/

// Step 8: merge data from all backends (for spec removal)
ksClear (setKs);
backendsMerge (backends, setKs);

// Step 9: run the spec plugin to remove copied metadata
if (handle->hooks.spec.plugin && handle->hooks.spec.remove (handle->hooks.spec.plugin, setKs, parentKey) == -1)
{
goto rollback;
}

// Step 10: split setKs for remaining phases
if (!backendsDivide (backends, setKs))
{
ELEKTRA_SET_INTERNAL_ERROR (parentKey,
"Couldn't divide keys into mountpoints after spec removal. Please report this bug at "
"https://issues.libelektra.org.");
goto rollback;
}

// Step 11a: run storage phase
if (!runSetPhase (backends, parentKey, KDB_SET_PHASE_STORAGE, false, KDB_SET_FN_SET))
{
Expand Down
1 change: 0 additions & 1 deletion src/plugins/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
- infos/licence = BSD
- infos/needs =
- infos/provides = check apply
- infos/placements = postgetstorage presetstorage
- infos/status = recommended productive nodep configurable global unfinished
- infos/description = allows to give specifications for keys

Expand Down
128 changes: 38 additions & 90 deletions src/plugins/spec/spec.c
Original file line number Diff line number Diff line change
Expand Up @@ -902,28 +902,12 @@ static int processSpecKey (Key * specKey, Key * parentKey, KeySet * ks, const Co
return ret;
}

int elektraSpecGet (Plugin * handle, KeySet * returned, Key * parentKey)
int elektraSpecCopy (Plugin * handle, KeySet * returned, Key * parentKey, bool isKdbGet)
{
if (!elektraStrCmp (keyName (parentKey), "system:/elektra/modules/spec"))
{
KeySet * contract =
ksNew (30, keyNew ("system:/elektra/modules/spec", KEY_VALUE, "spec plugin waits for your orders", KEY_END),
keyNew ("system:/elektra/modules/spec/exports", KEY_END),
keyNew ("system:/elektra/modules/spec/exports/get", KEY_FUNC, elektraSpecGet, KEY_END),
keyNew ("system:/elektra/modules/spec/exports/set", KEY_FUNC, elektraSpecSet, KEY_END),
#include ELEKTRA_README
keyNew ("system:/elektra/modules/spec/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
ksAppend (returned, contract);
ksDel (contract);

return ELEKTRA_PLUGIN_STATUS_SUCCESS; // success
}

// parse configuration
ConflictHandling ch;

KeySet * config = elektraPluginGetConfig (handle);
parseConfig (config, &ch, CONFIG_BASE_NAME_GET);
parseConfig (config, &ch, isKdbGet ? CONFIG_BASE_NAME_GET : CONFIG_BASE_NAME_SET);

// build spec
KeySet * specKS = ksNew (0, KS_END);
Expand Down Expand Up @@ -964,13 +948,23 @@ int elektraSpecGet (Plugin * handle, KeySet * returned, Key * parentKey)
ksRewind (specKS);
while ((specKey = ksNext (specKS)) != NULL)
{
if (processSpecKey (specKey, parentKey, ks, &ch, true) != 0)
if (processSpecKey (specKey, parentKey, ks, &ch, isKdbGet) != 0)
{
ret = ELEKTRA_PLUGIN_STATUS_ERROR;
}

if (!isKdbGet)
{
keySetMeta (specKey, "internal/spec/array/validated", NULL);

if (keyGetMeta (specKey, "internal/spec/array") == NULL && keyGetMeta (specKey, "internal/spec/remove") == NULL)
{
ksAppendKey (returned, specKey);
}
}
}

if (processAllConflicts (specKey, ks, parentKey, &ch, true) != 0)
if (processAllConflicts (specKey, ks, parentKey, &ch, isKdbGet) != 0)
{
ret = ELEKTRA_PLUGIN_STATUS_ERROR;
}
Expand All @@ -988,72 +982,10 @@ int elektraSpecGet (Plugin * handle, KeySet * returned, Key * parentKey)
return ret;
}

int elektraSpecSet (Plugin * handle, KeySet * returned, Key * parentKey)
int elektraSpecRemove (ELEKTRA_UNUSED Plugin * handle, KeySet * returned, Key * parentKey)
{
// parse configuration
ConflictHandling ch;

KeySet * config = elektraPluginGetConfig (handle);
parseConfig (config, &ch, CONFIG_BASE_NAME_SET);

// build spec
KeySet * specKS = ksNew (0, KS_END);

Key * cur;
ksRewind (returned);
while ((cur = ksNext (returned)) != NULL)
{
if (keyGetNamespace (cur) == KEY_NS_SPEC)
{
if (isArraySpec (cur))
{
KeySet * specs = instantiateArraySpec (returned, cur, parentKey, ch.member);
ksAppend (specKS, specs);
ksDel (specs);
}

ksAppendKey (specKS, cur);
}
}

int ret = ELEKTRA_PLUGIN_STATUS_SUCCESS;
if (keyGetMeta (parentKey, "internal/spec/error") != NULL)
{
ret = ELEKTRA_PLUGIN_STATUS_ERROR;
}

// remove spec namespace from returned
Key * specParent = keyNew ("spec:/", KEY_END);
ksDel (ksCut (returned, specParent));
keyDel (specParent);

// extract other namespaces
KeySet * ks = ksCut (returned, parentKey);

// do actual work
Key * specKey;
ksRewind (specKS);
while ((specKey = ksNext (specKS)) != NULL)
{
if (processSpecKey (specKey, parentKey, ks, &ch, false) != 0)
{
ret = ELEKTRA_PLUGIN_STATUS_ERROR;
}

keySetMeta (specKey, "internal/spec/array/validated", NULL);

if (keyGetMeta (specKey, "internal/spec/array") == NULL && keyGetMeta (specKey, "internal/spec/remove") == NULL)
{
ksAppendKey (returned, specKey);
}
}

if (processAllConflicts (specKey, ks, parentKey, &ch, false) != 0)
{
ret = ELEKTRA_PLUGIN_STATUS_ERROR;
}

// reconstruct KeySet
KeySet * ks = returned;
ksRewind (ks);
while ((cur = ksNext (ks)) != NULL)
{
Expand All @@ -1070,21 +1002,37 @@ int elektraSpecSet (Plugin * handle, KeySet * returned, Key * parentKey)
}
}

// cleanup
ksDel (ks);
ksDel (specKS);

keySetMeta (parentKey, "internal/spec/error", NULL);

return ret;
return ELEKTRA_PLUGIN_STATUS_SUCCESS;
}

int elektraSpecGet (ELEKTRA_UNUSED Plugin * handle, KeySet * returned, Key * parentKey)
{
if (!elektraStrCmp (keyName (parentKey), "system:/elektra/modules/spec"))
{
KeySet * contract =
ksNew (30, keyNew ("system:/elektra/modules/spec", KEY_VALUE, "spec plugin waits for your orders", KEY_END),
keyNew ("system:/elektra/modules/spec/exports", KEY_END),
keyNew ("system:/elektra/modules/spec/exports/get", KEY_FUNC, elektraSpecGet, KEY_END),
keyNew ("system:/elektra/modules/spec/exports/hook/spec/copy", KEY_FUNC, elektraSpecCopy, KEY_END),
keyNew ("system:/elektra/modules/spec/exports/hook/spec/remove", KEY_FUNC, elektraSpecRemove, KEY_END),
#include ELEKTRA_README
keyNew ("system:/elektra/modules/spec/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
ksAppend (returned, contract);
ksDel (contract);

return ELEKTRA_PLUGIN_STATUS_SUCCESS; // success
}

return ELEKTRA_PLUGIN_STATUS_SUCCESS;
}

Plugin * ELEKTRA_PLUGIN_EXPORT
{
// clang-format off
return elektraPluginExport ("spec",
ELEKTRA_PLUGIN_GET, &elektraSpecGet,
ELEKTRA_PLUGIN_SET, &elektraSpecSet,
ELEKTRA_PLUGIN_END);
}

Loading