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