diff options
-rw-r--r-- | daemon/gdm-display.c | 47 | ||||
-rw-r--r-- | daemon/gdm-display.h | 8 | ||||
-rw-r--r-- | daemon/gdm-manager.c | 58 | ||||
-rw-r--r-- | daemon/gdm-session-worker.c | 135 | ||||
-rw-r--r-- | daemon/gdm-session-worker.xml | 3 | ||||
-rw-r--r-- | daemon/gdm-session.c | 58 | ||||
-rw-r--r-- | daemon/gdm-session.h | 1 | ||||
-rw-r--r-- | daemon/gdm-simple-slave.c | 11 | ||||
-rw-r--r-- | daemon/gdm-slave.c | 27 | ||||
-rw-r--r-- | daemon/gdm-slave.h | 1 | ||||
-rw-r--r-- | daemon/gdm-slave.xml | 3 |
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> |