void test_iterate() { Key *key; key = keyNew ("user/test", KEY_END); exit_if_fail (key, "could not create new key"); succeed_if (keyRewindMeta(key) == 0, "Could not rewind empty key"); succeed_if (keyNextMeta(key) == 0, "Could get next meta name, even if it is empty"); succeed_if (keyCurrentMeta(key) == 0, "Could get next meta value, even if it is empty"); keySetMeta (key, "meta1", "meta_value"); succeed_if (keyRewindMeta(key) == 0, "Could not rewind key"); succeed_if (!strcmp(keyName(keyNextMeta(key)), "meta1"), "keyNextMeta does not work at 1. iteration"); succeed_if (!strcmp(keyValue(keyCurrentMeta(key)), "meta_value"), "keyCurrentMeta does not work at 1. iteration"); succeed_if (keyNextMeta(key) == 0, "Could get next meta name, even if it is empty at 2. iteration"); succeed_if (keyCurrentMeta(key) == 0, "Could get next meta value, even if it is empty at 2. iteration"); succeed_if (keyNextMeta(key) == 0, "Could get next meta name, even if it is empty at 3. iteration"); succeed_if (keyCurrentMeta(key) == 0, "Could get next meta value, even if it is empty at 3. iteration"); succeed_if (keyNextMeta(key) == 0, "Could get next meta name, even if it is empty at 4. iteration"); succeed_if (keyCurrentMeta(key) == 0, "Could get next meta value, even if it is empty at 4. iteration"); keyDel (key); }
/** * @brief Compares metadata of two keys * * @retval KEY_META if there is a difference * @retval 0 if metadata is identical */ int keyCompareMeta (const Key * k1, const Key * k2) { const Key * meta1; const Key * meta2; Key * key1 = (Key *)k1; Key * key2 = (Key *)k2; keyRewindMeta (key1); keyRewindMeta (key2); while ((meta1 = keyNextMeta (key1)) != 0) { meta2 = keyNextMeta (key2); if (!meta2) { return KEY_META; } if (strcmp (keyName (meta1), keyName (meta2))) return KEY_META; if (strcmp (keyString (meta1), keyString (meta2))) return KEY_META; } // TODO: rewind metadata to previous position return 0; }
static int handleErrors (Key * parentKey, KeySet * ks, Key * key, Key * specKey, ConflictHandling * ch, Direction dir) { cursor_t cursor = ksGetCursor (ks); int ret = 0; ConflictHandling * localCh = elektraMalloc (sizeof (ConflictHandling)); memcpy (localCh, ch, sizeof (ConflictHandling)); parseLocalConfig (specKey, localCh, dir); Key * parentLookup = keyDup (key); keySetBaseName (parentLookup, 0); Key * parent = ksLookup (ks, parentLookup, KDB_O_NONE); keyDel (parentLookup); keyRewindMeta (parent); Conflict conflict; Key * meta; while (keyNextMeta (parent) != NULL) { meta = (Key *) keyCurrentMeta (parent); conflict = getConflict (meta); if (conflict != NAC) { ret |= handleError (parentKey, parent, specKey, meta, conflict, localCh); keySetMeta (parent, keyName (meta), 0); } else if (!strncmp (keyName (meta), "conflict/#", 10) || !strncmp (keyName (meta), "conflict/invalid/hasmember/#", 28)) { keySetMeta (parent, keyName (meta), 0); } } keyRewindMeta (key); while (keyNextMeta (key) != NULL) { meta = (Key *) keyCurrentMeta (key); conflict = getConflict (meta); if (conflict != NAC) { ret |= handleError (parentKey, key, specKey, meta, conflict, localCh); keySetMeta (key, keyName (meta), 0); } else if (!strncmp (keyName (meta), "conflict/#", 10) || !strncmp (keyName (meta), "conflict/invalid/hasmember/#", 28)) { keySetMeta (key, keyName (meta), 0); } } elektraFree (localCh); ksSetCursor (ks, cursor); return ret; }
void print_warnings(Key * err) { const Key *meta = 0; keyRewindMeta(err); while((meta = keyNextMeta(err)) != 0) { printf("%s:\t%s\n", keyName(meta), keyString(meta)); } }
void output_meta(Key *k) { const Key *meta; keyRewindMeta (k); while ((meta = keyNextMeta (k))!=0) { printf (", %s: %s", keyName(meta), (const char*)keyValue(meta)); } printf ("\n"); }
static int copyError (Key * dest, Key * src) { keyRewindMeta (src); const Key * metaKey = keyGetMeta (src, "error"); if (!metaKey) return 0; keySetMeta (dest, keyName (metaKey), keyString (metaKey)); while ((metaKey = keyNextMeta (src)) != NULL) { if (strncmp (keyName (metaKey), "error/", 6) != 0) break; keySetMeta (dest, keyName (metaKey), keyString (metaKey)); } return 1; }
/** * @internal * Helper which iterates over MetaKeys from key * and removes all MetaKeys starting with * searchfor. */ void elektraRemoveMetaData (Key * key, const char * searchfor) { const Key * iter_key; keyRewindMeta (key); while ((iter_key = keyNextMeta (key)) != 0) { /*startsWith*/ if (strncmp (searchfor, keyName (iter_key), strlen (searchfor)) == 0) { keySetMeta (key, keyName (iter_key), 0); } } }
/* Helper which iterates over MetaKeys from key * and removes all MetaKeys starting with * searchfor. */ void removeMetaData (Key * key, const char * searchfor) { const Key * iter_key; keyRewindMeta (key); while ((iter_key = keyNextMeta (key)) != 0) { /*startsWith*/ if (strncmp (searchfor, keyName (iter_key), strlen (searchfor)) == 0) { if (keySetMeta (key, keyName (iter_key), 0) != 0) printf ("Error while deleting %s\n", searchfor); } } }
int main () { Key * k; Key * c; const Key * meta; k = keyNew ("user/metakey", KEY_END); c = keyNew ("user/metacopy", KEY_END); keySetMeta (k, "hello", "hello_world"); keySetMeta (k, "mode", "0644"); keySetMeta (k, "time", "1271234264"); keySetMeta (k, "empty", ""); meta = keyGetMeta (k, "hello"); printf ("Metadata %s has the value %s with the value size %zd\n", keyName (meta), (const char *)keyValue (meta), keyGetValueSize (meta)); printf ("Metadata mode has the value %s\n", (const char *)keyValue (keyGetMeta (k, "mode"))); printf ("Metadata time has the value %s\n", (const char *)keyValue (keyGetMeta (k, "time"))); printf ("Metadata empty has the value %s\n", (const char *)keyValue (keyGetMeta (k, "empty"))); if (!keyGetMeta (k, "nonexist")) printf ("Check if a metadata exist\n"); keySetMeta (k, "hello", "between"); keyCopyMeta (c, k, "hello"); if (keyGetMeta (k, "hello") == keyGetMeta (c, "hello")) printf ("Check if they point to the same metadata after a copy\n"); printf ("Metadata hello now has the value %s\n", (const char *)keyValue (keyGetMeta (k, "hello"))); keySetMeta (k, "hello", 0); printf ("Metadata hello now has the value %s (after dropping)\n", (const char *)keyValue (keyGetMeta (k, "hello"))); keySetMeta (k, "hello", "goodbye"); printf ("Metadata hello now has the value %s\n", (const char *)keyValue (keyGetMeta (k, "hello"))); printf ("Now we will output all metadata of the key:\n"); keyRewindMeta (k); while ((meta = keyNextMeta (k)) != 0) { printf ("%s=%s\n", keyName (meta), (const char *)keyValue (meta)); } keyDel (k); return 0; }
/** * Generate a C-Style key and stream it. * * This keyset can be used to include as c-code for * applikations using elektra. * * @param key the key object to work with * @param stream the file pointer where to send the stream * @param options KDB_O_SHOWINDICES, KDB_O_IGNORE_COMMENT, KDB_O_SHOWINFO * @retval 1 on success * @ingroup stream */ int keyGenerate (const Key * key, FILE * stream, option_t options) { size_t n = keyGetNameSize (key); if (n > 1) { char * nam = (char *) elektraMalloc (n); if (nam == NULL) return -1; keyGetName (key, nam, n); fprintf (stream, "\tkeyNew (\"%s\"", nam); elektraFree (nam); } size_t s = keyGetValueSize (key); if (s > 1) { char * str = (char *) elektraMalloc (s); if (str == NULL) return -1; if (keyIsBinary (key)) { keyGetBinary (key, str, s); fprintf (stream, ", KEY_SIZE, \"%zd\"", keyGetValueSize (key)); } else { keyGetString (key, str, s); } fprintf (stream, ", KEY_VALUE, \"%s\"", str); elektraFree (str); } const Key * meta; Key * dup = keyDup (key); keyRewindMeta (dup); while ((meta = keyNextMeta (dup))) { fprintf (stream, ", KEY_META, \"%s\", \"%s\"", keyName (meta), keyString (meta)); } keyDel (dup); fprintf (stream, ", KEY_END)"); if (options == 0) return 1; /* dummy to make icc happy */ return 1; }
/** 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 }