/** * @internal * Global mount given plugin at run-time. * * Reads placements from the plugin directly inserts the plugin. * Also supports adding itself to the list plugin at run-time if present * at requested global placement. * * @param kdb KDB handle * @param plugin Plugin handle * @retval 0 on errors * @retval 1 on success */ static int mountGlobalPlugin (KDB * kdb, Plugin * plugin) { ELEKTRA_NOT_NULL (kdb); ELEKTRA_NOT_NULL (plugin); char * placementList = getPluginPlacementList (plugin); // Parse plament list (contains placements from README.md seperated by // whitespace) char * placement = strtok (placementList, " "); while (placement != NULL) { // Convert placement name to internal index int placementIndex = placementToPosition (placement); if (placementIndex == -1) { elektraFree (placementList); return 0; } if (kdb->globalPlugins[placementIndex][MAXONCE] == NULL) { // Insert directly as global plugin kdb->globalPlugins[placementIndex][MAXONCE] = plugin; } else { Plugin * pluginAtPlacement = kdb->globalPlugins[placementIndex][MAXONCE]; // Add plugin to list plugin if (strcmp (pluginAtPlacement->name, "list") == 0) { ELEKTRA_LOG_DEBUG ("required position %s/maxonce taken by list plugin, adding plugin", placement); int result = listAddPlugin (pluginAtPlacement, plugin, placement); if (!result) { ELEKTRA_LOG_WARNING ("could not add plugin to list plugin at position %s/maxonce", placement); elektraFree (placementList); return 0; } } else { // cannot manually add list module here. configuration is broken: // the list module needs to be mounted in every position to keep track // of the current position ELEKTRA_LOG_WARNING ("required position %s/maxonce taken by plugin %s, aborting!", placement, pluginAtPlacement->name); elektraFree (placementList); return 0; } } // Process next placement in list placement = strtok (NULL, " "); } elektraFree (placementList); return 1; }
/** * @internal * Unmount global plugin at run-time. * * Removes a plugin at all placements. * Undos `mountGlobalPlugin()`. * * @param kdb KDB handle * @param plugin Plugin handle * @retval 0 on errors * @retval 1 on success */ static int unmountGlobalPlugin (KDB * kdb, Plugin * plugin) { ELEKTRA_NOT_NULL (kdb); ELEKTRA_NOT_NULL (plugin); char * placementList = getPluginPlacementList (plugin); // Parse plament list (contains placements from README.md seperated by // whitespace) char * placement = strtok (placementList, " "); while (placement != NULL) { // Convert placement name to internal index int placementIndex = placementToPosition (placement); if (placementIndex == -1) { elektraFree (placementList); return 0; } if (kdb->globalPlugins[placementIndex][MAXONCE] == plugin) { // Remove from direct placement as global plugin kdb->globalPlugins[placementIndex][MAXONCE] = NULL; } else { Plugin * pluginAtPlacement = kdb->globalPlugins[placementIndex][MAXONCE]; // Add plugin to list plugin if (strcmp (pluginAtPlacement->name, "list") == 0) { ELEKTRA_LOG_DEBUG ( "required position %s/maxonce taken by list plugin, " "removing plugin", placement); int result = listRemovePlugin (pluginAtPlacement, plugin); if (!result) { ELEKTRA_LOG_WARNING ("could not remove plugin from list plugin at position %s/maxonce", placement); elektraFree (placementList); return 0; } } else { ELEKTRA_LOG_WARNING ( "required position %s/maxonce taken by plugin %s, " "should be either list or plugin!", placement, pluginAtPlacement->name); } } // Process next placement in list placement = strtok (NULL, " "); } elektraFree (placementList); return 1; }
/** * @internal * Check if two keys have the same name. * If one of the keys is cascading only the cascading names are compared. * * @param key key * @param check check * @retval 1 if keys have the same name * @retval 0 otherwise */ static int checkKeyIsSame (Key * key, Key * check) { int result = 0; if (keyGetNamespace (check) == KEY_NS_CASCADING || keyGetNamespace (key) == KEY_NS_CASCADING) { const char * cascadingCheck = strrchr (keyName (check), '/'); const char * cascadingKey = strrchr (keyName (key), '/'); if (cascadingCheck != NULL && cascadingKey != NULL) { result = elektraStrCmp (cascadingKey, cascadingCheck) == 0; } else { if (cascadingCheck == NULL) { ELEKTRA_LOG_WARNING ("invalid key given: '%s' is not a valid key", cascadingCheck); } if (cascadingKey == NULL) { ELEKTRA_LOG_WARNING ("invalid key given: '%s' is not a valid key", cascadingKey); } } } else { result = elektraStrCmp (keyName (check), keyName (key)) == 0; } return result; }
int elektraDbusSendMessage (DBusBusType type, const char * keyName, const char * signalName) { DBusConnection * connection; DBusError error; DBusMessage * message; const char * dest = NULL; // to all receivers const char * interface = "org.libelektra"; const char * path = "/org/libelektra/configuration"; dbus_error_init (&error); connection = dbus_bus_get (type, &error); if (connection == NULL) { ELEKTRA_LOG_WARNING ("Failed to open connection to %s message bus: %s", (type == DBUS_BUS_SYSTEM) ? "system" : "session", error.message); dbus_connection_unref (connection); dbus_error_free (&error); return -1; } dbus_connection_set_exit_on_disconnect (connection, FALSE); message = dbus_message_new_signal (path, interface, signalName); if (message == NULL) { ELEKTRA_LOG_WARNING ("Couldn't allocate D-Bus message"); dbus_connection_unref (connection); dbus_error_free (&error); return -1; } if (dest && !dbus_message_set_destination (message, dest)) { ELEKTRA_LOG_WARNING ("Not enough memory"); dbus_message_unref (message); dbus_connection_unref (connection); dbus_error_free (&error); return -1; } if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &keyName, DBUS_TYPE_INVALID)) { ELEKTRA_LOG_WARNING ("Couldn't add message argument"); dbus_message_unref (message); dbus_connection_unref (connection); dbus_error_free (&error); return -1; } dbus_connection_send (connection, message, NULL); dbus_connection_flush (connection); dbus_message_unref (message); dbus_connection_unref (connection); dbus_error_free (&error); return 1; }
/** * @internal * Setup ZeroMq for receiving notifications. * * @param data plugin data containing context, socket, etc. */ void elektraZeroMqRecvSetup (ElektraZeroMqRecvPluginData * data) { // create zmq context if (!data->zmqContext) { data->zmqContext = zmq_ctx_new (); if (data->zmqContext == NULL) { ELEKTRA_LOG_WARNING ("zmq_ctx_new failed %s", zmq_strerror (zmq_errno ())); return; } } // create publish socket if (!data->zmqSubscriber) { data->zmqSubscriber = zmq_socket (data->zmqContext, ZMQ_SUB); if (data->zmqSubscriber == NULL) { ELEKTRA_LOG_WARNING ("zmq_socket failed %s", zmq_strerror (zmq_errno ())); zmq_close (data->zmqSubscriber); return; } // subscribe to notifications char * keyCommitType = "Commit"; if (zmq_setsockopt (data->zmqSubscriber, ZMQ_SUBSCRIBE, keyCommitType, elektraStrLen (keyCommitType)) != 0) { ELEKTRA_LOG_WARNING ("failed to subscribe to %s messages", keyCommitType); } // connect to endpoint int result = zmq_connect (data->zmqSubscriber, data->endpoint); if (result != 0) { ELEKTRA_LOG_WARNING ("zmq_connect error: %s\n", zmq_strerror (zmq_errno ())); zmq_close (data->zmqSubscriber); data->zmqSubscriber = NULL; return; } } if (!data->zmqAdapter) { // attach ZeroMq adater and wait for socket to be writable data->zmqAdapter = elektraIoAdapterZeroMqAttach (data->zmqSubscriber, data->ioBinding, ELEKTRA_IO_ADAPTER_ZEROMQCB_READ, zeroMqRecvSocketReadable, data); if (!data->zmqAdapter) { ELEKTRA_LOG_WARNING ("could not attach zmq adapter"); zmq_close (data->zmqSubscriber); data->zmqSubscriber = NULL; return; } } }
/** * @internal * Called whenever the socket becomes readable. * ZeroMq since sends multipart messages atomically (all or nothing) * both message parts are instantly available. * * @param socket ZeroMq socket * @param context context passed to elektraIoAdapterZeroMqAttach() */ static void zeroMqRecvSocketReadable (void * socket, void * context) { ElektraZeroMqRecvPluginData * data = context; char * changeType; char * changedKeyName; zmq_msg_t message; zmq_msg_init (&message); int result = zmq_msg_recv (&message, socket, ZMQ_DONTWAIT); if (result == -1) { ELEKTRA_LOG_WARNING ("receiving change type failed: %s; aborting", zmq_strerror (zmq_errno ())); zmq_msg_close (&message); return; } if (!zmq_msg_more (&message)) { ELEKTRA_LOG_WARNING ("message has only one part; aborting"); zmq_msg_close (&message); return; } int length = zmq_msg_size (&message); changeType = elektraStrNDup (zmq_msg_data (&message), length + 1); changeType[length] = '\0'; ELEKTRA_LOG_DEBUG ("received change type %s", changeType); result = zmq_msg_recv (&message, socket, ZMQ_DONTWAIT); if (result == -1) { ELEKTRA_LOG_WARNING ("receiving key name failed: %s; aborting", zmq_strerror (zmq_errno ())); elektraFree (changeType); zmq_msg_close (&message); return; } length = zmq_msg_size (&message); changedKeyName = elektraStrNDup (zmq_msg_data (&message), length + 1); changedKeyName[length] = '\0'; ELEKTRA_LOG_DEBUG ("received key name %s", changedKeyName); // notify about changes Key * changedKey = keyNew (changedKeyName, KEY_END); data->notificationCallback (changedKey, data->notificationContext); zmq_msg_close (&message); elektraFree (changeType); elektraFree (changedKeyName); }
/** * @internal * Read placement list from plugin. * * The returned string needs to be freed. * * @param plugin Plugin * @return Space separated list of placement names */ static char * getPluginPlacementList (Plugin * plugin) { ELEKTRA_NOT_NULL (plugin); // Get placements from plugin Key * pluginInfo = keyNew ("system/elektra/modules/", KEY_END); keyAddBaseName (pluginInfo, plugin->name); KeySet * ksResult = ksNew (0, KS_END); plugin->kdbGet (plugin, ksResult, pluginInfo); Key * placementsKey = keyDup (pluginInfo); keyAddBaseName (placementsKey, "infos"); keyAddBaseName (placementsKey, "placements"); Key * placements = ksLookup (ksResult, placementsKey, 0); if (placements == NULL) { ELEKTRA_LOG_WARNING ("could not read placements from plugin"); return 0; } char * placementList = elektraStrDup (keyString (placements)); keyDel (pluginInfo); keyDel (placementsKey); ksDel (ksResult); return placementList; }
int elektraNotificationSetConversionErrorCallback (KDB * kdb, ElektraNotificationConversionErrorCallback callback, void * context) { if (!kdb || !callback) { ELEKTRA_LOG_WARNING ("null pointer passed"); return 0; } // Find notification plugin Plugin * notificationPlugin = getNotificationPlugin (kdb); if (!notificationPlugin) { return 0; } // Get register function from plugin size_t func = elektraPluginGetFunction (notificationPlugin, "setConversionErrorCallback"); if (!func) { return 0; } // Call register function ElektraNotificationSetConversionErrorCallback setCallbackFunc = (ElektraNotificationSetConversionErrorCallback) func; setCallbackFunc (notificationPlugin, callback, context); return 1; }
int elektraNotificationRegisterCallbackSameOrBelow (KDB * kdb, Key * key, ElektraNotificationChangeCallback callback, void * context) { if (!kdb || !key || !callback) { ELEKTRA_LOG_WARNING ("null pointer passed"); return 0; } // Find notification plugin Plugin * notificationPlugin = getNotificationPlugin (kdb); if (!notificationPlugin) { return 0; } // Get register function from plugin size_t func = elektraPluginGetFunction (notificationPlugin, "registerCallbackSameOrBelow"); if (!func) { return 0; } // Call register function ElektraNotificationPluginRegisterCallbackSameOrBelow registerFunc = (ElektraNotificationPluginRegisterCallbackSameOrBelow) func; return registerFunc (notificationPlugin, key, callback, context); }
/** * @brief Wrapper for open(). * * @param parentKey containing the filename * @param flags file access mode * @param mode file mode bits when file is created * * @return file descriptor */ static int openFile (Key * parentKey, int flag, mode_t mode) { int fd; ELEKTRA_LOG_DEBUG ("opening file %s", keyString (parentKey)); if ((fd = open (keyString (parentKey), flag, mode)) == -1) { ELEKTRA_LOG_WARNING ("error opening file %s", keyString (parentKey)); } return fd; }
/** * @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; }
int elektraNotificationClose (KDB * kdb) { // Make sure kdb is not null if (!kdb) { ELEKTRA_LOG_WARNING ("kdb was not set"); return 0; } // Make sure open was called if (!kdb->notificationPlugin) { ELEKTRA_LOG_WARNING ("elektraNotificationOpen not called before elektraPluginClose"); return 0; } Plugin * notificationPlugin = kdb->notificationPlugin; // Unmount the plugin int result = unmountGlobalPlugin (kdb, notificationPlugin); if (!result) { return 0; } // Unload the notification plugin result = unloadPlugin (notificationPlugin); if (!result) { return 0; } // Close notification for plugins pluginsCloseNotification (kdb); elektraFree (kdb->notificationCallbackContext); kdb->notificationPlugin = NULL; kdb->notificationCallbackContext = NULL; return 1; }
/** * @internal * Get notification plugin from kdb. * * @param kdb KDB handle * @return Notification plugin handle or NULL if not present */ static Plugin * getNotificationPlugin (KDB * kdb) { ELEKTRA_NOT_NULL (kdb); if (kdb->notificationPlugin) { return kdb->notificationPlugin; } else { ELEKTRA_LOG_WARNING ( "notificationPlugin not set. use " "elektraNotificationOpen before calling other " "elektraNotification-functions"); return NULL; } }
/** * @internal * Load plugin by name. * * Uses module cache from KDB handle. * The plugin only needs to be closed after use. * * @param kdb KDB handle * @param name Plugin name * @param config Plugin configuration * @return Plugin handle or NULL on error */ static Plugin * loadPlugin (KDB * kdb, char * name, KeySet * config) { // Load required plugin Key * errorKey = keyNew (0); KeySet * moduleCache = kdb->modules; // use kdb module cache Plugin * plugin = elektraPluginOpen (name, moduleCache, config, errorKey); int hasError = keyGetMeta (errorKey, "error") != NULL; keyDel (errorKey); if (!plugin || hasError) { ELEKTRA_LOG_WARNING ("elektraPluginOpen failed!\n"); return NULL; } return plugin; }
/** * @internal * Unload plugin by plugin handle. * * @param plugin Plugin handle * @retval 0 on error * @retval 1 on success */ static int unloadPlugin (Plugin * plugin) { ELEKTRA_NOT_NULL (plugin); Key * errorKey = keyNew (0); int result = elektraPluginClose (plugin, errorKey); int hasError = keyGetMeta (errorKey, "error") != NULL; keyDel (errorKey); if (!result || hasError) { ELEKTRA_LOG_WARNING ("elektraPluginClose failed: result=%d", result); return 0; } else { return 1; } }
/** * @internal * Convert plament name to list plugin's position type. * * The list plugin distinguishes between three position types: get, set & error. * Plament names are converted to one of these position types. * * @param placement Placement name * @return Placement type for list plugin or NULL on unknown placement * name */ static char * placementToListPositionType (char * placement) { if (strcmp (placement, "prerollback") == 0) { return "error"; } else if (strcmp (placement, "rollback") == 0) { return "error"; } else if (strcmp (placement, "postrollback") == 0) { return "error"; } else if (strcmp (placement, "getresolver") == 0) { return "get"; } else if (strcmp (placement, "pregetstorage") == 0) { return "get"; } else if (strcmp (placement, "getstorage") == 0) { return "get"; } else if (strcmp (placement, "postgetstorage") == 0) { return "get"; } else if (strcmp (placement, "setresolver") == 0) { return "set"; } else if (strcmp (placement, "postgetcleanup") == 0) { return "get"; } else if (strcmp (placement, "presetstorage") == 0) { return "set"; } else if (strcmp (placement, "setstorage") == 0) { return "set"; } else if (strcmp (placement, "presetstorage") == 0) { return "set"; } else if (strcmp (placement, "setstorage") == 0) { return "set"; } else if (strcmp (placement, "presetcleanup") == 0) { return "set"; } else if (strcmp (placement, "precommit") == 0) { return "set"; } else if (strcmp (placement, "commit") == 0) { return "set"; } else if (strcmp (placement, "postcommit") == 0) { return "set"; } else { ELEKTRA_LOG_WARNING ("unknown placement name \"%s\"", placement); return NULL; } }
/** * @internal * Converts a placement name to an index in the globalPlugins array of * the internal KDB structure. * * @param placement Placement name * @return Placement index or -1 on unknown placement name */ static int placementToPosition (char * placement) { if (strcmp (placement, "prerollback") == 0) { return PREROLLBACK; } else if (strcmp (placement, "rollback") == 0) { return ROLLBACK; } else if (strcmp (placement, "postrollback") == 0) { return POSTROLLBACK; } else if (strcmp (placement, "getresolver") == 0) { return GETRESOLVER; } else if (strcmp (placement, "pregetstorage") == 0) { return PREGETSTORAGE; } else if (strcmp (placement, "getstorage") == 0) { return GETSTORAGE; } else if (strcmp (placement, "postgetstorage") == 0) { return POSTGETSTORAGE; } else if (strcmp (placement, "setresolver") == 0) { return SETRESOLVER; } else if (strcmp (placement, "postgetcleanup") == 0) { return POSTGETCLEANUP; } else if (strcmp (placement, "presetstorage") == 0) { return PRESETSTORAGE; } else if (strcmp (placement, "setstorage") == 0) { return SETSTORAGE; } else if (strcmp (placement, "presetstorage") == 0) { return PRESETSTORAGE; } else if (strcmp (placement, "setstorage") == 0) { return SETSTORAGE; } else if (strcmp (placement, "presetcleanup") == 0) { return PRESETCLEANUP; } else if (strcmp (placement, "precommit") == 0) { return PRECOMMIT; } else if (strcmp (placement, "commit") == 0) { return COMMIT; } else if (strcmp (placement, "postcommit") == 0) { return POSTCOMMIT; } else { ELEKTRA_LOG_WARNING ("unknown placement name \"%s\"", placement); return -1; } }
int elektraNotificationOpen (KDB * kdb) { // Make sure kdb is not null if (!kdb) { ELEKTRA_LOG_WARNING ("kdb was not set"); return 0; } // Allow open only once if (kdb->notificationPlugin) { ELEKTRA_LOG_WARNING ("elektraNotificationOpen already called for kdb"); return 0; } Plugin * notificationPlugin = loadPlugin (kdb, "internalnotification", NULL); if (!notificationPlugin) { return 0; } int mountResult = mountGlobalPlugin (kdb, notificationPlugin); if (!mountResult) { Key * errorKey = keyNew (0); elektraPluginClose (notificationPlugin, errorKey); keyDel (errorKey); return 0; } // Create context for notification callback ElektraNotificationCallbackContext * context = elektraMalloc (sizeof (*context)); if (context == NULL) { unmountGlobalPlugin (kdb, notificationPlugin); Key * errorKey = keyNew (0); elektraPluginClose (notificationPlugin, errorKey); keyDel (errorKey); return 0; } context->kdb = kdb; context->kdbUpdate = &elektraNotificationKdbUpdate; context->notificationPlugin = notificationPlugin; // Get notification callback from notification plugin size_t func = elektraPluginGetFunction (notificationPlugin, "notificationCallback"); if (!func) { unmountGlobalPlugin (kdb, notificationPlugin); Key * errorKey = keyNew (0); elektraPluginClose (notificationPlugin, errorKey); keyDel (errorKey); return 0; } ElektraNotificationCallback notificationCallback = (ElektraNotificationCallback) func; // Open notification for plugins pluginsOpenNotification (kdb, notificationCallback, context); kdb->notificationPlugin = notificationPlugin; kdb->notificationCallbackContext = context; return 1; }
/* See documentation in header file. */ int ini_parse_file (FILE * file, const struct IniConfig * config, void * user) { /* Uses a fair bit of stack (use heap instead if you need to) */ char * line; char section[MAX_SECTION] = ""; char prev_name[MAX_NAME] = ""; char * start; char * end; char * name; char * value; char delim = config->delim; int lineno = 0; int error = 0; line = (char *)malloc (INI_MAX_LINE); ELEKTRA_LOG_DEBUG ("Allocated memory for line"); if (!line) { return -2; } /* Scan through file line by line */ while (fgets (line, INI_MAX_LINE, file) != NULL) { lineno++; ELEKTRA_LOG_DEBUG ("Read line %d with content “%s”", lineno, line); start = line; #if INI_ALLOW_BOM if (lineno == 1 && (unsigned char)start[0] == 0xEF && (unsigned char)start[1] == 0xBB && (unsigned char)start[2] == 0xBF) { start += 3; config->bomHandler (user, 1); } else { config->bomHandler (user, 0); } #endif if (*start == '\n') { if (!config->commentHandler (user, "") && !error) error = lineno; continue; } start = lskip (line); if (*start == '\0') { if (!config->commentHandler (user, "") && !error) error = lineno; continue; } if (isContinuation (line, config) && config->supportMultiline && *prev_name) { start = line + strlen (config->continuationString); if (*start == '"') ++start; end = line + (strlen (line) - 1); while ((*end != '"') && (!isprint (*end)) && (end > start)) { if (*end == '\n') *end = '\0'; --end; } if (*end == '"') *end = '\0'; if (!config->keyHandler (user, section, prev_name, start, 1) && !error) error = lineno; } else if (isSection (line)) { ELEKTRA_LOG_DEBUG ("Line contains a section"); end = line + (strlen (line) - 1); while (end > start) { if (*end == ']') break; --end; } ++start; if (*end == ']') { *end = '\0'; strncpy0 (section, start, sizeof (section)); *prev_name = '\0'; ELEKTRA_LOG_DEBUG ("Found section “%s”", section); size_t numberBackslashes = 0; for (char * endSection = section + strlen (section) - 1; endSection >= section && *endSection == '\\'; endSection--) { numberBackslashes++; } if (numberBackslashes % 2 != 0) { ELEKTRA_LOG_WARNING ("Found uneven number of backlashes at end of section"); error = lineno; break; } if (!config->sectionHandler (user, section) && !error) error = lineno; } else { end = line + (strlen (line) - 1); if (*end == '\n') { strncpy0 (section, start, sizeof (section)); while (fgets (line, INI_MAX_LINE, file)) { end = line + (strlen (line) - 1); while ((end > line) && *end != ']') --end; if (*end == ']') { *end = '\0'; strncpy0 (section + strlen (section), line, sizeof (section) - strlen (section)); *prev_name = '\0'; if (!config->sectionHandler (user, section) && !error) error = lineno; break; } else { strncpy0 (section + strlen (section), line, sizeof (section) - strlen (section)); } } } else { error = lineno; } } } else if (isComment (line)) { start = line; end = line + (strlen (line) - 1); if (*end == '\n') *end = '\0'; if (!config->commentHandler (user, start) && !error) error = lineno; } else { ELEKTRA_LOG_DEBUG ("Line contains a key"); char * ptr = start; unsigned int assign = 0; ELEKTRA_LOG_DEBUG ("Search for delimiter “%c”", delim); while (*ptr) { if (*ptr == delim) { ++assign; } ++ptr; } if (assign == 1) { ELEKTRA_LOG_DEBUG ("Found exactly one delimiter"); name = start; end = strchr (start, delim); if (*name == '"') { ELEKTRA_LOG_DEBUG ("Name starts with double quote character"); ++name; if (*(end - 2) == '"') { *(end - 2) = '\0'; } else if (*(end - 1) == '"') { *(end - 1) = '\0'; } else { ELEKTRA_LOG_DEBUG ("Did not find closing double quote characters in current line"); strncpy0 (prev_name, name, sizeof (prev_name)); while (fgets (line, INI_MAX_LINE, file)) { ELEKTRA_LOG_DEBUG ("Read continuation line with content “%s”", line); end = line + (strlen (line) - 1); while (end > line && *end != '"') --end; if (*end == '"') { ELEKTRA_LOG_DEBUG ("Found closing double quote character"); *(end++) = '\0'; strncpy0 (prev_name + strlen (prev_name), line, sizeof (prev_name) - strlen (prev_name)); break; } else { ELEKTRA_LOG_DEBUG ("Found name continuation"); strncpy (prev_name + strlen (prev_name), line, sizeof (prev_name) - strlen (prev_name)); } ELEKTRA_LOG_DEBUG ("New extended name is “%s”", prev_name); } name = prev_name; ELEKTRA_LOG_DEBUG ("Name of key is “%s”", name); } } if (*end != delim) { ELEKTRA_LOG_DEBUG ("Search for delimiter in “%s”", end); ptr = lskip (end + 1); end = strchr (ptr, delim); if (end && *end == delim) { *end = '\0'; ELEKTRA_LOG_DEBUG ("Found delimiter – New name is “%s”", end); } else { ELEKTRA_LOG_WARNING ("Unable to find delimiter"); error = lineno; break; } } else { *end = '\0'; } if (name != prev_name && end > line) { rstrip (end - 1); } value = lskip (end + 1); end = find_char_or_comment (value, '\0'); if (*end == ';') *end = '\0'; rstrip (value); if (*value == '"') { *(value++) = '\0'; while ((*end != '"') && !isprint (*end) && end > value) --end; if (*end == '"') *end = '\0'; } if (prev_name != name) strncpy0 (prev_name, name, sizeof (prev_name)); if (!config->keyHandler (user, section, name, value, 0) && !error) error = lineno; } else if (assign == 0) { ELEKTRA_LOG_DEBUG ("Found no delimiter"); if (*start == '"') { ELEKTRA_LOG_DEBUG ("Found initial double quote character"); ++start; end = line + (strlen (line) - 1); while (end > start && *end != '"') --end; if (*end == '"' && end != start) { *end = '\0'; if (!config->keyHandler (user, section, start, NULL, 0) && !error) error = lineno; } else { ELEKTRA_LOG_DEBUG ("Did not find closing double quote character"); strncpy0 (prev_name, start, sizeof (prev_name)); while (fgets (line, INI_MAX_LINE, file)) { end = line + (strlen (line) - 1); ELEKTRA_LOG_DEBUG ("Read continuation line with content “%s”", line); while (end > line && *end != '"') --end; if (*end == '"') { ELEKTRA_LOG_DEBUG ("Found closing double quote character"); *end = '\0'; strncpy0 (prev_name + strlen (prev_name), line, sizeof (prev_name) - strlen (prev_name)); break; } else { ELEKTRA_LOG_DEBUG ("Found name continuation"); strncpy (prev_name + strlen (prev_name), line, sizeof (prev_name) - strlen (prev_name)); } ELEKTRA_LOG_DEBUG ("New extended name is “%s”", prev_name); } name = prev_name; ptr = end + 1; end = strchr (ptr, '='); if (!end) end = strchr (ptr, ':'); if (!end) { if (!config->keyHandler (user, section, name, NULL, 0) && !error) error = lineno; } else { *end = '\0'; value = lskip (end + 1); if (*value == '"') end = find_char_or_comment (value, '\0'); if (*end == ';') *end = '\0'; rstrip (value); if (*value == '"' || *(value + 1) == '"') { if (*value == '"') *(value++) = '\0'; else if (*(value + 1) == '"') { *(value + 1) = '\0'; value += 2; } while ((*end != '"') && !isprint (*end) && end > value) --end; if (*end == '"') *end = '\0'; } if (prev_name != name) strncpy0 (prev_name, name, sizeof (prev_name)); if (!config->keyHandler (user, section, name, value, 0) && !error) error = lineno; } } } else { name = rstrip (start); strncpy0 (prev_name, name, sizeof (prev_name)); if (!config->keyHandler (user, section, name, NULL, 0) && !error) error = lineno; } } else { ELEKTRA_LOG_DEBUG ("Found multiple delimiters"); ptr = start + 1; while (*ptr) { if (*ptr == delim) { if (*(ptr + 1) == '"' || *(ptr + 2) == '"' || *(ptr - 1) == '"' || *(ptr - 2) == '"') break; } ++ptr; } if (*ptr) { ELEKTRA_LOG_DEBUG ("Found double quote character"); char tmpDel[4] = { ' ', delim, ' ', '\0' }; end = strstr (ptr, tmpDel); name = NULL; if (end) { // keyname == "=" or " = " where '=' is the delimiter if (*(ptr + 1) == '"') { *(ptr + 1) = '\0'; } else if (*(ptr + 2) == '"') { *(ptr + 2) = '\0'; } if (*(ptr - 1) == '"') *(ptr - 1) = '\0'; else if (*(ptr - 2) == '"') *(ptr - 2) = '\0'; name = ptr; } else if (*ptr == delim) { *ptr = '\0'; rstrip (start); if (*start == '"') ++start; if (*(ptr - 1) == '"') *(ptr - 1) = '\0'; else if (*(ptr - 2) == '"') *(ptr - 2) = '\0'; name = start; } else { if (!end) end = strrstr (start + 1, tmpDel); *end = '\0'; ptr = end + 2; rstrip (start); name = start; } value = ptr + 1; end = find_char_or_comment (value, '\0'); if (*end == ';') *end = '\0'; rstrip (value); if (*value == '"' || *(value + 1) == '"') { if (*value == '"') *(value++) = '\0'; else if (*(value + 1) == '"') { *(value + 1) = '\0'; value += 2; } while ((*end != '"') && !isprint (*end) && end > value) --end; if (*end == '"') *end = '\0'; } } else { ELEKTRA_LOG_DEBUG ("Found no double quote character"); rstrip (start); name = start; end = strchr (start, delim); if (!end) { ELEKTRA_LOG_DEBUG ("Found no delimiter"); value = NULL; } else { ELEKTRA_LOG_DEBUG ("Found delimiter"); if (*end == delim) *end = '\0'; rstrip (end - 1); value = lskip (end + 1); rstrip (value); if (*value == '"') { *(value++) = '\0'; while ((*end != '"') && !isprint (*end) && end > value) --end; if (*end == '"') *end = '\0'; } } } strncpy0 (prev_name, name, sizeof (prev_name)); if (!config->keyHandler (user, section, name, value, 0) && !error) error = lineno; } } #if INI_STOP_ON_FIRST_ERROR if (error) break; #endif } free (line); return error; }
/** * @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; }
static void test_mmap_wrong_magic_keyset (const char * tmpFile) { // first write a mmap file { Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); KeySet * conf = ksNew (0, KS_END); PLUGIN_OPEN ("mmapstorage"); KeySet * ks = simpleTestKeySet (); succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful"); keyDel (parentKey); ksDel (ks); PLUGIN_CLOSE (); } // now manipulate magic keyset inside the mapped region FILE * fp; if ((fp = fopen (tmpFile, "r+")) == 0) { yield_error ("fopen() error"); } struct stat sbuf; if (stat (tmpFile, &sbuf) == -1) { yield_error ("stat() error"); } int fd = fileno (fp); char * mappedRegion = mmap (0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mappedRegion == MAP_FAILED) { ELEKTRA_LOG_WARNING ("error mapping file %s\nmmapSize: " ELEKTRA_STAT_ST_SIZE_F, tmpFile, sbuf.st_size); yield_error ("mmap() error"); return; } if (fp) { fclose (fp); } KeySet * magicKs = (KeySet *) (mappedRegion + sizeof (MmapHeader)); magicKs->size = 1234; // magic keyset contains SIZE_MAX here if (msync ((void *) mappedRegion, sbuf.st_size, MS_SYNC) != 0) { yield_error ("msync() error"); return; } if (munmap (mappedRegion, sbuf.st_size) != 0) { yield_error ("munmap() error"); return; } // manipulated magic keyset should be detected now { // we expect an error here Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); KeySet * conf = ksNew (0, KS_END); PLUGIN_OPEN ("mmapstorage"); KeySet * ks = ksNew (0, KS_END); succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR, "kdbGet did not detect wrong magic keyset"); keyDel (parentKey); ksDel (ks); PLUGIN_CLOSE (); } }
static void test_mmap_wrong_format_version (const char * tmpFile) { // first write a mmap file { Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); KeySet * conf = ksNew (0, KS_END); PLUGIN_OPEN ("mmapstorage"); KeySet * ks = simpleTestKeySet (); succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful"); keyDel (parentKey); ksDel (ks); PLUGIN_CLOSE (); } // set wrong version number in mmap header FILE * fp; if ((fp = fopen (tmpFile, "r+")) == 0) { yield_error ("fopen() error"); } struct stat sbuf; if (stat (tmpFile, &sbuf) == -1) { yield_error ("stat() error"); } int fd = fileno (fp); char * mappedRegion = mmap (0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mappedRegion == MAP_FAILED) { ELEKTRA_LOG_WARNING ("error mapping file %s\nmmapSize: " ELEKTRA_STAT_ST_SIZE_F, tmpFile, sbuf.st_size); yield_error ("mmap() error"); return; } if (fp) { fclose (fp); } MmapHeader * mmapHeader = (MmapHeader *) mappedRegion; mmapHeader->formatVersion = ELEKTRA_MMAP_FORMAT_VERSION + 1; if (msync ((void *) mappedRegion, sbuf.st_size, MS_SYNC) != 0) { yield_error ("msync() error"); return; } if (munmap (mappedRegion, sbuf.st_size) != 0) { yield_error ("munmap() error"); return; } // wrong format version should be detected now { // we expect an error here Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); KeySet * conf = ksNew (0, KS_END); PLUGIN_OPEN ("mmapstorage"); KeySet * ks = ksNew (0, KS_END); succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR, "kdbGet did not detect wrong format version"); keyDel (parentKey); ksDel (ks); PLUGIN_CLOSE (); } }