summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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>