[gdm/wip/wayland: 9/10] daemon: support launching sessions on separate VT
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gdm/wip/wayland: 9/10] daemon: support launching sessions on separate VT
- Date: Thu, 19 Sep 2013 04:37:54 +0000 (UTC)
commit 432250fe210f566a4efaa71a1c5a9ea07d0e3b23
Author: Ray Strode <rstrode redhat com>
Date: Mon Sep 16 17:19:26 2013 -0400
daemon: support launching sessions on separate VT
If an xsession file sets X-GDM-NeedsVT to true, then GDM will
now 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 its functionality will get moved into logind.
daemon/gdm-manager.c | 22 ++++++++-
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, 193 insertions(+), 2 deletions(-)
---
diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c
index 111f4eb..74d3ca9 100644
--- a/daemon/gdm-manager.c
+++ b/daemon/gdm-manager.c
@@ -104,6 +104,9 @@ static void gdm_manager_finalize (GObject *object);
static void on_initial_session_set_up (GdmDisplay *display,
GAsyncResult *result,
GdmManager *manager);
+static void on_initial_session_started (GdmDisplay *display,
+ GAsyncResult *result,
+ GdmManager *manager);
static void create_session_for_display (GdmManager *manager,
GdmDisplay *display,
uid_t allowed_user);
@@ -963,6 +966,7 @@ on_initial_session_stopped (GdmDisplay *display,
{
GError *error = NULL;
gboolean stopped;
+ gboolean needs_vt;
stopped = gdm_display_stop_initial_session_finish (display, result, &error);
@@ -975,7 +979,23 @@ on_initial_session_stopped (GdmDisplay *display,
return;
}
- start_user_session (operation->manager, display, operation);
+ needs_vt = gdm_session_needs_vt (operation->session);
+
+ if (needs_vt) {
+ g_debug ("GdmManager: session needs own VT, reinstating login screen on this VT");
+ /* if we're going to run on a different VT, reset this display for a login screen */
+ g_object_set_data (G_OBJECT (display), "gdm-session", NULL);
+ g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL);
+ gdm_display_start_initial_session (display,
+ NULL,
+ (GAsyncReadyCallback)
+ on_initial_session_started,
+ operation->manager);
+
+ start_user_session (operation->manager, NULL, operation);
+ } else {
+ start_user_session (operation->manager, display, operation);
+ }
}
static gboolean
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]