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 #ifdef GDK_WINDOWING_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 #ifdef GDK_WINDOWING_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