1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2007 Novell, Inc.
4 * Copyright (C) 2008 Red Hat, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA
19 * 02110-1335, USA.
20 */
21
22 #include "config.h"
23
24 #include "eggdesktopfile.h"
25
26 #include "csm-marshal.h"
27 #include "csm-client.h"
28 #include "csm-exported-client.h"
29
30 static guint32 client_serial = 1;
31
32 #define CSM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSM_TYPE_CLIENT, CsmClientPrivate))
33
34 struct CsmClientPrivate
35 {
36 char *id;
37 char *startup_id;
38 char *app_id;
39 guint status;
40 CsmExportedClient *skeleton;
41 GDBusConnection *connection;
42 };
43
44 enum {
45 PROP_0,
46 PROP_ID,
47 PROP_STARTUP_ID,
48 PROP_APP_ID,
49 PROP_STATUS
50 };
51
52 enum {
53 DISCONNECTED,
54 END_SESSION_RESPONSE,
55 LAST_SIGNAL
56 };
57
58 static guint signals[LAST_SIGNAL] = { 0 };
59
60 G_DEFINE_ABSTRACT_TYPE (CsmClient, csm_client, G_TYPE_OBJECT)
61
62 #define CSM_CLIENT_DBUS_IFACE "org.gnome.SessionManager.Client"
63
64 static const GDBusErrorEntry csm_client_error_entries[] = {
65 { CSM_CLIENT_ERROR_GENERAL, CSM_CLIENT_DBUS_IFACE ".GeneralError" },
66 { CSM_CLIENT_ERROR_NOT_REGISTERED, CSM_CLIENT_DBUS_IFACE ".NotRegistered" }
67 };
68
69 GQuark
csm_client_error_quark(void)70 csm_client_error_quark (void)
71 {
72 static volatile gsize quark_volatile = 0;
73
74 g_dbus_error_register_error_domain ("csm_client_error",
75 &quark_volatile,
76 csm_client_error_entries,
77 G_N_ELEMENTS (csm_client_error_entries));
78 return quark_volatile;
79 }
80
81 static guint32
get_next_client_serial(void)82 get_next_client_serial (void)
83 {
84 guint32 serial;
85
86 serial = client_serial++;
87
88 if ((gint32)client_serial < 0) {
89 client_serial = 1;
90 }
91
92 return serial;
93 }
94
95 static gboolean
csm_client_get_startup_id(CsmExportedClient * skeleton,GDBusMethodInvocation * invocation,CsmClient * client)96 csm_client_get_startup_id (CsmExportedClient *skeleton,
97 GDBusMethodInvocation *invocation,
98 CsmClient *client)
99 {
100 csm_exported_client_complete_get_startup_id (skeleton,
101 invocation,
102 client->priv->startup_id);
103 return TRUE;
104 }
105
106 static gboolean
csm_client_get_app_id(CsmExportedClient * skeleton,GDBusMethodInvocation * invocation,CsmClient * client)107 csm_client_get_app_id (CsmExportedClient *skeleton,
108 GDBusMethodInvocation *invocation,
109 CsmClient *client)
110 {
111 csm_exported_client_complete_get_app_id (skeleton,
112 invocation,
113 client->priv->app_id);
114 return TRUE;
115 }
116
117 static gboolean
csm_client_get_restart_style_hint(CsmExportedClient * skeleton,GDBusMethodInvocation * invocation,CsmClient * client)118 csm_client_get_restart_style_hint (CsmExportedClient *skeleton,
119 GDBusMethodInvocation *invocation,
120 CsmClient *client)
121 {
122 guint hint;
123
124 hint = CSM_CLIENT_GET_CLASS (client)->impl_get_restart_style_hint (client);
125 csm_exported_client_complete_get_restart_style_hint (skeleton,
126 invocation,
127 hint);
128 return TRUE;
129 }
130
131 static gboolean
csm_client_get_status(CsmExportedClient * skeleton,GDBusMethodInvocation * invocation,CsmClient * client)132 csm_client_get_status (CsmExportedClient *skeleton,
133 GDBusMethodInvocation *invocation,
134 CsmClient *client)
135 {
136 csm_exported_client_complete_get_status (skeleton,
137 invocation,
138 client->priv->status);
139 return TRUE;
140 }
141
142 static gboolean
csm_client_get_unix_process_id(CsmExportedClient * skeleton,GDBusMethodInvocation * invocation,CsmClient * client)143 csm_client_get_unix_process_id (CsmExportedClient *skeleton,
144 GDBusMethodInvocation *invocation,
145 CsmClient *client)
146 {
147 guint pid;
148
149 pid = CSM_CLIENT_GET_CLASS (client)->impl_get_unix_process_id (client);
150 csm_exported_client_complete_get_unix_process_id (skeleton,
151 invocation,
152 pid);
153 return TRUE;
154 }
155
156 static gboolean
csm_client_stop_dbus(CsmExportedClient * skeleton,GDBusMethodInvocation * invocation,CsmClient * client)157 csm_client_stop_dbus (CsmExportedClient *skeleton,
158 GDBusMethodInvocation *invocation,
159 CsmClient *client)
160 {
161 GError *error = NULL;
162 csm_client_stop (client, &error);
163
164 if (error != NULL) {
165 g_dbus_method_invocation_take_error (invocation, error);
166 } else {
167 csm_exported_client_complete_stop (skeleton, invocation);
168 }
169
170 return TRUE;
171 }
172
173 static gboolean
register_client(CsmClient * client)174 register_client (CsmClient *client)
175 {
176 CsmExportedClient *skeleton;
177 GError *error;
178
179 error = NULL;
180 client->priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
181
182 if (client->priv->connection == NULL) {
183 if (error != NULL) {
184 g_critical ("error getting session bus: %s", error->message);
185 g_error_free (error);
186 }
187 return FALSE;
188 }
189
190 skeleton = csm_exported_client_skeleton_new ();
191 client->priv->skeleton = skeleton;
192
193 g_debug ("exporting client to object path: %s", client->priv->id);
194 g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
195 client->priv->connection,
196 client->priv->id, &error);
197
198 if (error != NULL) {
199 g_critical ("error exporting client on session bus: %s", error->message);
200 g_error_free (error);
201 return FALSE;
202 }
203
204 g_signal_connect (skeleton, "handle-get-app-id",
205 G_CALLBACK (csm_client_get_app_id), client);
206 g_signal_connect (skeleton, "handle-get-restart-style-hint",
207 G_CALLBACK (csm_client_get_restart_style_hint), client);
208 g_signal_connect (skeleton, "handle-get-startup-id",
209 G_CALLBACK (csm_client_get_startup_id), client);
210 g_signal_connect (skeleton, "handle-get-status",
211 G_CALLBACK (csm_client_get_status), client);
212 g_signal_connect (skeleton, "handle-get-unix-process-id",
213 G_CALLBACK (csm_client_get_unix_process_id), client);
214 g_signal_connect (skeleton, "handle-stop",
215 G_CALLBACK (csm_client_stop_dbus), client);
216
217 return TRUE;
218 }
219
220 static GObject *
csm_client_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)221 csm_client_constructor (GType type,
222 guint n_construct_properties,
223 GObjectConstructParam *construct_properties)
224 {
225 CsmClient *client;
226 gboolean res;
227
228 client = CSM_CLIENT (G_OBJECT_CLASS (csm_client_parent_class)->constructor (type,
229 n_construct_properties,
230 construct_properties));
231
232 g_free (client->priv->id);
233 client->priv->id = g_strdup_printf ("/org/gnome/SessionManager/Client%u", get_next_client_serial ());
234
235 res = register_client (client);
236 if (! res) {
237 g_warning ("Unable to register client with session bus");
238 }
239
240 return G_OBJECT (client);
241 }
242
243 static void
csm_client_init(CsmClient * client)244 csm_client_init (CsmClient *client)
245 {
246 client->priv = CSM_CLIENT_GET_PRIVATE (client);
247 }
248
249 static void
csm_client_finalize(GObject * object)250 csm_client_finalize (GObject *object)
251 {
252 CsmClient *client;
253
254 g_return_if_fail (object != NULL);
255 g_return_if_fail (CSM_IS_CLIENT (object));
256
257 client = CSM_CLIENT (object);
258
259 g_return_if_fail (client->priv != NULL);
260
261 g_free (client->priv->id);
262 g_free (client->priv->startup_id);
263 g_free (client->priv->app_id);
264
265 if (client->priv->skeleton != NULL) {
266 g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (client->priv->skeleton),
267 client->priv->connection);
268 g_clear_object (&client->priv->skeleton);
269 }
270
271 g_clear_object (&client->priv->connection);
272
273 G_OBJECT_CLASS (csm_client_parent_class)->finalize (object);
274 }
275
276 void
csm_client_set_status(CsmClient * client,guint status)277 csm_client_set_status (CsmClient *client,
278 guint status)
279 {
280 g_return_if_fail (CSM_IS_CLIENT (client));
281 if (client->priv->status != status) {
282 client->priv->status = status;
283 g_object_notify (G_OBJECT (client), "status");
284 }
285 }
286
287 static void
csm_client_set_startup_id(CsmClient * client,const char * startup_id)288 csm_client_set_startup_id (CsmClient *client,
289 const char *startup_id)
290 {
291 g_return_if_fail (CSM_IS_CLIENT (client));
292
293 g_free (client->priv->startup_id);
294
295 if (startup_id != NULL) {
296 client->priv->startup_id = g_strdup (startup_id);
297 } else {
298 client->priv->startup_id = g_strdup ("");
299 }
300 g_object_notify (G_OBJECT (client), "startup-id");
301 }
302
303 void
csm_client_set_app_id(CsmClient * client,const char * app_id)304 csm_client_set_app_id (CsmClient *client,
305 const char *app_id)
306 {
307 g_return_if_fail (CSM_IS_CLIENT (client));
308
309 g_free (client->priv->app_id);
310
311 if (app_id != NULL) {
312 client->priv->app_id = g_strdup (app_id);
313 } else {
314 client->priv->app_id = g_strdup ("");
315 }
316 g_object_notify (G_OBJECT (client), "app-id");
317 }
318
319 static void
csm_client_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)320 csm_client_set_property (GObject *object,
321 guint prop_id,
322 const GValue *value,
323 GParamSpec *pspec)
324 {
325 CsmClient *self;
326
327 self = CSM_CLIENT (object);
328
329 switch (prop_id) {
330 case PROP_STARTUP_ID:
331 csm_client_set_startup_id (self, g_value_get_string (value));
332 break;
333 case PROP_APP_ID:
334 csm_client_set_app_id (self, g_value_get_string (value));
335 break;
336 case PROP_STATUS:
337 csm_client_set_status (self, g_value_get_uint (value));
338 break;
339 default:
340 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
341 break;
342 }
343 }
344
345 static void
csm_client_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)346 csm_client_get_property (GObject *object,
347 guint prop_id,
348 GValue *value,
349 GParamSpec *pspec)
350 {
351 CsmClient *self;
352
353 self = CSM_CLIENT (object);
354
355 switch (prop_id) {
356 case PROP_STARTUP_ID:
357 g_value_set_string (value, self->priv->startup_id);
358 break;
359 case PROP_APP_ID:
360 g_value_set_string (value, self->priv->app_id);
361 break;
362 case PROP_STATUS:
363 g_value_set_uint (value, self->priv->status);
364 break;
365 default:
366 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
367 break;
368 }
369 }
370
371 static gboolean
default_stop(CsmClient * client,GError ** error)372 default_stop (CsmClient *client,
373 GError **error)
374 {
375 g_return_val_if_fail (CSM_IS_CLIENT (client), FALSE);
376
377 g_warning ("Stop not implemented");
378
379 return TRUE;
380 }
381
382 static void
csm_client_dispose(GObject * object)383 csm_client_dispose (GObject *object)
384 {
385 CsmClient *client;
386
387 g_return_if_fail (object != NULL);
388 g_return_if_fail (CSM_IS_CLIENT (object));
389
390 client = CSM_CLIENT (object);
391
392 g_debug ("CsmClient: disposing %s", client->priv->id);
393
394 G_OBJECT_CLASS (csm_client_parent_class)->dispose (object);
395 }
396
397 static void
csm_client_class_init(CsmClientClass * klass)398 csm_client_class_init (CsmClientClass *klass)
399 {
400 GObjectClass *object_class = G_OBJECT_CLASS (klass);
401
402 object_class->get_property = csm_client_get_property;
403 object_class->set_property = csm_client_set_property;
404 object_class->constructor = csm_client_constructor;
405 object_class->finalize = csm_client_finalize;
406 object_class->dispose = csm_client_dispose;
407
408 klass->impl_stop = default_stop;
409
410 signals[DISCONNECTED] =
411 g_signal_new ("disconnected",
412 G_OBJECT_CLASS_TYPE (object_class),
413 G_SIGNAL_RUN_LAST,
414 G_STRUCT_OFFSET (CsmClientClass, disconnected),
415 NULL, NULL, NULL,
416 G_TYPE_NONE,
417 0);
418 signals[END_SESSION_RESPONSE] =
419 g_signal_new ("end-session-response",
420 G_OBJECT_CLASS_TYPE (object_class),
421 G_SIGNAL_RUN_LAST,
422 G_STRUCT_OFFSET (CsmClientClass, end_session_response),
423 NULL, NULL, NULL,
424 G_TYPE_NONE,
425 4, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING);
426
427 g_object_class_install_property (object_class,
428 PROP_STARTUP_ID,
429 g_param_spec_string ("startup-id",
430 "startup-id",
431 "startup-id",
432 "",
433 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
434 g_object_class_install_property (object_class,
435 PROP_APP_ID,
436 g_param_spec_string ("app-id",
437 "app-id",
438 "app-id",
439 "",
440 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
441 g_object_class_install_property (object_class,
442 PROP_STATUS,
443 g_param_spec_uint ("status",
444 "status",
445 "status",
446 0,
447 G_MAXINT,
448 CSM_CLIENT_UNREGISTERED,
449 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
450
451 g_type_class_add_private (klass, sizeof (CsmClientPrivate));
452 }
453
454 const char *
csm_client_peek_id(CsmClient * client)455 csm_client_peek_id (CsmClient *client)
456 {
457 g_return_val_if_fail (CSM_IS_CLIENT (client), NULL);
458
459 return client->priv->id;
460 }
461
462 /**
463 * csm_client_peek_app_id:
464 * @client: a #CsmClient.
465 *
466 * Note that the application ID might not be known; this happens when for XSMP
467 * clients that we did not start ourselves, for instance.
468 *
469 * Returns: the application ID of the client, or %NULL if no such ID is
470 * known. The string is owned by @client.
471 **/
472 const char *
csm_client_peek_app_id(CsmClient * client)473 csm_client_peek_app_id (CsmClient *client)
474 {
475 g_return_val_if_fail (CSM_IS_CLIENT (client), NULL);
476
477 return client->priv->app_id;
478 }
479
480 const char *
csm_client_peek_startup_id(CsmClient * client)481 csm_client_peek_startup_id (CsmClient *client)
482 {
483 g_return_val_if_fail (CSM_IS_CLIENT (client), NULL);
484
485 return client->priv->startup_id;
486 }
487
488 guint
csm_client_peek_status(CsmClient * client)489 csm_client_peek_status (CsmClient *client)
490 {
491 g_return_val_if_fail (CSM_IS_CLIENT (client), CSM_CLIENT_UNREGISTERED);
492
493 return client->priv->status;
494 }
495
496 guint
csm_client_peek_restart_style_hint(CsmClient * client)497 csm_client_peek_restart_style_hint (CsmClient *client)
498 {
499 g_return_val_if_fail (CSM_IS_CLIENT (client), CSM_CLIENT_RESTART_NEVER);
500
501 return CSM_CLIENT_GET_CLASS (client)->impl_get_restart_style_hint (client);
502 }
503
504 /**
505 * csm_client_get_app_name:
506 * @client: a #CsmClient.
507 *
508 * Returns: a copy of the application name of the client, or %NULL if no such
509 * name is known.
510 **/
511 char *
csm_client_get_app_name(CsmClient * client)512 csm_client_get_app_name (CsmClient *client)
513 {
514 g_return_val_if_fail (CSM_IS_CLIENT (client), NULL);
515
516 return CSM_CLIENT_GET_CLASS (client)->impl_get_app_name (client);
517 }
518
519 gboolean
csm_client_cancel_end_session(CsmClient * client,GError ** error)520 csm_client_cancel_end_session (CsmClient *client,
521 GError **error)
522 {
523 g_return_val_if_fail (CSM_IS_CLIENT (client), FALSE);
524
525 return CSM_CLIENT_GET_CLASS (client)->impl_cancel_end_session (client, error);
526 }
527
528
529 gboolean
csm_client_query_end_session(CsmClient * client,guint flags,GError ** error)530 csm_client_query_end_session (CsmClient *client,
531 guint flags,
532 GError **error)
533 {
534 g_return_val_if_fail (CSM_IS_CLIENT (client), FALSE);
535
536 return CSM_CLIENT_GET_CLASS (client)->impl_query_end_session (client, flags, error);
537 }
538
539 gboolean
csm_client_end_session(CsmClient * client,guint flags,GError ** error)540 csm_client_end_session (CsmClient *client,
541 guint flags,
542 GError **error)
543 {
544 g_return_val_if_fail (CSM_IS_CLIENT (client), FALSE);
545
546 return CSM_CLIENT_GET_CLASS (client)->impl_end_session (client, flags, error);
547 }
548
549 gboolean
csm_client_stop(CsmClient * client,GError ** error)550 csm_client_stop (CsmClient *client,
551 GError **error)
552 {
553 g_return_val_if_fail (CSM_IS_CLIENT (client), FALSE);
554
555 return CSM_CLIENT_GET_CLASS (client)->impl_stop (client, error);
556 }
557
558 void
csm_client_disconnected(CsmClient * client)559 csm_client_disconnected (CsmClient *client)
560 {
561 g_signal_emit (client, signals[DISCONNECTED], 0);
562 }
563
564 GKeyFile *
csm_client_save(CsmClient * client,GError ** error)565 csm_client_save (CsmClient *client,
566 GError **error)
567 {
568 g_return_val_if_fail (CSM_IS_CLIENT (client), FALSE);
569
570 return CSM_CLIENT_GET_CLASS (client)->impl_save (client, error);
571 }
572
573 void
csm_client_end_session_response(CsmClient * client,gboolean is_ok,gboolean do_last,gboolean cancel,const char * reason)574 csm_client_end_session_response (CsmClient *client,
575 gboolean is_ok,
576 gboolean do_last,
577 gboolean cancel,
578 const char *reason)
579 {
580 g_signal_emit (client, signals[END_SESSION_RESPONSE], 0,
581 is_ok, do_last, cancel, reason);
582 }
583