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