--- gdm-2.6.0.3/config/gdm.conf.in.vnc-support Fri Jun 18 04:07:49 2004 +++ gdm-2.6.0.3/config/gdm.conf.in Fri Jun 18 04:10:18 2004 @@ -234,6 +234,10 @@ # or mail details for some user, or some such. #Willing=@EXPANDED_SYSCONFDIR@/gdm/Xwilling +[vnc] +Enable=true +Port=5900 + [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 --- gdm-2.6.0.3/daemon/Makefile.am.vnc-support Fri Jun 18 04:07:49 2004 +++ gdm-2.6.0.3/daemon/Makefile.am Fri Jun 18 04:10:18 2004 @@ -42,6 +42,8 @@ cookie.h \ xdmcp.c \ xdmcp.h \ + vnc.c \ + vnc.h \ choose.c \ choose.h \ filecheck.c \ --- gdm-2.6.0.3/daemon/display.c.vnc-support Fri Jun 18 04:07:49 2004 +++ gdm-2.6.0.3/daemon/display.c Fri Jun 18 04:10:18 2004 @@ -34,12 +34,14 @@ #include "slave.h" #include "misc.h" #include "xdmcp.h" +#include "vnc.h" #include "choose.h" #include "auth.h" #include "gdm-net.h" /* External vars */ extern gboolean GdmXdmcp; +extern gboolean GdmVnc; extern gint xdmcp_sessions; extern gint flexi_servers; extern gint xdmcp_pending; @@ -166,6 +168,7 @@ static void whack_old_slave (GdmDisplay *d, gboolean kill_connection) { + int doit=0; time_t t = time (NULL); gboolean waitsleep = TRUE; @@ -188,8 +191,18 @@ waitsleep = FALSE; /* Kill slave */ - if (d->slavepid > 1 && - (d->dispstat == DISPLAY_DEAD || kill (d->slavepid, SIGTERM) == 0)) { + if (d->slavepid > 1) + { + if (d->dispstat == DISPLAY_DEAD) + doit = 1; + if (!doit) + { + gdm_debug ("about to kill %d\n", d->slavepid); + doit = (kill (d->slavepid, SIGTERM) == 0); + } + } + if (doit) + { int exitstatus; int ret; wait_again: @@ -300,6 +313,10 @@ if (GdmXdmcp) gdm_xdmcp_close (); + /* Close VNC fd in slave process */ + if (GdmVnc) + gdm_vnc_close (); + gdm_connection_close (fifoconn); fifoconn = NULL; gdm_connection_close (pipeconn); @@ -308,20 +325,22 @@ unixconn = NULL; closelog (); - +#if 0 /* Close everything */ - gdm_close_all_descriptors (0 /* from */, fds[0] /* except */, slave_fifo_pipe_fd /* except2 */); + gdm_close_all_descriptors (0 /* from */, fds[0] /* except */, slave_fifo_pipe_fd /* except2 */, -1); /* No error checking here - if it's messed the best response * is to ignore & try to continue */ gdm_open_dev_null (O_RDONLY); /* open stdin - fd 0 */ gdm_open_dev_null (O_RDWR); /* open stdout - fd 1 */ gdm_open_dev_null (O_RDWR); /* open stderr - fd 2 */ +#endif openlog ("gdm", LOG_PID, LOG_DAEMON); d->slave_notify_fd = fds[0]; +// write(d->vnc_fd, "four", sizeof("four")); gdm_slave_start (d); /* should never retern */ --- gdm-2.6.0.3/daemon/gdm.c.vnc-support Fri Jun 18 04:07:49 2004 +++ gdm-2.6.0.3/daemon/gdm.c Fri Jun 18 04:10:18 2004 @@ -51,6 +51,7 @@ #include "slave.h" #include "server.h" #include "xdmcp.h" +#include "vnc.h" #include "verify.h" #include "display.h" #include "choose.h" @@ -196,6 +197,9 @@ gchar *GdmSoundOnLoginFile = NULL; gchar *GdmConsoleCannotHandle = NULL; +gboolean GdmVnc = FALSE; +gint GdmVncPort = 0; + /* set in the main function */ char **stored_argv = NULL; @@ -209,20 +213,21 @@ static gboolean monte_carlo_sqrt2 = FALSE; -static gboolean +gboolean display_exists (int num) { GSList *li; for (li = displays; li != NULL; li = li->next) { GdmDisplay *disp = li->data; + gdm_debug ("display_exists: server %d!", disp->dispnum); if (disp->dispnum == num) return TRUE; } return FALSE; } -static int +int compare_displays (gconstpointer a, gconstpointer b) { const GdmDisplay *d1 = a; @@ -399,6 +404,9 @@ GdmPingInterval = ve_config_get_int (cfg, GDM_KEY_PINGINTERVAL); GdmWilling = ve_config_get_string (cfg, GDM_KEY_WILLING); + GdmVnc = ve_config_get_bool (cfg, GDM_KEY_VNC); + GdmVncPort = ve_config_get_int (cfg, GDM_KEY_VNCPORT); + GdmStandardXServer = ve_config_get_string (cfg, GDM_KEY_STANDARD_XSERVER); bin = ve_first_word (GdmStandardXServer); if G_UNLIKELY (ve_string_empty (bin) || @@ -576,13 +584,13 @@ } ve_config_free_list_of_strings (list); - if G_UNLIKELY (displays == NULL && ! GdmXdmcp) { + if G_UNLIKELY (displays == NULL && !GdmXdmcp && !GdmVnc) { char *server = NULL; /* if we requested no local servers (there is no console), then don't display errors in console messages */ if (no_console) { - gdm_fail (_("%s: XDMCP disabled and no local servers defined. Aborting!"), "gdm_config_parse"); + gdm_fail (_("%s: XDMCP & Vnc disabled and no local servers defined. Aborting!"), "gdm_config_parse"); } bin = ve_first_word (GdmStandardXServer); @@ -600,7 +608,7 @@ if (server != NULL) { int num = gdm_get_free_display (0 /* start */, 0 /* server uid */); - gdm_error (_("%s: XDMCP disabled and no local servers defined. Adding %s on :%d to allow configuration!"), + gdm_error (_("%s: XDMCP and Vnc disabled and no local servers defined. Adding %s on :%d to allow configuration!"), "gdm_config_parse", server, num); @@ -623,7 +631,7 @@ GDM_CONFIG_FILE); gdm_text_message_dialog (s); GdmPidFile = NULL; - gdm_fail (_("%s: XDMCP disabled and no local servers defined. Aborting!"), "gdm_config_parse"); + gdm_fail (_("%s: XDMCP and Vnc disabled and no local servers defined. Aborting!"), "gdm_config_parse"); } } @@ -1075,7 +1083,7 @@ closelog (); - gdm_close_all_descriptors (0 /* from */, -1 /* except */, -1 /* except2 */); + gdm_close_all_descriptors (0 /* from */, -1, -1); /* No error checking here - if it's messed the best response * is to ignore & try to continue */ @@ -1601,7 +1609,11 @@ gdm_display_unmanage (d); } /* Remote displays will send a request to be managed */ - } else /* TYPE_XDMCP */ { + } + else if (d->type == TYPE_VNC) { + gdm_display_unmanage (d); + } + else /* TYPE_XDMCP */ { gdm_display_unmanage (d); } @@ -2180,11 +2192,16 @@ #endif gdm_debug ("gdm_main: Here we go..."); + gdm_error ("HERE WE GO %d %d\n", GdmXdmcp, GdmVnc); /* Init XDMCP if applicable */ if (GdmXdmcp && ! gdm_wait_for_go) gdm_xdmcp_init(); + /* Init VNC if applicable */ + if (GdmVnc) + gdm_vnc_init(); + create_connections (); /* make sure things (currently /tmp/.ICE-unix and /tmp/.X11-unix) @@ -2200,6 +2217,12 @@ gdm_xdmcp_run(); } + if (GdmVnc) { + gdm_debug ("Accepting VNC connections..."); + gdm_vnc_run(); + } + + /* We always exit via exit(), and sadly we need to g_main_quit() * at times not knowing if it's this main or a recursive one we're * quitting. @@ -2340,6 +2363,49 @@ } } +static gboolean +try_migrate_vnc_login (GdmDisplay *display) +{ + GdmDisplay *new_display; + GSList *li; + + if (ve_string_empty (display->login)) + return FALSE; + + new_display = NULL; + for (li = displays; li != NULL; li = li->next) { + GdmDisplay *d = li->data; + + if (d == display) + continue; + + if (strcmp (display->login, ve_sure_string (d->login)) != 0) + continue; + + new_display = d; + break; + } + + if (new_display == NULL) { + gdm_debug ("try_migrate_vnc_login: no existing logged in session, starting new session for '%s'", + display->login); + return FALSE; + } + + gdm_debug ("try_migrate_vnc_login: migrating login for '%s' from display %s to display %s", + display->login, display->name, new_display->name); + + //FIXMEHARDER + gdm_vnc_migrate_clients_from_server (display->vnc_server, + new_display->vnc_server); + + return TRUE; + + /* FIXME: now we need to inform the slave + * to *not* start a new session + */ +// send_slave_command (display, GDM_NOTIFY_SOFT_RESTART_SERVERS); +} static void gdm_handle_message (GdmConnection *conn, const char *msg, gpointer data) @@ -2486,6 +2552,9 @@ if ( ! logged_in && unixconn != NULL) gdm_kill_subconnections_with_display (unixconn, d); + if (d->type == TYPE_VNC) + gdm_vnc_set_server_logged_in (d->vnc_server, d->logged_in); + /* if the user just logged out, * let's see if it's safe to restart */ if ( ! d->logged_in) { @@ -2560,9 +2629,30 @@ g_free (d->login); d->login = g_strdup (p); gdm_debug ("Got LOGIN == %s", p); + /* send ack */ send_slave_ack (d, NULL); } + } else if (strncmp (msg, GDM_SOP_VNC " ", + strlen (GDM_SOP_VNC " ")) == 0) { + GdmDisplay *d; + long slave_pid; + int vnc_logged_in; + if (sscanf (msg, GDM_SOP_VNC " %ld %d", &slave_pid, + &vnc_logged_in) != 2) + return; + + /* Find out who this slave belongs to */ + d = gdm_display_lookup (slave_pid); + + if (d != NULL) { + /* send ack */ + //FIXMEHARDER + if (try_migrate_vnc_login (d)) + send_slave_command (d, GDM_NOTIFY_VNCQUIT); + else + send_slave_command (d, GDM_NOTIFY_VNCNOQUIT); + } } else if (strncmp (msg, GDM_SOP_QUERYLOGIN " ", strlen (GDM_SOP_QUERYLOGIN " ")) == 0) { GdmDisplay *d; @@ -2589,7 +2679,8 @@ gdm_debug ("Got QUERYLOGIN %s", p); for (li = displays; li != NULL; li = li->next) { GdmDisplay *di = li->data; - if (di->logged_in && + if (di->type != TYPE_VNC && + di->logged_in && di->login != NULL && strcmp (di->login, p) == 0) { if (resp == NULL) { --- gdm-2.6.0.3/daemon/gdm.h.vnc-support Fri Jun 18 04:07:49 2004 +++ gdm-2.6.0.3/daemon/gdm.h Fri Jun 18 04:10:18 2004 @@ -34,10 +34,12 @@ #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_VNC 5 #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_VNC) #define SERVER_IS_FLEXI(d) ((d)->type == TYPE_FLEXI || \ (d)->type == TYPE_FLEXI_XNEST) @@ -220,6 +222,9 @@ #define GDM_KEY_PINGINTERVAL "xdmcp/PingIntervalSeconds=15" #define GDM_KEY_WILLING "xdmcp/Willing=" EXPANDED_SYSCONFDIR "/gdm/Xwilling" +#define GDM_KEY_VNC "vnc/Enable=false" +#define GDM_KEY_VNCPORT "vnc/Port=5000" + #define GDM_KEY_GTK_THEME "gui/GtkTheme=Default" #define GDM_KEY_GTKRC "gui/GtkRC=" #define GDM_KEY_ICONWIDTH "gui/MaxIconWidth=128" @@ -301,6 +306,9 @@ typedef struct _GdmConnection GdmConnection; #endif /* TYPEDEF_GDM_CONNECTION */ +typedef struct _GdmVncServer GdmVncServer; +typedef struct _GdmVncClient GdmVncClient; + typedef enum { GDM_LOGOUT_ACTION_NONE = 0, GDM_LOGOUT_ACTION_HALT, @@ -407,6 +415,9 @@ uid_t server_uid; GdmConnection *socket_conn; + /* Vnc connection */ + GdmVncServer *vnc_server; + /* Notification connection */ int master_notify_fd; /* write part of the connection */ int slave_notify_fd; /* read part of the connection */ @@ -522,6 +533,7 @@ #define GDM_SOP_CHOOSERPID "CHOOSERPID" /* */ #define GDM_SOP_LOGGED_IN "LOGGED_IN" /* */ #define GDM_SOP_LOGIN "LOGIN" /* */ +#define GDM_SOP_VNC "VNC" /* */ #define GDM_SOP_COOKIE "COOKIE" /* */ #define GDM_SOP_QUERYLOGIN "QUERYLOGIN" /* */ /* if user already logged in somewhere, the ack response will be @@ -586,6 +598,8 @@ /* commands, seel GDM_SLAVE_NOTIFY_COMMAND */ #define GDM_NOTIFY_DIRTY_SERVERS "DIRTY_SERVERS" #define GDM_NOTIFY_SOFT_RESTART_SERVERS "SOFT_RESTART_SERVERS" +#define GDM_NOTIFY_VNCQUIT "VNCQUIT" +#define GDM_NOTIFY_VNCNOQUIT "VNCNOQUIT" #define GDM_NOTIFY_GO "GO" #define GDM_NOTIFY_TWIDDLE_POINTER "TWIDDLE_POINTER" --- gdm-2.6.0.3/daemon/server.c.vnc-support Fri Jun 18 04:07:49 2004 +++ gdm-2.6.0.3/daemon/server.c Fri Jun 18 04:10:18 2004 @@ -40,6 +40,7 @@ #include #include "gdm.h" +#include "vnc.h" #include "server.h" #include "misc.h" #include "xdmcp.h" @@ -559,7 +560,7 @@ * fortunately there is no such case yet and probably * never will, but just for code anality's sake */ gdm_sleep_no_signal (5); - } else if (d->server_uid != 0) { + } else if ((d->server_uid != 0) || (d->type == TYPE_VNC)) { int i; /* FIXME: This is not likely to work in reinit, @@ -989,7 +990,11 @@ query_in_arglist = TRUE; } - if (resolve_flags && GdmDisallowTCP && ! query_in_arglist) { + if ( + resolve_flags && GdmDisallowTCP && + !query_in_arglist && d->type != TYPE_VNC + ) + { argv[len++] = g_strdup ("-nolisten"); argv[len++] = g_strdup ("tcp"); d->tcp_disallowed = TRUE; @@ -1071,27 +1076,32 @@ closelog (); - /* close things */ - gdm_close_all_descriptors (0 /* from */, -1 /* except */, -1 /* except2 */); - /* No error checking here - if it's messed the best response * is to ignore & try to continue */ - gdm_open_dev_null (O_RDONLY); /* open stdin - fd 0 */ - gdm_open_dev_null (O_RDWR); /* open stdout - fd 1 */ - gdm_open_dev_null (O_RDWR); /* open stderr - fd 2 */ + if (d->type == TYPE_VNC) + gdm_vnc_prepare_server_descriptors (d->vnc_server); + else + { + /* close things */ + gdm_close_all_descriptors (0 /* from */, -1, -1); + gdm_open_dev_null (O_RDONLY); /* open stdin - fd 0 */ + gdm_open_dev_null (O_RDWR); /* open stdout - fd 1 */ + gdm_open_dev_null (O_RDWR); /* open stderr - fd 2 */ + } openlog ("gdm", LOG_PID, LOG_DAEMON); /* Rotate the X server logs */ rotate_logs (d->name); - /* Log all output from spawned programs to a file */ + /* Log all output from spawned programs to a file */ logfile = gdm_make_filename (GdmLogDir, d->name, ".log"); VE_IGNORE_EINTR (unlink (logfile)); VE_IGNORE_EINTR (logfd = open (logfile, O_CREAT|O_TRUNC|O_WRONLY|O_EXCL, 0644)); if (logfd != -1) { - VE_IGNORE_EINTR (dup2 (logfd, 1)); + if (d->type != TYPE_VNC) + VE_IGNORE_EINTR (dup2 (logfd, 1)); VE_IGNORE_EINTR (dup2 (logfd, 2)); close (logfd); } else { @@ -1324,6 +1334,7 @@ d->dispstat = DISPLAY_UNBORN; d->greetpid = 0; d->name = g_strdup_printf (":%d", id); + gdm_error ("NEW name is %s", d->name); d->hostname = g_strdup (hostname); /* Not really used for not XDMCP */ memset (&(d->addr), 0, sizeof (d->addr)); @@ -1354,6 +1365,8 @@ d->sleep_before_run = 0; d->login = NULL; + d->vnc_server = NULL; + d->timed_login_ok = FALSE; d->slave_notify_fd = -1; --- gdm-2.6.0.3/daemon/slave.c.vnc-support Fri Jun 18 04:07:49 2004 +++ gdm-2.6.0.3/daemon/slave.c Fri Jun 18 04:10:18 2004 @@ -88,6 +88,7 @@ static gboolean greet = FALSE; static gboolean configurator = FALSE; static gboolean remanage_asap = FALSE; +static gboolean remanage_vnc = FALSE; static gboolean got_xfsz_signal = FALSE; static gboolean do_timed_login = FALSE; /* if this is true, login the timed login */ @@ -136,6 +137,7 @@ /* wait for a GO in the SOP protocol */ extern gboolean gdm_wait_for_go; +gboolean gdm_wait_for_vnc_go = TRUE; /* Configuration option variables */ extern gchar *GdmUser; @@ -867,11 +869,13 @@ check_notifies_now (); gdm_debug ("gdm_slave_start: Loop Thingie"); +// write(display->vnc_fd, "five", sizeof("five")); gdm_slave_run (display); /* remote and flexi only run once */ - if (display->type != TYPE_LOCAL || - ! parent_exists ()) { + if ((display->type != TYPE_LOCAL && display->type != TYPE_VNC) + || ! parent_exists ()) { + gdm_debug ("gdm_slave_start: murder"); gdm_server_stop (display); gdm_slave_send_num (GDM_SOP_XPID, 0); gdm_slave_quick_exit (DISPLAY_REMANAGE); @@ -1204,10 +1208,13 @@ * the XOpenDisplay to find out if a server is ready (as with Xnest) */ d->dsp = NULL; + gdm_debug ("gdm_slave_run: HERE 1\n"); + /* if this is local display start a server if one doesn't * exist */ - if (SERVER_IS_LOCAL (d) && - d->servpid <= 0) { + if (SERVER_IS_LOCAL (d) && d->servpid <= 0) { + gdm_debug ("gdm_slave_run: HERE 2\n"); +// write(d->vnc_fd, "six", sizeof("six")); if G_UNLIKELY ( ! gdm_server_start (d, TRUE /* try_again_if_busy */, FALSE /* treat_as_flexi */, @@ -1419,6 +1426,9 @@ gdm_slave_session_start (); } + if (remanage_vnc) + gdm_slave_quick_exit (DISPLAY_REMANAGE); + gdm_slave_send_num (GDM_SOP_LOGGED_IN, FALSE); d->logged_in = FALSE; gdm_slave_send_string (GDM_SOP_LOGIN, ""); @@ -3844,6 +3854,49 @@ gdm_debug ("gdm_slave_session_start: Attempting session for user '%s'", login); + if (d->type == TYPE_VNC) + { + //FIXME + gdm_error (_("%s: VNC login occuring, test for existing vnc session \ + and swap connection if necessary"), "gdm_slave_session_start"); + gdm_wait_for_vnc_go = TRUE; + gdm_slave_send_num (GDM_SOP_VNC, TRUE); + + while G_UNLIKELY (gdm_wait_for_vnc_go) { + struct timeval tv; + /* Wait 1 second. */ + tv.tv_sec = 1; + tv.tv_usec = 0; + select (0, NULL, NULL, NULL, &tv); + /* don't want to use sleep since we're using alarm + for pinging */ + } + gdm_wait_for_vnc_go = TRUE; + + + + if (remanage_vnc) + { + gdm_error (_("VNC duplicated login, returning to greeter %d\n"), remanage_asap); + + + gdm_wait_for_vnc_go = TRUE; + while G_UNLIKELY (gdm_wait_for_vnc_go) { + struct timeval tv; + /* Wait 1 second. */ + tv.tv_sec = 1; + tv.tv_usec = 0; + select (0, NULL, NULL, NULL, &tv); + /* don't want to use sleep since we're using alarm + for pinging */ + } + gdm_wait_for_vnc_go = TRUE; + + remanage_vnc = FALSE; + return; + } + } + pwent = getpwnam (login); if G_UNLIKELY (pwent == NULL) { @@ -4431,8 +4484,11 @@ } if (session_started) { - SIGNAL_EXIT_WITH_JMP (d, JMP_SESSION_STOP_AND_QUIT); + fprintf(stderr, "ARSE\n"); +// SIGNAL_EXIT_WITH_JMP (d, JMP_SESSION_STOP_AND_QUIT); + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); } else { + fprintf(stderr, "ONE\n"); SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); } @@ -4689,6 +4745,16 @@ remanage_asap = TRUE; } } + } else if (strcmp (&s[1], GDM_NOTIFY_VNCQUIT) == 0) { + gdm_debug ("VNCQUIT"); + gdm_wait_for_vnc_go = FALSE; + remanage_vnc = TRUE; + gdm_got_ack = TRUE; + } else if (strcmp (&s[1], GDM_NOTIFY_VNCNOQUIT) == 0) { + gdm_debug ("VNCNOQUIT"); + gdm_wait_for_vnc_go = FALSE; + remanage_vnc = FALSE; + gdm_got_ack = TRUE; } else if (strcmp (&s[1], GDM_NOTIFY_GO) == 0) { gdm_wait_for_go = FALSE; } else if (strcmp (&s[1], GDM_NOTIFY_TWIDDLE_POINTER) == 0) { --- gdm-2.6.0.3/daemon/vnc.c.vnc-support Fri Jun 18 04:07:49 2004 +++ gdm-2.6.0.3/daemon/vnc.c Fri Jun 18 04:10:18 2004 @@ -0,0 +1,987 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "gdm.h" +#include "vnc.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "display.h" +#include "server.h" + +/* FIXME: should be configurable */ +#define GdmXvncServer "/usr/bin/Xvnc -managed -SecurityTypes=TLS -AuthTypes=none -ac -Log=*:stderr:100 -depth 24" +#define GdmXvncServers 15 /* FIXME: implement limit on no. of VNC servers */ + +typedef struct _GdmVncData GdmVncData; +typedef struct _GdmVncState GdmVncState; + +struct _GdmVncData { + int vnc_fd; + GIOChannel *vnc_channel; + guint vnc_watch; +}; + +struct _GdmVncServer { + GdmDisplay *display; + + int daemon_server_fd; + int slave_server_fd; + GIOChannel *server_channel; + guint server_watch; + GString *server_data_buf; + + GSList *clients; + GSList *clients_awaiting_id; + GdmVncServer *migrating_to; + + guint logged_in : 1; +}; + +struct _GdmVncState { + int major_version; + int minor_version; + + int width; + int height; + + int depth; + int bpp; + gulong red_mask; + gulong green_mask; + gulong blue_mask; + + GArray *encodings; + + int security_type; + int auth_type; + + guint big_endian : 1; + guint true_color : 1; +}; + +struct _GdmVncClient { + int vnc_sock; + char *client_addr; + int client_port; + + GdmVncServer *server; + GdmVncServer *migrate_to; + + int client_id; + + GdmVncState state; + + guint reading_state : 1; + guint have_state : 1; +}; + +/* FIXME: should be in a header */ +gboolean display_exists (int num); + +static void gdm_vnc_migrate_client (GdmVncClient *client, + GdmVncServer *server); +static void gdm_vnc_start_client_migration (GdmVncClient *client, + GdmVncServer *to_server); + +static gboolean gdm_vnc_handle_new_connection (GIOChannel *source, + GIOCondition condition, + GdmVncData *vnc_data); +static gboolean gdm_vnc_handle_server_data (GIOChannel *source, + GIOCondition condition, + GdmVncServer *server); + +static GdmVncData gdm_vnc_data = { 0, }; + +extern gboolean GdmVnc; +extern int GdmVncPort; +extern GSList *displays; + +gboolean +gdm_vnc_init (void) +{ + struct sockaddr_in saddr; + int listenfd = 0; + int opt; + + gdm_debug ("gdm_vnc_init: Initialising VNC support on port %d", GdmVncPort); + + if G_UNLIKELY ((listenfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { + gdm_error (_("%s: Unable to create socket: %s"), + "gdm_vnc_init", g_strerror (errno)); + goto error_and_close; + } + + if G_UNLIKELY (fcntl (listenfd, F_SETFL, O_NONBLOCK) < 0) { + gdm_error (_("%s: Unable to make socket non-blocking: %s"), + "gdm_vnc_init", g_strerror (errno)); + goto error_and_close; + } + + opt = 1; + if G_UNLIKELY (setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (int)) < 0) { + gdm_error (_("%s: Error setting SO_REUSEADDR socket option: %s"), + "gdm_vnc_init", g_strerror (errno)); + goto error_and_close; + } + + memset (&saddr, 0, sizeof (struct sockaddr_in)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons (GdmVncPort); + saddr.sin_addr.s_addr = htonl (INADDR_ANY); + + if G_UNLIKELY (bind (listenfd, (struct sockaddr *) &saddr, sizeof (saddr)) < 0) { + gdm_error (_("%s: Unable to bind to port %d: %s"), + "gdm_vnc_init", GdmVncPort, g_strerror (errno)); + goto error_and_close; + } + + if G_UNLIKELY (listen (listenfd, 1024) < 0 ) { + gdm_error (_("%s: Unable to create listening socket on port %d: %s"), + "gdm_vnc_init", GdmVncPort, g_strerror (errno)); + goto error_and_close; + } + + gdm_vnc_data.vnc_fd = listenfd; + + return TRUE; + + error_and_close: + if (listenfd != 0) + VE_IGNORE_EINTR (close (listenfd)); + listenfd = 0; + + GdmVnc = FALSE; + + return FALSE; +} + +void +gdm_vnc_run (void) +{ + g_assert (gdm_vnc_data.vnc_fd > 0); + + gdm_vnc_data.vnc_channel = g_io_channel_unix_new (gdm_vnc_data.vnc_fd); + gdm_vnc_data.vnc_watch = g_io_add_watch (gdm_vnc_data.vnc_channel, + G_IO_IN|G_IO_PRI, + (GIOFunc) gdm_vnc_handle_new_connection, + &gdm_vnc_data); +} + +void +gdm_vnc_close (void) +{ + if (gdm_vnc_data.vnc_watch > 0) + g_source_remove (gdm_vnc_data.vnc_watch); + gdm_vnc_data.vnc_watch = 0; + + if (gdm_vnc_data.vnc_fd > 0) + VE_IGNORE_EINTR (close (gdm_vnc_data.vnc_fd)); + gdm_vnc_data.vnc_fd = 0; +} + +static inline gboolean +setup_server_socketpair (int *daemon_server_fd, + int *slave_server_fd) +{ + int fds [2] = { 0, 0 }; + + g_assert (daemon_server_fd != NULL); + g_assert (slave_server_fd != NULL); + + if G_UNLIKELY (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + gdm_error (_("%s: Unable to create socket pair: %s"), + "gdm_vnc_setup_server_socketpair", g_strerror (errno)); + return FALSE; + } + + if G_UNLIKELY (fcntl (fds [0], F_SETFL, O_NONBLOCK) < 0) { + gdm_error (_("%s: Unable to make socket non-blocking: %s"), + "gdm_vnc_setup_server_socketpair", g_strerror (errno)); + VE_IGNORE_EINTR (close (fds [0])); + VE_IGNORE_EINTR (close (fds [1])); + return FALSE; + } + + *daemon_server_fd = fds [0]; + *slave_server_fd = fds [1]; + + return TRUE; +} + +static gboolean +gdm_vnc_handle_new_connection (GIOChannel *source, + GIOCondition condition, + GdmVncData *vnc_data) +{ + GdmVncClient *client; + GdmVncServer *server; + struct sockaddr_in caddr; + int caddr_len = sizeof (caddr); + int sock; + int display_num; + + if G_UNLIKELY ((sock = accept (vnc_data->vnc_fd, (struct sockaddr *) &caddr, &caddr_len)) < 0) { + gdm_error (_("%s: Error accepting on socket: %s"), + "gdm_vnc_handle_new_connection", g_strerror (errno)); + return TRUE; + } + + if G_UNLIKELY (fcntl (sock, F_SETFL, O_NONBLOCK) < 0) { + gdm_error (_("%s: Error setting O_NONBLOCK flag on socket: %s"), + "gdm_vnc_handle_new_connection", g_strerror (errno)); + close (sock); + return TRUE; + } + + client = g_new0 (GdmVncClient, 1); + + client->vnc_sock = sock; + client->client_addr = g_strdup (inet_ntoa (caddr.sin_addr)); + client->client_port = ntohs (caddr.sin_port); + + gdm_debug ("gdm_vnc_handle_new_connection: Accepted new VNC client from %s:%d on port %d", + client->client_addr, client->client_port, GdmVncPort); + + client->server = server = g_new0 (GdmVncServer, 1); + + server->clients_awaiting_id = + g_slist_append (server->clients_awaiting_id, client); + + if G_UNLIKELY (!setup_server_socketpair (&server->daemon_server_fd, &server->slave_server_fd)) { + gdm_vnc_shutdown_server (server); + return TRUE; + } + + /* FIXME: this just isn't right. We should be doing it + * with gdm_get_free_display() and it probably + * makes more sense to do it in the slave + */ + display_num = 0; + while (display_exists (display_num)) + display_num++; + + gdm_debug ("gdm_vnc_handle_new_connection: new server number is %d", display_num); + + server->display = gdm_server_alloc (display_num, GdmXvncServer); + if G_UNLIKELY (server->display == NULL) { + gdm_error (_("%s: Error allocating new server"), "gdm_vnc_handle_new_connection"); + gdm_vnc_shutdown_server (server); + return TRUE; + } + + server->display->type = TYPE_VNC; + server->display->vnc_server = server; + displays = g_slist_append (displays, server->display); + + if (!gdm_display_manage (server->display)) { + gdm_error (_("%s: Failed to start display for VNC client from %s:%d on port %d"), + "gdm_vnc_handle_new_connection", + client->client_addr, client->client_port, GdmVncPort); + gdm_vnc_shutdown_server (server); + return TRUE; + } + + VE_IGNORE_EINTR (close (server->slave_server_fd)); + server->slave_server_fd = 0; + + server->server_data_buf = g_string_new (NULL); + server->server_channel = g_io_channel_unix_new (server->daemon_server_fd); + server->server_watch = g_io_add_watch (server->server_channel, + G_IO_IN|G_IO_PRI, + (GIOFunc) gdm_vnc_handle_server_data, + server); + + gdm_debug("adding server_watch %d\n", server->server_watch); + return TRUE; +} + +void +gdm_vnc_finalize_client (GdmVncClient *client) +{ + if (client->state.encodings != NULL) + g_array_free (client->state.encodings, TRUE); + client->state.encodings = NULL; + + if (client->client_addr != NULL) + g_free (client->client_addr); + client->client_addr = NULL; + + if (client->vnc_sock != 0) + VE_IGNORE_EINTR (close (client->vnc_sock)); + client->vnc_sock = 0; + + g_free (client); +} + +/* FIXME: maybe this should be gdm_vnc_dispose_server() which gets + * called by gdm_display_dispose() + * We'd always call gdm_display_unmanage() rather than calling + * this function directly and this one wouldn't unmanage the + * display. + * i.e. disposing the vnc server is a side effect of unmanaging + * the display, not the other way round + */ +void +gdm_vnc_shutdown_server (GdmVncServer *server) +{ + GSList *l; + + gdm_debug ("gdm_vnc_shutdown_server: shutting down VNC server for display %s", + server->display ? server->display->name : ""); + + if (server->display) + { + gdm_debug ("start of odd\n"); + gdm_display_unmanage (server->display); + gdm_debug ("end of odd\n"); + } + server->display = NULL; + + for (l = server->clients_awaiting_id; l != NULL; l = l->next) + gdm_vnc_finalize_client (l->data); + g_slist_free (server->clients_awaiting_id); + server->clients_awaiting_id = NULL; + + for (l = server->clients; l != NULL; l = l->next) + gdm_vnc_finalize_client (l->data); + g_slist_free (server->clients); + server->clients = NULL; + + if (server->server_data_buf != NULL) + g_string_free (server->server_data_buf, TRUE); + server->server_data_buf = NULL; + + gdm_debug("removing server_watch %d\n", server->server_watch); + if (server->server_watch != 0) + g_source_remove (server->server_watch); + server->server_watch = 0; + + if (server->daemon_server_fd != 0) + VE_IGNORE_EINTR (close (server->daemon_server_fd)); + server->daemon_server_fd = 0; + + if (server->slave_server_fd != 0) + VE_IGNORE_EINTR (close (server->slave_server_fd)); + server->slave_server_fd = 0; + + g_free (server); +} + +static gboolean +gdm_vnc_handle_server_command (GdmVncServer *server, + const char *command) +{ + if (strncmp (command, "CLIENT=", strlen ("CLIENT=")) == 0) { + int client_id; + + if G_UNLIKELY (sscanf (command, "CLIENT=%d\n", &client_id) != 1) { + gdm_error (_("%s: incorrect CLIENT command format from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + return TRUE; + } + + gdm_debug ("gdm_vnc_handle_server_command: got client ID %d from VNC server\n", + client_id); + + if (server->clients_awaiting_id != NULL) { + GdmVncClient *client = server->clients_awaiting_id->data; + + client->client_id = client_id; + + server->clients_awaiting_id = + g_slist_delete_link (server->clients_awaiting_id, + server->clients_awaiting_id); + server->clients = g_slist_append (server->clients, client); + + if (server->migrating_to) + gdm_vnc_start_client_migration (client, server->migrating_to); + } + + return TRUE; + + } else if (strncmp (command, "CLIENT GONE=", strlen ("CLIENT GONE=")) == 0) { + GSList *l; + int client_id; + + if G_UNLIKELY (sscanf (command, "CLIENT GONE=%d\n", &client_id) != 1) { + gdm_error (_("%s: incorrect CLIENT GONE command format from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + return TRUE; + } + + gdm_debug ("gdm_vnc_handle_server_command: client ID %d is gone", client_id); + + for (l = server->clients; l != NULL; l = l->next) { + GdmVncClient *client = l->data; + + if (client->client_id != client_id) + continue; + + server->clients = g_slist_delete_link (server->clients, l); + + if (client->migrate_to != NULL && client->have_state) { + gdm_vnc_migrate_client (client, client->migrate_to); + client->migrate_to = FALSE; + } else { + gdm_vnc_finalize_client (client); + } + + if (!server->logged_in && server->clients == NULL) + { + gdm_debug ("this one is bad\n"); + gdm_vnc_shutdown_server (server); + return FALSE; + } + + break; + } + + return TRUE; + + } else if (strncmp (command, "CLIENT STATE START=", strlen ("CLIENT STATE START=")) == 0) { + GSList *l; + int client_id; + + if G_UNLIKELY (sscanf (command, "CLIENT STATE START=%d\n", &client_id) != 1) { + gdm_error (_("%s: incorrect CLIENT STATE START command format from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + return TRUE; + } + + gdm_debug ("gdm_vnc_handle_server_command: reading client state for client %d\n", + client_id); + + for (l = server->clients; l != NULL; l = l->next) { + GdmVncClient *client = l->data; + + if (client->client_id != client_id) + continue; + + client->reading_state = TRUE; + client->have_state = FALSE; + break; + } + + return TRUE; + + } else if (strcmp (command, "CLIENT STATE END\n") == 0) { + GSList *l; + + for (l = server->clients; l != NULL; l = l->next) { + GdmVncClient *client = l->data; + + if (!client->reading_state) + continue; + + gdm_debug ("gdm_vnc_handle_server_command: finished reading client %d state", + client->client_id); + + client->reading_state = FALSE; + client->have_state = TRUE; + break; + } + + return TRUE; + + } else if (strncmp (command, "RFB VERSION=", strlen ("RFB VERSION=")) == 0) { + GSList *l; + + for (l = server->clients; l != NULL; l = l->next) { + GdmVncClient *client = l->data; + int major, minor; + + if (!client->reading_state) + continue; + + if G_UNLIKELY (sscanf (command, "RFB VERSION=%d.%d\n", &major, &minor) != 2) { + gdm_error (_("%s: incorrect RFB VERSION command format from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + return TRUE; + } + + gdm_debug ("gdm_vnc_handle_server_command: client %d RFB protocol version %d.%d", + client->client_id, major, minor); + + client->state.major_version = major; + client->state.minor_version = minor; + break; + } + + return TRUE; + + } else if (strncmp (command, "GEOMETRY=", strlen ("GEOMETRY=")) == 0) { + GSList *l; + + for (l = server->clients; l != NULL; l = l->next) { + GdmVncClient *client = l->data; + int width, height; + + if (!client->reading_state) + continue; + + if G_UNLIKELY (sscanf (command, "GEOMETRY=%d,%d\n", &width, &height) != 2) { + gdm_error (_("%s: incorrect GEOMETRY command format from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + return TRUE; + } + + gdm_debug ("gdm_vnc_handle_server_command: framebuffer geometry for client %d is %dx%d", + client->client_id, width, height); + + client->state.width = width; + client->state.height = height; + break; + } + + return TRUE; + + } else if (!strncmp (command, "PIXEL FORMAT=", strlen ("PIXEL FORMAT="))) { + GSList *l; + + for (l = server->clients; l != NULL; l = l->next) { + GdmVncClient *client = l->data; + int depth, bpp; + int big_endian, true_color; + gulong red_mask, green_mask, blue_mask; + + if (!client->reading_state) + continue; + + if G_UNLIKELY (sscanf (command, "PIXEL FORMAT=%d,%d,%d,%d,0x%lx,0x%lx,0x%lx\n", + &depth, &bpp, &big_endian, &true_color, + &red_mask, &green_mask, &blue_mask) != 7) { + gdm_error (_("%s: incorrect PIXEL FORMAT command format from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + return TRUE; + } + + gdm_debug ("gdm_vnc_handle_server_command: pixel format is bpp=%d, depth=%d, " + "big endian=%d, true color=%d, red=0x%lx, green=0x%lx, blue=0x%lx", + depth, bpp, big_endian, true_color, red_mask, green_mask, blue_mask); + + client->state.depth = depth; + client->state.bpp = bpp; + client->state.big_endian = big_endian != 0; + client->state.true_color = true_color != 0; + client->state.red_mask = red_mask; + client->state.green_mask = green_mask; + client->state.blue_mask = blue_mask; + break; + } + + return TRUE; + + } else if (!strncmp (command, "ENCODINGS=", strlen ("ENCODINGS="))) { + GSList *l; + + for (l = server->clients; l != NULL; l = l->next) { + GdmVncClient *client = l->data; + char **encodings; + int i; + + if (!client->reading_state) + continue; + + if (client->state.encodings == NULL) + client->state.encodings = g_array_new (FALSE, FALSE, sizeof (guint)); + else + g_array_set_size (client->state.encodings, 0); + + encodings = g_strsplit (command + strlen ("ENCODINGS="), ",", 0); + for (i = 0; encodings [i] != NULL; i++) { + guint encoding; + + if G_UNLIKELY (sscanf (encodings [i], "0x%x", &encoding) != 1) { + gdm_error (_("%s: incorrect ENCODINGS command format from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + return TRUE; + } + + g_array_append_val (client->state.encodings, encoding); + } + + gdm_debug ("gdm_vnc_handle_server_command: got %d encodings from VNC server: %s\n", + client->state.encodings->len, command + strlen ("ENCODINGS=")); + + } + + return TRUE; + + } else if (strncmp (command, "SECURITY TYPE=", strlen ("SECURITY TYPE=")) == 0) { + GSList *l; + + for (l = server->clients; l != NULL; l = l->next) { + GdmVncClient *client = l->data; + int security_type; + + if (!client->reading_state) + continue; + + if G_UNLIKELY (sscanf (command, "SECURITY TYPE=%d\n", &security_type) != 1) { + gdm_error (_("%s: incorrect SECURITY TYPE command format from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + return TRUE; + } + + gdm_debug ("gdm_vnc_handle_server_command: security type for client %d is %d", + client->client_id, security_type); + + client->state.security_type = security_type; + break; + } + + return TRUE; + + } else if (strncmp (command, "AUTH TYPE=", strlen ("AUTH TYPE=")) == 0) { + GSList *l; + + for (l = server->clients; l != NULL; l = l->next) { + GdmVncClient *client = l->data; + int auth_type; + + if (!client->reading_state) + continue; + + if G_UNLIKELY (sscanf (command, "AUTH TYPE=%d\n", &auth_type) != 1) { + gdm_error (_("%s: incorrect AUTH TYPE command format from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + return TRUE; + } + + gdm_debug ("gdm_vnc_handle_server_command: auth type for client %d is %d", + client->client_id, auth_type); + + client->state.auth_type = auth_type; + break; + } + + return TRUE; + } + + gdm_error (_("%s: unknown command from VNC server: '%s'"), + "gdm_vnc_handle_server_command", command); + + return TRUE; +} + +static gboolean +gdm_vnc_handle_server_data (GIOChannel *source, + GIOCondition condition, + GdmVncServer *server) +{ + GIOStatus status; + + do +{ + gdm_debug("this server_watch is %d\n", server->server_watch); + + status = g_io_channel_read_line_string (source, + server->server_data_buf, + NULL, NULL); + gdm_debug("server_watch is %d\n", server->server_watch); + gdm_debug("XVNC string is %s\n", server->server_data_buf ? server->server_data_buf->str : ""); + switch (status) { + case G_IO_STATUS_ERROR: + gdm_error (_("%s: got error from VNC server: %s"), + "gdm_vnc_handle_server_data", g_strerror (errno)); + return FALSE; + case G_IO_STATUS_EOF: + gdm_error (_("%s: got EOF from VNC server on display %s"), + "gdm_vnc_handle_server_data", server->display->name); + goto error_shutdown; + + case G_IO_STATUS_AGAIN: + return TRUE; + + default: /* G_IO_STATUS_NORMAL */ + break; + } + + if (!gdm_vnc_handle_server_command(server, server->server_data_buf->str)) + return FALSE; + + g_string_truncate (server->server_data_buf, 0); +} while (status == G_IO_STATUS_NORMAL); + + return TRUE; + + error_shutdown: + gdm_vnc_shutdown_server (server); + + return FALSE; +} + + +static inline void +dup2_or_fail (int oldfd, int newfd) +{ + int ret; + VE_IGNORE_EINTR (ret = dup2 (oldfd, newfd)); + if (ret < 0) + gdm_fail (_("%s: Failed to redirect output or input of child process: %s"), + "gdm_vnc_prepare_server_desciptors", g_strerror (errno)); +} + +void +gdm_vnc_prepare_server_descriptors (GdmVncServer *server) +{ + GdmVncClient *client; + + if (server->clients_awaiting_id == NULL) + gdm_fail (_("%s: no VNC client to launch a server for?"), + "gdm_vnc_prepare_server_descriptors"); + else if (g_slist_length (server->clients_awaiting_id) > 1) + gdm_error (_("%s: more than one VNC client for this server? Ignoring other clients"), + "gdm_vnc_prepare_server_descriptors"); + + client = (GdmVncClient *) server->clients_awaiting_id->data; + + gdm_close_all_descriptors (0, client->vnc_sock, server->slave_server_fd); + + /* make VNC socket be stdin of the child */ + dup2_or_fail (client->vnc_sock, 0); + VE_IGNORE_EINTR (close (client->vnc_sock)); + + /* make the manager socket be stdout */ + dup2_or_fail (server->slave_server_fd, 1); + VE_IGNORE_EINTR (close (server->slave_server_fd)); + + /* open stderr - fd 2 */ + gdm_open_dev_null (O_RDWR); + + server->daemon_server_fd = 0; + server->slave_server_fd = 0; +} + +static void +gdm_vnc_send_server_command (GdmVncServer *server, + const char *command) +{ + int len; + + len = strlen (command); + while (len > 0) { + int n; + + VE_IGNORE_EINTR (n = write (server->daemon_server_fd, command, len)); + if (n <= 0 && errno != EAGAIN) { + gdm_error (_("%s: Error writing message to VNC server on display %s: %s"), + "gdm_vnc_send_server_command", + server->display->name, g_strerror (errno)); + break; + } + + len -= n; + command += n; + } +} + +static void +gdm_vnc_send_server_command_with_fd (GdmVncServer *server, + const char *command, + int fd) +{ + union { + struct cmsghdr cmsg; + char control [CMSG_SPACE (sizeof (int))]; + } msg_control; + struct cmsghdr *cmsg; + struct msghdr msg; + struct iovec iov [1]; + + /* put the descriptor in the ancillary data */ + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof (msg_control); + + cmsg = CMSG_FIRSTHDR (&msg); + + cmsg->cmsg_len = CMSG_LEN (sizeof (int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + *((int *) CMSG_DATA (cmsg)) = fd; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_flags = 0; + + iov [0].iov_base = (char *) command; + iov [0].iov_len = strlen (command); + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + while (iov [0].iov_len > 0) { + int n; + + VE_IGNORE_EINTR (n = sendmsg (server->daemon_server_fd, &msg, 0)); + if (n <= 0 && errno != EAGAIN) { + gdm_error (_("%s: Error writing message to VNC server on display %s: %s"), + "gdm_vnc_send_server_command", + server->display->name, g_strerror (errno)); + break; + + } + + iov [0].iov_base += n; + iov [0].iov_len -= n; + } +} + +static void +gdm_vnc_send_encodings_command (GdmVncServer *server, + GArray *encodings) +{ + GString *command; + int i; + + if (encodings == NULL) + return; + + command = g_string_new ("ENCODINGS="); + + i = 0; + while (i < encodings->len) { + g_string_append_printf (command, + "0x%x", + g_array_index (encodings, guint, i)); + + if (++i < encodings->len) + g_string_append_c (command, ','); + } + + g_string_append_c (command, '\n'); + + gdm_vnc_send_server_command (server, command->str); + + g_string_free (command, TRUE); +} + +static void +gdm_vnc_migrate_client (GdmVncClient *client, + GdmVncServer *server) +{ + char *command; + gdm_debug ("gdm_vnc_migrate_client: migrating client %d\n", client->client_id); +#if 0 + gdm_debug ("gdm_vnc_migrate_client: migrating client %d from display %s to %s", + client->client_id, client->server ? client->server->display->name : "NULL", server->display->name); +#endif + + gdm_vnc_send_server_command (server, "CLIENT STATE START\n"); + + command = g_strdup_printf ("RFB VERSION=%d.%d\n", + client->state.major_version, + client->state.minor_version); + gdm_vnc_send_server_command (server, command); + g_free (command); + + command = g_strdup_printf ("GEOMETRY=%d,%d\n", + client->state.width, + client->state.height); + gdm_vnc_send_server_command (server, command); + g_free (command); + + command = g_strdup_printf ("PIXEL FORMAT=%d,%d,%d,%d,0x%lx,0x%lx,0x%lx\n", + client->state.depth, + client->state.bpp, + client->state.big_endian ? 1 : 0, + client->state.true_color ? 1 : 0, + client->state.red_mask, + client->state.green_mask, + client->state.blue_mask); + gdm_vnc_send_server_command (server, command); + g_free (command); + + gdm_vnc_send_encodings_command (server, client->state.encodings); + + if (client->state.security_type) { + command = g_strdup_printf ("SECURITY TYPE=%d\n", client->state.security_type); + gdm_vnc_send_server_command (server, command); + g_free (command); + } + + if (client->state.auth_type) { + command = g_strdup_printf ("AUTH TYPE=%d\n", client->state.auth_type); + gdm_vnc_send_server_command (server, command); + g_free (command); + } + + gdm_vnc_send_server_command (server, "CLIENT STATE END\n"); + + gdm_vnc_send_server_command_with_fd (server, "NEW CLIENT\n", client->vnc_sock); + + client->server = server; + server->clients_awaiting_id = + g_slist_append (server->clients_awaiting_id, client); +} + +static void +gdm_vnc_start_client_migration (GdmVncClient *client, + GdmVncServer *to_server) +{ + char *command; + + command = g_strdup_printf ("END CLIENT=%d\n", client->client_id); + gdm_vnc_send_server_command (client->server, command); + g_free (command); + + client->migrate_to = to_server; +} + +void +gdm_vnc_migrate_clients_from_server (GdmVncServer *from_server, + GdmVncServer *to_server) +{ + GSList *l; + + from_server->migrating_to = to_server; + from_server->logged_in = FALSE; + + for (l = from_server->clients; l != NULL; l = l->next) + gdm_vnc_start_client_migration (l->data, to_server); +} + +void +gdm_vnc_set_server_logged_in (GdmVncServer *server, + gboolean logged_in) +{ + gdm_debug ("gdm_vnc_set_server_logged_in: %smarking VNC server on display %s as logged in", + logged_in ? "" : "un", server->display->name); + + server->logged_in = logged_in != FALSE; + + if (!server->logged_in && server->clients == NULL) + { + gdm_debug ("this one is good\n"); + gdm_vnc_shutdown_server (server); + } +} --- gdm-2.6.0.3/daemon/vnc.h.vnc-support Fri Jun 18 04:07:49 2004 +++ gdm-2.6.0.3/daemon/vnc.h Fri Jun 18 04:10:18 2004 @@ -0,0 +1,46 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __GDM_VNC_H__ +#define __GDM_VNC_H__ + +#include + +#include "gdm.h" + +G_BEGIN_DECLS + +gboolean gdm_vnc_init (void); +void gdm_vnc_run (void); +void gdm_vnc_close (void); + +void gdm_vnc_shutdown_server (GdmVncServer *server); +void gdm_vnc_finalize_client (GdmVncClient *client); + +void gdm_vnc_set_server_logged_in (GdmVncServer *server, + gboolean logged_in); +void gdm_vnc_migrate_clients_from_server (GdmVncServer *from_server, + GdmVncServer *to_server); + +/* Called right before exec()-ing Xvnc after forking from the slave */ +void gdm_vnc_prepare_server_descriptors (GdmVncServer *server); + +G_END_DECLS + +#endif /* __GDM_VNC_H__ */