diff --git a/SOURCES/0001-data-add-system-dconf-databases-to-gdm-profile.patch b/SOURCES/0001-data-add-system-dconf-databases-to-gdm-profile.patch new file mode 100644 index 0000000..5ae16e2 --- /dev/null +++ b/SOURCES/0001-data-add-system-dconf-databases-to-gdm-profile.patch @@ -0,0 +1,24 @@ +From 53c549876bcb7363d1d6dca70f2e1d3889741a06 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 31 Jul 2013 17:32:55 -0400 +Subject: [PATCH] data: add system dconf databases to gdm profile + +This way system settings can affect the login screen. +--- + data/dconf/gdm.in | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/data/dconf/gdm.in b/data/dconf/gdm.in +index 4d8bf1748..606e0b863 100644 +--- a/data/dconf/gdm.in ++++ b/data/dconf/gdm.in +@@ -1,2 +1,6 @@ + user-db:user ++system-db:gdm ++system-db:local ++system-db:site ++system-db:distro + file-db:@DATADIR@/@PACKAGE@/greeter-dconf-defaults +-- +2.26.0 + diff --git a/SOURCES/0001-display-factory-avoid-removing-a-display-from-store-.patch b/SOURCES/0001-display-factory-avoid-removing-a-display-from-store-.patch new file mode 100644 index 0000000..3176b34 --- /dev/null +++ b/SOURCES/0001-display-factory-avoid-removing-a-display-from-store-.patch @@ -0,0 +1,433 @@ +From 25721ebd70cd8022b0acd95f1c7494ac2ef8b2d8 Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Tue, 17 Jul 2018 20:20:55 +0000 +Subject: [PATCH 01/48] display-factory: avoid removing a display from store + while iterating it + +--- + daemon/gdm-display-factory.c | 41 ++++++++++++++++++++++++++++++ + daemon/gdm-display-factory.h | 1 + + daemon/gdm-local-display-factory.c | 7 ++--- + daemon/gdm-xdmcp-display-factory.c | 7 ++--- + 4 files changed, 46 insertions(+), 10 deletions(-) + +diff --git a/daemon/gdm-display-factory.c b/daemon/gdm-display-factory.c +index d86a4c8ad..c520e1088 100644 +--- a/daemon/gdm-display-factory.c ++++ b/daemon/gdm-display-factory.c +@@ -8,84 +8,120 @@ + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + #include "config.h" + + #include + #include + + #include + #include + #include + + #include "gdm-display-factory.h" + #include "gdm-display-store.h" + + #define GDM_DISPLAY_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DISPLAY_FACTORY, GdmDisplayFactoryPrivate)) + + struct GdmDisplayFactoryPrivate + { + GdmDisplayStore *display_store; ++ guint purge_displays_id; + }; + + enum { + PROP_0, + PROP_DISPLAY_STORE, + }; + + static void gdm_display_factory_class_init (GdmDisplayFactoryClass *klass); + static void gdm_display_factory_init (GdmDisplayFactory *factory); + static void gdm_display_factory_finalize (GObject *object); + + G_DEFINE_ABSTRACT_TYPE (GdmDisplayFactory, gdm_display_factory, G_TYPE_OBJECT) + + GQuark + gdm_display_factory_error_quark (void) + { + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_display_factory_error"); + } + + return ret; + } + ++static gboolean ++purge_display (char *id, ++ GdmDisplay *display, ++ gpointer user_data) ++{ ++ int status; ++ ++ status = gdm_display_get_status (display); ++ ++ switch (status) { ++ case GDM_DISPLAY_FINISHED: ++ case GDM_DISPLAY_FAILED: ++ return TRUE; ++ default: ++ return FALSE; ++ } ++} ++ ++static void ++purge_displays (GdmDisplayFactory *factory) ++{ ++ factory->priv->purge_displays_id = 0; ++ gdm_display_store_foreach_remove (factory->priv->display_store, ++ (GdmDisplayStoreFunc)purge_display, ++ NULL); ++} ++ ++void ++gdm_display_factory_queue_purge_displays (GdmDisplayFactory *factory) ++{ ++ if (factory->priv->purge_displays_id == 0) { ++ factory->priv->purge_displays_id = g_idle_add ((GSourceFunc) purge_displays, factory); ++ } ++} ++ + GdmDisplayStore * + gdm_display_factory_get_display_store (GdmDisplayFactory *factory) + { + g_return_val_if_fail (GDM_IS_DISPLAY_FACTORY (factory), NULL); + + return factory->priv->display_store; + } + + gboolean + gdm_display_factory_start (GdmDisplayFactory *factory) + { + gboolean ret; + + g_return_val_if_fail (GDM_IS_DISPLAY_FACTORY (factory), FALSE); + + g_object_ref (factory); + ret = GDM_DISPLAY_FACTORY_GET_CLASS (factory)->start (factory); + g_object_unref (factory); + + return ret; + } + + gboolean + gdm_display_factory_stop (GdmDisplayFactory *factory) + { + gboolean ret; + + g_return_val_if_fail (GDM_IS_DISPLAY_FACTORY (factory), FALSE); + + g_object_ref (factory); +@@ -160,32 +196,37 @@ gdm_display_factory_class_init (GdmDisplayFactoryClass *klass) + + g_object_class_install_property (object_class, + PROP_DISPLAY_STORE, + g_param_spec_object ("display-store", + "display store", + "display store", + GDM_TYPE_DISPLAY_STORE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (klass, sizeof (GdmDisplayFactoryPrivate)); + } + + static void + gdm_display_factory_init (GdmDisplayFactory *factory) + { + factory->priv = GDM_DISPLAY_FACTORY_GET_PRIVATE (factory); + } + + static void + gdm_display_factory_finalize (GObject *object) + { + GdmDisplayFactory *factory; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_DISPLAY_FACTORY (object)); + + factory = GDM_DISPLAY_FACTORY (object); + + g_return_if_fail (factory->priv != NULL); + ++ if (factory->priv->purge_displays_id != 0) { ++ g_source_remove (factory->priv->purge_displays_id); ++ factory->priv->purge_displays_id = 0; ++ } ++ + G_OBJECT_CLASS (gdm_display_factory_parent_class)->finalize (object); + } +diff --git a/daemon/gdm-display-factory.h b/daemon/gdm-display-factory.h +index 6b30f83dc..1cffa1bd5 100644 +--- a/daemon/gdm-display-factory.h ++++ b/daemon/gdm-display-factory.h +@@ -37,34 +37,35 @@ G_BEGIN_DECLS + + typedef struct GdmDisplayFactoryPrivate GdmDisplayFactoryPrivate; + + typedef struct + { + GObject parent; + GdmDisplayFactoryPrivate *priv; + } GdmDisplayFactory; + + typedef struct + { + GObjectClass parent_class; + + gboolean (*start) (GdmDisplayFactory *factory); + gboolean (*stop) (GdmDisplayFactory *factory); + } GdmDisplayFactoryClass; + + typedef enum + { + GDM_DISPLAY_FACTORY_ERROR_GENERAL + } GdmDisplayFactoryError; + + #define GDM_DISPLAY_FACTORY_ERROR gdm_display_factory_error_quark () + + GQuark gdm_display_factory_error_quark (void); + GType gdm_display_factory_get_type (void); + + gboolean gdm_display_factory_start (GdmDisplayFactory *manager); + gboolean gdm_display_factory_stop (GdmDisplayFactory *manager); + GdmDisplayStore * gdm_display_factory_get_display_store (GdmDisplayFactory *manager); ++void gdm_display_factory_queue_purge_displays (GdmDisplayFactory *manager); + + G_END_DECLS + + #endif /* __GDM_DISPLAY_FACTORY_H */ +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index ab7e12e91..1a9196ee1 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -222,107 +222,104 @@ gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *fact + "seat-id", "seat0", + "allow-timed-login", FALSE, + NULL); + + store_display (factory, display); + + if (! gdm_display_manage (display)) { + display = NULL; + goto out; + } + + if (! gdm_display_get_id (display, id, NULL)) { + display = NULL; + goto out; + } + + ret = TRUE; + out: + /* ref either held by store or not at all */ + g_object_unref (display); + + return ret; + } + + static void + on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmLocalDisplayFactory *factory) + { + int status; +- GdmDisplayStore *store; + int num; + char *seat_id = NULL; + char *session_type = NULL; + gboolean is_initial = TRUE; + gboolean is_local = TRUE; + + num = -1; + gdm_display_get_x11_display_number (display, &num, NULL); + +- store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); +- + g_object_get (display, + "seat-id", &seat_id, + "is-initial", &is_initial, + "is-local", &is_local, + "session-type", &session_type, + NULL); + + status = gdm_display_get_status (display); + + g_debug ("GdmLocalDisplayFactory: display status changed: %d", status); + switch (status) { + case GDM_DISPLAY_FINISHED: + /* remove the display number from factory->priv->used_display_numbers + so that it may be reused */ + if (num != -1) { + g_hash_table_remove (factory->priv->used_display_numbers, GUINT_TO_POINTER (num)); + } +- gdm_display_store_remove (store, display); ++ gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory)); + + /* if this is a local display, do a full resync. Only + * seats without displays will get created anyway. This + * ensures we get a new login screen when the user logs out, + * if there isn't one. + */ + if (is_local) { + /* reset num failures */ + factory->priv->num_failures = 0; + + gdm_local_display_factory_sync_seats (factory); + } + break; + case GDM_DISPLAY_FAILED: + /* leave the display number in factory->priv->used_display_numbers + so that it doesn't get reused */ +- gdm_display_store_remove (store, display); ++ gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory)); + + /* Create a new equivalent display if it was static */ + if (is_local) { + + factory->priv->num_failures++; + + if (factory->priv->num_failures > MAX_DISPLAY_FAILURES) { + /* oh shit */ + g_warning ("GdmLocalDisplayFactory: maximum number of X display failures reached: check X server log for errors"); + } else { + #ifdef ENABLE_WAYLAND_SUPPORT + if (g_strcmp0 (session_type, "wayland") == 0) { + g_free (session_type); + session_type = NULL; + + /* workaround logind race for now + * bug 1643874 + */ + sleep (2); + } + + #endif + create_display (factory, seat_id, session_type, is_initial); + } + } + break; + case GDM_DISPLAY_UNMANAGED: + break; + case GDM_DISPLAY_PREPARED: + break; +diff --git a/daemon/gdm-xdmcp-display-factory.c b/daemon/gdm-xdmcp-display-factory.c +index 46a0d9ffa..5b5786c6f 100644 +--- a/daemon/gdm-xdmcp-display-factory.c ++++ b/daemon/gdm-xdmcp-display-factory.c +@@ -2039,93 +2039,90 @@ on_hostname_selected (GdmXdmcpChooserDisplay *display, + char *ip; + ic->chosen_address = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen); + + ip = NULL; + gdm_address_get_numeric_info (ic->chosen_address, &ip, NULL); + g_debug ("GdmXdmcpDisplayFactory: hostname resolves to %s", + ip ? ip : "(null)"); + g_free (ip); + } + + freeaddrinfo (ai_list); + } + + static void + on_client_disconnected (GdmDisplay *display) + { + if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) + return; + + gdm_display_stop_greeter_session (display); + gdm_display_unmanage (display); + gdm_display_finish (display); + } + + static void + on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmXdmcpDisplayFactory *factory) + { + int status; +- GdmDisplayStore *store; + GdmLaunchEnvironment *launch_environment; + GdmSession *session; + GdmAddress *address; + gint32 session_number; + int display_number; + +- store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); +- + launch_environment = NULL; + g_object_get (display, "launch-environment", &launch_environment, NULL); + + session = NULL; + if (launch_environment != NULL) { + session = gdm_launch_environment_get_session (launch_environment); + } + + status = gdm_display_get_status (display); + + g_debug ("GdmXdmcpDisplayFactory: xdmcp display status changed: %d", status); + switch (status) { + case GDM_DISPLAY_FINISHED: + g_object_get (display, + "remote-address", &address, + "x11-display-number", &display_number, + "session-number", &session_number, + NULL); + gdm_xdmcp_send_alive (factory, address, display_number, session_number); + +- gdm_display_store_remove (store, display); ++ gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory)); + break; + case GDM_DISPLAY_FAILED: +- gdm_display_store_remove (store, display); ++ gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory)); + break; + case GDM_DISPLAY_UNMANAGED: + if (session != NULL) { + g_signal_handlers_disconnect_by_func (G_OBJECT (session), + G_CALLBACK (on_client_disconnected), + display); + } + break; + case GDM_DISPLAY_PREPARED: + break; + case GDM_DISPLAY_MANAGED: + if (session != NULL) { + g_signal_connect_object (G_OBJECT (session), + "client-disconnected", + G_CALLBACK (on_client_disconnected), + display, G_CONNECT_SWAPPED); + g_signal_connect_object (G_OBJECT (session), + "disconnected", + G_CALLBACK (on_client_disconnected), + display, G_CONNECT_SWAPPED); + } + break; + default: + g_assert_not_reached (); + break; + } + } + + static GdmDisplay * + gdm_xdmcp_display_create (GdmXdmcpDisplayFactory *factory, +-- +2.26.0 + diff --git a/SOURCES/0001-manager-don-t-kill-timed-login-session-immediately-a.patch b/SOURCES/0001-manager-don-t-kill-timed-login-session-immediately-a.patch index 6a4f22b..014d385 100644 --- a/SOURCES/0001-manager-don-t-kill-timed-login-session-immediately-a.patch +++ b/SOURCES/0001-manager-don-t-kill-timed-login-session-immediately-a.patch @@ -15,10 +15,10 @@ treated as autologin sessions. 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c -index 2118c5834..b2d0578f5 100644 +index 1943d89e4..72d44b006 100644 --- a/daemon/gdm-manager.c +++ b/daemon/gdm-manager.c -@@ -1841,61 +1841,62 @@ on_start_user_session (StartUserSessionOperation *operation) +@@ -1838,61 +1838,62 @@ on_start_user_session (StartUserSessionOperation *operation) NULL); } else { uid_t allowed_uid; @@ -52,23 +52,16 @@ index 2118c5834..b2d0578f5 100644 - if (g_strcmp0 (operation->service_name, "gdm-autologin") == 0) { + if ((g_strcmp0 (operation->service_name, "gdm-autologin") == 0) && + !gdm_session_client_is_connected (operation->session)) { - gboolean was_initial = FALSE; - - g_object_get (G_OBJECT (display), "is-initial", &was_initial, NULL); - /* remove the unused prepared greeter display since we're not going * to have a greeter */ gdm_display_store_remove (self->priv->display_store, display); g_object_unref (display); - - should_be_initial = was_initial; } /* Give the user session a new display object for bookkeeping purposes */ create_display_for_user_session (operation->manager, operation->session, - session_id, - should_be_initial); + session_id); } if (starting_user_session_right_away) { @@ -82,6 +75,13 @@ index 2118c5834..b2d0578f5 100644 static void queue_start_user_session (GdmManager *manager, GdmSession *session, + const char *service_name) + { + StartUserSessionOperation *operation; + + operation = g_slice_new0 (StartUserSessionOperation); + operation->manager = manager; + operation->session = g_object_ref (session); -- -2.21.0 +2.26.0 diff --git a/SOURCES/0001-manager-ensure-is-initial-is-transfered-to-autologin.patch b/SOURCES/0001-manager-ensure-is-initial-is-transfered-to-autologin.patch deleted file mode 100644 index 32cf1c2..0000000 --- a/SOURCES/0001-manager-ensure-is-initial-is-transfered-to-autologin.patch +++ /dev/null @@ -1,198 +0,0 @@ -From 4b2db2cf52be75e2eec4aa29f8ee082392ded410 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Thu, 1 Nov 2018 13:03:37 -0400 -Subject: [PATCH] manager: ensure is-initial is transfered to autologin display - -At the moment, we don't handle transferring the is-initial property to -the autologin display. - -That prevents autologin from working if wayland fails and X falls back, -since autologin currently requires an initial display. - -This commit makes sure we properly transfer is-initial from greeter to -user display. ---- - daemon/gdm-manager.c | 14 ++++++++++++-- - 1 file changed, 12 insertions(+), 2 deletions(-) - -diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c -index 1943d89e4..2118c5834 100644 ---- a/daemon/gdm-manager.c -+++ b/daemon/gdm-manager.c -@@ -1740,90 +1740,93 @@ start_user_session (GdmManager *manager, - - g_object_get (G_OBJECT (display), "is-connected", &is_connected, NULL); - - if (is_connected) { - auth_file = NULL; - username = gdm_session_get_username (operation->session); - gdm_display_add_user_authorization (display, - username, - &auth_file, - NULL); - - g_assert (auth_file != NULL); - - g_object_set (operation->session, - "user-x11-authority-file", auth_file, - NULL); - - g_free (auth_file); - } - } - - gdm_session_start_session (operation->session, - operation->service_name); - - destroy_start_user_session_operation (operation); - } - - static void - create_display_for_user_session (GdmManager *self, - GdmSession *session, -- const char *session_id) -+ const char *session_id, -+ gboolean is_initial) - { - GdmDisplay *display; - /* at the moment we only create GdmLocalDisplay objects on seat0 */ - const char *seat_id = "seat0"; - - display = gdm_local_display_new (); - - g_object_set (G_OBJECT (display), - "session-class", "user", - "seat-id", seat_id, - "session-id", session_id, -+ "is-initial", is_initial, - NULL); - gdm_display_store_add (self->priv->display_store, - display); - g_object_set_data (G_OBJECT (session), "gdm-display", display); - g_object_set_data_full (G_OBJECT (display), - "gdm-user-session", - g_object_ref (session), - (GDestroyNotify) - clean_user_session); - } - - static gboolean - on_start_user_session (StartUserSessionOperation *operation) - { - GdmManager *self = operation->manager; - gboolean migrated; - gboolean fail_if_already_switched = TRUE; - gboolean doing_initial_setup = FALSE; -+ gboolean should_be_initial = FALSE; - gboolean starting_user_session_right_away = TRUE; - GdmDisplay *display; - const char *session_id; - - g_debug ("GdmManager: start or jump to session"); - - /* If there's already a session running, jump to it. - * If the only session running is the one we just opened, - * start a session on it. - */ - migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched); - - g_debug ("GdmManager: migrated: %d", migrated); - if (migrated) { - /* We don't stop the manager here because - when Xorg exits it switches to the VT it was - started from. That interferes with fast - user switching. */ - gdm_session_reset (operation->session); - destroy_start_user_session_operation (operation); - goto out; - } - - display = get_display_for_user_session (operation->session); - - g_object_get (G_OBJECT (display), "doing-initial-setup", &doing_initial_setup, NULL); - - session_id = gdm_session_get_conversation_session_id (operation->session, - operation->service_name); - -@@ -1839,70 +1842,77 @@ on_start_user_session (StartUserSessionOperation *operation) - } else { - uid_t allowed_uid; - - g_object_ref (display); - if (doing_initial_setup) { - g_debug ("GdmManager: closing down initial setup display"); - gdm_display_stop_greeter_session (display); - gdm_display_unmanage (display); - gdm_display_finish (display); - - /* We can't start the user session until the finished display - * starts to respawn (since starting an X server and bringing - * one down at the same time is a no go) - */ - g_assert (self->priv->initial_login_operation == NULL); - self->priv->initial_login_operation = operation; - starting_user_session_right_away = FALSE; - } else { - g_debug ("GdmManager: session has its display server, reusing our server for another login screen"); - } - - /* The user session is going to follow the session worker - * into the new display. Untie it from this display and - * create a new session for a future user login. */ - allowed_uid = gdm_session_get_allowed_user (operation->session); - g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); - g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL); - create_user_session_for_display (operation->manager, display, allowed_uid); - - if (g_strcmp0 (operation->service_name, "gdm-autologin") == 0) { -+ gboolean was_initial = FALSE; -+ -+ g_object_get (G_OBJECT (display), "is-initial", &was_initial, NULL); -+ - /* remove the unused prepared greeter display since we're not going - * to have a greeter */ - gdm_display_store_remove (self->priv->display_store, display); - g_object_unref (display); -+ -+ should_be_initial = was_initial; - } - - /* Give the user session a new display object for bookkeeping purposes */ - create_display_for_user_session (operation->manager, - operation->session, -- session_id); -+ session_id, -+ should_be_initial); - } - - if (starting_user_session_right_away) { - start_user_session (operation->manager, operation); - } - - out: - return G_SOURCE_REMOVE; - } - - static void - queue_start_user_session (GdmManager *manager, - GdmSession *session, - const char *service_name) - { - StartUserSessionOperation *operation; - - operation = g_slice_new0 (StartUserSessionOperation); - operation->manager = manager; - operation->session = g_object_ref (session); - operation->service_name = g_strdup (service_name); - - operation->idle_id = g_idle_add ((GSourceFunc) on_start_user_session, operation); - g_object_set_data (G_OBJECT (session), "start-user-session-operation", operation); - } - - static void - start_user_session_if_ready (GdmManager *manager, - GdmSession *session, - const char *service_name) --- -2.19.1 - diff --git a/SOURCES/0002-local-display-factory-Add-gdm_local_display_factory_.patch b/SOURCES/0002-local-display-factory-Add-gdm_local_display_factory_.patch new file mode 100644 index 0000000..79c717b --- /dev/null +++ b/SOURCES/0002-local-display-factory-Add-gdm_local_display_factory_.patch @@ -0,0 +1,164 @@ +From 1541f84c4e782ba5e403dd5fc4cbb2cede9ca568 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 16 May 2018 14:10:34 +0100 +Subject: [PATCH 02/48] local-display-factory: Add + gdm_local_display_factory_use_wayland() helper + +Factor out the code which decides if Xorg or Wayland should be used into +a helper function. +--- + daemon/gdm-local-display-factory.c | 23 +++++++++++++++-------- + 1 file changed, 15 insertions(+), 8 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 1a9196ee1..b21e3aee0 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -158,60 +158,73 @@ take_next_display_number (GdmLocalDisplayFactory *factory) + ret = num + 1; + break; + } + } + out: + + /* now reserve this number */ + g_debug ("GdmLocalDisplayFactory: Reserving X display: %u", ret); + g_hash_table_insert (factory->priv->used_display_numbers, GUINT_TO_POINTER (ret), NULL); + + return ret; + } + + static void + on_display_disposed (GdmLocalDisplayFactory *factory, + GdmDisplay *display) + { + g_debug ("GdmLocalDisplayFactory: Display %p disposed", display); + } + + static void + store_display (GdmLocalDisplayFactory *factory, + GdmDisplay *display) + { + GdmDisplayStore *store; + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + gdm_display_store_add (store, display); + } + ++static gboolean ++gdm_local_display_factory_use_wayland (void) ++{ ++#ifdef ENABLE_WAYLAND_SUPPORT ++ gboolean wayland_enabled = FALSE; ++ if (gdm_settings_direct_get_boolean (GDM_KEY_WAYLAND_ENABLE, &wayland_enabled)) { ++ if (wayland_enabled && g_file_test ("/usr/bin/Xwayland", G_FILE_TEST_IS_EXECUTABLE) ) ++ return TRUE; ++ } ++#endif ++ return FALSE; ++} ++ + /* + Example: + dbus-send --system --dest=org.gnome.DisplayManager \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/gnome/DisplayManager/Manager \ + org.gnome.DisplayManager.Manager.GetDisplays + */ + gboolean + gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *factory, + char **id, + GError **error) + { + gboolean ret; + GdmDisplay *display = NULL; + + g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE); + + ret = FALSE; + + g_debug ("GdmLocalDisplayFactory: Creating transient display"); + + #ifdef ENABLE_USER_DISPLAY_SERVER + display = gdm_local_display_new (); + #else + if (display == NULL) { + guint32 num; + + num = take_next_display_number (factory); + + display = gdm_legacy_display_new (num); +@@ -422,68 +435,62 @@ gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory) + GVariant *array; + GVariantIter iter; + const char *seat; + + result = g_dbus_connection_call_sync (factory->priv->connection, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListSeats", + NULL, + G_VARIANT_TYPE ("(a(so))"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, &error); + + if (!result) { + g_warning ("GdmLocalDisplayFactory: Failed to issue method call: %s", error->message); + g_clear_error (&error); + return FALSE; + } + + array = g_variant_get_child_value (result, 0); + g_variant_iter_init (&iter, array); + + while (g_variant_iter_loop (&iter, "(&so)", &seat, NULL)) { + gboolean is_initial; + const char *session_type = NULL; + + if (g_strcmp0 (seat, "seat0") == 0) { + is_initial = TRUE; +-#ifdef ENABLE_WAYLAND_SUPPORT +- gboolean wayland_enabled = FALSE; +- if (gdm_settings_direct_get_boolean (GDM_KEY_WAYLAND_ENABLE, &wayland_enabled)) { +- if (wayland_enabled && g_file_test ("/usr/bin/Xwayland", G_FILE_TEST_IS_EXECUTABLE) ) { +- session_type = "wayland"; +- } +- } +-#endif ++ if (gdm_local_display_factory_use_wayland ()) ++ session_type = "wayland"; + } else { + is_initial = FALSE; + } + + create_display (factory, seat, session_type, is_initial); + } + + g_variant_unref (result); + g_variant_unref (array); + return TRUE; + } + + static void + on_seat_new (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) + { + const char *seat; + + g_variant_get (parameters, "(&s&o)", &seat, NULL); + create_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat, NULL, FALSE); + } + + static void + on_seat_removed (GDBusConnection *connection, + const gchar *sender_name, +-- +2.26.0 + diff --git a/SOURCES/0003-local-display-factory-Use-correct-session-type-for-n.patch b/SOURCES/0003-local-display-factory-Use-correct-session-type-for-n.patch new file mode 100644 index 0000000..edb6b82 --- /dev/null +++ b/SOURCES/0003-local-display-factory-Use-correct-session-type-for-n.patch @@ -0,0 +1,83 @@ +From 67f9363b7735654dda753512ac5d204d6349eae6 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 16 May 2018 18:36:50 +0100 +Subject: [PATCH 03/48] local-display-factory: Use correct session-type for new + transient displays + +Use the new gdm_local_display_factory_use_wayland() helper to correctly +set the session-type properties for displays created through +gdm_local_display_factory_create_transient_display(). +--- + daemon/gdm-local-display-factory.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index b21e3aee0..e52360a56 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -194,60 +194,62 @@ gdm_local_display_factory_use_wayland (void) + if (wayland_enabled && g_file_test ("/usr/bin/Xwayland", G_FILE_TEST_IS_EXECUTABLE) ) + return TRUE; + } + #endif + return FALSE; + } + + /* + Example: + dbus-send --system --dest=org.gnome.DisplayManager \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/gnome/DisplayManager/Manager \ + org.gnome.DisplayManager.Manager.GetDisplays + */ + gboolean + gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *factory, + char **id, + GError **error) + { + gboolean ret; + GdmDisplay *display = NULL; + + g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE); + + ret = FALSE; + + g_debug ("GdmLocalDisplayFactory: Creating transient display"); + + #ifdef ENABLE_USER_DISPLAY_SERVER + display = gdm_local_display_new (); ++ if (gdm_local_display_factory_use_wayland ()) ++ g_object_set (G_OBJECT (display), "session-type", "wayland", NULL); + #else + if (display == NULL) { + guint32 num; + + num = take_next_display_number (factory); + + display = gdm_legacy_display_new (num); + } + #endif + + g_object_set (display, + "seat-id", "seat0", + "allow-timed-login", FALSE, + NULL); + + store_display (factory, display); + + if (! gdm_display_manage (display)) { + display = NULL; + goto out; + } + + if (! gdm_display_get_id (display, id, NULL)) { + display = NULL; + goto out; + } + + ret = TRUE; + out: + /* ref either held by store or not at all */ +-- +2.26.0 + diff --git a/SOURCES/0004-manager-make-get_login_window_session_id-fail-if-no-.patch b/SOURCES/0004-manager-make-get_login_window_session_id-fail-if-no-.patch new file mode 100644 index 0000000..e5ace0e --- /dev/null +++ b/SOURCES/0004-manager-make-get_login_window_session_id-fail-if-no-.patch @@ -0,0 +1,137 @@ +From 3969532fd960915ddf835e4076489334c6264cf9 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 1 Aug 2018 15:46:11 -0400 +Subject: [PATCH 04/48] manager: make get_login_window_session_id fail if no + login screen + +Right now we oddly succeed from get_login_window_session_id +if we can't find a login window. + +None of the caller expect that, so fail instead. +--- + daemon/gdm-manager.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index e44b94373..a9d5628ea 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1372,113 +1372,113 @@ maybe_start_pending_initial_login (GdmManager *manager, + NULL); + + if (g_strcmp0 (greeter_seat_id, user_session_seat_id) == 0) { + start_user_session (manager, operation); + manager->priv->initial_login_operation = NULL; + } + + g_free (greeter_seat_id); + g_free (user_session_seat_id); + } + + static gboolean + get_login_window_session_id (const char *seat_id, + char **session_id) + { + gboolean ret; + int res, i; + char **sessions; + char *service_id; + char *service_class; + char *state; + + res = sd_seat_get_sessions (seat_id, &sessions, NULL, NULL); + if (res < 0) { + g_debug ("Failed to determine sessions: %s", strerror (-res)); + return FALSE; + } + + if (sessions == NULL || sessions[0] == NULL) { + *session_id = NULL; +- ret = TRUE; ++ ret = FALSE; + goto out; + } + + for (i = 0; sessions[i]; i ++) { + + res = sd_session_get_class (sessions[i], &service_class); + if (res < 0) { + g_debug ("failed to determine class of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_class, "greeter") != 0) { + free (service_class); + continue; + } + + free (service_class); + + ret = sd_session_get_state (sessions[i], &state); + if (ret < 0) { + g_debug ("failed to determine state of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (g_strcmp0 (state, "closing") == 0) { + free (state); + continue; + } + free (state); + + res = sd_session_get_service (sessions[i], &service_id); + if (res < 0) { + g_debug ("failed to determine service of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_id, "gdm-launch-environment") == 0) { + *session_id = g_strdup (sessions[i]); + ret = TRUE; + + free (service_id); + goto out; + } + + free (service_id); + } + + *session_id = NULL; +- ret = TRUE; ++ ret = FALSE; + + out: + if (sessions) { + for (i = 0; sessions[i]; i ++) { + free (sessions[i]); + } + + free (sessions); + } + + return ret; + } + + static void + activate_login_window_session_on_seat (GdmManager *self, + const char *seat_id) + { + char *session_id; + + if (!get_login_window_session_id (seat_id, &session_id)) { + return; + } + + activate_session_id (self, seat_id, session_id); + } + + static void + maybe_activate_other_session (GdmManager *self, + GdmDisplay *old_display) + { +-- +2.26.0 + diff --git a/SOURCES/0005-manager-avoid-leaking-session_id.patch b/SOURCES/0005-manager-avoid-leaking-session_id.patch new file mode 100644 index 0000000..957b176 --- /dev/null +++ b/SOURCES/0005-manager-avoid-leaking-session_id.patch @@ -0,0 +1,79 @@ +From eda3497315a4c6210fe77316b943483aa397e7b0 Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Mon, 18 Jun 2018 12:33:42 +0200 +Subject: [PATCH 05/48] manager: avoid leaking session_id + +get_login_window_session_id() duplicates the session id. +--- + daemon/gdm-manager.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index a9d5628ea..71f55ec65 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1449,60 +1449,61 @@ get_login_window_session_id (const char *seat_id, + + free (service_id); + } + + *session_id = NULL; + ret = FALSE; + + out: + if (sessions) { + for (i = 0; sessions[i]; i ++) { + free (sessions[i]); + } + + free (sessions); + } + + return ret; + } + + static void + activate_login_window_session_on_seat (GdmManager *self, + const char *seat_id) + { + char *session_id; + + if (!get_login_window_session_id (seat_id, &session_id)) { + return; + } + + activate_session_id (self, seat_id, session_id); ++ g_free (session_id); + } + + static void + maybe_activate_other_session (GdmManager *self, + GdmDisplay *old_display) + { + char *seat_id = NULL; + char *session_id; + int ret; + + g_object_get (G_OBJECT (old_display), + "seat-id", &seat_id, + NULL); + + ret = sd_seat_get_active (seat_id, &session_id, NULL); + + if (ret == 0) { + GdmDisplay *display; + + display = gdm_display_store_find (self->priv->display_store, + lookup_by_session_id, + (gpointer) session_id); + + if (display == NULL) { + activate_login_window_session_on_seat (self, seat_id); + } + } + + g_free (seat_id); + } +-- +2.26.0 + diff --git a/SOURCES/0006-manager-gracefully-handle-the-case-of-no-session-for.patch b/SOURCES/0006-manager-gracefully-handle-the-case-of-no-session-for.patch new file mode 100644 index 0000000..de4daf7 --- /dev/null +++ b/SOURCES/0006-manager-gracefully-handle-the-case-of-no-session-for.patch @@ -0,0 +1,89 @@ +From 9ceb85f41a46165c46c82cf6947191fad864ec5b Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Mon, 18 Jun 2018 12:33:51 +0200 +Subject: [PATCH 06/48] manager: gracefully handle the case of no session for + login window + +get_login_window_session_id() will return TRUE with session_id=NULL when +there's no session. This restults in an assertion failure on +constructing the o.fd.login1.Manager.ActivateSessionOnSeat() arguments: + + GLib: g_variant_new_string: assertion 'string != NULL' failed +--- + daemon/gdm-manager.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index 71f55ec65..7a5554e9d 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1448,62 +1448,64 @@ get_login_window_session_id (const char *seat_id, + } + + free (service_id); + } + + *session_id = NULL; + ret = FALSE; + + out: + if (sessions) { + for (i = 0; sessions[i]; i ++) { + free (sessions[i]); + } + + free (sessions); + } + + return ret; + } + + static void + activate_login_window_session_on_seat (GdmManager *self, + const char *seat_id) + { + char *session_id; + + if (!get_login_window_session_id (seat_id, &session_id)) { + return; + } + +- activate_session_id (self, seat_id, session_id); +- g_free (session_id); ++ if (session_id) { ++ activate_session_id (self, seat_id, session_id); ++ g_free (session_id); ++ } + } + + static void + maybe_activate_other_session (GdmManager *self, + GdmDisplay *old_display) + { + char *seat_id = NULL; + char *session_id; + int ret; + + g_object_get (G_OBJECT (old_display), + "seat-id", &seat_id, + NULL); + + ret = sd_seat_get_active (seat_id, &session_id, NULL); + + if (ret == 0) { + GdmDisplay *display; + + display = gdm_display_store_find (self->priv->display_store, + lookup_by_session_id, + (gpointer) session_id); + + if (display == NULL) { + activate_login_window_session_on_seat (self, seat_id); + } + } + + g_free (seat_id); + } +-- +2.26.0 + diff --git a/SOURCES/0007-common-dedupe-gdm_get_login_window_session_id.patch b/SOURCES/0007-common-dedupe-gdm_get_login_window_session_id.patch new file mode 100644 index 0000000..687804d --- /dev/null +++ b/SOURCES/0007-common-dedupe-gdm_get_login_window_session_id.patch @@ -0,0 +1,432 @@ +From f4e7c422bec2167c9648cb063f0cb204789d5f05 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 1 Aug 2018 16:34:30 -0400 +Subject: [PATCH 07/48] common: dedupe gdm_get_login_window_session_id + +Right now there are two slightly different cut-and-pastes of +the function to get the session id of the login session in +the code. + +This commit deduplicates them. +--- + common/gdm-common.c | 47 ++++++++++++++++++++++++++++++++------------ + common/gdm-common.h | 2 ++ + daemon/gdm-manager.c | 4 ++-- + 3 files changed, 38 insertions(+), 15 deletions(-) + +diff --git a/common/gdm-common.c b/common/gdm-common.c +index c44fa998d..00daf0df8 100644 +--- a/common/gdm-common.c ++++ b/common/gdm-common.c +@@ -364,186 +364,207 @@ create_transient_display (GDBusConnection *connection, + + static gboolean + activate_session_id (GDBusConnection *connection, + const char *seat_id, + const char *session_id) + { + GError *local_error = NULL; + GVariant *reply; + + reply = g_dbus_connection_call_sync (connection, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ActivateSessionOnSeat", + g_variant_new ("(ss)", session_id, seat_id), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, &local_error); + if (reply == NULL) { + g_warning ("Unable to activate session: %s", local_error->message); + g_error_free (local_error); + return FALSE; + } + + g_variant_unref (reply); + + return TRUE; + } + +-static gboolean +-get_login_window_session_id (const char *seat_id, +- char **session_id) ++gboolean ++gdm_get_login_window_session_id (const char *seat_id, ++ char **session_id) + { + gboolean ret; + int res, i; + char **sessions; ++ char *service_id; + char *service_class; + char *state; + + res = sd_seat_get_sessions (seat_id, &sessions, NULL, NULL); + if (res < 0) { + g_debug ("Failed to determine sessions: %s", strerror (-res)); + return FALSE; + } + + if (sessions == NULL || sessions[0] == NULL) { + *session_id = NULL; +- ret = TRUE; ++ ret = FALSE; + goto out; + } + + for (i = 0; sessions[i]; i ++) { ++ + res = sd_session_get_class (sessions[i], &service_class); + if (res < 0) { ++ if (res == -ENOENT) { ++ free (service_class); ++ continue; ++ } ++ + g_debug ("failed to determine class of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_class, "greeter") != 0) { + free (service_class); + continue; + } + + free (service_class); + + ret = sd_session_get_state (sessions[i], &state); + if (ret < 0) { + g_debug ("failed to determine state of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (g_strcmp0 (state, "closing") == 0) { + free (state); + continue; + } + free (state); + +- *session_id = g_strdup (sessions[i]); +- ret = TRUE; +- break; ++ res = sd_session_get_service (sessions[i], &service_id); ++ if (res < 0) { ++ g_debug ("failed to determine service of session %s: %s", sessions[i], strerror (-res)); ++ ret = FALSE; ++ goto out; ++ } + ++ if (strcmp (service_id, "gdm-launch-environment") == 0) { ++ *session_id = g_strdup (sessions[i]); ++ ret = TRUE; ++ ++ free (service_id); ++ goto out; ++ } ++ ++ free (service_id); + } + + *session_id = NULL; +- ret = TRUE; ++ ret = FALSE; + + out: +- for (i = 0; sessions[i]; i ++) { +- free (sessions[i]); +- } ++ if (sessions) { ++ for (i = 0; sessions[i]; i ++) { ++ free (sessions[i]); ++ } + +- free (sessions); ++ free (sessions); ++ } + + return ret; + } + + static gboolean + goto_login_session (GDBusConnection *connection, + GError **error) + { + gboolean ret; + int res; + char *our_session; + char *session_id; + char *seat_id; + + ret = FALSE; + session_id = NULL; + seat_id = NULL; + + /* First look for any existing LoginWindow sessions on the seat. + If none are found, create a new one. */ + + /* Note that we mostly use free () here, instead of g_free () + * since the data allocated is from libsystemd-logind, which + * does not use GLib's g_malloc (). */ + + res = sd_pid_get_session (0, &our_session); + if (res < 0) { + g_debug ("failed to determine own session: %s", strerror (-res)); + g_set_error (error, GDM_COMMON_ERROR, 0, _("Could not identify the current session.")); + + return FALSE; + } + + res = sd_session_get_seat (our_session, &seat_id); + free (our_session); + if (res < 0) { + g_debug ("failed to determine own seat: %s", strerror (-res)); + g_set_error (error, GDM_COMMON_ERROR, 0, _("Could not identify the current seat.")); + + return FALSE; + } + + res = sd_seat_can_multi_session (seat_id); + if (res < 0) { + free (seat_id); + + g_debug ("failed to determine whether seat can do multi session: %s", strerror (-res)); + g_set_error (error, GDM_COMMON_ERROR, 0, _("The system is unable to determine whether to switch to an existing login screen or start up a new login screen.")); + + return FALSE; + } + + if (res == 0) { + free (seat_id); + + g_set_error (error, GDM_COMMON_ERROR, 0, _("The system is unable to start up a new login screen.")); + + return FALSE; + } + +- res = get_login_window_session_id (seat_id, &session_id); ++ res = gdm_get_login_window_session_id (seat_id, &session_id); + if (res && session_id != NULL) { + res = activate_session_id (connection, seat_id, session_id); + + if (res) { + ret = TRUE; + } + } + + if (! ret && g_strcmp0 (seat_id, "seat0") == 0) { + res = create_transient_display (connection, error); + if (res) { + ret = TRUE; + } + } + + free (seat_id); + g_free (session_id); + + return ret; + } + + gboolean + gdm_goto_login_session (GError **error) + { + GError *local_error; + GDBusConnection *connection; + + local_error = NULL; + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &local_error); + if (connection == NULL) { +diff --git a/common/gdm-common.h b/common/gdm-common.h +index 8d83a1246..c9cbd9c48 100644 +--- a/common/gdm-common.h ++++ b/common/gdm-common.h +@@ -26,51 +26,53 @@ + #include + + #define VE_IGNORE_EINTR(expr) \ + do { \ + errno = 0; \ + expr; \ + } while G_UNLIKELY (errno == EINTR); + + GQuark gdm_common_error_quark (void); + #define GDM_COMMON_ERROR gdm_common_error_quark() + + typedef char * (*GdmExpandVarFunc) (const char *var, + gpointer user_data); + + G_BEGIN_DECLS + + int gdm_wait_on_pid (int pid); + int gdm_wait_on_and_disown_pid (int pid, + int timeout); + int gdm_signal_pid (int pid, + int signal); + gboolean gdm_get_pwent_for_name (const char *name, + struct passwd **pwentp); + + gboolean gdm_clear_close_on_exec_flag (int fd); + + const char * gdm_make_temp_dir (char *template); + + char *gdm_generate_random_bytes (gsize size, + GError **error); ++gboolean gdm_get_login_window_session_id (const char *seat_id, ++ char **session_id); + gboolean gdm_goto_login_session (GError **error); + + GPtrArray *gdm_get_script_environment (const char *username, + const char *display_name, + const char *display_hostname, + const char *display_x11_authority_file); + gboolean gdm_run_script (const char *dir, + const char *username, + const char *display_name, + const char *display_hostname, + const char *display_x11_authority_file); + + gboolean gdm_shell_var_is_valid_char (char c, + gboolean first); + char * gdm_shell_expand (const char *str, + GdmExpandVarFunc expand_func, + gpointer user_data); + + G_END_DECLS + + #endif /* _GDM_COMMON_H */ +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index 7a5554e9d..375ef6f80 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1444,61 +1444,61 @@ get_login_window_session_id (const char *seat_id, + ret = TRUE; + + free (service_id); + goto out; + } + + free (service_id); + } + + *session_id = NULL; + ret = FALSE; + + out: + if (sessions) { + for (i = 0; sessions[i]; i ++) { + free (sessions[i]); + } + + free (sessions); + } + + return ret; + } + + static void + activate_login_window_session_on_seat (GdmManager *self, + const char *seat_id) + { + char *session_id; + +- if (!get_login_window_session_id (seat_id, &session_id)) { ++ if (!gdm_get_login_window_session_id (seat_id, &session_id)) { + return; + } + + if (session_id) { + activate_session_id (self, seat_id, session_id); + g_free (session_id); + } + } + + static void + maybe_activate_other_session (GdmManager *self, + GdmDisplay *old_display) + { + char *seat_id = NULL; + char *session_id; + int ret; + + g_object_get (G_OBJECT (old_display), + "seat-id", &seat_id, + NULL); + + ret = sd_seat_get_active (seat_id, &session_id, NULL); + + if (ret == 0) { + GdmDisplay *display; + + display = gdm_display_store_find (self->priv->display_store, + lookup_by_session_id, + (gpointer) session_id); + +@@ -2082,61 +2082,61 @@ on_user_session_exited (GdmSession *session, + + static void + on_user_session_died (GdmSession *session, + int signal_number, + GdmManager *manager) + { + g_debug ("GdmManager: session died with signal %s", strsignal (signal_number)); + remove_user_session (manager, session); + } + + static char * + get_display_device (GdmManager *manager, + GdmDisplay *display) + { + /* systemd finds the display device out on its own based on the display */ + return NULL; + } + + static void + on_session_reauthenticated (GdmSession *session, + const char *service_name, + GdmManager *manager) + { + gboolean fail_if_already_switched = FALSE; + + if (gdm_session_get_display_mode (session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) { + const char *seat_id; + char *session_id; + + seat_id = gdm_session_get_display_seat_id (session); +- if (get_login_window_session_id (seat_id, &session_id)) { ++ if (gdm_get_login_window_session_id (seat_id, &session_id)) { + GdmDisplay *display = gdm_display_store_find (manager->priv->display_store, + lookup_by_session_id, + (gpointer) session_id); + + if (display != NULL) { + gdm_display_stop_greeter_session (display); + gdm_display_unmanage (display); + gdm_display_finish (display); + } + g_free (session_id); + } + } + + /* There should already be a session running, so jump to its + * VT. In the event we're already on the right VT, (i.e. user + * used an unlock screen instead of a user switched login screen), + * then silently succeed and unlock the session. + */ + switch_to_compatible_user_session (manager, session, fail_if_already_switched); + } + + static void + on_session_client_ready_for_session_to_start (GdmSession *session, + const char *service_name, + gboolean client_is_ready, + GdmManager *manager) + { + gboolean waiting_to_start_user_session; + + if (client_is_ready) { +-- +2.26.0 + diff --git a/SOURCES/0008-common-dedupe-activate_session_id.patch b/SOURCES/0008-common-dedupe-activate_session_id.patch new file mode 100644 index 0000000..32b99c7 --- /dev/null +++ b/SOURCES/0008-common-dedupe-activate_session_id.patch @@ -0,0 +1,401 @@ +From 7c09ab6a932bb6d7836aed51a9f590809d95b0f9 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 2 Aug 2018 14:00:46 -0400 +Subject: [PATCH 08/48] common: dedupe activate_session_id + +Right now there are three copies of activate_session_id. + +This commit consolidates the code to gdm-common.c +--- + common/gdm-common.c | 10 +++++----- + common/gdm-common.h | 6 ++++++ + daemon/gdm-manager.c | 33 +-------------------------------- + 3 files changed, 12 insertions(+), 37 deletions(-) + +diff --git a/common/gdm-common.c b/common/gdm-common.c +index 00daf0df8..59317a889 100644 +--- a/common/gdm-common.c ++++ b/common/gdm-common.c +@@ -335,64 +335,64 @@ static gboolean + create_transient_display (GDBusConnection *connection, + GError **error) + { + GError *local_error = NULL; + GVariant *reply; + const char *value; + + reply = g_dbus_connection_call_sync (connection, + GDM_DBUS_NAME, + GDM_DBUS_LOCAL_DISPLAY_FACTORY_PATH, + GDM_DBUS_LOCAL_DISPLAY_FACTORY_INTERFACE, + "CreateTransientDisplay", + NULL, /* parameters */ + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, &local_error); + if (reply == NULL) { + g_warning ("Unable to create transient display: %s", local_error->message); + g_propagate_error (error, local_error); + return FALSE; + } + + g_variant_get (reply, "(&o)", &value); + g_debug ("Started %s", value); + + g_variant_unref (reply); + return TRUE; + } + +-static gboolean +-activate_session_id (GDBusConnection *connection, +- const char *seat_id, +- const char *session_id) ++gboolean ++gdm_activate_session_by_id (GDBusConnection *connection, ++ const char *seat_id, ++ const char *session_id) + { + GError *local_error = NULL; + GVariant *reply; + + reply = g_dbus_connection_call_sync (connection, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ActivateSessionOnSeat", + g_variant_new ("(ss)", session_id, seat_id), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, &local_error); + if (reply == NULL) { + g_warning ("Unable to activate session: %s", local_error->message); + g_error_free (local_error); + return FALSE; + } + + g_variant_unref (reply); + + return TRUE; + } + + gboolean + gdm_get_login_window_session_id (const char *seat_id, + char **session_id) + { + gboolean ret; +@@ -512,61 +512,61 @@ goto_login_session (GDBusConnection *connection, + + res = sd_session_get_seat (our_session, &seat_id); + free (our_session); + if (res < 0) { + g_debug ("failed to determine own seat: %s", strerror (-res)); + g_set_error (error, GDM_COMMON_ERROR, 0, _("Could not identify the current seat.")); + + return FALSE; + } + + res = sd_seat_can_multi_session (seat_id); + if (res < 0) { + free (seat_id); + + g_debug ("failed to determine whether seat can do multi session: %s", strerror (-res)); + g_set_error (error, GDM_COMMON_ERROR, 0, _("The system is unable to determine whether to switch to an existing login screen or start up a new login screen.")); + + return FALSE; + } + + if (res == 0) { + free (seat_id); + + g_set_error (error, GDM_COMMON_ERROR, 0, _("The system is unable to start up a new login screen.")); + + return FALSE; + } + + res = gdm_get_login_window_session_id (seat_id, &session_id); + if (res && session_id != NULL) { +- res = activate_session_id (connection, seat_id, session_id); ++ res = gdm_activate_session_by_id (connection, seat_id, session_id); + + if (res) { + ret = TRUE; + } + } + + if (! ret && g_strcmp0 (seat_id, "seat0") == 0) { + res = create_transient_display (connection, error); + if (res) { + ret = TRUE; + } + } + + free (seat_id); + g_free (session_id); + + return ret; + } + + gboolean + gdm_goto_login_session (GError **error) + { + GError *local_error; + GDBusConnection *connection; + + local_error = NULL; + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &local_error); + if (connection == NULL) { + g_debug ("Failed to connect to the D-Bus daemon: %s", local_error->message); + g_propagate_error (error, local_error); +diff --git a/common/gdm-common.h b/common/gdm-common.h +index c9cbd9c48..3fbf07653 100644 +--- a/common/gdm-common.h ++++ b/common/gdm-common.h +@@ -1,78 +1,84 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + #ifndef _GDM_COMMON_H + #define _GDM_COMMON_H + + #include ++#include ++ + #include + #include + + #define VE_IGNORE_EINTR(expr) \ + do { \ + errno = 0; \ + expr; \ + } while G_UNLIKELY (errno == EINTR); + + GQuark gdm_common_error_quark (void); + #define GDM_COMMON_ERROR gdm_common_error_quark() + + typedef char * (*GdmExpandVarFunc) (const char *var, + gpointer user_data); + + G_BEGIN_DECLS + + int gdm_wait_on_pid (int pid); + int gdm_wait_on_and_disown_pid (int pid, + int timeout); + int gdm_signal_pid (int pid, + int signal); + gboolean gdm_get_pwent_for_name (const char *name, + struct passwd **pwentp); + + gboolean gdm_clear_close_on_exec_flag (int fd); + + const char * gdm_make_temp_dir (char *template); + + char *gdm_generate_random_bytes (gsize size, + GError **error); + gboolean gdm_get_login_window_session_id (const char *seat_id, + char **session_id); + gboolean gdm_goto_login_session (GError **error); + + GPtrArray *gdm_get_script_environment (const char *username, + const char *display_name, + const char *display_hostname, + const char *display_x11_authority_file); + gboolean gdm_run_script (const char *dir, + const char *username, + const char *display_name, + const char *display_hostname, + const char *display_x11_authority_file); + + gboolean gdm_shell_var_is_valid_char (char c, + gboolean first); + char * gdm_shell_expand (const char *str, + GdmExpandVarFunc expand_func, + gpointer user_data); + ++gboolean gdm_activate_session_by_id (GDBusConnection *connection, ++ const char *seat_id, ++ const char *session_id); ++ + G_END_DECLS + + #endif /* _GDM_COMMON_H */ +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index 375ef6f80..617ee36f0 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -267,91 +267,60 @@ lookup_by_session_id (const char *id, + + static gboolean + is_login_session (GdmManager *self, + const char *session_id, + GError **error) + { + char *session_class = NULL; + int ret; + + ret = sd_session_get_class (session_id, &session_class); + + if (ret < 0) { + g_set_error (error, + GDM_DISPLAY_ERROR, + GDM_DISPLAY_ERROR_GETTING_SESSION_INFO, + "Error getting class for session id %s from systemd: %s", + session_id, + g_strerror (-ret)); + return FALSE; + } + + if (g_strcmp0 (session_class, "greeter") != 0) { + g_free (session_class); + return FALSE; + } + + g_free (session_class); + return TRUE; + } + +-static gboolean +-activate_session_id (GdmManager *manager, +- const char *seat_id, +- const char *session_id) +-{ +- GError *error = NULL; +- GVariant *reply; +- +- reply = g_dbus_connection_call_sync (manager->priv->connection, +- "org.freedesktop.login1", +- "/org/freedesktop/login1", +- "org.freedesktop.login1.Manager", +- "ActivateSessionOnSeat", +- g_variant_new ("(ss)", session_id, seat_id), +- NULL, /* expected reply */ +- G_DBUS_CALL_FLAGS_NONE, +- -1, +- NULL, +- &error); +- if (reply == NULL) { +- g_debug ("GdmManager: logind 'ActivateSessionOnSeat' %s raised:\n %s\n\n", +- g_dbus_error_get_remote_error (error), error->message); +- g_error_free (error); +- return FALSE; +- } +- +- g_variant_unref (reply); +- +- return TRUE; +-} +- + static gboolean + session_unlock (GdmManager *manager, + const char *ssid) + { + GError *error = NULL; + GVariant *reply; + + g_debug ("Unlocking session %s", ssid); + + reply = g_dbus_connection_call_sync (manager->priv->connection, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "UnlockSession", + g_variant_new ("(s)", ssid), + NULL, /* expected reply */ + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (reply == NULL) { + g_debug ("GdmManager: logind 'UnlockSession' %s raised:\n %s\n\n", + g_dbus_error_get_remote_error (error), error->message); + g_error_free (error); + return FALSE; + } + + g_variant_unref (reply); + + return TRUE; +@@ -627,61 +596,61 @@ switch_to_compatible_user_session (GdmManager *manager, + ret = FALSE; + + username = gdm_session_get_username (session); + seat_id = gdm_session_get_display_seat_id (session); + + if (!fail_if_already_switched) + ssid_to_activate = gdm_session_get_session_id (session); + + if (ssid_to_activate == NULL) { + if (!seat_id || !sd_seat_can_multi_session (seat_id)) { + g_debug ("GdmManager: unable to activate existing sessions from login screen unless on seat0"); + goto out; + } + + if (!fail_if_already_switched) { + session = NULL; + } + + existing_session = find_session_for_user_on_seat (manager, username, seat_id, session); + + if (existing_session != NULL) { + ssid_to_activate = gdm_session_get_session_id (existing_session); + } + } + + if (ssid_to_activate == NULL) { + goto out; + } + + if (seat_id != NULL) { +- res = activate_session_id (manager, seat_id, ssid_to_activate); ++ res = gdm_activate_session_by_id (manager->priv->connection, seat_id, ssid_to_activate); + if (! res) { + g_debug ("GdmManager: unable to activate session: %s", ssid_to_activate); + goto out; + } + } + + res = session_unlock (manager, ssid_to_activate); + if (!res) { + /* this isn't fatal */ + g_debug ("GdmManager: unable to unlock session: %s", ssid_to_activate); + } + + ret = TRUE; + + out: + return ret; + } + + static GdmDisplay * + get_display_for_user_session (GdmSession *session) + { + return g_object_get_data (G_OBJECT (session), "gdm-display"); + } + + static GdmSession * + get_user_session_for_display (GdmDisplay *display) + { + if (display == NULL) { + return NULL; + } +-- +2.26.0 + diff --git a/SOURCES/0009-manager-plug-leak-in-maybe_activate_other_session.patch b/SOURCES/0009-manager-plug-leak-in-maybe_activate_other_session.patch new file mode 100644 index 0000000..f0a3b95 --- /dev/null +++ b/SOURCES/0009-manager-plug-leak-in-maybe_activate_other_session.patch @@ -0,0 +1,103 @@ +From cf679006446e7831c56f2883a6e7932d81655533 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 22 Jun 2018 14:44:11 -0400 +Subject: [PATCH 09/48] manager: plug leak in maybe_activate_other_session + +The function asks logind what the currently active session is on the +given seat. It then leaks the response. + +This commit plugs the leak. +--- + daemon/gdm-manager.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index 617ee36f0..a4dad92ee 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1428,79 +1428,81 @@ out: + free (sessions[i]); + } + + free (sessions); + } + + return ret; + } + + static void + activate_login_window_session_on_seat (GdmManager *self, + const char *seat_id) + { + char *session_id; + + if (!gdm_get_login_window_session_id (seat_id, &session_id)) { + return; + } + + if (session_id) { + activate_session_id (self, seat_id, session_id); + g_free (session_id); + } + } + + static void + maybe_activate_other_session (GdmManager *self, + GdmDisplay *old_display) + { + char *seat_id = NULL; +- char *session_id; ++ char *session_id = NULL; + int ret; + + g_object_get (G_OBJECT (old_display), + "seat-id", &seat_id, + NULL); + + ret = sd_seat_get_active (seat_id, &session_id, NULL); + + if (ret == 0) { + GdmDisplay *display; + + display = gdm_display_store_find (self->priv->display_store, + lookup_by_session_id, + (gpointer) session_id); + + if (display == NULL) { + activate_login_window_session_on_seat (self, seat_id); + } ++ ++ g_free (session_id); + } + + g_free (seat_id); + } + + static const char * + get_username_for_greeter_display (GdmManager *manager, + GdmDisplay *display) + { + gboolean doing_initial_setup = FALSE; + + g_object_get (G_OBJECT (display), + "doing-initial-setup", &doing_initial_setup, + NULL); + + if (doing_initial_setup) { + return INITIAL_SETUP_USERNAME; + } else { + return GDM_USERNAME; + } + } + + static void + set_up_automatic_login_session (GdmManager *manager, + GdmDisplay *display) + { + GdmSession *session; + char *display_session_type = NULL; + gboolean is_initial; + +-- +2.26.0 + diff --git a/SOURCES/0010-manager-start-login-screen-if-old-one-is-finished.patch b/SOURCES/0010-manager-start-login-screen-if-old-one-is-finished.patch new file mode 100644 index 0000000..56bd016 --- /dev/null +++ b/SOURCES/0010-manager-start-login-screen-if-old-one-is-finished.patch @@ -0,0 +1,92 @@ +From bd1bcea8be7b551da17ee5846ab968aacee322b7 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 22 Jun 2018 14:55:39 -0400 +Subject: [PATCH 10/48] manager: start login screen if old one is finished + +Since commit 22c332ba we try to start a login screen if we end up +on an empty VT and there isn't one running. + +Unfortunately the check for "is on an empty VT" is a little busted. +It counts the VT has non-empty if there's a display associated with +it, even if that display is in the FINISHED state about to be +reaped. + +That means, in some cases, we'll still leave the user on an empty +VT with no login screen. + +This commit addresses the problem by explicitly checking for +FINISHED displays, and proceeding even in their presense. +--- + daemon/gdm-manager.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index a4dad92ee..a6f13dec7 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1444,61 +1444,61 @@ activate_login_window_session_on_seat (GdmManager *self, + return; + } + + if (session_id) { + activate_session_id (self, seat_id, session_id); + g_free (session_id); + } + } + + static void + maybe_activate_other_session (GdmManager *self, + GdmDisplay *old_display) + { + char *seat_id = NULL; + char *session_id = NULL; + int ret; + + g_object_get (G_OBJECT (old_display), + "seat-id", &seat_id, + NULL); + + ret = sd_seat_get_active (seat_id, &session_id, NULL); + + if (ret == 0) { + GdmDisplay *display; + + display = gdm_display_store_find (self->priv->display_store, + lookup_by_session_id, + (gpointer) session_id); + +- if (display == NULL) { ++ if (display == NULL || gdm_display_get_status (display) == GDM_DISPLAY_FINISHED) { + activate_login_window_session_on_seat (self, seat_id); + } + + g_free (session_id); + } + + g_free (seat_id); + } + + static const char * + get_username_for_greeter_display (GdmManager *manager, + GdmDisplay *display) + { + gboolean doing_initial_setup = FALSE; + + g_object_get (G_OBJECT (display), + "doing-initial-setup", &doing_initial_setup, + NULL); + + if (doing_initial_setup) { + return INITIAL_SETUP_USERNAME; + } else { + return GDM_USERNAME; + } + } + + static void + set_up_automatic_login_session (GdmManager *manager, + GdmDisplay *display) + { +-- +2.26.0 + diff --git a/SOURCES/0011-manager-don-t-bail-if-session-disappears-out-from-un.patch b/SOURCES/0011-manager-don-t-bail-if-session-disappears-out-from-un.patch new file mode 100644 index 0000000..642e25a --- /dev/null +++ b/SOURCES/0011-manager-don-t-bail-if-session-disappears-out-from-un.patch @@ -0,0 +1,127 @@ +From 78e737441f32d408dbd66d1d1fee384c0b7f94b4 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 22 Jun 2018 15:26:03 -0400 +Subject: [PATCH 11/48] manager: don't bail if session disappears out from + under us + +It's entirely possible for a session returned by +sd_seat_get_sessions to disappear immediately after the +sd_seat_get_sessions call returns. This is especially +likely at logout time where the session will briefly be +in the "closing" state before getting reaped. + +If that happens when we're looking for a greeter session, we +stop looking for a greeter session and bail out all confused. + +This commit fixes the confusion by gracefully handling the +session disappearing by just proceeding to the next session +in the list. +--- + daemon/gdm-manager.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index a6f13dec7..ede22e771 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1349,87 +1349,97 @@ maybe_start_pending_initial_login (GdmManager *manager, + g_free (user_session_seat_id); + } + + static gboolean + get_login_window_session_id (const char *seat_id, + char **session_id) + { + gboolean ret; + int res, i; + char **sessions; + char *service_id; + char *service_class; + char *state; + + res = sd_seat_get_sessions (seat_id, &sessions, NULL, NULL); + if (res < 0) { + g_debug ("Failed to determine sessions: %s", strerror (-res)); + return FALSE; + } + + if (sessions == NULL || sessions[0] == NULL) { + *session_id = NULL; + ret = FALSE; + goto out; + } + + for (i = 0; sessions[i]; i ++) { + + res = sd_session_get_class (sessions[i], &service_class); + if (res < 0) { ++ if (res == -ENOENT) { ++ free (service_class); ++ continue; ++ } ++ + g_debug ("failed to determine class of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_class, "greeter") != 0) { + free (service_class); + continue; + } + + free (service_class); + + ret = sd_session_get_state (sessions[i], &state); + if (ret < 0) { ++ if (res == -ENOENT) ++ continue; ++ + g_debug ("failed to determine state of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (g_strcmp0 (state, "closing") == 0) { + free (state); + continue; + } + free (state); + + res = sd_session_get_service (sessions[i], &service_id); + if (res < 0) { ++ if (res == -ENOENT) ++ continue; + g_debug ("failed to determine service of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_id, "gdm-launch-environment") == 0) { + *session_id = g_strdup (sessions[i]); + ret = TRUE; + + free (service_id); + goto out; + } + + free (service_id); + } + + *session_id = NULL; + ret = FALSE; + + out: + if (sessions) { + for (i = 0; sessions[i]; i ++) { + free (sessions[i]); + } + + free (sessions); + } + + return ret; + } +-- +2.26.0 + diff --git a/SOURCES/0012-daemon-try-harder-to-get-to-a-login-screen-at-logout.patch b/SOURCES/0012-daemon-try-harder-to-get-to-a-login-screen-at-logout.patch new file mode 100644 index 0000000..d0a54c5 --- /dev/null +++ b/SOURCES/0012-daemon-try-harder-to-get-to-a-login-screen-at-logout.patch @@ -0,0 +1,563 @@ +From 3a3b24a4dc6596bb70be86a68f3a228048472c7c Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 30 Jul 2018 16:21:29 -0400 +Subject: [PATCH 12/48] daemon: try harder to get to a login screen at logout + +commit 22c332ba and some follow up commits try to ensure the +user never stays on a blank VT by jumping to a login screen in +the event they'd end up on one. + +Unfortunately, that part of the code can't start a login screen +if there's not one running at all. + +This commit moves the code to GdmLocalDisplyFactory where the +login screens are created, so users won't end up on a blank +VT even if no login screen is yet running. +--- + daemon/gdm-local-display-factory.c | 36 +++++++- + daemon/gdm-manager.c | 143 ----------------------------- + 2 files changed, 31 insertions(+), 148 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index e52360a56..0e454c880 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -1,60 +1,62 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + #include "config.h" + + #include + #include + + #include + #include + #include + #include + ++#include ++ + #include "gdm-common.h" + #include "gdm-manager.h" + #include "gdm-display-factory.h" + #include "gdm-local-display-factory.h" + #include "gdm-local-display-factory-glue.h" + + #include "gdm-settings-keys.h" + #include "gdm-settings-direct.h" + #include "gdm-display-store.h" + #include "gdm-local-display.h" + #include "gdm-legacy-display.h" + + #define GDM_LOCAL_DISPLAY_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_LOCAL_DISPLAY_FACTORY, GdmLocalDisplayFactoryPrivate)) + + #define GDM_DBUS_PATH "/org/gnome/DisplayManager" + #define GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH GDM_DBUS_PATH "/LocalDisplayFactory" + #define GDM_MANAGER_DBUS_NAME "org.gnome.DisplayManager.LocalDisplayFactory" + + #define MAX_DISPLAY_FAILURES 5 + + struct GdmLocalDisplayFactoryPrivate + { + GdmDBusLocalDisplayFactory *skeleton; + GDBusConnection *connection; + GHashTable *used_display_numbers; + + /* FIXME: this needs to be per seat? */ + guint num_failures; + + guint seat_new_id; +@@ -240,174 +242,198 @@ gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *fact + + store_display (factory, display); + + if (! gdm_display_manage (display)) { + display = NULL; + goto out; + } + + if (! gdm_display_get_id (display, id, NULL)) { + display = NULL; + goto out; + } + + ret = TRUE; + out: + /* ref either held by store or not at all */ + g_object_unref (display); + + return ret; + } + + static void + on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmLocalDisplayFactory *factory) + { + int status; + int num; + char *seat_id = NULL; + char *session_type = NULL; ++ char *session_class = NULL; + gboolean is_initial = TRUE; + gboolean is_local = TRUE; + + num = -1; + gdm_display_get_x11_display_number (display, &num, NULL); + + g_object_get (display, + "seat-id", &seat_id, + "is-initial", &is_initial, + "is-local", &is_local, + "session-type", &session_type, ++ "session-class", &session_class, + NULL); + + status = gdm_display_get_status (display); + + g_debug ("GdmLocalDisplayFactory: display status changed: %d", status); + switch (status) { + case GDM_DISPLAY_FINISHED: + /* remove the display number from factory->priv->used_display_numbers + so that it may be reused */ + if (num != -1) { + g_hash_table_remove (factory->priv->used_display_numbers, GUINT_TO_POINTER (num)); + } + gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory)); + + /* if this is a local display, do a full resync. Only + * seats without displays will get created anyway. This + * ensures we get a new login screen when the user logs out, + * if there isn't one. + */ +- if (is_local) { ++ if (is_local && g_strcmp0 (session_class, "greeter") != 0) { + /* reset num failures */ + factory->priv->num_failures = 0; + + gdm_local_display_factory_sync_seats (factory); + } + break; + case GDM_DISPLAY_FAILED: + /* leave the display number in factory->priv->used_display_numbers + so that it doesn't get reused */ + gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory)); + + /* Create a new equivalent display if it was static */ + if (is_local) { + + factory->priv->num_failures++; + + if (factory->priv->num_failures > MAX_DISPLAY_FAILURES) { + /* oh shit */ + g_warning ("GdmLocalDisplayFactory: maximum number of X display failures reached: check X server log for errors"); + } else { + #ifdef ENABLE_WAYLAND_SUPPORT + if (g_strcmp0 (session_type, "wayland") == 0) { + g_free (session_type); + session_type = NULL; + + /* workaround logind race for now + * bug 1643874 + */ + sleep (2); + } + + #endif + create_display (factory, seat_id, session_type, is_initial); + } + } + break; + case GDM_DISPLAY_UNMANAGED: + break; + case GDM_DISPLAY_PREPARED: + break; + case GDM_DISPLAY_MANAGED: + break; + default: + g_assert_not_reached (); + break; + } + + g_free (seat_id); + g_free (session_type); ++ g_free (session_class); + } + + static gboolean + lookup_by_seat_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + char *current; + gboolean res; + + g_object_get (G_OBJECT (display), "seat-id", ¤t, NULL); + + res = g_strcmp0 (current, looking_for) == 0; + + g_free(current); + + return res; + } + + static GdmDisplay * + create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial) + { + GdmDisplayStore *store; + GdmDisplay *display = NULL; ++ char *active_session_id = NULL; ++ int ret; + +- /* Ensure we don't create the same display more than once */ + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); +- display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); +- if (display != NULL) { +- return NULL; ++ ++ ret = sd_seat_get_active (seat_id, &active_session_id, NULL); ++ ++ if (ret == 0) { ++ char *login_session_id = NULL; ++ ++ /* If we already have a login window, switch to it */ ++ if (gdm_get_login_window_session_id (seat_id, &login_session_id)) { ++ if (g_strcmp0 (active_session_id, login_session_id) != 0) { ++ gdm_activate_session_by_id (factory->priv->connection, seat_id, login_session_id); ++ } ++ g_clear_pointer (&login_session_id, g_free); ++ g_clear_pointer (&active_session_id, g_free); ++ return NULL; ++ } ++ g_clear_pointer (&active_session_id, g_free); ++ } else { ++ /* Ensure we don't create the same display more than once */ ++ display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); ++ ++ if (display != NULL) { ++ return NULL; ++ } + } + + g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id); + + #ifdef ENABLE_USER_DISPLAY_SERVER + if (g_strcmp0 (seat_id, "seat0") == 0) { + display = gdm_local_display_new (); + if (session_type != NULL) { + g_object_set (G_OBJECT (display), "session-type", session_type, NULL); + } + } + #endif + + if (display == NULL) { + guint32 num; + + num = take_next_display_number (factory); + + display = gdm_legacy_display_new (num); + } + + g_object_set (display, "seat-id", seat_id, NULL); + g_object_set (display, "is-initial", initial, NULL); + + store_display (factory, display); + + /* let store own the ref */ + g_object_unref (display); + + if (! gdm_display_manage (display)) { +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index ede22e771..80f60d24c 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1322,202 +1322,60 @@ maybe_start_pending_initial_login (GdmManager *manager, + char *user_session_seat_id = NULL; + + /* There may be a user session waiting to be started. + * This would happen if we couldn't start it earlier because + * the login screen X server was coming up and two X servers + * can't be started on the same seat at the same time. + */ + + if (manager->priv->initial_login_operation == NULL) { + return; + } + + operation = manager->priv->initial_login_operation; + + g_object_get (G_OBJECT (greeter_display), + "seat-id", &greeter_seat_id, + NULL); + g_object_get (G_OBJECT (operation->session), + "display-seat-id", &user_session_seat_id, + NULL); + + if (g_strcmp0 (greeter_seat_id, user_session_seat_id) == 0) { + start_user_session (manager, operation); + manager->priv->initial_login_operation = NULL; + } + + g_free (greeter_seat_id); + g_free (user_session_seat_id); + } + +-static gboolean +-get_login_window_session_id (const char *seat_id, +- char **session_id) +-{ +- gboolean ret; +- int res, i; +- char **sessions; +- char *service_id; +- char *service_class; +- char *state; +- +- res = sd_seat_get_sessions (seat_id, &sessions, NULL, NULL); +- if (res < 0) { +- g_debug ("Failed to determine sessions: %s", strerror (-res)); +- return FALSE; +- } +- +- if (sessions == NULL || sessions[0] == NULL) { +- *session_id = NULL; +- ret = FALSE; +- goto out; +- } +- +- for (i = 0; sessions[i]; i ++) { +- +- res = sd_session_get_class (sessions[i], &service_class); +- if (res < 0) { +- if (res == -ENOENT) { +- free (service_class); +- continue; +- } +- +- g_debug ("failed to determine class of session %s: %s", sessions[i], strerror (-res)); +- ret = FALSE; +- goto out; +- } +- +- if (strcmp (service_class, "greeter") != 0) { +- free (service_class); +- continue; +- } +- +- free (service_class); +- +- ret = sd_session_get_state (sessions[i], &state); +- if (ret < 0) { +- if (res == -ENOENT) +- continue; +- +- g_debug ("failed to determine state of session %s: %s", sessions[i], strerror (-res)); +- ret = FALSE; +- goto out; +- } +- +- if (g_strcmp0 (state, "closing") == 0) { +- free (state); +- continue; +- } +- free (state); +- +- res = sd_session_get_service (sessions[i], &service_id); +- if (res < 0) { +- if (res == -ENOENT) +- continue; +- g_debug ("failed to determine service of session %s: %s", sessions[i], strerror (-res)); +- ret = FALSE; +- goto out; +- } +- +- if (strcmp (service_id, "gdm-launch-environment") == 0) { +- *session_id = g_strdup (sessions[i]); +- ret = TRUE; +- +- free (service_id); +- goto out; +- } +- +- free (service_id); +- } +- +- *session_id = NULL; +- ret = FALSE; +- +-out: +- if (sessions) { +- for (i = 0; sessions[i]; i ++) { +- free (sessions[i]); +- } +- +- free (sessions); +- } +- +- return ret; +-} +- +-static void +-activate_login_window_session_on_seat (GdmManager *self, +- const char *seat_id) +-{ +- char *session_id; +- +- if (!gdm_get_login_window_session_id (seat_id, &session_id)) { +- return; +- } +- +- if (session_id) { +- activate_session_id (self, seat_id, session_id); +- g_free (session_id); +- } +-} +- +-static void +-maybe_activate_other_session (GdmManager *self, +- GdmDisplay *old_display) +-{ +- char *seat_id = NULL; +- char *session_id = NULL; +- int ret; +- +- g_object_get (G_OBJECT (old_display), +- "seat-id", &seat_id, +- NULL); +- +- ret = sd_seat_get_active (seat_id, &session_id, NULL); +- +- if (ret == 0) { +- GdmDisplay *display; +- +- display = gdm_display_store_find (self->priv->display_store, +- lookup_by_session_id, +- (gpointer) session_id); +- +- if (display == NULL || gdm_display_get_status (display) == GDM_DISPLAY_FINISHED) { +- activate_login_window_session_on_seat (self, seat_id); +- } +- +- g_free (session_id); +- } +- +- g_free (seat_id); +-} +- + static const char * + get_username_for_greeter_display (GdmManager *manager, + GdmDisplay *display) + { + gboolean doing_initial_setup = FALSE; + + g_object_get (G_OBJECT (display), + "doing-initial-setup", &doing_initial_setup, + NULL); + + if (doing_initial_setup) { + return INITIAL_SETUP_USERNAME; + } else { + return GDM_USERNAME; + } + } + + static void + set_up_automatic_login_session (GdmManager *manager, + GdmDisplay *display) + { + GdmSession *session; + char *display_session_type = NULL; + gboolean is_initial; + + /* 0 is root user; since the daemon talks to the session object + * directly, itself, for automatic login + */ + session = create_user_session_for_display (manager, display, 0); + +@@ -1709,61 +1567,60 @@ on_display_status_changed (GdmDisplay *display, + if ((display_number == -1 && status == GDM_DISPLAY_PREPARED) || + (display_number != -1 && status == GDM_DISPLAY_MANAGED)) { + char *session_class; + + g_object_get (display, + "session-class", &session_class, + NULL); + if (g_strcmp0 (session_class, "greeter") == 0) + set_up_session (manager, display); + g_free (session_class); + } + + if (status == GDM_DISPLAY_MANAGED) { + greeter_display_started (manager, display); + } + break; + case GDM_DISPLAY_FAILED: + case GDM_DISPLAY_UNMANAGED: + case GDM_DISPLAY_FINISHED: + #ifdef WITH_PLYMOUTH + if (quit_plymouth) { + plymouth_quit_without_transition (); + manager->priv->plymouth_is_running = FALSE; + } + #endif + + if (status == GDM_DISPLAY_FINISHED || g_strcmp0 (session_type, "x11") == 0) { + manager->priv->ran_once = TRUE; + } + maybe_start_pending_initial_login (manager, display); +- maybe_activate_other_session (manager, display); + break; + default: + break; + } + + } + + static void + on_display_removed (GdmDisplayStore *display_store, + GdmDisplay *display, + GdmManager *manager) + { + char *id; + + gdm_display_get_id (display, &id, NULL); + g_dbus_object_manager_server_unexport (manager->priv->object_manager, id); + g_free (id); + + g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), manager); + + g_signal_emit (manager, signals[DISPLAY_REMOVED], 0, display); + } + + static void + destroy_start_user_session_operation (StartUserSessionOperation *operation) + { + g_object_set_data (G_OBJECT (operation->session), + "start-user-session-operation", + NULL); + g_object_unref (operation->session); +-- +2.26.0 + diff --git a/SOURCES/0013-local-display-factory-ensure-non-seat0-codepath-does.patch b/SOURCES/0013-local-display-factory-ensure-non-seat0-codepath-does.patch new file mode 100644 index 0000000..4cbc514 --- /dev/null +++ b/SOURCES/0013-local-display-factory-ensure-non-seat0-codepath-does.patch @@ -0,0 +1,87 @@ +From e9dc7113942146f26745494f6177d49fb9b97efb Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 3 Aug 2018 16:50:36 -0400 +Subject: [PATCH 13/48] local-display-factory: ensure non-seat0 codepath + doesn't affect seat0 + +create_display currently bails in some cases if any display is running +on the seat. That's the right thing to do on seats other than seat0, +but wrong for seat0 (which an have multiple sessions at the same +time). + +To ensure we never hit the case for seat0, add a call to check if +the passed seat is multi-session capable. +--- + daemon/gdm-local-display-factory.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 0e454c880..7f7735ca1 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -373,61 +373,61 @@ lookup_by_seat_id (const char *id, + } + + static GdmDisplay * + create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial) + { + GdmDisplayStore *store; + GdmDisplay *display = NULL; + char *active_session_id = NULL; + int ret; + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + + ret = sd_seat_get_active (seat_id, &active_session_id, NULL); + + if (ret == 0) { + char *login_session_id = NULL; + + /* If we already have a login window, switch to it */ + if (gdm_get_login_window_session_id (seat_id, &login_session_id)) { + if (g_strcmp0 (active_session_id, login_session_id) != 0) { + gdm_activate_session_by_id (factory->priv->connection, seat_id, login_session_id); + } + g_clear_pointer (&login_session_id, g_free); + g_clear_pointer (&active_session_id, g_free); + return NULL; + } + g_clear_pointer (&active_session_id, g_free); +- } else { ++ } else if (!sd_seat_can_multi_session (seat_id)) { + /* Ensure we don't create the same display more than once */ + display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); + + if (display != NULL) { + return NULL; + } + } + + g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id); + + #ifdef ENABLE_USER_DISPLAY_SERVER + if (g_strcmp0 (seat_id, "seat0") == 0) { + display = gdm_local_display_new (); + if (session_type != NULL) { + g_object_set (G_OBJECT (display), "session-type", session_type, NULL); + } + } + #endif + + if (display == NULL) { + guint32 num; + + num = take_next_display_number (factory); + + display = gdm_legacy_display_new (num); + } + + g_object_set (display, "seat-id", seat_id, NULL); + g_object_set (display, "is-initial", initial, NULL); + +-- +2.26.0 + diff --git a/SOURCES/0014-daemon-kill-and-restart-greeter-on-demand-under-wayl.patch b/SOURCES/0014-daemon-kill-and-restart-greeter-on-demand-under-wayl.patch new file mode 100644 index 0000000..7ae3218 --- /dev/null +++ b/SOURCES/0014-daemon-kill-and-restart-greeter-on-demand-under-wayl.patch @@ -0,0 +1,369 @@ +From 67c08ab2df5f86626b6e13f3318e41187bde0737 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 2 Aug 2018 15:06:09 -0400 +Subject: [PATCH 14/48] daemon: kill and restart greeter on demand under + wayland + +Right now we leave the greeter alive after the user logs in. +This is for two reasons: + +1) When the greeter is running Xorg, there's no way to kill +it when it's running on an inactive VT (X jumps to the foreground +when being killed) + +2) The greeter, in a way, provides a securepath for unlock. +Users in theory could know that by hitting ctrl-alt-f1 to secure +attention, the login screen presented is not spoofed. + +Since we use wayland by default, 1 isn't that much of a concern, +and 2 is a bit of niche feature that most users probably haven't +considered. + +And there's a huge downside to keeping the greeter alive: it uses +a very large amount of memory. + +This commit changes GDM to kill the login screen when switching +away from the login screen's VT and restarting it when switching +back. + +Based heavily on work by Hans de Goede + +Closes: https://gitlab.gnome.org/GNOME/gdm/issues/222 +--- + daemon/gdm-local-display-factory.c | 167 +++++++++++++++++++++++++++++ + 1 file changed, 167 insertions(+) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 7f7735ca1..4ae656ab3 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -34,60 +34,65 @@ + #include "gdm-manager.h" + #include "gdm-display-factory.h" + #include "gdm-local-display-factory.h" + #include "gdm-local-display-factory-glue.h" + + #include "gdm-settings-keys.h" + #include "gdm-settings-direct.h" + #include "gdm-display-store.h" + #include "gdm-local-display.h" + #include "gdm-legacy-display.h" + + #define GDM_LOCAL_DISPLAY_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_LOCAL_DISPLAY_FACTORY, GdmLocalDisplayFactoryPrivate)) + + #define GDM_DBUS_PATH "/org/gnome/DisplayManager" + #define GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH GDM_DBUS_PATH "/LocalDisplayFactory" + #define GDM_MANAGER_DBUS_NAME "org.gnome.DisplayManager.LocalDisplayFactory" + + #define MAX_DISPLAY_FAILURES 5 + + struct GdmLocalDisplayFactoryPrivate + { + GdmDBusLocalDisplayFactory *skeleton; + GDBusConnection *connection; + GHashTable *used_display_numbers; + + /* FIXME: this needs to be per seat? */ + guint num_failures; + + guint seat_new_id; + guint seat_removed_id; ++ ++#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) ++ char *tty_of_active_vt; ++ guint active_vt_watch_id; ++#endif + }; + + enum { + PROP_0, + }; + + static void gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass); + static void gdm_local_display_factory_init (GdmLocalDisplayFactory *factory); + static void gdm_local_display_factory_finalize (GObject *object); + + static GdmDisplay *create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial_display); + + static void on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmLocalDisplayFactory *factory); + + static gboolean gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory); + static gpointer local_display_factory_object = NULL; + + G_DEFINE_TYPE (GdmLocalDisplayFactory, gdm_local_display_factory, GDM_TYPE_DISPLAY_FACTORY) + + GQuark + gdm_local_display_factory_error_quark (void) + { + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_local_display_factory_error"); +@@ -507,98 +512,260 @@ gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory) + static void + on_seat_new (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) + { + const char *seat; + + g_variant_get (parameters, "(&s&o)", &seat, NULL); + create_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat, NULL, FALSE); + } + + static void + on_seat_removed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) + { + const char *seat; + + g_variant_get (parameters, "(&s&o)", &seat, NULL); + delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat); + } + ++#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) ++static gboolean ++lookup_by_session_id (const char *id, ++ GdmDisplay *display, ++ gpointer user_data) ++{ ++ const char *looking_for = user_data; ++ const char *current; ++ ++ current = gdm_display_get_session_id (display); ++ return g_strcmp0 (current, looking_for) == 0; ++} ++ ++static void ++maybe_stop_greeter_display (GdmDisplay *display) ++{ ++ g_autofree char *display_session_type = NULL; ++ ++ if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) ++ return; ++ ++ g_object_get (G_OBJECT (display), ++ "session-type", &display_session_type, ++ NULL); ++ ++ /* we can only stop greeter for wayland sessions, since ++ * X server would jump back on exit */ ++ if (g_strcmp0 (display_session_type, "wayland") != 0) ++ return; ++ ++ gdm_display_stop_greeter_session (display); ++ gdm_display_unmanage (display); ++ gdm_display_finish (display); ++} ++ ++static gboolean ++on_vt_changed (GIOChannel *source, ++ GIOCondition condition, ++ GdmLocalDisplayFactory *factory) ++{ ++ GIOStatus status; ++ static const char *tty_of_initial_vt = "tty" GDM_INITIAL_VT; ++ g_autofree char *tty_of_previous_vt = NULL; ++ g_autofree char *tty_of_active_vt = NULL; ++ g_autofree char *login_session_id = NULL; ++ g_autofree char *active_session_id = NULL; ++ const char *session_type = NULL; ++ int ret; ++ ++ g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL); ++ ++ if (condition & G_IO_PRI) { ++ g_autoptr (GError) error = NULL; ++ status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error); ++ ++ if (error != NULL) { ++ g_warning ("could not read active VT from kernel: %s", error->message); ++ } ++ switch (status) { ++ case G_IO_STATUS_ERROR: ++ return G_SOURCE_REMOVE; ++ case G_IO_STATUS_EOF: ++ return G_SOURCE_REMOVE; ++ case G_IO_STATUS_AGAIN: ++ return G_SOURCE_CONTINUE; ++ case G_IO_STATUS_NORMAL: ++ break; ++ } ++ } ++ ++ if ((condition & G_IO_ERR) || (condition & G_IO_HUP)) ++ return G_SOURCE_REMOVE; ++ ++ if (tty_of_active_vt == NULL) ++ return G_SOURCE_CONTINUE; ++ ++ g_strchomp (tty_of_active_vt); ++ ++ /* don't do anything if we're on the same VT we were before */ ++ if (g_strcmp0 (tty_of_active_vt, factory->priv->tty_of_active_vt) == 0) ++ return G_SOURCE_CONTINUE; ++ ++ tty_of_previous_vt = g_steal_pointer (&factory->priv->tty_of_active_vt); ++ factory->priv->tty_of_active_vt = g_steal_pointer (&tty_of_active_vt); ++ ++ /* if the old VT was running a wayland login screen kill it ++ */ ++ if (gdm_get_login_window_session_id ("seat0", &login_session_id)) { ++ unsigned int vt; ++ ++ ret = sd_session_get_vt (login_session_id, &vt); ++ if (ret == 0 && vt != 0) { ++ g_autofree char *tty_of_login_window_vt = NULL; ++ ++ tty_of_login_window_vt = g_strdup_printf ("tty%u", vt); ++ ++ if (g_strcmp0 (tty_of_login_window_vt, tty_of_previous_vt) == 0) { ++ GdmDisplayStore *store; ++ GdmDisplay *display; ++ ++ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); ++ display = gdm_display_store_find (store, ++ lookup_by_session_id, ++ (gpointer) login_session_id); ++ ++ if (display != NULL) ++ maybe_stop_greeter_display (display); ++ } ++ } ++ } ++ ++ /* if user jumped back to initial vt and it's empty put a login screen ++ * on it (unless a login screen is already running elsewhere, then ++ * jump to that login screen) ++ */ ++ if (strcmp (factory->priv->tty_of_active_vt, tty_of_initial_vt) != 0) { ++ return G_SOURCE_CONTINUE; ++ } ++ ++ ret = sd_seat_get_active ("seat0", &active_session_id, NULL); ++ ++ if (ret == 0) { ++ g_autofree char *state = NULL; ++ ret = sd_session_get_state (active_session_id, &state); ++ ++ /* if there's something already running on the active VT then bail */ ++ if (ret == 0 && g_strcmp0 (state, "closing") != 0) ++ return G_SOURCE_CONTINUE; ++ } ++ ++ if (gdm_local_display_factory_use_wayland ()) ++ session_type = "wayland"; ++ ++ create_display (factory, "seat0", session_type, TRUE); ++ ++ return G_SOURCE_CONTINUE; ++} ++#endif ++ + static void + gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory) + { ++ g_autoptr (GIOChannel) io_channel = NULL; ++ + factory->priv->seat_new_id = g_dbus_connection_signal_subscribe (factory->priv->connection, + "org.freedesktop.login1", + "org.freedesktop.login1.Manager", + "SeatNew", + "/org/freedesktop/login1", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_seat_new, + g_object_ref (factory), + g_object_unref); + factory->priv->seat_removed_id = g_dbus_connection_signal_subscribe (factory->priv->connection, + "org.freedesktop.login1", + "org.freedesktop.login1.Manager", + "SeatRemoved", + "/org/freedesktop/login1", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_seat_removed, + g_object_ref (factory), + g_object_unref); ++ ++#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) ++ io_channel = g_io_channel_new_file ("/sys/class/tty/tty0/active", "r", NULL); ++ ++ if (io_channel != NULL) { ++ factory->priv->active_vt_watch_id = ++ g_io_add_watch (io_channel, ++ G_IO_PRI, ++ (GIOFunc) ++ on_vt_changed, ++ factory); ++ } ++#endif + } + + static void + gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory) + { + if (factory->priv->seat_new_id) { + g_dbus_connection_signal_unsubscribe (factory->priv->connection, + factory->priv->seat_new_id); + factory->priv->seat_new_id = 0; + } + if (factory->priv->seat_removed_id) { + g_dbus_connection_signal_unsubscribe (factory->priv->connection, + factory->priv->seat_removed_id); + factory->priv->seat_removed_id = 0; + } ++#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) ++ if (factory->priv->active_vt_watch_id) { ++ g_source_remove (factory->priv->active_vt_watch_id); ++ factory->priv->active_vt_watch_id = 0; ++ } ++ ++ g_clear_pointer (&factory->priv->tty_of_active_vt, g_free); ++#endif + } + + static void + on_display_added (GdmDisplayStore *display_store, + const char *id, + GdmLocalDisplayFactory *factory) + { + GdmDisplay *display; + + display = gdm_display_store_lookup (display_store, id); + + if (display != NULL) { + g_signal_connect_object (display, "notify::status", + G_CALLBACK (on_display_status_changed), + factory, + 0); + + g_object_weak_ref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory); + } + } + + static void + on_display_removed (GdmDisplayStore *display_store, + GdmDisplay *display, + GdmLocalDisplayFactory *factory) + { + g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), factory); + g_object_weak_unref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory); + } + +-- +2.26.0 + diff --git a/SOURCES/0015-local-display-factory-add-more-debug-messages-to-new.patch b/SOURCES/0015-local-display-factory-add-more-debug-messages-to-new.patch new file mode 100644 index 0000000..bfb601e --- /dev/null +++ b/SOURCES/0015-local-display-factory-add-more-debug-messages-to-new.patch @@ -0,0 +1,235 @@ +From 0c8edb1580637502f9f1a0bae4535942eebee551 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Aug 2018 13:55:06 -0400 +Subject: [PATCH 15/48] local-display-factory: add more debug messages to new + vt handling code + +commit c0188a7030 added some complex code for starting and stopping +the login screen based on VT changes. + +That code currently has zero debug statements in it making it trickier +than necessary to debug. + +This commit sprinkles some g_debug's throughout the function. +--- + daemon/gdm-local-display-factory.c | 44 ++++++++++++++++++++++++------ + 1 file changed, 35 insertions(+), 9 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 4ae656ab3..2a2259f2a 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -530,175 +530,201 @@ on_seat_removed (GDBusConnection *connection, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) + { + const char *seat; + + g_variant_get (parameters, "(&s&o)", &seat, NULL); + delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat); + } + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + static gboolean + lookup_by_session_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + const char *current; + + current = gdm_display_get_session_id (display); + return g_strcmp0 (current, looking_for) == 0; + } + + static void + maybe_stop_greeter_display (GdmDisplay *display) + { + g_autofree char *display_session_type = NULL; + +- if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) ++ if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) { ++ g_debug ("GdmLocalDisplayFactory: login window not in managed state, so ignoring"); + return; ++ } + + g_object_get (G_OBJECT (display), + "session-type", &display_session_type, + NULL); + + /* we can only stop greeter for wayland sessions, since + * X server would jump back on exit */ +- if (g_strcmp0 (display_session_type, "wayland") != 0) ++ if (g_strcmp0 (display_session_type, "wayland") != 0) { ++ g_debug ("GdmLocalDisplayFactory: login window is running on Xorg, so ignoring"); + return; ++ } + ++ g_debug ("GdmLocalDisplayFactory: killing login window since its now unused"); + gdm_display_stop_greeter_session (display); + gdm_display_unmanage (display); + gdm_display_finish (display); + } + + static gboolean + on_vt_changed (GIOChannel *source, + GIOCondition condition, + GdmLocalDisplayFactory *factory) + { + GIOStatus status; + static const char *tty_of_initial_vt = "tty" GDM_INITIAL_VT; + g_autofree char *tty_of_previous_vt = NULL; + g_autofree char *tty_of_active_vt = NULL; + g_autofree char *login_session_id = NULL; + g_autofree char *active_session_id = NULL; + const char *session_type = NULL; + int ret; + ++ g_debug ("GdmLocalDisplayFactory: received VT change event"); + g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL); + + if (condition & G_IO_PRI) { + g_autoptr (GError) error = NULL; + status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error); + + if (error != NULL) { + g_warning ("could not read active VT from kernel: %s", error->message); + } + switch (status) { + case G_IO_STATUS_ERROR: + return G_SOURCE_REMOVE; + case G_IO_STATUS_EOF: + return G_SOURCE_REMOVE; + case G_IO_STATUS_AGAIN: + return G_SOURCE_CONTINUE; + case G_IO_STATUS_NORMAL: + break; + } + } + +- if ((condition & G_IO_ERR) || (condition & G_IO_HUP)) ++ if ((condition & G_IO_ERR) || (condition & G_IO_HUP)) { ++ g_debug ("GdmLocalDisplayFactory: kernel hung up active vt watch"); + return G_SOURCE_REMOVE; ++ } + +- if (tty_of_active_vt == NULL) ++ if (tty_of_active_vt == NULL) { ++ g_debug ("GdmLocalDisplayFactory: unable to read active VT from kernel"); + return G_SOURCE_CONTINUE; ++ } + + g_strchomp (tty_of_active_vt); + +- /* don't do anything if we're on the same VT we were before */ +- if (g_strcmp0 (tty_of_active_vt, factory->priv->tty_of_active_vt) == 0) +- return G_SOURCE_CONTINUE; +- + tty_of_previous_vt = g_steal_pointer (&factory->priv->tty_of_active_vt); + factory->priv->tty_of_active_vt = g_steal_pointer (&tty_of_active_vt); + ++ /* don't do anything at start up */ ++ if (tty_of_previous_vt == NULL) { ++ g_debug ("GdmLocalDisplayFactory: VT is %s at startup", ++ factory->priv->tty_of_active_vt); ++ return G_SOURCE_CONTINUE; ++ } ++ ++ g_debug ("GdmLocalDisplayFactory: VT changed from %s to %s", ++ tty_of_previous_vt, factory->priv->tty_of_active_vt); ++ + /* if the old VT was running a wayland login screen kill it + */ + if (gdm_get_login_window_session_id ("seat0", &login_session_id)) { + unsigned int vt; + + ret = sd_session_get_vt (login_session_id, &vt); + if (ret == 0 && vt != 0) { + g_autofree char *tty_of_login_window_vt = NULL; + + tty_of_login_window_vt = g_strdup_printf ("tty%u", vt); + ++ g_debug ("GdmLocalDisplayFactory: tty of login window is %s", tty_of_login_window_vt); + if (g_strcmp0 (tty_of_login_window_vt, tty_of_previous_vt) == 0) { + GdmDisplayStore *store; + GdmDisplay *display; + ++ g_debug ("GdmLocalDisplayFactory: VT switched from login window"); ++ + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + display = gdm_display_store_find (store, + lookup_by_session_id, + (gpointer) login_session_id); + + if (display != NULL) + maybe_stop_greeter_display (display); ++ } else { ++ g_debug ("GdmLocalDisplayFactory: VT not switched from login window"); + } + } + } + + /* if user jumped back to initial vt and it's empty put a login screen + * on it (unless a login screen is already running elsewhere, then + * jump to that login screen) + */ + if (strcmp (factory->priv->tty_of_active_vt, tty_of_initial_vt) != 0) { ++ g_debug ("GdmLocalDisplayFactory: active VT is not initial VT, so ignoring"); + return G_SOURCE_CONTINUE; + } + + ret = sd_seat_get_active ("seat0", &active_session_id, NULL); + + if (ret == 0) { + g_autofree char *state = NULL; + ret = sd_session_get_state (active_session_id, &state); + + /* if there's something already running on the active VT then bail */ +- if (ret == 0 && g_strcmp0 (state, "closing") != 0) ++ if (ret == 0 && g_strcmp0 (state, "closing") != 0) { ++ g_debug ("GdmLocalDisplayFactory: initial VT is in use, so ignoring"); + return G_SOURCE_CONTINUE; ++ } + } + + if (gdm_local_display_factory_use_wayland ()) + session_type = "wayland"; + ++ g_debug ("GdmLocalDisplayFactory: creating new display on seat0 because of VT change"); ++ + create_display (factory, "seat0", session_type, TRUE); + + return G_SOURCE_CONTINUE; + } + #endif + + static void + gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory) + { + g_autoptr (GIOChannel) io_channel = NULL; + + factory->priv->seat_new_id = g_dbus_connection_signal_subscribe (factory->priv->connection, + "org.freedesktop.login1", + "org.freedesktop.login1.Manager", + "SeatNew", + "/org/freedesktop/login1", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_seat_new, + g_object_ref (factory), + g_object_unref); + factory->priv->seat_removed_id = g_dbus_connection_signal_subscribe (factory->priv->connection, + "org.freedesktop.login1", + "org.freedesktop.login1.Manager", + "SeatRemoved", + "/org/freedesktop/login1", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_seat_removed, + g_object_ref (factory), +-- +2.26.0 + diff --git a/SOURCES/0016-local-display-factory-don-t-start-two-greeters-at-st.patch b/SOURCES/0016-local-display-factory-don-t-start-two-greeters-at-st.patch new file mode 100644 index 0000000..dad8b18 --- /dev/null +++ b/SOURCES/0016-local-display-factory-don-t-start-two-greeters-at-st.patch @@ -0,0 +1,97 @@ +From 171ea8c0310a215d278d3532232488987317f4aa Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Aug 2018 13:55:06 -0400 +Subject: [PATCH 16/48] local-display-factory: don't start two greeters at + startup + +commit c0188a7030 added some complex code for starting +the login screen when the user switches to the initial +VT if nothing is running on that VT. + +The problem is, we get a VT change event on that VT as +part of the start up process. + +This leads to an additional greeter getting started. + +This commit adds a check to side step the new code during +startup. + +Closes: https://gitlab.gnome.org/GNOME/gdm/issues/409 +--- + daemon/gdm-local-display-factory.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 2a2259f2a..9f377ba9a 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -600,60 +600,66 @@ on_vt_changed (GIOChannel *source, + g_autoptr (GError) error = NULL; + status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error); + + if (error != NULL) { + g_warning ("could not read active VT from kernel: %s", error->message); + } + switch (status) { + case G_IO_STATUS_ERROR: + return G_SOURCE_REMOVE; + case G_IO_STATUS_EOF: + return G_SOURCE_REMOVE; + case G_IO_STATUS_AGAIN: + return G_SOURCE_CONTINUE; + case G_IO_STATUS_NORMAL: + break; + } + } + + if ((condition & G_IO_ERR) || (condition & G_IO_HUP)) { + g_debug ("GdmLocalDisplayFactory: kernel hung up active vt watch"); + return G_SOURCE_REMOVE; + } + + if (tty_of_active_vt == NULL) { + g_debug ("GdmLocalDisplayFactory: unable to read active VT from kernel"); + return G_SOURCE_CONTINUE; + } + + g_strchomp (tty_of_active_vt); + ++ /* don't do anything if we're on the same VT we were before */ ++ if (g_strcmp0 (tty_of_active_vt, factory->priv->tty_of_active_vt) == 0) { ++ g_debug ("GdmLocalDisplayFactory: VT changed to the same VT, ignoring"); ++ return G_SOURCE_CONTINUE; ++ } ++ + tty_of_previous_vt = g_steal_pointer (&factory->priv->tty_of_active_vt); + factory->priv->tty_of_active_vt = g_steal_pointer (&tty_of_active_vt); + + /* don't do anything at start up */ + if (tty_of_previous_vt == NULL) { + g_debug ("GdmLocalDisplayFactory: VT is %s at startup", + factory->priv->tty_of_active_vt); + return G_SOURCE_CONTINUE; + } + + g_debug ("GdmLocalDisplayFactory: VT changed from %s to %s", + tty_of_previous_vt, factory->priv->tty_of_active_vt); + + /* if the old VT was running a wayland login screen kill it + */ + if (gdm_get_login_window_session_id ("seat0", &login_session_id)) { + unsigned int vt; + + ret = sd_session_get_vt (login_session_id, &vt); + if (ret == 0 && vt != 0) { + g_autofree char *tty_of_login_window_vt = NULL; + + tty_of_login_window_vt = g_strdup_printf ("tty%u", vt); + + g_debug ("GdmLocalDisplayFactory: tty of login window is %s", tty_of_login_window_vt); + if (g_strcmp0 (tty_of_login_window_vt, tty_of_previous_vt) == 0) { + GdmDisplayStore *store; + GdmDisplay *display; + + g_debug ("GdmLocalDisplayFactory: VT switched from login window"); +-- +2.26.0 + diff --git a/SOURCES/0017-session-worker-don-t-switch-VTs-if-we-re-already-on-.patch b/SOURCES/0017-session-worker-don-t-switch-VTs-if-we-re-already-on-.patch new file mode 100644 index 0000000..ef9db9b --- /dev/null +++ b/SOURCES/0017-session-worker-don-t-switch-VTs-if-we-re-already-on-.patch @@ -0,0 +1,128 @@ +From db7e9f8306716c4338b7de01cb1026d8e1dd8afa Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Aug 2018 14:04:44 -0400 +Subject: [PATCH 17/48] session-worker: don't switch VTs if we're already on + the right VT + +commit 5b5dccbd shows that switching VTs to the same VT isn't +exactly a no-op. In order to prevent unnecessary wakeups, avoid +switching VTs if the worker is already on the correct VT. +--- + daemon/gdm-session-worker.c | 22 ++++++++++++++++------ + 1 file changed, 16 insertions(+), 6 deletions(-) + +diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c +index 0322037e0..fd6470bab 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -936,91 +936,101 @@ fix_terminal_vt_mode (GdmSessionWorker *worker, + if (ioctl (tty_fd, KDGETMODE, &kernel_display_mode) < 0) { + g_debug ("GdmSessionWorker: couldn't query kernel display mode: %m"); + succeeded = FALSE; + } + + if (kernel_display_mode == KD_TEXT) { + goto out; + } + + /* VT is in the anti-social state of VT_AUTO + KD_GRAPHICS, + * fix it. + */ + succeeded = handle_terminal_vt_switches (worker, tty_fd); + mode_fixed = TRUE; + out: + if (!succeeded) { + g_error ("GdmSessionWorker: couldn't set up terminal, aborting..."); + return; + } + + g_debug ("GdmSessionWorker: VT mode did %sneed to be fixed", + mode_fixed? "" : "not "); + } + + static void + jump_to_vt (GdmSessionWorker *worker, + int vt_number) + { + int fd; + int active_vt_tty_fd; ++ int active_vt = -1; ++ struct vt_stat vt_state = { 0 }; + + g_debug ("GdmSessionWorker: jumping to VT %d", vt_number); + active_vt_tty_fd = open ("/dev/tty0", O_RDWR | O_NOCTTY); + + if (worker->priv->session_tty_fd != -1) { + fd = worker->priv->session_tty_fd; + + g_debug ("GdmSessionWorker: first setting graphics mode to prevent flicker"); + if (ioctl (fd, KDSETMODE, KD_GRAPHICS) < 0) { + g_debug ("GdmSessionWorker: couldn't set graphics mode: %m"); + } + + /* It's possible that the current VT was left in a broken + * combination of states (KD_GRAPHICS with VT_AUTO), that + * can't be switched away from. This call makes sure things + * are set in a way that VT_ACTIVATE should work and + * VT_WAITACTIVE shouldn't hang. + */ + fix_terminal_vt_mode (worker, active_vt_tty_fd); + } else { + fd = active_vt_tty_fd; + } + + handle_terminal_vt_switches (worker, fd); + +- if (ioctl (fd, VT_ACTIVATE, vt_number) < 0) { +- g_debug ("GdmSessionWorker: couldn't initiate jump to VT %d: %m", +- vt_number); +- } else if (ioctl (fd, VT_WAITACTIVE, vt_number) < 0) { +- g_debug ("GdmSessionWorker: couldn't finalize jump to VT %d: %m", +- vt_number); ++ if (ioctl (fd, VT_GETSTATE, &vt_state) <= 0) { ++ g_debug ("GdmSessionWorker: couldn't get current VT: %m"); ++ } else { ++ active_vt = vt_state.v_active; ++ } ++ ++ if (active_vt != vt_number) { ++ if (ioctl (fd, VT_ACTIVATE, vt_number) < 0) { ++ g_debug ("GdmSessionWorker: couldn't initiate jump to VT %d: %m", ++ vt_number); ++ } else if (ioctl (fd, VT_WAITACTIVE, vt_number) < 0) { ++ g_debug ("GdmSessionWorker: couldn't finalize jump to VT %d: %m", ++ vt_number); ++ } + } + + close (active_vt_tty_fd); + } + + static void + gdm_session_worker_set_state (GdmSessionWorker *worker, + GdmSessionWorkerState state) + { + if (worker->priv->state == state) + return; + + worker->priv->state = state; + g_object_notify (G_OBJECT (worker), "state"); + } + + static void + gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker, + int status) + { + g_debug ("GdmSessionWorker: uninitializing PAM"); + + if (worker->priv->pam_handle == NULL) + return; + + gdm_session_worker_get_username (worker, NULL); + + if (worker->priv->state >= GDM_SESSION_WORKER_STATE_SESSION_OPENED) { + pam_close_session (worker->priv->pam_handle, 0); + gdm_session_auditor_report_logout (worker->priv->auditor); +-- +2.26.0 + diff --git a/SOURCES/0018-session-worker-fix-current-vt-detection-short-circui.patch b/SOURCES/0018-session-worker-fix-current-vt-detection-short-circui.patch new file mode 100644 index 0000000..fc55b96 --- /dev/null +++ b/SOURCES/0018-session-worker-fix-current-vt-detection-short-circui.patch @@ -0,0 +1,87 @@ +From a77803467601cebc60ecc1c56105960a1aeb1a50 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 15:20:39 -0400 +Subject: [PATCH 18/48] session-worker: fix current vt detection short-circuit + logic + +commit 8169cd4 attempts to avoid changing VTs if the active VT +is the same as the VT getting jumped to. + +It fails to work, however, because accidentally treats a 0 return +code to the VT_GETSTATE ioctl as failure. + +this commit fixes that. +--- + daemon/gdm-session-worker.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c +index fd6470bab..391969d96 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -963,61 +963,61 @@ jump_to_vt (GdmSessionWorker *worker, + { + int fd; + int active_vt_tty_fd; + int active_vt = -1; + struct vt_stat vt_state = { 0 }; + + g_debug ("GdmSessionWorker: jumping to VT %d", vt_number); + active_vt_tty_fd = open ("/dev/tty0", O_RDWR | O_NOCTTY); + + if (worker->priv->session_tty_fd != -1) { + fd = worker->priv->session_tty_fd; + + g_debug ("GdmSessionWorker: first setting graphics mode to prevent flicker"); + if (ioctl (fd, KDSETMODE, KD_GRAPHICS) < 0) { + g_debug ("GdmSessionWorker: couldn't set graphics mode: %m"); + } + + /* It's possible that the current VT was left in a broken + * combination of states (KD_GRAPHICS with VT_AUTO), that + * can't be switched away from. This call makes sure things + * are set in a way that VT_ACTIVATE should work and + * VT_WAITACTIVE shouldn't hang. + */ + fix_terminal_vt_mode (worker, active_vt_tty_fd); + } else { + fd = active_vt_tty_fd; + } + + handle_terminal_vt_switches (worker, fd); + +- if (ioctl (fd, VT_GETSTATE, &vt_state) <= 0) { ++ if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) { + g_debug ("GdmSessionWorker: couldn't get current VT: %m"); + } else { + active_vt = vt_state.v_active; + } + + if (active_vt != vt_number) { + if (ioctl (fd, VT_ACTIVATE, vt_number) < 0) { + g_debug ("GdmSessionWorker: couldn't initiate jump to VT %d: %m", + vt_number); + } else if (ioctl (fd, VT_WAITACTIVE, vt_number) < 0) { + g_debug ("GdmSessionWorker: couldn't finalize jump to VT %d: %m", + vt_number); + } + } + + close (active_vt_tty_fd); + } + + static void + gdm_session_worker_set_state (GdmSessionWorker *worker, + GdmSessionWorkerState state) + { + if (worker->priv->state == state) + return; + + worker->priv->state = state; + g_object_notify (G_OBJECT (worker), "state"); + } + + static void +-- +2.26.0 + diff --git a/SOURCES/0019-local-display-factory-don-t-jump-to-failed-display.patch b/SOURCES/0019-local-display-factory-don-t-jump-to-failed-display.patch new file mode 100644 index 0000000..2a5288f --- /dev/null +++ b/SOURCES/0019-local-display-factory-don-t-jump-to-failed-display.patch @@ -0,0 +1,168 @@ +From d8a5641bf96c7081e2050c76e5381a82535d67b4 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 9 Aug 2018 12:32:31 -0400 +Subject: [PATCH 19/48] local-display-factory: don't jump to failed display + +Since commit 5e737a57 `create_display` will jump to any +already running login screen if it can find one. + +Right now if a display fails we call `create_display` to +create a new one. It will look for any already running +login screen and find the recently failed display. + +This commit make sure we never jump to a display that isn't +in good working order. +--- + daemon/gdm-local-display-factory.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 9f377ba9a..c58de9c17 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -60,60 +60,63 @@ struct GdmLocalDisplayFactoryPrivate + guint num_failures; + + guint seat_new_id; + guint seat_removed_id; + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + char *tty_of_active_vt; + guint active_vt_watch_id; + #endif + }; + + enum { + PROP_0, + }; + + static void gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass); + static void gdm_local_display_factory_init (GdmLocalDisplayFactory *factory); + static void gdm_local_display_factory_finalize (GObject *object); + + static GdmDisplay *create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial_display); + + static void on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmLocalDisplayFactory *factory); + + static gboolean gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory); + static gpointer local_display_factory_object = NULL; ++static gboolean lookup_by_session_id (const char *id, ++ GdmDisplay *display, ++ gpointer user_data); + + G_DEFINE_TYPE (GdmLocalDisplayFactory, gdm_local_display_factory, GDM_TYPE_DISPLAY_FACTORY) + + GQuark + gdm_local_display_factory_error_quark (void) + { + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_local_display_factory_error"); + } + + return ret; + } + + static void + listify_hash (gpointer key, + GdmDisplay *display, + GList **list) + { + *list = g_list_prepend (*list, key); + } + + static int + sort_nums (gpointer a, + gpointer b) + { + guint32 num_a; + guint32 num_b; + + num_a = GPOINTER_TO_UINT (a); +@@ -370,66 +373,74 @@ lookup_by_seat_id (const char *id, + + g_object_get (G_OBJECT (display), "seat-id", ¤t, NULL); + + res = g_strcmp0 (current, looking_for) == 0; + + g_free(current); + + return res; + } + + static GdmDisplay * + create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial) + { + GdmDisplayStore *store; + GdmDisplay *display = NULL; + char *active_session_id = NULL; + int ret; + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + + ret = sd_seat_get_active (seat_id, &active_session_id, NULL); + + if (ret == 0) { + char *login_session_id = NULL; + + /* If we already have a login window, switch to it */ + if (gdm_get_login_window_session_id (seat_id, &login_session_id)) { +- if (g_strcmp0 (active_session_id, login_session_id) != 0) { +- gdm_activate_session_by_id (factory->priv->connection, seat_id, login_session_id); ++ GdmDisplay *display; ++ ++ display = gdm_display_store_find (store, ++ lookup_by_session_id, ++ (gpointer) login_session_id); ++ if (display != NULL && gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { ++ if (g_strcmp0 (active_session_id, login_session_id) != 0) { ++ gdm_activate_session_by_id (factory->priv->connection, seat_id, login_session_id); ++ } ++ g_clear_pointer (&login_session_id, g_free); ++ g_clear_pointer (&active_session_id, g_free); ++ return NULL; + } + g_clear_pointer (&login_session_id, g_free); +- g_clear_pointer (&active_session_id, g_free); +- return NULL; + } + g_clear_pointer (&active_session_id, g_free); + } else if (!sd_seat_can_multi_session (seat_id)) { + /* Ensure we don't create the same display more than once */ + display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); + + if (display != NULL) { + return NULL; + } + } + + g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id); + + #ifdef ENABLE_USER_DISPLAY_SERVER + if (g_strcmp0 (seat_id, "seat0") == 0) { + display = gdm_local_display_new (); + if (session_type != NULL) { + g_object_set (G_OBJECT (display), "session-type", session_type, NULL); + } + } + #endif + + if (display == NULL) { + guint32 num; + + num = take_next_display_number (factory); + + display = gdm_legacy_display_new (num); + } + +-- +2.26.0 + diff --git a/SOURCES/0020-local-display-factory-add-some-more-debug-statements.patch b/SOURCES/0020-local-display-factory-add-some-more-debug-statements.patch new file mode 100644 index 0000000..ea10278 --- /dev/null +++ b/SOURCES/0020-local-display-factory-add-some-more-debug-statements.patch @@ -0,0 +1,161 @@ +From d2cf186fd0f0b860c6dd282cf8630c3a0f4935a6 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 9 Aug 2018 12:48:25 -0400 +Subject: [PATCH 20/48] local-display-factory: add some more debug statements + +This commit just sprinkles in a few more `g_debug`'s for +log file clarity. +--- + daemon/gdm-local-display-factory.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index c58de9c17..6f3a4c391 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -364,76 +364,80 @@ on_display_status_changed (GdmDisplay *display, + + static gboolean + lookup_by_seat_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + char *current; + gboolean res; + + g_object_get (G_OBJECT (display), "seat-id", ¤t, NULL); + + res = g_strcmp0 (current, looking_for) == 0; + + g_free(current); + + return res; + } + + static GdmDisplay * + create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial) + { + GdmDisplayStore *store; + GdmDisplay *display = NULL; + char *active_session_id = NULL; + int ret; + ++ g_debug ("GdmLocalDisplayFactory: %s login display for seat %s requested", ++ session_type? : "X11", seat_id); + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + + ret = sd_seat_get_active (seat_id, &active_session_id, NULL); + + if (ret == 0) { + char *login_session_id = NULL; + + /* If we already have a login window, switch to it */ + if (gdm_get_login_window_session_id (seat_id, &login_session_id)) { + GdmDisplay *display; + + display = gdm_display_store_find (store, + lookup_by_session_id, + (gpointer) login_session_id); + if (display != NULL && gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { + if (g_strcmp0 (active_session_id, login_session_id) != 0) { ++ g_debug ("GdmLocalDisplayFactory: session %s found, activating.", ++ login_session_id); + gdm_activate_session_by_id (factory->priv->connection, seat_id, login_session_id); + } + g_clear_pointer (&login_session_id, g_free); + g_clear_pointer (&active_session_id, g_free); + return NULL; + } + g_clear_pointer (&login_session_id, g_free); + } + g_clear_pointer (&active_session_id, g_free); + } else if (!sd_seat_can_multi_session (seat_id)) { + /* Ensure we don't create the same display more than once */ + display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); + + if (display != NULL) { + return NULL; + } + } + + g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id); + + #ifdef ENABLE_USER_DISPLAY_SERVER + if (g_strcmp0 (seat_id, "seat0") == 0) { + display = gdm_local_display_new (); + if (session_type != NULL) { + g_object_set (G_OBJECT (display), "session-type", session_type, NULL); + } + } + #endif + + if (display == NULL) { +@@ -453,60 +457,61 @@ create_display (GdmLocalDisplayFactory *factory, + g_object_unref (display); + + if (! gdm_display_manage (display)) { + gdm_display_unmanage (display); + } + + return display; + } + + static void + delete_display (GdmLocalDisplayFactory *factory, + const char *seat_id) { + + GdmDisplayStore *store; + + g_debug ("GdmLocalDisplayFactory: Removing used_display_numbers on seat %s", seat_id); + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + gdm_display_store_foreach_remove (store, lookup_by_seat_id, (gpointer) seat_id); + } + + static gboolean + gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory) + { + GError *error = NULL; + GVariant *result; + GVariant *array; + GVariantIter iter; + const char *seat; + ++ g_debug ("GdmLocalDisplayFactory: enumerating seats from logind"); + result = g_dbus_connection_call_sync (factory->priv->connection, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListSeats", + NULL, + G_VARIANT_TYPE ("(a(so))"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, &error); + + if (!result) { + g_warning ("GdmLocalDisplayFactory: Failed to issue method call: %s", error->message); + g_clear_error (&error); + return FALSE; + } + + array = g_variant_get_child_value (result, 0); + g_variant_iter_init (&iter, array); + + while (g_variant_iter_loop (&iter, "(&so)", &seat, NULL)) { + gboolean is_initial; + const char *session_type = NULL; + + if (g_strcmp0 (seat, "seat0") == 0) { + is_initial = TRUE; + if (gdm_local_display_factory_use_wayland ()) + session_type = "wayland"; + } else { + is_initial = FALSE; +-- +2.26.0 + diff --git a/SOURCES/0021-local-display-factory-ignore-spurios-SeatNew-signal-.patch b/SOURCES/0021-local-display-factory-ignore-spurios-SeatNew-signal-.patch new file mode 100644 index 0000000..2e21977 --- /dev/null +++ b/SOURCES/0021-local-display-factory-ignore-spurios-SeatNew-signal-.patch @@ -0,0 +1,162 @@ +From 31ba433072715f4f6e8eb2b1a65941469c27dbd8 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 14 Aug 2018 10:21:17 -0400 +Subject: [PATCH 21/48] local-display-factory: ignore spurios SeatNew signal at + start up + +Sometimes during startup, logind will send a `SeatNew` signal for +seat0 after GDM has already called `ListSeats` and processed `seat0`. + +That `SeatNew` signal leads to GDM calling `create_display` twice in +quick succession. + +This commit changes GDM to avoid such double processing, by ignoring +the `create_display` requests for seats that already have a prepared +display ("prepared" means "starting up"). + +Closes: https://gitlab.gnome.org/GNOME/gdm/issues/410 +--- + daemon/gdm-local-display-factory.c | 33 +++++++++++++++++++++++------- + 1 file changed, 26 insertions(+), 7 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 6f3a4c391..127127005 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -353,107 +353,126 @@ on_display_status_changed (GdmDisplay *display, + case GDM_DISPLAY_MANAGED: + break; + default: + g_assert_not_reached (); + break; + } + + g_free (seat_id); + g_free (session_type); + g_free (session_class); + } + + static gboolean + lookup_by_seat_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + char *current; + gboolean res; + + g_object_get (G_OBJECT (display), "seat-id", ¤t, NULL); + + res = g_strcmp0 (current, looking_for) == 0; + + g_free(current); + + return res; + } + ++static gboolean ++lookup_prepared_display_by_seat_id (const char *id, ++ GdmDisplay *display, ++ gpointer user_data) ++{ ++ int status; ++ ++ status = gdm_display_get_status (display); ++ ++ if (status != GDM_DISPLAY_PREPARED) ++ return FALSE; ++ ++ return lookup_by_seat_id (id, display, user_data); ++} ++ + static GdmDisplay * + create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial) + { + GdmDisplayStore *store; + GdmDisplay *display = NULL; + char *active_session_id = NULL; + int ret; + + g_debug ("GdmLocalDisplayFactory: %s login display for seat %s requested", + session_type? : "X11", seat_id); + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + ++ if (sd_seat_can_multi_session (seat_id)) ++ display = gdm_display_store_find (store, lookup_prepared_display_by_seat_id, (gpointer) seat_id); ++ else ++ display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); ++ ++ /* Ensure we don't create the same display more than once */ ++ if (display != NULL) { ++ g_debug ("GdmLocalDisplayFactory: display already created"); ++ return NULL; ++ } ++ + ret = sd_seat_get_active (seat_id, &active_session_id, NULL); + + if (ret == 0) { + char *login_session_id = NULL; + + /* If we already have a login window, switch to it */ + if (gdm_get_login_window_session_id (seat_id, &login_session_id)) { + GdmDisplay *display; + + display = gdm_display_store_find (store, + lookup_by_session_id, + (gpointer) login_session_id); + if (display != NULL && gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { + if (g_strcmp0 (active_session_id, login_session_id) != 0) { + g_debug ("GdmLocalDisplayFactory: session %s found, activating.", + login_session_id); + gdm_activate_session_by_id (factory->priv->connection, seat_id, login_session_id); + } + g_clear_pointer (&login_session_id, g_free); + g_clear_pointer (&active_session_id, g_free); + return NULL; + } + g_clear_pointer (&login_session_id, g_free); + } + g_clear_pointer (&active_session_id, g_free); +- } else if (!sd_seat_can_multi_session (seat_id)) { +- /* Ensure we don't create the same display more than once */ +- display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); +- +- if (display != NULL) { +- return NULL; +- } + } + + g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id); + + #ifdef ENABLE_USER_DISPLAY_SERVER + if (g_strcmp0 (seat_id, "seat0") == 0) { + display = gdm_local_display_new (); + if (session_type != NULL) { + g_object_set (G_OBJECT (display), "session-type", session_type, NULL); + } + } + #endif + + if (display == NULL) { + guint32 num; + + num = take_next_display_number (factory); + + display = gdm_legacy_display_new (num); + } + + g_object_set (display, "seat-id", seat_id, NULL); + g_object_set (display, "is-initial", initial, NULL); + + store_display (factory, display); + + /* let store own the ref */ + g_object_unref (display); + + if (! gdm_display_manage (display)) { +-- +2.26.0 + diff --git a/SOURCES/0022-common-remove-unnecessary-free.patch b/SOURCES/0022-common-remove-unnecessary-free.patch new file mode 100644 index 0000000..1c86eba --- /dev/null +++ b/SOURCES/0022-common-remove-unnecessary-free.patch @@ -0,0 +1,91 @@ +From d48fec7e35fb828b6ce8e0312579212a7be48c4f Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 13:04:56 -0400 +Subject: [PATCH 22/48] common: remove unnecessary free + +This commit drops an erroneous free call, that would +potentially free a dangling pointer. + +Luckily the error condition can never occur because the +error code checked is never returned, so the free call +is dead code. + +This commit removes the free call. A subsequent commit +will fix the error code checking. +--- + common/gdm-common.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/common/gdm-common.c b/common/gdm-common.c +index 59317a889..c909aceee 100644 +--- a/common/gdm-common.c ++++ b/common/gdm-common.c +@@ -391,64 +391,62 @@ gdm_activate_session_by_id (GDBusConnection *connection, + return TRUE; + } + + gboolean + gdm_get_login_window_session_id (const char *seat_id, + char **session_id) + { + gboolean ret; + int res, i; + char **sessions; + char *service_id; + char *service_class; + char *state; + + res = sd_seat_get_sessions (seat_id, &sessions, NULL, NULL); + if (res < 0) { + g_debug ("Failed to determine sessions: %s", strerror (-res)); + return FALSE; + } + + if (sessions == NULL || sessions[0] == NULL) { + *session_id = NULL; + ret = FALSE; + goto out; + } + + for (i = 0; sessions[i]; i ++) { + + res = sd_session_get_class (sessions[i], &service_class); + if (res < 0) { +- if (res == -ENOENT) { +- free (service_class); ++ if (res == -ENOENT) + continue; +- } + + g_debug ("failed to determine class of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_class, "greeter") != 0) { + free (service_class); + continue; + } + + free (service_class); + + ret = sd_session_get_state (sessions[i], &state); + if (ret < 0) { + g_debug ("failed to determine state of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (g_strcmp0 (state, "closing") == 0) { + free (state); + continue; + } + free (state); + + res = sd_session_get_service (sessions[i], &service_id); + if (res < 0) { + g_debug ("failed to determine service of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; +-- +2.26.0 + diff --git a/SOURCES/0023-common-don-t-bail-if-session-disappears-out-from-und.patch b/SOURCES/0023-common-don-t-bail-if-session-disappears-out-from-und.patch new file mode 100644 index 0000000..95a759f --- /dev/null +++ b/SOURCES/0023-common-don-t-bail-if-session-disappears-out-from-und.patch @@ -0,0 +1,133 @@ +From 72ca16c8c256e9b5f3fdf81163d5ef90b9c170b2 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 13:06:54 -0400 +Subject: [PATCH 23/48] common: don't bail if session disappears out from under + us + +It's entirely possible for a session returned by +sd_seat_get_sessions to disappear immediately after the +sd_seat_get_sessions call returns. This is especially +likely at logout time where the session will briefly be +in the "closing" state before getting reaped. + +If that happens when we're looking for a greeter session, we +stop looking for a greeter session and bail out all confused. + +This commit fixes the confusion by gracefully handling the +session disappearing by just proceeding to the next session +in the list. + +This commit is very similar to commit 155ee7eca which got +accidentally reverted during code consolidation. The main +difference is this commit checks the correct error code +of -ENXIO instead of -ENOENT, so it might actually fix +what it's ostensibly supposed to fix. +--- + common/gdm-common.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/common/gdm-common.c b/common/gdm-common.c +index c909aceee..59b8dfc44 100644 +--- a/common/gdm-common.c ++++ b/common/gdm-common.c +@@ -391,90 +391,96 @@ gdm_activate_session_by_id (GDBusConnection *connection, + return TRUE; + } + + gboolean + gdm_get_login_window_session_id (const char *seat_id, + char **session_id) + { + gboolean ret; + int res, i; + char **sessions; + char *service_id; + char *service_class; + char *state; + + res = sd_seat_get_sessions (seat_id, &sessions, NULL, NULL); + if (res < 0) { + g_debug ("Failed to determine sessions: %s", strerror (-res)); + return FALSE; + } + + if (sessions == NULL || sessions[0] == NULL) { + *session_id = NULL; + ret = FALSE; + goto out; + } + + for (i = 0; sessions[i]; i ++) { + + res = sd_session_get_class (sessions[i], &service_class); + if (res < 0) { +- if (res == -ENOENT) ++ if (res == -ENXIO) + continue; + + g_debug ("failed to determine class of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_class, "greeter") != 0) { + free (service_class); + continue; + } + + free (service_class); + + ret = sd_session_get_state (sessions[i], &state); + if (ret < 0) { ++ if (res == -ENXIO) ++ continue; ++ + g_debug ("failed to determine state of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (g_strcmp0 (state, "closing") == 0) { + free (state); + continue; + } + free (state); + + res = sd_session_get_service (sessions[i], &service_id); + if (res < 0) { ++ if (res == -ENXIO) ++ continue; ++ + g_debug ("failed to determine service of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_id, "gdm-launch-environment") == 0) { + *session_id = g_strdup (sessions[i]); + ret = TRUE; + + free (service_id); + goto out; + } + + free (service_id); + } + + *session_id = NULL; + ret = FALSE; + + out: + if (sessions) { + for (i = 0; sessions[i]; i ++) { + free (sessions[i]); + } + + free (sessions); + } + + return ret; + } +-- +2.26.0 + diff --git a/SOURCES/0024-manager-better-logind-handling.patch b/SOURCES/0024-manager-better-logind-handling.patch new file mode 100644 index 0000000..5dddea3 --- /dev/null +++ b/SOURCES/0024-manager-better-logind-handling.patch @@ -0,0 +1,138 @@ +From 685cac010ad94534b1b4f73b28768cad92b54703 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 14:01:55 -0400 +Subject: [PATCH 24/48] manager: better logind handling + +commit 9ee68d5c8 highlights we've incorrectly +used ENOENT instead of ENXIO when checking for +non-existing sessions/seats with logind. + +This commit mops up all the other cases. +--- + daemon/gdm-manager.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index 80f60d24c..367a731cc 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -361,113 +361,113 @@ find_session_for_user_on_seat (GdmManager *manager, + candidate_username); + + if (g_strcmp0 (candidate_username, username) == 0 && + g_strcmp0 (candidate_seat_id, seat_id) == 0) { + g_debug ("GdmManager: yes, found session %s", candidate_session_id); + return candidate_session; + } + + g_debug ("GdmManager: no, will not use session %s", candidate_session_id); + } + + g_debug ("GdmManager: no matching sessions found"); + return NULL; + } + + static gboolean + is_remote_session (GdmManager *self, + const char *session_id, + GError **error) + { + char *seat; + int ret; + gboolean is_remote; + + /* FIXME: The next release of logind is going to have explicit api for + * checking remoteness. + */ + seat = NULL; + ret = sd_session_get_seat (session_id, &seat); + +- if (ret < 0 && ret != -ENOENT) { ++ if (ret < 0 && ret != -ENXIO) { + g_debug ("GdmManager: Error while retrieving seat for session %s: %s", + session_id, strerror (-ret)); + } + + if (seat != NULL) { + is_remote = FALSE; + free (seat); + } else { + is_remote = TRUE; + } + + return is_remote; + } + + static char * + get_seat_id_for_session_id (const char *session_id, + GError **error) + { + int ret; + char *seat, *out_seat; + + seat = NULL; + ret = sd_session_get_seat (session_id, &seat); + +- if (ret == -ENOENT) { ++ if (ret == -ENXIO) { + out_seat = NULL; + } else if (ret < 0) { + g_set_error (error, + GDM_DISPLAY_ERROR, + GDM_DISPLAY_ERROR_GETTING_SESSION_INFO, + "Error getting uid for session id %s from systemd: %s", + session_id, + g_strerror (-ret)); + out_seat = NULL; + } else { + out_seat = g_strdup (seat); + free (seat); + } + + return out_seat; + } + + static char * + get_tty_for_session_id (const char *session_id, + GError **error) + { + int ret; + char *tty, *out_tty; + + ret = sd_session_get_tty (session_id, &tty); + +- if (ret == -ENOENT) { ++ if (ret == -ENXIO) { + out_tty = NULL; + } else if (ret < 0) { + g_set_error (error, + GDM_DISPLAY_ERROR, + GDM_DISPLAY_ERROR_GETTING_SESSION_INFO, + "Error getting tty for session id %s from systemd: %s", + session_id, + g_strerror (-ret)); + out_tty = NULL; + } else { + out_tty = g_strdup (tty); + free (tty); + } + + return out_tty; + } + + static void + get_display_and_details_for_bus_sender (GdmManager *self, + GDBusConnection *connection, + const char *sender, + GdmDisplay **out_display, + char **out_seat_id, + char **out_session_id, + char **out_tty, + GPid *out_pid, + uid_t *out_uid, + gboolean *out_is_login_screen, + gboolean *out_is_remote) + { +-- +2.26.0 + diff --git a/SOURCES/0025-session-worker-clear-VT-before-jumping-to-it.patch b/SOURCES/0025-session-worker-clear-VT-before-jumping-to-it.patch new file mode 100644 index 0000000..faed97f --- /dev/null +++ b/SOURCES/0025-session-worker-clear-VT-before-jumping-to-it.patch @@ -0,0 +1,90 @@ +From a497c065b1ac546cd28d3ef75f90b4592a5ee3c5 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 15:46:55 -0400 +Subject: [PATCH 25/48] session-worker: clear VT before jumping to it + +If we're going to jump to a new VT we should make sure it's free +of residual console text. That way if there's flicker the user +will be less likely to notice it. + +This commit sends a clear screen escape sequence to the tty +before jumping to it. +--- + daemon/gdm-session-worker.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c +index 391969d96..7ed2789da 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -943,60 +943,67 @@ fix_terminal_vt_mode (GdmSessionWorker *worker, + } + + /* VT is in the anti-social state of VT_AUTO + KD_GRAPHICS, + * fix it. + */ + succeeded = handle_terminal_vt_switches (worker, tty_fd); + mode_fixed = TRUE; + out: + if (!succeeded) { + g_error ("GdmSessionWorker: couldn't set up terminal, aborting..."); + return; + } + + g_debug ("GdmSessionWorker: VT mode did %sneed to be fixed", + mode_fixed? "" : "not "); + } + + static void + jump_to_vt (GdmSessionWorker *worker, + int vt_number) + { + int fd; + int active_vt_tty_fd; + int active_vt = -1; + struct vt_stat vt_state = { 0 }; + + g_debug ("GdmSessionWorker: jumping to VT %d", vt_number); + active_vt_tty_fd = open ("/dev/tty0", O_RDWR | O_NOCTTY); + + if (worker->priv->session_tty_fd != -1) { ++ static const char *clear_screen_escape_sequence = "\33[H\33[2J"; ++ ++ /* let's make sure the new VT is clear */ ++ write (worker->priv->session_tty_fd, ++ clear_screen_escape_sequence, ++ sizeof (clear_screen_escape_sequence)); ++ + fd = worker->priv->session_tty_fd; + + g_debug ("GdmSessionWorker: first setting graphics mode to prevent flicker"); + if (ioctl (fd, KDSETMODE, KD_GRAPHICS) < 0) { + g_debug ("GdmSessionWorker: couldn't set graphics mode: %m"); + } + + /* It's possible that the current VT was left in a broken + * combination of states (KD_GRAPHICS with VT_AUTO), that + * can't be switched away from. This call makes sure things + * are set in a way that VT_ACTIVATE should work and + * VT_WAITACTIVE shouldn't hang. + */ + fix_terminal_vt_mode (worker, active_vt_tty_fd); + } else { + fd = active_vt_tty_fd; + } + + handle_terminal_vt_switches (worker, fd); + + if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) { + g_debug ("GdmSessionWorker: couldn't get current VT: %m"); + } else { + active_vt = vt_state.v_active; + } + + if (active_vt != vt_number) { + if (ioctl (fd, VT_ACTIVATE, vt_number) < 0) { + g_debug ("GdmSessionWorker: couldn't initiate jump to VT %d: %m", + vt_number); +-- +2.26.0 + diff --git a/SOURCES/0026-manager-don-t-set-ran_once-after-running-initial-set.patch b/SOURCES/0026-manager-don-t-set-ran_once-after-running-initial-set.patch new file mode 100644 index 0000000..af73a27 --- /dev/null +++ b/SOURCES/0026-manager-don-t-set-ran_once-after-running-initial-set.patch @@ -0,0 +1,133 @@ +From cfc1dd96dee55afde7f377878905302db9cbdcf6 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 16:04:41 -0400 +Subject: [PATCH 26/48] manager: don't set ran_once after running initial-setup + +GdmManager tracks whether or not the user session has ran +once, so it won't autologin a user again after logout. + +Unfortunately the initial-setup session was counting toward the +ran_once count preventing initial-setup from logging the user +in afterward. + +This commit prevents ran_once from getting set in that case. +--- + daemon/gdm-manager.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index 367a731cc..c8197a043 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1519,105 +1519,107 @@ set_up_session (GdmManager *manager, + operation->username = username; + + g_signal_connect (user, + "notify::is-loaded", + G_CALLBACK (on_user_is_loaded_changed), + operation); + } + } + + static void + greeter_display_started (GdmManager *manager, + GdmDisplay *display) + { + if (manager->priv->ran_once) { + return; + } + + maybe_start_pending_initial_login (manager, display); + } + + static void + on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmManager *manager) + { + int status; + int display_number = -1; + char *session_type = NULL; + #ifdef WITH_PLYMOUTH + gboolean display_is_local = FALSE; ++ gboolean doing_initial_setup = FALSE; + gboolean quit_plymouth = FALSE; + + g_object_get (display, + "is-local", &display_is_local, ++ "doing-initial-setup", &doing_initial_setup, + NULL); + quit_plymouth = display_is_local && manager->priv->plymouth_is_running; + #endif + + g_object_get (display, + "x11-display-number", &display_number, + "session-type", &session_type, + NULL); + + status = gdm_display_get_status (display); + + switch (status) { + case GDM_DISPLAY_PREPARED: + case GDM_DISPLAY_MANAGED: + if ((display_number == -1 && status == GDM_DISPLAY_PREPARED) || + (display_number != -1 && status == GDM_DISPLAY_MANAGED)) { + char *session_class; + + g_object_get (display, + "session-class", &session_class, + NULL); + if (g_strcmp0 (session_class, "greeter") == 0) + set_up_session (manager, display); + g_free (session_class); + } + + if (status == GDM_DISPLAY_MANAGED) { + greeter_display_started (manager, display); + } + break; + case GDM_DISPLAY_FAILED: + case GDM_DISPLAY_UNMANAGED: + case GDM_DISPLAY_FINISHED: + #ifdef WITH_PLYMOUTH + if (quit_plymouth) { + plymouth_quit_without_transition (); + manager->priv->plymouth_is_running = FALSE; + } + #endif + +- if (status == GDM_DISPLAY_FINISHED || g_strcmp0 (session_type, "x11") == 0) { ++ if (!doing_initial_setup && (status == GDM_DISPLAY_FINISHED || g_strcmp0 (session_type, "x11") == 0)) { + manager->priv->ran_once = TRUE; + } + maybe_start_pending_initial_login (manager, display); + break; + default: + break; + } + + } + + static void + on_display_removed (GdmDisplayStore *display_store, + GdmDisplay *display, + GdmManager *manager) + { + char *id; + + gdm_display_get_id (display, &id, NULL); + g_dbus_object_manager_server_unexport (manager->priv->object_manager, id); + g_free (id); + + g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), manager); + + g_signal_emit (manager, signals[DISPLAY_REMOVED], 0, display); + } + + static void + destroy_start_user_session_operation (StartUserSessionOperation *operation) + { + g_object_set_data (G_OBJECT (operation->session), +-- +2.26.0 + diff --git a/SOURCES/0027-manager-start-initial-setup-right-away.patch b/SOURCES/0027-manager-start-initial-setup-right-away.patch new file mode 100644 index 0000000..30306a3 --- /dev/null +++ b/SOURCES/0027-manager-start-initial-setup-right-away.patch @@ -0,0 +1,418 @@ +From 0eada264dbce111730a8859152a7821a9df9c692 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 14:33:58 -0400 +Subject: [PATCH 27/48] manager: start initial setup right away + +We no longer restart the greeter as soon as it dies, since we +start the greeter on demand. This means, we no longer need to +defer starting initial setup until after the greeter respawns. + +Furthermore, it doesn't work anymore since it relied on the +respawn to trigger. + +This commit removes that code and scaffolding and just starts +initial setup directly. + +https://gitlab.gnome.org/GNOME/gdm/issues/415 +--- + daemon/gdm-manager.c | 66 +------------------------------------------- + 1 file changed, 1 insertion(+), 65 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index c8197a043..fb7b1ec4b 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -62,62 +62,60 @@ + #define GDM_MANAGER_DISPLAYS_PATH GDM_DBUS_PATH "/Displays" + + #define INITIAL_SETUP_USERNAME "gnome-initial-setup" + + typedef struct + { + GdmManager *manager; + GdmSession *session; + char *service_name; + guint idle_id; + } StartUserSessionOperation; + + struct GdmManagerPrivate + { + GdmDisplayStore *display_store; + GdmLocalDisplayFactory *local_factory; + #ifdef HAVE_LIBXDMCP + GdmXdmcpDisplayFactory *xdmcp_factory; + #endif + GList *user_sessions; + GHashTable *transient_sessions; + GHashTable *open_reauthentication_requests; + gboolean xdmcp_enabled; + + gboolean started; + gboolean show_local_greeter; + + GDBusConnection *connection; + GDBusObjectManagerServer *object_manager; + +- StartUserSessionOperation *initial_login_operation; +- + #ifdef WITH_PLYMOUTH + guint plymouth_is_running : 1; + #endif + guint ran_once : 1; + }; + + enum { + PROP_0, + PROP_XDMCP_ENABLED, + PROP_SHOW_LOCAL_GREETER + }; + + enum { + DISPLAY_ADDED, + DISPLAY_REMOVED, + LAST_SIGNAL + }; + + typedef enum { + SESSION_RECORD_LOGIN, + SESSION_RECORD_LOGOUT, + SESSION_RECORD_FAILED, + } SessionRecord; + + static guint signals [LAST_SIGNAL] = { 0, }; + + static void gdm_manager_class_init (GdmManagerClass *klass); + static void gdm_manager_init (GdmManager *manager); + static void gdm_manager_dispose (GObject *object); + +@@ -1286,96 +1284,60 @@ get_automatic_login_details (GdmManager *manager, + if (res && enabled) { + res = gdm_settings_direct_get_string (GDM_KEY_AUTO_LOGIN_USER, &username); + } + + if (enabled && res && username != NULL && username[0] != '\0') { + goto out; + } + + g_free (username); + username = NULL; + enabled = FALSE; + + out: + if (enabled) { + g_debug ("GdmDisplay: Got automatic login details for display: %d %s", + enabled, + username); + } else { + g_debug ("GdmDisplay: Got automatic login details for display: 0"); + } + + if (usernamep != NULL) { + *usernamep = username; + } else { + g_free (username); + } + + return enabled; + } + +-static void +-maybe_start_pending_initial_login (GdmManager *manager, +- GdmDisplay *greeter_display) +-{ +- StartUserSessionOperation *operation; +- char *greeter_seat_id = NULL; +- char *user_session_seat_id = NULL; +- +- /* There may be a user session waiting to be started. +- * This would happen if we couldn't start it earlier because +- * the login screen X server was coming up and two X servers +- * can't be started on the same seat at the same time. +- */ +- +- if (manager->priv->initial_login_operation == NULL) { +- return; +- } +- +- operation = manager->priv->initial_login_operation; +- +- g_object_get (G_OBJECT (greeter_display), +- "seat-id", &greeter_seat_id, +- NULL); +- g_object_get (G_OBJECT (operation->session), +- "display-seat-id", &user_session_seat_id, +- NULL); +- +- if (g_strcmp0 (greeter_seat_id, user_session_seat_id) == 0) { +- start_user_session (manager, operation); +- manager->priv->initial_login_operation = NULL; +- } +- +- g_free (greeter_seat_id); +- g_free (user_session_seat_id); +-} +- + static const char * + get_username_for_greeter_display (GdmManager *manager, + GdmDisplay *display) + { + gboolean doing_initial_setup = FALSE; + + g_object_get (G_OBJECT (display), + "doing-initial-setup", &doing_initial_setup, + NULL); + + if (doing_initial_setup) { + return INITIAL_SETUP_USERNAME; + } else { + return GDM_USERNAME; + } + } + + static void + set_up_automatic_login_session (GdmManager *manager, + GdmDisplay *display) + { + GdmSession *session; + char *display_session_type = NULL; + gboolean is_initial; + + /* 0 is root user; since the daemon talks to the session object + * directly, itself, for automatic login + */ + session = create_user_session_for_display (manager, display, 0); + +@@ -1498,131 +1460,115 @@ set_up_session (GdmManager *manager, + return; + } + #endif + + set_up_greeter_session (manager, display); + return; + } + + /* Check whether the user really exists before committing to autologin. */ + user_manager = act_user_manager_get_default (); + user = act_user_manager_get_user (user_manager, username); + g_object_get (user_manager, "is-loaded", &loaded, NULL); + + if (loaded) { + set_up_automatic_login_session_if_user_exists (manager, display, user); + } else { + UsernameLookupOperation *operation; + + operation = g_new (UsernameLookupOperation, 1); + operation->manager = g_object_ref (manager); + operation->display = g_object_ref (display); + operation->username = username; + + g_signal_connect (user, + "notify::is-loaded", + G_CALLBACK (on_user_is_loaded_changed), + operation); + } + } + +-static void +-greeter_display_started (GdmManager *manager, +- GdmDisplay *display) +-{ +- if (manager->priv->ran_once) { +- return; +- } +- +- maybe_start_pending_initial_login (manager, display); +-} +- + static void + on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmManager *manager) + { + int status; + int display_number = -1; + char *session_type = NULL; + #ifdef WITH_PLYMOUTH + gboolean display_is_local = FALSE; + gboolean doing_initial_setup = FALSE; + gboolean quit_plymouth = FALSE; + + g_object_get (display, + "is-local", &display_is_local, + "doing-initial-setup", &doing_initial_setup, + NULL); + quit_plymouth = display_is_local && manager->priv->plymouth_is_running; + #endif + + g_object_get (display, + "x11-display-number", &display_number, + "session-type", &session_type, + NULL); + + status = gdm_display_get_status (display); + + switch (status) { + case GDM_DISPLAY_PREPARED: + case GDM_DISPLAY_MANAGED: + if ((display_number == -1 && status == GDM_DISPLAY_PREPARED) || + (display_number != -1 && status == GDM_DISPLAY_MANAGED)) { + char *session_class; + + g_object_get (display, + "session-class", &session_class, + NULL); + if (g_strcmp0 (session_class, "greeter") == 0) + set_up_session (manager, display); + g_free (session_class); + } +- +- if (status == GDM_DISPLAY_MANAGED) { +- greeter_display_started (manager, display); +- } + break; + case GDM_DISPLAY_FAILED: + case GDM_DISPLAY_UNMANAGED: + case GDM_DISPLAY_FINISHED: + #ifdef WITH_PLYMOUTH + if (quit_plymouth) { + plymouth_quit_without_transition (); + manager->priv->plymouth_is_running = FALSE; + } + #endif + + if (!doing_initial_setup && (status == GDM_DISPLAY_FINISHED || g_strcmp0 (session_type, "x11") == 0)) { + manager->priv->ran_once = TRUE; + } +- maybe_start_pending_initial_login (manager, display); + break; + default: + break; + } + + } + + static void + on_display_removed (GdmDisplayStore *display_store, + GdmDisplay *display, + GdmManager *manager) + { + char *id; + + gdm_display_get_id (display, &id, NULL); + g_dbus_object_manager_server_unexport (manager->priv->object_manager, id); + g_free (id); + + g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), manager); + + g_signal_emit (manager, signals[DISPLAY_REMOVED], 0, display); + } + + static void + destroy_start_user_session_operation (StartUserSessionOperation *operation) + { + g_object_set_data (G_OBJECT (operation->session), + "start-user-session-operation", + NULL); + g_object_unref (operation->session); +@@ -1723,97 +1669,87 @@ on_start_user_session (StartUserSessionOperation *operation) + gdm_session_reset (operation->session); + destroy_start_user_session_operation (operation); + goto out; + } + + display = get_display_for_user_session (operation->session); + + g_object_get (G_OBJECT (display), "doing-initial-setup", &doing_initial_setup, NULL); + + session_id = gdm_session_get_conversation_session_id (operation->session, + operation->service_name); + + if (gdm_session_get_display_mode (operation->session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) { + /* In this case, the greeter's display is morphing into + * the user session display. Kill the greeter on this session + * and let the user session follow the same display. */ + gdm_display_stop_greeter_session (display); + g_object_set (G_OBJECT (display), + "session-class", "user", + "session-id", session_id, + NULL); + } else { + uid_t allowed_uid; + + g_object_ref (display); + if (doing_initial_setup) { + g_debug ("GdmManager: closing down initial setup display"); + gdm_display_stop_greeter_session (display); + gdm_display_unmanage (display); + gdm_display_finish (display); +- +- /* We can't start the user session until the finished display +- * starts to respawn (since starting an X server and bringing +- * one down at the same time is a no go) +- */ +- g_assert (self->priv->initial_login_operation == NULL); +- self->priv->initial_login_operation = operation; +- starting_user_session_right_away = FALSE; + } else { + g_debug ("GdmManager: session has its display server, reusing our server for another login screen"); + } + + /* The user session is going to follow the session worker + * into the new display. Untie it from this display and + * create a new session for a future user login. */ + allowed_uid = gdm_session_get_allowed_user (operation->session); + g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); + g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL); + create_user_session_for_display (operation->manager, display, allowed_uid); + + if ((g_strcmp0 (operation->service_name, "gdm-autologin") == 0) && + !gdm_session_client_is_connected (operation->session)) { + /* remove the unused prepared greeter display since we're not going + * to have a greeter */ + gdm_display_store_remove (self->priv->display_store, display); + g_object_unref (display); + } + + /* Give the user session a new display object for bookkeeping purposes */ + create_display_for_user_session (operation->manager, + operation->session, + session_id); + } + +- if (starting_user_session_right_away) { +- start_user_session (operation->manager, operation); +- } ++ start_user_session (operation->manager, operation); + + out: + return G_SOURCE_REMOVE; + } + + static void + queue_start_user_session (GdmManager *manager, + GdmSession *session, + const char *service_name) + { + StartUserSessionOperation *operation; + + operation = g_slice_new0 (StartUserSessionOperation); + operation->manager = manager; + operation->session = g_object_ref (session); + operation->service_name = g_strdup (service_name); + + operation->idle_id = g_idle_add ((GSourceFunc) on_start_user_session, operation); + g_object_set_data (G_OBJECT (session), "start-user-session-operation", operation); + } + + static void + start_user_session_if_ready (GdmManager *manager, + GdmSession *session, + const char *service_name) + { + gboolean start_when_ready; + + start_when_ready = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (session), "start-when-ready")); + if (start_when_ready) { +-- +2.26.0 + diff --git a/SOURCES/0028-gdm-wayland-session-gdm-x-session-register-after-del.patch b/SOURCES/0028-gdm-wayland-session-gdm-x-session-register-after-del.patch new file mode 100644 index 0000000..ca43916 --- /dev/null +++ b/SOURCES/0028-gdm-wayland-session-gdm-x-session-register-after-del.patch @@ -0,0 +1,309 @@ +From 00954e2f7a5668e582c7b498f9f1548c7d07ed33 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 16:09:02 -0400 +Subject: [PATCH 28/48] gdm-wayland-session,gdm-x-session: register after delay + +Right now gdm-x-session registers with GDM as soon as the +X server is started, and gdm-wayland-session registers as +soon as the session is started. + +Ideally registration wouldn't happen until the session +says things started successfully. + +This commit inches us toward that ideal but adding a little +timeout before proceeding with registration. + +A future commit will add a new xsession file key to allow +us to know whether or not the session manager of the session +supports doing registration. +--- + daemon/gdm-wayland-session.c | 23 ++++++++++++++++------- + daemon/gdm-x-session.c | 25 +++++++++++++++++-------- + 2 files changed, 33 insertions(+), 15 deletions(-) + +diff --git a/daemon/gdm-wayland-session.c b/daemon/gdm-wayland-session.c +index 94f49e19c..de1991b34 100644 +--- a/daemon/gdm-wayland-session.c ++++ b/daemon/gdm-wayland-session.c +@@ -427,60 +427,75 @@ init_state (State **state) + static State state_allocation; + + *state = &state_allocation; + } + + static void + clear_state (State **out_state) + { + State *state = *out_state; + + g_clear_object (&state->cancellable); + g_clear_object (&state->bus_connection); + g_clear_object (&state->session_subprocess); + g_clear_pointer (&state->environment, g_strfreev); + g_clear_pointer (&state->main_loop, g_main_loop_unref); + *out_state = NULL; + } + + static gboolean + on_sigterm (State *state) + { + g_cancellable_cancel (state->cancellable); + + if (g_main_loop_is_running (state->main_loop)) { + g_main_loop_quit (state->main_loop); + } + + return G_SOURCE_CONTINUE; + } + ++static gboolean ++on_registration_delay_complete (State *state) ++{ ++ gboolean ret; ++ ++ ret = register_display (state, state->cancellable); ++ ++ if (!ret) { ++ g_printerr ("Unable to register display with display manager\n"); ++ g_main_loop_quit (state->main_loop); ++ } ++ ++ return G_SOURCE_REMOVE; ++} ++ + int + main (int argc, + char **argv) + { + State *state = NULL; + GOptionContext *context = NULL; + static char **args = NULL; + gboolean debug = FALSE; + gboolean ret; + int exit_status = EX_OK; + static GOptionEntry entries [] = { + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, "", "" }, + { NULL } + }; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + setlocale (LC_ALL, ""); + + gdm_log_init (); + + context = g_option_context_new (_("GNOME Display Manager Wayland Session Launcher")); + g_option_context_add_main_entries (context, entries, NULL); + + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); + + if (args == NULL || args[0] == NULL || args[1] != NULL) { + g_warning ("gdm-wayland-session takes one argument (the session)"); + exit_status = EX_USAGE; +@@ -501,55 +516,49 @@ main (int argc, + } + + gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug); + state->debug_enabled = debug; + + gdm_log_set_debug (debug); + + state->main_loop = g_main_loop_new (NULL, FALSE); + state->cancellable = g_cancellable_new (); + + g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state); + + ret = spawn_bus (state, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run session message bus\n"); + exit_status = EX_SOFTWARE; + goto out; + } + + import_environment (state, state->cancellable); + + ret = spawn_session (state, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run session\n"); + exit_status = EX_SOFTWARE; + goto out; + } + +- ret = register_display (state, state->cancellable); +- +- if (!ret) { +- g_printerr ("Unable to register display with display manager\n"); +- exit_status = EX_SOFTWARE; +- goto out; +- } ++ g_timeout_add_seconds (2, (GSourceFunc) on_registration_delay_complete, state); + + g_main_loop_run (state->main_loop); + + /* Only use exit status of session if we're here because it exit */ + + if (state->session_subprocess == NULL) { + exit_status = state->session_exit_status; + } + + out: + if (state != NULL) { + signal_subprocesses (state); + wait_on_subprocesses (state); + clear_state (&state); + } + + return exit_status; + } +diff --git a/daemon/gdm-x-session.c b/daemon/gdm-x-session.c +index 3b2fcef47..412999cf5 100644 +--- a/daemon/gdm-x-session.c ++++ b/daemon/gdm-x-session.c +@@ -783,60 +783,75 @@ init_state (State **state) + } + + static void + clear_state (State **out_state) + { + State *state = *out_state; + + g_clear_object (&state->cancellable); + g_clear_object (&state->bus_connection); + g_clear_object (&state->session_subprocess); + g_clear_object (&state->x_subprocess); + g_clear_pointer (&state->environment, g_strfreev); + g_clear_pointer (&state->auth_file, g_free); + g_clear_pointer (&state->display_name, g_free); + g_clear_pointer (&state->main_loop, g_main_loop_unref); + *out_state = NULL; + } + + static gboolean + on_sigterm (State *state) + { + g_cancellable_cancel (state->cancellable); + + if (g_main_loop_is_running (state->main_loop)) { + g_main_loop_quit (state->main_loop); + } + + return G_SOURCE_CONTINUE; + } + ++static gboolean ++on_registration_delay_complete (State *state) ++{ ++ gboolean ret; ++ ++ ret = register_display (state, state->cancellable); ++ ++ if (!ret) { ++ g_printerr ("Unable to register display with display manager\n"); ++ g_main_loop_quit (state->main_loop); ++ } ++ ++ return G_SOURCE_REMOVE; ++} ++ + int + main (int argc, + char **argv) + { + State *state = NULL; + GOptionContext *context = NULL; + static char **args = NULL; + static gboolean run_script = FALSE; + static gboolean allow_remote_connections = FALSE; + gboolean debug = FALSE; + gboolean ret; + int exit_status = EX_OK; + static GOptionEntry entries [] = { + { "run-script", 'r', 0, G_OPTION_ARG_NONE, &run_script, N_("Run program through /etc/gdm/Xsession wrapper script"), NULL }, + { "allow-remote-connections", 'a', 0, G_OPTION_ARG_NONE, &allow_remote_connections, N_("Listen on TCP socket"), NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, "", "" }, + { NULL } + }; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + setlocale (LC_ALL, ""); + + gdm_log_init (); + + context = g_option_context_new (_("GNOME Display Manager X Session Launcher")); + g_option_context_add_main_entries (context, entries, NULL); + + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); +@@ -869,63 +884,57 @@ main (int argc, + state->cancellable = g_cancellable_new (); + + g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state); + + ret = spawn_x_server (state, allow_remote_connections, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run X server\n"); + exit_status = EX_SOFTWARE; + goto out; + } + + ret = spawn_bus (state, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run session message bus\n"); + exit_status = EX_SOFTWARE; + goto out; + } + + import_environment (state, state->cancellable); + + ret = update_bus_environment (state, state->cancellable); + + if (!ret) { + g_printerr ("Unable to update bus environment\n"); + exit_status = EX_SOFTWARE; + goto out; + } + +- ret = register_display (state, state->cancellable); +- +- if (!ret) { +- g_printerr ("Unable to register display with display manager\n"); +- exit_status = EX_SOFTWARE; +- goto out; +- } +- + ret = spawn_session (state, run_script, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run session\n"); + exit_status = EX_SOFTWARE; + goto out; + } + ++ g_timeout_add_seconds (2, (GSourceFunc) on_registration_delay_complete, state); ++ + g_main_loop_run (state->main_loop); + + /* Only use exit status of session if we're here because it exit */ + + if (state->session_subprocess == NULL) { + exit_status = state->session_exit_status; + } + + out: + if (state != NULL) { + signal_subprocesses (state); + wait_on_subprocesses (state); + clear_state (&state); + } + + return exit_status; + } +-- +2.26.0 + diff --git a/SOURCES/0029-local-display-factory-defer-killing-greeter-until-ne.patch b/SOURCES/0029-local-display-factory-defer-killing-greeter-until-ne.patch new file mode 100644 index 0000000..b19aed3 --- /dev/null +++ b/SOURCES/0029-local-display-factory-defer-killing-greeter-until-ne.patch @@ -0,0 +1,399 @@ +From 7bd3a19ad434754a91887c8b4baaaa23e9b30710 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 15:33:00 -0400 +Subject: [PATCH 29/48] local-display-factory: defer killing greeter until new + session registers + +At the moment we kill the greeter the second the VT change to the new +session happens. + +That can cause flicker if the new session doesn't take over the display +quickly enough. + +This commit defers killing the greeter until the new display registers. + +Closes https://gitlab.gnome.org/GNOME/gdm/issues/413 +--- + daemon/gdm-display.h | 1 + + daemon/gdm-local-display-factory.c | 43 +++++++++++++++++++++++++----- + 2 files changed, 38 insertions(+), 6 deletions(-) + +diff --git a/daemon/gdm-display.h b/daemon/gdm-display.h +index 6d5e88df9..33dc3be41 100644 +--- a/daemon/gdm-display.h ++++ b/daemon/gdm-display.h +@@ -13,60 +13,61 @@ + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + + #ifndef __GDM_DISPLAY_H + #define __GDM_DISPLAY_H + + #include + #include + + G_BEGIN_DECLS + + #define GDM_TYPE_DISPLAY (gdm_display_get_type ()) + #define GDM_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DISPLAY, GdmDisplay)) + #define GDM_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DISPLAY, GdmDisplayClass)) + #define GDM_IS_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DISPLAY)) + #define GDM_IS_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DISPLAY)) + #define GDM_DISPLAY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DISPLAY, GdmDisplayClass)) + + typedef struct GdmDisplayPrivate GdmDisplayPrivate; + + typedef enum { + GDM_DISPLAY_UNMANAGED = 0, + GDM_DISPLAY_PREPARED, + GDM_DISPLAY_MANAGED, ++ GDM_DISPLAY_WAITING_TO_FINISH, + GDM_DISPLAY_FINISHED, + GDM_DISPLAY_FAILED, + } GdmDisplayStatus; + + typedef struct + { + GObject parent; + GdmDisplayPrivate *priv; + } GdmDisplay; + + typedef struct + { + GObjectClass parent_class; + + /* methods */ + gboolean (*prepare) (GdmDisplay *display); + void (*manage) (GdmDisplay *self); + } GdmDisplayClass; + + typedef enum + { + GDM_DISPLAY_ERROR_GENERAL, + GDM_DISPLAY_ERROR_GETTING_USER_INFO, + GDM_DISPLAY_ERROR_GETTING_SESSION_INFO, + } GdmDisplayError; + + #define GDM_DISPLAY_ERROR gdm_display_error_quark () + + GQuark gdm_display_error_quark (void); + GType gdm_display_get_type (void); +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 127127005..bc6ac6855 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -241,60 +241,90 @@ gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *fact + + display = gdm_legacy_display_new (num); + } + #endif + + g_object_set (display, + "seat-id", "seat0", + "allow-timed-login", FALSE, + NULL); + + store_display (factory, display); + + if (! gdm_display_manage (display)) { + display = NULL; + goto out; + } + + if (! gdm_display_get_id (display, id, NULL)) { + display = NULL; + goto out; + } + + ret = TRUE; + out: + /* ref either held by store or not at all */ + g_object_unref (display); + + return ret; + } + ++static gboolean ++finish_display_on_seat_if_waiting (GdmDisplayStore *display_store, ++ GdmDisplay *display, ++ const char *seat_id) ++{ ++ if (gdm_display_get_status (display) != GDM_DISPLAY_WAITING_TO_FINISH) ++ return FALSE; ++ ++ g_debug ("GdmLocalDisplayFactory: finish background display\n"); ++ gdm_display_stop_greeter_session (display); ++ gdm_display_unmanage (display); ++ gdm_display_finish (display); ++ ++ return FALSE; ++} ++ ++static void ++finish_waiting_displays_on_seat (GdmLocalDisplayFactory *factory, ++ const char *seat_id) ++{ ++ GdmDisplayStore *store; ++ ++ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); ++ ++ gdm_display_store_foreach (store, ++ (GdmDisplayStoreFunc) finish_display_on_seat_if_waiting, ++ (gpointer) ++ seat_id); ++} ++ + static void + on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmLocalDisplayFactory *factory) + { + int status; + int num; + char *seat_id = NULL; + char *session_type = NULL; + char *session_class = NULL; + gboolean is_initial = TRUE; + gboolean is_local = TRUE; + + num = -1; + gdm_display_get_x11_display_number (display, &num, NULL); + + g_object_get (display, + "seat-id", &seat_id, + "is-initial", &is_initial, + "is-local", &is_local, + "session-type", &session_type, + "session-class", &session_class, + NULL); + + status = gdm_display_get_status (display); + + g_debug ("GdmLocalDisplayFactory: display status changed: %d", status); + switch (status) { + case GDM_DISPLAY_FINISHED: + /* remove the display number from factory->priv->used_display_numbers +@@ -324,60 +354,63 @@ on_display_status_changed (GdmDisplay *display, + /* Create a new equivalent display if it was static */ + if (is_local) { + + factory->priv->num_failures++; + + if (factory->priv->num_failures > MAX_DISPLAY_FAILURES) { + /* oh shit */ + g_warning ("GdmLocalDisplayFactory: maximum number of X display failures reached: check X server log for errors"); + } else { + #ifdef ENABLE_WAYLAND_SUPPORT + if (g_strcmp0 (session_type, "wayland") == 0) { + g_free (session_type); + session_type = NULL; + + /* workaround logind race for now + * bug 1643874 + */ + sleep (2); + } + + #endif + create_display (factory, seat_id, session_type, is_initial); + } + } + break; + case GDM_DISPLAY_UNMANAGED: + break; + case GDM_DISPLAY_PREPARED: + break; + case GDM_DISPLAY_MANAGED: ++ finish_waiting_displays_on_seat (factory, seat_id); ++ break; ++ case GDM_DISPLAY_WAITING_TO_FINISH: + break; + default: + g_assert_not_reached (); + break; + } + + g_free (seat_id); + g_free (session_type); + g_free (session_class); + } + + static gboolean + lookup_by_seat_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + char *current; + gboolean res; + + g_object_get (G_OBJECT (display), "seat-id", ¤t, NULL); + + res = g_strcmp0 (current, looking_for) == 0; + + g_free(current); + + return res; + } + + static gboolean +@@ -561,84 +594,82 @@ on_seat_new (GDBusConnection *connection, + + static void + on_seat_removed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) + { + const char *seat; + + g_variant_get (parameters, "(&s&o)", &seat, NULL); + delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat); + } + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + static gboolean + lookup_by_session_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + const char *current; + + current = gdm_display_get_session_id (display); + return g_strcmp0 (current, looking_for) == 0; + } + + static void +-maybe_stop_greeter_display (GdmDisplay *display) ++maybe_stop_greeter_in_background (GdmDisplay *display) + { + g_autofree char *display_session_type = NULL; + + if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) { + g_debug ("GdmLocalDisplayFactory: login window not in managed state, so ignoring"); + return; + } + + g_object_get (G_OBJECT (display), + "session-type", &display_session_type, + NULL); + + /* we can only stop greeter for wayland sessions, since + * X server would jump back on exit */ + if (g_strcmp0 (display_session_type, "wayland") != 0) { + g_debug ("GdmLocalDisplayFactory: login window is running on Xorg, so ignoring"); + return; + } + +- g_debug ("GdmLocalDisplayFactory: killing login window since its now unused"); +- gdm_display_stop_greeter_session (display); +- gdm_display_unmanage (display); +- gdm_display_finish (display); ++ g_debug ("GdmLocalDisplayFactory: killing login window once its unused"); ++ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); + } + + static gboolean + on_vt_changed (GIOChannel *source, + GIOCondition condition, + GdmLocalDisplayFactory *factory) + { + GIOStatus status; + static const char *tty_of_initial_vt = "tty" GDM_INITIAL_VT; + g_autofree char *tty_of_previous_vt = NULL; + g_autofree char *tty_of_active_vt = NULL; + g_autofree char *login_session_id = NULL; + g_autofree char *active_session_id = NULL; + const char *session_type = NULL; + int ret; + + g_debug ("GdmLocalDisplayFactory: received VT change event"); + g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL); + + if (condition & G_IO_PRI) { + g_autoptr (GError) error = NULL; + status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error); + + if (error != NULL) { + g_warning ("could not read active VT from kernel: %s", error->message); + } + switch (status) { + case G_IO_STATUS_ERROR: + return G_SOURCE_REMOVE; + case G_IO_STATUS_EOF: +@@ -678,61 +709,61 @@ on_vt_changed (GIOChannel *source, + return G_SOURCE_CONTINUE; + } + + g_debug ("GdmLocalDisplayFactory: VT changed from %s to %s", + tty_of_previous_vt, factory->priv->tty_of_active_vt); + + /* if the old VT was running a wayland login screen kill it + */ + if (gdm_get_login_window_session_id ("seat0", &login_session_id)) { + unsigned int vt; + + ret = sd_session_get_vt (login_session_id, &vt); + if (ret == 0 && vt != 0) { + g_autofree char *tty_of_login_window_vt = NULL; + + tty_of_login_window_vt = g_strdup_printf ("tty%u", vt); + + g_debug ("GdmLocalDisplayFactory: tty of login window is %s", tty_of_login_window_vt); + if (g_strcmp0 (tty_of_login_window_vt, tty_of_previous_vt) == 0) { + GdmDisplayStore *store; + GdmDisplay *display; + + g_debug ("GdmLocalDisplayFactory: VT switched from login window"); + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + display = gdm_display_store_find (store, + lookup_by_session_id, + (gpointer) login_session_id); + + if (display != NULL) +- maybe_stop_greeter_display (display); ++ maybe_stop_greeter_in_background (display); + } else { + g_debug ("GdmLocalDisplayFactory: VT not switched from login window"); + } + } + } + + /* if user jumped back to initial vt and it's empty put a login screen + * on it (unless a login screen is already running elsewhere, then + * jump to that login screen) + */ + if (strcmp (factory->priv->tty_of_active_vt, tty_of_initial_vt) != 0) { + g_debug ("GdmLocalDisplayFactory: active VT is not initial VT, so ignoring"); + return G_SOURCE_CONTINUE; + } + + ret = sd_seat_get_active ("seat0", &active_session_id, NULL); + + if (ret == 0) { + g_autofree char *state = NULL; + ret = sd_session_get_state (active_session_id, &state); + + /* if there's something already running on the active VT then bail */ + if (ret == 0 && g_strcmp0 (state, "closing") != 0) { + g_debug ("GdmLocalDisplayFactory: initial VT is in use, so ignoring"); + return G_SOURCE_CONTINUE; + } + } + + if (gdm_local_display_factory_use_wayland ()) + session_type = "wayland"; +-- +2.26.0 + diff --git a/SOURCES/0030-daemon-Move-the-waiting-the-session-to-have-taken-ov.patch b/SOURCES/0030-daemon-Move-the-waiting-the-session-to-have-taken-ov.patch new file mode 100644 index 0000000..fcafa2d --- /dev/null +++ b/SOURCES/0030-daemon-Move-the-waiting-the-session-to-have-taken-ov.patch @@ -0,0 +1,708 @@ +From e3083a2a97aeda3fa1155c1cd9c4458a918a8ccb Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 4 Sep 2018 10:56:45 +0200 +Subject: [PATCH 30/48] daemon: Move the waiting the session to have taken over + the fb to gdm-local-display-factory + +Commit 708618746683 ("gdm-wayland-session,gdm-x-session: register after +delay") delayed displays changing their status from PREPARED to MANAGED +so that their status would not change until the session has had a change +to install its own framebuffer and tell the GPU to scanout this new fb. + +Commit 74ee77717df7 ("local-display-factory: defer killing greeter until +new session registers") uses this to avoid a flicker when transitioning +from the greeter to the user-session by deferring the stopping of the +greeter-session until the new display moves to the MANAGED state. + +But this only works when transitioning to a new user-session, when moving +to an existing user-session (fast user switching) the display already +is in MANAGED state and instead of deferring the stopping of the greeter +commit 74ee77717df7 causes us to now never stop the greeter-session. + +This commit fixes this by starting a timeout when switching away from +the initial-vt and letting that timeout stop the greeter-session. + +This commit removes the finish_waiting_displays_on_seat() call when the +display's status changes to MANAGED, so that we still only have one code +path stopping the greeter and not two. + +This means we also no longer need to delay registering the display. So this +commit removes the code adding the delay (reverts commit 74ee77717df7). + +Note this commit uses a delay of 10 seconds, rather then 2 seconds. The +transition to a new user-session takes about 8 seconds on my budget +Apollo Lake based laptop (with SSD). + +Note this all really is a workaround, the proper solution for this would +be able to tell the kernel to keep the greeter framebuffer around until +a new framebuffer is installed. There is a patch to add a new unref_fb +ioctl for this: https://www.spinics.net/lists/dri-devel/msg140912.html . +We need to get this patch upstream and teach mutter to use it. +--- + daemon/gdm-local-display-factory.c | 29 ++++++++++++++++++++++++++--- + daemon/gdm-wayland-session.c | 23 +++++++---------------- + daemon/gdm-x-session.c | 25 ++++++++----------------- + 3 files changed, 41 insertions(+), 36 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index bc6ac6855..be6b377be 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -22,76 +22,78 @@ + + #include + #include + + #include + #include + #include + #include + + #include + + #include "gdm-common.h" + #include "gdm-manager.h" + #include "gdm-display-factory.h" + #include "gdm-local-display-factory.h" + #include "gdm-local-display-factory-glue.h" + + #include "gdm-settings-keys.h" + #include "gdm-settings-direct.h" + #include "gdm-display-store.h" + #include "gdm-local-display.h" + #include "gdm-legacy-display.h" + + #define GDM_LOCAL_DISPLAY_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_LOCAL_DISPLAY_FACTORY, GdmLocalDisplayFactoryPrivate)) + + #define GDM_DBUS_PATH "/org/gnome/DisplayManager" + #define GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH GDM_DBUS_PATH "/LocalDisplayFactory" + #define GDM_MANAGER_DBUS_NAME "org.gnome.DisplayManager.LocalDisplayFactory" + + #define MAX_DISPLAY_FAILURES 5 ++#define WAIT_TO_FINISH_TIMEOUT 10 /* seconds */ + + struct GdmLocalDisplayFactoryPrivate + { + GdmDBusLocalDisplayFactory *skeleton; + GDBusConnection *connection; + GHashTable *used_display_numbers; + + /* FIXME: this needs to be per seat? */ + guint num_failures; + + guint seat_new_id; + guint seat_removed_id; + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + char *tty_of_active_vt; + guint active_vt_watch_id; ++ guint wait_to_finish_timeout_id; + #endif + }; + + enum { + PROP_0, + }; + + static void gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass); + static void gdm_local_display_factory_init (GdmLocalDisplayFactory *factory); + static void gdm_local_display_factory_finalize (GObject *object); + + static GdmDisplay *create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial_display); + + static void on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmLocalDisplayFactory *factory); + + static gboolean gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory); + static gpointer local_display_factory_object = NULL; + static gboolean lookup_by_session_id (const char *id, + GdmDisplay *display, + gpointer user_data); + + G_DEFINE_TYPE (GdmLocalDisplayFactory, gdm_local_display_factory, GDM_TYPE_DISPLAY_FACTORY) + + GQuark + gdm_local_display_factory_error_quark (void) +@@ -354,61 +356,60 @@ on_display_status_changed (GdmDisplay *display, + /* Create a new equivalent display if it was static */ + if (is_local) { + + factory->priv->num_failures++; + + if (factory->priv->num_failures > MAX_DISPLAY_FAILURES) { + /* oh shit */ + g_warning ("GdmLocalDisplayFactory: maximum number of X display failures reached: check X server log for errors"); + } else { + #ifdef ENABLE_WAYLAND_SUPPORT + if (g_strcmp0 (session_type, "wayland") == 0) { + g_free (session_type); + session_type = NULL; + + /* workaround logind race for now + * bug 1643874 + */ + sleep (2); + } + + #endif + create_display (factory, seat_id, session_type, is_initial); + } + } + break; + case GDM_DISPLAY_UNMANAGED: + break; + case GDM_DISPLAY_PREPARED: + break; + case GDM_DISPLAY_MANAGED: +- finish_waiting_displays_on_seat (factory, seat_id); + break; + case GDM_DISPLAY_WAITING_TO_FINISH: + break; + default: + g_assert_not_reached (); + break; + } + + g_free (seat_id); + g_free (session_type); + g_free (session_class); + } + + static gboolean + lookup_by_seat_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + char *current; + gboolean res; + + g_object_get (G_OBJECT (display), "seat-id", ¤t, NULL); + + res = g_strcmp0 (current, looking_for) == 0; + + g_free(current); + + return res; + } +@@ -593,83 +594,101 @@ on_seat_new (GDBusConnection *connection, + } + + static void + on_seat_removed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) + { + const char *seat; + + g_variant_get (parameters, "(&s&o)", &seat, NULL); + delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat); + } + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + static gboolean + lookup_by_session_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + const char *current; + + current = gdm_display_get_session_id (display); + return g_strcmp0 (current, looking_for) == 0; + } + ++static gboolean ++wait_to_finish_timeout (GdmLocalDisplayFactory *factory) ++{ ++ finish_waiting_displays_on_seat (factory, "seat0"); ++ factory->priv->wait_to_finish_timeout_id = 0; ++ return G_SOURCE_REMOVE; ++} ++ + static void +-maybe_stop_greeter_in_background (GdmDisplay *display) ++maybe_stop_greeter_in_background (GdmLocalDisplayFactory *factory, ++ GdmDisplay *display) + { + g_autofree char *display_session_type = NULL; + + if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) { + g_debug ("GdmLocalDisplayFactory: login window not in managed state, so ignoring"); + return; + } + + g_object_get (G_OBJECT (display), + "session-type", &display_session_type, + NULL); + + /* we can only stop greeter for wayland sessions, since + * X server would jump back on exit */ + if (g_strcmp0 (display_session_type, "wayland") != 0) { + g_debug ("GdmLocalDisplayFactory: login window is running on Xorg, so ignoring"); + return; + } + + g_debug ("GdmLocalDisplayFactory: killing login window once its unused"); + g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); ++ ++ /* We stop the greeter after a timeout to avoid flicker */ ++ if (factory->priv->wait_to_finish_timeout_id != 0) ++ g_source_remove (factory->priv->wait_to_finish_timeout_id); ++ ++ factory->priv->wait_to_finish_timeout_id = ++ g_timeout_add_seconds (WAIT_TO_FINISH_TIMEOUT, ++ (GSourceFunc)wait_to_finish_timeout, ++ factory); + } + + static gboolean + on_vt_changed (GIOChannel *source, + GIOCondition condition, + GdmLocalDisplayFactory *factory) + { + GIOStatus status; + static const char *tty_of_initial_vt = "tty" GDM_INITIAL_VT; + g_autofree char *tty_of_previous_vt = NULL; + g_autofree char *tty_of_active_vt = NULL; + g_autofree char *login_session_id = NULL; + g_autofree char *active_session_id = NULL; + const char *session_type = NULL; + int ret; + + g_debug ("GdmLocalDisplayFactory: received VT change event"); + g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL); + + if (condition & G_IO_PRI) { + g_autoptr (GError) error = NULL; + status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error); + + if (error != NULL) { + g_warning ("could not read active VT from kernel: %s", error->message); + } + switch (status) { + case G_IO_STATUS_ERROR: + return G_SOURCE_REMOVE; + case G_IO_STATUS_EOF: +@@ -709,61 +728,61 @@ on_vt_changed (GIOChannel *source, + return G_SOURCE_CONTINUE; + } + + g_debug ("GdmLocalDisplayFactory: VT changed from %s to %s", + tty_of_previous_vt, factory->priv->tty_of_active_vt); + + /* if the old VT was running a wayland login screen kill it + */ + if (gdm_get_login_window_session_id ("seat0", &login_session_id)) { + unsigned int vt; + + ret = sd_session_get_vt (login_session_id, &vt); + if (ret == 0 && vt != 0) { + g_autofree char *tty_of_login_window_vt = NULL; + + tty_of_login_window_vt = g_strdup_printf ("tty%u", vt); + + g_debug ("GdmLocalDisplayFactory: tty of login window is %s", tty_of_login_window_vt); + if (g_strcmp0 (tty_of_login_window_vt, tty_of_previous_vt) == 0) { + GdmDisplayStore *store; + GdmDisplay *display; + + g_debug ("GdmLocalDisplayFactory: VT switched from login window"); + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + display = gdm_display_store_find (store, + lookup_by_session_id, + (gpointer) login_session_id); + + if (display != NULL) +- maybe_stop_greeter_in_background (display); ++ maybe_stop_greeter_in_background (factory, display); + } else { + g_debug ("GdmLocalDisplayFactory: VT not switched from login window"); + } + } + } + + /* if user jumped back to initial vt and it's empty put a login screen + * on it (unless a login screen is already running elsewhere, then + * jump to that login screen) + */ + if (strcmp (factory->priv->tty_of_active_vt, tty_of_initial_vt) != 0) { + g_debug ("GdmLocalDisplayFactory: active VT is not initial VT, so ignoring"); + return G_SOURCE_CONTINUE; + } + + ret = sd_seat_get_active ("seat0", &active_session_id, NULL); + + if (ret == 0) { + g_autofree char *state = NULL; + ret = sd_session_get_state (active_session_id, &state); + + /* if there's something already running on the active VT then bail */ + if (ret == 0 && g_strcmp0 (state, "closing") != 0) { + g_debug ("GdmLocalDisplayFactory: initial VT is in use, so ignoring"); + return G_SOURCE_CONTINUE; + } + } + + if (gdm_local_display_factory_use_wayland ()) + session_type = "wayland"; +@@ -803,60 +822,64 @@ gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory) + g_object_unref); + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + io_channel = g_io_channel_new_file ("/sys/class/tty/tty0/active", "r", NULL); + + if (io_channel != NULL) { + factory->priv->active_vt_watch_id = + g_io_add_watch (io_channel, + G_IO_PRI, + (GIOFunc) + on_vt_changed, + factory); + } + #endif + } + + static void + gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory) + { + if (factory->priv->seat_new_id) { + g_dbus_connection_signal_unsubscribe (factory->priv->connection, + factory->priv->seat_new_id); + factory->priv->seat_new_id = 0; + } + if (factory->priv->seat_removed_id) { + g_dbus_connection_signal_unsubscribe (factory->priv->connection, + factory->priv->seat_removed_id); + factory->priv->seat_removed_id = 0; + } + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) ++ if (factory->priv->wait_to_finish_timeout_id != 0) { ++ g_source_remove (factory->priv->wait_to_finish_timeout_id); ++ factory->priv->wait_to_finish_timeout_id = 0; ++ } + if (factory->priv->active_vt_watch_id) { + g_source_remove (factory->priv->active_vt_watch_id); + factory->priv->active_vt_watch_id = 0; + } + + g_clear_pointer (&factory->priv->tty_of_active_vt, g_free); + #endif + } + + static void + on_display_added (GdmDisplayStore *display_store, + const char *id, + GdmLocalDisplayFactory *factory) + { + GdmDisplay *display; + + display = gdm_display_store_lookup (display_store, id); + + if (display != NULL) { + g_signal_connect_object (display, "notify::status", + G_CALLBACK (on_display_status_changed), + factory, + 0); + + g_object_weak_ref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory); + } + } + + static void + on_display_removed (GdmDisplayStore *display_store, +diff --git a/daemon/gdm-wayland-session.c b/daemon/gdm-wayland-session.c +index de1991b34..94f49e19c 100644 +--- a/daemon/gdm-wayland-session.c ++++ b/daemon/gdm-wayland-session.c +@@ -427,75 +427,60 @@ init_state (State **state) + static State state_allocation; + + *state = &state_allocation; + } + + static void + clear_state (State **out_state) + { + State *state = *out_state; + + g_clear_object (&state->cancellable); + g_clear_object (&state->bus_connection); + g_clear_object (&state->session_subprocess); + g_clear_pointer (&state->environment, g_strfreev); + g_clear_pointer (&state->main_loop, g_main_loop_unref); + *out_state = NULL; + } + + static gboolean + on_sigterm (State *state) + { + g_cancellable_cancel (state->cancellable); + + if (g_main_loop_is_running (state->main_loop)) { + g_main_loop_quit (state->main_loop); + } + + return G_SOURCE_CONTINUE; + } + +-static gboolean +-on_registration_delay_complete (State *state) +-{ +- gboolean ret; +- +- ret = register_display (state, state->cancellable); +- +- if (!ret) { +- g_printerr ("Unable to register display with display manager\n"); +- g_main_loop_quit (state->main_loop); +- } +- +- return G_SOURCE_REMOVE; +-} +- + int + main (int argc, + char **argv) + { + State *state = NULL; + GOptionContext *context = NULL; + static char **args = NULL; + gboolean debug = FALSE; + gboolean ret; + int exit_status = EX_OK; + static GOptionEntry entries [] = { + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, "", "" }, + { NULL } + }; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + setlocale (LC_ALL, ""); + + gdm_log_init (); + + context = g_option_context_new (_("GNOME Display Manager Wayland Session Launcher")); + g_option_context_add_main_entries (context, entries, NULL); + + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); + + if (args == NULL || args[0] == NULL || args[1] != NULL) { + g_warning ("gdm-wayland-session takes one argument (the session)"); + exit_status = EX_USAGE; +@@ -516,49 +501,55 @@ main (int argc, + } + + gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug); + state->debug_enabled = debug; + + gdm_log_set_debug (debug); + + state->main_loop = g_main_loop_new (NULL, FALSE); + state->cancellable = g_cancellable_new (); + + g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state); + + ret = spawn_bus (state, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run session message bus\n"); + exit_status = EX_SOFTWARE; + goto out; + } + + import_environment (state, state->cancellable); + + ret = spawn_session (state, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run session\n"); + exit_status = EX_SOFTWARE; + goto out; + } + +- g_timeout_add_seconds (2, (GSourceFunc) on_registration_delay_complete, state); ++ ret = register_display (state, state->cancellable); ++ ++ if (!ret) { ++ g_printerr ("Unable to register display with display manager\n"); ++ exit_status = EX_SOFTWARE; ++ goto out; ++ } + + g_main_loop_run (state->main_loop); + + /* Only use exit status of session if we're here because it exit */ + + if (state->session_subprocess == NULL) { + exit_status = state->session_exit_status; + } + + out: + if (state != NULL) { + signal_subprocesses (state); + wait_on_subprocesses (state); + clear_state (&state); + } + + return exit_status; + } +diff --git a/daemon/gdm-x-session.c b/daemon/gdm-x-session.c +index 412999cf5..3b2fcef47 100644 +--- a/daemon/gdm-x-session.c ++++ b/daemon/gdm-x-session.c +@@ -783,75 +783,60 @@ init_state (State **state) + } + + static void + clear_state (State **out_state) + { + State *state = *out_state; + + g_clear_object (&state->cancellable); + g_clear_object (&state->bus_connection); + g_clear_object (&state->session_subprocess); + g_clear_object (&state->x_subprocess); + g_clear_pointer (&state->environment, g_strfreev); + g_clear_pointer (&state->auth_file, g_free); + g_clear_pointer (&state->display_name, g_free); + g_clear_pointer (&state->main_loop, g_main_loop_unref); + *out_state = NULL; + } + + static gboolean + on_sigterm (State *state) + { + g_cancellable_cancel (state->cancellable); + + if (g_main_loop_is_running (state->main_loop)) { + g_main_loop_quit (state->main_loop); + } + + return G_SOURCE_CONTINUE; + } + +-static gboolean +-on_registration_delay_complete (State *state) +-{ +- gboolean ret; +- +- ret = register_display (state, state->cancellable); +- +- if (!ret) { +- g_printerr ("Unable to register display with display manager\n"); +- g_main_loop_quit (state->main_loop); +- } +- +- return G_SOURCE_REMOVE; +-} +- + int + main (int argc, + char **argv) + { + State *state = NULL; + GOptionContext *context = NULL; + static char **args = NULL; + static gboolean run_script = FALSE; + static gboolean allow_remote_connections = FALSE; + gboolean debug = FALSE; + gboolean ret; + int exit_status = EX_OK; + static GOptionEntry entries [] = { + { "run-script", 'r', 0, G_OPTION_ARG_NONE, &run_script, N_("Run program through /etc/gdm/Xsession wrapper script"), NULL }, + { "allow-remote-connections", 'a', 0, G_OPTION_ARG_NONE, &allow_remote_connections, N_("Listen on TCP socket"), NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, "", "" }, + { NULL } + }; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + setlocale (LC_ALL, ""); + + gdm_log_init (); + + context = g_option_context_new (_("GNOME Display Manager X Session Launcher")); + g_option_context_add_main_entries (context, entries, NULL); + + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); +@@ -884,57 +869,63 @@ main (int argc, + state->cancellable = g_cancellable_new (); + + g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state); + + ret = spawn_x_server (state, allow_remote_connections, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run X server\n"); + exit_status = EX_SOFTWARE; + goto out; + } + + ret = spawn_bus (state, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run session message bus\n"); + exit_status = EX_SOFTWARE; + goto out; + } + + import_environment (state, state->cancellable); + + ret = update_bus_environment (state, state->cancellable); + + if (!ret) { + g_printerr ("Unable to update bus environment\n"); + exit_status = EX_SOFTWARE; + goto out; + } + ++ ret = register_display (state, state->cancellable); ++ ++ if (!ret) { ++ g_printerr ("Unable to register display with display manager\n"); ++ exit_status = EX_SOFTWARE; ++ goto out; ++ } ++ + ret = spawn_session (state, run_script, state->cancellable); + + if (!ret) { + g_printerr ("Unable to run session\n"); + exit_status = EX_SOFTWARE; + goto out; + } + +- g_timeout_add_seconds (2, (GSourceFunc) on_registration_delay_complete, state); +- + g_main_loop_run (state->main_loop); + + /* Only use exit status of session if we're here because it exit */ + + if (state->session_subprocess == NULL) { + exit_status = state->session_exit_status; + } + + out: + if (state != NULL) { + signal_subprocesses (state); + wait_on_subprocesses (state); + clear_state (&state); + } + + return exit_status; + } +-- +2.26.0 + diff --git a/SOURCES/0031-local-display-factory-don-t-autoreap-initial-setup.patch b/SOURCES/0031-local-display-factory-don-t-autoreap-initial-setup.patch new file mode 100644 index 0000000..3285094 --- /dev/null +++ b/SOURCES/0031-local-display-factory-don-t-autoreap-initial-setup.patch @@ -0,0 +1,100 @@ +From 9a78b7fe13a425d23ecbf13527daf425663fba8e Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 25 Sep 2018 14:52:15 -0400 +Subject: [PATCH 31/48] local-display-factory: don't autoreap initial-setup + +We automatically kill the login screen when switching VTs away +from it, but we should never kill the initial-setup screen in +that situation. + +This commit adds a check to prevent that from happening. +--- + daemon/gdm-local-display-factory.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index be6b377be..13d56dcff 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -607,70 +607,78 @@ on_seat_removed (GDBusConnection *connection, + g_variant_get (parameters, "(&s&o)", &seat, NULL); + delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat); + } + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + static gboolean + lookup_by_session_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + const char *current; + + current = gdm_display_get_session_id (display); + return g_strcmp0 (current, looking_for) == 0; + } + + static gboolean + wait_to_finish_timeout (GdmLocalDisplayFactory *factory) + { + finish_waiting_displays_on_seat (factory, "seat0"); + factory->priv->wait_to_finish_timeout_id = 0; + return G_SOURCE_REMOVE; + } + + static void + maybe_stop_greeter_in_background (GdmLocalDisplayFactory *factory, + GdmDisplay *display) + { + g_autofree char *display_session_type = NULL; ++ gboolean doing_initial_setup = FALSE; + + if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) { + g_debug ("GdmLocalDisplayFactory: login window not in managed state, so ignoring"); + return; + } + + g_object_get (G_OBJECT (display), + "session-type", &display_session_type, ++ "doing-initial-setup", &doing_initial_setup, + NULL); + ++ /* we don't ever stop initial-setup implicitly */ ++ if (doing_initial_setup) { ++ g_debug ("GdmLocalDisplayFactory: login window is performing initial-setup, so ignoring"); ++ return; ++ } ++ + /* we can only stop greeter for wayland sessions, since + * X server would jump back on exit */ + if (g_strcmp0 (display_session_type, "wayland") != 0) { + g_debug ("GdmLocalDisplayFactory: login window is running on Xorg, so ignoring"); + return; + } + + g_debug ("GdmLocalDisplayFactory: killing login window once its unused"); + g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); + + /* We stop the greeter after a timeout to avoid flicker */ + if (factory->priv->wait_to_finish_timeout_id != 0) + g_source_remove (factory->priv->wait_to_finish_timeout_id); + + factory->priv->wait_to_finish_timeout_id = + g_timeout_add_seconds (WAIT_TO_FINISH_TIMEOUT, + (GSourceFunc)wait_to_finish_timeout, + factory); + } + + static gboolean + on_vt_changed (GIOChannel *source, + GIOCondition condition, + GdmLocalDisplayFactory *factory) + { + GIOStatus status; + static const char *tty_of_initial_vt = "tty" GDM_INITIAL_VT; + g_autofree char *tty_of_previous_vt = NULL; + g_autofree char *tty_of_active_vt = NULL; + g_autofree char *login_session_id = NULL; +-- +2.26.0 + diff --git a/SOURCES/0032-manager-rework-how-autologin-is-figured-out.patch b/SOURCES/0032-manager-rework-how-autologin-is-figured-out.patch new file mode 100644 index 0000000..5b825bc --- /dev/null +++ b/SOURCES/0032-manager-rework-how-autologin-is-figured-out.patch @@ -0,0 +1,460 @@ +From a539080c470125871aa1774318a0b5aca14ce91d Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 25 Sep 2018 10:59:37 -0400 +Subject: [PATCH 32/48] manager: rework how autologin is figured out + +At the moment we decide whether or not to perform autologin, by +looking at if the display is the initial VT display and if autologin +hasn't been started before. + +That isn't going to work in the future when autologin is started +on a non-initial vt. + +This commit changes GDM to instead check if the seat is seat0, and +if autologin hasn't run before, before deciding to do autologin. +--- + daemon/gdm-manager.c | 46 ++++++++++++++++++++++++++++++++------------ + 1 file changed, 34 insertions(+), 12 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index fb7b1ec4b..228cec6ff 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -51,75 +51,76 @@ + #include "gdm-session.h" + #include "gdm-session-record.h" + #include "gdm-settings-direct.h" + #include "gdm-settings-keys.h" + #include "gdm-xdmcp-display-factory.h" + #include "gdm-xdmcp-chooser-display.h" + + #define GDM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_MANAGER, GdmManagerPrivate)) + + #define GDM_DBUS_PATH "/org/gnome/DisplayManager" + #define GDM_MANAGER_PATH GDM_DBUS_PATH "/Manager" + #define GDM_MANAGER_DISPLAYS_PATH GDM_DBUS_PATH "/Displays" + + #define INITIAL_SETUP_USERNAME "gnome-initial-setup" + + typedef struct + { + GdmManager *manager; + GdmSession *session; + char *service_name; + guint idle_id; + } StartUserSessionOperation; + + struct GdmManagerPrivate + { + GdmDisplayStore *display_store; + GdmLocalDisplayFactory *local_factory; + #ifdef HAVE_LIBXDMCP + GdmXdmcpDisplayFactory *xdmcp_factory; + #endif ++ GdmDisplay *automatic_login_display; + GList *user_sessions; + GHashTable *transient_sessions; + GHashTable *open_reauthentication_requests; + gboolean xdmcp_enabled; + + gboolean started; + gboolean show_local_greeter; + + GDBusConnection *connection; + GDBusObjectManagerServer *object_manager; + + #ifdef WITH_PLYMOUTH + guint plymouth_is_running : 1; + #endif +- guint ran_once : 1; ++ guint did_automatic_login : 1; + }; + + enum { + PROP_0, + PROP_XDMCP_ENABLED, + PROP_SHOW_LOCAL_GREETER + }; + + enum { + DISPLAY_ADDED, + DISPLAY_REMOVED, + LAST_SIGNAL + }; + + typedef enum { + SESSION_RECORD_LOGIN, + SESSION_RECORD_LOGOUT, + SESSION_RECORD_FAILED, + } SessionRecord; + + static guint signals [LAST_SIGNAL] = { 0, }; + + static void gdm_manager_class_init (GdmManagerClass *klass); + static void gdm_manager_init (GdmManager *manager); + static void gdm_manager_dispose (GObject *object); + + static GdmSession *create_user_session_for_display (GdmManager *manager, + GdmDisplay *display, + uid_t allowed_user); + static void start_user_session (GdmManager *manager, +@@ -1415,67 +1416,74 @@ typedef struct { + static void + destroy_username_lookup_operation (UsernameLookupOperation *operation) + { + g_object_unref (operation->manager); + g_object_unref (operation->display); + g_free (operation->username); + g_free (operation); + } + + static void + on_user_is_loaded_changed (ActUser *user, + GParamSpec *pspec, + UsernameLookupOperation *operation) + { + if (act_user_is_loaded (user)) { + set_up_automatic_login_session_if_user_exists (operation->manager, operation->display, user); + g_signal_handlers_disconnect_by_func (G_OBJECT (user), + G_CALLBACK (on_user_is_loaded_changed), + operation); + destroy_username_lookup_operation (operation); + } + } + + static void + set_up_session (GdmManager *manager, + GdmDisplay *display) + { + ActUserManager *user_manager; + ActUser *user; + gboolean loaded; +- gboolean is_initial_display = FALSE; ++ gboolean seat_can_autologin = FALSE, seat_did_autologin = FALSE; + gboolean autologin_enabled = FALSE; ++ g_autofree char *seat_id = NULL; + char *username = NULL; + +- g_object_get (G_OBJECT (display), "is-initial", &is_initial_display, NULL); ++ g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL); ++ ++ if (g_strcmp0 (seat_id, "seat0") == 0) ++ seat_can_autologin = TRUE; ++ ++ if (manager->priv->did_automatic_login || manager->priv->automatic_login_display != NULL) ++ seat_did_autologin = TRUE; + +- if (!manager->priv->ran_once && is_initial_display) ++ if (seat_can_autologin && !seat_did_autologin) + autologin_enabled = get_automatic_login_details (manager, &username); + + if (!autologin_enabled) { + g_free (username); + + #ifdef HAVE_LIBXDMCP + if (GDM_IS_XDMCP_CHOOSER_DISPLAY (display)) { + set_up_chooser_session (manager, display); + return; + } + #endif + + set_up_greeter_session (manager, display); + return; + } + + /* Check whether the user really exists before committing to autologin. */ + user_manager = act_user_manager_get_default (); + user = act_user_manager_get_user (user_manager, username); + g_object_get (user_manager, "is-loaded", &loaded, NULL); + + if (loaded) { + set_up_automatic_login_session_if_user_exists (manager, display, user); + } else { + UsernameLookupOperation *operation; + + operation = g_new (UsernameLookupOperation, 1); + operation->manager = g_object_ref (manager); + operation->display = g_object_ref (display); + operation->username = username; +@@ -1512,62 +1520,72 @@ on_display_status_changed (GdmDisplay *display, + "session-type", &session_type, + NULL); + + status = gdm_display_get_status (display); + + switch (status) { + case GDM_DISPLAY_PREPARED: + case GDM_DISPLAY_MANAGED: + if ((display_number == -1 && status == GDM_DISPLAY_PREPARED) || + (display_number != -1 && status == GDM_DISPLAY_MANAGED)) { + char *session_class; + + g_object_get (display, + "session-class", &session_class, + NULL); + if (g_strcmp0 (session_class, "greeter") == 0) + set_up_session (manager, display); + g_free (session_class); + } + break; + case GDM_DISPLAY_FAILED: + case GDM_DISPLAY_UNMANAGED: + case GDM_DISPLAY_FINISHED: + #ifdef WITH_PLYMOUTH + if (quit_plymouth) { + plymouth_quit_without_transition (); + manager->priv->plymouth_is_running = FALSE; + } + #endif + +- if (!doing_initial_setup && (status == GDM_DISPLAY_FINISHED || g_strcmp0 (session_type, "x11") == 0)) { +- manager->priv->ran_once = TRUE; ++ if (display == manager->priv->automatic_login_display) { ++ g_clear_weak_pointer (&manager->priv->automatic_login_display); ++ ++ manager->priv->did_automatic_login = TRUE; ++ ++#ifdef ENABLE_WAYLAND_SUPPORT ++ if (g_strcmp0 (session_type, "wayland") != 0 && status == GDM_DISPLAY_FAILED) { ++ /* we're going to fall back to X11, so try to autologin again ++ */ ++ manager->priv->did_automatic_login = FALSE; ++ } ++#endif + } + break; + default: + break; + } + + } + + static void + on_display_removed (GdmDisplayStore *display_store, + GdmDisplay *display, + GdmManager *manager) + { + char *id; + + gdm_display_get_id (display, &id, NULL); + g_dbus_object_manager_server_unexport (manager->priv->object_manager, id); + g_free (id); + + g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), manager); + + g_signal_emit (manager, signals[DISPLAY_REMOVED], 0, display); + } + + static void + destroy_start_user_session_operation (StartUserSessionOperation *operation) + { + g_object_set_data (G_OBJECT (operation->session), + "start-user-session-operation", + NULL); +@@ -1621,132 +1639,134 @@ create_display_for_user_session (GdmManager *self, + const char *session_id) + { + GdmDisplay *display; + /* at the moment we only create GdmLocalDisplay objects on seat0 */ + const char *seat_id = "seat0"; + + display = gdm_local_display_new (); + + g_object_set (G_OBJECT (display), + "session-class", "user", + "seat-id", seat_id, + "session-id", session_id, + NULL); + gdm_display_store_add (self->priv->display_store, + display); + g_object_set_data (G_OBJECT (session), "gdm-display", display); + g_object_set_data_full (G_OBJECT (display), + "gdm-user-session", + g_object_ref (session), + (GDestroyNotify) + clean_user_session); + } + + static gboolean + on_start_user_session (StartUserSessionOperation *operation) + { + GdmManager *self = operation->manager; + gboolean migrated; + gboolean fail_if_already_switched = TRUE; + gboolean doing_initial_setup = FALSE; +- gboolean starting_user_session_right_away = TRUE; + GdmDisplay *display; + const char *session_id; + + g_debug ("GdmManager: start or jump to session"); + + /* If there's already a session running, jump to it. + * If the only session running is the one we just opened, + * start a session on it. + */ + migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched); + + g_debug ("GdmManager: migrated: %d", migrated); + if (migrated) { + /* We don't stop the manager here because + when Xorg exits it switches to the VT it was + started from. That interferes with fast + user switching. */ + gdm_session_reset (operation->session); + destroy_start_user_session_operation (operation); + goto out; + } + + display = get_display_for_user_session (operation->session); + + g_object_get (G_OBJECT (display), "doing-initial-setup", &doing_initial_setup, NULL); + + session_id = gdm_session_get_conversation_session_id (operation->session, + operation->service_name); + + if (gdm_session_get_display_mode (operation->session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) { + /* In this case, the greeter's display is morphing into + * the user session display. Kill the greeter on this session + * and let the user session follow the same display. */ + gdm_display_stop_greeter_session (display); + g_object_set (G_OBJECT (display), + "session-class", "user", + "session-id", session_id, + NULL); + } else { + uid_t allowed_uid; + + g_object_ref (display); + if (doing_initial_setup) { + g_debug ("GdmManager: closing down initial setup display"); + gdm_display_stop_greeter_session (display); + gdm_display_unmanage (display); + gdm_display_finish (display); + } else { + g_debug ("GdmManager: session has its display server, reusing our server for another login screen"); + } + + /* The user session is going to follow the session worker + * into the new display. Untie it from this display and + * create a new session for a future user login. */ + allowed_uid = gdm_session_get_allowed_user (operation->session); + g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); + g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL); + create_user_session_for_display (operation->manager, display, allowed_uid); + ++ /* Give the user session a new display object for bookkeeping purposes */ ++ create_display_for_user_session (operation->manager, ++ operation->session, ++ session_id); ++ + if ((g_strcmp0 (operation->service_name, "gdm-autologin") == 0) && + !gdm_session_client_is_connected (operation->session)) { + /* remove the unused prepared greeter display since we're not going + * to have a greeter */ + gdm_display_store_remove (self->priv->display_store, display); + g_object_unref (display); +- } + +- /* Give the user session a new display object for bookkeeping purposes */ +- create_display_for_user_session (operation->manager, +- operation->session, +- session_id); ++ self->priv->automatic_login_display = g_object_get_data (G_OBJECT (operation->session), "gdm-display"); ++ g_object_add_weak_pointer (G_OBJECT (display), (gpointer *) &self->priv->automatic_login_display); ++ } + } + + start_user_session (operation->manager, operation); + + out: + return G_SOURCE_REMOVE; + } + + static void + queue_start_user_session (GdmManager *manager, + GdmSession *session, + const char *service_name) + { + StartUserSessionOperation *operation; + + operation = g_slice_new0 (StartUserSessionOperation); + operation->manager = manager; + operation->session = g_object_ref (session); + operation->service_name = g_strdup (service_name); + + operation->idle_id = g_idle_add ((GSourceFunc) on_start_user_session, operation); + g_object_set_data (G_OBJECT (session), "start-user-session-operation", operation); + } + + static void + start_user_session_if_ready (GdmManager *manager, + GdmSession *session, + const char *service_name) + { + gboolean start_when_ready; +@@ -2601,60 +2621,62 @@ unexport_display (const char *id, + GdmDisplay *display, + GdmManager *manager) + { + if (!g_dbus_connection_is_closed (manager->priv->connection)) + g_dbus_object_manager_server_unexport (manager->priv->object_manager, id); + } + + static void + finish_display (const char *id, + GdmDisplay *display, + GdmManager *manager) + { + gdm_display_stop_greeter_session (display); + if (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) + gdm_display_unmanage (display); + gdm_display_finish (display); + } + + static void + gdm_manager_dispose (GObject *object) + { + GdmManager *manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_MANAGER (object)); + + manager = GDM_MANAGER (object); + + g_return_if_fail (manager->priv != NULL); + ++ g_clear_weak_pointer (&manager->priv->automatic_login_display); ++ + #ifdef HAVE_LIBXDMCP + g_clear_object (&manager->priv->xdmcp_factory); + #endif + g_clear_object (&manager->priv->local_factory); + g_clear_pointer (&manager->priv->open_reauthentication_requests, + (GDestroyNotify) + g_hash_table_unref); + g_clear_pointer (&manager->priv->transient_sessions, + (GDestroyNotify) + g_hash_table_unref); + + g_list_foreach (manager->priv->user_sessions, + (GFunc) gdm_session_close, + NULL); + g_list_free_full (manager->priv->user_sessions, (GDestroyNotify) g_object_unref); + manager->priv->user_sessions = NULL; + + g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store), + G_CALLBACK (on_display_added), + manager); + g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store), + G_CALLBACK (on_display_removed), + manager); + + if (!g_dbus_connection_is_closed (manager->priv->connection)) { + gdm_display_store_foreach (manager->priv->display_store, + (GdmDisplayStoreFunc)unexport_display, + manager); + g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (manager)); + } +-- +2.26.0 + diff --git a/SOURCES/0033-manager-correct-display-confusion.patch b/SOURCES/0033-manager-correct-display-confusion.patch new file mode 100644 index 0000000..0bb3d86 --- /dev/null +++ b/SOURCES/0033-manager-correct-display-confusion.patch @@ -0,0 +1,85 @@ +From 60fff836d8ad0c6dcc35854c7d5d576d17bdeeee Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 1 Oct 2018 11:05:57 -0400 +Subject: [PATCH 33/48] manager: correct display confusion + +commit c5c5bf1f reworked autologin and broke it. + +This commit addresses the breakage by accessing +the proper display variable. + +Closes https://gitlab.gnome.org/GNOME/gdm/issues/426 +--- + daemon/gdm-manager.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index 228cec6ff..4c81dac7f 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1711,61 +1711,61 @@ on_start_user_session (StartUserSessionOperation *operation) + if (doing_initial_setup) { + g_debug ("GdmManager: closing down initial setup display"); + gdm_display_stop_greeter_session (display); + gdm_display_unmanage (display); + gdm_display_finish (display); + } else { + g_debug ("GdmManager: session has its display server, reusing our server for another login screen"); + } + + /* The user session is going to follow the session worker + * into the new display. Untie it from this display and + * create a new session for a future user login. */ + allowed_uid = gdm_session_get_allowed_user (operation->session); + g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); + g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL); + create_user_session_for_display (operation->manager, display, allowed_uid); + + /* Give the user session a new display object for bookkeeping purposes */ + create_display_for_user_session (operation->manager, + operation->session, + session_id); + + if ((g_strcmp0 (operation->service_name, "gdm-autologin") == 0) && + !gdm_session_client_is_connected (operation->session)) { + /* remove the unused prepared greeter display since we're not going + * to have a greeter */ + gdm_display_store_remove (self->priv->display_store, display); + g_object_unref (display); + + self->priv->automatic_login_display = g_object_get_data (G_OBJECT (operation->session), "gdm-display"); +- g_object_add_weak_pointer (G_OBJECT (display), (gpointer *) &self->priv->automatic_login_display); ++ g_object_add_weak_pointer (G_OBJECT (self->priv->automatic_login_display), (gpointer *) &self->priv->automatic_login_display); + } + } + + start_user_session (operation->manager, operation); + + out: + return G_SOURCE_REMOVE; + } + + static void + queue_start_user_session (GdmManager *manager, + GdmSession *session, + const char *service_name) + { + StartUserSessionOperation *operation; + + operation = g_slice_new0 (StartUserSessionOperation); + operation->manager = manager; + operation->session = g_object_ref (session); + operation->service_name = g_strdup (service_name); + + operation->idle_id = g_idle_add ((GSourceFunc) on_start_user_session, operation); + g_object_set_data (G_OBJECT (session), "start-user-session-operation", operation); + } + + static void + start_user_session_if_ready (GdmManager *manager, + GdmSession *session, + const char *service_name) + { +-- +2.26.0 + diff --git a/SOURCES/0034-manager-don-t-run-autologin-display-on-tty1.patch b/SOURCES/0034-manager-don-t-run-autologin-display-on-tty1.patch new file mode 100644 index 0000000..b0f8c16 --- /dev/null +++ b/SOURCES/0034-manager-don-t-run-autologin-display-on-tty1.patch @@ -0,0 +1,99 @@ +From f40be88d090ecdef4cf877d8c3aeb7b76f9cb785 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 24 Sep 2018 14:45:38 -0400 +Subject: [PATCH 34/48] manager: don't run autologin display on tty1 + +tty1 is really meant for the login screen. +If a user autologins on it and we need a login +screen later, then the login screen has to go +in some auxiliary VT which isn't very nice. + +This commit changes autologin to not use the +initial vt. +--- + daemon/gdm-manager.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index 4c81dac7f..e896c8945 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1308,74 +1308,72 @@ get_automatic_login_details (GdmManager *manager, + } else { + g_free (username); + } + + return enabled; + } + + static const char * + get_username_for_greeter_display (GdmManager *manager, + GdmDisplay *display) + { + gboolean doing_initial_setup = FALSE; + + g_object_get (G_OBJECT (display), + "doing-initial-setup", &doing_initial_setup, + NULL); + + if (doing_initial_setup) { + return INITIAL_SETUP_USERNAME; + } else { + return GDM_USERNAME; + } + } + + static void + set_up_automatic_login_session (GdmManager *manager, + GdmDisplay *display) + { + GdmSession *session; + char *display_session_type = NULL; +- gboolean is_initial; + + /* 0 is root user; since the daemon talks to the session object + * directly, itself, for automatic login + */ + session = create_user_session_for_display (manager, display, 0); + + g_object_get (G_OBJECT (display), +- "is-initial", &is_initial, + "session-type", &display_session_type, + NULL); + + g_object_set (G_OBJECT (session), +- "display-is-initial", is_initial, ++ "display-is-initial", FALSE, + NULL); + + g_debug ("GdmManager: Starting automatic login conversation"); + gdm_session_start_conversation (session, "gdm-autologin"); + } + + static void + set_up_chooser_session (GdmManager *manager, + GdmDisplay *display) + { + const char *allowed_user; + struct passwd *passwd_entry; + + allowed_user = get_username_for_greeter_display (manager, display); + + if (!gdm_get_pwent_for_name (allowed_user, &passwd_entry)) { + g_warning ("GdmManager: couldn't look up username %s", + allowed_user); + gdm_display_unmanage (display); + gdm_display_finish (display); + return; + } + + gdm_display_start_greeter_session (display); + } + + static void + set_up_greeter_session (GdmManager *manager, + GdmDisplay *display) + { +-- +2.26.0 + diff --git a/SOURCES/0035-local-display-factory-Remove-initial-VT-is-in-use-ch.patch b/SOURCES/0035-local-display-factory-Remove-initial-VT-is-in-use-ch.patch new file mode 100644 index 0000000..834f65b --- /dev/null +++ b/SOURCES/0035-local-display-factory-Remove-initial-VT-is-in-use-ch.patch @@ -0,0 +1,110 @@ +From c3d61627cfdfc41d89e21e57006f9cadb3917a79 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 4 Sep 2018 08:12:34 +0200 +Subject: [PATCH 35/48] local-display-factory: Remove initial VT is in use + check + +The initial VT is in use check in on_vt_changed() is racy, when switching +to VT1 from an active session, on_vt_changed() may run before logind has +processed the VT change and then sd_seat_get_active() will return the +active session which we are switching away from. This results in the greeter +not being started on VT1. + +On my system gdm reliably wins the race resulting in not getting a greeter +when manually switching from an active session to VT1. + +gdm already starts the greeter unconditionally from +gdm_local_display_factory_sync_seats() on both startup and when an user +session exits. gdm also starts it unconditionally when selecting +"Switch user" from an user session. + +Now autologin sessions avoid the initial VT as well. + +So we now can assume that the initial VT is free for the login screen's +use. And create_display already checks for and re-uses +an existing greeter, so we can safely remove the racy check. +--- + daemon/gdm-local-display-factory.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 13d56dcff..8e46dbca2 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -752,73 +752,60 @@ on_vt_changed (GIOChannel *source, + + g_debug ("GdmLocalDisplayFactory: tty of login window is %s", tty_of_login_window_vt); + if (g_strcmp0 (tty_of_login_window_vt, tty_of_previous_vt) == 0) { + GdmDisplayStore *store; + GdmDisplay *display; + + g_debug ("GdmLocalDisplayFactory: VT switched from login window"); + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + display = gdm_display_store_find (store, + lookup_by_session_id, + (gpointer) login_session_id); + + if (display != NULL) + maybe_stop_greeter_in_background (factory, display); + } else { + g_debug ("GdmLocalDisplayFactory: VT not switched from login window"); + } + } + } + + /* if user jumped back to initial vt and it's empty put a login screen + * on it (unless a login screen is already running elsewhere, then + * jump to that login screen) + */ + if (strcmp (factory->priv->tty_of_active_vt, tty_of_initial_vt) != 0) { + g_debug ("GdmLocalDisplayFactory: active VT is not initial VT, so ignoring"); + return G_SOURCE_CONTINUE; + } + +- ret = sd_seat_get_active ("seat0", &active_session_id, NULL); +- +- if (ret == 0) { +- g_autofree char *state = NULL; +- ret = sd_session_get_state (active_session_id, &state); +- +- /* if there's something already running on the active VT then bail */ +- if (ret == 0 && g_strcmp0 (state, "closing") != 0) { +- g_debug ("GdmLocalDisplayFactory: initial VT is in use, so ignoring"); +- return G_SOURCE_CONTINUE; +- } +- } +- + if (gdm_local_display_factory_use_wayland ()) + session_type = "wayland"; + + g_debug ("GdmLocalDisplayFactory: creating new display on seat0 because of VT change"); + + create_display (factory, "seat0", session_type, TRUE); + + return G_SOURCE_CONTINUE; + } + #endif + + static void + gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory) + { + g_autoptr (GIOChannel) io_channel = NULL; + + factory->priv->seat_new_id = g_dbus_connection_signal_subscribe (factory->priv->connection, + "org.freedesktop.login1", + "org.freedesktop.login1.Manager", + "SeatNew", + "/org/freedesktop/login1", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_seat_new, + g_object_ref (factory), + g_object_unref); + factory->priv->seat_removed_id = g_dbus_connection_signal_subscribe (factory->priv->connection, + "org.freedesktop.login1", + "org.freedesktop.login1.Manager", + "SeatRemoved", +-- +2.26.0 + diff --git a/SOURCES/0036-local-display-factory-Remove-same-VT-so-don-t-switch.patch b/SOURCES/0036-local-display-factory-Remove-same-VT-so-don-t-switch.patch new file mode 100644 index 0000000..7d3b9ed --- /dev/null +++ b/SOURCES/0036-local-display-factory-Remove-same-VT-so-don-t-switch.patch @@ -0,0 +1,140 @@ +From f9021afe364c757e0f2f129987e5ca466f05f1c9 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 25 Sep 2018 14:30:16 -0400 +Subject: [PATCH 36/48] local-display-factory: Remove same VT so don't switch + check + +We avoid changing to the login screen vt if we're already on it, +but the call is racy since we react to vt changes concurrently +with logind (who we query for the active vt). + +This check drops the active vt check since it's pointless and +getting in the way. +--- + daemon/gdm-local-display-factory.c | 39 ++++++++++-------------------- + 1 file changed, 13 insertions(+), 26 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 8e46dbca2..be7b43cff 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -410,103 +410,90 @@ lookup_by_seat_id (const char *id, + res = g_strcmp0 (current, looking_for) == 0; + + g_free(current); + + return res; + } + + static gboolean + lookup_prepared_display_by_seat_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + int status; + + status = gdm_display_get_status (display); + + if (status != GDM_DISPLAY_PREPARED) + return FALSE; + + return lookup_by_seat_id (id, display, user_data); + } + + static GdmDisplay * + create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial) + { + GdmDisplayStore *store; + GdmDisplay *display = NULL; +- char *active_session_id = NULL; +- int ret; ++ g_autofree char *login_session_id = NULL; + + g_debug ("GdmLocalDisplayFactory: %s login display for seat %s requested", + session_type? : "X11", seat_id); + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + + if (sd_seat_can_multi_session (seat_id)) + display = gdm_display_store_find (store, lookup_prepared_display_by_seat_id, (gpointer) seat_id); + else + display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); + + /* Ensure we don't create the same display more than once */ + if (display != NULL) { + g_debug ("GdmLocalDisplayFactory: display already created"); + return NULL; + } + +- ret = sd_seat_get_active (seat_id, &active_session_id, NULL); +- +- if (ret == 0) { +- char *login_session_id = NULL; +- +- /* If we already have a login window, switch to it */ +- if (gdm_get_login_window_session_id (seat_id, &login_session_id)) { +- GdmDisplay *display; +- +- display = gdm_display_store_find (store, +- lookup_by_session_id, +- (gpointer) login_session_id); +- if (display != NULL && gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { +- if (g_strcmp0 (active_session_id, login_session_id) != 0) { +- g_debug ("GdmLocalDisplayFactory: session %s found, activating.", +- login_session_id); +- gdm_activate_session_by_id (factory->priv->connection, seat_id, login_session_id); +- } +- g_clear_pointer (&login_session_id, g_free); +- g_clear_pointer (&active_session_id, g_free); +- return NULL; +- } +- g_clear_pointer (&login_session_id, g_free); ++ /* If we already have a login window, switch to it */ ++ if (gdm_get_login_window_session_id (seat_id, &login_session_id)) { ++ GdmDisplay *display; ++ ++ display = gdm_display_store_find (store, ++ lookup_by_session_id, ++ (gpointer) login_session_id); ++ if (display != NULL && gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { ++ g_debug ("GdmLocalDisplayFactory: session %s found, activating.", ++ login_session_id); ++ gdm_activate_session_by_id (factory->priv->connection, seat_id, login_session_id); ++ return NULL; + } +- g_clear_pointer (&active_session_id, g_free); + } + + g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id); + + #ifdef ENABLE_USER_DISPLAY_SERVER + if (g_strcmp0 (seat_id, "seat0") == 0) { + display = gdm_local_display_new (); + if (session_type != NULL) { + g_object_set (G_OBJECT (display), "session-type", session_type, NULL); + } + } + #endif + + if (display == NULL) { + guint32 num; + + num = take_next_display_number (factory); + + display = gdm_legacy_display_new (num); + } + + g_object_set (display, "seat-id", seat_id, NULL); + g_object_set (display, "is-initial", initial, NULL); + + store_display (factory, display); + + /* let store own the ref */ + g_object_unref (display); + + if (! gdm_display_manage (display)) { +-- +2.26.0 + diff --git a/SOURCES/0037-local-display-factory-handle-reviving-displays-that-.patch b/SOURCES/0037-local-display-factory-handle-reviving-displays-that-.patch new file mode 100644 index 0000000..18e5520 --- /dev/null +++ b/SOURCES/0037-local-display-factory-handle-reviving-displays-that-.patch @@ -0,0 +1,88 @@ +From 9c962b87b4e3d07b5ab2b212f0bcb662d7f4f7a2 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 25 Sep 2018 14:39:42 -0400 +Subject: [PATCH 37/48] local-display-factory: handle reviving displays that + are waiting to die + +We may end up re-using a display in waiting-to-finish state before it gets +finished in this case reset its state to managed to avoid it getting +finished while it is being used. + +Closes https://gitlab.gnome.org/GNOME/gdm/merge_requests/45 +--- + daemon/gdm-local-display-factory.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index be7b43cff..d999596b5 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -434,61 +434,64 @@ create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial) + { + GdmDisplayStore *store; + GdmDisplay *display = NULL; + g_autofree char *login_session_id = NULL; + + g_debug ("GdmLocalDisplayFactory: %s login display for seat %s requested", + session_type? : "X11", seat_id); + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + + if (sd_seat_can_multi_session (seat_id)) + display = gdm_display_store_find (store, lookup_prepared_display_by_seat_id, (gpointer) seat_id); + else + display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id); + + /* Ensure we don't create the same display more than once */ + if (display != NULL) { + g_debug ("GdmLocalDisplayFactory: display already created"); + return NULL; + } + + /* If we already have a login window, switch to it */ + if (gdm_get_login_window_session_id (seat_id, &login_session_id)) { + GdmDisplay *display; + + display = gdm_display_store_find (store, + lookup_by_session_id, + (gpointer) login_session_id); +- if (display != NULL && gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { ++ if (display != NULL && ++ (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED || ++ gdm_display_get_status (display) == GDM_DISPLAY_WAITING_TO_FINISH)) { ++ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_MANAGED, NULL); + g_debug ("GdmLocalDisplayFactory: session %s found, activating.", + login_session_id); + gdm_activate_session_by_id (factory->priv->connection, seat_id, login_session_id); + return NULL; + } + } + + g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id); + + #ifdef ENABLE_USER_DISPLAY_SERVER + if (g_strcmp0 (seat_id, "seat0") == 0) { + display = gdm_local_display_new (); + if (session_type != NULL) { + g_object_set (G_OBJECT (display), "session-type", session_type, NULL); + } + } + #endif + + if (display == NULL) { + guint32 num; + + num = take_next_display_number (factory); + + display = gdm_legacy_display_new (num); + } + + g_object_set (display, "seat-id", seat_id, NULL); + g_object_set (display, "is-initial", initial, NULL); + + store_display (factory, display); +-- +2.26.0 + diff --git a/SOURCES/0038-manager-don-t-kill-initial-setup-before-starting-use.patch b/SOURCES/0038-manager-don-t-kill-initial-setup-before-starting-use.patch new file mode 100644 index 0000000..5c283cc --- /dev/null +++ b/SOURCES/0038-manager-don-t-kill-initial-setup-before-starting-use.patch @@ -0,0 +1,151 @@ +From 1624c4229c83e7fcdb9a57ef59cd1e57257b5b2c Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 15:48:38 -0400 +Subject: [PATCH 38/48] manager: don't kill initial-setup before starting user + session on wayland + +Right now we kill initial-setup before starting the session for the user +initial-setup created. This is the right thing to do for Xorg, since +Xorg can't be killed in the background, but it adds unncessary flicker +for wayland. + +This commit checks if it's wayland and avoids killing it right away +in that case. +--- + daemon/gdm-manager.c | 26 +++++++++++++++++++++----- + 1 file changed, 21 insertions(+), 5 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index e896c8945..0823e8638 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1639,105 +1639,121 @@ create_display_for_user_session (GdmManager *self, + GdmDisplay *display; + /* at the moment we only create GdmLocalDisplay objects on seat0 */ + const char *seat_id = "seat0"; + + display = gdm_local_display_new (); + + g_object_set (G_OBJECT (display), + "session-class", "user", + "seat-id", seat_id, + "session-id", session_id, + NULL); + gdm_display_store_add (self->priv->display_store, + display); + g_object_set_data (G_OBJECT (session), "gdm-display", display); + g_object_set_data_full (G_OBJECT (display), + "gdm-user-session", + g_object_ref (session), + (GDestroyNotify) + clean_user_session); + } + + static gboolean + on_start_user_session (StartUserSessionOperation *operation) + { + GdmManager *self = operation->manager; + gboolean migrated; + gboolean fail_if_already_switched = TRUE; + gboolean doing_initial_setup = FALSE; + GdmDisplay *display; + const char *session_id; ++#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) ++ g_autofree char *display_session_type = NULL; ++#endif + + g_debug ("GdmManager: start or jump to session"); + + /* If there's already a session running, jump to it. + * If the only session running is the one we just opened, + * start a session on it. + */ + migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched); + + g_debug ("GdmManager: migrated: %d", migrated); + if (migrated) { + /* We don't stop the manager here because + when Xorg exits it switches to the VT it was + started from. That interferes with fast + user switching. */ + gdm_session_reset (operation->session); + destroy_start_user_session_operation (operation); + goto out; + } + + display = get_display_for_user_session (operation->session); + +- g_object_get (G_OBJECT (display), "doing-initial-setup", &doing_initial_setup, NULL); ++ g_object_get (G_OBJECT (display), ++ "doing-initial-setup", &doing_initial_setup, ++#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) ++ "session-type", &display_session_type, ++#endif ++ NULL); + + session_id = gdm_session_get_conversation_session_id (operation->session, + operation->service_name); + + if (gdm_session_get_display_mode (operation->session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) { + /* In this case, the greeter's display is morphing into + * the user session display. Kill the greeter on this session + * and let the user session follow the same display. */ + gdm_display_stop_greeter_session (display); + g_object_set (G_OBJECT (display), + "session-class", "user", + "session-id", session_id, + NULL); + } else { + uid_t allowed_uid; + + g_object_ref (display); + if (doing_initial_setup) { +- g_debug ("GdmManager: closing down initial setup display"); +- gdm_display_stop_greeter_session (display); +- gdm_display_unmanage (display); +- gdm_display_finish (display); ++#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) ++ if (g_strcmp0 (display_session_type, "wayland") == 0) { ++ g_debug ("GdmManager: closing down initial setup display in background"); ++ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); ++ } ++#endif ++ if (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { ++ g_debug ("GdmManager: closing down initial setup display"); ++ gdm_display_stop_greeter_session (display); ++ gdm_display_unmanage (display); ++ gdm_display_finish (display); ++ } + } else { + g_debug ("GdmManager: session has its display server, reusing our server for another login screen"); + } + + /* The user session is going to follow the session worker + * into the new display. Untie it from this display and + * create a new session for a future user login. */ + allowed_uid = gdm_session_get_allowed_user (operation->session); + g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); + g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL); + create_user_session_for_display (operation->manager, display, allowed_uid); + + /* Give the user session a new display object for bookkeeping purposes */ + create_display_for_user_session (operation->manager, + operation->session, + session_id); + + if ((g_strcmp0 (operation->service_name, "gdm-autologin") == 0) && + !gdm_session_client_is_connected (operation->session)) { + /* remove the unused prepared greeter display since we're not going + * to have a greeter */ + gdm_display_store_remove (self->priv->display_store, display); + g_object_unref (display); + + self->priv->automatic_login_display = g_object_get_data (G_OBJECT (operation->session), "gdm-display"); + g_object_add_weak_pointer (G_OBJECT (self->priv->automatic_login_display), (gpointer *) &self->priv->automatic_login_display); + } + } + + start_user_session (operation->manager, operation); +-- +2.26.0 + diff --git a/SOURCES/0039-manager-do-initial-setup-post-work-in-manager-code.patch b/SOURCES/0039-manager-do-initial-setup-post-work-in-manager-code.patch new file mode 100644 index 0000000..70b9357 --- /dev/null +++ b/SOURCES/0039-manager-do-initial-setup-post-work-in-manager-code.patch @@ -0,0 +1,694 @@ +From 0f1826708cc233bb9e538c029afa96c85ef62d56 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 6 Sep 2018 19:31:50 -0400 +Subject: [PATCH 39/48] manager: do initial-setup post work in manager code + +Right now we do the initial-setup related post work +when stopping the greeter, but the problem is we delay +stopping the greeter now until after the user session +is started. + +That post-work needs to be done before the user session +is started. + +This commit moves the code to a more logical place. +--- + daemon/gdm-display.c | 132 ------------------------------------------- + daemon/gdm-manager.c | 132 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 132 insertions(+), 132 deletions(-) + +diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c +index 875534272..1cef8c7c1 100644 +--- a/daemon/gdm-display.c ++++ b/daemon/gdm-display.c +@@ -22,61 +22,60 @@ + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #include + #include + + #include "gdm-common.h" + #include "gdm-display.h" + #include "gdm-display-glue.h" + #include "gdm-display-access-file.h" + #include "gdm-launch-environment.h" + + #include "gdm-settings-direct.h" + #include "gdm-settings-keys.h" + + #include "gdm-launch-environment.h" + #include "gdm-dbus-util.h" + +-#define INITIAL_SETUP_USERNAME "gnome-initial-setup" + #define GNOME_SESSION_SESSIONS_PATH DATADIR "/gnome-session/sessions" + + #define GDM_DISPLAY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DISPLAY, GdmDisplayPrivate)) + + struct GdmDisplayPrivate + { + char *id; + char *seat_id; + char *session_id; + char *session_class; + char *session_type; + + char *remote_hostname; + int x11_display_number; + char *x11_display_name; + int status; + time_t creation_time; + GTimer *server_timer; + + char *x11_cookie; + gsize x11_cookie_size; + GdmDisplayAccessFile *access_file; + + guint finish_idle_id; + + xcb_connection_t *xcb_connection; + int xcb_screen_number; + + GDBusConnection *connection; + GdmDisplayAccessFile *user_access_file; +@@ -98,131 +97,60 @@ enum { + PROP_0, + PROP_ID, + PROP_STATUS, + PROP_SEAT_ID, + PROP_SESSION_ID, + PROP_SESSION_CLASS, + PROP_SESSION_TYPE, + PROP_REMOTE_HOSTNAME, + PROP_X11_DISPLAY_NUMBER, + PROP_X11_DISPLAY_NAME, + PROP_X11_COOKIE, + PROP_X11_AUTHORITY_FILE, + PROP_IS_CONNECTED, + PROP_IS_LOCAL, + PROP_LAUNCH_ENVIRONMENT, + PROP_IS_INITIAL, + PROP_ALLOW_TIMED_LOGIN, + PROP_HAVE_EXISTING_USER_ACCOUNTS, + PROP_DOING_INITIAL_SETUP, + }; + + static void gdm_display_class_init (GdmDisplayClass *klass); + static void gdm_display_init (GdmDisplay *self); + static void gdm_display_finalize (GObject *object); + static void queue_finish (GdmDisplay *self); + static void _gdm_display_set_status (GdmDisplay *self, + int status); + static gboolean wants_initial_setup (GdmDisplay *self); + G_DEFINE_ABSTRACT_TYPE (GdmDisplay, gdm_display, G_TYPE_OBJECT) + +-static gboolean +-chown_file (GFile *file, +- uid_t uid, +- gid_t gid, +- GError **error) +-{ +- if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_UID, uid, +- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, +- NULL, error)) { +- return FALSE; +- } +- if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_GID, gid, +- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, +- NULL, error)) { +- return FALSE; +- } +- return TRUE; +-} +- +-static gboolean +-chown_recursively (GFile *dir, +- uid_t uid, +- gid_t gid, +- GError **error) +-{ +- GFile *file = NULL; +- GFileInfo *info = NULL; +- GFileEnumerator *enumerator = NULL; +- gboolean retval = FALSE; +- +- if (chown_file (dir, uid, gid, error) == FALSE) { +- goto out; +- } +- +- enumerator = g_file_enumerate_children (dir, +- G_FILE_ATTRIBUTE_STANDARD_TYPE"," +- G_FILE_ATTRIBUTE_STANDARD_NAME, +- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, +- NULL, error); +- if (!enumerator) { +- goto out; +- } +- +- while ((info = g_file_enumerator_next_file (enumerator, NULL, error)) != NULL) { +- file = g_file_get_child (dir, g_file_info_get_name (info)); +- +- if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { +- if (chown_recursively (file, uid, gid, error) == FALSE) { +- goto out; +- } +- } else if (chown_file (file, uid, gid, error) == FALSE) { +- goto out; +- } +- +- g_clear_object (&file); +- g_clear_object (&info); +- } +- +- if (*error) { +- goto out; +- } +- +- retval = TRUE; +-out: +- g_clear_object (&file); +- g_clear_object (&info); +- g_clear_object (&enumerator); +- +- return retval; +-} +- + GQuark + gdm_display_error_quark (void) + { + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_display_error"); + } + + return ret; + } + + time_t + gdm_display_get_creation_time (GdmDisplay *self) + { + g_return_val_if_fail (GDM_IS_DISPLAY (self), 0); + + return self->priv->creation_time; + } + + int + gdm_display_get_status (GdmDisplay *self) + { + g_return_val_if_fail (GDM_IS_DISPLAY (self), 0); + + return self->priv->status; + } + + const char * + gdm_display_get_session_id (GdmDisplay *self) + { +@@ -1649,145 +1577,85 @@ gdm_display_start_greeter_session (GdmDisplay *self) + G_CALLBACK (on_launch_environment_session_stopped), + self, 0); + g_signal_connect_object (self->priv->launch_environment, + "exited", + G_CALLBACK (on_launch_environment_session_exited), + self, 0); + g_signal_connect_object (self->priv->launch_environment, + "died", + G_CALLBACK (on_launch_environment_session_died), + self, 0); + + if (auth_file != NULL) { + g_object_set (self->priv->launch_environment, + "x11-authority-file", auth_file, + NULL); + } + + gdm_launch_environment_start (self->priv->launch_environment); + + session = gdm_launch_environment_get_session (self->priv->launch_environment); + g_object_set (G_OBJECT (session), + "display-is-initial", self->priv->is_initial, + NULL); + + g_free (display_name); + g_free (seat_id); + g_free (hostname); + g_free (auth_file); + } + +-static void +-chown_initial_setup_home_dir (void) +-{ +- GFile *dir; +- GError *error; +- char *gis_dir_path; +- char *gis_uid_path; +- char *gis_uid_contents; +- struct passwd *pwe; +- uid_t uid; +- +- if (!gdm_get_pwent_for_name (INITIAL_SETUP_USERNAME, &pwe)) { +- g_warning ("Unknown user %s", INITIAL_SETUP_USERNAME); +- return; +- } +- +- gis_dir_path = g_strdup (pwe->pw_dir); +- +- gis_uid_path = g_build_filename (gis_dir_path, +- "gnome-initial-setup-uid", +- NULL); +- if (!g_file_get_contents (gis_uid_path, &gis_uid_contents, NULL, NULL)) { +- g_warning ("Unable to read %s", gis_uid_path); +- goto out; +- } +- +- uid = (uid_t) atoi (gis_uid_contents); +- pwe = getpwuid (uid); +- if (uid == 0 || pwe == NULL) { +- g_warning ("UID '%s' in %s is not valid", gis_uid_contents, gis_uid_path); +- goto out; +- } +- +- error = NULL; +- dir = g_file_new_for_path (gis_dir_path); +- if (!chown_recursively (dir, pwe->pw_uid, pwe->pw_gid, &error)) { +- g_warning ("Failed to change ownership for %s: %s", gis_dir_path, error->message); +- g_error_free (error); +- } +- g_object_unref (dir); +-out: +- g_free (gis_uid_contents); +- g_free (gis_uid_path); +- g_free (gis_dir_path); +-} +- + void + gdm_display_stop_greeter_session (GdmDisplay *self) + { + GError *error = NULL; + + if (self->priv->launch_environment != NULL) { + + g_signal_handlers_disconnect_by_func (self->priv->launch_environment, + G_CALLBACK (on_launch_environment_session_opened), + self); + g_signal_handlers_disconnect_by_func (self->priv->launch_environment, + G_CALLBACK (on_launch_environment_session_started), + self); + g_signal_handlers_disconnect_by_func (self->priv->launch_environment, + G_CALLBACK (on_launch_environment_session_stopped), + self); + g_signal_handlers_disconnect_by_func (self->priv->launch_environment, + G_CALLBACK (on_launch_environment_session_exited), + self); + g_signal_handlers_disconnect_by_func (self->priv->launch_environment, + G_CALLBACK (on_launch_environment_session_died), + self); + gdm_launch_environment_stop (self->priv->launch_environment); + g_clear_object (&self->priv->launch_environment); + } +- +- if (self->priv->doing_initial_setup) { +- chown_initial_setup_home_dir (); +- +- if (!g_file_set_contents (ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT, +- "1", +- 1, +- &error)) { +- g_warning ("GdmDisplay: Could not write initial-setup-done marker to %s: %s", +- ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT, +- error->message); +- g_clear_error (&error); +- } +- } + } + + static xcb_window_t + get_root_window (xcb_connection_t *connection, + int screen_number) + { + xcb_screen_t *screen = NULL; + xcb_screen_iterator_t iter; + + iter = xcb_setup_roots_iterator (xcb_get_setup (connection)); + while (iter.rem) { + if (screen_number == 0) + screen = iter.data; + screen_number--; + xcb_screen_next (&iter); + } + + if (screen != NULL) { + return screen->root; + } + + return XCB_WINDOW_NONE; + } + + static void + gdm_display_set_windowpath (GdmDisplay *self) + { + /* setting WINDOWPATH for clients */ + xcb_intern_atom_cookie_t atom_cookie; + xcb_intern_atom_reply_t *atom_reply = NULL; +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index 0823e8638..cf982870c 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -35,60 +35,61 @@ + #include + + #include + + #include + + #include "gdm-common.h" + + #include "gdm-dbus-util.h" + #include "gdm-manager.h" + #include "gdm-manager-glue.h" + #include "gdm-display-store.h" + #include "gdm-display-factory.h" + #include "gdm-launch-environment.h" + #include "gdm-local-display.h" + #include "gdm-local-display-factory.h" + #include "gdm-session.h" + #include "gdm-session-record.h" + #include "gdm-settings-direct.h" + #include "gdm-settings-keys.h" + #include "gdm-xdmcp-display-factory.h" + #include "gdm-xdmcp-chooser-display.h" + + #define GDM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_MANAGER, GdmManagerPrivate)) + + #define GDM_DBUS_PATH "/org/gnome/DisplayManager" + #define GDM_MANAGER_PATH GDM_DBUS_PATH "/Manager" + #define GDM_MANAGER_DISPLAYS_PATH GDM_DBUS_PATH "/Displays" + + #define INITIAL_SETUP_USERNAME "gnome-initial-setup" ++#define ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT GDM_RUN_DIR "/gdm.ran-initial-setup" + + typedef struct + { + GdmManager *manager; + GdmSession *session; + char *service_name; + guint idle_id; + } StartUserSessionOperation; + + struct GdmManagerPrivate + { + GdmDisplayStore *display_store; + GdmLocalDisplayFactory *local_factory; + #ifdef HAVE_LIBXDMCP + GdmXdmcpDisplayFactory *xdmcp_factory; + #endif + GdmDisplay *automatic_login_display; + GList *user_sessions; + GHashTable *transient_sessions; + GHashTable *open_reauthentication_requests; + gboolean xdmcp_enabled; + + gboolean started; + gboolean show_local_greeter; + + GDBusConnection *connection; + GDBusObjectManagerServer *object_manager; + + #ifdef WITH_PLYMOUTH + guint plymouth_is_running : 1; +@@ -1630,130 +1631,261 @@ start_user_session (GdmManager *manager, + + destroy_start_user_session_operation (operation); + } + + static void + create_display_for_user_session (GdmManager *self, + GdmSession *session, + const char *session_id) + { + GdmDisplay *display; + /* at the moment we only create GdmLocalDisplay objects on seat0 */ + const char *seat_id = "seat0"; + + display = gdm_local_display_new (); + + g_object_set (G_OBJECT (display), + "session-class", "user", + "seat-id", seat_id, + "session-id", session_id, + NULL); + gdm_display_store_add (self->priv->display_store, + display); + g_object_set_data (G_OBJECT (session), "gdm-display", display); + g_object_set_data_full (G_OBJECT (display), + "gdm-user-session", + g_object_ref (session), + (GDestroyNotify) + clean_user_session); + } + ++static gboolean ++chown_file (GFile *file, ++ uid_t uid, ++ gid_t gid, ++ GError **error) ++{ ++ if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_UID, uid, ++ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, ++ NULL, error)) { ++ return FALSE; ++ } ++ if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_GID, gid, ++ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, ++ NULL, error)) { ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static gboolean ++chown_recursively (GFile *dir, ++ uid_t uid, ++ gid_t gid, ++ GError **error) ++{ ++ GFile *file = NULL; ++ GFileInfo *info = NULL; ++ GFileEnumerator *enumerator = NULL; ++ gboolean retval = FALSE; ++ ++ if (chown_file (dir, uid, gid, error) == FALSE) { ++ goto out; ++ } ++ ++ enumerator = g_file_enumerate_children (dir, ++ G_FILE_ATTRIBUTE_STANDARD_TYPE"," ++ G_FILE_ATTRIBUTE_STANDARD_NAME, ++ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, ++ NULL, error); ++ if (!enumerator) { ++ goto out; ++ } ++ ++ while ((info = g_file_enumerator_next_file (enumerator, NULL, error)) != NULL) { ++ file = g_file_get_child (dir, g_file_info_get_name (info)); ++ ++ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { ++ if (chown_recursively (file, uid, gid, error) == FALSE) { ++ goto out; ++ } ++ } else if (chown_file (file, uid, gid, error) == FALSE) { ++ goto out; ++ } ++ ++ g_clear_object (&file); ++ g_clear_object (&info); ++ } ++ ++ if (*error) { ++ goto out; ++ } ++ ++ retval = TRUE; ++out: ++ g_clear_object (&file); ++ g_clear_object (&info); ++ g_clear_object (&enumerator); ++ ++ return retval; ++} ++ ++static void ++chown_initial_setup_home_dir (void) ++{ ++ GFile *dir; ++ GError *error; ++ char *gis_dir_path; ++ char *gis_uid_path; ++ char *gis_uid_contents; ++ struct passwd *pwe; ++ uid_t uid; ++ ++ if (!gdm_get_pwent_for_name (INITIAL_SETUP_USERNAME, &pwe)) { ++ g_warning ("Unknown user %s", INITIAL_SETUP_USERNAME); ++ return; ++ } ++ ++ gis_dir_path = g_strdup (pwe->pw_dir); ++ ++ gis_uid_path = g_build_filename (gis_dir_path, ++ "gnome-initial-setup-uid", ++ NULL); ++ if (!g_file_get_contents (gis_uid_path, &gis_uid_contents, NULL, NULL)) { ++ g_warning ("Unable to read %s", gis_uid_path); ++ goto out; ++ } ++ ++ uid = (uid_t) atoi (gis_uid_contents); ++ pwe = getpwuid (uid); ++ if (uid == 0 || pwe == NULL) { ++ g_warning ("UID '%s' in %s is not valid", gis_uid_contents, gis_uid_path); ++ goto out; ++ } ++ ++ error = NULL; ++ dir = g_file_new_for_path (gis_dir_path); ++ if (!chown_recursively (dir, pwe->pw_uid, pwe->pw_gid, &error)) { ++ g_warning ("Failed to change ownership for %s: %s", gis_dir_path, error->message); ++ g_error_free (error); ++ } ++ g_object_unref (dir); ++out: ++ g_free (gis_uid_contents); ++ g_free (gis_uid_path); ++ g_free (gis_dir_path); ++} ++ + static gboolean + on_start_user_session (StartUserSessionOperation *operation) + { + GdmManager *self = operation->manager; + gboolean migrated; + gboolean fail_if_already_switched = TRUE; + gboolean doing_initial_setup = FALSE; + GdmDisplay *display; + const char *session_id; + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + g_autofree char *display_session_type = NULL; + #endif + + g_debug ("GdmManager: start or jump to session"); + + /* If there's already a session running, jump to it. + * If the only session running is the one we just opened, + * start a session on it. + */ + migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched); + + g_debug ("GdmManager: migrated: %d", migrated); + if (migrated) { + /* We don't stop the manager here because + when Xorg exits it switches to the VT it was + started from. That interferes with fast + user switching. */ + gdm_session_reset (operation->session); + destroy_start_user_session_operation (operation); + goto out; + } + + display = get_display_for_user_session (operation->session); + + g_object_get (G_OBJECT (display), + "doing-initial-setup", &doing_initial_setup, + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + "session-type", &display_session_type, + #endif + NULL); + + session_id = gdm_session_get_conversation_session_id (operation->session, + operation->service_name); + + if (gdm_session_get_display_mode (operation->session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) { + /* In this case, the greeter's display is morphing into + * the user session display. Kill the greeter on this session + * and let the user session follow the same display. */ + gdm_display_stop_greeter_session (display); + g_object_set (G_OBJECT (display), + "session-class", "user", + "session-id", session_id, + NULL); + } else { + uid_t allowed_uid; + + g_object_ref (display); + if (doing_initial_setup) { ++ g_autoptr(GError) error = NULL; ++ + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + if (g_strcmp0 (display_session_type, "wayland") == 0) { + g_debug ("GdmManager: closing down initial setup display in background"); + g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); + } + #endif + if (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { + g_debug ("GdmManager: closing down initial setup display"); + gdm_display_stop_greeter_session (display); + gdm_display_unmanage (display); + gdm_display_finish (display); + } ++ ++ chown_initial_setup_home_dir (); ++ ++ if (!g_file_set_contents (ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT, ++ "1", ++ 1, ++ &error)) { ++ g_warning ("GdmDisplay: Could not write initial-setup-done marker to %s: %s", ++ ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT, ++ error->message); ++ g_clear_error (&error); ++ } + } else { + g_debug ("GdmManager: session has its display server, reusing our server for another login screen"); + } + + /* The user session is going to follow the session worker + * into the new display. Untie it from this display and + * create a new session for a future user login. */ + allowed_uid = gdm_session_get_allowed_user (operation->session); + g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); + g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL); + create_user_session_for_display (operation->manager, display, allowed_uid); + + /* Give the user session a new display object for bookkeeping purposes */ + create_display_for_user_session (operation->manager, + operation->session, + session_id); + + if ((g_strcmp0 (operation->service_name, "gdm-autologin") == 0) && + !gdm_session_client_is_connected (operation->session)) { + /* remove the unused prepared greeter display since we're not going + * to have a greeter */ + gdm_display_store_remove (self->priv->display_store, display); + g_object_unref (display); + + self->priv->automatic_login_display = g_object_get_data (G_OBJECT (operation->session), "gdm-display"); + g_object_add_weak_pointer (G_OBJECT (self->priv->automatic_login_display), (gpointer *) &self->priv->automatic_login_display); + } + } + + start_user_session (operation->manager, operation); +-- +2.26.0 + diff --git a/SOURCES/0040-display-store-make-foreach-ignore-callback-return-va.patch b/SOURCES/0040-display-store-make-foreach-ignore-callback-return-va.patch new file mode 100644 index 0000000..60f7ddc --- /dev/null +++ b/SOURCES/0040-display-store-make-foreach-ignore-callback-return-va.patch @@ -0,0 +1,117 @@ +From 91c47f81635a77e803a1f6ff412bb6e2a2012d37 Mon Sep 17 00:00:00 2001 +From: xiaoguang wang +Date: Wed, 20 Feb 2019 09:26:02 +0800 +Subject: [PATCH 40/48] display-store: make foreach ignore callback return + value + +gdm_display_store_foreach is designed to iterate through all +displays in the display store. Under the hood, it currently +uses gdm_display_store_find, though, so will prematurely stop +it's loop if a callback returns TRUE. Callers are getting this +wrong. Some return TRUE with the expectation it goes on, and +some fail to return a value at all. + +This commit changes gdm_display_store_foreach to use +g_hash_table_foreach instead, so the callback return values no +longer matter. +--- + daemon/gdm-display-store.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/daemon/gdm-display-store.c b/daemon/gdm-display-store.c +index fd24334eb..910468cd7 100644 +--- a/daemon/gdm-display-store.c ++++ b/daemon/gdm-display-store.c +@@ -119,76 +119,86 @@ remove_display (char *id, + } + + gboolean + gdm_display_store_remove (GdmDisplayStore *store, + GdmDisplay *display) + { + g_return_val_if_fail (store != NULL, FALSE); + + gdm_display_store_foreach_remove (store, + (GdmDisplayStoreFunc)remove_display, + display); + return FALSE; + } + + typedef struct + { + GdmDisplayStoreFunc predicate; + gpointer user_data; + } FindClosure; + + static gboolean + find_func (const char *id, + StoredDisplay *stored_display, + FindClosure *closure) + { + return closure->predicate (id, + stored_display->display, + closure->user_data); + } + ++static void ++foreach_func (const char *id, ++ StoredDisplay *stored_display, ++ FindClosure *closure) ++{ ++ (void) closure->predicate (id, ++ stored_display->display, ++ closure->user_data); ++} ++ + void + gdm_display_store_foreach (GdmDisplayStore *store, + GdmDisplayStoreFunc func, + gpointer user_data) + { + FindClosure closure; + + g_return_if_fail (store != NULL); + g_return_if_fail (func != NULL); + + closure.predicate = func; + closure.user_data = user_data; + +- g_hash_table_find (store->priv->displays, +- (GHRFunc) find_func, +- &closure); ++ g_hash_table_foreach (store->priv->displays, ++ (GHFunc) foreach_func, ++ &closure); + } + + GdmDisplay * + gdm_display_store_lookup (GdmDisplayStore *store, + const char *id) + { + StoredDisplay *stored_display; + + g_return_val_if_fail (store != NULL, NULL); + g_return_val_if_fail (id != NULL, NULL); + + stored_display = g_hash_table_lookup (store->priv->displays, + id); + if (stored_display == NULL) { + return NULL; + } + + return stored_display->display; + } + + GdmDisplay * + gdm_display_store_find (GdmDisplayStore *store, + GdmDisplayStoreFunc predicate, + gpointer user_data) + { + StoredDisplay *stored_display; + FindClosure closure; + + g_return_val_if_fail (store != NULL, NULL); + g_return_val_if_fail (predicate != NULL, NULL); +-- +2.26.0 + diff --git a/SOURCES/0041-xdmcp-display-factory-don-t-return-value-from-foreac.patch b/SOURCES/0041-xdmcp-display-factory-don-t-return-value-from-foreac.patch new file mode 100644 index 0000000..87878db --- /dev/null +++ b/SOURCES/0041-xdmcp-display-factory-don-t-return-value-from-foreac.patch @@ -0,0 +1,180 @@ +From 53132e40e407e4af455504f08d733e485b6bd48e Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 21 Feb 2019 15:20:01 -0500 +Subject: [PATCH 41/48] xdmcp-display-factory: don't return value from foreach + funcs + +The xdmcp code is returning TRUE from its display store foreach +functions, which is useless since commit 47d01abe and wrong +before that. + +This commit makes it return void instead. +--- + daemon/gdm-xdmcp-display-factory.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/daemon/gdm-xdmcp-display-factory.c b/daemon/gdm-xdmcp-display-factory.c +index 5b5786c6f..2e14beab4 100644 +--- a/daemon/gdm-xdmcp-display-factory.c ++++ b/daemon/gdm-xdmcp-display-factory.c +@@ -639,76 +639,74 @@ gdm_xdmcp_host_allow (GdmAddress *address) + { + #ifdef HAVE_TCPWRAPPERS + char *client; + char *host; + gboolean ret; + + host = NULL; + client = NULL; + + /* Find client hostname */ + gdm_address_get_hostname (address, &client); + gdm_address_get_numeric_info (address, &host, NULL); + + /* Check with tcp_wrappers if client is allowed to access */ + ret = hosts_ctl ("gdm", client, host, ""); + + g_free (host); + g_free (client); + + return ret; + #else /* HAVE_TCPWRAPPERS */ + return (TRUE); + #endif /* HAVE_TCPWRAPPERS */ + } + + typedef struct { + GdmAddress *address; + int count; + } CountDisplayData; + +-static gboolean ++static void + count_displays_from_host (const char *id, + GdmDisplay *display, + CountDisplayData *data) + { + GdmAddress *address; + + if (GDM_IS_XDMCP_DISPLAY (display)) { + address = gdm_xdmcp_display_get_remote_address (GDM_XDMCP_DISPLAY (display)); + + if (gdm_address_equal (address, data->address)) { + data->count++; + } + } +- +- return TRUE; + } + + static int + gdm_xdmcp_num_displays_from_host (GdmXdmcpDisplayFactory *factory, + GdmAddress *address) + { + CountDisplayData data; + GdmDisplayStore *store; + + data.count = 0; + data.address = address; + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + gdm_display_store_foreach (store, + (GdmDisplayStoreFunc)count_displays_from_host, + &data); + + return data.count; + } + + typedef struct { + GdmAddress *address; + int display_num; + } LookupHostData; + + static gboolean + lookup_by_host (const char *id, + GdmDisplay *display, + LookupHostData *data) + { +@@ -1780,78 +1778,76 @@ gdm_xdmcp_send_managed_forward (GdmXdmcpDisplayFactory *factory, + + static void + gdm_xdmcp_send_got_managed_forward (GdmXdmcpDisplayFactory *factory, + GdmAddress *address, + GdmAddress *origin) + { + ARRAY8 addr; + XdmcpHeader header; + char *host; + + host = NULL; + gdm_address_get_numeric_info (address, &host, NULL); + g_debug ("GdmXdmcpDisplayFactory: Sending GOT_MANAGED_FORWARD to %s", + host ? host : "(null)"); + g_free (host); + + set_address_for_request (origin, &addr); + + header.opcode = (CARD16) GDM_XDMCP_GOT_MANAGED_FORWARD; + header.length = 4 + addr.length; + header.version = GDM_XDMCP_PROTOCOL_VERSION; + XdmcpWriteHeader (&factory->priv->buf, &header); + + XdmcpWriteARRAY8 (&factory->priv->buf, &addr); + XdmcpFlush (factory->priv->socket_fd, + &factory->priv->buf, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), + (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address))); + } + +-static gboolean ++static void + count_sessions (const char *id, + GdmDisplay *display, + GdmXdmcpDisplayFactory *factory) + { + if (GDM_IS_XDMCP_DISPLAY (display)) { + int status; + + status = gdm_display_get_status (display); + + if (status == GDM_DISPLAY_MANAGED) { + factory->priv->num_sessions++; + } else if (status == GDM_DISPLAY_UNMANAGED) { + factory->priv->num_pending_sessions++; + } + } +- +- return TRUE; + } + + static void + gdm_xdmcp_recount_sessions (GdmXdmcpDisplayFactory *factory) + { + GdmDisplayStore *store; + + factory->priv->num_sessions = 0; + factory->priv->num_pending_sessions = 0; + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + gdm_display_store_foreach (store, + (GdmDisplayStoreFunc)count_sessions, + factory); + } + + static gboolean + purge_displays (const char *id, + GdmDisplay *display, + GdmXdmcpDisplayFactory *factory) + { + if (GDM_IS_XDMCP_DISPLAY (display)) { + int status; + time_t currtime; + time_t acctime; + + currtime = time (NULL); + status = gdm_display_get_status (display); + acctime = gdm_display_get_creation_time (display); + +-- +2.26.0 + diff --git a/SOURCES/0042-GdmLocalDisplayFactory-Store-VT-number-not-tty-ident.patch b/SOURCES/0042-GdmLocalDisplayFactory-Store-VT-number-not-tty-ident.patch new file mode 100644 index 0000000..b18ff53 --- /dev/null +++ b/SOURCES/0042-GdmLocalDisplayFactory-Store-VT-number-not-tty-ident.patch @@ -0,0 +1,538 @@ +From e2a85ed5bf731d36575ae6d6ea899599589a12d0 Mon Sep 17 00:00:00 2001 +From: Iain Lane +Date: Tue, 7 May 2019 15:35:23 +0100 +Subject: [PATCH 42/48] GdmLocalDisplayFactory: Store VT number, not tty + identifier + +This makes the code a fair bit simpler. +--- + configure.ac | 6 ++-- + daemon/gdm-local-display-factory.c | 50 ++++++++++++++++-------------- + daemon/gdm-server.c | 2 +- + daemon/gdm-session-worker.c | 2 +- + 4 files changed, 31 insertions(+), 29 deletions(-) + +diff --git a/configure.ac b/configure.ac +index c549146ce..0c138ab38 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1477,66 +1477,66 @@ fi + AC_SUBST(DEBUG_CFLAGS) + + # + # Enable Profiling + # + AC_ARG_ENABLE(profiling, + AS_HELP_STRING([--enable-profiling], + [turn on profiling]),, + enable_profiling=yes) + + if test "$enable_profiling" = "yes"; then + AC_DEFINE(ENABLE_PROFILING,1,[enable profiling]) + fi + + # + # Set SHELL to use in scripts. + # + if test x$os_solaris = xyes ; then + XSESSION_SHELL=/bin/ksh + else + XSESSION_SHELL=/bin/sh + fi + + # + # Set VT to use for initial server + # + AC_ARG_WITH(initial-vt, + AS_HELP_STRING([--with-initial-vt=], + [Initial virtual terminal to use])) + if ! test -z "$with_initial_vt"; then +- GDM_INITIAL_VT="$with_initial_vt" ++ GDM_INITIAL_VT=$with_initial_vt + else +- GDM_INITIAL_VT="1" ++ GDM_INITIAL_VT=1 + fi + AC_SUBST(GDM_INITIAL_VT) +-AC_DEFINE_UNQUOTED(GDM_INITIAL_VT, "$GDM_INITIAL_VT", [Initial Virtual Terminal]) ++AC_DEFINE_UNQUOTED(GDM_INITIAL_VT, $GDM_INITIAL_VT, [Initial Virtual Terminal]) + + # Set configuration choices. + # + AC_SUBST(XSESSION_SHELL) + AC_DEFINE_UNQUOTED(XSESSION_SHELL,"$XSESSION_SHELL",[xsession shell]) + AC_SUBST(SOUND_PROGRAM) + AC_DEFINE_UNQUOTED(SOUND_PROGRAM,"$SOUND_PROGRAM",[]) + + AC_SUBST(X_PATH) + AC_SUBST(X_SERVER) + AC_SUBST(X_SERVER_PATH) + AC_DEFINE_UNQUOTED(X_SERVER,"$X_SERVER",[]) + AC_DEFINE_UNQUOTED(X_SERVER_PATH,"$X_SERVER_PATH",[]) + + ## Stuff for debian/changelog.in + #if test -e "debian/changelog"; then + # DEBIAN_DATESTAMP=`head -1 debian/changelog| sed -e 's/.*cvs.//' -e 's/).*//'` + # DEBIAN_DATE=`grep '^ --' debian/changelog | head -1 | sed -e 's/.* //'` + #else + # DEBIAN_DATESTAMP=`date +%Y%m%d%H%M%s` + # DEBIAN_DATE=`date -R` + #fi + # + #AC_SUBST(DEBIAN_DATESTAMP) + #AC_SUBST(DEBIAN_DATE) + + AC_CONFIG_FILES([ + Makefile + pam-extensions/Makefile + pam-extensions/gdm-pam-extensions.pc +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index d999596b5..7a013c694 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -37,61 +37,61 @@ + #include "gdm-local-display-factory-glue.h" + + #include "gdm-settings-keys.h" + #include "gdm-settings-direct.h" + #include "gdm-display-store.h" + #include "gdm-local-display.h" + #include "gdm-legacy-display.h" + + #define GDM_LOCAL_DISPLAY_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_LOCAL_DISPLAY_FACTORY, GdmLocalDisplayFactoryPrivate)) + + #define GDM_DBUS_PATH "/org/gnome/DisplayManager" + #define GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH GDM_DBUS_PATH "/LocalDisplayFactory" + #define GDM_MANAGER_DBUS_NAME "org.gnome.DisplayManager.LocalDisplayFactory" + + #define MAX_DISPLAY_FAILURES 5 + #define WAIT_TO_FINISH_TIMEOUT 10 /* seconds */ + + struct GdmLocalDisplayFactoryPrivate + { + GdmDBusLocalDisplayFactory *skeleton; + GDBusConnection *connection; + GHashTable *used_display_numbers; + + /* FIXME: this needs to be per seat? */ + guint num_failures; + + guint seat_new_id; + guint seat_removed_id; + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) +- char *tty_of_active_vt; ++ unsigned int active_vt; + guint active_vt_watch_id; + guint wait_to_finish_timeout_id; + #endif + }; + + enum { + PROP_0, + }; + + static void gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass); + static void gdm_local_display_factory_init (GdmLocalDisplayFactory *factory); + static void gdm_local_display_factory_finalize (GObject *object); + + static GdmDisplay *create_display (GdmLocalDisplayFactory *factory, + const char *seat_id, + const char *session_type, + gboolean initial_display); + + static void on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, + GdmLocalDisplayFactory *factory); + + static gboolean gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory); + static gpointer local_display_factory_object = NULL; + static gboolean lookup_by_session_id (const char *id, + GdmDisplay *display, + gpointer user_data); + + G_DEFINE_TYPE (GdmLocalDisplayFactory, gdm_local_display_factory, GDM_TYPE_DISPLAY_FACTORY) + +@@ -641,157 +641,161 @@ maybe_stop_greeter_in_background (GdmLocalDisplayFactory *factory, + g_debug ("GdmLocalDisplayFactory: login window is performing initial-setup, so ignoring"); + return; + } + + /* we can only stop greeter for wayland sessions, since + * X server would jump back on exit */ + if (g_strcmp0 (display_session_type, "wayland") != 0) { + g_debug ("GdmLocalDisplayFactory: login window is running on Xorg, so ignoring"); + return; + } + + g_debug ("GdmLocalDisplayFactory: killing login window once its unused"); + g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); + + /* We stop the greeter after a timeout to avoid flicker */ + if (factory->priv->wait_to_finish_timeout_id != 0) + g_source_remove (factory->priv->wait_to_finish_timeout_id); + + factory->priv->wait_to_finish_timeout_id = + g_timeout_add_seconds (WAIT_TO_FINISH_TIMEOUT, + (GSourceFunc)wait_to_finish_timeout, + factory); + } + + static gboolean + on_vt_changed (GIOChannel *source, + GIOCondition condition, + GdmLocalDisplayFactory *factory) + { + GIOStatus status; +- static const char *tty_of_initial_vt = "tty" GDM_INITIAL_VT; +- g_autofree char *tty_of_previous_vt = NULL; + g_autofree char *tty_of_active_vt = NULL; + g_autofree char *login_session_id = NULL; + g_autofree char *active_session_id = NULL; ++ unsigned int previous_vt, new_vt; + const char *session_type = NULL; +- int ret; ++ int ret, n_returned; + + g_debug ("GdmLocalDisplayFactory: received VT change event"); + g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL); + + if (condition & G_IO_PRI) { + g_autoptr (GError) error = NULL; + status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error); + + if (error != NULL) { + g_warning ("could not read active VT from kernel: %s", error->message); + } + switch (status) { + case G_IO_STATUS_ERROR: + return G_SOURCE_REMOVE; + case G_IO_STATUS_EOF: + return G_SOURCE_REMOVE; + case G_IO_STATUS_AGAIN: + return G_SOURCE_CONTINUE; + case G_IO_STATUS_NORMAL: + break; + } + } + + if ((condition & G_IO_ERR) || (condition & G_IO_HUP)) { + g_debug ("GdmLocalDisplayFactory: kernel hung up active vt watch"); + return G_SOURCE_REMOVE; + } + + if (tty_of_active_vt == NULL) { + g_debug ("GdmLocalDisplayFactory: unable to read active VT from kernel"); + return G_SOURCE_CONTINUE; + } + + g_strchomp (tty_of_active_vt); + ++ errno = 0; ++ n_returned = sscanf (tty_of_active_vt, "tty%u", &new_vt); ++ ++ if (n_returned != 1 || errno != 0) { ++ g_critical ("GdmLocalDisplayFactory: Couldn't read active VT (got '%s')", ++ tty_of_active_vt); ++ return G_SOURCE_CONTINUE; ++ } ++ + /* don't do anything if we're on the same VT we were before */ +- if (g_strcmp0 (tty_of_active_vt, factory->priv->tty_of_active_vt) == 0) { ++ if (new_vt == factory->priv->active_vt) { + g_debug ("GdmLocalDisplayFactory: VT changed to the same VT, ignoring"); + return G_SOURCE_CONTINUE; + } + +- tty_of_previous_vt = g_steal_pointer (&factory->priv->tty_of_active_vt); +- factory->priv->tty_of_active_vt = g_steal_pointer (&tty_of_active_vt); ++ previous_vt = factory->priv->active_vt; ++ factory->priv->active_vt = new_vt; + + /* don't do anything at start up */ +- if (tty_of_previous_vt == NULL) { +- g_debug ("GdmLocalDisplayFactory: VT is %s at startup", +- factory->priv->tty_of_active_vt); ++ if (previous_vt == 0) { ++ g_debug ("GdmLocalDisplayFactory: VT is %u at startup", ++ factory->priv->active_vt); + return G_SOURCE_CONTINUE; + } + +- g_debug ("GdmLocalDisplayFactory: VT changed from %s to %s", +- tty_of_previous_vt, factory->priv->tty_of_active_vt); ++ g_debug ("GdmLocalDisplayFactory: VT changed from %u to %u", ++ previous_vt, factory->priv->active_vt); + + /* if the old VT was running a wayland login screen kill it + */ + if (gdm_get_login_window_session_id ("seat0", &login_session_id)) { +- unsigned int vt; ++ unsigned int login_window_vt; + +- ret = sd_session_get_vt (login_session_id, &vt); +- if (ret == 0 && vt != 0) { +- g_autofree char *tty_of_login_window_vt = NULL; +- +- tty_of_login_window_vt = g_strdup_printf ("tty%u", vt); +- +- g_debug ("GdmLocalDisplayFactory: tty of login window is %s", tty_of_login_window_vt); +- if (g_strcmp0 (tty_of_login_window_vt, tty_of_previous_vt) == 0) { ++ ret = sd_session_get_vt (login_session_id, &login_window_vt); ++ if (ret == 0 && login_window_vt != 0) { ++ g_debug ("GdmLocalDisplayFactory: VT of login window is %u", login_window_vt); ++ if (login_window_vt == previous_vt) { + GdmDisplayStore *store; + GdmDisplay *display; + + g_debug ("GdmLocalDisplayFactory: VT switched from login window"); + + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + display = gdm_display_store_find (store, + lookup_by_session_id, + (gpointer) login_session_id); + + if (display != NULL) + maybe_stop_greeter_in_background (factory, display); + } else { + g_debug ("GdmLocalDisplayFactory: VT not switched from login window"); + } + } + } + + /* if user jumped back to initial vt and it's empty put a login screen + * on it (unless a login screen is already running elsewhere, then + * jump to that login screen) + */ +- if (strcmp (factory->priv->tty_of_active_vt, tty_of_initial_vt) != 0) { ++ if (factory->priv->active_vt != GDM_INITIAL_VT) { + g_debug ("GdmLocalDisplayFactory: active VT is not initial VT, so ignoring"); + return G_SOURCE_CONTINUE; + } + + if (gdm_local_display_factory_use_wayland ()) + session_type = "wayland"; + + g_debug ("GdmLocalDisplayFactory: creating new display on seat0 because of VT change"); + + create_display (factory, "seat0", session_type, TRUE); + + return G_SOURCE_CONTINUE; + } + #endif + + static void + gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory) + { + g_autoptr (GIOChannel) io_channel = NULL; + + factory->priv->seat_new_id = g_dbus_connection_signal_subscribe (factory->priv->connection, + "org.freedesktop.login1", + "org.freedesktop.login1.Manager", + "SeatNew", + "/org/freedesktop/login1", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_seat_new, + g_object_ref (factory), + g_object_unref); +@@ -815,62 +819,60 @@ gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory) + G_IO_PRI, + (GIOFunc) + on_vt_changed, + factory); + } + #endif + } + + static void + gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory) + { + if (factory->priv->seat_new_id) { + g_dbus_connection_signal_unsubscribe (factory->priv->connection, + factory->priv->seat_new_id); + factory->priv->seat_new_id = 0; + } + if (factory->priv->seat_removed_id) { + g_dbus_connection_signal_unsubscribe (factory->priv->connection, + factory->priv->seat_removed_id); + factory->priv->seat_removed_id = 0; + } + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + if (factory->priv->wait_to_finish_timeout_id != 0) { + g_source_remove (factory->priv->wait_to_finish_timeout_id); + factory->priv->wait_to_finish_timeout_id = 0; + } + if (factory->priv->active_vt_watch_id) { + g_source_remove (factory->priv->active_vt_watch_id); + factory->priv->active_vt_watch_id = 0; + } +- +- g_clear_pointer (&factory->priv->tty_of_active_vt, g_free); + #endif + } + + static void + on_display_added (GdmDisplayStore *display_store, + const char *id, + GdmLocalDisplayFactory *factory) + { + GdmDisplay *display; + + display = gdm_display_store_lookup (display_store, id); + + if (display != NULL) { + g_signal_connect_object (display, "notify::status", + G_CALLBACK (on_display_status_changed), + factory, + 0); + + g_object_weak_ref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory); + } + } + + static void + on_display_removed (GdmDisplayStore *display_store, + GdmDisplay *display, + GdmLocalDisplayFactory *factory) + { + g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), factory); + g_object_weak_unref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory); + } +diff --git a/daemon/gdm-server.c b/daemon/gdm-server.c +index 83fba99c8..04406a61a 100644 +--- a/daemon/gdm-server.c ++++ b/daemon/gdm-server.c +@@ -726,61 +726,61 @@ gdm_server_spawn (GdmServer *server, + (GChildWatchFunc)server_child_watch, + server); + + ret = TRUE; + out: + g_strfreev (argv); + if (env) { + g_ptr_array_foreach (env, (GFunc)g_free, NULL); + g_ptr_array_free (env, TRUE); + } + return ret; + } + + /** + * gdm_server_start: + * @disp: Pointer to a GdmDisplay structure + * + * Starts a local X server. Handles retries and fatal errors properly. + */ + + gboolean + gdm_server_start (GdmServer *server) + { + gboolean res = FALSE; + const char *vtarg = NULL; + GError *local_error = NULL; + GError **error = &local_error; + + /* Hardcode the VT for the initial X server, but nothing else */ + if (server->priv->is_initial) { +- vtarg = "vt" GDM_INITIAL_VT; ++ vtarg = "vt" G_STRINGIFY (GDM_INITIAL_VT); + } + + /* fork X server process */ + if (!gdm_server_spawn (server, vtarg, error)) { + goto out; + } + + res = TRUE; + out: + if (local_error) { + g_printerr ("%s\n", local_error->message); + g_clear_error (&local_error); + } + return res; + } + + static void + server_died (GdmServer *server) + { + int exit_status; + + g_debug ("GdmServer: Waiting on process %d", server->priv->pid); + exit_status = gdm_wait_on_pid (server->priv->pid); + + if (WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) != 0)) { + g_debug ("GdmServer: Wait on child process failed"); + } else { + /* exited normally */ + } + +diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c +index 7ed2789da..b4befaa83 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -2202,61 +2202,61 @@ gdm_session_worker_start_session (GdmSessionWorker *worker, + + g_debug ("GdmSessionWorker: state SESSION_STARTED"); + gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_SESSION_STARTED); + + gdm_session_worker_watch_child (worker); + + out: + if (error_code != PAM_SUCCESS) { + gdm_session_worker_uninitialize_pam (worker, error_code); + return FALSE; + } + + return TRUE; + } + + static gboolean + set_up_for_new_vt (GdmSessionWorker *worker) + { + int fd; + char vt_string[256], tty_string[256]; + int session_vt = 0; + + fd = open ("/dev/tty0", O_RDWR | O_NOCTTY); + + if (fd < 0) { + g_debug ("GdmSessionWorker: couldn't open VT master: %m"); + return FALSE; + } + + if (worker->priv->display_is_initial) { +- session_vt = atoi (GDM_INITIAL_VT); ++ session_vt = GDM_INITIAL_VT; + } else { + if (ioctl(fd, VT_OPENQRY, &session_vt) < 0) { + g_debug ("GdmSessionWorker: couldn't open new VT: %m"); + goto fail; + } + } + + worker->priv->session_vt = session_vt; + + close (fd); + fd = -1; + + g_assert (session_vt > 0); + + g_snprintf (vt_string, sizeof (vt_string), "%d", session_vt); + + /* Set the VTNR. This is used by logind to configure a session in + * the logind-managed case, but it doesn't hurt to set it always. + * When logind gains support for XDG_VTNR=auto, we can make the + * OPENQRY and this whole path only used by the new VT code. */ + gdm_session_worker_set_environment_variable (worker, + "XDG_VTNR", + vt_string); + + g_snprintf (tty_string, 256, "/dev/tty%d", session_vt); + 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; + +-- +2.26.0 + diff --git a/SOURCES/0043-gdm-session-worker-Drop-login_vt-assuming-it-is-GDM_.patch b/SOURCES/0043-gdm-session-worker-Drop-login_vt-assuming-it-is-GDM_.patch new file mode 100644 index 0000000..80b2640 --- /dev/null +++ b/SOURCES/0043-gdm-session-worker-Drop-login_vt-assuming-it-is-GDM_.patch @@ -0,0 +1,344 @@ +From 9d3d6d5b4ebe604959d2d718cefd658a85195409 Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Wed, 25 Sep 2019 14:51:40 +0200 +Subject: [PATCH 43/48] gdm-session-worker: Drop login_vt assuming it is + GDM_INITIAL_VT + +When a session ends, its "session worker" is closed. Since +3e8220921bb608afd06ed677104fd2244b901a28 (3.33.4), we uninitialise PAM +when this happens. As part of this procedure, we jump back to the login +screen, if the screen being killed is not itself the login screen. + +This has broken fast user switching. It goes like this - this +explanation is a bit complicated, bear with us: + +We want to jump back to the login screen when a normal user session +ends, so that people can log in again. We do not want to do this when a +login screen itself ends. When session workers start up, they query for +the *currently active VT* and save this in `login_vt`. Then later on, we +check if our session ID is the same as `login_vt`, and jump to +`login_vt` if they are different - this means that it was a user session +not a login session. Querying the currently active VT is fine for the +first greeter, but when initiating a user switch it's wrong as this +gives the user VT. + +GDM greeters are killed once they have spawned a session. They are +associated with a logind session, and therefore a PAM session. There are +some actions performed when unregistering PAM sessions, including the +previously mentioned VT jump. Before +3e8220921bb608afd06ed677104fd2244b901a28 we only uninitialised PAM when +the session itself exited so the bug was masked, but now (since this +commit), if the login screen's *worker* exits first - as happens in the +normal case when GDM kills it - we also do this uninitialisation. Since +we falsely recorded the login screen as the first user's VT, this means +that checking `login_vt != session_vt` returns `TRUE` and we jump back +to the previous user's session immediately after logging into the new +session: fast user switching is broken. + +Since the work on shutting down the GDM session has been finished, we +can assume that the login_vt is always on GDM_INITIAL_VT (see +example c71bc5d6c3bc2ec448b5c72ce9a811d9c0c7905e +"local-display-factory: Remove initial VT is in use check" and +39fb4ff64e6a0653e70a3bfab31da47b49227d59 "manager: don't run autologin +display on tty1"). So simply replace all usages of login_vt with +GDM_INITIAL_VT to solve the above problem. + +Note that in the case where ENABLE_USER_DISPLAY_SERVER is not enabled, +the login_vt is always the same as the session_vt. We can simply remove +the VT switching magic there and everything should be working as +expected. + +This is a simpler version of the patch by Iain Lane , +taking into account that we can make the assumption about the login_vt. + +Closes #515 +--- + daemon/gdm-session-worker.c | 43 +++++++++---------------------------- + 1 file changed, 10 insertions(+), 33 deletions(-) + +diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c +index b4befaa83..0bd78cfaf 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -119,61 +119,60 @@ typedef struct + + } ReauthenticationRequest; + + struct GdmSessionWorkerPrivate + { + GdmSessionWorkerState state; + + int exit_code; + + pam_handle_t *pam_handle; + + GPid child_pid; + guint child_watch_id; + + /* from Setup */ + char *service; + char *x11_display_name; + char *x11_authority_file; + char *display_device; + char *display_seat_id; + char *hostname; + char *username; + char *log_file; + char *session_id; + uid_t uid; + gid_t gid; + gboolean password_is_required; + char **extensions; + + 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 display_is_local : 1; + guint32 display_is_initial : 1; + guint state_change_idle_id; + GdmSessionDisplayMode display_mode; + + char *server_address; + GDBusConnection *connection; + GdmDBusWorkerManager *manager; + + GHashTable *reauthentication_requests; + + GdmSessionAuditor *auditor; + GdmSessionSettings *user_settings; + + GDBusMethodInvocation *pending_invocation; + }; + + #ifdef SUPPORTS_PAM_EXTENSIONS + static char gdm_pam_extension_environment_block[_POSIX_ARG_MAX]; + + static const char * const + gdm_supported_pam_extensions[] = { +@@ -1029,141 +1028,120 @@ gdm_session_worker_set_state (GdmSessionWorker *worker, + + static void + gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker, + int status) + { + g_debug ("GdmSessionWorker: uninitializing PAM"); + + if (worker->priv->pam_handle == NULL) + return; + + gdm_session_worker_get_username (worker, NULL); + + if (worker->priv->state >= GDM_SESSION_WORKER_STATE_SESSION_OPENED) { + pam_close_session (worker->priv->pam_handle, 0); + gdm_session_auditor_report_logout (worker->priv->auditor); + } else { + gdm_session_auditor_report_login_failure (worker->priv->auditor, + status, + pam_strerror (worker->priv->pam_handle, status)); + } + + if (worker->priv->state >= GDM_SESSION_WORKER_STATE_ACCREDITED) { + pam_setcred (worker->priv->pam_handle, PAM_DELETE_CRED); + } + + pam_end (worker->priv->pam_handle, status); + worker->priv->pam_handle = NULL; + + gdm_session_worker_stop_auditor (worker); + ++ /* If user-display-server is not enabled the login_vt is always ++ * identical to the session_vt. So in that case we never need to ++ * do a VT switch. */ ++#ifdef ENABLE_USER_DISPLAY_SERVER + if (g_strcmp0 (worker->priv->display_seat_id, "seat0") == 0) { +- if (worker->priv->login_vt != worker->priv->session_vt) { +- jump_to_vt (worker, worker->priv->login_vt); ++ /* Switch to the login VT if we are not the login screen. */ ++ if (worker->priv->session_vt != GDM_INITIAL_VT) { ++ jump_to_vt (worker, GDM_INITIAL_VT); + } + } ++#endif + +- worker->priv->login_vt = 0; + worker->priv->session_vt = 0; + + g_debug ("GdmSessionWorker: state NONE"); + gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_NONE); + } + + static char * + _get_tty_for_pam (const char *x11_display_name, + const char *display_device) + { + #ifdef __sun + return g_strdup (display_device); + #else + return g_strdup (x11_display_name); + #endif + } + + #ifdef PAM_XAUTHDATA + static struct pam_xauth_data * + _get_xauth_for_pam (const char *x11_authority_file) + { + FILE *fh; + Xauth *auth = NULL; + struct pam_xauth_data *retval = NULL; + gsize len = sizeof (*retval) + 1; + + fh = fopen (x11_authority_file, "r"); + if (fh) { + auth = XauReadAuth (fh); + fclose (fh); + } + if (auth) { + len += auth->name_length + auth->data_length; + retval = g_malloc0 (len); + } + if (retval) { + retval->namelen = auth->name_length; + retval->name = (char *) (retval + 1); + memcpy (retval->name, auth->name, auth->name_length); + retval->datalen = auth->data_length; + retval->data = retval->name + auth->name_length + 1; + memcpy (retval->data, auth->data, auth->data_length); + } + XauDisposeAuth (auth); + return retval; + } + #endif + +-static gboolean +-ensure_login_vt (GdmSessionWorker *worker) +-{ +- int fd; +- struct vt_stat vt_state = { 0 }; +- gboolean got_login_vt = FALSE; +- +- fd = open ("/dev/tty0", O_RDWR | O_NOCTTY); +- +- if (fd < 0) { +- g_debug ("GdmSessionWorker: couldn't open VT master: %m"); +- return FALSE; +- } +- +- if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) { +- g_debug ("GdmSessionWorker: couldn't get current VT: %m"); +- goto out; +- } +- +- worker->priv->login_vt = vt_state.v_active; +- got_login_vt = TRUE; +-out: +- close (fd); +- return got_login_vt; +-} +- + static gboolean + gdm_session_worker_initialize_pam (GdmSessionWorker *worker, + const char *service, + const char * const *extensions, + const char *username, + const char *hostname, + gboolean display_is_local, + const char *x11_display_name, + const char *x11_authority_file, + const char *display_device, + const char *seat_id, + GError **error) + { + struct pam_conv pam_conversation; + int error_code; + char tty_string[256]; + + g_assert (worker->priv->pam_handle == NULL); + + g_debug ("GdmSessionWorker: initializing PAM; service=%s username=%s seat=%s", + service ? service : "(null)", + username ? username : "(null)", + seat_id ? seat_id : "(null)"); + + #ifdef SUPPORTS_PAM_EXTENSIONS + if (extensions != NULL) { + GDM_PAM_EXTENSION_ADVERTISE_SUPPORTED_EXTENSIONS (gdm_pam_extension_environment_block, extensions); + } + #endif + +@@ -1204,64 +1182,63 @@ gdm_session_worker_initialize_pam (GdmSessionWorker *worker, + } + + /* set RHOST */ + if (hostname != NULL && hostname[0] != '\0') { + error_code = pam_set_item (worker->priv->pam_handle, PAM_RHOST, hostname); + g_debug ("error informing authentication system of user's hostname %s: %s", + hostname, + pam_strerror (worker->priv->pam_handle, error_code)); + + if (error_code != PAM_SUCCESS) { + g_set_error (error, + GDM_SESSION_WORKER_ERROR, + GDM_SESSION_WORKER_ERROR_AUTHENTICATING, + "%s", ""); + goto out; + } + } + + /* set seat ID */ + if (seat_id != NULL && seat_id[0] != '\0') { + gdm_session_worker_set_environment_variable (worker, "XDG_SEAT", seat_id); + } + + if (strcmp (service, "gdm-launch-environment") == 0) { + gdm_session_worker_set_environment_variable (worker, "XDG_SESSION_CLASS", "greeter"); + } + + g_debug ("GdmSessionWorker: state SETUP_COMPLETE"); + gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE); + +- /* Temporarily set PAM_TTY with the currently active VT (login screen) ++ /* Temporarily set PAM_TTY with the login VT, + PAM_TTY will be reset with the users VT right before the user session is opened */ +- ensure_login_vt (worker); +- g_snprintf (tty_string, 256, "/dev/tty%d", worker->priv->login_vt); ++ g_snprintf (tty_string, 256, "/dev/tty%d", GDM_INITIAL_VT); + pam_set_item (worker->priv->pam_handle, PAM_TTY, tty_string); + if (!display_is_local) + worker->priv->password_is_required = TRUE; + + out: + if (error_code != PAM_SUCCESS) { + gdm_session_worker_uninitialize_pam (worker, error_code); + return FALSE; + } + + return TRUE; + } + + static gboolean + gdm_session_worker_authenticate_user (GdmSessionWorker *worker, + gboolean password_is_required, + GError **error) + { + int error_code; + int authentication_flags; + + g_debug ("GdmSessionWorker: authenticating user %s", worker->priv->username); + + authentication_flags = 0; + + if (password_is_required) { + authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK; + } + + /* blocking call, does the actual conversation */ +-- +2.26.0 + diff --git a/SOURCES/0044-session-worker-ensure-initial-vt-is-never-picked-for.patch b/SOURCES/0044-session-worker-ensure-initial-vt-is-never-picked-for.patch new file mode 100644 index 0000000..ee72cf6 --- /dev/null +++ b/SOURCES/0044-session-worker-ensure-initial-vt-is-never-picked-for.patch @@ -0,0 +1,160 @@ +From 1b5a79dfcd083537c8e3d3dfcd26b7b1d19c51ca Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Apr 2020 14:37:41 -0400 +Subject: [PATCH 44/48] session-worker: ensure initial vt is never picked for + !is_initial displays + +Normally, a !is_initial display would never "get" tty1, since the system +boots to tty1. But if, for some reason, the user booted to runlevel 3, +then switched to runlevel 5, the login screen could get started when +tty1 is free. + +That means, e.g., an autologin user can end up getting allocated tty1, +which is bad, since we assume tty1 is used for the login screen. + +This commit opens up /dev/tty1 when querying for available VTs, so that +it never gets returned by the kernel as available. +--- + daemon/gdm-session-worker.c | 39 +++++++++++++++++++++++++------------ + 1 file changed, 27 insertions(+), 12 deletions(-) + +diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c +index 0bd78cfaf..42c415837 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -2167,105 +2167,120 @@ gdm_session_worker_start_session (GdmSessionWorker *worker, + + /* If we end up execing again, make sure we don't use the executable context set up + * by pam_selinux durin pam_open_session + */ + #ifdef HAVE_SELINUX + setexeccon (NULL); + #endif + + worker->priv->child_pid = session_pid; + + g_debug ("GdmSessionWorker: session opened creating reply..."); + g_assert (sizeof (GPid) <= sizeof (int)); + + g_debug ("GdmSessionWorker: state SESSION_STARTED"); + gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_SESSION_STARTED); + + gdm_session_worker_watch_child (worker); + + out: + if (error_code != PAM_SUCCESS) { + gdm_session_worker_uninitialize_pam (worker, error_code); + return FALSE; + } + + return TRUE; + } + + static gboolean + set_up_for_new_vt (GdmSessionWorker *worker) + { +- int fd; ++ int initial_vt_fd; + char vt_string[256], tty_string[256]; + int session_vt = 0; + +- fd = open ("/dev/tty0", O_RDWR | O_NOCTTY); +- +- if (fd < 0) { +- g_debug ("GdmSessionWorker: couldn't open VT master: %m"); ++ /* open the initial vt. We need it for two scenarios: ++ * ++ * 1) display_is_initial is TRUE. We need it directly. ++ * 2) display_is_initial is FALSE. We need it to mark ++ * the initial VT as "in use" so it doesn't get returned ++ * by VT_OPENQRY ++ * */ ++ g_snprintf (tty_string, sizeof (tty_string), "/dev/tty%d", GDM_INITIAL_VT); ++ initial_vt_fd = open (tty_string, O_RDWR | O_NOCTTY); ++ ++ if (initial_vt_fd < 0) { ++ g_debug ("GdmSessionWorker: couldn't open console of initial fd: %m"); + return FALSE; + } + + if (worker->priv->display_is_initial) { + session_vt = GDM_INITIAL_VT; + } else { +- if (ioctl(fd, VT_OPENQRY, &session_vt) < 0) { ++ ++ /* Typically VT_OPENQRY is called on /dev/tty0, but we already ++ * have /dev/tty1 open above, so might as well use it. ++ */ ++ if (ioctl (initial_vt_fd, VT_OPENQRY, &session_vt) < 0) { + g_debug ("GdmSessionWorker: couldn't open new VT: %m"); + goto fail; + } + } + + worker->priv->session_vt = session_vt; + +- close (fd); +- fd = -1; +- + g_assert (session_vt > 0); + + g_snprintf (vt_string, sizeof (vt_string), "%d", session_vt); + + /* Set the VTNR. This is used by logind to configure a session in + * the logind-managed case, but it doesn't hurt to set it always. + * When logind gains support for XDG_VTNR=auto, we can make the + * OPENQRY and this whole path only used by the new VT code. */ + gdm_session_worker_set_environment_variable (worker, + "XDG_VTNR", + vt_string); + +- g_snprintf (tty_string, 256, "/dev/tty%d", session_vt); +- worker->priv->session_tty_fd = open (tty_string, O_RDWR | O_NOCTTY); ++ if (worker->priv->display_is_initial) { ++ worker->priv->session_tty_fd = initial_vt_fd; ++ } else { ++ g_snprintf (tty_string, sizeof (tty_string), "/dev/tty%d", session_vt); ++ worker->priv->session_tty_fd = open (tty_string, O_RDWR | O_NOCTTY); ++ close (initial_vt_fd); ++ } ++ + pam_set_item (worker->priv->pam_handle, PAM_TTY, tty_string); + + return TRUE; + + fail: +- close (fd); ++ close (initial_vt_fd); + return FALSE; + } + + static gboolean + set_xdg_vtnr_to_current_vt (GdmSessionWorker *worker) + { + int fd; + char vt_string[256]; + struct vt_stat vt_state = { 0 }; + + fd = open ("/dev/tty0", O_RDWR | O_NOCTTY); + + if (fd < 0) { + g_debug ("GdmSessionWorker: couldn't open VT master: %m"); + return FALSE; + } + + if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) { + g_debug ("GdmSessionWorker: couldn't get current VT: %m"); + goto fail; + } + + close (fd); + fd = -1; + + g_snprintf (vt_string, sizeof (vt_string), "%d", vt_state.v_active); + + gdm_session_worker_set_environment_variable (worker, + "XDG_VTNR", + vt_string); +-- +2.26.0 + diff --git a/SOURCES/0045-local-display-factory-Always-force-login-screen-to-V.patch b/SOURCES/0045-local-display-factory-Always-force-login-screen-to-V.patch new file mode 100644 index 0000000..a2e7f6b --- /dev/null +++ b/SOURCES/0045-local-display-factory-Always-force-login-screen-to-V.patch @@ -0,0 +1,114 @@ +From e071fe9ff6808c9910854755fb975a800ffdd142 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 15 May 2020 10:08:24 -0400 +Subject: [PATCH 45/48] local-display-factory: Always force login screen to VT + 1 + +These days we always want the login screen on VT 1, even +when it's created by user switching. + +Unfortunately, since commit f843233ad the login screen +won't naturally pick VT 1 when user switching. + +This commit forces it to make the right choice. + +Closes https://gitlab.gnome.org/GNOME/gdm/-/issues/602 +--- + daemon/gdm-local-display-factory.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 7a013c694..a288f8765 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -197,84 +197,87 @@ store_display (GdmLocalDisplayFactory *factory, + gdm_display_store_add (store, display); + } + + static gboolean + gdm_local_display_factory_use_wayland (void) + { + #ifdef ENABLE_WAYLAND_SUPPORT + gboolean wayland_enabled = FALSE; + if (gdm_settings_direct_get_boolean (GDM_KEY_WAYLAND_ENABLE, &wayland_enabled)) { + if (wayland_enabled && g_file_test ("/usr/bin/Xwayland", G_FILE_TEST_IS_EXECUTABLE) ) + return TRUE; + } + #endif + return FALSE; + } + + /* + Example: + dbus-send --system --dest=org.gnome.DisplayManager \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/gnome/DisplayManager/Manager \ + org.gnome.DisplayManager.Manager.GetDisplays + */ + gboolean + gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *factory, + char **id, + GError **error) + { + gboolean ret; + GdmDisplay *display = NULL; ++ gboolean is_initial = FALSE; + + g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE); + + ret = FALSE; + + g_debug ("GdmLocalDisplayFactory: Creating transient display"); + + #ifdef ENABLE_USER_DISPLAY_SERVER + display = gdm_local_display_new (); + if (gdm_local_display_factory_use_wayland ()) + g_object_set (G_OBJECT (display), "session-type", "wayland", NULL); ++ is_initial = TRUE; + #else + if (display == NULL) { + guint32 num; + + num = take_next_display_number (factory); + + display = gdm_legacy_display_new (num); + } + #endif + + g_object_set (display, + "seat-id", "seat0", + "allow-timed-login", FALSE, ++ "is-initial", is_initial, + NULL); + + store_display (factory, display); + + if (! gdm_display_manage (display)) { + display = NULL; + goto out; + } + + if (! gdm_display_get_id (display, id, NULL)) { + display = NULL; + goto out; + } + + ret = TRUE; + out: + /* ref either held by store or not at all */ + g_object_unref (display); + + return ret; + } + + static gboolean + finish_display_on_seat_if_waiting (GdmDisplayStore *display_store, + GdmDisplay *display, + const char *seat_id) + { + if (gdm_display_get_status (display) != GDM_DISPLAY_WAITING_TO_FINISH) + return FALSE; + +-- +2.26.0 + diff --git a/SOURCES/0046-gdm-x-session-tell-x-server-to-not-vt-switch.patch b/SOURCES/0046-gdm-x-session-tell-x-server-to-not-vt-switch.patch new file mode 100644 index 0000000..baea8fc --- /dev/null +++ b/SOURCES/0046-gdm-x-session-tell-x-server-to-not-vt-switch.patch @@ -0,0 +1,81 @@ +From 8af83d1468b033b00a91298a5b569d74b88a29f8 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 10 Jul 2020 10:45:52 -0400 +Subject: [PATCH 46/48] gdm-x-session: tell x server to not vt switch + +gdm already handles the VT switching on X's behalf, +so it's redundant, and X does it at inopportune times, +so instruct it to not get involved. +--- + daemon/gdm-x-session.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/daemon/gdm-x-session.c b/daemon/gdm-x-session.c +index 3b2fcef47..d8e3c7d53 100644 +--- a/daemon/gdm-x-session.c ++++ b/daemon/gdm-x-session.c +@@ -247,60 +247,61 @@ spawn_x_server (State *state, + } + + g_ptr_array_add (arguments, "-displayfd"); + g_ptr_array_add (arguments, display_fd_string); + + g_ptr_array_add (arguments, "-auth"); + g_ptr_array_add (arguments, auth_file); + + /* If we were compiled with Xserver >= 1.17 we need to specify + * '-listen tcp' as the X server dosen't listen on tcp sockets + * by default anymore. In older versions we need to pass + * -nolisten tcp to disable listening on tcp sockets. + */ + #ifdef HAVE_XSERVER_THAT_DEFAULTS_TO_LOCAL_ONLY + if (allow_remote_connections) { + g_ptr_array_add (arguments, "-listen"); + g_ptr_array_add (arguments, "tcp"); + } + #else + if (!allow_remote_connections) { + g_ptr_array_add (arguments, "-nolisten"); + g_ptr_array_add (arguments, "tcp"); + } + #endif + + g_ptr_array_add (arguments, "-background"); + g_ptr_array_add (arguments, "none"); + + g_ptr_array_add (arguments, "-noreset"); + g_ptr_array_add (arguments, "-keeptty"); ++ g_ptr_array_add (arguments, "-novtswitch"); + + g_ptr_array_add (arguments, "-verbose"); + if (state->debug_enabled) { + g_ptr_array_add (arguments, "7"); + } else { + g_ptr_array_add (arguments, "3"); + } + + if (state->debug_enabled) { + g_ptr_array_add (arguments, "-core"); + } + g_ptr_array_add (arguments, NULL); + + subprocess = g_subprocess_launcher_spawnv (launcher, + (const char * const *) arguments->pdata, + &error); + g_free (display_fd_string); + g_clear_object (&launcher); + g_ptr_array_free (arguments, TRUE); + + if (subprocess == NULL) { + g_debug ("could not start X server: %s", error->message); + goto out; + } + + input_stream = g_unix_input_stream_new (pipe_fds[0], TRUE); + data_stream = g_data_input_stream_new (input_stream); + g_clear_object (&input_stream); + + display_number = g_data_input_stream_read_line (data_stream, +-- +2.26.0 + diff --git a/SOURCES/0047-local-display-factory-kill-X-on-login-just-like-wayl.patch b/SOURCES/0047-local-display-factory-kill-X-on-login-just-like-wayl.patch new file mode 100644 index 0000000..d5c315c --- /dev/null +++ b/SOURCES/0047-local-display-factory-kill-X-on-login-just-like-wayl.patch @@ -0,0 +1,115 @@ +From 053106dc82ff66206a78a496b845249be9173584 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 11 Oct 2018 07:15:56 -0400 +Subject: [PATCH 47/48] local-display-factory: kill X on login just like + wayland + +These days we kill the wayland login screen during login to +conserve system resources. + +We've been reluctant to do the same for X based login screens, +because X didn't handle being killed in the background so well. + +This is no longer a problem, since this commit: + +https://gitlab.freedesktop.org/xorg/xserver/-/commit/ff91c696ff8f5f56da40e107cb5c321539758a81 + +So let's go ahead and kill it now. +--- + daemon/gdm-local-display-factory.c | 9 --------- + 1 file changed, 9 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index a288f8765..aae226750 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -599,86 +599,77 @@ on_seat_removed (GDBusConnection *connection, + + g_variant_get (parameters, "(&s&o)", &seat, NULL); + delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat); + } + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + static gboolean + lookup_by_session_id (const char *id, + GdmDisplay *display, + gpointer user_data) + { + const char *looking_for = user_data; + const char *current; + + current = gdm_display_get_session_id (display); + return g_strcmp0 (current, looking_for) == 0; + } + + static gboolean + wait_to_finish_timeout (GdmLocalDisplayFactory *factory) + { + finish_waiting_displays_on_seat (factory, "seat0"); + factory->priv->wait_to_finish_timeout_id = 0; + return G_SOURCE_REMOVE; + } + + static void + maybe_stop_greeter_in_background (GdmLocalDisplayFactory *factory, + GdmDisplay *display) + { +- g_autofree char *display_session_type = NULL; + gboolean doing_initial_setup = FALSE; + + if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) { + g_debug ("GdmLocalDisplayFactory: login window not in managed state, so ignoring"); + return; + } + + g_object_get (G_OBJECT (display), +- "session-type", &display_session_type, + "doing-initial-setup", &doing_initial_setup, + NULL); + + /* we don't ever stop initial-setup implicitly */ + if (doing_initial_setup) { + g_debug ("GdmLocalDisplayFactory: login window is performing initial-setup, so ignoring"); + return; + } + +- /* we can only stop greeter for wayland sessions, since +- * X server would jump back on exit */ +- if (g_strcmp0 (display_session_type, "wayland") != 0) { +- g_debug ("GdmLocalDisplayFactory: login window is running on Xorg, so ignoring"); +- return; +- } +- + g_debug ("GdmLocalDisplayFactory: killing login window once its unused"); + g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); + + /* We stop the greeter after a timeout to avoid flicker */ + if (factory->priv->wait_to_finish_timeout_id != 0) + g_source_remove (factory->priv->wait_to_finish_timeout_id); + + factory->priv->wait_to_finish_timeout_id = + g_timeout_add_seconds (WAIT_TO_FINISH_TIMEOUT, + (GSourceFunc)wait_to_finish_timeout, + factory); + } + + static gboolean + on_vt_changed (GIOChannel *source, + GIOCondition condition, + GdmLocalDisplayFactory *factory) + { + GIOStatus status; + g_autofree char *tty_of_active_vt = NULL; + g_autofree char *login_session_id = NULL; + g_autofree char *active_session_id = NULL; + unsigned int previous_vt, new_vt; + const char *session_type = NULL; + int ret, n_returned; + + g_debug ("GdmLocalDisplayFactory: received VT change event"); + g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL); + + if (condition & G_IO_PRI) { +-- +2.26.0 + diff --git a/SOURCES/0048-manager-don-t-kill-initial-setup-right-away-with-Xor.patch b/SOURCES/0048-manager-don-t-kill-initial-setup-right-away-with-Xor.patch new file mode 100644 index 0000000..b667d46 --- /dev/null +++ b/SOURCES/0048-manager-don-t-kill-initial-setup-right-away-with-Xor.patch @@ -0,0 +1,149 @@ +From dbb50dee49fd68ba0983f8889d54191bf8acfe04 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 13 Jul 2020 09:23:06 -0400 +Subject: [PATCH 48/48] manager: don't kill initial-setup right away with Xorg + either + +The login screen for both Xorg and wayland sessions is now silently +killed in the background post login. + +We still kill initial-setup for Xorg sessions up front, though. + +This commit fixes that. +--- + daemon/gdm-manager.c | 20 ++------------------ + 1 file changed, 2 insertions(+), 18 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index cf982870c..b147d73db 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1757,123 +1757,107 @@ chown_initial_setup_home_dir (void) + + uid = (uid_t) atoi (gis_uid_contents); + pwe = getpwuid (uid); + if (uid == 0 || pwe == NULL) { + g_warning ("UID '%s' in %s is not valid", gis_uid_contents, gis_uid_path); + goto out; + } + + error = NULL; + dir = g_file_new_for_path (gis_dir_path); + if (!chown_recursively (dir, pwe->pw_uid, pwe->pw_gid, &error)) { + g_warning ("Failed to change ownership for %s: %s", gis_dir_path, error->message); + g_error_free (error); + } + g_object_unref (dir); + out: + g_free (gis_uid_contents); + g_free (gis_uid_path); + g_free (gis_dir_path); + } + + static gboolean + on_start_user_session (StartUserSessionOperation *operation) + { + GdmManager *self = operation->manager; + gboolean migrated; + gboolean fail_if_already_switched = TRUE; + gboolean doing_initial_setup = FALSE; + GdmDisplay *display; + const char *session_id; +-#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) +- g_autofree char *display_session_type = NULL; +-#endif + + g_debug ("GdmManager: start or jump to session"); + + /* If there's already a session running, jump to it. + * If the only session running is the one we just opened, + * start a session on it. + */ + migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched); + + g_debug ("GdmManager: migrated: %d", migrated); + if (migrated) { + /* We don't stop the manager here because + when Xorg exits it switches to the VT it was + started from. That interferes with fast + user switching. */ + gdm_session_reset (operation->session); + destroy_start_user_session_operation (operation); + goto out; + } + + display = get_display_for_user_session (operation->session); + + g_object_get (G_OBJECT (display), + "doing-initial-setup", &doing_initial_setup, +-#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) +- "session-type", &display_session_type, +-#endif + NULL); + + session_id = gdm_session_get_conversation_session_id (operation->session, + operation->service_name); + + if (gdm_session_get_display_mode (operation->session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) { + /* In this case, the greeter's display is morphing into + * the user session display. Kill the greeter on this session + * and let the user session follow the same display. */ + gdm_display_stop_greeter_session (display); + g_object_set (G_OBJECT (display), + "session-class", "user", + "session-id", session_id, + NULL); + } else { + uid_t allowed_uid; + + g_object_ref (display); + if (doing_initial_setup) { + g_autoptr(GError) error = NULL; + +-#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) +- if (g_strcmp0 (display_session_type, "wayland") == 0) { +- g_debug ("GdmManager: closing down initial setup display in background"); +- g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); +- } +-#endif +- if (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { +- g_debug ("GdmManager: closing down initial setup display"); +- gdm_display_stop_greeter_session (display); +- gdm_display_unmanage (display); +- gdm_display_finish (display); +- } ++ g_debug ("GdmManager: closing down initial setup display in background"); ++ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); + + chown_initial_setup_home_dir (); + + if (!g_file_set_contents (ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT, + "1", + 1, + &error)) { + g_warning ("GdmDisplay: Could not write initial-setup-done marker to %s: %s", + ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT, + error->message); + g_clear_error (&error); + } + } else { + g_debug ("GdmManager: session has its display server, reusing our server for another login screen"); + } + + /* The user session is going to follow the session worker + * into the new display. Untie it from this display and + * create a new session for a future user login. */ + allowed_uid = gdm_session_get_allowed_user (operation->session); + g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); + g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL); + create_user_session_for_display (operation->manager, display, allowed_uid); + + /* Give the user session a new display object for bookkeeping purposes */ + create_display_for_user_session (operation->manager, + operation->session, + session_id); + + if ((g_strcmp0 (operation->service_name, "gdm-autologin") == 0) && +-- +2.26.0 + diff --git a/SOURCES/system-dconf.patch b/SOURCES/system-dconf.patch deleted file mode 100644 index 83d2999..0000000 --- a/SOURCES/system-dconf.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 29d374ce6781df6f3b168a8c57163ddb582c998a Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Wed, 31 Jul 2013 17:32:55 -0400 -Subject: [PATCH] data: add system dconf databases to gdm profile - -This way system settings can affect the login screen. ---- - data/dconf/gdm.in | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/data/dconf/gdm.in b/data/dconf/gdm.in -index 4d8bf174..9694078f 100644 ---- a/data/dconf/gdm.in -+++ b/data/dconf/gdm.in -@@ -1,2 +1,5 @@ - user-db:user -+system-db:local -+system-db:site -+system-db:distro - file-db:@DATADIR@/@PACKAGE@/greeter-dconf-defaults --- -2.11.1 diff --git a/SPECS/gdm.spec b/SPECS/gdm.spec index eda8f91..0174bfc 100644 --- a/SPECS/gdm.spec +++ b/SPECS/gdm.spec @@ -10,7 +10,7 @@ Name: gdm Epoch: 1 Version: 3.28.3 -Release: 29%{?dist} +Release: 31%{?dist} Summary: The GNOME Display Manager License: GPLv2+ @@ -30,8 +30,6 @@ Patch30004: 0001-data-only-disable-wayland-on-passthrough-virt-setups.patch Patch40001: 0001-local-display-factory-pause-for-a-few-seconds-before.patch -Patch50001: 0001-manager-ensure-is-initial-is-transfered-to-autologin.patch - Patch60001: 0001-session-ensure-login-screen-over-XDMCP-connects-to-i.patch Patch70001: 0001-worker-don-t-load-user-settings-for-program-sessions.patch @@ -57,7 +55,59 @@ Patch110001: 0001-display-ask-accountservice-if-there-are-users-rather.patch Patch120001: 0001-daemon-fix-wayland-detection-when-deciding-to-bypass.patch -Patch999999: system-dconf.patch +# This truckload of patches reworks how VT allocation is done, and makes sure +# the login screen is killed after login +# https://bugzilla.redhat.com/show_bug.cgi?id=1618481 +Patch200001: 0001-display-factory-avoid-removing-a-display-from-store-.patch +Patch200002: 0002-local-display-factory-Add-gdm_local_display_factory_.patch +Patch200003: 0003-local-display-factory-Use-correct-session-type-for-n.patch +Patch200004: 0004-manager-make-get_login_window_session_id-fail-if-no-.patch +Patch200005: 0005-manager-avoid-leaking-session_id.patch +Patch200006: 0006-manager-gracefully-handle-the-case-of-no-session-for.patch +Patch200007: 0007-common-dedupe-gdm_get_login_window_session_id.patch +Patch200008: 0008-common-dedupe-activate_session_id.patch +Patch200009: 0009-manager-plug-leak-in-maybe_activate_other_session.patch +Patch200010: 0010-manager-start-login-screen-if-old-one-is-finished.patch +Patch200011: 0011-manager-don-t-bail-if-session-disappears-out-from-un.patch +Patch200012: 0012-daemon-try-harder-to-get-to-a-login-screen-at-logout.patch +Patch200013: 0013-local-display-factory-ensure-non-seat0-codepath-does.patch +Patch200014: 0014-daemon-kill-and-restart-greeter-on-demand-under-wayl.patch +Patch200015: 0015-local-display-factory-add-more-debug-messages-to-new.patch +Patch200016: 0016-local-display-factory-don-t-start-two-greeters-at-st.patch +Patch200017: 0017-session-worker-don-t-switch-VTs-if-we-re-already-on-.patch +Patch200018: 0018-session-worker-fix-current-vt-detection-short-circui.patch +Patch200019: 0019-local-display-factory-don-t-jump-to-failed-display.patch +Patch200020: 0020-local-display-factory-add-some-more-debug-statements.patch +Patch200021: 0021-local-display-factory-ignore-spurios-SeatNew-signal-.patch +Patch200022: 0022-common-remove-unnecessary-free.patch +Patch200023: 0023-common-don-t-bail-if-session-disappears-out-from-und.patch +Patch200024: 0024-manager-better-logind-handling.patch +Patch200025: 0025-session-worker-clear-VT-before-jumping-to-it.patch +Patch200026: 0026-manager-don-t-set-ran_once-after-running-initial-set.patch +Patch200027: 0027-manager-start-initial-setup-right-away.patch +Patch200028: 0028-gdm-wayland-session-gdm-x-session-register-after-del.patch +Patch200029: 0029-local-display-factory-defer-killing-greeter-until-ne.patch +Patch200030: 0030-daemon-Move-the-waiting-the-session-to-have-taken-ov.patch +Patch200031: 0031-local-display-factory-don-t-autoreap-initial-setup.patch +Patch200032: 0032-manager-rework-how-autologin-is-figured-out.patch +Patch200033: 0033-manager-correct-display-confusion.patch +Patch200034: 0034-manager-don-t-run-autologin-display-on-tty1.patch +Patch200035: 0035-local-display-factory-Remove-initial-VT-is-in-use-ch.patch +Patch200036: 0036-local-display-factory-Remove-same-VT-so-don-t-switch.patch +Patch200037: 0037-local-display-factory-handle-reviving-displays-that-.patch +Patch200038: 0038-manager-don-t-kill-initial-setup-before-starting-use.patch +Patch200039: 0039-manager-do-initial-setup-post-work-in-manager-code.patch +Patch200040: 0040-display-store-make-foreach-ignore-callback-return-va.patch +Patch200041: 0041-xdmcp-display-factory-don-t-return-value-from-foreac.patch +Patch200042: 0042-GdmLocalDisplayFactory-Store-VT-number-not-tty-ident.patch +Patch200043: 0043-gdm-session-worker-Drop-login_vt-assuming-it-is-GDM_.patch +Patch200044: 0044-session-worker-ensure-initial-vt-is-never-picked-for.patch +Patch200045: 0045-local-display-factory-Always-force-login-screen-to-V.patch +Patch200046: 0046-gdm-x-session-tell-x-server-to-not-vt-switch.patch +Patch200047: 0047-local-display-factory-kill-X-on-login-just-like-wayl.patch +Patch200048: 0048-manager-don-t-kill-initial-setup-right-away-with-Xor.patch + +Patch900001: 0001-data-add-system-dconf-databases-to-gdm-profile.patch BuildRequires: pam-devel >= 0:%{pam_version} BuildRequires: desktop-file-utils >= %{desktop_file_utils_version} @@ -116,6 +166,7 @@ Requires: system-logos Requires: xorg-x11-server-utils Requires: xorg-x11-xinit Recommends: xorg-x11-server-Xorg +Conflicts: xorg-x11-server-Xorg < 1.20.8-4 Obsoletes: gdm-libs < 1:3.12.0-3 Provides: gdm-libs%{?_isa} = %{epoch}:%{version}-%{release} @@ -211,6 +262,8 @@ mkdir -p %{buildroot}%{_datadir}/gdm/autostart/LoginWindow mkdir -p %{buildroot}/run/gdm +mkdir -p %{buildroot}%{_sysconfdir}/dconf/db/gdm.d/locks + rm -f %{buildroot}%{_bindir}/gdm-screenshot find %{buildroot} -name '*.a' -delete @@ -357,6 +410,14 @@ fi %{_libdir}/pkgconfig/gdm-pam-extensions.pc %changelog +* Thu Jul 23 2020 Ray Strode - 3.28.3-31 +- add back gdm system db to dconf profile + Resolves: #1833158 + +* Mon Jul 13 2020 Ray Strode - 3.28.3-30 +- Make sure login screen is killed during login + Resolves: #1618481 + * Wed Feb 05 2020 Ray Strode - 3.28.3-29 - Make GNOME work slightly better in the multiple logins case. Related: #1710882