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 "gdbusinterface.h"
26 : : #include "gdbusinterfaceskeleton.h"
27 : : #include "gdbusobjectskeleton.h"
28 : : #include "gioenumtypes.h"
29 : : #include "gdbusprivate.h"
30 : : #include "gdbusmethodinvocation.h"
31 : : #include "gdbusconnection.h"
32 : : #include "gmarshal-internal.h"
33 : : #include "gtask.h"
34 : : #include "gioerror.h"
35 : :
36 : : #include "glibintl.h"
37 : :
38 : : /**
39 : : * GDBusInterfaceSkeleton:
40 : : *
41 : : * Abstract base class for D-Bus interfaces on the service side.
42 : : *
43 : : * Since: 2.30
44 : : */
45 : :
46 : : struct _GDBusInterfaceSkeletonPrivate
47 : : {
48 : : GMutex lock;
49 : :
50 : : GDBusObject *object;
51 : : GDBusInterfaceSkeletonFlags flags;
52 : :
53 : : GSList *connections; /* List of ConnectionData */
54 : : gchar *object_path; /* The object path for this skeleton */
55 : : GDBusInterfaceVTable *hooked_vtable;
56 : : };
57 : :
58 : : typedef struct
59 : : {
60 : : GDBusConnection *connection;
61 : : guint registration_id;
62 : : } ConnectionData;
63 : :
64 : : enum
65 : : {
66 : : G_AUTHORIZE_METHOD_SIGNAL,
67 : : LAST_SIGNAL
68 : : };
69 : :
70 : : enum
71 : : {
72 : : PROP_0,
73 : : PROP_G_FLAGS
74 : : };
75 : :
76 : : static guint signals[LAST_SIGNAL] = {0};
77 : :
78 : : static void dbus_interface_interface_init (GDBusInterfaceIface *iface);
79 : :
80 : : static void set_object_path_locked (GDBusInterfaceSkeleton *interface_,
81 : : const gchar *object_path);
82 : : static void remove_connection_locked (GDBusInterfaceSkeleton *interface_,
83 : : GDBusConnection *connection);
84 : : static void skeleton_intercept_handle_method_call (GDBusConnection *connection,
85 : : const gchar *sender,
86 : : const gchar *object_path,
87 : : const gchar *interface_name,
88 : : const gchar *method_name,
89 : : GVariant *parameters,
90 : : GDBusMethodInvocation *invocation,
91 : : gpointer user_data);
92 : :
93 : :
94 : 4206 : G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GDBusInterfaceSkeleton, g_dbus_interface_skeleton, G_TYPE_OBJECT,
95 : : G_ADD_PRIVATE (GDBusInterfaceSkeleton)
96 : : G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_INTERFACE, dbus_interface_interface_init))
97 : :
98 : : static void
99 : 41 : g_dbus_interface_skeleton_finalize (GObject *object)
100 : : {
101 : 41 : GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (object);
102 : :
103 : : /* Hold the lock just in case any code we call verifies that the lock is held */
104 : 41 : g_mutex_lock (&interface->priv->lock);
105 : :
106 : : /* unexport from all connections if we're exported anywhere */
107 : 43 : while (interface->priv->connections != NULL)
108 : : {
109 : 2 : ConnectionData *data = interface->priv->connections->data;
110 : 2 : remove_connection_locked (interface, data->connection);
111 : : }
112 : :
113 : 41 : set_object_path_locked (interface, NULL);
114 : :
115 : 41 : g_mutex_unlock (&interface->priv->lock);
116 : :
117 : 41 : g_free (interface->priv->hooked_vtable);
118 : :
119 : 41 : if (interface->priv->object != NULL)
120 : 0 : g_object_remove_weak_pointer (G_OBJECT (interface->priv->object), (gpointer *) &interface->priv->object);
121 : :
122 : 41 : g_mutex_clear (&interface->priv->lock);
123 : :
124 : 41 : G_OBJECT_CLASS (g_dbus_interface_skeleton_parent_class)->finalize (object);
125 : 41 : }
126 : :
127 : : static void
128 : 0 : g_dbus_interface_skeleton_get_property (GObject *object,
129 : : guint prop_id,
130 : : GValue *value,
131 : : GParamSpec *pspec)
132 : : {
133 : 0 : GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (object);
134 : :
135 : 0 : switch (prop_id)
136 : : {
137 : 0 : case PROP_G_FLAGS:
138 : 0 : g_value_set_flags (value, g_dbus_interface_skeleton_get_flags (interface));
139 : 0 : break;
140 : :
141 : 0 : default:
142 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
143 : 0 : break;
144 : : }
145 : 0 : }
146 : :
147 : : static void
148 : 0 : g_dbus_interface_skeleton_set_property (GObject *object,
149 : : guint prop_id,
150 : : const GValue *value,
151 : : GParamSpec *pspec)
152 : : {
153 : 0 : GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (object);
154 : :
155 : 0 : switch (prop_id)
156 : : {
157 : 0 : case PROP_G_FLAGS:
158 : 0 : g_dbus_interface_skeleton_set_flags (interface, g_value_get_flags (value));
159 : 0 : break;
160 : :
161 : 0 : default:
162 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
163 : 0 : break;
164 : : }
165 : 0 : }
166 : :
167 : : static gboolean
168 : 6 : g_dbus_interface_skeleton_g_authorize_method_default (GDBusInterfaceSkeleton *interface,
169 : : GDBusMethodInvocation *invocation)
170 : : {
171 : 6 : return TRUE;
172 : : }
173 : :
174 : : static void
175 : 7 : g_dbus_interface_skeleton_class_init (GDBusInterfaceSkeletonClass *klass)
176 : : {
177 : : GObjectClass *gobject_class;
178 : :
179 : 7 : gobject_class = G_OBJECT_CLASS (klass);
180 : 7 : gobject_class->finalize = g_dbus_interface_skeleton_finalize;
181 : 7 : gobject_class->set_property = g_dbus_interface_skeleton_set_property;
182 : 7 : gobject_class->get_property = g_dbus_interface_skeleton_get_property;
183 : :
184 : 7 : klass->g_authorize_method = g_dbus_interface_skeleton_g_authorize_method_default;
185 : :
186 : : /**
187 : : * GDBusInterfaceSkeleton:g-flags:
188 : : *
189 : : * Flags from the #GDBusInterfaceSkeletonFlags enumeration.
190 : : *
191 : : * Since: 2.30
192 : : */
193 : 7 : g_object_class_install_property (gobject_class,
194 : : PROP_G_FLAGS,
195 : : g_param_spec_flags ("g-flags", NULL, NULL,
196 : : G_TYPE_DBUS_INTERFACE_SKELETON_FLAGS,
197 : : G_DBUS_INTERFACE_SKELETON_FLAGS_NONE,
198 : : G_PARAM_READABLE |
199 : : G_PARAM_WRITABLE |
200 : : G_PARAM_STATIC_STRINGS));
201 : :
202 : : /**
203 : : * GDBusInterfaceSkeleton::g-authorize-method:
204 : : * @interface: The #GDBusInterfaceSkeleton emitting the signal.
205 : : * @invocation: A #GDBusMethodInvocation.
206 : : *
207 : : * Emitted when a method is invoked by a remote caller and used to
208 : : * determine if the method call is authorized.
209 : : *
210 : : * Note that this signal is emitted in a thread dedicated to
211 : : * handling the method call so handlers are allowed to perform
212 : : * blocking IO. This means that it is appropriate to call e.g.
213 : : * [polkit_authority_check_authorization_sync()](http://hal.freedesktop.org/docs/polkit/PolkitAuthority.html#polkit-authority-check-authorization-sync)
214 : : * with the
215 : : * [POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION](http://hal.freedesktop.org/docs/polkit/PolkitAuthority.html#POLKIT-CHECK-AUTHORIZATION-FLAGS-ALLOW-USER-INTERACTION:CAPS)
216 : : * flag set.
217 : : *
218 : : * If %FALSE is returned then no further handlers are run and the
219 : : * signal handler must take a reference to @invocation and finish
220 : : * handling the call (e.g. return an error via
221 : : * g_dbus_method_invocation_return_error()).
222 : : *
223 : : * Otherwise, if %TRUE is returned, signal emission continues. If no
224 : : * handlers return %FALSE, then the method is dispatched. If
225 : : * @interface has an enclosing #GDBusObjectSkeleton, then the
226 : : * #GDBusObjectSkeleton::authorize-method signal handlers run before
227 : : * the handlers for this signal.
228 : : *
229 : : * The default class handler just returns %TRUE.
230 : : *
231 : : * Please note that the common case is optimized: if no signals
232 : : * handlers are connected and the default class handler isn't
233 : : * overridden (for both @interface and the enclosing
234 : : * #GDBusObjectSkeleton, if any) and #GDBusInterfaceSkeleton:g-flags does
235 : : * not have the
236 : : * %G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD
237 : : * flags set, no dedicated thread is ever used and the call will be
238 : : * handled in the same thread as the object that @interface belongs
239 : : * to was exported in.
240 : : *
241 : : * Returns: %TRUE if the call is authorized, %FALSE otherwise.
242 : : *
243 : : * Since: 2.30
244 : : */
245 : 7 : signals[G_AUTHORIZE_METHOD_SIGNAL] =
246 : 7 : g_signal_new (I_("g-authorize-method"),
247 : : G_TYPE_DBUS_INTERFACE_SKELETON,
248 : : G_SIGNAL_RUN_LAST,
249 : : G_STRUCT_OFFSET (GDBusInterfaceSkeletonClass, g_authorize_method),
250 : : _g_signal_accumulator_false_handled,
251 : : NULL,
252 : : _g_cclosure_marshal_BOOLEAN__OBJECT,
253 : : G_TYPE_BOOLEAN,
254 : : 1,
255 : : G_TYPE_DBUS_METHOD_INVOCATION);
256 : 7 : g_signal_set_va_marshaller (signals[G_AUTHORIZE_METHOD_SIGNAL],
257 : : G_TYPE_FROM_CLASS (klass),
258 : : _g_cclosure_marshal_BOOLEAN__OBJECTv);
259 : 7 : }
260 : :
261 : : static void
262 : 63 : g_dbus_interface_skeleton_init (GDBusInterfaceSkeleton *interface)
263 : : {
264 : 63 : interface->priv = g_dbus_interface_skeleton_get_instance_private (interface);
265 : 63 : g_mutex_init (&interface->priv->lock);
266 : 63 : }
267 : :
268 : : /* ---------------------------------------------------------------------------------------------------- */
269 : :
270 : : /**
271 : : * g_dbus_interface_skeleton_get_flags:
272 : : * @interface_: A #GDBusInterfaceSkeleton.
273 : : *
274 : : * Gets the #GDBusInterfaceSkeletonFlags that describes what the behavior
275 : : * of @interface_
276 : : *
277 : : * Returns: One or more flags from the #GDBusInterfaceSkeletonFlags enumeration.
278 : : *
279 : : * Since: 2.30
280 : : */
281 : : GDBusInterfaceSkeletonFlags
282 : 6 : g_dbus_interface_skeleton_get_flags (GDBusInterfaceSkeleton *interface_)
283 : : {
284 : 6 : g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), G_DBUS_INTERFACE_SKELETON_FLAGS_NONE);
285 : 6 : return interface_->priv->flags;
286 : : }
287 : :
288 : : /**
289 : : * g_dbus_interface_skeleton_set_flags:
290 : : * @interface_: A #GDBusInterfaceSkeleton.
291 : : * @flags: Flags from the #GDBusInterfaceSkeletonFlags enumeration.
292 : : *
293 : : * Sets flags describing what the behavior of @skeleton should be.
294 : : *
295 : : * Since: 2.30
296 : : */
297 : : void
298 : 7 : g_dbus_interface_skeleton_set_flags (GDBusInterfaceSkeleton *interface_,
299 : : GDBusInterfaceSkeletonFlags flags)
300 : : {
301 : 7 : g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_));
302 : 7 : g_mutex_lock (&interface_->priv->lock);
303 : 7 : if (interface_->priv->flags != flags)
304 : : {
305 : 7 : interface_->priv->flags = flags;
306 : 7 : g_mutex_unlock (&interface_->priv->lock);
307 : 7 : g_object_notify (G_OBJECT (interface_), "g-flags");
308 : : }
309 : : else
310 : : {
311 : 0 : g_mutex_unlock (&interface_->priv->lock);
312 : : }
313 : : }
314 : :
315 : : /**
316 : : * g_dbus_interface_skeleton_get_info:
317 : : * @interface_: A #GDBusInterfaceSkeleton.
318 : : *
319 : : * Gets D-Bus introspection information for the D-Bus interface
320 : : * implemented by @interface_.
321 : : *
322 : : * Returns: (transfer none): A #GDBusInterfaceInfo (never %NULL). Do not free.
323 : : *
324 : : * Since: 2.30
325 : : */
326 : : GDBusInterfaceInfo *
327 : 240 : g_dbus_interface_skeleton_get_info (GDBusInterfaceSkeleton *interface_)
328 : : {
329 : : GDBusInterfaceInfo *ret;
330 : 240 : g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), NULL);
331 : 240 : ret = G_DBUS_INTERFACE_SKELETON_GET_CLASS (interface_)->get_info (interface_);
332 : 240 : g_warn_if_fail (ret != NULL);
333 : 240 : return ret;
334 : : }
335 : :
336 : : /**
337 : : * g_dbus_interface_skeleton_get_vtable:
338 : : * @interface_: A #GDBusInterfaceSkeleton.
339 : : *
340 : : * Gets the interface vtable for the D-Bus interface implemented by
341 : : * @interface_. The returned function pointers should expect @interface_
342 : : * itself to be passed as @user_data.
343 : : *
344 : : * Returns: (not nullable) (transfer none): the vtable of the D-Bus interface implemented by the skeleton
345 : : *
346 : : * Since: 2.30
347 : : */
348 : : GDBusInterfaceVTable *
349 : 107 : g_dbus_interface_skeleton_get_vtable (GDBusInterfaceSkeleton *interface_)
350 : : {
351 : : GDBusInterfaceVTable *ret;
352 : 107 : g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), NULL);
353 : 107 : ret = G_DBUS_INTERFACE_SKELETON_GET_CLASS (interface_)->get_vtable (interface_);
354 : 107 : g_warn_if_fail (ret != NULL);
355 : 107 : return ret;
356 : : }
357 : :
358 : : /**
359 : : * g_dbus_interface_skeleton_get_properties:
360 : : * @interface_: A #GDBusInterfaceSkeleton.
361 : : *
362 : : * Gets all D-Bus properties for @interface_.
363 : : *
364 : : * Returns: (transfer full): A #GVariant of type
365 : : * ['a{sv}'][G-VARIANT-TYPE-VARDICT:CAPS].
366 : : * Free with g_variant_unref().
367 : : *
368 : : * Since: 2.30
369 : : */
370 : : GVariant *
371 : 58 : g_dbus_interface_skeleton_get_properties (GDBusInterfaceSkeleton *interface_)
372 : : {
373 : : GVariant *ret;
374 : 58 : g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), NULL);
375 : 58 : ret = G_DBUS_INTERFACE_SKELETON_GET_CLASS (interface_)->get_properties (interface_);
376 : 58 : return g_variant_take_ref (ret);
377 : : }
378 : :
379 : : /**
380 : : * g_dbus_interface_skeleton_flush:
381 : : * @interface_: A #GDBusInterfaceSkeleton.
382 : : *
383 : : * If @interface_ has outstanding changes, request for these changes to be
384 : : * emitted immediately.
385 : : *
386 : : * For example, an exported D-Bus interface may queue up property
387 : : * changes and emit the
388 : : * `org.freedesktop.DBus.Properties.PropertiesChanged`
389 : : * signal later (e.g. in an idle handler). This technique is useful
390 : : * for collapsing multiple property changes into one.
391 : : *
392 : : * Since: 2.30
393 : : */
394 : : void
395 : 6 : g_dbus_interface_skeleton_flush (GDBusInterfaceSkeleton *interface_)
396 : : {
397 : 6 : g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_));
398 : 6 : G_DBUS_INTERFACE_SKELETON_GET_CLASS (interface_)->flush (interface_);
399 : : }
400 : :
401 : : /* ---------------------------------------------------------------------------------------------------- */
402 : :
403 : : static GDBusInterfaceInfo *
404 : 42 : _g_dbus_interface_skeleton_get_info (GDBusInterface *interface_)
405 : : {
406 : 42 : GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (interface_);
407 : 42 : return g_dbus_interface_skeleton_get_info (interface);
408 : : }
409 : :
410 : : static GDBusObject *
411 : 12 : g_dbus_interface_skeleton_get_object (GDBusInterface *interface_)
412 : : {
413 : 12 : GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (interface_);
414 : : GDBusObject *ret;
415 : 12 : g_mutex_lock (&interface->priv->lock);
416 : 12 : ret = interface->priv->object;
417 : 12 : g_mutex_unlock (&interface->priv->lock);
418 : 12 : return ret;
419 : : }
420 : :
421 : : static GDBusObject *
422 : 3 : g_dbus_interface_skeleton_dup_object (GDBusInterface *interface_)
423 : : {
424 : 3 : GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (interface_);
425 : : GDBusObject *ret;
426 : 3 : g_mutex_lock (&interface->priv->lock);
427 : 3 : ret = interface->priv->object;
428 : 3 : if (ret != NULL)
429 : 3 : g_object_ref (ret);
430 : 3 : g_mutex_unlock (&interface->priv->lock);
431 : 3 : return ret;
432 : : }
433 : :
434 : : static void
435 : 49 : g_dbus_interface_skeleton_set_object (GDBusInterface *interface_,
436 : : GDBusObject *object)
437 : : {
438 : 49 : GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (interface_);
439 : 49 : g_mutex_lock (&interface->priv->lock);
440 : 49 : if (interface->priv->object != NULL)
441 : 15 : g_object_remove_weak_pointer (G_OBJECT (interface->priv->object), (gpointer *) &interface->priv->object);
442 : 49 : interface->priv->object = object;
443 : 49 : if (object != NULL)
444 : 34 : g_object_add_weak_pointer (G_OBJECT (interface->priv->object), (gpointer *) &interface->priv->object);
445 : 49 : g_mutex_unlock (&interface->priv->lock);
446 : 49 : }
447 : :
448 : : static void
449 : 7 : dbus_interface_interface_init (GDBusInterfaceIface *iface)
450 : : {
451 : 7 : iface->get_info = _g_dbus_interface_skeleton_get_info;
452 : 7 : iface->get_object = g_dbus_interface_skeleton_get_object;
453 : 7 : iface->dup_object = g_dbus_interface_skeleton_dup_object;
454 : 7 : iface->set_object = g_dbus_interface_skeleton_set_object;
455 : 7 : }
456 : :
457 : : /* ---------------------------------------------------------------------------------------------------- */
458 : :
459 : : typedef struct
460 : : {
461 : : gint ref_count; /* (atomic) */
462 : : GDBusInterfaceMethodCallFunc method_call_func;
463 : : GDBusMethodInvocation *invocation; /* (owned) */
464 : : } DispatchData;
465 : :
466 : : static void
467 : 15 : dispatch_data_unref (DispatchData *data)
468 : : {
469 : 15 : if (g_atomic_int_dec_and_test (&data->ref_count))
470 : : {
471 : 12 : g_clear_object (&data->invocation);
472 : 12 : g_slice_free (DispatchData, data);
473 : : }
474 : 15 : }
475 : :
476 : : static DispatchData *
477 : 3 : dispatch_data_ref (DispatchData *data)
478 : : {
479 : 3 : g_atomic_int_inc (&data->ref_count);
480 : 3 : return data;
481 : : }
482 : :
483 : : static gboolean
484 : 3 : dispatch_invoke_in_context_func (gpointer user_data)
485 : : {
486 : 3 : DispatchData *data = user_data;
487 : 3 : data->method_call_func (g_dbus_method_invocation_get_connection (data->invocation),
488 : : g_dbus_method_invocation_get_sender (data->invocation),
489 : : g_dbus_method_invocation_get_object_path (data->invocation),
490 : : g_dbus_method_invocation_get_interface_name (data->invocation),
491 : : g_dbus_method_invocation_get_method_name (data->invocation),
492 : : g_dbus_method_invocation_get_parameters (data->invocation),
493 : : data->invocation,
494 : : g_dbus_method_invocation_get_user_data (data->invocation));
495 : 3 : return FALSE;
496 : : }
497 : :
498 : : static void
499 : 12 : dispatch_in_thread_func (GTask *task,
500 : : gpointer source_object,
501 : : gpointer task_data,
502 : : GCancellable *cancellable)
503 : : {
504 : 12 : DispatchData *data = task_data;
505 : 12 : GDBusInterfaceSkeleton *interface = g_task_get_source_object (task);
506 : : GDBusInterfaceSkeletonFlags flags;
507 : : GDBusObject *object;
508 : : gboolean authorized;
509 : :
510 : 12 : g_mutex_lock (&interface->priv->lock);
511 : 12 : flags = interface->priv->flags;
512 : 12 : object = interface->priv->object;
513 : 12 : if (object != NULL)
514 : 9 : g_object_ref (object);
515 : 12 : g_mutex_unlock (&interface->priv->lock);
516 : :
517 : : /* first check on the enclosing object (if any), then the interface */
518 : 12 : authorized = TRUE;
519 : 12 : if (object != NULL)
520 : : {
521 : 9 : g_signal_emit_by_name (object,
522 : : "authorize-method",
523 : : interface,
524 : : data->invocation,
525 : : &authorized);
526 : : }
527 : 12 : if (authorized)
528 : : {
529 : 9 : g_signal_emit (interface,
530 : : signals[G_AUTHORIZE_METHOD_SIGNAL],
531 : : 0,
532 : : data->invocation,
533 : : &authorized);
534 : : }
535 : :
536 : 12 : if (authorized)
537 : : {
538 : : gboolean run_in_thread;
539 : 6 : run_in_thread = (flags & G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
540 : 6 : if (run_in_thread)
541 : : {
542 : : /* might as well just re-use the existing thread */
543 : 3 : data->method_call_func (g_dbus_method_invocation_get_connection (data->invocation),
544 : : g_dbus_method_invocation_get_sender (data->invocation),
545 : : g_dbus_method_invocation_get_object_path (data->invocation),
546 : : g_dbus_method_invocation_get_interface_name (data->invocation),
547 : : g_dbus_method_invocation_get_method_name (data->invocation),
548 : : g_dbus_method_invocation_get_parameters (data->invocation),
549 : : data->invocation,
550 : : g_dbus_method_invocation_get_user_data (data->invocation));
551 : : }
552 : : else
553 : : {
554 : : /* bah, back to original context */
555 : 3 : g_main_context_invoke_full (g_task_get_context (task),
556 : : g_task_get_priority (task),
557 : : dispatch_invoke_in_context_func,
558 : 3 : dispatch_data_ref (data),
559 : : (GDestroyNotify) dispatch_data_unref);
560 : : }
561 : : }
562 : : else
563 : : {
564 : : /* do nothing */
565 : : }
566 : :
567 : 12 : if (object != NULL)
568 : 9 : g_object_unref (object);
569 : :
570 : 12 : g_task_return_boolean (task, TRUE);
571 : 12 : }
572 : :
573 : : static void
574 : 49 : g_dbus_interface_method_dispatch_helper (GDBusInterfaceSkeleton *interface,
575 : : GDBusInterfaceMethodCallFunc method_call_func,
576 : : GDBusMethodInvocation *invocation)
577 : : {
578 : : gboolean has_handlers;
579 : : gboolean has_default_class_handler;
580 : : gboolean emit_authorized_signal;
581 : : gboolean run_in_thread;
582 : : GDBusInterfaceSkeletonFlags flags;
583 : : GDBusObject *object;
584 : :
585 : 49 : g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface));
586 : 49 : g_return_if_fail (method_call_func != NULL);
587 : 49 : g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
588 : :
589 : 49 : g_mutex_lock (&interface->priv->lock);
590 : 49 : flags = interface->priv->flags;
591 : 49 : object = interface->priv->object;
592 : 49 : if (object != NULL)
593 : 9 : g_object_ref (object);
594 : 49 : g_mutex_unlock (&interface->priv->lock);
595 : :
596 : : /* optimization for the common case where
597 : : *
598 : : * a) no handler is connected and class handler is not overridden (both interface and object); and
599 : : * b) method calls are not dispatched in a thread
600 : : */
601 : 49 : has_handlers = g_signal_has_handler_pending (interface,
602 : : signals[G_AUTHORIZE_METHOD_SIGNAL],
603 : : 0,
604 : : TRUE);
605 : 49 : has_default_class_handler = (G_DBUS_INTERFACE_SKELETON_GET_CLASS (interface)->g_authorize_method ==
606 : : g_dbus_interface_skeleton_g_authorize_method_default);
607 : :
608 : 49 : emit_authorized_signal = (has_handlers || !has_default_class_handler);
609 : 49 : if (!emit_authorized_signal)
610 : : {
611 : 40 : if (object != NULL)
612 : 0 : emit_authorized_signal = _g_dbus_object_skeleton_has_authorize_method_handlers (G_DBUS_OBJECT_SKELETON (object));
613 : : }
614 : :
615 : 49 : run_in_thread = (flags & G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
616 : 49 : if (!emit_authorized_signal && !run_in_thread)
617 : : {
618 : 37 : method_call_func (g_dbus_method_invocation_get_connection (invocation),
619 : : g_dbus_method_invocation_get_sender (invocation),
620 : : g_dbus_method_invocation_get_object_path (invocation),
621 : : g_dbus_method_invocation_get_interface_name (invocation),
622 : : g_dbus_method_invocation_get_method_name (invocation),
623 : : g_dbus_method_invocation_get_parameters (invocation),
624 : : invocation,
625 : : g_dbus_method_invocation_get_user_data (invocation));
626 : : }
627 : : else
628 : : {
629 : : GTask *task;
630 : : DispatchData *data;
631 : :
632 : 12 : data = g_slice_new0 (DispatchData);
633 : 12 : data->method_call_func = method_call_func;
634 : 12 : data->invocation = g_object_ref (invocation);
635 : 12 : data->ref_count = 1;
636 : :
637 : 12 : task = g_task_new (interface, NULL, NULL, NULL);
638 : 12 : g_task_set_source_tag (task, g_dbus_interface_method_dispatch_helper);
639 : 12 : g_task_set_name (task, "[gio] D-Bus interface method dispatch");
640 : 12 : g_task_set_task_data (task, data, (GDestroyNotify) dispatch_data_unref);
641 : 12 : g_task_run_in_thread (task, dispatch_in_thread_func);
642 : 12 : g_object_unref (task);
643 : : }
644 : :
645 : 49 : if (object != NULL)
646 : 9 : g_object_unref (object);
647 : : }
648 : :
649 : : static void
650 : 49 : skeleton_intercept_handle_method_call (GDBusConnection *connection,
651 : : const gchar *sender,
652 : : const gchar *object_path,
653 : : const gchar *interface_name,
654 : : const gchar *method_name,
655 : : GVariant *parameters,
656 : : GDBusMethodInvocation *invocation,
657 : : gpointer user_data)
658 : : {
659 : 49 : GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON (user_data);
660 : 49 : g_dbus_interface_method_dispatch_helper (interface,
661 : 49 : g_dbus_interface_skeleton_get_vtable (interface)->method_call,
662 : : invocation);
663 : 49 : }
664 : :
665 : : /* ---------------------------------------------------------------------------------------------------- */
666 : :
667 : : static ConnectionData *
668 : 67 : new_connection (GDBusConnection *connection,
669 : : guint registration_id)
670 : : {
671 : : ConnectionData *data;
672 : :
673 : 67 : data = g_slice_new0 (ConnectionData);
674 : 67 : data->connection = g_object_ref (connection);
675 : 67 : data->registration_id = registration_id;
676 : :
677 : 67 : return data;
678 : : }
679 : :
680 : : static void
681 : 67 : free_connection (ConnectionData *data)
682 : : {
683 : 67 : if (data != NULL)
684 : : {
685 : 67 : g_object_unref (data->connection);
686 : 67 : g_slice_free (ConnectionData, data);
687 : : }
688 : 67 : }
689 : :
690 : : static gboolean
691 : 67 : add_connection_locked (GDBusInterfaceSkeleton *interface_,
692 : : GDBusConnection *connection,
693 : : GError **error)
694 : : {
695 : : ConnectionData *data;
696 : : guint registration_id;
697 : 67 : gboolean ret = FALSE;
698 : :
699 : 67 : if (interface_->priv->hooked_vtable == NULL)
700 : : {
701 : : /* Hook the vtable since we need to intercept method calls for
702 : : * ::g-authorize-method and for dispatching in thread vs
703 : : * context
704 : : *
705 : : * We need to wait until subclasses have had time to initialize
706 : : * properly before building the hooked_vtable, so we create it
707 : : * once at the last minute.
708 : : */
709 : 54 : interface_->priv->hooked_vtable = g_memdup2 (g_dbus_interface_skeleton_get_vtable (interface_), sizeof (GDBusInterfaceVTable));
710 : 54 : interface_->priv->hooked_vtable->method_call = skeleton_intercept_handle_method_call;
711 : : }
712 : :
713 : 134 : registration_id = g_dbus_connection_register_object (connection,
714 : 67 : interface_->priv->object_path,
715 : : g_dbus_interface_skeleton_get_info (interface_),
716 : 67 : interface_->priv->hooked_vtable,
717 : : interface_,
718 : : NULL, /* user_data_free_func */
719 : : error);
720 : :
721 : 67 : if (registration_id > 0)
722 : : {
723 : 67 : data = new_connection (connection, registration_id);
724 : 67 : interface_->priv->connections = g_slist_append (interface_->priv->connections, data);
725 : 67 : ret = TRUE;
726 : : }
727 : :
728 : 67 : return ret;
729 : : }
730 : :
731 : : static void
732 : 67 : remove_connection_locked (GDBusInterfaceSkeleton *interface_,
733 : : GDBusConnection *connection)
734 : : {
735 : : ConnectionData *data;
736 : : GSList *l;
737 : :
738 : : /* Get the connection in the list and unregister ... */
739 : 67 : for (l = interface_->priv->connections; l != NULL; l = l->next)
740 : : {
741 : 67 : data = l->data;
742 : 67 : if (data->connection == connection)
743 : : {
744 : 67 : g_warn_if_fail (g_dbus_connection_unregister_object (data->connection, data->registration_id));
745 : 67 : free_connection (data);
746 : 67 : interface_->priv->connections = g_slist_delete_link (interface_->priv->connections, l);
747 : : /* we are guaranteed that the connection is only added once, so bail out early */
748 : 67 : goto out;
749 : : }
750 : : }
751 : 0 : out:
752 : : ;
753 : 67 : }
754 : :
755 : : static void
756 : 173 : set_object_path_locked (GDBusInterfaceSkeleton *interface_,
757 : : const gchar *object_path)
758 : : {
759 : 173 : if (g_strcmp0 (interface_->priv->object_path, object_path) != 0)
760 : : {
761 : 132 : g_free (interface_->priv->object_path);
762 : 264 : interface_->priv->object_path = g_strdup (object_path);
763 : : }
764 : 173 : }
765 : :
766 : : /* ---------------------------------------------------------------------------------------------------- */
767 : :
768 : : /**
769 : : * g_dbus_interface_skeleton_get_connection:
770 : : * @interface_: A #GDBusInterfaceSkeleton.
771 : : *
772 : : * Gets the first connection that @interface_ is exported on, if any.
773 : : *
774 : : * Returns: (nullable) (transfer none): A #GDBusConnection or %NULL if @interface_ is
775 : : * not exported anywhere. Do not free, the object belongs to @interface_.
776 : : *
777 : : * Since: 2.30
778 : : */
779 : : GDBusConnection *
780 : 776 : g_dbus_interface_skeleton_get_connection (GDBusInterfaceSkeleton *interface_)
781 : : {
782 : : ConnectionData *data;
783 : : GDBusConnection *ret;
784 : :
785 : 776 : g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), NULL);
786 : 776 : g_mutex_lock (&interface_->priv->lock);
787 : :
788 : 776 : ret = NULL;
789 : 776 : if (interface_->priv->connections != NULL)
790 : : {
791 : 742 : data = interface_->priv->connections->data;
792 : 742 : if (data != NULL)
793 : 742 : ret = data->connection;
794 : : }
795 : :
796 : 776 : g_mutex_unlock (&interface_->priv->lock);
797 : :
798 : 776 : return ret;
799 : : }
800 : :
801 : : /**
802 : : * g_dbus_interface_skeleton_get_connections:
803 : : * @interface_: A #GDBusInterfaceSkeleton.
804 : : *
805 : : * Gets a list of the connections that @interface_ is exported on.
806 : : *
807 : : * Returns: (element-type GDBusConnection) (transfer full): A list of
808 : : * all the connections that @interface_ is exported on. The returned
809 : : * list should be freed with g_list_free() after each element has
810 : : * been freed with g_object_unref().
811 : : *
812 : : * Since: 2.32
813 : : */
814 : : GList *
815 : 68 : g_dbus_interface_skeleton_get_connections (GDBusInterfaceSkeleton *interface_)
816 : : {
817 : : GList *connections;
818 : : GSList *l;
819 : : ConnectionData *data;
820 : :
821 : 68 : g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), NULL);
822 : :
823 : 68 : g_mutex_lock (&interface_->priv->lock);
824 : 68 : connections = NULL;
825 : :
826 : 138 : for (l = interface_->priv->connections; l != NULL; l = l->next)
827 : : {
828 : 70 : data = l->data;
829 : 70 : connections = g_list_prepend (connections,
830 : : /* Return a reference to each connection */
831 : 70 : g_object_ref (data->connection));
832 : : }
833 : :
834 : 68 : g_mutex_unlock (&interface_->priv->lock);
835 : :
836 : 68 : return g_list_reverse (connections);
837 : : }
838 : :
839 : : /**
840 : : * g_dbus_interface_skeleton_has_connection:
841 : : * @interface_: A #GDBusInterfaceSkeleton.
842 : : * @connection: A #GDBusConnection.
843 : : *
844 : : * Checks if @interface_ is exported on @connection.
845 : : *
846 : : * Returns: %TRUE if @interface_ is exported on @connection, %FALSE otherwise.
847 : : *
848 : : * Since: 2.32
849 : : */
850 : : gboolean
851 : 3 : g_dbus_interface_skeleton_has_connection (GDBusInterfaceSkeleton *interface_,
852 : : GDBusConnection *connection)
853 : : {
854 : : GSList *l;
855 : 3 : gboolean ret = FALSE;
856 : :
857 : 3 : g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), FALSE);
858 : 3 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
859 : :
860 : 3 : g_mutex_lock (&interface_->priv->lock);
861 : :
862 : 3 : for (l = interface_->priv->connections; l != NULL; l = l->next)
863 : : {
864 : 0 : ConnectionData *data = l->data;
865 : 0 : if (data->connection == connection)
866 : : {
867 : 0 : ret = TRUE;
868 : 0 : goto out;
869 : : }
870 : : }
871 : :
872 : 3 : out:
873 : 3 : g_mutex_unlock (&interface_->priv->lock);
874 : 3 : return ret;
875 : : }
876 : :
877 : : /**
878 : : * g_dbus_interface_skeleton_get_object_path:
879 : : * @interface_: A #GDBusInterfaceSkeleton.
880 : : *
881 : : * Gets the object path that @interface_ is exported on, if any.
882 : : *
883 : : * Returns: (nullable): A string owned by @interface_ or %NULL if @interface_ is not exported
884 : : * anywhere. Do not free, the string belongs to @interface_.
885 : : *
886 : : * Since: 2.30
887 : : */
888 : : const gchar *
889 : 708 : g_dbus_interface_skeleton_get_object_path (GDBusInterfaceSkeleton *interface_)
890 : : {
891 : : const gchar *ret;
892 : 708 : g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), NULL);
893 : 708 : g_mutex_lock (&interface_->priv->lock);
894 : 708 : ret = interface_->priv->object_path;
895 : 708 : g_mutex_unlock (&interface_->priv->lock);
896 : 708 : return ret;
897 : : }
898 : :
899 : : /**
900 : : * g_dbus_interface_skeleton_export:
901 : : * @interface_: The D-Bus interface to export.
902 : : * @connection: A #GDBusConnection to export @interface_ on.
903 : : * @object_path: The path to export the interface at.
904 : : * @error: Return location for error or %NULL.
905 : : *
906 : : * Exports @interface_ at @object_path on @connection.
907 : : *
908 : : * This can be called multiple times to export the same @interface_
909 : : * onto multiple connections however the @object_path provided must be
910 : : * the same for all connections.
911 : : *
912 : : * Use g_dbus_interface_skeleton_unexport() to unexport the object.
913 : : *
914 : : * Returns: %TRUE if the interface was exported on @connection, otherwise %FALSE with
915 : : * @error set.
916 : : *
917 : : * Since: 2.30
918 : : */
919 : : gboolean
920 : 67 : g_dbus_interface_skeleton_export (GDBusInterfaceSkeleton *interface_,
921 : : GDBusConnection *connection,
922 : : const gchar *object_path,
923 : : GError **error)
924 : : {
925 : 67 : gboolean ret = FALSE;
926 : :
927 : 67 : g_return_val_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_), FALSE);
928 : 67 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
929 : 67 : g_return_val_if_fail (g_variant_is_object_path (object_path), FALSE);
930 : 67 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
931 : :
932 : : /* Assert that the object path is the same for multiple connections here */
933 : 67 : g_return_val_if_fail (interface_->priv->object_path == NULL ||
934 : : g_strcmp0 (interface_->priv->object_path, object_path) == 0, FALSE);
935 : :
936 : 67 : g_mutex_lock (&interface_->priv->lock);
937 : :
938 : : /* Set the object path */
939 : 67 : set_object_path_locked (interface_, object_path);
940 : :
941 : : /* Add the connection */
942 : 67 : ret = add_connection_locked (interface_, connection, error);
943 : :
944 : 67 : g_mutex_unlock (&interface_->priv->lock);
945 : 67 : return ret;
946 : : }
947 : :
948 : : /**
949 : : * g_dbus_interface_skeleton_unexport:
950 : : * @interface_: A #GDBusInterfaceSkeleton.
951 : : *
952 : : * Stops exporting @interface_ on all connections it is exported on.
953 : : *
954 : : * To unexport @interface_ from only a single connection, use
955 : : * g_dbus_interface_skeleton_unexport_from_connection()
956 : : *
957 : : * Since: 2.30
958 : : */
959 : : void
960 : 65 : g_dbus_interface_skeleton_unexport (GDBusInterfaceSkeleton *interface_)
961 : : {
962 : 65 : g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_));
963 : 65 : g_return_if_fail (interface_->priv->connections != NULL);
964 : :
965 : 65 : g_mutex_lock (&interface_->priv->lock);
966 : :
967 : 65 : g_assert (interface_->priv->object_path != NULL);
968 : 65 : g_assert (interface_->priv->hooked_vtable != NULL);
969 : :
970 : : /* Remove all connections */
971 : 130 : while (interface_->priv->connections != NULL)
972 : : {
973 : 65 : ConnectionData *data = interface_->priv->connections->data;
974 : 65 : remove_connection_locked (interface_, data->connection);
975 : : }
976 : :
977 : : /* Unset the object path since there are no connections left */
978 : 65 : set_object_path_locked (interface_, NULL);
979 : :
980 : 65 : g_mutex_unlock (&interface_->priv->lock);
981 : : }
982 : :
983 : :
984 : : /**
985 : : * g_dbus_interface_skeleton_unexport_from_connection:
986 : : * @interface_: A #GDBusInterfaceSkeleton.
987 : : * @connection: A #GDBusConnection.
988 : : *
989 : : * Stops exporting @interface_ on @connection.
990 : : *
991 : : * To stop exporting on all connections the interface is exported on,
992 : : * use g_dbus_interface_skeleton_unexport().
993 : : *
994 : : * Since: 2.32
995 : : */
996 : : void
997 : 0 : g_dbus_interface_skeleton_unexport_from_connection (GDBusInterfaceSkeleton *interface_,
998 : : GDBusConnection *connection)
999 : : {
1000 : 0 : g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface_));
1001 : 0 : g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
1002 : 0 : g_return_if_fail (interface_->priv->connections != NULL);
1003 : :
1004 : 0 : g_mutex_lock (&interface_->priv->lock);
1005 : :
1006 : 0 : g_assert (interface_->priv->object_path != NULL);
1007 : 0 : g_assert (interface_->priv->hooked_vtable != NULL);
1008 : :
1009 : 0 : remove_connection_locked (interface_, connection);
1010 : :
1011 : : /* Reset the object path if we removed the last connection */
1012 : 0 : if (interface_->priv->connections == NULL)
1013 : 0 : set_object_path_locked (interface_, NULL);
1014 : :
1015 : 0 : g_mutex_unlock (&interface_->priv->lock);
1016 : : }
1017 : :
1018 : : /* ---------------------------------------------------------------------------------------------------- */
|