summaryrefslogtreecommitdiff
path: root/mate-session/gsm-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'mate-session/gsm-manager.c')
-rw-r--r--mate-session/gsm-manager.c3481
1 files changed, 3481 insertions, 0 deletions
diff --git a/mate-session/gsm-manager.c b/mate-session/gsm-manager.c
new file mode 100644
index 0000000..cbddf21
--- /dev/null
+++ b/mate-session/gsm-manager.c
@@ -0,0 +1,3481 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * 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 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <upower.h>
+
+#include <gtk/gtk.h> /* for logout dialog */
+#include <mateconf/mateconf-client.h>
+
+#include "gsm-manager.h"
+#include "gsm-manager-glue.h"
+
+#include "gsm-store.h"
+#include "gsm-inhibitor.h"
+#include "gsm-presence.h"
+
+#include "gsm-xsmp-client.h"
+#include "gsm-dbus-client.h"
+
+#include "gsm-autostart-app.h"
+
+#include "gsm-util.h"
+#include "mdm.h"
+#include "gsm-logout-dialog.h"
+#include "gsm-inhibit-dialog.h"
+#include "gsm-consolekit.h"
+#include "gsm-session-save.h"
+
+#define GSM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_MANAGER, GsmManagerPrivate))
+
+#define GSM_MANAGER_DBUS_PATH "/org/mate/SessionManager"
+#define GSM_MANAGER_DBUS_NAME "org.mate.SessionManager"
+
+#define GSM_MANAGER_PHASE_TIMEOUT 10 /* seconds */
+
+#define MDM_FLEXISERVER_COMMAND "mdmflexiserver"
+#define MDM_FLEXISERVER_ARGS "--startnew Standard"
+
+
+#define KEY_LOCKDOWN_DIR "/desktop/mate/lockdown"
+#define KEY_LOCK_DISABLE KEY_LOCKDOWN_DIR "/disable_lock_screen"
+#define KEY_USER_SWITCH_DISABLE KEY_LOCKDOWN_DIR "/disable_user_switching"
+
+#define KEY_DESKTOP_DIR "/desktop/mate/session"
+#define KEY_IDLE_DELAY KEY_DESKTOP_DIR "/idle_delay"
+
+#define KEY_MATE_SESSION_DIR "/apps/mate-session/options"
+#define KEY_AUTOSAVE KEY_MATE_SESSION_DIR "/auto_save_session"
+
+#define KEY_SLEEP_LOCK "/apps/mate-screensaver/lock_enabled"
+
+#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0')
+
+typedef enum
+{
+ GSM_MANAGER_LOGOUT_NONE,
+ GSM_MANAGER_LOGOUT_LOGOUT,
+ GSM_MANAGER_LOGOUT_REBOOT,
+ GSM_MANAGER_LOGOUT_REBOOT_INTERACT,
+ GSM_MANAGER_LOGOUT_REBOOT_MDM,
+ GSM_MANAGER_LOGOUT_SHUTDOWN,
+ GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT,
+ GSM_MANAGER_LOGOUT_SHUTDOWN_MDM
+} GsmManagerLogoutType;
+
+struct GsmManagerPrivate
+{
+ gboolean failsafe;
+ GsmStore *clients;
+ GsmStore *inhibitors;
+ GsmStore *apps;
+ GsmPresence *presence;
+
+ /* Current status */
+ GsmManagerPhase phase;
+ guint phase_timeout_id;
+ GSList *pending_apps;
+ gboolean forceful_logout;
+ GSList *query_clients;
+ guint query_timeout_id;
+ /* This is used for GSM_MANAGER_PHASE_END_SESSION only at the moment,
+ * since it uses a sublist of all running client that replied in a
+ * specific way */
+ GSList *next_query_clients;
+ /* This is the action that will be done just before we exit */
+ GsmManagerLogoutType logout_type;
+
+ GtkWidget *inhibit_dialog;
+
+ /* List of clients which were disconnected due to disabled condition
+ * and shouldn't be automatically restarted */
+ GSList *condition_clients;
+
+ MateConfClient *mateconf_client;
+ guint desktop_notify_id;
+ guint lockdown_notify_id;
+
+ DBusGProxy *bus_proxy;
+ DBusGConnection *connection;
+
+ /* Interface with other parts of the system */
+ UpClient *up_client;
+};
+
+enum {
+ PROP_0,
+ PROP_CLIENT_STORE,
+ PROP_FAILSAFE
+};
+
+enum {
+ PHASE_CHANGED,
+ CLIENT_ADDED,
+ CLIENT_REMOVED,
+ INHIBITOR_ADDED,
+ INHIBITOR_REMOVED,
+ SESSION_RUNNING,
+ SESSION_OVER,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+static void gsm_manager_class_init (GsmManagerClass *klass);
+static void gsm_manager_init (GsmManager *manager);
+static void gsm_manager_finalize (GObject *object);
+
+static gboolean auto_save_is_enabled (GsmManager *manager);
+static void maybe_save_session (GsmManager *manager);
+
+static gpointer manager_object = NULL;
+
+G_DEFINE_TYPE (GsmManager, gsm_manager, G_TYPE_OBJECT)
+
+GQuark
+gsm_manager_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gsm_manager_error");
+ }
+
+ return ret;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+gsm_manager_error_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ ENUM_ENTRY (GSM_MANAGER_ERROR_GENERAL, "GeneralError"),
+ ENUM_ENTRY (GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION, "NotInInitialization"),
+ ENUM_ENTRY (GSM_MANAGER_ERROR_NOT_IN_RUNNING, "NotInRunning"),
+ ENUM_ENTRY (GSM_MANAGER_ERROR_ALREADY_REGISTERED, "AlreadyRegistered"),
+ ENUM_ENTRY (GSM_MANAGER_ERROR_NOT_REGISTERED, "NotRegistered"),
+ ENUM_ENTRY (GSM_MANAGER_ERROR_INVALID_OPTION, "InvalidOption"),
+ { 0, 0, 0 }
+ };
+
+ g_assert (GSM_MANAGER_NUM_ERRORS == G_N_ELEMENTS (values) - 1);
+
+ etype = g_enum_register_static ("GsmManagerError", values);
+ }
+
+ return etype;
+}
+
+static gboolean
+_debug_client (const char *id,
+ GsmClient *client,
+ GsmManager *manager)
+{
+ g_debug ("GsmManager: Client %s", gsm_client_peek_id (client));
+ return FALSE;
+}
+
+static void
+debug_clients (GsmManager *manager)
+{
+ gsm_store_foreach (manager->priv->clients,
+ (GsmStoreFunc)_debug_client,
+ manager);
+}
+
+static gboolean
+_debug_inhibitor (const char *id,
+ GsmInhibitor *inhibitor,
+ GsmManager *manager)
+{
+ g_debug ("GsmManager: Inhibitor app:%s client:%s bus-name:%s reason:%s",
+ gsm_inhibitor_peek_app_id (inhibitor),
+ gsm_inhibitor_peek_client_id (inhibitor),
+ gsm_inhibitor_peek_bus_name (inhibitor),
+ gsm_inhibitor_peek_reason (inhibitor));
+ return FALSE;
+}
+
+static void
+debug_inhibitors (GsmManager *manager)
+{
+ gsm_store_foreach (manager->priv->inhibitors,
+ (GsmStoreFunc)_debug_inhibitor,
+ manager);
+}
+
+static gboolean
+_find_by_cookie (const char *id,
+ GsmInhibitor *inhibitor,
+ guint *cookie_ap)
+{
+ guint cookie_b;
+
+ cookie_b = gsm_inhibitor_peek_cookie (inhibitor);
+
+ return (*cookie_ap == cookie_b);
+}
+
+static gboolean
+_find_by_startup_id (const char *id,
+ GsmClient *client,
+ const char *startup_id_a)
+{
+ const char *startup_id_b;
+
+ startup_id_b = gsm_client_peek_startup_id (client);
+ if (IS_STRING_EMPTY (startup_id_b)) {
+ return FALSE;
+ }
+
+ return (strcmp (startup_id_a, startup_id_b) == 0);
+}
+
+static void
+app_condition_changed (GsmApp *app,
+ gboolean condition,
+ GsmManager *manager)
+{
+ GsmClient *client;
+
+ g_debug ("GsmManager: app:%s condition changed condition:%d",
+ gsm_app_peek_id (app),
+ condition);
+
+ client = (GsmClient *)gsm_store_find (manager->priv->clients,
+ (GsmStoreFunc)_find_by_startup_id,
+ (char *)gsm_app_peek_startup_id (app));
+
+ if (condition) {
+ if (!gsm_app_is_running (app) && client == NULL) {
+ GError *error;
+ gboolean res;
+
+ g_debug ("GsmManager: starting app '%s'", gsm_app_peek_id (app));
+
+ error = NULL;
+ res = gsm_app_start (app, &error);
+ if (error != NULL) {
+ g_warning ("Not able to start app from its condition: %s",
+ error->message);
+ g_error_free (error);
+ }
+ } else {
+ g_debug ("GsmManager: not starting - app still running '%s'", gsm_app_peek_id (app));
+ }
+ } else {
+ GError *error;
+ gboolean res;
+
+ if (client != NULL) {
+ /* Kill client in case condition if false and make sure it won't
+ * be automatically restarted by adding the client to
+ * condition_clients */
+ manager->priv->condition_clients =
+ g_slist_prepend (manager->priv->condition_clients, client);
+
+ g_debug ("GsmManager: stopping client %s for app", gsm_client_peek_id (client));
+
+ error = NULL;
+ res = gsm_client_stop (client, &error);
+ if (error != NULL) {
+ g_warning ("Not able to stop app client from its condition: %s",
+ error->message);
+ g_error_free (error);
+ }
+ } else {
+ g_debug ("GsmManager: stopping app %s", gsm_app_peek_id (app));
+
+ /* If we don't have a client then we should try to kill the app */
+ error = NULL;
+ res = gsm_app_stop (app, &error);
+ if (error != NULL) {
+ g_warning ("Not able to stop app from its condition: %s",
+ error->message);
+ g_error_free (error);
+ }
+ }
+ }
+}
+
+static const char *
+phase_num_to_name (guint phase)
+{
+ const char *name;
+
+ switch (phase) {
+ case GSM_MANAGER_PHASE_STARTUP:
+ name = "STARTUP";
+ break;
+ case GSM_MANAGER_PHASE_INITIALIZATION:
+ name = "INITIALIZATION";
+ break;
+ case GSM_MANAGER_PHASE_WINDOW_MANAGER:
+ name = "WINDOW_MANAGER";
+ break;
+ case GSM_MANAGER_PHASE_PANEL:
+ name = "PANEL";
+ break;
+ case GSM_MANAGER_PHASE_DESKTOP:
+ name = "DESKTOP";
+ break;
+ case GSM_MANAGER_PHASE_APPLICATION:
+ name = "APPLICATION";
+ break;
+ case GSM_MANAGER_PHASE_RUNNING:
+ name = "RUNNING";
+ break;
+ case GSM_MANAGER_PHASE_QUERY_END_SESSION:
+ name = "QUERY_END_SESSION";
+ break;
+ case GSM_MANAGER_PHASE_END_SESSION:
+ name = "END_SESSION";
+ break;
+ case GSM_MANAGER_PHASE_EXIT:
+ name = "EXIT";
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return name;
+}
+
+static void start_phase (GsmManager *manager);
+
+static void
+quit_request_completed (GsmConsolekit *consolekit,
+ GError *error,
+ gpointer user_data)
+{
+ MdmLogoutAction fallback_action = GPOINTER_TO_INT (user_data);
+
+ if (error != NULL) {
+ mdm_set_logout_action (fallback_action);
+ }
+
+ g_object_unref (consolekit);
+
+ gtk_main_quit ();
+}
+
+static void
+gsm_manager_quit (GsmManager *manager)
+{
+ GsmConsolekit *consolekit;
+
+ /* See the comment in request_reboot() for some more details about how
+ * this works. */
+
+ switch (manager->priv->logout_type) {
+ case GSM_MANAGER_LOGOUT_LOGOUT:
+ gtk_main_quit ();
+ break;
+ case GSM_MANAGER_LOGOUT_REBOOT:
+ case GSM_MANAGER_LOGOUT_REBOOT_INTERACT:
+ mdm_set_logout_action (MDM_LOGOUT_ACTION_NONE);
+
+ consolekit = gsm_get_consolekit ();
+ g_signal_connect (consolekit,
+ "request-completed",
+ G_CALLBACK (quit_request_completed),
+ GINT_TO_POINTER (MDM_LOGOUT_ACTION_REBOOT));
+ gsm_consolekit_attempt_restart (consolekit);
+ break;
+ case GSM_MANAGER_LOGOUT_REBOOT_MDM:
+ mdm_set_logout_action (MDM_LOGOUT_ACTION_REBOOT);
+ gtk_main_quit ();
+ break;
+ case GSM_MANAGER_LOGOUT_SHUTDOWN:
+ case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT:
+ mdm_set_logout_action (MDM_LOGOUT_ACTION_NONE);
+
+ consolekit = gsm_get_consolekit ();
+ g_signal_connect (consolekit,
+ "request-completed",
+ G_CALLBACK (quit_request_completed),
+ GINT_TO_POINTER (MDM_LOGOUT_ACTION_SHUTDOWN));
+ gsm_consolekit_attempt_stop (consolekit);
+ break;
+ case GSM_MANAGER_LOGOUT_SHUTDOWN_MDM:
+ mdm_set_logout_action (MDM_LOGOUT_ACTION_SHUTDOWN);
+ gtk_main_quit ();
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+end_phase (GsmManager *manager)
+{
+ g_debug ("GsmManager: ending phase %s\n",
+ phase_num_to_name (manager->priv->phase));
+
+ g_slist_free (manager->priv->pending_apps);
+ manager->priv->pending_apps = NULL;
+
+ g_slist_free (manager->priv->query_clients);
+ manager->priv->query_clients = NULL;
+
+ g_slist_free (manager->priv->next_query_clients);
+ manager->priv->next_query_clients = NULL;
+
+ if (manager->priv->phase_timeout_id > 0) {
+ g_source_remove (manager->priv->phase_timeout_id);
+ manager->priv->phase_timeout_id = 0;
+ }
+
+ switch (manager->priv->phase) {
+ case GSM_MANAGER_PHASE_STARTUP:
+ case GSM_MANAGER_PHASE_INITIALIZATION:
+ case GSM_MANAGER_PHASE_WINDOW_MANAGER:
+ case GSM_MANAGER_PHASE_PANEL:
+ case GSM_MANAGER_PHASE_DESKTOP:
+ case GSM_MANAGER_PHASE_APPLICATION:
+ case GSM_MANAGER_PHASE_RUNNING:
+ case GSM_MANAGER_PHASE_QUERY_END_SESSION:
+ manager->priv->phase++;
+ start_phase (manager);
+ break;
+ case GSM_MANAGER_PHASE_END_SESSION:
+ if (auto_save_is_enabled (manager)) {
+ maybe_save_session (manager);
+ }
+ manager->priv->phase++;
+ start_phase (manager);
+ break;
+ case GSM_MANAGER_PHASE_EXIT:
+ gsm_manager_quit (manager);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+app_registered (GsmApp *app,
+ GsmManager *manager)
+{
+ manager->priv->pending_apps = g_slist_remove (manager->priv->pending_apps, app);
+ g_signal_handlers_disconnect_by_func (app, app_registered, manager);
+
+ if (manager->priv->pending_apps == NULL) {
+ if (manager->priv->phase_timeout_id > 0) {
+ g_source_remove (manager->priv->phase_timeout_id);
+ manager->priv->phase_timeout_id = 0;
+ }
+
+ end_phase (manager);
+ }
+}
+
+static gboolean
+_client_failed_to_stop (const char *id,
+ GsmClient *client,
+ gpointer user_data)
+{
+ g_debug ("GsmManager: client failed to stop: %s, %s", gsm_client_peek_id (client), gsm_client_peek_app_id (client));
+ return FALSE;
+}
+
+static gboolean
+on_phase_timeout (GsmManager *manager)
+{
+ GSList *a;
+
+ manager->priv->phase_timeout_id = 0;
+
+ switch (manager->priv->phase) {
+ case GSM_MANAGER_PHASE_STARTUP:
+ case GSM_MANAGER_PHASE_INITIALIZATION:
+ case GSM_MANAGER_PHASE_WINDOW_MANAGER:
+ case GSM_MANAGER_PHASE_PANEL:
+ case GSM_MANAGER_PHASE_DESKTOP:
+ case GSM_MANAGER_PHASE_APPLICATION:
+ for (a = manager->priv->pending_apps; a; a = a->next) {
+ g_warning ("Application '%s' failed to register before timeout",
+ gsm_app_peek_app_id (a->data));
+ g_signal_handlers_disconnect_by_func (a->data, app_registered, manager);
+ /* FIXME: what if the app was filling in a required slot? */
+ }
+ break;
+ case GSM_MANAGER_PHASE_RUNNING:
+ break;
+ case GSM_MANAGER_PHASE_QUERY_END_SESSION:
+ case GSM_MANAGER_PHASE_END_SESSION:
+ break;
+ case GSM_MANAGER_PHASE_EXIT:
+ gsm_store_foreach (manager->priv->clients,
+ (GsmStoreFunc)_client_failed_to_stop,
+ NULL);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ end_phase (manager);
+
+ return FALSE;
+}
+
+static gboolean
+_start_app (const char *id,
+ GsmApp *app,
+ GsmManager *manager)
+{
+ GError *error;
+ gboolean res;
+
+ if (gsm_app_peek_phase (app) != manager->priv->phase) {
+ goto out;
+ }
+
+ /* Keep track of app autostart condition in order to react
+ * accordingly in the future. */
+ g_signal_connect (app,
+ "condition-changed",
+ G_CALLBACK (app_condition_changed),
+ manager);
+
+ if (gsm_app_peek_is_disabled (app)
+ || gsm_app_peek_is_conditionally_disabled (app)) {
+ g_debug ("GsmManager: Skipping disabled app: %s", id);
+ goto out;
+ }
+
+ error = NULL;
+ res = gsm_app_start (app, &error);
+ if (!res) {
+ if (error != NULL) {
+ g_warning ("Could not launch application '%s': %s",
+ gsm_app_peek_app_id (app),
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ goto out;
+ }
+
+ if (manager->priv->phase < GSM_MANAGER_PHASE_APPLICATION) {
+ g_signal_connect (app,
+ "exited",
+ G_CALLBACK (app_registered),
+ manager);
+ g_signal_connect (app,
+ "registered",
+ G_CALLBACK (app_registered),
+ manager);
+ manager->priv->pending_apps = g_slist_prepend (manager->priv->pending_apps, app);
+ }
+ out:
+ return FALSE;
+}
+
+static void
+do_phase_startup (GsmManager *manager)
+{
+ gsm_store_foreach (manager->priv->apps,
+ (GsmStoreFunc)_start_app,
+ manager);
+
+ if (manager->priv->pending_apps != NULL) {
+ if (manager->priv->phase < GSM_MANAGER_PHASE_APPLICATION) {
+ manager->priv->phase_timeout_id = g_timeout_add_seconds (GSM_MANAGER_PHASE_TIMEOUT,
+ (GSourceFunc)on_phase_timeout,
+ manager);
+ }
+ } else {
+ end_phase (manager);
+ }
+}
+
+typedef struct {
+ GsmManager *manager;
+ guint flags;
+} ClientEndSessionData;
+
+
+static gboolean
+_client_end_session (GsmClient *client,
+ ClientEndSessionData *data)
+{
+ gboolean ret;
+ GError *error;
+
+ error = NULL;
+ ret = gsm_client_end_session (client, data->flags, &error);
+ if (! ret) {
+ g_warning ("Unable to query client: %s", error->message);
+ g_error_free (error);
+ /* FIXME: what should we do if we can't communicate with client? */
+ } else {
+ g_debug ("GsmManager: adding client to end-session clients: %s", gsm_client_peek_id (client));
+ data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients,
+ client);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+_client_end_session_helper (const char *id,
+ GsmClient *client,
+ ClientEndSessionData *data)
+{
+ return _client_end_session (client, data);
+}
+
+static void
+do_phase_end_session (GsmManager *manager)
+{
+ ClientEndSessionData data;
+
+ data.manager = manager;
+ data.flags = 0;
+
+ if (manager->priv->forceful_logout) {
+ data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL;
+ }
+ if (auto_save_is_enabled (manager)) {
+ data.flags |= GSM_CLIENT_END_SESSION_FLAG_SAVE;
+ }
+
+ if (manager->priv->phase_timeout_id > 0) {
+ g_source_remove (manager->priv->phase_timeout_id);
+ manager->priv->phase_timeout_id = 0;
+ }
+
+ if (gsm_store_size (manager->priv->clients) > 0) {
+ manager->priv->phase_timeout_id = g_timeout_add_seconds (GSM_MANAGER_PHASE_TIMEOUT,
+ (GSourceFunc)on_phase_timeout,
+ manager);
+
+ gsm_store_foreach (manager->priv->clients,
+ (GsmStoreFunc)_client_end_session_helper,
+ &data);
+ } else {
+ end_phase (manager);
+ }
+}
+
+static void
+do_phase_end_session_part_2 (GsmManager *manager)
+{
+ ClientEndSessionData data;
+
+ data.manager = manager;
+ data.flags = 0;
+
+ if (manager->priv->forceful_logout) {
+ data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL;
+ }
+ if (auto_save_is_enabled (manager)) {
+ data.flags |= GSM_CLIENT_END_SESSION_FLAG_SAVE;
+ }
+ data.flags |= GSM_CLIENT_END_SESSION_FLAG_LAST;
+
+ /* keep the timeout that was started at the beginning of the
+ * GSM_MANAGER_PHASE_END_SESSION phase */
+
+ if (g_slist_length (manager->priv->next_query_clients) > 0) {
+ g_slist_foreach (manager->priv->next_query_clients,
+ (GFunc)_client_end_session,
+ &data);
+
+ g_slist_free (manager->priv->next_query_clients);
+ manager->priv->next_query_clients = NULL;
+ } else {
+ end_phase (manager);
+ }
+}
+
+static gboolean
+_client_stop (const char *id,
+ GsmClient *client,
+ gpointer user_data)
+{
+ gboolean ret;
+ GError *error;
+
+ error = NULL;
+ ret = gsm_client_stop (client, &error);
+ if (! ret) {
+ g_warning ("Unable to stop client: %s", error->message);
+ g_error_free (error);
+ /* FIXME: what should we do if we can't communicate with client? */
+ } else {
+ g_debug ("GsmManager: stopped client: %s", gsm_client_peek_id (client));
+ }
+
+ return FALSE;
+}
+
+static void
+do_phase_exit (GsmManager *manager)
+{
+ if (gsm_store_size (manager->priv->clients) > 0) {
+ manager->priv->phase_timeout_id = g_timeout_add_seconds (GSM_MANAGER_PHASE_TIMEOUT,
+ (GSourceFunc)on_phase_timeout,
+ manager);
+
+ gsm_store_foreach (manager->priv->clients,
+ (GsmStoreFunc)_client_stop,
+ NULL);
+ } else {
+ end_phase (manager);
+ }
+}
+
+static gboolean
+_client_query_end_session (const char *id,
+ GsmClient *client,
+ ClientEndSessionData *data)
+{
+ gboolean ret;
+ GError *error;
+
+ error = NULL;
+ ret = gsm_client_query_end_session (client, data->flags, &error);
+ if (! ret) {
+ g_warning ("Unable to query client: %s", error->message);
+ g_error_free (error);
+ /* FIXME: what should we do if we can't communicate with client? */
+ } else {
+ g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client));
+ data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients,
+ client);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+inhibitor_has_flag (gpointer key,
+ GsmInhibitor *inhibitor,
+ gpointer data)
+{
+ guint flag;
+ guint flags;
+
+ flag = GPOINTER_TO_UINT (data);
+
+ flags = gsm_inhibitor_peek_flags (inhibitor);
+
+ return (flags & flag);
+}
+
+static gboolean
+gsm_manager_is_logout_inhibited (GsmManager *manager)
+{
+ GsmInhibitor *inhibitor;
+
+ if (manager->priv->inhibitors == NULL) {
+ return FALSE;
+ }
+
+ inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors,
+ (GsmStoreFunc)inhibitor_has_flag,
+ GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_LOGOUT));
+ if (inhibitor == NULL) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+gsm_manager_is_idle_inhibited (GsmManager *manager)
+{
+ GsmInhibitor *inhibitor;
+
+ if (manager->priv->inhibitors == NULL) {
+ return FALSE;
+ }
+
+ inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors,
+ (GsmStoreFunc)inhibitor_has_flag,
+ GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_IDLE));
+ if (inhibitor == NULL) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+_client_cancel_end_session (const char *id,
+ GsmClient *client,
+ GsmManager *manager)
+{
+ gboolean res;
+ GError *error;
+
+ error = NULL;
+ res = gsm_client_cancel_end_session (client, &error);
+ if (! res) {
+ g_warning ("Unable to cancel end session: %s", error->message);
+ g_error_free (error);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+inhibitor_is_jit (gpointer key,
+ GsmInhibitor *inhibitor,
+ GsmManager *manager)
+{
+ gboolean matches;
+ const char *id;
+
+ id = gsm_inhibitor_peek_client_id (inhibitor);
+
+ matches = (id != NULL && id[0] != '\0');
+
+ return matches;
+}
+
+static void
+cancel_end_session (GsmManager *manager)
+{
+ /* just ignore if received outside of shutdown */
+ if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+ return;
+ }
+
+ /* switch back to running phase */
+ g_debug ("GsmManager: Cancelling the end of session");
+
+ /* remove the dialog before we remove the inhibitors, else the dialog
+ * will activate itself automatically when the last inhibitor will be
+ * removed */
+ if (manager->priv->inhibit_dialog)
+ gtk_widget_destroy (GTK_WIDGET (manager->priv->inhibit_dialog));
+ manager->priv->inhibit_dialog = NULL;
+
+ /* clear all JIT inhibitors */
+ gsm_store_foreach_remove (manager->priv->inhibitors,
+ (GsmStoreFunc)inhibitor_is_jit,
+ (gpointer)manager);
+
+ gsm_store_foreach (manager->priv->clients,
+ (GsmStoreFunc)_client_cancel_end_session,
+ NULL);
+
+ gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_RUNNING);
+ manager->priv->forceful_logout = FALSE;
+
+ manager->priv->logout_type = GSM_MANAGER_LOGOUT_NONE;
+ mdm_set_logout_action (MDM_LOGOUT_ACTION_NONE);
+
+ start_phase (manager);
+}
+
+
+static void
+manager_switch_user (GsmManager *manager)
+{
+ GError *error;
+ gboolean res;
+ char *command;
+
+ command = g_strdup_printf ("%s %s",
+ MDM_FLEXISERVER_COMMAND,
+ MDM_FLEXISERVER_ARGS);
+
+ error = NULL;
+ res = gdk_spawn_command_line_on_screen (gdk_screen_get_default (),
+ command,
+ &error);
+
+ g_free (command);
+
+ if (! res) {
+ g_debug ("GsmManager: Unable to start MDM greeter: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+sleep_lock_is_enabled (GsmManager *manager)
+{
+ GError *error;
+ gboolean enable_lock;
+
+ error = NULL;
+ enable_lock = mateconf_client_get_bool (manager->priv->mateconf_client,
+ KEY_SLEEP_LOCK, &error);
+
+ if (error) {
+ g_warning ("Error retrieving configuration key '%s': %s",
+ KEY_SLEEP_LOCK, error->message);
+ g_error_free (error);
+
+ /* If we fail to query mateconf key, just enable locking */
+ enable_lock = TRUE;
+ }
+
+ return enable_lock;
+}
+
+static void
+manager_perhaps_lock (GsmManager *manager)
+{
+ GError *error;
+ gboolean ret;
+
+ /* only lock if mate-screensaver is set to lock */
+ if (!sleep_lock_is_enabled (manager)) {
+ return;
+ }
+
+ /* do this sync to ensure it's on the screen when we start suspending */
+ error = NULL;
+ ret = g_spawn_command_line_sync ("mate-screensaver-command --lock", NULL, NULL, NULL, &error);
+ if (!ret) {
+ g_warning ("Couldn't lock screen: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+manager_attempt_hibernate (GsmManager *manager)
+{
+ gboolean can_hibernate;
+ GError *error;
+ gboolean ret;
+
+ can_hibernate = up_client_get_can_hibernate (manager->priv->up_client);
+ if (can_hibernate) {
+
+ /* lock the screen before we suspend */
+ manager_perhaps_lock (manager);
+
+ error = NULL;
+ ret = up_client_hibernate_sync (manager->priv->up_client, NULL, &error);
+ if (!ret) {
+ g_warning ("Unexpected hibernate failure: %s",
+ error->message);
+ g_error_free (error);
+ }
+ }
+}
+
+static void
+manager_attempt_suspend (GsmManager *manager)
+{
+ gboolean can_suspend;
+ GError *error;
+ gboolean ret;
+
+ can_suspend = up_client_get_can_suspend (manager->priv->up_client);
+ if (can_suspend) {
+
+ /* lock the screen before we suspend */
+ manager_perhaps_lock (manager);
+
+ error = NULL;
+ ret = up_client_suspend_sync (manager->priv->up_client, NULL, &error);
+ if (!ret) {
+ g_warning ("Unexpected suspend failure: %s",
+ error->message);
+ g_error_free (error);
+ }
+ }
+}
+
+static void
+do_inhibit_dialog_action (GsmManager *manager,
+ int action)
+{
+ switch (action) {
+ case GSM_LOGOUT_ACTION_SWITCH_USER:
+ manager_switch_user (manager);
+ break;
+ case GSM_LOGOUT_ACTION_HIBERNATE:
+ manager_attempt_hibernate (manager);
+ break;
+ case GSM_LOGOUT_ACTION_SLEEP:
+ manager_attempt_suspend (manager);
+ break;
+ case GSM_LOGOUT_ACTION_SHUTDOWN:
+ case GSM_LOGOUT_ACTION_REBOOT:
+ case GSM_LOGOUT_ACTION_LOGOUT:
+ manager->priv->forceful_logout = TRUE;
+ end_phase (manager);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+inhibit_dialog_response (GsmInhibitDialog *dialog,
+ guint response_id,
+ GsmManager *manager)
+{
+ int action;
+
+ g_debug ("GsmManager: Inhibit dialog response: %d", response_id);
+
+ /* must destroy dialog before cancelling since we'll
+ remove JIT inhibitors and we don't want to trigger
+ action. */
+ g_object_get (dialog, "action", &action, NULL);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ manager->priv->inhibit_dialog = NULL;
+
+ /* In case of dialog cancel, switch user, hibernate and
+ * suspend, we just perform the respective action and return,
+ * without shutting down the session. */
+ switch (response_id) {
+ case GTK_RESPONSE_CANCEL:
+ case GTK_RESPONSE_NONE:
+ case GTK_RESPONSE_DELETE_EVENT:
+ if (action == GSM_LOGOUT_ACTION_LOGOUT
+ || action == GSM_LOGOUT_ACTION_SHUTDOWN
+ || action == GSM_LOGOUT_ACTION_REBOOT) {
+ cancel_end_session (manager);
+ }
+ break;
+ case GTK_RESPONSE_ACCEPT:
+ g_debug ("GsmManager: doing action %d", action);
+ do_inhibit_dialog_action (manager, action);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+query_end_session_complete (GsmManager *manager)
+{
+ GsmLogoutAction action;
+
+ g_debug ("GsmManager: query end session complete");
+
+ /* Remove the timeout since this can be called from outside the timer
+ * and we don't want to have it called twice */
+ if (manager->priv->query_timeout_id > 0) {
+ g_source_remove (manager->priv->query_timeout_id);
+ manager->priv->query_timeout_id = 0;
+ }
+
+ if (! gsm_manager_is_logout_inhibited (manager)) {
+ end_phase (manager);
+ return;
+ }
+
+ if (manager->priv->inhibit_dialog != NULL) {
+ g_debug ("GsmManager: inhibit dialog already up");
+ gtk_window_present (GTK_WINDOW (manager->priv->inhibit_dialog));
+ return;
+ }
+
+ switch (manager->priv->logout_type) {
+ case GSM_MANAGER_LOGOUT_LOGOUT:
+ action = GSM_LOGOUT_ACTION_LOGOUT;
+ break;
+ case GSM_MANAGER_LOGOUT_REBOOT:
+ case GSM_MANAGER_LOGOUT_REBOOT_INTERACT:
+ case GSM_MANAGER_LOGOUT_REBOOT_MDM:
+ action = GSM_LOGOUT_ACTION_REBOOT;
+ break;
+ case GSM_MANAGER_LOGOUT_SHUTDOWN:
+ case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT:
+ case GSM_MANAGER_LOGOUT_SHUTDOWN_MDM:
+ action = GSM_LOGOUT_ACTION_SHUTDOWN;
+ break;
+ default:
+ g_warning ("Unexpected logout type %d when creating inhibit dialog",
+ manager->priv->logout_type);
+ action = GSM_LOGOUT_ACTION_LOGOUT;
+ break;
+ }
+
+ /* Note: GSM_LOGOUT_ACTION_SHUTDOWN and GSM_LOGOUT_ACTION_REBOOT are
+ * actually handled the same way as GSM_LOGOUT_ACTION_LOGOUT in the
+ * inhibit dialog; the action, if the button is clicked, will be to
+ * simply go to the next phase. */
+ manager->priv->inhibit_dialog = gsm_inhibit_dialog_new (manager->priv->inhibitors,
+ manager->priv->clients,
+ action);
+
+ g_signal_connect (manager->priv->inhibit_dialog,
+ "response",
+ G_CALLBACK (inhibit_dialog_response),
+ manager);
+ gtk_widget_show (manager->priv->inhibit_dialog);
+
+}
+
+static guint32
+generate_cookie (void)
+{
+ guint32 cookie;
+
+ cookie = (guint32)g_random_int_range (1, G_MAXINT32);
+
+ return cookie;
+}
+
+static guint32
+_generate_unique_cookie (GsmManager *manager)
+{
+ guint32 cookie;
+
+ do {
+ cookie = generate_cookie ();
+ } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL);
+
+ return cookie;
+}
+
+static gboolean
+_on_query_end_session_timeout (GsmManager *manager)
+{
+ GSList *l;
+
+ manager->priv->query_timeout_id = 0;
+
+ g_debug ("GsmManager: query end session timed out");
+
+ for (l = manager->priv->query_clients; l != NULL; l = l->next) {
+ guint cookie;
+ GsmInhibitor *inhibitor;
+ const char *bus_name;
+ char *app_id;
+
+ g_warning ("Client '%s' failed to reply before timeout",
+ gsm_client_peek_id (l->data));
+
+ /* Add JIT inhibit for unresponsive client */
+ if (GSM_IS_DBUS_CLIENT (l->data)) {
+ bus_name = gsm_dbus_client_get_bus_name (l->data);
+ } else {
+ bus_name = NULL;
+ }
+
+ app_id = g_strdup (gsm_client_peek_app_id (l->data));
+ if (IS_STRING_EMPTY (app_id)) {
+ /* XSMP clients don't give us an app id unless we start them */
+ g_free (app_id);
+ app_id = gsm_client_get_app_name (l->data);
+ }
+
+ cookie = _generate_unique_cookie (manager);
+ inhibitor = gsm_inhibitor_new_for_client (gsm_client_peek_id (l->data),
+ app_id,
+ GSM_INHIBITOR_FLAG_LOGOUT,
+ _("Not responding"),
+ bus_name,
+ cookie);
+ g_free (app_id);
+ gsm_store_add (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
+ g_object_unref (inhibitor);
+ }
+
+ g_slist_free (manager->priv->query_clients);
+ manager->priv->query_clients = NULL;
+
+ query_end_session_complete (manager);
+
+ return FALSE;
+}
+
+static void
+do_phase_query_end_session (GsmManager *manager)
+{
+ ClientEndSessionData data;
+
+ data.manager = manager;
+ data.flags = 0;
+
+ if (manager->priv->forceful_logout) {
+ data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL;
+ }
+ /* We only query if an app is ready to log out, so we don't use
+ * GSM_CLIENT_END_SESSION_FLAG_SAVE here.
+ */
+
+ debug_clients (manager);
+ g_debug ("GsmManager: sending query-end-session to clients forceful:%d", manager->priv->forceful_logout);
+ gsm_store_foreach (manager->priv->clients,
+ (GsmStoreFunc)_client_query_end_session,
+ &data);
+
+ /* This phase doesn't time out. This separate timer is only used to
+ * show UI. */
+ manager->priv->query_timeout_id = g_timeout_add_seconds (1, (GSourceFunc)_on_query_end_session_timeout, manager);
+}
+
+static void
+update_idle (GsmManager *manager)
+{
+ if (gsm_manager_is_idle_inhibited (manager)) {
+ gsm_presence_set_idle_enabled (manager->priv->presence, FALSE);
+ } else {
+ gsm_presence_set_idle_enabled (manager->priv->presence, TRUE);
+ }
+}
+
+static void
+start_phase (GsmManager *manager)
+{
+
+ g_debug ("GsmManager: starting phase %s\n",
+ phase_num_to_name (manager->priv->phase));
+
+ /* reset state */
+ g_slist_free (manager->priv->pending_apps);
+ manager->priv->pending_apps = NULL;
+ g_slist_free (manager->priv->query_clients);
+ manager->priv->query_clients = NULL;
+ g_slist_free (manager->priv->next_query_clients);
+ manager->priv->next_query_clients = NULL;
+
+ if (manager->priv->query_timeout_id > 0) {
+ g_source_remove (manager->priv->query_timeout_id);
+ manager->priv->query_timeout_id = 0;
+ }
+ if (manager->priv->phase_timeout_id > 0) {
+ g_source_remove (manager->priv->phase_timeout_id);
+ manager->priv->phase_timeout_id = 0;
+ }
+
+ switch (manager->priv->phase) {
+ case GSM_MANAGER_PHASE_STARTUP:
+ case GSM_MANAGER_PHASE_INITIALIZATION:
+ case GSM_MANAGER_PHASE_WINDOW_MANAGER:
+ case GSM_MANAGER_PHASE_PANEL:
+ case GSM_MANAGER_PHASE_DESKTOP:
+ case GSM_MANAGER_PHASE_APPLICATION:
+ do_phase_startup (manager);
+ break;
+ case GSM_MANAGER_PHASE_RUNNING:
+ g_signal_emit (manager, signals[SESSION_RUNNING], 0);
+ update_idle (manager);
+ break;
+ case GSM_MANAGER_PHASE_QUERY_END_SESSION:
+ do_phase_query_end_session (manager);
+ break;
+ case GSM_MANAGER_PHASE_END_SESSION:
+ do_phase_end_session (manager);
+ break;
+ case GSM_MANAGER_PHASE_EXIT:
+ do_phase_exit (manager);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static gboolean
+_debug_app_for_phase (const char *id,
+ GsmApp *app,
+ gpointer data)
+{
+ guint phase;
+
+ phase = GPOINTER_TO_UINT (data);
+
+ if (gsm_app_peek_phase (app) != phase) {
+ return FALSE;
+ }
+
+ g_debug ("GsmManager:\tID: %s\tapp-id:%s\tis-disabled:%d\tis-conditionally-disabled:%d",
+ gsm_app_peek_id (app),
+ gsm_app_peek_app_id (app),
+ gsm_app_peek_is_disabled (app),
+ gsm_app_peek_is_conditionally_disabled (app));
+
+ return FALSE;
+}
+
+static void
+debug_app_summary (GsmManager *manager)
+{
+ guint phase;
+
+ g_debug ("GsmManager: App startup summary");
+ for (phase = GSM_MANAGER_PHASE_INITIALIZATION; phase < GSM_MANAGER_PHASE_RUNNING; phase++) {
+ g_debug ("GsmManager: Phase %s", phase_num_to_name (phase));
+ gsm_store_foreach (manager->priv->apps,
+ (GsmStoreFunc)_debug_app_for_phase,
+ GUINT_TO_POINTER (phase));
+ }
+}
+
+void
+gsm_manager_start (GsmManager *manager)
+{
+ g_debug ("GsmManager: GSM starting to manage");
+
+ g_return_if_fail (GSM_IS_MANAGER (manager));
+
+ gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_INITIALIZATION);
+ debug_app_summary (manager);
+ start_phase (manager);
+}
+
+static gboolean
+_app_has_app_id (const char *id,
+ GsmApp *app,
+ const char *app_id_a)
+{
+ const char *app_id_b;
+
+ app_id_b = gsm_app_peek_app_id (app);
+ return (app_id_b != NULL && strcmp (app_id_a, app_id_b) == 0);
+}
+
+static GsmApp *
+find_app_for_app_id (GsmManager *manager,
+ const char *app_id)
+{
+ GsmApp *app;
+ app = (GsmApp *)gsm_store_find (manager->priv->apps,
+ (GsmStoreFunc)_app_has_app_id,
+ (char *)app_id);
+ return app;
+}
+
+static gboolean
+inhibitor_has_client_id (gpointer key,
+ GsmInhibitor *inhibitor,
+ const char *client_id_a)
+{
+ gboolean matches;
+ const char *client_id_b;
+
+ client_id_b = gsm_inhibitor_peek_client_id (inhibitor);
+
+ matches = FALSE;
+ if (! IS_STRING_EMPTY (client_id_a) && ! IS_STRING_EMPTY (client_id_b)) {
+ matches = (strcmp (client_id_a, client_id_b) == 0);
+ if (matches) {
+ g_debug ("GsmManager: removing JIT inhibitor for %s for reason '%s'",
+ gsm_inhibitor_peek_client_id (inhibitor),
+ gsm_inhibitor_peek_reason (inhibitor));
+ }
+ }
+
+ return matches;
+}
+
+static gboolean
+_app_has_startup_id (const char *id,
+ GsmApp *app,
+ const char *startup_id_a)
+{
+ const char *startup_id_b;
+
+ startup_id_b = gsm_app_peek_startup_id (app);
+
+ if (IS_STRING_EMPTY (startup_id_b)) {
+ return FALSE;
+ }
+
+ return (strcmp (startup_id_a, startup_id_b) == 0);
+}
+
+static GsmApp *
+find_app_for_startup_id (GsmManager *manager,
+ const char *startup_id)
+{
+ GsmApp *found_app;
+ GSList *a;
+
+ found_app = NULL;
+
+ /* If we're starting up the session, try to match the new client
+ * with one pending apps for the current phase. If not, try to match
+ * with any of the autostarted apps. */
+ if (manager->priv->phase < GSM_MANAGER_PHASE_APPLICATION) {
+ for (a = manager->priv->pending_apps; a != NULL; a = a->next) {
+ GsmApp *app = GSM_APP (a->data);
+
+ if (strcmp (startup_id, gsm_app_peek_startup_id (app)) == 0) {
+ found_app = app;
+ goto out;
+ }
+ }
+ } else {
+ GsmApp *app;
+
+ app = (GsmApp *)gsm_store_find (manager->priv->apps,
+ (GsmStoreFunc)_app_has_startup_id,
+ (char *)startup_id);
+ if (app != NULL) {
+ found_app = app;
+ goto out;
+ }
+ }
+ out:
+ return found_app;
+}
+
+static void
+_disconnect_client (GsmManager *manager,
+ GsmClient *client)
+{
+ gboolean is_condition_client;
+ GsmApp *app;
+ GError *error;
+ gboolean res;
+ const char *app_id;
+ const char *startup_id;
+ gboolean app_restart;
+ GsmClientRestartStyle client_restart_hint;
+
+ g_debug ("GsmManager: disconnect client: %s", gsm_client_peek_id (client));
+
+ /* take a ref so it doesn't get finalized */
+ g_object_ref (client);
+
+ gsm_client_set_status (client, GSM_CLIENT_FINISHED);
+
+ is_condition_client = FALSE;
+ if (g_slist_find (manager->priv->condition_clients, client)) {
+ manager->priv->condition_clients = g_slist_remove (manager->priv->condition_clients, client);
+
+ is_condition_client = TRUE;
+ }
+
+ /* remove any inhibitors for this client */
+ gsm_store_foreach_remove (manager->priv->inhibitors,
+ (GsmStoreFunc)inhibitor_has_client_id,
+ (gpointer)gsm_client_peek_id (client));
+
+ app = NULL;
+
+ /* first try to match on startup ID */
+ startup_id = gsm_client_peek_startup_id (client);
+ if (! IS_STRING_EMPTY (startup_id)) {
+ app = find_app_for_startup_id (manager, startup_id);
+
+ }
+
+ /* then try to find matching app-id */
+ if (app == NULL) {
+ app_id = gsm_client_peek_app_id (client);
+ if (! IS_STRING_EMPTY (app_id)) {
+ g_debug ("GsmManager: disconnect for app '%s'", app_id);
+ app = find_app_for_app_id (manager, app_id);
+ }
+ }
+
+ if (app == NULL) {
+ g_debug ("GsmManager: unable to find application for client - not restarting");
+ goto out;
+ }
+
+ if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+ g_debug ("GsmManager: in shutdown, not restarting application");
+ goto out;
+ }
+
+ app_restart = gsm_app_peek_autorestart (app);
+ client_restart_hint = gsm_client_peek_restart_style_hint (client);
+
+ /* allow legacy clients to override the app info */
+ if (! app_restart
+ && client_restart_hint != GSM_CLIENT_RESTART_IMMEDIATELY) {
+ g_debug ("GsmManager: autorestart not set, not restarting application");
+ goto out;
+ }
+
+ if (is_condition_client) {
+ g_debug ("GsmManager: app conditionally disabled, not restarting application");
+ goto out;
+ }
+
+ g_debug ("GsmManager: restarting app");
+
+ error = NULL;
+ res = gsm_app_restart (app, &error);
+ if (error != NULL) {
+ g_warning ("Error on restarting session managed app: %s", error->message);
+ g_error_free (error);
+ }
+
+ out:
+ g_object_unref (client);
+}
+
+typedef struct {
+ const char *service_name;
+ GsmManager *manager;
+} RemoveClientData;
+
+static gboolean
+_disconnect_dbus_client (const char *id,
+ GsmClient *client,
+ RemoveClientData *data)
+{
+ const char *name;
+
+ if (! GSM_IS_DBUS_CLIENT (client)) {
+ return FALSE;
+ }
+
+ name = gsm_dbus_client_get_bus_name (GSM_DBUS_CLIENT (client));
+ if (IS_STRING_EMPTY (name)) {
+ return FALSE;
+ }
+
+ if (strcmp (data->service_name, name) == 0) {
+ _disconnect_client (data->manager, client);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+remove_clients_for_connection (GsmManager *manager,
+ const char *service_name)
+{
+ RemoveClientData data;
+
+ data.service_name = service_name;
+ data.manager = manager;
+
+ /* disconnect dbus clients for name */
+ gsm_store_foreach_remove (manager->priv->clients,
+ (GsmStoreFunc)_disconnect_dbus_client,
+ &data);
+
+ if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION
+ && gsm_store_size (manager->priv->clients) == 0) {
+ g_debug ("GsmManager: last client disconnected - exiting");
+ end_phase (manager);
+ }
+}
+
+static gboolean
+inhibitor_has_bus_name (gpointer key,
+ GsmInhibitor *inhibitor,
+ RemoveClientData *data)
+{
+ gboolean matches;
+ const char *bus_name_b;
+
+ bus_name_b = gsm_inhibitor_peek_bus_name (inhibitor);
+
+ matches = FALSE;
+ if (! IS_STRING_EMPTY (data->service_name) && ! IS_STRING_EMPTY (bus_name_b)) {
+ matches = (strcmp (data->service_name, bus_name_b) == 0);
+ if (matches) {
+ g_debug ("GsmManager: removing inhibitor from %s for reason '%s' on connection %s",
+ gsm_inhibitor_peek_app_id (inhibitor),
+ gsm_inhibitor_peek_reason (inhibitor),
+ gsm_inhibitor_peek_bus_name (inhibitor));
+ }
+ }
+
+ return matches;
+}
+
+static void
+remove_inhibitors_for_connection (GsmManager *manager,
+ const char *service_name)
+{
+ guint n_removed;
+ RemoveClientData data;
+
+ data.service_name = service_name;
+ data.manager = manager;
+
+ debug_inhibitors (manager);
+
+ n_removed = gsm_store_foreach_remove (manager->priv->inhibitors,
+ (GsmStoreFunc)inhibitor_has_bus_name,
+ &data);
+}
+
+static void
+bus_name_owner_changed (DBusGProxy *bus_proxy,
+ const char *service_name,
+ const char *old_service_name,
+ const char *new_service_name,
+ GsmManager *manager)
+{
+ if (strlen (new_service_name) == 0
+ && strlen (old_service_name) > 0) {
+ /* service removed */
+ remove_inhibitors_for_connection (manager, old_service_name);
+ remove_clients_for_connection (manager, old_service_name);
+ } else if (strlen (old_service_name) == 0
+ && strlen (new_service_name) > 0) {
+ /* service added */
+
+ /* use this if we support automatically registering
+ * well known bus names */
+ }
+}
+
+static gboolean
+register_manager (GsmManager *manager)
+{
+ GError *error = NULL;
+
+ error = NULL;
+ manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+ if (manager->priv->connection == NULL) {
+ if (error != NULL) {
+ g_critical ("error getting session bus: %s", error->message);
+ g_error_free (error);
+ }
+ exit (1);
+ }
+
+ manager->priv->bus_proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+ dbus_g_proxy_add_signal (manager->priv->bus_proxy,
+ "NameOwnerChanged",
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (manager->priv->bus_proxy,
+ "NameOwnerChanged",
+ G_CALLBACK (bus_name_owner_changed),
+ manager,
+ NULL);
+
+ dbus_g_connection_register_g_object (manager->priv->connection, GSM_MANAGER_DBUS_PATH, G_OBJECT (manager));
+
+ return TRUE;
+}
+
+static void
+gsm_manager_set_failsafe (GsmManager *manager,
+ gboolean enabled)
+{
+ g_return_if_fail (GSM_IS_MANAGER (manager));
+
+ manager->priv->failsafe = enabled;
+}
+
+static gboolean
+_client_has_startup_id (const char *id,
+ GsmClient *client,
+ const char *startup_id_a)
+{
+ const char *startup_id_b;
+
+ startup_id_b = gsm_client_peek_startup_id (client);
+
+ if (IS_STRING_EMPTY (startup_id_b)) {
+ return FALSE;
+ }
+
+ return (strcmp (startup_id_a, startup_id_b) == 0);
+}
+
+static void
+on_client_disconnected (GsmClient *client,
+ GsmManager *manager)
+{
+ g_debug ("GsmManager: disconnect client");
+ _disconnect_client (manager, client);
+ gsm_store_remove (manager->priv->clients, gsm_client_peek_id (client));
+ if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION
+ && gsm_store_size (manager->priv->clients) == 0) {
+ g_debug ("GsmManager: last client disconnected - exiting");
+ end_phase (manager);
+ }
+}
+
+static gboolean
+on_xsmp_client_register_request (GsmXSMPClient *client,
+ char **id,
+ GsmManager *manager)
+{
+ gboolean handled;
+ char *new_id;
+ GsmApp *app;
+
+ handled = TRUE;
+ new_id = NULL;
+
+ if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+ goto out;
+ }
+
+ if (IS_STRING_EMPTY (*id)) {
+ new_id = gsm_util_generate_startup_id ();
+ } else {
+ GsmClient *client;
+
+ client = (GsmClient *)gsm_store_find (manager->priv->clients,
+ (GsmStoreFunc)_client_has_startup_id,
+ *id);
+ /* We can't have two clients with the same id. */
+ if (client != NULL) {
+ goto out;
+ }
+
+ new_id = g_strdup (*id);
+ }
+
+ g_debug ("GsmManager: Adding new client %s to session", new_id);
+
+ g_signal_connect (client,
+ "disconnected",
+ G_CALLBACK (on_client_disconnected),
+ manager);
+
+ /* If it's a brand new client id, we just accept the client*/
+ if (IS_STRING_EMPTY (*id)) {
+ goto out;
+ }
+
+ app = find_app_for_startup_id (manager, new_id);
+ if (app != NULL) {
+ gsm_client_set_app_id (GSM_CLIENT (client), gsm_app_peek_app_id (app));
+ gsm_app_registered (app);
+ goto out;
+ }
+
+ /* app not found */
+ g_free (new_id);
+ new_id = NULL;
+
+ out:
+ g_free (*id);
+ *id = new_id;
+
+ return handled;
+}
+
+static gboolean
+auto_save_is_enabled (GsmManager *manager)
+{
+ GError *error;
+ gboolean auto_save;
+
+ error = NULL;
+ auto_save = mateconf_client_get_bool (manager->priv->mateconf_client,
+ KEY_AUTOSAVE,
+ &error);
+
+ if (error) {
+ g_warning ("Error retrieving configuration key '%s': %s",
+ KEY_AUTOSAVE,
+ error->message);
+ g_error_free (error);
+
+ /* If we fail to query mateconf key, disable auto save */
+ auto_save = FALSE;
+ }
+
+ return auto_save;
+}
+
+static void
+maybe_save_session (GsmManager *manager)
+{
+ GsmConsolekit *consolekit;
+ char *session_type;
+ GError *error;
+
+ consolekit = gsm_get_consolekit ();
+ session_type = gsm_consolekit_get_current_session_type (consolekit);
+
+ if (g_strcmp0 (session_type, GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW) == 0) {
+ goto out;
+ }
+
+ /* We only allow session saving when session is running or when
+ * logging out */
+ if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING &&
+ manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) {
+ goto out;
+ }
+
+ error = NULL;
+ gsm_session_save (manager->priv->clients, &error);
+
+ if (error) {
+ g_warning ("Error saving session: %s", error->message);
+ g_error_free (error);
+ }
+
+out:
+ g_object_unref (consolekit);
+ g_free (session_type);
+}
+
+static void
+on_client_end_session_response (GsmClient *client,
+ gboolean is_ok,
+ gboolean do_last,
+ gboolean cancel,
+ const char *reason,
+ GsmManager *manager)
+{
+ /* just ignore if received outside of shutdown */
+ if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+ return;
+ }
+
+ g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :"");
+
+ if (cancel) {
+ cancel_end_session (manager);
+ return;
+ }
+
+ manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client);
+
+ if (! is_ok) {
+ guint cookie;
+ GsmInhibitor *inhibitor;
+ char *app_id;
+ const char *bus_name;
+
+ /* FIXME: do we support updating the reason? */
+
+ /* Create JIT inhibit */
+ if (GSM_IS_DBUS_CLIENT (client)) {
+ bus_name = gsm_dbus_client_get_bus_name (GSM_DBUS_CLIENT (client));
+ } else {
+ bus_name = NULL;
+ }
+
+ app_id = g_strdup (gsm_client_peek_app_id (client));
+ if (IS_STRING_EMPTY (app_id)) {
+ /* XSMP clients don't give us an app id unless we start them */
+ g_free (app_id);
+ app_id = gsm_client_get_app_name (client);
+ }
+
+ cookie = _generate_unique_cookie (manager);
+ inhibitor = gsm_inhibitor_new_for_client (gsm_client_peek_id (client),
+ app_id,
+ GSM_INHIBITOR_FLAG_LOGOUT,
+ reason != NULL ? reason : _("Not responding"),
+ bus_name,
+ cookie);
+ g_free (app_id);
+ gsm_store_add (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
+ g_object_unref (inhibitor);
+ } else {
+ gsm_store_foreach_remove (manager->priv->inhibitors,
+ (GsmStoreFunc)inhibitor_has_client_id,
+ (gpointer)gsm_client_peek_id (client));
+ }
+
+ if (manager->priv->phase == GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+ if (manager->priv->query_clients == NULL) {
+ query_end_session_complete (manager);
+ }
+ } else if (manager->priv->phase == GSM_MANAGER_PHASE_END_SESSION) {
+ if (do_last) {
+ /* This only makes sense if we're in part 1 of
+ * GSM_MANAGER_PHASE_END_SESSION. Doing this in part 2
+ * can only happen because of a buggy client that loops
+ * wanting to be last again and again. The phase
+ * timeout will take care of this issue. */
+ manager->priv->next_query_clients = g_slist_prepend (manager->priv->next_query_clients,
+ client);
+ }
+
+ /* we can continue to the next step if all clients have replied
+ * and if there's no inhibitor */
+ if (manager->priv->query_clients != NULL
+ || gsm_manager_is_logout_inhibited (manager)) {
+ return;
+ }
+
+ if (manager->priv->next_query_clients != NULL) {
+ do_phase_end_session_part_2 (manager);
+ } else {
+ end_phase (manager);
+ }
+ }
+}
+
+static void
+on_xsmp_client_logout_request (GsmXSMPClient *client,
+ gboolean show_dialog,
+ GsmManager *manager)
+{
+ GError *error;
+ int logout_mode;
+
+ if (show_dialog) {
+ logout_mode = GSM_MANAGER_LOGOUT_MODE_NORMAL;
+ } else {
+ logout_mode = GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION;
+ }
+
+ error = NULL;
+ gsm_manager_logout (manager, logout_mode, &error);
+ if (error != NULL) {
+ g_warning ("Unable to logout: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+on_store_client_added (GsmStore *store,
+ const char *id,
+ GsmManager *manager)
+{
+ GsmClient *client;
+
+ g_debug ("GsmManager: Client added: %s", id);
+
+ client = (GsmClient *)gsm_store_lookup (store, id);
+
+ /* a bit hacky */
+ if (GSM_IS_XSMP_CLIENT (client)) {
+ g_signal_connect (client,
+ "register-request",
+ G_CALLBACK (on_xsmp_client_register_request),
+ manager);
+ g_signal_connect (client,
+ "logout-request",
+ G_CALLBACK (on_xsmp_client_logout_request),
+ manager);
+ }
+
+ g_signal_connect (client,
+ "end-session-response",
+ G_CALLBACK (on_client_end_session_response),
+ manager);
+
+ g_signal_emit (manager, signals [CLIENT_ADDED], 0, id);
+ /* FIXME: disconnect signal handler */
+}
+
+static void
+on_store_client_removed (GsmStore *store,
+ const char *id,
+ GsmManager *manager)
+{
+ g_debug ("GsmManager: Client removed: %s", id);
+
+ g_signal_emit (manager, signals [CLIENT_REMOVED], 0, id);
+}
+
+static void
+gsm_manager_set_client_store (GsmManager *manager,
+ GsmStore *store)
+{
+ g_return_if_fail (GSM_IS_MANAGER (manager));
+
+ if (store != NULL) {
+ g_object_ref (store);
+ }
+
+ if (manager->priv->clients != NULL) {
+ g_signal_handlers_disconnect_by_func (manager->priv->clients,
+ on_store_client_added,
+ manager);
+ g_signal_handlers_disconnect_by_func (manager->priv->clients,
+ on_store_client_removed,
+ manager);
+
+ g_object_unref (manager->priv->clients);
+ }
+
+
+ g_debug ("GsmManager: setting client store %p", store);
+
+ manager->priv->clients = store;
+
+ if (manager->priv->clients != NULL) {
+ g_signal_connect (manager->priv->clients,
+ "added",
+ G_CALLBACK (on_store_client_added),
+ manager);
+ g_signal_connect (manager->priv->clients,
+ "removed",
+ G_CALLBACK (on_store_client_removed),
+ manager);
+ }
+}
+
+static void
+gsm_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GsmManager *self;
+
+ self = GSM_MANAGER (object);
+
+ switch (prop_id) {
+ case PROP_FAILSAFE:
+ gsm_manager_set_failsafe (self, g_value_get_boolean (value));
+ break;
+ case PROP_CLIENT_STORE:
+ gsm_manager_set_client_store (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gsm_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GsmManager *self;
+
+ self = GSM_MANAGER (object);
+
+ switch (prop_id) {
+ case PROP_FAILSAFE:
+ g_value_set_boolean (value, self->priv->failsafe);
+ break;
+ case PROP_CLIENT_STORE:
+ g_value_set_object (value, self->priv->clients);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+_find_app_provides (const char *id,
+ GsmApp *app,
+ const char *service)
+{
+ return gsm_app_provides (app, service);
+}
+
+static GObject *
+gsm_manager_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GsmManager *manager;
+
+ manager = GSM_MANAGER (G_OBJECT_CLASS (gsm_manager_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+ return G_OBJECT (manager);
+}
+
+static void
+on_store_inhibitor_added (GsmStore *store,
+ const char *id,
+ GsmManager *manager)
+{
+ g_debug ("GsmManager: Inhibitor added: %s", id);
+ g_signal_emit (manager, signals [INHIBITOR_ADDED], 0, id);
+ update_idle (manager);
+}
+
+static void
+on_store_inhibitor_removed (GsmStore *store,
+ const char *id,
+ GsmManager *manager)
+{
+ g_debug ("GsmManager: Inhibitor removed: %s", id);
+ g_signal_emit (manager, signals [INHIBITOR_REMOVED], 0, id);
+ update_idle (manager);
+}
+
+static void
+gsm_manager_dispose (GObject *object)
+{
+ GsmManager *manager = GSM_MANAGER (object);
+
+ g_debug ("GsmManager: disposing manager");
+
+ if (manager->priv->clients != NULL) {
+ g_signal_handlers_disconnect_by_func (manager->priv->clients,
+ on_store_client_added,
+ manager);
+ g_signal_handlers_disconnect_by_func (manager->priv->clients,
+ on_store_client_removed,
+ manager);
+ g_object_unref (manager->priv->clients);
+ manager->priv->clients = NULL;
+ }
+
+ if (manager->priv->apps != NULL) {
+ g_object_unref (manager->priv->apps);
+ manager->priv->apps = NULL;
+ }
+
+ if (manager->priv->inhibitors != NULL) {
+ g_signal_handlers_disconnect_by_func (manager->priv->inhibitors,
+ on_store_inhibitor_added,
+ manager);
+ g_signal_handlers_disconnect_by_func (manager->priv->inhibitors,
+ on_store_inhibitor_removed,
+ manager);
+
+ g_object_unref (manager->priv->inhibitors);
+ manager->priv->inhibitors = NULL;
+ }
+
+ if (manager->priv->presence != NULL) {
+ g_object_unref (manager->priv->presence);
+ manager->priv->presence = NULL;
+ }
+
+ if (manager->priv->mateconf_client) {
+ if (manager->priv->desktop_notify_id != 0) {
+ mateconf_client_remove_dir (manager->priv->mateconf_client,
+ KEY_DESKTOP_DIR,
+ NULL);
+ mateconf_client_notify_remove (manager->priv->mateconf_client,
+ manager->priv->desktop_notify_id);
+ manager->priv->desktop_notify_id = 0;
+ }
+ if (manager->priv->lockdown_notify_id != 0) {
+ mateconf_client_remove_dir (manager->priv->mateconf_client,
+ KEY_LOCKDOWN_DIR,
+ NULL);
+ mateconf_client_notify_remove (manager->priv->mateconf_client,
+ manager->priv->lockdown_notify_id);
+ manager->priv->lockdown_notify_id = 0;
+ }
+
+ g_object_unref (manager->priv->mateconf_client);
+ manager->priv->mateconf_client = NULL;
+ }
+
+ if (manager->priv->up_client != NULL) {
+ g_object_unref (manager->priv->up_client);
+ manager->priv->up_client = NULL;
+ }
+
+ G_OBJECT_CLASS (gsm_manager_parent_class)->dispose (object);
+}
+
+static void
+gsm_manager_class_init (GsmManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gsm_manager_get_property;
+ object_class->set_property = gsm_manager_set_property;
+ object_class->constructor = gsm_manager_constructor;
+ object_class->finalize = gsm_manager_finalize;
+ object_class->dispose = gsm_manager_dispose;
+
+ signals [PHASE_CHANGED] =
+ g_signal_new ("phase-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmManagerClass, phase_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+
+ signals [SESSION_RUNNING] =
+ g_signal_new ("session-running",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmManagerClass, session_running),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals [SESSION_OVER] =
+ g_signal_new ("session-over",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmManagerClass, session_over),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [CLIENT_ADDED] =
+ g_signal_new ("client-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmManagerClass, client_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1, DBUS_TYPE_G_OBJECT_PATH);
+ signals [CLIENT_REMOVED] =
+ g_signal_new ("client-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmManagerClass, client_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1, DBUS_TYPE_G_OBJECT_PATH);
+ signals [INHIBITOR_ADDED] =
+ g_signal_new ("inhibitor-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmManagerClass, inhibitor_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1, DBUS_TYPE_G_OBJECT_PATH);
+ signals [INHIBITOR_REMOVED] =
+ g_signal_new ("inhibitor-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmManagerClass, inhibitor_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1, DBUS_TYPE_G_OBJECT_PATH);
+
+ g_object_class_install_property (object_class,
+ PROP_FAILSAFE,
+ g_param_spec_boolean ("failsafe",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_CLIENT_STORE,
+ g_param_spec_object ("client-store",
+ NULL,
+ NULL,
+ GSM_TYPE_STORE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (klass, sizeof (GsmManagerPrivate));
+
+ dbus_g_object_type_install_info (GSM_TYPE_MANAGER, &dbus_glib_gsm_manager_object_info);
+ dbus_g_error_domain_register (GSM_MANAGER_ERROR, NULL, GSM_MANAGER_TYPE_ERROR);
+}
+
+static void
+invalid_type_warning (const char *type)
+{
+ g_warning ("Error retrieving configuration key '%s': Invalid type",
+ type);
+}
+
+static void
+load_idle_delay_from_mateconf (GsmManager *manager)
+{
+ GError *error;
+ glong value;
+
+ error = NULL;
+ value = mateconf_client_get_int (manager->priv->mateconf_client,
+ KEY_IDLE_DELAY,
+ &error);
+ if (error == NULL) {
+ gsm_presence_set_idle_timeout (manager->priv->presence, value * 60000);
+ } else {
+ g_warning ("Error retrieving configuration key '%s': %s",
+ KEY_IDLE_DELAY,
+ error->message);
+ g_error_free (error);
+ }
+
+}
+
+static void
+on_mateconf_key_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ GsmManager *manager)
+{
+ const char *key;
+ MateConfValue *value;
+
+ key = mateconf_entry_get_key (entry);
+
+ if (! g_str_has_prefix (key, KEY_DESKTOP_DIR)
+ && ! g_str_has_prefix (key, KEY_LOCKDOWN_DIR)) {
+ return;
+ }
+
+ value = mateconf_entry_get_value (entry);
+
+ if (strcmp (key, KEY_IDLE_DELAY) == 0) {
+ if (value->type == MATECONF_VALUE_INT) {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+
+ gsm_presence_set_idle_timeout (manager->priv->presence, delay * 60000);
+ } else {
+ invalid_type_warning (key);
+ }
+ } else if (strcmp (key, KEY_LOCK_DISABLE) == 0) {
+ if (value->type == MATECONF_VALUE_BOOL) {
+ gboolean disabled;
+
+ disabled = mateconf_value_get_bool (value);
+
+ /* FIXME: handle this */
+ } else {
+ invalid_type_warning (key);
+ }
+ } else if (strcmp (key, KEY_USER_SWITCH_DISABLE) == 0) {
+
+ if (value->type == MATECONF_VALUE_BOOL) {
+ gboolean disabled;
+
+ disabled = mateconf_value_get_bool (value);
+
+ /* FIXME: handle this */
+ } else {
+ invalid_type_warning (key);
+ }
+
+ } else {
+ g_debug ("Config key not handled: %s", key);
+ }
+}
+
+static void
+on_presence_status_changed (GsmPresence *presence,
+ guint status,
+ GsmManager *manager)
+{
+ GsmConsolekit *consolekit;
+
+ consolekit = gsm_get_consolekit ();
+ gsm_consolekit_set_session_idle (consolekit,
+ (status == GSM_PRESENCE_STATUS_IDLE));
+}
+
+static void
+gsm_manager_init (GsmManager *manager)
+{
+
+ manager->priv = GSM_MANAGER_GET_PRIVATE (manager);
+
+ manager->priv->mateconf_client = mateconf_client_get_default ();
+
+ manager->priv->inhibitors = gsm_store_new ();
+ g_signal_connect (manager->priv->inhibitors,
+ "added",
+ G_CALLBACK (on_store_inhibitor_added),
+ manager);
+ g_signal_connect (manager->priv->inhibitors,
+ "removed",
+ G_CALLBACK (on_store_inhibitor_removed),
+ manager);
+
+ manager->priv->apps = gsm_store_new ();
+
+ manager->priv->presence = gsm_presence_new ();
+ g_signal_connect (manager->priv->presence,
+ "status-changed",
+ G_CALLBACK (on_presence_status_changed),
+ manager);
+
+ manager->priv->up_client = up_client_new ();
+
+ /* MateConf setup */
+ mateconf_client_add_dir (manager->priv->mateconf_client,
+ KEY_DESKTOP_DIR,
+ MATECONF_CLIENT_PRELOAD_RECURSIVE, NULL);
+ mateconf_client_add_dir (manager->priv->mateconf_client,
+ KEY_LOCKDOWN_DIR,
+ MATECONF_CLIENT_PRELOAD_NONE, NULL);
+
+ manager->priv->desktop_notify_id = mateconf_client_notify_add (manager->priv->mateconf_client,
+ KEY_DESKTOP_DIR,
+ (MateConfClientNotifyFunc)on_mateconf_key_changed,
+ manager,
+ NULL, NULL);
+ manager->priv->lockdown_notify_id = mateconf_client_notify_add (manager->priv->mateconf_client,
+ KEY_LOCKDOWN_DIR,
+ (MateConfClientNotifyFunc)on_mateconf_key_changed,
+ manager,
+ NULL, NULL);
+
+ load_idle_delay_from_mateconf (manager);
+}
+
+static void
+gsm_manager_finalize (GObject *object)
+{
+ GsmManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSM_IS_MANAGER (object));
+
+ manager = GSM_MANAGER (object);
+
+ g_return_if_fail (manager->priv != NULL);
+
+ G_OBJECT_CLASS (gsm_manager_parent_class)->finalize (object);
+}
+
+GsmManager *
+gsm_manager_new (GsmStore *client_store,
+ gboolean failsafe)
+{
+ if (manager_object != NULL) {
+ g_object_ref (manager_object);
+ } else {
+ gboolean res;
+
+ manager_object = g_object_new (GSM_TYPE_MANAGER,
+ "client-store", client_store,
+ "failsafe", failsafe,
+ NULL);
+
+ g_object_add_weak_pointer (manager_object,
+ (gpointer *) &manager_object);
+ res = register_manager (manager_object);
+ if (! res) {
+ g_object_unref (manager_object);
+ return NULL;
+ }
+ }
+
+ return GSM_MANAGER (manager_object);
+}
+
+gboolean
+gsm_manager_setenv (GsmManager *manager,
+ const char *variable,
+ const char *value,
+ GError **error)
+{
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ if (manager->priv->phase > GSM_MANAGER_PHASE_INITIALIZATION) {
+ g_set_error (error,
+ GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION,
+ "Setenv interface is only available during the Initialization phase");
+ return FALSE;
+ }
+
+ gsm_util_setenv (variable, value);
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_initialization_error (GsmManager *manager,
+ const char *message,
+ gboolean fatal,
+ GError **error)
+{
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ if (manager->priv->phase > GSM_MANAGER_PHASE_INITIALIZATION) {
+ g_set_error (error,
+ GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION,
+ "InitializationError interface is only available during the Initialization phase");
+ return FALSE;
+ }
+
+ gsm_util_init_error (fatal, "%s", message);
+
+ return TRUE;
+}
+
+static gboolean
+gsm_manager_is_switch_user_inhibited (GsmManager *manager)
+{
+ GsmInhibitor *inhibitor;
+
+ if (manager->priv->inhibitors == NULL) {
+ return FALSE;
+ }
+
+ inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors,
+ (GsmStoreFunc)inhibitor_has_flag,
+ GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_SWITCH_USER));
+ if (inhibitor == NULL) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+gsm_manager_is_suspend_inhibited (GsmManager *manager)
+{
+ GsmInhibitor *inhibitor;
+
+ if (manager->priv->inhibitors == NULL) {
+ return FALSE;
+ }
+
+ inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors,
+ (GsmStoreFunc)inhibitor_has_flag,
+ GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_SUSPEND));
+ if (inhibitor == NULL) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+request_reboot_privileges_completed (GsmConsolekit *consolekit,
+ gboolean success,
+ gboolean ask_later,
+ GError *error,
+ GsmManager *manager)
+{
+ /* make sure we disconnect the signal handler so that it's not called
+ * again next time the event is fired -- this can happen if the reboot
+ * is cancelled. */
+ g_signal_handlers_disconnect_by_func (consolekit,
+ request_reboot_privileges_completed,
+ manager);
+
+ g_object_unref (consolekit);
+
+ if (success) {
+ if (ask_later) {
+ manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT_INTERACT;
+ } else {
+ manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT;
+ }
+
+ end_phase (manager);
+ }
+}
+
+static void
+request_reboot (GsmManager *manager)
+{
+ GsmConsolekit *consolekit;
+ gboolean success;
+
+ g_debug ("GsmManager: requesting reboot");
+
+ /* We request the privileges before doing anything. There are a few
+ * different cases here:
+ *
+ * - no ConsoleKit: we fallback on MDM
+ * - no password required: everything is fine
+ * - password asked once: we ask for it now. If the user enters it
+ * fine, then all is great. If the user doesn't enter it fine, we
+ * don't do anything (so no logout).
+ * - password asked each time: we don't ask it for now since we don't
+ * want to ask for it twice. Instead we'll ask for it at the very
+ * end. If the user will enter it fine, then all is great again. If
+ * the user doesn't enter it fine, then we'll just fallback to MDM.
+ *
+ * The last case (password asked each time) is a bit broken, but
+ * there's really nothing we can do about it. Generally speaking,
+ * though, the password will only be asked once (unless the system is
+ * configured in paranoid mode), and most probably only if there are
+ * more than one user connected. So the general case is that it will
+ * just work fine.
+ */
+
+ consolekit = gsm_get_consolekit ();
+ g_signal_connect (consolekit,
+ "privileges-completed",
+ G_CALLBACK (request_reboot_privileges_completed),
+ manager);
+ success = gsm_consolekit_get_restart_privileges (consolekit);
+
+ if (!success) {
+ g_signal_handlers_disconnect_by_func (consolekit,
+ request_reboot_privileges_completed,
+ manager);
+ g_object_unref (consolekit);
+
+ manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT_MDM;
+ end_phase (manager);
+ }
+}
+
+static void
+request_shutdown_privileges_completed (GsmConsolekit *consolekit,
+ gboolean success,
+ gboolean ask_later,
+ GError *error,
+ GsmManager *manager)
+{
+ /* make sure we disconnect the signal handler so that it's not called
+ * again next time the event is fired -- this can happen if the reboot
+ * is cancelled. */
+ g_signal_handlers_disconnect_by_func (consolekit,
+ request_shutdown_privileges_completed,
+ manager);
+
+ g_object_unref (consolekit);
+
+ if (success) {
+ if (ask_later) {
+ manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT;
+ } else {
+ manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN;
+ }
+
+ end_phase (manager);
+ }
+}
+
+static void
+request_shutdown (GsmManager *manager)
+{
+ GsmConsolekit *consolekit;
+ gboolean success;
+
+ g_debug ("GsmManager: requesting shutdown");
+
+ /* See the comment in request_reboot() for some more details about how
+ * this works. */
+
+ consolekit = gsm_get_consolekit ();
+ g_signal_connect (consolekit,
+ "privileges-completed",
+ G_CALLBACK (request_shutdown_privileges_completed),
+ manager);
+ success = gsm_consolekit_get_stop_privileges (consolekit);
+
+ if (!success) {
+ g_signal_handlers_disconnect_by_func (consolekit,
+ request_shutdown_privileges_completed,
+ manager);
+ g_object_unref (consolekit);
+
+ manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN_MDM;
+ end_phase (manager);
+ }
+}
+
+static void
+request_suspend (GsmManager *manager)
+{
+ g_debug ("GsmManager: requesting suspend");
+
+ if (! gsm_manager_is_suspend_inhibited (manager)) {
+ manager_attempt_suspend (manager);
+ return;
+ }
+
+ if (manager->priv->inhibit_dialog != NULL) {
+ g_debug ("GsmManager: inhibit dialog already up");
+ gtk_window_present (GTK_WINDOW (manager->priv->inhibit_dialog));
+ return;
+ }
+
+ manager->priv->inhibit_dialog = gsm_inhibit_dialog_new (manager->priv->inhibitors,
+ manager->priv->clients,
+ GSM_LOGOUT_ACTION_SLEEP);
+
+ g_signal_connect (manager->priv->inhibit_dialog,
+ "response",
+ G_CALLBACK (inhibit_dialog_response),
+ manager);
+ gtk_widget_show (manager->priv->inhibit_dialog);
+}
+
+static void
+request_hibernate (GsmManager *manager)
+{
+ g_debug ("GsmManager: requesting hibernate");
+
+ /* hibernate uses suspend inhibit */
+ if (! gsm_manager_is_suspend_inhibited (manager)) {
+ manager_attempt_hibernate (manager);
+ return;
+ }
+
+ if (manager->priv->inhibit_dialog != NULL) {
+ g_debug ("GsmManager: inhibit dialog already up");
+ gtk_window_present (GTK_WINDOW (manager->priv->inhibit_dialog));
+ return;
+ }
+
+ manager->priv->inhibit_dialog = gsm_inhibit_dialog_new (manager->priv->inhibitors,
+ manager->priv->clients,
+ GSM_LOGOUT_ACTION_HIBERNATE);
+
+ g_signal_connect (manager->priv->inhibit_dialog,
+ "response",
+ G_CALLBACK (inhibit_dialog_response),
+ manager);
+ gtk_widget_show (manager->priv->inhibit_dialog);
+}
+
+
+static void
+request_logout (GsmManager *manager,
+ gboolean forceful_logout)
+{
+ g_debug ("GsmManager: requesting logout");
+
+ manager->priv->forceful_logout = forceful_logout;
+ manager->priv->logout_type = GSM_MANAGER_LOGOUT_LOGOUT;
+
+ end_phase (manager);
+}
+
+static void
+request_switch_user (GsmManager *manager)
+{
+ g_debug ("GsmManager: requesting user switch");
+
+ if (! gsm_manager_is_switch_user_inhibited (manager)) {
+ manager_switch_user (manager);
+ return;
+ }
+
+ if (manager->priv->inhibit_dialog != NULL) {
+ g_debug ("GsmManager: inhibit dialog already up");
+ gtk_window_present (GTK_WINDOW (manager->priv->inhibit_dialog));
+ return;
+ }
+
+ manager->priv->inhibit_dialog = gsm_inhibit_dialog_new (manager->priv->inhibitors,
+ manager->priv->clients,
+ GSM_LOGOUT_ACTION_SWITCH_USER);
+
+ g_signal_connect (manager->priv->inhibit_dialog,
+ "response",
+ G_CALLBACK (inhibit_dialog_response),
+ manager);
+ gtk_widget_show (manager->priv->inhibit_dialog);
+}
+
+static void
+logout_dialog_response (GsmLogoutDialog *logout_dialog,
+ guint response_id,
+ GsmManager *manager)
+{
+ g_debug ("GsmManager: Logout dialog response: %d", response_id);
+
+ gtk_widget_destroy (GTK_WIDGET (logout_dialog));
+
+ /* In case of dialog cancel, switch user, hibernate and
+ * suspend, we just perform the respective action and return,
+ * without shutting down the session. */
+ switch (response_id) {
+ case GTK_RESPONSE_CANCEL:
+ case GTK_RESPONSE_NONE:
+ case GTK_RESPONSE_DELETE_EVENT:
+ break;
+ case GSM_LOGOUT_RESPONSE_SWITCH_USER:
+ request_switch_user (manager);
+ break;
+ case GSM_LOGOUT_RESPONSE_HIBERNATE:
+ request_hibernate (manager);
+ break;
+ case GSM_LOGOUT_RESPONSE_SLEEP:
+ request_suspend (manager);
+ break;
+ case GSM_LOGOUT_RESPONSE_SHUTDOWN:
+ request_shutdown (manager);
+ break;
+ case GSM_LOGOUT_RESPONSE_REBOOT:
+ request_reboot (manager);
+ break;
+ case GSM_LOGOUT_RESPONSE_LOGOUT:
+ request_logout (manager, FALSE);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+show_shutdown_dialog (GsmManager *manager)
+{
+ GtkWidget *dialog;
+
+ if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+ /* Already shutting down, nothing more to do */
+ return;
+ }
+
+ dialog = gsm_get_shutdown_dialog (gdk_screen_get_default (),
+ gtk_get_current_event_time ());
+
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (logout_dialog_response),
+ manager);
+ gtk_widget_show (dialog);
+}
+
+static void
+show_logout_dialog (GsmManager *manager)
+{
+ GtkWidget *dialog;
+
+ if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+ /* Already shutting down, nothing more to do */
+ return;
+ }
+
+ dialog = gsm_get_logout_dialog (gdk_screen_get_default (),
+ gtk_get_current_event_time ());
+
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (logout_dialog_response),
+ manager);
+ gtk_widget_show (dialog);
+}
+
+static void
+user_logout (GsmManager *manager,
+ gboolean show_confirmation,
+ gboolean forceful_logout)
+{
+ gboolean logout_prompt;
+
+ if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+ /* Already shutting down, nothing more to do */
+ return;
+ }
+
+ logout_prompt =
+ mateconf_client_get_bool (manager->priv->mateconf_client,
+ "/apps/mate-session/options/logout_prompt",
+ NULL);
+
+ /* Global settings overides input parameter in order to disable confirmation
+ * dialog accordingly. If we're shutting down, we always show the confirmation
+ * dialog */
+ logout_prompt = (logout_prompt && show_confirmation);
+
+ if (logout_prompt) {
+ show_logout_dialog (manager);
+ } else {
+ request_logout (manager, forceful_logout);
+ }
+}
+
+/*
+ dbus-send --session --type=method_call --print-reply
+ --dest=org.mate.SessionManager
+ /org/mate/SessionManager
+ org.freedesktop.DBus.Introspectable.Introspect
+*/
+
+gboolean
+gsm_manager_set_phase (GsmManager *manager,
+ GsmManagerPhase phase)
+{
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+ manager->priv->phase = phase;
+ return (TRUE);
+}
+
+gboolean
+gsm_manager_shutdown (GsmManager *manager,
+ GError **error)
+{
+ g_debug ("GsmManager: Shutdown called");
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) {
+ g_set_error (error,
+ GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_NOT_IN_RUNNING,
+ "Shutdown interface is only available during the Running phase");
+ return FALSE;
+ }
+
+ show_shutdown_dialog (manager);
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_can_shutdown (GsmManager *manager,
+ gboolean *shutdown_available,
+ GError **error)
+{
+ GsmConsolekit *consolekit;
+ gboolean can_suspend;
+ gboolean can_hibernate;
+
+ g_object_get (manager->priv->up_client,
+ "can-suspend", &can_suspend,
+ "can-hibernate", &can_hibernate,
+ NULL);
+
+ g_debug ("GsmManager: CanShutdown called");
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ consolekit = gsm_get_consolekit ();
+ *shutdown_available = gsm_consolekit_can_stop (consolekit)
+ || gsm_consolekit_can_restart (consolekit)
+ || can_suspend
+ || can_hibernate;
+ g_object_unref (consolekit);
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_logout (GsmManager *manager,
+ guint logout_mode,
+ GError **error)
+{
+ g_debug ("GsmManager: Logout called");
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) {
+ g_set_error (error,
+ GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_NOT_IN_RUNNING,
+ "Shutdown interface is only available during the Running phase");
+ return FALSE;
+ }
+
+ switch (logout_mode) {
+ case GSM_MANAGER_LOGOUT_MODE_NORMAL:
+ user_logout (manager, TRUE, FALSE);
+ break;
+
+ case GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION:
+ user_logout (manager, FALSE, FALSE);
+ break;
+
+ case GSM_MANAGER_LOGOUT_MODE_FORCE:
+ user_logout (manager, FALSE, TRUE);
+ break;
+
+ default:
+ g_debug ("Unknown logout mode option");
+
+ g_set_error (error,
+ GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_INVALID_OPTION,
+ "Unknown logout mode flag");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_register_client (GsmManager *manager,
+ const char *app_id,
+ const char *startup_id,
+ DBusGMethodInvocation *context)
+{
+ char *new_startup_id;
+ char *sender;
+ GsmClient *client;
+ GsmApp *app;
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ app = NULL;
+ client = NULL;
+
+ g_debug ("GsmManager: RegisterClient %s", startup_id);
+
+ if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+ GError *new_error;
+
+ g_debug ("Unable to register client: shutting down");
+
+ new_error = g_error_new (GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_NOT_IN_RUNNING,
+ "Unable to register client");
+ dbus_g_method_return_error (context, new_error);
+ g_error_free (new_error);
+ return FALSE;
+ }
+
+ if (IS_STRING_EMPTY (startup_id)) {
+ new_startup_id = gsm_util_generate_startup_id ();
+ } else {
+
+ client = (GsmClient *)gsm_store_find (manager->priv->clients,
+ (GsmStoreFunc)_client_has_startup_id,
+ (char *)startup_id);
+ /* We can't have two clients with the same startup id. */
+ if (client != NULL) {
+ GError *new_error;
+
+ g_debug ("Unable to register client: already registered");
+
+ new_error = g_error_new (GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_ALREADY_REGISTERED,
+ "Unable to register client");
+ dbus_g_method_return_error (context, new_error);
+ g_error_free (new_error);
+ return FALSE;
+ }
+
+ new_startup_id = g_strdup (startup_id);
+ }
+
+ g_debug ("GsmManager: Adding new client %s to session", new_startup_id);
+
+ if (app == NULL && !IS_STRING_EMPTY (startup_id)) {
+ app = find_app_for_startup_id (manager, startup_id);
+ }
+ if (app == NULL && !IS_STRING_EMPTY (app_id)) {
+ /* try to associate this app id with a known app */
+ app = find_app_for_app_id (manager, app_id);
+ }
+
+ sender = dbus_g_method_get_sender (context);
+ client = gsm_dbus_client_new (new_startup_id, sender);
+ g_free (sender);
+ if (client == NULL) {
+ GError *new_error;
+
+ g_debug ("Unable to create client");
+
+ new_error = g_error_new (GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_GENERAL,
+ "Unable to register client");
+ dbus_g_method_return_error (context, new_error);
+ g_error_free (new_error);
+ return FALSE;
+ }
+
+ gsm_store_add (manager->priv->clients, gsm_client_peek_id (client), G_OBJECT (client));
+ /* the store will own the ref */
+ g_object_unref (client);
+
+ if (app != NULL) {
+ gsm_client_set_app_id (client, gsm_app_peek_app_id (app));
+ gsm_app_registered (app);
+ } else {
+ /* if an app id is specified store it in the client
+ so we can save it later */
+ gsm_client_set_app_id (client, app_id);
+ }
+
+ gsm_client_set_status (client, GSM_CLIENT_REGISTERED);
+
+ g_assert (new_startup_id != NULL);
+ g_free (new_startup_id);
+
+ dbus_g_method_return (context, gsm_client_peek_id (client));
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_unregister_client (GsmManager *manager,
+ const char *client_id,
+ DBusGMethodInvocation *context)
+{
+ GsmClient *client;
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ g_debug ("GsmManager: UnregisterClient %s", client_id);
+
+ client = (GsmClient *)gsm_store_lookup (manager->priv->clients, client_id);
+ if (client == NULL) {
+ GError *new_error;
+
+ g_debug ("Unable to unregister client: not registered");
+
+ new_error = g_error_new (GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_NOT_REGISTERED,
+ "Unable to unregister client");
+ dbus_g_method_return_error (context, new_error);
+ g_error_free (new_error);
+ return FALSE;
+ }
+
+ /* don't disconnect client here, only change the status.
+ Wait until it leaves the bus before disconnecting it */
+ gsm_client_set_status (client, GSM_CLIENT_UNREGISTERED);
+
+ dbus_g_method_return (context);
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_inhibit (GsmManager *manager,
+ const char *app_id,
+ guint toplevel_xid,
+ const char *reason,
+ guint flags,
+ DBusGMethodInvocation *context)
+{
+ GsmInhibitor *inhibitor;
+ guint cookie;
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ g_debug ("GsmManager: Inhibit xid=%u app_id=%s reason=%s flags=%u",
+ toplevel_xid,
+ app_id,
+ reason,
+ flags);
+
+ if (IS_STRING_EMPTY (app_id)) {
+ GError *new_error;
+
+ new_error = g_error_new (GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_GENERAL,
+ "Application ID not specified");
+ g_debug ("GsmManager: Unable to inhibit: %s", new_error->message);
+ dbus_g_method_return_error (context, new_error);
+ g_error_free (new_error);
+ return FALSE;
+ }
+
+ if (IS_STRING_EMPTY (reason)) {
+ GError *new_error;
+
+ new_error = g_error_new (GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_GENERAL,
+ "Reason not specified");
+ g_debug ("GsmManager: Unable to inhibit: %s", new_error->message);
+ dbus_g_method_return_error (context, new_error);
+ g_error_free (new_error);
+ return FALSE;
+ }
+
+ if (flags == 0) {
+ GError *new_error;
+
+ new_error = g_error_new (GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_GENERAL,
+ "Invalid inhibit flags");
+ g_debug ("GsmManager: Unable to inhibit: %s", new_error->message);
+ dbus_g_method_return_error (context, new_error);
+ g_error_free (new_error);
+ return FALSE;
+ }
+
+ cookie = _generate_unique_cookie (manager);
+ inhibitor = gsm_inhibitor_new (app_id,
+ toplevel_xid,
+ flags,
+ reason,
+ dbus_g_method_get_sender (context),
+ cookie);
+ gsm_store_add (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
+ g_object_unref (inhibitor);
+
+ dbus_g_method_return (context, cookie);
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_uninhibit (GsmManager *manager,
+ guint cookie,
+ DBusGMethodInvocation *context)
+{
+ GsmInhibitor *inhibitor;
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ g_debug ("GsmManager: Uninhibit %u", cookie);
+
+ inhibitor = (GsmInhibitor *)gsm_store_find (manager->priv->inhibitors,
+ (GsmStoreFunc)_find_by_cookie,
+ &cookie);
+ if (inhibitor == NULL) {
+ GError *new_error;
+
+ new_error = g_error_new (GSM_MANAGER_ERROR,
+ GSM_MANAGER_ERROR_GENERAL,
+ "Unable to uninhibit: Invalid cookie");
+ dbus_g_method_return_error (context, new_error);
+ g_debug ("Unable to uninhibit: %s", new_error->message);
+ g_error_free (new_error);
+ return FALSE;
+ }
+
+ g_debug ("GsmManager: removing inhibitor %s %u reason '%s' %u connection %s",
+ gsm_inhibitor_peek_app_id (inhibitor),
+ gsm_inhibitor_peek_toplevel_xid (inhibitor),
+ gsm_inhibitor_peek_reason (inhibitor),
+ gsm_inhibitor_peek_flags (inhibitor),
+ gsm_inhibitor_peek_bus_name (inhibitor));
+
+ gsm_store_remove (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor));
+
+ dbus_g_method_return (context);
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_is_inhibited (GsmManager *manager,
+ guint flags,
+ gboolean *is_inhibited,
+ GError *error)
+{
+ GsmInhibitor *inhibitor;
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ if (manager->priv->inhibitors == NULL
+ || gsm_store_size (manager->priv->inhibitors) == 0) {
+ *is_inhibited = FALSE;
+ return TRUE;
+ }
+
+ inhibitor = (GsmInhibitor *) gsm_store_find (manager->priv->inhibitors,
+ (GsmStoreFunc)inhibitor_has_flag,
+ GUINT_TO_POINTER (flags));
+ if (inhibitor == NULL) {
+ *is_inhibited = FALSE;
+ } else {
+ *is_inhibited = TRUE;
+ }
+
+ return TRUE;
+
+}
+
+static gboolean
+listify_store_ids (char *id,
+ GObject *object,
+ GPtrArray **array)
+{
+ g_ptr_array_add (*array, g_strdup (id));
+ return FALSE;
+}
+
+gboolean
+gsm_manager_get_clients (GsmManager *manager,
+ GPtrArray **clients,
+ GError **error)
+{
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ if (clients == NULL) {
+ return FALSE;
+ }
+
+ *clients = g_ptr_array_new ();
+ gsm_store_foreach (manager->priv->clients, (GsmStoreFunc)listify_store_ids, clients);
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_get_inhibitors (GsmManager *manager,
+ GPtrArray **inhibitors,
+ GError **error)
+{
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ if (inhibitors == NULL) {
+ return FALSE;
+ }
+
+ *inhibitors = g_ptr_array_new ();
+ gsm_store_foreach (manager->priv->inhibitors,
+ (GsmStoreFunc) listify_store_ids,
+ inhibitors);
+
+ return TRUE;
+}
+
+
+static gboolean
+_app_has_autostart_condition (const char *id,
+ GsmApp *app,
+ const char *condition)
+{
+ gboolean has;
+ gboolean disabled;
+
+ has = gsm_app_has_autostart_condition (app, condition);
+ disabled = gsm_app_peek_is_disabled (app);
+
+ return has && !disabled;
+}
+
+gboolean
+gsm_manager_is_autostart_condition_handled (GsmManager *manager,
+ const char *condition,
+ gboolean *handled,
+ GError **error)
+{
+ GsmApp *app;
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+ app = (GsmApp *) gsm_store_find (manager->priv->apps,(
+ GsmStoreFunc) _app_has_autostart_condition,
+ (char *)condition);
+
+ if (app != NULL) {
+ *handled = TRUE;
+ } else {
+ *handled = FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+append_app (GsmManager *manager,
+ GsmApp *app)
+{
+ const char *id;
+ const char *app_id;
+ GsmApp *dup;
+
+ id = gsm_app_peek_id (app);
+ if (IS_STRING_EMPTY (id)) {
+ g_debug ("GsmManager: not adding app: no id");
+ return;
+ }
+
+ dup = (GsmApp *)gsm_store_lookup (manager->priv->apps, id);
+ if (dup != NULL) {
+ g_debug ("GsmManager: not adding app: already added");
+ return;
+ }
+
+ app_id = gsm_app_peek_app_id (app);
+ if (IS_STRING_EMPTY (app_id)) {
+ g_debug ("GsmManager: not adding app: no app-id");
+ return;
+ }
+
+ dup = find_app_for_app_id (manager, app_id);
+ if (dup != NULL) {
+ g_debug ("GsmManager: not adding app: app-id already exists");
+ return;
+ }
+
+ gsm_store_add (manager->priv->apps, id, G_OBJECT (app));
+}
+
+gboolean
+gsm_manager_add_autostart_app (GsmManager *manager,
+ const char *path,
+ const char *provides)
+{
+ GsmApp *app;
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ /* first check to see if service is already provided */
+ if (provides != NULL) {
+ GsmApp *dup;
+
+ dup = (GsmApp *)gsm_store_find (manager->priv->apps,
+ (GsmStoreFunc)_find_app_provides,
+ (char *)provides);
+ if (dup != NULL) {
+ g_debug ("GsmManager: service '%s' is already provided", provides);
+ return FALSE;
+ }
+ }
+
+ app = gsm_autostart_app_new (path);
+ if (app == NULL) {
+ g_warning ("could not read %s", path);
+ return FALSE;
+ }
+
+ g_debug ("GsmManager: read %s", path);
+ append_app (manager, app);
+ g_object_unref (app);
+
+ return TRUE;
+}
+
+gboolean
+gsm_manager_add_autostart_apps_from_dir (GsmManager *manager,
+ const char *path)
+{
+ GDir *dir;
+ const char *name;
+
+ g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ g_debug ("GsmManager: *** Adding autostart apps for %s", path);
+
+ dir = g_dir_open (path, 0, NULL);
+ if (dir == NULL) {
+ return FALSE;
+ }
+
+ while ((name = g_dir_read_name (dir))) {
+ char *desktop_file;
+
+ if (!g_str_has_suffix (name, ".desktop")) {
+ continue;
+ }
+
+ desktop_file = g_build_filename (path, name, NULL);
+ gsm_manager_add_autostart_app (manager, desktop_file, NULL);
+ g_free (desktop_file);
+ }
+
+ g_dir_close (dir);
+
+ return TRUE;
+}