summaryrefslogtreecommitdiff
path: root/mate-session/gsm-dbus-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'mate-session/gsm-dbus-client.c')
-rw-r--r--mate-session/gsm-dbus-client.c700
1 files changed, 700 insertions, 0 deletions
diff --git a/mate-session/gsm-dbus-client.c b/mate-session/gsm-dbus-client.c
new file mode 100644
index 0000000..65b393c
--- /dev/null
+++ b/mate-session/gsm-dbus-client.c
@@ -0,0 +1,700 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gsm-dbus-client.h"
+#include "gsm-marshal.h"
+
+#include "gsm-manager.h"
+
+#define GSM_DBUS_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_DBUS_CLIENT, GsmDBusClientPrivate))
+
+#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0')
+
+
+#define SM_DBUS_NAME "org.mate.SessionManager"
+#define SM_DBUS_CLIENT_PRIVATE_INTERFACE "org.mate.SessionManager.ClientPrivate"
+
+struct GsmDBusClientPrivate
+{
+ char *bus_name;
+ GPid caller_pid;
+ GsmClientRestartStyle restart_style_hint;
+ DBusConnection *connection;
+};
+
+enum {
+ PROP_0,
+ PROP_BUS_NAME
+};
+
+G_DEFINE_TYPE (GsmDBusClient, gsm_dbus_client, GSM_TYPE_CLIENT)
+
+GQuark
+gsm_dbus_client_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gsm_dbus_client_error");
+ }
+
+ return ret;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+gsm_dbus_client_error_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ ENUM_ENTRY (GSM_DBUS_CLIENT_ERROR_GENERAL, "GeneralError"),
+ ENUM_ENTRY (GSM_DBUS_CLIENT_ERROR_NOT_CLIENT, "NotClient"),
+ { 0, 0, 0 }
+ };
+
+ g_assert (GSM_DBUS_CLIENT_NUM_ERRORS == G_N_ELEMENTS (values) - 1);
+
+ etype = g_enum_register_static ("GsmDbusClientError", values);
+ }
+
+ return etype;
+}
+
+static gboolean
+setup_connection (GsmDBusClient *client)
+{
+ DBusError error;
+
+ dbus_error_init (&error);
+
+ if (client->priv->connection == NULL) {
+ client->priv->connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
+ if (client->priv->connection == NULL) {
+ if (dbus_error_is_set (&error)) {
+ g_debug ("GsmDbusClient: Couldn't connect to session bus: %s",
+ error.message);
+ dbus_error_free (&error);
+ }
+ return FALSE;
+ }
+
+ dbus_connection_setup_with_g_main (client->priv->connection, NULL);
+ dbus_connection_set_exit_on_disconnect (client->priv->connection, FALSE);
+ }
+
+ return TRUE;
+}
+
+static void
+raise_error (DBusConnection *connection,
+ DBusMessage *in_reply_to,
+ const char *error_name,
+ char *format, ...)
+{
+ char buf[512];
+ DBusMessage *reply;
+
+ va_list args;
+ va_start (args, format);
+ vsnprintf (buf, sizeof (buf), format, args);
+ va_end (args);
+
+ reply = dbus_message_new_error (in_reply_to, error_name, buf);
+ if (reply == NULL) {
+ g_error ("No memory");
+ }
+ if (! dbus_connection_send (connection, reply, NULL)) {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+}
+
+static void
+handle_end_session_response (GsmDBusClient *client,
+ DBusMessage *message)
+{
+ const char *sender;
+ DBusMessage *reply;
+ DBusError error;
+ dbus_bool_t is_ok;
+ const char *reason;
+
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_BOOLEAN, &is_ok,
+ DBUS_TYPE_STRING, &reason,
+ DBUS_TYPE_INVALID)) {
+ if (dbus_error_is_set (&error)) {
+ g_warning ("Invalid method call: %s", error.message);
+ dbus_error_free (&error);
+ }
+ raise_error (client->priv->connection,
+ message,
+ DBUS_ERROR_FAILED,
+ "There is a syntax error in the invocation of the method EndSessionResponse");
+ return;
+ }
+
+ g_debug ("GsmDBusClient: got EndSessionResponse is-ok:%d reason=%s", is_ok, reason);
+
+ /* make sure it is from our client */
+ sender = dbus_message_get_sender (message);
+ if (sender == NULL
+ || IS_STRING_EMPTY (client->priv->bus_name)
+ || strcmp (sender, client->priv->bus_name) != 0) {
+
+ raise_error (client->priv->connection,
+ message,
+ DBUS_ERROR_FAILED,
+ "Caller not recognized as the client");
+ return;
+ }
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL) {
+ g_error ("No memory");
+ }
+
+ gsm_client_end_session_response (GSM_CLIENT (client),
+ is_ok, FALSE, FALSE, reason);
+
+
+ if (! dbus_connection_send (client->priv->connection, reply, NULL)) {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+}
+
+static DBusHandlerResult
+client_dbus_filter_function (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ GsmDBusClient *client = GSM_DBUS_CLIENT (user_data);
+ const char *path;
+
+ g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+ g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+ path = dbus_message_get_path (message);
+
+ g_debug ("GsmDBusClient: obj_path=%s interface=%s method=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message));
+
+ if (dbus_message_is_method_call (message, SM_DBUS_CLIENT_PRIVATE_INTERFACE, "EndSessionResponse")) {
+ g_assert (gsm_client_peek_id (GSM_CLIENT (client)) != NULL);
+
+ if (path != NULL && strcmp (path, gsm_client_peek_id (GSM_CLIENT (client))) != 0) {
+ /* Different object path */
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ handle_end_session_response (client, message);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static GObject *
+gsm_dbus_client_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GsmDBusClient *client;
+
+ client = GSM_DBUS_CLIENT (G_OBJECT_CLASS (gsm_dbus_client_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ if (! setup_connection (client)) {
+ g_object_unref (client);
+ return NULL;
+ }
+
+ /* Object path is already registered by base class */
+ dbus_connection_add_filter (client->priv->connection, client_dbus_filter_function, client, NULL);
+
+ return G_OBJECT (client);
+}
+
+static void
+gsm_dbus_client_init (GsmDBusClient *client)
+{
+ client->priv = GSM_DBUS_CLIENT_GET_PRIVATE (client);
+}
+
+/* adapted from PolicyKit */
+static gboolean
+get_caller_info (GsmDBusClient *client,
+ const char *sender,
+ uid_t *calling_uid,
+ pid_t *calling_pid)
+{
+ gboolean res;
+ GError *error;
+ DBusGConnection *connection;
+ DBusGProxy *bus_proxy;
+
+ res = FALSE;
+ bus_proxy = NULL;
+
+ if (sender == NULL) {
+ goto out;
+ }
+
+ error = NULL;
+ connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+ if (connection == NULL) {
+ if (error != NULL) {
+ g_warning ("error getting session bus: %s", error->message);
+ g_error_free (error);
+ }
+ goto out;
+ }
+
+ bus_proxy = dbus_g_proxy_new_for_name (connection,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+
+ error = NULL;
+ if (! dbus_g_proxy_call (bus_proxy, "GetConnectionUnixUser", &error,
+ G_TYPE_STRING, sender,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, calling_uid,
+ G_TYPE_INVALID)) {
+ g_debug ("GetConnectionUnixUser() failed: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ error = NULL;
+ if (! dbus_g_proxy_call (bus_proxy, "GetConnectionUnixProcessID", &error,
+ G_TYPE_STRING, sender,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, calling_pid,
+ G_TYPE_INVALID)) {
+ g_debug ("GetConnectionUnixProcessID() failed: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ res = TRUE;
+
+ g_debug ("uid = %d", *calling_uid);
+ g_debug ("pid = %d", *calling_pid);
+
+out:
+ if (bus_proxy != NULL) {
+ g_object_unref (bus_proxy);
+ }
+ return res;
+}
+
+static void
+gsm_dbus_client_set_bus_name (GsmDBusClient *client,
+ const char *bus_name)
+{
+ uid_t uid;
+ pid_t pid;
+
+ g_return_if_fail (GSM_IS_DBUS_CLIENT (client));
+
+ g_free (client->priv->bus_name);
+
+ client->priv->bus_name = g_strdup (bus_name);
+ g_object_notify (G_OBJECT (client), "bus-name");
+
+ if (client->priv->bus_name != NULL) {
+ gboolean res;
+
+ res = get_caller_info (client, bus_name, &uid, &pid);
+ if (! res) {
+ pid = 0;
+ }
+ } else {
+ pid = 0;
+ }
+ client->priv->caller_pid = pid;
+}
+
+const char *
+gsm_dbus_client_get_bus_name (GsmDBusClient *client)
+{
+ g_return_val_if_fail (GSM_IS_DBUS_CLIENT (client), NULL);
+
+ return client->priv->bus_name;
+}
+
+static void
+gsm_dbus_client_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GsmDBusClient *self;
+
+ self = GSM_DBUS_CLIENT (object);
+
+ switch (prop_id) {
+ case PROP_BUS_NAME:
+ gsm_dbus_client_set_bus_name (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gsm_dbus_client_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GsmDBusClient *self;
+
+ self = GSM_DBUS_CLIENT (object);
+
+ switch (prop_id) {
+ case PROP_BUS_NAME:
+ g_value_set_string (value, self->priv->bus_name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gsm_dbus_client_finalize (GObject *object)
+{
+ GsmDBusClient *client = (GsmDBusClient *) object;
+
+ g_free (client->priv->bus_name);
+
+ G_OBJECT_CLASS (gsm_dbus_client_parent_class)->finalize (object);
+}
+
+static GKeyFile *
+dbus_client_save (GsmClient *client,
+ GError **error)
+{
+ g_debug ("GsmDBusClient: saving client with id %s",
+ gsm_client_peek_id (client));
+
+ /* FIXME: We still don't support client saving for D-Bus
+ * session clients */
+
+ return NULL;
+}
+
+static gboolean
+dbus_client_stop (GsmClient *client,
+ GError **error)
+{
+ GsmDBusClient *dbus_client = (GsmDBusClient *) client;
+ DBusMessage *message;
+ gboolean ret;
+
+ ret = FALSE;
+
+ /* unicast the signal to only the registered bus name */
+ message = dbus_message_new_signal (gsm_client_peek_id (client),
+ SM_DBUS_CLIENT_PRIVATE_INTERFACE,
+ "Stop");
+ if (message == NULL) {
+ goto out;
+ }
+ if (!dbus_message_set_destination (message, dbus_client->priv->bus_name)) {
+ goto out;
+ }
+
+ if (!dbus_connection_send (dbus_client->priv->connection, message, NULL)) {
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ if (message != NULL) {
+ dbus_message_unref (message);
+ }
+
+ return ret;
+}
+
+static char *
+dbus_client_get_app_name (GsmClient *client)
+{
+ /* Always use app-id instead */
+ return NULL;
+}
+
+static GsmClientRestartStyle
+dbus_client_get_restart_style_hint (GsmClient *client)
+{
+ return (GSM_DBUS_CLIENT (client)->priv->restart_style_hint);
+}
+
+static guint
+dbus_client_get_unix_process_id (GsmClient *client)
+{
+ return (GSM_DBUS_CLIENT (client)->priv->caller_pid);
+}
+
+static gboolean
+dbus_client_query_end_session (GsmClient *client,
+ guint flags,
+ GError **error)
+{
+ GsmDBusClient *dbus_client = (GsmDBusClient *) client;
+ DBusMessage *message;
+ DBusMessageIter iter;
+ gboolean ret;
+
+ ret = FALSE;
+
+ if (dbus_client->priv->bus_name == NULL) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Client is not registered");
+ return FALSE;
+ }
+
+ g_debug ("GsmDBusClient: sending QueryEndSession signal to %s", dbus_client->priv->bus_name);
+
+ /* unicast the signal to only the registered bus name */
+ message = dbus_message_new_signal (gsm_client_peek_id (client),
+ SM_DBUS_CLIENT_PRIVATE_INTERFACE,
+ "QueryEndSession");
+ if (message == NULL) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Unable to send QueryEndSession message");
+ goto out;
+ }
+ if (!dbus_message_set_destination (message, dbus_client->priv->bus_name)) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Unable to send QueryEndSession message");
+ goto out;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &flags);
+
+ if (!dbus_connection_send (dbus_client->priv->connection, message, NULL)) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Unable to send QueryEndSession message");
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ if (message != NULL) {
+ dbus_message_unref (message);
+ }
+
+ return ret;
+}
+
+static gboolean
+dbus_client_end_session (GsmClient *client,
+ guint flags,
+ GError **error)
+{
+ GsmDBusClient *dbus_client = (GsmDBusClient *) client;
+ DBusMessage *message;
+ DBusMessageIter iter;
+ gboolean ret;
+
+ ret = FALSE;
+
+ /* unicast the signal to only the registered bus name */
+ message = dbus_message_new_signal (gsm_client_peek_id (client),
+ SM_DBUS_CLIENT_PRIVATE_INTERFACE,
+ "EndSession");
+ if (message == NULL) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Unable to send EndSession message");
+ goto out;
+ }
+ if (!dbus_message_set_destination (message, dbus_client->priv->bus_name)) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Unable to send EndSession message");
+ goto out;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &flags);
+
+ if (!dbus_connection_send (dbus_client->priv->connection, message, NULL)) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Unable to send EndSession message");
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ if (message != NULL) {
+ dbus_message_unref (message);
+ }
+ return ret;
+}
+
+static gboolean
+dbus_client_cancel_end_session (GsmClient *client,
+ GError **error)
+{
+ GsmDBusClient *dbus_client = (GsmDBusClient *) client;
+ DBusMessage *message;
+ gboolean ret;
+
+ /* unicast the signal to only the registered bus name */
+ message = dbus_message_new_signal (gsm_client_peek_id (client),
+ SM_DBUS_CLIENT_PRIVATE_INTERFACE,
+ "CancelEndSession");
+ if (message == NULL) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Unable to send CancelEndSession message");
+ goto out;
+ }
+ if (!dbus_message_set_destination (message, dbus_client->priv->bus_name)) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Unable to send CancelEndSession message");
+ goto out;
+ }
+
+ if (!dbus_connection_send (dbus_client->priv->connection, message, NULL)) {
+ g_set_error (error,
+ GSM_CLIENT_ERROR,
+ GSM_CLIENT_ERROR_NOT_REGISTERED,
+ "Unable to send CancelEndSession message");
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ if (message != NULL) {
+ dbus_message_unref (message);
+ }
+
+ return ret;
+}
+
+static void
+gsm_dbus_client_dispose (GObject *object)
+{
+ GsmDBusClient *client;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSM_IS_DBUS_CLIENT (object));
+
+ client = GSM_DBUS_CLIENT (object);
+
+ dbus_connection_remove_filter (client->priv->connection, client_dbus_filter_function, client);
+
+ G_OBJECT_CLASS (gsm_dbus_client_parent_class)->dispose (object);
+}
+
+static void
+gsm_dbus_client_class_init (GsmDBusClientClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GsmClientClass *client_class = GSM_CLIENT_CLASS (klass);
+
+ object_class->finalize = gsm_dbus_client_finalize;
+ object_class->constructor = gsm_dbus_client_constructor;
+ object_class->get_property = gsm_dbus_client_get_property;
+ object_class->set_property = gsm_dbus_client_set_property;
+ object_class->dispose = gsm_dbus_client_dispose;
+
+ client_class->impl_save = dbus_client_save;
+ client_class->impl_stop = dbus_client_stop;
+ client_class->impl_query_end_session = dbus_client_query_end_session;
+ client_class->impl_end_session = dbus_client_end_session;
+ client_class->impl_cancel_end_session = dbus_client_cancel_end_session;
+ client_class->impl_get_app_name = dbus_client_get_app_name;
+ client_class->impl_get_restart_style_hint = dbus_client_get_restart_style_hint;
+ client_class->impl_get_unix_process_id = dbus_client_get_unix_process_id;
+
+ g_object_class_install_property (object_class,
+ PROP_BUS_NAME,
+ g_param_spec_string ("bus-name",
+ "bus-name",
+ "bus-name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (klass, sizeof (GsmDBusClientPrivate));
+}
+
+GsmClient *
+gsm_dbus_client_new (const char *startup_id,
+ const char *bus_name)
+{
+ GsmDBusClient *client;
+
+ client = g_object_new (GSM_TYPE_DBUS_CLIENT,
+ "startup-id", startup_id,
+ "bus-name", bus_name,
+ NULL);
+
+ return GSM_CLIENT (client);
+}