evolution-3.6.4/smclient/eggsmclient-xsmp.c

No issues found

   1 /*
   2  * Copyright (C) 2007 Novell, Inc.
   3  *
   4  * Inspired by various other pieces of code including GsmClient (C)
   5  * 2001 Havoc Pennington, GnomeClient (C) 1998 Carsten Schaar, and twm
   6  * session code (C) 1998 The Open Group.
   7  *
   8  * This library is free software; you can redistribute it and/or
   9  * modify it under the terms of the GNU Library General Public
  10  * License as published by the Free Software Foundation; either
  11  * version 2 of the License, or (at your option) any later version.
  12  *
  13  * This library is distributed in the hope that it will be useful,
  14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16  * Library General Public License for more details.
  17  *
  18  * You should have received a copy of the GNU Library General Public
  19  * License along with this library; if not, write to the
  20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  21  * Boston, MA 02111-1307, USA.
  22  */
  23 
  24 #ifdef HAVE_CONFIG_H
  25 #include <config.h>
  26 #endif
  27 
  28 #include "eggsmclient.h"
  29 #include "eggsmclient-private.h"
  30 
  31 #include "eggdesktopfile.h"
  32 
  33 #include <errno.h>
  34 #include <fcntl.h>
  35 #include <stdlib.h>
  36 #include <string.h>
  37 #include <unistd.h>
  38 #include <X11/SM/SMlib.h>
  39 
  40 #include <gdk/gdkx.h>
  41 
  42 #define EGG_TYPE_SM_CLIENT_XSMP \
  43 	(egg_sm_client_xsmp_get_type ())
  44 #define EGG_SM_CLIENT_XSMP(obj) \
  45 	(G_TYPE_CHECK_INSTANCE_CAST \
  46 	((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP))
  47 #define EGG_SM_CLIENT_XSMP_CLASS(class) \
  48 	(G_TYPE_CHECK_CLASS_CAST \
  49 	((class), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
  50 #define EGG_IS_SM_CLIENT_XSMP(obj) \
  51 	(G_TYPE_CHECK_INSTANCE_TYPE \
  52 	((obj), EGG_TYPE_SM_CLIENT_XSMP))
  53 #define EGG_IS_SM_CLIENT_XSMP_CLASS(class) \
  54 	(G_TYPE_CHECK_CLASS_TYPE \
  55 	((class), EGG_TYPE_SM_CLIENT_XSMP))
  56 #define EGG_SM_CLIENT_XSMP_GET_CLASS(obj) \
  57 	(G_TYPE_INSTANCE_GET_CLASS \
  58 	((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
  59 
  60 typedef struct _EggSMClientXSMP        EggSMClientXSMP;
  61 typedef struct _EggSMClientXSMPClass   EggSMClientXSMPClass;
  62 
  63 /* These mostly correspond to the similarly-named states in section
  64  * 9.1 of the XSMP spec. Some of the states there aren't represented
  65  * here, because we don't need them. SHUTDOWN_CANCELLED is slightly
  66  * different from the spec; we use it when the client is IDLE after a
  67  * ShutdownCancelled message, but the application is still interacting
  68  * and doesn't know the shutdown has been cancelled yet.
  69  */
  70 typedef enum
  71 {
  72   XSMP_STATE_IDLE,
  73   XSMP_STATE_SAVE_YOURSELF,
  74   XSMP_STATE_INTERACT_REQUEST,
  75   XSMP_STATE_INTERACT,
  76   XSMP_STATE_SAVE_YOURSELF_DONE,
  77   XSMP_STATE_SHUTDOWN_CANCELLED,
  78   XSMP_STATE_CONNECTION_CLOSED
  79 } EggSMClientXSMPState;
  80 
  81 static const gchar *state_names[] = {
  82   "idle",
  83   "save-yourself",
  84   "interact-request",
  85   "interact",
  86   "save-yourself-done",
  87   "shutdown-cancelled",
  88   "connection-closed"
  89 };
  90 
  91 #define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
  92 
  93 struct _EggSMClientXSMP
  94 {
  95   EggSMClient parent;
  96 
  97   SmcConn connection;
  98   gchar *client_id;
  99 
 100   EggSMClientXSMPState state;
 101   gchar **restart_command;
 102   gboolean set_restart_command;
 103   gint restart_style;
 104 
 105   guint idle;
 106 
 107   /* Current SaveYourself state */
 108   guint expecting_initial_save_yourself : 1;
 109   guint need_save_state : 1;
 110   guint need_quit_requested : 1;
 111   guint interact_errors : 1;
 112   guint shutting_down : 1;
 113 
 114   /* Todo list */
 115   guint waiting_to_set_initial_properties : 1;
 116   guint waiting_to_emit_quit : 1;
 117   guint waiting_to_emit_quit_cancelled : 1;
 118   guint waiting_to_save_myself : 1;
 119 
 120 };
 121 
 122 struct _EggSMClientXSMPClass
 123 {
 124   EggSMClientClass parent_class;
 125 
 126 };
 127 
 128 static void     sm_client_xsmp_startup (EggSMClient *client,
 129 					const gchar  *client_id);
 130 static void     sm_client_xsmp_set_restart_command (EggSMClient  *client,
 131 						    gint           argc,
 132 						    const gchar  **argv);
 133 static void     sm_client_xsmp_will_quit (EggSMClient *client,
 134 					  gboolean     will_quit);
 135 static gboolean sm_client_xsmp_end_session (EggSMClient         *client,
 136 					    EggSMClientEndStyle  style,
 137 					    gboolean  request_confirmation);
 138 
 139 static void xsmp_save_yourself      (SmcConn   smc_conn,
 140 				     SmPointer client_data,
 141 				     gint       save_style,
 142 				     Bool      shutdown,
 143 				     gint       interact_style,
 144 				     Bool      fast);
 145 static void xsmp_die                (SmcConn   smc_conn,
 146 				     SmPointer client_data);
 147 static void xsmp_save_complete      (SmcConn   smc_conn,
 148 				     SmPointer client_data);
 149 static void xsmp_shutdown_cancelled (SmcConn   smc_conn,
 150 				     SmPointer client_data);
 151 static void xsmp_interact           (SmcConn   smc_conn,
 152 				     SmPointer client_data);
 153 
 154 static SmProp *array_prop        (const gchar    *name,
 155 				  ...);
 156 static SmProp *ptrarray_prop     (const gchar    *name,
 157 				  GPtrArray     *values);
 158 static SmProp *string_prop       (const gchar    *name,
 159 				  const gchar    *value);
 160 static SmProp *card8_prop        (const gchar    *name,
 161 				  guchar  value);
 162 
 163 static void set_properties         (EggSMClientXSMP *xsmp, ...);
 164 static void delete_properties      (EggSMClientXSMP *xsmp, ...);
 165 
 166 static GPtrArray *generate_command (gchar       **restart_command,
 167 				    const gchar  *client_id,
 168 				    const gchar  *state_file);
 169 
 170 static void save_state            (EggSMClientXSMP *xsmp);
 171 static void do_save_yourself      (EggSMClientXSMP *xsmp);
 172 static void update_pending_events (EggSMClientXSMP *xsmp);
 173 
 174 static void     ice_init             (void);
 175 static gboolean process_ice_messages (IceConn       ice_conn);
 176 static void     smc_error_handler    (SmcConn       smc_conn,
 177 				      Bool          swap,
 178 				      gint           offending_minor_opcode,
 179 				      gulong offending_sequence,
 180 				      gint           error_class,
 181 				      gint           severity,
 182 				      SmPointer     values);
 183 
 184 G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
 185 
 186 static void
 187 egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
 188 {
 189   xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
 190   xsmp->connection = NULL;
 191   xsmp->restart_style = SmRestartIfRunning;
 192 }
 193 
 194 static void
 195 egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *class)
 196 {
 197   EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (class);
 198 
 199   sm_client_class->startup             = sm_client_xsmp_startup;
 200   sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
 201   sm_client_class->will_quit           = sm_client_xsmp_will_quit;
 202   sm_client_class->end_session         = sm_client_xsmp_end_session;
 203 }
 204 
 205 EggSMClient *
 206 egg_sm_client_xsmp_new (void)
 207 {
 208   if (!g_getenv ("SESSION_MANAGER"))
 209     return NULL;
 210 
 211   return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
 212 }
 213 
 214 static gboolean
 215 sm_client_xsmp_set_initial_properties (gpointer user_data)
 216 {
 217   EggSMClientXSMP *xsmp = user_data;
 218   EggDesktopFile *desktop_file;
 219   GPtrArray *clone, *restart;
 220   gchar pid_str[64];
 221 
 222   if (xsmp->idle)
 223     {
 224       g_source_remove (xsmp->idle);
 225       xsmp->idle = 0;
 226     }
 227   xsmp->waiting_to_set_initial_properties = FALSE;
 228 
 229   if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
 230     xsmp->restart_style = SmRestartNever;
 231 
 232   /* Parse info out of desktop file */
 233   desktop_file = egg_get_desktop_file ();
 234   if (desktop_file)
 235     {
 236       GError *err = NULL;
 237       gchar *cmdline, **argv;
 238       gint argc;
 239 
 240       if (xsmp->restart_style == SmRestartIfRunning)
 241 	{
 242 	  if (egg_desktop_file_get_boolean (desktop_file,
 243 					    "X-GNOME-AutoRestart", NULL))
 244 	    xsmp->restart_style = SmRestartImmediately;
 245 	}
 246 
 247       if (!xsmp->set_restart_command)
 248 	{
 249 	  cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
 250 	  if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
 251 	    {
 252 	      egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
 253 						 argc, (const gchar **) argv);
 254 	      g_strfreev (argv);
 255 	    }
 256 	  else
 257 	    {
 258 	      g_warning ("Could not parse Exec line in desktop file: %s",
 259 			 err->message);
 260 	      g_error_free (err);
 261 	    }
 262 	  g_free (cmdline);
 263 	}
 264     }
 265 
 266   if (!xsmp->set_restart_command)
 267     xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
 268 
 269   clone = generate_command (xsmp->restart_command, NULL, NULL);
 270   restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
 271 
 272   g_debug ("Setting initial properties");
 273 
 274   /* Program, CloneCommand, RestartCommand, and UserID are required.
 275    * ProcessID isn't required, but the SM may be able to do something
 276    * useful with it.
 277    */
 278   g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ());
 279   set_properties (xsmp,
 280 		  string_prop   (SmProgram, g_get_prgname ()),
 281 		  ptrarray_prop (SmCloneCommand, clone),
 282 		  ptrarray_prop (SmRestartCommand, restart),
 283 		  string_prop   (SmUserID, g_get_user_name ()),
 284 		  string_prop   (SmProcessID, pid_str),
 285 		  card8_prop    (SmRestartStyleHint, xsmp->restart_style),
 286 		  NULL);
 287   g_ptr_array_free (clone, TRUE);
 288   g_ptr_array_free (restart, TRUE);
 289 
 290   if (desktop_file)
 291     {
 292       set_properties (
 293 		xsmp, string_prop ("_GSM_DesktopFile",
 294 		egg_desktop_file_get_source (desktop_file)), NULL);
 295     }
 296 
 297   update_pending_events (xsmp);
 298   return FALSE;
 299 }
 300 
 301 /* This gets called from two different places: xsmp_die() (when the
 302  * server asks us to disconnect) and process_ice_messages() (when the
 303  * server disconnects unexpectedly).
 304  */
 305 static void
 306 sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
 307 {
 308   SmcConn connection;
 309 
 310   if (!xsmp->connection)
 311     return;
 312 
 313   g_debug ("Disconnecting");
 314 
 315   connection = xsmp->connection;
 316   xsmp->connection = NULL;
 317   SmcCloseConnection (connection, 0, NULL);
 318   xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
 319 
 320   xsmp->waiting_to_save_myself = FALSE;
 321   update_pending_events (xsmp);
 322 }
 323 
 324 static void
 325 sm_client_xsmp_startup (EggSMClient *client,
 326                         const gchar *client_id)
 327 {
 328   EggSMClientXSMP *xsmp = (EggSMClientXSMP *) client;
 329   SmcCallbacks callbacks;
 330   gchar *ret_client_id;
 331   gchar error_string_ret[256];
 332 
 333   xsmp->client_id = g_strdup (client_id);
 334 
 335   ice_init ();
 336   SmcSetErrorHandler (smc_error_handler);
 337 
 338   callbacks.save_yourself.callback      = xsmp_save_yourself;
 339   callbacks.die.callback                = xsmp_die;
 340   callbacks.save_complete.callback      = xsmp_save_complete;
 341   callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
 342 
 343   callbacks.save_yourself.client_data      = xsmp;
 344   callbacks.die.client_data                = xsmp;
 345   callbacks.save_complete.client_data      = xsmp;
 346   callbacks.shutdown_cancelled.client_data = xsmp;
 347 
 348   client_id = NULL;
 349   error_string_ret[0] = '\0';
 350   xsmp->connection =
 351     SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
 352 		       SmcSaveYourselfProcMask | SmcDieProcMask |
 353 		       SmcSaveCompleteProcMask |
 354 		       SmcShutdownCancelledProcMask,
 355 		       &callbacks,
 356 		       xsmp->client_id, &ret_client_id,
 357 		       sizeof (error_string_ret), error_string_ret);
 358 
 359   if (!xsmp->connection)
 360     {
 361       g_warning ("Failed to connect to the session manager: %s\n",
 362 		 error_string_ret[0] ?
 363 		 error_string_ret : "no error message given");
 364       xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
 365       return;
 366     }
 367 
 368   /* We expect a pointless initial SaveYourself if either (a) we
 369    * didn't have an initial client ID, or (b) we DID have an initial
 370    * client ID, but the server rejected it and gave us a new one.
 371    */
 372   if (!xsmp->client_id ||
 373       (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
 374     xsmp->expecting_initial_save_yourself = TRUE;
 375 
 376   if (ret_client_id)
 377     {
 378       g_free (xsmp->client_id);
 379       xsmp->client_id = g_strdup (ret_client_id);
 380       free (ret_client_id);
 381 
 382       gdk_x11_set_sm_client_id (xsmp->client_id);
 383 
 384       g_debug ("Got client ID \"%s\"", xsmp->client_id);
 385     }
 386 
 387   xsmp->state = XSMP_STATE_IDLE;
 388 
 389   /* Do not set the initial properties until we reach the main loop,
 390    * so that the application has a chance to call
 391    * egg_set_desktop_file(). (This may also help the session manager
 392    * have a better idea of when the application is fully up and
 393    * running.)
 394    */
 395   xsmp->waiting_to_set_initial_properties = TRUE;
 396   xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
 397 }
 398 
 399 static void
 400 sm_client_xsmp_set_restart_command (EggSMClient *client,
 401                                     gint argc,
 402                                     const gchar **argv)
 403 {
 404   EggSMClientXSMP *xsmp = (EggSMClientXSMP *) client;
 405   gint i;
 406 
 407   g_strfreev (xsmp->restart_command);
 408 
 409   xsmp->restart_command = g_new (gchar *, argc + 1);
 410   for (i = 0; i < argc; i++)
 411     xsmp->restart_command[i] = g_strdup (argv[i]);
 412   xsmp->restart_command[i] = NULL;
 413 
 414   xsmp->set_restart_command = TRUE;
 415 }
 416 
 417 static void
 418 sm_client_xsmp_will_quit (EggSMClient *client,
 419                           gboolean will_quit)
 420 {
 421   EggSMClientXSMP *xsmp = (EggSMClientXSMP *) client;
 422 
 423   if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED)
 424     {
 425       /* The session manager has already exited! Schedule a quit
 426        * signal.
 427        */
 428       xsmp->waiting_to_emit_quit = TRUE;
 429       update_pending_events (xsmp);
 430       return;
 431     }
 432   else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
 433     {
 434       /* We received a ShutdownCancelled message while the application
 435        * was interacting; Schedule a quit_cancelled signal.
 436        */
 437       xsmp->waiting_to_emit_quit_cancelled = TRUE;
 438       update_pending_events (xsmp);
 439       return;
 440     }
 441 
 442   g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT);
 443 
 444   g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True");
 445   SmcInteractDone (xsmp->connection, !will_quit);
 446 
 447   if (will_quit && xsmp->need_save_state)
 448     save_state (xsmp);
 449 
 450   g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
 451   SmcSaveYourselfDone (xsmp->connection, will_quit);
 452   xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
 453 }
 454 
 455 static gboolean
 456 sm_client_xsmp_end_session (EggSMClient *client,
 457                             EggSMClientEndStyle style,
 458                             gboolean request_confirmation)
 459 {
 460   EggSMClientXSMP *xsmp = (EggSMClientXSMP *) client;
 461   gint save_type;
 462 
 463   /* To end the session via XSMP, we have to send a
 464    * SaveYourselfRequest. We aren't allowed to do that if anything
 465    * else is going on, but we don't want to expose this fact to the
 466    * application. So we do our best to patch things up here...
 467    *
 468    * In the worst case, this method might block for some length of
 469    * time in process_ice_messages, but the only time that code path is
 470    * honestly likely to get hit is if the application tries to end the
 471    * session as the very first thing it does, in which case it
 472    * probably won't actually block anyway. It's not worth gunking up
 473    * the API to try to deal nicely with the other 0.01% of cases where
 474    * this happens.
 475    */
 476 
 477   while (xsmp->state != XSMP_STATE_IDLE ||
 478 	 xsmp->expecting_initial_save_yourself)
 479     {
 480       /* If we're already shutting down, we don't need to do anything. */
 481       if (xsmp->shutting_down)
 482 	return TRUE;
 483 
 484       switch (xsmp->state)
 485 	{
 486 	case XSMP_STATE_CONNECTION_CLOSED:
 487 	  return FALSE;
 488 
 489 	case XSMP_STATE_SAVE_YOURSELF:
 490 	  /* Trying to log out from the save_state callback? Whatever.
 491 	   * Abort the save_state.
 492 	   */
 493 	  SmcSaveYourselfDone (xsmp->connection, FALSE);
 494 	  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
 495 	  break;
 496 
 497 	case XSMP_STATE_INTERACT_REQUEST:
 498 	case XSMP_STATE_INTERACT:
 499 	case XSMP_STATE_SHUTDOWN_CANCELLED:
 500 	  /* Already in a shutdown-related state, just ignore
 501 	   * the new shutdown request...
 502 	   */
 503 	  return TRUE;
 504 
 505 	case XSMP_STATE_IDLE:
 506 	  if (xsmp->waiting_to_set_initial_properties)
 507 	    sm_client_xsmp_set_initial_properties (xsmp);
 508 
 509 	  if (!xsmp->expecting_initial_save_yourself)
 510 	    break;
 511 	  /* else fall through */
 512 
 513 	case XSMP_STATE_SAVE_YOURSELF_DONE:
 514 	  /* We need to wait for some response from the server.*/
 515 	  process_ice_messages (SmcGetIceConnection (xsmp->connection));
 516 	  break;
 517 
 518 	default:
 519 	  /* Hm... shouldn't happen */
 520 	  return FALSE;
 521 	}
 522     }
 523 
 524   /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and
 525    * the user chooses to save the session. But gnome-session will do
 526    * the wrong thing if we pass SmSaveBoth and the user chooses NOT to
 527    * save the session... Sigh.
 528    */
 529   if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session"))
 530     save_type = SmSaveBoth;
 531   else
 532     save_type = SmSaveGlobal;
 533 
 534   g_debug (
 535     "Sending SaveYourselfRequest("
 536     "SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)",
 537     request_confirmation ? "!" : "");
 538   SmcRequestSaveYourself (xsmp->connection,
 539 			  save_type,
 540 			  True, /* shutdown */
 541 			  SmInteractStyleAny,
 542 			  !request_confirmation, /* fast */
 543 			  True /* global */);
 544   return TRUE;
 545 }
 546 
 547 static gboolean
 548 idle_do_pending_events (gpointer data)
 549 {
 550   EggSMClientXSMP *xsmp = data;
 551   EggSMClient *client = data;
 552 
 553   xsmp->idle = 0;
 554 
 555   if (xsmp->waiting_to_emit_quit)
 556     {
 557       xsmp->waiting_to_emit_quit = FALSE;
 558       egg_sm_client_quit (client);
 559       goto out;
 560     }
 561 
 562   if (xsmp->waiting_to_emit_quit_cancelled)
 563     {
 564       xsmp->waiting_to_emit_quit_cancelled = FALSE;
 565       egg_sm_client_quit_cancelled (client);
 566       xsmp->state = XSMP_STATE_IDLE;
 567     }
 568 
 569   if (xsmp->waiting_to_save_myself)
 570     {
 571       xsmp->waiting_to_save_myself = FALSE;
 572       do_save_yourself (xsmp);
 573     }
 574 
 575  out:
 576   return FALSE;
 577 }
 578 
 579 static void
 580 update_pending_events (EggSMClientXSMP *xsmp)
 581 {
 582   gboolean want_idle =
 583     xsmp->waiting_to_emit_quit ||
 584     xsmp->waiting_to_emit_quit_cancelled ||
 585     xsmp->waiting_to_save_myself;
 586 
 587   if (want_idle)
 588     {
 589       if (xsmp->idle == 0)
 590 	xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
 591     }
 592   else
 593     {
 594       if (xsmp->idle != 0)
 595 	g_source_remove (xsmp->idle);
 596       xsmp->idle = 0;
 597     }
 598 }
 599 
 600 static void
 601 fix_broken_state (EggSMClientXSMP *xsmp,
 602                   const gchar *message,
 603                   gboolean send_interact_done,
 604                   gboolean send_save_yourself_done)
 605 {
 606   g_warning ("Received XSMP %s message in state %s: client or server error",
 607 	     message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
 608 
 609   /* Forget any pending SaveYourself plans we had */
 610   xsmp->waiting_to_save_myself = FALSE;
 611   update_pending_events (xsmp);
 612 
 613   if (send_interact_done)
 614     SmcInteractDone (xsmp->connection, False);
 615   if (send_save_yourself_done)
 616     SmcSaveYourselfDone (xsmp->connection, True);
 617 
 618   xsmp->state = send_save_yourself_done ?
 619     XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
 620 }
 621 
 622 /* SM callbacks */
 623 
 624 static void
 625 xsmp_save_yourself (SmcConn smc_conn,
 626                     SmPointer client_data,
 627                     gint save_type,
 628                     Bool shutdown,
 629                     gint interact_style,
 630                     Bool fast)
 631 {
 632   EggSMClientXSMP *xsmp = client_data;
 633   gboolean wants_quit_requested;
 634 
 635   g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
 636 	   save_type == SmSaveLocal ? "SmSaveLocal" :
 637 	   save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
 638 	   shutdown ? "Shutdown" : "!Shutdown",
 639 	   interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
 640 	   interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
 641 	   "SmInteractStyleNone", fast ? "Fast" : "!Fast",
 642 	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
 643 
 644   if (xsmp->state != XSMP_STATE_IDLE &&
 645       xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
 646     {
 647       fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
 648       return;
 649     }
 650 
 651   if (xsmp->waiting_to_set_initial_properties)
 652     sm_client_xsmp_set_initial_properties (xsmp);
 653 
 654   /* If this is the initial SaveYourself, ignore it; we've already set
 655    * properties and there's no reason to actually save state too.
 656    */
 657   if (xsmp->expecting_initial_save_yourself)
 658     {
 659       xsmp->expecting_initial_save_yourself = FALSE;
 660 
 661       if (save_type == SmSaveLocal &&
 662 	  interact_style == SmInteractStyleNone &&
 663 	  !shutdown && !fast)
 664 	{
 665 	  g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
 666 	  SmcSaveYourselfDone (xsmp->connection, True);
 667 	  /* As explained in the comment at the end of
 668 	   * do_save_yourself(), SAVE_YOURSELF_DONE is the correct
 669 	   * state here, not IDLE.
 670 	   */
 671 	  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
 672 	  return;
 673 	}
 674       else
 675 	g_warning ("First SaveYourself was not the expected one!");
 676     }
 677 
 678   /* Even ignoring the "fast" flag completely, there are still 18
 679    * different combinations of save_type, shutdown and interact_style.
 680    * We interpret them as follows:
 681    *
 682    *   Type  Shutdown  Interact  Interpretation
 683    *     G      F       A/E/N    do nothing (1)
 684    *     G      T         N      do nothing (1)*
 685    *     G      T        A/E     quit_requested (2)
 686    *    L/B     F       A/E/N    save_state (3)
 687    *    L/B     T         N      save_state (3)*
 688    *    L/B     T        A/E     quit_requested, then save_state (4)
 689    *
 690    *   1. Do nothing, because the SM asked us to do something
 691    *      uninteresting (save open files, but then don't quit
 692    *      afterward) or rude (save open files without asking the user
 693    *      for confirmation).
 694    *
 695    *   2. Request interaction and then emit ::quit_requested. This
 696    *      perhaps isn't quite correct for the SmInteractStyleErrors
 697    *      case, but we don't care.
 698    *
 699    *   3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these
 700    *      rows essentially get demoted to SmSaveLocal, because their
 701    *      Global halves correspond to "do nothing".
 702    *
 703    *   4. Request interaction, emit ::quit_requested, and then emit
 704    *      ::save_state after interacting. This is the SmSaveBoth
 705    *      equivalent of #2, but we also promote SmSaveLocal shutdown
 706    *      SaveYourselfs to SmSaveBoth here, because we want to give
 707    *      the user a chance to save open files before quitting.
 708    *
 709    * (* It would be nice if we could do something useful when the
 710    * session manager sends a SaveYourself with shutdown True and
 711    * SmInteractStyleNone. But we can't, so we just pretend it didn't
 712    * even tell us it was shutting down. The docs for ::quit mention
 713    * that it might not always be preceded by ::quit_requested.)
 714    */
 715 
 716   /* As an optimization, we don't actually request interaction and
 717    * emit ::quit_requested if the application isn't listening to the
 718    * signal.
 719    */
 720   wants_quit_requested = g_signal_has_handler_pending (
 721     xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE);
 722 
 723   xsmp->need_save_state     = (save_type != SmSaveGlobal);
 724   xsmp->need_quit_requested = (shutdown && wants_quit_requested &&
 725 			       interact_style != SmInteractStyleNone);
 726   xsmp->interact_errors     = (interact_style == SmInteractStyleErrors);
 727 
 728   xsmp->shutting_down       = shutdown;
 729 
 730   do_save_yourself (xsmp);
 731 }
 732 
 733 static void
 734 do_save_yourself (EggSMClientXSMP *xsmp)
 735 {
 736   if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
 737     {
 738       /* The SM cancelled a previous SaveYourself, but we haven't yet
 739        * had a chance to tell the application, so we can't start
 740        * processing this SaveYourself yet.
 741        */
 742       xsmp->waiting_to_save_myself = TRUE;
 743       update_pending_events (xsmp);
 744       return;
 745     }
 746 
 747   if (xsmp->need_quit_requested)
 748     {
 749       xsmp->state = XSMP_STATE_INTERACT_REQUEST;
 750 
 751       g_debug ("Sending InteractRequest(%s)",
 752 	       xsmp->interact_errors ? "Error" : "Normal");
 753       SmcInteractRequest (xsmp->connection,
 754 			  xsmp->interact_errors ? SmDialogError : SmDialogNormal,
 755 			  xsmp_interact,
 756 			  xsmp);
 757       return;
 758     }
 759 
 760   if (xsmp->need_save_state)
 761     {
 762       save_state (xsmp);
 763 
 764       /* Though unlikely, the client could have been disconnected
 765        * while the application was saving its state.
 766        */
 767       if (!xsmp->connection)
 768 	 return;
 769     }
 770 
 771   g_debug ("Sending SaveYourselfDone(True)");
 772   SmcSaveYourselfDone (xsmp->connection, True);
 773 
 774   /* The client state diagram in the XSMP spec says that after a
 775    * non-shutdown SaveYourself, we go directly back to "idle". But
 776    * everything else in both the XSMP spec and the libSM docs
 777    * disagrees.
 778    */
 779   xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
 780 }
 781 
 782 static void
 783 save_state (EggSMClientXSMP *xsmp)
 784 {
 785   GKeyFile *state_file;
 786   gchar *state_file_path, *data;
 787   EggDesktopFile *desktop_file;
 788   GPtrArray *restart;
 789   gint offset, fd;
 790 
 791   /* We set xsmp->state before emitting save_state, but our caller is
 792    * responsible for setting it back afterward.
 793    */
 794   xsmp->state = XSMP_STATE_SAVE_YOURSELF;
 795 
 796   state_file = egg_sm_client_save_state ((EggSMClient *) xsmp);
 797   if (!state_file)
 798     {
 799       restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
 800       set_properties (xsmp,
 801 		      ptrarray_prop (SmRestartCommand, restart),
 802 		      NULL);
 803       g_ptr_array_free (restart, TRUE);
 804       delete_properties (xsmp, SmDiscardCommand, NULL);
 805       return;
 806     }
 807 
 808   desktop_file = egg_get_desktop_file ();
 809   if (desktop_file)
 810     {
 811       GKeyFile *merged_file;
 812       gchar *desktop_file_path;
 813 
 814       merged_file = g_key_file_new ();
 815       desktop_file_path =
 816 	g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
 817 			     NULL, NULL);
 818       if (desktop_file_path &&
 819 	  g_key_file_load_from_file (merged_file, desktop_file_path,
 820 				     G_KEY_FILE_KEEP_COMMENTS |
 821 				     G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
 822 	{
 823 	  guint g, k, i;
 824 	  gchar **groups, **keys, *value, *exec;
 825 
 826 	  groups = g_key_file_get_groups (state_file, NULL);
 827 	  for (g = 0; groups[g]; g++)
 828 	    {
 829 	      keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
 830 	      for (k = 0; keys[k]; k++)
 831 		{
 832 		  value = g_key_file_get_value (state_file, groups[g],
 833 						keys[k], NULL);
 834 		  if (value)
 835 		    {
 836 		      g_key_file_set_value (merged_file, groups[g],
 837 					    keys[k], value);
 838 		      g_free (value);
 839 		    }
 840 		}
 841 	      g_strfreev (keys);
 842 	    }
 843 	  g_strfreev (groups);
 844 
 845 	  g_key_file_free (state_file);
 846 	  state_file = merged_file;
 847 
 848 	  /* Update Exec key using "--sm-client-state-file %k" */
 849 	  restart = generate_command (xsmp->restart_command,
 850 				      NULL, "%k");
 851 	  for (i = 0; i < restart->len; i++)
 852 	    restart->pdata[i] = g_shell_quote (restart->pdata[i]);
 853 	  g_ptr_array_add (restart, NULL);
 854 	  exec = g_strjoinv (" ", (gchar **) restart->pdata);
 855 	  g_strfreev ((gchar **) restart->pdata);
 856 	  g_ptr_array_free (restart, FALSE);
 857 
 858 	  g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
 859 				 EGG_DESKTOP_FILE_KEY_EXEC,
 860 				 exec);
 861 	  g_free (exec);
 862 	}
 863       else
 864 	desktop_file = NULL;
 865 
 866       g_free (desktop_file_path);
 867     }
 868 
 869   /* Now write state_file to disk. (We can't use mktemp(), because
 870    * that requires the filename to end with "XXXXXX", and we want
 871    * it to end with ".desktop".)
 872    */
 873 
 874   data = g_key_file_to_data (state_file, NULL, NULL);
 875   g_key_file_free (state_file);
 876 
 877   offset = 0;
 878   while (1)
 879     {
 880       state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
 881 					 g_get_user_config_dir (),
 882 					 G_DIR_SEPARATOR, G_DIR_SEPARATOR,
 883 					 g_get_prgname (),
 884 					 (long) time (NULL) + offset,
 885 					 desktop_file ? "desktop" : "state");
 886 
 887       fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
 888       if (fd == -1)
 889 	{
 890 	  if (errno == EEXIST)
 891 	    {
 892 	      offset++;
 893 	      g_free (state_file_path);
 894 	      continue;
 895 	    }
 896 	  else if (errno == ENOTDIR || errno == ENOENT)
 897 	    {
 898 	      gchar *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
 899 
 900 	      *sep = '\0';
 901 	      if (g_mkdir_with_parents (state_file_path, 0755) != 0)
 902 		{
 903 		  g_warning ("Could not create directory '%s'",
 904 			     state_file_path);
 905 		  g_free (state_file_path);
 906 		  state_file_path = NULL;
 907 		  break;
 908 		}
 909 
 910 	      continue;
 911 	    }
 912 
 913 	  g_warning ("Could not create file '%s': %s",
 914 		     state_file_path, g_strerror (errno));
 915 	  g_free (state_file_path);
 916 	  state_file_path = NULL;
 917 	  break;
 918 	}
 919 
 920       close (fd);
 921       g_file_set_contents (state_file_path, data, -1, NULL);
 922       break;
 923     }
 924   g_free (data);
 925 
 926   restart = generate_command (xsmp->restart_command, xsmp->client_id,
 927 			      state_file_path);
 928   set_properties (xsmp,
 929 		  ptrarray_prop (SmRestartCommand, restart),
 930 		  NULL);
 931   g_ptr_array_free (restart, TRUE);
 932 
 933   if (state_file_path)
 934     {
 935       set_properties (xsmp,
 936 		      array_prop (SmDiscardCommand,
 937 				  "/bin/rm", "-rf", state_file_path,
 938 				  NULL),
 939 		      NULL);
 940       g_free (state_file_path);
 941     }
 942 }
 943 
 944 static void
 945 xsmp_interact (SmcConn smc_conn,
 946                SmPointer client_data)
 947 {
 948   EggSMClientXSMP *xsmp = client_data;
 949   EggSMClient *client = client_data;
 950 
 951   g_debug ("Received Interact message in state %s",
 952 	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
 953 
 954   if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
 955     {
 956       fix_broken_state (xsmp, "Interact", TRUE, TRUE);
 957       return;
 958     }
 959 
 960   xsmp->state = XSMP_STATE_INTERACT;
 961   egg_sm_client_quit_requested (client);
 962 }
 963 
 964 static void
 965 xsmp_die (SmcConn smc_conn,
 966           SmPointer client_data)
 967 {
 968   EggSMClientXSMP *xsmp = client_data;
 969   EggSMClient *client = client_data;
 970 
 971   g_debug ("Received Die message in state %s",
 972 	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
 973 
 974   sm_client_xsmp_disconnect (xsmp);
 975   egg_sm_client_quit (client);
 976 }
 977 
 978 static void
 979 xsmp_save_complete (SmcConn smc_conn,
 980                     SmPointer client_data)
 981 {
 982   EggSMClientXSMP *xsmp = client_data;
 983 
 984   g_debug ("Received SaveComplete message in state %s",
 985 	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
 986 
 987   if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
 988     xsmp->state = XSMP_STATE_IDLE;
 989   else
 990     fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
 991 }
 992 
 993 static void
 994 xsmp_shutdown_cancelled (SmcConn smc_conn,
 995                          SmPointer client_data)
 996 {
 997   EggSMClientXSMP *xsmp = client_data;
 998   EggSMClient *client = client_data;
 999 
1000   g_debug ("Received ShutdownCancelled message in state %s",
1001 	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
1002 
1003   xsmp->shutting_down = FALSE;
1004 
1005   if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
1006     {
1007       /* We've finished interacting and now the SM has agreed to
1008        * cancel the shutdown.
1009        */
1010       xsmp->state = XSMP_STATE_IDLE;
1011       egg_sm_client_quit_cancelled (client);
1012     }
1013   else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
1014     {
1015       /* Hm... ok, so we got a shutdown SaveYourself, which got
1016        * cancelled, but the application was still interacting, so we
1017        * didn't tell it yet, and then *another* SaveYourself arrived,
1018        * which we must still be waiting to tell the app about, except
1019        * that now that SaveYourself has been cancelled too! Dizzy yet?
1020        */
1021       xsmp->waiting_to_save_myself = FALSE;
1022       update_pending_events (xsmp);
1023     }
1024   else
1025     {
1026       g_debug ("Sending SaveYourselfDone(False)");
1027       SmcSaveYourselfDone (xsmp->connection, False);
1028 
1029       if (xsmp->state == XSMP_STATE_INTERACT)
1030 	{
1031 	  /* The application is currently interacting, so we can't
1032 	   * tell it about the cancellation yet; we will wait until
1033 	   * after it calls egg_sm_client_will_quit().
1034 	   */
1035 	  xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED;
1036 	}
1037       else
1038 	{
1039 	  /* The shutdown was cancelled before the application got a
1040 	   * chance to interact.
1041 	   */
1042 	  xsmp->state = XSMP_STATE_IDLE;
1043 	}
1044     }
1045 }
1046 
1047 /* Utilities */
1048 
1049 /* Create a restart/clone/Exec command based on @restart_command.
1050  * If @client_id is non-%NULL, add "--sm-client-id @client_id".
1051  * If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
1052  *
1053  * None of the input strings are g_strdup()ed; the caller must keep
1054  * them around until it is done with the returned GPtrArray, and must
1055  * then free the array, but not its contents.
1056  */
1057 static GPtrArray *
1058 generate_command (gchar **restart_command,
1059                   const gchar *client_id,
1060                   const gchar *state_file)
1061 {
1062   GPtrArray *cmd;
1063   gint i;
1064 
1065   cmd = g_ptr_array_new ();
1066   g_ptr_array_add (cmd, restart_command[0]);
1067 
1068   if (client_id)
1069     {
1070       g_ptr_array_add (cmd, (gchar *)"--sm-client-id");
1071       g_ptr_array_add (cmd, (gchar *) client_id);
1072     }
1073 
1074   if (state_file)
1075     {
1076       g_ptr_array_add (cmd, (gchar *)"--sm-client-state-file");
1077       g_ptr_array_add (cmd, (gchar *) state_file);
1078     }
1079 
1080   for (i = 1; restart_command[i]; i++)
1081     g_ptr_array_add (cmd, restart_command[i]);
1082 
1083   return cmd;
1084 }
1085 
1086 /* Takes a NULL-terminated list of SmProp * values, created by
1087  * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
1088  * frees them.
1089  */
1090 static void
1091 set_properties (EggSMClientXSMP *xsmp,
1092                 ...)
1093 {
1094   GPtrArray *props;
1095   SmProp *prop;
1096   va_list ap;
1097   guint i;
1098 
1099   props = g_ptr_array_new ();
1100 
1101   va_start (ap, xsmp);
1102   while ((prop = va_arg (ap, SmProp *)))
1103     g_ptr_array_add (props, prop);
1104   va_end (ap);
1105 
1106   if (xsmp->connection)
1107     {
1108       SmcSetProperties (xsmp->connection, props->len,
1109 			(SmProp **) props->pdata);
1110     }
1111 
1112   for (i = 0; i < props->len; i++)
1113     {
1114       prop = props->pdata[i];
1115       g_free (prop->vals);
1116       g_free (prop);
1117     }
1118   g_ptr_array_free (props, TRUE);
1119 }
1120 
1121 /* Takes a NULL-terminated list of property names and deletes them. */
1122 static void
1123 delete_properties (EggSMClientXSMP *xsmp,
1124                    ...)
1125 {
1126   GPtrArray *props;
1127   gchar *prop;
1128   va_list ap;
1129 
1130   if (!xsmp->connection)
1131     return;
1132 
1133   props = g_ptr_array_new ();
1134 
1135   va_start (ap, xsmp);
1136   while ((prop = va_arg (ap, gchar *)))
1137     g_ptr_array_add (props, prop);
1138   va_end (ap);
1139 
1140   SmcDeleteProperties (xsmp->connection, props->len,
1141 		       (gchar **) props->pdata);
1142 
1143   g_ptr_array_free (props, TRUE);
1144 }
1145 
1146 /* Takes an array of strings and creates a LISTofARRAY8 property. The
1147  * strings are neither dupped nor freed; they need to remain valid
1148  * until you're done with the SmProp.
1149  */
1150 static SmProp *
1151 array_prop (const gchar *name,
1152             ...)
1153 {
1154   SmProp *prop;
1155   SmPropValue pv;
1156   GArray *vals;
1157   gchar *value;
1158   va_list ap;
1159 
1160   prop = g_new (SmProp, 1);
1161   prop->name = (gchar *) name;
1162   prop->type = (gchar *) SmLISTofARRAY8;
1163 
1164   vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
1165 
1166   va_start (ap, name);
1167   while ((value = va_arg (ap, gchar *)))
1168     {
1169       pv.length = strlen (value);
1170       pv.value = value;
1171       g_array_append_val (vals, pv);
1172     }
1173 
1174   prop->num_vals = vals->len;
1175   prop->vals = (SmPropValue *) vals->data;
1176 
1177   g_array_free (vals, FALSE);
1178 
1179   return prop;
1180 }
1181 
1182 /* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
1183  * The array contents are neither dupped nor freed; they need to
1184  * remain valid until you're done with the SmProp.
1185  */
1186 static SmProp *
1187 ptrarray_prop (const gchar *name,
1188                GPtrArray *values)
1189 {
1190   SmProp *prop;
1191   SmPropValue pv;
1192   GArray *vals;
1193   guint i;
1194 
1195   prop = g_new (SmProp, 1);
1196   prop->name = (gchar *) name;
1197   prop->type = (gchar *) SmLISTofARRAY8;
1198 
1199   vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
1200 
1201   for (i = 0; i < values->len; i++)
1202     {
1203       pv.length = strlen (values->pdata[i]);
1204       pv.value = values->pdata[i];
1205       g_array_append_val (vals, pv);
1206     }
1207 
1208   prop->num_vals = vals->len;
1209   prop->vals = (SmPropValue *) vals->data;
1210 
1211   g_array_free (vals, FALSE);
1212 
1213   return prop;
1214 }
1215 
1216 /* Takes a string and creates an ARRAY8 property. The string is
1217  * neither dupped nor freed; it needs to remain valid until you're
1218  * done with the SmProp.
1219  */
1220 static SmProp *
1221 string_prop (const gchar *name,
1222              const gchar *value)
1223 {
1224   SmProp *prop;
1225 
1226   prop = g_new (SmProp, 1);
1227   prop->name = (gchar *) name;
1228   prop->type = (gchar *) SmARRAY8;
1229 
1230   prop->num_vals = 1;
1231   prop->vals = g_new (SmPropValue, 1);
1232 
1233   prop->vals[0].length = strlen (value);
1234   prop->vals[0].value = (gchar *) value;
1235 
1236   return prop;
1237 }
1238 
1239 /* Takes a gchar and creates a CARD8 property. */
1240 static SmProp *
1241 card8_prop (const gchar *name,
1242             guchar value)
1243 {
1244   SmProp *prop;
1245   gchar *card8val;
1246 
1247   /* To avoid having to allocate and free prop->vals[0], we cheat and
1248    * make vals a 2-element-long array and then use the second element
1249    * to store value.
1250    */
1251 
1252   prop = g_new (SmProp, 1);
1253   prop->name = (gchar *) name;
1254   prop->type = (gchar *) SmCARD8;
1255 
1256   prop->num_vals = 1;
1257   prop->vals = g_new (SmPropValue, 2);
1258   card8val = (gchar *)(&prop->vals[1]);
1259   card8val[0] = value;
1260 
1261   prop->vals[0].length = 1;
1262   prop->vals[0].value = card8val;
1263 
1264   return prop;
1265 }
1266 
1267 /* ICE code. This makes no effort to play nice with anyone else trying
1268  * to use libICE. Fortunately, no one uses libICE for anything other
1269  * than SM. (DCOP uses ICE, but it has its own private copy of
1270  * libICE.)
1271  *
1272  * When this moves to gtk, it will need to be cleverer, to avoid
1273  * tripping over old apps that use GnomeClient or that use libSM
1274  * directly.
1275  */
1276 
1277 #include <X11/ICE/ICElib.h>
1278 #include <fcntl.h>
1279 
1280 static void        ice_error_handler    (IceConn        ice_conn,
1281 					 Bool           swap,
1282 					 gint            offending_minor_opcode,
1283 					 gulong  offending_sequence,
1284 					 gint            error_class,
1285 					 gint            severity,
1286 					 IcePointer     values);
1287 static void        ice_io_error_handler (IceConn        ice_conn);
1288 static void        ice_connection_watch (IceConn        ice_conn,
1289 					 IcePointer     client_data,
1290 					 Bool           opening,
1291 					 IcePointer    *watch_data);
1292 
1293 static void
1294 ice_init (void)
1295 {
1296   IceSetIOErrorHandler (ice_io_error_handler);
1297   IceSetErrorHandler (ice_error_handler);
1298   IceAddConnectionWatch (ice_connection_watch, NULL);
1299 }
1300 
1301 static gboolean
1302 process_ice_messages (IceConn ice_conn)
1303 {
1304   IceProcessMessagesStatus status;
1305 
1306   status = IceProcessMessages (ice_conn, NULL, NULL);
1307 
1308   switch (status)
1309     {
1310     case IceProcessMessagesSuccess:
1311       return TRUE;
1312 
1313     case IceProcessMessagesIOError:
1314       sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
1315       return FALSE;
1316 
1317     case IceProcessMessagesConnectionClosed:
1318       return FALSE;
1319 
1320     default:
1321       g_assert_not_reached ();
1322     }
1323 }
1324 
1325 static gboolean
1326 ice_iochannel_watch (GIOChannel *channel,
1327                      GIOCondition condition,
1328                      gpointer client_data)
1329 {
1330   return process_ice_messages (client_data);
1331 }
1332 
1333 static void
1334 ice_connection_watch (IceConn ice_conn,
1335                       IcePointer client_data,
1336                       Bool opening,
1337                       IcePointer *watch_data)
1338 {
1339   guint watch_id;
1340 
1341   if (opening)
1342     {
1343       GIOChannel *channel;
1344       gint fd = IceConnectionNumber (ice_conn);
1345 
1346       fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
1347       channel = g_io_channel_unix_new (fd);
1348       watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
1349 				 ice_iochannel_watch, ice_conn);
1350       g_io_channel_unref (channel);
1351 
1352       *watch_data = GUINT_TO_POINTER (watch_id);
1353     }
1354   else
1355     {
1356       watch_id = GPOINTER_TO_UINT (*watch_data);
1357       g_source_remove (watch_id);
1358     }
1359 }
1360 
1361 static void
1362 ice_error_handler (IceConn ice_conn,
1363                    Bool swap,
1364                    gint offending_minor_opcode,
1365                    gulong offending_sequence,
1366                    gint error_class,
1367                    gint severity,
1368                    IcePointer values)
1369 {
1370   /* Do nothing */
1371 }
1372 
1373 static void
1374 ice_io_error_handler (IceConn ice_conn)
1375 {
1376   /* Do nothing */
1377 }
1378 
1379 static void
1380 smc_error_handler (SmcConn smc_conn,
1381                    Bool swap,
1382                    gint offending_minor_opcode,
1383                    gulong offending_sequence,
1384                    gint error_class,
1385                    gint severity,
1386                    SmPointer values)
1387 {
1388   /* Do nothing */
1389 }