static void elektraResolveUser (resolverHandle * p, Key * warningsKey) { p->filename = elektraMalloc (KDB_MAX_PATH_LENGTH); #if defined(_WIN32) CHAR home[MAX_PATH]; if (SUCCEEDED (SHGetFolderPath (NULL, CSIDL_PROFILE, NULL, 0, home))) { escapePath (home); } else { strcpy (home, ""); ELEKTRA_ADD_WARNING (90, warningsKey, "could not get home (CSIDL_PROFILE), using /"); } #else char * home = (char *)getenv ("HOME"); if (!home) { home = ""; ELEKTRA_ADD_WARNING (90, warningsKey, "could not get home, using /"); } #endif strcpy (p->filename, home); strcat (p->filename, "/"); strncat (p->filename, p->path, KDB_MAX_PATH_LENGTH); }
static int elektraResolvePasswdHome (resolverHandle * p, Key * warningsKey) { ssize_t bufSize = sysconf (_SC_GETPW_R_SIZE_MAX); if (bufSize == -1) bufSize = 16384; // man 3 getpwuid char * buf = elektraMalloc (bufSize); if (!buf) return -1; struct passwd pwd; struct passwd * result; int s; s = getpwuid_r (getuid (), &pwd, buf, bufSize, &result); if (result == NULL) { elektraFree (buf); if (s != 0) { ELEKTRA_ADD_WARNING (90, warningsKey, strerror (s)); } return -1; } const char * home = pwd.pw_dir; size_t filenameSize = elektraStrLen (home) + elektraStrLen (p->path) - 1; p->filename = elektraMalloc (filenameSize); snprintf (p->filename, filenameSize, "%s/%s", home, (p->path) + 2); elektraFree (buf); return 0; }
static void elektraDropCurrentKey (KeySet * ks, Key * warningKey, const Backend * curHandle, const char * msg) { const Key * k = ksCurrent (ks); const size_t sizeOfStaticText = 100; char * warningMsg = elektraMalloc (keyGetNameSize (curHandle->mountpoint) + keyGetValueSize (curHandle->mountpoint) + keyGetNameSize (k) + strlen (msg) + sizeOfStaticText); strcpy (warningMsg, "drop key "); const char * name = keyName (k); if (name) { strcat (warningMsg, name); } else { strcat (warningMsg, "(no name)"); } strcat (warningMsg, " not belonging to "); strcat (warningMsg, keyName (curHandle->mountpoint)); strcat (warningMsg, " with name "); strcat (warningMsg, keyString (curHandle->mountpoint)); strcat (warningMsg, " because "); strcat (warningMsg, msg); ELEKTRA_ADD_WARNING (79, warningKey, warningMsg); elektraFree (warningMsg); cursor_t c = ksGetCursor (ks); keyDel (elektraKsPopAtCursor (ks, c)); ksSetCursor (ks, c); elektraKsPrev (ks); // next ksNext() will point correctly again }
static void elektraResolveSystem (resolverHandle * p, Key * errorKey) { char * system = getenv ("ALLUSERSPROFILE"); if (!system) { system = ""; ELEKTRA_ADD_WARNING (90, errorKey, "could not get ALLUSERSPROFILE, using /"); } else { escapePath (system); } if (p->path[0] == '/') { /* Use absolute path */ size_t filenameSize = strlen (system) + strlen (p->path) + 1; p->filename = elektraMalloc (filenameSize); strcpy (p->filename, system); strcat (p->filename, p->path); return; } size_t filenameSize = sizeof (KDB_DB_SYSTEM) + strlen (system) + strlen (p->path) + sizeof ("/") + 1; p->filename = elektraMalloc (filenameSize); strcpy (p->filename, system); strcat (p->filename, KDB_DB_SYSTEM); strcat (p->filename, "/"); strcat (p->filename, p->path); return; }
/** * Closes the session with the Key database. * * @pre The handle must be a valid handle as returned from kdbOpen() * * @pre errorKey must be a valid key, e.g. created with keyNew() * * This is the counterpart of kdbOpen(). * * You must call this method when you finished your affairs with the key * database. You can manipulate Key and KeySet objects also after * kdbClose(), but you must not use any kdb*() call afterwards. * * The @p handle parameter will be finalized and all resources associated to it * will be freed. After a kdbClose(), the @p handle cannot be used anymore. * * @param handle contains internal information of * @link kdbOpen() opened @endlink key database * @param errorKey the key which holds error/warning information * @retval 0 on success * @retval -1 on NULL pointer * @ingroup kdb */ int kdbClose (KDB * handle, Key * errorKey) { if (!handle) { return -1; } Key * initialParent = keyDup (errorKey); int errnosave = errno; splitDel (handle->split); trieClose (handle->trie, errorKey); backendClose (handle->defaultBackend, errorKey); handle->defaultBackend = 0; // not set in fallback mode, so lets check: if (handle->initBackend) { backendClose (handle->initBackend, errorKey); handle->initBackend = 0; } for (int i = 0; i < NR_GLOBAL_POSITIONS; ++i) { for (int j = 0; j < NR_GLOBAL_SUBPOSITIONS; ++j) { elektraPluginClose (handle->globalPlugins[i][j], errorKey); } } if (handle->modules) { elektraModulesClose (handle->modules, errorKey); ksDel (handle->modules); } else { ELEKTRA_ADD_WARNING (47, errorKey, "modules were not open"); } elektraFree (handle); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; }
/** * @brief read the desired iteration count from config * @param errorKey may hold a warning if an invalid configuration is provided * @param config KeySet holding the plugin configuration * @returns the number of iterations for the key derivation function */ kdb_unsigned_long_t CRYPTO_PLUGIN_FUNCTION (getIterationCount) (Key * errorKey, KeySet * config) { Key * k = ksLookupByName (config, ELEKTRA_CRYPTO_PARAM_ITERATION_COUNT, 0); if (k) { const kdb_unsigned_long_t iterations = strtoul (keyString (k), NULL, 10); if (iterations > 0) { return iterations; } else { ELEKTRA_ADD_WARNING (ELEKTRA_WARNING_CRYPTO_CONFIG, errorKey, "iteration count provided at " ELEKTRA_CRYPTO_PARAM_ITERATION_COUNT " is invalid. Using default value instead."); } } return ELEKTRA_CRYPTO_DEFAULT_ITERATION_COUNT; }
static int elektraResolvePasswd (resolverHandle * p, Key * warningsKey) { struct passwd pwd; struct passwd * result; char * buf; ssize_t bufsize; int s; bufsize = sysconf (_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) /* Value was indeterminate */ { bufsize = 16384; /* Should be more than enough */ } buf = elektraMalloc (bufsize); if (buf == NULL) { return 0; } s = getpwuid_r (getuid (), &pwd, buf, bufsize, &result); if (result == NULL) { elektraFree (buf); if (s != 0) { ELEKTRA_ADD_WARNING (90, warningsKey, strerror (s)); } return 0; } /* printf("Info: %s; UID: %ld0, Home: %s\n", pwd.pw_gecos, (long) pwd.pw_uid, pwd.pw_dir); */ elektraResolveUsingHome (p, pwd.pw_dir, true); elektraFree (buf); return 1; }
int elektraPluginClose (Plugin * handle, Key * errorKey) { int rc = 0; if (!handle) return 0; --handle->refcounter; /* Check if we have the last reference on the plugin (unsigned!) */ if (handle->refcounter > 0) return 0; if (handle->kdbClose) { rc = handle->kdbClose (handle, errorKey); if (rc == -1) ELEKTRA_ADD_WARNING (12, errorKey, "kdbClose() failed"); } ksDel (handle->config); elektraFree (handle); return rc; }
/** * @return freshly allocated buffer with current working directory * * @param warningsKey where warnings are added */ static char * elektraGetCwd (Key * warningsKey) { int size = 4096; char * cwd = elektraMalloc (size); if (cwd == NULL) { ELEKTRA_ADD_WARNING (83, warningsKey, "could not alloc for getcwd, defaulting to /"); return 0; } char * ret = NULL; while (ret == NULL) { ret = getcwd (cwd, size); if (ret == NULL) { if (errno != ERANGE) { // give up, we cannot handle the problem elektraFree (cwd); ELEKTRA_ADD_WARNINGF (ELEKTRA_WARNING_NOCWD, warningsKey, "getcwd failed with errno %d \"%s\", defaulting to /", errno, strerror (errno)); return 0; } // try to double the space size *= 2; elektraRealloc ((void **) &cwd, size); if (cwd == NULL) { ELEKTRA_ADD_WARNINGF (83, warningsKey, "could not realloc for getcwd size %d, defaulting to /", size); return 0; } } } return ret; }
/**This function avoid that every return path need to release the * configuration. */ inline static int openHelper (ckdb::Plugin * handle, kdb::KeySet & config, ckdb::Key * errorKey, Builder builder) { if (config.lookup ("/module")) { // suppress warnings if it is just a module // don't buildup the Delegated then return 0; } try { elektraPluginSetData (handle, (*builder) (config)); } catch (const char * msg) { #ifdef KDBERRORS_H ELEKTRA_ADD_WARNING (69, errorKey, msg); #endif return -1; } return get (handle) != nullptr ? 1 : -1; }
/** * @internal * @brief Does the rollback * * @param split all information for iteration * @param parentKey to add warnings (also passed to plugins for the same reason) */ static void elektraSetRollback (Split * split, Key * parentKey) { for (size_t p = 0; p < NR_OF_PLUGINS; ++p) { for (size_t i = 0; i < split->size; i++) { int ret = 0; Backend * backend = split->handles[i]; ksRewind (split->keysets[i]); if (backend->errorplugins[p]) { keySetName (parentKey, keyName (split->parents[i])); ret = backend->errorplugins[p]->kdbError (backend->errorplugins[p], split->keysets[i], parentKey); } if (ret == -1) { ELEKTRA_ADD_WARNING (81, parentKey, keyName (backend->mountpoint)); } } } }
/** * @internal * @brief Does the commit * * @param split all information for iteration * @param parentKey to add warnings (also passed to plugins for the same reason) */ static void elektraSetCommit (Split * split, Key * parentKey) { for (size_t p = COMMIT_PLUGIN; p < NR_OF_PLUGINS; ++p) { for (size_t i = 0; i < split->size; i++) { int ret = 0; Backend * backend = split->handles[i]; if (backend->setplugins[p] && backend->setplugins[p]->kdbSet) { if (p != COMMIT_PLUGIN) { keySetString (parentKey, keyString (split->parents[i])); } keySetName (parentKey, keyName (split->parents[i])); #if DEBUG && VERBOSE printf ("elektraSetCommit: %p # %zu with %s - %s\n", backend, p, keyName (parentKey), keyString (parentKey)); #endif ksRewind (split->keysets[i]); ret = backend->setplugins[p]->kdbSet (backend->setplugins[p], split->keysets[i], parentKey); if (p == COMMIT_PLUGIN) { // name of non-temp file keySetString (split->parents[i], keyString (parentKey)); } } if (ret == -1) { ELEKTRA_ADD_WARNING (80, parentKey, keyName (backend->mountpoint)); } } } }
static char * elektraResolvePasswd (Key * warningsKey) { ssize_t bufSize = sysconf (_SC_GETPW_R_SIZE_MAX); if (bufSize == -1) bufSize = 16384; // man 3 getpwuid char * buf = elektraMalloc (bufSize); if (!buf) return NULL; struct passwd pwd; struct passwd * result; int s = getpwuid_r (getuid (), &pwd, buf, bufSize, &result); if (result == NULL) { elektraFree (buf); if (s != 0) { ELEKTRA_ADD_WARNING (90, warningsKey, strerror (s)); } return NULL; } char * resolved = elektraStrDup (pwd.pw_dir); elektraFree (buf); return resolved; }
/**Builds a backend out of the configuration supplied * from: * @verbatim system/elektra/mountpoints/<name> @endverbatim * * The root key must be like the above example. You do * not need to rewind the keyset. But every key must be * below the root key. * * The internal consistency will be checked in this * function. If necessary parts are missing, like * no plugins, they cant be loaded or similar 0 * will be returned. * * ksCut() is perfectly suitable for cutting out the * configuration like needed. * * @note The given KeySet will be deleted within the function, * don't use it afterwards. * * @param elektraConfig the configuration to work with. * It is used to build up this backend. * @param modules used to load new modules or get references * to existing one * @return a pointer to a freshly allocated backend * this could be the requested backend or a so called * "missing backend". * @retval 0 if out of memory * @ingroup backend */ Backend* elektraBackendOpen(KeySet *elektraConfig, KeySet *modules, Key *errorKey) { Key * cur; Key * root; KeySet *referencePlugins = 0; KeySet *systemConfig = 0; int failure = 0; referencePlugins = ksNew(0, KS_END); ksRewind(elektraConfig); root = ksNext (elektraConfig); Backend *backend = elektraBackendAllocate(); while ((cur = ksNext(elektraConfig)) != 0) { if (keyRel (root, cur) == 1) { // direct below root key KeySet *cut = ksCut (elektraConfig, cur); if (!strcmp(keyBaseName(cur), "config")) { systemConfig = elektraRenameKeys(cut, "system"); ksDel (cut); } else if (!strcmp(keyBaseName(cur), "getplugins")) { if (elektraProcessPlugins(backend->getplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(13, errorKey, "elektraProcessPlugins for get failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "mountpoint")) { backend->mountpoint = keyNew("", KEY_VALUE, keyBaseName(root), KEY_END); elektraKeySetName(backend->mountpoint, keyString(cur), KEY_CASCADING_NAME | KEY_EMPTY_NAME); if (!backend->mountpoint) { if (!failure) ELEKTRA_ADD_WARNINGF(14, errorKey, "Could not create mountpoint with name %s and value %s", keyString(cur), keyBaseName(root)); failure = 1; } keyIncRef(backend->mountpoint); ksDel (cut); } else if (!strcmp(keyBaseName(cur), "setplugins")) { if (elektraProcessPlugins(backend->setplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for set failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "errorplugins")) { if (elektraProcessPlugins(backend->errorplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for error failed"); failure = 1; } } else { // no one cares about that config if (!failure) ELEKTRA_ADD_WARNING(16, errorKey, keyBaseName(cur)); ksDel (cut); } } } if (failure) { Backend *tmpBackend = elektraBackendOpenMissing(backend->mountpoint); elektraBackendClose(backend, errorKey); backend = tmpBackend; } ksDel (systemConfig); ksDel (elektraConfig); ksDel (referencePlugins); return backend; }
/** @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; }
/** * @brief Opens the session with the Key database. * * @pre errorKey must be a valid key, e.g. created with keyNew() * * The method will bootstrap itself the following way. * The first step is to open the default backend. With it * system/elektra/mountpoints will be loaded and all needed * libraries and mountpoints will be determined. * These libraries for backends will be loaded and with it the * @p KDB data structure will be initialized. * * You must always call this method before retrieving or committing any * keys to the database. In the end of the program, * after using the key database, you must not forget to kdbClose(). * * The pointer to the @p KDB structure returned will be initialized * like described above, and it must be passed along on any kdb*() * method your application calls. * * Get a @p KDB handle for every thread using elektra. Don't share the * handle across threads, and also not the pointer accessing it: * * @snippet kdbopen.c open * * You don't need kdbOpen() if you only want to * manipulate plain in-memory Key or KeySet objects. * * @pre errorKey must be a valid key, e.g. created with keyNew() * * @param errorKey the key which holds errors and warnings which were issued * @see kdbGet(), kdbClose() to end all affairs to the key database. * @retval handle on success * @retval NULL on failure * @ingroup kdb */ KDB * kdbOpen (Key * errorKey) { if (!errorKey) { ELEKTRA_LOG ("no error key passed"); return 0; } ELEKTRA_LOG ("called with %s", keyName (errorKey)); int errnosave = errno; KDB * handle = elektraCalloc (sizeof (struct _KDB)); Key * initialParent = keyDup (errorKey); handle->modules = ksNew (0, KS_END); if (elektraModulesInit (handle->modules, errorKey) == -1) { ksDel (handle->modules); elektraFree (handle); ELEKTRA_SET_ERROR (94, errorKey, "elektraModulesInit returned with -1"); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; } KeySet * keys = ksNew (0, KS_END); int inFallback = 0; switch (elektraOpenBootstrap (handle, keys, errorKey)) { case -1: ksDel (handle->modules); elektraFree (handle); ELEKTRA_SET_ERROR (40, errorKey, "could not open default backend"); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; case 0: ELEKTRA_ADD_WARNING (17, errorKey, "Initial kdbGet() failed, you should either fix " KDB_DB_INIT " or the fallback " KDB_DB_FILE); break; case 2: ELEKTRA_LOG ("entered fallback code for bootstrapping"); inFallback = 1; break; } keySetString (errorKey, "kdbOpen(): mountGlobals"); if (mountGlobals (handle, ksDup (keys), handle->modules, errorKey) == -1) { // mountGlobals also sets a warning containing the name of the plugin that failed to load ELEKTRA_ADD_WARNING (139, errorKey, "Mounting global plugins failed"); } keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, "kdbOpen(): backendClose"); backendClose (handle->defaultBackend, errorKey); splitDel (handle->split); handle->defaultBackend = 0; handle->trie = 0; #ifdef HAVE_LOGGER if (inFallback) ELEKTRA_LOG_WARNING ("fallback for bootstrapping: you might want to run `kdb upgrade-bootstrap`"); Key * key; ksRewind (keys); for (key = ksNext (keys); key; key = ksNext (keys)) { ELEKTRA_LOG_DEBUG ("config for createTrie name: %s value: %s", keyName (key), keyString (key)); } #endif handle->split = splitNew (); keySetString (errorKey, "kdbOpen(): mountOpen"); // Open the trie, keys will be deleted within mountOpen if (mountOpen (handle, keys, handle->modules, errorKey) == -1) { ELEKTRA_ADD_WARNING (93, errorKey, "Initial loading of trie did not work"); } keySetString (errorKey, "kdbOpen(): mountDefault"); if (mountDefault (handle, handle->modules, inFallback, errorKey) == -1) { ELEKTRA_SET_ERROR (40, errorKey, "could not reopen and mount default backend"); keySetString (errorKey, "kdbOpen(): close"); kdbClose (handle, errorKey); keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return 0; } keySetString (errorKey, "kdbOpen(): mountVersion"); mountVersion (handle, errorKey); keySetString (errorKey, "kdbOpen(): mountModules"); if (mountModules (handle, handle->modules, errorKey) == -1) { ELEKTRA_ADD_WARNING (92, errorKey, "Mounting modules did not work"); } keySetName (errorKey, keyName (initialParent)); keySetString (errorKey, keyString (initialParent)); keyDel (initialParent); errno = errnosave; return handle; }
/** * @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; }
static int csvRead(KeySet *returned, Key *parentKey, char delim, short useHeader, unsigned long fixColumnCount, const char **colNames) { const char *fileName; fileName = keyString(parentKey); FILE *fp = NULL; fp = fopen(fileName, "rb"); if(!fp) { ELEKTRA_SET_ERRORF(116, parentKey, "couldn't open file %s\n", fileName); return -1; } unsigned long length = 0; length = getLineLength(fp); if(length == 0) { ELEKTRA_ADD_WARNING(118, parentKey, "Empty file"); fclose(fp); return -2; } char *lineBuffer; lineBuffer = elektraMalloc((length * sizeof(char))+1); if(!lineBuffer) { ELEKTRA_SET_ERROR(87, parentKey, "Out of memory"); return -1; } if(!fgets(lineBuffer, length, fp)) { ELEKTRA_SET_ERROR(116, parentKey, "Cant read from file"); return -1; } unsigned long columns = 0; columns = getColumnCount(lineBuffer, delim); if(fixColumnCount) { if(columns != fixColumnCount) { ELEKTRA_SET_ERROR(117, parentKey, "illegal number of columns in Header line"); elektraFree(lineBuffer); fclose(fp); return -1; } } unsigned long colCounter = 0; unsigned long lineCounter = 0; unsigned long offset = 0; char *col; char buf[INTSTR_MAX]; int nr_keys = 1; KeySet *header = ksNew(0, KS_END); Key *key; if(useHeader == 1) { colCounter = 0; offset = 0; while((col = parseLine(lineBuffer, delim, offset, parentKey, lineCounter)) != NULL) { offset += elektraStrLen(col); key = keyDup(parentKey); if(colNames && (colNames+colCounter)) { keyAddBaseName(key, colNames[colCounter]); } else { keyAddBaseName(key, col); } keySetMeta(key, "csv/order", itostr(buf, colCounter, sizeof(buf)-1)); ksAppendKey(header, key); ++colCounter; } fseek(fp, 0, SEEK_SET); } else { colCounter = 0; //if no headerline exists name the columns 0..N where N is the number of columns key = keyDup(parentKey); keyAddName(key, "#"); while(colCounter < columns) { if(elektraArrayIncName(key) == -1) { elektraFree(lineBuffer); keyDel(key); ksDel(header); fclose(fp); return -1; } keySetMeta(key, "csv/order", itostr(buf, colCounter, sizeof(buf)-1)); if(colNames && (colNames+colCounter)) keySetBaseName(key, colNames[colCounter]); ksAppendKey(header, keyDup(key)); ++colCounter; } keyDel(key); if(useHeader == 0) fseek(fp, 0, SEEK_SET); } Key *dirKey; Key *cur; dirKey = keyDup(parentKey); keyAddName(dirKey, "#"); while(!feof(fp)) { length = getLineLength(fp); if(length == 0) break; if(elektraRealloc((void **)&lineBuffer, (length * sizeof(char))+1) < 0) { fclose(fp); elektraFree(lineBuffer); ksDel(header); keyDel(dirKey); ELEKTRA_SET_ERROR(87, parentKey, "Out of memory"); return -1; } fgets(lineBuffer, length, fp); if(elektraArrayIncName(dirKey) == -1) { elektraFree(lineBuffer); keyDel(dirKey); ksDel(header); fclose(fp); return -1; } ++nr_keys; offset = 0; colCounter = 0; char *lastIndex = "#0"; while((col = parseLine(lineBuffer, delim, offset, parentKey, lineCounter)) != NULL) { cur = getKeyByOrderNr(header, colCounter); offset += elektraStrLen(col); key = keyDup(dirKey); keyAddBaseName(key, keyBaseName(cur)); keySetString(key, col); keySetMeta(key, "csv/order", itostr(buf, colCounter, sizeof(buf)-1)); ksAppendKey(returned, key); lastIndex = (char *)keyBaseName(key); ++nr_keys; ++colCounter; } keySetString(dirKey, lastIndex); ksAppendKey(returned, keyDup(dirKey)); if(colCounter != columns) { if(fixColumnCount) { ELEKTRA_SET_ERRORF(117, parentKey, "illegal number of columns in line %lu", lineCounter); elektraFree(lineBuffer); fclose(fp); keyDel(dirKey); ksDel(header); return -1; } ELEKTRA_ADD_WARNINGF(118, parentKey, "illegal number of columns in line %lu", lineCounter); } ++lineCounter; } key = keyDup(parentKey); keySetString(key, keyBaseName(dirKey)); ksAppendKey(returned, key); keyDel(dirKey); fclose(fp); elektraFree(lineBuffer); ksDel(header); return 1; }
/** * Load a plugin. * * The array of plugins must be set to 0. * Its length is NR_OF_PLUGINS. * * systemConfig will only be used, not deleted. * * @param config the config with the information how the * plugins should be put together * @param systemConfig the shared (system) config for the plugins. * Every plugin additional get this config. * * @retval -1 on failure */ int elektraProcessPlugins (Plugin ** plugins, KeySet * modules, KeySet * referencePlugins, KeySet * config, KeySet * systemConfig, Key * errorKey) { Key * root; Key * cur; ksRewind (config); root = ksNext (config); while ((cur = ksNext (config)) != 0) { if (keyRel (root, cur) == 1) { char * pluginName = 0; char * referenceName = 0; int pluginNumber = 0; Key * key; if (elektraProcessPlugin (cur, &pluginNumber, &pluginName, &referenceName, errorKey) == -1) { elektraFree (pluginName); elektraFree (referenceName); ksDel (config); return -1; } if (pluginName) { key = keyDup (cur); keyAddBaseName (key, "config"); KeySet * cutConfig = ksCut (config, key); keyDel (key); KeySet * pluginConfig = elektraRenameKeys (cutConfig, "user"); ksDel (cutConfig); if (!pluginConfig) return -1; ksAppend (pluginConfig, systemConfig); ksRewind (pluginConfig); /* TODO: bug ksAppend invalidates cursor */ /* case 1, we create a new plugin, note that errorKey is not passed here, because it would set error information but we only want a warning instead. */ plugins[pluginNumber] = elektraPluginOpen (pluginName, modules, pluginConfig, errorKey); if (!plugins[pluginNumber]) { ELEKTRA_ADD_WARNING (64, errorKey, pluginName); /* Loading plugin did not work */ elektraFree (pluginName); elektraFree (referenceName); ksDel (config); return -1; } /* case 2, we label it for later use */ if (referenceName) ksAppendKey (referencePlugins, keyNew (referenceName, KEY_BINARY, KEY_SIZE, sizeof (plugins[pluginNumber]), KEY_VALUE, &plugins[pluginNumber], KEY_END)); } else { /* case 3, we use an existing plugin */ Key * lookup = ksLookup (referencePlugins, keyNew (referenceName, KEY_END), KDB_O_DEL); if (!lookup) { ELEKTRA_ADD_WARNING (65, errorKey, referenceName); /* Getting a reference plugin at a previous stage did not work. Note that this check is necessary, because loading the plugin could fail for example at errorplugins and at a later point, for example at setplugins it is tried to refer to that.*/ elektraFree (referenceName); ksDel (config); return -1; } plugins[pluginNumber] = *(Plugin **)keyValue (lookup); ++plugins[pluginNumber]->refcounter; } elektraFree (pluginName); elektraFree (referenceName); } else { ELEKTRA_ADD_WARNING (21, errorKey, keyString (cur)); } } ksDel (config); return 0; }
/** * Opens a plugin. * * The config will be used as is. So be sure to transfer ownership * of the config to it, with e.g. ksDup(). * elektraPluginClose() will delete the config. * * @return a pointer to a new created plugin or 0 on error */ Plugin * elektraPluginOpen (const char * name, KeySet * modules, KeySet * config, Key * errorKey) { Plugin * handle = 0; const char * n; elektraPluginFactory pluginFactory = 0; if (!name || name[0] == '\0') { ELEKTRA_ADD_WARNING (39, errorKey, "name is null or empty"); goto err_clup; } n = name; while (*n != '\0') { if (*n == '/') ++n; else break; } if (*n == '\0') { ELEKTRA_ADD_WARNING (39, errorKey, "name contained slashes only"); goto err_clup; } pluginFactory = elektraModulesLoad (modules, name, errorKey); if (pluginFactory == 0) { /* warning already set by elektraModulesLoad */ goto err_clup; } handle = pluginFactory (); if (handle == 0) { ELEKTRA_ADD_WARNING (6, errorKey, name); goto err_clup; } /* init reference counting */ handle->refcounter = 1; handle->config = config; config = 0; // for err_clup case /* let the plugin initialize itself */ if (handle->kdbOpen) { if ((handle->kdbOpen (handle, errorKey)) == -1) { ELEKTRA_ADD_WARNING (11, errorKey, name); elektraPluginClose (handle, errorKey); goto err_clup; } } #if DEBUG && VERBOSE printf ("Finished loading plugin %s\n", name); #endif return handle; err_clup: #if DEBUG printf ("Failed to load plugin %s\n", name); #endif ksDel (config); return 0; }
/**Builds a backend out of the configuration supplied * from: * @verbatim system/elektra/mountpoints/<name> @endverbatim * * The root key must be like the above example. You do * not need to rewind the keyset. But every key must be * below the root key. * * The internal consistency will be checked in this * function. If necessary parts are missing, like * no plugins, they cant be loaded or similar 0 * will be returned. * * ksCut() is perfectly suitable for cutting out the * configuration like needed. * * @note The given KeySet will be deleted within the function, * don't use it afterwards. * * @param elektraConfig the configuration to work with. * It is used to build up this backend. * @param modules used to load new modules or get references * to existing one * @param errorKey the key where an error and warnings are added * * @return a pointer to a freshly allocated backend * this could be the requested backend or a so called * "missing backend". * @retval 0 if out of memory * @ingroup backend */ Backend* elektraBackendOpen(KeySet *elektraConfig, KeySet *modules, Key *errorKey) { Key * cur; KeySet *referencePlugins = 0; KeySet *systemConfig = 0; int failure = 0; referencePlugins = ksNew(0, KS_END); ksRewind(elektraConfig); Key * root = ksNext (elektraConfig); Backend *backend = elektraBackendAllocate(); if (elektraBackendSetMountpoint(backend, elektraConfig, errorKey) == -1) { // warning already set failure = 1; } while ((cur = ksNext(elektraConfig)) != 0) { if (keyRel (root, cur) == 1) { // direct below root key KeySet *cut = ksCut (elektraConfig, cur); if (!strcmp(keyBaseName(cur), "config")) { systemConfig = elektraRenameKeys(cut, "system"); ksDel (cut); } else if (!strcmp(keyBaseName(cur), "errorplugins")) { if (elektraProcessPlugins(backend->errorplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for error failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "getplugins")) { if (elektraProcessPlugins(backend->getplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(13, errorKey, "elektraProcessPlugins for get failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "mountpoint")) { ksDel (cut); // already handled by elektraBackendSetMountpoint continue; } else if (!strcmp(keyBaseName(cur), "setplugins")) { if (elektraProcessPlugins(backend->setplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for set failed"); failure = 1; } } else { // no one cares about that config if (!failure) ELEKTRA_ADD_WARNING(16, errorKey, keyBaseName(cur)); ksDel (cut); } } } if (failure) { Backend *tmpBackend = elektraBackendOpenMissing(backend->mountpoint); elektraBackendClose(backend, errorKey); backend = tmpBackend; } ksDel (systemConfig); ksDel (elektraConfig); ksDel (referencePlugins); return backend; }
int elektraSimpleiniGet (Plugin * handle, KeySet * returned, Key * parentKey) { /* get all keys */ if (!strcmp (keyName (parentKey), "system/elektra/modules/simpleini")) { KeySet * moduleConfig = ksNew ( 30, keyNew ("system/elektra/modules/simpleini", KEY_VALUE, "simpleini plugin waits for your orders", KEY_END), keyNew ("system/elektra/modules/simpleini/exports", KEY_END), keyNew ("system/elektra/modules/simpleini/exports/get", KEY_FUNC, elektraSimpleiniGet, KEY_END), keyNew ("system/elektra/modules/simpleini/exports/set", KEY_FUNC, elektraSimpleiniSet, KEY_END), #include "readme_simpleini.c" keyNew ("system/elektra/modules/simpleini/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), keyNew ("system/elektra/modules/simpleini/config/needs", KEY_VALUE, "the needed configuration to work in a backend", KEY_END), keyNew ("system/elektra/modules/simpleini/config/needs/chars", KEY_VALUE, "Characters needed", KEY_END), // space in value now works: // TODO: characters present in format should be escaped /* keyNew ("system/elektra/modules/simpleini/config/needs/chars/20", KEY_VALUE, "61", KEY_END), // space -> a keyNew ("system/elektra/modules/simpleini/config/needs/chars/23", KEY_VALUE, "62", KEY_END), // # -> b keyNew ("system/elektra/modules/simpleini/config/needs/chars/25", KEY_VALUE, "63", KEY_END), // % -> c (escape character) keyNew ("system/elektra/modules/simpleini/config/needs/chars/3B", KEY_VALUE, "64", KEY_END), // ; -> d keyNew ("system/elektra/modules/simpleini/config/needs/chars/3D", KEY_VALUE, "65", KEY_END), // = -> e keyNew ("system/elektra/modules/simpleini/config/needs/chars/5C", KEY_VALUE, "66", KEY_END), // \\ -> f */ keyNew ("system/elektra/modules/simpleini/config/needs/chars/0A", KEY_VALUE, "67", KEY_END), // enter (NL) -> g keyNew ("system/elektra/modules/simpleini/config/needs/chars/0D", KEY_VALUE, "68", KEY_END), // CR -> h keyNew ("system/elektra/modules/simpleini/config/needs/escape", KEY_VALUE, "25", KEY_END), KS_END); ksAppend (returned, moduleConfig); ksDel (moduleConfig); return 1; } char * key = 0; char * value = 0; int errnosave = errno; FILE * fp = fopen (keyString (parentKey), "r"); if (!fp) { ELEKTRA_SET_ERROR_GET (parentKey); errno = errnosave; return -1; } char * format = getFormat (handle, "%ms", "%m[^\n]"); ELEKTRA_LOG ("Read from '%s' with format '%s'", keyString (parentKey), format); int n = 0; size_t size = 0; ssize_t ksize = 0; #pragma GCC diagnostic ignored "-Wformat" // icc warning #269: invalid format string conversion while ((n = fscanf (fp, format, &key, &value)) >= 0) { ELEKTRA_LOG_DEBUG ("Read %d parts: '%s' with value '%s'", n, key, value); if (n == 0) { // discard line getline (&key, &size, fp); ELEKTRA_LOG_DEBUG ("Discard '%s'", key); elektraFree (key); key = 0; continue; } Key * read = keyNew (keyName (parentKey), KEY_END); if (keyAddName (read, key) == -1) { ELEKTRA_ADD_WARNING (ELEKTRA_WARNING_INVALID_KEY, parentKey, key); keyDel (read); continue; } if (n == 2) { keySetString (read, value); elektraFree (value); value = 0; } if (ksAppendKey (returned, read) != ksize + 1) { ELEKTRA_SET_ERROR (ELEKTRA_ERROR_NOEOF, parentKey, "duplicated key"); return -1; } ++ksize; elektraFree (key); key = 0; } if (feof (fp) == 0) { elektraFree (format); fclose (fp); ELEKTRA_SET_ERROR (ELEKTRA_ERROR_NOEOF, parentKey, "not at the end of file"); return -1; } elektraFree (format); fclose (fp); return 1; /* success */ }
/** * @retval 1 and an allocated string of the pluginName if a new plugins should be created. * @retval 2 and an allocated string of the referenceName if an old plugin should be used * @retval 3 and both if a new plugin should be created and made available for later * back referencing. * @retval -1 on error */ int elektraProcessPlugin (Key * cur, int * pluginNumber, char ** pluginName, char ** referenceName, Key * errorKey) { const char * fullname = keyBaseName (cur); size_t fullsize = keyGetBaseNameSize (cur); if (fullname[0] != '#') { ELEKTRA_ADD_WARNING (18, errorKey, fullname); return -1; } if (fullname[1] < '0' || fullname[1] > '9') { ELEKTRA_ADD_WARNING (19, errorKey, fullname); return -1; } *pluginNumber = fullname[1] - '0'; if (*pluginNumber > NR_OF_PLUGINS) { ELEKTRA_ADD_WARNING (20, errorKey, fullname); return -1; } if (fullname[2] == '#') { char prefixReferenceName[] = "system/elektra/plugins/"; /* We have a back reference here */ if (fullname[fullsize - 2] == '#') { const char * iter = &fullname[3]; size_t pluginNameSize = 1; /* For null character */ size_t referenceNameSize = 0; /* We will introduce a new plugin */ while (*iter != '#') { ++iter; ++pluginNameSize; } *pluginName = elektraMalloc (pluginNameSize); strncpy (*pluginName, &fullname[3], pluginNameSize); (*pluginName)[pluginNameSize - 1] = 0; referenceNameSize = fullsize - pluginNameSize - 4; ++iter; /* advance to one after hash */ *referenceName = elektraMalloc (referenceNameSize + sizeof (prefixReferenceName)); strncpy (*referenceName, prefixReferenceName, sizeof (prefixReferenceName)); strncat (*referenceName, iter, referenceNameSize); (*referenceName)[referenceNameSize + sizeof (prefixReferenceName) - 2] = 0; return 3; } else { /* We reference back to a plugin */ *referenceName = elektraMalloc (fullsize - 3 + sizeof (prefixReferenceName) - 1); strncpy (*referenceName, prefixReferenceName, sizeof (prefixReferenceName)); strncat (*referenceName, &fullname[3], fullsize - 3); return 2; } } else { *pluginName = elektraMalloc (fullsize - 2); /* don't alloc for #n */ strncpy (*pluginName, &fullname[2], fullsize - 2); return 1; } /* Should not be reached */ return 0; }