summaryrefslogtreecommitdiff
path: root/daemon/gdm-session-worker.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/gdm-session-worker.c')
-rw-r--r--daemon/gdm-session-worker.c135
1 files changed, 131 insertions, 4 deletions
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;