Esempio n. 1
0
/**
 * @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;
}
Esempio n. 2
0
/**
 * @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;
}
Esempio n. 3
0
/**
 * @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;
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
/**
 * @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;
		}
	}
}
Esempio n. 6
0
/**
 * @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);
}
Esempio n. 7
0
/**
 * @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;
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
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);
}
Esempio n. 10
0
/**
 * @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;
}
Esempio n. 11
0
/**
 * @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;
}
Esempio n. 12
0
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;
}
Esempio n. 13
0
/**
 * @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;
	}
}
Esempio n. 14
0
/**
 * @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;
}
Esempio n. 15
0
/**
 * @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;
	}
}
Esempio n. 16
0
/**
 * @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;
	}
}
Esempio n. 17
0
/**
 * @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;
	}
}
Esempio n. 18
0
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;
}
Esempio n. 19
0
/* 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;
}
Esempio n. 20
0
/**
 * @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;
}
Esempio n. 21
0
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 ();
	}
}
Esempio n. 22
0
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 ();
	}
}