/** * Allocates a new split object. * * Splits up a keyset into multiple keysets where each * of them will passed to the correct kdbSet(). * * Initially the size is 0 and alloc is APPROXIMATE_NR_OF_BACKENDS. * * @return a fresh allocated split object * @ingroup split * @see elektraSplitDel() **/ Split * elektraSplitNew (void) { Split * ret = elektraCalloc (sizeof (Split)); ret->size = 0; ret->alloc = APPROXIMATE_NR_OF_BACKENDS; ret->keysets = elektraCalloc (sizeof (KeySet *) * ret->alloc); ret->handles = elektraCalloc (sizeof (KDB *) * ret->alloc); ret->parents = elektraCalloc (sizeof (Key *) * ret->alloc); ret->syncbits = elektraCalloc (sizeof (int) * ret->alloc); return ret; }
static void test_elektraMalloc (void) { char * buffer = 0; buffer = elektraMalloc (50); exit_if_fail (buffer, "buffer must not be 0 after allocation"); elektraRealloc ((void **)&buffer, 100); exit_if_fail (buffer, "buffer must not be 0 after reallocation"); elektraRealloc ((void **)&buffer, 20); exit_if_fail (buffer, "buffer must not be 0 after reallocation"); elektraFree (buffer); buffer = elektraCalloc (50); exit_if_fail (buffer, "buffer must not be 0 after allocation"); for (int i = 0; i < 50; ++i) { succeed_if (buffer[i] == 0, "elektraCalloc did not initialize buffer with zeros"); } elektraRealloc ((void **)&buffer, 100); exit_if_fail (buffer, "buffer must not be 0 after reallocation"); elektraRealloc ((void **)&buffer, 20); exit_if_fail (buffer, "buffer must not be 0 after reallocation"); char * dup = elektraStrNDup (buffer, 20); exit_if_fail (dup, "could not duplicate buffer"); elektraFree (buffer); buffer = 0; for (int i = 0; i < 20; ++i) { succeed_if (dup[i] == 0, "elektraStrNDup did not correctly copy zero-buffer"); } elektraFree (dup); }
Trie *test_insert (Trie *trie, char *name, char* value) { Backend *backend = elektraCalloc (sizeof (Backend)); backend->mountpoint = keyNew (name, KEY_VALUE, value, KEY_END); backend->refcounter = 1; keyIncRef (backend->mountpoint); return elektraTrieInsert(trie, name, backend); }
/** * Creates a new ElektraError using the provided values. * The returned value will be allocated with elektraCalloc(). * * @param code The error code of the error. * @param description The description of the error. * @param severity The severity of the error. Only use ELEKTRA_ERROR_SEVERITY_FATAL, * if the error will be raised with elektraFatalError(). * @param group The group to which this error belongs. * @param module The module from which this error originates. * @return A newly allocated ElektraError (free with elektraFree()). */ ElektraError * elektraErrorCreate (ElektraErrorCode code, const char * description, ElektraErrorSeverity severity) { ElektraError * const error = elektraCalloc (sizeof (struct _ElektraError)); error->code = code; error->description = elektraStrDup (description); error->severity = severity; return error; }
ssize_t elektraFinalizeEmptyName (Key * key) { key->key = elektraCalloc (2); // two null pointers key->keySize = 1; key->keyUSize = 1; key->flags |= KEY_FLAG_SYNC; return key->keySize; }
Backend *b_new(const char *name, const char *value) { Backend *backend = elektraCalloc (sizeof (Backend)); backend->refcounter = 1; backend->mountpoint = keyNew (name, KEY_VALUE, value, KEY_END); keyIncRef (backend->mountpoint); return backend; }
/** * @internal * Create new data structure for binding operations. * * @return new data structure */ static UvBindingData * newBindingData (void) { UvBindingData * bindingData = elektraCalloc (sizeof (*bindingData)); if (bindingData == NULL) { ELEKTRA_LOG_WARNING ("elektraCalloc failed"); return NULL; } return bindingData; }
/** * @brief Allocate a backend * * Initialize everything with zero, except: sizes with -1 * and refcounter with 1 * * @return */ static Backend* elektraBackendAllocate() { Backend *backend = elektraCalloc(sizeof(struct _Backend)); backend->refcounter = 1; backend->specsize = -1; backend->dirsize = -1; backend->usersize = -1; backend->systemsize = -1; return backend; }
static void elektraGenTempFilename (ElektraResolved * handle, ElektraResolveTempfile tmpDir) { char * tmpFile = NULL; size_t len = 0; size_t tmpFilenameSize = 0; if (tmpDir == ELEKTRA_RESOLVER_TEMPFILE_SAMEDIR) { tmpFilenameSize = strlen (handle->fullPath) + POSTFIX_SIZE; tmpFile = elektraCalloc (tmpFilenameSize); len = sprintf (tmpFile, "%s", handle->fullPath); } else if (tmpDir == ELEKTRA_RESOLVER_TEMPFILE_TMPDIR) { tmpFilenameSize = sizeof ("/tmp/") + strlen (handle->fullPath) + POSTFIX_SIZE; tmpFile = elektraCalloc (tmpFilenameSize); len = sprintf (tmpFile, "/tmp/%s", handle->fullPath); } struct timeval tv; memset (&tv, 0, sizeof (struct timeval)); gettimeofday (&tv, 0); snprintf (tmpFile + len, POSTFIX_SIZE - 1, ".%d:%ld." ELEKTRA_TIME_USEC_F ".tmp", getpid (), tv.tv_sec, tv.tv_usec); handle->tmpFile = tmpFile; }
static void genCheckoutFileName (GitData * data) { // generate temp filename: /tmp/branch_filename_tv_sec:tv_usec struct timeval tv; gettimeofday (&tv, 0); const char * fileName = strrchr (data->file, '/'); if (!fileName) fileName = data->file; else fileName += 1; size_t len = strlen (DEFAULT_CHECKOUT_LOCATION) + strlen (data->branch) + strlen (fileName) + TV_MAX_DIGITS + 1; data->tmpFile = elektraCalloc (len); snprintf (data->tmpFile, len, "%s%s_%s_%lu:" ELEKTRA_TIME_USEC_F, DEFAULT_CHECKOUT_LOCATION, data->branch, fileName, tv.tv_sec, tv.tv_usec); }
/** * @brief Allows one to Export Methods for a Plugin. * * This function must be called within ELEKTRA_PLUGIN_EXPORT. * It define the plugin's methods that will be exported. * * All KDB methods implemented by the plugin basically could * have random names (convention is elektraName*), except * ELEKTRA_PLUGIN_EXPORT. * * This is the single symbol that will be looked up * when loading the plugin, and the first method of the backend * implementation that will be called. * * You need to use a macro so that both dynamic and static loading * of the plugin works. For example for the doc plugin: * @snippet doc.c export * * The first parameter is the name of the plugin. * Then every plugin should have: * @c ELEKTRA_PLUGIN_OPEN, * @c ELEKTRA_PLUGIN_CLOSE, * @c ELEKTRA_PLUGIN_GET, * @c ELEKTRA_PLUGIN_SET and optionally * @c ELEKTRA_PLUGIN_ERROR. * * The list is terminated with * @c ELEKTRA_PLUGIN_END. * * You must use static "char arrays" in a read only segment. * Don't allocate storage, it won't be freed. * * @param pluginName the name of this plugin * @return an object that contains all plugin information needed by * libelektra.so * @ingroup plugin */ Plugin * elektraPluginExport (const char * pluginName, ...) { va_list va; Plugin * returned; plugin_t method = 0; if (pluginName == 0) return 0; returned = elektraCalloc (sizeof (struct _Plugin)); /* Start processing parameters */ va_start (va, pluginName); returned->name = pluginName; while ((method = va_arg (va, plugin_t))) { switch (method) { case ELEKTRA_PLUGIN_OPEN: returned->kdbOpen = va_arg (va, kdbOpenPtr); break; case ELEKTRA_PLUGIN_CLOSE: returned->kdbClose = va_arg (va, kdbClosePtr); break; case ELEKTRA_PLUGIN_GET: returned->kdbGet = va_arg (va, kdbGetPtr); break; case ELEKTRA_PLUGIN_SET: returned->kdbSet = va_arg (va, kdbSetPtr); break; case ELEKTRA_PLUGIN_ERROR: returned->kdbError = va_arg (va, kdbErrorPtr); break; default: ELEKTRA_ASSERT (0, "plugin passed something unexpected"); // fallthrough, will end here case ELEKTRA_PLUGIN_END: va_end (va); return returned; } } return returned; }
Trie* elektraTrieInsert(Trie *trie, const char *name, Backend *value) { char* p; unsigned char idx; if (name==0) name=""; idx=(unsigned char)name[0]; if (trie==NULL) { trie=elektraCalloc(sizeof(Trie)); if (!strcmp("",name)) { trie->empty_value=value; return trie; } trie->textlen[idx]=strlen(name); trie->text[idx]=elektraStrDup(name); trie->value[idx]=value; return trie; } if (!strcmp("",name)) { trie->empty_value=value; return trie; } if (trie->text[idx]) { /* there exists an entry with the same first character */ if ((p=elektraTrieStartsWith(name, trie->text[idx]))==0) { /* the name in the trie is part of the searched name --> continue search */ trie->children[idx]=elektraTrieInsert(trie->children[idx],name+trie->textlen[idx],value); } else { /* name in trie doesn't match name --> split trie */ char *newname; Trie *child; unsigned char idx2; newname=elektraStrDup(p); *p=0; /* shorten the old name in the trie */ trie->textlen[idx]=strlen(trie->text[idx]); child=trie->children[idx]; /* insert the name given as a parameter into the new trie entry */ trie->children[idx]=elektraTrieInsert(NULL, name+(p-trie->text[idx]), value); /* insert the split try into the new trie entry */ idx2 = (unsigned char) newname[0]; trie->children[idx]->text[idx2]=newname; trie->children[idx]->textlen[idx2]=strlen(newname); trie->children[idx]->value[idx2]=trie->value[idx]; trie->children[idx]->children[idx2]=child; trie->value[idx]=0; } } else { /* there doesn't exist an entry with the same first character */ trie->text[idx]=elektraStrDup(name); trie->value[idx]=(void*)value; trie->textlen[idx]=strlen(name); } return trie; }
KDB* kdb_new() { KDB *kdb = elektraCalloc (sizeof (KDB)); kdb->split = elektraSplitNew(); return kdb; }
int elektraSortTopology (KeySet * ks, Key ** array) { if (ks == NULL || array == NULL) return -1; KeySet * done = ksNew (0, KS_END); ksRewind (ks); Key * cur; ssize_t size = ksGetSize (ks); Key * orderCounter = keyNew ("/#", KEY_CASCADING_NAME, KEY_END); elektraArrayIncName (orderCounter); _adjMatrix adjMatrix[size]; int i = 0; int retVal = 1; int depCount = 0; Key ** localArray = elektraMalloc (size * sizeof (Key *)); elektraKsToMemArray (ks, localArray); qsort (localArray, size, sizeof (Key *), topCmpOrder); for (long j = 0; j < size; ++j) { adjMatrix[j].key = localArray[j]; adjMatrix[j].isResolved = 0; adjMatrix[j].deps = elektraCalloc (sizeof (unsigned long) * size); } kdb_octet_t hasOrder = 0; if (keyGetMeta (localArray[0], "order")) hasOrder = 1; unsigned int unresolved = 0; for (int j = 0; j < size; ++j) { cur = localArray[j]; KeySet * deps = elektraMetaArrayToKS (cur, "dep"); keyDel (ksLookupByName (deps, "dep", KDB_O_POP)); Key * tmpDep; switch (ksGetSize (deps)) { case -1: { // key has no dependencies, give it an order number and add it to list of resolved dependencies keySetMeta (cur, "order", keyBaseName (orderCounter)); elektraArrayIncName (orderCounter); ksAppendKey (done, keyDup (cur)); adjMatrix[j].isResolved = 1; ksDel (deps); break; } case 1: { // only 1 dependency: // test if it's reflexive tmpDep = ksHead (deps); if (!strcmp (keyName (cur), keyString (tmpDep))) { keySetMeta (cur, "order", keyBaseName (orderCounter)); elektraArrayIncName (orderCounter); ksAppendKey (done, keyDup (cur)); adjMatrix[j].isResolved = 1; ksDel (deps); break; } // if not, fallthrough to normal dependency handling } default: { int gotUnresolved = 0; while ((tmpDep = ksNext (deps)) != NULL) { if (!isValidKeyName (keyString (tmpDep))) { // invalid keyname -> ERROR retVal = -1; break; } i = getArrayIndex (tmpDep, adjMatrix, size); if (i == -1) { // key doesn't exist yet but has valid name, ignore it. continue; } else if (i == j) { // reflexiv depencency, do nothing } else { if (!adjMatrix[i].isResolved) { // unresolved dependency adjMatrix[j].deps[i] = 1; ++gotUnresolved; // simple cycle detection if (adjMatrix[i].deps[j]) { retVal = 0; break; } } } } if (gotUnresolved) { adjMatrix[j].isResolved = 0; ++unresolved; // cound unresolved dependencies depCount += gotUnresolved; } ksDel (deps); break; } } if (retVal <= 0) break; } if (retVal <= 0) { // error or cycle: goto cleanup goto TopSortCleanup; } // resolve all dependencies that can be resolved immediately for (int j = 0; j < size; ++j) { if (adjMatrix[j].isResolved) depCount -= resolveDep (j, adjMatrix, size); } ssize_t resolved = ksGetSize (done); if (((depCount + resolved) >= size) && (unresolved)) { // more dependencies dependencies than keys: // cycle found ! retVal = 0; goto TopSortCleanup; } if (unresolved) { int found = 1; // we have unresolved dependencies for (int j = 0; j < size + 1; ++j) { // loop until no dependency can be resolved anymore if (j == size) { if (found) { found = 0; j = -1; unresolved = 0; continue; } else break; } if (adjMatrix[j].isResolved) continue; ++unresolved; if (hasOrder) { // resolve by order int ret = resolveDeps (j, adjMatrix, size, done, orderCounter); if (ret == -1) break; j = -1; found = 1; continue; } else { // resolve next possible dependency in keyset if (!hasUnresolvedDependencies (j, adjMatrix, size)) { adjMatrix[j].isResolved = 1; resolveDep (j, adjMatrix, size); keySetMeta (localArray[j], "order", keyBaseName (orderCounter)); elektraArrayIncName (orderCounter); ksAppendKey (done, keyDup (localArray[j])); found = 1; } } } } if (unresolved == 0) { // everything resolved // add dependencies in topological order to array elektraKsToMemArray (ks, array); qsort (array, size, sizeof (Key *), topCmpOrder); retVal = 1; } else { // still unresolved dependencies left: // there must be a cycle somewhere retVal = 0; } TopSortCleanup: ksDel (done); keyDel (orderCounter); elektraFree (localArray); for (ssize_t j = 0; j < size; ++j) { elektraFree (adjMatrix[j].deps); } return retVal; }
/** * @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; }