From 10d480e908e6a9bcb06dca4f71def60c3cf6d6f0 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 30 Mar 2021 09:05:38 -0400 Subject: [PATCH] import gdm-3.28.3-39.el8 --- ...stem-dconf-databases-to-gdm-profile.patch} | 12 +- ...h-failure-if-loading-existing-users-.patch | 140 ++ ...avoid-removing-a-display-from-store-.patch | 433 ++++ ...ctory-pause-for-a-few-seconds-before.patch | 2 +- ...1-manager-Don-t-leak-session-objects.patch | 353 +++ ...ll-timed-login-session-immediately-a.patch | 22 +- ...s-initial-is-transfered-to-autologin.patch | 198 -- ...on-t-switch-back-VTs-until-session-i.patch | 683 ++++++ ...ctory-Add-gdm_local_display_factory_.patch | 164 ++ ...-Don-t-leak-remote-greeter-interface.patch | 87 + ...ctory-Use-correct-session-type-for-n.patch | 83 + ...ctory-Clear-launch-environment-when-.patch | 87 + ..._login_window_session_id-fail-if-no-.patch | 137 ++ ...005-manager-avoid-leaking-session_id.patch | 79 + ...ly-handle-the-case-of-no-session-for.patch | 89 + ...dupe-gdm_get_login_window_session_id.patch | 432 ++++ ...08-common-dedupe-activate_session_id.patch | 401 ++++ ...leak-in-maybe_activate_other_session.patch | 103 + ...-login-screen-if-old-one-is-finished.patch | 92 + ...il-if-session-disappears-out-from-un.patch | 127 ++ ...r-to-get-to-a-login-screen-at-logout.patch | 563 +++++ ...ctory-ensure-non-seat0-codepath-does.patch | 87 + ...restart-greeter-on-demand-under-wayl.patch | 369 ++++ ...ctory-add-more-debug-messages-to-new.patch | 235 ++ ...ctory-don-t-start-two-greeters-at-st.patch | 97 + ...on-t-switch-VTs-if-we-re-already-on-.patch | 128 ++ ...ix-current-vt-detection-short-circui.patch | 87 + ...factory-don-t-jump-to-failed-display.patch | 168 ++ ...ctory-add-some-more-debug-statements.patch | 161 ++ ...ctory-ignore-spurios-SeatNew-signal-.patch | 162 ++ .../0022-common-remove-unnecessary-free.patch | 91 + ...l-if-session-disappears-out-from-und.patch | 133 ++ .../0024-manager-better-logind-handling.patch | 138 ++ ...worker-clear-VT-before-jumping-to-it.patch | 90 + ...t-ran_once-after-running-initial-set.patch | 133 ++ ...nager-start-initial-setup-right-away.patch | 418 ++++ ...ion-gdm-x-session-register-after-del.patch | 309 +++ ...ctory-defer-killing-greeter-until-ne.patch | 399 ++++ ...waiting-the-session-to-have-taken-ov.patch | 708 ++++++ ...factory-don-t-autoreap-initial-setup.patch | 100 + ...-rework-how-autologin-is-figured-out.patch | 460 ++++ ...33-manager-correct-display-confusion.patch | 85 + ...-don-t-run-autologin-display-on-tty1.patch | 99 + ...ctory-Remove-initial-VT-is-in-use-ch.patch | 110 + ...ctory-Remove-same-VT-so-don-t-switch.patch | 140 ++ ...ctory-handle-reviving-displays-that-.patch | 88 + ...ll-initial-setup-before-starting-use.patch | 151 ++ ...tial-setup-post-work-in-manager-code.patch | 694 ++++++ ...ke-foreach-ignore-callback-return-va.patch | 117 + ...ctory-don-t-return-value-from-foreac.patch | 180 ++ ...actory-Store-VT-number-not-tty-ident.patch | 538 +++++ ...er-Drop-login_vt-assuming-it-is-GDM_.patch | 344 +++ ...nsure-initial-vt-is-never-picked-for.patch | 160 ++ ...ctory-Always-force-login-screen-to-V.patch | 114 + ...ssion-tell-x-server-to-not-vt-switch.patch | 81 + ...ctory-kill-X-on-login-just-like-wayl.patch | 115 + ...ll-initial-setup-right-away-with-Xor.patch | 149 ++ ...dmDisplay-Add-RegisterSession-method.patch | 551 +++++ ...-Allow-sessions-to-register-with-GDM.patch | 1917 +++++++++++++++++ ...-failure-before-display-registration.patch | 173 ++ SPECS/gdm.spec | 109 +- 61 files changed, 14456 insertions(+), 219 deletions(-) rename SOURCES/{system-dconf.patch => 0001-data-add-system-dconf-databases-to-gdm-profile.patch} (77%) create mode 100644 SOURCES/0001-display-Exit-with-failure-if-loading-existing-users-.patch create mode 100644 SOURCES/0001-display-factory-avoid-removing-a-display-from-store-.patch create mode 100644 SOURCES/0001-manager-Don-t-leak-session-objects.patch delete mode 100644 SOURCES/0001-manager-ensure-is-initial-is-transfered-to-autologin.patch create mode 100644 SOURCES/0001-session-worker-Don-t-switch-back-VTs-until-session-i.patch create mode 100644 SOURCES/0002-local-display-factory-Add-gdm_local_display_factory_.patch create mode 100644 SOURCES/0002-session-Don-t-leak-remote-greeter-interface.patch create mode 100644 SOURCES/0003-local-display-factory-Use-correct-session-type-for-n.patch create mode 100644 SOURCES/0003-xdmcp-display-factory-Clear-launch-environment-when-.patch create mode 100644 SOURCES/0004-manager-make-get_login_window_session_id-fail-if-no-.patch create mode 100644 SOURCES/0005-manager-avoid-leaking-session_id.patch create mode 100644 SOURCES/0006-manager-gracefully-handle-the-case-of-no-session-for.patch create mode 100644 SOURCES/0007-common-dedupe-gdm_get_login_window_session_id.patch create mode 100644 SOURCES/0008-common-dedupe-activate_session_id.patch create mode 100644 SOURCES/0009-manager-plug-leak-in-maybe_activate_other_session.patch create mode 100644 SOURCES/0010-manager-start-login-screen-if-old-one-is-finished.patch create mode 100644 SOURCES/0011-manager-don-t-bail-if-session-disappears-out-from-un.patch create mode 100644 SOURCES/0012-daemon-try-harder-to-get-to-a-login-screen-at-logout.patch create mode 100644 SOURCES/0013-local-display-factory-ensure-non-seat0-codepath-does.patch create mode 100644 SOURCES/0014-daemon-kill-and-restart-greeter-on-demand-under-wayl.patch create mode 100644 SOURCES/0015-local-display-factory-add-more-debug-messages-to-new.patch create mode 100644 SOURCES/0016-local-display-factory-don-t-start-two-greeters-at-st.patch create mode 100644 SOURCES/0017-session-worker-don-t-switch-VTs-if-we-re-already-on-.patch create mode 100644 SOURCES/0018-session-worker-fix-current-vt-detection-short-circui.patch create mode 100644 SOURCES/0019-local-display-factory-don-t-jump-to-failed-display.patch create mode 100644 SOURCES/0020-local-display-factory-add-some-more-debug-statements.patch create mode 100644 SOURCES/0021-local-display-factory-ignore-spurios-SeatNew-signal-.patch create mode 100644 SOURCES/0022-common-remove-unnecessary-free.patch create mode 100644 SOURCES/0023-common-don-t-bail-if-session-disappears-out-from-und.patch create mode 100644 SOURCES/0024-manager-better-logind-handling.patch create mode 100644 SOURCES/0025-session-worker-clear-VT-before-jumping-to-it.patch create mode 100644 SOURCES/0026-manager-don-t-set-ran_once-after-running-initial-set.patch create mode 100644 SOURCES/0027-manager-start-initial-setup-right-away.patch create mode 100644 SOURCES/0028-gdm-wayland-session-gdm-x-session-register-after-del.patch create mode 100644 SOURCES/0029-local-display-factory-defer-killing-greeter-until-ne.patch create mode 100644 SOURCES/0030-daemon-Move-the-waiting-the-session-to-have-taken-ov.patch create mode 100644 SOURCES/0031-local-display-factory-don-t-autoreap-initial-setup.patch create mode 100644 SOURCES/0032-manager-rework-how-autologin-is-figured-out.patch create mode 100644 SOURCES/0033-manager-correct-display-confusion.patch create mode 100644 SOURCES/0034-manager-don-t-run-autologin-display-on-tty1.patch create mode 100644 SOURCES/0035-local-display-factory-Remove-initial-VT-is-in-use-ch.patch create mode 100644 SOURCES/0036-local-display-factory-Remove-same-VT-so-don-t-switch.patch create mode 100644 SOURCES/0037-local-display-factory-handle-reviving-displays-that-.patch create mode 100644 SOURCES/0038-manager-don-t-kill-initial-setup-before-starting-use.patch create mode 100644 SOURCES/0039-manager-do-initial-setup-post-work-in-manager-code.patch create mode 100644 SOURCES/0040-display-store-make-foreach-ignore-callback-return-va.patch create mode 100644 SOURCES/0041-xdmcp-display-factory-don-t-return-value-from-foreac.patch create mode 100644 SOURCES/0042-GdmLocalDisplayFactory-Store-VT-number-not-tty-ident.patch create mode 100644 SOURCES/0043-gdm-session-worker-Drop-login_vt-assuming-it-is-GDM_.patch create mode 100644 SOURCES/0044-session-worker-ensure-initial-vt-is-never-picked-for.patch create mode 100644 SOURCES/0045-local-display-factory-Always-force-login-screen-to-V.patch create mode 100644 SOURCES/0046-gdm-x-session-tell-x-server-to-not-vt-switch.patch create mode 100644 SOURCES/0047-local-display-factory-kill-X-on-login-just-like-wayl.patch create mode 100644 SOURCES/0048-manager-don-t-kill-initial-setup-right-away-with-Xor.patch create mode 100644 SOURCES/0049-GdmManager-GdmDisplay-Add-RegisterSession-method.patch create mode 100644 SOURCES/0050-Allow-sessions-to-register-with-GDM.patch create mode 100644 SOURCES/0051-display-Handle-failure-before-display-registration.patch diff --git a/SOURCES/system-dconf.patch b/SOURCES/0001-data-add-system-dconf-databases-to-gdm-profile.patch similarity index 77% rename from SOURCES/system-dconf.patch rename to SOURCES/0001-data-add-system-dconf-databases-to-gdm-profile.patch index 83d2999..5ae16e2 100644 --- a/SOURCES/system-dconf.patch +++ b/SOURCES/0001-data-add-system-dconf-databases-to-gdm-profile.patch @@ -1,4 +1,4 @@ -From 29d374ce6781df6f3b168a8c57163ddb582c998a Mon Sep 17 00:00:00 2001 +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 @@ -9,14 +9,16 @@ This way system settings can affect the login screen. 1 file changed, 4 insertions(+) diff --git a/data/dconf/gdm.in b/data/dconf/gdm.in -index 4d8bf174..9694078f 100644 +index 4d8bf1748..606e0b863 100644 --- a/data/dconf/gdm.in +++ b/data/dconf/gdm.in -@@ -1,2 +1,5 @@ +@@ -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.11.1 +-- +2.26.0 + diff --git a/SOURCES/0001-display-Exit-with-failure-if-loading-existing-users-.patch b/SOURCES/0001-display-Exit-with-failure-if-loading-existing-users-.patch new file mode 100644 index 0000000..ffd5b94 --- /dev/null +++ b/SOURCES/0001-display-Exit-with-failure-if-loading-existing-users-.patch @@ -0,0 +1,140 @@ +From e339ad74ca408c665a62bb4bd98dd1ef6caedd20 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 27 Oct 2020 15:14:27 +0100 +Subject: [PATCH] display: Exit with failure if loading existing users fails + +Given not having users may make GDM to launch initial setup, that +allows to create new users (potentially with sudo capabilities), it's +better to make look_for_existing_users() to return its status and only +if it didn't fail continue the gdm execution. + +GHSL-2020-202 +CVE-2020-16125 + +Fixes #642 +--- + daemon/gdm-display.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c +index 929fa13bd..1b60eb621 100644 +--- a/daemon/gdm-display.c ++++ b/daemon/gdm-display.c +@@ -436,106 +436,110 @@ finish_idle (GdmDisplay *self) + static void + queue_finish (GdmDisplay *self) + { + if (self->priv->finish_idle_id == 0) { + self->priv->finish_idle_id = g_idle_add ((GSourceFunc)finish_idle, self); + } + } + + static void + _gdm_display_set_status (GdmDisplay *self, + int status) + { + if (status != self->priv->status) { + self->priv->status = status; + g_object_notify (G_OBJECT (self), "status"); + } + } + + static gboolean + gdm_display_real_prepare (GdmDisplay *self) + { + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + g_debug ("GdmDisplay: prepare display"); + + _gdm_display_set_status (self, GDM_DISPLAY_PREPARED); + + return TRUE; + } + +-static void ++static gboolean + look_for_existing_users_sync (GdmDisplay *self) + { + g_autoptr (GVariant) result = NULL; + g_autoptr (GVariant) result_child = NULL; + g_autoptr (GError) error = NULL; + gboolean has_no_users = FALSE; + + result = g_dbus_connection_call_sync (self->priv->connection, + "org.freedesktop.Accounts", + "/org/freedesktop/Accounts", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", "org.freedesktop.Accounts", "HasNoUsers"), + G_VARIANT_TYPE ("(v)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + if (result == NULL) { +- g_warning ("Failed to contact accountsservice: %s", error->message); +- return; ++ g_critical ("Failed to contact accountsservice: %s", error->message); ++ goto out; + } + + g_variant_get (result, "(v)", &result_child); + has_no_users = g_variant_get_boolean (result_child); + self->priv->have_existing_user_accounts = !has_no_users; + + g_debug ("GdmDisplay: machine does %shave existing user accounts", + has_no_users? "not " : ""); ++out: ++ return result != NULL; + } + + gboolean + gdm_display_prepare (GdmDisplay *self) + { + gboolean ret; + + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + g_debug ("GdmDisplay: Preparing display: %s", self->priv->id); + + /* FIXME: we should probably do this in a more global place, + * asynchronously + */ +- look_for_existing_users_sync (self); ++ if (!look_for_existing_users_sync (self)) { ++ exit (EXIT_FAILURE); ++ } + + self->priv->doing_initial_setup = wants_initial_setup (self); + + g_object_ref (self); + ret = GDM_DISPLAY_GET_CLASS (self)->prepare (self); + g_object_unref (self); + + return ret; + } + + gboolean + gdm_display_manage (GdmDisplay *self) + { + gboolean res; + + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + g_debug ("GdmDisplay: Managing display: %s", self->priv->id); + + /* If not explicitly prepared, do it now */ + if (self->priv->status == GDM_DISPLAY_UNMANAGED) { + res = gdm_display_prepare (self); + if (! res) { + return FALSE; + } + } + + if (g_strcmp0 (self->priv->session_class, "greeter") == 0) { + if (GDM_DISPLAY_GET_CLASS (self)->manage != NULL) { + GDM_DISPLAY_GET_CLASS (self)->manage (self); +-- +2.28.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..bd73d6d --- /dev/null +++ b/SOURCES/0001-display-factory-avoid-removing-a-display-from-store-.patch @@ -0,0 +1,433 @@ +From a9422c7b5f4200ad36300bb06134d545bb9d48d2 Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Tue, 17 Jul 2018 20:20:55 +0000 +Subject: [PATCH 01/51] 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 + */ + g_usleep (2 * G_USEC_PER_SEC); + } + + #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.27.0 + diff --git a/SOURCES/0001-local-display-factory-pause-for-a-few-seconds-before.patch b/SOURCES/0001-local-display-factory-pause-for-a-few-seconds-before.patch index 543ecb9..56e096c 100644 --- a/SOURCES/0001-local-display-factory-pause-for-a-few-seconds-before.patch +++ b/SOURCES/0001-local-display-factory-pause-for-a-few-seconds-before.patch @@ -54,7 +54,7 @@ index 403921d32..ab7e12e91 100644 + /* workaround logind race for now + * bug 1643874 + */ -+ sleep (2); ++ g_usleep (2 * G_USEC_PER_SEC); } #endif diff --git a/SOURCES/0001-manager-Don-t-leak-session-objects.patch b/SOURCES/0001-manager-Don-t-leak-session-objects.patch new file mode 100644 index 0000000..909ca20 --- /dev/null +++ b/SOURCES/0001-manager-Don-t-leak-session-objects.patch @@ -0,0 +1,353 @@ +From de229615d80fd7c8a38ab5d5d7b30aa98f43721d Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 14 Sep 2020 16:20:09 -0400 +Subject: [PATCH 1/3] manager: Don't leak session objects + +The first is from create_user_session_for display. Most callers don't +check the return value, so it should just be void. + +The user data associated with the session also isn't unlinked from the +display when the display is finishing up, preventing the display and +session object from getting freed. + +This commit makes both changes. +--- + daemon/gdm-manager.c | 17 +++++++++-------- + 1 file changed, 9 insertions(+), 8 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index bff602a07..738671679 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -94,63 +94,63 @@ struct GdmManagerPrivate + #ifdef WITH_PLYMOUTH + guint plymouth_is_running : 1; + #endif + 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 create_user_session_for_display (GdmManager *manager, ++ GdmDisplay *display, ++ uid_t allowed_user); + static void start_user_session (GdmManager *manager, + StartUserSessionOperation *operation); + static void clean_user_session (GdmSession *session); + + static gpointer manager_object = NULL; + + static void manager_interface_init (GdmDBusManagerIface *interface); + + G_DEFINE_TYPE_WITH_CODE (GdmManager, + gdm_manager, + GDM_DBUS_TYPE_MANAGER_SKELETON, + G_IMPLEMENT_INTERFACE (GDM_DBUS_TYPE_MANAGER, + manager_interface_init)); + + #ifdef WITH_PLYMOUTH + static gboolean + plymouth_is_running (void) + { + int status; + gboolean res; + GError *error; + + error = NULL; + res = g_spawn_command_line_sync ("/bin/plymouth --ping", + NULL, NULL, &status, &error); + if (! res) { + g_debug ("Could not ping plymouth: %s", error->message); + g_error_free (error); + return FALSE; + } +@@ -1343,61 +1343,62 @@ get_automatic_login_details (GdmManager *manager, + 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; + + /* 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); ++ create_user_session_for_display (manager, display, 0); ++ session = get_user_session_for_display (display); + + g_object_get (G_OBJECT (display), + "session-type", &display_session_type, + NULL); + + g_object_set (G_OBJECT (session), + "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; + } + +@@ -1549,60 +1550,62 @@ 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 + ++ g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); ++ + 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); +@@ -2292,61 +2295,61 @@ on_session_reauthentication_started (GdmSession *session, + int pid_of_caller, + const char *address, + GdmManager *manager) + { + GDBusMethodInvocation *invocation; + gpointer source_tag; + + g_debug ("GdmManager: reauthentication started"); + + source_tag = GINT_TO_POINTER (pid_of_caller); + + invocation = g_hash_table_lookup (manager->priv->open_reauthentication_requests, + source_tag); + + if (invocation != NULL) { + g_hash_table_steal (manager->priv->open_reauthentication_requests, + source_tag); + gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager), + invocation, + address); + } + } + + static void + clean_user_session (GdmSession *session) + { + g_object_set_data (G_OBJECT (session), "gdm-display", NULL); + g_object_unref (session); + } + +-static GdmSession * ++static void + create_user_session_for_display (GdmManager *manager, + GdmDisplay *display, + uid_t allowed_user) + { + GdmSession *session; + gboolean display_is_local = FALSE; + char *display_name = NULL; + char *display_device = NULL; + char *remote_hostname = NULL; + char *display_auth_file = NULL; + char *display_seat_id = NULL; + char *display_id = NULL; + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + char *display_session_type = NULL; + gboolean greeter_is_wayland; + #endif + + g_object_get (G_OBJECT (display), + "id", &display_id, + "x11-display-name", &display_name, + "is-local", &display_is_local, + "remote-hostname", &remote_hostname, + "x11-authority-file", &display_auth_file, + "seat-id", &display_seat_id, + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + "session-type", &display_session_type, + #endif + NULL); + display_device = get_display_device (manager, display); + +@@ -2402,70 +2405,68 @@ create_user_session_for_display (GdmManager *manager, + "conversation-stopped", + G_CALLBACK (on_session_conversation_stopped), + manager); + g_signal_connect (session, + "authentication-failed", + G_CALLBACK (on_session_authentication_failed), + manager); + g_signal_connect (session, + "session-opened", + G_CALLBACK (on_user_session_opened), + manager); + g_signal_connect (session, + "session-started", + G_CALLBACK (on_user_session_started), + manager); + g_signal_connect (session, + "session-start-failed", + G_CALLBACK (on_session_start_failed), + manager); + g_signal_connect (session, + "session-exited", + G_CALLBACK (on_user_session_exited), + manager); + g_signal_connect (session, + "session-died", + G_CALLBACK (on_user_session_died), + manager); + 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), ++ session, + (GDestroyNotify) + clean_user_session); + + #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) + greeter_is_wayland = g_strcmp0 (display_session_type, "wayland") == 0; + g_object_set (G_OBJECT (session), "ignore-wayland", !greeter_is_wayland, NULL); + #endif +- +- return session; + } + + static void + on_display_added (GdmDisplayStore *display_store, + const char *id, + GdmManager *manager) + { + GdmDisplay *display; + + display = gdm_display_store_lookup (display_store, id); + + if (display != NULL) { + g_dbus_object_manager_server_export (manager->priv->object_manager, + gdm_display_get_object_skeleton (display)); + + g_signal_connect (display, "notify::status", + G_CALLBACK (on_display_status_changed), + manager); + g_signal_emit (manager, signals[DISPLAY_ADDED], 0, id); + } + } + + GQuark + gdm_manager_error_quark (void) + { + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_manager_error"); + } + +-- +2.26.2 + 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/0001-session-worker-Don-t-switch-back-VTs-until-session-i.patch b/SOURCES/0001-session-worker-Don-t-switch-back-VTs-until-session-i.patch new file mode 100644 index 0000000..9a4c4df --- /dev/null +++ b/SOURCES/0001-session-worker-Don-t-switch-back-VTs-until-session-i.patch @@ -0,0 +1,683 @@ +From 3864af1ea06d2125c1b1f5afa6fc12caa833980a Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 10 Dec 2020 15:14:20 -0500 +Subject: [PATCH] session-worker: Don't switch back VTs until session is fully + exited + +There's a race condition on shutdown where the session worker is +switching VTs back to the initial VT at the same time as the session +exit is being processed. + +This means that manager may try to start a login screen (because of +the VT switch) when autologin is enabled when there shouldn't be a +login screen. + +This commit makes sure both the PostSession script, and session-exited +signal emission are complete before initiating the VT switch back +to the initial VT. + +https://gitlab.gnome.org/GNOME/gdm/-/issues/660 +--- + daemon/Makefile.am | 12 +++++ + daemon/gdm-session-worker.c | 93 +++++++++++++++++++++++++++------ + daemon/org.freedesktop.DBus.xml | 12 +++++ + 3 files changed, 101 insertions(+), 16 deletions(-) + create mode 100644 daemon/org.freedesktop.DBus.xml + +diff --git a/daemon/Makefile.am b/daemon/Makefile.am +index 86a8ee32f..b323f6455 100644 +--- a/daemon/Makefile.am ++++ b/daemon/Makefile.am +@@ -7,83 +7,92 @@ AM_CPPFLAGS = \ + -I$(top_srcdir)/pam-extensions \ + -I$(top_builddir)/common \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DDMCONFDIR=\"$(dmconfdir)\" \ + -DGDMCONFDIR=\"$(gdmconfdir)\" \ + -DLIBDIR=\"$(libdir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ + -DLOGDIR=\"$(logdir)\" \ + -DSBINDIR=\"$(sbindir)\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ + -DGDM_RUN_DIR=\"$(GDM_RUN_DIR)\" \ + -DGDM_XAUTH_DIR=\"$(GDM_XAUTH_DIR)\" \ + -DGDM_SCREENSHOT_DIR=\"$(GDM_SCREENSHOT_DIR)\" \ + -DGDM_CACHE_DIR=\""$(localstatedir)/cache/gdm"\" \ + -DGDM_SESSION_DEFAULT_PATH=\"$(GDM_SESSION_DEFAULT_PATH)\" \ + $(DISABLE_DEPRECATED_CFLAGS) \ + $(DAEMON_CFLAGS) \ + $(XLIB_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DEBUG_CFLAGS) \ + $(SYSTEMD_CFLAGS) \ + $(JOURNALD_CFLAGS) \ + $(LIBSELINUX_CFLAGS) \ + -DLANG_CONFIG_FILE=\"$(LANG_CONFIG_FILE)\" \ + $(NULL) + + BUILT_SOURCES = \ ++ gdm-dbus-glue.h \ + gdm-display-glue.h \ + gdm-manager-glue.h \ + gdm-local-display-glue.h \ + gdm-local-display-factory-glue.h \ + gdm-session-glue.h \ + gdm-session-worker-glue.h \ + gdm-session-enum-types.h \ + gdm-session-worker-enum-types.h \ + com.redhat.AccountsServiceUser.System.h \ + $(NULL) + + gdm-session-enum-types.h: gdm-session-enum-types.h.in gdm-session.h + $(AM_V_GEN) glib-mkenums --template $^ > $@ + + gdm-session-enum-types.c: gdm-session-enum-types.c.in gdm-session.h + $(AM_V_GEN) glib-mkenums --template $^ > $@ + + gdm-session-worker-enum-types.h: gdm-session-worker-enum-types.h.in gdm-session-worker.h + $(AM_V_GEN) glib-mkenums --template $^ > $@ + + gdm-session-worker-enum-types.c: gdm-session-worker-enum-types.c.in gdm-session-worker.h + $(AM_V_GEN) glib-mkenums --template $^ > $@ + ++gdm-dbus-glue.c gdm-dbus-glue.h: org.freedesktop.DBus.xml Makefile.am ++ $(AM_V_GEN)gdbus-codegen \ ++ --c-namespace=GdmDBus \ ++ --interface-prefix=org.freedesktop.DBus \ ++ --generate-c-code=gdm-dbus-glue \ ++ --c-generate-autocleanup=all \ ++ $(srcdir)/org.freedesktop.DBus.xml ++ + gdm-display-glue.c gdm-display-glue.h: gdm-display.xml Makefile.am + $(AM_V_GEN)gdbus-codegen \ + --c-namespace=GdmDBus \ + --interface-prefix=org.gnome.DisplayManager \ + --generate-c-code=gdm-display-glue \ + $(srcdir)/gdm-display.xml + + gdm-local-display-glue.c gdm-local-display-glue.h: gdm-local-display.xml Makefile.am + $(AM_V_GEN)gdbus-codegen \ + --c-namespace=GdmDBus \ + --interface-prefix=org.gnome.DisplayManager \ + --generate-c-code=gdm-local-display-glue \ + $(srcdir)/gdm-local-display.xml + + gdm-local-display-factory-glue.c gdm-local-display-factory-glue.h : gdm-local-display-factory.xml Makefile.am + $(AM_V_GEN)gdbus-codegen \ + --c-namespace=GdmDBus \ + --interface-prefix=org.gnome.DisplayManager \ + --generate-c-code=gdm-local-display-factory-glue \ + $(srcdir)/gdm-local-display-factory.xml + + gdm-manager-glue.c gdm-manager-glue.h : gdm-manager.xml Makefile.am + $(AM_V_GEN)gdbus-codegen \ + --c-namespace=GdmDBus \ + --interface-prefix=org.gnome.DisplayManager \ + --generate-c-code=gdm-manager-glue \ + $(srcdir)/gdm-manager.xml + + gdm-session-glue.c gdm-session-glue.h : gdm-session.xml Makefile.am + $(AM_V_GEN)gdbus-codegen \ +@@ -130,60 +139,62 @@ libexec_PROGRAMS = \ + gdm-wayland-session \ + gdm-x-session \ + $(NULL) + + gdm_session_worker_SOURCES = \ + session-worker-main.c \ + com.redhat.AccountsServiceUser.System.h \ + com.redhat.AccountsServiceUser.System.c \ + gdm-session.c \ + gdm-session.h \ + gdm-session-settings.h \ + gdm-session-settings.c \ + gdm-session-auditor.h \ + gdm-session-auditor.c \ + gdm-session-record.c \ + gdm-session-record.h \ + gdm-session-worker.h \ + gdm-session-worker.c \ + gdm-session-worker-job.c \ + gdm-session-worker-common.c \ + gdm-session-worker-common.h \ + gdm-dbus-util.c \ + gdm-dbus-util.h \ + $(NULL) + + if SUPPORTS_PAM_EXTENSIONS + gdm_session_worker_SOURCES += $(top_srcdir)/pam-extensions/gdm-pam-extensions.h + endif + + nodist_gdm_session_worker_SOURCES = \ ++ gdm-dbus-glue.h \ ++ gdm-dbus-glue.c \ + gdm-session-glue.h \ + gdm-session-glue.c \ + gdm-session-worker-glue.c \ + gdm-session-worker-glue.h \ + gdm-session-enum-types.c \ + gdm-session-worker-enum-types.c \ + gdm-session-enum-types.h \ + $(NULL) + + gdm_wayland_session_LDADD = \ + $(top_builddir)/common/libgdmcommon.la \ + $(GTK_LIBS) \ + $(COMMON_LIBS) \ + $(SYSTEMD_LIBS) \ + $(NULL) + + gdm_wayland_session_SOURCES = \ + gdm-manager-glue.h \ + gdm-manager-glue.c \ + gdm-wayland-session.c \ + $(NULL) + + gdm_x_session_LDADD = \ + $(top_builddir)/common/libgdmcommon.la \ + $(GTK_LIBS) \ + $(COMMON_LIBS) \ + $(SYSTEMD_LIBS) \ + $(XLIB_LIBS) \ + $(NULL) + +@@ -271,50 +282,51 @@ nodist_gdm_SOURCES = \ + + XDMCP_SOURCES = \ + gdm-xdmcp-display-factory.c \ + gdm-xdmcp-display-factory.h \ + gdm-xdmcp-display.c \ + gdm-xdmcp-display.h \ + gdm-xdmcp-chooser-display.c \ + gdm-xdmcp-chooser-display.h \ + $(NULL) + + if XDMCP_SUPPORT + gdm_SOURCES += $(XDMCP_SOURCES) + endif + + EXTRA_gdm_SOURCES = \ + $(XDMCP_SOURCES) \ + $(NULL) + + gdm_LDADD = \ + $(top_builddir)/common/libgdmcommon.la \ + $(XLIB_LIBS) \ + $(DAEMON_LIBS) \ + $(XDMCP_LIBS) \ + $(LIBWRAP_LIBS) \ + $(SYSTEMD_LIBS) \ + $(JOURNALD_LIBS) \ + $(EXTRA_DAEMON_LIBS) \ + $(NULL) + + CLEANFILES = \ ++ gdm-dbus-glue.c \ + gdm-display-glue.c \ + gdm-local-display-factory-glue.c \ + gdm-manager-glue.c \ + gdm-session-glue.c \ + gdm-session-worker-glue.c \ + gdm-session-enum-types.c \ + gdm-local-display-glue.c \ + $(BUILT_SOURCES) \ + $(NULL) + + EXTRA_DIST = \ + gdm-manager.xml \ + gdm-session-worker.xml \ + gdm-session.xml \ + gdm-display.xml \ + gdm-local-display.xml \ + gdm-local-display-factory.xml \ + gdm-session-enum-types.c.in \ + gdm-session-enum-types.h.in \ + $(NULL) +diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c +index 42c415837..c1b2c1765 100644 +--- a/daemon/gdm-session-worker.c ++++ b/daemon/gdm-session-worker.c +@@ -39,60 +39,61 @@ + + #ifdef HAVE_LOGINCAP + #include + #endif + + #include + #include + #include + #include + #include + + #include + + #include + + #ifdef ENABLE_SYSTEMD_JOURNAL + #include + #endif + + #ifdef HAVE_SELINUX + #include + #endif /* HAVE_SELINUX */ + + #include "gdm-common.h" + #include "gdm-log.h" + + #ifdef SUPPORTS_PAM_EXTENSIONS + #include "gdm-pam-extensions.h" + #endif + ++#include "gdm-dbus-glue.h" + #include "gdm-session-worker.h" + #include "gdm-session-glue.h" + #include "gdm-session.h" + + #if defined (HAVE_ADT) + #include "gdm-session-solaris-auditor.h" + #elif defined (HAVE_LIBAUDIT) + #include "gdm-session-linux-auditor.h" + #else + #include "gdm-session-auditor.h" + #endif + + #include "gdm-session-settings.h" + + #define GDM_SESSION_WORKER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SESSION_WORKER, GdmSessionWorkerPrivate)) + + #define GDM_SESSION_DBUS_PATH "/org/gnome/DisplayManager/Session" + #define GDM_SESSION_DBUS_NAME "org.gnome.DisplayManager.Session" + #define GDM_SESSION_DBUS_ERROR_CANCEL "org.gnome.DisplayManager.Session.Error.Cancel" + + #define GDM_WORKER_DBUS_PATH "/org/gnome/DisplayManager/Worker" + + #ifndef GDM_PASSWD_AUXILLARY_BUFFER_SIZE + #define GDM_PASSWD_AUXILLARY_BUFFER_SIZE 1024 + #endif + + #ifndef GDM_SESSION_DEFAULT_PATH + #define GDM_SESSION_DEFAULT_PATH "/usr/local/bin:/usr/bin:/bin" + #endif + +@@ -1028,72 +1029,60 @@ 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) { +- /* 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->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); +@@ -1752,86 +1741,155 @@ gdm_session_worker_accredit_user (GdmSessionWorker *worker, + gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_ACCREDITED); + } else { + gdm_session_worker_uninitialize_pam (worker, error_code); + } + + return ret; + } + + static const char * const * + gdm_session_worker_get_environment (GdmSessionWorker *worker) + { + return (const char * const *) pam_getenvlist (worker->priv->pam_handle); + } + + static gboolean + run_script (GdmSessionWorker *worker, + const char *dir) + { + /* scripts are for non-program sessions only */ + if (worker->priv->is_program_session) { + return TRUE; + } + + return gdm_run_script (dir, + worker->priv->username, + worker->priv->x11_display_name, + worker->priv->display_is_local? NULL : worker->priv->hostname, + worker->priv->x11_authority_file); + } + ++static void ++wait_until_dbus_signal_emission_to_manager_finishes (GdmSessionWorker *worker) ++{ ++ g_autoptr (GdmDBusPeer) peer_proxy = NULL; ++ g_autoptr (GError) error = NULL; ++ gboolean pinged; ++ ++ peer_proxy = gdm_dbus_peer_proxy_new_sync (worker->priv->connection, ++ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, ++ NULL, ++ "/org/freedesktop/DBus", ++ NULL, ++ &error); ++ ++ if (peer_proxy == NULL) { ++ g_debug ("GdmSessionWorker: could not create peer proxy to daemon: %s", ++ error->message); ++ return; ++ } ++ ++ pinged = gdm_dbus_peer_call_ping_sync (peer_proxy, NULL, &error); ++ ++ if (!pinged) { ++ g_debug ("GdmSessionWorker: could not ping daemon: %s", ++ error->message); ++ return; ++ } ++} ++ ++static void ++jump_back_to_initial_vt (GdmSessionWorker *worker) ++{ ++ if (worker->priv->session_vt == 0) ++ return; ++ ++ if (worker->priv->session_vt == GDM_INITIAL_VT) ++ return; ++ ++ if (g_strcmp0 (worker->priv->display_seat_id, "seat0") != 0) ++ return; ++ ++#ifdef ENABLE_USER_DISPLAY_SERVER ++ jump_to_vt (worker, GDM_INITIAL_VT); ++ worker->priv->session_vt = 0; ++#endif ++} ++ + static void + session_worker_child_watch (GPid pid, + int status, + GdmSessionWorker *worker) + { + g_debug ("GdmSessionWorker: child (pid:%d) done (%s:%d)", + (int) pid, + WIFEXITED (status) ? "status" + : WIFSIGNALED (status) ? "signal" + : "unknown", + WIFEXITED (status) ? WEXITSTATUS (status) + : WIFSIGNALED (status) ? WTERMSIG (status) + : -1); + +- + gdm_session_worker_uninitialize_pam (worker, PAM_SUCCESS); + ++ worker->priv->child_pid = -1; ++ worker->priv->child_watch_id = 0; ++ run_script (worker, GDMCONFDIR "/PostSession"); ++ + gdm_dbus_worker_emit_session_exited (GDM_DBUS_WORKER (worker), + worker->priv->service, + status); + + killpg (pid, SIGHUP); + +- worker->priv->child_pid = -1; +- worker->priv->child_watch_id = 0; +- run_script (worker, GDMCONFDIR "/PostSession"); ++ /* FIXME: It's important to give the manager an opportunity to process the ++ * session-exited emission above before switching VTs. ++ * ++ * This is because switching VTs makes the manager try to put a login screen ++ * up on VT 1, but it may actually want to try to auto login again in response ++ * to session-exited. ++ * ++ * This function just does a manager roundtrip over the bus to make sure the ++ * signal has been dispatched before jumping. ++ * ++ * Ultimately, we may want to improve the manager<->worker interface. ++ * ++ * See: ++ * ++ * https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/123 ++ * ++ * for some ideas and more discussion. ++ * ++ */ ++ wait_until_dbus_signal_emission_to_manager_finishes (worker); ++ ++ jump_back_to_initial_vt (worker); + } + + static void + gdm_session_worker_watch_child (GdmSessionWorker *worker) + { + g_debug ("GdmSession worker: watching pid %d", worker->priv->child_pid); + worker->priv->child_watch_id = g_child_watch_add (worker->priv->child_pid, + (GChildWatchFunc)session_worker_child_watch, + worker); + + } + + static gboolean + _is_loggable_file (const char* filename) + { + struct stat file_info; + + if (g_lstat (filename, &file_info) < 0) { + return FALSE; + } + + return S_ISREG (file_info.st_mode) && g_access (filename, R_OK | W_OK) == 0; + } + + static void + rotate_logs (const char *path, + guint n_copies) + { + int i; + +@@ -2401,60 +2459,61 @@ gdm_session_worker_open_session (GdmSessionWorker *worker, + + flags = 0; + + if (worker->priv->is_program_session) { + flags |= PAM_SILENT; + } + + error_code = pam_open_session (worker->priv->pam_handle, flags); + + if (error_code != PAM_SUCCESS) { + g_set_error (error, + GDM_SESSION_WORKER_ERROR, + GDM_SESSION_WORKER_ERROR_OPENING_SESSION, + "%s", pam_strerror (worker->priv->pam_handle, error_code)); + goto out; + } + + g_debug ("GdmSessionWorker: state SESSION_OPENED"); + gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_SESSION_OPENED); + + session_id = gdm_session_worker_get_environment_variable (worker, "XDG_SESSION_ID"); + + if (session_id != NULL) { + g_free (worker->priv->session_id); + worker->priv->session_id = session_id; + } + + out: + if (error_code != PAM_SUCCESS) { + gdm_session_worker_uninitialize_pam (worker, error_code); ++ worker->priv->session_vt = 0; + return FALSE; + } + + gdm_session_worker_get_username (worker, NULL); + gdm_session_auditor_report_login (worker->priv->auditor); + + return TRUE; + } + + static void + gdm_session_worker_set_server_address (GdmSessionWorker *worker, + const char *address) + { + g_free (worker->priv->server_address); + worker->priv->server_address = g_strdup (address); + } + + static void + gdm_session_worker_set_is_reauth_session (GdmSessionWorker *worker, + gboolean is_reauth_session) + { + worker->priv->is_reauth_session = is_reauth_session; + } + + static void + gdm_session_worker_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + { +@@ -3565,60 +3624,62 @@ gdm_session_worker_unwatch_child (GdmSessionWorker *worker) + return; + + g_source_remove (worker->priv->child_watch_id); + worker->priv->child_watch_id = 0; + } + + + static void + gdm_session_worker_finalize (GObject *object) + { + GdmSessionWorker *worker; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_SESSION_WORKER (object)); + + worker = GDM_SESSION_WORKER (object); + + g_return_if_fail (worker->priv != NULL); + + gdm_session_worker_unwatch_child (worker); + + if (worker->priv->child_pid > 0) { + gdm_signal_pid (worker->priv->child_pid, SIGTERM); + gdm_wait_on_pid (worker->priv->child_pid); + } + + if (worker->priv->pam_handle != NULL) { + gdm_session_worker_uninitialize_pam (worker, PAM_SUCCESS); + } + ++ jump_back_to_initial_vt (worker); ++ + g_clear_object (&worker->priv->user_settings); + g_free (worker->priv->service); + g_free (worker->priv->x11_display_name); + g_free (worker->priv->x11_authority_file); + g_free (worker->priv->display_device); + g_free (worker->priv->display_seat_id); + g_free (worker->priv->hostname); + g_free (worker->priv->username); + g_free (worker->priv->server_address); + g_strfreev (worker->priv->arguments); + g_strfreev (worker->priv->extensions); + + g_hash_table_unref (worker->priv->reauthentication_requests); + + G_OBJECT_CLASS (gdm_session_worker_parent_class)->finalize (object); + } + + GdmSessionWorker * + gdm_session_worker_new (const char *address, + gboolean is_reauth_session) + { + GObject *object; + + object = g_object_new (GDM_TYPE_SESSION_WORKER, + "server-address", address, + "is-reauth-session", is_reauth_session, + NULL); + + return GDM_SESSION_WORKER (object); + } +diff --git a/daemon/org.freedesktop.DBus.xml b/daemon/org.freedesktop.DBus.xml +new file mode 100644 +index 000000000..5e0814bde +--- /dev/null ++++ b/daemon/org.freedesktop.DBus.xml +@@ -0,0 +1,12 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +-- +2.28.0 + 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..b93dbda --- /dev/null +++ b/SOURCES/0002-local-display-factory-Add-gdm_local_display_factory_.patch @@ -0,0 +1,164 @@ +From 417f0aed42959719c40f0f8ec65050dcf2510bd1 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 16 May 2018 14:10:34 +0100 +Subject: [PATCH 02/51] 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.27.0 + diff --git a/SOURCES/0002-session-Don-t-leak-remote-greeter-interface.patch b/SOURCES/0002-session-Don-t-leak-remote-greeter-interface.patch new file mode 100644 index 0000000..b3e80dd --- /dev/null +++ b/SOURCES/0002-session-Don-t-leak-remote-greeter-interface.patch @@ -0,0 +1,87 @@ +From aeb88313c2110389ec530c8c7d5d816bac24d254 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 15 Sep 2020 00:41:00 -0400 +Subject: [PATCH 2/3] session: Don't leak remote greeter interface + +XDMCP login screens get a "Remote Geeter Interface" exported over +the bus connection (so the login window can provide a Disconnect +button). + +This interface is getting leaked when the session object is disposed, +leaving the bus connection itself undisposed, which causes an fd +leak. + +This commit plugs the interface leak, and thus the fd leak. +--- + daemon/gdm-session.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c +index 540a2534d..d6d8f128a 100644 +--- a/daemon/gdm-session.c ++++ b/daemon/gdm-session.c +@@ -3602,60 +3602,61 @@ gdm_session_get_property (GObject *object, + break; + #ifdef ENABLE_WAYLAND_SUPPORT + case PROP_IGNORE_WAYLAND: + g_value_set_boolean (value, self->priv->ignore_wayland); + break; + #endif + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + + static void + gdm_session_dispose (GObject *object) + { + GdmSession *self; + + self = GDM_SESSION (object); + + g_debug ("GdmSession: Disposing session"); + + gdm_session_close (self); + + g_clear_pointer (&self->priv->conversations, + g_hash_table_unref); + + g_clear_object (&self->priv->user_verifier_interface); + g_clear_pointer (&self->priv->user_verifier_extensions, + g_hash_table_unref); + g_clear_object (&self->priv->greeter_interface); ++ g_clear_object (&self->priv->remote_greeter_interface); + g_clear_object (&self->priv->chooser_interface); + + g_free (self->priv->display_name); + self->priv->display_name = NULL; + + g_free (self->priv->display_hostname); + self->priv->display_hostname = NULL; + + g_free (self->priv->display_device); + self->priv->display_device = NULL; + + g_free (self->priv->display_seat_id); + self->priv->display_seat_id = NULL; + + g_free (self->priv->display_x11_authority_file); + self->priv->display_x11_authority_file = NULL; + + g_strfreev (self->priv->conversation_environment); + self->priv->conversation_environment = NULL; + + if (self->priv->worker_server != NULL) { + g_dbus_server_stop (self->priv->worker_server); + g_clear_object (&self->priv->worker_server); + } + + if (self->priv->outside_server != NULL) { + g_dbus_server_stop (self->priv->outside_server); + g_clear_object (&self->priv->outside_server); + } + +-- +2.26.2 + 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..3bc4336 --- /dev/null +++ b/SOURCES/0003-local-display-factory-Use-correct-session-type-for-n.patch @@ -0,0 +1,83 @@ +From db47f4de3aab9f4cd6b381ecd9341c94add59bf9 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 16 May 2018 18:36:50 +0100 +Subject: [PATCH 03/51] 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.27.0 + diff --git a/SOURCES/0003-xdmcp-display-factory-Clear-launch-environment-when-.patch b/SOURCES/0003-xdmcp-display-factory-Clear-launch-environment-when-.patch new file mode 100644 index 0000000..2ff5c46 --- /dev/null +++ b/SOURCES/0003-xdmcp-display-factory-Clear-launch-environment-when-.patch @@ -0,0 +1,87 @@ +From 7c9e236f9015aae2ea5868b67ff8036766cb7099 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 15 Sep 2020 11:28:48 -0400 +Subject: [PATCH 3/3] xdmcp-display-factory: Clear launch environment when done + with it + +The XDMCP disply factory examines the sessions of its displays' +launch environments when the displays change status. + +Unfortunately it leaks a reference to the launch environment when +doing that. + +This commit fixes the reference leak which leads to an fd leak. +--- + daemon/gdm-xdmcp-display-factory.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/daemon/gdm-xdmcp-display-factory.c b/daemon/gdm-xdmcp-display-factory.c +index 2e14beab4..98232113f 100644 +--- a/daemon/gdm-xdmcp-display-factory.c ++++ b/daemon/gdm-xdmcp-display-factory.c +@@ -2091,60 +2091,62 @@ on_display_status_changed (GdmDisplay *display, + gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory)); + break; + case GDM_DISPLAY_FAILED: + 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; + } ++ ++ g_clear_object (&launch_environment); + } + + static GdmDisplay * + gdm_xdmcp_display_create (GdmXdmcpDisplayFactory *factory, + const char *hostname, + GdmAddress *address, + int displaynum) + { + GdmDisplay *display; + GdmDisplayStore *store; + gboolean use_chooser; + + g_debug ("GdmXdmcpDisplayFactory: Creating xdmcp display for %s:%d", + hostname ? hostname : "(null)", displaynum); + + use_chooser = FALSE; + if (factory->priv->honor_indirect) { + IndirectClient *ic; + + ic = indirect_client_lookup (factory, address); + + /* This was an indirect thingie and nothing was yet chosen, + * use a chooser */ + if (ic != NULL && ic->chosen_address == NULL) { + use_chooser = TRUE; + } + } + + if (use_chooser) { + display = gdm_xdmcp_chooser_display_new (hostname, +-- +2.26.2 + 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..5f95b3b --- /dev/null +++ b/SOURCES/0004-manager-make-get_login_window_session_id-fail-if-no-.patch @@ -0,0 +1,137 @@ +From e2e5d2a7d73baa7c24d1f14b276cb653c06dd82f Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 1 Aug 2018 15:46:11 -0400 +Subject: [PATCH 04/51] 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.27.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..c287705 --- /dev/null +++ b/SOURCES/0005-manager-avoid-leaking-session_id.patch @@ -0,0 +1,79 @@ +From 34238a9e845455ae2b92159c71b75b7abedc2eb9 Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Mon, 18 Jun 2018 12:33:42 +0200 +Subject: [PATCH 05/51] 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.27.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..f3dc218 --- /dev/null +++ b/SOURCES/0006-manager-gracefully-handle-the-case-of-no-session-for.patch @@ -0,0 +1,89 @@ +From 454a3daad5148a8ef30cb298af82aa0713e73af7 Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Mon, 18 Jun 2018 12:33:51 +0200 +Subject: [PATCH 06/51] 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.27.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..b2c2e03 --- /dev/null +++ b/SOURCES/0007-common-dedupe-gdm_get_login_window_session_id.patch @@ -0,0 +1,432 @@ +From 2fc9a5f9db7c9d2ab828bcff4ee5dec9c3cf3d3c Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 1 Aug 2018 16:34:30 -0400 +Subject: [PATCH 07/51] 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.27.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..3e99665 --- /dev/null +++ b/SOURCES/0008-common-dedupe-activate_session_id.patch @@ -0,0 +1,401 @@ +From 9d8e72ea9171566e9d74caaf28c8b5933ef34874 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 2 Aug 2018 14:00:46 -0400 +Subject: [PATCH 08/51] 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.27.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..f3236f8 --- /dev/null +++ b/SOURCES/0009-manager-plug-leak-in-maybe_activate_other_session.patch @@ -0,0 +1,103 @@ +From 5bc19a4eb6de02ba549252026911dcce86e0fc21 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 22 Jun 2018 14:44:11 -0400 +Subject: [PATCH 09/51] 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.27.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..090b728 --- /dev/null +++ b/SOURCES/0010-manager-start-login-screen-if-old-one-is-finished.patch @@ -0,0 +1,92 @@ +From 2ec1b65c7bc2cefeabbd9a7a3a50436e1233804c Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 22 Jun 2018 14:55:39 -0400 +Subject: [PATCH 10/51] 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.27.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..b843be7 --- /dev/null +++ b/SOURCES/0011-manager-don-t-bail-if-session-disappears-out-from-un.patch @@ -0,0 +1,127 @@ +From 67d29b19ff4e53d58879b14c2e79a3bda419576f Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 22 Jun 2018 15:26:03 -0400 +Subject: [PATCH 11/51] 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.27.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..55868c2 --- /dev/null +++ b/SOURCES/0012-daemon-try-harder-to-get-to-a-login-screen-at-logout.patch @@ -0,0 +1,563 @@ +From 9a3e9cb9a6bbf68ed6eb9f13d143a63af940f3ae Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 30 Jul 2018 16:21:29 -0400 +Subject: [PATCH 12/51] 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 + */ + g_usleep (2 * G_USEC_PER_SEC); + } + + #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.27.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..34f4120 --- /dev/null +++ b/SOURCES/0013-local-display-factory-ensure-non-seat0-codepath-does.patch @@ -0,0 +1,87 @@ +From 299a0981f4e9fc02716d64abf5e5e692e2ad2951 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 3 Aug 2018 16:50:36 -0400 +Subject: [PATCH 13/51] 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.27.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..84b33da --- /dev/null +++ b/SOURCES/0014-daemon-kill-and-restart-greeter-on-demand-under-wayl.patch @@ -0,0 +1,369 @@ +From 08f5f88ca6fb0edfc94af4c85912484b6048691b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 2 Aug 2018 15:06:09 -0400 +Subject: [PATCH 14/51] 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.27.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..456bc34 --- /dev/null +++ b/SOURCES/0015-local-display-factory-add-more-debug-messages-to-new.patch @@ -0,0 +1,235 @@ +From 59533722f1749d4e71360c5d717a18006c1f64c0 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Aug 2018 13:55:06 -0400 +Subject: [PATCH 15/51] 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.27.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..dc9b1b7 --- /dev/null +++ b/SOURCES/0016-local-display-factory-don-t-start-two-greeters-at-st.patch @@ -0,0 +1,97 @@ +From e5bf6d78ff8f54bbb74e572f05ccbf1c0df24017 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Aug 2018 13:55:06 -0400 +Subject: [PATCH 16/51] 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.27.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..76ab241 --- /dev/null +++ b/SOURCES/0017-session-worker-don-t-switch-VTs-if-we-re-already-on-.patch @@ -0,0 +1,128 @@ +From de4b24f63e62a39b2cb10a8c58c54d86559f7f26 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Aug 2018 14:04:44 -0400 +Subject: [PATCH 17/51] 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.27.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..c9a114c --- /dev/null +++ b/SOURCES/0018-session-worker-fix-current-vt-detection-short-circui.patch @@ -0,0 +1,87 @@ +From b9e5a2879a410b6a85be6c01c6f49cd7eb24c800 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 15:20:39 -0400 +Subject: [PATCH 18/51] 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.27.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..45148f2 --- /dev/null +++ b/SOURCES/0019-local-display-factory-don-t-jump-to-failed-display.patch @@ -0,0 +1,168 @@ +From fe680d77cff9272843cb171c7e590c239f7afe5a Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 9 Aug 2018 12:32:31 -0400 +Subject: [PATCH 19/51] 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.27.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..31d91a0 --- /dev/null +++ b/SOURCES/0020-local-display-factory-add-some-more-debug-statements.patch @@ -0,0 +1,161 @@ +From 94207dd5699f1cd2fe7d516c20e1de2b2e2778fb Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 9 Aug 2018 12:48:25 -0400 +Subject: [PATCH 20/51] 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.27.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..f605db9 --- /dev/null +++ b/SOURCES/0021-local-display-factory-ignore-spurios-SeatNew-signal-.patch @@ -0,0 +1,162 @@ +From abd8e1ef71d093a3ab5c110aea5fa2012d59d5e2 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 14 Aug 2018 10:21:17 -0400 +Subject: [PATCH 21/51] 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.27.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..5c78e7d --- /dev/null +++ b/SOURCES/0022-common-remove-unnecessary-free.patch @@ -0,0 +1,91 @@ +From 9b8c21080286f943d0a19431bc8c0061f4833443 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 13:04:56 -0400 +Subject: [PATCH 22/51] 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.27.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..c27d5bf --- /dev/null +++ b/SOURCES/0023-common-don-t-bail-if-session-disappears-out-from-und.patch @@ -0,0 +1,133 @@ +From 4a948e4b203fdf5fcd9b5e53dd4a80ef2786c0cd Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 13:06:54 -0400 +Subject: [PATCH 23/51] 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.27.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..194c156 --- /dev/null +++ b/SOURCES/0024-manager-better-logind-handling.patch @@ -0,0 +1,138 @@ +From fac23cfa3e8e9fe21563733c0c1b739ddecead8a Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 14:01:55 -0400 +Subject: [PATCH 24/51] 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.27.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..cc3744a --- /dev/null +++ b/SOURCES/0025-session-worker-clear-VT-before-jumping-to-it.patch @@ -0,0 +1,90 @@ +From cabcd21c17ca98e517a3eea7c9d5ce269445e3e0 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 15:46:55 -0400 +Subject: [PATCH 25/51] 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.27.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..5a98652 --- /dev/null +++ b/SOURCES/0026-manager-don-t-set-ran_once-after-running-initial-set.patch @@ -0,0 +1,133 @@ +From b773eb570d8c5f9d2222ee39eecbc6a622d108d8 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 16:04:41 -0400 +Subject: [PATCH 26/51] 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.27.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..b16bcfb --- /dev/null +++ b/SOURCES/0027-manager-start-initial-setup-right-away.patch @@ -0,0 +1,418 @@ +From 3d4199f82136e7046b5b08fc7c583e3fce2d04a2 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 14:33:58 -0400 +Subject: [PATCH 27/51] 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.27.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..67ee336 --- /dev/null +++ b/SOURCES/0028-gdm-wayland-session-gdm-x-session-register-after-del.patch @@ -0,0 +1,309 @@ +From 3073c23c673ede5093c1f93fb0775c2cd3203d7f Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 30 Aug 2018 16:09:02 -0400 +Subject: [PATCH 28/51] 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.27.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..df23fbd --- /dev/null +++ b/SOURCES/0029-local-display-factory-defer-killing-greeter-until-ne.patch @@ -0,0 +1,399 @@ +From d85448e2e523deb1487e7e405a480e1c4e6a5f6f Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 15:33:00 -0400 +Subject: [PATCH 29/51] 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 + */ + g_usleep (2 * G_USEC_PER_SEC); + } + + #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.27.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..889088d --- /dev/null +++ b/SOURCES/0030-daemon-Move-the-waiting-the-session-to-have-taken-ov.patch @@ -0,0 +1,708 @@ +From 0ff911467265831006aac6216060dbecff84c1cb Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 4 Sep 2018 10:56:45 +0200 +Subject: [PATCH 30/51] 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 + */ + g_usleep (2 * G_USEC_PER_SEC); + } + + #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.27.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..5605385 --- /dev/null +++ b/SOURCES/0031-local-display-factory-don-t-autoreap-initial-setup.patch @@ -0,0 +1,100 @@ +From 59b3b809400dfac25410cf99dbc15cb5f66f85a3 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 25 Sep 2018 14:52:15 -0400 +Subject: [PATCH 31/51] 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.27.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..adf484e --- /dev/null +++ b/SOURCES/0032-manager-rework-how-autologin-is-figured-out.patch @@ -0,0 +1,460 @@ +From 566720ce07db8745c0ae6780ff289292dc0a9b60 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 25 Sep 2018 10:59:37 -0400 +Subject: [PATCH 32/51] 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.27.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..a898d87 --- /dev/null +++ b/SOURCES/0033-manager-correct-display-confusion.patch @@ -0,0 +1,85 @@ +From 05e49542a9de81731fce68614babe22d437e8fff Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 1 Oct 2018 11:05:57 -0400 +Subject: [PATCH 33/51] 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.27.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..757ec9e --- /dev/null +++ b/SOURCES/0034-manager-don-t-run-autologin-display-on-tty1.patch @@ -0,0 +1,99 @@ +From e4c9a998f89d429d31b02997f146c8218c0742bc Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 24 Sep 2018 14:45:38 -0400 +Subject: [PATCH 34/51] 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.27.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..e53ccef --- /dev/null +++ b/SOURCES/0035-local-display-factory-Remove-initial-VT-is-in-use-ch.patch @@ -0,0 +1,110 @@ +From 2843a951ef826afd01fa3c7340780b1929db38c7 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 4 Sep 2018 08:12:34 +0200 +Subject: [PATCH 35/51] 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.27.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..85f9af9 --- /dev/null +++ b/SOURCES/0036-local-display-factory-Remove-same-VT-so-don-t-switch.patch @@ -0,0 +1,140 @@ +From f5acee2766c05403235c06da6b1bb68af744da79 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 25 Sep 2018 14:30:16 -0400 +Subject: [PATCH 36/51] 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.27.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..35639e9 --- /dev/null +++ b/SOURCES/0037-local-display-factory-handle-reviving-displays-that-.patch @@ -0,0 +1,88 @@ +From 59a4538e335362a92186217be03529f3694697e1 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 25 Sep 2018 14:39:42 -0400 +Subject: [PATCH 37/51] 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.27.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..1925cab --- /dev/null +++ b/SOURCES/0038-manager-don-t-kill-initial-setup-before-starting-use.patch @@ -0,0 +1,151 @@ +From c65a0dbd195be52f0db0c49ea0355fe1b5eb4c09 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 31 Aug 2018 15:48:38 -0400 +Subject: [PATCH 38/51] 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.27.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..dff5a1f --- /dev/null +++ b/SOURCES/0039-manager-do-initial-setup-post-work-in-manager-code.patch @@ -0,0 +1,694 @@ +From c08afca0807d8820030c19a40e7590f72878c788 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 6 Sep 2018 19:31:50 -0400 +Subject: [PATCH 39/51] 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.27.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..622db0c --- /dev/null +++ b/SOURCES/0040-display-store-make-foreach-ignore-callback-return-va.patch @@ -0,0 +1,117 @@ +From 49383786d96414e7204ea50ca5ea0263be97b581 Mon Sep 17 00:00:00 2001 +From: xiaoguang wang +Date: Wed, 20 Feb 2019 09:26:02 +0800 +Subject: [PATCH 40/51] 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.27.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..7b8cf5c --- /dev/null +++ b/SOURCES/0041-xdmcp-display-factory-don-t-return-value-from-foreac.patch @@ -0,0 +1,180 @@ +From 323358ef61d969588ea048d5b0eba6fd102d3dcf Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 21 Feb 2019 15:20:01 -0500 +Subject: [PATCH 41/51] 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.27.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..c03b87f --- /dev/null +++ b/SOURCES/0042-GdmLocalDisplayFactory-Store-VT-number-not-tty-ident.patch @@ -0,0 +1,538 @@ +From 3cf5b4b12d3d39fa858ff593adeecfe711cdddaf Mon Sep 17 00:00:00 2001 +From: Iain Lane +Date: Tue, 7 May 2019 15:35:23 +0100 +Subject: [PATCH 42/51] 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.27.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..ea5f20c --- /dev/null +++ b/SOURCES/0043-gdm-session-worker-Drop-login_vt-assuming-it-is-GDM_.patch @@ -0,0 +1,344 @@ +From 476230f7b721781c682d26983c9a2fd82afc45e1 Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Wed, 25 Sep 2019 14:51:40 +0200 +Subject: [PATCH 43/51] 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.27.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..0e55560 --- /dev/null +++ b/SOURCES/0044-session-worker-ensure-initial-vt-is-never-picked-for.patch @@ -0,0 +1,160 @@ +From 75b65846ca77bd2d42e25365b4b7242a406330cf Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 7 Apr 2020 14:37:41 -0400 +Subject: [PATCH 44/51] 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.27.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..d464a2a --- /dev/null +++ b/SOURCES/0045-local-display-factory-Always-force-login-screen-to-V.patch @@ -0,0 +1,114 @@ +From fc3503f16e9de535d2a36b904720b360370f880f Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 15 May 2020 10:08:24 -0400 +Subject: [PATCH 45/51] 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.27.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..e29138c --- /dev/null +++ b/SOURCES/0046-gdm-x-session-tell-x-server-to-not-vt-switch.patch @@ -0,0 +1,81 @@ +From 763e31a576a4cd665e5ad06ad0eb4610cecc0b42 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 10 Jul 2020 10:45:52 -0400 +Subject: [PATCH 46/51] 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.27.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..306b7fb --- /dev/null +++ b/SOURCES/0047-local-display-factory-kill-X-on-login-just-like-wayl.patch @@ -0,0 +1,115 @@ +From a1c74e2e42dea464ab0b439b767da5c12cbf3986 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 11 Oct 2018 07:15:56 -0400 +Subject: [PATCH 47/51] 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.27.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..041a8be --- /dev/null +++ b/SOURCES/0048-manager-don-t-kill-initial-setup-right-away-with-Xor.patch @@ -0,0 +1,149 @@ +From f1b7d85b46dfc253176d6a043dcce26da3a26dfb Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 13 Jul 2020 09:23:06 -0400 +Subject: [PATCH 48/51] 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.27.0 + diff --git a/SOURCES/0049-GdmManager-GdmDisplay-Add-RegisterSession-method.patch b/SOURCES/0049-GdmManager-GdmDisplay-Add-RegisterSession-method.patch new file mode 100644 index 0000000..79e7eda --- /dev/null +++ b/SOURCES/0049-GdmManager-GdmDisplay-Add-RegisterSession-method.patch @@ -0,0 +1,551 @@ +From 2724b4fd6d4ac527acc481f056f535141b63fe24 Mon Sep 17 00:00:00 2001 +From: Iain Lane +Date: Tue, 7 May 2019 15:57:43 +0100 +Subject: [PATCH 49/51] GdmManager, GdmDisplay: Add RegisterSession method + +Window managers can use this to register with GDM when they've finished +starting up and started displaying. +--- + daemon/gdm-display.c | 24 ++++++++++++++++++++++++ + daemon/gdm-manager.c | 30 ++++++++++++++++++++++++++++++ + daemon/gdm-manager.xml | 3 +++ + 3 files changed, 57 insertions(+) + +diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c +index 1cef8c7c1..56799741d 100644 +--- a/daemon/gdm-display.c ++++ b/daemon/gdm-display.c +@@ -64,82 +64,84 @@ struct GdmDisplayPrivate + 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; + + GdmDBusDisplay *display_skeleton; + GDBusObjectSkeleton *object_skeleton; + + /* this spawns and controls the greeter session */ + GdmLaunchEnvironment *launch_environment; + + guint is_local : 1; + guint is_initial : 1; + guint allow_timed_login : 1; + guint have_existing_user_accounts : 1; + guint doing_initial_setup : 1; ++ guint session_registered : 1; + }; + + 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, ++ PROP_SESSION_REGISTERED, + }; + + 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) + + 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; + } + +@@ -733,60 +735,68 @@ static void + _gdm_display_set_x11_display_number (GdmDisplay *self, + int num) + { + self->priv->x11_display_number = num; + } + + static void + _gdm_display_set_x11_display_name (GdmDisplay *self, + const char *x11_display) + { + g_free (self->priv->x11_display_name); + self->priv->x11_display_name = g_strdup (x11_display); + } + + static void + _gdm_display_set_x11_cookie (GdmDisplay *self, + const char *x11_cookie) + { + g_free (self->priv->x11_cookie); + self->priv->x11_cookie = g_strdup (x11_cookie); + } + + static void + _gdm_display_set_is_local (GdmDisplay *self, + gboolean is_local) + { + g_debug ("GdmDisplay: local: %s", is_local? "yes" : "no"); + self->priv->is_local = is_local; + } + ++static void ++_gdm_display_set_session_registered (GdmDisplay *self, ++ gboolean registered) ++{ ++ g_debug ("GdmDisplay: session registered: %s", registered? "yes" : "no"); ++ self->priv->session_registered = registered; ++} ++ + static void + _gdm_display_set_launch_environment (GdmDisplay *self, + GdmLaunchEnvironment *launch_environment) + { + g_clear_object (&self->priv->launch_environment); + + self->priv->launch_environment = g_object_ref (launch_environment); + } + + static void + _gdm_display_set_is_initial (GdmDisplay *self, + gboolean initial) + { + g_debug ("GdmDisplay: initial: %s", initial? "yes" : "no"); + self->priv->is_initial = initial; + } + + static void + _gdm_display_set_allow_timed_login (GdmDisplay *self, + gboolean allow_timed_login) + { + g_debug ("GdmDisplay: allow timed login: %s", allow_timed_login? "yes" : "no"); + self->priv->allow_timed_login = allow_timed_login; + } + + static void + gdm_display_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +@@ -811,60 +821,63 @@ gdm_display_set_property (GObject *object, + case PROP_SESSION_CLASS: + _gdm_display_set_session_class (self, g_value_get_string (value)); + break; + case PROP_SESSION_TYPE: + _gdm_display_set_session_type (self, g_value_get_string (value)); + break; + case PROP_REMOTE_HOSTNAME: + _gdm_display_set_remote_hostname (self, g_value_get_string (value)); + break; + case PROP_X11_DISPLAY_NUMBER: + _gdm_display_set_x11_display_number (self, g_value_get_int (value)); + break; + case PROP_X11_DISPLAY_NAME: + _gdm_display_set_x11_display_name (self, g_value_get_string (value)); + break; + case PROP_X11_COOKIE: + _gdm_display_set_x11_cookie (self, g_value_get_string (value)); + break; + case PROP_IS_LOCAL: + _gdm_display_set_is_local (self, g_value_get_boolean (value)); + break; + case PROP_ALLOW_TIMED_LOGIN: + _gdm_display_set_allow_timed_login (self, g_value_get_boolean (value)); + break; + case PROP_LAUNCH_ENVIRONMENT: + _gdm_display_set_launch_environment (self, g_value_get_object (value)); + break; + case PROP_IS_INITIAL: + _gdm_display_set_is_initial (self, g_value_get_boolean (value)); + break; ++ case PROP_SESSION_REGISTERED: ++ _gdm_display_set_session_registered (self, g_value_get_boolean (value)); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + + static void + gdm_display_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) + { + GdmDisplay *self; + + self = GDM_DISPLAY (object); + + switch (prop_id) { + case PROP_ID: + g_value_set_string (value, self->priv->id); + break; + case PROP_STATUS: + g_value_set_int (value, self->priv->status); + break; + case PROP_SEAT_ID: + g_value_set_string (value, self->priv->seat_id); + break; + case PROP_SESSION_ID: + g_value_set_string (value, self->priv->session_id); + break; + case PROP_SESSION_CLASS: +@@ -881,60 +894,63 @@ gdm_display_get_property (GObject *object, + break; + case PROP_X11_DISPLAY_NAME: + g_value_set_string (value, self->priv->x11_display_name); + break; + case PROP_X11_COOKIE: + g_value_set_string (value, self->priv->x11_cookie); + break; + case PROP_X11_AUTHORITY_FILE: + g_value_take_string (value, + self->priv->access_file? + gdm_display_access_file_get_path (self->priv->access_file) : NULL); + break; + case PROP_IS_LOCAL: + g_value_set_boolean (value, self->priv->is_local); + break; + case PROP_IS_CONNECTED: + g_value_set_boolean (value, self->priv->xcb_connection != NULL); + break; + case PROP_LAUNCH_ENVIRONMENT: + g_value_set_object (value, self->priv->launch_environment); + break; + case PROP_IS_INITIAL: + g_value_set_boolean (value, self->priv->is_initial); + break; + case PROP_HAVE_EXISTING_USER_ACCOUNTS: + g_value_set_boolean (value, self->priv->have_existing_user_accounts); + break; + case PROP_DOING_INITIAL_SETUP: + g_value_set_boolean (value, self->priv->doing_initial_setup); + break; ++ case PROP_SESSION_REGISTERED: ++ g_value_set_boolean (value, priv->session_registered); ++ break; + case PROP_ALLOW_TIMED_LOGIN: + g_value_set_boolean (value, self->priv->allow_timed_login); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + + static gboolean + handle_get_id (GdmDBusDisplay *skeleton, + GDBusMethodInvocation *invocation, + GdmDisplay *self) + { + char *id; + + gdm_display_get_id (self, &id, NULL); + + gdm_dbus_display_complete_get_id (skeleton, invocation, id); + + g_free (id); + return TRUE; + } + + static gboolean + handle_get_remote_hostname (GdmDBusDisplay *skeleton, + GDBusMethodInvocation *invocation, + GdmDisplay *self) + { + char *hostname; +@@ -1197,60 +1213,68 @@ gdm_display_class_init (GdmDisplayClass *klass) + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + PROP_IS_LOCAL, + g_param_spec_boolean ("is-local", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_IS_CONNECTED, + g_param_spec_boolean ("is-connected", + NULL, + NULL, + TRUE, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_HAVE_EXISTING_USER_ACCOUNTS, + g_param_spec_boolean ("have-existing-user-accounts", + NULL, + NULL, + FALSE, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_DOING_INITIAL_SETUP, + g_param_spec_boolean ("doing-initial-setup", + NULL, + NULL, + FALSE, + G_PARAM_READABLE)); ++ g_object_class_install_property (object_class, ++ PROP_SESSION_REGISTERED, ++ g_param_spec_boolean ("session-registered", ++ NULL, ++ NULL, ++ FALSE, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ + g_object_class_install_property (object_class, + PROP_LAUNCH_ENVIRONMENT, + g_param_spec_object ("launch-environment", + NULL, + NULL, + GDM_TYPE_LAUNCH_ENVIRONMENT, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_STATUS, + g_param_spec_int ("status", + "status", + "status", + -1, + G_MAXINT, + GDM_DISPLAY_UNMANAGED, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GdmDisplayPrivate)); + } + + static void + gdm_display_init (GdmDisplay *self) + { + + self->priv = GDM_DISPLAY_GET_PRIVATE (self); + + self->priv->creation_time = time (NULL); + self->priv->server_timer = g_timer_new (); + } + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index b147d73db..bff602a07 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -789,60 +789,89 @@ gdm_manager_handle_register_display (GdmDBusManager *manager, + if (session != NULL) { + GPid pid; + + if (x11_display_name != NULL) { + g_object_set (G_OBJECT (session), "display-name", x11_display_name, NULL); + g_object_set (G_OBJECT (display), "x11-display-name", x11_display_name, NULL); + } + + /* FIXME: this should happen in gdm-session.c when the session is opened + */ + if (tty != NULL) + g_object_set (G_OBJECT (session), "display-device", tty, NULL); + + pid = gdm_session_get_pid (session); + + if (pid > 0) { + add_session_record (self, session, pid, SESSION_RECORD_LOGIN); + } + } + + g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_MANAGED, NULL); + + gdm_dbus_manager_complete_register_display (GDM_DBUS_MANAGER (manager), + invocation); + + g_clear_pointer (&x11_display_name, g_free); + g_clear_pointer (&tty, g_free); + return TRUE; + } + ++static gboolean ++gdm_manager_handle_register_session (GdmDBusManager *manager, ++ GDBusMethodInvocation *invocation, ++ GVariant *details) ++{ ++ GdmManager *self = GDM_MANAGER (manager); ++ GdmDisplay *display; ++ const char *sender; ++ GDBusConnection *connection; ++ ++ sender = g_dbus_method_invocation_get_sender (invocation); ++ connection = g_dbus_method_invocation_get_connection (invocation); ++ ++ get_display_and_details_for_bus_sender (self, connection, sender, &display, ++ NULL, NULL, NULL, NULL, NULL, NULL, NULL); ++ ++ g_debug ("GdmManager: trying to register new session on display %p", display); ++ ++ if (display != NULL) ++ g_object_set (G_OBJECT (display), "session-registered", TRUE, NULL); ++ else ++ g_debug ("GdmManager: No display, not registering"); ++ ++ gdm_dbus_manager_complete_register_session (GDM_DBUS_MANAGER (manager), ++ invocation); ++ ++ return TRUE; ++} ++ + static gboolean + gdm_manager_handle_open_session (GdmDBusManager *manager, + GDBusMethodInvocation *invocation) + { + GdmManager *self = GDM_MANAGER (manager); + const char *sender; + GDBusConnection *connection; + GdmDisplay *display = NULL; + GdmSession *session = NULL; + const char *address; + GPid pid = 0; + uid_t uid = (uid_t) -1; + uid_t allowed_user; + + g_debug ("GdmManager: trying to open new session"); + + sender = g_dbus_method_invocation_get_sender (invocation); + connection = g_dbus_method_invocation_get_connection (invocation); + get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, NULL, &pid, &uid, NULL, NULL); + + if (display == NULL) { + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + _("No session available")); + + return TRUE; + } + + #ifdef HAVE_LIBXDMCP +@@ -1167,60 +1196,61 @@ gdm_manager_handle_open_reauthentication_channel (GdmDBusManager *manager + g_hash_table_insert (self->priv->open_reauthentication_requests, + GINT_TO_POINTER (pid), + invocation); + } else if (is_login_screen) { + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Login screen only allowed to open reauthentication channels for running sessions"); + return TRUE; + } else { + char *address; + address = open_temporary_reauthentication_channel (self, + seat_id, + session_id, + pid, + uid, + is_remote); + gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager), + invocation, + address); + g_free (address); + } + + return TRUE; + } + + static void + manager_interface_init (GdmDBusManagerIface *interface) + { + interface->handle_register_display = gdm_manager_handle_register_display; ++ interface->handle_register_session = gdm_manager_handle_register_session; + interface->handle_open_session = gdm_manager_handle_open_session; + interface->handle_open_reauthentication_channel = gdm_manager_handle_open_reauthentication_channel; + } + + static gboolean + display_is_on_seat0 (GdmDisplay *display) + { + gboolean is_on_seat0 = TRUE; + char *seat_id = NULL; + + g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL); + + if (g_strcmp0 (seat_id, "seat0") != 0) { + is_on_seat0 = FALSE; + } + + g_free (seat_id); + + return is_on_seat0; + } + + static gboolean + get_timed_login_details (GdmManager *manager, + char **usernamep, + int *delayp) + { + gboolean res; + gboolean enabled; + + int delay; +diff --git a/daemon/gdm-manager.xml b/daemon/gdm-manager.xml +index f11f3fb73..92ef1d02d 100644 +--- a/daemon/gdm-manager.xml ++++ b/daemon/gdm-manager.xml +@@ -1,16 +1,19 @@ + + + + + + ++ ++ ++ + + + + + + + + + + +-- +2.28.0 + diff --git a/SOURCES/0050-Allow-sessions-to-register-with-GDM.patch b/SOURCES/0050-Allow-sessions-to-register-with-GDM.patch new file mode 100644 index 0000000..5a53144 --- /dev/null +++ b/SOURCES/0050-Allow-sessions-to-register-with-GDM.patch @@ -0,0 +1,1917 @@ +From d6aca6da3ab9c16e907c7f46cf6d7bc2abbc2890 Mon Sep 17 00:00:00 2001 +From: Iain Lane +Date: Tue, 7 May 2019 16:00:14 +0100 +Subject: [PATCH 50/51] Allow sessions to register with GDM +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Recording when sessions start, for Wayland → Xorg fallback or +transitioning to the user session, is currently done with timeouts. + +This isn't ideal, because on some very slow machines the timeout can be +hit before the session has had a chance to fail: if gnome-session takes +more than 3 seconds to fail then the session will be considered to have +exited rather than failed, and so we don't do Xorg fallback. + +We can do this more reliably if we allow sessions to optionally register +themselves with GDM. Then we will know when they've started, so can shut +down the greeter or fall back to Xorg as appropriate. The mechanism is +that they specify X-GDM-SessionRegisters=true in their file, and then +call RegsterSession on the DisplayManager interface on the bus (added in +the previous commit) to say that they've started up. + +If X-GDM-SessionRegisters is missing or false, GDM will call the same +method for them after 10 seconds. + +Closes: #483 +--- + common/gdm-common.h | 2 + + daemon/gdm-display.c | 19 ++----- + daemon/gdm-local-display-factory.c | 49 +++++++++--------- + daemon/gdm-session.c | 47 +++++++++++++++-- + daemon/gdm-session.h | 1 + + daemon/gdm-wayland-session.c | 81 +++++++++++++++++++++++------- + daemon/gdm-x-session.c | 81 +++++++++++++++++++++++------- + 7 files changed, 201 insertions(+), 79 deletions(-) + +diff --git a/common/gdm-common.h b/common/gdm-common.h +index 3fbf07653..7fd6458d7 100644 +--- a/common/gdm-common.h ++++ b/common/gdm-common.h +@@ -1,59 +1,61 @@ + /* -*- 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 REGISTER_SESSION_TIMEOUT 10 ++ + #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); +diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c +index 09cc2e116..ae20491cd 100644 +--- a/daemon/gdm-display.c ++++ b/daemon/gdm-display.c +@@ -39,61 +39,60 @@ + + #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 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; + + GdmDBusDisplay *display_skeleton; + GDBusObjectSkeleton *object_skeleton; + + /* this spawns and controls the greeter session */ + GdmLaunchEnvironment *launch_environment; + + guint is_local : 1; + guint is_initial : 1; + guint allow_timed_login : 1; + guint have_existing_user_accounts : 1; + guint doing_initial_setup : 1; + guint session_registered : 1; + }; + + enum { + PROP_0, + PROP_ID, +@@ -510,62 +509,60 @@ gdm_display_prepare (GdmDisplay *self) + * asynchronously + */ + look_for_existing_users_sync (self); + + self->priv->doing_initial_setup = wants_initial_setup (self); + + g_object_ref (self); + ret = GDM_DISPLAY_GET_CLASS (self)->prepare (self); + g_object_unref (self); + + return ret; + } + + gboolean + gdm_display_manage (GdmDisplay *self) + { + gboolean res; + + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + g_debug ("GdmDisplay: Managing display: %s", self->priv->id); + + /* If not explicitly prepared, do it now */ + if (self->priv->status == GDM_DISPLAY_UNMANAGED) { + res = gdm_display_prepare (self); + if (! res) { + return FALSE; + } + } + +- g_timer_start (self->priv->server_timer); +- + if (g_strcmp0 (self->priv->session_class, "greeter") == 0) { + if (GDM_DISPLAY_GET_CLASS (self)->manage != NULL) { + GDM_DISPLAY_GET_CLASS (self)->manage (self); + } + } + + return TRUE; + } + + gboolean + gdm_display_finish (GdmDisplay *self) + { + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + if (self->priv->finish_idle_id != 0) { + g_source_remove (self->priv->finish_idle_id); + self->priv->finish_idle_id = 0; + } + + _gdm_display_set_status (self, GDM_DISPLAY_FINISHED); + + g_debug ("GdmDisplay: finish display"); + + return TRUE; + } + + static void + gdm_display_disconnect (GdmDisplay *self) + { + /* These 3 bits are reserved/unused by the X protocol */ +@@ -576,85 +573,80 @@ gdm_display_disconnect (GdmDisplay *self) + + if (self->priv->xcb_connection == NULL) { + return; + } + + setup = xcb_get_setup (self->priv->xcb_connection); + + /* resource_id_mask is the bits given to each client for + * addressing resources */ + highest_client = (XID) ~unused_bits & ~setup->resource_id_mask; + client_increment = setup->resource_id_mask + 1; + + /* Kill every client but ourselves, then close our own connection + */ + for (client = 0; + client <= highest_client; + client += client_increment) { + + if (client != setup->resource_id_base) + xcb_kill_client (self->priv->xcb_connection, client); + } + + xcb_flush (self->priv->xcb_connection); + + g_clear_pointer (&self->priv->xcb_connection, xcb_disconnect); + } + + gboolean + gdm_display_unmanage (GdmDisplay *self) + { +- gdouble elapsed; +- + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + g_debug ("GdmDisplay: unmanage display"); + + gdm_display_disconnect (self); + +- g_timer_stop (self->priv->server_timer); +- + if (self->priv->user_access_file != NULL) { + gdm_display_access_file_close (self->priv->user_access_file); + g_object_unref (self->priv->user_access_file); + self->priv->user_access_file = NULL; + } + + if (self->priv->access_file != NULL) { + gdm_display_access_file_close (self->priv->access_file); + g_object_unref (self->priv->access_file); + self->priv->access_file = NULL; + } + +- elapsed = g_timer_elapsed (self->priv->server_timer, NULL); +- if (elapsed < 3) { +- g_warning ("GdmDisplay: display lasted %lf seconds", elapsed); ++ if (!self->priv->session_registered) { ++ g_warning ("GdmDisplay: Session never registered, failing"); + _gdm_display_set_status (self, GDM_DISPLAY_FAILED); + } else { + _gdm_display_set_status (self, GDM_DISPLAY_UNMANAGED); + } + + return TRUE; + } + + gboolean + gdm_display_get_id (GdmDisplay *self, + char **id, + GError **error) + { + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + if (id != NULL) { + *id = g_strdup (self->priv->id); + } + + return TRUE; + } + + gboolean + gdm_display_get_x11_display_name (GdmDisplay *self, + char **x11_display, + GError **error) + { + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + if (x11_display != NULL) { +@@ -898,61 +890,61 @@ gdm_display_get_property (GObject *object, + case PROP_X11_DISPLAY_NAME: + g_value_set_string (value, self->priv->x11_display_name); + break; + case PROP_X11_COOKIE: + g_value_set_string (value, self->priv->x11_cookie); + break; + case PROP_X11_AUTHORITY_FILE: + g_value_take_string (value, + self->priv->access_file? + gdm_display_access_file_get_path (self->priv->access_file) : NULL); + break; + case PROP_IS_LOCAL: + g_value_set_boolean (value, self->priv->is_local); + break; + case PROP_IS_CONNECTED: + g_value_set_boolean (value, self->priv->xcb_connection != NULL); + break; + case PROP_LAUNCH_ENVIRONMENT: + g_value_set_object (value, self->priv->launch_environment); + break; + case PROP_IS_INITIAL: + g_value_set_boolean (value, self->priv->is_initial); + break; + case PROP_HAVE_EXISTING_USER_ACCOUNTS: + g_value_set_boolean (value, self->priv->have_existing_user_accounts); + break; + case PROP_DOING_INITIAL_SETUP: + g_value_set_boolean (value, self->priv->doing_initial_setup); + break; + case PROP_SESSION_REGISTERED: +- g_value_set_boolean (value, priv->session_registered); ++ g_value_set_boolean (value, self->priv->session_registered); + break; + case PROP_ALLOW_TIMED_LOGIN: + g_value_set_boolean (value, self->priv->allow_timed_login); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + + static gboolean + handle_get_id (GdmDBusDisplay *skeleton, + GDBusMethodInvocation *invocation, + GdmDisplay *self) + { + char *id; + + gdm_display_get_id (self, &id, NULL); + + gdm_dbus_display_complete_get_id (skeleton, invocation, id); + + g_free (id); + return TRUE; + } + + static gboolean + handle_get_remote_hostname (GdmDBusDisplay *skeleton, + GDBusMethodInvocation *invocation, + GdmDisplay *self) + { +@@ -1251,99 +1243,94 @@ gdm_display_class_init (GdmDisplayClass *klass) + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_LAUNCH_ENVIRONMENT, + g_param_spec_object ("launch-environment", + NULL, + NULL, + GDM_TYPE_LAUNCH_ENVIRONMENT, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_STATUS, + g_param_spec_int ("status", + "status", + "status", + -1, + G_MAXINT, + GDM_DISPLAY_UNMANAGED, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GdmDisplayPrivate)); + } + + static void + gdm_display_init (GdmDisplay *self) + { + + self->priv = GDM_DISPLAY_GET_PRIVATE (self); + + self->priv->creation_time = time (NULL); +- self->priv->server_timer = g_timer_new (); + } + + static void + gdm_display_finalize (GObject *object) + { + GdmDisplay *self; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_DISPLAY (object)); + + self = GDM_DISPLAY (object); + + g_return_if_fail (self->priv != NULL); + + g_debug ("GdmDisplay: Finalizing display: %s", self->priv->id); + g_free (self->priv->id); + g_free (self->priv->seat_id); + g_free (self->priv->session_class); + g_free (self->priv->remote_hostname); + g_free (self->priv->x11_display_name); + g_free (self->priv->x11_cookie); + + g_clear_object (&self->priv->display_skeleton); + g_clear_object (&self->priv->object_skeleton); + g_clear_object (&self->priv->connection); + + if (self->priv->access_file != NULL) { + g_object_unref (self->priv->access_file); + } + + if (self->priv->user_access_file != NULL) { + g_object_unref (self->priv->user_access_file); + } + +- if (self->priv->server_timer != NULL) { +- g_timer_destroy (self->priv->server_timer); +- } +- + G_OBJECT_CLASS (gdm_display_parent_class)->finalize (object); + } + + GDBusObjectSkeleton * + gdm_display_get_object_skeleton (GdmDisplay *self) + { + return self->priv->object_skeleton; + } + + static void + on_launch_environment_session_opened (GdmLaunchEnvironment *launch_environment, + GdmDisplay *self) + { + char *session_id; + + g_debug ("GdmDisplay: Greeter session opened"); + session_id = gdm_launch_environment_get_session_id (launch_environment); + _gdm_display_set_session_id (self, session_id); + g_free (session_id); + } + + static void + on_launch_environment_session_started (GdmLaunchEnvironment *launch_environment, + GdmDisplay *self) + { + g_debug ("GdmDisplay: Greeter started"); + } + + static void + self_destruct (GdmDisplay *self) +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index aae226750..35880563d 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -39,61 +39,60 @@ + #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) + 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) + + GQuark + gdm_local_display_factory_error_quark (void) +@@ -276,60 +275,79 @@ gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *fact + 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_session_registered_cb (GObject *gobject, ++ GParamSpec *pspec, ++ gpointer user_data) ++{ ++ GdmDisplay *display = GDM_DISPLAY (gobject); ++ GdmLocalDisplayFactory *factory = GDM_LOCAL_DISPLAY_FACTORY (user_data); ++ gboolean registered; ++ ++ g_object_get (display, "session-registered", ®istered, NULL); ++ ++ if (!registered) ++ return; ++ ++ g_debug ("GdmLocalDisplayFactory: session registered on display, looking for any background displays to kill"); ++ ++ finish_waiting_displays_on_seat (factory, "seat0"); ++} ++ + 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 +@@ -359,60 +377,67 @@ 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 + */ + g_usleep (2 * G_USEC_PER_SEC); + } + + #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: ++#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER) ++ g_signal_connect_object (display, ++ "notify::session-registered", ++ G_CALLBACK (on_session_registered_cb), ++ factory, ++ 0); ++#endif + 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; + } +@@ -587,100 +612,83 @@ 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 (GdmLocalDisplayFactory *factory, + GdmDisplay *display) + { + 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), + "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; + } + + 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) { + 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; +@@ -722,61 +730,60 @@ on_vt_changed (GIOChannel *source, + factory->priv->active_vt = new_vt; + + /* don't do anything at start up */ + 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 %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 login_window_vt; + + 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 (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) +@@ -805,64 +812,60 @@ 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; + } + #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) +diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c +index 5b76ba129..540a2534d 100644 +--- a/daemon/gdm-session.c ++++ b/daemon/gdm-session.c +@@ -2849,132 +2849,139 @@ on_start_program_cb (GdmDBusWorker *worker, + + self = conversation->session; + service_name = conversation->service_name; + + if (worked) { + self->priv->session_pid = pid; + self->priv->session_conversation = conversation; + + g_debug ("GdmSession: Emitting 'session-started' signal with pid '%d'", pid); + g_signal_emit (self, signals[SESSION_STARTED], 0, service_name, pid); + } else { + gdm_session_stop_conversation (self, service_name); + + g_debug ("GdmSession: Emitting 'session-start-failed' signal"); + g_signal_emit (self, signals[SESSION_START_FAILED], 0, service_name, error->message); + } + } + + void + gdm_session_start_session (GdmSession *self, + const char *service_name) + { + GdmSessionConversation *conversation; + GdmSessionDisplayMode display_mode; + gboolean is_x11 = TRUE; + gboolean run_launcher = FALSE; + gboolean allow_remote_connections = FALSE; + gboolean run_separate_bus = FALSE; + char *command; + char *program; ++ gboolean register_session; + + g_return_if_fail (GDM_IS_SESSION (self)); + g_return_if_fail (self->priv->session_conversation == NULL); + + conversation = find_conversation_by_name (self, service_name); + + if (conversation == NULL) { + g_warning ("GdmSession: Tried to start session of " + "nonexistent conversation %s", service_name); + return; + } + + stop_all_other_conversations (self, conversation, FALSE); + + display_mode = gdm_session_get_display_mode (self); + + #ifdef ENABLE_WAYLAND_SUPPORT + is_x11 = g_strcmp0 (self->priv->session_type, "wayland") != 0; + #endif + + if (display_mode == GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED || + display_mode == GDM_SESSION_DISPLAY_MODE_NEW_VT) { + run_launcher = TRUE; + } + + if (g_strcmp0 (self->priv->display_seat_id, "seat0") != 0 && !run_launcher) { + run_separate_bus = TRUE; + } + ++ register_session = !gdm_session_session_registers (self); ++ + if (self->priv->selected_program == NULL) { + gboolean run_xsession_script; + + command = get_session_command (self); + + run_xsession_script = !gdm_session_bypasses_xsession (self); + + if (self->priv->display_is_local) { + gboolean disallow_tcp = TRUE; + gdm_settings_direct_get_boolean (GDM_KEY_DISALLOW_TCP, &disallow_tcp); + allow_remote_connections = !disallow_tcp; + } else { + allow_remote_connections = TRUE; + } + + if (run_launcher) { + if (is_x11) { +- program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s %s\"%s\"", ++ program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s%s %s\"%s\"", ++ register_session ? "--register-session " : "", + run_xsession_script? "--run-script " : "", + allow_remote_connections? "--allow-remote-connections " : "", + command); + } else { +- program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session \"%s\"", ++ program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session %s\"%s\"", ++ register_session ? "--register-session " : "", + command); + } + } else if (run_xsession_script) { + if (run_separate_bus) { + program = g_strdup_printf ("dbus-run-session -- " GDMCONFDIR "/Xsession \"%s\"", command); + } else { + program = g_strdup_printf (GDMCONFDIR "/Xsession \"%s\"", command); + } + } else { + program = g_strdup (command); + } + + g_free (command); + } else { + if (run_launcher) { + if (is_x11) { +- program = g_strdup_printf (LIBEXECDIR "/gdm-x-session \"%s\"", ++ program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s\"%s\"", ++ register_session ? "--register-session " : "", + self->priv->selected_program); + } else { +- program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session \"%s\"", ++ program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session %s\"%s\"", ++ register_session ? "--register-session " : "", + self->priv->selected_program); + } + } else { + if (run_separate_bus) { + program = g_strdup_printf ("dbus-run-session -- %s", + self->priv->selected_program); + } else { + program = g_strdup (self->priv->selected_program); + } + } + } + + set_up_session_environment (self); + send_environment (self, conversation); + + gdm_dbus_worker_call_start_program (conversation->worker_proxy, + program, + conversation->worker_cancellable, + (GAsyncReadyCallback) on_start_program_cb, + conversation); + g_free (program); + } + + static void + stop_all_conversations (GdmSession *self) + { + stop_all_other_conversations (self, NULL, TRUE); + } + + static void +@@ -3199,60 +3206,92 @@ gdm_session_is_wayland_session (GdmSession *self) + } + + if (full_path != NULL && strstr (full_path, "/wayland-sessions/") != NULL) { + is_wayland_session = TRUE; + } + } + + out: + g_debug ("GdmSession: checking if file '%s' is wayland session: %s", filename, is_wayland_session? "yes" : "no"); + + return is_wayland_session; + } + #endif + + static void + update_session_type (GdmSession *self) + { + #ifdef ENABLE_WAYLAND_SUPPORT + gboolean is_wayland_session = FALSE; + + is_wayland_session = gdm_session_is_wayland_session (self); + + if (is_wayland_session) { + set_session_type (self, "wayland"); + } else { + set_session_type (self, NULL); + } + #endif + } + ++gboolean ++gdm_session_session_registers (GdmSession *self) ++{ ++ g_autoptr(GError) error = NULL; ++ g_autoptr(GKeyFile) key_file = NULL; ++ gboolean session_registers = FALSE; ++ g_autofree char *filename = NULL; ++ ++ g_return_val_if_fail (self != NULL, FALSE); ++ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE); ++ ++ filename = get_session_filename (self); ++ ++ key_file = load_key_file_for_file (self, filename, NULL, NULL); ++ ++ session_registers = g_key_file_get_boolean (key_file, ++ G_KEY_FILE_DESKTOP_GROUP, ++ "X-GDM-SessionRegisters", ++ &error); ++ if (!session_registers && ++ error != NULL && ++ !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) { ++ g_warning ("GdmSession: Couldn't read session file '%s'", filename); ++ return FALSE; ++ } ++ ++ g_debug ("GdmSession: '%s' %s self", filename, ++ session_registers ? "registers" : "does not register"); ++ ++ return session_registers; ++} ++ + gboolean + gdm_session_bypasses_xsession (GdmSession *self) + { + GError *error; + GKeyFile *key_file; + gboolean res; + gboolean bypasses_xsession = FALSE; + char *filename = NULL; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (GDM_IS_SESSION (self), FALSE); + + #ifdef ENABLE_WAYLAND_SUPPORT + if (gdm_session_is_wayland_session (self)) { + bypasses_xsession = TRUE; + goto out; + } + #endif + + filename = get_session_filename (self); + + key_file = load_key_file_for_file (self, filename, "x11", NULL); + + error = NULL; + res = g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-BypassXsession", NULL); + if (!res) { + goto out; + } else { + bypasses_xsession = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-BypassXsession", &error); + if (error) { +diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h +index a22c09543..682d2c99f 100644 +--- a/daemon/gdm-session.h ++++ b/daemon/gdm-session.h +@@ -108,60 +108,61 @@ typedef struct + void (* conversation_started) (GdmSession *session, + const char *service_name); + void (* conversation_stopped) (GdmSession *session, + const char *service_name); + void (* setup_complete) (GdmSession *session, + const char *service_name); + } GdmSessionClass; + + GType gdm_session_get_type (void); + + GdmSession *gdm_session_new (GdmSessionVerificationMode verification_mode, + uid_t allowed_user, + const char *display_name, + const char *display_hostname, + const char *display_device, + const char *display_seat_id, + const char *display_x11_authority_file, + gboolean display_is_local, + const char * const *environment); + uid_t gdm_session_get_allowed_user (GdmSession *session); + void gdm_session_start_reauthentication (GdmSession *session, + GPid pid_of_caller, + uid_t uid_of_caller); + + const char *gdm_session_get_server_address (GdmSession *session); + const char *gdm_session_get_username (GdmSession *session); + const char *gdm_session_get_display_device (GdmSession *session); + const char *gdm_session_get_display_seat_id (GdmSession *session); + const char *gdm_session_get_session_id (GdmSession *session); + gboolean gdm_session_bypasses_xsession (GdmSession *session); ++gboolean gdm_session_session_registers (GdmSession *session); + GdmSessionDisplayMode gdm_session_get_display_mode (GdmSession *session); + + #ifdef ENABLE_WAYLAND_SUPPORT + void gdm_session_set_ignore_wayland (GdmSession *session, + gboolean ignore_wayland); + #endif + gboolean gdm_session_start_conversation (GdmSession *session, + const char *service_name); + void gdm_session_stop_conversation (GdmSession *session, + const char *service_name); + const char *gdm_session_get_conversation_session_id (GdmSession *session, + const char *service_name); + void gdm_session_setup (GdmSession *session, + const char *service_name); + void gdm_session_setup_for_user (GdmSession *session, + const char *service_name, + const char *username); + void gdm_session_setup_for_program (GdmSession *session, + const char *service_name, + const char *username, + const char *log_file); + void gdm_session_set_environment_variable (GdmSession *session, + const char *key, + const char *value); + void gdm_session_send_environment (GdmSession *self, + const char *service_name); + void gdm_session_reset (GdmSession *session); + void gdm_session_cancel (GdmSession *session); + void gdm_session_authenticate (GdmSession *session, + const char *service_name); +diff --git a/daemon/gdm-wayland-session.c b/daemon/gdm-wayland-session.c +index 94f49e19c..35679b194 100644 +--- a/daemon/gdm-wayland-session.c ++++ b/daemon/gdm-wayland-session.c +@@ -18,68 +18,71 @@ + * 02110-1301, USA. + */ + #include "config.h" + + #include + #include + + #include "gdm-common.h" + #include "gdm-settings-direct.h" + #include "gdm-settings-keys.h" + #include "gdm-log.h" + + #include "gdm-manager-glue.h" + + #include + #include + #include + #include + + #include + + #define BUS_ADDRESS_FILENO (STDERR_FILENO + 1) + + typedef struct + { + GdmSettings *settings; + GCancellable *cancellable; + + GSubprocess *bus_subprocess; + GDBusConnection *bus_connection; ++ GdmDBusManager *display_manager_proxy; + char *bus_address; + + char **environment; + + GSubprocess *session_subprocess; + char *session_command; + int session_exit_status; + ++ guint register_session_id; ++ + GMainLoop *main_loop; + + guint32 debug_enabled : 1; + } State; + + static void + on_bus_finished (GSubprocess *subprocess, + GAsyncResult *result, + State *state) + { + gboolean cancelled; + + cancelled = !g_subprocess_wait_finish (subprocess, result, NULL); + + if (cancelled) { + goto out; + } + + if (g_subprocess_get_if_exited (subprocess)) { + int exit_status; + + exit_status = g_subprocess_get_exit_status (subprocess); + + g_debug ("message bus exited with status %d", exit_status); + } else { + int signal_number; + + signal_number = g_subprocess_get_term_sig (subprocess); + g_debug ("message bus was killed with status %d", signal_number); + } +@@ -358,140 +361,170 @@ out: + } + + static void + signal_subprocesses (State *state) + { + if (state->session_subprocess != NULL) { + g_subprocess_send_signal (state->session_subprocess, SIGTERM); + } + + if (state->bus_subprocess != NULL) { + g_subprocess_send_signal (state->bus_subprocess, SIGTERM); + } + } + + static void + wait_on_subprocesses (State *state) + { + if (state->bus_subprocess != NULL) { + g_subprocess_wait (state->bus_subprocess, NULL, NULL); + } + + if (state->session_subprocess != NULL) { + g_subprocess_wait (state->session_subprocess, NULL, NULL); + } + } + + static gboolean + register_display (State *state, + GCancellable *cancellable) + { +- GdmDBusManager *manager = NULL; + GError *error = NULL; + gboolean registered = FALSE; + GVariantBuilder details; + +- manager = gdm_dbus_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, +- G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | +- G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, +- "org.gnome.DisplayManager", +- "/org/gnome/DisplayManager/Manager", +- cancellable, +- &error); +- +- if (!manager) { +- g_debug ("could not contact display manager: %s", error->message); +- g_error_free (error); +- goto out; +- } +- + g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); + g_variant_builder_add (&details, "{ss}", "session-type", "wayland"); + +- registered = gdm_dbus_manager_call_register_display_sync (manager, ++ registered = gdm_dbus_manager_call_register_display_sync (state->display_manager_proxy, + g_variant_builder_end (&details), + cancellable, + &error); + if (error != NULL) { + g_debug ("Could not register display: %s", error->message); + g_error_free (error); + } + +-out: +- g_clear_object (&manager); + return registered; + } + + static void + 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); ++ g_clear_handle_id (&state->register_session_id, g_source_remove); + *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 ++register_session_timeout_cb (gpointer user_data) ++{ ++ State *state; ++ GError *error = NULL; ++ ++ state = (State *) user_data; ++ ++ gdm_dbus_manager_call_register_session_sync (state->display_manager_proxy, ++ g_variant_new ("a{sv}", NULL), ++ state->cancellable, ++ &error); ++ ++ if (error != NULL) { ++ g_warning ("Could not register session: %s", error->message); ++ g_error_free (error); ++ } ++ ++ return G_SOURCE_REMOVE; ++} ++ ++static gboolean ++connect_to_display_manager (State *state) ++{ ++ g_autoptr (GError) error = NULL; ++ ++ state->display_manager_proxy = gdm_dbus_manager_proxy_new_for_bus_sync ( ++ G_BUS_TYPE_SYSTEM, ++ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, ++ "org.gnome.DisplayManager", ++ "/org/gnome/DisplayManager/Manager", ++ state->cancellable, ++ &error); ++ ++ if (state->display_manager_proxy == NULL) { ++ g_printerr ("gdm-wayland-session: could not contact display manager: %s\n", ++ error->message); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + 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 gboolean register_session = FALSE; ++ + static GOptionEntry entries [] = { ++ { "register-session", 0, 0, G_OPTION_ARG_NONE, ®ister_session, "Register session after a delay", 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 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; + goto out; + } + + init_state (&state); + + state->session_command = args[0]; + + state->settings = gdm_settings_new (); + ret = gdm_settings_direct_init (state->settings, DATADIR "/gdm/gdm.schemas", "/"); + + if (!ret) { +@@ -501,55 +534,67 @@ 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; + } + ++ if (!connect_to_display_manager (state)) ++ 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; + } + ++ if (register_session) { ++ g_debug ("gdm-wayland-session: Will register session in %d seconds", REGISTER_SESSION_TIMEOUT); ++ state->register_session_id = g_timeout_add_seconds (REGISTER_SESSION_TIMEOUT, ++ register_session_timeout_cb, ++ state); ++ } else { ++ g_debug ("gdm-wayland-session: Session will register itself"); ++ } ++ + 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 d8e3c7d53..f0082fdc6 100644 +--- a/daemon/gdm-x-session.c ++++ b/daemon/gdm-x-session.c +@@ -24,68 +24,71 @@ + + #include "gdm-common.h" + #include "gdm-settings-direct.h" + #include "gdm-settings-keys.h" + #include "gdm-log.h" + + #include "gdm-manager-glue.h" + + #include + #include + #include + #include + #include + #include + #include + + #define DISPLAY_FILENO (STDERR_FILENO + 1) + #define BUS_ADDRESS_FILENO (DISPLAY_FILENO + 1) + + typedef struct + { + GdmSettings *settings; + GCancellable *cancellable; + + GSubprocess *x_subprocess; + char *auth_file; + char *display_name; + + GSubprocess *bus_subprocess; + GDBusConnection *bus_connection; ++ GdmDBusManager *display_manager_proxy; + char *bus_address; + + char **environment; + + GSubprocess *session_subprocess; + char *session_command; + int session_exit_status; + ++ guint register_session_id; ++ + GMainLoop *main_loop; + + guint32 debug_enabled : 1; + } State; + + static FILE * + create_auth_file (char **filename) + { + char *auth_dir = NULL; + char *auth_file = NULL; + int fd; + FILE *fp = NULL; + + auth_dir = g_build_filename (g_get_user_runtime_dir (), + "gdm", + NULL); + + g_mkdir_with_parents (auth_dir, 0711); + auth_file = g_build_filename (auth_dir, "Xauthority", NULL); + g_clear_pointer (&auth_dir, g_free); + + fd = g_open (auth_file, O_RDWR | O_CREAT | O_TRUNC, 0700); + + if (fd < 0) { + g_debug ("could not open %s to store auth cookie: %m", + auth_file); + g_clear_pointer (&auth_file, g_free); + goto out; + } + +@@ -711,148 +714,178 @@ signal_subprocesses (State *state) + + if (state->bus_subprocess != NULL) { + g_subprocess_send_signal (state->bus_subprocess, SIGTERM); + } + + if (state->x_subprocess != NULL) { + g_subprocess_send_signal (state->x_subprocess, SIGTERM); + } + } + + static void + wait_on_subprocesses (State *state) + { + if (state->x_subprocess != NULL) { + g_subprocess_wait (state->x_subprocess, NULL, NULL); + } + + if (state->bus_subprocess != NULL) { + g_subprocess_wait (state->bus_subprocess, NULL, NULL); + } + + if (state->session_subprocess != NULL) { + g_subprocess_wait (state->session_subprocess, NULL, NULL); + } + } + + static gboolean + register_display (State *state, + GCancellable *cancellable) + { +- GdmDBusManager *manager = NULL; + GError *error = NULL; + gboolean registered = FALSE; + GVariantBuilder details; + +- manager = gdm_dbus_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, +- G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | +- G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, +- "org.gnome.DisplayManager", +- "/org/gnome/DisplayManager/Manager", +- cancellable, +- &error); +- +- if (!manager) { +- g_debug ("could not contact display manager: %s", error->message); +- g_error_free (error); +- goto out; +- } +- + g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); + g_variant_builder_add (&details, "{ss}", "session-type", "x11"); + g_variant_builder_add (&details, "{ss}", "x11-display-name", state->display_name); + +- registered = gdm_dbus_manager_call_register_display_sync (manager, ++ registered = gdm_dbus_manager_call_register_display_sync (state->display_manager_proxy, + g_variant_builder_end (&details), + cancellable, + &error); + if (error != NULL) { + g_debug ("Could not register display: %s", error->message); + g_error_free (error); + } + +-out: +- g_clear_object (&manager); + return registered; + } + + static void + 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_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); ++ g_clear_handle_id (&state->register_session_id, g_source_remove); + *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 ++register_session_timeout_cb (gpointer user_data) ++{ ++ State *state; ++ GError *error = NULL; ++ ++ state = (State *) user_data; ++ ++ gdm_dbus_manager_call_register_session_sync (state->display_manager_proxy, ++ g_variant_new ("a{sv}", NULL), ++ state->cancellable, ++ &error); ++ ++ if (error != NULL) { ++ g_warning ("Could not register session: %s", error->message); ++ g_error_free (error); ++ } ++ ++ return G_SOURCE_REMOVE; ++} ++ ++static gboolean ++connect_to_display_manager (State *state) ++{ ++ g_autoptr (GError) error = NULL; ++ ++ state->display_manager_proxy = gdm_dbus_manager_proxy_new_for_bus_sync ( ++ G_BUS_TYPE_SYSTEM, ++ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, ++ "org.gnome.DisplayManager", ++ "/org/gnome/DisplayManager/Manager", ++ state->cancellable, ++ &error); ++ ++ if (state->display_manager_proxy == NULL) { ++ g_printerr ("gdm-x-session: could not contact display manager: %s\n", ++ error->message); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + 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 gboolean register_session = FALSE; ++ + 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 }, ++ { "register-session", 0, 0, G_OPTION_ARG_NONE, ®ister_session, "Register session after a delay", 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); + + if (args == NULL || args[0] == NULL || args[1] != NULL) { + g_warning ("gdm-x-session takes one argument (the session)"); + exit_status = EX_USAGE; + goto out; + } + + init_state (&state); + + state->session_command = args[0]; + + state->settings = gdm_settings_new (); + ret = gdm_settings_direct_init (state->settings, DATADIR "/gdm/gdm.schemas", "/"); + + if (!ret) { +@@ -870,63 +903,75 @@ 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; + } + ++ if (!connect_to_display_manager (state)) ++ 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; + } + ++ if (register_session) { ++ g_debug ("gdm-x-session: Will register session in %d seconds", REGISTER_SESSION_TIMEOUT); ++ state->register_session_id = g_timeout_add_seconds (REGISTER_SESSION_TIMEOUT, ++ register_session_timeout_cb, ++ state); ++ } else { ++ g_debug ("gdm-x-session: Session will register itself"); ++ } ++ + 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.27.0 + diff --git a/SOURCES/0051-display-Handle-failure-before-display-registration.patch b/SOURCES/0051-display-Handle-failure-before-display-registration.patch new file mode 100644 index 0000000..1e4c7c0 --- /dev/null +++ b/SOURCES/0051-display-Handle-failure-before-display-registration.patch @@ -0,0 +1,173 @@ +From f6a8a36717afc7ce00bdb2305a6219c28abc36fb Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 1 Sep 2020 13:49:27 -0400 +Subject: [PATCH 51/51] display: Handle failure before display registration + +Normally, e.g., gdm-wayland-session would register its display +before starting the session. This display registration is how +the display moves to the "managed" state. We currently detect +session failure in gdm_display_unmanage. If gdm-wayland-session +is killed before it registers the display, gdm_display_unmanage +won't run, and failure won't be detected. + +This commit make gdm_display_unmanage get called, even if the +display isn't yet fully managed. +--- + daemon/gdm-display.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c +index ae20491cd..b8ccbbd72 100644 +--- a/daemon/gdm-display.c ++++ b/daemon/gdm-display.c +@@ -575,80 +575,79 @@ gdm_display_disconnect (GdmDisplay *self) + return; + } + + setup = xcb_get_setup (self->priv->xcb_connection); + + /* resource_id_mask is the bits given to each client for + * addressing resources */ + highest_client = (XID) ~unused_bits & ~setup->resource_id_mask; + client_increment = setup->resource_id_mask + 1; + + /* Kill every client but ourselves, then close our own connection + */ + for (client = 0; + client <= highest_client; + client += client_increment) { + + if (client != setup->resource_id_base) + xcb_kill_client (self->priv->xcb_connection, client); + } + + xcb_flush (self->priv->xcb_connection); + + g_clear_pointer (&self->priv->xcb_connection, xcb_disconnect); + } + + gboolean + gdm_display_unmanage (GdmDisplay *self) + { + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + +- g_debug ("GdmDisplay: unmanage display"); +- + gdm_display_disconnect (self); + + if (self->priv->user_access_file != NULL) { + gdm_display_access_file_close (self->priv->user_access_file); + g_object_unref (self->priv->user_access_file); + self->priv->user_access_file = NULL; + } + + if (self->priv->access_file != NULL) { + gdm_display_access_file_close (self->priv->access_file); + g_object_unref (self->priv->access_file); + self->priv->access_file = NULL; + } + + if (!self->priv->session_registered) { + g_warning ("GdmDisplay: Session never registered, failing"); + _gdm_display_set_status (self, GDM_DISPLAY_FAILED); + } else { ++ g_debug ("GdmDisplay: Unmanage display"); + _gdm_display_set_status (self, GDM_DISPLAY_UNMANAGED); + } + + return TRUE; + } + + gboolean + gdm_display_get_id (GdmDisplay *self, + char **id, + GError **error) + { + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + if (id != NULL) { + *id = g_strdup (self->priv->id); + } + + return TRUE; + } + + gboolean + gdm_display_get_x11_display_name (GdmDisplay *self, + char **x11_display, + GError **error) + { + g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE); + + if (x11_display != NULL) { + *x11_display = g_strdup (self->priv->x11_display_name); + } +@@ -1309,63 +1308,62 @@ gdm_display_finalize (GObject *object) + + GDBusObjectSkeleton * + gdm_display_get_object_skeleton (GdmDisplay *self) + { + return self->priv->object_skeleton; + } + + static void + on_launch_environment_session_opened (GdmLaunchEnvironment *launch_environment, + GdmDisplay *self) + { + char *session_id; + + g_debug ("GdmDisplay: Greeter session opened"); + session_id = gdm_launch_environment_get_session_id (launch_environment); + _gdm_display_set_session_id (self, session_id); + g_free (session_id); + } + + static void + on_launch_environment_session_started (GdmLaunchEnvironment *launch_environment, + GdmDisplay *self) + { + g_debug ("GdmDisplay: Greeter started"); + } + + static void + self_destruct (GdmDisplay *self) + { + g_object_ref (self); +- if (gdm_display_get_status (self) == GDM_DISPLAY_MANAGED) { +- gdm_display_unmanage (self); +- } ++ g_debug ("GdmDisplay: initiating display self-destruct"); ++ gdm_display_unmanage (self); + + if (gdm_display_get_status (self) != GDM_DISPLAY_FINISHED) { + queue_finish (self); + } + g_object_unref (self); + } + + static void + on_launch_environment_session_stopped (GdmLaunchEnvironment *launch_environment, + GdmDisplay *self) + { + g_debug ("GdmDisplay: Greeter stopped"); + self_destruct (self); + } + + static void + on_launch_environment_session_exited (GdmLaunchEnvironment *launch_environment, + int code, + GdmDisplay *self) + { + g_debug ("GdmDisplay: Greeter exited: %d", code); + self_destruct (self); + } + + static void + on_launch_environment_session_died (GdmLaunchEnvironment *launch_environment, + int signal, + GdmDisplay *self) + { + g_debug ("GdmDisplay: Greeter died: %d", signal); +-- +2.27.0 + diff --git a/SPECS/gdm.spec b/SPECS/gdm.spec index eda8f91..5679bd8 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: 39%{?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,72 @@ 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 +Patch200049: 0049-GdmManager-GdmDisplay-Add-RegisterSession-method.patch +Patch200050: 0050-Allow-sessions-to-register-with-GDM.patch +Patch200051: 0051-display-Handle-failure-before-display-registration.patch + +# CVE-2020-16125 +Patch210001: 0001-display-Exit-with-failure-if-loading-existing-users-.patch + +# CVE-2020-27837 +Patch220001: 0001-session-worker-Don-t-switch-back-VTs-until-session-i.patch + +Patch300001: 0001-manager-Don-t-leak-session-objects.patch +Patch300002: 0002-session-Don-t-leak-remote-greeter-interface.patch +Patch300003: 0003-xdmcp-display-factory-Clear-launch-environment-when-.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 +179,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 +275,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 @@ -303,6 +369,8 @@ fi %dir %{_sysconfdir}/gdm/PreSession %dir %{_sysconfdir}/gdm/PostSession %dir %{_sysconfdir}/gdm/PostLogin +%dir %{_sysconfdir}/dconf/db/gdm.d +%dir %{_sysconfdir}/dconf/db/gdm.d/locks %{_datadir}/pixmaps/*.png %{_datadir}/glib-2.0/schemas/org.gnome.login-screen.gschema.xml %{_datadir}/glib-2.0/schemas/org.gnome.login-screen.gschema.override @@ -357,6 +425,39 @@ fi %{_libdir}/pkgconfig/gdm-pam-extensions.pc %changelog +* Wed Jan 27 2021 Ray Strode - 3.28.3-39 +- Ensure login screen display server is is killed at log in +- Pull in fixes for two security issues + Resolves: #1918391 + +* Tue Nov 10 2020 Jonas Ådahl - 3.28.3-38 +- Re-add disabling Wayland for server GPUs + Related: #1670273 + +* Tue Nov 10 2020 Jonas Ådahl - 3.28.3-35 +- Stop disabling Wayland for server GPUs + Related: #1670273 + +* Tue Sep 15 2020 Ray Strode - 3.28.3-34 +- Fix file descriptor leak + Resolves: #1877853 + +* Tue Sep 01 2020 Ray Strode - 3.28.3-33 +- Fix problem with Xorg fallback + Resolves: #1868260 + +* Tue Aug 11 2020 Ray Strode - 3.28.3-32 +- Add dconf db to file manifest + Related: #1833158 + +* 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