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