[gdm] session-worker: Implement support for the different display server modes



commit 505b8178e8208aa58b90c898d2f223b487b688cf
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Wed Feb 12 18:35:19 2014 -0500

    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 8c1ea77..88f8439 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -28,6 +28,8 @@
 #include <string.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/vt.h>
 #include <errno.h>
 #include <grp.h>
 #include <pwd.h>
@@ -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;
@@ -949,6 +955,23 @@ gdm_session_worker_stop_auditor (GdmSessionWorker *worker)
 }
 
 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
          */
@@ -1927,6 +1977,60 @@ gdm_session_worker_start_session (GdmSessionWorker  *worker,
 }
 
 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;
 
@@ -2171,6 +2291,18 @@ gdm_session_worker_handle_set_session_type (GdmDBusWorker         *object,
 }
 
 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,
                                              const char            *language_name)
@@ -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 4595ac7..afd724d 100644
--- a/daemon/gdm-session-worker.xml
+++ b/daemon/gdm-session-worker.xml
@@ -16,6 +16,9 @@
     <method name="SetSessionType">
       <arg name="session_type" direction="in" type="s"/>
     </method>
+    <method name="SetSessionDisplayMode">
+      <arg name="mode" direction="in" type="s"/>
+    </method>
     <method name="SetEnvironmentVariable">
       <arg name="name" direction="in" type="s"/>
       <arg name="value" direction="in" type="s"/>
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 85fbf0d..989981b 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 1d2ee7e..8bc8a57 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;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]