summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark McLoughlin <mark@skynet.ie>2005-04-22 16:29:20 +0000
committerMark McLoughlin <markmc@src.gnome.org>2005-04-22 16:29:20 +0000
commitcbacaed0e62147451be3883e64ec55898caf8d15 (patch)
tree9a1177f5dccf2cd8d5ed9c1c46972cc1ecd47727
parentedfe5f290bb21db973205b7186f802df8b6ffa74 (diff)
downloadgdm-cbacaed0e62147451be3883e64ec55898caf8d15.tar.gz
Add the ability to make GDM spawn XDMCP sessions on a local X proxy
2005-04-22 Mark McLoughlin <mark@skynet.ie> Add the ability to make GDM spawn XDMCP sessions on a local X proxy server. Experimental support for disconnect/reconnect or "session migration" is also implemented using DMX. More details in bug #301602. * configure.in: require glib 2.6.0 and check for DMX. * config/gdm.conf.in: add xdmpc/EnableProxy, xdmcp/ProxyXServer and xdmcp/ProxyReconnect config keys. * docs/C/gdm.xml: document new config keys. * daemon/gdm.h: add TYPE_XDMCP_PROXY server type, add #defines for the new config keys, re-name various xnest related GdmDisplay members to be applicable for all proxy servers, add xdmcp_dispnum GdmDisplay member, modify the format of GDM_SOP_QUERYLOGIN and add GDM_SOP_AUTHFILE and GDM_SOP_MIGRATE. * daemon/gdm.c: read the new config keys, change format of what GDM_SOP_QUERYLOGIN returns, implement GDM_SOP_AUTHFILE and GDM_SOP_MIGRATE. * daemon/xdmcp.h: add gdm_xdmcp_migrate. * daemon/xdmcp.c: when xdmpc/EnableProxy and xdmcp/ProxyXServer, set things up such that a proxy sever (which displays on the requesting X server) is run locally by the slave. Also implement migrating the proxy server from one parent display to another using the xdmcp/ProxyReconnect command. * daemon/server.c: if we're running a proxy server (e.g. Xnest or Xdmx), hold a connection open to the parent display so that it doesn't reset. Run proxy servers with -display rather than setting $DISPLAY. * daemon/slave.c: Once we've successfully logged the user in, send SOP_QUERYLOGIN to the slave - if the result from that indicates that the user is already logged in and that session is migratable (e.g. by reconnecting a proxy server or changing the VT), then migrate to the existing session using SOP_MIGRATE. In the case of an Xdmcp proxy, hold open the connection to the parent display so that it doesn't reset. Exit the slave as soon as that connection closes. * utils/Makefile.am, utils/gdm-dmx-reconnect-proxy.c: implementation of xdmcp/ProxyReconnect for use with Xdmx.
-rw-r--r--ChangeLog50
-rw-r--r--config/gdm.conf.in9
-rw-r--r--configure.in69
-rw-r--r--daemon/display.c29
-rw-r--r--daemon/gdm.c119
-rw-r--r--daemon/gdm.h28
-rw-r--r--daemon/server.c126
-rw-r--r--daemon/server.h2
-rw-r--r--daemon/slave.c231
-rw-r--r--daemon/xdmcp.c121
-rw-r--r--daemon/xdmcp.h2
-rw-r--r--docs/C/gdm.xml59
-rw-r--r--utils/.cvsignore1
-rw-r--r--utils/Makefile.am16
-rw-r--r--utils/gdm-dmx-reconnect-proxy.c133
15 files changed, 816 insertions, 179 deletions
diff --git a/ChangeLog b/ChangeLog
index f84886e1..71a69459 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,53 @@
+2005-04-22 Mark McLoughlin <mark@skynet.ie>
+
+ Add the ability to make GDM spawn XDMCP sessions on a local
+ X proxy server. Experimental support for disconnect/reconnect
+ or "session migration" is also implemented using DMX.
+ More details in bug #301602.
+
+ * configure.in: require glib 2.6.0 and check for DMX.
+
+ * config/gdm.conf.in: add xdmpc/EnableProxy, xdmcp/ProxyXServer
+ and xdmcp/ProxyReconnect config keys.
+
+ * docs/C/gdm.xml: document new config keys.
+
+ * daemon/gdm.h: add TYPE_XDMCP_PROXY server type, add #defines
+ for the new config keys, re-name various xnest related GdmDisplay
+ members to be applicable for all proxy servers, add xdmcp_dispnum
+ GdmDisplay member, modify the format of GDM_SOP_QUERYLOGIN and
+ add GDM_SOP_AUTHFILE and GDM_SOP_MIGRATE.
+
+ * daemon/gdm.c: read the new config keys, change format of what
+ GDM_SOP_QUERYLOGIN returns, implement GDM_SOP_AUTHFILE and
+ GDM_SOP_MIGRATE.
+
+ * daemon/xdmcp.h: add gdm_xdmcp_migrate.
+
+ * daemon/xdmcp.c: when xdmpc/EnableProxy and xdmcp/ProxyXServer,
+ set things up such that a proxy sever (which displays on the
+ requesting X server) is run locally by the slave.
+ Also implement migrating the proxy server from one parent display
+ to another using the xdmcp/ProxyReconnect command.
+
+ * daemon/server.c: if we're running a proxy server (e.g. Xnest
+ or Xdmx), hold a connection open to the parent display so that
+ it doesn't reset.
+ Run proxy servers with -display rather than setting $DISPLAY.
+
+ * daemon/slave.c:
+ Once we've successfully logged the user in, send SOP_QUERYLOGIN
+ to the slave - if the result from that indicates that the user
+ is already logged in and that session is migratable (e.g. by
+ reconnecting a proxy server or changing the VT), then migrate
+ to the existing session using SOP_MIGRATE.
+ In the case of an Xdmcp proxy, hold open the connection to the
+ parent display so that it doesn't reset. Exit the slave as
+ soon as that connection closes.
+
+ * utils/Makefile.am, utils/gdm-dmx-reconnect-proxy.c: implementation
+ of xdmcp/ProxyReconnect for use with Xdmx.
+
2005-04-21 Mark McLoughlin <mark@skynet.ie>
Don't use gdm_debug() from signal handlers - the message
diff --git a/config/gdm.conf.in b/config/gdm.conf.in
index 973c2b96..ea270856 100644
--- a/config/gdm.conf.in
+++ b/config/gdm.conf.in
@@ -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@ -geometry 768x576
+#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
diff --git a/configure.in b/configure.in
index 1ee26e5f..77531cae 100644
--- a/configure.in
+++ b/configure.in
@@ -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 >= $GTK_REQUIRED libgnomeui-2.0 >= $LIBGNOMEUI
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 =>
diff --git a/daemon/display.c b/daemon/display.c
index aab9d865..8b2c9793 100644
--- a/daemon/display.c
+++ b/daemon/display.c
@@ -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;
diff --git a/daemon/gdm.c b/daemon/gdm.c
index 3e4abcd4..9c735511 100644
--- a/daemon/gdm.c
+++ b/daemon/gdm.c
@@ -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, const char *msg, gpointer data)
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, const char *msg, gpointer data)
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, const char *msg, gpointer data)
/* 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, int type, const char *server,
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 *conn, const char *msg, gpointer data)
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);
}
diff --git a/daemon/gdm.h b/daemon/gdm.h
index bc92345d..157425e5 100644
--- a/daemon/gdm.h
+++ b/daemon/gdm.h
@@ -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> */
diff --git a/daemon/server.c b/daemon/server.c
index a7e5039a..0bb84425 100644
--- a/daemon/server.c
+++ b/daemon/server.c
@@ -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 *disp)
* 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 char *vtarg)
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) {
@@ -1382,43 +1443,42 @@ gdm_server_alloc (gint id, const gchar *command)
}
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);
}
diff --git a/daemon/server.h b/daemon/server.h
index 1b23c506..131d023b 100644
--- a/daemon/server.h
+++ b/daemon/server.h
@@ -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,
diff --git a/daemon/slave.c b/daemon/slave.c
index d5514dbf..398d1524 100644
--- a/daemon/slave.c
+++ b/daemon/slave.c
@@ -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];
-
- 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)
+ char *migrate_to = NULL;
+
+ /* 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 (const char *user)
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 (const char *user)
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 (r <= 0)
+ return TRUE;
- if (vt >= 0) {
- if (r == 2) /* Abort */
+ 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 (const char *user)
*/
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_post_session,
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 */
@@ -4748,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,
@@ -4757,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;
@@ -5081,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);
}
@@ -5094,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);
}
}
@@ -5110,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;
}
}
}
@@ -5147,7 +5220,7 @@ gdm_slave_exec_script (GdmDisplay *d, const gchar *dir, const char *login,
}
}
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);
@@ -5237,7 +5310,7 @@ gdm_slave_exec_script (GdmDisplay *d, const gchar *dir, const char *login,
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 */
@@ -5382,7 +5455,7 @@ gdm_parse_enriched_login (const gchar *s, GdmDisplay *display)
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);
diff --git a/daemon/xdmcp.c b/daemon/xdmcp.c
index 798af81a..868ae3d1 100644
--- a/daemon/xdmcp.c
+++ b/daemon/xdmcp.c
@@ -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 script */
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_managed_forwards (struct sockaddr_in *clnt_sa
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 sockaddr_in *addr)
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 sockaddr_in *addr, int dspnum)
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 sockaddr_in *clnt_sa, gint len)
}
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 */
@@ -2727,6 +2804,12 @@ 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 */
/* EOF */
diff --git a/daemon/xdmcp.h b/daemon/xdmcp.h
index 55165933..48afd1f2 100644
--- a/daemon/xdmcp.h
+++ b/daemon/xdmcp.h
@@ -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);
diff --git a/docs/C/gdm.xml b/docs/C/gdm.xml
index 2ed65cd4..4a3503c6 100644
--- a/docs/C/gdm.xml
+++ b/docs/C/gdm.xml
@@ -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>
@@ -2050,6 +2073,42 @@ XKeepsCrashing
</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>
+
+ <varlistentry>
<term>Willing</term>
<listitem>
<synopsis>Willing=&lt;etc&gt;/gdm/Xwilling</synopsis>
diff --git a/utils/.cvsignore b/utils/.cvsignore
index 80868fe5..9f61016a 100644
--- a/utils/.cvsignore
+++ b/utils/.cvsignore
@@ -6,3 +6,4 @@ gdmaskpass
gdmopen
gdmmktemp
gdmtranslate
+gdm-dmx-reconnect-proxy
diff --git a/utils/Makefile.am b/utils/Makefile.am
index f22e4363..7ec28603 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -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
diff --git a/utils/gdm-dmx-reconnect-proxy.c b/utils/gdm-dmx-reconnect-proxy.c
new file mode 100644
index 00000000..352db3f7
--- /dev/null
+++ b/utils/gdm-dmx-reconnect-proxy.c
@@ -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;
+}