[gnome-keyring] [daemon] Rework the startup again, singleton crontrolled via dbus.



commit 43590189620c9dc5cda9854fef899fe89b24d308
Author: Stef Walter <stef memberwebs com>
Date:   Sun Feb 14 05:15:10 2010 +0000

    [daemon] Rework the startup again, singleton crontrolled via dbus.
    
    Account for starting dbus via the autostart files (ie: various
    components individually), but without a gnome-keyring-daemon running
    from pam. This previously caused multiple processes. We use a dbus
    singleton in this case.

 daemon/control/gkd-control-client.c |    4 +-
 daemon/dbus/Makefile.am             |    1 -
 daemon/dbus/gkd-dbus-service.c      |  155 ---------------------
 daemon/dbus/gkd-dbus.c              |  252 +++++++++++++++++++++++++++++++----
 daemon/dbus/gkd-dbus.h              |    4 +
 daemon/gkd-main.c                   |   66 +++++++---
 6 files changed, 280 insertions(+), 202 deletions(-)
---
diff --git a/daemon/control/gkd-control-client.c b/daemon/control/gkd-control-client.c
index eaf9d6d..58d7d62 100644
--- a/daemon/control/gkd-control-client.c
+++ b/daemon/control/gkd-control-client.c
@@ -208,10 +208,8 @@ gkd_control_initialize (const gchar *directory, const gchar *components,
 
 	egg_buffer_uninit (&buffer);
 
-	if (!ret || res != GKD_CONTROL_RESULT_OK) {
-		g_message ("couldn't initialize running daemon");
+	if (!ret || res != GKD_CONTROL_RESULT_OK)
 		return NULL;
-	}
 
 	return env;
 }
diff --git a/daemon/dbus/Makefile.am b/daemon/dbus/Makefile.am
index 3ec7399..3a1a9ee 100644
--- a/daemon/dbus/Makefile.am
+++ b/daemon/dbus/Makefile.am
@@ -15,7 +15,6 @@ libgkd_dbus_la_SOURCES = \
 	gkd-dbus-environment.c \
 	gkd-dbus-private.h \
 	gkd-dbus-secrets.c \
-	gkd-dbus-service.c \
 	gkd-dbus-session.c \
 	gkd-dbus-util.c gkd-dbus-util.h \
 	gkd-secret-change.c gkd-secret-change.h \
diff --git a/daemon/dbus/gkd-dbus.c b/daemon/dbus/gkd-dbus.c
index 6b469f8..4688d18 100644
--- a/daemon/dbus/gkd-dbus.c
+++ b/daemon/dbus/gkd-dbus.c
@@ -26,6 +26,8 @@
 #include "gkd-dbus.h"
 #include "gkd-dbus-private.h"
 
+#include "gkd-util.h"
+
 #include "egg/egg-cleanup.h"
 #include "egg/egg-dbus.h"
 
@@ -34,35 +36,32 @@
 #include <dbus/dbus.h>
 
 static DBusConnection *dbus_conn = NULL;
-static gboolean dbus_do_session = TRUE;
+static gboolean object_registered = FALSE;
+static gboolean acquired_asked = FALSE;
+static gboolean acquired_service = FALSE;
+
+#define GNOME_KEYRING_DAEMON_SERVICE    "org.gnome.keyring"
+#define GNOME_KEYRING_DAEMON_PATH       "/org/gnome/keyring/daemon"
+#define GNOME_KEYRING_DAEMON_INTERFACE  "org.gnome.keyring.Daemon"
 
 static void
-daemon_dbus_cleanup (gpointer unused)
+cleanup_session_bus (gpointer unused)
 {
 	if (!dbus_conn)
 		return;
 
-	gkd_dbus_secrets_cleanup (dbus_conn);
-
-	if (dbus_do_session) {
-		gkd_dbus_session_cleanup (dbus_conn);
-		gkd_dbus_environment_cleanup (dbus_conn);
-	}
-
-	gkd_dbus_service_cleanup (dbus_conn);
-
 	egg_dbus_disconnect_from_mainloop (dbus_conn, NULL);
 	dbus_connection_unref (dbus_conn);
 	dbus_conn = NULL;
 }
 
-void
-gkd_dbus_setup (void)
+static gboolean
+connect_to_session_bus (void)
 {
 	DBusError derr = { 0 };
 
 	if (dbus_conn)
-		return;
+		return TRUE;
 
 	dbus_error_init (&derr);
 
@@ -71,25 +70,228 @@ gkd_dbus_setup (void)
 	if (!dbus_conn) {
 		g_message ("couldn't connect to dbus session bus: %s", derr.message);
 		dbus_error_free (&derr);
-		return;
+		return FALSE;
 	}
 
-	egg_cleanup_register (daemon_dbus_cleanup, NULL);
-
 	egg_dbus_connect_with_mainloop (dbus_conn, NULL);
-
-	/* Make sure dbus doesn't kill our app */
 	dbus_connection_set_exit_on_disconnect (dbus_conn, FALSE);
+	egg_cleanup_register (cleanup_session_bus, NULL);
+	return TRUE;
+}
 
-	/* Gnome Keyring service */
-	gkd_dbus_service_init (dbus_conn);
+static DBusHandlerResult
+message_handler_cb (DBusConnection *conn, DBusMessage *message, void *user_data)
+{
+	/*
+	 * Here we handle the requests to our own gnome-keyring DBus interfaces
+	 */
 
-	/* Session stuff */
-	if (dbus_do_session) {
-		gkd_dbus_environment_init (dbus_conn);
-		gkd_dbus_session_init (dbus_conn);
+	DBusMessageIter args;
+	DBusMessage *reply = NULL;
+
+	/* GetEnvironment */
+	if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL &&
+	    dbus_message_is_method_call (message, GNOME_KEYRING_DAEMON_INTERFACE, "GetEnvironment") &&
+	    g_str_equal (dbus_message_get_signature (message), "")) {
+
+		const gchar **env;
+		DBusMessageIter items, entry;
+		gchar **parts;
+
+		env = gkd_util_get_environment ();
+		g_return_val_if_fail (env, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+		/* Setup the result */
+		reply = dbus_message_new_method_return (message);
+		dbus_message_iter_init_append (reply, &args);
+		if (!dbus_message_iter_open_container (&args, DBUS_TYPE_ARRAY, "{ss}", &items))
+			g_return_val_if_reached (DBUS_HANDLER_RESULT_NEED_MEMORY);
+		while (*env) {
+			parts = g_strsplit (*env, "=", 2);
+			g_return_val_if_fail (parts && parts[0] && parts[1], DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+			if (!dbus_message_iter_open_container (&items, DBUS_TYPE_DICT_ENTRY, NULL, &entry) ||
+			    !dbus_message_iter_append_basic (&entry, DBUS_TYPE_STRING, &parts[0]) ||
+			    !dbus_message_iter_append_basic (&entry, DBUS_TYPE_STRING, &parts[1]) ||
+			    !dbus_message_iter_close_container (&items, &entry))
+				g_return_val_if_reached (DBUS_HANDLER_RESULT_NEED_MEMORY);
+			++env;
+		}
+		if (!dbus_message_iter_close_container (&args, &items))
+			g_return_val_if_reached (DBUS_HANDLER_RESULT_NEED_MEMORY);
+
+	/* GetControlDirectory */
+	} else if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL &&
+	           dbus_message_is_method_call (message, GNOME_KEYRING_DAEMON_INTERFACE, "GetControlDirectory") &&
+	           g_str_equal (dbus_message_get_signature (message), "")) {
+
+		/* Setup the result */
+		const gchar *directory = gkd_util_get_master_directory ();
+		reply = dbus_message_new_method_return (message);
+		dbus_message_append_args (reply, DBUS_TYPE_STRING, &directory, DBUS_TYPE_INVALID);
+
+	/* Unknown call */
+	} else {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+
+	/* Send the reply */
+	if (!dbus_connection_send (conn, reply, NULL))
+		g_return_val_if_reached (DBUS_HANDLER_RESULT_NEED_MEMORY);
+	dbus_connection_flush (conn);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusObjectPathVTable object_vtable  = {
+	NULL,
+	message_handler_cb,
+	NULL,
+};
+
+static void
+cleanup_singleton (gpointer unused)
+{
+	g_return_if_fail (dbus_conn);
+	if (object_registered)
+		dbus_connection_unregister_object_path (dbus_conn, GNOME_KEYRING_DAEMON_PATH);
+	object_registered = FALSE;
+}
+
+gboolean
+gkd_dbus_singleton_acquire (gboolean *acquired)
+{
+	DBusError derr = DBUS_ERROR_INIT;
+	dbus_uint32_t res = 0;
+	const gchar *service = NULL;
+	unsigned int flags;
+
+	g_assert (acquired);
+
+	if (!connect_to_session_bus ())
+		return FALSE;
+
+	/* First register the object */
+	if (!object_registered) {
+		if (dbus_connection_register_object_path (dbus_conn, GNOME_KEYRING_DAEMON_PATH,
+		                                          &object_vtable, NULL)) {
+			object_registered = TRUE;
+			egg_cleanup_register (cleanup_singleton, NULL);
+		} else {
+			g_message ("couldn't register dbus object path");
+		}
 	}
 
+	/* Try and grab our name */
+	if (!acquired_asked) {
+
+#ifdef WITH_TESTS
+		service = g_getenv ("GNOME_KEYRING_TEST_SERVICE");
+		if (service && service[0])
+			flags = DBUS_NAME_FLAG_ALLOW_REPLACEMENT | DBUS_NAME_FLAG_REPLACE_EXISTING;
+		else
+#endif
+			service = GNOME_KEYRING_DAEMON_SERVICE;
+
+		res = dbus_bus_request_name (dbus_conn, service, 0, &derr);
+		if (dbus_error_is_set (&derr)) {
+			g_message ("couldn't request name '%s' on session bus: %s", service, derr.message);
+			dbus_error_free (&derr);
+			return FALSE;
+		}
+
+		acquired_asked = TRUE;
+		switch (res) {
+		/* We acquired the service name */
+		case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+		case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+			acquired_service = TRUE;
+			break;
+			/* Another daemon is running */
+		case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
+		case DBUS_REQUEST_NAME_REPLY_EXISTS:
+			acquired_service = FALSE;
+			break;
+		default:
+			acquired_service = FALSE;
+			g_return_val_if_reached (FALSE);
+			break;
+		};
+	}
+
+	*acquired = acquired_service;
+	return TRUE;
+}
+
+gchar*
+gkd_dbus_singleton_control (void)
+{
+	DBusError derr = DBUS_ERROR_INIT;
+	DBusMessage *msg, *reply;
+	gchar *control = NULL;
+	const char *path;
+
+	/* Must have tried to aquire the service, and failed */
+	g_return_val_if_fail (acquired_asked, NULL);
+	g_return_val_if_fail (!acquired_service, NULL);
+
+	if (!connect_to_session_bus())
+		return NULL;
+
+	msg = dbus_message_new_method_call (GNOME_KEYRING_DAEMON_SERVICE,
+	                                    GNOME_KEYRING_DAEMON_PATH,
+	                                    GNOME_KEYRING_DAEMON_INTERFACE,
+	                                    "GetControlDirectory");
+	g_return_val_if_fail (msg, NULL);
+
+	/* Send message and get a handle for a reply */
+	reply = dbus_connection_send_with_reply_and_block (dbus_conn, msg, 1000, &derr);
+	dbus_message_unref (msg);
+
+	if (!reply) {
+		g_message ("couldn't communicate with already running daemon: %s", derr.message);
+		dbus_error_free (&derr);
+		return NULL;
+	}
+
+	/* Get out our client path */
+	if (!dbus_message_get_args (reply, &derr, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) {
+		g_message ("couldn't parse response from already running daemon: %s", derr.message);
+		dbus_error_free (&derr);
+		control = NULL;
+	} else {
+		control = g_strdup (path);
+	}
+
+	dbus_message_unref (reply);
+	return control;
+}
+
+static void
+dbus_cleanup (gpointer unused)
+{
+	g_return_if_fail (dbus_conn);
+	gkd_dbus_secrets_cleanup (dbus_conn);
+	gkd_dbus_session_cleanup (dbus_conn);
+	gkd_dbus_environment_cleanup (dbus_conn);
+}
+
+void
+gkd_dbus_setup (void)
+{
+	gboolean unused;
+
+	if (!connect_to_session_bus ())
+		return;
+
+	/* Our singleton, and internal service API */
+	gkd_dbus_singleton_acquire (&unused);
+
+	/* Session stuff */
+	gkd_dbus_environment_init (dbus_conn);
+	gkd_dbus_session_init (dbus_conn);
+
 	/* Secrets API */
 	gkd_dbus_secrets_init (dbus_conn);
+
+	egg_cleanup_register (dbus_cleanup, NULL);
 }
diff --git a/daemon/dbus/gkd-dbus.h b/daemon/dbus/gkd-dbus.h
index ed20138..93cfe9a 100644
--- a/daemon/dbus/gkd-dbus.h
+++ b/daemon/dbus/gkd-dbus.h
@@ -30,4 +30,8 @@ void      gkd_dbus_setup            (void);
 
 gboolean  gkd_dbus_secrets_startup  (void);
 
+gboolean      gkd_dbus_singleton_acquire        (gboolean *acquired);
+
+gchar*        gkd_dbus_singleton_control        (void);
+
 #endif /* GKD_DBUS_H */
diff --git a/daemon/gkd-main.c b/daemon/gkd-main.c
index e1455b4..dab83cc 100644
--- a/daemon/gkd-main.c
+++ b/daemon/gkd-main.c
@@ -453,15 +453,11 @@ print_environment (pid_t pid)
 		printf ("GNOME_KEYRING_PID=%d\n", (gint)pid);
 }
 
-
 static gboolean
-start_or_initialize_daemon (const gchar *directory)
+initialize_daemon_at (const gchar *directory)
 {
 	gchar **ourenv, **daemonenv, **e;
 
-	if (!directory)
-		return FALSE;
-
 	/* Exchange environment variables, and try to initialize daemon */
 	ourenv = gkd_util_build_environment (GKD_UTIL_IN_ENVIRONMENT);
 	daemonenv = gkd_control_initialize (directory, run_components,
@@ -477,19 +473,47 @@ start_or_initialize_daemon (const gchar *directory)
 		gkd_util_push_environment_full (*e);
 	g_strfreev (daemonenv);
 
-	/*
-	 * Now we've initialized the daemon, we need to print out
-	 * the daemon's environment for any callers, and possibly
-	 * block if we've been asked to remain in the foreground.
-	 */
-	print_environment (0);
+	return TRUE;
+}
 
-	/* TODO: Better way to sleep forever? */
-	if (run_foreground) {
-		while (sleep(0x08000000) == 0);
+static gboolean
+initialize_daemon (const gchar *directory)
+{
+	gchar *control = NULL;
+	gboolean acquired, ret;
+
+	/* A pre-specified directory to control at */
+	if (directory) {
+		if (initialize_daemon_at (directory))
+			return TRUE;
 	}
 
-	return TRUE;
+	/* An environment variable from an already running daemon */
+	directory = g_getenv (GKD_UTIL_ENV_CONTROL);
+	if (directory && directory[0]) {
+		if (initialize_daemon_at (directory))
+			return TRUE;
+	}
+
+	/* See if we can contact a daemon running, that didn't set an env variable */
+	if (!gkd_dbus_singleton_acquire (&acquired))
+		return FALSE;
+
+	/* We're the main daemon */
+	if (acquired)
+		return FALSE;
+
+	/* Figure out the path to the other daemon's control directory */
+	directory = control = gkd_dbus_singleton_control ();
+	if (directory) {
+		ret = initialize_daemon_at (directory);
+		g_free (control);
+		if (ret == TRUE)
+			return TRUE;
+	}
+
+	g_message ("couldn't initialize running daemon");
+	return FALSE;
 }
 
 static void
@@ -720,10 +744,16 @@ main (int argc, char *argv[])
 
 	/* The --start option */
 	if (run_for_start) {
-		if (!control_directory)
-			control_directory = g_getenv (GKD_UTIL_ENV_CONTROL);
-		if (start_or_initialize_daemon (control_directory))
+		if (initialize_daemon (control_directory)) {
+			/*
+			 * Another daemon was initialized, print out environment
+			 * for any callers, and quit or go comatose.
+			 */
+			print_environment (0);
+			if (run_foreground)
+				while (sleep(0x08000000) == 0);
 			cleanup_and_exit (0);
+		}
 	}
 
 	/* Initialize the main directory */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]