/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* gkd-dbus.c - hook into dbus, call other bits Copyright (C) 2007, 2009, Stefan Walter The Gnome Keyring Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The Gnome Keyring Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the Gnome Library; see the file COPYING.LIB. If not, . Author: Stef Walter */ #include "config.h" #include "gkd-daemon-generated.h" #include "gkd-dbus.h" #include "gkd-dbus-private.h" #include "daemon/gkd-main.h" #include "daemon/gkd-util.h" #include "egg/egg-cleanup.h" #include #include static GDBusConnection *dbus_conn = NULL; 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 cleanup_session_bus (gpointer unused) { if (!dbus_conn) return; g_clear_object (&dbus_conn); } static void on_connection_close (gpointer user_data) { g_debug ("dbus connection closed, exiting"); gkd_main_quit (); } static gboolean connect_to_session_bus (void) { GError *error = NULL; if (dbus_conn) return TRUE; dbus_conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); if (!dbus_conn) { g_message ("couldn't connect to dbus session bus: %s", error->message); g_error_free (error); return FALSE; } g_signal_connect (dbus_conn, "closed", G_CALLBACK (on_connection_close), NULL); egg_cleanup_register (cleanup_session_bus, NULL); return TRUE; } static gboolean handle_get_environment (GkdExportedDaemon *skeleton, GDBusMethodInvocation *invocation, gpointer user_data) { const gchar **env; gchar **parts; GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); for (env = gkd_util_get_environment (); *env != NULL; env++) { parts = g_strsplit (*env, "=", 2); g_variant_builder_add (&builder, "{ss}", parts[0], parts[1]); g_strfreev (parts); } gkd_exported_daemon_complete_get_environment (skeleton, invocation, g_variant_builder_end (&builder)); return TRUE; } static gboolean handle_get_control_directory (GkdExportedDaemon *skeleton, GDBusMethodInvocation *invocation, gpointer user_data) { gkd_exported_daemon_complete_get_control_directory (skeleton, invocation, gkd_util_get_master_directory ()); return TRUE; } static void cleanup_singleton (gpointer user_data) { GkdExportedDaemon *skeleton = user_data; g_return_if_fail (dbus_conn); if (object_registered) { g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (skeleton), dbus_conn); g_object_unref (skeleton); } object_registered = FALSE; } gboolean gkd_dbus_singleton_acquire (gboolean *acquired) { const gchar *service = NULL; GBusNameOwnerFlags flags = G_BUS_NAME_OWNER_FLAGS_NONE; GVariant *acquire_variant; guint res; GError *error = NULL; GkdExportedDaemon *skeleton; g_assert (acquired); if (!connect_to_session_bus ()) return FALSE; /* First register the object */ if (!object_registered) { skeleton = gkd_exported_daemon_skeleton_new (); g_signal_connect (skeleton, "handle-get-control-directory", G_CALLBACK (handle_get_control_directory), NULL); g_signal_connect (skeleton, "handle-get-environment", G_CALLBACK (handle_get_environment), NULL); g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton), dbus_conn, GNOME_KEYRING_DAEMON_PATH, &error); if (error == NULL) { object_registered = TRUE; egg_cleanup_register (cleanup_singleton, skeleton); } else { g_message ("couldn't register dbus object path: %s", error->message); g_clear_error (&error); } } /* Try and grab our name */ if (!acquired_asked) { #ifdef WITH_DEBUG service = g_getenv ("GNOME_KEYRING_TEST_SERVICE"); if (service && service[0]) flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE; else #endif service = GNOME_KEYRING_DAEMON_SERVICE; /* attempt to acquire the name */ acquire_variant = g_dbus_connection_call_sync (dbus_conn, "org.freedesktop.DBus", /* bus name */ "/org/freedesktop/DBus", /* object path */ "org.freedesktop.DBus", /* interface name */ "RequestName", /* method name */ g_variant_new ("(su)", service, flags), G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error != NULL) { g_message ("couldn't request name '%s' on session bus: %s", service, error->message); g_error_free (error); return FALSE; } acquired_asked = TRUE; g_variant_get (acquire_variant, "(u)", &res); g_variant_unref (acquire_variant); switch (res) { /* We acquired the service name */ case 1: /* DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER */ case 4: /* DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER */ acquired_service = TRUE; break; /* Another daemon is running */ case 2: /* DBUS_REQUEST_NAME_REPLY_IN_QUEUE */ case 3: /* 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) { gchar *control = NULL; GError *error = NULL; GVariant *control_variant; /* If tried to aquire the service must have failed */ g_return_val_if_fail (!acquired_service, NULL); if (!connect_to_session_bus ()) return NULL; control_variant = g_dbus_connection_call_sync (dbus_conn, GNOME_KEYRING_DAEMON_SERVICE, GNOME_KEYRING_DAEMON_PATH, GNOME_KEYRING_DAEMON_INTERFACE, "GetControlDirectory", NULL, NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, NULL, &error); if (error != NULL) { if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER)) g_message ("couldn't communicate with already running daemon: %s", error->message); g_error_free (error); return NULL; } g_variant_get (control_variant, "(s)", &control); g_variant_unref (control_variant); 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); } gboolean gkd_dbus_setup (void) { gboolean unused; if (!connect_to_session_bus ()) return FALSE; /* 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); return TRUE; } gboolean gkd_dbus_invocation_matches_caller (GDBusMethodInvocation *invocation, const char *caller) { const char *invocation_caller; invocation_caller = g_dbus_method_invocation_get_sender (invocation); if (!g_str_equal (invocation_caller, caller)) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Invalid caller"); return FALSE; } return TRUE; }