1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2008 Red Hat, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <gio/gio.h>
29 
30 #include "csm-exported-client-private.h"
31 #include "csm-dbus-client.h"
32 
33 #include "csm-manager.h"
34 #include "csm-util.h"
35 
36 #define CSM_DBUS_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSM_TYPE_DBUS_CLIENT, CsmDBusClientPrivate))
37 
38 
39 #define SM_DBUS_NAME                     "org.gnome.SessionManager"
40 #define SM_DBUS_CLIENT_PRIVATE_INTERFACE "org.gnome.SessionManager.ClientPrivate"
41 
42 struct CsmDBusClientPrivate
43 {
44         char                 *bus_name;
45         GPid                  caller_pid;
46         CsmClientRestartStyle restart_style_hint;
47 
48         GDBusConnection      *connection;
49         CsmExportedClientPrivate *skeleton;
50         guint                 watch_id;
51 };
52 
53 enum {
54         PROP_0,
55         PROP_BUS_NAME
56 };
57 
G_DEFINE_TYPE(CsmDBusClient,csm_dbus_client,CSM_TYPE_CLIENT)58 G_DEFINE_TYPE (CsmDBusClient, csm_dbus_client, CSM_TYPE_CLIENT)
59 
60 static gboolean
61 setup_connection (CsmDBusClient *client)
62 {
63         GError *error = NULL;
64 
65         if (client->priv->connection == NULL) {
66                 client->priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
67                 if (error != NULL) {
68                         g_debug ("CsmDbusClient: Couldn't connect to session bus: %s",
69                                  error->message);
70                         g_error_free (error);
71                         return FALSE;
72                 }
73         }
74 
75         return TRUE;
76 }
77 
78 static gboolean
handle_end_session_response(CsmExportedClientPrivate * skeleton,GDBusMethodInvocation * invocation,gboolean is_ok,const char * reason,CsmDBusClient * client)79 handle_end_session_response (CsmExportedClientPrivate *skeleton,
80                              GDBusMethodInvocation    *invocation,
81                              gboolean                  is_ok,
82                              const char               *reason,
83                              CsmDBusClient            *client)
84 {
85         g_debug ("CsmDBusClient: got EndSessionResponse is-ok:%d reason=%s", is_ok, reason);
86         csm_client_end_session_response (CSM_CLIENT (client),
87                                          is_ok, FALSE, FALSE, reason);
88 
89         csm_exported_client_private_complete_end_session_response (skeleton, invocation);
90         return TRUE;
91 }
92 
93 static GObject *
csm_dbus_client_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)94 csm_dbus_client_constructor (GType                  type,
95                              guint                  n_construct_properties,
96                              GObjectConstructParam *construct_properties)
97 {
98         CsmDBusClient *client;
99         GError *error = NULL;
100         CsmExportedClientPrivate *skeleton;
101 
102         client = CSM_DBUS_CLIENT (G_OBJECT_CLASS (csm_dbus_client_parent_class)->constructor (type,
103                                                                                               n_construct_properties,
104                                                                                               construct_properties));
105 
106         if (! setup_connection (client)) {
107                 g_object_unref (client);
108                 return NULL;
109         }
110 
111         skeleton = csm_exported_client_private_skeleton_new ();
112         client->priv->skeleton = skeleton;
113         g_debug ("exporting dbus client to object path: %s", csm_client_peek_id (CSM_CLIENT (client)));
114         g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
115                                           client->priv->connection,
116                                           csm_client_peek_id (CSM_CLIENT (client)),
117                                           &error);
118 
119         if (error != NULL) {
120                 g_critical ("error exporting client private on session bus: %s", error->message);
121                 g_error_free (error);
122                 g_object_unref (client);
123                 return NULL;
124         }
125 
126         g_signal_connect (skeleton, "handle-end-session-response",
127                           G_CALLBACK (handle_end_session_response), client);
128 
129         return G_OBJECT (client);
130 }
131 
132 static void
csm_dbus_client_init(CsmDBusClient * client)133 csm_dbus_client_init (CsmDBusClient *client)
134 {
135         client->priv = CSM_DBUS_CLIENT_GET_PRIVATE (client);
136 }
137 
138 /* adapted from PolicyKit */
139 static gboolean
get_caller_info(CsmDBusClient * client,const char * sender,uid_t * calling_uid_out,pid_t * calling_pid_out)140 get_caller_info (CsmDBusClient *client,
141                  const char    *sender,
142                  uid_t         *calling_uid_out,
143                  pid_t         *calling_pid_out)
144 {
145         GDBusConnection *connection;
146         gboolean         retval;
147         GError          *error;
148         GVariant        *uid_variant, *pid_variant;
149         uid_t            uid;
150         pid_t            pid;
151 
152         retval = FALSE;
153         connection = NULL;
154         uid_variant = pid_variant = NULL;
155 
156         if (sender == NULL) {
157                 goto out;
158         }
159 
160         error = NULL;
161         connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
162 
163         if (error != NULL) {
164                 g_warning ("error getting session bus: %s", error->message);
165                 g_error_free (error);
166                 goto out;
167         }
168 
169         uid_variant = g_dbus_connection_call_sync (connection,
170                                                    "org.freedesktop.DBus",
171                                                    "/org/freedesktop/DBus",
172                                                    "org.freedesktop.DBus",
173                                                    "GetConnectionUnixUser",
174                                                    g_variant_new ("(s)", sender),
175                                                    G_VARIANT_TYPE ("(u)"),
176                                                    G_DBUS_CALL_FLAGS_NONE,
177                                                    -1, NULL, &error);
178 
179         if (error != NULL) {
180                 g_debug ("GetConnectionUnixUser() failed: %s", error->message);
181                 g_error_free (error);
182                 goto out;
183         }
184 
185         pid_variant = g_dbus_connection_call_sync (connection,
186                                                    "org.freedesktop.DBus",
187                                                    "/org/freedesktop/DBus",
188                                                    "org.freedesktop.DBus",
189                                                    "GetConnectionUnixProcessID",
190                                                    g_variant_new ("(s)", sender),
191                                                    G_VARIANT_TYPE ("(u)"),
192                                                    G_DBUS_CALL_FLAGS_NONE,
193                                                    -1, NULL, &error);
194 
195         if (error != NULL) {
196                 g_debug ("GetConnectionUnixProcessID() failed: %s", error->message);
197                 g_error_free (error);
198                 goto out;
199         }
200 
201         g_variant_get (uid_variant, "(u)", &uid);
202         g_variant_get (pid_variant, "(u)", &pid);
203 
204         if (calling_uid_out != NULL) {
205                 *calling_uid_out = uid;
206         }
207         if (calling_pid_out != NULL) {
208                 *calling_pid_out = pid;
209         }
210 
211         retval = TRUE;
212 
213         g_debug ("uid = %d", uid);
214         g_debug ("pid = %d", pid);
215 
216 out:
217         g_clear_pointer (&uid_variant, (GDestroyNotify) g_variant_unref);
218         g_clear_pointer (&pid_variant, (GDestroyNotify) g_variant_unref);
219         g_clear_object (&connection);
220 
221         return retval;
222 }
223 
224 static void
on_client_vanished(GDBusConnection * connection,const char * name,gpointer user_data)225 on_client_vanished (GDBusConnection *connection,
226                     const char      *name,
227                     gpointer         user_data)
228 {
229         CsmDBusClient  *client = user_data;
230 
231         g_bus_unwatch_name (client->priv->watch_id);
232         client->priv->watch_id = 0;
233 
234         csm_client_disconnected (CSM_CLIENT (client));
235 }
236 
237 static void
csm_dbus_client_set_bus_name(CsmDBusClient * client,const char * bus_name)238 csm_dbus_client_set_bus_name (CsmDBusClient  *client,
239                               const char     *bus_name)
240 {
241         g_return_if_fail (CSM_IS_DBUS_CLIENT (client));
242 
243         g_free (client->priv->bus_name);
244 
245         client->priv->bus_name = g_strdup (bus_name);
246         g_object_notify (G_OBJECT (client), "bus-name");
247 
248         if (!get_caller_info (client, bus_name, NULL, &client->priv->caller_pid)) {
249                 client->priv->caller_pid = 0;
250         }
251 
252         client->priv->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
253                                                    bus_name,
254                                                    G_BUS_NAME_WATCHER_FLAGS_NONE,
255                                                    NULL,
256                                                    on_client_vanished,
257                                                    client,
258                                                    NULL);
259 }
260 
261 const char *
csm_dbus_client_get_bus_name(CsmDBusClient * client)262 csm_dbus_client_get_bus_name (CsmDBusClient  *client)
263 {
264         g_return_val_if_fail (CSM_IS_DBUS_CLIENT (client), NULL);
265 
266         return client->priv->bus_name;
267 }
268 
269 static void
csm_dbus_client_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)270 csm_dbus_client_set_property (GObject       *object,
271                               guint          prop_id,
272                               const GValue  *value,
273                               GParamSpec    *pspec)
274 {
275         CsmDBusClient *self;
276 
277         self = CSM_DBUS_CLIENT (object);
278 
279         switch (prop_id) {
280         case PROP_BUS_NAME:
281                 csm_dbus_client_set_bus_name (self, g_value_get_string (value));
282                 break;
283         default:
284                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
285                 break;
286         }
287 }
288 
289 static void
csm_dbus_client_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)290 csm_dbus_client_get_property (GObject    *object,
291                               guint       prop_id,
292                               GValue     *value,
293                               GParamSpec *pspec)
294 {
295         CsmDBusClient *self;
296 
297         self = CSM_DBUS_CLIENT (object);
298 
299         switch (prop_id) {
300         case PROP_BUS_NAME:
301                 g_value_set_string (value, self->priv->bus_name);
302                 break;
303         default:
304                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
305                 break;
306         }
307 }
308 
309 static void
csm_dbus_client_finalize(GObject * object)310 csm_dbus_client_finalize (GObject *object)
311 {
312         CsmDBusClient *client = (CsmDBusClient *) object;
313 
314         g_free (client->priv->bus_name);
315 
316         if (client->priv->skeleton != NULL) {
317                 g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (client->priv->skeleton),
318                                                                     client->priv->connection);
319                 g_clear_object (&client->priv->skeleton);
320         }
321 
322         g_clear_object (&client->priv->connection);
323 
324         if (client->priv->watch_id != 0)
325                 g_bus_unwatch_name (client->priv->watch_id);
326 
327         G_OBJECT_CLASS (csm_dbus_client_parent_class)->finalize (object);
328 }
329 
330 static GKeyFile *
dbus_client_save(CsmClient * client,GError ** error)331 dbus_client_save (CsmClient *client,
332                   GError   **error)
333 {
334         g_debug ("CsmDBusClient: saving client with id %s",
335                  csm_client_peek_id (client));
336 
337         /* FIXME: We still don't support client saving for D-Bus
338          * session clients */
339 
340         return NULL;
341 }
342 
343 static gboolean
dbus_client_stop(CsmClient * client,GError ** error)344 dbus_client_stop (CsmClient *client,
345                   GError   **error)
346 {
347         CsmDBusClient  *dbus_client = (CsmDBusClient *) client;
348         csm_exported_client_private_emit_stop (dbus_client->priv->skeleton);
349         return TRUE;
350 }
351 
352 static char *
dbus_client_get_app_name(CsmClient * client)353 dbus_client_get_app_name (CsmClient *client)
354 {
355         /* Always use app-id instead */
356         return NULL;
357 }
358 
359 static CsmClientRestartStyle
dbus_client_get_restart_style_hint(CsmClient * client)360 dbus_client_get_restart_style_hint (CsmClient *client)
361 {
362         return (CSM_DBUS_CLIENT (client)->priv->restart_style_hint);
363 }
364 
365 static guint
dbus_client_get_unix_process_id(CsmClient * client)366 dbus_client_get_unix_process_id (CsmClient *client)
367 {
368         return (CSM_DBUS_CLIENT (client)->priv->caller_pid);
369 }
370 
371 static gboolean
dbus_client_query_end_session(CsmClient * client,CsmClientEndSessionFlag flags,GError ** error)372 dbus_client_query_end_session (CsmClient                *client,
373                                CsmClientEndSessionFlag   flags,
374                                GError                  **error)
375 {
376         CsmDBusClient  *dbus_client = (CsmDBusClient *) client;
377 
378         if (dbus_client->priv->bus_name == NULL) {
379                 g_set_error (error,
380                              CSM_CLIENT_ERROR,
381                              CSM_CLIENT_ERROR_NOT_REGISTERED,
382                              "Client is not registered");
383                 return FALSE;
384         }
385 
386         g_debug ("CsmDBusClient: sending QueryEndSession signal to %s", dbus_client->priv->bus_name);
387 
388         csm_exported_client_private_emit_query_end_session (dbus_client->priv->skeleton, flags);
389         return TRUE;
390 }
391 
392 static gboolean
dbus_client_end_session(CsmClient * client,CsmClientEndSessionFlag flags,GError ** error)393 dbus_client_end_session (CsmClient                *client,
394                          CsmClientEndSessionFlag   flags,
395                          GError                  **error)
396 {
397         CsmDBusClient  *dbus_client = (CsmDBusClient *) client;
398 
399         csm_exported_client_private_emit_end_session (dbus_client->priv->skeleton, flags);
400         return TRUE;
401 }
402 
403 static gboolean
dbus_client_cancel_end_session(CsmClient * client,GError ** error)404 dbus_client_cancel_end_session (CsmClient *client,
405                                 GError   **error)
406 {
407         CsmDBusClient  *dbus_client = (CsmDBusClient *) client;
408         csm_exported_client_private_emit_cancel_end_session (dbus_client->priv->skeleton);
409         return TRUE;
410 }
411 
412 static void
csm_dbus_client_class_init(CsmDBusClientClass * klass)413 csm_dbus_client_class_init (CsmDBusClientClass *klass)
414 {
415         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
416         CsmClientClass *client_class = CSM_CLIENT_CLASS (klass);
417 
418         object_class->finalize             = csm_dbus_client_finalize;
419         object_class->constructor          = csm_dbus_client_constructor;
420         object_class->get_property         = csm_dbus_client_get_property;
421         object_class->set_property         = csm_dbus_client_set_property;
422 
423         client_class->impl_save                   = dbus_client_save;
424         client_class->impl_stop                   = dbus_client_stop;
425         client_class->impl_query_end_session      = dbus_client_query_end_session;
426         client_class->impl_end_session            = dbus_client_end_session;
427         client_class->impl_cancel_end_session     = dbus_client_cancel_end_session;
428         client_class->impl_get_app_name           = dbus_client_get_app_name;
429         client_class->impl_get_restart_style_hint = dbus_client_get_restart_style_hint;
430         client_class->impl_get_unix_process_id    = dbus_client_get_unix_process_id;
431 
432         g_object_class_install_property (object_class,
433                                          PROP_BUS_NAME,
434                                          g_param_spec_string ("bus-name",
435                                                               "bus-name",
436                                                               "bus-name",
437                                                               NULL,
438                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
439 
440         g_type_class_add_private (klass, sizeof (CsmDBusClientPrivate));
441 }
442 
443 CsmClient *
csm_dbus_client_new(const char * startup_id,const char * bus_name)444 csm_dbus_client_new (const char *startup_id,
445                      const char *bus_name)
446 {
447         CsmDBusClient *client;
448 
449         client = g_object_new (CSM_TYPE_DBUS_CLIENT,
450                                "startup-id", startup_id,
451                                "bus-name", bus_name,
452                                NULL);
453 
454         return CSM_CLIENT (client);
455 }
456