summaryrefslogtreecommitdiff
path: root/xfce4-session/xfsm-client.c
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2020-12-23 06:03:14 -0700
committerYves-Alexis Perez <corsac@debian.org>2020-12-23 06:03:14 -0700
commit59a58d510b83a1d4d20a9c2dcf0db409b308040d (patch)
tree5d11755353dc01399e6a832bb197d9509259824d /xfce4-session/xfsm-client.c
downloadxfce4-session-59a58d510b83a1d4d20a9c2dcf0db409b308040d.tar.gz
Import xfce4-session_4.16.0.orig.tar.bz2
[dgit import orig xfce4-session_4.16.0.orig.tar.bz2]
Diffstat (limited to 'xfce4-session/xfsm-client.c')
-rw-r--r--xfce4-session/xfsm-client.c963
1 files changed, 963 insertions, 0 deletions
diff --git a/xfce4-session/xfsm-client.c b/xfce4-session/xfsm-client.c
new file mode 100644
index 0000000..c079e0f
--- /dev/null
+++ b/xfce4-session/xfsm-client.c
@@ -0,0 +1,963 @@
+/* $Id$ */
+/*-
+ * Copyright (c) 2003-2004 Benedikt Meurer <benny@xfce.org>
+ * Copyright (c) 2008 Brian Tarricone <bjt23@cornell.edu>
+ * All rights reserved.
+ *
+ * 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, 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 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+
+#include <libxfsm/xfsm-util.h>
+
+#include <xfce4-session/xfsm-client.h>
+#include <xfce4-session/xfsm-manager.h>
+#include <xfce4-session/xfsm-global.h>
+#include <xfce4-session/xfsm-marshal.h>
+#include <xfce4-session/xfsm-error.h>
+#include <xfce4-session/xfsm-client-dbus.h>
+
+#define XFSM_CLIENT_OBJECT_PATH_PREFIX "/org/xfce/SessionClients/"
+
+struct _XfsmClient
+{
+ XfsmDbusClientSkeleton parent;
+
+ XfsmManager *manager;
+
+ gchar *id;
+ gchar *app_id;
+ gchar *object_path;
+ gchar *service_name;
+ guint quit_timeout;
+
+ XfsmClientState state;
+ XfsmProperties *properties;
+ SmsConn sms_conn;
+ GDBusConnection *connection;
+};
+
+typedef struct _XfsmClientClass
+{
+ XfsmDbusClientSkeletonClass parent;
+} XfsmClientClass;
+
+typedef struct
+{
+ SmProp *props;
+ gint count;
+} HtToPropsData;
+
+
+
+static void xfsm_client_finalize (GObject *obj);
+
+static void xfsm_properties_discard_command_changed (XfsmProperties *properties,
+ gchar **old_discard);
+static void xfsm_client_dbus_class_init (XfsmClientClass *klass);
+static void xfsm_client_dbus_init (XfsmClient *client);
+static void xfsm_client_iface_init (XfsmDbusClientIface *iface);
+static void xfsm_client_dbus_cleanup (XfsmClient *client);
+
+
+G_DEFINE_TYPE_WITH_CODE (XfsmClient, xfsm_client, XFSM_DBUS_TYPE_CLIENT_SKELETON, G_IMPLEMENT_INTERFACE (XFSM_DBUS_TYPE_CLIENT, xfsm_client_iface_init));
+
+
+static void
+xfsm_client_class_init (XfsmClientClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->finalize = xfsm_client_finalize;
+
+ xfsm_client_dbus_class_init (klass);
+}
+
+
+static void
+xfsm_client_init (XfsmClient *client)
+{
+
+}
+
+static void
+xfsm_client_finalize (GObject *obj)
+{
+ XfsmClient *client = XFSM_CLIENT (obj);
+
+ xfsm_client_dbus_cleanup (client);
+
+ if (client->properties != NULL)
+ xfsm_properties_free (client->properties);
+
+ if (client->quit_timeout != 0)
+ g_source_remove (client->quit_timeout);
+
+ g_free (client->id);
+ g_free (client->app_id);
+ g_free (client->object_path);
+ g_free (client->service_name);
+
+ G_OBJECT_CLASS (xfsm_client_parent_class)->finalize (obj);
+}
+
+
+
+
+static const gchar*
+get_state (XfsmClientState state)
+{
+ static const gchar *client_state[XFSM_CLIENT_STATE_COUNT] =
+ {
+ "XFSM_CLIENT_IDLE",
+ "XFSM_CLIENT_INTERACTING",
+ "XFSM_CLIENT_SAVEDONE",
+ "XFSM_CLIENT_SAVING",
+ "XFSM_CLIENT_SAVINGLOCAL",
+ "XFSM_CLIENT_WAITFORINTERACT",
+ "XFSM_CLIENT_WAITFORPHASE2",
+ "XFSM_CLIENT_DISCONNECTED"
+ };
+
+ return client_state[state];
+}
+
+static void
+xfsm_properties_discard_command_changed (XfsmProperties *properties,
+ gchar **old_discard)
+{
+ gchar **new_discard;
+
+ g_return_if_fail (properties != NULL);
+ g_return_if_fail (old_discard != NULL);
+
+ new_discard = xfsm_properties_get_strv (properties, SmDiscardCommand);
+
+ if (!xfsm_strv_equal (old_discard, new_discard))
+ {
+ xfsm_verbose ("Client Id = %s, running old discard command.\n\n",
+ properties->client_id);
+
+ g_spawn_sync (xfsm_properties_get_string(properties, SmCurrentDirectory),
+ old_discard,
+ xfsm_properties_get_strv(properties, SmEnvironment),
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
+ }
+}
+
+
+static void
+xfsm_client_signal_prop_change (XfsmClient *client,
+ const gchar *name)
+{
+ const GValue *value;
+ GVariant *variant = NULL;
+ XfsmProperties *properties = client->properties;
+
+ value = xfsm_properties_get (properties, name);
+ if (value)
+ {
+ /* convert the gvalue to gvariant because gdbus requires it */
+ if (G_VALUE_HOLDS_STRING (value))
+ {
+ variant = g_dbus_gvalue_to_gvariant(value, G_VARIANT_TYPE_STRING);
+ }
+ else if (G_VALUE_HOLDS_UCHAR (value))
+ {
+ variant = g_dbus_gvalue_to_gvariant(value, G_VARIANT_TYPE ("y"));
+ }
+ else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
+ {
+ variant = g_dbus_gvalue_to_gvariant(value, G_VARIANT_TYPE_STRING_ARRAY);
+ }
+ else
+ {
+ g_warning ("xfsm_client.c:xfsm_client_signal_prop_change: Value type not supported");
+ return;
+ }
+
+// xfsm_dbus_client_emit_sm_property_changed (XFSM_DBUS_CLIENT (client), name, variant);
+ g_variant_unref (variant);
+ }
+}
+
+
+
+XfsmClient*
+xfsm_client_new (XfsmManager *manager,
+ SmsConn sms_conn,
+ GDBusConnection *connection)
+{
+ XfsmClient *client;
+
+ client = g_object_new (XFSM_TYPE_CLIENT, NULL);
+
+ client->manager = manager;
+ client->sms_conn = sms_conn;
+ client->connection = g_object_ref (connection);
+ client->state = XFSM_CLIENT_IDLE;
+
+ return client;
+}
+
+
+void
+xfsm_client_set_initial_properties (XfsmClient *client,
+ XfsmProperties *properties)
+{
+ g_return_if_fail (XFSM_IS_CLIENT (client));
+ g_return_if_fail (properties != NULL);
+
+ if (client->properties != NULL)
+ xfsm_properties_free (client->properties);
+ client->properties = properties;
+
+ client->id = g_strdup (properties->client_id);
+
+ g_free (client->object_path);
+ client->object_path = g_strconcat (XFSM_CLIENT_OBJECT_PATH_PREFIX,
+ client->id, NULL);
+ g_strcanon (client->object_path + strlen (XFSM_CLIENT_OBJECT_PATH_PREFIX),
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_",
+ '_');
+
+ xfsm_client_dbus_init (client);
+}
+
+
+XfsmClientState
+xfsm_client_get_state (XfsmClient *client)
+{
+ g_return_val_if_fail (XFSM_IS_CLIENT (client), XFSM_CLIENT_DISCONNECTED);
+ return client->state;
+}
+
+
+
+static const gchar*
+get_client_id (XfsmClient *client)
+{
+ const gchar *client_id;
+
+ if (client->app_id)
+ client_id = client->app_id;
+ else
+ client_id = client->id;
+
+ return client_id;
+}
+
+
+
+void
+xfsm_client_set_state (XfsmClient *client,
+ XfsmClientState state)
+{
+ g_return_if_fail (XFSM_IS_CLIENT (client));
+
+ if (G_LIKELY (client->state != state))
+ {
+ XfsmClientState old_state = client->state;
+ client->state = state;
+ xfsm_dbus_client_emit_state_changed (XFSM_DBUS_CLIENT (client), old_state, state);
+
+ xfsm_verbose ("%s client state was %s and now is %s\n", get_client_id (client), get_state(old_state), get_state(state));
+
+ /* During a save, we need to ask the client if it's ok to shutdown */
+ if (state == XFSM_CLIENT_SAVING && xfsm_manager_get_state (client->manager) == XFSM_MANAGER_SHUTDOWN)
+ {
+ xfsm_dbus_client_emit_query_end_session (XFSM_DBUS_CLIENT (client), 1);
+ }
+ else if (state == XFSM_CLIENT_SAVING && xfsm_manager_get_state (client->manager) == XFSM_MANAGER_SHUTDOWNPHASE2)
+ {
+ xfsm_dbus_client_emit_end_session(XFSM_DBUS_CLIENT (client), 1);
+ }
+ }
+}
+
+
+const gchar *
+xfsm_client_get_id (XfsmClient *client)
+{
+ g_return_val_if_fail (XFSM_IS_CLIENT (client), NULL);
+ return client->id;
+}
+
+
+const gchar *
+xfsm_client_get_app_id (XfsmClient *client)
+{
+ g_return_val_if_fail (XFSM_IS_CLIENT (client), NULL);
+ return client->app_id;
+}
+
+
+SmsConn
+xfsm_client_get_sms_connection (XfsmClient *client)
+{
+ g_return_val_if_fail (XFSM_IS_CLIENT (client), NULL);
+ return client->sms_conn;
+}
+
+
+XfsmProperties *
+xfsm_client_get_properties (XfsmClient *client)
+{
+ g_return_val_if_fail (XFSM_IS_CLIENT (client), NULL);
+ return client->properties;
+}
+
+
+XfsmProperties *
+xfsm_client_steal_properties (XfsmClient *client)
+{
+ XfsmProperties *properties;
+
+ g_return_val_if_fail(XFSM_IS_CLIENT (client), NULL);
+
+ properties = client->properties;
+ client->properties = NULL;
+
+ return properties;
+}
+
+
+void
+xfsm_client_merge_properties (XfsmClient *client,
+ SmProp **props,
+ gint num_props)
+{
+ XfsmProperties *properties;
+ SmProp *prop;
+ gint n;
+
+ g_return_if_fail (XFSM_IS_CLIENT (client));
+ g_return_if_fail (client->properties != NULL);
+
+ properties = client->properties;
+
+ for (n = 0; n < num_props; ++n)
+ {
+ gchar **old_discard = NULL;
+
+ prop = props[n];
+
+ if (!strcmp (prop->name, SmDiscardCommand))
+ {
+ old_discard = xfsm_properties_get_strv (properties, SmDiscardCommand);
+ if (old_discard)
+ old_discard = g_strdupv (old_discard);
+ }
+
+ if (xfsm_properties_set_from_smprop (properties, prop))
+ {
+ if (old_discard)
+ xfsm_properties_discard_command_changed (properties, old_discard);
+
+ xfsm_client_signal_prop_change (client, prop->name);
+ }
+
+ g_strfreev (old_discard);
+ }
+}
+
+
+void
+xfsm_client_delete_properties (XfsmClient *client,
+ gchar **prop_names,
+ gint num_props)
+{
+ XfsmProperties *properties;
+ gint n;
+
+ g_return_if_fail (XFSM_IS_CLIENT (client));
+ g_return_if_fail (client->properties != NULL);
+
+ properties = client->properties;
+
+ for (n = 0; n < num_props; ++n)
+ {
+ if (xfsm_properties_remove (properties, prop_names[n]))
+ {
+ xfsm_dbus_client_emit_sm_property_deleted (XFSM_DBUS_CLIENT (client), prop_names[n]);
+ }
+ }
+}
+
+
+const gchar *
+xfsm_client_get_object_path (XfsmClient *client)
+{
+ g_return_val_if_fail (XFSM_IS_CLIENT (client), NULL);
+ return client->object_path;
+}
+
+
+
+
+void
+xfsm_client_set_service_name (XfsmClient *client,
+ const gchar *service_name)
+{
+ g_free (client->service_name);
+ client->service_name = g_strdup (service_name);
+}
+
+
+const gchar*
+xfsm_client_get_service_name (XfsmClient *client)
+{
+ return client->service_name;
+}
+
+
+
+static void
+xfsm_client_save_restart_command (XfsmClient *client)
+{
+ XfsmProperties *properties = client->properties;
+ gchar *input;
+ gchar *output = NULL;
+ gint exit_status;
+ GError *error = NULL;
+
+ input = g_strdup_printf ("ps -p %u -o args=", properties->pid);
+
+ if(g_spawn_command_line_sync (input, &output, NULL, &exit_status, &error))
+ {
+ gchar **strv = g_new0(gchar*, 2);
+
+ /* remove the newline at the end of the string */
+ output[strcspn(output, "\n")] = 0;
+
+ strv[0] = output;
+ strv[1] = NULL;
+
+ xfsm_verbose ("%s restart command %s\n", input, output);
+ xfsm_properties_set_strv (properties, "RestartCommand", strv);
+ }
+ else
+ {
+ xfsm_verbose ("Failed to get the process command line using the command %s, error was %s\n", input, error->message);
+ }
+
+ g_free (input);
+}
+
+
+
+static void
+xfsm_client_save_program_name (XfsmClient *client)
+{
+ XfsmProperties *properties = client->properties;
+ gchar *input;
+ gchar *output = NULL;
+ gint exit_status;
+ GError *error = NULL;
+
+ input = g_strdup_printf ("ps -p %u -o comm=", properties->pid);
+
+ if(g_spawn_command_line_sync (input, &output, NULL, &exit_status, &error))
+ {
+ /* remove the newline at the end of the string */
+ output[strcspn(output, "\n")] = 0;
+
+ xfsm_verbose ("%s program name %s\n", input, output);
+ xfsm_properties_set_string (properties, "Program", output);
+ }
+ else
+ {
+ xfsm_verbose ("Failed to get the process command line using the command %s, error was %s\n", input, error->message);
+ }
+
+ g_free (input);
+}
+
+
+
+static void
+xfsm_client_save_desktop_file (XfsmClient *client)
+{
+ XfsmProperties *properties = client->properties;
+ GDesktopAppInfo *app_info = NULL;
+ const gchar *app_id = client->app_id;
+ gchar *desktop_file = NULL;
+
+ if (app_id == NULL)
+ return;
+
+ /* First attempt to append .desktop to the filename since the desktop file
+ * may match the application id. I.e. org.gnome.Devhelp.desktop matches
+ * the GApplication org.gnome.Devhelp
+ */
+ desktop_file = g_strdup_printf("%s.desktop", app_id);
+ xfsm_verbose ("looking for desktop file %s\n", desktop_file);
+ app_info = g_desktop_app_info_new (desktop_file);
+
+ if (app_info == NULL || g_desktop_app_info_get_filename (app_info) == NULL)
+ {
+ gchar *begin;
+ g_free (desktop_file);
+ desktop_file = NULL;
+
+ /* Find the last '.' and try to load that. This is because the app_id is
+ * in the funky org.xfce.parole format and the desktop file may just be
+ * parole.desktop */
+ begin = g_strrstr (app_id, ".");
+
+ /* maybe it doesn't have dots in the name? */
+ if (begin == NULL || begin++ == NULL)
+ return;
+
+ desktop_file = g_strdup_printf ("%s.desktop", begin);
+ xfsm_verbose ("looking for desktop file %s\n", desktop_file);
+ app_info = g_desktop_app_info_new (desktop_file);
+
+ if (app_info == NULL || g_desktop_app_info_get_filename (app_info) == NULL)
+ {
+ /* Failed to get a desktop file, maybe it doesn't have one */
+ xfsm_verbose ("failed to get a desktop file for the client\n");
+ g_free (desktop_file);
+ return;
+ }
+ }
+
+ /* if we got here we found a .desktop file, save it */
+ xfsm_properties_set_string (properties, GsmDesktopFile, g_desktop_app_info_get_filename (app_info));
+
+ g_free (desktop_file);
+}
+
+
+
+
+void
+xfsm_client_set_pid (XfsmClient *client,
+ pid_t pid)
+{
+ XfsmProperties *properties;
+ gchar *pid_str;
+
+ g_return_if_fail (XFSM_IS_CLIENT (client));
+ g_return_if_fail (client->properties != NULL);
+
+ properties = client->properties;
+
+ /* save the pid */
+ properties->pid = pid;
+
+ /* convert it to a string */
+ pid_str = g_strdup_printf ("%d", pid);
+
+ /* store the string as well (so we can export it over dbus */
+ xfsm_properties_set_string (properties, "ProcessID", pid_str);
+
+ /* save the command line for the process so we can restart it if needed */
+ xfsm_client_save_restart_command (client);
+
+ /* save the program name */
+ xfsm_client_save_program_name (client);
+
+ g_free (pid_str);
+}
+
+
+void
+xfsm_client_set_app_id (XfsmClient *client,
+ const gchar *app_id)
+{
+ client->app_id = g_strdup (app_id);
+
+ /* save the desktop file */
+ xfsm_client_save_desktop_file (client);
+}
+
+
+
+static gboolean
+kill_hung_client (gpointer user_data)
+{
+ XfsmClient *client = XFSM_CLIENT (user_data);
+
+ client->quit_timeout = 0;
+
+ if (!client->properties)
+ return FALSE;
+
+ if (client->properties->pid < 2)
+ return FALSE;
+
+ xfsm_verbose ("killing unresponsive client %s\n", get_client_id (client));
+ kill (client->properties->pid, SIGKILL);
+
+ return FALSE;
+}
+
+
+
+void
+xfsm_client_terminate (XfsmClient *client)
+{
+ xfsm_verbose ("emitting stop signal for client %s\n", get_client_id (client));
+
+ /* Ask the client to shutdown gracefully */
+ xfsm_dbus_client_emit_stop (XFSM_DBUS_CLIENT (client));
+
+ /* add a timeout so we can forcefully stop the client */
+ client->quit_timeout = g_timeout_add_seconds (15, kill_hung_client, client);
+}
+
+
+
+void
+xfsm_client_end_session (XfsmClient *client)
+{
+ xfsm_verbose ("emitting end session signal for client %s\n", get_client_id (client));
+
+ /* Start the client shutdown */
+ xfsm_dbus_client_emit_end_session (XFSM_DBUS_CLIENT (client), 1);
+}
+
+
+void xfsm_client_cancel_shutdown (XfsmClient *client)
+{
+ xfsm_verbose ("emitting cancel session signal for client %s\n", get_client_id (client));
+
+ /* Cancel the client shutdown */
+ xfsm_dbus_client_emit_cancel_end_session (XFSM_DBUS_CLIENT (client));
+
+}
+
+
+
+/*
+ * dbus server impl
+ */
+
+static gboolean xfsm_client_dbus_get_id (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation);
+static gboolean xfsm_client_dbus_get_state (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation);
+static gboolean xfsm_client_dbus_get_all_sm_properties (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation);
+static gboolean xfsm_client_dbus_get_sm_properties (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation,
+ const gchar *const *arg_names);
+static gboolean xfsm_client_dbus_set_sm_properties (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation,
+ GVariant *arg_properties);
+static gboolean xfsm_client_dbus_delete_sm_properties (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation,
+ const gchar *const *arg_names);
+static gboolean xfsm_client_dbus_terminate (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation);
+static gboolean xfsm_client_dbus_end_session_response (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation,
+ gboolean arg_is_ok,
+ const gchar *arg_reason);
+
+
+
+static void
+xfsm_client_dbus_class_init (XfsmClientClass *klass)
+{
+}
+
+
+static void
+xfsm_client_dbus_init (XfsmClient *client)
+{
+ GError *error = NULL;
+
+ if (G_UNLIKELY(!client->connection))
+ {
+ g_critical ("Unable to contact D-Bus session bus: %s", error ? error->message : "Unknown error");
+ return;
+ }
+
+ xfsm_verbose ("exporting path %s\n", client->object_path);
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (XFSM_DBUS_CLIENT (client)),
+ client->connection,
+ client->object_path,
+ &error)) {
+ if (error != NULL) {
+ g_critical ("error exporting interface: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+ }
+
+ xfsm_verbose ("exported on %s\n", g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (XFSM_DBUS_CLIENT (client))));
+}
+
+static void
+xfsm_client_iface_init (XfsmDbusClientIface *iface)
+{
+ iface->handle_delete_sm_properties = xfsm_client_dbus_delete_sm_properties;
+ iface->handle_get_all_sm_properties = xfsm_client_dbus_get_all_sm_properties;
+ iface->handle_get_id = xfsm_client_dbus_get_id;
+ iface->handle_get_sm_properties = xfsm_client_dbus_get_sm_properties;
+ iface->handle_get_state = xfsm_client_dbus_get_state;
+ iface->handle_set_sm_properties = xfsm_client_dbus_set_sm_properties;
+ iface->handle_terminate = xfsm_client_dbus_terminate;
+ iface->handle_end_session_response = xfsm_client_dbus_end_session_response;
+}
+
+static void
+xfsm_client_dbus_cleanup (XfsmClient *client)
+{
+ if (G_LIKELY (client->connection))
+ {
+ g_object_unref (client->connection);
+ client->connection = NULL;
+ }
+}
+
+
+static gboolean
+xfsm_client_dbus_get_id (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation)
+{
+ xfsm_dbus_client_complete_get_id (object, invocation, XFSM_CLIENT(object)->id);
+ return TRUE;
+}
+
+
+static gboolean
+xfsm_client_dbus_get_state (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation)
+{
+ xfsm_dbus_client_complete_get_state (object, invocation, XFSM_CLIENT(object)->state);
+ return TRUE;
+}
+
+
+static void
+builder_add_value (GVariantBuilder *builder,
+ const gchar *name,
+ const GValue *value)
+{
+ if (name == NULL)
+ {
+ g_warning ("xfsm_client.c:builder_add_value: name must not be NULL");
+ return;
+ }
+
+ if (G_VALUE_HOLDS_STRING (value))
+ {
+ g_variant_builder_add (builder, "{sv}", name, g_dbus_gvalue_to_gvariant(value, G_VARIANT_TYPE_STRING));
+ }
+ else if (G_VALUE_HOLDS_UCHAR (value))
+ {
+ g_variant_builder_add (builder, "{sv}", name, g_dbus_gvalue_to_gvariant(value, G_VARIANT_TYPE ("y")));
+ }
+ else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
+ {
+ g_variant_builder_add (builder, "{sv}", name, g_dbus_gvalue_to_gvariant(value, G_VARIANT_TYPE_STRING_ARRAY));
+ }
+ else
+ {
+ g_warning ("xfsm_client.c:builder_add_value: Value type not supported");
+ }
+}
+
+
+static gboolean
+xfsm_client_properties_tree_foreach (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ gchar *prop_name = key;
+ GValue *prop_value = value;
+ GVariantBuilder *out_properties = data;
+
+ builder_add_value (out_properties, prop_name, prop_value);
+ return FALSE;
+}
+
+static gboolean
+xfsm_client_dbus_get_all_sm_properties (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation)
+{
+ XfsmProperties *properties = XFSM_CLIENT(object)->properties;
+ GVariantBuilder out_properties;
+
+ if (G_UNLIKELY (properties == NULL))
+ {
+ throw_error (invocation, XFSM_ERROR_BAD_VALUE, "The client doesn't have any properties set yet");
+ return TRUE;
+ }
+
+ g_variant_builder_init (&out_properties, G_VARIANT_TYPE ("a{sv}"));
+
+ g_tree_foreach (properties->sm_properties,
+ xfsm_client_properties_tree_foreach,
+ &out_properties);
+
+ xfsm_dbus_client_complete_get_all_sm_properties (object, invocation, g_variant_builder_end (&out_properties));
+ return TRUE;
+}
+
+
+static gboolean
+xfsm_client_dbus_get_sm_properties (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation,
+ const gchar *const *arg_names)
+{
+ XfsmProperties *properties = XFSM_CLIENT(object)->properties;
+ GVariantBuilder out_properties;
+ gint i;
+
+ if (G_UNLIKELY (properties == NULL))
+ {
+ throw_error (invocation, XFSM_ERROR_BAD_VALUE, "The client doesn't have any properties set yet");
+ return TRUE;
+ }
+
+ g_variant_builder_init (&out_properties, G_VARIANT_TYPE ("a{sv}"));
+
+ for (i = 0; arg_names[i]; ++i)
+ {
+ GValue *value = g_tree_lookup (properties->sm_properties, arg_names[i]);
+
+ if (value != NULL)
+ builder_add_value (&out_properties, arg_names[i], value);
+ }
+
+ xfsm_dbus_client_complete_get_all_sm_properties (object, invocation, g_variant_builder_end (&out_properties));
+ return TRUE;
+}
+
+
+static gboolean
+xfsm_client_dbus_set_sm_properties (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation,
+ GVariant *arg_properties)
+{
+ XfsmProperties *properties = XFSM_CLIENT(object)->properties;
+ GVariantIter *iter;
+ gchar *prop_name;
+ GVariant *variant;
+
+ if (G_UNLIKELY (properties == NULL))
+ {
+ throw_error (invocation, XFSM_ERROR_BAD_VALUE, "The client doesn't have any properties set yet");
+ return TRUE;
+ }
+
+ g_variant_get (arg_properties, "a{sv}", &iter);
+
+ while (g_variant_iter_next (iter, "{sv}", &prop_name, &variant))
+ {
+ GValue value;
+
+ g_dbus_gvariant_to_gvalue (variant, &value);
+ xfsm_properties_set (properties, prop_name, &value);
+
+ g_variant_unref (variant);
+ }
+
+ xfsm_dbus_client_complete_set_sm_properties (object, invocation);
+ return TRUE;
+}
+
+
+static gboolean
+xfsm_client_dbus_delete_sm_properties (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation,
+ const gchar *const *arg_names)
+{
+ XfsmProperties *properties = XFSM_CLIENT(object)->properties;
+ gchar **names = g_strdupv((gchar**)arg_names);
+
+ if (G_UNLIKELY (properties == NULL))
+ {
+ throw_error (invocation, XFSM_ERROR_BAD_VALUE, "The client doesn't have any properties set yet");
+ return TRUE;
+ }
+
+ xfsm_client_delete_properties (XFSM_CLIENT(object), names, g_strv_length (names));
+
+ g_strfreev (names);
+ xfsm_dbus_client_complete_delete_sm_properties (object, invocation);
+ return TRUE;
+}
+
+
+static gboolean
+xfsm_client_dbus_terminate (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation)
+{
+ GError *error = NULL;
+
+ xfsm_manager_terminate_client (XFSM_CLIENT(object)->manager, XFSM_CLIENT(object), &error);
+ if (error != NULL)
+ {
+ throw_error (invocation, XFSM_ERROR_BAD_STATE, "Unable to terminate client, error was: %s", error->message);
+ g_clear_error (&error);
+ return TRUE;
+ }
+
+ xfsm_dbus_client_complete_terminate (object, invocation);
+ return TRUE;
+}
+
+static gboolean
+xfsm_client_dbus_end_session_response (XfsmDbusClient *object,
+ GDBusMethodInvocation *invocation,
+ gboolean arg_is_ok,
+ const gchar *arg_reason)
+{
+ XfsmClient *client = XFSM_CLIENT (object);
+
+ xfsm_verbose ("got response for client %s, manager state is %s\n",
+ get_client_id (client),
+ xfsm_manager_get_state (client->manager) == XFSM_MANAGER_SHUTDOWN ? "XFSM_MANAGER_SHUTDOWN" :
+ xfsm_manager_get_state (client->manager) == XFSM_MANAGER_SHUTDOWNPHASE2 ? "XFSM_MANAGER_SHUTDOWNPHASE2" :
+ "Invalid time to respond");
+
+ if (xfsm_manager_get_state (client->manager) == XFSM_MANAGER_SHUTDOWN)
+ {
+ xfsm_manager_save_yourself_done (client->manager, client, arg_is_ok);
+ }
+ else if (xfsm_manager_get_state (client->manager) == XFSM_MANAGER_SHUTDOWNPHASE2)
+ {
+ xfsm_manager_close_connection (client->manager, client, TRUE);
+ }
+ else
+ {
+ throw_error (invocation, XFSM_ERROR_BAD_STATE,
+ "This method should be sent in response to a QueryEndSession or EndSession signal only");
+ return TRUE;
+ }
+
+ xfsm_dbus_client_complete_end_session_response (object, invocation);
+ return TRUE;
+}