Bug#291081: tsclient: XDMCP option assumes display :1 is free and fails

Joachim Nilsson Joachim Nilsson <joachim.nilsson@member.fsf.org>, 291081@bugs.debian.org
Tue, 18 Jan 2005 17:03:41 +0100


This is a multi-part MIME message sent by reportbug.

--===============1710534465==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Package: tsclient
Version: 0.132-2
Severity: normal
Tags: patch

tsclient assumes that display :1 is free when using the
XDMCP option to start Xnest.

Included in this report is a patch with a function stolen
from gdm-2.6.0.6:daemon/misc.c:gdm_get_free_display(). The
function tsc_get_free_display() has been adapted to tsclient
by removing all gdm specific code and should work for all
types of use cases.

Regards
 /Jocke

-- System Information:
Debian Release: 3.1
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: i386 (i686)
Kernel: Linux 2.6.8-1-k7
Locale: LANG=sv_SE.ISO-8859-15, LC_CTYPE=sv_SE.ISO-8859-15 (charmap=ISO-8859-15)

Versions of packages tsclient depends on:
ii  libart-2.0-2             2.3.16-6        Library of functions for 2D graphi
ii  libatk1.0-0              1.8.0-4         The ATK accessibility toolkit
ii  libbonobo2-0             2.8.0-4         Bonobo CORBA interfaces library
ii  libbonoboui2-0           2.8.0-2         The Bonobo UI library
ii  libc6                    2.3.2.ds1-20    GNU C Library: Shared libraries an
ii  libgconf2-4              2.8.1-4         GNOME configuration database syste
ii  libglib2.0-0             2.4.8-1         The GLib library of C routines
ii  libgnome2-0              2.8.0-6         The GNOME 2 library - runtime file
ii  libgnomecanvas2-0        2.8.0-1         A powerful object-oriented display
ii  libgnomeui-0             2.8.0-3         The GNOME 2 libraries (User Interf
ii  libgnomevfs2-0           2.8.3-8         The GNOME virtual file-system libr
ii  libgtk2.0-0              2.4.14-2        The GTK+ graphical user interface 
ii  libice6                  4.3.0.dfsg.1-10 Inter-Client Exchange library
ii  liborbit2                1:2.10.2-1.1    libraries for ORBit2 - a CORBA ORB
ii  libpanel-applet2-0       2.8.2-1         Library for GNOME 2 Panel applets
ii  libpango1.0-0            1.6.0-3         Layout and rendering of internatio
ii  libpopt0                 1.7-5           lib for parsing cmdline parameters
ii  libsm6                   4.3.0.dfsg.1-10 X Window System Session Management
ii  libxml2                  2.6.11-5        GNOME XML library
ii  rdesktop                 1.3.1-1.1       RDP client for Windows NT/2000 Ter
ii  xlibs                    4.3.0.dfsg.1-10 X Keyboard Extension (XKB) configu
ii  zlib1g                   1:1.2.2-3       compression library - runtime

-- no debconf information

--===============1710534465==
Content-Type: text/x-c; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="patch-tsclient-0.132-get_free_display.diff"

--- support.c	2003-11-09 08:12:17.000000000 +0100
+++ support_new.c	2005-01-18 16:49:48.000000000 +0100
@@ -1,4 +1,3 @@
-
 #ifdef HAVE_CONFIG_H
 #  include <config.h>
 #endif
@@ -23,9 +22,23 @@
 #include <dirent.h> 
 #include <fcntl.h> 
 
+#include <arpa/inet.h>
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#include <errno.h>
+#include <signal.h>
+
 #include "rdpfile.h"
 #include "support.h"
 
+/* Stolen from jirka's vicious lib */
+#define VE_IGNORE_EINTR(expr)                   \
+        do {                                    \
+                errno = 0;                      \
+                expr;                           \
+        } while G_UNLIKELY (errno == EINTR);
+
 
 GtkWidget* lookup_widget (GtkWidget *widget, const gchar *widget_name) {
   GtkWidget *parent, *found_widget;
@@ -188,6 +201,124 @@
 }
 
 
+/**
+ * tsc_get_free_display
+ * @start: Start at this display, use 0 as safe value.
+ * @server_uid: UID of X server
+ *
+ * Evil function to figure out which display number is free.
+ *
+ * Stolen from gdm-2.6.0:daemon/misc.c
+ *
+ * Returns: A free X display number or -1 on failure.
+ */
+
+int tsc_get_free_display (int start, uid_t server_uid)
+{
+  int sock;
+  int i;
+  struct sockaddr_in serv_addr = {0};
+
+  serv_addr.sin_family = AF_INET;
+  serv_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+  /* Cap this at 3000, I'm not sure we can ever seriously
+   * go that far */
+  for (i = start; i < 3000; i ++) {
+    struct stat s;
+    char buf[256];
+    FILE *fp;
+    int r;
+
+#ifdef ENABLE_IPV6
+    if (have_ipv6 ()) {
+      struct sockaddr_in6 serv6_addr= {0};
+
+      sock = socket (AF_INET6, SOCK_STREAM,0);
+
+      serv6_addr.sin6_family = AF_INET6;
+      serv6_addr.sin6_addr = in6addr_loopback;
+      serv6_addr.sin6_port = htons (6000 + i);
+      errno = 0;
+      VE_IGNORE_EINTR (connect (sock,
+                                (struct sockaddr *)&serv6_addr,
+                                sizeof (serv6_addr)));
+    }
+    else
+#endif
+      {
+        sock = socket (AF_INET, SOCK_STREAM, 0);
+
+        serv_addr.sin_port = htons (6000 + i);
+
+        errno = 0;
+        VE_IGNORE_EINTR (connect (sock,
+                                  (struct sockaddr *)&serv_addr,
+                                  sizeof (serv_addr)));
+      }
+    if (errno != 0 && errno != ECONNREFUSED) {
+      VE_IGNORE_EINTR (close (sock));
+      continue;
+    }
+    VE_IGNORE_EINTR (close (sock));
+
+    /* if lock file exists and the process exists */
+    g_snprintf (buf, sizeof (buf), "/tmp/.X%d-lock", i);
+    VE_IGNORE_EINTR (r = stat (buf, &s));
+    if (r == 0 &&
+        ! S_ISREG (s.st_mode)) {
+      /* Eeeek! not a regular file?  Perhaps someone
+         is trying to play tricks on us */
+      continue;
+    }
+    VE_IGNORE_EINTR (fp = fopen (buf, "r"));
+    if (fp != NULL) {
+      char buf2[100];
+      char *getsret;
+      VE_IGNORE_EINTR (getsret = fgets (buf2, sizeof (buf2), fp));
+      if (getsret != NULL) {
+        gulong pid;
+        if (sscanf (buf2, "%lu", &pid) == 1 &&
+            kill (pid, 0) == 0) {
+          VE_IGNORE_EINTR (fclose (fp));
+          continue;
+        }
+
+      }
+      VE_IGNORE_EINTR (fclose (fp));
+
+      /* whack the file, it's a stale lock file */
+      VE_IGNORE_EINTR (unlink (buf));
+    }
+
+    /* if starting as root, we'll be able to overwrite any
+     * stale sockets or lock files, but a user may not be
+     * able to */
+    if (server_uid > 0) {
+      g_snprintf (buf, sizeof (buf),
+                  "/tmp/.X11-unix/X%d", i);
+      VE_IGNORE_EINTR (r = stat (buf, &s));
+      if (r == 0 &&
+          s.st_uid != server_uid) {
+        continue;
+      }
+
+      g_snprintf (buf, sizeof (buf),
+                  "/tmp/.X%d-lock", i);
+      VE_IGNORE_EINTR (r = stat (buf, &s));
+      if (r == 0 &&
+          s.st_uid != server_uid) {
+        continue;
+      }
+    }
+
+    return i;
+  }
+
+  return -1;
+}
+
+
 /***************************************
 *                                      *
 *   tsc_launch_remote                  *
@@ -469,6 +600,7 @@
       c_argv[c_argc++] = g_strdup (buffer);
 
     } else if (rdp->protocol == 2) {
+      int display;
     
       if (g_find_program_in_path ("Xnest")) {
         sflags += G_SPAWN_SEARCH_PATH;
@@ -480,7 +612,13 @@
         return 1;
       }
         
-      sprintf(buffer, ":1");
+      /* Starting search from :1 (assuming we run at :0) */
+      display = tsc_get_free_display (1, getuid());
+      if (-1 == display) {
+        tsc_error_message (_("Could not find a free X display."));
+        return 1;
+      }
+      sprintf(buffer, ":%d", display);
       c_argv[c_argc++] = strdup(buffer);
 
       sprintf(buffer, "-once");

--===============1710534465==--