Blob Blame History Raw
From cb356d992cec51cb87e2243edadec6a3d3348bd7 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 19 Jul 2016 16:54:43 -0400
Subject: [PATCH 1/3] xdmcp-display-factory: notify remote display when session
 ended

gnome-shell and the session dbus daemon don't automatically exit
when gnome-session does.  They, instead, wait for the display to
exit or regenerate. If the display is remote, that won't happen
until the keep alive timeout.

This commit changes GDM to explicitly notify the remote display
when the session is over, so that it can regenerate immediately.
---
 daemon/gdm-xdmcp-display-factory.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/daemon/gdm-xdmcp-display-factory.c b/daemon/gdm-xdmcp-display-factory.c
index d01f7ff..4132dd1 100644
--- a/daemon/gdm-xdmcp-display-factory.c
+++ b/daemon/gdm-xdmcp-display-factory.c
@@ -183,61 +183,64 @@ struct GdmXdmcpDisplayFactoryPrivate
         /* configuration */
         guint            port;
         gboolean         use_multicast;
         char            *multicast_address;
         gboolean         honor_indirect;
         char            *willing_script;
         guint            max_displays_per_host;
         guint            max_displays;
         guint            max_pending_displays;
         guint            max_wait;
         guint            max_wait_indirect;
 };
 
 enum {
         PROP_0,
         PROP_PORT,
         PROP_USE_MULTICAST,
         PROP_MULTICAST_ADDRESS,
         PROP_HONOR_INDIRECT,
         PROP_WILLING_SCRIPT,
         PROP_MAX_DISPLAYS_PER_HOST,
         PROP_MAX_DISPLAYS,
         PROP_MAX_PENDING_DISPLAYS,
         PROP_MAX_WAIT,
         PROP_MAX_WAIT_INDIRECT,
 };
 
 static void     gdm_xdmcp_display_factory_class_init    (GdmXdmcpDisplayFactoryClass *klass);
 static void     gdm_xdmcp_display_factory_init          (GdmXdmcpDisplayFactory      *manager);
 static void     gdm_xdmcp_display_factory_finalize      (GObject                     *object);
-
+static void     gdm_xdmcp_send_alive (GdmXdmcpDisplayFactory *factory,
+                                      GdmAddress             *address,
+                                      CARD16                  dspnum,
+                                      CARD32                  sessid);
 static gpointer xdmcp_display_factory_object = NULL;
 
 G_DEFINE_TYPE (GdmXdmcpDisplayFactory, gdm_xdmcp_display_factory, GDM_TYPE_DISPLAY_FACTORY)
 
 /* Theory of operation:
  *
  * Process idles waiting for UDP packets on port 177.
  * Incoming packets are decoded and checked against tcp_wrapper.
  *
  * A typical session looks like this:
  *
  * Display sends Query/BroadcastQuery to Manager.
  *
  * Manager selects an appropriate authentication scheme from the
  * display's list of supported ones and sends Willing/Unwilling.
  *
  * Assuming the display accepts the auth. scheme it sends back a
  * Request.
  *
  * If the manager accepts to service the display (i.e. loadavg is low)
  * it sends back an Accept containing a unique SessionID. The
  * SessionID is stored in an accept queue by the Manager. Should the
  * manager refuse to start a session a Decline is sent to the display.
  *
  * The display returns a Manage request containing the supplied
  * SessionID. The manager will then start a session on the display. In
  * case the SessionID is not on the accept queue the manager returns
  * Refuse. If the manager fails to open the display for connections
  * Failed is returned.
  *
@@ -2026,68 +2029,78 @@ on_hostname_selected (GdmXdmcpChooserDisplay *display,
                 g_warning ("Unable to get address: %s", gai_strerror (gaierr));
                 g_free (xdmcp_port);
                 return;
         }
         g_free (xdmcp_port);
 
         /* just take the first one */
         ai = ai_list;
 
         if (ai != NULL) {
                 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_display_status_changed (GdmDisplay             *display,
                            GParamSpec             *arg1,
                            GdmXdmcpDisplayFactory *factory)
 {
         int              status;
         GdmDisplayStore *store;
+        GdmAddress *address;
+        gint32  session_number;
+        int display_number;
 
         store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
 
         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);
                 break;
         case GDM_DISPLAY_FAILED:
                 gdm_display_store_remove (store, display);
                 break;
         case GDM_DISPLAY_UNMANAGED:
                 break;
         case GDM_DISPLAY_PREPARED:
                 break;
         case GDM_DISPLAY_MANAGED:
                 break;
         default:
                 g_assert_not_reached ();
                 break;
         }
 }
 
 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);
 
-- 
2.7.4


From e35b483f4c32d4095ea0d67991fff1e2b8339f2c Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 20 Jul 2016 11:43:45 -0400
Subject: [PATCH 2/3] worker: kill process group when session exits

Send a hangup signal to the session pg when it exits, so things
have a chance to get cleaned up.
---
 daemon/gdm-session-worker.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index e07d32e..bc4a7ea 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -1603,60 +1603,62 @@ run_script (GdmSessionWorker *worker,
                                worker->priv->username,
                                worker->priv->x11_display_name,
                                worker->priv->display_is_local? NULL : worker->priv->hostname,
                                worker->priv->x11_authority_file);
 }
 
 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);
 
 #ifdef WITH_CONSOLE_KIT
         close_ck_session (worker);
 #endif
 
         gdm_session_worker_uninitialize_pam (worker, PAM_SUCCESS);
 
         gdm_dbus_worker_emit_session_exited (GDM_DBUS_WORKER (worker),
                                              worker->priv->service,
                                              status);
 
+        killpg (pid, SIGHUP);
+
         worker->priv->child_pid = -1;
 
         run_script (worker, GDMCONFDIR "/PostSession");
 }
 
 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)
-- 
2.7.4


From fa6bc4c99f49a9c6ccd5cf1728eabf71685c34d0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 20 Jul 2016 17:23:05 -0400
Subject: [PATCH 3/3] slave: close display when we're done with it.

When we're done with the display we need to kill off any clients
that are lingering and close our own connection to the display.
---
 configure.ac       |  2 +-
 daemon/gdm-slave.c | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index a131535..1ac2801 100644
--- a/configure.ac
+++ b/configure.ac
@@ -63,61 +63,61 @@ dnl ---------------------------------------------------------------------------
 GLIB_REQUIRED_VERSION=2.36.0
 GTK_REQUIRED_VERSION=2.91.1
 LIBCANBERRA_GTK_REQUIRED_VERSION=0.4
 ACCOUNTS_SERVICE_REQUIRED_VERSION=0.6.12
 
 EXTRA_COMPILE_WARNINGS(yes)
 
 PKG_CHECK_MODULES(GTHREAD, gthread-2.0)
 AC_SUBST(GTHREAD_CFLAGS)
 AC_SUBST(GTHREAD_LIBS)
 
 PKG_CHECK_MODULES(COMMON,
         gobject-2.0 >= $GLIB_REQUIRED_VERSION
         gio-2.0 >= $GLIB_REQUIRED_VERSION
         gio-unix-2.0 >= $GLIB_REQUIRED_VERSION
 )
 AC_SUBST(COMMON_CFLAGS)
 AC_SUBST(COMMON_LIBS)
 
 PKG_CHECK_MODULES(DAEMON,
         gobject-2.0 >= $GLIB_REQUIRED_VERSION
         gio-2.0 >= $GLIB_REQUIRED_VERSION
         gio-unix-2.0 >= $GLIB_REQUIRED_VERSION
         accountsservice >= $ACCOUNTS_SERVICE_REQUIRED_VERSION
 )
 AC_SUBST(DAEMON_CFLAGS)
 AC_SUBST(DAEMON_LIBS)
 
 GLIB_GSETTINGS
 
-PKG_CHECK_MODULES(XLIB, x11 xau xrandr, ,
+PKG_CHECK_MODULES(XLIB, x11 xau xrandr x11-xcb, ,
   [AC_PATH_XTRA
     if test "x$no_x" = xyes; then
       AC_MSG_ERROR("no (requires X development libraries)")
     else
       XLIB_LIBS="$X_PRE_LIBS $X_LIBS -lXau -lX11 -lXext -lXrandr $X_EXTRA_LIBS"
       XLIB_CFLAGS=$X_CFLAGS
     fi])
 AC_SUBST(XLIB_CFLAGS)
 AC_SUBST(XLIB_LIBS)
 
 PKG_CHECK_MODULES(GTK,
         gtk+-3.0 >= $GTK_REQUIRED_VERSION
 )
 AC_SUBST(GTK_CFLAGS)
 AC_SUBST(GTK_LIBS)
 
 PKG_CHECK_MODULES(CANBERRA_GTK,
         libcanberra-gtk3 >= $LIBCANBERRA_GTK_REQUIRED_VERSION
 )
 AC_SUBST(CANBERRA_GTK_CFLAGS)
 AC_SUBST(CANBERRA_GTK_LIBS)
 
 AC_ARG_WITH(selinux,
             AS_HELP_STRING([--with-selinux],
                            [Add SELinux support]),,
             with_selinux=auto)
 
 PKG_CHECK_MODULES(LIBSELINUX, libselinux, have_selinux=yes, have_selinux=no)
 
 use_selinux=no
diff --git a/daemon/gdm-slave.c b/daemon/gdm-slave.c
index b4bbcfd..3a22cc6 100644
--- a/daemon/gdm-slave.c
+++ b/daemon/gdm-slave.c
@@ -11,65 +11,68 @@
  * 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 <stdlib.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <errno.h>
 #include <pwd.h>
 #include <grp.h>
 
 #include <glib.h>
 #include <glib/gstdio.h>
 #include <glib/gi18n.h>
 #include <glib-object.h>
 
 #include <X11/Xlib.h> /* for Display */
+#include <X11/Xlib-xcb.h>
 #include <X11/Xatom.h> /* for XA_PIXMAP */
 #include <X11/cursorfont.h> /* for watch cursor */
 #include <X11/extensions/Xrandr.h>
 #include <X11/Xatom.h>
 
+#include <xcb/xcb.h>
+
 #ifdef WITH_SYSTEMD
 #include <systemd/sd-login.h>
 #endif
 
 #include "gdm-common.h"
 #include "gdm-xerrors.h"
 
 #include "gdm-slave.h"
 #include "gdm-display.h"
 #include "gdm-display-glue.h"
 
 #include "gdm-server.h"
 
 #define GDM_SLAVE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SLAVE, GdmSlavePrivate))
 
 struct GdmSlavePrivate
 {
         GPid             pid;
         guint            output_watch_id;
         guint            error_watch_id;
 
         Display         *server_display;
 
         char            *session_id;
 
         GdmDisplay      *display;
 
         /* cached display values */
         char            *display_name;
         int              display_number;
@@ -453,60 +456,95 @@ gdm_slave_real_start (GdmSlave *slave)
                 g_error_free (error);
                 return FALSE;
         }
 
         error = NULL;
         res = gdm_display_get_seat_id (slave->priv->display, &slave->priv->display_seat_id, &error);
         if (! res) {
                 g_warning ("Failed to get value: %s", error->message);
                 g_error_free (error);
                 return FALSE;
         }
 
         error = NULL;
         res = gdm_display_is_initial (slave->priv->display, &slave->priv->display_is_initial, &error);
         if (! res) {
                 g_warning ("Failed to get value: %s", error->message);
                 g_error_free (error);
                 return FALSE;
         }
 
         return TRUE;
 }
 
 static gboolean
 gdm_slave_real_stop (GdmSlave *slave)
 {
         g_debug ("GdmSlave: Stopping slave");
 
         g_clear_object (&slave->priv->display);
 
+        if (slave->priv->server_display) {
+                xcb_connection_t *connection;
+                const xcb_setup_t *setup;
+
+                /* These 3 bits are reserved/unused by the X protocol */
+                guint32 unused_bits = 0b11100000000000000000000000000000;
+                XID highest_client, client;
+                guint32 client_increment;
+
+                connection = XGetXCBConnection (slave->priv->server_display);
+                setup = xcb_get_setup (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;
+
+                gdm_error_trap_push ();
+
+                /* 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)
+                                XKillClient (slave->priv->server_display, client);
+                }
+
+                XCloseDisplay (slave->priv->server_display);
+                gdm_error_trap_pop ();
+
+                slave->priv->server_display = NULL;
+        }
+
         return TRUE;
 }
 
 gboolean
 gdm_slave_start (GdmSlave *slave)
 {
         gboolean ret;
 
         g_return_val_if_fail (GDM_IS_SLAVE (slave), FALSE);
 
         g_debug ("GdmSlave: starting slave");
 
         g_object_ref (slave);
         ret = GDM_SLAVE_GET_CLASS (slave)->start (slave);
         g_object_unref (slave);
 
         return ret;
 }
 
 gboolean
 gdm_slave_stop (GdmSlave *slave)
 {
         gboolean ret;
 
         g_return_val_if_fail (GDM_IS_SLAVE (slave), FALSE);
 
         g_debug ("GdmSlave: stopping slave");
 
         g_object_ref (slave);
 
-- 
2.7.4