summaryrefslogtreecommitdiffstats
path: root/daemon/gdm-session.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/gdm-session.c')
-rw-r--r--daemon/gdm-session.c4132
1 files changed, 4132 insertions, 0 deletions
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
new file mode 100644
index 0000000..4b70973
--- /dev/null
+++ b/daemon/gdm-session.c
@@ -0,0 +1,4132 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gdm-session.h"
+#include "gdm-session-glue.h"
+#include "gdm-dbus-util.h"
+
+#include "gdm-session.h"
+#include "gdm-session-enum-types.h"
+#include "gdm-session-worker-common.h"
+#include "gdm-session-worker-job.h"
+#include "gdm-session-worker-glue.h"
+#include "gdm-common.h"
+
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+#define GDM_SESSION_DBUS_ERROR_CANCEL "org.gnome.DisplayManager.Session.Error.Cancel"
+#define GDM_SESSION_DBUS_OBJECT_PATH "/org/gnome/DisplayManager/Session"
+
+#define GDM_WORKER_DBUS_PATH "/org/gnome/DisplayManager/Worker"
+
+typedef struct
+{
+ GdmSession *session;
+ GdmSessionWorkerJob *job;
+ GPid worker_pid;
+ char *service_name;
+ GDBusMethodInvocation *starting_invocation;
+ char *starting_username;
+ GDBusMethodInvocation *pending_invocation;
+ GdmDBusWorkerManager *worker_manager_interface;
+ GdmDBusWorker *worker_proxy;
+ GCancellable *worker_cancellable;
+ char *session_id;
+ guint32 is_stopping : 1;
+
+ GPid reauth_pid_of_caller;
+} GdmSessionConversation;
+
+struct _GdmSession
+{
+ GObject parent;
+
+ /* per open scope */
+ char *selected_program;
+ char *selected_session;
+ char *saved_session;
+ char *saved_session_type;
+ char *saved_language;
+ char *selected_user;
+ char *user_x11_authority_file;
+
+ char *timed_login_username;
+ int timed_login_delay;
+ GList *pending_timed_login_invocations;
+
+ GHashTable *conversations;
+
+ GdmSessionConversation *session_conversation;
+
+ char **conversation_environment;
+
+ GdmDBusUserVerifier *user_verifier_interface;
+ GHashTable *user_verifier_extensions;
+ GdmDBusGreeter *greeter_interface;
+ GdmDBusRemoteGreeter *remote_greeter_interface;
+ GdmDBusChooser *chooser_interface;
+
+ GList *pending_worker_connections;
+ GList *outside_connections;
+
+ GPid session_pid;
+
+ /* object lifetime scope */
+ char *session_type;
+ char *display_name;
+ char *display_hostname;
+ char *display_device;
+ char *display_seat_id;
+ char *display_x11_authority_file;
+ gboolean display_is_local;
+
+ GdmSessionVerificationMode verification_mode;
+
+ uid_t allowed_user;
+
+ char *fallback_session_name;
+
+ GDBusServer *worker_server;
+ GDBusServer *outside_server;
+ GHashTable *environment;
+
+ GStrv supported_session_types;
+
+ guint32 is_program_session : 1;
+ guint32 display_is_initial : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_VERIFICATION_MODE,
+ PROP_ALLOWED_USER,
+ PROP_DISPLAY_NAME,
+ PROP_DISPLAY_HOSTNAME,
+ PROP_DISPLAY_IS_LOCAL,
+ PROP_DISPLAY_IS_INITIAL,
+ PROP_SESSION_TYPE,
+ PROP_DISPLAY_DEVICE,
+ PROP_DISPLAY_SEAT_ID,
+ PROP_DISPLAY_X11_AUTHORITY_FILE,
+ PROP_USER_X11_AUTHORITY_FILE,
+ PROP_CONVERSATION_ENVIRONMENT,
+ PROP_SUPPORTED_SESSION_TYPES,
+};
+
+enum {
+ CONVERSATION_STARTED = 0,
+ CONVERSATION_STOPPED,
+ SETUP_COMPLETE,
+ CANCELLED,
+ HOSTNAME_SELECTED,
+ CLIENT_REJECTED,
+ CLIENT_CONNECTED,
+ CLIENT_DISCONNECTED,
+ CLIENT_READY_FOR_SESSION_TO_START,
+ DISCONNECTED,
+ AUTHENTICATION_FAILED,
+ VERIFICATION_COMPLETE,
+ SESSION_OPENED,
+ SESSION_OPENED_FAILED,
+ SESSION_STARTED,
+ SESSION_START_FAILED,
+ SESSION_EXITED,
+ SESSION_DIED,
+ REAUTHENTICATION_STARTED,
+ REAUTHENTICATED,
+ LAST_SIGNAL
+};
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+static gboolean gdm_session_is_wayland_session (GdmSession *self);
+#endif
+static void update_session_type (GdmSession *self);
+static void set_session_type (GdmSession *self,
+ const char *session_type);
+static void close_conversation (GdmSessionConversation *conversation);
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GdmSession,
+ gdm_session,
+ G_TYPE_OBJECT);
+
+static GdmSessionConversation *
+find_conversation_by_name (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ conversation = g_hash_table_lookup (self->conversations, service_name);
+
+ if (conversation == NULL) {
+ g_warning ("Tried to look up non-existent conversation %s", service_name);
+ }
+
+ return conversation;
+}
+
+static void
+report_and_stop_conversation (GdmSession *self,
+ const char *service_name,
+ GError *error)
+{
+ g_dbus_error_strip_remote_error (error);
+
+ if (self->user_verifier_interface != NULL) {
+ if (g_error_matches (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE) ||
+ g_error_matches (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_TOO_MANY_RETRIES)) {
+ gdm_dbus_user_verifier_emit_service_unavailable (self->user_verifier_interface,
+ service_name,
+ error->message);
+ } else {
+ gdm_dbus_user_verifier_emit_problem (self->user_verifier_interface,
+ service_name,
+ error->message);
+ }
+ gdm_dbus_user_verifier_emit_verification_failed (self->user_verifier_interface,
+ service_name);
+ }
+
+ gdm_session_stop_conversation (self, service_name);
+}
+
+static void
+on_authenticate_cb (GdmDBusWorker *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+
+ worked = gdm_dbus_worker_call_authenticate_finish (proxy, res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (worked) {
+ gdm_session_authorize (self, service_name);
+ } else {
+ g_signal_emit (self,
+ signals[AUTHENTICATION_FAILED],
+ 0,
+ service_name,
+ conversation->worker_pid);
+ report_and_stop_conversation (self, service_name, error);
+ }
+}
+
+static void
+on_authorize_cb (GdmDBusWorker *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+
+ worked = gdm_dbus_worker_call_authorize_finish (proxy, res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (worked) {
+ gdm_session_accredit (self, service_name);
+ } else {
+ report_and_stop_conversation (self, service_name, error);
+ }
+}
+
+static void
+on_establish_credentials_cb (GdmDBusWorker *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+
+ worked = gdm_dbus_worker_call_establish_credentials_finish (proxy, res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = g_object_ref (conversation->session);
+ service_name = g_strdup (conversation->service_name);
+
+ if (worked) {
+ switch (self->verification_mode) {
+ case GDM_SESSION_VERIFICATION_MODE_LOGIN:
+ case GDM_SESSION_VERIFICATION_MODE_CHOOSER:
+ gdm_session_open_session (self, service_name);
+ break;
+ case GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE:
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_verification_complete (self->user_verifier_interface,
+ service_name);
+ g_signal_emit (self, signals[VERIFICATION_COMPLETE], 0, service_name);
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ report_and_stop_conversation (self, service_name, error);
+ }
+
+ g_free (service_name);
+ g_object_unref (self);
+}
+
+static gboolean
+supports_session_type (GdmSession *self,
+ const char *session_type)
+{
+ if (session_type == NULL)
+ return TRUE;
+
+ return g_strv_contains ((const char * const *) self->supported_session_types,
+ session_type);
+}
+
+static char **
+get_system_session_dirs (GdmSession *self,
+ const char *type)
+{
+ GArray *search_array = NULL;
+ char **search_dirs;
+ int i, j;
+ const gchar * const *system_data_dirs = g_get_system_data_dirs ();
+
+ static const char *x_search_dirs[] = {
+ "/etc/X11/sessions/",
+ DMCONFDIR "/Sessions/",
+ DATADIR "/gdm/BuiltInSessions/",
+ DATADIR "/xsessions/",
+ };
+
+ static const char *wayland_search_dir = DATADIR "/wayland-sessions/";
+
+ search_array = g_array_new (TRUE, TRUE, sizeof (char *));
+
+ for (j = 0; self->supported_session_types[j] != NULL; j++) {
+ const char *supported_type = self->supported_session_types[j];
+
+ if (g_str_equal (supported_type, "x11") &&
+ (type == NULL || g_str_equal (type, supported_type))) {
+ for (i = 0; system_data_dirs[i]; i++) {
+ gchar *dir = g_build_filename (system_data_dirs[i], "xsessions", NULL);
+ g_array_append_val (search_array, dir);
+ }
+
+ g_array_append_vals (search_array, x_search_dirs, G_N_ELEMENTS (x_search_dirs));
+ }
+
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ if (g_str_equal (supported_type, "wayland") &&
+ (type == NULL || g_str_equal (type, supported_type))) {
+ for (i = 0; system_data_dirs[i]; i++) {
+ gchar *dir = g_build_filename (system_data_dirs[i], "wayland-sessions", NULL);
+ g_array_append_val (search_array, dir);
+ }
+
+ g_array_append_val (search_array, wayland_search_dir);
+ }
+#endif
+ }
+
+ search_dirs = g_strdupv ((char **) search_array->data);
+
+ g_array_free (search_array, TRUE);
+
+ return search_dirs;
+}
+
+static gboolean
+is_prog_in_path (const char *prog)
+{
+ char *f;
+ gboolean ret;
+
+ f = g_find_program_in_path (prog);
+ ret = (f != NULL);
+ g_free (f);
+ return ret;
+}
+
+static GKeyFile *
+load_key_file_for_file (GdmSession *self,
+ const char *file,
+ const char *type,
+ char **full_path)
+{
+ GKeyFile *key_file;
+ GError *error = NULL;
+ gboolean res;
+ char **search_dirs;
+
+ key_file = g_key_file_new ();
+
+ search_dirs = get_system_session_dirs (self, type);
+
+ error = NULL;
+ res = g_key_file_load_from_dirs (key_file,
+ file,
+ (const char **) search_dirs,
+ full_path,
+ G_KEY_FILE_NONE,
+ &error);
+ if (! res) {
+ g_debug ("GdmSession: File '%s' not found in search dirs", file);
+ if (error != NULL) {
+ g_debug ("GdmSession: %s", error->message);
+ g_error_free (error);
+ }
+ g_key_file_free (key_file);
+ key_file = NULL;
+ }
+
+ g_strfreev (search_dirs);
+
+ return key_file;
+}
+
+static gboolean
+get_session_command_for_file (GdmSession *self,
+ const char *file,
+ const char *type,
+ char **command)
+{
+ GKeyFile *key_file;
+ GError *error;
+ char *exec;
+ gboolean ret;
+ gboolean res;
+
+ exec = NULL;
+ ret = FALSE;
+ if (command != NULL) {
+ *command = NULL;
+ }
+
+ if (!supports_session_type (self, type)) {
+ g_debug ("GdmSession: ignoring %s session command request for file '%s'",
+ type, file);
+ goto out;
+ }
+
+ g_debug ("GdmSession: getting session command for file '%s'", file);
+ key_file = load_key_file_for_file (self, file, type, NULL);
+ if (key_file == NULL) {
+ goto out;
+ }
+
+ error = NULL;
+ res = g_key_file_get_boolean (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_HIDDEN,
+ &error);
+ if (error == NULL && res) {
+ g_debug ("GdmSession: Session %s is marked as hidden", file);
+ goto out;
+ }
+
+ exec = g_key_file_get_string (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
+ NULL);
+ if (exec != NULL) {
+ res = is_prog_in_path (exec);
+ g_free (exec);
+ exec = NULL;
+
+ if (! res) {
+ g_debug ("GdmSession: Command not found: %s",
+ G_KEY_FILE_DESKTOP_KEY_TRY_EXEC);
+ goto out;
+ }
+ }
+
+ error = NULL;
+ exec = g_key_file_get_string (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
+ &error);
+ if (error != NULL) {
+ g_debug ("GdmSession: %s key not found: %s",
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (command != NULL) {
+ *command = g_strdup (exec);
+ }
+ ret = TRUE;
+
+out:
+ g_free (exec);
+
+ return ret;
+}
+
+static gboolean
+get_session_command_for_name (GdmSession *self,
+ const char *name,
+ const char *type,
+ char **command)
+{
+ gboolean res;
+ char *filename;
+
+ filename = g_strdup_printf ("%s.desktop", name);
+ res = get_session_command_for_file (self, filename, type, command);
+ g_free (filename);
+
+ return res;
+}
+
+static const char *
+get_default_language_name (GdmSession *self)
+{
+ const char *default_language;
+
+ if (self->saved_language != NULL) {
+ return self->saved_language;
+ }
+
+ default_language = g_hash_table_lookup (self->environment,
+ "LANG");
+
+ if (default_language != NULL) {
+ return default_language;
+ }
+
+ return setlocale (LC_MESSAGES, NULL);
+}
+
+static const char *
+get_fallback_session_name (GdmSession *self)
+{
+ char **search_dirs;
+ int i;
+ char *name;
+ GSequence *sessions;
+ GSequenceIter *session;
+
+ if (self->fallback_session_name != NULL) {
+ /* verify that the cached version still exists */
+ if (get_session_command_for_name (self, self->fallback_session_name, NULL, NULL)) {
+ goto out;
+ }
+ }
+
+ name = g_strdup ("gnome");
+ if (get_session_command_for_name (self, name, NULL, NULL)) {
+ g_free (self->fallback_session_name);
+ self->fallback_session_name = name;
+ goto out;
+ }
+ g_free (name);
+
+ sessions = g_sequence_new (g_free);
+
+ search_dirs = get_system_session_dirs (self, NULL);
+ for (i = 0; search_dirs[i] != NULL; i++) {
+ GDir *dir;
+ const char *base_name;
+
+ dir = g_dir_open (search_dirs[i], 0, NULL);
+
+ if (dir == NULL) {
+ continue;
+ }
+
+ do {
+ base_name = g_dir_read_name (dir);
+
+ if (base_name == NULL) {
+ break;
+ }
+
+ if (!g_str_has_suffix (base_name, ".desktop")) {
+ continue;
+ }
+
+ if (get_session_command_for_file (self, base_name, NULL, NULL)) {
+ name = g_strndup (base_name, strlen (base_name) - strlen (".desktop"));
+ g_sequence_insert_sorted (sessions, name, (GCompareDataFunc) g_strcmp0, NULL);
+ }
+ } while (base_name != NULL);
+
+ g_dir_close (dir);
+ }
+ g_strfreev (search_dirs);
+
+ name = NULL;
+ session = g_sequence_get_begin_iter (sessions);
+
+ if (g_sequence_iter_is_end (session))
+ g_error ("GdmSession: no session desktop files installed, aborting...");
+
+ do {
+ name = g_sequence_get (session);
+ if (name) {
+ break;
+ }
+ session = g_sequence_iter_next (session);
+ } while (!g_sequence_iter_is_end (session));
+
+ g_free (self->fallback_session_name);
+ self->fallback_session_name = g_strdup (name);
+
+ g_sequence_free (sessions);
+
+ out:
+ return self->fallback_session_name;
+}
+
+static const char *
+get_default_session_name (GdmSession *self)
+{
+ if (self->saved_session != NULL) {
+ return self->saved_session;
+ }
+
+ return get_fallback_session_name (self);
+}
+
+static void
+gdm_session_defaults_changed (GdmSession *self)
+{
+
+ update_session_type (self);
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_emit_default_language_name_changed (self->greeter_interface,
+ get_default_language_name (self));
+ gdm_dbus_greeter_emit_default_session_name_changed (self->greeter_interface,
+ get_default_session_name (self));
+ }
+}
+
+void
+gdm_session_select_user (GdmSession *self,
+ const char *text)
+{
+
+ g_debug ("GdmSession: selecting user '%s' for session '%s' (%p)",
+ text,
+ gdm_session_get_session_id (self),
+ self);
+
+ g_free (self->selected_user);
+ self->selected_user = g_strdup (text);
+
+ g_free (self->saved_session);
+ self->saved_session = NULL;
+
+ g_free (self->saved_session_type);
+ self->saved_session_type = NULL;
+
+ g_free (self->saved_language);
+ self->saved_language = NULL;
+}
+
+static void
+cancel_pending_query (GdmSessionConversation *conversation)
+{
+ if (conversation->pending_invocation == NULL) {
+ return;
+ }
+
+ g_debug ("GdmSession: Cancelling pending query");
+
+ g_dbus_method_invocation_return_dbus_error (conversation->pending_invocation,
+ GDM_SESSION_DBUS_ERROR_CANCEL,
+ "Operation cancelled");
+ conversation->pending_invocation = NULL;
+}
+
+static void
+answer_pending_query (GdmSessionConversation *conversation,
+ const char *answer)
+{
+ g_dbus_method_invocation_return_value (conversation->pending_invocation,
+ g_variant_new ("(s)", answer));
+ conversation->pending_invocation = NULL;
+}
+
+static void
+set_pending_query (GdmSessionConversation *conversation,
+ GDBusMethodInvocation *message)
+{
+ g_assert (conversation->pending_invocation == NULL);
+
+ conversation->pending_invocation = g_object_ref (message);
+}
+
+static gboolean
+gdm_session_handle_choice_list_query (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *prompt_message,
+ GVariant *query,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+ GdmDBusUserVerifierChoiceList *choice_list_interface = NULL;
+
+ g_debug ("GdmSession: choice query for service '%s'", service_name);
+
+ if (self->user_verifier_extensions != NULL)
+ choice_list_interface = g_hash_table_lookup (self->user_verifier_extensions,
+ gdm_dbus_user_verifier_choice_list_interface_info ()->name);
+
+ if (choice_list_interface == NULL) {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "ChoiceList interface not supported by client");
+ return TRUE;
+ }
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ set_pending_query (conversation, invocation);
+
+ g_debug ("GdmSession: emitting choice query '%s'", prompt_message);
+ gdm_dbus_user_verifier_choice_list_emit_choice_query (choice_list_interface,
+ service_name,
+ prompt_message,
+ query);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_info_query (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *query,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (self->user_verifier_interface != NULL, FALSE);
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ set_pending_query (conversation, invocation);
+
+ gdm_dbus_user_verifier_emit_info_query (self->user_verifier_interface,
+ service_name,
+ query);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_secret_info_query (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *query,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (self->user_verifier_interface != NULL, FALSE);
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ set_pending_query (conversation, invocation);
+
+ gdm_dbus_user_verifier_emit_secret_info_query (self->user_verifier_interface,
+ service_name,
+ query);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_info (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *info,
+ GdmSession *self)
+{
+ gdm_dbus_worker_manager_complete_info (worker_manager_interface,
+ invocation);
+
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_info (self->user_verifier_interface,
+ service_name,
+ info);
+ }
+
+ return TRUE;
+}
+
+static void
+worker_on_cancel_pending_query (GdmDBusWorker *worker,
+ GdmSessionConversation *conversation)
+{
+ cancel_pending_query (conversation);
+}
+
+static gboolean
+gdm_session_handle_problem (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *problem,
+ GdmSession *self)
+{
+ gdm_dbus_worker_manager_complete_problem (worker_manager_interface,
+ invocation);
+
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_problem (self->user_verifier_interface,
+ service_name,
+ problem);
+ }
+ return TRUE;
+}
+
+static void
+on_opened (GdmDBusWorker *worker,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+ char *session_id;
+
+ worked = gdm_dbus_worker_call_open_finish (worker,
+ &session_id,
+ res,
+ &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (worked) {
+ g_clear_pointer (&conversation->session_id,
+ (GDestroyNotify) g_free);
+
+ conversation->session_id = g_strdup (session_id);
+
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_verification_complete (self->user_verifier_interface,
+ service_name);
+ g_signal_emit (self, signals[VERIFICATION_COMPLETE], 0, service_name);
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_emit_session_opened (self->greeter_interface,
+ service_name);
+ }
+
+ g_debug ("GdmSession: Emitting 'session-opened' signal");
+ g_signal_emit (self, signals[SESSION_OPENED], 0, service_name, session_id);
+ } else {
+ report_and_stop_conversation (self, service_name, error);
+
+ g_debug ("GdmSession: Emitting 'session-start-failed' signal");
+ g_signal_emit (self, signals[SESSION_OPENED_FAILED], 0, service_name, error->message);
+ }
+}
+
+static void
+worker_on_username_changed (GdmDBusWorker *worker,
+ const char *username,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ g_debug ("GdmSession: changing username from '%s' to '%s'",
+ self->selected_user != NULL ? self->selected_user : "<unset>",
+ (strlen (username)) ? username : "<unset>");
+
+ gdm_session_select_user (self, (strlen (username) > 0) ? g_strdup (username) : NULL);
+ gdm_session_defaults_changed (self);
+}
+
+static void
+worker_on_session_exited (GdmDBusWorker *worker,
+ const char *service_name,
+ int status,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ self->session_conversation = NULL;
+
+ if (WIFEXITED (status)) {
+ g_debug ("GdmSession: Emitting 'session-exited' signal with exit code '%d'",
+ WEXITSTATUS (status));
+ g_signal_emit (self, signals[SESSION_EXITED], 0, WEXITSTATUS (status));
+ } else if (WIFSIGNALED (status)) {
+ g_debug ("GdmSession: Emitting 'session-died' signal with signal number '%d'",
+ WTERMSIG (status));
+ g_signal_emit (self, signals[SESSION_DIED], 0, WTERMSIG (status));
+ }
+}
+
+static void
+on_reauthentication_started_cb (GdmDBusWorker *worker,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+
+ GError *error = NULL;
+ gboolean worked;
+ char *address;
+
+ worked = gdm_dbus_worker_call_start_reauthentication_finish (worker,
+ &address,
+ res,
+ &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+
+ if (worked) {
+ GPid pid_of_caller = conversation->reauth_pid_of_caller;
+ g_debug ("GdmSession: Emitting 'reauthentication-started' signal for caller pid '%d'", pid_of_caller);
+ g_signal_emit (self, signals[REAUTHENTICATION_STARTED], 0, pid_of_caller, address);
+ }
+
+ conversation->reauth_pid_of_caller = 0;
+}
+
+static void
+worker_on_reauthenticated (GdmDBusWorker *worker,
+ const char *service_name,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+ g_debug ("GdmSession: Emitting 'reauthenticated' signal ");
+ g_signal_emit (self, signals[REAUTHENTICATED], 0, service_name);
+}
+
+static void
+worker_on_saved_language_name_read (GdmDBusWorker *worker,
+ const char *language_name,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ if (strlen (language_name) > 0) {
+ g_free (self->saved_language);
+ self->saved_language = g_strdup (language_name);
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_emit_default_language_name_changed (self->greeter_interface,
+ language_name);
+ }
+ }
+}
+
+static void
+worker_on_saved_session_name_read (GdmDBusWorker *worker,
+ const char *session_name,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ if (! get_session_command_for_name (self, session_name, self->saved_session_type, NULL)) {
+ /* ignore sessions that don't exist */
+ g_debug ("GdmSession: not using invalid .dmrc session: %s", session_name);
+ g_free (self->saved_session);
+ self->saved_session = NULL;
+ update_session_type (self);
+ } else {
+ if (strcmp (session_name,
+ get_default_session_name (self)) != 0) {
+ g_free (self->saved_session);
+ self->saved_session = g_strdup (session_name);
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_emit_default_session_name_changed (self->greeter_interface,
+ session_name);
+ }
+ }
+ if (self->saved_session_type != NULL)
+ set_session_type (self, self->saved_session_type);
+ else
+ update_session_type (self);
+ }
+
+}
+
+static void
+worker_on_saved_session_type_read (GdmDBusWorker *worker,
+ const char *session_type,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ g_free (self->saved_session_type);
+ self->saved_session_type = g_strdup (session_type);
+}
+
+static GdmSessionConversation *
+find_conversation_by_pid (GdmSession *self,
+ GPid pid)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, self->conversations);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GdmSessionConversation *conversation;
+
+ conversation = (GdmSessionConversation *) value;
+
+ if (conversation->worker_pid == pid) {
+ return conversation;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+allow_worker_function (GDBusAuthObserver *observer,
+ GIOStream *stream,
+ GCredentials *credentials,
+ GdmSession *self)
+{
+ uid_t connecting_user;
+
+ connecting_user = g_credentials_get_unix_user (credentials, NULL);
+
+ if (connecting_user == 0) {
+ return TRUE;
+ }
+
+ if (connecting_user == self->allowed_user) {
+ return TRUE;
+ }
+
+ g_debug ("GdmSession: User not allowed");
+
+ return FALSE;
+}
+
+static void
+on_worker_connection_closed (GDBusConnection *connection,
+ gboolean remote_peer_vanished,
+ GError *error,
+ GdmSession *self)
+{
+ self->pending_worker_connections =
+ g_list_remove (self->pending_worker_connections,
+ connection);
+ g_object_unref (connection);
+}
+
+static gboolean
+register_worker (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+ GDBusConnection *connection;
+ GList *connection_node;
+ GCredentials *credentials;
+ GPid pid;
+
+ g_debug ("GdmSession: Authenticating new connection");
+
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ connection_node = g_list_find (self->pending_worker_connections, connection);
+
+ if (connection_node == NULL) {
+ g_debug ("GdmSession: Ignoring connection that we aren't tracking");
+ return FALSE;
+ }
+
+ /* connection was ref'd when it was added to list, we're taking that
+ * reference over and removing it from the list
+ */
+ self->pending_worker_connections =
+ g_list_delete_link (self->pending_worker_connections,
+ connection_node);
+
+ g_signal_handlers_disconnect_by_func (connection,
+ G_CALLBACK (on_worker_connection_closed),
+ self);
+
+ credentials = g_dbus_connection_get_peer_credentials (connection);
+ pid = g_credentials_get_unix_pid (credentials, NULL);
+
+ conversation = find_conversation_by_pid (self, (GPid) pid);
+
+ if (conversation == NULL) {
+ g_warning ("GdmSession: New worker connection is from unknown source");
+
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "Connection is not from a known conversation");
+ g_dbus_connection_close_sync (connection, NULL, NULL);
+ return TRUE;
+ }
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+
+ conversation->worker_proxy = gdm_dbus_worker_proxy_new_sync (connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ GDM_WORKER_DBUS_PATH,
+ NULL, NULL);
+ /* drop the reference we stole from the pending connections list
+ * since the proxy owns the connection now */
+ g_object_unref (connection);
+
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (conversation->worker_proxy), G_MAXINT);
+
+ conversation->worker_cancellable = g_cancellable_new ();
+
+ g_signal_connect (conversation->worker_proxy,
+ "username-changed",
+ G_CALLBACK (worker_on_username_changed), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "session-exited",
+ G_CALLBACK (worker_on_session_exited), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "reauthenticated",
+ G_CALLBACK (worker_on_reauthenticated), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "saved-language-name-read",
+ G_CALLBACK (worker_on_saved_language_name_read), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "saved-session-name-read",
+ G_CALLBACK (worker_on_saved_session_name_read), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "saved-session-type-read",
+ G_CALLBACK (worker_on_saved_session_type_read), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "cancel-pending-query",
+ G_CALLBACK (worker_on_cancel_pending_query), conversation);
+
+ conversation->worker_manager_interface = g_object_ref (worker_manager_interface);
+ g_debug ("GdmSession: worker connection is %p", connection);
+
+ g_debug ("GdmSession: Emitting conversation-started signal");
+ g_signal_emit (self, signals[CONVERSATION_STARTED], 0, conversation->service_name);
+
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_conversation_started (self->user_verifier_interface,
+ conversation->service_name);
+ }
+
+ if (conversation->starting_invocation != NULL) {
+ if (conversation->starting_username != NULL) {
+ gdm_session_setup_for_user (self, conversation->service_name, conversation->starting_username);
+
+ g_clear_pointer (&conversation->starting_username,
+ (GDestroyNotify)
+ g_free);
+ } else {
+ gdm_session_setup (self, conversation->service_name);
+ }
+ }
+
+ g_debug ("GdmSession: Conversation started");
+
+ return TRUE;
+}
+
+static void
+export_worker_manager_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusWorkerManager *worker_manager_interface;
+
+ worker_manager_interface = GDM_DBUS_WORKER_MANAGER (gdm_dbus_worker_manager_skeleton_new ());
+ g_signal_connect (worker_manager_interface,
+ "handle-hello",
+ G_CALLBACK (register_worker),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-info-query",
+ G_CALLBACK (gdm_session_handle_info_query),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-secret-info-query",
+ G_CALLBACK (gdm_session_handle_secret_info_query),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-info",
+ G_CALLBACK (gdm_session_handle_info),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-problem",
+ G_CALLBACK (gdm_session_handle_problem),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-choice-list-query",
+ G_CALLBACK (gdm_session_handle_choice_list_query),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (worker_manager_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+}
+
+static void
+unexport_worker_manager_interface (GdmSession *self,
+ GdmDBusWorkerManager *worker_manager_interface)
+{
+
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (worker_manager_interface));
+
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (register_worker),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_info_query),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_secret_info_query),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_info),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_problem),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_choice_list_query),
+ self);
+}
+
+static gboolean
+handle_connection_from_worker (GDBusServer *server,
+ GDBusConnection *connection,
+ GdmSession *self)
+{
+
+ g_debug ("GdmSession: Handling new connection from worker");
+
+ /* add to the list of pending connections. We won't be able to
+ * associate it with a specific worker conversation until we have
+ * authenticated the connection (from the Hello handler).
+ */
+ self->pending_worker_connections =
+ g_list_prepend (self->pending_worker_connections,
+ g_object_ref (connection));
+
+ g_signal_connect_object (connection,
+ "closed",
+ G_CALLBACK (on_worker_connection_closed),
+ self,
+ 0);
+
+ export_worker_manager_interface (self, connection);
+
+ return TRUE;
+}
+
+static GdmSessionConversation *
+begin_verification_conversation (GdmSession *self,
+ GDBusMethodInvocation *invocation,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation = NULL;
+ gboolean conversation_started;
+
+ conversation_started = gdm_session_start_conversation (self, service_name);
+
+ if (conversation_started) {
+ conversation = find_conversation_by_name (self, service_name);
+ }
+
+ if (conversation == NULL) {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_SPAWN_FAILED,
+ _("Could not create authentication helper process"));
+ }
+
+ return conversation;
+}
+
+static gboolean
+gdm_session_handle_client_select_choice (GdmDBusUserVerifierChoiceList *choice_list_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *answer,
+ GdmSession *self)
+{
+ g_debug ("GdmSession: user selected choice '%s'", answer);
+ gdm_dbus_user_verifier_choice_list_complete_select_choice (choice_list_interface, invocation);
+ gdm_session_answer_query (self, service_name, answer);
+ return TRUE;
+}
+
+static void
+export_user_verifier_choice_list_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusUserVerifierChoiceList *interface;
+
+ interface = GDM_DBUS_USER_VERIFIER_CHOICE_LIST (gdm_dbus_user_verifier_choice_list_skeleton_new ());
+
+ g_signal_connect (interface,
+ "handle-select-choice",
+ G_CALLBACK (gdm_session_handle_client_select_choice),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ g_hash_table_insert (self->user_verifier_extensions,
+ gdm_dbus_user_verifier_choice_list_interface_info ()->name,
+ interface);
+}
+
+static gboolean
+gdm_session_handle_client_enable_extensions (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ const char * const * extensions,
+ GDBusConnection *connection)
+{
+ GdmSession *self = g_object_get_data (G_OBJECT (connection), "gdm-session");
+ size_t i;
+
+ g_hash_table_remove_all (self->user_verifier_extensions);
+
+ for (i = 0; extensions[i] != NULL; i++) {
+ if (g_hash_table_lookup (self->user_verifier_extensions, extensions[i]) != NULL)
+ continue;
+
+ if (strcmp (extensions[i],
+ gdm_dbus_user_verifier_choice_list_interface_info ()->name) == 0)
+ export_user_verifier_choice_list_interface (self, connection);
+
+ }
+
+ gdm_dbus_user_verifier_complete_enable_extensions (user_verifier_interface, invocation);
+
+ return TRUE;
+}
+static gboolean
+gdm_session_handle_client_begin_verification (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ conversation = begin_verification_conversation (self, invocation, service_name);
+
+ if (conversation != NULL) {
+ conversation->starting_invocation = g_object_ref (invocation);
+ conversation->starting_username = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_begin_verification_for_user (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *username,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ conversation = begin_verification_conversation (self, invocation, service_name);
+
+ if (conversation != NULL) {
+ conversation->starting_invocation = g_object_ref (invocation);
+ conversation->starting_username = g_strdup (username);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_answer_query (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *answer,
+ GdmSession *self)
+{
+ gdm_dbus_user_verifier_complete_answer_query (user_verifier_interface,
+ invocation);
+ gdm_session_answer_query (self, service_name, answer);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_cancel (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ GdmSession *self)
+{
+ gdm_dbus_user_verifier_complete_cancel (user_verifier_interface,
+ invocation);
+ gdm_session_cancel (self);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_select_session (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ const char *session,
+ GdmSession *self)
+{
+ if (gdm_session_is_running (self)) {
+ const char *username;
+
+ username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing to select session %s since it's already running (for user %s)",
+ session,
+ username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already running for user %s",
+ username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_select_session (greeter_interface,
+ invocation);
+ }
+ gdm_session_select_session (self, session);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_select_user (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ const char *username,
+ GdmSession *self)
+{
+ if (gdm_session_is_running (self)) {
+ const char *session_username;
+
+ session_username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing to select user %s, since session (%p) already running (for user %s)",
+ username,
+ self,
+ session_username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already running for user %s",
+ session_username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_select_user (greeter_interface,
+ invocation);
+ }
+ g_debug ("GdmSession: client selected user '%s' on session (%p)", username, self);
+ gdm_session_select_user (self, username);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_start_session_when_ready (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ gboolean client_is_ready,
+ GdmSession *self)
+{
+ if (gdm_session_is_running (self)) {
+ const char *username;
+
+ username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing to start session (%p), since it's already running (for user %s)",
+ self,
+ username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already running for user %s",
+ username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_start_session_when_ready (greeter_interface,
+ invocation);
+ }
+ g_signal_emit (G_OBJECT (self),
+ signals [CLIENT_READY_FOR_SESSION_TO_START],
+ 0,
+ service_name,
+ client_is_ready);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_get_timed_login_details (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ GdmSession *self)
+{
+ if (gdm_session_is_running (self)) {
+ const char *username;
+
+ username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing to give timed login details, session (%p) already running (for user %s)",
+ self,
+ username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already running for user %s",
+ username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_get_timed_login_details (greeter_interface,
+ invocation,
+ self->timed_login_username != NULL,
+ self->timed_login_username != NULL? self->timed_login_username : "",
+ self->timed_login_delay);
+ if (self->timed_login_username != NULL) {
+ gdm_dbus_greeter_emit_timed_login_requested (self->greeter_interface,
+ self->timed_login_username,
+ self->timed_login_delay);
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_begin_auto_login (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ const char *username,
+ GdmSession *self)
+{
+ const char *session_username;
+
+ if (gdm_session_is_running (self)) {
+ session_username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing auto login operation, session (%p) already running for user %s (%s requested)",
+ self,
+ session_username,
+ username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already owned by user %s",
+ session_username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_begin_auto_login (greeter_interface,
+ invocation);
+ }
+
+ g_debug ("GdmSession: client requesting automatic login for user '%s' on session '%s' (%p)",
+ username,
+ gdm_session_get_session_id (self),
+ self);
+
+ gdm_session_setup_for_user (self, "gdm-autologin", username);
+
+ return TRUE;
+}
+
+static void
+export_user_verifier_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusUserVerifier *user_verifier_interface;
+ user_verifier_interface = GDM_DBUS_USER_VERIFIER (gdm_dbus_user_verifier_skeleton_new ());
+
+ g_object_set_data (G_OBJECT (connection), "gdm-session", self);
+
+ g_signal_connect (user_verifier_interface,
+ "handle-enable-extensions",
+ G_CALLBACK (gdm_session_handle_client_enable_extensions),
+ connection);
+ g_signal_connect (user_verifier_interface,
+ "handle-begin-verification",
+ G_CALLBACK (gdm_session_handle_client_begin_verification),
+ self);
+ g_signal_connect (user_verifier_interface,
+ "handle-begin-verification-for-user",
+ G_CALLBACK (gdm_session_handle_client_begin_verification_for_user),
+ self);
+ g_signal_connect (user_verifier_interface,
+ "handle-answer-query",
+ G_CALLBACK (gdm_session_handle_client_answer_query),
+ self);
+ g_signal_connect (user_verifier_interface,
+ "handle-cancel",
+ G_CALLBACK (gdm_session_handle_client_cancel),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (user_verifier_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ self->user_verifier_interface = user_verifier_interface;
+}
+
+static void
+export_greeter_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusGreeter *greeter_interface;
+
+ greeter_interface = GDM_DBUS_GREETER (gdm_dbus_greeter_skeleton_new ());
+
+ g_signal_connect (greeter_interface,
+ "handle-begin-auto-login",
+ G_CALLBACK (gdm_session_handle_client_begin_auto_login),
+ self);
+ g_signal_connect (greeter_interface,
+ "handle-select-session",
+ G_CALLBACK (gdm_session_handle_client_select_session),
+ self);
+ g_signal_connect (greeter_interface,
+ "handle-select-user",
+ G_CALLBACK (gdm_session_handle_client_select_user),
+ self);
+ g_signal_connect (greeter_interface,
+ "handle-start-session-when-ready",
+ G_CALLBACK (gdm_session_handle_client_start_session_when_ready),
+ self);
+ g_signal_connect (greeter_interface,
+ "handle-get-timed-login-details",
+ G_CALLBACK (gdm_session_handle_get_timed_login_details),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (greeter_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ self->greeter_interface = greeter_interface;
+
+}
+
+static gboolean
+gdm_session_handle_client_disconnect (GdmDBusChooser *chooser_interface,
+ GDBusMethodInvocation *invocation,
+ GdmSession *self)
+{
+ gdm_dbus_chooser_complete_disconnect (chooser_interface,
+ invocation);
+ g_signal_emit (self, signals[DISCONNECTED], 0);
+ return TRUE;
+}
+
+static void
+export_remote_greeter_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusRemoteGreeter *remote_greeter_interface;
+
+ remote_greeter_interface = GDM_DBUS_REMOTE_GREETER (gdm_dbus_remote_greeter_skeleton_new ());
+
+ g_signal_connect (remote_greeter_interface,
+ "handle-disconnect",
+ G_CALLBACK (gdm_session_handle_client_disconnect),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (remote_greeter_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ self->remote_greeter_interface = remote_greeter_interface;
+
+}
+
+static gboolean
+gdm_session_handle_client_select_hostname (GdmDBusChooser *chooser_interface,
+ GDBusMethodInvocation *invocation,
+ const char *hostname,
+ GdmSession *self)
+{
+
+ gdm_dbus_chooser_complete_select_hostname (chooser_interface,
+ invocation);
+ g_signal_emit (self, signals[HOSTNAME_SELECTED], 0, hostname);
+ return TRUE;
+}
+
+static void
+export_chooser_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusChooser *chooser_interface;
+
+ chooser_interface = GDM_DBUS_CHOOSER (gdm_dbus_chooser_skeleton_new ());
+
+ g_signal_connect (chooser_interface,
+ "handle-select-hostname",
+ G_CALLBACK (gdm_session_handle_client_select_hostname),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (chooser_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ self->chooser_interface = chooser_interface;
+}
+
+static void
+on_outside_connection_closed (GDBusConnection *connection,
+ gboolean remote_peer_vanished,
+ GError *error,
+ GdmSession *self)
+{
+ GCredentials *credentials;
+ GPid pid_of_client;
+
+ g_debug ("GdmSession: external connection closed");
+
+ self->outside_connections = g_list_remove (self->outside_connections,
+ connection);
+
+ credentials = g_dbus_connection_get_peer_credentials (connection);
+ pid_of_client = g_credentials_get_unix_pid (credentials, NULL);
+
+ g_signal_emit (G_OBJECT (self),
+ signals [CLIENT_DISCONNECTED],
+ 0,
+ credentials,
+ (guint)
+ pid_of_client);
+
+ g_object_unref (connection);
+}
+
+static gboolean
+handle_connection_from_outside (GDBusServer *server,
+ GDBusConnection *connection,
+ GdmSession *self)
+{
+ GCredentials *credentials;
+ GPid pid_of_client;
+
+ g_debug ("GdmSession: Handling new connection from outside");
+
+ self->outside_connections = g_list_prepend (self->outside_connections,
+ g_object_ref (connection));
+
+ g_signal_connect_object (connection,
+ "closed",
+ G_CALLBACK (on_outside_connection_closed),
+ self,
+ 0);
+
+ export_user_verifier_interface (self, connection);
+
+ switch (self->verification_mode) {
+ case GDM_SESSION_VERIFICATION_MODE_LOGIN:
+ export_greeter_interface (self, connection);
+ break;
+
+ case GDM_SESSION_VERIFICATION_MODE_CHOOSER:
+ export_chooser_interface (self, connection);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!self->display_is_local) {
+ export_remote_greeter_interface (self, connection);
+ }
+
+ credentials = g_dbus_connection_get_peer_credentials (connection);
+ pid_of_client = g_credentials_get_unix_pid (credentials, NULL);
+
+ g_signal_emit (G_OBJECT (self),
+ signals [CLIENT_CONNECTED],
+ 0,
+ credentials,
+ (guint)
+ pid_of_client);
+
+ return TRUE;
+}
+
+static void
+setup_worker_server (GdmSession *self)
+{
+ GDBusAuthObserver *observer;
+ GDBusServer *server;
+ GError *error = NULL;
+
+ g_debug ("GdmSession: Creating D-Bus server for worker for session");
+
+ observer = g_dbus_auth_observer_new ();
+ g_signal_connect_object (observer,
+ "authorize-authenticated-peer",
+ G_CALLBACK (allow_worker_function),
+ self,
+ 0);
+
+ server = gdm_dbus_setup_private_server (observer, &error);
+ g_object_unref (observer);
+
+ if (server == NULL) {
+ g_warning ("Cannot create worker D-Bus server for the session: %s",
+ error->message);
+ return;
+ }
+
+ g_signal_connect_object (server,
+ "new-connection",
+ G_CALLBACK (handle_connection_from_worker),
+ self,
+ 0);
+ self->worker_server = server;
+
+ g_dbus_server_start (server);
+
+ g_debug ("GdmSession: D-Bus server for workers listening on %s",
+ g_dbus_server_get_client_address (self->worker_server));
+}
+
+static gboolean
+allow_user_function (GDBusAuthObserver *observer,
+ GIOStream *stream,
+ GCredentials *credentials,
+ GdmSession *self)
+{
+ uid_t client_uid;
+ GPid pid_of_client;
+
+ client_uid = g_credentials_get_unix_user (credentials, NULL);
+ if (client_uid == self->allowed_user) {
+ return TRUE;
+ }
+
+ g_debug ("GdmSession: User not allowed");
+
+ pid_of_client = g_credentials_get_unix_pid (credentials, NULL);
+ g_signal_emit (G_OBJECT (self),
+ signals [CLIENT_REJECTED],
+ 0,
+ credentials,
+ (guint)
+ pid_of_client);
+
+
+ return FALSE;
+}
+
+static void
+setup_outside_server (GdmSession *self)
+{
+ GDBusAuthObserver *observer;
+ GDBusServer *server;
+ GError *error = NULL;
+
+ g_debug ("GdmSession: Creating D-Bus server for greeters and such for session %s (%p)",
+ gdm_session_get_session_id (self),
+ self);
+
+ observer = g_dbus_auth_observer_new ();
+ g_signal_connect_object (observer,
+ "authorize-authenticated-peer",
+ G_CALLBACK (allow_user_function),
+ self,
+ 0);
+
+ server = gdm_dbus_setup_private_server (observer, &error);
+ g_object_unref (observer);
+
+ if (server == NULL) {
+ g_warning ("Cannot create greeter D-Bus server for the session: %s",
+ error->message);
+ return;
+ }
+
+ g_signal_connect_object (server,
+ "new-connection",
+ G_CALLBACK (handle_connection_from_outside),
+ self,
+ 0);
+ self->outside_server = server;
+
+ g_dbus_server_start (server);
+
+ g_debug ("GdmSession: D-Bus server for greeters listening on %s",
+ g_dbus_server_get_client_address (self->outside_server));
+}
+
+static void
+free_conversation (GdmSessionConversation *conversation)
+{
+ close_conversation (conversation);
+
+ if (conversation->job != NULL) {
+ g_warning ("Freeing conversation '%s' with active job", conversation->service_name);
+ }
+
+ g_free (conversation->service_name);
+ g_free (conversation->starting_username);
+ g_free (conversation->session_id);
+ g_clear_object (&conversation->worker_manager_interface);
+
+ g_cancellable_cancel (conversation->worker_cancellable);
+ g_clear_object (&conversation->worker_cancellable);
+
+ if (conversation->worker_proxy != NULL) {
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_username_changed),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_session_exited),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_reauthenticated),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_saved_language_name_read),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_saved_session_name_read),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_saved_session_type_read),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_cancel_pending_query),
+ conversation);
+ g_clear_object (&conversation->worker_proxy);
+ }
+ g_clear_object (&conversation->session);
+ g_free (conversation);
+}
+
+static void
+load_lang_config_file (GdmSession *self)
+{
+ static const char *config_file = LANG_CONFIG_FILE;
+ gchar *contents = NULL;
+ gchar *p;
+ gchar *key;
+ gchar *value;
+ gsize length;
+ GError *error;
+ GString *line;
+ GRegex *re;
+
+ if (!g_file_test (config_file, G_FILE_TEST_EXISTS)) {
+ g_debug ("Cannot access '%s'", config_file);
+ return;
+ }
+
+ error = NULL;
+ if (!g_file_get_contents (config_file, &contents, &length, &error)) {
+ g_debug ("Failed to parse '%s': %s",
+ LANG_CONFIG_FILE,
+ (error && error->message) ? error->message : "(null)");
+ g_error_free (error);
+ return;
+ }
+
+ if (!g_utf8_validate (contents, length, NULL)) {
+ g_warning ("Invalid UTF-8 in '%s'", config_file);
+ g_free (contents);
+ return;
+ }
+
+ re = g_regex_new ("(?P<key>(LANG|LANGUAGE|LC_CTYPE|LC_NUMERIC|LC_TIME|LC_COLLATE|LC_MONETARY|LC_MESSAGES|LC_PAPER|LC_NAME|LC_ADDRESS|LC_TELEPHONE|LC_MEASUREMENT|LC_IDENTIFICATION|LC_ALL))=(\")?(?P<value>[^\"]*)?(\")?", 0, 0, &error);
+ if (re == NULL) {
+ g_warning ("Failed to regex: %s",
+ (error && error->message) ? error->message : "(null)");
+ g_error_free (error);
+ g_free (contents);
+ return;
+ }
+
+ line = g_string_new ("");
+ for (p = contents; p && *p; p = g_utf8_find_next_char (p, NULL)) {
+ gunichar ch;
+ GMatchInfo *match_info = NULL;
+
+ ch = g_utf8_get_char (p);
+ if ((ch != '\n') && (ch != '\0')) {
+ g_string_append_unichar (line, ch);
+ continue;
+ }
+
+ if (line->str && g_utf8_get_char (line->str) == '#') {
+ goto next_line;
+ }
+
+ if (!g_regex_match (re, line->str, 0, &match_info)) {
+ goto next_line;
+ }
+
+ if (!g_match_info_matches (match_info)) {
+ goto next_line;
+ }
+
+ key = g_match_info_fetch_named (match_info, "key");
+ value = g_match_info_fetch_named (match_info, "value");
+
+ if (key && *key && value && *value) {
+ g_setenv (key, value, TRUE);
+ }
+
+ g_free (key);
+ g_free (value);
+next_line:
+ g_match_info_free (match_info);
+ g_string_set_size (line, 0);
+ }
+
+ g_string_free (line, TRUE);
+ g_regex_unref (re);
+ g_free (contents);
+}
+
+static void
+unexport_and_free_user_verifier_extension (GDBusInterfaceSkeleton *interface)
+{
+ g_dbus_interface_skeleton_unexport (interface);
+
+ g_object_run_dispose (G_OBJECT (interface));
+ g_object_unref (interface);
+}
+
+static void
+gdm_session_init (GdmSession *self)
+{
+ self->conversations = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify)
+ free_conversation);
+ self->environment = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+ self->user_verifier_extensions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify)
+ unexport_and_free_user_verifier_extension);
+
+ load_lang_config_file (self);
+ setup_worker_server (self);
+ setup_outside_server (self);
+}
+
+static void
+worker_started (GdmSessionWorkerJob *job,
+ GdmSessionConversation *conversation)
+{
+ g_debug ("GdmSession: Worker job started");
+
+}
+
+static void
+worker_exited (GdmSessionWorkerJob *job,
+ int code,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ g_debug ("GdmSession: Worker job exited: %d", code);
+
+ g_hash_table_steal (self->conversations, conversation->service_name);
+
+ g_object_ref (conversation->job);
+ if (self->session_conversation == conversation) {
+ g_signal_emit (self, signals[SESSION_EXITED], 0, code);
+ self->session_conversation = NULL;
+ }
+
+ g_debug ("GdmSession: Emitting conversation-stopped signal");
+ g_signal_emit (self, signals[CONVERSATION_STOPPED], 0, conversation->service_name);
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_conversation_stopped (self->user_verifier_interface,
+ conversation->service_name);
+ }
+ g_object_unref (conversation->job);
+
+ if (conversation->is_stopping) {
+ g_object_unref (conversation->job);
+ conversation->job = NULL;
+ }
+
+ free_conversation (conversation);
+}
+
+static void
+worker_died (GdmSessionWorkerJob *job,
+ int signum,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ g_debug ("GdmSession: Worker job died: %d", signum);
+
+ g_hash_table_steal (self->conversations, conversation->service_name);
+
+ g_object_ref (conversation->job);
+ if (self->session_conversation == conversation) {
+ g_signal_emit (self, signals[SESSION_DIED], 0, signum);
+ self->session_conversation = NULL;
+ }
+
+ g_debug ("GdmSession: Emitting conversation-stopped signal");
+ g_signal_emit (self, signals[CONVERSATION_STOPPED], 0, conversation->service_name);
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_conversation_stopped (self->user_verifier_interface,
+ conversation->service_name);
+ }
+ g_object_unref (conversation->job);
+
+ if (conversation->is_stopping) {
+ g_object_unref (conversation->job);
+ conversation->job = NULL;
+ }
+
+ free_conversation (conversation);
+}
+
+static GdmSessionConversation *
+start_conversation (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+ char *job_name;
+
+ conversation = g_new0 (GdmSessionConversation, 1);
+ conversation->session = g_object_ref (self);
+ conversation->service_name = g_strdup (service_name);
+ conversation->worker_pid = -1;
+ conversation->job = gdm_session_worker_job_new ();
+ gdm_session_worker_job_set_server_address (conversation->job,
+ g_dbus_server_get_client_address (self->worker_server));
+ gdm_session_worker_job_set_for_reauth (conversation->job,
+ self->verification_mode == GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE);
+
+ if (self->conversation_environment != NULL) {
+ gdm_session_worker_job_set_environment (conversation->job,
+ (const char * const *)
+ self->conversation_environment);
+
+ }
+ g_signal_connect (conversation->job,
+ "started",
+ G_CALLBACK (worker_started),
+ conversation);
+ g_signal_connect (conversation->job,
+ "exited",
+ G_CALLBACK (worker_exited),
+ conversation);
+ g_signal_connect (conversation->job,
+ "died",
+ G_CALLBACK (worker_died),
+ conversation);
+
+ job_name = g_strdup_printf ("gdm-session-worker [pam/%s]", service_name);
+ if (!gdm_session_worker_job_start (conversation->job, job_name)) {
+ g_object_unref (conversation->job);
+ g_free (conversation->service_name);
+ g_free (conversation);
+ g_free (job_name);
+ return NULL;
+ }
+
+ g_free (job_name);
+
+ conversation->worker_pid = gdm_session_worker_job_get_pid (conversation->job);
+
+ return conversation;
+}
+
+static void
+close_conversation (GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ if (conversation->worker_manager_interface != NULL) {
+ unexport_worker_manager_interface (self, conversation->worker_manager_interface);
+ g_clear_object (&conversation->worker_manager_interface);
+ }
+
+ if (conversation->worker_proxy != NULL) {
+ GDBusConnection *connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (conversation->worker_proxy));
+ g_dbus_connection_close_sync (connection, NULL, NULL);
+ }
+}
+
+static void
+stop_conversation (GdmSessionConversation *conversation)
+{
+ close_conversation (conversation);
+
+ conversation->is_stopping = TRUE;
+ gdm_session_worker_job_stop (conversation->job);
+}
+
+static void
+stop_conversation_now (GdmSessionConversation *conversation)
+{
+ close_conversation (conversation);
+
+ gdm_session_worker_job_stop_now (conversation->job);
+ g_clear_object (&conversation->job);
+}
+
+void
+gdm_session_set_supported_session_types (GdmSession *self,
+ const char * const *supported_session_types)
+{
+ const char * const session_types[] = { "wayland", "x11", NULL };
+ g_strfreev (self->supported_session_types);
+
+ if (supported_session_types == NULL)
+ self->supported_session_types = g_strdupv ((GStrv) session_types);
+ else
+ self->supported_session_types = g_strdupv ((GStrv) supported_session_types);
+}
+
+gboolean
+gdm_session_start_conversation (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+ conversation = g_hash_table_lookup (self->conversations,
+ service_name);
+
+ if (conversation != NULL) {
+ if (!conversation->is_stopping) {
+ g_warning ("GdmSession: conversation %s started more than once", service_name);
+ return FALSE;
+ }
+ g_debug ("GdmSession: stopping old conversation %s", service_name);
+ gdm_session_worker_job_stop_now (conversation->job);
+ g_object_unref (conversation->job);
+ conversation->job = NULL;
+ }
+
+ g_debug ("GdmSession: starting conversation %s for session (%p)", service_name, self);
+
+ conversation = start_conversation (self, service_name);
+
+ g_hash_table_insert (self->conversations,
+ g_strdup (service_name), conversation);
+ return TRUE;
+}
+
+void
+gdm_session_stop_conversation (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ g_debug ("GdmSession: stopping conversation %s", service_name);
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation != NULL) {
+ stop_conversation (conversation);
+ }
+}
+
+static void
+on_initialization_complete_cb (GdmDBusWorker *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ GVariant *ret;
+
+ ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (ret != NULL) {
+ if (conversation->starting_invocation) {
+ g_dbus_method_invocation_return_value (conversation->starting_invocation,
+ NULL);
+ }
+
+ g_signal_emit (G_OBJECT (self),
+ signals [SETUP_COMPLETE],
+ 0,
+ service_name);
+
+ gdm_session_authenticate (self, service_name);
+ g_variant_unref (ret);
+
+ } else {
+ g_dbus_method_invocation_return_gerror (conversation->starting_invocation, error);
+ report_and_stop_conversation (self, service_name, error);
+ g_error_free (error);
+ }
+
+ g_clear_object (&conversation->starting_invocation);
+}
+
+static void
+initialize (GdmSession *self,
+ const char *service_name,
+ const char *username,
+ const char *log_file)
+{
+ GVariantBuilder details;
+ const char **extensions;
+ GdmSessionConversation *conversation;
+
+ g_assert (service_name != NULL);
+
+ g_variant_builder_init (&details, G_VARIANT_TYPE ("a{sv}"));
+
+ g_variant_builder_add_parsed (&details, "{'service', <%s>}", service_name);
+ extensions = (const char **) g_hash_table_get_keys_as_array (self->user_verifier_extensions, NULL);
+
+ g_variant_builder_add_parsed (&details, "{'extensions', <%^as>}", extensions);
+
+ if (username != NULL)
+ g_variant_builder_add_parsed (&details, "{'username', <%s>}", username);
+
+ if (log_file != NULL)
+ g_variant_builder_add_parsed (&details, "{'log-file', <%s>}", log_file);
+
+ if (self->is_program_session)
+ g_variant_builder_add_parsed (&details, "{'is-program-session', <%b>}", self->is_program_session);
+
+ if (self->display_name != NULL)
+ g_variant_builder_add_parsed (&details, "{'x11-display-name', <%s>}", self->display_name);
+
+ if (self->display_hostname != NULL)
+ g_variant_builder_add_parsed (&details, "{'hostname', <%s>}", self->display_hostname);
+
+ if (self->display_is_local)
+ g_variant_builder_add_parsed (&details, "{'display-is-local', <%b>}", self->display_is_local);
+
+ if (self->display_is_initial)
+ g_variant_builder_add_parsed (&details, "{'display-is-initial', <%b>}", self->display_is_initial);
+
+ if (self->display_device != NULL)
+ g_variant_builder_add_parsed (&details, "{'console', <%s>}", self->display_device);
+
+ if (self->display_seat_id != NULL)
+ g_variant_builder_add_parsed (&details, "{'seat-id', <%s>}", self->display_seat_id);
+
+ if (self->display_x11_authority_file != NULL)
+ g_variant_builder_add_parsed (&details, "{'x11-authority-file', <%s>}", self->display_x11_authority_file);
+
+ g_debug ("GdmSession: Beginning initialization");
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ gdm_dbus_worker_call_initialize (conversation->worker_proxy,
+ g_variant_builder_end (&details),
+
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_initialization_complete_cb,
+ conversation);
+ }
+
+ g_free (extensions);
+}
+
+void
+gdm_session_setup (GdmSession *self,
+ const char *service_name)
+{
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ update_session_type (self);
+
+ initialize (self, service_name, NULL, NULL);
+ gdm_session_defaults_changed (self);
+}
+
+
+void
+gdm_session_setup_for_user (GdmSession *self,
+ const char *service_name,
+ const char *username)
+{
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+ g_return_if_fail (username != NULL);
+
+ update_session_type (self);
+
+ g_debug ("GdmSession: Set up service %s for username %s on session (%p)",
+ service_name,
+ username,
+ self);
+ gdm_session_select_user (self, username);
+
+ self->is_program_session = FALSE;
+ initialize (self, service_name, self->selected_user, NULL);
+ gdm_session_defaults_changed (self);
+}
+
+void
+gdm_session_setup_for_program (GdmSession *self,
+ const char *service_name,
+ const char *username,
+ const char *log_file)
+{
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ self->is_program_session = TRUE;
+ initialize (self, service_name, username, log_file);
+}
+
+void
+gdm_session_authenticate (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ gdm_dbus_worker_call_authenticate (conversation->worker_proxy,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_authenticate_cb,
+ conversation);
+ }
+}
+
+void
+gdm_session_authorize (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ gdm_dbus_worker_call_authorize (conversation->worker_proxy,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_authorize_cb,
+ conversation);
+ }
+}
+
+void
+gdm_session_accredit (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ gdm_dbus_worker_call_establish_credentials (conversation->worker_proxy,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_establish_credentials_cb,
+ conversation);
+ }
+
+}
+
+static void
+send_environment_variable (const char *key,
+ const char *value,
+ GdmSessionConversation *conversation)
+{
+ gdm_dbus_worker_call_set_environment_variable (conversation->worker_proxy,
+ key, value,
+ conversation->worker_cancellable,
+ NULL, NULL);
+}
+
+static void
+send_environment (GdmSession *self,
+ GdmSessionConversation *conversation)
+{
+
+ g_hash_table_foreach (self->environment,
+ (GHFunc) send_environment_variable,
+ conversation);
+}
+
+void
+gdm_session_send_environment (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ send_environment (self, conversation);
+ }
+}
+
+static const char *
+get_session_name (GdmSession *self)
+{
+ /* FIXME: test the session names before we use them? */
+
+ if (self->selected_session != NULL) {
+ return self->selected_session;
+ }
+
+ return get_default_session_name (self);
+}
+
+static char *
+get_session_command (GdmSession *self)
+{
+ gboolean res;
+ char *command;
+ const char *session_name;
+
+ session_name = get_session_name (self);
+
+ command = NULL;
+ res = get_session_command_for_name (self, session_name, NULL, &command);
+ if (! res) {
+ g_critical ("Cannot find a command for specified session: %s", session_name);
+ exit (EXIT_FAILURE);
+ }
+
+ return command;
+}
+
+static gchar *
+get_session_desktop_names (GdmSession *self)
+{
+ gchar *filename;
+ GKeyFile *keyfile;
+ gchar *desktop_names = NULL;
+
+ if (self->selected_program != NULL) {
+ return g_strdup ("GNOME-Greeter:GNOME");
+ }
+
+ filename = g_strdup_printf ("%s.desktop", get_session_name (self));
+ g_debug ("GdmSession: getting desktop names for file '%s'", filename);
+ keyfile = load_key_file_for_file (self, filename, NULL, NULL);
+ if (keyfile != NULL) {
+ gchar **names;
+
+ names = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+ "DesktopNames", NULL, NULL);
+ if (names != NULL) {
+ desktop_names = g_strjoinv (":", names);
+
+ g_strfreev (names);
+ }
+ }
+
+ g_key_file_free (keyfile);
+ g_free (filename);
+ return desktop_names;
+}
+
+void
+gdm_session_set_environment_variable (GdmSession *self,
+ const char *key,
+ const char *value)
+{
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (value != NULL);
+
+ g_hash_table_replace (self->environment,
+ g_strdup (key),
+ g_strdup (value));
+}
+
+static void
+set_up_session_language (GdmSession *self)
+{
+ char **environment;
+ int i;
+ const char *value;
+
+ environment = g_listenv ();
+ for (i = 0; environment[i] != NULL; i++) {
+ if (strcmp (environment[i], "LANG") != 0 &&
+ strcmp (environment[i], "LANGUAGE") != 0 &&
+ !g_str_has_prefix (environment[i], "LC_")) {
+ continue;
+ }
+
+ value = g_getenv (environment[i]);
+
+ gdm_session_set_environment_variable (self,
+ environment[i],
+ value);
+ }
+ g_strfreev (environment);
+}
+
+static void
+set_up_session_environment (GdmSession *self)
+{
+ GdmSessionDisplayMode display_mode;
+ gchar *desktop_names;
+ char *locale;
+
+ if (self->selected_program == NULL) {
+ gdm_session_set_environment_variable (self,
+ "GDMSESSION",
+ get_session_name (self));
+ gdm_session_set_environment_variable (self,
+ "DESKTOP_SESSION",
+ get_session_name (self));
+ gdm_session_set_environment_variable (self,
+ "XDG_SESSION_DESKTOP",
+ get_session_name (self));
+ }
+
+ desktop_names = get_session_desktop_names (self);
+ if (desktop_names != NULL) {
+ gdm_session_set_environment_variable (self, "XDG_CURRENT_DESKTOP", desktop_names);
+ }
+
+ set_up_session_language (self);
+
+ locale = g_strdup (get_default_language_name (self));
+
+ if (locale != NULL && locale[0] != '\0') {
+ gdm_session_set_environment_variable (self,
+ "LANG",
+ locale);
+ gdm_session_set_environment_variable (self,
+ "GDM_LANG",
+ locale);
+ }
+
+ g_free (locale);
+
+ display_mode = gdm_session_get_display_mode (self);
+ if (display_mode == GDM_SESSION_DISPLAY_MODE_REUSE_VT) {
+ gdm_session_set_environment_variable (self,
+ "DISPLAY",
+ self->display_name);
+
+ if (self->user_x11_authority_file != NULL) {
+ gdm_session_set_environment_variable (self,
+ "XAUTHORITY",
+ self->user_x11_authority_file);
+ }
+ }
+
+ if (g_getenv ("WINDOWPATH") != NULL) {
+ gdm_session_set_environment_variable (self,
+ "WINDOWPATH",
+ g_getenv ("WINDOWPATH"));
+ }
+
+ g_free (desktop_names);
+}
+
+static void
+send_display_mode (GdmSession *self,
+ GdmSessionConversation *conversation)
+{
+ GdmSessionDisplayMode mode;
+
+ mode = gdm_session_get_display_mode (self);
+ gdm_dbus_worker_call_set_session_display_mode (conversation->worker_proxy,
+ gdm_session_display_mode_to_string (mode),
+ conversation->worker_cancellable,
+ NULL, NULL);
+}
+
+static void
+send_session_type (GdmSession *self,
+ GdmSessionConversation *conversation)
+{
+ const char *session_type = "x11";
+
+ if (self->session_type != NULL) {
+ session_type = self->session_type;
+ }
+
+ gdm_dbus_worker_call_set_environment_variable (conversation->worker_proxy,
+ "XDG_SESSION_TYPE",
+ session_type,
+ conversation->worker_cancellable,
+ NULL, NULL);
+}
+
+void
+gdm_session_open_session (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation != NULL) {
+ send_display_mode (self, conversation);
+ send_session_type (self, conversation);
+
+ gdm_dbus_worker_call_open (conversation->worker_proxy,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_opened, conversation);
+ }
+}
+
+static void
+stop_all_other_conversations (GdmSession *self,
+ GdmSessionConversation *conversation_to_keep,
+ gboolean now)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ if (self->conversations == NULL) {
+ return;
+ }
+
+ if (conversation_to_keep == NULL) {
+ g_debug ("GdmSession: Stopping all conversations");
+ } else {
+ g_debug ("GdmSession: Stopping all conversations except for %s",
+ conversation_to_keep->service_name);
+ }
+
+ g_hash_table_iter_init (&iter, self->conversations);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GdmSessionConversation *conversation;
+
+ conversation = (GdmSessionConversation *) value;
+
+ if (conversation == conversation_to_keep) {
+ if (now) {
+ g_hash_table_iter_steal (&iter);
+ g_free (key);
+ }
+ } else {
+ if (now) {
+ stop_conversation_now (conversation);
+ } else {
+ stop_conversation (conversation);
+ }
+ }
+ }
+
+ if (now) {
+ g_hash_table_remove_all (self->conversations);
+
+ if (conversation_to_keep != NULL) {
+ g_hash_table_insert (self->conversations,
+ g_strdup (conversation_to_keep->service_name),
+ conversation_to_keep);
+ }
+
+ if (self->session_conversation != conversation_to_keep) {
+ self->session_conversation = NULL;
+ }
+ }
+
+}
+
+static void
+on_start_program_cb (GdmDBusWorker *worker,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+ GPid pid;
+
+ worked = gdm_dbus_worker_call_start_program_finish (worker,
+ &pid,
+ res,
+ &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (worked) {
+ self->session_pid = pid;
+ self->session_conversation = conversation;
+
+ g_debug ("GdmSession: Emitting 'session-started' signal with pid '%d'", pid);
+ g_signal_emit (self, signals[SESSION_STARTED], 0, service_name, pid);
+ } else {
+ gdm_session_stop_conversation (self, service_name);
+
+ g_debug ("GdmSession: Emitting 'session-start-failed' signal");
+ g_signal_emit (self, signals[SESSION_START_FAILED], 0, service_name, error->message);
+ }
+}
+
+void
+gdm_session_start_session (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+ GdmSessionDisplayMode display_mode;
+ gboolean is_x11 = TRUE;
+ gboolean run_launcher = FALSE;
+ gboolean allow_remote_connections = FALSE;
+ char *command;
+ char *program;
+ gboolean register_session;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+ g_return_if_fail (self->session_conversation == NULL);
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation == NULL) {
+ g_warning ("GdmSession: Tried to start session of "
+ "nonexistent conversation %s", service_name);
+ return;
+ }
+
+ stop_all_other_conversations (self, conversation, FALSE);
+
+ display_mode = gdm_session_get_display_mode (self);
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ is_x11 = g_strcmp0 (self->session_type, "wayland") != 0;
+#endif
+
+ if (display_mode == GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED ||
+ display_mode == GDM_SESSION_DISPLAY_MODE_NEW_VT) {
+ run_launcher = TRUE;
+ }
+
+ register_session = !gdm_session_session_registers (self);
+
+ if (self->selected_program == NULL) {
+ gboolean run_xsession_script;
+
+ command = get_session_command (self);
+
+ run_xsession_script = !gdm_session_bypasses_xsession (self);
+
+ if (self->display_is_local) {
+ gboolean disallow_tcp = TRUE;
+ gdm_settings_direct_get_boolean (GDM_KEY_DISALLOW_TCP, &disallow_tcp);
+ allow_remote_connections = !disallow_tcp;
+ } else {
+ allow_remote_connections = TRUE;
+ }
+
+ if (run_launcher) {
+ if (is_x11) {
+ program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s%s %s\"%s\"",
+ register_session ? "--register-session " : "",
+ run_xsession_script? "--run-script " : "",
+ allow_remote_connections? "--allow-remote-connections " : "",
+ command);
+ } else {
+ program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session %s\"%s\"",
+ register_session ? "--register-session " : "",
+ command);
+ }
+ } else if (run_xsession_script) {
+ program = g_strdup_printf (GDMCONFDIR "/Xsession \"%s\"", command);
+ } else {
+ program = g_strdup (command);
+ }
+
+ g_free (command);
+ } else {
+ /* FIXME:
+ * Always use a separate DBus bus for each greeter session.
+ * Firstly, this means that if we run multiple greeter session
+ * (which we really should not do, but have to currently), then
+ * each one will get its own DBus session bus.
+ * But, we also explicitly do this for seat0, because that way
+ * it cannot make use of systemd to run the GNOME session. This
+ * prevents the session lookup logic from getting confused.
+ * This has a similar effect as passing --builtin to gnome-session.
+ *
+ * We really should not be doing this. But the fix is to use
+ * separate dynamically created users and that requires some
+ * major refactorings.
+ */
+ if (run_launcher) {
+ if (is_x11) {
+ program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s\"dbus-run-session -- %s\"",
+ register_session ? "--register-session " : "",
+ self->selected_program);
+ } else {
+ program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session %s\"dbus-run-session -- %s\"",
+ register_session ? "--register-session " : "",
+ self->selected_program);
+ }
+ } else {
+ program = g_strdup_printf ("dbus-run-session -- %s",
+ self->selected_program);
+ }
+ }
+
+ set_up_session_environment (self);
+ send_environment (self, conversation);
+
+ gdm_dbus_worker_call_start_program (conversation->worker_proxy,
+ program,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_start_program_cb,
+ conversation);
+ g_free (program);
+}
+
+static void
+stop_all_conversations (GdmSession *self)
+{
+ stop_all_other_conversations (self, NULL, TRUE);
+}
+
+static void
+do_reset (GdmSession *self)
+{
+ stop_all_conversations (self);
+
+ g_list_free_full (self->pending_worker_connections, g_object_unref);
+ self->pending_worker_connections = NULL;
+
+ g_free (self->selected_user);
+ self->selected_user = NULL;
+
+ g_free (self->selected_session);
+ self->selected_session = NULL;
+
+ g_free (self->saved_session);
+ self->saved_session = NULL;
+
+ g_free (self->saved_language);
+ self->saved_language = NULL;
+
+ g_free (self->user_x11_authority_file);
+ self->user_x11_authority_file = NULL;
+
+ g_hash_table_remove_all (self->environment);
+
+ self->session_pid = -1;
+ self->session_conversation = NULL;
+}
+
+void
+gdm_session_close (GdmSession *self)
+{
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ g_debug ("GdmSession: Closing session");
+ do_reset (self);
+
+ g_list_free_full (self->outside_connections, g_object_unref);
+ self->outside_connections = NULL;
+}
+
+void
+gdm_session_answer_query (GdmSession *self,
+ const char *service_name,
+ const char *text)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation != NULL) {
+ answer_pending_query (conversation, text);
+ }
+}
+
+void
+gdm_session_cancel (GdmSession *self)
+{
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ g_signal_emit (G_OBJECT (self), signals [CANCELLED], 0);
+}
+
+void
+gdm_session_reset (GdmSession *self)
+{
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_reset (self->user_verifier_interface);
+ }
+
+ do_reset (self);
+}
+
+void
+gdm_session_set_timed_login_details (GdmSession *self,
+ const char *username,
+ int delay)
+{
+ g_debug ("GdmSession: timed login details %s %d", username, delay);
+ self->timed_login_username = g_strdup (username);
+ self->timed_login_delay = delay;
+}
+
+gboolean
+gdm_session_is_running (GdmSession *self)
+{
+ return self->session_pid > 0;
+}
+
+gboolean
+gdm_session_client_is_connected (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+ return self->outside_connections != NULL;
+}
+
+uid_t
+gdm_session_get_allowed_user (GdmSession *self)
+{
+ return self->allowed_user;
+}
+
+void
+gdm_session_start_reauthentication (GdmSession *session,
+ GPid pid_of_caller,
+ uid_t uid_of_caller)
+{
+ GdmSessionConversation *conversation = session->session_conversation;
+
+ g_return_if_fail (conversation != NULL);
+
+ g_debug ("GdmSession: starting reauthentication for session %s for client with pid %d",
+ conversation->session_id,
+ (int) uid_of_caller);
+
+ conversation->reauth_pid_of_caller = pid_of_caller;
+
+ gdm_dbus_worker_call_start_reauthentication (conversation->worker_proxy,
+ (int) pid_of_caller,
+ (int) uid_of_caller,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_reauthentication_started_cb,
+ conversation);
+}
+
+const char *
+gdm_session_get_server_address (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ return g_dbus_server_get_client_address (self->outside_server);
+}
+
+const char *
+gdm_session_get_username (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ return self->selected_user;
+}
+
+const char *
+gdm_session_get_display_device (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ return self->display_device;
+}
+
+const char *
+gdm_session_get_display_seat_id (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ return g_strdup (self->display_seat_id);
+}
+
+const char *
+gdm_session_get_session_id (GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ conversation = self->session_conversation;
+
+ if (conversation == NULL) {
+ return NULL;
+ }
+
+ return conversation->session_id;
+}
+
+const char *
+gdm_session_get_conversation_session_id (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation == NULL) {
+ return NULL;
+ }
+
+ return conversation->session_id;
+}
+
+static char *
+get_session_filename (GdmSession *self)
+{
+ return g_strdup_printf ("%s.desktop", get_session_name (self));
+}
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+static gboolean
+gdm_session_is_wayland_session (GdmSession *self)
+{
+ GKeyFile *key_file;
+ gboolean is_wayland_session = FALSE;
+ char *filename;
+ g_autofree char *full_path = NULL;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+ filename = get_session_filename (self);
+
+ key_file = load_key_file_for_file (self, filename, NULL, &full_path);
+
+ if (key_file == NULL) {
+ goto out;
+ }
+
+ if (full_path != NULL && strstr (full_path, "/wayland-sessions/") != NULL) {
+ is_wayland_session = TRUE;
+ }
+ g_debug ("GdmSession: checking if file '%s' is wayland session: %s", filename, is_wayland_session? "yes" : "no");
+
+out:
+ g_clear_pointer (&key_file, g_key_file_free);
+ g_free (filename);
+ return is_wayland_session;
+}
+#endif
+
+static void
+update_session_type (GdmSession *self)
+{
+#ifdef ENABLE_WAYLAND_SUPPORT
+ gboolean is_wayland_session = FALSE;
+
+ if (supports_session_type (self, "wayland"))
+ is_wayland_session = gdm_session_is_wayland_session (self);
+
+ if (is_wayland_session) {
+ set_session_type (self, "wayland");
+ } else {
+ set_session_type (self, NULL);
+ }
+#endif
+}
+
+gboolean
+gdm_session_session_registers (GdmSession *self)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GKeyFile) key_file = NULL;
+ gboolean session_registers = FALSE;
+ g_autofree char *filename = NULL;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+ filename = get_session_filename (self);
+
+ key_file = load_key_file_for_file (self, filename, NULL, NULL);
+
+ session_registers = g_key_file_get_boolean (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ "X-GDM-SessionRegisters",
+ &error);
+ if (!session_registers &&
+ error != NULL &&
+ !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+ g_warning ("GdmSession: Couldn't read session file '%s'", filename);
+ return FALSE;
+ }
+
+ g_debug ("GdmSession: '%s' %s self", filename,
+ session_registers ? "registers" : "does not register");
+
+ return session_registers;
+}
+
+gboolean
+gdm_session_bypasses_xsession (GdmSession *self)
+{
+ GError *error;
+ GKeyFile *key_file;
+ gboolean res;
+ gboolean bypasses_xsession = FALSE;
+ char *filename = NULL;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ if (gdm_session_is_wayland_session (self)) {
+ bypasses_xsession = TRUE;
+ goto out;
+ }
+#endif
+
+ filename = get_session_filename (self);
+
+ key_file = load_key_file_for_file (self, filename, "x11", NULL);
+
+ error = NULL;
+ res = g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-BypassXsession", NULL);
+ if (!res) {
+ goto out;
+ } else {
+ bypasses_xsession = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-BypassXsession", &error);
+ if (error) {
+ bypasses_xsession = FALSE;
+ g_error_free (error);
+ goto out;
+ }
+ }
+
+out:
+ if (bypasses_xsession) {
+ g_debug ("GdmSession: Session %s bypasses Xsession wrapper script", filename);
+ }
+ g_free (filename);
+ return bypasses_xsession;
+}
+
+GdmSessionDisplayMode
+gdm_session_get_display_mode (GdmSession *self)
+{
+ g_debug ("GdmSession: type %s, program? %s, seat %s",
+ self->session_type,
+ self->is_program_session? "yes" : "no",
+ self->display_seat_id);
+
+ /* Non-seat0 sessions share their X server with their login screen
+ * for now.
+ */
+ if (g_strcmp0 (self->display_seat_id, "seat0") != 0) {
+ return GDM_SESSION_DISPLAY_MODE_REUSE_VT;
+ }
+
+#ifdef ENABLE_USER_DISPLAY_SERVER
+ /* All other cases (wayland login screen, X login screen,
+ * wayland user session, X user session) use the NEW_VT
+ * display mode. That display mode means that GDM allocates
+ * a new VT and jumps to it before starting the session. The
+ * session is expected to use logind to gain access to the
+ * display and input devices.
+ *
+ * GDM also has a LOGIND_MANAGED display mode which we can't
+ * use yet. The difference between it and NEW_VT, is with it,
+ * GDM doesn't do any VT handling at all, expecting the session
+ * and logind to do everything. The problem is, for wayland
+ * sessions it will cause flicker until * this bug is fixed:
+ *
+ * https://bugzilla.gnome.org/show_bug.cgi?id=745141
+ *
+ * Likewise, for X sessions it's problematic because
+ * 1) X doesn't call TakeControl before switching VTs
+ * 2) X doesn't support getting started "in the background"
+ * right now. It will die with an error if logind devices
+ * are paused when handed out.
+ */
+ return GDM_SESSION_DISPLAY_MODE_NEW_VT;
+#else
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ /* Wayland sessions are for now assumed to run in a
+ * mutter-launch-like environment, so we allocate
+ * a new VT for them. */
+ if (g_strcmp0 (self->session_type, "wayland") == 0) {
+ return GDM_SESSION_DISPLAY_MODE_NEW_VT;
+ }
+#endif
+ return GDM_SESSION_DISPLAY_MODE_REUSE_VT;
+#endif
+}
+
+void
+gdm_session_select_program (GdmSession *self,
+ const char *text)
+{
+
+ g_free (self->selected_program);
+
+ self->selected_program = g_strdup (text);
+}
+
+void
+gdm_session_select_session (GdmSession *self,
+ const char *text)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_debug ("GdmSession: selecting session '%s'", text);
+
+ g_free (self->selected_session);
+ self->selected_session = g_strdup (text);
+
+ update_session_type (self);
+
+ g_hash_table_iter_init (&iter, self->conversations);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GdmSessionConversation *conversation;
+
+ conversation = (GdmSessionConversation *) value;
+
+ gdm_dbus_worker_call_set_session_name (conversation->worker_proxy,
+ get_session_name (self),
+ conversation->worker_cancellable,
+ NULL, NULL);
+ }
+}
+
+static void
+set_display_name (GdmSession *self,
+ const char *name)
+{
+ g_free (self->display_name);
+ self->display_name = g_strdup (name);
+}
+
+static void
+set_display_hostname (GdmSession *self,
+ const char *name)
+{
+ g_free (self->display_hostname);
+ self->display_hostname = g_strdup (name);
+}
+
+static void
+set_display_device (GdmSession *self,
+ const char *name)
+{
+ g_debug ("GdmSession: Setting display device: %s", name);
+ g_free (self->display_device);
+ self->display_device = g_strdup (name);
+}
+
+static void
+set_display_seat_id (GdmSession *self,
+ const char *name)
+{
+ g_free (self->display_seat_id);
+ self->display_seat_id = g_strdup (name);
+}
+
+static void
+set_user_x11_authority_file (GdmSession *self,
+ const char *name)
+{
+ g_free (self->user_x11_authority_file);
+ self->user_x11_authority_file = g_strdup (name);
+}
+
+static void
+set_display_x11_authority_file (GdmSession *self,
+ const char *name)
+{
+ g_free (self->display_x11_authority_file);
+ self->display_x11_authority_file = g_strdup (name);
+}
+
+static void
+set_display_is_local (GdmSession *self,
+ gboolean is_local)
+{
+ self->display_is_local = is_local;
+}
+
+static void
+set_display_is_initial (GdmSession *self,
+ gboolean is_initial)
+{
+ self->display_is_initial = is_initial;
+}
+
+static void
+set_verification_mode (GdmSession *self,
+ GdmSessionVerificationMode verification_mode)
+{
+ self->verification_mode = verification_mode;
+}
+
+static void
+set_allowed_user (GdmSession *self,
+ uid_t allowed_user)
+{
+ self->allowed_user = allowed_user;
+}
+
+static void
+set_conversation_environment (GdmSession *self,
+ char **environment)
+{
+ g_strfreev (self->conversation_environment);
+ self->conversation_environment = g_strdupv (environment);
+}
+
+static void
+set_session_type (GdmSession *self,
+ const char *session_type)
+{
+
+ if (g_strcmp0 (self->session_type, session_type) != 0) {
+ g_debug ("GdmSession: setting session to type '%s'", session_type? session_type : "");
+ g_free (self->session_type);
+ self->session_type = g_strdup (session_type);
+ }
+}
+
+static void
+gdm_session_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSession *self;
+
+ self = GDM_SESSION (object);
+
+ switch (prop_id) {
+ case PROP_SESSION_TYPE:
+ set_session_type (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_NAME:
+ set_display_name (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_HOSTNAME:
+ set_display_hostname (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_DEVICE:
+ set_display_device (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_SEAT_ID:
+ set_display_seat_id (self, g_value_get_string (value));
+ break;
+ case PROP_USER_X11_AUTHORITY_FILE:
+ set_user_x11_authority_file (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_X11_AUTHORITY_FILE:
+ set_display_x11_authority_file (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_IS_LOCAL:
+ set_display_is_local (self, g_value_get_boolean (value));
+ break;
+ case PROP_DISPLAY_IS_INITIAL:
+ set_display_is_initial (self, g_value_get_boolean (value));
+ break;
+ case PROP_VERIFICATION_MODE:
+ set_verification_mode (self, g_value_get_enum (value));
+ break;
+ case PROP_ALLOWED_USER:
+ set_allowed_user (self, g_value_get_uint (value));
+ break;
+ case PROP_CONVERSATION_ENVIRONMENT:
+ set_conversation_environment (self, g_value_get_pointer (value));
+ break;
+ case PROP_SUPPORTED_SESSION_TYPES:
+ gdm_session_set_supported_session_types (self, g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_session_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSession *self;
+
+ self = GDM_SESSION (object);
+
+ switch (prop_id) {
+ case PROP_SESSION_TYPE:
+ g_value_set_string (value, self->session_type);
+ break;
+ case PROP_DISPLAY_NAME:
+ g_value_set_string (value, self->display_name);
+ break;
+ case PROP_DISPLAY_HOSTNAME:
+ g_value_set_string (value, self->display_hostname);
+ break;
+ case PROP_DISPLAY_DEVICE:
+ g_value_set_string (value, self->display_device);
+ break;
+ case PROP_DISPLAY_SEAT_ID:
+ g_value_set_string (value, self->display_seat_id);
+ break;
+ case PROP_USER_X11_AUTHORITY_FILE:
+ g_value_set_string (value, self->user_x11_authority_file);
+ break;
+ case PROP_DISPLAY_X11_AUTHORITY_FILE:
+ g_value_set_string (value, self->display_x11_authority_file);
+ break;
+ case PROP_DISPLAY_IS_LOCAL:
+ g_value_set_boolean (value, self->display_is_local);
+ break;
+ case PROP_DISPLAY_IS_INITIAL:
+ g_value_set_boolean (value, self->display_is_initial);
+ break;
+ case PROP_VERIFICATION_MODE:
+ g_value_set_enum (value, self->verification_mode);
+ break;
+ case PROP_ALLOWED_USER:
+ g_value_set_uint (value, self->allowed_user);
+ break;
+ case PROP_CONVERSATION_ENVIRONMENT:
+ g_value_set_pointer (value, self->environment);
+ break;
+ case PROP_SUPPORTED_SESSION_TYPES:
+ g_value_set_boxed (value, self->supported_session_types);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_session_dispose (GObject *object)
+{
+ GdmSession *self;
+
+ self = GDM_SESSION (object);
+
+ g_debug ("GdmSession: Disposing session");
+
+ gdm_session_close (self);
+
+ g_clear_pointer (&self->supported_session_types,
+ g_strfreev);
+ g_clear_pointer (&self->conversations,
+ g_hash_table_unref);
+
+ g_clear_object (&self->user_verifier_interface);
+ g_clear_pointer (&self->user_verifier_extensions,
+ g_hash_table_unref);
+ g_clear_object (&self->greeter_interface);
+ g_clear_object (&self->remote_greeter_interface);
+ g_clear_object (&self->chooser_interface);
+
+ g_free (self->display_name);
+ self->display_name = NULL;
+
+ g_free (self->display_hostname);
+ self->display_hostname = NULL;
+
+ g_free (self->display_device);
+ self->display_device = NULL;
+
+ g_free (self->display_seat_id);
+ self->display_seat_id = NULL;
+
+ g_free (self->display_x11_authority_file);
+ self->display_x11_authority_file = NULL;
+
+ g_strfreev (self->conversation_environment);
+ self->conversation_environment = NULL;
+
+ if (self->worker_server != NULL) {
+ g_dbus_server_stop (self->worker_server);
+ g_clear_object (&self->worker_server);
+ }
+
+ if (self->outside_server != NULL) {
+ g_dbus_server_stop (self->outside_server);
+ g_clear_object (&self->outside_server);
+ }
+
+ if (self->environment != NULL) {
+ g_hash_table_destroy (self->environment);
+ self->environment = NULL;
+ }
+
+ G_OBJECT_CLASS (gdm_session_parent_class)->dispose (object);
+}
+
+static void
+gdm_session_finalize (GObject *object)
+{
+ GdmSession *self;
+ GObjectClass *parent_class;
+
+ self = GDM_SESSION (object);
+
+ g_free (self->selected_user);
+ g_free (self->selected_session);
+ g_free (self->saved_session);
+ g_free (self->saved_language);
+
+ g_free (self->fallback_session_name);
+
+ parent_class = G_OBJECT_CLASS (gdm_session_parent_class);
+
+ if (parent_class->finalize != NULL)
+ parent_class->finalize (object);
+}
+
+static GObject *
+gdm_session_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmSession *self;
+
+ self = GDM_SESSION (G_OBJECT_CLASS (gdm_session_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+ return G_OBJECT (self);
+}
+
+static void
+gdm_session_class_init (GdmSessionClass *session_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (session_class);
+
+ object_class->get_property = gdm_session_get_property;
+ object_class->set_property = gdm_session_set_property;
+ object_class->constructor = gdm_session_constructor;
+ object_class->dispose = gdm_session_dispose;
+ object_class->finalize = gdm_session_finalize;
+
+ signals [CONVERSATION_STARTED] =
+ g_signal_new ("conversation-started",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [CONVERSATION_STOPPED] =
+ g_signal_new ("conversation-stopped",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [SETUP_COMPLETE] =
+ g_signal_new ("setup-complete",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ signals [AUTHENTICATION_FAILED] =
+ g_signal_new ("authentication-failed",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_INT);
+ signals [VERIFICATION_COMPLETE] =
+ g_signal_new ("verification-complete",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+ signals [SESSION_OPENED] =
+ g_signal_new ("session-opened",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+ signals [SESSION_OPENED_FAILED] =
+ g_signal_new ("session-opened-failed",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING, G_TYPE_STRING);
+ signals [SESSION_STARTED] =
+ g_signal_new ("session-started",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_INT);
+ signals [SESSION_START_FAILED] =
+ g_signal_new ("session-start-failed",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING, G_TYPE_STRING);
+ signals [SESSION_EXITED] =
+ g_signal_new ("session-exited",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+ signals [SESSION_DIED] =
+ g_signal_new ("session-died",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ signals [REAUTHENTICATION_STARTED] =
+ g_signal_new ("reauthentication-started",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_INT,
+ G_TYPE_STRING);
+ signals [REAUTHENTICATED] =
+ g_signal_new ("reauthenticated",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+ signals [CANCELLED] =
+ g_signal_new ("cancelled",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals [CLIENT_REJECTED] =
+ g_signal_new ("client-rejected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_CREDENTIALS,
+ G_TYPE_UINT);
+
+ signals [CLIENT_CONNECTED] =
+ g_signal_new ("client-connected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_CREDENTIALS,
+ G_TYPE_UINT);
+
+ signals [CLIENT_DISCONNECTED] =
+ g_signal_new ("client-disconnected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_CREDENTIALS,
+ G_TYPE_UINT);
+ signals [CLIENT_READY_FOR_SESSION_TO_START] =
+ g_signal_new ("client-ready-for-session-to-start",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN);
+
+ signals [HOSTNAME_SELECTED] =
+ g_signal_new ("hostname-selected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+ signals [DISCONNECTED] =
+ g_signal_new ("disconnected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_object_class_install_property (object_class,
+ PROP_VERIFICATION_MODE,
+ g_param_spec_enum ("verification-mode",
+ "verification mode",
+ "verification mode",
+ GDM_TYPE_SESSION_VERIFICATION_MODE,
+ GDM_SESSION_VERIFICATION_MODE_LOGIN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_ALLOWED_USER,
+ g_param_spec_uint ("allowed-user",
+ "allowed user",
+ "allowed user ",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_CONVERSATION_ENVIRONMENT,
+ g_param_spec_pointer ("conversation-environment",
+ "conversation environment",
+ "conversation environment",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_SESSION_TYPE,
+ g_param_spec_string ("session-type",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_NAME,
+ g_param_spec_string ("display-name",
+ "display name",
+ "display name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_HOSTNAME,
+ g_param_spec_string ("display-hostname",
+ "display hostname",
+ "display hostname",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_IS_LOCAL,
+ g_param_spec_boolean ("display-is-local",
+ "display is local",
+ "display is local",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_IS_INITIAL,
+ g_param_spec_boolean ("display-is-initial",
+ "display is initial",
+ "display is initial",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_X11_AUTHORITY_FILE,
+ g_param_spec_string ("display-x11-authority-file",
+ "display x11 authority file",
+ "display x11 authority file",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ /* not construct only */
+ g_object_class_install_property (object_class,
+ PROP_USER_X11_AUTHORITY_FILE,
+ g_param_spec_string ("user-x11-authority-file",
+ "",
+ "",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_DEVICE,
+ g_param_spec_string ("display-device",
+ "display device",
+ "display device",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_SEAT_ID,
+ g_param_spec_string ("display-seat-id",
+ "display seat id",
+ "display seat id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_SUPPORTED_SESSION_TYPES,
+ g_param_spec_boxed ("supported-session-types",
+ "supported session types",
+ "supported session types",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ /* Ensure we can resolve errors */
+ gdm_dbus_error_ensure (GDM_SESSION_WORKER_ERROR);
+}
+
+GdmSession *
+gdm_session_new (GdmSessionVerificationMode verification_mode,
+ uid_t allowed_user,
+ const char *display_name,
+ const char *display_hostname,
+ const char *display_device,
+ const char *display_seat_id,
+ const char *display_x11_authority_file,
+ gboolean display_is_local,
+ const char * const *environment)
+{
+ GdmSession *self;
+
+ self = g_object_new (GDM_TYPE_SESSION,
+ "verification-mode", verification_mode,
+ "allowed-user", (guint) allowed_user,
+ "display-name", display_name,
+ "display-hostname", display_hostname,
+ "display-device", display_device,
+ "display-seat-id", display_seat_id,
+ "display-x11-authority-file", display_x11_authority_file,
+ "display-is-local", display_is_local,
+ "conversation-environment", environment,
+ NULL);
+
+ return self;
+}
+
+GdmSessionDisplayMode
+gdm_session_display_mode_from_string (const char *str)
+{
+ if (strcmp (str, "reuse-vt") == 0)
+ return GDM_SESSION_DISPLAY_MODE_REUSE_VT;
+ if (strcmp (str, "new-vt") == 0)
+ return GDM_SESSION_DISPLAY_MODE_NEW_VT;
+ if (strcmp (str, "logind-managed") == 0)
+ return GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED;
+
+ g_warning ("Unknown GdmSessionDisplayMode %s", str);
+ return -1;
+}
+
+const char *
+gdm_session_display_mode_to_string (GdmSessionDisplayMode mode)
+{
+ switch (mode) {
+ case GDM_SESSION_DISPLAY_MODE_REUSE_VT:
+ return "reuse-vt";
+ case GDM_SESSION_DISPLAY_MODE_NEW_VT:
+ return "new-vt";
+ case GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED:
+ return "logind-managed";
+ default:
+ break;
+ }
+
+ g_warning ("Unknown GdmSessionDisplayMode %d", mode);
+ return "";
+}
+
+GPid
+gdm_session_get_pid (GdmSession *session)
+{
+ return session->session_pid;
+}