[gdm/wip/wayland: 10/10] daemon: Add new X-GDM-NeedsVT flag in xsession file



commit 3cea0b4b5cc32a9c75e94202d714e582562a329e
Author: Ray Strode <rstrode redhat com>
Date:   Mon Sep 16 17:19:26 2013 -0400

    daemon: Add new X-GDM-NeedsVT flag in xsession file
    
    If an xsession file sets X-GDM-NeedsVT to true, then GDM will
    automatically allocate a new VT for that session and jump to
    it before starting the session.  Once the session completes, the
    worker will automatically jump back to the VT it started on.
    
    This will be useful for getting mutter-launch to be able to launch
    a gnome-shell wayland sesssion.  Longer term, I think mutter-launch
    will go away and it's functionality will get moved into logind.

 daemon/gdm-manager.c          |   38 ++++++++-----
 daemon/gdm-session-worker.c   |  115 ++++++++++++++++++++++++++++++++++++++++-
 daemon/gdm-session-worker.xml |    3 +
 daemon/gdm-session.c          |   54 +++++++++++++++++++
 daemon/gdm-session.h          |    1 +
 5 files changed, 195 insertions(+), 16 deletions(-)
---
diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c
index 2b3d10b..bf15264 100644
--- a/daemon/gdm-manager.c
+++ b/daemon/gdm-manager.c
@@ -334,6 +334,7 @@ get_session_id_for_user_on_seat_systemd (const char  *username,
                         continue;
                 }
 
+                /* WAYLAND FIXME:  we can't filter on X11 sessions here unless we can make wayland show up 
as an X11 session */
                 is_x11 = g_strcmp0 (type, "x11") == 0;
                 free (type);
 
@@ -1202,24 +1203,26 @@ start_user_session (GdmManager *manager,
                     GdmDisplay *display,
                     StartUserSessionOperation *operation)
 {
-        char *auth_file;
-        char *username;
+        if (display != NULL) {
+                char *auth_file;
+                char *username;
 
-        auth_file = NULL;
-        username = gdm_session_get_username (operation->session);
-        gdm_display_add_user_authorization (display,
-                                            username,
-                                            &auth_file,
-                                            NULL);
-        g_free (username);
+                auth_file = NULL;
+                username = gdm_session_get_username (operation->session);
+                gdm_display_add_user_authorization (display,
+                                                    username,
+                                                    &auth_file,
+                                                    NULL);
+                g_free (username);
 
-        g_assert (auth_file != NULL);
+                g_assert (auth_file != NULL);
 
-        g_object_set (operation->session,
-                      "user-x11-authority-file", auth_file,
-                      NULL);
+                g_object_set (operation->session,
+                              "user-x11-authority-file", auth_file,
+                              NULL);
 
-        g_free (auth_file);
+                g_free (auth_file);
+        }
 
         gdm_session_start_session (operation->session,
                                    operation->service_name);
@@ -1250,7 +1253,8 @@ on_initial_session_stopped (GdmDisplay   *display,
 static gboolean
 on_start_user_session (StartUserSessionOperation *operation)
 {
-        gboolean migrated;
+        gboolean migrated = FALSE;
+        gboolean needs_vt;
         gboolean fail_if_already_switched = TRUE;
         char     *username;
         char     *seat_id;
@@ -1269,6 +1273,8 @@ on_start_user_session (StartUserSessionOperation *operation)
         migrated = switch_to_user_session (operation->manager, seat_id, username, fail_if_already_switched);
         g_free (seat_id);
 
+        needs_vt = gdm_session_needs_vt (operation->session);
+
         g_debug ("GdmManager: migrated: %d", migrated);
         if (migrated) {
                 /* We don't stop the manager here because
@@ -1277,6 +1283,8 @@ on_start_user_session (StartUserSessionOperation *operation)
                    user switching. */
                 gdm_session_reset (operation->session);
                 destroy_start_user_session_operation (operation);
+        } else if (needs_vt) {
+                start_user_session (operation->manager, NULL, operation);
         } else {
                 GdmDisplay *display;
 
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index 1f05c82..6b29734 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -54,6 +54,10 @@
 #include <systemd/sd-journal.h>
 #endif
 
+#include <sys/vt.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
 #ifdef HAVE_SELINUX
 #include <selinux/selinux.h>
 #endif /* HAVE_SELINUX */
@@ -152,12 +156,15 @@ struct GdmSessionWorkerPrivate
         gboolean          password_is_required;
 
         int               cred_flags;
+        int               login_vt;
+        int               session_vt;
 
         char            **arguments;
         guint32           cancelled : 1;
         guint32           timed_out : 1;
         guint32           is_program_session : 1;
         guint32           is_reauth_session : 1;
+        guint32           session_needs_vt : 1;
         guint32           display_is_local : 1;
         guint             state_change_idle_id;
 
@@ -948,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: %s",
+                         vt_number, g_strerror (errno));
+        } else if (ioctl (fd, VT_WAITACTIVE, vt_number) < 0) {
+                g_debug ("GdmSessionWorker: couldn't finalize jump to VT %d: %s",
+                         vt_number, g_strerror (errno));
+        }
+        close(fd);
+}
+
+static void
 gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker,
                                      int               status)
 {
@@ -976,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;
 }
@@ -1756,6 +1787,10 @@ gdm_session_worker_start_session (GdmSessionWorker  *worker,
 
         error_code = PAM_SUCCESS;
 
+        if (worker->priv->session_vt != worker->priv->login_vt) {
+                jump_to_vt (worker, worker->priv->session_vt);
+        }
+
         session_pid = fork ();
 
         if (session_pid < 0) {
@@ -1913,6 +1948,58 @@ 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: %s",
+                         g_strerror (errno));
+                return FALSE;
+        }
+
+        if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) {
+                g_debug ("GdmSessionWorker: couldn't get current VT: %s",
+                         g_strerror (errno));
+                goto fail;
+        }
+
+        if (ioctl(fd, VT_OPENQRY, &session_vt) < 0) {
+                g_debug ("GdmSessionWorker: couldn't open new VT: %s",
+                         g_strerror (errno));
+                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, 256, "%d", session_vt);
+        g_snprintf(tty_string, 256, "tty%d", session_vt);
+
+        gdm_session_worker_set_environment_variable (worker,
+                                                     "XDG_VTNR",
+                                                     vt_string);
+
+        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)
 {
@@ -1990,7 +2077,19 @@ 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);
+        if (worker->priv->session_needs_vt) {
+                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;
+                }
+        } else {
+                if (!set_up_for_current_vt (worker, error)) {
+                        return FALSE;
+                }
+        }
 
         flags = 0;
 
@@ -2141,6 +2240,19 @@ gdm_session_worker_handle_set_session_type (GdmDBusWorker         *object,
 }
 
 static gboolean
+gdm_session_worker_handle_set_session_needs_vt (GdmDBusWorker         *object,
+                                                GDBusMethodInvocation *invocation,
+                                                gboolean               needs_vt)
+{
+        GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+
+        g_debug ("GdmSessionWorker: session needs VT: %s", needs_vt? "yes" : "no");
+        worker->priv->session_needs_vt = needs_vt;
+        gdm_dbus_worker_complete_set_session_needs_vt (object, invocation);
+        return TRUE;
+}
+
+static gboolean
 gdm_session_worker_handle_set_language_name (GdmDBusWorker         *object,
                                              GDBusMethodInvocation *invocation,
                                              const char            *language_name)
@@ -2923,6 +3035,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_needs_vt = gdm_session_worker_handle_set_session_needs_vt;
         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..98b2fdb 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="SetSessionNeedsVT">
+      <arg name="needs_vt" direction="in" type="b"/>
+    </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 f8f0967..17b5bf9 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -2725,6 +2725,54 @@ out:
         return bypasses_xsession;
 }
 
+gboolean
+gdm_session_needs_vt (GdmSession *self)
+{
+        GError     *error;
+        GKeyFile   *key_file;
+        gboolean    res;
+        gboolean    needs_vt = FALSE;
+        char       *filename;
+
+        g_return_val_if_fail (self != NULL, FALSE);
+        g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+        filename = g_strdup_printf ("%s.desktop", get_session_name (self));
+
+        key_file = g_key_file_new ();
+        error = NULL;
+        res = g_key_file_load_from_dirs (key_file,
+                                         filename,
+                                         get_system_session_dirs (),
+                                         NULL,
+                                         G_KEY_FILE_NONE,
+                                         &error);
+        if (! res) {
+                g_debug ("GdmSession: File '%s' not found: %s", filename, error->message);
+                goto out;
+        }
+
+        error = NULL;
+        res = g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-NeedsVT", NULL);
+        if (!res) {
+                goto out;
+        } else {
+                needs_vt = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-NeedsVT", 
&error);
+                if (error) {
+                        needs_vt = FALSE;
+                        g_error_free (error);
+                        goto out;
+                }
+                if (needs_vt) {
+                        g_debug ("GdmSession: Session %s runs on its own VT", filename);
+                }
+        }
+
+out:
+        g_free (filename);
+        return needs_vt;
+}
+
 void
 gdm_session_select_program (GdmSession *self,
                             const char *text)
@@ -2760,6 +2808,7 @@ gdm_session_select_session (GdmSession *self,
 {
         GHashTableIter iter;
         gpointer key, value;
+        gboolean needs_vt;
 
         g_free (self->priv->selected_session);
 
@@ -2769,6 +2818,8 @@ gdm_session_select_session (GdmSession *self,
                 self->priv->selected_session = g_strdup (text);
         }
 
+        needs_vt = gdm_session_needs_vt (self);
+
         g_hash_table_iter_init (&iter, self->priv->conversations);
         while (g_hash_table_iter_next (&iter, &key, &value)) {
                 GdmSessionConversation *conversation;
@@ -2778,6 +2829,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_needs_vt (conversation->worker_proxy,
+                                                           needs_vt,
+                                                           NULL, NULL, NULL);
         }
 }
 
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index 4faf5fc..ccdbca0 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -110,6 +110,7 @@ char             *gdm_session_get_display_device          (GdmSession     *sessi
 char             *gdm_session_get_display_seat_id         (GdmSession     *session);
 char             *gdm_session_get_session_id              (GdmSession     *session);
 gboolean          gdm_session_bypasses_xsession           (GdmSession     *session);
+gboolean          gdm_session_needs_vt                    (GdmSession     *session);
 
 void              gdm_session_start_conversation          (GdmSession *session,
                                                            const char *service_name);


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