diff options
author | Mark McLoughlin <mark@skynet.ie> | 2005-04-22 16:29:20 +0000 |
---|---|---|
committer | Mark McLoughlin <markmc@src.gnome.org> | 2005-04-22 16:29:20 +0000 |
commit | cbacaed0e62147451be3883e64ec55898caf8d15 (patch) | |
tree | 9a1177f5dccf2cd8d5ed9c1c46972cc1ecd47727 | |
parent | edfe5f290bb21db973205b7186f802df8b6ffa74 (diff) | |
download | gdm-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-- | ChangeLog | 50 | ||||
-rw-r--r-- | config/gdm.conf.in | 9 | ||||
-rw-r--r-- | configure.in | 69 | ||||
-rw-r--r-- | daemon/display.c | 29 | ||||
-rw-r--r-- | daemon/gdm.c | 119 | ||||
-rw-r--r-- | daemon/gdm.h | 28 | ||||
-rw-r--r-- | daemon/server.c | 126 | ||||
-rw-r--r-- | daemon/server.h | 2 | ||||
-rw-r--r-- | daemon/slave.c | 231 | ||||
-rw-r--r-- | daemon/xdmcp.c | 121 | ||||
-rw-r--r-- | daemon/xdmcp.h | 2 | ||||
-rw-r--r-- | docs/C/gdm.xml | 59 | ||||
-rw-r--r-- | utils/.cvsignore | 1 | ||||
-rw-r--r-- | utils/Makefile.am | 16 | ||||
-rw-r--r-- | utils/gdm-dmx-reconnect-proxy.c | 133 |
15 files changed, 816 insertions, 179 deletions
@@ -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=<etc>/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; +} |