Bug Summary

File:cafe-panel/libegg/eggsmclient-xsmp.c
Warning:line 1194, column 16
Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption

Annotated Source Code

Press '?' to see keyboard shortcuts

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