Local proxy X server with XDMCP



Hey,
	(Here's a big patch that Brian asked me to forward to the list)

	What I want to do is add a feature where by you can run XDMCP sessions
on a local X proxy (say, on something like Xnest). One of the reasonings
behind this is because by running the session on a local proxy, you
should cut down on the amount of roundtrips made by apps that need to go
over the network. Think about GetWindowProperty - the property state is
stored in the proxy server, so you never need to go across the network
to the backend X server.

	However, the twist, and the real motivation for the feature, is that if
you have a proxy server which supports disconnecting from its backend
server and reconnecting to another backend server, you can implement a
"session migration" feature - i.e. that when you go to login with XDMCP,
if you have an existing session on a migratable X proxy server, you can
just reconnect that proxy server to your display.

	At the moment, the Distributed Multihead X (DMX) server has fairly
flakey support for this disconnect/reconnect feature and I think the
NoMachine NX guys have implemented in their proxy too. I think it would
be really, really cool if GDM could use the feature in this way.

	I prototyped something similar[1] to this with VNC last year. At
GUADEC, I sat down and went through the patch with George and he gave me
lots of suggestions. I think I've incorporated any of those suggestions
that we relevant to this version.

	So, the changes:

  - Server type changes:
       + Added a TYPE_XDMCP_PROXY type
       + Added that to SERVER_IS_LOCAL() and SERVER_IS_FLEXI() - its a 
         local Xserver, and its launched the same way as flexi servers
       + Added SERVER_IS_PROXY() for FLEXI_XNEST and XDMCP_PROXY and
         changed the Xnest code to be a little more generic - e.g.
         s/xnest_disp/parent_disp/ etc - so that it applies to both
         types.

  - Added new config keys - xdmcp/EnableProxy, xdmp/ProxyXServer and 
    xdmcp/ProxyReconnect

  - Changed how QUERYLOGIN works
     + Basically, the format change is:
         -   <display>,<vt>,<display>,<vt>,... */
         +   <display>,<migratable>,<display>,<migratable>,... */
     + The migratable flag is 1 if it is possible to migrate the user
       from the current display to the specified display
     + This goes for VT switching - if both displays are on a VT, then
       we switch to the session on the other VT
     + It also goes for re-connectable proxies - you can migrate the
       existing session on the other display to the new parent display

  - Added a new MIGRATE command:
     + It tells the master to migrate the user from the current display
       to the specified one
     + If they're both console displays, it involves VT switching to
       the specified display
     + If they're both proxied XDMCP servers, you get the master to
       connect the specifed display to the parent display
     + The slave side of all this is
       gdm_slave_check_user_wants_to_log_in() and the master side is in
       gdm_handle_message()
     + Note, the slave doesn't exit when you migrate an XDMCP session.
       It stays alive as long as the backend X server stays alive,
       because XDMCP servers reset when the seed connection closes
       and the slave holds that seed connection. See 
       server.c:connect_to_parent() and 
       slave.c:wait_for_display_to_die()

  - XDMCP changes:
     + Added code to setup a proxy X server on XDMCP Request
     + Added gdm_xdmcp_migrate() which launches the ProxyReconnect
       command - also added an implementation of ProxyReconnect for
       DMX.
     + Add a new GdmDisplay member (:xdmcp_dispnum) which contains the
       display number in the XDMCP Request message. The existing XDMCP
       code used :dispnum for this, but when we put a proxy into the
       equation :dispnum should be the display number for the proxy.
     + Changed gdm_xdmcp_display_dispose_check() to take the hostname
       and display number rather than the full display name, because
       the :name member might contain the proxies display name rather
       than the parent display's name.

  - Made all proxy servers, include Xnest, be launched with -display
    rather than setting $DISPLAY. This is because Xdmx needs -display.

Cheers,
Mark.

[1] - http://www.gnome.org/~markmc/remote-desktop-2.html
Index: configure.in
===================================================================
RCS file: /cvs/gnome/gdm2/configure.in,v
retrieving revision 1.262
diff -u -p -r1.262 configure.in
--- configure.in	1 Apr 2005 02:33:19 -0000	1.262
+++ configure.in	21 Apr 2005 14:47:58 -0000
@@ -7,6 +7,7 @@ AM_MAINTAINER_MODE
 
 AC_PROG_INTLTOOL([0.28])
 
+GLIB_REQUIRED=2.6.0
 GTK_REQUIRED=2.3.0
 LIBGLADE_REQUIRED=1.99.2
 LIBGNOME_REQUIRED=1.96.0
@@ -61,6 +62,10 @@ AC_ARG_WITH(tcp-wrappers,
   [  --with-tcp-wrappers=[auto/yes/no]  Use TCP Wrappers [default=auto]],,
   with_tcp_wrappers=auto)
 
+AC_ARG_WITH(dmx,
+  [  --with-dmx=[auto/yes/no]  Add DMX (Distributed Multihead X) support [default=auto]],,
+  with_dmx=auto)
+
 AC_ARG_WITH(selinux, [  --with-selinux  Add SELinux support])
 
 withval=""
@@ -145,6 +150,10 @@ PKG_CHECK_MODULES(UTILS, gtk+-2.0 >= $GT
 AC_SUBST(UTILS_CFLAGS)
 AC_SUBST(UTILS_LIBS)
 
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= $GLIB_REQUIRED)
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
 dnl Allow users to run gdmsetup using the console helper PAM stuff.
 if test "x$enable_console_helper" = "xyes"; then
   AM_CONDITIONAL(CONSOLE_HELPER, true)
@@ -582,6 +591,28 @@ AC_SUBST(XINERAMA_LIBS)
 CPPFLAGS="$xinerama_save_cppflags"
 
 #
+# Distributed Multihead X extension (DMX)
+#
+DMX_SUPPORT=""
+DMX_LIBS=""
+if test x$with_dmx != xno ; then
+	dmx_save_CPPFLAGS="$CPPFLAGS"
+	CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+	AC_CHECK_HEADER(X11/extensions/dmxext.h, [
+		AC_CHECK_LIB(dmx, DMXQueryExtension, [
+			DMX_LIBS="-ldmx"
+			DMX_SUPPORT=yes],,[$X_LIBS -lX11 $X_EXTRA_LIBS])
+	],,[#include <X11/Xlib.h>])
+
+	if test x$with_dmx = xyes -a x$DMX_SUPPORT = x ; then
+		AC_MSG_ERROR(DMX support requested but DMX librariy not found)
+	fi
+	CPPFLAGS="$dmx_save_CPPFLAGS"
+fi
+AC_SUBST(DMX_LIBS)
+AM_CONDITIONAL(DMX_SUPPORT, test x$DMX_SUPPORT = xyes)
+
+#
 # SELinux stuff
 #
 if test "x$with_selinux" = "xyes" ; then
@@ -665,6 +696,13 @@ EXPANDED_LOGDIR=`eval echo $LOGDIR_TMP`
 AC_SUBST(EXPANDED_LOGDIR)
 AC_DEFINE_UNQUOTED(EXPANDED_LOGDIR,"$EXPANDED_LOGDIR")
 
+if test x$DMX_SUPPORT = xyes ; then
+	GDM_RECONNECT_PROXY=$EXPANDED_BINDIR/gdm-dmx-reconnect-proxy
+else
+	GDM_RECONNECT_PROXY=
+fi
+AC_SUBST(GDM_RECONNECT_PROXY)
+
 withval=""
 AC_ARG_WITH(at-bindir,
 [  --with-at-bindir=<PATH>   PATH to Accessible Technology programs [default=EXPANDED_BINDIR]],)
@@ -929,45 +967,52 @@ echo ""
 
 dnl <= TCP Wrappers support? =>
 if test x"$LIBWRAP_PATH" = x ; then
-	echo "TCP Wrappers support              : NO"
+	echo "TCP Wrappers support                  : NO"
 else
-	echo "TCP Wrappers support              : YES"
+	echo "TCP Wrappers support                  : YES"
 fi
 
 dnl <= XINERAMA =>
 if test x"$XINERAMA_SUPPORT" = xyes ; then
-	echo "Xinerama support                  : YES"
+	echo "Xinerama support                      : YES"
 else
-	echo "Xinerama support                  : NO"
+	echo "Xinerama support                      : NO"
 fi
 
 dnl <= XDMCP =>
 if test x"$XDMCP_SUPPORT" = xyes ; then
-	echo "XDMCP (remote login) support      : YES"
+	echo "XDMCP (remote login) support          : YES"
+else
+	echo "XDMCP (remote login) support          : NO"
+fi
+
+dnl <= DMX =>
+if test x"$DMX_SUPPORT" = xyes ; then
+	echo "DMX (Distributed Multihead X) support : YES"
 else
-	echo "XDMCP (remote login) support      : NO"
+	echo "DMX (Distributed Multihead X) support : NO"
 fi
 
 dnl <= Console Helper =>
 if test "x$enable_console_helper" = "xyes"; then
-	echo "Console helper                    : YES"
+	echo "Console helper                        : YES"
 else
-	echo "Console helper                    : NO"
+	echo "Console helper                        : NO"
 fi
 
 dnl <= SELinux support =>
 if test "x$with_selinux" = "xyes" ; then
-	echo "SELinux support                   : YES"
+	echo "SELinux support                       : YES"
 else
-	echo "SELinux support                   : NO"
+	echo "SELinux support                       : NO"
 fi
 
 
 dnl <= Authentication scheme =>
-echo "Authentication scheme             : $VRFY"
+echo "Authentication scheme                 : $VRFY"
 
 dnl <= Utils built =>
-echo "Extra utilities built             : "`echo $GDMOPEN $GDMASKPASS`
+echo "Extra utilities built                 : "`echo $GDMOPEN $GDMASKPASS`
 
 echo ""
 dnl <= End of configuration summary =>
Index: config/gdm.conf.in
===================================================================
RCS file: /cvs/gnome/gdm2/config/gdm.conf.in,v
retrieving revision 1.129
diff -u -p -r1.129 gdm.conf.in
--- config/gdm.conf.in	20 Apr 2005 00:47:23 -0000	1.129
+++ config/gdm.conf.in	21 Apr 2005 14:47:59 -0000
@@ -236,6 +236,15 @@ Enable=false
 # or mail details for some user, or some such.
 #Willing= EXPANDED_SYSCONFDIR@/gdm/Xwilling
 
+# Run XDMCP sessions on a local proxy X server
+#EnableProxy=false
+# Proxy X server on which XDMCP session should be run
+#ProxyXServer= X_XNEST_PATH@/Xnest @X_XNEST_CONFIG_OPTIONS@
+#ProxyXServer= X_SERVER_PATH@/Xdmx @X_CONFIG_OPTIONS@ -addremovescreens -norender -noglxproxy
+# Command which enables the proxy X server to be reconnected
+# to another backend display
+#ProxyReconnect= GDM_RECONNECT_PROXY@
+
 [gui]
 # The specific gtkrc file we use.  It should be the full path to the gtkrc
 # that we need.  Unless you need a specific gtkrc that doesn't correspond to
Index: daemon/display.c
===================================================================
RCS file: /cvs/gnome/gdm2/daemon/display.c,v
retrieving revision 1.71
diff -u -p -r1.71 display.c
--- daemon/display.c	6 Apr 2005 19:37:28 -0000	1.71
+++ daemon/display.c	21 Apr 2005 14:48:00 -0000
@@ -409,12 +409,13 @@ count_session_limits (void)
 
 	for (li = displays; li != NULL; li = li->next) {
 		GdmDisplay *d = li->data;
-		if (d->type == TYPE_XDMCP) {
+		if (SERVER_IS_XDMCP (d)) {
 			if (d->dispstat == XDMCP_MANAGED)
 				xdmcp_sessions ++;
 			else if (d->dispstat == XDMCP_PENDING)
 				xdmcp_pending ++;
-		} else if (SERVER_IS_FLEXI (d)) {
+		}
+		if (SERVER_IS_FLEXI (d)) {
 			flexi_servers ++;
 		}
 	}
@@ -482,11 +483,19 @@ gdm_display_dispose (GdmDisplay *d)
     g_free (d->authfile_gdm);
     d->authfile_gdm = NULL;
 
-    if (d->xnest_temp_auth_file != NULL) {
-	    VE_IGNORE_EINTR (unlink (d->xnest_temp_auth_file));
+    if (d->type == TYPE_XDMCP_PROXY) {
+	    if (d->parent_auth_file != NULL) {
+		    VE_IGNORE_EINTR (unlink (d->parent_auth_file));
+	    }
+	    g_free (d->parent_auth_file);
+	    d->parent_auth_file = NULL;
+    }
+
+    if (d->parent_temp_auth_file != NULL) {
+	    VE_IGNORE_EINTR (unlink (d->parent_temp_auth_file));
     }
-    g_free (d->xnest_temp_auth_file);
-    d->xnest_temp_auth_file = NULL;
+    g_free (d->parent_temp_auth_file);
+    d->parent_temp_auth_file = NULL;
 
     if (d->auths) {
 	    gdm_auth_free_auth_list (d->auths);
@@ -514,11 +523,11 @@ gdm_display_dispose (GdmDisplay *d)
 	    gdm_choose_indirect_dispose_empty_id (d->indirect_id);
     d->indirect_id = 0;
 
-    g_free (d->xnest_disp);
-    d->xnest_disp = NULL;
+    g_free (d->parent_disp);
+    d->parent_disp = NULL;
 
-    g_free (d->xnest_auth_file);
-    d->xnest_auth_file = NULL;
+    g_free (d->parent_auth_file);
+    d->parent_auth_file = NULL;
 
     g_free (d->login);
     d->login = NULL;
Index: daemon/gdm.c
===================================================================
RCS file: /cvs/gnome/gdm2/daemon/gdm.c,v
retrieving revision 1.212
diff -u -p -r1.212 gdm.c
--- daemon/gdm.c	20 Apr 2005 00:47:24 -0000	1.212
+++ daemon/gdm.c	21 Apr 2005 14:48:02 -0000
@@ -193,6 +193,9 @@ gint  GdmMaxIndirect = 0;
 gint  GdmMaxIndirectWait = 0;
 gint  GdmPingInterval = 0;
 gchar *GdmWilling = NULL;
+gboolean GdmXdmcpProxy = FALSE;
+gchar *GdmXdmcpProxyCommand = NULL;
+gchar *GdmXdmcpProxyReconnect = NULL;
 gboolean  GdmDebug = FALSE;
 gboolean  GdmAllowRoot = FALSE;
 gboolean  GdmAllowRemoteRoot = FALSE;
@@ -421,6 +424,14 @@ gdm_config_parse (void)
     GdmPingInterval = ve_config_get_int (cfg, GDM_KEY_PINGINTERVAL);    
     GdmWilling = ve_config_get_string (cfg, GDM_KEY_WILLING);    
 
+    GdmXdmcpProxy = ve_config_get_bool (cfg, GDM_KEY_XDMCP_PROXY);
+    GdmXdmcpProxyCommand = ve_config_get_string (cfg, GDM_KEY_XDMCP_PROXY_XSERVER);
+    if (ve_string_empty (GdmXdmcpProxyCommand))
+	    GdmXdmcpProxyCommand = NULL;
+    GdmXdmcpProxyReconnect = ve_config_get_string (cfg, GDM_KEY_XDMCP_PROXY_RECONNECT);
+    if (ve_string_empty (GdmXdmcpProxyReconnect))
+	    GdmXdmcpProxyReconnect = NULL;
+
     GdmStandardXServer = ve_config_get_string (cfg, GDM_KEY_STANDARD_XSERVER);    
     bin = ve_first_word (GdmStandardXServer);
     if G_UNLIKELY (ve_string_empty (bin) ||
@@ -982,8 +993,8 @@ gdm_final_cleanup (void)
 	   slaves, we'll wait for them later */
 	for (li = displays; li != NULL; li = li->next) {
 		GdmDisplay *d = li->data;
-		if (d->type == TYPE_XDMCP ||
-		    d->type == TYPE_FLEXI_XNEST) {
+		if (SERVER_IS_XDMCP (d) ||
+		    SERVER_IS_PROXY (d)) {
 			/* set to DEAD so that we won't kill it again */
 			d->dispstat = DISPLAY_DEAD;
 			if (d->slavepid > 1)
@@ -1000,8 +1011,8 @@ gdm_final_cleanup (void)
 	list = g_slist_reverse (list);
 	for (li = list; li != NULL; li = li->next) {
 		GdmDisplay *d = li->data;
-		if (d->type == TYPE_XDMCP ||
-		    d->type == TYPE_FLEXI_XNEST)
+		if (SERVER_IS_XDMCP (d) ||
+		    SERVER_IS_PROXY (d))
 			continue;
 		/* HACK! Wait 2 seconds between killing of local servers
 		 * because X is stupid and full of races and will otherwise
@@ -1023,8 +1034,8 @@ gdm_final_cleanup (void)
 	list = g_slist_copy (displays);
 	for (li = list; li != NULL; li = li->next) {
 		GdmDisplay *d = li->data;
-		if (d->type == TYPE_XDMCP ||
-		    d->type == TYPE_FLEXI_XNEST)
+		if (SERVER_IS_XDMCP (d) ||
+		    SERVER_IS_PROXY (d))
 			gdm_display_unmanage (d);
 	}
 	g_slist_free (list);
@@ -2365,9 +2376,7 @@ write_x_servers (GdmDisplay *d)
 		VE_IGNORE_EINTR (fprintf (fp, "%s local /usr/X11R6/bin/Xbogus\n", buf));
 	}
 
-	if (d->type == TYPE_XDMCP) {
-		VE_IGNORE_EINTR (fprintf (fp, "%s foreign\n", d->name));
-	} else {
+	if (SERVER_IS_LOCAL (d)) {
 		char **argv;
 		char *command;
 		argv = gdm_server_resolve_command_line
@@ -2377,6 +2386,8 @@ write_x_servers (GdmDisplay *d)
 		g_strfreev (argv);
 		VE_IGNORE_EINTR (fprintf (fp, "%s local %s\n", d->name, command));
 		g_free (command);
+	} else {
+		VE_IGNORE_EINTR (fprintf (fp, "%s foreign\n", d->name));
 	}
 
 	/* FIXME: What about out of disk space errors? */
@@ -2689,13 +2700,23 @@ gdm_handle_message (GdmConnection *conn,
 				if (di->logged_in &&
 				    di->login != NULL &&
 				    strcmp (di->login, p) == 0) {
-					if (resp == NULL) {
-						resp = g_string_new (di->name);
-						g_string_append_printf (resp, ",%d", di->vt);
-					} else {
-						g_string_append_printf (resp, ",%s,%d",
-									di->name, di->vt);
-					}
+					gboolean migratable = FALSE;
+
+					if (resp == NULL)
+						resp = g_string_new (NULL);
+					else
+						resp = g_string_append_c (resp, ',');
+
+					g_string_append (resp, di->name);
+					g_string_append_c (resp, ',');
+
+					if (d->console && di->console && di->vt > 0)
+						migratable = TRUE;
+					else if (GdmXdmcpProxyReconnect != NULL &&
+						 d->type == TYPE_XDMCP_PROXY && di->type == TYPE_XDMCP_PROXY)
+						migratable = TRUE;
+
+					g_string_append_c (resp, migratable ? '1' : '0');
 				}
 			}
 
@@ -2707,6 +2728,39 @@ gdm_handle_message (GdmConnection *conn,
 				send_slave_ack (d, NULL);
 			}
 		}
+	} else if (strncmp (msg, GDM_SOP_MIGRATE " ",
+		            strlen (GDM_SOP_MIGRATE " ")) == 0) {
+		GdmDisplay *d;
+		long slave_pid;
+		char *p;
+		GSList *li;
+
+		if (sscanf (msg, GDM_SOP_MIGRATE " %ld", &slave_pid) != 1)
+			return;
+		p = strchr (msg, ' ');
+		if (p != NULL)
+			p = strchr (p+1, ' ');
+		if (p == NULL)
+			return;
+
+		p++;
+
+		/* Find out who this slave belongs to */
+		d = gdm_display_lookup (slave_pid);
+		if (d == NULL)
+			return;
+
+		gdm_debug ("Got MIGRATE %s", p);
+		for (li = displays; li != NULL; li = li->next) {
+			GdmDisplay *di = li->data;
+			if (di->logged_in && strcmp (di->name, p) == 0) {
+				if (d->console && di->vt > 0)
+					gdm_change_vt (di->vt);
+				else if (d->type == TYPE_XDMCP_PROXY && di->type == TYPE_XDMCP_PROXY)
+					gdm_xdmcp_migrate (d, di);
+			}
+		}
+		send_slave_ack (d, NULL);
 	} else if (strncmp (msg, GDM_SOP_COOKIE " ",
 		            strlen (GDM_SOP_COOKIE " ")) == 0) {
 		GdmDisplay *d;
@@ -2734,6 +2788,33 @@ gdm_handle_message (GdmConnection *conn,
 			/* send ack */
 			send_slave_ack (d, NULL);
 		}
+	} else if (strncmp (msg, GDM_SOP_AUTHFILE " ",
+		            strlen (GDM_SOP_AUTHFILE " ")) == 0) {
+		GdmDisplay *d;
+		long slave_pid;
+		char *p;
+
+		if (sscanf (msg, GDM_SOP_AUTHFILE " %ld",
+			    &slave_pid) != 1)
+			return;
+		p = strchr (msg, ' ');
+		if (p != NULL)
+			p = strchr (p+1, ' ');
+		if (p == NULL)
+			return;
+
+		p++;
+
+		/* Find out who this slave belongs to */
+		d = gdm_display_lookup (slave_pid);
+
+		if (d != NULL) {
+			g_free (d->authfile);
+			d->authfile = g_strdup (p);
+			gdm_debug ("Got AUTHFILE == %s", d->authfile);
+			/* send ack */
+			send_slave_ack (d, NULL);
+		}
 	} else if (strncmp (msg, GDM_SOP_FLEXI_ERR " ",
 		            strlen (GDM_SOP_FLEXI_ERR " ")) == 0) {
 		GdmDisplay *d;
@@ -3268,8 +3349,8 @@ handle_flexi_server (GdmConnection *conn
 
 	display->type = type;
 	display->socket_conn = conn;
-	display->xnest_disp = g_strdup (xnest_disp);
-	display->xnest_auth_file = g_strdup (xnest_auth_file);
+	display->parent_disp = g_strdup (xnest_disp);
+	display->parent_auth_file = g_strdup (xnest_auth_file);
 	if (conn != NULL)
 		gdm_connection_set_close_notify (conn, display, close_conn);
 	displays = g_slist_append (displays, display);
@@ -3771,7 +3852,7 @@ gdm_handle_user_message (GdmConnection *
 			sep = ";";
 			if (disp->type == TYPE_FLEXI_XNEST) {
 				g_string_append (msg, 
-					 ve_sure_string (disp->xnest_disp));
+					 ve_sure_string (disp->parent_disp));
 			} else {
 				g_string_append_printf (msg, "%d", disp->vt);
 			}
Index: daemon/gdm.h
===================================================================
RCS file: /cvs/gnome/gdm2/daemon/gdm.h,v
retrieving revision 1.180
diff -u -p -r1.180 gdm.h
--- daemon/gdm.h	20 Apr 2005 00:47:24 -0000	1.180
+++ daemon/gdm.h	21 Apr 2005 14:48:02 -0000
@@ -34,12 +34,19 @@
 #define TYPE_XDMCP 2		/* Remote display */
 #define TYPE_FLEXI 3		/* Local Flexi X server */
 #define TYPE_FLEXI_XNEST 4	/* Local Flexi Xnest server */
+#define TYPE_XDMCP_PROXY 5      /* Proxy X server for XDMCP */
 
 #define SERVER_IS_LOCAL(d) ((d)->type == TYPE_LOCAL || \
 			    (d)->type == TYPE_FLEXI || \
-			    (d)->type == TYPE_FLEXI_XNEST)
+			    (d)->type == TYPE_FLEXI_XNEST || \
+			    (d)->type == TYPE_XDMCP_PROXY)
 #define SERVER_IS_FLEXI(d) ((d)->type == TYPE_FLEXI || \
-			    (d)->type == TYPE_FLEXI_XNEST)
+			    (d)->type == TYPE_FLEXI_XNEST || \
+			    (d)->type == TYPE_XDMCP_PROXY)
+#define SERVER_IS_PROXY(d) ((d)->type == TYPE_FLEXI_XNEST || \
+			    (d)->type == TYPE_XDMCP_PROXY)
+#define SERVER_IS_XDMCP(d) ((d)->type == TYPE_XDMCP || \
+			    (d)->type == TYPE_XDMCP_PROXY)
 
 /* These are the servstat values, also used as server
  * process exit codes */
@@ -216,6 +223,10 @@ enum {
 #define GDM_KEY_PINGINTERVAL "xdmcp/PingIntervalSeconds=15"
 #define GDM_KEY_WILLING "xdmcp/Willing=" EXPANDED_SYSCONFDIR "/gdm/Xwilling"
 
+#define GDM_KEY_XDMCP_PROXY "xdmcp/EnableProxy=false"
+#define GDM_KEY_XDMCP_PROXY_XSERVER "xdmcp/ProxyXServer="
+#define GDM_KEY_XDMCP_PROXY_RECONNECT "xdmcp/ProxyReconnect="
+
 #define GDM_KEY_GTK_THEME "gui/GtkTheme=Default"
 #define GDM_KEY_GTKRC "gui/GtkRC="
 #define GDM_KEY_ICONWIDTH "gui/MaxIconWidth=128"
@@ -409,12 +420,15 @@ struct _GdmDisplay {
     int lrh_offsety; /* lower right hand corner y offset */
 
     /* Flexi stuff */
-    char *xnest_disp;
-    char *xnest_auth_file;
-    char *xnest_temp_auth_file;
+    char *parent_disp;
+    Display *parent_dsp;
+    char *parent_auth_file;
+    char *parent_temp_auth_file;
     uid_t server_uid;
     GdmConnection *socket_conn;
 
+    int xdmcp_dispnum;
+
     /* Notification connection */
     int master_notify_fd;  /* write part of the connection */
     int slave_notify_fd; /* read part of the connection */
@@ -531,9 +545,11 @@ void		gdm_final_cleanup	(void);
 #define GDM_SOP_LOGGED_IN    "LOGGED_IN" /* <slave pid> <logged_in as int> */
 #define GDM_SOP_LOGIN        "LOGIN" /* <slave pid> <username> */
 #define GDM_SOP_COOKIE       "COOKIE" /* <slave pid> <cookie> */
+#define GDM_SOP_AUTHFILE     "AUTHFILE" /* <slave pid> <authfile> */
 #define GDM_SOP_QUERYLOGIN   "QUERYLOGIN" /* <slave pid> <username> */
 /* if user already logged in somewhere, the ack response will be
-   <display>,<vt>,<display>,<vt>,... */
+   <display>,<migratable>,<display>,<migratable>,... */
+#define GDM_SOP_MIGRATE      "MIGRATE" /* <slave pid> <display> */
 #define GDM_SOP_DISP_NUM     "DISP_NUM" /* <slave pid> <display as int> */
 /* For Linux only currently */
 #define GDM_SOP_VT_NUM       "VT_NUM" /* <slave pid> <vt as int> */
Index: daemon/server.c
===================================================================
RCS file: /cvs/gnome/gdm2/daemon/server.c,v
retrieving revision 1.115
diff -u -p -r1.115 server.c
--- daemon/server.c	6 Apr 2005 19:37:28 -0000	1.115
+++ daemon/server.c	21 Apr 2005 14:48:05 -0000
@@ -276,11 +276,20 @@ gdm_server_stop (GdmDisplay *disp)
     if (disp->dsp != NULL) {
 	    /* on XDMCP servers first kill everything in sight */
 	    if (disp->type == TYPE_XDMCP)
-		    gdm_server_whack_clients (d);
+		    gdm_server_whack_clients (disp->dsp);
 	    XCloseDisplay (disp->dsp);
 	    disp->dsp = NULL;
     }
 
+    /* Kill our parent connection if one existed */
+    if (disp->parent_dsp != NULL) {
+	    /* on XDMCP servers first kill everything in sight */
+	    if (disp->type == TYPE_XDMCP_PROXY)
+		    gdm_server_whack_clients (disp->parent_dsp);
+	    XCloseDisplay (disp->parent_dsp);
+	    disp->parent_dsp = NULL;
+    }
+
     if (disp->servpid <= 0)
 	    return;
 
@@ -369,7 +378,7 @@ busy_ask_user (GdmDisplay *disp)
 
 /* Checks only output, no XFree86 v4 logfile */
 static gboolean
-display_xnest_no_connect (GdmDisplay *disp)
+display_parent_no_connect (GdmDisplay *disp)
 {
 	char *logname = gdm_make_filename (GdmLogDir, d->name, ".log");
 	FILE *fp;
@@ -393,7 +402,7 @@ display_xnest_no_connect (GdmDisplay *di
 		 * of course additions are welcome to make this more robust */
 		if (strstr (buf, "Unable to open display \"") == buf) {
 			gdm_error (_("Display '%s' cannot be opened by Xnest"),
-				   ve_sure_string (disp->xnest_disp));
+				   ve_sure_string (disp->parent_disp));
 			VE_IGNORE_EINTR (fclose (fp));
 			return TRUE;
 		}
@@ -662,6 +671,45 @@ do_server_wait (GdmDisplay *d)
     }
 }
 
+/* We keep a connection (parent_dsp) open with the parent X server
+ * before running a proxy on it to prevent the X server resetting
+ * as we open and close other connections.
+ * Note that XDMCP servers, by default, reset when the seed X
+ * connection closes whereas usually the X server only quits when
+ * all X connections have closed.
+ */
+static gboolean
+connect_to_parent (GdmDisplay *d)
+{
+	int maxtries;
+	int openretries;
+
+	gdm_debug ("gdm_server_start: Connecting to parent display \'%s\'",
+		   d->parent_disp);
+
+	d->parent_dsp = NULL;
+
+	maxtries = SERVER_IS_XDMCP (d) ? 10 : 2;
+
+	openretries = 0;
+	while (openretries < maxtries &&
+	       d->parent_dsp == NULL) {
+		d->parent_dsp = XOpenDisplay (d->parent_disp);
+
+		if G_UNLIKELY (d->parent_dsp == NULL) {
+			gdm_debug ("gdm_server_start: Sleeping %d on a retry", 1+openretries*2);
+			gdm_sleep_no_signal (1+openretries*2);
+			openretries++;
+		}
+	}
+
+	if (d->parent_dsp == NULL)
+		gdm_error (_("%s: failed to connect to parent display '\%s\'"),
+			   "gdm_server_start", d->parent_disp);
+
+	return d->parent_dsp != NULL;
+}
+
 /**
  * gdm_server_start:
  * @disp: Pointer to a GdmDisplay structure
@@ -708,6 +756,9 @@ gdm_server_start (GdmDisplay *disp,
 	    gdm_slave_send_num (GDM_SOP_DISP_NUM, flexi_disp);
     }
 
+    if (d->type == TYPE_XDMCP_PROXY &&
+	! connect_to_parent (d))
+	    return FALSE;
 
     gdm_debug ("gdm_server_start: %s", d->name);
 
@@ -715,6 +766,7 @@ gdm_server_start (GdmDisplay *disp,
     if ( ! gdm_auth_secure_display (d)) 
 	    return FALSE;
     gdm_slave_send_string (GDM_SOP_COOKIE, d->cookie);
+    gdm_slave_send_string (GDM_SOP_AUTHFILE, d->authfile);
     ve_setenv ("DISPLAY", d->name, TRUE);
 
     if ( ! setup_server_wait (d))
@@ -774,10 +826,10 @@ gdm_server_start (GdmDisplay *disp,
 	    break;
     }
 
-    if (disp->type == TYPE_FLEXI_XNEST &&
-	display_xnest_no_connect (disp)) {
+    if (SERVER_IS_PROXY (disp) &&
+	display_parent_no_connect (disp)) {
 	    gdm_slave_send_num (GDM_SOP_FLEXI_ERR,
-				5 /* Xnest can't connect */);
+				5 /* proxy can't connect */);
 	    _exit (DISPLAY_REMANAGE);
     }
 
@@ -1146,27 +1198,36 @@ gdm_server_spawn (GdmDisplay *d, const c
 	sigemptyset (&mask);
 	sigprocmask (SIG_SETMASK, &mask, NULL);
 
-	if (d->type == TYPE_FLEXI_XNEST) {
-		char *font_path = NULL;
-		ve_setenv ("DISPLAY", d->xnest_disp, TRUE);
-		if (d->xnest_auth_file != NULL)
-			ve_setenv ("XAUTHORITY", d->xnest_auth_file, TRUE);
+	if (SERVER_IS_PROXY (d)) {
+		int argc = ve_vector_len (argv);
+
+		ve_unsetenv ("DISPLAY");
+		if (d->parent_auth_file != NULL)
+			ve_setenv ("XAUTHORITY", d->parent_auth_file, TRUE);
 		else
 			ve_unsetenv ("XAUTHORITY");
 
-		/* Add -fp with the current font path, but only if not
-		 * already among the arguments */
-		if (strstr (command, "-fp") == NULL)
-			font_path = get_font_path (d->xnest_disp);
-		if (font_path != NULL) {
-			int argc = ve_vector_len (argv);
-			argv = g_renew (char *, argv, argc + 3);
-			argv[argc++] = "-fp";
-			argv[argc++] = font_path;
-			argv[argc++] = NULL;
-			command = g_strconcat (command, " -fp ",
-					       font_path, NULL);
+		if (d->type == TYPE_FLEXI_XNEST) {
+			char *font_path = NULL;
+			/* Add -fp with the current font path, but only if not
+			 * already among the arguments */
+			if (strstr (command, "-fp") == NULL)
+				font_path = get_font_path (d->parent_disp);
+			if (font_path != NULL) {
+				argv = g_renew (char *, argv, argc + 2);
+				argv[argc++] = "-fp";
+				argv[argc++] = font_path;
+				command = g_strconcat (command, " -fp ",
+						       font_path, NULL);
+			}
 		}
+
+		argv = g_renew (char *, argv, argc + 3);
+		argv[argc++] = "-display";
+		argv[argc++] = d->parent_disp;
+		argv[argc++] = NULL;
+		command = g_strconcat (command, " -display ",
+				       d->parent_disp, NULL);
 	}
 
 	if (argv[0] == NULL) {
@@ -1274,8 +1335,6 @@ gdm_server_usr1_handler (gint sig)
     d->servstat = SERVER_RUNNING; /* Server ready to accept connections */
     d->starttime = time (NULL);
 
-    gdm_debug ("gdm_server_usr1_handler: Got SIGUSR1, server running");
-
     server_signal_notified = TRUE;
     /* this will quit the select */
     VE_IGNORE_EINTR (write (server_signal_pipe[1], "Yay!", 4));
@@ -1296,8 +1355,6 @@ gdm_server_child_handler (int signal)
 {
 	gdm_in_signal++;
 
-	gdm_debug ("gdm_server_child_handler: Got SIGCHLD");
-
 	/* go to the main child handler */
 	gdm_slave_child_handler (signal);
 
@@ -1386,43 +1443,42 @@ gdm_server_alloc (gint id, const gchar *
 }
 
 void
-gdm_server_whack_clients (GdmDisplay *disp)
+gdm_server_whack_clients (Display *dsp)
 {
 	int i, screen_count;
 	int (* old_xerror_handler) (Display *, XErrorEvent *);
 
-	if (disp == NULL ||
-	    disp->dsp == NULL)
+	if (dsp == NULL)
 		return;
 
 	old_xerror_handler = XSetErrorHandler (ignore_xerror_handler);
 
-	XGrabServer (disp->dsp);
+	XGrabServer (dsp);
 
-	screen_count = ScreenCount (disp->dsp);
+	screen_count = ScreenCount (dsp);
 
 	for (i = 0; i < screen_count; i++) {
 		Window root_ret, parent_ret;
 		Window *childs = NULL;
 		unsigned int childs_count = 0;
-		Window root = RootWindow (disp->dsp, i);
+		Window root = RootWindow (dsp, i);
 
-		while (XQueryTree (disp->dsp, root, &root_ret, &parent_ret,
+		while (XQueryTree (dsp, root, &root_ret, &parent_ret,
 				   &childs, &childs_count) &&
 		       childs_count > 0) {
 			int ii;
 
 			for (ii = 0; ii < childs_count; ii++) {
-				XKillClient (disp->dsp, childs[ii]);
+				XKillClient (dsp, childs[ii]);
 			}
 
 			XFree (childs);
 		}
 	}
 
-	XUngrabServer (disp->dsp);
+	XUngrabServer (dsp);
 
-	XSync (disp->dsp, False);
+	XSync (dsp, False);
 	XSetErrorHandler (old_xerror_handler);
 }
 
Index: daemon/server.h
===================================================================
RCS file: /cvs/gnome/gdm2/daemon/server.h,v
retrieving revision 1.15
diff -u -p -r1.15 server.h
--- daemon/server.h	26 Oct 2004 21:50:46 -0000	1.15
+++ daemon/server.h	21 Apr 2005 14:48:05 -0000
@@ -36,7 +36,7 @@ void		gdm_server_stop		(GdmDisplay *d);
 gboolean	gdm_server_reinit	(GdmDisplay *d);
 GdmDisplay *	gdm_server_alloc	(gint id,
 					 const gchar *command);
-void		gdm_server_whack_clients (GdmDisplay *disp);
+void		gdm_server_whack_clients (Display *dsp);
 void		gdm_server_checklog	(GdmDisplay *disp);
 
 char **		gdm_server_resolve_command_line (GdmDisplay *disp,
Index: daemon/slave.c
===================================================================
RCS file: /cvs/gnome/gdm2/daemon/slave.c,v
retrieving revision 1.281
diff -u -p -r1.281 slave.c
--- daemon/slave.c	20 Apr 2005 00:47:24 -0000	1.281
+++ daemon/slave.c	21 Apr 2005 14:48:09 -0000
@@ -773,8 +773,7 @@ gdm_slave_start (GdmDisplay *display)
 	sigaddset (&mask, SIGCHLD);
 	sigaddset (&mask, SIGUSR2);
 	sigaddset (&mask, SIGUSR1); /* normally we ignore USR1 */
-	if (display->type == TYPE_XDMCP &&
-	    GdmPingInterval > 0) {
+	if ( ! SERVER_IS_LOCAL (display) && GdmPingInterval > 0) {
 		sigaddset (&mask, SIGALRM);
 	}
 	/* must set signal mask before the Setjmp as it will be
@@ -804,8 +803,7 @@ gdm_slave_start (GdmDisplay *display)
 		_exit (DISPLAY_REMANAGE);
 	}
 
-	if (display->type == TYPE_XDMCP &&
-	    GdmPingInterval > 0) {
+	if ( ! SERVER_IS_LOCAL (display) && GdmPingInterval > 0) {
 		/* Handle a ALRM signals from our ping alarms */
 		alrm.sa_handler = gdm_slave_alrm_handler;
 		alrm.sa_flags = SA_RESTART | SA_NODEFER;
@@ -909,6 +907,7 @@ gdm_slave_start (GdmDisplay *display)
 				gdm_slave_quick_exit (DISPLAY_REMANAGE);
 			}
 			gdm_slave_send_string (GDM_SOP_COOKIE, d->cookie);
+			gdm_slave_send_string (GDM_SOP_AUTHFILE, d->authfile);
 
 			if G_UNLIKELY ( ! gdm_server_reinit (d)) {
 				gdm_error ("Error reinitilizing server");
@@ -1085,22 +1084,60 @@ gdm_slave_whack_greeter (void)
 	gdm_slave_whack_temp_auth_file ();
 }
 
+static void
+wait_for_display_to_die (Display    *display,
+			 const char *display_name)
+{
+	fd_set rfds;
+	int    fd;
+
+	gdm_debug ("wait_for_display_to_die: waiting for display '%s' to die",
+		   display_name);
+
+	fd = ConnectionNumber (display);
+
+	FD_ZERO (&rfds);
+	FD_SET (fd, &rfds);
+
+	while (1) {
+		char           buf[256];
+		struct timeval tv;
+		int            n;
+
+		tv.tv_sec  = 5;
+		tv.tv_usec = 0;
+
+		n = select (fd + 1, &rfds, NULL, NULL, &tv);
+		if (G_LIKELY (n == 0)) {
+			XSync (display, True);
+		} else if (n > 0) {
+			VE_IGNORE_EINTR (n = read (fd, buf, sizeof (buf)));
+			if (n <= 0)
+				break;
+		} else if (errno != EINTR) {
+			break;
+		}
+
+		FD_CLR (fd, &rfds);
+	}
+
+	gdm_debug ("wait_for_display_to_die: '%s' dead", display_name);
+}
+
 gboolean
 gdm_slave_check_user_wants_to_log_in (const char *user)
 {
 	gboolean loggedin = FALSE;
-	int vt = -1;
 	int i;
 	char **vec;
 	char *msg;
-	int r;
-	char *but[4];
+	char *migrate_to = NULL;
 
-	if ( ! GdmDoubleLoginWarning ||
-	    /* always ignore root here, this is mostly a special case
-	     * since a root login may not be a real login, such as the
-	     config stuff, and people shouldn't log in as root anyway */
-	    strcmp (user, gdm_root_user ()) == 0)
+	/* always ignore root here, this is mostly a special case
+	 * since a root login may not be a real login, such as the
+	 * config stuff, and people shouldn't log in as root anyway
+	 */
+	if (strcmp (user, gdm_root_user ()) == 0)
 		return TRUE;
 
 	gdm_slave_send_string (GDM_SOP_QUERYLOGIN, user);
@@ -1110,11 +1147,15 @@ gdm_slave_check_user_wants_to_log_in (co
 	if (vec == NULL)
 		return TRUE;
 
+	gdm_debug ("QUERYLOGIN response: %s\n", gdm_ack_response);
+
 	for (i = 0; vec[i] != NULL && vec[i+1] != NULL; i += 2) {
 		int ii;
 		loggedin = TRUE;
-		if (d->console && vt < 0 && sscanf (vec[i+1], "%d", &ii) == 1)
-			vt = ii;
+		if (sscanf (vec[i+1], "%d", &ii) == 1 && ii == 1) {
+			migrate_to = g_strdup (vec[i]);
+			break;
+		}
 	}
 
 	g_strfreev (vec);
@@ -1122,37 +1163,46 @@ gdm_slave_check_user_wants_to_log_in (co
 	if ( ! loggedin)
 		return TRUE;
 
-	but[0] = _("Log in anyway");
-	if (vt >= 0) {
-		msg = _("You are already logged in.  "
-			"You can log in anyway, return to your "
-			"previous login session, or abort this "
-			"login");
-		but[1] = _("Return to previous login");
-		but[2] = _("Abort login");
-		but[3] = NULL;
-	} else {
-		msg = _("You are already logged in.  "
-			"You can log in anyway or abort this "
-			"login");
-		but[1] = _("Abort login");
-		but[2] = NULL;
-	}
+	if (d->type != TYPE_XDMCP_PROXY) {
+		int r;
+		char *but[4];
 
-	if (greet)
-		gdm_slave_greeter_ctl_no_ret (GDM_DISABLE, "");
+		if (!GdmDoubleLoginWarning)
+			return TRUE;
 
-	r = gdm_failsafe_ask_buttons (d, msg, but);
+		but[0] = _("Log in anyway");
+		if (migrate_to != NULL) {
+			msg = _("You are already logged in.  "
+				"You can log in anyway, return to your "
+				"previous login session, or abort this "
+				"login");
+			but[1] = _("Return to previous login");
+			but[2] = _("Abort login");
+			but[3] = NULL;
+		} else {
+			msg = _("You are already logged in.  "
+				"You can log in anyway or abort this "
+				"login");
+			but[1] = _("Abort login");
+			but[2] = NULL;
+		}
+
+		if (greet)
+			gdm_slave_greeter_ctl_no_ret (GDM_DISABLE, "");
 
-	if (greet)
-		gdm_slave_greeter_ctl_no_ret (GDM_ENABLE, "");
+		r = gdm_failsafe_ask_buttons (d, msg, but);
 
-	if (r <= 0)
-		return TRUE;
+		if (greet)
+			gdm_slave_greeter_ctl_no_ret (GDM_ENABLE, "");
 
-	if (vt >= 0) {
-		if (r == 2) /* Abort */
+		if (r <= 0)
+			return TRUE;
+
+		if (migrate_to == NULL ||
+		    (migrate_to != NULL && r == 2)) {
+			g_free (migrate_to);
 			return FALSE;
+		}
 
 		/* Must be that r == 1, that is
 		   return to previous login */
@@ -1166,23 +1216,49 @@ gdm_slave_check_user_wants_to_log_in (co
 			 */
 			gdm_sleep_no_signal (1);
 
-			gdm_change_vt (vt);
+			gdm_slave_send_string (GDM_SOP_MIGRATE, migrate_to);
+			g_free (migrate_to);
 
 			/* we are no longer needed so just die.
 			   REMANAGE == ABORT here really */
 			gdm_slave_quick_exit (DISPLAY_REMANAGE);
 		}
 
-		gdm_change_vt (vt);
-
-		/* abort this login attempt */
-		return FALSE;
+		gdm_slave_send_string (GDM_SOP_MIGRATE, migrate_to);
+		g_free (migrate_to);
 	} else {
-		if (r == 1) /* Abort */
-			return FALSE;
-		else
+		Display *parent_dsp;
+
+		if (migrate_to == NULL)
 			return TRUE;
+		
+		gdm_slave_send_string (GDM_SOP_MIGRATE, migrate_to);
+		g_free (migrate_to);
+
+		/*
+		 * We must stay running and hold open our connection to the
+		 * parent display because with XDMCP the Xserver resets when
+		 * the initial X client closes its connection (rather than
+		 * when *all* X clients have closed their connection)
+		 */
+
+		gdm_slave_whack_greeter ();
+
+		parent_dsp = d->parent_dsp;
+		d->parent_dsp = NULL;
+		gdm_server_stop (d);
+
+		gdm_slave_send_num (GDM_SOP_XPID, 0);
+
+		gdm_debug ("Slave not exiting in order to hold open the connection to the parent display");
+
+		wait_for_display_to_die (d->parent_dsp, d->parent_disp);
+
+		gdm_slave_quick_exit (DISPLAY_ABORT);
 	}
+
+	/* abort this login attempt */
+	return FALSE;
 }
 
 static gboolean do_xfailed_on_xio_error = FALSE;
@@ -1339,8 +1415,7 @@ gdm_slave_run (GdmDisplay *display)
     do_xfailed_on_xio_error = FALSE;
 
     /* If XDMCP setup pinging */
-    if (d->type == TYPE_XDMCP &&
-	GdmPingInterval > 0) {
+    if ( ! SERVER_IS_LOCAL (d) && GdmPingInterval > 0) {
 	    alarm (GdmPingInterval);
     }
 
@@ -1447,7 +1522,7 @@ gdm_slave_run (GdmDisplay *display)
     } while (greet);
 
     /* If XDMCP stop pinging */
-    if (d->type == TYPE_XDMCP)
+    if ( ! SERVER_IS_LOCAL (d))
 	    alarm (0);
 }
 
@@ -2622,10 +2697,8 @@ gdm_slave_greeter (void)
 		ve_unsetenv ("GDM_TIMED_LOGIN_OK");
 	}
 
-	if (d->type == TYPE_FLEXI) {
+	if (SERVER_IS_FLEXI (d)) {
 		ve_setenv ("GDM_FLEXI_SERVER", "yes", TRUE);
-	} else if (d->type == TYPE_FLEXI_XNEST) {
-		ve_setenv ("GDM_FLEXI_SERVER", "Xnest", TRUE);
 	} else {
 		ve_unsetenv ("GDM_FLEXI_SERVER");
 	}
@@ -3128,7 +3201,7 @@ gdm_slave_chooser (void)
 			char *host = d->chooser_last_line;
 			d->chooser_last_line = NULL;
 
-			if (d->type == TYPE_XDMCP) {
+			if (SERVER_IS_XDMCP (d)) {
 				send_chosen_host (d, host);
 				gdm_slave_quick_exit (DISPLAY_CHOSEN);
 			} else {
@@ -3547,6 +3620,8 @@ session_child_run (struct passwd *pwent,
 		ve_setenv ("GDM_XSERVER_LOCATION", "flexi", TRUE);
 	} else if (d->type == TYPE_FLEXI_XNEST) {
 		ve_setenv ("GDM_XSERVER_LOCATION", "flexi-xnest", TRUE);
+	} else if (d->type == TYPE_XDMCP_PROXY) {
+		ve_setenv ("GDM_XSERVER_LOCATION", "xdmcp-proxy", TRUE);
 	} else {
 		/* huh? */
 		ve_setenv ("GDM_XSERVER_LOCATION", "unknown", TRUE);
@@ -4109,7 +4184,7 @@ gdm_slave_session_start (void)
     }
 
     if (GdmKillInitClients)
-	    gdm_server_whack_clients (d);
+	    gdm_server_whack_clients (d->dsp);
 
     /* Now that we will set up the user authorization we will
        need to run session_stop to whack it */
@@ -4376,7 +4451,7 @@ gdm_slave_session_stop (gboolean run_pos
        This is to fix #126071, that is kill processes that may still hold open
        fd's in the home directory to allow a clean unmount.  However note of course
        that this is a race. */
-    gdm_server_whack_clients (d);
+    gdm_server_whack_clients (d->dsp);
 
 #if defined(_POSIX_PRIORITY_SCHEDULING) && defined(HAVE_SCHED_YIELD)
     /* let the other processes die perhaps or whatnot */
@@ -4592,8 +4667,6 @@ gdm_slave_child_handler (int sig)
 
     gdm_in_signal++;
 
-    gdm_debug ("gdm_slave_child_handler");
-
     old = geteuid ();
     if (old != 0)
 	    seteuid (0);
@@ -4601,8 +4674,6 @@ gdm_slave_child_handler (int sig)
     while ((pid = waitpid (-1, &status, WNOHANG)) > 0) {
         GSList *li;
 
-	gdm_debug ("gdm_slave_child_handler: %d died", pid);
-
 	for (li = slave_waitpids; li != NULL; li = li->next) {
 		GdmWaitPid *wp = li->data;
 		if (wp->pid == pid) {
@@ -4613,13 +4684,6 @@ gdm_slave_child_handler (int sig)
 		}
 	}
 	
-	if (WIFEXITED (status))
-	    gdm_debug ("gdm_slave_child_handler: %d returned %d",
-		       (int)pid, (int)WEXITSTATUS (status));
-	if (WIFSIGNALED (status))
-	    gdm_debug ("gdm_slave_child_handler: %d died of %d",
-		       (int)pid, (int)WTERMSIG (status));
-
 	if (pid == d->greetpid && greet) {
 		if (WIFEXITED (status) &&
 		    WEXITSTATUS (status) == DISPLAY_RESTARTGREETER) {
@@ -4759,8 +4823,7 @@ gdm_slave_handle_usr2_message (void)
 			if (strcmp (&s[1], GDM_NOTIFY_DIRTY_SERVERS) == 0) {
 				/* never restart flexi servers
 				 * they whack themselves */
-				if (d->type != TYPE_FLEXI_XNEST &&
-				    d->type != TYPE_FLEXI)
+				if (!SERVER_IS_FLEXI (d))
 					remanage_asap = TRUE;
 			} else if (strcmp (&s[1], GDM_NOTIFY_SOFT_RESTART_SERVERS) == 0) {
 				/* never restart flexi servers,
@@ -4768,8 +4831,7 @@ gdm_slave_handle_usr2_message (void)
 				/* FIXME: here we should handle actual
 				 * restarts of flexi servers, but it probably
 				 * doesn't matter */
-				if (d->type != TYPE_FLEXI_XNEST &&
-				    d->type != TYPE_FLEXI) {
+				if (!SERVER_IS_FLEXI (d)) {
 					if ( ! d->logged_in) {
 						if (gdm_in_signal > 0) {
 							exit_code_to_use = DISPLAY_REMANAGE;
@@ -4799,8 +4861,6 @@ gdm_slave_usr2_handler (int sig)
 	gdm_in_signal++;
 	in_usr2_signal++;
 
-	gdm_debug ("gdm_slave_usr2_handler: %s got USR2 signal", d->name);
-
 	gdm_slave_handle_usr2_message ();
 
 	in_usr2_signal--;
@@ -5094,11 +5154,11 @@ gdm_slave_whack_temp_auth_file (void)
 	old = geteuid ();
 	if (old != 0)
 		seteuid (0);
-	if (d->xnest_temp_auth_file != NULL) {
-		VE_IGNORE_EINTR (unlink (d->xnest_temp_auth_file));
+	if (d->parent_temp_auth_file != NULL) {
+		VE_IGNORE_EINTR (unlink (d->parent_temp_auth_file));
 	}
-	g_free (d->xnest_temp_auth_file);
-	d->xnest_temp_auth_file = NULL;
+	g_free (d->parent_temp_auth_file);
+	d->parent_temp_auth_file = NULL;
 	if (old != 0)
 		seteuid (old);
 }
@@ -5107,15 +5167,15 @@ static void
 create_temp_auth_file (void)
 {
 	if (d->type == TYPE_FLEXI_XNEST &&
-	    d->xnest_auth_file != NULL) {
-		if (d->xnest_temp_auth_file != NULL) {
-			VE_IGNORE_EINTR (unlink (d->xnest_temp_auth_file));
+	    d->parent_auth_file != NULL) {
+		if (d->parent_temp_auth_file != NULL) {
+			VE_IGNORE_EINTR (unlink (d->parent_temp_auth_file));
 		}
-		g_free (d->xnest_temp_auth_file);
-		d->xnest_temp_auth_file =
+		g_free (d->parent_temp_auth_file);
+		d->parent_temp_auth_file =
 			copy_auth_file (d->server_uid,
 					GdmUserId,
-					d->xnest_auth_file);
+					d->parent_auth_file);
 	}
 }
 
@@ -5123,12 +5183,12 @@ static void
 set_xnest_parent_stuff (void)
 {
 	if (d->type == TYPE_FLEXI_XNEST) {
-		ve_setenv ("GDM_PARENT_DISPLAY", d->xnest_disp, TRUE);
-		if (d->xnest_temp_auth_file != NULL) {
+		ve_setenv ("GDM_PARENT_DISPLAY", d->parent_disp, TRUE);
+		if (d->parent_temp_auth_file != NULL) {
 			ve_setenv ("GDM_PARENT_XAUTHORITY",
-				      d->xnest_temp_auth_file, TRUE);
-			g_free (d->xnest_temp_auth_file);
-			d->xnest_temp_auth_file = NULL;
+				      d->parent_temp_auth_file, TRUE);
+			g_free (d->parent_temp_auth_file);
+			d->parent_temp_auth_file = NULL;
 		}
 	}
 }
@@ -5160,7 +5220,7 @@ gdm_slave_exec_script (GdmDisplay *d, co
 	    }
     }
     if (script == NULL &&
-	d->type == TYPE_XDMCP) {
+	SERVER_IS_XDMCP (d)) {
 	    script = g_build_filename (dir, "XDMCP", NULL);
 	    if (access (script, R_OK|X_OK) != 0) {
 		    g_free (script);
@@ -5250,7 +5310,7 @@ gdm_slave_exec_script (GdmDisplay *d, co
 					    d->name, ".Xservers");
 	ve_setenv ("X_SERVERS", x_servers_file, TRUE);
 	g_free (x_servers_file);
-	if (d->type == TYPE_XDMCP)
+	if (SERVER_IS_XDMCP (d))
 		ve_setenv ("REMOTE_HOST", d->hostname, TRUE);
 
 	/* Runs as root */
@@ -5395,7 +5455,7 @@ gdm_parse_enriched_login (const gchar *s
 	    else
 		    ve_unsetenv ("XAUTHORITY");
 	    ve_setenv ("DISPLAY", display->name, TRUE);
-	    if (display->type == TYPE_XDMCP)
+	    if (SERVER_IS_XDMCP (display))
 		    ve_setenv ("REMOTE_HOST", display->hostname, TRUE);
 	    ve_setenv ("PATH", GdmRootPath, TRUE);
 	    ve_setenv ("SHELL", "/bin/sh", TRUE);
Index: daemon/xdmcp.c
===================================================================
RCS file: /cvs/gnome/gdm2/daemon/xdmcp.c,v
retrieving revision 1.84
diff -u -p -r1.84 xdmcp.c
--- daemon/xdmcp.c	6 Apr 2005 19:37:28 -0000	1.84
+++ daemon/xdmcp.c	21 Apr 2005 14:48:09 -0000
@@ -121,6 +121,7 @@ static gboolean initted = FALSE;
 extern GSList *displays;
 extern gint xdmcp_pending;
 extern gint xdmcp_sessions;
+extern uid_t GdmUserId;
 extern gchar *GdmLogDir;
 extern gchar *GdmServAuthDir;
 
@@ -138,6 +139,11 @@ extern gchar *GdmWilling;	/* The willing
 
 extern gboolean GdmXdmcp;	/* xdmcp enabled */
 
+extern gboolean GdmXdmcpProxy;        /* Proxy X server enabled */
+extern gchar *GdmXdmcpProxyCommand;   /* Proxy X server command */
+extern gchar *GdmXdmcpProxyReconnect; /* Proxy reconnect command */
+
+
 extern gboolean GdmDebug;	/* debug enabled */
 
 /* Local prototypes */
@@ -227,7 +233,7 @@ static void gdm_xdmcp_whack_queued_manag
 static void gdm_xdmcp_send_got_managed_forward (struct sockaddr_in *clnt_sa,
 						struct in_addr *origin);
 static GdmDisplay *gdm_xdmcp_display_lookup (CARD32 sessid);
-static void gdm_xdmcp_display_dispose_check (const gchar *name);
+static void gdm_xdmcp_display_dispose_check (const gchar *hostname, int dspnum);
 static void gdm_xdmcp_displays_check (void);
 static void gdm_forward_query_dispose (GdmForwardQuery *q);
 
@@ -298,7 +304,7 @@ gdm_xdmcp_displays_from_host (struct soc
 
 	for (li = displays; li != NULL; li = li->next) {
 		GdmDisplay *disp = li->data;
-		if (disp->type == TYPE_XDMCP) { 
+		if (SERVER_IS_XDMCP (disp)) {
 #ifdef ENABLE_IPV6
 		    if (disp->addrtype == AF_INET6 &&
 			memcmp (&disp->addr6, &((struct sockaddr_in6 *)addr)->sin6_addr, sizeof (struct in6_addr)) == 0)
@@ -325,15 +331,15 @@ gdm_xdmcp_display_lookup_by_host (struct
 
 	for (li = displays; li != NULL; li = li->next) {
 		GdmDisplay *disp = li->data;
-		if (disp->type == TYPE_XDMCP) {
+		if (SERVER_IS_XDMCP (disp)) {
 #ifdef ENABLE_IPV6
 		    if (disp->addrtype == AF_INET6) {
-			if ((memcmp (&disp->addr6, &((struct sockaddr_in6 *)addr)->sin6_addr, sizeof (struct in6_addr)) == 0) && disp->dispnum == dspnum)
+			if ((memcmp (&disp->addr6, &((struct sockaddr_in6 *)addr)->sin6_addr, sizeof (struct in6_addr)) == 0) && disp->xdmcp_dispnum == dspnum)
 			return disp;
 		    }
 		    else
 #endif
-		    if ((memcmp (&disp->addr, &((struct sockaddr_in *)addr)->sin_addr, sizeof (struct in_addr)) == 0) && disp->dispnum == dspnum)
+		    if ((memcmp (&disp->addr, &((struct sockaddr_in *)addr)->sin_addr, sizeof (struct in_addr)) == 0) && disp->xdmcp_dispnum == dspnum)
 			return disp;
 		}
 	}
@@ -1757,14 +1763,11 @@ gdm_xdmcp_handle_request (struct sockadd
 	}
 
 	if (entered) {
-	    char *disp;
 	    GdmHostent *he;
 		he = gdm_gethostbyaddr (clnt_sa);
-	    disp = g_strdup_printf ("%s:%d", he->hostname, clnt_dspnum);
 
 	    /* Check if we are already talking to this host */
-	    gdm_xdmcp_display_dispose_check (disp);
-	    g_free (disp);
+	    gdm_xdmcp_display_dispose_check (he->hostname, clnt_dspnum);
 
 	    if (xdmcp_pending >= GdmMaxPending) {
 		    gdm_debug ("gdm_xdmcp_handle_request: maximum pending");
@@ -2537,17 +2540,24 @@ gdm_xdmcp_display_alloc (
     GdmDisplay *d = NULL;
     
     d = g_new0 (GdmDisplay, 1);
+
+    if (GdmXdmcpProxy && GdmXdmcpProxyCommand != NULL) {
+	    d->type = TYPE_XDMCP_PROXY;
+	    d->command = g_strdup (GdmXdmcpProxyCommand);
+	    gdm_debug ("Using proxy server for XDMCP: %s\n", d->command);
+    } else {
+	    d->type = TYPE_XDMCP;
+    }
+
     d->logout_action = GDM_LOGOUT_ACTION_NONE;
     d->authfile = NULL;
     d->auths = NULL;
     d->userauth = NULL;
-    d->command = NULL;
     d->greetpid = 0;
     d->servpid = 0;
     d->servstat = 0;
     d->sesspid = 0;
     d->slavepid = 0;
-    d->type = TYPE_XDMCP;
     d->console = FALSE;
     d->dispstat = XDMCP_PENDING;
     d->sessionid = globsessid++;
@@ -2555,6 +2565,7 @@ gdm_xdmcp_display_alloc (
 	    d->sessionid = globsessid++;
     d->acctime = time (NULL);
     d->dispnum = displaynum;
+    d->xdmcp_dispnum = displaynum;
 
     d->handled = TRUE;
     d->tcp_disallowed = FALSE;
@@ -2608,11 +2619,22 @@ gdm_xdmcp_display_alloc (
     d->chooser_last_line = NULL;
 
     d->theme_name = NULL;
-    
+
     /* Secure display with cookie */
     if G_UNLIKELY (! gdm_auth_secure_display (d))
 	gdm_error ("gdm_xdmcp_display_alloc: Error setting up cookies for %s", d->name);
-    
+
+    if (d->type == TYPE_XDMCP_PROXY) {
+	    d->parent_disp = d->name;
+	    d->name = g_strdup (":-1");
+	    d->dispnum = -1;
+
+	    d->server_uid = GdmUserId;
+
+	    d->parent_auth_file = d->authfile;
+	    d->authfile = NULL;
+    }
+
     displays = g_slist_append (displays, d);
     
     xdmcp_pending++;
@@ -2647,22 +2669,23 @@ gdm_xdmcp_display_lookup (CARD32 sessid)
 
 
 static void
-gdm_xdmcp_display_dispose_check (const gchar *name)
+gdm_xdmcp_display_dispose_check (const gchar *hostname, int dspnum)
 {
 	GSList *dlist;
 
-	if (name == NULL)
+	if (hostname == NULL)
 		return;
 
-	gdm_debug ("gdm_xdmcp_display_dispose_check (%s)", name);
+	gdm_debug ("gdm_xdmcp_display_dispose_check (%s:%d)", hostname, dspnum);
 
 	dlist = displays;
 	while (dlist != NULL) {
 		GdmDisplay *d = dlist->data;
 
 		if (d != NULL &&
-		    d->type == TYPE_XDMCP &&
-		    strcmp (d->name, name) == 0) {
+		    SERVER_IS_XDMCP (d) &&
+		    d->xdmcp_dispnum == dspnum &&
+		    strcmp (d->hostname, hostname) == 0) {
 			if (d->dispstat == XDMCP_MANAGED)
 				gdm_display_unmanage (d);
 			else
@@ -2689,7 +2712,7 @@ gdm_xdmcp_displays_check (void)
 		GdmDisplay *d = dlist->data;
 
 		if (d != NULL &&
-		    d->type == TYPE_XDMCP &&
+		    SERVER_IS_XDMCP (d) &&
 		    d->dispstat == XDMCP_PENDING &&
 		    curtime > d->acctime + GdmMaxManageWait) {
 			gdm_debug ("gdm_xdmcp_displays_check: Disposing session id %ld",
@@ -2705,6 +2728,60 @@ gdm_xdmcp_displays_check (void)
 	}
 }
 
+static void
+reconnect_to_parent (GdmDisplay *to)
+{
+	GError *error;
+	gchar *command_argv[10];
+
+	command_argv[0] = GdmXdmcpProxyReconnect;
+	command_argv[1] = "--display";
+	command_argv[2] = to->parent_disp;
+	command_argv[3] = "--display-authfile";
+	command_argv[4] = to->parent_auth_file;
+	command_argv[5] = "--to";
+	command_argv[6] = to->name;
+	command_argv[7] = "--to-authfile";
+	command_argv[8] = to->authfile;
+	command_argv[9] = NULL;
+
+	gdm_debug ("XDMCP: migrating display by running "
+		   "'%s --display %s --display-authfile %s --to %s --to-authfile %s'",
+		   GdmXdmcpProxyReconnect,
+		   to->parent_disp, to->parent_auth_file,
+		   to->name, to->authfile);
+
+	error = NULL;
+	if (!g_spawn_async (NULL, command_argv, NULL, 0, NULL, NULL, NULL, &error)) {
+		gdm_error (_("%s: Failed to run "
+			     "'%s --display %s --display-authfile %s --to %s --to-authfile %s': %s"),
+			   "gdm_xdmcp_migrate",
+			   GdmXdmcpProxyReconnect,
+			   to->parent_disp, to->parent_auth_file,
+			   to->name, to->authfile,
+			   error->message);
+		g_error_free (error);
+	}
+}
+
+void
+gdm_xdmcp_migrate (GdmDisplay *from, GdmDisplay *to)
+{
+	if (from->type != TYPE_XDMCP_PROXY ||
+	    to->type   != TYPE_XDMCP_PROXY)
+		return;
+
+	g_free (to->parent_disp);
+	to->parent_disp = from->parent_disp;
+	from->parent_disp = NULL;
+
+	g_free (to->parent_auth_file);
+	to->parent_auth_file = from->parent_auth_file;
+	from->parent_auth_file = NULL;
+
+	reconnect_to_parent (to);
+}
+
 #else /* HAVE_LIBXDMCP */
 
 /* Here come some empty stubs for no XDMCP support */
@@ -2725,6 +2802,12 @@ void
 gdm_xdmcp_close (void)
 {
 	gdm_error (_("%s: No XDMCP support"), "gdm_xdmcp_close");
+}
+
+void
+gdm_xdmcp_migrate (GdmDisplay *from, GdmDisplay *to)
+{
+	gdm_error (_("%s: No XDMCP support"), "gdm_xdmcp_migrate");
 }
 
 #endif /* HAVE_LIBXDMCP */
Index: daemon/xdmcp.h
===================================================================
RCS file: /cvs/gnome/gdm2/daemon/xdmcp.h,v
retrieving revision 1.4
diff -u -p -r1.4 xdmcp.h
--- daemon/xdmcp.h	28 Oct 2001 19:13:42 -0000	1.4
+++ daemon/xdmcp.h	21 Apr 2005 14:48:09 -0000
@@ -33,6 +33,8 @@ gboolean	gdm_xdmcp_init	(void);
 void		gdm_xdmcp_run	(void);
 void		gdm_xdmcp_close	(void);
 
+void gdm_xdmcp_migrate (GdmDisplay *from, GdmDisplay *to);
+
 #ifdef HAVE_LIBXDMCP
 /* Fix broken X includes */
 int XdmcpReallocARRAY8 (ARRAY8Ptr array, int length);
Index: docs/C/gdm.xml
===================================================================
RCS file: /cvs/gnome/gdm2/docs/C/gdm.xml,v
retrieving revision 1.63
diff -u -p -r1.63 gdm.xml
--- docs/C/gdm.xml	20 Apr 2005 00:47:24 -0000	1.63
+++ docs/C/gdm.xml	21 Apr 2005 14:48:13 -0000
@@ -1919,6 +1919,29 @@ XKeepsCrashing
           </varlistentry>
           
           <varlistentry>
+            <term>EnableProxy</term>
+            <listitem>
+              <synopsis>EnableProxy=false</synopsis>
+              <para>
+                Setting this to true enables support for running XDMCP sessions
+                on a local proxy X server. This may improve the performance of
+                XDMCP sessions, especially on high latency networks, as many
+                X protocol operations can be completed without going over the
+                network.
+              </para>
+              <para>
+                Note, however, that this mode will significantly increase the
+                burden on the server hosting the XDMCP sessions
+              </para>
+              <para>
+                See the <filename>FlexiProxy</filename> and
+                <filename>FlexiProxyDisconnect</filename> options for further
+                 details on how to configure support for this feature.
+              </para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
             <term>HonorIndirect</term>
             <listitem>
               <synopsis>HonorIndirect=true</synopsis>
@@ -2045,6 +2068,42 @@ XKeepsCrashing
                 XDMCP would be used (such as terminal labs), a lag of more
                 than 15 or so seconds would really mean that the terminal was
                 turned off or rebooted and you would want to end the session.
+              </para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>ProxyReconnect</term>
+            <listitem>
+              <synopsis>FlexiProxyReconnect=</synopsis>
+              <para>
+                Setting this option enables experimental support for session
+                migration with XDMCP sessions. This enables users to disconnect
+                from their session and later reconnect to that same session,
+                possibly from a different terminal.
+              </para>
+              <para>
+                In order to use this feature, you must have a nested X server
+                available which supports disconnecting from its parent X server
+                and reconnecting to another X server. Currently, the Distributed
+                Multihead X (DMX) server supports this feature to some extent
+                and other projects like NoMachine NX are busy implementing it.
+              </para>
+              <para>
+                This option should be set to the path of a command which will
+                handle reconnecting the XDMCP proxy to another backend display.
+                A sample implementation for use with DMX is supplied.
+              </para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>ProxyXServer</term>
+            <listitem>
+              <synopsis>ProxyXServer=</synopsis>
+              <para>
+                The X server command line for a XDMCP proxy. Any nested X server
+                like Xnest, Xephr or Xdmx should work fairly well.
               </para>
             </listitem>
           </varlistentry>
Index: utils/.cvsignore
===================================================================
RCS file: /cvs/gnome/gdm2/utils/.cvsignore,v
retrieving revision 1.4
diff -u -p -r1.4 .cvsignore
--- utils/.cvsignore	13 May 2004 01:39:52 -0000	1.4
+++ utils/.cvsignore	21 Apr 2005 14:48:13 -0000
@@ -6,3 +6,4 @@ gdmaskpass
 gdmopen
 gdmmktemp
 gdmtranslate
+gdm-dmx-reconnect-proxy
Index: utils/Makefile.am
===================================================================
RCS file: /cvs/gnome/gdm2/utils/Makefile.am,v
retrieving revision 1.9
diff -u -p -r1.9 Makefile.am
--- utils/Makefile.am	19 Jul 2003 23:35:37 -0000	1.9
+++ utils/Makefile.am	21 Apr 2005 14:48:13 -0000
@@ -20,6 +20,10 @@ libexec_PROGRAMS = \
 # bin_PROGRAMS = \
 #	gdmmktemp
 
+if DMX_SUPPORT
+bin_PROGRAMS = gdm-dmx-reconnect-proxy
+endif
+
 EXTRA_PROGRAMS = gdmaskpass gdmopen
 
 gdmaskpass_SOURCES = \
@@ -47,3 +51,15 @@ gdmtranslate_LDADD = \
 
 #gdmmktemp_LDADD = \
 #	$(INTLLIBS)
+
+if DMX_SUPPORT
+gdm_dmx_reconnect_proxy_SOURCES = \
+	gdm-dmx-reconnect-proxy.c
+
+gdm_dmx_reconnect_proxy_LDADD = \
+	$(GLIB_LIBS) \
+	$(X_EXTRA_LIBS) \
+	$(X_LIBS) \
+	-lX11 \
+	$(DMX_LIBS)
+endif
Index: utils/gdm-dmx-reconnect-proxy.c
===================================================================
RCS file: utils/gdm-dmx-reconnect-proxy.c
diff -N utils/gdm-dmx-reconnect-proxy.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ utils/gdm-dmx-reconnect-proxy.c	21 Apr 2005 14:48:13 -0000
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/dmxext.h>
+
+#include <ve-misc.h>
+
+static char *to_display = NULL;
+static char *backend_display = NULL;
+static char *to_authfile = NULL;
+static char *backend_authfile = NULL;
+
+static GOptionEntry options[] = {
+	{
+		"to", 0, 0, G_OPTION_ARG_STRING, &to_display,
+		N_("DMX display to migrate to"),
+		N_("DISPLAY")
+	},
+	{
+		"display", 0, 0, G_OPTION_ARG_STRING, &backend_display,
+		N_("Backend display name"),
+		N_("DISPLAY")
+	},
+	{
+		"to-authfile", 0, 0, G_OPTION_ARG_STRING, &to_authfile,
+		N_("Xauthority file for destination display"),
+		N_("AUTHFILE")
+	},
+	{
+		"display-authfile", 0, 0, G_OPTION_ARG_STRING, &backend_authfile,
+		N_("Xauthority file for backend display"),
+		N_("AUTHFILE")
+	},
+	{ NULL }
+};
+
+static Display *
+get_dmx_display (const char *display_name,
+		 const char *authfile)
+{
+	Display *display;
+	int event_base, error_base;
+	const char *old_authfile;
+
+	old_authfile = getenv ("XAUTHORITY");
+	ve_setenv ("XAUTHORITY", authfile, TRUE);
+
+	if ((display = XOpenDisplay (display_name)) == NULL)
+		g_printerr (_("Failed to open display \"%s\"\n"), display_name);
+
+	if (display != NULL &&
+	    !DMXQueryExtension (display, &event_base, &error_base)) {
+		g_printerr (_("DMX extension not present on \"%s\"\n"), display_name);
+		XCloseDisplay (display);
+		display = NULL;
+	}
+
+	ve_setenv ("XAUTHORITY", old_authfile, TRUE);
+
+	return display;
+}
+
+int
+main (int argc, char **argv)
+{
+	GOptionContext *options_context;
+	Display *display;
+	DMXScreenAttributes attr;
+	guint mask;
+	int screen;
+
+	setlocale (LC_ALL, "");
+	bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+	textdomain (GETTEXT_PACKAGE);
+
+	options_context = g_option_context_new (_("- migrate a backend display from one DMX display to another"));
+	g_option_context_add_main_entries (options_context, options, GETTEXT_PACKAGE);
+	g_option_context_parse (options_context, &argc, &argv, NULL);
+
+	if (to_display == NULL) {
+		g_printerr (_("You must specify a destination DMX display using %s\n"), "--to");
+		return 1;
+	}
+
+	if (backend_display == NULL) {
+		g_printerr (_("You must specify a backend display which using %s\n"), "--display");
+		return 1;
+	}
+
+	if ((display = get_dmx_display (to_display, to_authfile)) == NULL)
+		return 1;
+
+	/* Note, we have no way yet of using backend_authfile to ensure
+	 * that the DMX server can authenticate against the backend Xserver.
+	 * For now, we must disable access control on the backend server.
+	 */
+
+	mask = 0;
+	screen = 0;
+	if (!DMXAddScreen (display, backend_display, mask, &attr, &screen)) {
+		g_printerr (_("DMXAddScreen \"%s\" failed on \"%s\"\n"),
+			    backend_display, to_display);
+		XCloseDisplay (display);
+		return 1;
+	}
+
+	XCloseDisplay (display);
+
+	return 0;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]