summaryrefslogtreecommitdiffstats
path: root/daemon/gdm-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/gdm-manager.c')
-rw-r--r--daemon/gdm-manager.c2841
1 files changed, 2841 insertions, 0 deletions
diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c
new file mode 100644
index 0000000..e828544
--- /dev/null
+++ b/daemon/gdm-manager.c
@@ -0,0 +1,2841 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+
+#include <act/act-user-manager.h>
+
+#include <systemd/sd-login.h>
+
+#include "gdm-common.h"
+
+#include "gdm-dbus-util.h"
+#include "gdm-manager.h"
+#include "gdm-manager-glue.h"
+#include "gdm-display-store.h"
+#include "gdm-display-factory.h"
+#include "gdm-launch-environment.h"
+#include "gdm-local-display.h"
+#include "gdm-local-display-factory.h"
+#include "gdm-session.h"
+#include "gdm-session-record.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+#include "gdm-xdmcp-display-factory.h"
+#include "gdm-xdmcp-chooser-display.h"
+
+#define GDM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_MANAGER, GdmManagerPrivate))
+
+#define GDM_DBUS_PATH "/org/gnome/DisplayManager"
+#define GDM_MANAGER_PATH GDM_DBUS_PATH "/Manager"
+#define GDM_MANAGER_DISPLAYS_PATH GDM_DBUS_PATH "/Displays"
+
+#define INITIAL_SETUP_USERNAME "gnome-initial-setup"
+#define ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT GDM_RUN_DIR "/gdm.ran-initial-setup"
+
+typedef struct
+{
+ GdmManager *manager;
+ GdmSession *session;
+ char *service_name;
+ guint idle_id;
+} StartUserSessionOperation;
+
+struct GdmManagerPrivate
+{
+ GdmDisplayStore *display_store;
+ GdmLocalDisplayFactory *local_factory;
+#ifdef HAVE_LIBXDMCP
+ GdmXdmcpDisplayFactory *xdmcp_factory;
+#endif
+ GdmDisplay *automatic_login_display;
+ GList *user_sessions;
+ GHashTable *transient_sessions;
+ GHashTable *open_reauthentication_requests;
+ gboolean xdmcp_enabled;
+
+ gboolean started;
+ gboolean show_local_greeter;
+
+ GDBusConnection *connection;
+ GDBusObjectManagerServer *object_manager;
+
+#ifdef WITH_PLYMOUTH
+ guint plymouth_is_running : 1;
+#endif
+ guint did_automatic_login : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_XDMCP_ENABLED,
+ PROP_SHOW_LOCAL_GREETER
+};
+
+enum {
+ DISPLAY_ADDED,
+ DISPLAY_REMOVED,
+ LAST_SIGNAL
+};
+
+typedef enum {
+ SESSION_RECORD_LOGIN,
+ SESSION_RECORD_LOGOUT,
+ SESSION_RECORD_FAILED,
+} SessionRecord;
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_manager_class_init (GdmManagerClass *klass);
+static void gdm_manager_init (GdmManager *manager);
+static void gdm_manager_dispose (GObject *object);
+
+static GdmSession *create_user_session_for_display (GdmManager *manager,
+ GdmDisplay *display,
+ uid_t allowed_user);
+static void start_user_session (GdmManager *manager,
+ StartUserSessionOperation *operation);
+static void clean_user_session (GdmSession *session);
+
+static gpointer manager_object = NULL;
+
+static void manager_interface_init (GdmDBusManagerIface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (GdmManager,
+ gdm_manager,
+ GDM_DBUS_TYPE_MANAGER_SKELETON,
+ G_IMPLEMENT_INTERFACE (GDM_DBUS_TYPE_MANAGER,
+ manager_interface_init)
+ G_ADD_PRIVATE (GdmManager));
+
+#ifdef WITH_PLYMOUTH
+static gboolean
+plymouth_is_running (void)
+{
+ int status;
+ gboolean res;
+ GError *error;
+
+ error = NULL;
+ res = g_spawn_command_line_sync ("plymouth --ping",
+ NULL, NULL, &status, &error);
+ if (! res) {
+ g_debug ("Could not ping plymouth: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
+}
+
+static void
+plymouth_prepare_for_transition (void)
+{
+ gboolean res;
+ GError *error;
+
+ error = NULL;
+ res = g_spawn_command_line_sync ("plymouth deactivate",
+ NULL, NULL, NULL, &error);
+ if (! res) {
+ g_warning ("Could not deactivate plymouth: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+plymouth_quit_with_transition (void)
+{
+ gboolean res;
+ GError *error;
+
+ error = NULL;
+ res = g_spawn_command_line_async ("plymouth quit --retain-splash", &error);
+ if (! res) {
+ g_warning ("Could not quit plymouth: %s", error->message);
+ g_error_free (error);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+plymouth_quit_without_transition (void)
+{
+ gboolean res;
+ GError *error;
+
+ error = NULL;
+ res = g_spawn_command_line_async ("plymouth quit", &error);
+ if (! res) {
+ g_warning ("Could not quit plymouth: %s", error->message);
+ g_error_free (error);
+ }
+}
+#endif
+
+static char *
+get_session_id_for_pid (pid_t pid,
+ GError **error)
+{
+ char *session, *gsession;
+ int ret;
+
+ session = NULL;
+ ret = sd_pid_get_session (pid, &session);
+ if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting session id from systemd: %s",
+ g_strerror (-ret));
+ return NULL;
+ }
+
+ if (session != NULL) {
+ gsession = g_strdup (session);
+ free (session);
+
+ return gsession;
+ } else {
+ return NULL;
+ }
+}
+
+static gboolean
+get_uid_for_session_id (const char *session_id,
+ uid_t *uid,
+ GError **error)
+{
+ int ret;
+
+ ret = sd_session_get_uid (session_id, uid);
+ if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting uid for session id %s from systemd: %s",
+ session_id,
+ g_strerror (-ret));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+lookup_by_session_id (const char *id,
+ GdmDisplay *display,
+ gpointer user_data)
+{
+ const char *looking_for = user_data;
+ const char *current;
+
+ current = gdm_display_get_session_id (display);
+ return g_strcmp0 (current, looking_for) == 0;
+}
+
+static gboolean
+is_login_session (GdmManager *self,
+ const char *session_id,
+ GError **error)
+{
+ char *session_class = NULL;
+ int ret;
+
+ ret = sd_session_get_class (session_id, &session_class);
+
+ if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting class for session id %s from systemd: %s",
+ session_id,
+ g_strerror (-ret));
+ return FALSE;
+ }
+
+ if (g_strcmp0 (session_class, "greeter") != 0) {
+ g_free (session_class);
+ return FALSE;
+ }
+
+ g_free (session_class);
+ return TRUE;
+}
+
+static gboolean
+session_unlock (GdmManager *manager,
+ const char *ssid)
+{
+ GError *error = NULL;
+ GVariant *reply;
+
+ g_debug ("Unlocking session %s", ssid);
+
+ reply = g_dbus_connection_call_sync (manager->priv->connection,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "UnlockSession",
+ g_variant_new ("(s)", ssid),
+ NULL, /* expected reply */
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (reply == NULL) {
+ g_debug ("GdmManager: logind 'UnlockSession' %s raised:\n %s\n\n",
+ g_dbus_error_get_remote_error (error), error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ g_variant_unref (reply);
+
+ return TRUE;
+}
+
+static GdmSession *
+find_session_for_user_on_seat (GdmManager *manager,
+ const char *username,
+ const char *seat_id,
+ GdmSession *dont_count_session)
+{
+ GList *node;
+
+ for (node = manager->priv->user_sessions; node != NULL; node = node->next) {
+ GdmSession *candidate_session = node->data;
+ const char *candidate_username, *candidate_seat_id, *candidate_session_id;
+
+ candidate_session_id = gdm_session_get_session_id (candidate_session);
+
+ if (candidate_session == dont_count_session) {
+ g_debug ("GdmSession: Ignoring session %s as requested",
+ candidate_session_id);
+ continue;
+ }
+
+ if (!gdm_session_is_running (candidate_session)) {
+ g_debug ("GdmSession: Ignoring session %s as it isn't running",
+ candidate_session_id);
+ continue;
+ }
+
+ candidate_username = gdm_session_get_username (candidate_session);
+ candidate_seat_id = gdm_session_get_display_seat_id (candidate_session);
+
+ g_debug ("GdmManager: Considering session %s on seat %s belonging to user %s",
+ candidate_session_id,
+ candidate_seat_id,
+ candidate_username);
+
+ if (g_strcmp0 (candidate_username, username) == 0 &&
+ g_strcmp0 (candidate_seat_id, seat_id) == 0) {
+ g_debug ("GdmManager: yes, found session %s", candidate_session_id);
+ return candidate_session;
+ }
+
+ g_debug ("GdmManager: no, will not use session %s", candidate_session_id);
+ }
+
+ g_debug ("GdmManager: no matching sessions found");
+ return NULL;
+}
+
+static gboolean
+is_remote_session (GdmManager *self,
+ const char *session_id,
+ GError **error)
+{
+ char *seat;
+ int ret;
+ gboolean is_remote;
+
+ /* FIXME: The next release of logind is going to have explicit api for
+ * checking remoteness.
+ */
+ seat = NULL;
+ ret = sd_session_get_seat (session_id, &seat);
+
+ if (ret < 0 && ret != -ENXIO) {
+ g_debug ("GdmManager: Error while retrieving seat for session %s: %s",
+ session_id, strerror (-ret));
+ }
+
+ if (seat != NULL) {
+ is_remote = FALSE;
+ free (seat);
+ } else {
+ is_remote = TRUE;
+ }
+
+ return is_remote;
+}
+
+static char *
+get_seat_id_for_session_id (const char *session_id,
+ GError **error)
+{
+ int ret;
+ char *seat, *out_seat;
+
+ seat = NULL;
+ ret = sd_session_get_seat (session_id, &seat);
+
+ if (ret == -ENXIO) {
+ out_seat = NULL;
+ } else if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting uid for session id %s from systemd: %s",
+ session_id,
+ g_strerror (-ret));
+ out_seat = NULL;
+ } else {
+ out_seat = g_strdup (seat);
+ free (seat);
+ }
+
+ return out_seat;
+}
+
+static char *
+get_tty_for_session_id (const char *session_id,
+ GError **error)
+{
+ int ret;
+ char *tty, *out_tty;
+
+ ret = sd_session_get_tty (session_id, &tty);
+
+ if (ret == -ENXIO) {
+ out_tty = NULL;
+ } else if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting tty for session id %s from systemd: %s",
+ session_id,
+ g_strerror (-ret));
+ out_tty = NULL;
+ } else {
+ out_tty = g_strdup (tty);
+ free (tty);
+ }
+
+ return out_tty;
+}
+
+static void
+get_display_and_details_for_bus_sender (GdmManager *self,
+ GDBusConnection *connection,
+ const char *sender,
+ GdmDisplay **out_display,
+ char **out_seat_id,
+ char **out_session_id,
+ char **out_tty,
+ GPid *out_pid,
+ uid_t *out_uid,
+ gboolean *out_is_login_screen,
+ gboolean *out_is_remote)
+{
+ GdmDisplay *display = NULL;
+ char *session_id = NULL;
+ GError *error = NULL;
+ int ret;
+ GPid pid;
+ uid_t caller_uid, session_uid;
+
+ ret = gdm_dbus_get_pid_for_name (sender, &pid, &error);
+
+ if (!ret) {
+ g_debug ("GdmManager: Error while retrieving pid for sender: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (out_pid != NULL) {
+ *out_pid = pid;
+ }
+
+ ret = gdm_dbus_get_uid_for_name (sender, &caller_uid, &error);
+
+ if (!ret) {
+ g_debug ("GdmManager: Error while retrieving uid for sender: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ ret = gdm_find_display_session (pid, caller_uid, &session_id, &error);
+
+ if (!ret) {
+ g_debug ("GdmManager: Unable to find display session for uid %d: %s",
+ (int) caller_uid,
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (out_session_id != NULL) {
+ *out_session_id = g_strdup (session_id);
+ }
+
+ if (out_is_login_screen != NULL) {
+ *out_is_login_screen = is_login_session (self, session_id, &error);
+
+ if (error != NULL) {
+ g_debug ("GdmManager: Error while checking if sender is login screen: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+ }
+
+ if (!get_uid_for_session_id (session_id, &session_uid, &error)) {
+ g_debug ("GdmManager: Error while retrieving uid for session: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (out_uid != NULL) {
+ *out_uid = caller_uid;
+ }
+
+ if (caller_uid != session_uid) {
+ g_debug ("GdmManager: uid for sender and uid for session don't match");
+ goto out;
+ }
+
+ if (out_seat_id != NULL) {
+ *out_seat_id = get_seat_id_for_session_id (session_id, &error);
+
+ if (error != NULL) {
+ g_debug ("GdmManager: Error while retrieving seat id for session: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (out_is_remote != NULL) {
+ *out_is_remote = is_remote_session (self, session_id, &error);
+
+ if (error != NULL) {
+ g_debug ("GdmManager: Error while retrieving remoteness for session: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (out_tty != NULL) {
+ *out_tty = get_tty_for_session_id (session_id, &error);
+
+ if (error != NULL) {
+ g_debug ("GdmManager: Error while retrieving tty for session: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ display = gdm_display_store_find (self->priv->display_store,
+ lookup_by_session_id,
+ (gpointer) session_id);
+
+out:
+ if (out_display != NULL) {
+ *out_display = display;
+ }
+
+ g_free (session_id);
+}
+
+static gboolean
+switch_to_compatible_user_session (GdmManager *manager,
+ GdmSession *session,
+ gboolean fail_if_already_switched)
+{
+ gboolean res;
+ gboolean ret;
+ const char *username;
+ const char *seat_id;
+ const char *ssid_to_activate;
+ GdmSession *existing_session;
+
+ ret = FALSE;
+
+ username = gdm_session_get_username (session);
+ seat_id = gdm_session_get_display_seat_id (session);
+
+ if (!fail_if_already_switched) {
+ session = NULL;
+ }
+
+ existing_session = find_session_for_user_on_seat (manager, username, seat_id, session);
+
+ if (existing_session != NULL) {
+ ssid_to_activate = gdm_session_get_session_id (existing_session);
+ if (seat_id != NULL) {
+ res = gdm_activate_session_by_id (manager->priv->connection, seat_id, ssid_to_activate);
+ if (! res) {
+ g_debug ("GdmManager: unable to activate session: %s", ssid_to_activate);
+ goto out;
+ }
+ }
+
+ res = session_unlock (manager, ssid_to_activate);
+ if (!res) {
+ /* this isn't fatal */
+ g_debug ("GdmManager: unable to unlock session: %s", ssid_to_activate);
+ }
+ } else {
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+static GdmDisplay *
+get_display_for_user_session (GdmSession *session)
+{
+ return g_object_get_data (G_OBJECT (session), "gdm-display");
+}
+
+static GdmSession *
+get_user_session_for_display (GdmDisplay *display)
+{
+ if (display == NULL) {
+ return NULL;
+ }
+
+ return g_object_get_data (G_OBJECT (display), "gdm-user-session");
+}
+
+static gboolean
+add_session_record (GdmManager *manager,
+ GdmSession *session,
+ GPid pid,
+ SessionRecord record)
+{
+ const char *username;
+ char *display_name, *hostname, *display_device;
+ gboolean recorded = FALSE;
+
+ display_name = NULL;
+ username = NULL;
+ hostname = NULL;
+ display_device = NULL;
+
+ username = gdm_session_get_username (session);
+
+ if (username == NULL) {
+ goto out;
+ }
+
+ g_object_get (G_OBJECT (session),
+ "display-name", &display_name,
+ "display-hostname", &hostname,
+ "display-device", &display_device,
+ NULL);
+
+ if (display_name == NULL && display_device == NULL) {
+ goto out;
+ }
+
+ switch (record) {
+ case SESSION_RECORD_LOGIN:
+ gdm_session_record_login (pid,
+ username,
+ hostname,
+ display_name,
+ display_device);
+ break;
+ case SESSION_RECORD_LOGOUT:
+ gdm_session_record_logout (pid,
+ username,
+ hostname,
+ display_name,
+ display_device);
+ break;
+ case SESSION_RECORD_FAILED:
+ gdm_session_record_failed (pid,
+ username,
+ hostname,
+ display_name,
+ display_device);
+ break;
+ }
+
+ recorded = TRUE;
+out:
+ g_free (display_name);
+ g_free (hostname);
+ g_free (display_device);
+
+ return recorded;
+}
+
+static GdmSession *
+find_user_session_for_display (GdmManager *self,
+ GdmDisplay *display)
+{
+
+ GList *node = self->priv->user_sessions;
+
+ while (node != NULL) {
+ GdmSession *session = node->data;
+ GdmDisplay *candidate_display;
+ GList *next_node = node->next;
+
+ candidate_display = get_display_for_user_session (session);
+
+ if (candidate_display == display)
+ return session;
+
+ node = next_node;
+ }
+
+ return NULL;
+}
+
+static gboolean
+gdm_manager_handle_register_display (GdmDBusManager *manager,
+ GDBusMethodInvocation *invocation,
+ GVariant *details)
+{
+ GdmManager *self = GDM_MANAGER (manager);
+ const char *sender;
+ GDBusConnection *connection;
+ GdmDisplay *display = NULL;
+ GdmSession *session;
+ GVariantIter iter;
+ char *key = NULL;
+ char *value = NULL;
+ char *x11_display_name = NULL;
+ char *tty = NULL;
+
+ g_debug ("GdmManager: trying to register new display");
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, &tty, NULL, NULL, NULL, NULL);
+
+ if (display == NULL) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("No display available"));
+
+ return TRUE;
+ }
+
+ g_variant_iter_init (&iter, details);
+ while (g_variant_iter_loop (&iter, "{&s&s}", &key, &value)) {
+ if (g_strcmp0 (key, "x11-display-name") == 0) {
+ x11_display_name = g_strdup (value);
+ break;
+ }
+ }
+
+ session = find_user_session_for_display (self, display);
+
+ if (session != NULL) {
+ GPid pid;
+
+ if (x11_display_name != NULL) {
+ g_object_set (G_OBJECT (session), "display-name", x11_display_name, NULL);
+ g_object_set (G_OBJECT (display), "x11-display-name", x11_display_name, NULL);
+ }
+
+ /* FIXME: this should happen in gdm-session.c when the session is opened
+ */
+ if (tty != NULL)
+ g_object_set (G_OBJECT (session), "display-device", tty, NULL);
+
+ pid = gdm_session_get_pid (session);
+
+ if (pid > 0) {
+ add_session_record (self, session, pid, SESSION_RECORD_LOGIN);
+ }
+ }
+
+ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_MANAGED, NULL);
+
+ gdm_dbus_manager_complete_register_display (GDM_DBUS_MANAGER (manager),
+ invocation);
+
+ g_clear_pointer (&x11_display_name, g_free);
+ g_clear_pointer (&tty, g_free);
+ return TRUE;
+}
+
+static gboolean
+gdm_manager_handle_register_session (GdmDBusManager *manager,
+ GDBusMethodInvocation *invocation,
+ GVariant *details)
+{
+ GdmManager *self = GDM_MANAGER (manager);
+ GdmDisplay *display = NULL;
+ const char *sender;
+ GDBusConnection *connection;
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+ connection = g_dbus_method_invocation_get_connection (invocation);
+
+ get_display_and_details_for_bus_sender (self, connection, sender, &display,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ g_debug ("GdmManager: trying to register new session on display %p", display);
+
+ if (display != NULL)
+ g_object_set (G_OBJECT (display), "session-registered", TRUE, NULL);
+ else
+ g_debug ("GdmManager: No display, not registering");
+
+ gdm_dbus_manager_complete_register_session (GDM_DBUS_MANAGER (manager),
+ invocation);
+
+ return TRUE;
+}
+
+static gboolean
+gdm_manager_handle_open_session (GdmDBusManager *manager,
+ GDBusMethodInvocation *invocation)
+{
+ GdmManager *self = GDM_MANAGER (manager);
+ const char *sender;
+ GDBusConnection *connection;
+ GdmDisplay *display = NULL;
+ GdmSession *session = NULL;
+ const char *address;
+ GPid pid = 0;
+ uid_t uid = (uid_t) -1;
+ uid_t allowed_user;
+
+ g_debug ("GdmManager: trying to open new session");
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, NULL, &pid, &uid, NULL, NULL);
+
+ if (display == NULL) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("No session available"));
+
+ return TRUE;
+ }
+
+#ifdef HAVE_LIBXDMCP
+ if (GDM_IS_XDMCP_CHOOSER_DISPLAY (display)) {
+ GdmLaunchEnvironment *launch_environment;
+
+ g_object_get (display, "launch-environment", &launch_environment, NULL);
+
+ if (launch_environment != NULL) {
+ session = gdm_launch_environment_get_session (launch_environment);
+ }
+
+ if (session == NULL) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("Chooser session unavailable"));
+ return TRUE;
+ }
+ }
+#endif
+ if (session == NULL) {
+ session = get_user_session_for_display (display);
+ g_debug ("GdmSession: Considering session %s for username %s",
+ gdm_session_get_session_id (session),
+ gdm_session_get_username (session));
+
+ if (gdm_session_is_running (session)) {
+ g_debug ("GdmSession: the session is running, and therefore can't be used");
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("Can only be called before user is logged in"));
+ return TRUE;
+ }
+ }
+
+ allowed_user = gdm_session_get_allowed_user (session);
+
+ if (uid != allowed_user) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("Caller not GDM"));
+ return TRUE;
+ }
+
+ address = gdm_session_get_server_address (session);
+
+ if (address == NULL) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("Unable to open private communication channel"));
+ return TRUE;
+ }
+
+ gdm_dbus_manager_complete_open_session (GDM_DBUS_MANAGER (manager),
+ invocation,
+ address);
+ return TRUE;
+}
+
+static void
+close_transient_session (GdmManager *self,
+ GdmSession *session)
+{
+ GPid pid;
+ pid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (session), "caller-pid"));
+ gdm_session_close (session);
+ g_hash_table_remove (self->priv->transient_sessions,
+ GUINT_TO_POINTER (pid));
+}
+
+static void
+on_reauthentication_client_connected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *self)
+{
+ g_debug ("GdmManager: client connected to reauthentication server");
+}
+
+static void
+on_reauthentication_client_disconnected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *self)
+{
+ g_debug ("GdmManger: client disconnected from reauthentication server");
+ close_transient_session (self, session);
+}
+
+static void
+on_reauthentication_client_rejected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *self)
+{
+ GPid pid;
+
+ g_debug ("GdmManger: client with pid %ld rejected from reauthentication server", (long) pid_of_client);
+
+ if (gdm_session_client_is_connected (session)) {
+ /* we already have a client connected, ignore this rejected one */
+ return;
+ }
+
+ pid = (GPid) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (session), "caller-pid"));
+
+ if (pid != pid_of_client) {
+ const char *session_id;
+ char *client_session_id;
+
+ /* rejected client isn't the process that started the
+ * transient reauthentication session. If it's not even from the
+ * same audit session, ignore it since it doesn't "own" the
+ * reauthentication session
+ */
+ client_session_id = get_session_id_for_pid (pid_of_client,
+ NULL);
+ session_id = g_object_get_data (G_OBJECT (session), "caller-session-id");
+
+ if (g_strcmp0 (session_id, client_session_id) != 0) {
+ return;
+ }
+ }
+
+ /* client was rejected, so clean up its session object
+ */
+ close_transient_session (self, session);
+}
+
+static void
+on_reauthentication_cancelled (GdmSession *session,
+ GdmManager *self)
+{
+ g_debug ("GdmManager: client cancelled reauthentication request");
+ close_transient_session (self, session);
+}
+
+static void
+on_reauthentication_conversation_started (GdmSession *session,
+ const char *service_name,
+ GdmManager *self)
+{
+ g_debug ("GdmManager: reauthentication service '%s' started",
+ service_name);
+}
+
+static void
+on_reauthentication_conversation_stopped (GdmSession *session,
+ const char *service_name,
+ GdmManager *self)
+{
+ g_debug ("GdmManager: reauthentication service '%s' stopped",
+ service_name);
+}
+
+static void
+on_reauthentication_verification_complete (GdmSession *session,
+ const char *service_name,
+ GdmManager *self)
+{
+ const char *session_id;
+ session_id = g_object_get_data (G_OBJECT (session), "caller-session-id");
+ g_debug ("GdmManager: reauthenticated user in unmanaged session '%s' with service '%s'",
+ session_id, service_name);
+ session_unlock (self, session_id);
+ close_transient_session (self, session);
+}
+
+static char *
+open_temporary_reauthentication_channel (GdmManager *self,
+ char *seat_id,
+ char *session_id,
+ GPid pid,
+ uid_t uid,
+ gboolean is_remote)
+{
+ GdmSession *session;
+ char **environment;
+ const char *display, *auth_file;
+ const char *address;
+
+ /* Note we're just using a minimal environment here rather than the
+ * session's environment because the caller is unprivileged and the
+ * associated worker will be privileged */
+ environment = g_get_environ ();
+ display = "";
+ auth_file = "/dev/null";
+
+ session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE,
+ uid,
+ display,
+ NULL,
+ NULL,
+ seat_id,
+ auth_file,
+ is_remote == FALSE,
+ (const char * const *)
+ environment);
+ g_strfreev (environment);
+
+ g_debug ("GdmSession: Created session for temporary reauthentication channel for user %d (seat %s)",
+ (int) uid,
+ seat_id);
+
+ g_object_set_data_full (G_OBJECT (session),
+ "caller-session-id",
+ g_strdup (session_id),
+ (GDestroyNotify)
+ g_free);
+ g_object_set_data (G_OBJECT (session),
+ "caller-pid",
+ GUINT_TO_POINTER (pid));
+ g_hash_table_insert (self->priv->transient_sessions,
+ GINT_TO_POINTER (pid),
+ session);
+
+ g_signal_connect (session,
+ "client-connected",
+ G_CALLBACK (on_reauthentication_client_connected),
+ self);
+ g_signal_connect (session,
+ "client-disconnected",
+ G_CALLBACK (on_reauthentication_client_disconnected),
+ self);
+ g_signal_connect (session,
+ "client-rejected",
+ G_CALLBACK (on_reauthentication_client_rejected),
+ self);
+ g_signal_connect (session,
+ "cancelled",
+ G_CALLBACK (on_reauthentication_cancelled),
+ self);
+ g_signal_connect (session,
+ "conversation-started",
+ G_CALLBACK (on_reauthentication_conversation_started),
+ self);
+ g_signal_connect (session,
+ "conversation-stopped",
+ G_CALLBACK (on_reauthentication_conversation_stopped),
+ self);
+ g_signal_connect (session,
+ "verification-complete",
+ G_CALLBACK (on_reauthentication_verification_complete),
+ self);
+
+ address = gdm_session_get_server_address (session);
+
+ return g_strdup (address);
+}
+
+static gboolean
+gdm_manager_handle_open_reauthentication_channel (GdmDBusManager *manager,
+ GDBusMethodInvocation *invocation,
+ const char *username)
+{
+ GdmManager *self = GDM_MANAGER (manager);
+ const char *sender;
+ GdmDisplay *display = NULL;
+ GdmSession *session;
+ GDBusConnection *connection;
+ char *seat_id = NULL;
+ char *session_id = NULL;
+ GPid pid = 0;
+ uid_t uid = (uid_t) -1;
+ gboolean is_login_screen = FALSE;
+ gboolean is_remote = FALSE;
+
+ g_debug ("GdmManager: trying to open reauthentication channel for user %s", username);
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ get_display_and_details_for_bus_sender (self, connection, sender, &display, &seat_id, &session_id, NULL, &pid, &uid, &is_login_screen, &is_remote);
+
+ if (session_id == NULL || pid == 0 || uid == (uid_t) -1) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("No session available"));
+
+ return TRUE;
+ }
+
+ if (is_login_screen) {
+ g_debug ("GdmManager: looking for login screen session for user %s on seat %s", username, seat_id);
+ session = find_session_for_user_on_seat (self,
+ username,
+ seat_id,
+ NULL);
+ } else {
+ g_debug ("GdmManager: looking for user session on display");
+ session = get_user_session_for_display (display);
+ }
+
+ if (session != NULL && gdm_session_is_running (session)) {
+ gdm_session_start_reauthentication (session, pid, uid);
+ g_hash_table_insert (self->priv->open_reauthentication_requests,
+ GINT_TO_POINTER (pid),
+ invocation);
+ } else if (is_login_screen) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "Login screen only allowed to open reauthentication channels for running sessions");
+ return TRUE;
+ } else {
+ char *address;
+ address = open_temporary_reauthentication_channel (self,
+ seat_id,
+ session_id,
+ pid,
+ uid,
+ is_remote);
+ gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager),
+ invocation,
+ address);
+ g_free (address);
+ }
+
+ return TRUE;
+}
+
+static void
+manager_interface_init (GdmDBusManagerIface *interface)
+{
+ interface->handle_register_display = gdm_manager_handle_register_display;
+ interface->handle_register_session = gdm_manager_handle_register_session;
+ interface->handle_open_session = gdm_manager_handle_open_session;
+ interface->handle_open_reauthentication_channel = gdm_manager_handle_open_reauthentication_channel;
+}
+
+static gboolean
+display_is_on_seat0 (GdmDisplay *display)
+{
+ gboolean is_on_seat0 = TRUE;
+ char *seat_id = NULL;
+
+ g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL);
+
+ if (g_strcmp0 (seat_id, "seat0") != 0) {
+ is_on_seat0 = FALSE;
+ }
+
+ g_free (seat_id);
+
+ return is_on_seat0;
+}
+
+static gboolean
+get_timed_login_details (GdmManager *manager,
+ char **usernamep,
+ int *delayp)
+{
+ gboolean res;
+ gboolean enabled;
+
+ int delay;
+ char *username = NULL;
+
+ enabled = FALSE;
+ username = NULL;
+ delay = 0;
+
+ res = gdm_settings_direct_get_boolean (GDM_KEY_TIMED_LOGIN_ENABLE, &enabled);
+ if (res && ! enabled) {
+ goto out;
+ }
+
+ res = gdm_settings_direct_get_string (GDM_KEY_TIMED_LOGIN_USER, &username);
+ if (res && (username == NULL || username[0] == '\0')) {
+ g_clear_pointer (&username, g_free);
+ goto out;
+ }
+
+ delay = 0;
+ res = gdm_settings_direct_get_int (GDM_KEY_TIMED_LOGIN_DELAY, &delay);
+
+ if (res && delay <= 0) {
+ /* we don't allow the timed login to have a zero delay */
+ delay = 10;
+ }
+
+ out:
+ if (enabled) {
+ g_debug ("GdmDisplay: Got timed login details for display: %d %s %d",
+ enabled,
+ username,
+ delay);
+ } else {
+ g_debug ("GdmDisplay: Got timed login details for display: 0");
+ }
+
+ if (usernamep != NULL) {
+ *usernamep = username;
+ } else {
+ g_free (username);
+ }
+ if (delayp != NULL) {
+ *delayp = delay;
+ }
+
+ return enabled;
+}
+
+static gboolean
+get_automatic_login_details (GdmManager *manager,
+ char **usernamep)
+{
+ gboolean res;
+ gboolean enabled;
+ char *username = NULL;
+
+ enabled = FALSE;
+ username = NULL;
+
+ res = gdm_settings_direct_get_boolean (GDM_KEY_AUTO_LOGIN_ENABLE, &enabled);
+ if (res && enabled) {
+ res = gdm_settings_direct_get_string (GDM_KEY_AUTO_LOGIN_USER, &username);
+ }
+
+ if (enabled && res && username != NULL && username[0] != '\0') {
+ goto out;
+ }
+
+ g_free (username);
+ username = NULL;
+ enabled = FALSE;
+
+ out:
+ if (enabled) {
+ g_debug ("GdmDisplay: Got automatic login details for display: %d %s",
+ enabled,
+ username);
+ } else {
+ g_debug ("GdmDisplay: Got automatic login details for display: 0");
+ }
+
+ if (usernamep != NULL) {
+ *usernamep = username;
+ } else {
+ g_free (username);
+ }
+
+ return enabled;
+}
+
+static const char *
+get_username_for_greeter_display (GdmManager *manager,
+ GdmDisplay *display)
+{
+ gboolean doing_initial_setup = FALSE;
+
+ g_object_get (G_OBJECT (display),
+ "doing-initial-setup", &doing_initial_setup,
+ NULL);
+
+ if (doing_initial_setup) {
+ return INITIAL_SETUP_USERNAME;
+ } else {
+ return GDM_USERNAME;
+ }
+}
+
+static void
+set_up_automatic_login_session (GdmManager *manager,
+ GdmDisplay *display)
+{
+ GdmSession *session;
+ char *display_session_type = NULL;
+
+ /* 0 is root user; since the daemon talks to the session object
+ * directly, itself, for automatic login
+ */
+ session = create_user_session_for_display (manager, display, 0);
+
+ g_object_get (G_OBJECT (display),
+ "session-type", &display_session_type,
+ NULL);
+
+ g_object_set (G_OBJECT (session),
+ "display-is-initial", FALSE,
+ NULL);
+
+ g_debug ("GdmManager: Starting automatic login conversation");
+ gdm_session_start_conversation (session, "gdm-autologin");
+}
+
+static void
+set_up_chooser_session (GdmManager *manager,
+ GdmDisplay *display)
+{
+ const char *allowed_user;
+ struct passwd *passwd_entry;
+
+ allowed_user = get_username_for_greeter_display (manager, display);
+
+ if (!gdm_get_pwent_for_name (allowed_user, &passwd_entry)) {
+ g_warning ("GdmManager: couldn't look up username %s",
+ allowed_user);
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+ return;
+ }
+
+ gdm_display_start_greeter_session (display);
+}
+
+static void
+set_up_greeter_session (GdmManager *manager,
+ GdmDisplay *display)
+{
+ const char *allowed_user;
+ struct passwd *passwd_entry;
+
+ allowed_user = get_username_for_greeter_display (manager, display);
+
+ if (!gdm_get_pwent_for_name (allowed_user, &passwd_entry)) {
+ g_warning ("GdmManager: couldn't look up username %s",
+ allowed_user);
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+ return;
+ }
+
+ create_user_session_for_display (manager, display, passwd_entry->pw_uid);
+ gdm_display_start_greeter_session (display);
+}
+
+static void
+set_up_automatic_login_session_if_user_exists (GdmManager *manager,
+ GdmDisplay *display,
+ ActUser *user)
+{
+ if (act_user_is_nonexistent (user))
+ set_up_greeter_session (manager, display);
+ else
+ set_up_automatic_login_session (manager, display);
+}
+
+typedef struct {
+ GdmManager *manager;
+ GdmDisplay *display;
+ char *username;
+} UsernameLookupOperation;
+
+static void
+destroy_username_lookup_operation (UsernameLookupOperation *operation)
+{
+ g_object_unref (operation->manager);
+ g_object_unref (operation->display);
+ g_free (operation->username);
+ g_free (operation);
+}
+
+static void
+on_user_is_loaded_changed (ActUser *user,
+ GParamSpec *pspec,
+ UsernameLookupOperation *operation)
+{
+ if (act_user_is_loaded (user)) {
+ set_up_automatic_login_session_if_user_exists (operation->manager, operation->display, user);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (user),
+ G_CALLBACK (on_user_is_loaded_changed),
+ operation);
+ destroy_username_lookup_operation (operation);
+ }
+}
+
+static void
+set_up_session (GdmManager *manager,
+ GdmDisplay *display)
+{
+ ActUserManager *user_manager;
+ ActUser *user;
+ gboolean loaded;
+ gboolean seat_can_autologin = FALSE, seat_did_autologin = FALSE;
+ gboolean autologin_enabled = FALSE;
+ g_autofree char *seat_id = NULL;
+ char *username = NULL;
+
+ g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL);
+
+ if (g_strcmp0 (seat_id, "seat0") == 0)
+ seat_can_autologin = TRUE;
+
+ if (manager->priv->did_automatic_login || manager->priv->automatic_login_display != NULL)
+ seat_did_autologin = TRUE;
+
+ if (seat_can_autologin && !seat_did_autologin)
+ autologin_enabled = get_automatic_login_details (manager, &username);
+
+ if (!autologin_enabled) {
+ g_free (username);
+
+#ifdef HAVE_LIBXDMCP
+ if (GDM_IS_XDMCP_CHOOSER_DISPLAY (display)) {
+ set_up_chooser_session (manager, display);
+ return;
+ }
+#endif
+
+ set_up_greeter_session (manager, display);
+ return;
+ }
+
+ /* Check whether the user really exists before committing to autologin. */
+ user_manager = act_user_manager_get_default ();
+ user = act_user_manager_get_user (user_manager, username);
+ g_object_get (user_manager, "is-loaded", &loaded, NULL);
+
+ if (loaded) {
+ set_up_automatic_login_session_if_user_exists (manager, display, user);
+ } else {
+ UsernameLookupOperation *operation;
+
+ operation = g_new (UsernameLookupOperation, 1);
+ operation->manager = g_object_ref (manager);
+ operation->display = g_object_ref (display);
+ operation->username = username;
+
+ g_signal_connect (user,
+ "notify::is-loaded",
+ G_CALLBACK (on_user_is_loaded_changed),
+ operation);
+ }
+}
+
+static void
+on_display_status_changed (GdmDisplay *display,
+ GParamSpec *arg1,
+ GdmManager *manager)
+{
+ int status;
+ int display_number = -1;
+ char *session_type = NULL;
+ gboolean doing_initial_setup = FALSE;
+#ifdef WITH_PLYMOUTH
+ gboolean display_is_local = FALSE;
+ gboolean quit_plymouth = FALSE;
+
+ g_object_get (display,
+ "is-local", &display_is_local,
+ NULL);
+ quit_plymouth = display_is_local && manager->priv->plymouth_is_running;
+#endif
+
+ g_object_get (display,
+ "x11-display-number", &display_number,
+ "session-type", &session_type,
+ "doing-initial-setup", &doing_initial_setup,
+ NULL);
+
+ status = gdm_display_get_status (display);
+
+ switch (status) {
+ case GDM_DISPLAY_PREPARED:
+ case GDM_DISPLAY_MANAGED:
+ if ((display_number == -1 && status == GDM_DISPLAY_PREPARED) ||
+ (display_number != -1 && status == GDM_DISPLAY_MANAGED)) {
+ char *session_class;
+
+ g_object_get (display,
+ "session-class", &session_class,
+ NULL);
+ if (g_strcmp0 (session_class, "greeter") == 0)
+ set_up_session (manager, display);
+ g_free (session_class);
+ }
+ break;
+ case GDM_DISPLAY_FAILED:
+ case GDM_DISPLAY_UNMANAGED:
+ case GDM_DISPLAY_FINISHED:
+#ifdef WITH_PLYMOUTH
+ if (quit_plymouth) {
+ plymouth_quit_without_transition ();
+ manager->priv->plymouth_is_running = FALSE;
+ }
+#endif
+
+ if (display == manager->priv->automatic_login_display) {
+ g_clear_weak_pointer (&manager->priv->automatic_login_display);
+
+ manager->priv->did_automatic_login = TRUE;
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ if (g_strcmp0 (session_type, "wayland") != 0 && status == GDM_DISPLAY_FAILED) {
+ /* we're going to fall back to X11, so try to autologin again
+ */
+ manager->priv->did_automatic_login = FALSE;
+ }
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
+static void
+on_display_removed (GdmDisplayStore *display_store,
+ GdmDisplay *display,
+ GdmManager *manager)
+{
+ char *id;
+
+ gdm_display_get_id (display, &id, NULL);
+ g_dbus_object_manager_server_unexport (manager->priv->object_manager, id);
+ g_free (id);
+
+ g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), manager);
+
+ g_signal_emit (manager, signals[DISPLAY_REMOVED], 0, display);
+}
+
+static void
+destroy_start_user_session_operation (StartUserSessionOperation *operation)
+{
+ g_object_set_data (G_OBJECT (operation->session),
+ "start-user-session-operation",
+ NULL);
+ g_object_unref (operation->session);
+ g_free (operation->service_name);
+ g_slice_free (StartUserSessionOperation, operation);
+}
+
+static void
+start_user_session (GdmManager *manager,
+ StartUserSessionOperation *operation)
+{
+ GdmDisplay *display;
+
+ display = get_display_for_user_session (operation->session);
+
+ if (display != NULL) {
+ char *auth_file;
+ const char *username;
+ gboolean is_connected = FALSE;
+
+ g_object_get (G_OBJECT (display), "is-connected", &is_connected, NULL);
+
+ if (is_connected) {
+ auth_file = NULL;
+ username = gdm_session_get_username (operation->session);
+ gdm_display_add_user_authorization (display,
+ username,
+ &auth_file,
+ NULL);
+
+ g_assert (auth_file != NULL);
+
+ g_object_set (operation->session,
+ "user-x11-authority-file", auth_file,
+ NULL);
+
+ g_free (auth_file);
+ }
+ }
+
+ gdm_session_start_session (operation->session,
+ operation->service_name);
+
+ destroy_start_user_session_operation (operation);
+}
+
+static void
+create_display_for_user_session (GdmManager *self,
+ GdmSession *session,
+ const char *session_id)
+{
+ GdmDisplay *display;
+ /* at the moment we only create GdmLocalDisplay objects on seat0 */
+ const char *seat_id = "seat0";
+
+ display = gdm_local_display_new ();
+
+ g_object_set (G_OBJECT (display),
+ "session-class", "user",
+ "seat-id", seat_id,
+ "session-id", session_id,
+ NULL);
+ gdm_display_store_add (self->priv->display_store,
+ display);
+ g_object_set_data (G_OBJECT (session), "gdm-display", display);
+ g_object_set_data_full (G_OBJECT (display),
+ "gdm-user-session",
+ g_object_ref (session),
+ (GDestroyNotify)
+ clean_user_session);
+}
+
+static gboolean
+chown_file (GFile *file,
+ uid_t uid,
+ gid_t gid,
+ GError **error)
+{
+ if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_UID, uid,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, error)) {
+ return FALSE;
+ }
+ if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_GID, gid,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, error)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+chown_recursively (GFile *dir,
+ uid_t uid,
+ gid_t gid,
+ GError **error)
+{
+ GFile *file = NULL;
+ GFileInfo *info = NULL;
+ GFileEnumerator *enumerator = NULL;
+ gboolean retval = FALSE;
+
+ if (chown_file (dir, uid, gid, error) == FALSE) {
+ goto out;
+ }
+
+ enumerator = g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE","
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, error);
+ if (!enumerator) {
+ goto out;
+ }
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, error)) != NULL) {
+ file = g_file_get_child (dir, g_file_info_get_name (info));
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+ if (chown_recursively (file, uid, gid, error) == FALSE) {
+ goto out;
+ }
+ } else if (chown_file (file, uid, gid, error) == FALSE) {
+ goto out;
+ }
+
+ g_clear_object (&file);
+ g_clear_object (&info);
+ }
+
+ if (*error) {
+ goto out;
+ }
+
+ retval = TRUE;
+out:
+ g_clear_object (&file);
+ g_clear_object (&info);
+ g_clear_object (&enumerator);
+
+ return retval;
+}
+
+static void
+chown_initial_setup_home_dir (void)
+{
+ GFile *dir;
+ GError *error;
+ char *gis_dir_path;
+ char *gis_uid_path;
+ char *gis_uid_contents;
+ struct passwd *pwe;
+ uid_t uid;
+
+ if (!gdm_get_pwent_for_name (INITIAL_SETUP_USERNAME, &pwe)) {
+ g_warning ("Unknown user %s", INITIAL_SETUP_USERNAME);
+ return;
+ }
+
+ gis_dir_path = g_strdup (pwe->pw_dir);
+
+ gis_uid_path = g_build_filename (gis_dir_path,
+ "gnome-initial-setup-uid",
+ NULL);
+ if (!g_file_get_contents (gis_uid_path, &gis_uid_contents, NULL, NULL)) {
+ g_warning ("Unable to read %s", gis_uid_path);
+ goto out;
+ }
+
+ uid = (uid_t) atoi (gis_uid_contents);
+ pwe = getpwuid (uid);
+ if (uid == 0 || pwe == NULL) {
+ g_warning ("UID '%s' in %s is not valid", gis_uid_contents, gis_uid_path);
+ goto out;
+ }
+
+ error = NULL;
+ dir = g_file_new_for_path (gis_dir_path);
+ if (!chown_recursively (dir, pwe->pw_uid, pwe->pw_gid, &error)) {
+ g_warning ("Failed to change ownership for %s: %s", gis_dir_path, error->message);
+ g_error_free (error);
+ }
+ g_object_unref (dir);
+out:
+ g_free (gis_uid_contents);
+ g_free (gis_uid_path);
+ g_free (gis_dir_path);
+}
+
+static gboolean
+on_start_user_session (StartUserSessionOperation *operation)
+{
+ GdmManager *self = operation->manager;
+ gboolean migrated;
+ gboolean fail_if_already_switched = TRUE;
+ gboolean doing_initial_setup = FALSE;
+ GdmDisplay *display;
+ const char *session_id;
+
+ g_debug ("GdmManager: start or jump to session");
+
+ /* If there's already a session running, jump to it.
+ * If the only session running is the one we just opened,
+ * start a session on it.
+ */
+ migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched);
+
+ g_debug ("GdmManager: migrated: %d", migrated);
+ if (migrated) {
+ /* We don't stop the manager here because
+ when Xorg exits it switches to the VT it was
+ started from. That interferes with fast
+ user switching. */
+ gdm_session_reset (operation->session);
+ destroy_start_user_session_operation (operation);
+ goto out;
+ }
+
+ display = get_display_for_user_session (operation->session);
+
+ g_object_get (G_OBJECT (display),
+ "doing-initial-setup", &doing_initial_setup,
+ NULL);
+
+ if (doing_initial_setup)
+ chown_initial_setup_home_dir ();
+
+ session_id = gdm_session_get_conversation_session_id (operation->session,
+ operation->service_name);
+
+ if (gdm_session_get_display_mode (operation->session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) {
+ /* In this case, the greeter's display is morphing into
+ * the user session display. Kill the greeter on this session
+ * and let the user session follow the same display. */
+ gdm_display_stop_greeter_session (display);
+ g_object_set (G_OBJECT (display),
+ "session-class", "user",
+ "session-id", session_id,
+ NULL);
+ } else {
+ uid_t allowed_uid;
+
+ g_object_ref (display);
+ if (doing_initial_setup) {
+ g_autoptr(GError) error = NULL;
+
+ g_debug ("GdmManager: closing down initial setup display in background");
+ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL);
+
+ if (!g_file_set_contents (ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT,
+ "1",
+ 1,
+ &error)) {
+ g_warning ("GdmDisplay: Could not write initial-setup-done marker to %s: %s",
+ ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT,
+ error->message);
+ g_clear_error (&error);
+ }
+ } else {
+ g_debug ("GdmManager: session has its display server, reusing our server for another login screen");
+ }
+
+ /* The user session is going to follow the session worker
+ * into the new display. Untie it from this display and
+ * create a new session for a future user login. */
+ allowed_uid = gdm_session_get_allowed_user (operation->session);
+ g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL);
+ g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL);
+ create_user_session_for_display (operation->manager, display, allowed_uid);
+
+ /* Give the user session a new display object for bookkeeping purposes */
+ create_display_for_user_session (operation->manager,
+ operation->session,
+ session_id);
+
+
+ if (g_strcmp0 (operation->service_name, "gdm-autologin") == 0 &&
+ !gdm_session_client_is_connected (operation->session)) {
+ /* remove the unused prepared greeter display since we're not going
+ * to have a greeter */
+ gdm_display_store_remove (self->priv->display_store, display);
+ g_object_unref (display);
+
+ self->priv->automatic_login_display = g_object_get_data (G_OBJECT (operation->session), "gdm-display");
+ g_object_add_weak_pointer (G_OBJECT (self->priv->automatic_login_display), (gpointer *) &self->priv->automatic_login_display);
+ }
+ }
+
+ start_user_session (operation->manager, operation);
+
+ out:
+ return G_SOURCE_REMOVE;
+}
+
+static void
+queue_start_user_session (GdmManager *manager,
+ GdmSession *session,
+ const char *service_name)
+{
+ StartUserSessionOperation *operation;
+
+ operation = g_slice_new0 (StartUserSessionOperation);
+ operation->manager = manager;
+ operation->session = g_object_ref (session);
+ operation->service_name = g_strdup (service_name);
+
+ operation->idle_id = g_idle_add ((GSourceFunc) on_start_user_session, operation);
+ g_object_set_data (G_OBJECT (session), "start-user-session-operation", operation);
+}
+
+static void
+start_user_session_if_ready (GdmManager *manager,
+ GdmSession *session,
+ const char *service_name)
+{
+ gboolean start_when_ready;
+
+ start_when_ready = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (session), "start-when-ready"));
+ if (start_when_ready) {
+ g_object_set_data (G_OBJECT (session), "waiting-to-start", GINT_TO_POINTER (FALSE));
+ queue_start_user_session (manager, session, service_name);
+ } else {
+ g_object_set_data (G_OBJECT (session), "waiting-to-start", GINT_TO_POINTER (TRUE));
+ }
+}
+
+static void
+on_session_authentication_failed (GdmSession *session,
+ const char *service_name,
+ GPid conversation_pid,
+ GdmManager *manager)
+{
+ add_session_record (manager, session, conversation_pid, SESSION_RECORD_FAILED);
+}
+
+static void
+on_user_session_opened (GdmSession *session,
+ const char *service_name,
+ const char *session_id,
+ GdmManager *manager)
+{
+ manager->priv->user_sessions = g_list_append (manager->priv->user_sessions,
+ g_object_ref (session));
+ if (g_strcmp0 (service_name, "gdm-autologin") == 0 &&
+ !gdm_session_client_is_connected (session)) {
+ /* If we're auto logging in then don't wait for the go-ahead from a greeter,
+ * (since there is no greeter) */
+ g_object_set_data (G_OBJECT (session), "start-when-ready", GINT_TO_POINTER (TRUE));
+ }
+
+ start_user_session_if_ready (manager, session, service_name);
+}
+
+static void
+on_user_session_started (GdmSession *session,
+ const char *service_name,
+ GPid pid,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: session started %d", pid);
+ add_session_record (manager, session, pid, SESSION_RECORD_LOGIN);
+
+#ifdef WITH_PLYMOUTH
+ if (g_strcmp0 (service_name, "gdm-autologin") == 0) {
+ if (manager->priv->plymouth_is_running) {
+ g_timeout_add_seconds (20, (GSourceFunc) plymouth_quit_with_transition, NULL);
+ manager->priv->plymouth_is_running = FALSE;
+ }
+ }
+#endif
+}
+
+static void
+remove_user_session (GdmManager *manager,
+ GdmSession *session)
+{
+ GList *node;
+ GdmDisplay *display;
+
+ display = get_display_for_user_session (session);
+
+ if (display != NULL) {
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+ }
+
+ node = g_list_find (manager->priv->user_sessions, session);
+
+ if (node != NULL) {
+ manager->priv->user_sessions = g_list_delete_link (manager->priv->user_sessions, node);
+ gdm_session_close (session);
+ g_object_unref (session);
+ }
+}
+
+static void
+on_session_start_failed (GdmSession *session,
+ const char *service_name,
+ const char *message,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: session failed to start: %s", message);
+ remove_user_session (manager, session);
+}
+
+static void
+on_user_session_exited (GdmSession *session,
+ int code,
+ GdmManager *manager)
+{
+ GPid pid;
+
+ g_debug ("GdmManager: session exited with status %d", code);
+ pid = gdm_session_get_pid (session);
+
+ if (pid > 0) {
+ add_session_record (manager, session, pid, SESSION_RECORD_LOGOUT);
+ }
+
+ remove_user_session (manager, session);
+}
+
+static void
+on_user_session_died (GdmSession *session,
+ int signal_number,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: session died with signal %s", strsignal (signal_number));
+ remove_user_session (manager, session);
+}
+
+static char *
+get_display_device (GdmManager *manager,
+ GdmDisplay *display)
+{
+ /* systemd finds the display device out on its own based on the display */
+ return NULL;
+}
+
+static void
+on_session_reauthenticated (GdmSession *session,
+ const char *service_name,
+ GdmManager *manager)
+{
+ gboolean fail_if_already_switched = FALSE;
+
+ if (gdm_session_get_display_mode (session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) {
+ const char *seat_id;
+ char *session_id;
+
+ seat_id = gdm_session_get_display_seat_id (session);
+ if (gdm_get_login_window_session_id (seat_id, &session_id)) {
+ GdmDisplay *display = gdm_display_store_find (manager->priv->display_store,
+ lookup_by_session_id,
+ (gpointer) session_id);
+
+ if (display != NULL) {
+ gdm_display_stop_greeter_session (display);
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+ }
+ g_free (session_id);
+ }
+ }
+
+ /* There should already be a session running, so jump to its
+ * VT. In the event we're already on the right VT, (i.e. user
+ * used an unlock screen instead of a user switched login screen),
+ * then silently succeed and unlock the session.
+ */
+ switch_to_compatible_user_session (manager, session, fail_if_already_switched);
+}
+
+static void
+on_session_client_ready_for_session_to_start (GdmSession *session,
+ const char *service_name,
+ gboolean client_is_ready,
+ GdmManager *manager)
+{
+ gboolean waiting_to_start_user_session;
+
+ if (client_is_ready) {
+ g_debug ("GdmManager: Will start session when ready");
+ } else {
+ g_debug ("GdmManager: Will start session when ready and told");
+ }
+
+ waiting_to_start_user_session = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (session),
+ "waiting-to-start"));
+
+ g_object_set_data (G_OBJECT (session),
+ "start-when-ready",
+ GINT_TO_POINTER (client_is_ready));
+
+ if (client_is_ready && waiting_to_start_user_session) {
+ start_user_session_if_ready (manager, session, service_name);
+ }
+}
+
+static void
+on_session_client_connected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *manager)
+{
+ GdmDisplay *display;
+ char *username;
+ int delay;
+ gboolean enabled;
+ gboolean allow_timed_login = FALSE;
+
+ g_debug ("GdmManager: client with pid %d connected", (int) pid_of_client);
+
+ if (gdm_session_is_running (session)) {
+ const char *session_username;
+ session_username = gdm_session_get_username (session);
+ g_debug ("GdmManager: ignoring connection, since session already running (for user %s)",
+ session_username);
+ return;
+ }
+
+ display = get_display_for_user_session (session);
+
+ if (display == NULL) {
+ return;
+ }
+
+ if (!display_is_on_seat0 (display)) {
+ return;
+ }
+
+#ifdef WITH_PLYMOUTH
+ if (manager->priv->plymouth_is_running) {
+ plymouth_quit_with_transition ();
+ manager->priv->plymouth_is_running = FALSE;
+ }
+#endif
+
+ g_object_get (G_OBJECT (display), "allow-timed-login", &allow_timed_login, NULL);
+
+ if (!allow_timed_login) {
+ return;
+ }
+
+ enabled = get_timed_login_details (manager, &username, &delay);
+
+ if (! enabled) {
+ return;
+ }
+
+ gdm_session_set_timed_login_details (session, username, delay);
+
+ g_debug ("GdmManager: Starting automatic login conversation (for timed login)");
+ gdm_session_start_conversation (session, "gdm-autologin");
+
+ g_free (username);
+
+}
+
+static void
+on_session_client_disconnected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: client with pid %d disconnected", (int) pid_of_client);
+}
+
+typedef struct
+{
+ GdmManager *manager;
+ GdmSession *session;
+ guint idle_id;
+} ResetSessionOperation;
+
+static void
+destroy_reset_session_operation (ResetSessionOperation *operation)
+{
+ g_object_set_data (G_OBJECT (operation->session),
+ "reset-session-operation",
+ NULL);
+ g_object_unref (operation->session);
+ g_slice_free (ResetSessionOperation, operation);
+}
+
+static gboolean
+on_reset_session (ResetSessionOperation *operation)
+{
+ gdm_session_reset (operation->session);
+
+ destroy_reset_session_operation (operation);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+queue_session_reset (GdmManager *manager,
+ GdmSession *session)
+{
+ ResetSessionOperation *operation;
+
+ operation = g_object_get_data (G_OBJECT (session), "reset-session-operation");
+
+ if (operation != NULL) {
+ return;
+ }
+
+ operation = g_slice_new0 (ResetSessionOperation);
+ operation->manager = manager;
+ operation->session = g_object_ref (session);
+ operation->idle_id = g_idle_add ((GSourceFunc) on_reset_session, operation);
+
+ g_object_set_data (G_OBJECT (session), "reset-session-operation", operation);
+}
+
+static void
+on_session_cancelled (GdmSession *session,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: Session was cancelled");
+ queue_session_reset (manager, session);
+}
+
+static void
+on_session_conversation_started (GdmSession *session,
+ const char *service_name,
+ GdmManager *manager)
+{
+ GdmDisplay *display;
+ gboolean enabled;
+ char *username;
+
+ g_debug ("GdmManager: session conversation started for service %s on session", service_name);
+
+ if (g_strcmp0 (service_name, "gdm-autologin") != 0) {
+ g_debug ("GdmManager: ignoring session conversation since its not automatic login conversation");
+ return;
+ }
+
+ display = get_display_for_user_session (session);
+
+ if (display == NULL) {
+ g_debug ("GdmManager: conversation has no associated display");
+ return;
+ }
+
+ if (!display_is_on_seat0 (display)) {
+ return;
+ }
+
+ enabled = get_automatic_login_details (manager, &username);
+
+ if (! enabled) {
+ return;
+ }
+
+ g_debug ("GdmManager: begin auto login for user '%s'", username);
+
+ /* service_name will be "gdm-autologin"
+ */
+ gdm_session_setup_for_user (session, service_name, username);
+
+ g_free (username);
+}
+
+static void
+on_session_conversation_stopped (GdmSession *session,
+ const char *service_name,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: session conversation '%s' stopped", service_name);
+}
+
+static void
+on_session_reauthentication_started (GdmSession *session,
+ int pid_of_caller,
+ const char *address,
+ GdmManager *manager)
+{
+ GDBusMethodInvocation *invocation;
+ gpointer source_tag;
+
+ g_debug ("GdmManager: reauthentication started");
+
+ source_tag = GINT_TO_POINTER (pid_of_caller);
+
+ invocation = g_hash_table_lookup (manager->priv->open_reauthentication_requests,
+ source_tag);
+
+ if (invocation != NULL) {
+ g_hash_table_steal (manager->priv->open_reauthentication_requests,
+ source_tag);
+ gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager),
+ invocation,
+ address);
+ }
+}
+
+static void
+clean_user_session (GdmSession *session)
+{
+ g_object_set_data (G_OBJECT (session), "gdm-display", NULL);
+ g_object_unref (session);
+}
+
+static GdmSession *
+create_user_session_for_display (GdmManager *manager,
+ GdmDisplay *display,
+ uid_t allowed_user)
+{
+ GdmSession *session;
+ gboolean display_is_local = FALSE;
+ char *display_name = NULL;
+ char *display_device = NULL;
+ char *remote_hostname = NULL;
+ char *display_auth_file = NULL;
+ char *display_seat_id = NULL;
+ char *display_id = NULL;
+#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
+ g_autofree char *display_session_type = NULL;
+ gboolean greeter_is_wayland;
+#endif
+
+ g_object_get (G_OBJECT (display),
+ "id", &display_id,
+ "x11-display-name", &display_name,
+ "is-local", &display_is_local,
+ "remote-hostname", &remote_hostname,
+ "x11-authority-file", &display_auth_file,
+ "seat-id", &display_seat_id,
+#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
+ "session-type", &display_session_type,
+#endif
+ NULL);
+ display_device = get_display_device (manager, display);
+
+ session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_LOGIN,
+ allowed_user,
+ display_name,
+ remote_hostname,
+ display_device,
+ display_seat_id,
+ display_auth_file,
+ display_is_local,
+ NULL);
+
+ g_debug ("GdmSession: Created user session for user %d on display %s (seat %s)",
+ (int) allowed_user,
+ display_id,
+ display_seat_id);
+
+ g_free (display_name);
+ g_free (remote_hostname);
+ g_free (display_auth_file);
+ g_free (display_seat_id);
+
+ g_signal_connect (session,
+ "reauthentication-started",
+ G_CALLBACK (on_session_reauthentication_started),
+ manager);
+ g_signal_connect (session,
+ "reauthenticated",
+ G_CALLBACK (on_session_reauthenticated),
+ manager);
+ g_signal_connect (session,
+ "client-ready-for-session-to-start",
+ G_CALLBACK (on_session_client_ready_for_session_to_start),
+ manager);
+ g_signal_connect (session,
+ "client-connected",
+ G_CALLBACK (on_session_client_connected),
+ manager);
+ g_signal_connect (session,
+ "client-disconnected",
+ G_CALLBACK (on_session_client_disconnected),
+ manager);
+ g_signal_connect (session,
+ "cancelled",
+ G_CALLBACK (on_session_cancelled),
+ manager);
+ g_signal_connect (session,
+ "conversation-started",
+ G_CALLBACK (on_session_conversation_started),
+ manager);
+ g_signal_connect (session,
+ "conversation-stopped",
+ G_CALLBACK (on_session_conversation_stopped),
+ manager);
+ g_signal_connect (session,
+ "authentication-failed",
+ G_CALLBACK (on_session_authentication_failed),
+ manager);
+ g_signal_connect (session,
+ "session-opened",
+ G_CALLBACK (on_user_session_opened),
+ manager);
+ g_signal_connect (session,
+ "session-started",
+ G_CALLBACK (on_user_session_started),
+ manager);
+ g_signal_connect (session,
+ "session-start-failed",
+ G_CALLBACK (on_session_start_failed),
+ manager);
+ g_signal_connect (session,
+ "session-exited",
+ G_CALLBACK (on_user_session_exited),
+ manager);
+ g_signal_connect (session,
+ "session-died",
+ G_CALLBACK (on_user_session_died),
+ manager);
+ g_object_set_data (G_OBJECT (session), "gdm-display", display);
+ g_object_set_data_full (G_OBJECT (display),
+ "gdm-user-session",
+ g_object_ref (session),
+ (GDestroyNotify)
+ clean_user_session);
+
+#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
+ greeter_is_wayland = g_strcmp0 (display_session_type, "wayland") == 0;
+ g_object_set (G_OBJECT (session), "ignore-wayland", !greeter_is_wayland, NULL);
+#endif
+
+ return session;
+}
+
+static void
+on_display_added (GdmDisplayStore *display_store,
+ const char *id,
+ GdmManager *manager)
+{
+ GdmDisplay *display;
+
+ display = gdm_display_store_lookup (display_store, id);
+
+ if (display != NULL) {
+ g_dbus_object_manager_server_export (manager->priv->object_manager,
+ gdm_display_get_object_skeleton (display));
+
+ g_signal_connect (display, "notify::status",
+ G_CALLBACK (on_display_status_changed),
+ manager);
+ g_signal_emit (manager, signals[DISPLAY_ADDED], 0, id);
+ }
+}
+
+GQuark
+gdm_manager_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gdm_manager_error");
+ }
+
+ return ret;
+}
+
+static void
+listify_display_ids (const char *id,
+ GdmDisplay *display,
+ GPtrArray **array)
+{
+ g_ptr_array_add (*array, g_strdup (id));
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.gnome.DisplayManager \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/gnome/DisplayManager/Displays \
+ org.freedesktop.ObjectManager.GetAll
+*/
+gboolean
+gdm_manager_get_displays (GdmManager *manager,
+ GPtrArray **displays,
+ GError **error)
+{
+ g_return_val_if_fail (GDM_IS_MANAGER (manager), FALSE);
+
+ if (displays == NULL) {
+ return FALSE;
+ }
+
+ *displays = g_ptr_array_new ();
+ gdm_display_store_foreach (manager->priv->display_store,
+ (GdmDisplayStoreFunc)listify_display_ids,
+ displays);
+
+ return TRUE;
+}
+
+void
+gdm_manager_stop (GdmManager *manager)
+{
+ g_debug ("GdmManager: GDM stopping");
+
+ if (manager->priv->local_factory != NULL) {
+ gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->priv->local_factory));
+ }
+
+#ifdef HAVE_LIBXDMCP
+ if (manager->priv->xdmcp_factory != NULL) {
+ gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory));
+ }
+#endif
+
+ manager->priv->started = FALSE;
+}
+
+void
+gdm_manager_start (GdmManager *manager)
+{
+ g_debug ("GdmManager: GDM starting to manage displays");
+
+#ifdef WITH_PLYMOUTH
+ manager->priv->plymouth_is_running = plymouth_is_running ();
+
+ if (manager->priv->plymouth_is_running) {
+ plymouth_prepare_for_transition ();
+ }
+#endif
+ if (!manager->priv->xdmcp_enabled || manager->priv->show_local_greeter) {
+ gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->priv->local_factory));
+ }
+
+#ifdef HAVE_LIBXDMCP
+ /* Accept remote connections */
+ if (manager->priv->xdmcp_enabled) {
+#ifdef WITH_PLYMOUTH
+ /* Quit plymouth if xdmcp is the only display */
+ if (!manager->priv->show_local_greeter && manager->priv->plymouth_is_running) {
+ plymouth_quit_without_transition ();
+ manager->priv->plymouth_is_running = FALSE;
+ }
+#endif
+ if (manager->priv->xdmcp_factory != NULL) {
+ g_debug ("GdmManager: Accepting XDMCP connections...");
+ gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory));
+ }
+ }
+#endif
+
+ manager->priv->started = TRUE;
+}
+
+static gboolean
+register_manager (GdmManager *manager)
+{
+ GError *error = NULL;
+ GDBusObjectManagerServer *object_server;
+
+ error = NULL;
+ manager->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
+ NULL,
+ &error);
+ if (manager->priv->connection == NULL) {
+ g_critical ("error getting system bus: %s", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ object_server = g_dbus_object_manager_server_new (GDM_MANAGER_DISPLAYS_PATH);
+ g_dbus_object_manager_server_set_connection (object_server, manager->priv->connection);
+ manager->priv->object_manager = object_server;
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager),
+ manager->priv->connection,
+ GDM_MANAGER_PATH,
+ &error)) {
+ g_critical ("error exporting interface to %s: %s",
+ GDM_MANAGER_PATH,
+ error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ return TRUE;
+}
+
+void
+gdm_manager_set_xdmcp_enabled (GdmManager *manager,
+ gboolean enabled)
+{
+ g_return_if_fail (GDM_IS_MANAGER (manager));
+
+ if (manager->priv->xdmcp_enabled != enabled) {
+ manager->priv->xdmcp_enabled = enabled;
+#ifdef HAVE_LIBXDMCP
+ if (manager->priv->xdmcp_enabled) {
+ manager->priv->xdmcp_factory = gdm_xdmcp_display_factory_new (manager->priv->display_store);
+ if (manager->priv->started) {
+ gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory));
+ }
+ } else {
+ if (manager->priv->started) {
+ gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory));
+ }
+
+ g_object_unref (manager->priv->xdmcp_factory);
+ manager->priv->xdmcp_factory = NULL;
+ }
+#endif
+ }
+
+}
+
+void
+gdm_manager_set_show_local_greeter (GdmManager *manager,
+ gboolean show_local_greeter)
+{
+ g_return_if_fail (GDM_IS_MANAGER (manager));
+
+ manager->priv->show_local_greeter = show_local_greeter;
+}
+
+static void
+gdm_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmManager *self;
+
+ self = GDM_MANAGER (object);
+
+ switch (prop_id) {
+ case PROP_XDMCP_ENABLED:
+ gdm_manager_set_xdmcp_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_SHOW_LOCAL_GREETER:
+ gdm_manager_set_show_local_greeter (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmManager *self;
+
+ self = GDM_MANAGER (object);
+
+ switch (prop_id) {
+ case PROP_XDMCP_ENABLED:
+ g_value_set_boolean (value, self->priv->xdmcp_enabled);
+ break;
+ case PROP_SHOW_LOCAL_GREETER:
+ g_value_set_boolean (value, self->priv->show_local_greeter);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+gdm_manager_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmManager *manager;
+
+ manager = GDM_MANAGER (G_OBJECT_CLASS (gdm_manager_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ gdm_dbus_manager_set_version (GDM_DBUS_MANAGER (manager), PACKAGE_VERSION);
+
+ manager->priv->local_factory = gdm_local_display_factory_new (manager->priv->display_store);
+
+#ifdef HAVE_LIBXDMCP
+ if (manager->priv->xdmcp_enabled) {
+ manager->priv->xdmcp_factory = gdm_xdmcp_display_factory_new (manager->priv->display_store);
+ }
+#endif
+
+ return G_OBJECT (manager);
+}
+
+static void
+gdm_manager_class_init (GdmManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_manager_get_property;
+ object_class->set_property = gdm_manager_set_property;
+ object_class->constructor = gdm_manager_constructor;
+ object_class->dispose = gdm_manager_dispose;
+
+ signals [DISPLAY_ADDED] =
+ g_signal_new ("display-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmManagerClass, display_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [DISPLAY_REMOVED] =
+ g_signal_new ("display-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmManagerClass, display_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, G_TYPE_OBJECT);
+
+ g_object_class_install_property (object_class,
+ PROP_XDMCP_ENABLED,
+ g_param_spec_boolean ("xdmcp-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdm_manager_init (GdmManager *manager)
+{
+
+ manager->priv = GDM_MANAGER_GET_PRIVATE (manager);
+
+ manager->priv->display_store = gdm_display_store_new ();
+ manager->priv->user_sessions = NULL;
+ manager->priv->open_reauthentication_requests = g_hash_table_new_full (NULL,
+ NULL,
+ (GDestroyNotify)
+ NULL,
+ (GDestroyNotify)
+ g_object_unref);
+ manager->priv->transient_sessions = g_hash_table_new_full (NULL,
+ NULL,
+ (GDestroyNotify)
+ NULL,
+ (GDestroyNotify)
+ g_object_unref);
+ g_signal_connect (G_OBJECT (manager->priv->display_store),
+ "display-added",
+ G_CALLBACK (on_display_added),
+ manager);
+
+ g_signal_connect (G_OBJECT (manager->priv->display_store),
+ "display-removed",
+ G_CALLBACK (on_display_removed),
+ manager);
+}
+
+static void
+unexport_display (const char *id,
+ GdmDisplay *display,
+ GdmManager *manager)
+{
+ if (!g_dbus_connection_is_closed (manager->priv->connection))
+ g_dbus_object_manager_server_unexport (manager->priv->object_manager, id);
+}
+
+static void
+finish_display (const char *id,
+ GdmDisplay *display,
+ GdmManager *manager)
+{
+ gdm_display_stop_greeter_session (display);
+ if (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED)
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+}
+
+static void
+gdm_manager_dispose (GObject *object)
+{
+ GdmManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_MANAGER (object));
+
+ manager = GDM_MANAGER (object);
+
+ g_return_if_fail (manager->priv != NULL);
+
+ g_clear_weak_pointer (&manager->priv->automatic_login_display);
+
+#ifdef HAVE_LIBXDMCP
+ g_clear_object (&manager->priv->xdmcp_factory);
+#endif
+ g_clear_object (&manager->priv->local_factory);
+ g_clear_pointer (&manager->priv->open_reauthentication_requests,
+ g_hash_table_unref);
+ g_clear_pointer (&manager->priv->transient_sessions,
+ g_hash_table_unref);
+
+ g_list_foreach (manager->priv->user_sessions,
+ (GFunc) gdm_session_close,
+ NULL);
+ g_list_free_full (manager->priv->user_sessions, (GDestroyNotify) g_object_unref);
+ manager->priv->user_sessions = NULL;
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store),
+ G_CALLBACK (on_display_added),
+ manager);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store),
+ G_CALLBACK (on_display_removed),
+ manager);
+
+ if (!g_dbus_connection_is_closed (manager->priv->connection)) {
+ gdm_display_store_foreach (manager->priv->display_store,
+ (GdmDisplayStoreFunc)unexport_display,
+ manager);
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (manager));
+ }
+
+ gdm_display_store_foreach (manager->priv->display_store,
+ (GdmDisplayStoreFunc) finish_display,
+ manager);
+
+ gdm_display_store_clear (manager->priv->display_store);
+
+ g_dbus_object_manager_server_set_connection (manager->priv->object_manager, NULL);
+
+ g_clear_object (&manager->priv->connection);
+ g_clear_object (&manager->priv->object_manager);
+ g_clear_object (&manager->priv->display_store);
+
+ G_OBJECT_CLASS (gdm_manager_parent_class)->dispose (object);
+}
+
+GdmManager *
+gdm_manager_new (void)
+{
+ if (manager_object != NULL) {
+ g_object_ref (manager_object);
+ } else {
+ gboolean res;
+
+ manager_object = g_object_new (GDM_TYPE_MANAGER, NULL);
+ g_object_add_weak_pointer (manager_object,
+ (gpointer *) &manager_object);
+ res = register_manager (manager_object);
+ if (! res) {
+ g_object_unref (manager_object);
+ return NULL;
+ }
+ }
+
+ return GDM_MANAGER (manager_object);
+}