Bug Summary

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