//! [Basic Copy All] void l(Key *k) { // receive c keyCopyAllMeta(k, c); // the caller will see the changed key k // with all the metadata from c }
int elektraGlobMatch (Key * key, const Key * match, const char * globFlags) { char * tokenList = elektraStrDup (globFlags); char delimiter[] = ","; char * flagName = strtok (tokenList, delimiter); int flags = 0; while (flagName != NULL) { for (size_t i = 0; i < sizeof (flagMaps) / sizeof (struct GlobFlagMap); i++) { if (!strcmp (flagName, flagMaps[i].name)) { flags |= flagMaps[i].flag; } } flagName = strtok (NULL, delimiter); } free (tokenList); if (!fnmatch (keyString (match), keyName (key), flags)) { keyCopyAllMeta (key, match); return 1; } return 0; }
//! [Shared Meta All] void o(KeySet *ks) { Key *current; Key *shared = keyNew (0); keySetMeta(shared, "shared1", "this meta data should be shared among many keys"); keySetMeta(shared, "shared2", "this meta data should be shared among many keys also"); keySetMeta(shared, "shared3", "this meta data should be shared among many keys too"); ksRewind(ks); while ((current = ksNext(ks)) != 0) { if (needsSharedData(current)) keyCopyAllMeta(current, shared); } keyDel(shared); }
/** 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 }
void test_copyall() { printf ("Test key meta copy all\n"); Key *key1; Key *key2; succeed_if (key1 = keyNew(0), "could not create key"); succeed_if (key2 = keyNew(0), "could not create key"); succeed_if (keyCopyAllMeta(key2, key1) == 0, "could not do anything"); succeed_if (keyValue(keyGetMeta(key2, "nonexist")) == 0, "should not be there"); keyDel (key1); keyDel (key2); succeed_if (key1 = keyNew(0), "could not create key"); succeed_if (key2 = keyNew(0), "could not create key"); succeed_if (keySetMeta(key1, "mymeta", "a longer meta value") == sizeof("a longer meta value"), "could not set meta value"); succeed_if (keyCopyAllMeta(key2, key1) == 1, "could not copy meta value"); succeed_if (!strcmp(keyValue(keyGetMeta(key1, "mymeta")), "a longer meta value"), "old meta data should be unchanged"); succeed_if (!strcmp(keyValue(keyGetMeta(key2, "mymeta")), "a longer meta value"), "old meta data should be unchanged"); succeed_if (keyGetMeta(key1, "mymeta") == keyGetMeta(key2, "mymeta"), "reference to the same key"); succeed_if (keyCopyAllMeta(key1, key2) == 1, "did nothing in the end"); succeed_if (!strcmp(keyValue(keyGetMeta(key1, "mymeta")), "a longer meta value"), "old meta data should be unchanged"); succeed_if (!strcmp(keyValue(keyGetMeta(key2, "mymeta")), "a longer meta value"), "old meta data should be unchanged"); succeed_if (keyGetMeta(key1, "mymeta") == keyGetMeta(key2, "mymeta"), "reference to the same key"); keyDel (key1); keyDel (key2); succeed_if (key1 = keyNew(0), "could not create key"); succeed_if (key2 = keyNew(0), "could not create key"); succeed_if (keySetMeta(key1, "mymeta", "a longer meta value") == sizeof("a longer meta value"), "could not set meta value"); succeed_if (keyCopyAllMeta(key2, key1) == 1, "could not copy meta value"); succeed_if (!strcmp(keyValue(keyGetMeta(key1, "mymeta")), "a longer meta value"), "old meta data should be unchanged"); succeed_if (!strcmp(keyValue(keyGetMeta(key2, "mymeta")), "a longer meta value"), "old meta data should be unchanged"); succeed_if (keyGetMeta(key1, "mymeta") == keyGetMeta(key2, "mymeta"), "reference to the same key"); succeed_if (keySetMeta(key1, "mymeta", "a longer meta value") == sizeof("a longer meta value"), "could not set meta value"); succeed_if (!strcmp(keyValue(keyGetMeta(key1, "mymeta")), "a longer meta value"), "old meta data should be unchanged"); succeed_if (!strcmp(keyValue(keyGetMeta(key2, "mymeta")), "a longer meta value"), "old meta data should be unchanged"); succeed_if (keyGetMeta(key1, "mymeta") != keyGetMeta(key2, "mymeta"), "reference to another key"); succeed_if (keySetMeta(key1, "mymeta", "a longer meta value2") == sizeof("a longer meta value2"), "could not set meta value2"); succeed_if (!strcmp(keyValue(keyGetMeta(key1, "mymeta")), "a longer meta value2"), "old meta data should be unchanged"); succeed_if (!strcmp(keyValue(keyGetMeta(key2, "mymeta")), "a longer meta value"), "old meta data should be unchanged"); succeed_if (keyGetMeta(key1, "mymeta") != keyGetMeta(key2, "mymeta"), "reference to another key (with another value)"); keyDel (key1); keyDel (key2); Key *k; Key *c; k=keyNew ("user/metakey", KEY_META, "t", "test1", KEY_META, "a", "another", KEY_META, "cya", "see the meta data later", KEY_META, "mode", "0775", KEY_END); c=keyNew ("user/metacopy", KEY_END); succeed_if (keyGetMeta(k, "t") != 0, "could not get meta key"); succeed_if (keyGetMeta(k, "a") != 0, "could not get meta key"); succeed_if (keyGetMeta(c, "t") == 0, "could get meta key not there"); succeed_if (keyGetMeta(c, "a") == 0, "could get meta key not there"); succeed_if (keyCopyAllMeta(c, k) == 1, "could not copy meta data"); succeed_if (keyGetMeta(k, "t") == keyGetMeta(c, "t"), "not the same meta data after copy"); succeed_if (keyGetMeta(k, "a") == keyGetMeta(c, "a"), "not the same meta data after copy"); succeed_if (keyGetMeta(k, "cya") == keyGetMeta(c, "cya"), "not the same meta data after copy"); succeed_if (keyGetMeta(k, "mode") == keyGetMeta(c, "mode"), "not the same meta data after copy"); succeed_if (keyValue(keyGetMeta(k, "nonexist")) == 0, "should not be there"); succeed_if (keyValue(keyGetMeta(c, "nonexist")) == 0, "should not be there"); succeed_if (keyCopyAllMeta(c, k) == 1, "could not copy meta data (again)"); succeed_if (keyGetMeta(k, "t") == keyGetMeta(c, "t"), "not the same meta data after copy"); succeed_if (keyGetMeta(k, "a") == keyGetMeta(c, "a"), "not the same meta data after copy"); succeed_if (keyGetMeta(k, "cya") == keyGetMeta(c, "cya"), "not the same meta data after copy"); succeed_if (keyGetMeta(k, "mode") == keyGetMeta(c, "mode"), "not the same meta data after copy"); succeed_if (keyValue(keyGetMeta(k, "nonexist")) == 0, "should not be there"); succeed_if (keyValue(keyGetMeta(c, "nonexist")) == 0, "should not be there"); keyDel (k); keyDel (c); }