summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2013-09-16 17:19:26 -0400
committerRay Strode <rstrode@redhat.com>2013-12-05 10:41:08 -0500
commit899ff9504a9c63953d9a482d511273333fc091c0 (patch)
tree995ac99d4c84d892014d8a50cb05250f4e8f9ec6
parent11014c4e11aa7628c16cd4a3c76117e75a182a98 (diff)
downloadgdm-wip/wayland.tar.gz
daemon: support launching sessions on separate VTwip/wayland
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.
-rw-r--r--daemon/gdm-display.c47
-rw-r--r--daemon/gdm-display.h8
-rw-r--r--daemon/gdm-manager.c58
-rw-r--r--daemon/gdm-session-worker.c135
-rw-r--r--daemon/gdm-session-worker.xml3
-rw-r--r--daemon/gdm-session.c58
-rw-r--r--daemon/gdm-session.h1
-rw-r--r--daemon/gdm-simple-slave.c11
-rw-r--r--daemon/gdm-slave.c27
-rw-r--r--daemon/gdm-slave.h1
-rw-r--r--daemon/gdm-slave.xml3
11 files changed, 348 insertions, 4 deletions
diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c
index 0d719c1c..950ab1f9 100644
--- a/daemon/gdm-display.c
+++ b/daemon/gdm-display.c
@@ -1690,6 +1690,53 @@ gdm_display_start_initial_session_finish (GdmDisplay *display,
}
static void
+on_slave_initial_session_reset (GdmDBusSlave *slave,
+ GAsyncResult *result,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!gdm_dbus_slave_call_reset_initial_session_finish (slave, result, &error)) {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_debug ("GdmDisplay: slave reset initial session");
+
+ g_task_return_boolean (task, TRUE);
+}
+
+void
+gdm_display_reset_initial_session (GdmDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (display, cancellable, callback, user_data);
+
+ gdm_dbus_slave_call_reset_initial_session (display->priv->slave_proxy,
+ cancellable,
+ (GAsyncReadyCallback)
+ on_slave_initial_session_reset,
+ task);
+}
+
+gboolean
+gdm_display_reset_initial_session_finish (GdmDisplay *display,
+ GAsyncResult *result,
+ GError **error)
+{
+ gboolean outcome;
+
+ outcome = g_task_propagate_boolean (G_TASK (result), error);
+ g_object_unref (G_OBJECT (result));
+
+ return outcome;
+}
+
+static void
on_slave_initial_session_stopped (GdmDBusSlave *slave,
GAsyncResult *result,
GTask *task)
diff --git a/daemon/gdm-display.h b/daemon/gdm-display.h
index 2ea091d3..a29eabb3 100644
--- a/daemon/gdm-display.h
+++ b/daemon/gdm-display.h
@@ -162,6 +162,14 @@ void gdm_display_start_initial_session (GdmDisplay *dis
gboolean gdm_display_start_initial_session_finish (GdmDisplay *display,
GAsyncResult *result,
GError **error);
+void gdm_display_reset_initial_session (GdmDisplay *display,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean gdm_display_reset_initial_session_finish (GdmDisplay *display,
+ GAsyncResult *result,
+ GError **error);
void gdm_display_stop_initial_session (GdmDisplay *display,
const char *username,
GCancellable *cancellable,
diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c
index e77e740e..a2b4c9c2 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);
@@ -957,6 +960,27 @@ start_user_session (GdmManager *manager,
}
static void
+reset_session_on_display (GdmManager *manager,
+ GdmDisplay *display)
+{
+ GdmSession *session;
+ uid_t allowed_uid;
+
+ session = get_session_for_display (manager, display);
+
+ if (session == NULL) {
+ return;
+ }
+
+ allowed_uid = gdm_session_get_allowed_user (session);
+
+ g_object_set_data (G_OBJECT (display), "gdm-session", NULL);
+ g_object_set_data (G_OBJECT (session), "gdm-display", NULL);
+
+ create_session_for_display (manager, display, allowed_uid);
+}
+
+static void
on_initial_session_stopped (GdmDisplay *display,
GAsyncResult *result,
StartUserSessionOperation *operation)
@@ -978,11 +1002,31 @@ on_initial_session_stopped (GdmDisplay *display,
start_user_session (operation->manager, display, operation);
}
+static void
+on_initial_session_reset (GdmDisplay *display,
+ GAsyncResult *result)
+{
+ GError *error = NULL;
+ gboolean reset;
+
+ reset = gdm_display_reset_initial_session_finish (display, result, &error);
+
+ if (!reset) {
+ g_warning ("Couldn't reset initial session on display: %s",
+ error->message);
+ g_error_free (error);
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+ return;
+ }
+}
+
static gboolean
on_start_user_session (StartUserSessionOperation *operation)
{
gboolean migrated;
gboolean fail_if_already_switched = TRUE;
+ gboolean needs_vt;
g_debug ("GdmManager: start or jump to session");
@@ -991,6 +1035,7 @@ on_start_user_session (StartUserSessionOperation *operation)
* start a session on it.
*/
migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched);
+ needs_vt = gdm_session_needs_vt (operation->session);
g_debug ("GdmManager: migrated: %d", migrated);
if (migrated) {
@@ -1000,6 +1045,19 @@ on_start_user_session (StartUserSessionOperation *operation)
user switching. */
gdm_session_reset (operation->session);
destroy_start_user_session_operation (operation);
+ } else if (needs_vt) {
+ GdmDisplay *display;
+
+ 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 */
+ display = get_display_for_session (operation->manager, operation->session);
+ reset_session_on_display (operation->manager, display);
+ gdm_display_reset_initial_session (display,
+ NULL,
+ (GAsyncReadyCallback)
+ on_initial_session_reset,
+ NULL);
+ start_user_session (operation->manager, NULL, operation);
} else {
GdmDisplay *display;
char *username;
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index 1f05c829..515ceb36 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,16 @@ struct GdmSessionWorkerPrivate
gboolean password_is_required;
int cred_flags;
+ int login_vt;
+ int session_vt;
+ int session_tty_fd;
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 +956,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 +1001,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 +1788,11 @@ gdm_session_worker_start_session (GdmSessionWorker *worker,
error_code = PAM_SUCCESS;
+ if (worker->priv->session_vt != worker->priv->login_vt) {
+ gdm_session_worker_set_environment_variable (worker, "DISPLAY", NULL);
+ jump_to_vt (worker, worker->priv->session_vt);
+ }
+
session_pid = fork ();
if (session_pid < 0) {
@@ -1773,9 +1810,15 @@ 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);
+ 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;
@@ -1886,6 +1929,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
*/
@@ -1913,6 +1961,59 @@ 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, "/dev/tty%d", session_vt);
+
+ gdm_session_worker_set_environment_variable (worker,
+ "XDG_VTNR",
+ vt_string);
+
+ 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)
{
@@ -1990,7 +2091,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 +2254,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 +3049,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 4595ac7e..98b2fdb1 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 f8f09670..41ed3f90 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -2689,6 +2689,10 @@ gdm_session_bypasses_xsession (GdmSession *self)
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+ if (gdm_session_needs_vt (self)) {
+ return TRUE;
+ }
+
filename = g_strdup_printf ("%s.desktop", get_session_name (self));
key_file = g_key_file_new ();
@@ -2725,6 +2729,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 +2812,7 @@ gdm_session_select_session (GdmSession *self,
{
GHashTableIter iter;
gpointer key, value;
+ gboolean needs_vt;
g_free (self->priv->selected_session);
@@ -2769,6 +2822,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 +2833,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 4faf5fc4..ccdbca09 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);
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 7e336a7b..4a2531bb 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -762,6 +762,16 @@ gdm_simple_slave_start_initial_session (GdmSlave *slave)
}
}
+static void
+gdm_simple_slave_reset_initial_session (GdmSlave *slave)
+{
+ GdmSimpleSlave *self = GDM_SIMPLE_SLAVE (slave);
+
+ if (self->priv->greeter_environment != NULL) {
+ gdm_launch_environment_reset (GDM_LAUNCH_ENVIRONMENT (self->priv->greeter_environment));
+ }
+}
+
static gboolean
idle_connect_to_display (GdmSimpleSlave *slave)
{
@@ -1014,6 +1024,7 @@ gdm_simple_slave_class_init (GdmSimpleSlaveClass *klass)
slave_class->stop = gdm_simple_slave_stop;
slave_class->set_up_initial_session = gdm_simple_slave_set_up_initial_session;
slave_class->start_initial_session = gdm_simple_slave_start_initial_session;
+ slave_class->reset_initial_session = gdm_simple_slave_reset_initial_session;
slave_class->stop_initial_session = gdm_simple_slave_stop_initial_session;
g_type_class_add_private (klass, sizeof (GdmSimpleSlavePrivate));
diff --git a/daemon/gdm-slave.c b/daemon/gdm-slave.c
index f7934839..69b98bb3 100644
--- a/daemon/gdm-slave.c
+++ b/daemon/gdm-slave.c
@@ -1283,6 +1283,29 @@ handle_start_initial_session (GdmDBusSlave *skeleton,
}
static gboolean
+handle_reset_initial_session (GdmDBusSlave *skeleton,
+ GDBusMethodInvocation *invocation,
+ GdmSlave *slave)
+{
+ GdmSlaveClass *slave_class;
+
+ slave_class = GDM_SLAVE_GET_CLASS (slave);
+ if (slave_class->reset_initial_session == NULL) {
+ g_dbus_method_invocation_return_dbus_error (invocation,
+ "org.gnome.DisplayManager.Slave.Unsupported",
+ "Connections to the slave are not supported by this slave");
+ return TRUE;
+ }
+
+ slave_class->reset_initial_session (slave);
+
+ gdm_dbus_slave_complete_reset_initial_session (skeleton, invocation);
+
+ return TRUE;
+}
+
+
+static gboolean
handle_stop_initial_session (GdmDBusSlave *skeleton,
GDBusMethodInvocation *invocation,
const char *username,
@@ -1331,6 +1354,10 @@ register_slave (GdmSlave *slave)
G_CALLBACK (handle_start_initial_session),
slave);
g_signal_connect (slave->priv->skeleton,
+ "handle-reset-initial-session",
+ G_CALLBACK (handle_reset_initial_session),
+ slave);
+ g_signal_connect (slave->priv->skeleton,
"handle-stop-initial-session",
G_CALLBACK (handle_stop_initial_session),
slave);
diff --git a/daemon/gdm-slave.h b/daemon/gdm-slave.h
index e3264f86..14c9ff7d 100644
--- a/daemon/gdm-slave.h
+++ b/daemon/gdm-slave.h
@@ -53,6 +53,7 @@ typedef struct
void (* set_up_initial_session) (GdmSlave *slave,
char **username);
void (* start_initial_session) (GdmSlave *slave);
+ void (* reset_initial_session) (GdmSlave *slave);
void (* stop_initial_session) (GdmSlave *slave,
const char *username);
diff --git a/daemon/gdm-slave.xml b/daemon/gdm-slave.xml
index 3f563b43..6e9e2d18 100644
--- a/daemon/gdm-slave.xml
+++ b/daemon/gdm-slave.xml
@@ -11,6 +11,9 @@
<method name="StartInitialSession">
</method>
+ <method name="ResetInitialSession">
+ </method>
+
<method name="StopInitialSession">
<arg name="username" type="s" direction="in" />
</method>