static void test_mmap_ksCopy (const char * tmpFile) { Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); KeySet * conf = ksNew (0, KS_END); PLUGIN_OPEN ("mmapstorage"); KeySet * ks = simpleTestKeySet (); succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful"); succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful"); succeed_if ((ks->flags & KS_FLAG_MMAP_ARRAY) == KS_FLAG_MMAP_ARRAY, "KeySet array not in mmap"); KeySet * copyKs = ksNew (0, KS_END); if (ksCopy (copyKs, ks) == 1) { compare_keyset (copyKs, ks); compare_keyset (ks, copyKs); } else { yield_error ("ksCopy failed"); } ksDel (copyKs); keyDel (parentKey); ksDel (ks); PLUGIN_CLOSE (); }
static void test_ksCopy (const size_t storagePlugin, const char * tmpFile) { Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); open_storage_plugin (storagePlugin); Plugin * plugin = plugins[storagePlugin]; KeySet * ks = simpleTestKeySet (); succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful"); succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful"); KeySet * copyKs = ksNew (0, KS_END); if (ksCopy (copyKs, ks) == 1) { compare_keyset (copyKs, ks); compare_keyset (ks, copyKs); } else { yield_error ("ksCopy failed"); } ksDel (copyKs); keyDel (parentKey); ksDel (ks); closeStoragePlugin (storagePlugin); }
static void test_mmap_ks_copy_with_meta (const char * tmpFile) { Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); KeySet * conf = ksNew (0, KS_END); PLUGIN_OPEN ("mmapstorage"); KeySet * ks = metaTestKeySet (); succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful"); ksDel (ks); KeySet * returned = ksNew (0, KS_END); succeed_if (plugin->kdbGet (plugin, returned, parentKey) == 1, "kdbGet was not successful"); KeySet * expected = metaTestKeySet (); compare_keyset (expected, returned); KeySet * copiedKs = ksNew (0, KS_END); ksCopy (copiedKs, returned); compare_keyset (expected, copiedKs); ksDel (copiedKs); ksDel (expected); ksDel (returned); keyDel (parentKey); PLUGIN_CLOSE (); }
/** Call a plugin's function in a child process * * This will wrap all the required information to execute the given * command in a keyset and send it over to the child process. Then * it waits for the child process's answer and copies the result * back into the original plugin keyset and plugin key. * * Typically called like * @code int elektraPluginSet (Plugin * handle, KeySet * returned, Key * parentKey) { ElektraPluginProcess * pp = elektraPluginGetData (handle); if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_SET, returned, parentKey); // actual plugin functionality to be executed in a child process return ELEKTRA_PLUGIN_STATUS_SUCCESS; } * @endcode * * @param pp the data structure containing the plugin's process information * @param command the plugin command that should be executed, e.g. ELEKTRA_PLUGINPROCESS_GET * @param originalKeySet the original key set that the parent process receives * @param key the original key the parent process receives * @retval ELEKTRA_PLUGIN_STATUS_ERROR if the child process communication failed * @retval the called plugin's return value otherwise * @see elektraPluginProcessIsParent for checking if we are in the parent or child process * @ingroup processplugin **/ int elektraPluginProcessSend (const ElektraPluginProcess * pp, pluginprocess_t command, KeySet * originalKeySet, Key * key) { // Ensure we have a keyset when trying to call GET SET and ERROR if ((command == ELEKTRA_PLUGINPROCESS_GET || command == ELEKTRA_PLUGINPROCESS_SET || command == ELEKTRA_PLUGINPROCESS_ERROR) && originalKeySet == NULL) { ELEKTRA_SET_ERROR (191, key, "originalKeySet has to exist when calling GET SET and ERROR via pluginprocess; but it is NULL"); return ELEKTRA_PLUGIN_STATUS_ERROR; } // Construct the command set that controls the pluginprocess communication KeySet * commandKeySet = ksNew (6, KS_END); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/parent/name", KEY_VALUE, keyName (key), KEY_END)); Key * parentKey = keyDup (key); keySetName (parentKey, "/pluginprocess/parent"); ksAppendKey (commandKeySet, parentKey); char * commandStr = longToStr (command); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/command", KEY_VALUE, commandStr, KEY_END)); elektraFree (commandStr); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/version", KEY_VALUE, "1", KEY_END)); // Some plugin functions don't use keysets, in that case don't send any actual payload, signal via flag KeySet * keySet = originalKeySet != NULL ? ksDup (originalKeySet) : NULL; char * payloadSizeStr = longToStr (ksGetSize (originalKeySet)); ksAppendKey (commandKeySet, keyNew ("/pluginprocess/payload/size", KEY_VALUE, originalKeySet == NULL ? "-1" : payloadSizeStr, KEY_END)); elektraFree (payloadSizeStr); // Serialize, currently statically use dump as our default format, this already writes everything out to the pipe ELEKTRA_LOG ("Parent: Sending data to issue command %u it through pipe %s", command, keyString (pp->parentCommandPipeKey)); elektraInvoke2Args (pp->dump, "set", commandKeySet, pp->parentCommandPipeKey); if (keySet != NULL) { ELEKTRA_LOG ("Parent: Sending the payload keyset with %zd keys through the pipe %s", ksGetSize (keySet), keyString (pp->parentPayloadPipeKey)); elektraInvoke2Args (pp->dump, "set", keySet, pp->parentPayloadPipeKey); } // Deserialize ELEKTRA_LOG_DEBUG ("Parent: Waiting for the result now on pipe %s", keyString (pp->childCommandPipeKey)); elektraInvoke2Args (pp->dump, "get", commandKeySet, pp->childCommandPipeKey); if (keySet != NULL) { // clear the keyset before to avoid memleaks caused by dump char * endPtr; int prevErrno = errno; errno = 0; long payloadSize = strtol (keyString (ksLookupByName (commandKeySet, "/pluginprocess/payload/size", KDB_O_NONE)), &endPtr, 10); // in case the payload size fails to be transferred, that it shouldn't, we simply assume the previous size if (*endPtr != '\0' || errno == ERANGE || payloadSize < 0) payloadSize = ksGetSize (keySet); errno = prevErrno; ksDel (keySet); keySet = ksNew (payloadSize, KS_END); elektraInvoke2Args (pp->dump, "get", keySet, pp->childPayloadPipeKey); ELEKTRA_LOG ("Parent: We received %zd keys in return", ksGetSize (keySet)); } // Bring everything back in order by removing our process-related keys Key * parentDeserializedKey = ksLookupByName (commandKeySet, "/pluginprocess/parent", KDB_O_NONE); Key * resultKey = ksLookupByName (commandKeySet, "/pluginprocess/result", KDB_O_NONE); // Parse the result value char * endPtr; int prevErrno = errno; errno = 0; long lresult = strtol (keyString (resultKey), &endPtr, 10); if (*endPtr != '\0' || errno == ERANGE || lresult > INT_MAX || lresult < INT_MIN) { ELEKTRA_SET_ERRORF (191, key, "Received invalid return code or no KeySet: %s", keyString (resultKey)); lresult = ELEKTRA_PLUGIN_STATUS_ERROR; } else // Copy everything back into the actual keysets { Key * parentKeyInOriginalKeySet = keySet != NULL ? ksLookup (originalKeySet, key, KDB_O_NONE) : NULL; // maybe there are just 2 keys with the same name, can happen in theory, so compare memory int parentKeyExistsInOriginalKeySet = parentKeyInOriginalKeySet == key; // if the child added the parent key to the keyset pop it from the keyset // then reinsert key after we copied the data and delete this serialized copy Key * parentKeyInKeySet = keySet != NULL ? ksLookup (keySet, key, KDB_O_POP) : NULL; int childAddedParentKey = parentKeyInKeySet != NULL; // Unfortunately we can't use keyCopy here as ksAppendKey locks it so it will fail // This is the case if the parent key is also contained in the originalKeySet / has been appended // As an invariant we assume plugins don't change the parent key's name during a plugin call // This would interfere with keyset memberships keySetString (key, keyString (parentDeserializedKey)); // Clear metadata before, we allow children to modify it keyRewindMeta (key); const Key * currentMeta; while ((currentMeta = keyNextMeta (key)) != NULL) { keySetMeta (key, keyName (currentMeta), 0); } keyCopyAllMeta (key, parentDeserializedKey); if (childAddedParentKey) keyCopyAllMeta (key, parentKeyInKeySet); if (keySet != NULL) { // in case originalKeySet contains key this would make it stuck // thus remove it here and re-add it afterwards if (parentKeyExistsInOriginalKeySet) ksLookup (originalKeySet, parentKeyInOriginalKeySet, KDB_O_POP); ksCopy (originalKeySet, keySet); if (parentKeyExistsInOriginalKeySet || childAddedParentKey) ksAppendKey (originalKeySet, key); if (childAddedParentKey) keyDel (parentKeyInKeySet); } } errno = prevErrno; // Command finished, cleanup the remaining memory now ksDel (commandKeySet); if (keySet != NULL) ksDel (keySet); return lresult; // Safe, we had a bound check before, and plugins should return values in the int range }
static void test_ksResize () { int i; KeySet * ks = 0; KeySet * copy = ksNew (0, KS_END); char name[NAME_SIZE]; ks = ksNew (20, keyNew ("user/test01", KEY_END), keyNew ("user/test02", KEY_END), keyNew ("user/test03", KEY_END), keyNew ("user/test04", KEY_END), keyNew ("user/test05", KEY_END), keyNew ("user/test11", KEY_END), keyNew ("user/test12", KEY_END), keyNew ("user/test13", KEY_END), keyNew ("user/test14", KEY_END), keyNew ("user/test15", KEY_END), keyNew ("user/test21", KEY_END), keyNew ("user/test22", KEY_END), keyNew ("user/test23", KEY_END), keyNew ("user/test24", KEY_END), keyNew ("user/test25", KEY_END), keyNew ("user/test31", KEY_END), keyNew ("user/test32", KEY_END), keyNew ("user/test33", KEY_END), keyNew ("user/test34", KEY_END), keyNew ("user/test35", KEY_END), KS_END); succeed_if (ksGetAlloc (ks) == 20, "20 keys with alloc 20 should work"); ksDel (ks); printf ("Test resize of keyset\n"); exit_if_fail ((ks = ksNew (0, KS_END)) != 0, "could not create new keyset"); for (i = 0; i < 100; i++) { snprintf (name, NAME_SIZE, "user/test%d", i); ksAppendKey (ks, keyNew (name, KEY_END)); if (i >= 63) { succeed_if (ksGetAlloc (ks) == 127, "allocation size wrong"); } else if (i >= 31) { succeed_if (ksGetAlloc (ks) == 63, "allocation size wrong"); } else if (i >= 15) { succeed_if (ksGetAlloc (ks) == 31, "allocation size wrong"); } else if (i >= 0) { succeed_if (ksGetAlloc (ks) == 15, "allocation size wrong"); } } succeed_if (ksGetSize (ks) == 100, "could not append 100 keys"); succeed_if (ksGetAlloc (ks) == 127, "allocation size wrong"); for (i = 100; i >= 0; i--) { keyDel (ksPop (ks)); if (i >= 64) { succeed_if (ksGetAlloc (ks) == 127, "allocation size wrong"); } else if (i >= 32) { succeed_if (ksGetAlloc (ks) == 63, "allocation size wrong"); } else if (i >= 16) { succeed_if (ksGetAlloc (ks) == 31, "allocation size wrong"); } else if (i >= 0) { succeed_if (ksGetAlloc (ks) == 15, "allocation size wrong"); } } succeed_if (ksGetSize (ks) == 0, "could not pop 100 keys"); succeed_if (ksGetAlloc (ks) == 15, "allocation size wrong"); ksDel (ks); exit_if_fail ((ks = ksNew (0, KS_END)) != 0, "could not create new keyset"); ksResize (ks, 100); succeed_if (ksGetAlloc (ks) == 100, "allocation size wrong"); for (i = 0; i < 100; i++) { snprintf (name, NAME_SIZE, "user/test%d", i); ksAppendKey (ks, keyNew (name, KEY_END)); succeed_if (ksGetAlloc (ks) == 100, "allocation size wrong"); } succeed_if (ksGetSize (ks) == 100, "could not append 100 keys"); succeed_if (ksGetAlloc (ks) == 100, "allocation size wrong"); ksDel (ks); ks = #include "data_keyset.c" succeed_if (ksGetSize (ks) == 102, "Problem loading keyset with 102 keys"); succeed_if (ksGetAlloc (ks) == 102, "alloc size wrong"); ksCopy (copy, ks); succeed_if (ksGetSize (copy) == 102, "Problem copy keyset with 102 keys"); succeed_if (ksGetAlloc (copy) == 127, "alloc of copy size wrong"); compare_keyset (copy, ks); ksClear (copy); // useless, just test for double free ksCopy (copy, ks); succeed_if (ksGetSize (copy) == 102, "Problem copy keyset with 102 keys"); succeed_if (ksGetAlloc (copy) == 127, "alloc of copy size wrong"); compare_keyset (copy, ks); ksDel (copy); ksDel (ks); }