Branch data Line data Source code
1 : : /* GDBus - GLib D-Bus Library
2 : : *
3 : : * Copyright (C) 2008-2010 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: David Zeuthen <davidz@redhat.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include "gdbusobject.h"
26 : : #include "gdbusobjectskeleton.h"
27 : : #include "gdbusinterfaceskeleton.h"
28 : : #include "gdbusprivate.h"
29 : : #include "gdbusmethodinvocation.h"
30 : : #include "gdbusintrospection.h"
31 : : #include "gdbusinterface.h"
32 : : #include "gdbusutils.h"
33 : :
34 : : #include "glibintl.h"
35 : :
36 : : /**
37 : : * GDBusObjectSkeleton:
38 : : *
39 : : * A `GDBusObjectSkeleton` instance is essentially a group of D-Bus
40 : : * interfaces. The set of exported interfaces on the object may be
41 : : * dynamic and change at runtime.
42 : : *
43 : : * This type is intended to be used with [iface@Gio.DBusObjectManager].
44 : : *
45 : : * Since: 2.30
46 : : */
47 : :
48 : : struct _GDBusObjectSkeletonPrivate
49 : : {
50 : : GMutex lock;
51 : : gchar *object_path;
52 : : GHashTable *map_name_to_iface;
53 : : };
54 : :
55 : : enum
56 : : {
57 : : PROP_0,
58 : : PROP_G_OBJECT_PATH
59 : : };
60 : :
61 : : enum
62 : : {
63 : : AUTHORIZE_METHOD_SIGNAL,
64 : : LAST_SIGNAL,
65 : : };
66 : :
67 : : static guint signals[LAST_SIGNAL] = {0};
68 : :
69 : : static void dbus_object_interface_init (GDBusObjectIface *iface);
70 : :
71 : 323 : G_DEFINE_TYPE_WITH_CODE (GDBusObjectSkeleton, g_dbus_object_skeleton, G_TYPE_OBJECT,
72 : : G_ADD_PRIVATE (GDBusObjectSkeleton)
73 : : G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT, dbus_object_interface_init))
74 : :
75 : :
76 : : static void
77 : 13 : g_dbus_object_skeleton_finalize (GObject *_object)
78 : : {
79 : 13 : GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (_object);
80 : :
81 : 13 : g_free (object->priv->object_path);
82 : 13 : g_hash_table_unref (object->priv->map_name_to_iface);
83 : :
84 : 13 : g_mutex_clear (&object->priv->lock);
85 : :
86 : 13 : if (G_OBJECT_CLASS (g_dbus_object_skeleton_parent_class)->finalize != NULL)
87 : 13 : G_OBJECT_CLASS (g_dbus_object_skeleton_parent_class)->finalize (_object);
88 : 13 : }
89 : :
90 : : static void
91 : 1 : g_dbus_object_skeleton_get_property (GObject *_object,
92 : : guint prop_id,
93 : : GValue *value,
94 : : GParamSpec *pspec)
95 : : {
96 : 1 : GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (_object);
97 : :
98 : 1 : switch (prop_id)
99 : : {
100 : 1 : case PROP_G_OBJECT_PATH:
101 : 1 : g_mutex_lock (&object->priv->lock);
102 : 1 : g_value_set_string (value, object->priv->object_path);
103 : 1 : g_mutex_unlock (&object->priv->lock);
104 : 1 : break;
105 : :
106 : 0 : default:
107 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
108 : 0 : break;
109 : : }
110 : 1 : }
111 : :
112 : : static void
113 : 20 : g_dbus_object_skeleton_set_property (GObject *_object,
114 : : guint prop_id,
115 : : const GValue *value,
116 : : GParamSpec *pspec)
117 : : {
118 : 20 : GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (_object);
119 : :
120 : 20 : switch (prop_id)
121 : : {
122 : 20 : case PROP_G_OBJECT_PATH:
123 : 20 : g_dbus_object_skeleton_set_object_path (object, g_value_get_string (value));
124 : 20 : break;
125 : :
126 : 0 : default:
127 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
128 : 0 : break;
129 : : }
130 : 20 : }
131 : :
132 : : static gboolean
133 : 6 : g_dbus_object_skeleton_authorize_method_default (GDBusObjectSkeleton *object,
134 : : GDBusInterfaceSkeleton *interface,
135 : : GDBusMethodInvocation *invocation)
136 : : {
137 : 6 : return TRUE;
138 : : }
139 : :
140 : : static void
141 : 6 : g_dbus_object_skeleton_class_init (GDBusObjectSkeletonClass *klass)
142 : : {
143 : 6 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
144 : :
145 : 6 : gobject_class->finalize = g_dbus_object_skeleton_finalize;
146 : 6 : gobject_class->set_property = g_dbus_object_skeleton_set_property;
147 : 6 : gobject_class->get_property = g_dbus_object_skeleton_get_property;
148 : :
149 : 6 : klass->authorize_method = g_dbus_object_skeleton_authorize_method_default;
150 : :
151 : : /**
152 : : * GDBusObjectSkeleton:g-object-path:
153 : : *
154 : : * The object path where the object is exported.
155 : : *
156 : : * Since: 2.30
157 : : */
158 : 6 : g_object_class_install_property (gobject_class,
159 : : PROP_G_OBJECT_PATH,
160 : : g_param_spec_string ("g-object-path", NULL, NULL,
161 : : NULL,
162 : : G_PARAM_READABLE |
163 : : G_PARAM_WRITABLE |
164 : : G_PARAM_CONSTRUCT |
165 : : G_PARAM_STATIC_STRINGS));
166 : :
167 : : /**
168 : : * GDBusObjectSkeleton::authorize-method:
169 : : * @object: The #GDBusObjectSkeleton emitting the signal.
170 : : * @interface: The #GDBusInterfaceSkeleton that @invocation is for.
171 : : * @invocation: A #GDBusMethodInvocation.
172 : : *
173 : : * Emitted when a method is invoked by a remote caller and used to
174 : : * determine if the method call is authorized.
175 : : *
176 : : * This signal is like #GDBusInterfaceSkeleton's
177 : : * #GDBusInterfaceSkeleton::g-authorize-method signal,
178 : : * except that it is for the enclosing object.
179 : : *
180 : : * The default class handler just returns %TRUE.
181 : : *
182 : : * Returns: %TRUE if the call is authorized, %FALSE otherwise.
183 : : *
184 : : * Since: 2.30
185 : : */
186 : 6 : signals[AUTHORIZE_METHOD_SIGNAL] =
187 : 6 : g_signal_new (I_("authorize-method"),
188 : : G_TYPE_DBUS_OBJECT_SKELETON,
189 : : G_SIGNAL_RUN_LAST,
190 : : G_STRUCT_OFFSET (GDBusObjectSkeletonClass, authorize_method),
191 : : _g_signal_accumulator_false_handled,
192 : : NULL,
193 : : NULL,
194 : : G_TYPE_BOOLEAN,
195 : : 2,
196 : : G_TYPE_DBUS_INTERFACE_SKELETON,
197 : : G_TYPE_DBUS_METHOD_INVOCATION);
198 : 6 : }
199 : :
200 : : static void
201 : 20 : g_dbus_object_skeleton_init (GDBusObjectSkeleton *object)
202 : : {
203 : 20 : object->priv = g_dbus_object_skeleton_get_instance_private (object);
204 : 20 : g_mutex_init (&object->priv->lock);
205 : 20 : object->priv->map_name_to_iface = g_hash_table_new_full (g_str_hash,
206 : : g_str_equal,
207 : : g_free,
208 : : (GDestroyNotify) g_object_unref);
209 : 20 : }
210 : :
211 : : /**
212 : : * g_dbus_object_skeleton_new:
213 : : * @object_path: An object path.
214 : : *
215 : : * Creates a new #GDBusObjectSkeleton.
216 : : *
217 : : * Returns: A #GDBusObjectSkeleton. Free with g_object_unref().
218 : : *
219 : : * Since: 2.30
220 : : */
221 : : GDBusObjectSkeleton *
222 : 7 : g_dbus_object_skeleton_new (const gchar *object_path)
223 : : {
224 : 7 : g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
225 : 7 : return G_DBUS_OBJECT_SKELETON (g_object_new (G_TYPE_DBUS_OBJECT_SKELETON,
226 : : "g-object-path", object_path,
227 : : NULL));
228 : : }
229 : :
230 : : /**
231 : : * g_dbus_object_skeleton_set_object_path:
232 : : * @object: A #GDBusObjectSkeleton.
233 : : * @object_path: A valid D-Bus object path.
234 : : *
235 : : * Sets the object path for @object.
236 : : *
237 : : * Since: 2.30
238 : : */
239 : : void
240 : 23 : g_dbus_object_skeleton_set_object_path (GDBusObjectSkeleton *object,
241 : : const gchar *object_path)
242 : : {
243 : 23 : g_return_if_fail (G_IS_DBUS_OBJECT_SKELETON (object));
244 : 23 : g_return_if_fail (object_path == NULL || g_variant_is_object_path (object_path));
245 : 23 : g_mutex_lock (&object->priv->lock);
246 : : /* TODO: fail if object is currently exported */
247 : 23 : if (g_strcmp0 (object->priv->object_path, object_path) != 0)
248 : : {
249 : 22 : g_free (object->priv->object_path);
250 : 22 : object->priv->object_path = g_strdup (object_path);
251 : 22 : g_mutex_unlock (&object->priv->lock);
252 : 22 : g_object_notify (G_OBJECT (object), "g-object-path");
253 : : }
254 : : else
255 : : {
256 : 1 : g_mutex_unlock (&object->priv->lock);
257 : : }
258 : : }
259 : :
260 : : static const gchar *
261 : 74 : g_dbus_object_skeleton_get_object_path (GDBusObject *_object)
262 : : {
263 : 74 : GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (_object);
264 : : const gchar *ret;
265 : 74 : g_mutex_lock (&object->priv->lock);
266 : 74 : ret = object->priv->object_path;
267 : 74 : g_mutex_unlock (&object->priv->lock);
268 : 74 : return ret;
269 : : }
270 : :
271 : : /**
272 : : * g_dbus_object_skeleton_add_interface:
273 : : * @object: A #GDBusObjectSkeleton.
274 : : * @interface_: A #GDBusInterfaceSkeleton.
275 : : *
276 : : * Adds @interface_ to @object.
277 : : *
278 : : * If @object already contains a #GDBusInterfaceSkeleton with the same
279 : : * interface name, it is removed before @interface_ is added.
280 : : *
281 : : * Note that @object takes its own reference on @interface_ and holds
282 : : * it until removed.
283 : : *
284 : : * Since: 2.30
285 : : */
286 : : void
287 : 34 : g_dbus_object_skeleton_add_interface (GDBusObjectSkeleton *object,
288 : : GDBusInterfaceSkeleton *interface_)
289 : : {
290 : : GDBusInterfaceInfo *info;
291 : : GDBusInterface *interface_to_remove;
292 : :
293 : 34 : g_return_if_fail (G_IS_DBUS_OBJECT_SKELETON (object));
294 : 34 : g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_));
295 : :
296 : 34 : g_mutex_lock (&object->priv->lock);
297 : :
298 : 34 : info = g_dbus_interface_skeleton_get_info (interface_);
299 : 34 : g_object_ref (interface_);
300 : :
301 : 34 : interface_to_remove = g_hash_table_lookup (object->priv->map_name_to_iface, info->name);
302 : 34 : if (interface_to_remove != NULL)
303 : : {
304 : 6 : g_object_ref (interface_to_remove);
305 : 6 : g_warn_if_fail (g_hash_table_remove (object->priv->map_name_to_iface, info->name));
306 : : }
307 : 34 : g_hash_table_insert (object->priv->map_name_to_iface,
308 : 68 : g_strdup (info->name),
309 : : g_object_ref (interface_));
310 : 34 : g_dbus_interface_set_object (G_DBUS_INTERFACE (interface_), G_DBUS_OBJECT (object));
311 : :
312 : 34 : g_mutex_unlock (&object->priv->lock);
313 : :
314 : 34 : if (interface_to_remove != NULL)
315 : : {
316 : 6 : g_dbus_interface_set_object (interface_to_remove, NULL);
317 : 6 : g_signal_emit_by_name (object,
318 : : "interface-removed",
319 : : interface_to_remove);
320 : 6 : g_object_unref (interface_to_remove);
321 : : }
322 : :
323 : 34 : g_signal_emit_by_name (object,
324 : : "interface-added",
325 : : interface_);
326 : 34 : g_object_unref (interface_);
327 : : }
328 : :
329 : : /**
330 : : * g_dbus_object_skeleton_remove_interface:
331 : : * @object: A #GDBusObjectSkeleton.
332 : : * @interface_: A #GDBusInterfaceSkeleton.
333 : : *
334 : : * Removes @interface_ from @object.
335 : : *
336 : : * Since: 2.30
337 : : */
338 : : void
339 : 0 : g_dbus_object_skeleton_remove_interface (GDBusObjectSkeleton *object,
340 : : GDBusInterfaceSkeleton *interface_)
341 : : {
342 : : GDBusInterfaceSkeleton *other_interface;
343 : : GDBusInterfaceInfo *info;
344 : :
345 : 0 : g_return_if_fail (G_IS_DBUS_OBJECT_SKELETON (object));
346 : 0 : g_return_if_fail (G_IS_DBUS_INTERFACE (interface_));
347 : :
348 : 0 : g_mutex_lock (&object->priv->lock);
349 : :
350 : 0 : info = g_dbus_interface_skeleton_get_info (interface_);
351 : :
352 : 0 : other_interface = g_hash_table_lookup (object->priv->map_name_to_iface, info->name);
353 : 0 : if (other_interface == NULL)
354 : : {
355 : 0 : g_mutex_unlock (&object->priv->lock);
356 : 0 : g_warning ("Tried to remove interface with name %s from object "
357 : : "at path %s but no such interface exists",
358 : : info->name,
359 : : object->priv->object_path);
360 : : }
361 : 0 : else if (other_interface != interface_)
362 : : {
363 : 0 : g_mutex_unlock (&object->priv->lock);
364 : 0 : g_warning ("Tried to remove interface %p with name %s from object "
365 : : "at path %s but the object has the interface %p",
366 : : interface_,
367 : : info->name,
368 : : object->priv->object_path,
369 : : other_interface);
370 : : }
371 : : else
372 : : {
373 : 0 : g_object_ref (interface_);
374 : 0 : g_warn_if_fail (g_hash_table_remove (object->priv->map_name_to_iface, info->name));
375 : 0 : g_mutex_unlock (&object->priv->lock);
376 : 0 : g_dbus_interface_set_object (G_DBUS_INTERFACE (interface_), NULL);
377 : 0 : g_signal_emit_by_name (object,
378 : : "interface-removed",
379 : : interface_);
380 : 0 : g_object_unref (interface_);
381 : : }
382 : : }
383 : :
384 : :
385 : : /**
386 : : * g_dbus_object_skeleton_remove_interface_by_name:
387 : : * @object: A #GDBusObjectSkeleton.
388 : : * @interface_name: A D-Bus interface name.
389 : : *
390 : : * Removes the #GDBusInterface with @interface_name from @object.
391 : : *
392 : : * If no D-Bus interface of the given interface exists, this function
393 : : * does nothing.
394 : : *
395 : : * Since: 2.30
396 : : */
397 : : void
398 : 12 : g_dbus_object_skeleton_remove_interface_by_name (GDBusObjectSkeleton *object,
399 : : const gchar *interface_name)
400 : : {
401 : : GDBusInterface *interface;
402 : :
403 : 12 : g_return_if_fail (G_IS_DBUS_OBJECT_SKELETON (object));
404 : 12 : g_return_if_fail (g_dbus_is_interface_name (interface_name));
405 : :
406 : 12 : g_mutex_lock (&object->priv->lock);
407 : 12 : interface = g_hash_table_lookup (object->priv->map_name_to_iface, interface_name);
408 : 12 : if (interface != NULL)
409 : : {
410 : 9 : g_object_ref (interface);
411 : 9 : g_warn_if_fail (g_hash_table_remove (object->priv->map_name_to_iface, interface_name));
412 : 9 : g_mutex_unlock (&object->priv->lock);
413 : 9 : g_dbus_interface_set_object (interface, NULL);
414 : 9 : g_signal_emit_by_name (object,
415 : : "interface-removed",
416 : : interface);
417 : 9 : g_object_unref (interface);
418 : : }
419 : : else
420 : : {
421 : 3 : g_mutex_unlock (&object->priv->lock);
422 : : }
423 : : }
424 : :
425 : : static GDBusInterface *
426 : 6 : g_dbus_object_skeleton_get_interface (GDBusObject *_object,
427 : : const gchar *interface_name)
428 : : {
429 : 6 : GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (_object);
430 : : GDBusInterface *ret;
431 : :
432 : 6 : g_return_val_if_fail (G_IS_DBUS_OBJECT_SKELETON (object), NULL);
433 : 6 : g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL);
434 : :
435 : 6 : g_mutex_lock (&object->priv->lock);
436 : 6 : ret = g_hash_table_lookup (object->priv->map_name_to_iface, interface_name);
437 : 6 : if (ret != NULL)
438 : 3 : g_object_ref (ret);
439 : 6 : g_mutex_unlock (&object->priv->lock);
440 : 6 : return ret;
441 : : }
442 : :
443 : : static GList *
444 : 13 : g_dbus_object_skeleton_get_interfaces (GDBusObject *_object)
445 : : {
446 : 13 : GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (_object);
447 : : GList *ret;
448 : :
449 : 13 : g_return_val_if_fail (G_IS_DBUS_OBJECT_SKELETON (object), NULL);
450 : :
451 : 13 : ret = NULL;
452 : :
453 : 13 : g_mutex_lock (&object->priv->lock);
454 : 13 : ret = g_hash_table_get_values (object->priv->map_name_to_iface);
455 : 13 : g_list_foreach (ret, (GFunc) g_object_ref, NULL);
456 : 13 : g_mutex_unlock (&object->priv->lock);
457 : :
458 : 13 : return ret;
459 : : }
460 : :
461 : : /**
462 : : * g_dbus_object_skeleton_flush:
463 : : * @object: A #GDBusObjectSkeleton.
464 : : *
465 : : * This method simply calls g_dbus_interface_skeleton_flush() on all
466 : : * interfaces belonging to @object. See that method for when flushing
467 : : * is useful.
468 : : *
469 : : * Since: 2.30
470 : : */
471 : : void
472 : 0 : g_dbus_object_skeleton_flush (GDBusObjectSkeleton *object)
473 : : {
474 : : GPtrArray *to_flush;
475 : :
476 : 0 : g_mutex_lock (&object->priv->lock);
477 : 0 : to_flush = g_hash_table_get_values_as_ptr_array (object->priv->map_name_to_iface);
478 : 0 : g_ptr_array_foreach (to_flush, (GFunc) g_object_ref, NULL);
479 : 0 : g_ptr_array_set_free_func (to_flush, g_object_unref);
480 : 0 : g_mutex_unlock (&object->priv->lock);
481 : :
482 : 0 : for (guint i = 0; i < to_flush->len; ++i)
483 : : {
484 : 0 : g_dbus_interface_skeleton_flush (
485 : 0 : G_DBUS_INTERFACE_SKELETON (g_ptr_array_index (to_flush, i)));
486 : : }
487 : :
488 : 0 : g_clear_pointer (&to_flush, g_ptr_array_unref);
489 : 0 : }
490 : :
491 : : static void
492 : 6 : dbus_object_interface_init (GDBusObjectIface *iface)
493 : : {
494 : 6 : iface->get_object_path = g_dbus_object_skeleton_get_object_path;
495 : 6 : iface->get_interfaces = g_dbus_object_skeleton_get_interfaces;
496 : 6 : iface->get_interface = g_dbus_object_skeleton_get_interface;
497 : 6 : }
498 : :
499 : : gboolean
500 : 0 : _g_dbus_object_skeleton_has_authorize_method_handlers (GDBusObjectSkeleton *object)
501 : : {
502 : : gboolean has_handlers;
503 : : gboolean has_default_class_handler;
504 : :
505 : 0 : has_handlers = g_signal_has_handler_pending (object,
506 : : signals[AUTHORIZE_METHOD_SIGNAL],
507 : : 0,
508 : : TRUE);
509 : 0 : has_default_class_handler = (G_DBUS_OBJECT_SKELETON_GET_CLASS (object)->authorize_method ==
510 : : g_dbus_object_skeleton_authorize_method_default);
511 : :
512 : 0 : return has_handlers || !has_default_class_handler;
513 : : }
|