Bug Summary

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