From 505b8178e8208aa58b90c898d2f223b487b688cf Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Wed, 12 Feb 2014 18:35:19 -0500 Subject: session-worker: Implement support for the different display server modes https://bugzilla.gnome.org/show_bug.cgi?id=726380 --- daemon/gdm-session-worker.c | 141 ++++++++++++++++++++++++++++++++++++++++-- daemon/gdm-session-worker.xml | 3 + daemon/gdm-session.c | 38 ++++++++++++ daemon/gdm-session.h | 3 + 4 files changed, 181 insertions(+), 4 deletions(-) diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c index 8c1ea778..88f8439f 100644 --- a/daemon/gdm-session-worker.c +++ b/daemon/gdm-session-worker.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -152,6 +154,9 @@ struct GdmSessionWorkerPrivate gboolean password_is_required; int cred_flags; + int login_vt; + int session_vt; + int session_tty_fd; char **arguments; guint32 cancelled : 1; @@ -160,6 +165,7 @@ struct GdmSessionWorkerPrivate guint32 is_reauth_session : 1; guint32 display_is_local : 1; guint state_change_idle_id; + GdmSessionDisplayMode display_mode; char *server_address; GDBusConnection *connection; @@ -948,6 +954,23 @@ gdm_session_worker_stop_auditor (GdmSessionWorker *worker) worker->priv->auditor = NULL; } +static void +jump_to_vt (GdmSessionWorker *worker, + int vt_number) +{ + int fd; + + fd = open ("/dev/tty0", O_RDWR | O_NOCTTY); + if (ioctl (fd, VT_ACTIVATE, vt_number) < 0) { + g_debug ("GdmSessionWorker: couldn't initiate jump to VT %d: %m", + vt_number); + } else if (ioctl (fd, VT_WAITACTIVE, vt_number) < 0) { + g_debug ("GdmSessionWorker: couldn't finalize jump to VT %d: %m", + vt_number); + } + close(fd); +} + static void gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker, int status) @@ -977,6 +1000,13 @@ gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker, gdm_session_worker_stop_auditor (worker); + if (worker->priv->login_vt != worker->priv->session_vt) { + jump_to_vt (worker, worker->priv->login_vt); + } + + worker->priv->login_vt = 0; + worker->priv->session_vt = 0; + g_debug ("GdmSessionWorker: state NONE"); worker->priv->state = GDM_SESSION_WORKER_STATE_NONE; } @@ -1770,6 +1800,13 @@ gdm_session_worker_start_session (GdmSessionWorker *worker, error_code = PAM_SUCCESS; + /* If we're in new vt mode, jump to the new vt now. There's no need to jump for + * the other two modes: in the logind case, the session will activate itself when + * ready, and in the reuse server case, we're already on the correct VT. */ + if (worker->priv->display_mode == GDM_SESSION_DISPLAY_MODE_NEW_VT) { + jump_to_vt (worker, worker->priv->session_vt); + } + session_pid = fork (); if (session_pid < 0) { @@ -1787,9 +1824,17 @@ gdm_session_worker_start_session (GdmSessionWorker *worker, int stdin_fd = -1, stdout_fd = -1, stderr_fd = -1; gboolean has_journald = FALSE; - stdin_fd = open ("/dev/null", O_RDWR); - dup2 (stdin_fd, STDIN_FILENO); - close (stdin_fd); + /* Leak the TTY into the session as stdin so that it stays open + * without any races. */ + if (worker->priv->session_tty_fd > 0) { + dup2 (worker->priv->session_tty_fd, STDIN_FILENO); + close (worker->priv->session_tty_fd); + worker->priv->session_tty_fd = -1; + } else { + stdin_fd = open ("/dev/null", O_RDWR); + dup2 (stdin_fd, STDIN_FILENO); + close (stdin_fd); + } #ifdef ENABLE_SYSTEMD_JOURNAL has_journald = sd_booted() > 0; @@ -1900,6 +1945,11 @@ gdm_session_worker_start_session (GdmSessionWorker *worker, _exit (127); } + if (worker->priv->session_tty_fd > 0) { + close (worker->priv->session_tty_fd); + worker->priv->session_tty_fd = -1; + } + /* If we end up execing again, make sure we don't use the executable context set up * by pam_selinux durin pam_open_session */ @@ -1926,6 +1976,60 @@ gdm_session_worker_start_session (GdmSessionWorker *worker, return TRUE; } +static gboolean +set_up_for_new_vt (GdmSessionWorker *worker) +{ + int fd; + char vt_string[256], tty_string[256]; + struct vt_stat vt_state = { 0 }; + int session_vt = 0; + + fd = open ("/dev/tty0", O_RDWR | O_NOCTTY); + + if (fd < 0) { + g_debug ("GdmSessionWorker: couldn't open VT master: %m"); + return FALSE; + } + + if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) { + g_debug ("GdmSessionWorker: couldn't get current VT: %m"); + goto fail; + } + + if (ioctl(fd, VT_OPENQRY, &session_vt) < 0) { + g_debug ("GdmSessionWorker: couldn't open new VT: %m"); + goto fail; + } + + worker->priv->login_vt = vt_state.v_active; + worker->priv->session_vt = session_vt; + + close (fd); + fd = -1; + + g_assert (session_vt > 0); + + g_snprintf (vt_string, sizeof (vt_string), "%d", session_vt); + + /* Set the VTNR. This is used by logind to configure a session in + * the logind-managed case, but it doesn't hurt to set it always. + * When logind gains support for XDG_VTNR=auto, we can make the + * OPENQRY and this whole path only used by the new VT code. */ + gdm_session_worker_set_environment_variable (worker, + "XDG_VTNR", + vt_string); + + g_snprintf (tty_string, 256, "/dev/tty%d", session_vt); + worker->priv->session_tty_fd = open (tty_string, O_RDWR | O_NOCTTY); + pam_set_item (worker->priv->pam_handle, PAM_TTY, tty_string); + + return TRUE; + +fail: + close (fd); + return FALSE; +} + static gboolean set_up_for_current_vt (GdmSessionWorker *worker, GError **error) @@ -2004,7 +2108,23 @@ gdm_session_worker_open_session (GdmSessionWorker *worker, g_assert (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED); g_assert (geteuid () == 0); - set_up_for_current_vt (worker, NULL); + switch (worker->priv->display_mode) { + case GDM_SESSION_DISPLAY_MODE_REUSE_VT: + if (!set_up_for_current_vt (worker, error)) { + return FALSE; + } + break; + case GDM_SESSION_DISPLAY_MODE_NEW_VT: + case GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED: + if (!set_up_for_new_vt (worker)) { + g_set_error (error, + GDM_SESSION_WORKER_ERROR, + GDM_SESSION_WORKER_ERROR_OPENING_SESSION, + "Unable to open VT"); + return FALSE; + } + break; + } flags = 0; @@ -2170,6 +2290,18 @@ gdm_session_worker_handle_set_session_type (GdmDBusWorker *object, return TRUE; } +static gboolean +gdm_session_worker_handle_set_session_display_mode (GdmDBusWorker *object, + GDBusMethodInvocation *invocation, + const char *str) +{ + GdmSessionWorker *worker = GDM_SESSION_WORKER (object); + g_debug ("GdmSessionWorker: session display mode set to %s", str); + worker->priv->display_mode = gdm_session_display_mode_from_string (str); + gdm_dbus_worker_complete_set_session_display_mode (object, invocation); + return TRUE; +} + static gboolean gdm_session_worker_handle_set_language_name (GdmDBusWorker *object, GDBusMethodInvocation *invocation, @@ -2952,6 +3084,7 @@ worker_interface_init (GdmDBusWorkerIface *interface) interface->handle_set_language_name = gdm_session_worker_handle_set_language_name; interface->handle_set_session_name = gdm_session_worker_handle_set_session_name; interface->handle_set_session_type = gdm_session_worker_handle_set_session_type; + interface->handle_set_session_display_mode = gdm_session_worker_handle_set_session_display_mode; interface->handle_set_environment_variable = gdm_session_worker_handle_set_environment_variable; interface->handle_start_program = gdm_session_worker_handle_start_program; interface->handle_start_reauthentication = gdm_session_worker_handle_start_reauthentication; diff --git a/daemon/gdm-session-worker.xml b/daemon/gdm-session-worker.xml index 4595ac7e..afd724da 100644 --- a/daemon/gdm-session-worker.xml +++ b/daemon/gdm-session-worker.xml @@ -16,6 +16,9 @@ + + + diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c index 85fbf0d1..989981b0 100644 --- a/daemon/gdm-session.c +++ b/daemon/gdm-session.c @@ -2734,6 +2734,7 @@ gdm_session_select_session (GdmSession *self, { GHashTableIter iter; gpointer key, value; + GdmSessionDisplayMode mode; g_free (self->priv->selected_session); @@ -2743,6 +2744,8 @@ gdm_session_select_session (GdmSession *self, self->priv->selected_session = g_strdup (text); } + mode = gdm_session_get_display_mode (self); + g_hash_table_iter_init (&iter, self->priv->conversations); while (g_hash_table_iter_next (&iter, &key, &value)) { GdmSessionConversation *conversation; @@ -2752,6 +2755,9 @@ gdm_session_select_session (GdmSession *self, gdm_dbus_worker_call_set_session_name (conversation->worker_proxy, get_session_name (self), NULL, NULL, NULL); + gdm_dbus_worker_call_set_session_display_mode (conversation->worker_proxy, + gdm_session_display_mode_to_string (mode), + NULL, NULL, NULL); } } @@ -3327,3 +3333,35 @@ gdm_session_new (GdmSessionVerificationMode verification_mode, 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 ""; +} diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h index 1d2ee7e6..8bc8a576 100644 --- a/daemon/gdm-session.h +++ b/daemon/gdm-session.h @@ -59,6 +59,9 @@ typedef enum { GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED, } GdmSessionDisplayMode; +GdmSessionDisplayMode gdm_session_display_mode_from_string (const char *str); +const char * gdm_session_display_mode_to_string (GdmSessionDisplayMode mode); + typedef struct { GObject parent; -- cgit v1.2.1