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