Bug Summary

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