nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* GDBus - GLib D-Bus Library |
2 | * |
||
3 | * Copyright (C) 2008-2010 Red Hat, Inc. |
||
4 | * |
||
5 | * This library is free software; you can redistribute it and/or |
||
6 | * modify it under the terms of the GNU Lesser General Public |
||
7 | * License as published by the Free Software Foundation; either |
||
8 | * version 2 of the License, or (at your option) any later version. |
||
9 | * |
||
10 | * This library is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
13 | * Lesser General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU Lesser General |
||
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
17 | * |
||
18 | * Author: David Zeuthen <davidz@redhat.com> |
||
19 | */ |
||
20 | |||
21 | /* |
||
22 | * TODO for GDBus: |
||
23 | * |
||
24 | * - would be nice to expose GDBusAuthMechanism and an extension point |
||
25 | * |
||
26 | * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular |
||
27 | * the mechanism VFuncs need to be able to set an error. |
||
28 | * |
||
29 | * - Need to document other mechanisms/sources for determining the D-Bus |
||
30 | * address of a well-known bus. |
||
31 | * |
||
32 | * - e.g. on Win32 we need code like from here |
||
33 | * |
||
34 | * http://cgit.freedesktop.org/~david/gdbus-standalone/tree/gdbus/gdbusaddress.c#n900 |
||
35 | * |
||
36 | * that was never copied over here because it originally was copy-paste |
||
37 | * from the GPLv2 / AFL 2.1 libdbus sources. |
||
38 | * |
||
39 | * - on OS X we need to look in launchd for the address |
||
40 | * |
||
41 | * https://bugs.freedesktop.org/show_bug.cgi?id=14259 |
||
42 | * |
||
43 | * - on X11 we need to look in a X11 property on the X server |
||
44 | * - (we can also just use dbus-launch(1) from the D-Bus |
||
45 | * distribution) |
||
46 | * |
||
47 | * - (ideally) this requires D-Bus spec work because none of |
||
48 | * this has never really been specced out properly (except |
||
49 | * the X11 bits) |
||
50 | * |
||
51 | * - Related to the above, we also need to be able to launch a message bus |
||
52 | * instance.... Since we don't want to write our own bus daemon we should |
||
53 | * launch dbus-daemon(1) (thus: Win32 and OS X need to bundle it) |
||
54 | * |
||
55 | * - probably want a G_DBUS_NONCE_TCP_TMPDIR environment variable |
||
56 | * to specify where the nonce is stored. This will allow people to use |
||
57 | * G_DBUS_NONCE_TCP_TMPDIR=/mnt/secure.company.server/dbus-nonce-dir |
||
58 | * to easily achieve secure RPC via nonce-tcp. |
||
59 | * |
||
60 | * - need to expose an extension point for resolving D-Bus address and |
||
61 | * turning them into GIOStream objects. This will allow us to implement |
||
62 | * e.g. X11 D-Bus transports without dlopen()'ing or linking against |
||
63 | * libX11 from libgio. |
||
64 | * - see g_dbus_address_connect() in gdbusaddress.c |
||
65 | * |
||
66 | * - would be cute to use kernel-specific APIs to resolve fds for |
||
67 | * debug output when using G_DBUS_DEBUG=message, e.g. in addition to |
||
68 | * |
||
69 | * fd 21: dev=8:1,mode=0100644,ino=1171231,uid=0,gid=0,rdev=0:0,size=234,atime=1273070640,mtime=1267126160,ctime=1267126160 |
||
70 | * |
||
71 | * maybe we can show more information about what fd 21 really is. |
||
72 | * Ryan suggests looking in /proc/self/fd for clues / symlinks! |
||
73 | * Initial experiments on Linux 2.6 suggests that the symlink looks |
||
74 | * like this: |
||
75 | * |
||
76 | * 3 -> /proc/18068/fd |
||
77 | * |
||
78 | * e.g. not of much use. |
||
79 | * |
||
80 | * - GDBus High-Level docs |
||
81 | * - Proxy: properties, signals... |
||
82 | * - Connection: IOStream based, ::close, connection setup steps |
||
83 | * mainloop integration, threading |
||
84 | * - Differences from libdbus (extend "Migrating from") |
||
85 | * - the message handling thread |
||
86 | * - Using GVariant instead of GValue |
||
87 | * - Explain why the high-level API is a good thing and what |
||
88 | * kind of pitfalls it avoids |
||
89 | * - Export objects before claiming names |
||
90 | * - Talk about auto-starting services (cf. GBusNameWatcherFlags) |
||
91 | * |
||
92 | * - use abstract sockets in test code |
||
93 | * - right now it doesn't work, dbus-daemon(1) fails with |
||
94 | * |
||
95 | * /gdbus/connection/filter: Failed to start message bus: Failed to bind |
||
96 | * socket "/tmp/g-dbus-tests-pid-28531": Address already in use |
||
97 | * ** WARNING **: Error reading address from dbus daemon, 0 bytes read |
||
98 | * |
||
99 | * or similar. |
||
100 | */ |
||
101 | |||
102 | #include "config.h" |
||
103 | |||
104 | #include <stdlib.h> |
||
105 | #include <string.h> |
||
106 | |||
107 | #include "gdbusauth.h" |
||
108 | #include "gdbusutils.h" |
||
109 | #include "gdbusaddress.h" |
||
110 | #include "gdbusmessage.h" |
||
111 | #include "gdbusconnection.h" |
||
112 | #include "gdbuserror.h" |
||
113 | #include "gioenumtypes.h" |
||
114 | #include "gdbusintrospection.h" |
||
115 | #include "gdbusmethodinvocation.h" |
||
116 | #include "gdbusprivate.h" |
||
117 | #include "gdbusauthobserver.h" |
||
118 | #include "ginitable.h" |
||
119 | #include "gasyncinitable.h" |
||
120 | #include "giostream.h" |
||
121 | #include "gasyncresult.h" |
||
122 | #include "gtask.h" |
||
123 | |||
124 | #ifdef G_OS_UNIX |
||
125 | #include "gunixconnection.h" |
||
126 | #include "gunixfdmessage.h" |
||
127 | #endif |
||
128 | |||
129 | #include "glibintl.h" |
||
130 | |||
131 | /** |
||
132 | * SECTION:gdbusconnection |
||
133 | * @short_description: D-Bus Connections |
||
134 | * @include: gio/gio.h |
||
135 | * |
||
136 | * The #GDBusConnection type is used for D-Bus connections to remote |
||
137 | * peers such as a message buses. It is a low-level API that offers a |
||
138 | * lot of flexibility. For instance, it lets you establish a connection |
||
139 | * over any transport that can by represented as an #GIOStream. |
||
140 | * |
||
141 | * This class is rarely used directly in D-Bus clients. If you are writing |
||
142 | * a D-Bus client, it is often easier to use the g_bus_own_name(), |
||
143 | * g_bus_watch_name() or g_dbus_proxy_new_for_bus() APIs. |
||
144 | * |
||
145 | * As an exception to the usual GLib rule that a particular object must not |
||
146 | * be used by two threads at the same time, #GDBusConnection's methods may be |
||
147 | * called from any thread. This is so that g_bus_get() and g_bus_get_sync() |
||
148 | * can safely return the same #GDBusConnection when called from any thread. |
||
149 | * |
||
150 | * Most of the ways to obtain a #GDBusConnection automatically initialize it |
||
151 | * (i.e. connect to D-Bus): for instance, g_dbus_connection_new() and |
||
152 | * g_bus_get(), and the synchronous versions of those methods, give you an |
||
153 | * initialized connection. Language bindings for GIO should use |
||
154 | * g_initable_new() or g_async_initable_new_async(), which also initialize the |
||
155 | * connection. |
||
156 | * |
||
157 | * If you construct an uninitialized #GDBusConnection, such as via |
||
158 | * g_object_new(), you must initialize it via g_initable_init() or |
||
159 | * g_async_initable_init_async() before using its methods or properties. |
||
160 | * Calling methods or accessing properties on a #GDBusConnection that has not |
||
161 | * completed initialization successfully is considered to be invalid, and leads |
||
162 | * to undefined behaviour. In particular, if initialization fails with a |
||
163 | * #GError, the only valid thing you can do with that #GDBusConnection is to |
||
164 | * free it with g_object_unref(). |
||
165 | * |
||
166 | * ## An example D-Bus server # {#gdbus-server} |
||
167 | * |
||
168 | * Here is an example for a D-Bus server: |
||
169 | * [gdbus-example-server.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-example-server.c) |
||
170 | * |
||
171 | * ## An example for exporting a subtree # {#gdbus-subtree-server} |
||
172 | * |
||
173 | * Here is an example for exporting a subtree: |
||
174 | * [gdbus-example-subtree.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-example-subtree.c) |
||
175 | * |
||
176 | * ## An example for file descriptor passing # {#gdbus-unix-fd-client} |
||
177 | * |
||
178 | * Here is an example for passing UNIX file descriptors: |
||
179 | * [gdbus-unix-fd-client.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-unix-fd-client.c) |
||
180 | * |
||
181 | * ## An example for exporting a GObject # {#gdbus-export} |
||
182 | * |
||
183 | * Here is an example for exporting a #GObject: |
||
184 | * [gdbus-example-export.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-example-export.c) |
||
185 | */ |
||
186 | |||
187 | /* ---------------------------------------------------------------------------------------------------- */ |
||
188 | |||
189 | typedef struct _GDBusConnectionClass GDBusConnectionClass; |
||
190 | |||
191 | /** |
||
192 | * GDBusConnectionClass: |
||
193 | * @closed: Signal class handler for the #GDBusConnection::closed signal. |
||
194 | * |
||
195 | * Class structure for #GDBusConnection. |
||
196 | * |
||
197 | * Since: 2.26 |
||
198 | */ |
||
199 | struct _GDBusConnectionClass |
||
200 | { |
||
201 | /*< private >*/ |
||
202 | GObjectClass parent_class; |
||
203 | |||
204 | /*< public >*/ |
||
205 | /* Signals */ |
||
206 | void (*closed) (GDBusConnection *connection, |
||
207 | gboolean remote_peer_vanished, |
||
208 | GError *error); |
||
209 | }; |
||
210 | |||
211 | G_LOCK_DEFINE_STATIC (message_bus_lock); |
||
212 | |||
213 | static GWeakRef the_session_bus; |
||
214 | static GWeakRef the_system_bus; |
||
215 | |||
216 | /* Extra pseudo-member of GDBusSendMessageFlags. |
||
217 | * Set by initable_init() to indicate that despite not being initialized yet, |
||
218 | * enough of the only-valid-after-init members are set that we can send a |
||
219 | * message, and we're being called from its thread, so no memory barrier is |
||
220 | * required before accessing them. |
||
221 | */ |
||
222 | #define SEND_MESSAGE_FLAGS_INITIALIZING (1u << 31) |
||
223 | |||
224 | /* Same as SEND_MESSAGE_FLAGS_INITIALIZING, but in GDBusCallFlags */ |
||
225 | #define CALL_FLAGS_INITIALIZING (1u << 31) |
||
226 | |||
227 | /* ---------------------------------------------------------------------------------------------------- */ |
||
228 | |||
229 | typedef struct |
||
230 | { |
||
231 | GDestroyNotify callback; |
||
232 | gpointer user_data; |
||
233 | GMainContext *context; |
||
234 | } CallDestroyNotifyData; |
||
235 | |||
236 | static gboolean |
||
237 | call_destroy_notify_data_in_idle (gpointer user_data) |
||
238 | { |
||
239 | CallDestroyNotifyData *data = user_data; |
||
240 | data->callback (data->user_data); |
||
241 | return FALSE; |
||
242 | } |
||
243 | |||
244 | static void |
||
245 | call_destroy_notify_data_free (CallDestroyNotifyData *data) |
||
246 | { |
||
247 | if (data->context != NULL) |
||
248 | g_main_context_unref (data->context); |
||
249 | g_free (data); |
||
250 | } |
||
251 | |||
252 | /* |
||
253 | * call_destroy_notify: <internal> |
||
254 | * @context: (allow-none): A #GMainContext or %NULL. |
||
255 | * @callback: (allow-none): A #GDestroyNotify or %NULL. |
||
256 | * @user_data: Data to pass to @callback. |
||
257 | * |
||
258 | * Schedules @callback to run in @context. |
||
259 | */ |
||
260 | static void |
||
261 | call_destroy_notify (GMainContext *context, |
||
262 | GDestroyNotify callback, |
||
263 | gpointer user_data) |
||
264 | { |
||
265 | GSource *idle_source; |
||
266 | CallDestroyNotifyData *data; |
||
267 | |||
268 | if (callback == NULL) |
||
269 | goto out; |
||
270 | |||
271 | data = g_new0 (CallDestroyNotifyData, 1); |
||
272 | data->callback = callback; |
||
273 | data->user_data = user_data; |
||
274 | data->context = context; |
||
275 | if (data->context != NULL) |
||
276 | g_main_context_ref (data->context); |
||
277 | |||
278 | idle_source = g_idle_source_new (); |
||
279 | g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); |
||
280 | g_source_set_callback (idle_source, |
||
281 | call_destroy_notify_data_in_idle, |
||
282 | data, |
||
283 | (GDestroyNotify) call_destroy_notify_data_free); |
||
284 | g_source_set_name (idle_source, "[gio] call_destroy_notify_data_in_idle"); |
||
285 | g_source_attach (idle_source, data->context); |
||
286 | g_source_unref (idle_source); |
||
287 | |||
288 | out: |
||
289 | ; |
||
290 | } |
||
291 | |||
292 | /* ---------------------------------------------------------------------------------------------------- */ |
||
293 | |||
294 | static gboolean |
||
295 | _g_strv_has_string (const gchar* const *haystack, |
||
296 | const gchar *needle) |
||
297 | { |
||
298 | guint n; |
||
299 | |||
300 | for (n = 0; haystack != NULL && haystack[n] != NULL; n++) |
||
301 | { |
||
302 | if (g_strcmp0 (haystack[n], needle) == 0) |
||
303 | return TRUE; |
||
304 | } |
||
305 | return FALSE; |
||
306 | } |
||
307 | |||
308 | /* ---------------------------------------------------------------------------------------------------- */ |
||
309 | |||
310 | #ifdef G_OS_WIN32 |
||
311 | #define CONNECTION_ENSURE_LOCK(obj) do { ; } while (FALSE) |
||
312 | #else |
||
313 | // TODO: for some reason this doesn't work on Windows |
||
314 | #define CONNECTION_ENSURE_LOCK(obj) do { \ |
||
315 | if (G_UNLIKELY (g_mutex_trylock(&(obj)->lock))) \ |
||
316 | { \ |
||
317 | g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
||
318 | "CONNECTION_ENSURE_LOCK: GDBusConnection object lock is not locked"); \ |
||
319 | } \ |
||
320 | } while (FALSE) |
||
321 | #endif |
||
322 | |||
323 | #define CONNECTION_LOCK(obj) do { \ |
||
324 | g_mutex_lock (&(obj)->lock); \ |
||
325 | } while (FALSE) |
||
326 | |||
327 | #define CONNECTION_UNLOCK(obj) do { \ |
||
328 | g_mutex_unlock (&(obj)->lock); \ |
||
329 | } while (FALSE) |
||
330 | |||
331 | /* Flags in connection->atomic_flags */ |
||
332 | enum { |
||
333 | FLAG_INITIALIZED = 1 << 0, |
||
334 | FLAG_EXIT_ON_CLOSE = 1 << 1, |
||
335 | FLAG_CLOSED = 1 << 2 |
||
336 | }; |
||
337 | |||
338 | /** |
||
339 | * GDBusConnection: |
||
340 | * |
||
341 | * The #GDBusConnection structure contains only private data and |
||
342 | * should only be accessed using the provided API. |
||
343 | * |
||
344 | * Since: 2.26 |
||
345 | */ |
||
346 | struct _GDBusConnection |
||
347 | { |
||
348 | /*< private >*/ |
||
349 | GObject parent_instance; |
||
350 | |||
351 | /* ------------------------------------------------------------------------ */ |
||
352 | /* -- General object state ------------------------------------------------ */ |
||
353 | /* ------------------------------------------------------------------------ */ |
||
354 | |||
355 | /* General-purpose lock for most fields */ |
||
356 | GMutex lock; |
||
357 | |||
358 | /* A lock used in the init() method of the GInitable interface - see comments |
||
359 | * in initable_init() for why a separate lock is needed. |
||
360 | * |
||
361 | * If you need both @lock and @init_lock, you must take @init_lock first. |
||
362 | */ |
||
363 | GMutex init_lock; |
||
364 | |||
365 | /* Set (by loading the contents of /var/lib/dbus/machine-id) the first time |
||
366 | * someone calls org.freedesktop.DBus.Peer.GetMachineId(). Protected by @lock. |
||
367 | */ |
||
368 | gchar *machine_id; |
||
369 | |||
370 | /* The underlying stream used for communication |
||
371 | * Read-only after initable_init(), so it may be read if you either |
||
372 | * hold @init_lock or check for initialization first. |
||
373 | */ |
||
374 | GIOStream *stream; |
||
375 | |||
376 | /* The object used for authentication (if any). |
||
377 | * Read-only after initable_init(), so it may be read if you either |
||
378 | * hold @init_lock or check for initialization first. |
||
379 | */ |
||
380 | GDBusAuth *auth; |
||
381 | |||
382 | /* Last serial used. Protected by @lock. */ |
||
383 | guint32 last_serial; |
||
384 | |||
385 | /* The object used to send/receive messages. |
||
386 | * Read-only after initable_init(), so it may be read if you either |
||
387 | * hold @init_lock or check for initialization first. |
||
388 | */ |
||
389 | GDBusWorker *worker; |
||
390 | |||
391 | /* If connected to a message bus, this contains the unique name assigned to |
||
392 | * us by the bus (e.g. ":1.42"). |
||
393 | * Read-only after initable_init(), so it may be read if you either |
||
394 | * hold @init_lock or check for initialization first. |
||
395 | */ |
||
396 | gchar *bus_unique_name; |
||
397 | |||
398 | /* The GUID returned by the other side if we authenticed as a client or |
||
399 | * the GUID to use if authenticating as a server. |
||
400 | * Read-only after initable_init(), so it may be read if you either |
||
401 | * hold @init_lock or check for initialization first. |
||
402 | */ |
||
403 | gchar *guid; |
||
404 | |||
405 | /* FLAG_INITIALIZED is set exactly when initable_init() has finished running. |
||
406 | * Inspect @initialization_error to see whether it succeeded or failed. |
||
407 | * |
||
408 | * FLAG_EXIT_ON_CLOSE is the exit-on-close property. |
||
409 | * |
||
410 | * FLAG_CLOSED is the closed property. It may be read at any time, but |
||
411 | * may only be written while holding @lock. |
||
412 | */ |
||
413 | volatile gint atomic_flags; |
||
414 | |||
415 | /* If the connection could not be established during initable_init(), |
||
416 | * this GError will be set. |
||
417 | * Read-only after initable_init(), so it may be read if you either |
||
418 | * hold @init_lock or check for initialization first. |
||
419 | */ |
||
420 | GError *initialization_error; |
||
421 | |||
422 | /* The result of g_main_context_ref_thread_default() when the object |
||
423 | * was created (the GObject _init() function) - this is used for delivery |
||
424 | * of the :closed GObject signal. |
||
425 | * |
||
426 | * Only set in the GObject init function, so no locks are needed. |
||
427 | */ |
||
428 | GMainContext *main_context_at_construction; |
||
429 | |||
430 | /* Read-only construct properties, no locks needed */ |
||
431 | gchar *address; |
||
432 | GDBusConnectionFlags flags; |
||
433 | |||
434 | /* Map used for managing method replies, protected by @lock */ |
||
435 | GHashTable *map_method_serial_to_task; /* guint32 -> GTask* */ |
||
436 | |||
437 | /* Maps used for managing signal subscription, protected by @lock */ |
||
438 | GHashTable *map_rule_to_signal_data; /* match rule (gchar*) -> SignalData */ |
||
439 | GHashTable *map_id_to_signal_data; /* id (guint) -> SignalData */ |
||
440 | GHashTable *map_sender_unique_name_to_signal_data_array; /* unique sender (gchar*) -> GPtrArray* of SignalData */ |
||
441 | |||
442 | /* Maps used for managing exported objects and subtrees, |
||
443 | * protected by @lock |
||
444 | */ |
||
445 | GHashTable *map_object_path_to_eo; /* gchar* -> ExportedObject* */ |
||
446 | GHashTable *map_id_to_ei; /* guint -> ExportedInterface* */ |
||
447 | GHashTable *map_object_path_to_es; /* gchar* -> ExportedSubtree* */ |
||
448 | GHashTable *map_id_to_es; /* guint -> ExportedSubtree* */ |
||
449 | |||
450 | /* Map used for storing last used serials for each thread, protected by @lock */ |
||
451 | GHashTable *map_thread_to_last_serial; |
||
452 | |||
453 | /* Structure used for message filters, protected by @lock */ |
||
454 | GPtrArray *filters; |
||
455 | |||
456 | /* Capabilities negotiated during authentication |
||
457 | * Read-only after initable_init(), so it may be read without holding a |
||
458 | * lock, if you check for initialization first. |
||
459 | */ |
||
460 | GDBusCapabilityFlags capabilities; |
||
461 | |||
462 | /* Protected by @init_lock */ |
||
463 | GDBusAuthObserver *authentication_observer; |
||
464 | |||
465 | /* Read-only after initable_init(), so it may be read if you either |
||
466 | * hold @init_lock or check for initialization first. |
||
467 | */ |
||
468 | GCredentials *credentials; |
||
469 | |||
470 | /* set to TRUE when finalizing */ |
||
471 | gboolean finalizing; |
||
472 | }; |
||
473 | |||
474 | typedef struct ExportedObject ExportedObject; |
||
475 | static void exported_object_free (ExportedObject *eo); |
||
476 | |||
477 | typedef struct ExportedSubtree ExportedSubtree; |
||
478 | static void exported_subtree_free (ExportedSubtree *es); |
||
479 | |||
480 | enum |
||
481 | { |
||
482 | CLOSED_SIGNAL, |
||
483 | LAST_SIGNAL, |
||
484 | }; |
||
485 | |||
486 | enum |
||
487 | { |
||
488 | PROP_0, |
||
489 | PROP_STREAM, |
||
490 | PROP_ADDRESS, |
||
491 | PROP_FLAGS, |
||
492 | PROP_GUID, |
||
493 | PROP_UNIQUE_NAME, |
||
494 | PROP_CLOSED, |
||
495 | PROP_EXIT_ON_CLOSE, |
||
496 | PROP_CAPABILITY_FLAGS, |
||
497 | PROP_AUTHENTICATION_OBSERVER, |
||
498 | }; |
||
499 | |||
500 | static void distribute_signals (GDBusConnection *connection, |
||
501 | GDBusMessage *message); |
||
502 | |||
503 | static void distribute_method_call (GDBusConnection *connection, |
||
504 | GDBusMessage *message); |
||
505 | |||
506 | static gboolean handle_generic_unlocked (GDBusConnection *connection, |
||
507 | GDBusMessage *message); |
||
508 | |||
509 | |||
510 | static void purge_all_signal_subscriptions (GDBusConnection *connection); |
||
511 | static void purge_all_filters (GDBusConnection *connection); |
||
512 | |||
513 | static void schedule_method_call (GDBusConnection *connection, |
||
514 | GDBusMessage *message, |
||
515 | guint registration_id, |
||
516 | guint subtree_registration_id, |
||
517 | const GDBusInterfaceInfo *interface_info, |
||
518 | const GDBusMethodInfo *method_info, |
||
519 | const GDBusPropertyInfo *property_info, |
||
520 | GVariant *parameters, |
||
521 | const GDBusInterfaceVTable *vtable, |
||
522 | GMainContext *main_context, |
||
523 | gpointer user_data); |
||
524 | |||
525 | #define _G_ENSURE_LOCK(name) do { \ |
||
526 | if (G_UNLIKELY (G_TRYLOCK(name))) \ |
||
527 | { \ |
||
528 | g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
||
529 | "_G_ENSURE_LOCK: Lock '" #name "' is not locked"); \ |
||
530 | } \ |
||
531 | } while (FALSE) \ |
||
532 | |||
533 | static guint signals[LAST_SIGNAL] = { 0 }; |
||
534 | |||
535 | static void initable_iface_init (GInitableIface *initable_iface); |
||
536 | static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface); |
||
537 | |||
538 | G_DEFINE_TYPE_WITH_CODE (GDBusConnection, g_dbus_connection, G_TYPE_OBJECT, |
||
539 | G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) |
||
540 | G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) |
||
541 | ); |
||
542 | |||
543 | /* |
||
544 | * Check that all members of @connection that can only be accessed after |
||
545 | * the connection is initialized can safely be accessed. If not, |
||
546 | * log a critical warning. This function is a memory barrier. |
||
547 | * |
||
548 | * Returns: %TRUE if initialized |
||
549 | */ |
||
550 | static gboolean |
||
551 | check_initialized (GDBusConnection *connection) |
||
552 | { |
||
553 | /* The access to @atomic_flags isn't conditional, so that this function |
||
554 | * provides a memory barrier for thread-safety even if checks are disabled. |
||
555 | * (If you don't want this stricter guarantee, you can call |
||
556 | * g_return_if_fail (check_initialized (c)).) |
||
557 | * |
||
558 | * This isn't strictly necessary now that we've decided use of an |
||
559 | * uninitialized GDBusConnection is undefined behaviour, but it seems |
||
560 | * better to be as deterministic as is feasible. |
||
561 | * |
||
562 | * (Anything that could suffer a crash from seeing undefined values |
||
563 | * must have a race condition - thread A initializes the connection while |
||
564 | * thread B calls a method without initialization, hoping that thread A will |
||
565 | * win the race - so its behaviour is undefined anyway.) |
||
566 | */ |
||
567 | gint flags = g_atomic_int_get (&connection->atomic_flags); |
||
568 | |||
569 | g_return_val_if_fail (flags & FLAG_INITIALIZED, FALSE); |
||
570 | |||
571 | /* We can safely access this, due to the memory barrier above */ |
||
572 | g_return_val_if_fail (connection->initialization_error == NULL, FALSE); |
||
573 | |||
574 | return TRUE; |
||
575 | } |
||
576 | |||
577 | typedef enum { |
||
578 | MAY_BE_UNINITIALIZED = (1<<1) |
||
579 | } CheckUnclosedFlags; |
||
580 | |||
581 | /* |
||
582 | * Check the same thing as check_initialized(), and also that the |
||
583 | * connection is not closed. If the connection is uninitialized, |
||
584 | * raise a critical warning (it's programmer error); if it's closed, |
||
585 | * raise a recoverable GError (it's a runtime error). |
||
586 | * |
||
587 | * This function is a memory barrier. |
||
588 | * |
||
589 | * Returns: %TRUE if initialized and not closed |
||
590 | */ |
||
591 | static gboolean |
||
592 | check_unclosed (GDBusConnection *connection, |
||
593 | CheckUnclosedFlags check, |
||
594 | GError **error) |
||
595 | { |
||
596 | /* check_initialized() is effectively inlined, so we don't waste time |
||
597 | * doing two memory barriers |
||
598 | */ |
||
599 | gint flags = g_atomic_int_get (&connection->atomic_flags); |
||
600 | |||
601 | if (!(check & MAY_BE_UNINITIALIZED)) |
||
602 | { |
||
603 | g_return_val_if_fail (flags & FLAG_INITIALIZED, FALSE); |
||
604 | g_return_val_if_fail (connection->initialization_error == NULL, FALSE); |
||
605 | } |
||
606 | |||
607 | if (flags & FLAG_CLOSED) |
||
608 | { |
||
609 | g_set_error_literal (error, |
||
610 | G_IO_ERROR, |
||
611 | G_IO_ERROR_CLOSED, |
||
612 | _("The connection is closed")); |
||
613 | return FALSE; |
||
614 | } |
||
615 | |||
616 | return TRUE; |
||
617 | } |
||
618 | |||
619 | static GHashTable *alive_connections = NULL; |
||
620 | |||
621 | static void |
||
622 | g_dbus_connection_dispose (GObject *object) |
||
623 | { |
||
624 | GDBusConnection *connection = G_DBUS_CONNECTION (object); |
||
625 | |||
626 | G_LOCK (message_bus_lock); |
||
627 | CONNECTION_LOCK (connection); |
||
628 | if (connection->worker != NULL) |
||
629 | { |
||
630 | _g_dbus_worker_stop (connection->worker); |
||
631 | connection->worker = NULL; |
||
632 | if (alive_connections != NULL) |
||
633 | g_warn_if_fail (g_hash_table_remove (alive_connections, connection)); |
||
634 | } |
||
635 | else |
||
636 | { |
||
637 | if (alive_connections != NULL) |
||
638 | g_warn_if_fail (g_hash_table_lookup (alive_connections, connection) == NULL); |
||
639 | } |
||
640 | CONNECTION_UNLOCK (connection); |
||
641 | G_UNLOCK (message_bus_lock); |
||
642 | |||
643 | if (G_OBJECT_CLASS (g_dbus_connection_parent_class)->dispose != NULL) |
||
644 | G_OBJECT_CLASS (g_dbus_connection_parent_class)->dispose (object); |
||
645 | } |
||
646 | |||
647 | static void |
||
648 | g_dbus_connection_finalize (GObject *object) |
||
649 | { |
||
650 | GDBusConnection *connection = G_DBUS_CONNECTION (object); |
||
651 | |||
652 | connection->finalizing = TRUE; |
||
653 | |||
654 | purge_all_signal_subscriptions (connection); |
||
655 | |||
656 | purge_all_filters (connection); |
||
657 | g_ptr_array_unref (connection->filters); |
||
658 | |||
659 | if (connection->authentication_observer != NULL) |
||
660 | g_object_unref (connection->authentication_observer); |
||
661 | |||
662 | if (connection->auth != NULL) |
||
663 | g_object_unref (connection->auth); |
||
664 | |||
665 | if (connection->credentials) |
||
666 | g_object_unref (connection->credentials); |
||
667 | |||
668 | if (connection->stream != NULL) |
||
669 | { |
||
670 | g_object_unref (connection->stream); |
||
671 | connection->stream = NULL; |
||
672 | } |
||
673 | |||
674 | g_free (connection->address); |
||
675 | |||
676 | g_free (connection->guid); |
||
677 | g_free (connection->bus_unique_name); |
||
678 | |||
679 | if (connection->initialization_error != NULL) |
||
680 | g_error_free (connection->initialization_error); |
||
681 | |||
682 | g_hash_table_unref (connection->map_method_serial_to_task); |
||
683 | |||
684 | g_hash_table_unref (connection->map_rule_to_signal_data); |
||
685 | g_hash_table_unref (connection->map_id_to_signal_data); |
||
686 | g_hash_table_unref (connection->map_sender_unique_name_to_signal_data_array); |
||
687 | |||
688 | g_hash_table_unref (connection->map_id_to_ei); |
||
689 | g_hash_table_unref (connection->map_object_path_to_eo); |
||
690 | g_hash_table_unref (connection->map_id_to_es); |
||
691 | g_hash_table_unref (connection->map_object_path_to_es); |
||
692 | |||
693 | g_hash_table_unref (connection->map_thread_to_last_serial); |
||
694 | |||
695 | g_main_context_unref (connection->main_context_at_construction); |
||
696 | |||
697 | g_free (connection->machine_id); |
||
698 | |||
699 | g_mutex_clear (&connection->init_lock); |
||
700 | g_mutex_clear (&connection->lock); |
||
701 | |||
702 | G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize (object); |
||
703 | } |
||
704 | |||
705 | /* called in any user thread, with the connection's lock not held */ |
||
706 | static void |
||
707 | g_dbus_connection_get_property (GObject *object, |
||
708 | guint prop_id, |
||
709 | GValue *value, |
||
710 | GParamSpec *pspec) |
||
711 | { |
||
712 | GDBusConnection *connection = G_DBUS_CONNECTION (object); |
||
713 | |||
714 | switch (prop_id) |
||
715 | { |
||
716 | case PROP_STREAM: |
||
717 | g_value_set_object (value, g_dbus_connection_get_stream (connection)); |
||
718 | break; |
||
719 | |||
720 | case PROP_GUID: |
||
721 | g_value_set_string (value, g_dbus_connection_get_guid (connection)); |
||
722 | break; |
||
723 | |||
724 | case PROP_UNIQUE_NAME: |
||
725 | g_value_set_string (value, g_dbus_connection_get_unique_name (connection)); |
||
726 | break; |
||
727 | |||
728 | case PROP_CLOSED: |
||
729 | g_value_set_boolean (value, g_dbus_connection_is_closed (connection)); |
||
730 | break; |
||
731 | |||
732 | case PROP_EXIT_ON_CLOSE: |
||
733 | g_value_set_boolean (value, g_dbus_connection_get_exit_on_close (connection)); |
||
734 | break; |
||
735 | |||
736 | case PROP_CAPABILITY_FLAGS: |
||
737 | g_value_set_flags (value, g_dbus_connection_get_capabilities (connection)); |
||
738 | break; |
||
739 | |||
740 | default: |
||
741 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
||
742 | break; |
||
743 | } |
||
744 | } |
||
745 | |||
746 | /* called in any user thread, with the connection's lock not held */ |
||
747 | static void |
||
748 | g_dbus_connection_set_property (GObject *object, |
||
749 | guint prop_id, |
||
750 | const GValue *value, |
||
751 | GParamSpec *pspec) |
||
752 | { |
||
753 | GDBusConnection *connection = G_DBUS_CONNECTION (object); |
||
754 | |||
755 | switch (prop_id) |
||
756 | { |
||
757 | case PROP_STREAM: |
||
758 | connection->stream = g_value_dup_object (value); |
||
759 | break; |
||
760 | |||
761 | case PROP_GUID: |
||
762 | connection->guid = g_value_dup_string (value); |
||
763 | break; |
||
764 | |||
765 | case PROP_ADDRESS: |
||
766 | connection->address = g_value_dup_string (value); |
||
767 | break; |
||
768 | |||
769 | case PROP_FLAGS: |
||
770 | connection->flags = g_value_get_flags (value); |
||
771 | break; |
||
772 | |||
773 | case PROP_EXIT_ON_CLOSE: |
||
774 | g_dbus_connection_set_exit_on_close (connection, g_value_get_boolean (value)); |
||
775 | break; |
||
776 | |||
777 | case PROP_AUTHENTICATION_OBSERVER: |
||
778 | connection->authentication_observer = g_value_dup_object (value); |
||
779 | break; |
||
780 | |||
781 | default: |
||
782 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
||
783 | break; |
||
784 | } |
||
785 | } |
||
786 | |||
787 | /* Base-class implementation of GDBusConnection::closed. |
||
788 | * |
||
789 | * Called in a user thread, by the main context that was thread-default when |
||
790 | * the object was constructed. |
||
791 | */ |
||
792 | static void |
||
793 | g_dbus_connection_real_closed (GDBusConnection *connection, |
||
794 | gboolean remote_peer_vanished, |
||
795 | GError *error) |
||
796 | { |
||
797 | gint flags = g_atomic_int_get (&connection->atomic_flags); |
||
798 | |||
799 | /* Because atomic int access is a memory barrier, we can safely read |
||
800 | * initialization_error without a lock, as long as we do it afterwards. |
||
801 | */ |
||
802 | if (remote_peer_vanished && |
||
803 | (flags & FLAG_EXIT_ON_CLOSE) != 0 && |
||
804 | (flags & FLAG_INITIALIZED) != 0 && |
||
805 | connection->initialization_error == NULL) |
||
806 | { |
||
807 | raise (SIGTERM); |
||
808 | } |
||
809 | } |
||
810 | |||
811 | static void |
||
812 | g_dbus_connection_class_init (GDBusConnectionClass *klass) |
||
813 | { |
||
814 | GObjectClass *gobject_class; |
||
815 | |||
816 | gobject_class = G_OBJECT_CLASS (klass); |
||
817 | |||
818 | gobject_class->finalize = g_dbus_connection_finalize; |
||
819 | gobject_class->dispose = g_dbus_connection_dispose; |
||
820 | gobject_class->set_property = g_dbus_connection_set_property; |
||
821 | gobject_class->get_property = g_dbus_connection_get_property; |
||
822 | |||
823 | klass->closed = g_dbus_connection_real_closed; |
||
824 | |||
825 | /** |
||
826 | * GDBusConnection:stream: |
||
827 | * |
||
828 | * The underlying #GIOStream used for I/O. |
||
829 | * |
||
830 | * If this is passed on construction and is a #GSocketConnection, |
||
831 | * then the corresponding #GSocket will be put into non-blocking mode. |
||
832 | * |
||
833 | * While the #GDBusConnection is active, it will interact with this |
||
834 | * stream from a worker thread, so it is not safe to interact with |
||
835 | * the stream directly. |
||
836 | * |
||
837 | * Since: 2.26 |
||
838 | */ |
||
839 | g_object_class_install_property (gobject_class, |
||
840 | PROP_STREAM, |
||
841 | g_param_spec_object ("stream", |
||
842 | P_("IO Stream"), |
||
843 | P_("The underlying streams used for I/O"), |
||
844 | G_TYPE_IO_STREAM, |
||
845 | G_PARAM_READABLE | |
||
846 | G_PARAM_WRITABLE | |
||
847 | G_PARAM_CONSTRUCT_ONLY | |
||
848 | G_PARAM_STATIC_NAME | |
||
849 | G_PARAM_STATIC_BLURB | |
||
850 | G_PARAM_STATIC_NICK)); |
||
851 | |||
852 | /** |
||
853 | * GDBusConnection:address: |
||
854 | * |
||
855 | * A D-Bus address specifying potential endpoints that can be used |
||
856 | * when establishing the connection. |
||
857 | * |
||
858 | * Since: 2.26 |
||
859 | */ |
||
860 | g_object_class_install_property (gobject_class, |
||
861 | PROP_ADDRESS, |
||
862 | g_param_spec_string ("address", |
||
863 | P_("Address"), |
||
864 | P_("D-Bus address specifying potential socket endpoints"), |
||
865 | NULL, |
||
866 | G_PARAM_WRITABLE | |
||
867 | G_PARAM_CONSTRUCT_ONLY | |
||
868 | G_PARAM_STATIC_NAME | |
||
869 | G_PARAM_STATIC_BLURB | |
||
870 | G_PARAM_STATIC_NICK)); |
||
871 | |||
872 | /** |
||
873 | * GDBusConnection:flags: |
||
874 | * |
||
875 | * Flags from the #GDBusConnectionFlags enumeration. |
||
876 | * |
||
877 | * Since: 2.26 |
||
878 | */ |
||
879 | g_object_class_install_property (gobject_class, |
||
880 | PROP_FLAGS, |
||
881 | g_param_spec_flags ("flags", |
||
882 | P_("Flags"), |
||
883 | P_("Flags"), |
||
884 | G_TYPE_DBUS_CONNECTION_FLAGS, |
||
885 | G_DBUS_CONNECTION_FLAGS_NONE, |
||
886 | G_PARAM_WRITABLE | |
||
887 | G_PARAM_CONSTRUCT_ONLY | |
||
888 | G_PARAM_STATIC_NAME | |
||
889 | G_PARAM_STATIC_BLURB | |
||
890 | G_PARAM_STATIC_NICK)); |
||
891 | |||
892 | /** |
||
893 | * GDBusConnection:guid: |
||
894 | * |
||
895 | * The GUID of the peer performing the role of server when |
||
896 | * authenticating. |
||
897 | * |
||
898 | * If you are constructing a #GDBusConnection and pass |
||
899 | * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER in the |
||
900 | * #GDBusConnection:flags property then you MUST also set this |
||
901 | * property to a valid guid. |
||
902 | * |
||
903 | * If you are constructing a #GDBusConnection and pass |
||
904 | * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT in the |
||
905 | * #GDBusConnection:flags property you will be able to read the GUID |
||
906 | * of the other peer here after the connection has been successfully |
||
907 | * initialized. |
||
908 | * |
||
909 | * Since: 2.26 |
||
910 | */ |
||
911 | g_object_class_install_property (gobject_class, |
||
912 | PROP_GUID, |
||
913 | g_param_spec_string ("guid", |
||
914 | P_("GUID"), |
||
915 | P_("GUID of the server peer"), |
||
916 | NULL, |
||
917 | G_PARAM_READABLE | |
||
918 | G_PARAM_WRITABLE | |
||
919 | G_PARAM_CONSTRUCT_ONLY | |
||
920 | G_PARAM_STATIC_NAME | |
||
921 | G_PARAM_STATIC_BLURB | |
||
922 | G_PARAM_STATIC_NICK)); |
||
923 | |||
924 | /** |
||
925 | * GDBusConnection:unique-name: |
||
926 | * |
||
927 | * The unique name as assigned by the message bus or %NULL if the |
||
928 | * connection is not open or not a message bus connection. |
||
929 | * |
||
930 | * Since: 2.26 |
||
931 | */ |
||
932 | g_object_class_install_property (gobject_class, |
||
933 | PROP_UNIQUE_NAME, |
||
934 | g_param_spec_string ("unique-name", |
||
935 | P_("unique-name"), |
||
936 | P_("Unique name of bus connection"), |
||
937 | NULL, |
||
938 | G_PARAM_READABLE | |
||
939 | G_PARAM_STATIC_NAME | |
||
940 | G_PARAM_STATIC_BLURB | |
||
941 | G_PARAM_STATIC_NICK)); |
||
942 | |||
943 | /** |
||
944 | * GDBusConnection:closed: |
||
945 | * |
||
946 | * A boolean specifying whether the connection has been closed. |
||
947 | * |
||
948 | * Since: 2.26 |
||
949 | */ |
||
950 | g_object_class_install_property (gobject_class, |
||
951 | PROP_CLOSED, |
||
952 | g_param_spec_boolean ("closed", |
||
953 | P_("Closed"), |
||
954 | P_("Whether the connection is closed"), |
||
955 | FALSE, |
||
956 | G_PARAM_READABLE | |
||
957 | G_PARAM_STATIC_NAME | |
||
958 | G_PARAM_STATIC_BLURB | |
||
959 | G_PARAM_STATIC_NICK)); |
||
960 | |||
961 | /** |
||
962 | * GDBusConnection:exit-on-close: |
||
963 | * |
||
964 | * A boolean specifying whether the process will be terminated (by |
||
965 | * calling `raise(SIGTERM)`) if the connection is closed by the |
||
966 | * remote peer. |
||
967 | * |
||
968 | * Note that #GDBusConnection objects returned by g_bus_get_finish() |
||
969 | * and g_bus_get_sync() will (usually) have this property set to %TRUE. |
||
970 | * |
||
971 | * Since: 2.26 |
||
972 | */ |
||
973 | g_object_class_install_property (gobject_class, |
||
974 | PROP_EXIT_ON_CLOSE, |
||
975 | g_param_spec_boolean ("exit-on-close", |
||
976 | P_("Exit on close"), |
||
977 | P_("Whether the process is terminated when the connection is closed"), |
||
978 | FALSE, |
||
979 | G_PARAM_READABLE | |
||
980 | G_PARAM_WRITABLE | |
||
981 | G_PARAM_STATIC_NAME | |
||
982 | G_PARAM_STATIC_BLURB | |
||
983 | G_PARAM_STATIC_NICK)); |
||
984 | |||
985 | /** |
||
986 | * GDBusConnection:capabilities: |
||
987 | * |
||
988 | * Flags from the #GDBusCapabilityFlags enumeration |
||
989 | * representing connection features negotiated with the other peer. |
||
990 | * |
||
991 | * Since: 2.26 |
||
992 | */ |
||
993 | g_object_class_install_property (gobject_class, |
||
994 | PROP_CAPABILITY_FLAGS, |
||
995 | g_param_spec_flags ("capabilities", |
||
996 | P_("Capabilities"), |
||
997 | P_("Capabilities"), |
||
998 | G_TYPE_DBUS_CAPABILITY_FLAGS, |
||
999 | G_DBUS_CAPABILITY_FLAGS_NONE, |
||
1000 | G_PARAM_READABLE | |
||
1001 | G_PARAM_STATIC_NAME | |
||
1002 | G_PARAM_STATIC_BLURB | |
||
1003 | G_PARAM_STATIC_NICK)); |
||
1004 | |||
1005 | /** |
||
1006 | * GDBusConnection:authentication-observer: |
||
1007 | * |
||
1008 | * A #GDBusAuthObserver object to assist in the authentication process or %NULL. |
||
1009 | * |
||
1010 | * Since: 2.26 |
||
1011 | */ |
||
1012 | g_object_class_install_property (gobject_class, |
||
1013 | PROP_AUTHENTICATION_OBSERVER, |
||
1014 | g_param_spec_object ("authentication-observer", |
||
1015 | P_("Authentication Observer"), |
||
1016 | P_("Object used to assist in the authentication process"), |
||
1017 | G_TYPE_DBUS_AUTH_OBSERVER, |
||
1018 | G_PARAM_WRITABLE | |
||
1019 | G_PARAM_CONSTRUCT_ONLY | |
||
1020 | G_PARAM_STATIC_NAME | |
||
1021 | G_PARAM_STATIC_BLURB | |
||
1022 | G_PARAM_STATIC_NICK)); |
||
1023 | |||
1024 | /** |
||
1025 | * GDBusConnection::closed: |
||
1026 | * @connection: the #GDBusConnection emitting the signal |
||
1027 | * @remote_peer_vanished: %TRUE if @connection is closed because the |
||
1028 | * remote peer closed its end of the connection |
||
1029 | * @error: (allow-none): a #GError with more details about the event or %NULL |
||
1030 | * |
||
1031 | * Emitted when the connection is closed. |
||
1032 | * |
||
1033 | * The cause of this event can be |
||
1034 | * |
||
1035 | * - If g_dbus_connection_close() is called. In this case |
||
1036 | * @remote_peer_vanished is set to %FALSE and @error is %NULL. |
||
1037 | * |
||
1038 | * - If the remote peer closes the connection. In this case |
||
1039 | * @remote_peer_vanished is set to %TRUE and @error is set. |
||
1040 | * |
||
1041 | * - If the remote peer sends invalid or malformed data. In this |
||
1042 | * case @remote_peer_vanished is set to %FALSE and @error is set. |
||
1043 | * |
||
1044 | * Upon receiving this signal, you should give up your reference to |
||
1045 | * @connection. You are guaranteed that this signal is emitted only |
||
1046 | * once. |
||
1047 | * |
||
1048 | * Since: 2.26 |
||
1049 | */ |
||
1050 | signals[CLOSED_SIGNAL] = g_signal_new (I_("closed"), |
||
1051 | G_TYPE_DBUS_CONNECTION, |
||
1052 | G_SIGNAL_RUN_LAST, |
||
1053 | G_STRUCT_OFFSET (GDBusConnectionClass, closed), |
||
1054 | NULL, |
||
1055 | NULL, |
||
1056 | NULL, |
||
1057 | G_TYPE_NONE, |
||
1058 | 2, |
||
1059 | G_TYPE_BOOLEAN, |
||
1060 | G_TYPE_ERROR); |
||
1061 | } |
||
1062 | |||
1063 | static void |
||
1064 | g_dbus_connection_init (GDBusConnection *connection) |
||
1065 | { |
||
1066 | g_mutex_init (&connection->lock); |
||
1067 | g_mutex_init (&connection->init_lock); |
||
1068 | |||
1069 | connection->map_method_serial_to_task = g_hash_table_new (g_direct_hash, g_direct_equal); |
||
1070 | |||
1071 | connection->map_rule_to_signal_data = g_hash_table_new (g_str_hash, |
||
1072 | g_str_equal); |
||
1073 | connection->map_id_to_signal_data = g_hash_table_new (g_direct_hash, |
||
1074 | g_direct_equal); |
||
1075 | connection->map_sender_unique_name_to_signal_data_array = g_hash_table_new_full (g_str_hash, |
||
1076 | g_str_equal, |
||
1077 | g_free, |
||
1078 | (GDestroyNotify) g_ptr_array_unref); |
||
1079 | |||
1080 | connection->map_object_path_to_eo = g_hash_table_new_full (g_str_hash, |
||
1081 | g_str_equal, |
||
1082 | NULL, |
||
1083 | (GDestroyNotify) exported_object_free); |
||
1084 | |||
1085 | connection->map_id_to_ei = g_hash_table_new (g_direct_hash, |
||
1086 | g_direct_equal); |
||
1087 | |||
1088 | connection->map_object_path_to_es = g_hash_table_new_full (g_str_hash, |
||
1089 | g_str_equal, |
||
1090 | NULL, |
||
1091 | (GDestroyNotify) exported_subtree_free); |
||
1092 | |||
1093 | connection->map_id_to_es = g_hash_table_new (g_direct_hash, |
||
1094 | g_direct_equal); |
||
1095 | |||
1096 | connection->map_thread_to_last_serial = g_hash_table_new (g_direct_hash, |
||
1097 | g_direct_equal); |
||
1098 | |||
1099 | connection->main_context_at_construction = g_main_context_ref_thread_default (); |
||
1100 | |||
1101 | connection->filters = g_ptr_array_new (); |
||
1102 | } |
||
1103 | |||
1104 | /** |
||
1105 | * g_dbus_connection_get_stream: |
||
1106 | * @connection: a #GDBusConnection |
||
1107 | * |
||
1108 | * Gets the underlying stream used for IO. |
||
1109 | * |
||
1110 | * While the #GDBusConnection is active, it will interact with this |
||
1111 | * stream from a worker thread, so it is not safe to interact with |
||
1112 | * the stream directly. |
||
1113 | * |
||
1114 | * Returns: (transfer none): the stream used for IO |
||
1115 | * |
||
1116 | * Since: 2.26 |
||
1117 | */ |
||
1118 | GIOStream * |
||
1119 | g_dbus_connection_get_stream (GDBusConnection *connection) |
||
1120 | { |
||
1121 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); |
||
1122 | |||
1123 | /* do not use g_return_val_if_fail(), we want the memory barrier */ |
||
1124 | if (!check_initialized (connection)) |
||
1125 | return NULL; |
||
1126 | |||
1127 | return connection->stream; |
||
1128 | } |
||
1129 | |||
1130 | /** |
||
1131 | * g_dbus_connection_start_message_processing: |
||
1132 | * @connection: a #GDBusConnection |
||
1133 | * |
||
1134 | * If @connection was created with |
||
1135 | * %G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, this method |
||
1136 | * starts processing messages. Does nothing on if @connection wasn't |
||
1137 | * created with this flag or if the method has already been called. |
||
1138 | * |
||
1139 | * Since: 2.26 |
||
1140 | */ |
||
1141 | void |
||
1142 | g_dbus_connection_start_message_processing (GDBusConnection *connection) |
||
1143 | { |
||
1144 | g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); |
||
1145 | |||
1146 | /* do not use g_return_val_if_fail(), we want the memory barrier */ |
||
1147 | if (!check_initialized (connection)) |
||
1148 | return; |
||
1149 | |||
1150 | g_assert (connection->worker != NULL); |
||
1151 | _g_dbus_worker_unfreeze (connection->worker); |
||
1152 | } |
||
1153 | |||
1154 | /** |
||
1155 | * g_dbus_connection_is_closed: |
||
1156 | * @connection: a #GDBusConnection |
||
1157 | * |
||
1158 | * Gets whether @connection is closed. |
||
1159 | * |
||
1160 | * Returns: %TRUE if the connection is closed, %FALSE otherwise |
||
1161 | * |
||
1162 | * Since: 2.26 |
||
1163 | */ |
||
1164 | gboolean |
||
1165 | g_dbus_connection_is_closed (GDBusConnection *connection) |
||
1166 | { |
||
1167 | gint flags; |
||
1168 | |||
1169 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
1170 | |||
1171 | flags = g_atomic_int_get (&connection->atomic_flags); |
||
1172 | |||
1173 | return (flags & FLAG_CLOSED) ? TRUE : FALSE; |
||
1174 | } |
||
1175 | |||
1176 | /** |
||
1177 | * g_dbus_connection_get_capabilities: |
||
1178 | * @connection: a #GDBusConnection |
||
1179 | * |
||
1180 | * Gets the capabilities negotiated with the remote peer |
||
1181 | * |
||
1182 | * Returns: zero or more flags from the #GDBusCapabilityFlags enumeration |
||
1183 | * |
||
1184 | * Since: 2.26 |
||
1185 | */ |
||
1186 | GDBusCapabilityFlags |
||
1187 | g_dbus_connection_get_capabilities (GDBusConnection *connection) |
||
1188 | { |
||
1189 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_DBUS_CAPABILITY_FLAGS_NONE); |
||
1190 | |||
1191 | /* do not use g_return_val_if_fail(), we want the memory barrier */ |
||
1192 | if (!check_initialized (connection)) |
||
1193 | return G_DBUS_CAPABILITY_FLAGS_NONE; |
||
1194 | |||
1195 | return connection->capabilities; |
||
1196 | } |
||
1197 | |||
1198 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1199 | |||
1200 | /* Called in a temporary thread without holding locks. */ |
||
1201 | static void |
||
1202 | flush_in_thread_func (GTask *task, |
||
1203 | gpointer source_object, |
||
1204 | gpointer task_data, |
||
1205 | GCancellable *cancellable) |
||
1206 | { |
||
1207 | GError *error = NULL; |
||
1208 | |||
1209 | if (g_dbus_connection_flush_sync (source_object, |
||
1210 | cancellable, |
||
1211 | &error)) |
||
1212 | g_task_return_boolean (task, TRUE); |
||
1213 | else |
||
1214 | g_task_return_error (task, error); |
||
1215 | } |
||
1216 | |||
1217 | /** |
||
1218 | * g_dbus_connection_flush: |
||
1219 | * @connection: a #GDBusConnection |
||
1220 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
1221 | * @callback: (allow-none): a #GAsyncReadyCallback to call when the |
||
1222 | * request is satisfied or %NULL if you don't care about the result |
||
1223 | * @user_data: The data to pass to @callback |
||
1224 | * |
||
1225 | * Asynchronously flushes @connection, that is, writes all queued |
||
1226 | * outgoing message to the transport and then flushes the transport |
||
1227 | * (using g_output_stream_flush_async()). This is useful in programs |
||
1228 | * that wants to emit a D-Bus signal and then exit immediately. Without |
||
1229 | * flushing the connection, there is no guaranteed that the message has |
||
1230 | * been sent to the networking buffers in the OS kernel. |
||
1231 | * |
||
1232 | * This is an asynchronous method. When the operation is finished, |
||
1233 | * @callback will be invoked in the |
||
1234 | * [thread-default main context][g-main-context-push-thread-default] |
||
1235 | * of the thread you are calling this method from. You can |
||
1236 | * then call g_dbus_connection_flush_finish() to get the result of the |
||
1237 | * operation. See g_dbus_connection_flush_sync() for the synchronous |
||
1238 | * version. |
||
1239 | * |
||
1240 | * Since: 2.26 |
||
1241 | */ |
||
1242 | void |
||
1243 | g_dbus_connection_flush (GDBusConnection *connection, |
||
1244 | GCancellable *cancellable, |
||
1245 | GAsyncReadyCallback callback, |
||
1246 | gpointer user_data) |
||
1247 | { |
||
1248 | GTask *task; |
||
1249 | |||
1250 | g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); |
||
1251 | |||
1252 | task = g_task_new (connection, cancellable, callback, user_data); |
||
1253 | g_task_run_in_thread (task, flush_in_thread_func); |
||
1254 | g_object_unref (task); |
||
1255 | } |
||
1256 | |||
1257 | /** |
||
1258 | * g_dbus_connection_flush_finish: |
||
1259 | * @connection: a #GDBusConnection |
||
1260 | * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed |
||
1261 | * to g_dbus_connection_flush() |
||
1262 | * @error: return location for error or %NULL |
||
1263 | * |
||
1264 | * Finishes an operation started with g_dbus_connection_flush(). |
||
1265 | * |
||
1266 | * Returns: %TRUE if the operation succeeded, %FALSE if @error is set |
||
1267 | * |
||
1268 | * Since: 2.26 |
||
1269 | */ |
||
1270 | gboolean |
||
1271 | g_dbus_connection_flush_finish (GDBusConnection *connection, |
||
1272 | GAsyncResult *res, |
||
1273 | GError **error) |
||
1274 | { |
||
1275 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
1276 | g_return_val_if_fail (g_task_is_valid (res, connection), FALSE); |
||
1277 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
||
1278 | |||
1279 | return g_task_propagate_boolean (G_TASK (res), error); |
||
1280 | } |
||
1281 | |||
1282 | /** |
||
1283 | * g_dbus_connection_flush_sync: |
||
1284 | * @connection: a #GDBusConnection |
||
1285 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
1286 | * @error: return location for error or %NULL |
||
1287 | * |
||
1288 | * Synchronously flushes @connection. The calling thread is blocked |
||
1289 | * until this is done. See g_dbus_connection_flush() for the |
||
1290 | * asynchronous version of this method and more details about what it |
||
1291 | * does. |
||
1292 | * |
||
1293 | * Returns: %TRUE if the operation succeeded, %FALSE if @error is set |
||
1294 | * |
||
1295 | * Since: 2.26 |
||
1296 | */ |
||
1297 | gboolean |
||
1298 | g_dbus_connection_flush_sync (GDBusConnection *connection, |
||
1299 | GCancellable *cancellable, |
||
1300 | GError **error) |
||
1301 | { |
||
1302 | gboolean ret; |
||
1303 | |||
1304 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
1305 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
||
1306 | |||
1307 | ret = FALSE; |
||
1308 | |||
1309 | /* This is only a best-effort attempt to see whether the connection is |
||
1310 | * closed, so it doesn't need the lock. If the connection closes just |
||
1311 | * after this check, but before scheduling the flush operation, the |
||
1312 | * result will be more or less the same as if the connection closed while |
||
1313 | * the flush operation was pending - it'll fail with either CLOSED or |
||
1314 | * CANCELLED. |
||
1315 | */ |
||
1316 | if (!check_unclosed (connection, 0, error)) |
||
1317 | goto out; |
||
1318 | |||
1319 | g_assert (connection->worker != NULL); |
||
1320 | |||
1321 | ret = _g_dbus_worker_flush_sync (connection->worker, |
||
1322 | cancellable, |
||
1323 | error); |
||
1324 | |||
1325 | out: |
||
1326 | return ret; |
||
1327 | } |
||
1328 | |||
1329 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1330 | |||
1331 | typedef struct |
||
1332 | { |
||
1333 | GDBusConnection *connection; |
||
1334 | GError *error; |
||
1335 | gboolean remote_peer_vanished; |
||
1336 | } EmitClosedData; |
||
1337 | |||
1338 | static void |
||
1339 | emit_closed_data_free (EmitClosedData *data) |
||
1340 | { |
||
1341 | g_object_unref (data->connection); |
||
1342 | if (data->error != NULL) |
||
1343 | g_error_free (data->error); |
||
1344 | g_free (data); |
||
1345 | } |
||
1346 | |||
1347 | /* Called in a user thread that has acquired the main context that was |
||
1348 | * thread-default when the object was constructed |
||
1349 | */ |
||
1350 | static gboolean |
||
1351 | emit_closed_in_idle (gpointer user_data) |
||
1352 | { |
||
1353 | EmitClosedData *data = user_data; |
||
1354 | gboolean result; |
||
1355 | |||
1356 | g_object_notify (G_OBJECT (data->connection), "closed"); |
||
1357 | g_signal_emit (data->connection, |
||
1358 | signals[CLOSED_SIGNAL], |
||
1359 | 0, |
||
1360 | data->remote_peer_vanished, |
||
1361 | data->error, |
||
1362 | &result); |
||
1363 | return FALSE; |
||
1364 | } |
||
1365 | |||
1366 | /* Can be called from any thread, must hold lock. |
||
1367 | * FLAG_CLOSED must already have been set. |
||
1368 | */ |
||
1369 | static void |
||
1370 | schedule_closed_unlocked (GDBusConnection *connection, |
||
1371 | gboolean remote_peer_vanished, |
||
1372 | GError *error) |
||
1373 | { |
||
1374 | GSource *idle_source; |
||
1375 | EmitClosedData *data; |
||
1376 | |||
1377 | CONNECTION_ENSURE_LOCK (connection); |
||
1378 | |||
1379 | data = g_new0 (EmitClosedData, 1); |
||
1380 | data->connection = g_object_ref (connection); |
||
1381 | data->remote_peer_vanished = remote_peer_vanished; |
||
1382 | data->error = error != NULL ? g_error_copy (error) : NULL; |
||
1383 | |||
1384 | idle_source = g_idle_source_new (); |
||
1385 | g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); |
||
1386 | g_source_set_callback (idle_source, |
||
1387 | emit_closed_in_idle, |
||
1388 | data, |
||
1389 | (GDestroyNotify) emit_closed_data_free); |
||
1390 | g_source_set_name (idle_source, "[gio] emit_closed_in_idle"); |
||
1391 | g_source_attach (idle_source, connection->main_context_at_construction); |
||
1392 | g_source_unref (idle_source); |
||
1393 | } |
||
1394 | |||
1395 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1396 | |||
1397 | /** |
||
1398 | * g_dbus_connection_close: |
||
1399 | * @connection: a #GDBusConnection |
||
1400 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
1401 | * @callback: (allow-none): a #GAsyncReadyCallback to call when the request is |
||
1402 | * satisfied or %NULL if you don't care about the result |
||
1403 | * @user_data: The data to pass to @callback |
||
1404 | * |
||
1405 | * Closes @connection. Note that this never causes the process to |
||
1406 | * exit (this might only happen if the other end of a shared message |
||
1407 | * bus connection disconnects, see #GDBusConnection:exit-on-close). |
||
1408 | * |
||
1409 | * Once the connection is closed, operations such as sending a message |
||
1410 | * will return with the error %G_IO_ERROR_CLOSED. Closing a connection |
||
1411 | * will not automatically flush the connection so queued messages may |
||
1412 | * be lost. Use g_dbus_connection_flush() if you need such guarantees. |
||
1413 | * |
||
1414 | * If @connection is already closed, this method fails with |
||
1415 | * %G_IO_ERROR_CLOSED. |
||
1416 | * |
||
1417 | * When @connection has been closed, the #GDBusConnection::closed |
||
1418 | * signal is emitted in the |
||
1419 | * [thread-default main context][g-main-context-push-thread-default] |
||
1420 | * of the thread that @connection was constructed in. |
||
1421 | * |
||
1422 | * This is an asynchronous method. When the operation is finished, |
||
1423 | * @callback will be invoked in the |
||
1424 | * [thread-default main context][g-main-context-push-thread-default] |
||
1425 | * of the thread you are calling this method from. You can |
||
1426 | * then call g_dbus_connection_close_finish() to get the result of the |
||
1427 | * operation. See g_dbus_connection_close_sync() for the synchronous |
||
1428 | * version. |
||
1429 | * |
||
1430 | * Since: 2.26 |
||
1431 | */ |
||
1432 | void |
||
1433 | g_dbus_connection_close (GDBusConnection *connection, |
||
1434 | GCancellable *cancellable, |
||
1435 | GAsyncReadyCallback callback, |
||
1436 | gpointer user_data) |
||
1437 | { |
||
1438 | GTask *task; |
||
1439 | |||
1440 | g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); |
||
1441 | |||
1442 | /* do not use g_return_val_if_fail(), we want the memory barrier */ |
||
1443 | if (!check_initialized (connection)) |
||
1444 | return; |
||
1445 | |||
1446 | g_assert (connection->worker != NULL); |
||
1447 | |||
1448 | task = g_task_new (connection, cancellable, callback, user_data); |
||
1449 | _g_dbus_worker_close (connection->worker, task); |
||
1450 | g_object_unref (task); |
||
1451 | } |
||
1452 | |||
1453 | /** |
||
1454 | * g_dbus_connection_close_finish: |
||
1455 | * @connection: a #GDBusConnection |
||
1456 | * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed |
||
1457 | * to g_dbus_connection_close() |
||
1458 | * @error: return location for error or %NULL |
||
1459 | * |
||
1460 | * Finishes an operation started with g_dbus_connection_close(). |
||
1461 | * |
||
1462 | * Returns: %TRUE if the operation succeeded, %FALSE if @error is set |
||
1463 | * |
||
1464 | * Since: 2.26 |
||
1465 | */ |
||
1466 | gboolean |
||
1467 | g_dbus_connection_close_finish (GDBusConnection *connection, |
||
1468 | GAsyncResult *res, |
||
1469 | GError **error) |
||
1470 | { |
||
1471 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
1472 | g_return_val_if_fail (g_task_is_valid (res, connection), FALSE); |
||
1473 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
||
1474 | |||
1475 | return g_task_propagate_boolean (G_TASK (res), error); |
||
1476 | } |
||
1477 | |||
1478 | typedef struct { |
||
1479 | GMainLoop *loop; |
||
1480 | GAsyncResult *result; |
||
1481 | } SyncCloseData; |
||
1482 | |||
1483 | /* Can be called by any thread, without the connection lock */ |
||
1484 | static void |
||
1485 | sync_close_cb (GObject *source_object, |
||
1486 | GAsyncResult *res, |
||
1487 | gpointer user_data) |
||
1488 | { |
||
1489 | SyncCloseData *data = user_data; |
||
1490 | |||
1491 | data->result = g_object_ref (res); |
||
1492 | g_main_loop_quit (data->loop); |
||
1493 | } |
||
1494 | |||
1495 | /** |
||
1496 | * g_dbus_connection_close_sync: |
||
1497 | * @connection: a #GDBusConnection |
||
1498 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
1499 | * @error: return location for error or %NULL |
||
1500 | * |
||
1501 | * Synchronously closees @connection. The calling thread is blocked |
||
1502 | * until this is done. See g_dbus_connection_close() for the |
||
1503 | * asynchronous version of this method and more details about what it |
||
1504 | * does. |
||
1505 | * |
||
1506 | * Returns: %TRUE if the operation succeeded, %FALSE if @error is set |
||
1507 | * |
||
1508 | * Since: 2.26 |
||
1509 | */ |
||
1510 | gboolean |
||
1511 | g_dbus_connection_close_sync (GDBusConnection *connection, |
||
1512 | GCancellable *cancellable, |
||
1513 | GError **error) |
||
1514 | { |
||
1515 | gboolean ret; |
||
1516 | |||
1517 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
1518 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
||
1519 | |||
1520 | ret = FALSE; |
||
1521 | |||
1522 | if (check_unclosed (connection, 0, error)) |
||
1523 | { |
||
1524 | GMainContext *context; |
||
1525 | SyncCloseData data; |
||
1526 | |||
1527 | context = g_main_context_new (); |
||
1528 | g_main_context_push_thread_default (context); |
||
1529 | data.loop = g_main_loop_new (context, TRUE); |
||
1530 | data.result = NULL; |
||
1531 | |||
1532 | g_dbus_connection_close (connection, cancellable, sync_close_cb, &data); |
||
1533 | g_main_loop_run (data.loop); |
||
1534 | ret = g_dbus_connection_close_finish (connection, data.result, error); |
||
1535 | |||
1536 | g_object_unref (data.result); |
||
1537 | g_main_loop_unref (data.loop); |
||
1538 | g_main_context_pop_thread_default (context); |
||
1539 | g_main_context_unref (context); |
||
1540 | } |
||
1541 | |||
1542 | return ret; |
||
1543 | } |
||
1544 | |||
1545 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1546 | |||
1547 | /** |
||
1548 | * g_dbus_connection_get_last_serial: |
||
1549 | * @connection: a #GDBusConnection |
||
1550 | * |
||
1551 | * Retrieves the last serial number assigned to a #GDBusMessage on |
||
1552 | * the current thread. This includes messages sent via both low-level |
||
1553 | * API such as g_dbus_connection_send_message() as well as |
||
1554 | * high-level API such as g_dbus_connection_emit_signal(), |
||
1555 | * g_dbus_connection_call() or g_dbus_proxy_call(). |
||
1556 | * |
||
1557 | * Returns: the last used serial or zero when no message has been sent |
||
1558 | * within the current thread |
||
1559 | * |
||
1560 | * Since: 2.34 |
||
1561 | */ |
||
1562 | guint32 |
||
1563 | g_dbus_connection_get_last_serial (GDBusConnection *connection) |
||
1564 | { |
||
1565 | guint32 ret; |
||
1566 | |||
1567 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); |
||
1568 | |||
1569 | CONNECTION_LOCK (connection); |
||
1570 | ret = GPOINTER_TO_UINT (g_hash_table_lookup (connection->map_thread_to_last_serial, |
||
1571 | g_thread_self ())); |
||
1572 | CONNECTION_UNLOCK (connection); |
||
1573 | |||
1574 | return ret; |
||
1575 | } |
||
1576 | |||
1577 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1578 | |||
1579 | /* Can be called by any thread, with the connection lock held */ |
||
1580 | static gboolean |
||
1581 | g_dbus_connection_send_message_unlocked (GDBusConnection *connection, |
||
1582 | GDBusMessage *message, |
||
1583 | GDBusSendMessageFlags flags, |
||
1584 | volatile guint32 *out_serial, |
||
1585 | GError **error) |
||
1586 | { |
||
1587 | guchar *blob; |
||
1588 | gsize blob_size; |
||
1589 | guint32 serial_to_use; |
||
1590 | gboolean ret; |
||
1591 | |||
1592 | CONNECTION_ENSURE_LOCK (connection); |
||
1593 | |||
1594 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
1595 | g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE); |
||
1596 | |||
1597 | /* TODO: check all necessary headers are present */ |
||
1598 | |||
1599 | ret = FALSE; |
||
1600 | blob = NULL; |
||
1601 | |||
1602 | if (out_serial != NULL) |
||
1603 | *out_serial = 0; |
||
1604 | |||
1605 | /* If we're in initable_init(), don't check for being initialized, to avoid |
||
1606 | * chicken-and-egg problems. initable_init() is responsible for setting up |
||
1607 | * our prerequisites (mainly connection->worker), and only calling us |
||
1608 | * from its own thread (so no memory barrier is needed). |
||
1609 | */ |
||
1610 | if (!check_unclosed (connection, |
||
1611 | (flags & SEND_MESSAGE_FLAGS_INITIALIZING) ? MAY_BE_UNINITIALIZED : 0, |
||
1612 | error)) |
||
1613 | goto out; |
||
1614 | |||
1615 | blob = g_dbus_message_to_blob (message, |
||
1616 | &blob_size, |
||
1617 | connection->capabilities, |
||
1618 | error); |
||
1619 | if (blob == NULL) |
||
1620 | goto out; |
||
1621 | |||
1622 | if (flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) |
||
1623 | serial_to_use = g_dbus_message_get_serial (message); |
||
1624 | else |
||
1625 | serial_to_use = ++connection->last_serial; /* TODO: handle overflow */ |
||
1626 | |||
1627 | switch (blob[0]) |
||
1628 | { |
||
1629 | case 'l': |
||
1630 | ((guint32 *) blob)[2] = GUINT32_TO_LE (serial_to_use); |
||
1631 | break; |
||
1632 | case 'B': |
||
1633 | ((guint32 *) blob)[2] = GUINT32_TO_BE (serial_to_use); |
||
1634 | break; |
||
1635 | default: |
||
1636 | g_assert_not_reached (); |
||
1637 | break; |
||
1638 | } |
||
1639 | |||
1640 | #if 0 |
||
1641 | g_printerr ("Writing message of %" G_GSIZE_FORMAT " bytes (serial %d) on %p:\n", |
||
1642 | blob_size, serial_to_use, connection); |
||
1643 | g_printerr ("----\n"); |
||
1644 | hexdump (blob, blob_size); |
||
1645 | g_printerr ("----\n"); |
||
1646 | #endif |
||
1647 | |||
1648 | /* TODO: use connection->auth to encode the blob */ |
||
1649 | |||
1650 | if (out_serial != NULL) |
||
1651 | *out_serial = serial_to_use; |
||
1652 | |||
1653 | /* store used serial for the current thread */ |
||
1654 | /* TODO: watch the thread disposal and remove associated record |
||
1655 | * from hashtable |
||
1656 | * - see https://bugzilla.gnome.org/show_bug.cgi?id=676825#c7 |
||
1657 | */ |
||
1658 | g_hash_table_replace (connection->map_thread_to_last_serial, |
||
1659 | g_thread_self (), |
||
1660 | GUINT_TO_POINTER (serial_to_use)); |
||
1661 | |||
1662 | if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL)) |
||
1663 | g_dbus_message_set_serial (message, serial_to_use); |
||
1664 | |||
1665 | g_dbus_message_lock (message); |
||
1666 | _g_dbus_worker_send_message (connection->worker, |
||
1667 | message, |
||
1668 | (gchar*) blob, |
||
1669 | blob_size); |
||
1670 | blob = NULL; /* since _g_dbus_worker_send_message() steals the blob */ |
||
1671 | |||
1672 | ret = TRUE; |
||
1673 | |||
1674 | out: |
||
1675 | g_free (blob); |
||
1676 | |||
1677 | return ret; |
||
1678 | } |
||
1679 | |||
1680 | /** |
||
1681 | * g_dbus_connection_send_message: |
||
1682 | * @connection: a #GDBusConnection |
||
1683 | * @message: a #GDBusMessage |
||
1684 | * @flags: flags affecting how the message is sent |
||
1685 | * @out_serial: (out) (allow-none): return location for serial number assigned |
||
1686 | * to @message when sending it or %NULL |
||
1687 | * @error: Return location for error or %NULL |
||
1688 | * |
||
1689 | * Asynchronously sends @message to the peer represented by @connection. |
||
1690 | * |
||
1691 | * Unless @flags contain the |
||
1692 | * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number |
||
1693 | * will be assigned by @connection and set on @message via |
||
1694 | * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the |
||
1695 | * serial number used will be written to this location prior to |
||
1696 | * submitting the message to the underlying transport. |
||
1697 | * |
||
1698 | * If @connection is closed then the operation will fail with |
||
1699 | * %G_IO_ERROR_CLOSED. If @message is not well-formed, |
||
1700 | * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. |
||
1701 | * |
||
1702 | * See this [server][gdbus-server] and [client][gdbus-unix-fd-client] |
||
1703 | * for an example of how to use this low-level API to send and receive |
||
1704 | * UNIX file descriptors. |
||
1705 | * |
||
1706 | * Note that @message must be unlocked, unless @flags contain the |
||
1707 | * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag. |
||
1708 | * |
||
1709 | * Returns: %TRUE if the message was well-formed and queued for |
||
1710 | * transmission, %FALSE if @error is set |
||
1711 | * |
||
1712 | * Since: 2.26 |
||
1713 | */ |
||
1714 | gboolean |
||
1715 | g_dbus_connection_send_message (GDBusConnection *connection, |
||
1716 | GDBusMessage *message, |
||
1717 | GDBusSendMessageFlags flags, |
||
1718 | volatile guint32 *out_serial, |
||
1719 | GError **error) |
||
1720 | { |
||
1721 | gboolean ret; |
||
1722 | |||
1723 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
1724 | g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE); |
||
1725 | g_return_val_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message), FALSE); |
||
1726 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
||
1727 | |||
1728 | CONNECTION_LOCK (connection); |
||
1729 | ret = g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, error); |
||
1730 | CONNECTION_UNLOCK (connection); |
||
1731 | return ret; |
||
1732 | } |
||
1733 | |||
1734 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1735 | |||
1736 | typedef struct |
||
1737 | { |
||
1738 | guint32 serial; |
||
1739 | |||
1740 | gulong cancellable_handler_id; |
||
1741 | |||
1742 | GSource *timeout_source; |
||
1743 | |||
1744 | gboolean delivered; |
||
1745 | } SendMessageData; |
||
1746 | |||
1747 | /* Can be called from any thread with or without lock held */ |
||
1748 | static void |
||
1749 | send_message_data_free (SendMessageData *data) |
||
1750 | { |
||
1751 | g_assert (data->timeout_source == NULL); |
||
1752 | g_assert (data->cancellable_handler_id == 0); |
||
1753 | |||
1754 | g_slice_free (SendMessageData, data); |
||
1755 | } |
||
1756 | |||
1757 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1758 | |||
1759 | /* can be called from any thread with lock held */ |
||
1760 | static void |
||
1761 | send_message_with_reply_cleanup (GTask *task, gboolean remove) |
||
1762 | { |
||
1763 | GDBusConnection *connection = g_task_get_source_object (task); |
||
1764 | SendMessageData *data = g_task_get_task_data (task); |
||
1765 | |||
1766 | CONNECTION_ENSURE_LOCK (connection); |
||
1767 | |||
1768 | g_assert (!data->delivered); |
||
1769 | |||
1770 | data->delivered = TRUE; |
||
1771 | |||
1772 | if (data->timeout_source != NULL) |
||
1773 | { |
||
1774 | g_source_destroy (data->timeout_source); |
||
1775 | data->timeout_source = NULL; |
||
1776 | } |
||
1777 | if (data->cancellable_handler_id > 0) |
||
1778 | { |
||
1779 | g_cancellable_disconnect (g_task_get_cancellable (task), data->cancellable_handler_id); |
||
1780 | data->cancellable_handler_id = 0; |
||
1781 | } |
||
1782 | |||
1783 | if (remove) |
||
1784 | { |
||
1785 | gboolean removed = g_hash_table_remove (connection->map_method_serial_to_task, |
||
1786 | GUINT_TO_POINTER (data->serial)); |
||
1787 | g_warn_if_fail (removed); |
||
1788 | } |
||
1789 | |||
1790 | g_object_unref (task); |
||
1791 | } |
||
1792 | |||
1793 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1794 | |||
1795 | /* Called from GDBus worker thread with lock held */ |
||
1796 | static void |
||
1797 | send_message_data_deliver_reply_unlocked (GTask *task, |
||
1798 | GDBusMessage *reply) |
||
1799 | { |
||
1800 | SendMessageData *data = g_task_get_task_data (task); |
||
1801 | |||
1802 | if (data->delivered) |
||
1803 | goto out; |
||
1804 | |||
1805 | g_task_return_pointer (task, g_object_ref (reply), g_object_unref); |
||
1806 | |||
1807 | send_message_with_reply_cleanup (task, TRUE); |
||
1808 | |||
1809 | out: |
||
1810 | ; |
||
1811 | } |
||
1812 | |||
1813 | /* Called from a user thread, lock is not held */ |
||
1814 | static void |
||
1815 | send_message_data_deliver_error (GTask *task, |
||
1816 | GQuark domain, |
||
1817 | gint code, |
||
1818 | const char *message) |
||
1819 | { |
||
1820 | GDBusConnection *connection = g_task_get_source_object (task); |
||
1821 | SendMessageData *data = g_task_get_task_data (task); |
||
1822 | |||
1823 | CONNECTION_LOCK (connection); |
||
1824 | if (data->delivered) |
||
1825 | { |
||
1826 | CONNECTION_UNLOCK (connection); |
||
1827 | return; |
||
1828 | } |
||
1829 | |||
1830 | g_object_ref (task); |
||
1831 | send_message_with_reply_cleanup (task, TRUE); |
||
1832 | CONNECTION_UNLOCK (connection); |
||
1833 | |||
1834 | g_task_return_new_error (task, domain, code, "%s", message); |
||
1835 | g_object_unref (task); |
||
1836 | } |
||
1837 | |||
1838 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1839 | |||
1840 | /* Called from a user thread, lock is not held */ |
||
1841 | static gboolean |
||
1842 | send_message_with_reply_cancelled_idle_cb (gpointer user_data) |
||
1843 | { |
||
1844 | GTask *task = user_data; |
||
1845 | |||
1846 | send_message_data_deliver_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, |
||
1847 | _("Operation was cancelled")); |
||
1848 | return FALSE; |
||
1849 | } |
||
1850 | |||
1851 | /* Can be called from any thread with or without lock held */ |
||
1852 | static void |
||
1853 | send_message_with_reply_cancelled_cb (GCancellable *cancellable, |
||
1854 | gpointer user_data) |
||
1855 | { |
||
1856 | GTask *task = user_data; |
||
1857 | GSource *idle_source; |
||
1858 | |||
1859 | /* postpone cancellation to idle handler since we may be called directly |
||
1860 | * via g_cancellable_connect() (e.g. holding lock) |
||
1861 | */ |
||
1862 | idle_source = g_idle_source_new (); |
||
1863 | g_source_set_name (idle_source, "[gio] send_message_with_reply_cancelled_idle_cb"); |
||
1864 | g_task_attach_source (task, idle_source, send_message_with_reply_cancelled_idle_cb); |
||
1865 | g_source_unref (idle_source); |
||
1866 | } |
||
1867 | |||
1868 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1869 | |||
1870 | /* Called from a user thread, lock is not held */ |
||
1871 | static gboolean |
||
1872 | send_message_with_reply_timeout_cb (gpointer user_data) |
||
1873 | { |
||
1874 | GTask *task = user_data; |
||
1875 | |||
1876 | send_message_data_deliver_error (task, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, |
||
1877 | _("Timeout was reached")); |
||
1878 | return FALSE; |
||
1879 | } |
||
1880 | |||
1881 | /* ---------------------------------------------------------------------------------------------------- */ |
||
1882 | |||
1883 | /* Called from a user thread, connection's lock is held */ |
||
1884 | static void |
||
1885 | g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connection, |
||
1886 | GDBusMessage *message, |
||
1887 | GDBusSendMessageFlags flags, |
||
1888 | gint timeout_msec, |
||
1889 | volatile guint32 *out_serial, |
||
1890 | GCancellable *cancellable, |
||
1891 | GAsyncReadyCallback callback, |
||
1892 | gpointer user_data) |
||
1893 | { |
||
1894 | GTask *task; |
||
1895 | SendMessageData *data; |
||
1896 | GError *error = NULL; |
||
1897 | volatile guint32 serial; |
||
1898 | |||
1899 | if (out_serial == NULL) |
||
1900 | out_serial = &serial; |
||
1901 | |||
1902 | if (timeout_msec == -1) |
||
1903 | timeout_msec = 25 * 1000; |
||
1904 | |||
1905 | data = g_slice_new0 (SendMessageData); |
||
1906 | task = g_task_new (connection, cancellable, callback, user_data); |
||
1907 | g_task_set_task_data (task, data, (GDestroyNotify) send_message_data_free); |
||
1908 | |||
1909 | if (g_task_return_error_if_cancelled (task)) |
||
1910 | { |
||
1911 | g_object_unref (task); |
||
1912 | return; |
||
1913 | } |
||
1914 | |||
1915 | if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error)) |
||
1916 | { |
||
1917 | g_task_return_error (task, error); |
||
1918 | g_object_unref (task); |
||
1919 | return; |
||
1920 | } |
||
1921 | data->serial = *out_serial; |
||
1922 | |||
1923 | if (cancellable != NULL) |
||
1924 | { |
||
1925 | data->cancellable_handler_id = g_cancellable_connect (cancellable, |
||
1926 | G_CALLBACK (send_message_with_reply_cancelled_cb), |
||
1927 | g_object_ref (task), |
||
1928 | g_object_unref); |
||
1929 | } |
||
1930 | |||
1931 | if (timeout_msec != G_MAXINT) |
||
1932 | { |
||
1933 | data->timeout_source = g_timeout_source_new (timeout_msec); |
||
1934 | g_task_attach_source (task, data->timeout_source, |
||
1935 | (GSourceFunc) send_message_with_reply_timeout_cb); |
||
1936 | g_source_unref (data->timeout_source); |
||
1937 | } |
||
1938 | |||
1939 | g_hash_table_insert (connection->map_method_serial_to_task, |
||
1940 | GUINT_TO_POINTER (*out_serial), |
||
1941 | task); |
||
1942 | } |
||
1943 | |||
1944 | /** |
||
1945 | * g_dbus_connection_send_message_with_reply: |
||
1946 | * @connection: a #GDBusConnection |
||
1947 | * @message: a #GDBusMessage |
||
1948 | * @flags: flags affecting how the message is sent |
||
1949 | * @timeout_msec: the timeout in milliseconds, -1 to use the default |
||
1950 | * timeout or %G_MAXINT for no timeout |
||
1951 | * @out_serial: (out) (allow-none): return location for serial number assigned |
||
1952 | * to @message when sending it or %NULL |
||
1953 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
1954 | * @callback: (allow-none): a #GAsyncReadyCallback to call when the request |
||
1955 | * is satisfied or %NULL if you don't care about the result |
||
1956 | * @user_data: The data to pass to @callback |
||
1957 | * |
||
1958 | * Asynchronously sends @message to the peer represented by @connection. |
||
1959 | * |
||
1960 | * Unless @flags contain the |
||
1961 | * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number |
||
1962 | * will be assigned by @connection and set on @message via |
||
1963 | * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the |
||
1964 | * serial number used will be written to this location prior to |
||
1965 | * submitting the message to the underlying transport. |
||
1966 | * |
||
1967 | * If @connection is closed then the operation will fail with |
||
1968 | * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the operation will |
||
1969 | * fail with %G_IO_ERROR_CANCELLED. If @message is not well-formed, |
||
1970 | * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. |
||
1971 | * |
||
1972 | * This is an asynchronous method. When the operation is finished, @callback |
||
1973 | * will be invoked in the |
||
1974 | * [thread-default main context][g-main-context-push-thread-default] |
||
1975 | * of the thread you are calling this method from. You can then call |
||
1976 | * g_dbus_connection_send_message_with_reply_finish() to get the result of the operation. |
||
1977 | * See g_dbus_connection_send_message_with_reply_sync() for the synchronous version. |
||
1978 | * |
||
1979 | * Note that @message must be unlocked, unless @flags contain the |
||
1980 | * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag. |
||
1981 | * |
||
1982 | * See this [server][gdbus-server] and [client][gdbus-unix-fd-client] |
||
1983 | * for an example of how to use this low-level API to send and receive |
||
1984 | * UNIX file descriptors. |
||
1985 | * |
||
1986 | * Since: 2.26 |
||
1987 | */ |
||
1988 | void |
||
1989 | g_dbus_connection_send_message_with_reply (GDBusConnection *connection, |
||
1990 | GDBusMessage *message, |
||
1991 | GDBusSendMessageFlags flags, |
||
1992 | gint timeout_msec, |
||
1993 | volatile guint32 *out_serial, |
||
1994 | GCancellable *cancellable, |
||
1995 | GAsyncReadyCallback callback, |
||
1996 | gpointer user_data) |
||
1997 | { |
||
1998 | g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); |
||
1999 | g_return_if_fail (G_IS_DBUS_MESSAGE (message)); |
||
2000 | g_return_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message)); |
||
2001 | g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1); |
||
2002 | |||
2003 | CONNECTION_LOCK (connection); |
||
2004 | g_dbus_connection_send_message_with_reply_unlocked (connection, |
||
2005 | message, |
||
2006 | flags, |
||
2007 | timeout_msec, |
||
2008 | out_serial, |
||
2009 | cancellable, |
||
2010 | callback, |
||
2011 | user_data); |
||
2012 | CONNECTION_UNLOCK (connection); |
||
2013 | } |
||
2014 | |||
2015 | /** |
||
2016 | * g_dbus_connection_send_message_with_reply_finish: |
||
2017 | * @connection: a #GDBusConnection |
||
2018 | * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed to |
||
2019 | * g_dbus_connection_send_message_with_reply() |
||
2020 | * @error: teturn location for error or %NULL |
||
2021 | * |
||
2022 | * Finishes an operation started with g_dbus_connection_send_message_with_reply(). |
||
2023 | * |
||
2024 | * Note that @error is only set if a local in-process error |
||
2025 | * occurred. That is to say that the returned #GDBusMessage object may |
||
2026 | * be of type %G_DBUS_MESSAGE_TYPE_ERROR. Use |
||
2027 | * g_dbus_message_to_gerror() to transcode this to a #GError. |
||
2028 | * |
||
2029 | * See this [server][gdbus-server] and [client][gdbus-unix-fd-client] |
||
2030 | * for an example of how to use this low-level API to send and receive |
||
2031 | * UNIX file descriptors. |
||
2032 | * |
||
2033 | * Returns: (transfer full): a locked #GDBusMessage or %NULL if @error is set |
||
2034 | * |
||
2035 | * Since: 2.26 |
||
2036 | */ |
||
2037 | GDBusMessage * |
||
2038 | g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection, |
||
2039 | GAsyncResult *res, |
||
2040 | GError **error) |
||
2041 | { |
||
2042 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); |
||
2043 | g_return_val_if_fail (g_task_is_valid (res, connection), NULL); |
||
2044 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
2045 | |||
2046 | return g_task_propagate_pointer (G_TASK (res), error); |
||
2047 | } |
||
2048 | |||
2049 | /* ---------------------------------------------------------------------------------------------------- */ |
||
2050 | |||
2051 | typedef struct |
||
2052 | { |
||
2053 | GAsyncResult *res; |
||
2054 | GMainContext *context; |
||
2055 | GMainLoop *loop; |
||
2056 | } SendMessageSyncData; |
||
2057 | |||
2058 | /* Called from a user thread, lock is not held */ |
||
2059 | static void |
||
2060 | send_message_with_reply_sync_cb (GDBusConnection *connection, |
||
2061 | GAsyncResult *res, |
||
2062 | gpointer user_data) |
||
2063 | { |
||
2064 | SendMessageSyncData *data = user_data; |
||
2065 | data->res = g_object_ref (res); |
||
2066 | g_main_loop_quit (data->loop); |
||
2067 | } |
||
2068 | |||
2069 | /** |
||
2070 | * g_dbus_connection_send_message_with_reply_sync: |
||
2071 | * @connection: a #GDBusConnection |
||
2072 | * @message: a #GDBusMessage |
||
2073 | * @flags: flags affecting how the message is sent. |
||
2074 | * @timeout_msec: the timeout in milliseconds, -1 to use the default |
||
2075 | * timeout or %G_MAXINT for no timeout |
||
2076 | * @out_serial: (out) (allow-none): return location for serial number |
||
2077 | * assigned to @message when sending it or %NULL |
||
2078 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
2079 | * @error: return location for error or %NULL |
||
2080 | * |
||
2081 | * Synchronously sends @message to the peer represented by @connection |
||
2082 | * and blocks the calling thread until a reply is received or the |
||
2083 | * timeout is reached. See g_dbus_connection_send_message_with_reply() |
||
2084 | * for the asynchronous version of this method. |
||
2085 | * |
||
2086 | * Unless @flags contain the |
||
2087 | * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number |
||
2088 | * will be assigned by @connection and set on @message via |
||
2089 | * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the |
||
2090 | * serial number used will be written to this location prior to |
||
2091 | * submitting the message to the underlying transport. |
||
2092 | * |
||
2093 | * If @connection is closed then the operation will fail with |
||
2094 | * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the operation will |
||
2095 | * fail with %G_IO_ERROR_CANCELLED. If @message is not well-formed, |
||
2096 | * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. |
||
2097 | * |
||
2098 | * Note that @error is only set if a local in-process error |
||
2099 | * occurred. That is to say that the returned #GDBusMessage object may |
||
2100 | * be of type %G_DBUS_MESSAGE_TYPE_ERROR. Use |
||
2101 | * g_dbus_message_to_gerror() to transcode this to a #GError. |
||
2102 | * |
||
2103 | * See this [server][gdbus-server] and [client][gdbus-unix-fd-client] |
||
2104 | * for an example of how to use this low-level API to send and receive |
||
2105 | * UNIX file descriptors. |
||
2106 | * |
||
2107 | * Note that @message must be unlocked, unless @flags contain the |
||
2108 | * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag. |
||
2109 | * |
||
2110 | * Returns: (transfer full): a locked #GDBusMessage that is the reply |
||
2111 | * to @message or %NULL if @error is set |
||
2112 | * |
||
2113 | * Since: 2.26 |
||
2114 | */ |
||
2115 | GDBusMessage * |
||
2116 | g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, |
||
2117 | GDBusMessage *message, |
||
2118 | GDBusSendMessageFlags flags, |
||
2119 | gint timeout_msec, |
||
2120 | volatile guint32 *out_serial, |
||
2121 | GCancellable *cancellable, |
||
2122 | GError **error) |
||
2123 | { |
||
2124 | SendMessageSyncData data; |
||
2125 | GDBusMessage *reply; |
||
2126 | |||
2127 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); |
||
2128 | g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); |
||
2129 | g_return_val_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message), NULL); |
||
2130 | g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); |
||
2131 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
2132 | |||
2133 | data.res = NULL; |
||
2134 | data.context = g_main_context_new (); |
||
2135 | data.loop = g_main_loop_new (data.context, FALSE); |
||
2136 | |||
2137 | g_main_context_push_thread_default (data.context); |
||
2138 | |||
2139 | g_dbus_connection_send_message_with_reply (connection, |
||
2140 | message, |
||
2141 | flags, |
||
2142 | timeout_msec, |
||
2143 | out_serial, |
||
2144 | cancellable, |
||
2145 | (GAsyncReadyCallback) send_message_with_reply_sync_cb, |
||
2146 | &data); |
||
2147 | g_main_loop_run (data.loop); |
||
2148 | reply = g_dbus_connection_send_message_with_reply_finish (connection, |
||
2149 | data.res, |
||
2150 | error); |
||
2151 | |||
2152 | g_main_context_pop_thread_default (data.context); |
||
2153 | |||
2154 | g_main_context_unref (data.context); |
||
2155 | g_main_loop_unref (data.loop); |
||
2156 | if (data.res) |
||
2157 | g_object_unref (data.res); |
||
2158 | |||
2159 | return reply; |
||
2160 | } |
||
2161 | |||
2162 | /* ---------------------------------------------------------------------------------------------------- */ |
||
2163 | |||
2164 | typedef struct |
||
2165 | { |
||
2166 | guint id; |
||
2167 | guint ref_count; |
||
2168 | GDBusMessageFilterFunction filter_function; |
||
2169 | gpointer user_data; |
||
2170 | GDestroyNotify user_data_free_func; |
||
2171 | GMainContext *context; |
||
2172 | } FilterData; |
||
2173 | |||
2174 | /* requires CONNECTION_LOCK */ |
||
2175 | static FilterData ** |
||
2176 | copy_filter_list (GPtrArray *filters) |
||
2177 | { |
||
2178 | FilterData **copy; |
||
2179 | guint n; |
||
2180 | |||
2181 | copy = g_new (FilterData *, filters->len + 1); |
||
2182 | for (n = 0; n < filters->len; n++) |
||
2183 | { |
||
2184 | copy[n] = filters->pdata[n]; |
||
2185 | copy[n]->ref_count++; |
||
2186 | } |
||
2187 | copy[n] = NULL; |
||
2188 | |||
2189 | return copy; |
||
2190 | } |
||
2191 | |||
2192 | /* requires CONNECTION_LOCK */ |
||
2193 | static void |
||
2194 | free_filter_list (FilterData **filters) |
||
2195 | { |
||
2196 | guint n; |
||
2197 | |||
2198 | for (n = 0; filters[n]; n++) |
||
2199 | { |
||
2200 | filters[n]->ref_count--; |
||
2201 | if (filters[n]->ref_count == 0) |
||
2202 | { |
||
2203 | call_destroy_notify (filters[n]->context, |
||
2204 | filters[n]->user_data_free_func, |
||
2205 | filters[n]->user_data); |
||
2206 | g_main_context_unref (filters[n]->context); |
||
2207 | g_free (filters[n]); |
||
2208 | } |
||
2209 | } |
||
2210 | g_free (filters); |
||
2211 | } |
||
2212 | |||
2213 | /* Called in GDBusWorker's thread - we must not block - with no lock held */ |
||
2214 | static void |
||
2215 | on_worker_message_received (GDBusWorker *worker, |
||
2216 | GDBusMessage *message, |
||
2217 | gpointer user_data) |
||
2218 | { |
||
2219 | GDBusConnection *connection; |
||
2220 | FilterData **filters; |
||
2221 | guint n; |
||
2222 | gboolean alive; |
||
2223 | |||
2224 | G_LOCK (message_bus_lock); |
||
2225 | alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); |
||
2226 | if (!alive) |
||
2227 | { |
||
2228 | G_UNLOCK (message_bus_lock); |
||
2229 | return; |
||
2230 | } |
||
2231 | connection = G_DBUS_CONNECTION (user_data); |
||
2232 | g_object_ref (connection); |
||
2233 | G_UNLOCK (message_bus_lock); |
||
2234 | |||
2235 | //g_debug ("in on_worker_message_received"); |
||
2236 | |||
2237 | g_object_ref (message); |
||
2238 | g_dbus_message_lock (message); |
||
2239 | |||
2240 | //g_debug ("boo ref_count = %d %p %p", G_OBJECT (connection)->ref_count, connection, connection->worker); |
||
2241 | |||
2242 | /* First collect the set of callback functions */ |
||
2243 | CONNECTION_LOCK (connection); |
||
2244 | filters = copy_filter_list (connection->filters); |
||
2245 | CONNECTION_UNLOCK (connection); |
||
2246 | |||
2247 | /* then call the filters in order (without holding the lock) */ |
||
2248 | for (n = 0; filters[n]; n++) |
||
2249 | { |
||
2250 | message = filters[n]->filter_function (connection, |
||
2251 | message, |
||
2252 | TRUE, |
||
2253 | filters[n]->user_data); |
||
2254 | if (message == NULL) |
||
2255 | break; |
||
2256 | g_dbus_message_lock (message); |
||
2257 | } |
||
2258 | |||
2259 | CONNECTION_LOCK (connection); |
||
2260 | free_filter_list (filters); |
||
2261 | CONNECTION_UNLOCK (connection); |
||
2262 | |||
2263 | /* Standard dispatch unless the filter ate the message - no need to |
||
2264 | * do anything if the message was altered |
||
2265 | */ |
||
2266 | if (message != NULL) |
||
2267 | { |
||
2268 | GDBusMessageType message_type; |
||
2269 | |||
2270 | message_type = g_dbus_message_get_message_type (message); |
||
2271 | if (message_type == G_DBUS_MESSAGE_TYPE_METHOD_RETURN || message_type == G_DBUS_MESSAGE_TYPE_ERROR) |
||
2272 | { |
||
2273 | guint32 reply_serial; |
||
2274 | GTask *task; |
||
2275 | |||
2276 | reply_serial = g_dbus_message_get_reply_serial (message); |
||
2277 | CONNECTION_LOCK (connection); |
||
2278 | task = g_hash_table_lookup (connection->map_method_serial_to_task, |
||
2279 | GUINT_TO_POINTER (reply_serial)); |
||
2280 | if (task != NULL) |
||
2281 | { |
||
2282 | //g_debug ("delivering reply/error for serial %d for %p", reply_serial, connection); |
||
2283 | send_message_data_deliver_reply_unlocked (task, message); |
||
2284 | } |
||
2285 | else |
||
2286 | { |
||
2287 | //g_debug ("message reply/error for serial %d but no SendMessageData found for %p", reply_serial, connection); |
||
2288 | } |
||
2289 | CONNECTION_UNLOCK (connection); |
||
2290 | } |
||
2291 | else if (message_type == G_DBUS_MESSAGE_TYPE_SIGNAL) |
||
2292 | { |
||
2293 | CONNECTION_LOCK (connection); |
||
2294 | distribute_signals (connection, message); |
||
2295 | CONNECTION_UNLOCK (connection); |
||
2296 | } |
||
2297 | else if (message_type == G_DBUS_MESSAGE_TYPE_METHOD_CALL) |
||
2298 | { |
||
2299 | CONNECTION_LOCK (connection); |
||
2300 | distribute_method_call (connection, message); |
||
2301 | CONNECTION_UNLOCK (connection); |
||
2302 | } |
||
2303 | } |
||
2304 | |||
2305 | if (message != NULL) |
||
2306 | g_object_unref (message); |
||
2307 | g_object_unref (connection); |
||
2308 | } |
||
2309 | |||
2310 | /* Called in GDBusWorker's thread, lock is not held */ |
||
2311 | static GDBusMessage * |
||
2312 | on_worker_message_about_to_be_sent (GDBusWorker *worker, |
||
2313 | GDBusMessage *message, |
||
2314 | gpointer user_data) |
||
2315 | { |
||
2316 | GDBusConnection *connection; |
||
2317 | FilterData **filters; |
||
2318 | guint n; |
||
2319 | gboolean alive; |
||
2320 | |||
2321 | G_LOCK (message_bus_lock); |
||
2322 | alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); |
||
2323 | if (!alive) |
||
2324 | { |
||
2325 | G_UNLOCK (message_bus_lock); |
||
2326 | return message; |
||
2327 | } |
||
2328 | connection = G_DBUS_CONNECTION (user_data); |
||
2329 | g_object_ref (connection); |
||
2330 | G_UNLOCK (message_bus_lock); |
||
2331 | |||
2332 | //g_debug ("in on_worker_message_about_to_be_sent"); |
||
2333 | |||
2334 | /* First collect the set of callback functions */ |
||
2335 | CONNECTION_LOCK (connection); |
||
2336 | filters = copy_filter_list (connection->filters); |
||
2337 | CONNECTION_UNLOCK (connection); |
||
2338 | |||
2339 | /* then call the filters in order (without holding the lock) */ |
||
2340 | for (n = 0; filters[n]; n++) |
||
2341 | { |
||
2342 | g_dbus_message_lock (message); |
||
2343 | message = filters[n]->filter_function (connection, |
||
2344 | message, |
||
2345 | FALSE, |
||
2346 | filters[n]->user_data); |
||
2347 | if (message == NULL) |
||
2348 | break; |
||
2349 | } |
||
2350 | |||
2351 | CONNECTION_LOCK (connection); |
||
2352 | free_filter_list (filters); |
||
2353 | CONNECTION_UNLOCK (connection); |
||
2354 | |||
2355 | g_object_unref (connection); |
||
2356 | |||
2357 | return message; |
||
2358 | } |
||
2359 | |||
2360 | /* called with connection lock held, in GDBusWorker thread */ |
||
2361 | static gboolean |
||
2362 | cancel_method_on_close (gpointer key, gpointer value, gpointer user_data) |
||
2363 | { |
||
2364 | GTask *task = value; |
||
2365 | SendMessageData *data = g_task_get_task_data (task); |
||
2366 | |||
2367 | if (data->delivered) |
||
2368 | return FALSE; |
||
2369 | |||
2370 | g_task_return_new_error (task, |
||
2371 | G_IO_ERROR, |
||
2372 | G_IO_ERROR_CLOSED, |
||
2373 | _("The connection is closed")); |
||
2374 | |||
2375 | /* Ask send_message_with_reply_cleanup not to remove the element from the |
||
2376 | * hash table - we're in the middle of a foreach; that would be unsafe. |
||
2377 | * Instead, return TRUE from this function so that it gets removed safely. |
||
2378 | */ |
||
2379 | send_message_with_reply_cleanup (task, FALSE); |
||
2380 | return TRUE; |
||
2381 | } |
||
2382 | |||
2383 | /* Called in GDBusWorker's thread - we must not block - without lock held */ |
||
2384 | static void |
||
2385 | on_worker_closed (GDBusWorker *worker, |
||
2386 | gboolean remote_peer_vanished, |
||
2387 | GError *error, |
||
2388 | gpointer user_data) |
||
2389 | { |
||
2390 | GDBusConnection *connection; |
||
2391 | gboolean alive; |
||
2392 | guint old_atomic_flags; |
||
2393 | |||
2394 | G_LOCK (message_bus_lock); |
||
2395 | alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); |
||
2396 | if (!alive) |
||
2397 | { |
||
2398 | G_UNLOCK (message_bus_lock); |
||
2399 | return; |
||
2400 | } |
||
2401 | connection = G_DBUS_CONNECTION (user_data); |
||
2402 | g_object_ref (connection); |
||
2403 | G_UNLOCK (message_bus_lock); |
||
2404 | |||
2405 | //g_debug ("in on_worker_closed: %s", error->message); |
||
2406 | |||
2407 | CONNECTION_LOCK (connection); |
||
2408 | /* Even though this is atomic, we do it inside the lock to avoid breaking |
||
2409 | * assumptions in remove_match_rule(). We'd need the lock in a moment |
||
2410 | * anyway, so, no loss. |
||
2411 | */ |
||
2412 | old_atomic_flags = g_atomic_int_or (&connection->atomic_flags, FLAG_CLOSED); |
||
2413 | |||
2414 | if (!(old_atomic_flags & FLAG_CLOSED)) |
||
2415 | { |
||
2416 | g_hash_table_foreach_remove (connection->map_method_serial_to_task, cancel_method_on_close, NULL); |
||
2417 | schedule_closed_unlocked (connection, remote_peer_vanished, error); |
||
2418 | } |
||
2419 | CONNECTION_UNLOCK (connection); |
||
2420 | |||
2421 | g_object_unref (connection); |
||
2422 | } |
||
2423 | |||
2424 | /* ---------------------------------------------------------------------------------------------------- */ |
||
2425 | |||
2426 | /* Determines the biggest set of capabilities we can support on this |
||
2427 | * connection. |
||
2428 | * |
||
2429 | * Called with the init_lock held. |
||
2430 | */ |
||
2431 | static GDBusCapabilityFlags |
||
2432 | get_offered_capabilities_max (GDBusConnection *connection) |
||
2433 | { |
||
2434 | GDBusCapabilityFlags ret; |
||
2435 | ret = G_DBUS_CAPABILITY_FLAGS_NONE; |
||
2436 | #ifdef G_OS_UNIX |
||
2437 | if (G_IS_UNIX_CONNECTION (connection->stream)) |
||
2438 | ret |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING; |
||
2439 | #endif |
||
2440 | return ret; |
||
2441 | } |
||
2442 | |||
2443 | /* Called in a user thread, lock is not held */ |
||
2444 | static gboolean |
||
2445 | initable_init (GInitable *initable, |
||
2446 | GCancellable *cancellable, |
||
2447 | GError **error) |
||
2448 | { |
||
2449 | GDBusConnection *connection = G_DBUS_CONNECTION (initable); |
||
2450 | gboolean ret; |
||
2451 | |||
2452 | /* This method needs to be idempotent to work with the singleton |
||
2453 | * pattern. See the docs for g_initable_init(). We implement this by |
||
2454 | * locking. |
||
2455 | * |
||
2456 | * Unfortunately we can't use the main lock since the on_worker_*() |
||
2457 | * callbacks above needs the lock during initialization (for message |
||
2458 | * bus connections we do a synchronous Hello() call on the bus). |
||
2459 | */ |
||
2460 | g_mutex_lock (&connection->init_lock); |
||
2461 | |||
2462 | ret = FALSE; |
||
2463 | |||
2464 | /* Make this a no-op if we're already initialized (successfully or |
||
2465 | * unsuccessfully) |
||
2466 | */ |
||
2467 | if ((g_atomic_int_get (&connection->atomic_flags) & FLAG_INITIALIZED)) |
||
2468 | { |
||
2469 | ret = (connection->initialization_error == NULL); |
||
2470 | goto out; |
||
2471 | } |
||
2472 | |||
2473 | /* Because of init_lock, we can't get here twice in different threads */ |
||
2474 | g_assert (connection->initialization_error == NULL); |
||
2475 | |||
2476 | /* The user can pass multiple (but mutally exclusive) construct |
||
2477 | * properties: |
||
2478 | * |
||
2479 | * - stream (of type GIOStream) |
||
2480 | * - address (of type gchar*) |
||
2481 | * |
||
2482 | * At the end of the day we end up with a non-NULL GIOStream |
||
2483 | * object in connection->stream. |
||
2484 | */ |
||
2485 | if (connection->address != NULL) |
||
2486 | { |
||
2487 | g_assert (connection->stream == NULL); |
||
2488 | |||
2489 | if ((connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) || |
||
2490 | (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS)) |
||
2491 | { |
||
2492 | g_set_error_literal (&connection->initialization_error, |
||
2493 | G_IO_ERROR, |
||
2494 | G_IO_ERROR_INVALID_ARGUMENT, |
||
2495 | _("Unsupported flags encountered when constructing a client-side connection")); |
||
2496 | goto out; |
||
2497 | } |
||
2498 | |||
2499 | connection->stream = g_dbus_address_get_stream_sync (connection->address, |
||
2500 | NULL, /* TODO: out_guid */ |
||
2501 | cancellable, |
||
2502 | &connection->initialization_error); |
||
2503 | if (connection->stream == NULL) |
||
2504 | goto out; |
||
2505 | } |
||
2506 | else if (connection->stream != NULL) |
||
2507 | { |
||
2508 | /* nothing to do */ |
||
2509 | } |
||
2510 | else |
||
2511 | { |
||
2512 | g_assert_not_reached (); |
||
2513 | } |
||
2514 | |||
2515 | /* Authenticate the connection */ |
||
2516 | if (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) |
||
2517 | { |
||
2518 | g_assert (!(connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT)); |
||
2519 | g_assert (connection->guid != NULL); |
||
2520 | connection->auth = _g_dbus_auth_new (connection->stream); |
||
2521 | if (!_g_dbus_auth_run_server (connection->auth, |
||
2522 | connection->authentication_observer, |
||
2523 | connection->guid, |
||
2524 | (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS), |
||
2525 | get_offered_capabilities_max (connection), |
||
2526 | &connection->capabilities, |
||
2527 | &connection->credentials, |
||
2528 | cancellable, |
||
2529 | &connection->initialization_error)) |
||
2530 | goto out; |
||
2531 | } |
||
2532 | else if (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT) |
||
2533 | { |
||
2534 | g_assert (!(connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER)); |
||
2535 | g_assert (connection->guid == NULL); |
||
2536 | connection->auth = _g_dbus_auth_new (connection->stream); |
||
2537 | connection->guid = _g_dbus_auth_run_client (connection->auth, |
||
2538 | connection->authentication_observer, |
||
2539 | get_offered_capabilities_max (connection), |
||
2540 | &connection->capabilities, |
||
2541 | cancellable, |
||
2542 | &connection->initialization_error); |
||
2543 | if (connection->guid == NULL) |
||
2544 | goto out; |
||
2545 | } |
||
2546 | |||
2547 | if (connection->authentication_observer != NULL) |
||
2548 | { |
||
2549 | g_object_unref (connection->authentication_observer); |
||
2550 | connection->authentication_observer = NULL; |
||
2551 | } |
||
2552 | |||
2553 | //g_output_stream_flush (G_SOCKET_CONNECTION (connection->stream) |
||
2554 | |||
2555 | //g_debug ("haz unix fd passing powers: %d", connection->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); |
||
2556 | |||
2557 | #ifdef G_OS_UNIX |
||
2558 | /* We want all IO operations to be non-blocking since they happen in |
||
2559 | * the worker thread which is shared by _all_ connections. |
||
2560 | */ |
||
2561 | if (G_IS_SOCKET_CONNECTION (connection->stream)) |
||
2562 | { |
||
2563 | g_socket_set_blocking (g_socket_connection_get_socket (G_SOCKET_CONNECTION (connection->stream)), FALSE); |
||
2564 | } |
||
2565 | #endif |
||
2566 | |||
2567 | G_LOCK (message_bus_lock); |
||
2568 | if (alive_connections == NULL) |
||
2569 | alive_connections = g_hash_table_new (g_direct_hash, g_direct_equal); |
||
2570 | g_hash_table_insert (alive_connections, connection, connection); |
||
2571 | G_UNLOCK (message_bus_lock); |
||
2572 | |||
2573 | connection->worker = _g_dbus_worker_new (connection->stream, |
||
2574 | connection->capabilities, |
||
2575 | ((connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING) != 0), |
||
2576 | on_worker_message_received, |
||
2577 | on_worker_message_about_to_be_sent, |
||
2578 | on_worker_closed, |
||
2579 | connection); |
||
2580 | |||
2581 | /* if a bus connection, call org.freedesktop.DBus.Hello - this is how we're getting a name */ |
||
2582 | if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) |
||
2583 | { |
||
2584 | GVariant *hello_result; |
||
2585 | |||
2586 | /* we could lift this restriction by adding code in gdbusprivate.c */ |
||
2587 | if (connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING) |
||
2588 | { |
||
2589 | g_set_error_literal (&connection->initialization_error, |
||
2590 | G_IO_ERROR, |
||
2591 | G_IO_ERROR_FAILED, |
||
2592 | "Cannot use DELAY_MESSAGE_PROCESSING with MESSAGE_BUS_CONNECTION"); |
||
2593 | goto out; |
||
2594 | } |
||
2595 | |||
2596 | hello_result = g_dbus_connection_call_sync (connection, |
||
2597 | "org.freedesktop.DBus", /* name */ |
||
2598 | "/org/freedesktop/DBus", /* path */ |
||
2599 | "org.freedesktop.DBus", /* interface */ |
||
2600 | "Hello", |
||
2601 | NULL, /* parameters */ |
||
2602 | G_VARIANT_TYPE ("(s)"), |
||
2603 | CALL_FLAGS_INITIALIZING, |
||
2604 | -1, |
||
2605 | NULL, /* TODO: cancellable */ |
||
2606 | &connection->initialization_error); |
||
2607 | if (hello_result == NULL) |
||
2608 | goto out; |
||
2609 | |||
2610 | g_variant_get (hello_result, "(s)", &connection->bus_unique_name); |
||
2611 | g_variant_unref (hello_result); |
||
2612 | //g_debug ("unique name is '%s'", connection->bus_unique_name); |
||
2613 | } |
||
2614 | |||
2615 | ret = TRUE; |
||
2616 | out: |
||
2617 | if (!ret) |
||
2618 | { |
||
2619 | g_assert (connection->initialization_error != NULL); |
||
2620 | g_propagate_error (error, g_error_copy (connection->initialization_error)); |
||
2621 | } |
||
2622 | |||
2623 | g_atomic_int_or (&connection->atomic_flags, FLAG_INITIALIZED); |
||
2624 | g_mutex_unlock (&connection->init_lock); |
||
2625 | |||
2626 | return ret; |
||
2627 | } |
||
2628 | |||
2629 | static void |
||
2630 | initable_iface_init (GInitableIface *initable_iface) |
||
2631 | { |
||
2632 | initable_iface->init = initable_init; |
||
2633 | } |
||
2634 | |||
2635 | /* ---------------------------------------------------------------------------------------------------- */ |
||
2636 | |||
2637 | static void |
||
2638 | async_initable_iface_init (GAsyncInitableIface *async_initable_iface) |
||
2639 | { |
||
2640 | /* Use default */ |
||
2641 | } |
||
2642 | |||
2643 | /* ---------------------------------------------------------------------------------------------------- */ |
||
2644 | |||
2645 | /** |
||
2646 | * g_dbus_connection_new: |
||
2647 | * @stream: a #GIOStream |
||
2648 | * @guid: (allow-none): the GUID to use if a authenticating as a server or %NULL |
||
2649 | * @flags: flags describing how to make the connection |
||
2650 | * @observer: (allow-none): a #GDBusAuthObserver or %NULL |
||
2651 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
2652 | * @callback: a #GAsyncReadyCallback to call when the request is satisfied |
||
2653 | * @user_data: the data to pass to @callback |
||
2654 | * |
||
2655 | * Asynchronously sets up a D-Bus connection for exchanging D-Bus messages |
||
2656 | * with the end represented by @stream. |
||
2657 | * |
||
2658 | * If @stream is a #GSocketConnection, then the corresponding #GSocket |
||
2659 | * will be put into non-blocking mode. |
||
2660 | * |
||
2661 | * The D-Bus connection will interact with @stream from a worker thread. |
||
2662 | * As a result, the caller should not interact with @stream after this |
||
2663 | * method has been called, except by calling g_object_unref() on it. |
||
2664 | * |
||
2665 | * If @observer is not %NULL it may be used to control the |
||
2666 | * authentication process. |
||
2667 | * |
||
2668 | * When the operation is finished, @callback will be invoked. You can |
||
2669 | * then call g_dbus_connection_new_finish() to get the result of the |
||
2670 | * operation. |
||
2671 | * |
||
2672 | * This is a asynchronous failable constructor. See |
||
2673 | * g_dbus_connection_new_sync() for the synchronous |
||
2674 | * version. |
||
2675 | * |
||
2676 | * Since: 2.26 |
||
2677 | */ |
||
2678 | void |
||
2679 | g_dbus_connection_new (GIOStream *stream, |
||
2680 | const gchar *guid, |
||
2681 | GDBusConnectionFlags flags, |
||
2682 | GDBusAuthObserver *observer, |
||
2683 | GCancellable *cancellable, |
||
2684 | GAsyncReadyCallback callback, |
||
2685 | gpointer user_data) |
||
2686 | { |
||
2687 | g_return_if_fail (G_IS_IO_STREAM (stream)); |
||
2688 | g_async_initable_new_async (G_TYPE_DBUS_CONNECTION, |
||
2689 | G_PRIORITY_DEFAULT, |
||
2690 | cancellable, |
||
2691 | callback, |
||
2692 | user_data, |
||
2693 | "stream", stream, |
||
2694 | "guid", guid, |
||
2695 | "flags", flags, |
||
2696 | "authentication-observer", observer, |
||
2697 | NULL); |
||
2698 | } |
||
2699 | |||
2700 | /** |
||
2701 | * g_dbus_connection_new_finish: |
||
2702 | * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback |
||
2703 | * passed to g_dbus_connection_new(). |
||
2704 | * @error: return location for error or %NULL |
||
2705 | * |
||
2706 | * Finishes an operation started with g_dbus_connection_new(). |
||
2707 | * |
||
2708 | * Returns: a #GDBusConnection or %NULL if @error is set. Free |
||
2709 | * with g_object_unref(). |
||
2710 | * |
||
2711 | * Since: 2.26 |
||
2712 | */ |
||
2713 | GDBusConnection * |
||
2714 | g_dbus_connection_new_finish (GAsyncResult *res, |
||
2715 | GError **error) |
||
2716 | { |
||
2717 | GObject *object; |
||
2718 | GObject *source_object; |
||
2719 | |||
2720 | g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); |
||
2721 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
2722 | |||
2723 | source_object = g_async_result_get_source_object (res); |
||
2724 | g_assert (source_object != NULL); |
||
2725 | object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), |
||
2726 | res, |
||
2727 | error); |
||
2728 | g_object_unref (source_object); |
||
2729 | if (object != NULL) |
||
2730 | return G_DBUS_CONNECTION (object); |
||
2731 | else |
||
2732 | return NULL; |
||
2733 | } |
||
2734 | |||
2735 | /** |
||
2736 | * g_dbus_connection_new_sync: |
||
2737 | * @stream: a #GIOStream |
||
2738 | * @guid: (allow-none): the GUID to use if a authenticating as a server or %NULL |
||
2739 | * @flags: flags describing how to make the connection |
||
2740 | * @observer: (allow-none): a #GDBusAuthObserver or %NULL |
||
2741 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
2742 | * @error: return location for error or %NULL |
||
2743 | * |
||
2744 | * Synchronously sets up a D-Bus connection for exchanging D-Bus messages |
||
2745 | * with the end represented by @stream. |
||
2746 | * |
||
2747 | * If @stream is a #GSocketConnection, then the corresponding #GSocket |
||
2748 | * will be put into non-blocking mode. |
||
2749 | * |
||
2750 | * The D-Bus connection will interact with @stream from a worker thread. |
||
2751 | * As a result, the caller should not interact with @stream after this |
||
2752 | * method has been called, except by calling g_object_unref() on it. |
||
2753 | * |
||
2754 | * If @observer is not %NULL it may be used to control the |
||
2755 | * authentication process. |
||
2756 | * |
||
2757 | * This is a synchronous failable constructor. See |
||
2758 | * g_dbus_connection_new() for the asynchronous version. |
||
2759 | * |
||
2760 | * Returns: a #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). |
||
2761 | * |
||
2762 | * Since: 2.26 |
||
2763 | */ |
||
2764 | GDBusConnection * |
||
2765 | g_dbus_connection_new_sync (GIOStream *stream, |
||
2766 | const gchar *guid, |
||
2767 | GDBusConnectionFlags flags, |
||
2768 | GDBusAuthObserver *observer, |
||
2769 | GCancellable *cancellable, |
||
2770 | GError **error) |
||
2771 | { |
||
2772 | g_return_val_if_fail (G_IS_IO_STREAM (stream), NULL); |
||
2773 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
2774 | return g_initable_new (G_TYPE_DBUS_CONNECTION, |
||
2775 | cancellable, |
||
2776 | error, |
||
2777 | "stream", stream, |
||
2778 | "guid", guid, |
||
2779 | "flags", flags, |
||
2780 | "authentication-observer", observer, |
||
2781 | NULL); |
||
2782 | } |
||
2783 | |||
2784 | /* ---------------------------------------------------------------------------------------------------- */ |
||
2785 | |||
2786 | /** |
||
2787 | * g_dbus_connection_new_for_address: |
||
2788 | * @address: a D-Bus address |
||
2789 | * @flags: flags describing how to make the connection |
||
2790 | * @observer: (allow-none): a #GDBusAuthObserver or %NULL |
||
2791 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
2792 | * @callback: a #GAsyncReadyCallback to call when the request is satisfied |
||
2793 | * @user_data: the data to pass to @callback |
||
2794 | * |
||
2795 | * Asynchronously connects and sets up a D-Bus client connection for |
||
2796 | * exchanging D-Bus messages with an endpoint specified by @address |
||
2797 | * which must be in the D-Bus address format. |
||
2798 | * |
||
2799 | * This constructor can only be used to initiate client-side |
||
2800 | * connections - use g_dbus_connection_new() if you need to act as the |
||
2801 | * server. In particular, @flags cannot contain the |
||
2802 | * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER or |
||
2803 | * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS flags. |
||
2804 | * |
||
2805 | * When the operation is finished, @callback will be invoked. You can |
||
2806 | * then call g_dbus_connection_new_finish() to get the result of the |
||
2807 | * operation. |
||
2808 | * |
||
2809 | * If @observer is not %NULL it may be used to control the |
||
2810 | * authentication process. |
||
2811 | * |
||
2812 | * This is a asynchronous failable constructor. See |
||
2813 | * g_dbus_connection_new_for_address_sync() for the synchronous |
||
2814 | * version. |
||
2815 | * |
||
2816 | * Since: 2.26 |
||
2817 | */ |
||
2818 | void |
||
2819 | g_dbus_connection_new_for_address (const gchar *address, |
||
2820 | GDBusConnectionFlags flags, |
||
2821 | GDBusAuthObserver *observer, |
||
2822 | GCancellable *cancellable, |
||
2823 | GAsyncReadyCallback callback, |
||
2824 | gpointer user_data) |
||
2825 | { |
||
2826 | g_return_if_fail (address != NULL); |
||
2827 | g_async_initable_new_async (G_TYPE_DBUS_CONNECTION, |
||
2828 | G_PRIORITY_DEFAULT, |
||
2829 | cancellable, |
||
2830 | callback, |
||
2831 | user_data, |
||
2832 | "address", address, |
||
2833 | "flags", flags, |
||
2834 | "authentication-observer", observer, |
||
2835 | NULL); |
||
2836 | } |
||
2837 | |||
2838 | /** |
||
2839 | * g_dbus_connection_new_for_address_finish: |
||
2840 | * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed |
||
2841 | * to g_dbus_connection_new() |
||
2842 | * @error: return location for error or %NULL |
||
2843 | * |
||
2844 | * Finishes an operation started with g_dbus_connection_new_for_address(). |
||
2845 | * |
||
2846 | * Returns: a #GDBusConnection or %NULL if @error is set. Free with |
||
2847 | * g_object_unref(). |
||
2848 | * |
||
2849 | * Since: 2.26 |
||
2850 | */ |
||
2851 | GDBusConnection * |
||
2852 | g_dbus_connection_new_for_address_finish (GAsyncResult *res, |
||
2853 | GError **error) |
||
2854 | { |
||
2855 | GObject *object; |
||
2856 | GObject *source_object; |
||
2857 | |||
2858 | g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL); |
||
2859 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
2860 | |||
2861 | source_object = g_async_result_get_source_object (res); |
||
2862 | g_assert (source_object != NULL); |
||
2863 | object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), |
||
2864 | res, |
||
2865 | error); |
||
2866 | g_object_unref (source_object); |
||
2867 | if (object != NULL) |
||
2868 | return G_DBUS_CONNECTION (object); |
||
2869 | else |
||
2870 | return NULL; |
||
2871 | } |
||
2872 | |||
2873 | /** |
||
2874 | * g_dbus_connection_new_for_address_sync: |
||
2875 | * @address: a D-Bus address |
||
2876 | * @flags: flags describing how to make the connection |
||
2877 | * @observer: (allow-none): a #GDBusAuthObserver or %NULL |
||
2878 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
2879 | * @error: return location for error or %NULL |
||
2880 | * |
||
2881 | * Synchronously connects and sets up a D-Bus client connection for |
||
2882 | * exchanging D-Bus messages with an endpoint specified by @address |
||
2883 | * which must be in the D-Bus address format. |
||
2884 | * |
||
2885 | * This constructor can only be used to initiate client-side |
||
2886 | * connections - use g_dbus_connection_new_sync() if you need to act |
||
2887 | * as the server. In particular, @flags cannot contain the |
||
2888 | * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER or |
||
2889 | * %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS flags. |
||
2890 | * |
||
2891 | * This is a synchronous failable constructor. See |
||
2892 | * g_dbus_connection_new_for_address() for the asynchronous version. |
||
2893 | * |
||
2894 | * If @observer is not %NULL it may be used to control the |
||
2895 | * authentication process. |
||
2896 | * |
||
2897 | * Returns: a #GDBusConnection or %NULL if @error is set. Free with |
||
2898 | * g_object_unref(). |
||
2899 | * |
||
2900 | * Since: 2.26 |
||
2901 | */ |
||
2902 | GDBusConnection * |
||
2903 | g_dbus_connection_new_for_address_sync (const gchar *address, |
||
2904 | GDBusConnectionFlags flags, |
||
2905 | GDBusAuthObserver *observer, |
||
2906 | GCancellable *cancellable, |
||
2907 | GError **error) |
||
2908 | { |
||
2909 | g_return_val_if_fail (address != NULL, NULL); |
||
2910 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
2911 | return g_initable_new (G_TYPE_DBUS_CONNECTION, |
||
2912 | cancellable, |
||
2913 | error, |
||
2914 | "address", address, |
||
2915 | "flags", flags, |
||
2916 | "authentication-observer", observer, |
||
2917 | NULL); |
||
2918 | } |
||
2919 | |||
2920 | /* ---------------------------------------------------------------------------------------------------- */ |
||
2921 | |||
2922 | /** |
||
2923 | * g_dbus_connection_set_exit_on_close: |
||
2924 | * @connection: a #GDBusConnection |
||
2925 | * @exit_on_close: whether the process should be terminated |
||
2926 | * when @connection is closed by the remote peer |
||
2927 | * |
||
2928 | * Sets whether the process should be terminated when @connection is |
||
2929 | * closed by the remote peer. See #GDBusConnection:exit-on-close for |
||
2930 | * more details. |
||
2931 | * |
||
2932 | * Note that this function should be used with care. Most modern UNIX |
||
2933 | * desktops tie the notion of a user session the session bus, and expect |
||
2934 | * all of a users applications to quit when their bus connection goes away. |
||
2935 | * If you are setting @exit_on_close to %FALSE for the shared session |
||
2936 | * bus connection, you should make sure that your application exits |
||
2937 | * when the user session ends. |
||
2938 | * |
||
2939 | * Since: 2.26 |
||
2940 | */ |
||
2941 | void |
||
2942 | g_dbus_connection_set_exit_on_close (GDBusConnection *connection, |
||
2943 | gboolean exit_on_close) |
||
2944 | { |
||
2945 | g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); |
||
2946 | |||
2947 | if (exit_on_close) |
||
2948 | g_atomic_int_or (&connection->atomic_flags, FLAG_EXIT_ON_CLOSE); |
||
2949 | else |
||
2950 | g_atomic_int_and (&connection->atomic_flags, ~FLAG_EXIT_ON_CLOSE); |
||
2951 | |||
2952 | } |
||
2953 | |||
2954 | /** |
||
2955 | * g_dbus_connection_get_exit_on_close: |
||
2956 | * @connection: a #GDBusConnection |
||
2957 | * |
||
2958 | * Gets whether the process is terminated when @connection is |
||
2959 | * closed by the remote peer. See |
||
2960 | * #GDBusConnection:exit-on-close for more details. |
||
2961 | * |
||
2962 | * Returns: whether the process is terminated when @connection is |
||
2963 | * closed by the remote peer |
||
2964 | * |
||
2965 | * Since: 2.26 |
||
2966 | */ |
||
2967 | gboolean |
||
2968 | g_dbus_connection_get_exit_on_close (GDBusConnection *connection) |
||
2969 | { |
||
2970 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
2971 | |||
2972 | if (g_atomic_int_get (&connection->atomic_flags) & FLAG_EXIT_ON_CLOSE) |
||
2973 | return TRUE; |
||
2974 | else |
||
2975 | return FALSE; |
||
2976 | } |
||
2977 | |||
2978 | /** |
||
2979 | * g_dbus_connection_get_guid: |
||
2980 | * @connection: a #GDBusConnection |
||
2981 | * |
||
2982 | * The GUID of the peer performing the role of server when |
||
2983 | * authenticating. See #GDBusConnection:guid for more details. |
||
2984 | * |
||
2985 | * Returns: The GUID. Do not free this string, it is owned by |
||
2986 | * @connection. |
||
2987 | * |
||
2988 | * Since: 2.26 |
||
2989 | */ |
||
2990 | const gchar * |
||
2991 | g_dbus_connection_get_guid (GDBusConnection *connection) |
||
2992 | { |
||
2993 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); |
||
2994 | return connection->guid; |
||
2995 | } |
||
2996 | |||
2997 | /** |
||
2998 | * g_dbus_connection_get_unique_name: |
||
2999 | * @connection: a #GDBusConnection |
||
3000 | * |
||
3001 | * Gets the unique name of @connection as assigned by the message |
||
3002 | * bus. This can also be used to figure out if @connection is a |
||
3003 | * message bus connection. |
||
3004 | * |
||
3005 | * Returns: the unique name or %NULL if @connection is not a message |
||
3006 | * bus connection. Do not free this string, it is owned by |
||
3007 | * @connection. |
||
3008 | * |
||
3009 | * Since: 2.26 |
||
3010 | */ |
||
3011 | const gchar * |
||
3012 | g_dbus_connection_get_unique_name (GDBusConnection *connection) |
||
3013 | { |
||
3014 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); |
||
3015 | |||
3016 | /* do not use g_return_val_if_fail(), we want the memory barrier */ |
||
3017 | if (!check_initialized (connection)) |
||
3018 | return NULL; |
||
3019 | |||
3020 | return connection->bus_unique_name; |
||
3021 | } |
||
3022 | |||
3023 | /** |
||
3024 | * g_dbus_connection_get_peer_credentials: |
||
3025 | * @connection: a #GDBusConnection |
||
3026 | * |
||
3027 | * Gets the credentials of the authenticated peer. This will always |
||
3028 | * return %NULL unless @connection acted as a server |
||
3029 | * (e.g. %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER was passed) |
||
3030 | * when set up and the client passed credentials as part of the |
||
3031 | * authentication process. |
||
3032 | * |
||
3033 | * In a message bus setup, the message bus is always the server and |
||
3034 | * each application is a client. So this method will always return |
||
3035 | * %NULL for message bus clients. |
||
3036 | * |
||
3037 | * Returns: (transfer none) (nullable): a #GCredentials or %NULL if not |
||
3038 | * available. Do not free this object, it is owned by @connection. |
||
3039 | * |
||
3040 | * Since: 2.26 |
||
3041 | */ |
||
3042 | GCredentials * |
||
3043 | g_dbus_connection_get_peer_credentials (GDBusConnection *connection) |
||
3044 | { |
||
3045 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); |
||
3046 | |||
3047 | /* do not use g_return_val_if_fail(), we want the memory barrier */ |
||
3048 | if (!check_initialized (connection)) |
||
3049 | return NULL; |
||
3050 | |||
3051 | return connection->credentials; |
||
3052 | } |
||
3053 | |||
3054 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3055 | |||
3056 | static guint _global_filter_id = 1; |
||
3057 | |||
3058 | /** |
||
3059 | * g_dbus_connection_add_filter: |
||
3060 | * @connection: a #GDBusConnection |
||
3061 | * @filter_function: a filter function |
||
3062 | * @user_data: user data to pass to @filter_function |
||
3063 | * @user_data_free_func: function to free @user_data with when filter |
||
3064 | * is removed or %NULL |
||
3065 | * |
||
3066 | * Adds a message filter. Filters are handlers that are run on all |
||
3067 | * incoming and outgoing messages, prior to standard dispatch. Filters |
||
3068 | * are run in the order that they were added. The same handler can be |
||
3069 | * added as a filter more than once, in which case it will be run more |
||
3070 | * than once. Filters added during a filter callback won't be run on |
||
3071 | * the message being processed. Filter functions are allowed to modify |
||
3072 | * and even drop messages. |
||
3073 | * |
||
3074 | * Note that filters are run in a dedicated message handling thread so |
||
3075 | * they can't block and, generally, can't do anything but signal a |
||
3076 | * worker thread. Also note that filters are rarely needed - use API |
||
3077 | * such as g_dbus_connection_send_message_with_reply(), |
||
3078 | * g_dbus_connection_signal_subscribe() or g_dbus_connection_call() instead. |
||
3079 | * |
||
3080 | * If a filter consumes an incoming message the message is not |
||
3081 | * dispatched anywhere else - not even the standard dispatch machinery |
||
3082 | * (that API such as g_dbus_connection_signal_subscribe() and |
||
3083 | * g_dbus_connection_send_message_with_reply() relies on) will see the |
||
3084 | * message. Similary, if a filter consumes an outgoing message, the |
||
3085 | * message will not be sent to the other peer. |
||
3086 | * |
||
3087 | * If @user_data_free_func is non-%NULL, it will be called (in the |
||
3088 | * thread-default main context of the thread you are calling this |
||
3089 | * method from) at some point after @user_data is no longer |
||
3090 | * needed. (It is not guaranteed to be called synchronously when the |
||
3091 | * filter is removed, and may be called after @connection has been |
||
3092 | * destroyed.) |
||
3093 | * |
||
3094 | * Returns: a filter identifier that can be used with |
||
3095 | * g_dbus_connection_remove_filter() |
||
3096 | * |
||
3097 | * Since: 2.26 |
||
3098 | */ |
||
3099 | guint |
||
3100 | g_dbus_connection_add_filter (GDBusConnection *connection, |
||
3101 | GDBusMessageFilterFunction filter_function, |
||
3102 | gpointer user_data, |
||
3103 | GDestroyNotify user_data_free_func) |
||
3104 | { |
||
3105 | FilterData *data; |
||
3106 | |||
3107 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); |
||
3108 | g_return_val_if_fail (filter_function != NULL, 0); |
||
3109 | g_return_val_if_fail (check_initialized (connection), 0); |
||
3110 | |||
3111 | CONNECTION_LOCK (connection); |
||
3112 | data = g_new0 (FilterData, 1); |
||
3113 | data->id = _global_filter_id++; /* TODO: overflow etc. */ |
||
3114 | data->ref_count = 1; |
||
3115 | data->filter_function = filter_function; |
||
3116 | data->user_data = user_data; |
||
3117 | data->user_data_free_func = user_data_free_func; |
||
3118 | data->context = g_main_context_ref_thread_default (); |
||
3119 | g_ptr_array_add (connection->filters, data); |
||
3120 | CONNECTION_UNLOCK (connection); |
||
3121 | |||
3122 | return data->id; |
||
3123 | } |
||
3124 | |||
3125 | /* only called from finalize(), removes all filters */ |
||
3126 | static void |
||
3127 | purge_all_filters (GDBusConnection *connection) |
||
3128 | { |
||
3129 | guint n; |
||
3130 | for (n = 0; n < connection->filters->len; n++) |
||
3131 | { |
||
3132 | FilterData *data = connection->filters->pdata[n]; |
||
3133 | |||
3134 | call_destroy_notify (data->context, |
||
3135 | data->user_data_free_func, |
||
3136 | data->user_data); |
||
3137 | g_main_context_unref (data->context); |
||
3138 | g_free (data); |
||
3139 | } |
||
3140 | } |
||
3141 | |||
3142 | /** |
||
3143 | * g_dbus_connection_remove_filter: |
||
3144 | * @connection: a #GDBusConnection |
||
3145 | * @filter_id: an identifier obtained from g_dbus_connection_add_filter() |
||
3146 | * |
||
3147 | * Removes a filter. |
||
3148 | * |
||
3149 | * Note that since filters run in a different thread, there is a race |
||
3150 | * condition where it is possible that the filter will be running even |
||
3151 | * after calling g_dbus_connection_remove_filter(), so you cannot just |
||
3152 | * free data that the filter might be using. Instead, you should pass |
||
3153 | * a #GDestroyNotify to g_dbus_connection_add_filter(), which will be |
||
3154 | * called when it is guaranteed that the data is no longer needed. |
||
3155 | * |
||
3156 | * Since: 2.26 |
||
3157 | */ |
||
3158 | void |
||
3159 | g_dbus_connection_remove_filter (GDBusConnection *connection, |
||
3160 | guint filter_id) |
||
3161 | { |
||
3162 | guint n; |
||
3163 | FilterData *to_destroy; |
||
3164 | |||
3165 | g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); |
||
3166 | g_return_if_fail (check_initialized (connection)); |
||
3167 | |||
3168 | CONNECTION_LOCK (connection); |
||
3169 | to_destroy = NULL; |
||
3170 | for (n = 0; n < connection->filters->len; n++) |
||
3171 | { |
||
3172 | FilterData *data = connection->filters->pdata[n]; |
||
3173 | if (data->id == filter_id) |
||
3174 | { |
||
3175 | g_ptr_array_remove_index (connection->filters, n); |
||
3176 | data->ref_count--; |
||
3177 | if (data->ref_count == 0) |
||
3178 | to_destroy = data; |
||
3179 | break; |
||
3180 | } |
||
3181 | } |
||
3182 | CONNECTION_UNLOCK (connection); |
||
3183 | |||
3184 | /* do free without holding lock */ |
||
3185 | if (to_destroy != NULL) |
||
3186 | { |
||
3187 | if (to_destroy->user_data_free_func != NULL) |
||
3188 | to_destroy->user_data_free_func (to_destroy->user_data); |
||
3189 | g_main_context_unref (to_destroy->context); |
||
3190 | g_free (to_destroy); |
||
3191 | } |
||
3192 | else |
||
3193 | { |
||
3194 | g_warning ("g_dbus_connection_remove_filter: No filter found for filter_id %d", filter_id); |
||
3195 | } |
||
3196 | } |
||
3197 | |||
3198 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3199 | |||
3200 | typedef struct |
||
3201 | { |
||
3202 | gchar *rule; |
||
3203 | gchar *sender; |
||
3204 | gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */ |
||
3205 | gchar *interface_name; |
||
3206 | gchar *member; |
||
3207 | gchar *object_path; |
||
3208 | gchar *arg0; |
||
3209 | GDBusSignalFlags flags; |
||
3210 | GArray *subscribers; |
||
3211 | } SignalData; |
||
3212 | |||
3213 | typedef struct |
||
3214 | { |
||
3215 | GDBusSignalCallback callback; |
||
3216 | gpointer user_data; |
||
3217 | GDestroyNotify user_data_free_func; |
||
3218 | guint id; |
||
3219 | GMainContext *context; |
||
3220 | } SignalSubscriber; |
||
3221 | |||
3222 | static void |
||
3223 | signal_data_free (SignalData *signal_data) |
||
3224 | { |
||
3225 | g_free (signal_data->rule); |
||
3226 | g_free (signal_data->sender); |
||
3227 | g_free (signal_data->sender_unique_name); |
||
3228 | g_free (signal_data->interface_name); |
||
3229 | g_free (signal_data->member); |
||
3230 | g_free (signal_data->object_path); |
||
3231 | g_free (signal_data->arg0); |
||
3232 | g_array_free (signal_data->subscribers, TRUE); |
||
3233 | g_free (signal_data); |
||
3234 | } |
||
3235 | |||
3236 | static gchar * |
||
3237 | args_to_rule (const gchar *sender, |
||
3238 | const gchar *interface_name, |
||
3239 | const gchar *member, |
||
3240 | const gchar *object_path, |
||
3241 | const gchar *arg0, |
||
3242 | GDBusSignalFlags flags) |
||
3243 | { |
||
3244 | GString *rule; |
||
3245 | |||
3246 | rule = g_string_new ("type='signal'"); |
||
3247 | if (flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE) |
||
3248 | g_string_prepend_c (rule, '-'); |
||
3249 | if (sender != NULL) |
||
3250 | g_string_append_printf (rule, ",sender='%s'", sender); |
||
3251 | if (interface_name != NULL) |
||
3252 | g_string_append_printf (rule, ",interface='%s'", interface_name); |
||
3253 | if (member != NULL) |
||
3254 | g_string_append_printf (rule, ",member='%s'", member); |
||
3255 | if (object_path != NULL) |
||
3256 | g_string_append_printf (rule, ",path='%s'", object_path); |
||
3257 | |||
3258 | if (arg0 != NULL) |
||
3259 | { |
||
3260 | if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH) |
||
3261 | g_string_append_printf (rule, ",arg0path='%s'", arg0); |
||
3262 | else if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE) |
||
3263 | g_string_append_printf (rule, ",arg0namespace='%s'", arg0); |
||
3264 | else |
||
3265 | g_string_append_printf (rule, ",arg0='%s'", arg0); |
||
3266 | } |
||
3267 | |||
3268 | return g_string_free (rule, FALSE); |
||
3269 | } |
||
3270 | |||
3271 | static guint _global_subscriber_id = 1; |
||
3272 | static guint _global_registration_id = 1; |
||
3273 | static guint _global_subtree_registration_id = 1; |
||
3274 | |||
3275 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3276 | |||
3277 | /* Called in a user thread, lock is held */ |
||
3278 | static void |
||
3279 | add_match_rule (GDBusConnection *connection, |
||
3280 | const gchar *match_rule) |
||
3281 | { |
||
3282 | GError *error; |
||
3283 | GDBusMessage *message; |
||
3284 | |||
3285 | if (match_rule[0] == '-') |
||
3286 | return; |
||
3287 | |||
3288 | message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ |
||
3289 | "/org/freedesktop/DBus", /* path */ |
||
3290 | "org.freedesktop.DBus", /* interface */ |
||
3291 | "AddMatch"); |
||
3292 | g_dbus_message_set_body (message, g_variant_new ("(s)", match_rule)); |
||
3293 | error = NULL; |
||
3294 | if (!g_dbus_connection_send_message_unlocked (connection, |
||
3295 | message, |
||
3296 | G_DBUS_SEND_MESSAGE_FLAGS_NONE, |
||
3297 | NULL, |
||
3298 | &error)) |
||
3299 | { |
||
3300 | g_critical ("Error while sending AddMatch() message: %s", error->message); |
||
3301 | g_error_free (error); |
||
3302 | } |
||
3303 | g_object_unref (message); |
||
3304 | } |
||
3305 | |||
3306 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3307 | |||
3308 | /* Called in a user thread, lock is held */ |
||
3309 | static void |
||
3310 | remove_match_rule (GDBusConnection *connection, |
||
3311 | const gchar *match_rule) |
||
3312 | { |
||
3313 | GError *error; |
||
3314 | GDBusMessage *message; |
||
3315 | |||
3316 | if (match_rule[0] == '-') |
||
3317 | return; |
||
3318 | |||
3319 | message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ |
||
3320 | "/org/freedesktop/DBus", /* path */ |
||
3321 | "org.freedesktop.DBus", /* interface */ |
||
3322 | "RemoveMatch"); |
||
3323 | g_dbus_message_set_body (message, g_variant_new ("(s)", match_rule)); |
||
3324 | |||
3325 | error = NULL; |
||
3326 | if (!g_dbus_connection_send_message_unlocked (connection, |
||
3327 | message, |
||
3328 | G_DBUS_SEND_MESSAGE_FLAGS_NONE, |
||
3329 | NULL, |
||
3330 | &error)) |
||
3331 | { |
||
3332 | /* If we could get G_IO_ERROR_CLOSED here, it wouldn't be reasonable to |
||
3333 | * critical; but we're holding the lock, and our caller checked whether |
||
3334 | * we were already closed, so we can't get that error. |
||
3335 | */ |
||
3336 | g_critical ("Error while sending RemoveMatch() message: %s", error->message); |
||
3337 | g_error_free (error); |
||
3338 | } |
||
3339 | g_object_unref (message); |
||
3340 | } |
||
3341 | |||
3342 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3343 | |||
3344 | static gboolean |
||
3345 | is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) |
||
3346 | { |
||
3347 | return g_strcmp0 (signal_data->sender_unique_name, "org.freedesktop.DBus") == 0 && |
||
3348 | g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0 && |
||
3349 | g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0 && |
||
3350 | (g_strcmp0 (signal_data->member, "NameLost") == 0 || |
||
3351 | g_strcmp0 (signal_data->member, "NameAcquired") == 0); |
||
3352 | } |
||
3353 | |||
3354 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3355 | |||
3356 | /** |
||
3357 | * g_dbus_connection_signal_subscribe: |
||
3358 | * @connection: a #GDBusConnection |
||
3359 | * @sender: (allow-none): sender name to match on (unique or well-known name) |
||
3360 | * or %NULL to listen from all senders |
||
3361 | * @interface_name: (allow-none): D-Bus interface name to match on or %NULL to |
||
3362 | * match on all interfaces |
||
3363 | * @member: (allow-none): D-Bus signal name to match on or %NULL to match on |
||
3364 | * all signals |
||
3365 | * @object_path: (allow-none): object path to match on or %NULL to match on |
||
3366 | * all object paths |
||
3367 | * @arg0: (allow-none): contents of first string argument to match on or %NULL |
||
3368 | * to match on all kinds of arguments |
||
3369 | * @flags: #GDBusSignalFlags describing how arg0 is used in subscribing to the |
||
3370 | * signal |
||
3371 | * @callback: callback to invoke when there is a signal matching the requested data |
||
3372 | * @user_data: user data to pass to @callback |
||
3373 | * @user_data_free_func: (allow-none): function to free @user_data with when |
||
3374 | * subscription is removed or %NULL |
||
3375 | * |
||
3376 | * Subscribes to signals on @connection and invokes @callback with a whenever |
||
3377 | * the signal is received. Note that @callback will be invoked in the |
||
3378 | * [thread-default main context][g-main-context-push-thread-default] |
||
3379 | * of the thread you are calling this method from. |
||
3380 | * |
||
3381 | * If @connection is not a message bus connection, @sender must be |
||
3382 | * %NULL. |
||
3383 | * |
||
3384 | * If @sender is a well-known name note that @callback is invoked with |
||
3385 | * the unique name for the owner of @sender, not the well-known name |
||
3386 | * as one would expect. This is because the message bus rewrites the |
||
3387 | * name. As such, to avoid certain race conditions, users should be |
||
3388 | * tracking the name owner of the well-known name and use that when |
||
3389 | * processing the received signal. |
||
3390 | * |
||
3391 | * If one of %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE or |
||
3392 | * %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH are given, @arg0 is |
||
3393 | * interpreted as part of a namespace or path. The first argument |
||
3394 | * of a signal is matched against that part as specified by D-Bus. |
||
3395 | * |
||
3396 | * If @user_data_free_func is non-%NULL, it will be called (in the |
||
3397 | * thread-default main context of the thread you are calling this |
||
3398 | * method from) at some point after @user_data is no longer |
||
3399 | * needed. (It is not guaranteed to be called synchronously when the |
||
3400 | * signal is unsubscribed from, and may be called after @connection |
||
3401 | * has been destroyed.) |
||
3402 | * |
||
3403 | * Returns: a subscription identifier that can be used with g_dbus_connection_signal_unsubscribe() |
||
3404 | * |
||
3405 | * Since: 2.26 |
||
3406 | */ |
||
3407 | guint |
||
3408 | g_dbus_connection_signal_subscribe (GDBusConnection *connection, |
||
3409 | const gchar *sender, |
||
3410 | const gchar *interface_name, |
||
3411 | const gchar *member, |
||
3412 | const gchar *object_path, |
||
3413 | const gchar *arg0, |
||
3414 | GDBusSignalFlags flags, |
||
3415 | GDBusSignalCallback callback, |
||
3416 | gpointer user_data, |
||
3417 | GDestroyNotify user_data_free_func) |
||
3418 | { |
||
3419 | gchar *rule; |
||
3420 | SignalData *signal_data; |
||
3421 | SignalSubscriber subscriber; |
||
3422 | GPtrArray *signal_data_array; |
||
3423 | const gchar *sender_unique_name; |
||
3424 | |||
3425 | /* Right now we abort if AddMatch() fails since it can only fail with the bus being in |
||
3426 | * an OOM condition. We might want to change that but that would involve making |
||
3427 | * g_dbus_connection_signal_subscribe() asynchronous and having the call sites |
||
3428 | * handle that. And there's really no sensible way of handling this short of retrying |
||
3429 | * to add the match rule... and then there's the little thing that, hey, maybe there's |
||
3430 | * a reason the bus in an OOM condition. |
||
3431 | * |
||
3432 | * Doable, but not really sure it's worth it... |
||
3433 | */ |
||
3434 | |||
3435 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); |
||
3436 | g_return_val_if_fail (sender == NULL || (g_dbus_is_name (sender) && (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)), 0); |
||
3437 | g_return_val_if_fail (interface_name == NULL || g_dbus_is_interface_name (interface_name), 0); |
||
3438 | g_return_val_if_fail (member == NULL || g_dbus_is_member_name (member), 0); |
||
3439 | g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), 0); |
||
3440 | g_return_val_if_fail (callback != NULL, 0); |
||
3441 | g_return_val_if_fail (check_initialized (connection), 0); |
||
3442 | g_return_val_if_fail (!((flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH) && (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)), 0); |
||
3443 | g_return_val_if_fail (!(arg0 == NULL && (flags & (G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH | G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE))), 0); |
||
3444 | |||
3445 | CONNECTION_LOCK (connection); |
||
3446 | |||
3447 | /* If G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE was specified, we will end up |
||
3448 | * with a '-' character to prefix the rule (which will otherwise be |
||
3449 | * normal). |
||
3450 | * |
||
3451 | * This allows us to hash the rule and do our lifecycle tracking in |
||
3452 | * the usual way, but the '-' prevents the match rule from ever |
||
3453 | * actually being send to the bus (either for add or remove). |
||
3454 | */ |
||
3455 | rule = args_to_rule (sender, interface_name, member, object_path, arg0, flags); |
||
3456 | |||
3457 | if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0)) |
||
3458 | sender_unique_name = sender; |
||
3459 | else |
||
3460 | sender_unique_name = ""; |
||
3461 | |||
3462 | subscriber.callback = callback; |
||
3463 | subscriber.user_data = user_data; |
||
3464 | subscriber.user_data_free_func = user_data_free_func; |
||
3465 | subscriber.id = _global_subscriber_id++; /* TODO: overflow etc. */ |
||
3466 | subscriber.context = g_main_context_ref_thread_default (); |
||
3467 | |||
3468 | /* see if we've already have this rule */ |
||
3469 | signal_data = g_hash_table_lookup (connection->map_rule_to_signal_data, rule); |
||
3470 | if (signal_data != NULL) |
||
3471 | { |
||
3472 | g_array_append_val (signal_data->subscribers, subscriber); |
||
3473 | g_free (rule); |
||
3474 | goto out; |
||
3475 | } |
||
3476 | |||
3477 | signal_data = g_new0 (SignalData, 1); |
||
3478 | signal_data->rule = rule; |
||
3479 | signal_data->sender = g_strdup (sender); |
||
3480 | signal_data->sender_unique_name = g_strdup (sender_unique_name); |
||
3481 | signal_data->interface_name = g_strdup (interface_name); |
||
3482 | signal_data->member = g_strdup (member); |
||
3483 | signal_data->object_path = g_strdup (object_path); |
||
3484 | signal_data->arg0 = g_strdup (arg0); |
||
3485 | signal_data->flags = flags; |
||
3486 | signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); |
||
3487 | g_array_append_val (signal_data->subscribers, subscriber); |
||
3488 | |||
3489 | g_hash_table_insert (connection->map_rule_to_signal_data, |
||
3490 | signal_data->rule, |
||
3491 | signal_data); |
||
3492 | |||
3493 | /* Add the match rule to the bus... |
||
3494 | * |
||
3495 | * Avoid adding match rules for NameLost and NameAcquired messages - the bus will |
||
3496 | * always send such messages to us. |
||
3497 | */ |
||
3498 | if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) |
||
3499 | { |
||
3500 | if (!is_signal_data_for_name_lost_or_acquired (signal_data)) |
||
3501 | add_match_rule (connection, signal_data->rule); |
||
3502 | } |
||
3503 | |||
3504 | signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, |
||
3505 | signal_data->sender_unique_name); |
||
3506 | if (signal_data_array == NULL) |
||
3507 | { |
||
3508 | signal_data_array = g_ptr_array_new (); |
||
3509 | g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array, |
||
3510 | g_strdup (signal_data->sender_unique_name), |
||
3511 | signal_data_array); |
||
3512 | } |
||
3513 | g_ptr_array_add (signal_data_array, signal_data); |
||
3514 | |||
3515 | out: |
||
3516 | g_hash_table_insert (connection->map_id_to_signal_data, |
||
3517 | GUINT_TO_POINTER (subscriber.id), |
||
3518 | signal_data); |
||
3519 | |||
3520 | CONNECTION_UNLOCK (connection); |
||
3521 | |||
3522 | return subscriber.id; |
||
3523 | } |
||
3524 | |||
3525 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3526 | |||
3527 | /* called in any thread */ |
||
3528 | /* must hold lock when calling this (except if connection->finalizing is TRUE) */ |
||
3529 | static void |
||
3530 | unsubscribe_id_internal (GDBusConnection *connection, |
||
3531 | guint subscription_id, |
||
3532 | GArray *out_removed_subscribers) |
||
3533 | { |
||
3534 | SignalData *signal_data; |
||
3535 | GPtrArray *signal_data_array; |
||
3536 | guint n; |
||
3537 | |||
3538 | signal_data = g_hash_table_lookup (connection->map_id_to_signal_data, |
||
3539 | GUINT_TO_POINTER (subscription_id)); |
||
3540 | if (signal_data == NULL) |
||
3541 | { |
||
3542 | /* Don't warn here, we may have thrown all subscriptions out when the connection was closed */ |
||
3543 | goto out; |
||
3544 | } |
||
3545 | |||
3546 | for (n = 0; n < signal_data->subscribers->len; n++) |
||
3547 | { |
||
3548 | SignalSubscriber *subscriber; |
||
3549 | |||
3550 | subscriber = &(g_array_index (signal_data->subscribers, SignalSubscriber, n)); |
||
3551 | if (subscriber->id != subscription_id) |
||
3552 | continue; |
||
3553 | |||
3554 | g_warn_if_fail (g_hash_table_remove (connection->map_id_to_signal_data, |
||
3555 | GUINT_TO_POINTER (subscription_id))); |
||
3556 | g_array_append_val (out_removed_subscribers, *subscriber); |
||
3557 | g_array_remove_index (signal_data->subscribers, n); |
||
3558 | |||
3559 | if (signal_data->subscribers->len == 0) |
||
3560 | { |
||
3561 | g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule)); |
||
3562 | |||
3563 | signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, |
||
3564 | signal_data->sender_unique_name); |
||
3565 | g_warn_if_fail (signal_data_array != NULL); |
||
3566 | g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); |
||
3567 | |||
3568 | if (signal_data_array->len == 0) |
||
3569 | { |
||
3570 | g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array, |
||
3571 | signal_data->sender_unique_name)); |
||
3572 | } |
||
3573 | |||
3574 | /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ |
||
3575 | if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) && |
||
3576 | !is_signal_data_for_name_lost_or_acquired (signal_data) && |
||
3577 | !g_dbus_connection_is_closed (connection) && |
||
3578 | !connection->finalizing) |
||
3579 | { |
||
3580 | /* The check for g_dbus_connection_is_closed() means that |
||
3581 | * sending the RemoveMatch message can't fail with |
||
3582 | * G_IO_ERROR_CLOSED, because we're holding the lock, |
||
3583 | * so on_worker_closed() can't happen between the check we just |
||
3584 | * did, and releasing the lock later. |
||
3585 | */ |
||
3586 | remove_match_rule (connection, signal_data->rule); |
||
3587 | } |
||
3588 | |||
3589 | signal_data_free (signal_data); |
||
3590 | } |
||
3591 | |||
3592 | goto out; |
||
3593 | } |
||
3594 | |||
3595 | g_assert_not_reached (); |
||
3596 | |||
3597 | out: |
||
3598 | ; |
||
3599 | } |
||
3600 | |||
3601 | /** |
||
3602 | * g_dbus_connection_signal_unsubscribe: |
||
3603 | * @connection: a #GDBusConnection |
||
3604 | * @subscription_id: a subscription id obtained from |
||
3605 | * g_dbus_connection_signal_subscribe() |
||
3606 | * |
||
3607 | * Unsubscribes from signals. |
||
3608 | * |
||
3609 | * Since: 2.26 |
||
3610 | */ |
||
3611 | void |
||
3612 | g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, |
||
3613 | guint subscription_id) |
||
3614 | { |
||
3615 | GArray *subscribers; |
||
3616 | guint n; |
||
3617 | |||
3618 | g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); |
||
3619 | g_return_if_fail (check_initialized (connection)); |
||
3620 | |||
3621 | subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); |
||
3622 | |||
3623 | CONNECTION_LOCK (connection); |
||
3624 | unsubscribe_id_internal (connection, |
||
3625 | subscription_id, |
||
3626 | subscribers); |
||
3627 | CONNECTION_UNLOCK (connection); |
||
3628 | |||
3629 | /* invariant */ |
||
3630 | g_assert (subscribers->len == 0 || subscribers->len == 1); |
||
3631 | |||
3632 | /* call GDestroyNotify without lock held */ |
||
3633 | for (n = 0; n < subscribers->len; n++) |
||
3634 | { |
||
3635 | SignalSubscriber *subscriber; |
||
3636 | subscriber = &(g_array_index (subscribers, SignalSubscriber, n)); |
||
3637 | call_destroy_notify (subscriber->context, |
||
3638 | subscriber->user_data_free_func, |
||
3639 | subscriber->user_data); |
||
3640 | g_main_context_unref (subscriber->context); |
||
3641 | } |
||
3642 | |||
3643 | g_array_free (subscribers, TRUE); |
||
3644 | } |
||
3645 | |||
3646 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3647 | |||
3648 | typedef struct |
||
3649 | { |
||
3650 | guint subscription_id; |
||
3651 | GDBusSignalCallback callback; |
||
3652 | gpointer user_data; |
||
3653 | GDBusMessage *message; |
||
3654 | GDBusConnection *connection; |
||
3655 | const gchar *sender; |
||
3656 | const gchar *path; |
||
3657 | const gchar *interface; |
||
3658 | const gchar *member; |
||
3659 | } SignalInstance; |
||
3660 | |||
3661 | /* called on delivery thread (e.g. where g_dbus_connection_signal_subscribe() was called) with |
||
3662 | * no locks held |
||
3663 | */ |
||
3664 | static gboolean |
||
3665 | emit_signal_instance_in_idle_cb (gpointer data) |
||
3666 | { |
||
3667 | SignalInstance *signal_instance = data; |
||
3668 | GVariant *parameters; |
||
3669 | gboolean has_subscription; |
||
3670 | |||
3671 | parameters = g_dbus_message_get_body (signal_instance->message); |
||
3672 | if (parameters == NULL) |
||
3673 | { |
||
3674 | parameters = g_variant_new ("()"); |
||
3675 | g_variant_ref_sink (parameters); |
||
3676 | } |
||
3677 | else |
||
3678 | { |
||
3679 | g_variant_ref_sink (parameters); |
||
3680 | } |
||
3681 | |||
3682 | #if 0 |
||
3683 | g_print ("in emit_signal_instance_in_idle_cb (id=%d sender=%s path=%s interface=%s member=%s params=%s)\n", |
||
3684 | signal_instance->subscription_id, |
||
3685 | signal_instance->sender, |
||
3686 | signal_instance->path, |
||
3687 | signal_instance->interface, |
||
3688 | signal_instance->member, |
||
3689 | g_variant_print (parameters, TRUE)); |
||
3690 | #endif |
||
3691 | |||
3692 | /* Careful here, don't do the callback if we no longer has the subscription */ |
||
3693 | CONNECTION_LOCK (signal_instance->connection); |
||
3694 | has_subscription = FALSE; |
||
3695 | if (g_hash_table_lookup (signal_instance->connection->map_id_to_signal_data, |
||
3696 | GUINT_TO_POINTER (signal_instance->subscription_id)) != NULL) |
||
3697 | has_subscription = TRUE; |
||
3698 | CONNECTION_UNLOCK (signal_instance->connection); |
||
3699 | |||
3700 | if (has_subscription) |
||
3701 | signal_instance->callback (signal_instance->connection, |
||
3702 | signal_instance->sender, |
||
3703 | signal_instance->path, |
||
3704 | signal_instance->interface, |
||
3705 | signal_instance->member, |
||
3706 | parameters, |
||
3707 | signal_instance->user_data); |
||
3708 | |||
3709 | g_variant_unref (parameters); |
||
3710 | |||
3711 | return FALSE; |
||
3712 | } |
||
3713 | |||
3714 | static void |
||
3715 | signal_instance_free (SignalInstance *signal_instance) |
||
3716 | { |
||
3717 | g_object_unref (signal_instance->message); |
||
3718 | g_object_unref (signal_instance->connection); |
||
3719 | g_free (signal_instance); |
||
3720 | } |
||
3721 | |||
3722 | static gboolean |
||
3723 | namespace_rule_matches (const gchar *namespace, |
||
3724 | const gchar *name) |
||
3725 | { |
||
3726 | gint len_namespace; |
||
3727 | gint len_name; |
||
3728 | |||
3729 | len_namespace = strlen (namespace); |
||
3730 | len_name = strlen (name); |
||
3731 | |||
3732 | if (len_name < len_namespace) |
||
3733 | return FALSE; |
||
3734 | |||
3735 | if (memcmp (namespace, name, len_namespace) != 0) |
||
3736 | return FALSE; |
||
3737 | |||
3738 | return len_namespace == len_name || name[len_namespace] == '.'; |
||
3739 | } |
||
3740 | |||
3741 | static gboolean |
||
3742 | path_rule_matches (const gchar *path_a, |
||
3743 | const gchar *path_b) |
||
3744 | { |
||
3745 | gint len_a, len_b; |
||
3746 | |||
3747 | len_a = strlen (path_a); |
||
3748 | len_b = strlen (path_b); |
||
3749 | |||
3750 | if (len_a < len_b && (len_a == 0 || path_a[len_a - 1] != '/')) |
||
3751 | return FALSE; |
||
3752 | |||
3753 | if (len_b < len_a && (len_b == 0 || path_b[len_b - 1] != '/')) |
||
3754 | return FALSE; |
||
3755 | |||
3756 | return memcmp (path_a, path_b, MIN (len_a, len_b)) == 0; |
||
3757 | } |
||
3758 | |||
3759 | /* called in GDBusWorker thread WITH lock held */ |
||
3760 | static void |
||
3761 | schedule_callbacks (GDBusConnection *connection, |
||
3762 | GPtrArray *signal_data_array, |
||
3763 | GDBusMessage *message, |
||
3764 | const gchar *sender) |
||
3765 | { |
||
3766 | guint n, m; |
||
3767 | const gchar *interface; |
||
3768 | const gchar *member; |
||
3769 | const gchar *path; |
||
3770 | const gchar *arg0; |
||
3771 | |||
3772 | interface = NULL; |
||
3773 | member = NULL; |
||
3774 | path = NULL; |
||
3775 | arg0 = NULL; |
||
3776 | |||
3777 | interface = g_dbus_message_get_interface (message); |
||
3778 | member = g_dbus_message_get_member (message); |
||
3779 | path = g_dbus_message_get_path (message); |
||
3780 | arg0 = g_dbus_message_get_arg0 (message); |
||
3781 | |||
3782 | #if 0 |
||
3783 | g_print ("In schedule_callbacks:\n" |
||
3784 | " sender = '%s'\n" |
||
3785 | " interface = '%s'\n" |
||
3786 | " member = '%s'\n" |
||
3787 | " path = '%s'\n" |
||
3788 | " arg0 = '%s'\n", |
||
3789 | sender, |
||
3790 | interface, |
||
3791 | member, |
||
3792 | path, |
||
3793 | arg0); |
||
3794 | #endif |
||
3795 | |||
3796 | /* TODO: if this is slow, then we can change signal_data_array into |
||
3797 | * map_object_path_to_signal_data_array or something. |
||
3798 | */ |
||
3799 | for (n = 0; n < signal_data_array->len; n++) |
||
3800 | { |
||
3801 | SignalData *signal_data = signal_data_array->pdata[n]; |
||
3802 | |||
3803 | if (signal_data->interface_name != NULL && g_strcmp0 (signal_data->interface_name, interface) != 0) |
||
3804 | continue; |
||
3805 | |||
3806 | if (signal_data->member != NULL && g_strcmp0 (signal_data->member, member) != 0) |
||
3807 | continue; |
||
3808 | |||
3809 | if (signal_data->object_path != NULL && g_strcmp0 (signal_data->object_path, path) != 0) |
||
3810 | continue; |
||
3811 | |||
3812 | if (signal_data->arg0 != NULL) |
||
3813 | { |
||
3814 | if (arg0 == NULL) |
||
3815 | continue; |
||
3816 | |||
3817 | if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE) |
||
3818 | { |
||
3819 | if (!namespace_rule_matches (signal_data->arg0, arg0)) |
||
3820 | continue; |
||
3821 | } |
||
3822 | else if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH) |
||
3823 | { |
||
3824 | if (!path_rule_matches (signal_data->arg0, arg0)) |
||
3825 | continue; |
||
3826 | } |
||
3827 | else if (!g_str_equal (signal_data->arg0, arg0)) |
||
3828 | continue; |
||
3829 | } |
||
3830 | |||
3831 | for (m = 0; m < signal_data->subscribers->len; m++) |
||
3832 | { |
||
3833 | SignalSubscriber *subscriber; |
||
3834 | GSource *idle_source; |
||
3835 | SignalInstance *signal_instance; |
||
3836 | |||
3837 | subscriber = &(g_array_index (signal_data->subscribers, SignalSubscriber, m)); |
||
3838 | |||
3839 | signal_instance = g_new0 (SignalInstance, 1); |
||
3840 | signal_instance->subscription_id = subscriber->id; |
||
3841 | signal_instance->callback = subscriber->callback; |
||
3842 | signal_instance->user_data = subscriber->user_data; |
||
3843 | signal_instance->message = g_object_ref (message); |
||
3844 | signal_instance->connection = g_object_ref (connection); |
||
3845 | signal_instance->sender = sender; |
||
3846 | signal_instance->path = path; |
||
3847 | signal_instance->interface = interface; |
||
3848 | signal_instance->member = member; |
||
3849 | |||
3850 | idle_source = g_idle_source_new (); |
||
3851 | g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); |
||
3852 | g_source_set_callback (idle_source, |
||
3853 | emit_signal_instance_in_idle_cb, |
||
3854 | signal_instance, |
||
3855 | (GDestroyNotify) signal_instance_free); |
||
3856 | g_source_set_name (idle_source, "[gio] emit_signal_instance_in_idle_cb"); |
||
3857 | g_source_attach (idle_source, subscriber->context); |
||
3858 | g_source_unref (idle_source); |
||
3859 | } |
||
3860 | } |
||
3861 | } |
||
3862 | |||
3863 | /* called in GDBusWorker thread with lock held */ |
||
3864 | static void |
||
3865 | distribute_signals (GDBusConnection *connection, |
||
3866 | GDBusMessage *message) |
||
3867 | { |
||
3868 | GPtrArray *signal_data_array; |
||
3869 | const gchar *sender; |
||
3870 | |||
3871 | sender = g_dbus_message_get_sender (message); |
||
3872 | |||
3873 | if (G_UNLIKELY (_g_dbus_debug_signal ())) |
||
3874 | { |
||
3875 | _g_dbus_debug_print_lock (); |
||
3876 | g_print ("========================================================================\n" |
||
3877 | "GDBus-debug:Signal:\n" |
||
3878 | " <<<< RECEIVED SIGNAL %s.%s\n" |
||
3879 | " on object %s\n" |
||
3880 | " sent by name %s\n", |
||
3881 | g_dbus_message_get_interface (message), |
||
3882 | g_dbus_message_get_member (message), |
||
3883 | g_dbus_message_get_path (message), |
||
3884 | sender != NULL ? sender : "(none)"); |
||
3885 | _g_dbus_debug_print_unlock (); |
||
3886 | } |
||
3887 | |||
3888 | /* collect subscribers that match on sender */ |
||
3889 | if (sender != NULL) |
||
3890 | { |
||
3891 | signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, sender); |
||
3892 | if (signal_data_array != NULL) |
||
3893 | schedule_callbacks (connection, signal_data_array, message, sender); |
||
3894 | } |
||
3895 | |||
3896 | /* collect subscribers not matching on sender */ |
||
3897 | signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, ""); |
||
3898 | if (signal_data_array != NULL) |
||
3899 | schedule_callbacks (connection, signal_data_array, message, sender); |
||
3900 | } |
||
3901 | |||
3902 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3903 | |||
3904 | /* only called from finalize(), removes all subscriptions */ |
||
3905 | static void |
||
3906 | purge_all_signal_subscriptions (GDBusConnection *connection) |
||
3907 | { |
||
3908 | GHashTableIter iter; |
||
3909 | gpointer key; |
||
3910 | GArray *ids; |
||
3911 | GArray *subscribers; |
||
3912 | guint n; |
||
3913 | |||
3914 | ids = g_array_new (FALSE, FALSE, sizeof (guint)); |
||
3915 | g_hash_table_iter_init (&iter, connection->map_id_to_signal_data); |
||
3916 | while (g_hash_table_iter_next (&iter, &key, NULL)) |
||
3917 | { |
||
3918 | guint subscription_id = GPOINTER_TO_UINT (key); |
||
3919 | g_array_append_val (ids, subscription_id); |
||
3920 | } |
||
3921 | |||
3922 | subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); |
||
3923 | for (n = 0; n < ids->len; n++) |
||
3924 | { |
||
3925 | guint subscription_id = g_array_index (ids, guint, n); |
||
3926 | unsubscribe_id_internal (connection, |
||
3927 | subscription_id, |
||
3928 | subscribers); |
||
3929 | } |
||
3930 | g_array_free (ids, TRUE); |
||
3931 | |||
3932 | /* call GDestroyNotify without lock held */ |
||
3933 | for (n = 0; n < subscribers->len; n++) |
||
3934 | { |
||
3935 | SignalSubscriber *subscriber; |
||
3936 | subscriber = &(g_array_index (subscribers, SignalSubscriber, n)); |
||
3937 | call_destroy_notify (subscriber->context, |
||
3938 | subscriber->user_data_free_func, |
||
3939 | subscriber->user_data); |
||
3940 | g_main_context_unref (subscriber->context); |
||
3941 | } |
||
3942 | |||
3943 | g_array_free (subscribers, TRUE); |
||
3944 | } |
||
3945 | |||
3946 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3947 | |||
3948 | static GDBusInterfaceVTable * |
||
3949 | _g_dbus_interface_vtable_copy (const GDBusInterfaceVTable *vtable) |
||
3950 | { |
||
3951 | /* Don't waste memory by copying padding - remember to update this |
||
3952 | * when changing struct _GDBusInterfaceVTable in gdbusconnection.h |
||
3953 | */ |
||
3954 | return g_memdup ((gconstpointer) vtable, 3 * sizeof (gpointer)); |
||
3955 | } |
||
3956 | |||
3957 | static void |
||
3958 | _g_dbus_interface_vtable_free (GDBusInterfaceVTable *vtable) |
||
3959 | { |
||
3960 | g_free (vtable); |
||
3961 | } |
||
3962 | |||
3963 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3964 | |||
3965 | static GDBusSubtreeVTable * |
||
3966 | _g_dbus_subtree_vtable_copy (const GDBusSubtreeVTable *vtable) |
||
3967 | { |
||
3968 | /* Don't waste memory by copying padding - remember to update this |
||
3969 | * when changing struct _GDBusSubtreeVTable in gdbusconnection.h |
||
3970 | */ |
||
3971 | return g_memdup ((gconstpointer) vtable, 3 * sizeof (gpointer)); |
||
3972 | } |
||
3973 | |||
3974 | static void |
||
3975 | _g_dbus_subtree_vtable_free (GDBusSubtreeVTable *vtable) |
||
3976 | { |
||
3977 | g_free (vtable); |
||
3978 | } |
||
3979 | |||
3980 | /* ---------------------------------------------------------------------------------------------------- */ |
||
3981 | |||
3982 | struct ExportedObject |
||
3983 | { |
||
3984 | gchar *object_path; |
||
3985 | GDBusConnection *connection; |
||
3986 | |||
3987 | /* maps gchar* -> ExportedInterface* */ |
||
3988 | GHashTable *map_if_name_to_ei; |
||
3989 | }; |
||
3990 | |||
3991 | /* only called with lock held */ |
||
3992 | static void |
||
3993 | exported_object_free (ExportedObject *eo) |
||
3994 | { |
||
3995 | g_free (eo->object_path); |
||
3996 | g_hash_table_unref (eo->map_if_name_to_ei); |
||
3997 | g_free (eo); |
||
3998 | } |
||
3999 | |||
4000 | typedef struct |
||
4001 | { |
||
4002 | ExportedObject *eo; |
||
4003 | |||
4004 | guint id; |
||
4005 | gchar *interface_name; |
||
4006 | GDBusInterfaceVTable *vtable; |
||
4007 | GDBusInterfaceInfo *interface_info; |
||
4008 | |||
4009 | GMainContext *context; |
||
4010 | gpointer user_data; |
||
4011 | GDestroyNotify user_data_free_func; |
||
4012 | } ExportedInterface; |
||
4013 | |||
4014 | /* called with lock held */ |
||
4015 | static void |
||
4016 | exported_interface_free (ExportedInterface *ei) |
||
4017 | { |
||
4018 | g_dbus_interface_info_cache_release (ei->interface_info); |
||
4019 | g_dbus_interface_info_unref ((GDBusInterfaceInfo *) ei->interface_info); |
||
4020 | |||
4021 | call_destroy_notify (ei->context, |
||
4022 | ei->user_data_free_func, |
||
4023 | ei->user_data); |
||
4024 | |||
4025 | g_main_context_unref (ei->context); |
||
4026 | |||
4027 | g_free (ei->interface_name); |
||
4028 | _g_dbus_interface_vtable_free (ei->vtable); |
||
4029 | g_free (ei); |
||
4030 | } |
||
4031 | |||
4032 | /* ---------------------------------------------------------------------------------------------------- */ |
||
4033 | |||
4034 | /* Convenience function to check if @registration_id (if not zero) or |
||
4035 | * @subtree_registration_id (if not zero) has been unregistered. If |
||
4036 | * so, returns %TRUE. |
||
4037 | * |
||
4038 | * May be called by any thread. Caller must *not* hold lock. |
||
4039 | */ |
||
4040 | static gboolean |
||
4041 | has_object_been_unregistered (GDBusConnection *connection, |
||
4042 | guint registration_id, |
||
4043 | guint subtree_registration_id) |
||
4044 | { |
||
4045 | gboolean ret; |
||
4046 | |||
4047 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
4048 | |||
4049 | ret = FALSE; |
||
4050 | |||
4051 | CONNECTION_LOCK (connection); |
||
4052 | if (registration_id != 0 && g_hash_table_lookup (connection->map_id_to_ei, |
||
4053 | GUINT_TO_POINTER (registration_id)) == NULL) |
||
4054 | { |
||
4055 | ret = TRUE; |
||
4056 | } |
||
4057 | else if (subtree_registration_id != 0 && g_hash_table_lookup (connection->map_id_to_es, |
||
4058 | GUINT_TO_POINTER (subtree_registration_id)) == NULL) |
||
4059 | { |
||
4060 | ret = TRUE; |
||
4061 | } |
||
4062 | CONNECTION_UNLOCK (connection); |
||
4063 | |||
4064 | return ret; |
||
4065 | } |
||
4066 | |||
4067 | /* ---------------------------------------------------------------------------------------------------- */ |
||
4068 | |||
4069 | typedef struct |
||
4070 | { |
||
4071 | GDBusConnection *connection; |
||
4072 | GDBusMessage *message; |
||
4073 | gpointer user_data; |
||
4074 | const gchar *property_name; |
||
4075 | const GDBusInterfaceVTable *vtable; |
||
4076 | GDBusInterfaceInfo *interface_info; |
||
4077 | const GDBusPropertyInfo *property_info; |
||
4078 | guint registration_id; |
||
4079 | guint subtree_registration_id; |
||
4080 | } PropertyData; |
||
4081 | |||
4082 | static void |
||
4083 | property_data_free (PropertyData *data) |
||
4084 | { |
||
4085 | g_object_unref (data->connection); |
||
4086 | g_object_unref (data->message); |
||
4087 | g_free (data); |
||
4088 | } |
||
4089 | |||
4090 | /* called in thread where object was registered - no locks held */ |
||
4091 | static gboolean |
||
4092 | invoke_get_property_in_idle_cb (gpointer _data) |
||
4093 | { |
||
4094 | PropertyData *data = _data; |
||
4095 | GVariant *value; |
||
4096 | GError *error; |
||
4097 | GDBusMessage *reply; |
||
4098 | |||
4099 | if (has_object_been_unregistered (data->connection, |
||
4100 | data->registration_id, |
||
4101 | data->subtree_registration_id)) |
||
4102 | { |
||
4103 | reply = g_dbus_message_new_method_error (data->message, |
||
4104 | "org.freedesktop.DBus.Error.UnknownMethod", |
||
4105 | _("No such interface 'org.freedesktop.DBus.Properties' on object at path %s"), |
||
4106 | g_dbus_message_get_path (data->message)); |
||
4107 | g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4108 | g_object_unref (reply); |
||
4109 | goto out; |
||
4110 | } |
||
4111 | |||
4112 | error = NULL; |
||
4113 | value = data->vtable->get_property (data->connection, |
||
4114 | g_dbus_message_get_sender (data->message), |
||
4115 | g_dbus_message_get_path (data->message), |
||
4116 | data->interface_info->name, |
||
4117 | data->property_name, |
||
4118 | &error, |
||
4119 | data->user_data); |
||
4120 | |||
4121 | |||
4122 | if (value != NULL) |
||
4123 | { |
||
4124 | g_assert_no_error (error); |
||
4125 | |||
4126 | g_variant_take_ref (value); |
||
4127 | reply = g_dbus_message_new_method_reply (data->message); |
||
4128 | g_dbus_message_set_body (reply, g_variant_new ("(v)", value)); |
||
4129 | g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4130 | g_variant_unref (value); |
||
4131 | g_object_unref (reply); |
||
4132 | } |
||
4133 | else |
||
4134 | { |
||
4135 | gchar *dbus_error_name; |
||
4136 | g_assert (error != NULL); |
||
4137 | dbus_error_name = g_dbus_error_encode_gerror (error); |
||
4138 | reply = g_dbus_message_new_method_error_literal (data->message, |
||
4139 | dbus_error_name, |
||
4140 | error->message); |
||
4141 | g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4142 | g_free (dbus_error_name); |
||
4143 | g_error_free (error); |
||
4144 | g_object_unref (reply); |
||
4145 | } |
||
4146 | |||
4147 | out: |
||
4148 | return FALSE; |
||
4149 | } |
||
4150 | |||
4151 | /* called in thread where object was registered - no locks held */ |
||
4152 | static gboolean |
||
4153 | invoke_set_property_in_idle_cb (gpointer _data) |
||
4154 | { |
||
4155 | PropertyData *data = _data; |
||
4156 | GError *error; |
||
4157 | GDBusMessage *reply; |
||
4158 | GVariant *value; |
||
4159 | |||
4160 | error = NULL; |
||
4161 | value = NULL; |
||
4162 | |||
4163 | g_variant_get (g_dbus_message_get_body (data->message), |
||
4164 | "(ssv)", |
||
4165 | NULL, |
||
4166 | NULL, |
||
4167 | &value); |
||
4168 | |||
4169 | if (!data->vtable->set_property (data->connection, |
||
4170 | g_dbus_message_get_sender (data->message), |
||
4171 | g_dbus_message_get_path (data->message), |
||
4172 | data->interface_info->name, |
||
4173 | data->property_name, |
||
4174 | value, |
||
4175 | &error, |
||
4176 | data->user_data)) |
||
4177 | { |
||
4178 | gchar *dbus_error_name; |
||
4179 | g_assert (error != NULL); |
||
4180 | dbus_error_name = g_dbus_error_encode_gerror (error); |
||
4181 | reply = g_dbus_message_new_method_error_literal (data->message, |
||
4182 | dbus_error_name, |
||
4183 | error->message); |
||
4184 | g_free (dbus_error_name); |
||
4185 | g_error_free (error); |
||
4186 | } |
||
4187 | else |
||
4188 | { |
||
4189 | reply = g_dbus_message_new_method_reply (data->message); |
||
4190 | } |
||
4191 | |||
4192 | g_assert (reply != NULL); |
||
4193 | g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4194 | g_object_unref (reply); |
||
4195 | g_variant_unref (value); |
||
4196 | |||
4197 | return FALSE; |
||
4198 | } |
||
4199 | |||
4200 | /* called in any thread with connection's lock held */ |
||
4201 | static gboolean |
||
4202 | validate_and_maybe_schedule_property_getset (GDBusConnection *connection, |
||
4203 | GDBusMessage *message, |
||
4204 | guint registration_id, |
||
4205 | guint subtree_registration_id, |
||
4206 | gboolean is_get, |
||
4207 | GDBusInterfaceInfo *interface_info, |
||
4208 | const GDBusInterfaceVTable *vtable, |
||
4209 | GMainContext *main_context, |
||
4210 | gpointer user_data) |
||
4211 | { |
||
4212 | gboolean handled; |
||
4213 | const char *interface_name; |
||
4214 | const char *property_name; |
||
4215 | const GDBusPropertyInfo *property_info; |
||
4216 | GSource *idle_source; |
||
4217 | PropertyData *property_data; |
||
4218 | GDBusMessage *reply; |
||
4219 | |||
4220 | handled = FALSE; |
||
4221 | |||
4222 | if (is_get) |
||
4223 | g_variant_get (g_dbus_message_get_body (message), |
||
4224 | "(&s&s)", |
||
4225 | &interface_name, |
||
4226 | &property_name); |
||
4227 | else |
||
4228 | g_variant_get (g_dbus_message_get_body (message), |
||
4229 | "(&s&sv)", |
||
4230 | &interface_name, |
||
4231 | &property_name, |
||
4232 | NULL); |
||
4233 | |||
4234 | if (vtable == NULL) |
||
4235 | goto out; |
||
4236 | |||
4237 | /* Check that the property exists - if not fail with org.freedesktop.DBus.Error.InvalidArgs |
||
4238 | */ |
||
4239 | property_info = NULL; |
||
4240 | |||
4241 | /* TODO: the cost of this is O(n) - it might be worth caching the result */ |
||
4242 | property_info = g_dbus_interface_info_lookup_property (interface_info, property_name); |
||
4243 | if (property_info == NULL) |
||
4244 | { |
||
4245 | reply = g_dbus_message_new_method_error (message, |
||
4246 | "org.freedesktop.DBus.Error.InvalidArgs", |
||
4247 | _("No such property '%s'"), |
||
4248 | property_name); |
||
4249 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4250 | g_object_unref (reply); |
||
4251 | handled = TRUE; |
||
4252 | goto out; |
||
4253 | } |
||
4254 | |||
4255 | if (is_get && !(property_info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)) |
||
4256 | { |
||
4257 | reply = g_dbus_message_new_method_error (message, |
||
4258 | "org.freedesktop.DBus.Error.InvalidArgs", |
||
4259 | _("Property '%s' is not readable"), |
||
4260 | property_name); |
||
4261 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4262 | g_object_unref (reply); |
||
4263 | handled = TRUE; |
||
4264 | goto out; |
||
4265 | } |
||
4266 | else if (!is_get && !(property_info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)) |
||
4267 | { |
||
4268 | reply = g_dbus_message_new_method_error (message, |
||
4269 | "org.freedesktop.DBus.Error.InvalidArgs", |
||
4270 | _("Property '%s' is not writable"), |
||
4271 | property_name); |
||
4272 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4273 | g_object_unref (reply); |
||
4274 | handled = TRUE; |
||
4275 | goto out; |
||
4276 | } |
||
4277 | |||
4278 | if (!is_get) |
||
4279 | { |
||
4280 | GVariant *value; |
||
4281 | |||
4282 | /* Fail with org.freedesktop.DBus.Error.InvalidArgs if the type |
||
4283 | * of the given value is wrong |
||
4284 | */ |
||
4285 | g_variant_get_child (g_dbus_message_get_body (message), 2, "v", &value); |
||
4286 | if (g_strcmp0 (g_variant_get_type_string (value), property_info->signature) != 0) |
||
4287 | { |
||
4288 | reply = g_dbus_message_new_method_error (message, |
||
4289 | "org.freedesktop.DBus.Error.InvalidArgs", |
||
4290 | _("Error setting property '%s': Expected type '%s' but got '%s'"), |
||
4291 | property_name, property_info->signature, |
||
4292 | g_variant_get_type_string (value)); |
||
4293 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4294 | g_variant_unref (value); |
||
4295 | g_object_unref (reply); |
||
4296 | handled = TRUE; |
||
4297 | goto out; |
||
4298 | } |
||
4299 | |||
4300 | g_variant_unref (value); |
||
4301 | } |
||
4302 | |||
4303 | /* If the vtable pointer for get_property() resp. set_property() is |
||
4304 | * NULL then dispatch the call via the method_call() handler. |
||
4305 | */ |
||
4306 | if (is_get) |
||
4307 | { |
||
4308 | if (vtable->get_property == NULL) |
||
4309 | { |
||
4310 | schedule_method_call (connection, message, registration_id, subtree_registration_id, |
||
4311 | interface_info, NULL, property_info, g_dbus_message_get_body (message), |
||
4312 | vtable, main_context, user_data); |
||
4313 | handled = TRUE; |
||
4314 | goto out; |
||
4315 | } |
||
4316 | } |
||
4317 | else |
||
4318 | { |
||
4319 | if (vtable->set_property == NULL) |
||
4320 | { |
||
4321 | schedule_method_call (connection, message, registration_id, subtree_registration_id, |
||
4322 | interface_info, NULL, property_info, g_dbus_message_get_body (message), |
||
4323 | vtable, main_context, user_data); |
||
4324 | handled = TRUE; |
||
4325 | goto out; |
||
4326 | } |
||
4327 | } |
||
4328 | |||
4329 | /* ok, got the property info - call user code in an idle handler */ |
||
4330 | property_data = g_new0 (PropertyData, 1); |
||
4331 | property_data->connection = g_object_ref (connection); |
||
4332 | property_data->message = g_object_ref (message); |
||
4333 | property_data->user_data = user_data; |
||
4334 | property_data->property_name = property_name; |
||
4335 | property_data->vtable = vtable; |
||
4336 | property_data->interface_info = interface_info; |
||
4337 | property_data->property_info = property_info; |
||
4338 | property_data->registration_id = registration_id; |
||
4339 | property_data->subtree_registration_id = subtree_registration_id; |
||
4340 | |||
4341 | idle_source = g_idle_source_new (); |
||
4342 | g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); |
||
4343 | g_source_set_callback (idle_source, |
||
4344 | is_get ? invoke_get_property_in_idle_cb : invoke_set_property_in_idle_cb, |
||
4345 | property_data, |
||
4346 | (GDestroyNotify) property_data_free); |
||
4347 | if (is_get) |
||
4348 | g_source_set_name (idle_source, "[gio] invoke_get_property_in_idle_cb"); |
||
4349 | else |
||
4350 | g_source_set_name (idle_source, "[gio] invoke_set_property_in_idle_cb"); |
||
4351 | g_source_attach (idle_source, main_context); |
||
4352 | g_source_unref (idle_source); |
||
4353 | |||
4354 | handled = TRUE; |
||
4355 | |||
4356 | out: |
||
4357 | return handled; |
||
4358 | } |
||
4359 | |||
4360 | /* called in GDBusWorker thread with connection's lock held */ |
||
4361 | static gboolean |
||
4362 | handle_getset_property (GDBusConnection *connection, |
||
4363 | ExportedObject *eo, |
||
4364 | GDBusMessage *message, |
||
4365 | gboolean is_get) |
||
4366 | { |
||
4367 | ExportedInterface *ei; |
||
4368 | gboolean handled; |
||
4369 | const char *interface_name; |
||
4370 | const char *property_name; |
||
4371 | |||
4372 | handled = FALSE; |
||
4373 | |||
4374 | if (is_get) |
||
4375 | g_variant_get (g_dbus_message_get_body (message), |
||
4376 | "(&s&s)", |
||
4377 | &interface_name, |
||
4378 | &property_name); |
||
4379 | else |
||
4380 | g_variant_get (g_dbus_message_get_body (message), |
||
4381 | "(&s&sv)", |
||
4382 | &interface_name, |
||
4383 | &property_name, |
||
4384 | NULL); |
||
4385 | |||
4386 | /* Fail with org.freedesktop.DBus.Error.InvalidArgs if there is |
||
4387 | * no such interface registered |
||
4388 | */ |
||
4389 | ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_name); |
||
4390 | if (ei == NULL) |
||
4391 | { |
||
4392 | GDBusMessage *reply; |
||
4393 | reply = g_dbus_message_new_method_error (message, |
||
4394 | "org.freedesktop.DBus.Error.InvalidArgs", |
||
4395 | _("No such interface '%s'"), |
||
4396 | interface_name); |
||
4397 | g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4398 | g_object_unref (reply); |
||
4399 | handled = TRUE; |
||
4400 | goto out; |
||
4401 | } |
||
4402 | |||
4403 | handled = validate_and_maybe_schedule_property_getset (eo->connection, |
||
4404 | message, |
||
4405 | ei->id, |
||
4406 | 0, |
||
4407 | is_get, |
||
4408 | ei->interface_info, |
||
4409 | ei->vtable, |
||
4410 | ei->context, |
||
4411 | ei->user_data); |
||
4412 | out: |
||
4413 | return handled; |
||
4414 | } |
||
4415 | |||
4416 | /* ---------------------------------------------------------------------------------------------------- */ |
||
4417 | |||
4418 | typedef struct |
||
4419 | { |
||
4420 | GDBusConnection *connection; |
||
4421 | GDBusMessage *message; |
||
4422 | gpointer user_data; |
||
4423 | const GDBusInterfaceVTable *vtable; |
||
4424 | GDBusInterfaceInfo *interface_info; |
||
4425 | guint registration_id; |
||
4426 | guint subtree_registration_id; |
||
4427 | } PropertyGetAllData; |
||
4428 | |||
4429 | static void |
||
4430 | property_get_all_data_free (PropertyData *data) |
||
4431 | { |
||
4432 | g_object_unref (data->connection); |
||
4433 | g_object_unref (data->message); |
||
4434 | g_free (data); |
||
4435 | } |
||
4436 | |||
4437 | /* called in thread where object was registered - no locks held */ |
||
4438 | static gboolean |
||
4439 | invoke_get_all_properties_in_idle_cb (gpointer _data) |
||
4440 | { |
||
4441 | PropertyGetAllData *data = _data; |
||
4442 | GVariantBuilder builder; |
||
4443 | GDBusMessage *reply; |
||
4444 | guint n; |
||
4445 | |||
4446 | if (has_object_been_unregistered (data->connection, |
||
4447 | data->registration_id, |
||
4448 | data->subtree_registration_id)) |
||
4449 | { |
||
4450 | reply = g_dbus_message_new_method_error (data->message, |
||
4451 | "org.freedesktop.DBus.Error.UnknownMethod", |
||
4452 | _("No such interface 'org.freedesktop.DBus.Properties' on object at path %s"), |
||
4453 | g_dbus_message_get_path (data->message)); |
||
4454 | g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4455 | g_object_unref (reply); |
||
4456 | goto out; |
||
4457 | } |
||
4458 | |||
4459 | /* TODO: Right now we never fail this call - we just omit values if |
||
4460 | * a get_property() call is failing. |
||
4461 | * |
||
4462 | * We could fail the whole call if just a single get_property() call |
||
4463 | * returns an error. We need clarification in the D-Bus spec about this. |
||
4464 | */ |
||
4465 | g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a{sv})")); |
||
4466 | g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}")); |
||
4467 | for (n = 0; data->interface_info->properties != NULL && data->interface_info->properties[n] != NULL; n++) |
||
4468 | { |
||
4469 | const GDBusPropertyInfo *property_info = data->interface_info->properties[n]; |
||
4470 | GVariant *value; |
||
4471 | |||
4472 | if (!(property_info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)) |
||
4473 | continue; |
||
4474 | |||
4475 | value = data->vtable->get_property (data->connection, |
||
4476 | g_dbus_message_get_sender (data->message), |
||
4477 | g_dbus_message_get_path (data->message), |
||
4478 | data->interface_info->name, |
||
4479 | property_info->name, |
||
4480 | NULL, |
||
4481 | data->user_data); |
||
4482 | |||
4483 | if (value == NULL) |
||
4484 | continue; |
||
4485 | |||
4486 | g_variant_take_ref (value); |
||
4487 | g_variant_builder_add (&builder, |
||
4488 | "{sv}", |
||
4489 | property_info->name, |
||
4490 | value); |
||
4491 | g_variant_unref (value); |
||
4492 | } |
||
4493 | g_variant_builder_close (&builder); |
||
4494 | |||
4495 | reply = g_dbus_message_new_method_reply (data->message); |
||
4496 | g_dbus_message_set_body (reply, g_variant_builder_end (&builder)); |
||
4497 | g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4498 | g_object_unref (reply); |
||
4499 | |||
4500 | out: |
||
4501 | return FALSE; |
||
4502 | } |
||
4503 | |||
4504 | static gboolean |
||
4505 | interface_has_readable_properties (GDBusInterfaceInfo *interface_info) |
||
4506 | { |
||
4507 | gint i; |
||
4508 | |||
4509 | if (!interface_info->properties) |
||
4510 | return FALSE; |
||
4511 | |||
4512 | for (i = 0; interface_info->properties[i]; i++) |
||
4513 | if (interface_info->properties[i]->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) |
||
4514 | return TRUE; |
||
4515 | |||
4516 | return FALSE; |
||
4517 | } |
||
4518 | |||
4519 | /* called in any thread with connection's lock held */ |
||
4520 | static gboolean |
||
4521 | validate_and_maybe_schedule_property_get_all (GDBusConnection *connection, |
||
4522 | GDBusMessage *message, |
||
4523 | guint registration_id, |
||
4524 | guint subtree_registration_id, |
||
4525 | GDBusInterfaceInfo *interface_info, |
||
4526 | const GDBusInterfaceVTable *vtable, |
||
4527 | GMainContext *main_context, |
||
4528 | gpointer user_data) |
||
4529 | { |
||
4530 | gboolean handled; |
||
4531 | GSource *idle_source; |
||
4532 | PropertyGetAllData *property_get_all_data; |
||
4533 | |||
4534 | handled = FALSE; |
||
4535 | |||
4536 | if (vtable == NULL) |
||
4537 | goto out; |
||
4538 | |||
4539 | /* If the vtable pointer for get_property() is NULL but we have a |
||
4540 | * non-zero number of readable properties, then dispatch the call via |
||
4541 | * the method_call() handler. |
||
4542 | */ |
||
4543 | if (vtable->get_property == NULL && interface_has_readable_properties (interface_info)) |
||
4544 | { |
||
4545 | schedule_method_call (connection, message, registration_id, subtree_registration_id, |
||
4546 | interface_info, NULL, NULL, g_dbus_message_get_body (message), |
||
4547 | vtable, main_context, user_data); |
||
4548 | handled = TRUE; |
||
4549 | goto out; |
||
4550 | } |
||
4551 | |||
4552 | /* ok, got the property info - call user in an idle handler */ |
||
4553 | property_get_all_data = g_new0 (PropertyGetAllData, 1); |
||
4554 | property_get_all_data->connection = g_object_ref (connection); |
||
4555 | property_get_all_data->message = g_object_ref (message); |
||
4556 | property_get_all_data->user_data = user_data; |
||
4557 | property_get_all_data->vtable = vtable; |
||
4558 | property_get_all_data->interface_info = interface_info; |
||
4559 | property_get_all_data->registration_id = registration_id; |
||
4560 | property_get_all_data->subtree_registration_id = subtree_registration_id; |
||
4561 | |||
4562 | idle_source = g_idle_source_new (); |
||
4563 | g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); |
||
4564 | g_source_set_callback (idle_source, |
||
4565 | invoke_get_all_properties_in_idle_cb, |
||
4566 | property_get_all_data, |
||
4567 | (GDestroyNotify) property_get_all_data_free); |
||
4568 | g_source_set_name (idle_source, "[gio] invoke_get_all_properties_in_idle_cb"); |
||
4569 | g_source_attach (idle_source, main_context); |
||
4570 | g_source_unref (idle_source); |
||
4571 | |||
4572 | handled = TRUE; |
||
4573 | |||
4574 | out: |
||
4575 | return handled; |
||
4576 | } |
||
4577 | |||
4578 | /* called in GDBusWorker thread with connection's lock held */ |
||
4579 | static gboolean |
||
4580 | handle_get_all_properties (GDBusConnection *connection, |
||
4581 | ExportedObject *eo, |
||
4582 | GDBusMessage *message) |
||
4583 | { |
||
4584 | ExportedInterface *ei; |
||
4585 | gboolean handled; |
||
4586 | const char *interface_name; |
||
4587 | |||
4588 | handled = FALSE; |
||
4589 | |||
4590 | g_variant_get (g_dbus_message_get_body (message), |
||
4591 | "(&s)", |
||
4592 | &interface_name); |
||
4593 | |||
4594 | /* Fail with org.freedesktop.DBus.Error.InvalidArgs if there is |
||
4595 | * no such interface registered |
||
4596 | */ |
||
4597 | ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_name); |
||
4598 | if (ei == NULL) |
||
4599 | { |
||
4600 | GDBusMessage *reply; |
||
4601 | reply = g_dbus_message_new_method_error (message, |
||
4602 | "org.freedesktop.DBus.Error.InvalidArgs", |
||
4603 | _("No such interface"), |
||
4604 | interface_name); |
||
4605 | g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4606 | g_object_unref (reply); |
||
4607 | handled = TRUE; |
||
4608 | goto out; |
||
4609 | } |
||
4610 | |||
4611 | handled = validate_and_maybe_schedule_property_get_all (eo->connection, |
||
4612 | message, |
||
4613 | ei->id, |
||
4614 | 0, |
||
4615 | ei->interface_info, |
||
4616 | ei->vtable, |
||
4617 | ei->context, |
||
4618 | ei->user_data); |
||
4619 | out: |
||
4620 | return handled; |
||
4621 | } |
||
4622 | |||
4623 | /* ---------------------------------------------------------------------------------------------------- */ |
||
4624 | |||
4625 | static const gchar introspect_header[] = |
||
4626 | "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" |
||
4627 | " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" |
||
4628 | "<!-- GDBus " PACKAGE_VERSION " -->\n" |
||
4629 | "<node>\n"; |
||
4630 | |||
4631 | static const gchar introspect_tail[] = |
||
4632 | "</node>\n"; |
||
4633 | |||
4634 | static const gchar introspect_properties_interface[] = |
||
4635 | " <interface name=\"org.freedesktop.DBus.Properties\">\n" |
||
4636 | " <method name=\"Get\">\n" |
||
4637 | " <arg type=\"s\" name=\"interface_name\" direction=\"in\"/>\n" |
||
4638 | " <arg type=\"s\" name=\"property_name\" direction=\"in\"/>\n" |
||
4639 | " <arg type=\"v\" name=\"value\" direction=\"out\"/>\n" |
||
4640 | " </method>\n" |
||
4641 | " <method name=\"GetAll\">\n" |
||
4642 | " <arg type=\"s\" name=\"interface_name\" direction=\"in\"/>\n" |
||
4643 | " <arg type=\"a{sv}\" name=\"properties\" direction=\"out\"/>\n" |
||
4644 | " </method>\n" |
||
4645 | " <method name=\"Set\">\n" |
||
4646 | " <arg type=\"s\" name=\"interface_name\" direction=\"in\"/>\n" |
||
4647 | " <arg type=\"s\" name=\"property_name\" direction=\"in\"/>\n" |
||
4648 | " <arg type=\"v\" name=\"value\" direction=\"in\"/>\n" |
||
4649 | " </method>\n" |
||
4650 | " <signal name=\"PropertiesChanged\">\n" |
||
4651 | " <arg type=\"s\" name=\"interface_name\"/>\n" |
||
4652 | " <arg type=\"a{sv}\" name=\"changed_properties\"/>\n" |
||
4653 | " <arg type=\"as\" name=\"invalidated_properties\"/>\n" |
||
4654 | " </signal>\n" |
||
4655 | " </interface>\n"; |
||
4656 | |||
4657 | static const gchar introspect_introspectable_interface[] = |
||
4658 | " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" |
||
4659 | " <method name=\"Introspect\">\n" |
||
4660 | " <arg type=\"s\" name=\"xml_data\" direction=\"out\"/>\n" |
||
4661 | " </method>\n" |
||
4662 | " </interface>\n" |
||
4663 | " <interface name=\"org.freedesktop.DBus.Peer\">\n" |
||
4664 | " <method name=\"Ping\"/>\n" |
||
4665 | " <method name=\"GetMachineId\">\n" |
||
4666 | " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" |
||
4667 | " </method>\n" |
||
4668 | " </interface>\n"; |
||
4669 | |||
4670 | static void |
||
4671 | introspect_append_header (GString *s) |
||
4672 | { |
||
4673 | g_string_append (s, introspect_header); |
||
4674 | } |
||
4675 | |||
4676 | static void |
||
4677 | maybe_add_path (const gchar *path, gsize path_len, const gchar *object_path, GHashTable *set) |
||
4678 | { |
||
4679 | if (g_str_has_prefix (object_path, path) && strlen (object_path) > path_len && object_path[path_len-1] == '/') |
||
4680 | { |
||
4681 | const gchar *begin; |
||
4682 | const gchar *end; |
||
4683 | gchar *s; |
||
4684 | |||
4685 | begin = object_path + path_len; |
||
4686 | end = strchr (begin, '/'); |
||
4687 | if (end != NULL) |
||
4688 | s = g_strndup (begin, end - begin); |
||
4689 | else |
||
4690 | s = g_strdup (begin); |
||
4691 | |||
4692 | if (g_hash_table_lookup (set, s) == NULL) |
||
4693 | g_hash_table_insert (set, s, GUINT_TO_POINTER (1)); |
||
4694 | else |
||
4695 | g_free (s); |
||
4696 | } |
||
4697 | } |
||
4698 | |||
4699 | /* TODO: we want a nicer public interface for this */ |
||
4700 | /* called in any thread with connection's lock held */ |
||
4701 | static gchar ** |
||
4702 | g_dbus_connection_list_registered_unlocked (GDBusConnection *connection, |
||
4703 | const gchar *path) |
||
4704 | { |
||
4705 | GPtrArray *p; |
||
4706 | gchar **ret; |
||
4707 | GHashTableIter hash_iter; |
||
4708 | const gchar *object_path; |
||
4709 | gsize path_len; |
||
4710 | GHashTable *set; |
||
4711 | GList *keys; |
||
4712 | GList *l; |
||
4713 | |||
4714 | CONNECTION_ENSURE_LOCK (connection); |
||
4715 | |||
4716 | path_len = strlen (path); |
||
4717 | if (path_len > 1) |
||
4718 | path_len++; |
||
4719 | |||
4720 | set = g_hash_table_new (g_str_hash, g_str_equal); |
||
4721 | |||
4722 | g_hash_table_iter_init (&hash_iter, connection->map_object_path_to_eo); |
||
4723 | while (g_hash_table_iter_next (&hash_iter, (gpointer) &object_path, NULL)) |
||
4724 | maybe_add_path (path, path_len, object_path, set); |
||
4725 | |||
4726 | g_hash_table_iter_init (&hash_iter, connection->map_object_path_to_es); |
||
4727 | while (g_hash_table_iter_next (&hash_iter, (gpointer) &object_path, NULL)) |
||
4728 | maybe_add_path (path, path_len, object_path, set); |
||
4729 | |||
4730 | p = g_ptr_array_new (); |
||
4731 | keys = g_hash_table_get_keys (set); |
||
4732 | for (l = keys; l != NULL; l = l->next) |
||
4733 | g_ptr_array_add (p, l->data); |
||
4734 | g_hash_table_unref (set); |
||
4735 | g_list_free (keys); |
||
4736 | |||
4737 | g_ptr_array_add (p, NULL); |
||
4738 | ret = (gchar **) g_ptr_array_free (p, FALSE); |
||
4739 | return ret; |
||
4740 | } |
||
4741 | |||
4742 | /* called in any thread with connection's lock not held */ |
||
4743 | static gchar ** |
||
4744 | g_dbus_connection_list_registered (GDBusConnection *connection, |
||
4745 | const gchar *path) |
||
4746 | { |
||
4747 | gchar **ret; |
||
4748 | CONNECTION_LOCK (connection); |
||
4749 | ret = g_dbus_connection_list_registered_unlocked (connection, path); |
||
4750 | CONNECTION_UNLOCK (connection); |
||
4751 | return ret; |
||
4752 | } |
||
4753 | |||
4754 | /* called in GDBusWorker thread with connection's lock held */ |
||
4755 | static gboolean |
||
4756 | handle_introspect (GDBusConnection *connection, |
||
4757 | ExportedObject *eo, |
||
4758 | GDBusMessage *message) |
||
4759 | { |
||
4760 | guint n; |
||
4761 | GString *s; |
||
4762 | GDBusMessage *reply; |
||
4763 | GHashTableIter hash_iter; |
||
4764 | ExportedInterface *ei; |
||
4765 | gchar **registered; |
||
4766 | |||
4767 | /* first the header with the standard interfaces */ |
||
4768 | s = g_string_sized_new (sizeof (introspect_header) + |
||
4769 | sizeof (introspect_properties_interface) + |
||
4770 | sizeof (introspect_introspectable_interface) + |
||
4771 | sizeof (introspect_tail)); |
||
4772 | introspect_append_header (s); |
||
4773 | if (!g_hash_table_lookup (eo->map_if_name_to_ei, |
||
4774 | "org.freedesktop.DBus.Properties")) |
||
4775 | g_string_append (s, introspect_properties_interface); |
||
4776 | |||
4777 | if (!g_hash_table_lookup (eo->map_if_name_to_ei, |
||
4778 | "org.freedesktop.DBus.Introspectable")) |
||
4779 | g_string_append (s, introspect_introspectable_interface); |
||
4780 | |||
4781 | /* then include the registered interfaces */ |
||
4782 | g_hash_table_iter_init (&hash_iter, eo->map_if_name_to_ei); |
||
4783 | while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &ei)) |
||
4784 | g_dbus_interface_info_generate_xml (ei->interface_info, 2, s); |
||
4785 | |||
4786 | /* finally include nodes registered below us */ |
||
4787 | registered = g_dbus_connection_list_registered_unlocked (connection, eo->object_path); |
||
4788 | for (n = 0; registered != NULL && registered[n] != NULL; n++) |
||
4789 | g_string_append_printf (s, " <node name=\"%s\"/>\n", registered[n]); |
||
4790 | g_strfreev (registered); |
||
4791 | g_string_append (s, introspect_tail); |
||
4792 | |||
4793 | reply = g_dbus_message_new_method_reply (message); |
||
4794 | g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); |
||
4795 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4796 | g_object_unref (reply); |
||
4797 | g_string_free (s, TRUE); |
||
4798 | |||
4799 | return TRUE; |
||
4800 | } |
||
4801 | |||
4802 | /* called in thread where object was registered - no locks held */ |
||
4803 | static gboolean |
||
4804 | call_in_idle_cb (gpointer user_data) |
||
4805 | { |
||
4806 | GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data); |
||
4807 | GDBusInterfaceVTable *vtable; |
||
4808 | guint registration_id; |
||
4809 | guint subtree_registration_id; |
||
4810 | |||
4811 | registration_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (invocation), "g-dbus-registration-id")); |
||
4812 | subtree_registration_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (invocation), "g-dbus-subtree-registration-id")); |
||
4813 | |||
4814 | if (has_object_been_unregistered (g_dbus_method_invocation_get_connection (invocation), |
||
4815 | registration_id, |
||
4816 | subtree_registration_id)) |
||
4817 | { |
||
4818 | GDBusMessage *reply; |
||
4819 | reply = g_dbus_message_new_method_error (g_dbus_method_invocation_get_message (invocation), |
||
4820 | "org.freedesktop.DBus.Error.UnknownMethod", |
||
4821 | _("No such interface '%s' on object at path %s"), |
||
4822 | g_dbus_method_invocation_get_interface_name (invocation), |
||
4823 | g_dbus_method_invocation_get_object_path (invocation)); |
||
4824 | g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4825 | g_object_unref (reply); |
||
4826 | goto out; |
||
4827 | } |
||
4828 | |||
4829 | vtable = g_object_get_data (G_OBJECT (invocation), "g-dbus-interface-vtable"); |
||
4830 | g_assert (vtable != NULL && vtable->method_call != NULL); |
||
4831 | |||
4832 | vtable->method_call (g_dbus_method_invocation_get_connection (invocation), |
||
4833 | g_dbus_method_invocation_get_sender (invocation), |
||
4834 | g_dbus_method_invocation_get_object_path (invocation), |
||
4835 | g_dbus_method_invocation_get_interface_name (invocation), |
||
4836 | g_dbus_method_invocation_get_method_name (invocation), |
||
4837 | g_dbus_method_invocation_get_parameters (invocation), |
||
4838 | g_object_ref (invocation), |
||
4839 | g_dbus_method_invocation_get_user_data (invocation)); |
||
4840 | |||
4841 | out: |
||
4842 | return FALSE; |
||
4843 | } |
||
4844 | |||
4845 | /* called in GDBusWorker thread with connection's lock held */ |
||
4846 | static void |
||
4847 | schedule_method_call (GDBusConnection *connection, |
||
4848 | GDBusMessage *message, |
||
4849 | guint registration_id, |
||
4850 | guint subtree_registration_id, |
||
4851 | const GDBusInterfaceInfo *interface_info, |
||
4852 | const GDBusMethodInfo *method_info, |
||
4853 | const GDBusPropertyInfo *property_info, |
||
4854 | GVariant *parameters, |
||
4855 | const GDBusInterfaceVTable *vtable, |
||
4856 | GMainContext *main_context, |
||
4857 | gpointer user_data) |
||
4858 | { |
||
4859 | GDBusMethodInvocation *invocation; |
||
4860 | GSource *idle_source; |
||
4861 | |||
4862 | invocation = _g_dbus_method_invocation_new (g_dbus_message_get_sender (message), |
||
4863 | g_dbus_message_get_path (message), |
||
4864 | g_dbus_message_get_interface (message), |
||
4865 | g_dbus_message_get_member (message), |
||
4866 | method_info, |
||
4867 | property_info, |
||
4868 | connection, |
||
4869 | message, |
||
4870 | parameters, |
||
4871 | user_data); |
||
4872 | |||
4873 | /* TODO: would be nicer with a real MethodData like we already |
||
4874 | * have PropertyData and PropertyGetAllData... */ |
||
4875 | g_object_set_data (G_OBJECT (invocation), "g-dbus-interface-vtable", (gpointer) vtable); |
||
4876 | g_object_set_data (G_OBJECT (invocation), "g-dbus-registration-id", GUINT_TO_POINTER (registration_id)); |
||
4877 | g_object_set_data (G_OBJECT (invocation), "g-dbus-subtree-registration-id", GUINT_TO_POINTER (subtree_registration_id)); |
||
4878 | |||
4879 | idle_source = g_idle_source_new (); |
||
4880 | g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); |
||
4881 | g_source_set_callback (idle_source, |
||
4882 | call_in_idle_cb, |
||
4883 | invocation, |
||
4884 | g_object_unref); |
||
4885 | g_source_set_name (idle_source, "[gio] call_in_idle_cb"); |
||
4886 | g_source_attach (idle_source, main_context); |
||
4887 | g_source_unref (idle_source); |
||
4888 | } |
||
4889 | |||
4890 | /* called in GDBusWorker thread with connection's lock held */ |
||
4891 | static gboolean |
||
4892 | validate_and_maybe_schedule_method_call (GDBusConnection *connection, |
||
4893 | GDBusMessage *message, |
||
4894 | guint registration_id, |
||
4895 | guint subtree_registration_id, |
||
4896 | GDBusInterfaceInfo *interface_info, |
||
4897 | const GDBusInterfaceVTable *vtable, |
||
4898 | GMainContext *main_context, |
||
4899 | gpointer user_data) |
||
4900 | { |
||
4901 | GDBusMethodInfo *method_info; |
||
4902 | GDBusMessage *reply; |
||
4903 | GVariant *parameters; |
||
4904 | gboolean handled; |
||
4905 | GVariantType *in_type; |
||
4906 | |||
4907 | handled = FALSE; |
||
4908 | |||
4909 | /* TODO: the cost of this is O(n) - it might be worth caching the result */ |
||
4910 | method_info = g_dbus_interface_info_lookup_method (interface_info, g_dbus_message_get_member (message)); |
||
4911 | |||
4912 | /* if the method doesn't exist, return the org.freedesktop.DBus.Error.UnknownMethod |
||
4913 | * error to the caller |
||
4914 | */ |
||
4915 | if (method_info == NULL) |
||
4916 | { |
||
4917 | reply = g_dbus_message_new_method_error (message, |
||
4918 | "org.freedesktop.DBus.Error.UnknownMethod", |
||
4919 | _("No such method '%s'"), |
||
4920 | g_dbus_message_get_member (message)); |
||
4921 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4922 | g_object_unref (reply); |
||
4923 | handled = TRUE; |
||
4924 | goto out; |
||
4925 | } |
||
4926 | |||
4927 | parameters = g_dbus_message_get_body (message); |
||
4928 | if (parameters == NULL) |
||
4929 | { |
||
4930 | parameters = g_variant_new ("()"); |
||
4931 | g_variant_ref_sink (parameters); |
||
4932 | } |
||
4933 | else |
||
4934 | { |
||
4935 | g_variant_ref (parameters); |
||
4936 | } |
||
4937 | |||
4938 | /* Check that the incoming args are of the right type - if they are not, return |
||
4939 | * the org.freedesktop.DBus.Error.InvalidArgs error to the caller |
||
4940 | */ |
||
4941 | in_type = _g_dbus_compute_complete_signature (method_info->in_args); |
||
4942 | if (!g_variant_is_of_type (parameters, in_type)) |
||
4943 | { |
||
4944 | gchar *type_string; |
||
4945 | |||
4946 | type_string = g_variant_type_dup_string (in_type); |
||
4947 | |||
4948 | reply = g_dbus_message_new_method_error (message, |
||
4949 | "org.freedesktop.DBus.Error.InvalidArgs", |
||
4950 | _("Type of message, '%s', does not match expected type '%s'"), |
||
4951 | g_variant_get_type_string (parameters), |
||
4952 | type_string); |
||
4953 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
4954 | g_variant_type_free (in_type); |
||
4955 | g_variant_unref (parameters); |
||
4956 | g_object_unref (reply); |
||
4957 | g_free (type_string); |
||
4958 | handled = TRUE; |
||
4959 | goto out; |
||
4960 | } |
||
4961 | g_variant_type_free (in_type); |
||
4962 | |||
4963 | /* schedule the call in idle */ |
||
4964 | schedule_method_call (connection, message, registration_id, subtree_registration_id, |
||
4965 | interface_info, method_info, NULL, parameters, |
||
4966 | vtable, main_context, user_data); |
||
4967 | g_variant_unref (parameters); |
||
4968 | handled = TRUE; |
||
4969 | |||
4970 | out: |
||
4971 | return handled; |
||
4972 | } |
||
4973 | |||
4974 | /* ---------------------------------------------------------------------------------------------------- */ |
||
4975 | |||
4976 | /* called in GDBusWorker thread with connection's lock held */ |
||
4977 | static gboolean |
||
4978 | obj_message_func (GDBusConnection *connection, |
||
4979 | ExportedObject *eo, |
||
4980 | GDBusMessage *message) |
||
4981 | { |
||
4982 | const gchar *interface_name; |
||
4983 | const gchar *member; |
||
4984 | const gchar *signature; |
||
4985 | gboolean handled; |
||
4986 | |||
4987 | handled = FALSE; |
||
4988 | |||
4989 | interface_name = g_dbus_message_get_interface (message); |
||
4990 | member = g_dbus_message_get_member (message); |
||
4991 | signature = g_dbus_message_get_signature (message); |
||
4992 | |||
4993 | /* see if we have an interface for handling this call */ |
||
4994 | if (interface_name != NULL) |
||
4995 | { |
||
4996 | ExportedInterface *ei; |
||
4997 | ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_name); |
||
4998 | if (ei != NULL) |
||
4999 | { |
||
5000 | /* we do - invoke the handler in idle in the right thread */ |
||
5001 | |||
5002 | /* handle no vtable or handler being present */ |
||
5003 | if (ei->vtable == NULL || ei->vtable->method_call == NULL) |
||
5004 | goto out; |
||
5005 | |||
5006 | handled = validate_and_maybe_schedule_method_call (connection, |
||
5007 | message, |
||
5008 | ei->id, |
||
5009 | 0, |
||
5010 | ei->interface_info, |
||
5011 | ei->vtable, |
||
5012 | ei->context, |
||
5013 | ei->user_data); |
||
5014 | goto out; |
||
5015 | } |
||
5016 | } |
||
5017 | |||
5018 | if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Introspectable") == 0 && |
||
5019 | g_strcmp0 (member, "Introspect") == 0 && |
||
5020 | g_strcmp0 (signature, "") == 0) |
||
5021 | { |
||
5022 | handled = handle_introspect (connection, eo, message); |
||
5023 | goto out; |
||
5024 | } |
||
5025 | else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0 && |
||
5026 | g_strcmp0 (member, "Get") == 0 && |
||
5027 | g_strcmp0 (signature, "ss") == 0) |
||
5028 | { |
||
5029 | handled = handle_getset_property (connection, eo, message, TRUE); |
||
5030 | goto out; |
||
5031 | } |
||
5032 | else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0 && |
||
5033 | g_strcmp0 (member, "Set") == 0 && |
||
5034 | g_strcmp0 (signature, "ssv") == 0) |
||
5035 | { |
||
5036 | handled = handle_getset_property (connection, eo, message, FALSE); |
||
5037 | goto out; |
||
5038 | } |
||
5039 | else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0 && |
||
5040 | g_strcmp0 (member, "GetAll") == 0 && |
||
5041 | g_strcmp0 (signature, "s") == 0) |
||
5042 | { |
||
5043 | handled = handle_get_all_properties (connection, eo, message); |
||
5044 | goto out; |
||
5045 | } |
||
5046 | |||
5047 | out: |
||
5048 | return handled; |
||
5049 | } |
||
5050 | |||
5051 | /** |
||
5052 | * g_dbus_connection_register_object: |
||
5053 | * @connection: a #GDBusConnection |
||
5054 | * @object_path: the object path to register at |
||
5055 | * @interface_info: introspection data for the interface |
||
5056 | * @vtable: (allow-none): a #GDBusInterfaceVTable to call into or %NULL |
||
5057 | * @user_data: (allow-none): data to pass to functions in @vtable |
||
5058 | * @user_data_free_func: function to call when the object path is unregistered |
||
5059 | * @error: return location for error or %NULL |
||
5060 | * |
||
5061 | * Registers callbacks for exported objects at @object_path with the |
||
5062 | * D-Bus interface that is described in @interface_info. |
||
5063 | * |
||
5064 | * Calls to functions in @vtable (and @user_data_free_func) will happen |
||
5065 | * in the |
||
5066 | * [thread-default main context][g-main-context-push-thread-default] |
||
5067 | * of the thread you are calling this method from. |
||
5068 | * |
||
5069 | * Note that all #GVariant values passed to functions in @vtable will match |
||
5070 | * the signature given in @interface_info - if a remote caller passes |
||
5071 | * incorrect values, the `org.freedesktop.DBus.Error.InvalidArgs` |
||
5072 | * is returned to the remote caller. |
||
5073 | * |
||
5074 | * Additionally, if the remote caller attempts to invoke methods or |
||
5075 | * access properties not mentioned in @interface_info the |
||
5076 | * `org.freedesktop.DBus.Error.UnknownMethod` resp. |
||
5077 | * `org.freedesktop.DBus.Error.InvalidArgs` errors |
||
5078 | * are returned to the caller. |
||
5079 | * |
||
5080 | * It is considered a programming error if the |
||
5081 | * #GDBusInterfaceGetPropertyFunc function in @vtable returns a |
||
5082 | * #GVariant of incorrect type. |
||
5083 | * |
||
5084 | * If an existing callback is already registered at @object_path and |
||
5085 | * @interface_name, then @error is set to #G_IO_ERROR_EXISTS. |
||
5086 | * |
||
5087 | * GDBus automatically implements the standard D-Bus interfaces |
||
5088 | * org.freedesktop.DBus.Properties, org.freedesktop.DBus.Introspectable |
||
5089 | * and org.freedesktop.Peer, so you don't have to implement those for the |
||
5090 | * objects you export. You can implement org.freedesktop.DBus.Properties |
||
5091 | * yourself, e.g. to handle getting and setting of properties asynchronously. |
||
5092 | * |
||
5093 | * Note that the reference count on @interface_info will be |
||
5094 | * incremented by 1 (unless allocated statically, e.g. if the |
||
5095 | * reference count is -1, see g_dbus_interface_info_ref()) for as long |
||
5096 | * as the object is exported. Also note that @vtable will be copied. |
||
5097 | * |
||
5098 | * See this [server][gdbus-server] for an example of how to use this method. |
||
5099 | * |
||
5100 | * Returns: 0 if @error is set, otherwise a registration id (never 0) |
||
5101 | * that can be used with g_dbus_connection_unregister_object() |
||
5102 | * |
||
5103 | * Since: 2.26 |
||
5104 | */ |
||
5105 | guint |
||
5106 | g_dbus_connection_register_object (GDBusConnection *connection, |
||
5107 | const gchar *object_path, |
||
5108 | GDBusInterfaceInfo *interface_info, |
||
5109 | const GDBusInterfaceVTable *vtable, |
||
5110 | gpointer user_data, |
||
5111 | GDestroyNotify user_data_free_func, |
||
5112 | GError **error) |
||
5113 | { |
||
5114 | ExportedObject *eo; |
||
5115 | ExportedInterface *ei; |
||
5116 | guint ret; |
||
5117 | |||
5118 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); |
||
5119 | g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), 0); |
||
5120 | g_return_val_if_fail (interface_info != NULL, 0); |
||
5121 | g_return_val_if_fail (g_dbus_is_interface_name (interface_info->name), 0); |
||
5122 | g_return_val_if_fail (error == NULL || *error == NULL, 0); |
||
5123 | g_return_val_if_fail (check_initialized (connection), 0); |
||
5124 | |||
5125 | ret = 0; |
||
5126 | |||
5127 | CONNECTION_LOCK (connection); |
||
5128 | |||
5129 | eo = g_hash_table_lookup (connection->map_object_path_to_eo, object_path); |
||
5130 | if (eo == NULL) |
||
5131 | { |
||
5132 | eo = g_new0 (ExportedObject, 1); |
||
5133 | eo->object_path = g_strdup (object_path); |
||
5134 | eo->connection = connection; |
||
5135 | eo->map_if_name_to_ei = g_hash_table_new_full (g_str_hash, |
||
5136 | g_str_equal, |
||
5137 | NULL, |
||
5138 | (GDestroyNotify) exported_interface_free); |
||
5139 | g_hash_table_insert (connection->map_object_path_to_eo, eo->object_path, eo); |
||
5140 | } |
||
5141 | |||
5142 | ei = g_hash_table_lookup (eo->map_if_name_to_ei, interface_info->name); |
||
5143 | if (ei != NULL) |
||
5144 | { |
||
5145 | g_set_error (error, |
||
5146 | G_IO_ERROR, |
||
5147 | G_IO_ERROR_EXISTS, |
||
5148 | _("An object is already exported for the interface %s at %s"), |
||
5149 | interface_info->name, |
||
5150 | object_path); |
||
5151 | goto out; |
||
5152 | } |
||
5153 | |||
5154 | ei = g_new0 (ExportedInterface, 1); |
||
5155 | ei->id = _global_registration_id++; /* TODO: overflow etc. */ |
||
5156 | ei->eo = eo; |
||
5157 | ei->user_data = user_data; |
||
5158 | ei->user_data_free_func = user_data_free_func; |
||
5159 | ei->vtable = _g_dbus_interface_vtable_copy (vtable); |
||
5160 | ei->interface_info = g_dbus_interface_info_ref (interface_info); |
||
5161 | g_dbus_interface_info_cache_build (ei->interface_info); |
||
5162 | ei->interface_name = g_strdup (interface_info->name); |
||
5163 | ei->context = g_main_context_ref_thread_default (); |
||
5164 | |||
5165 | g_hash_table_insert (eo->map_if_name_to_ei, |
||
5166 | (gpointer) ei->interface_name, |
||
5167 | ei); |
||
5168 | g_hash_table_insert (connection->map_id_to_ei, |
||
5169 | GUINT_TO_POINTER (ei->id), |
||
5170 | ei); |
||
5171 | |||
5172 | ret = ei->id; |
||
5173 | |||
5174 | out: |
||
5175 | CONNECTION_UNLOCK (connection); |
||
5176 | |||
5177 | return ret; |
||
5178 | } |
||
5179 | |||
5180 | /** |
||
5181 | * g_dbus_connection_unregister_object: |
||
5182 | * @connection: a #GDBusConnection |
||
5183 | * @registration_id: a registration id obtained from |
||
5184 | * g_dbus_connection_register_object() |
||
5185 | * |
||
5186 | * Unregisters an object. |
||
5187 | * |
||
5188 | * Returns: %TRUE if the object was unregistered, %FALSE otherwise |
||
5189 | * |
||
5190 | * Since: 2.26 |
||
5191 | */ |
||
5192 | gboolean |
||
5193 | g_dbus_connection_unregister_object (GDBusConnection *connection, |
||
5194 | guint registration_id) |
||
5195 | { |
||
5196 | ExportedInterface *ei; |
||
5197 | ExportedObject *eo; |
||
5198 | gboolean ret; |
||
5199 | |||
5200 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
5201 | g_return_val_if_fail (check_initialized (connection), FALSE); |
||
5202 | |||
5203 | ret = FALSE; |
||
5204 | |||
5205 | CONNECTION_LOCK (connection); |
||
5206 | |||
5207 | ei = g_hash_table_lookup (connection->map_id_to_ei, |
||
5208 | GUINT_TO_POINTER (registration_id)); |
||
5209 | if (ei == NULL) |
||
5210 | goto out; |
||
5211 | |||
5212 | eo = ei->eo; |
||
5213 | |||
5214 | g_warn_if_fail (g_hash_table_remove (connection->map_id_to_ei, GUINT_TO_POINTER (ei->id))); |
||
5215 | g_warn_if_fail (g_hash_table_remove (eo->map_if_name_to_ei, ei->interface_name)); |
||
5216 | /* unregister object path if we have no more exported interfaces */ |
||
5217 | if (g_hash_table_size (eo->map_if_name_to_ei) == 0) |
||
5218 | g_warn_if_fail (g_hash_table_remove (connection->map_object_path_to_eo, |
||
5219 | eo->object_path)); |
||
5220 | |||
5221 | ret = TRUE; |
||
5222 | |||
5223 | out: |
||
5224 | CONNECTION_UNLOCK (connection); |
||
5225 | |||
5226 | return ret; |
||
5227 | } |
||
5228 | |||
5229 | typedef struct { |
||
5230 | GClosure *method_call_closure; |
||
5231 | GClosure *get_property_closure; |
||
5232 | GClosure *set_property_closure; |
||
5233 | } RegisterObjectData; |
||
5234 | |||
5235 | static RegisterObjectData * |
||
5236 | register_object_data_new (GClosure *method_call_closure, |
||
5237 | GClosure *get_property_closure, |
||
5238 | GClosure *set_property_closure) |
||
5239 | { |
||
5240 | RegisterObjectData *data; |
||
5241 | |||
5242 | data = g_new0 (RegisterObjectData, 1); |
||
5243 | |||
5244 | if (method_call_closure != NULL) |
||
5245 | { |
||
5246 | data->method_call_closure = g_closure_ref (method_call_closure); |
||
5247 | g_closure_sink (method_call_closure); |
||
5248 | if (G_CLOSURE_NEEDS_MARSHAL (method_call_closure)) |
||
5249 | g_closure_set_marshal (method_call_closure, g_cclosure_marshal_generic); |
||
5250 | } |
||
5251 | |||
5252 | if (get_property_closure != NULL) |
||
5253 | { |
||
5254 | data->get_property_closure = g_closure_ref (get_property_closure); |
||
5255 | g_closure_sink (get_property_closure); |
||
5256 | if (G_CLOSURE_NEEDS_MARSHAL (get_property_closure)) |
||
5257 | g_closure_set_marshal (get_property_closure, g_cclosure_marshal_generic); |
||
5258 | } |
||
5259 | |||
5260 | if (set_property_closure != NULL) |
||
5261 | { |
||
5262 | data->set_property_closure = g_closure_ref (set_property_closure); |
||
5263 | g_closure_sink (set_property_closure); |
||
5264 | if (G_CLOSURE_NEEDS_MARSHAL (set_property_closure)) |
||
5265 | g_closure_set_marshal (set_property_closure, g_cclosure_marshal_generic); |
||
5266 | } |
||
5267 | |||
5268 | return data; |
||
5269 | } |
||
5270 | |||
5271 | static void |
||
5272 | register_object_free_func (gpointer user_data) |
||
5273 | { |
||
5274 | RegisterObjectData *data = user_data; |
||
5275 | |||
5276 | g_clear_pointer (&data->method_call_closure, g_closure_unref); |
||
5277 | g_clear_pointer (&data->get_property_closure, g_closure_unref); |
||
5278 | g_clear_pointer (&data->set_property_closure, g_closure_unref); |
||
5279 | |||
5280 | g_free (data); |
||
5281 | } |
||
5282 | |||
5283 | static void |
||
5284 | register_with_closures_on_method_call (GDBusConnection *connection, |
||
5285 | const gchar *sender, |
||
5286 | const gchar *object_path, |
||
5287 | const gchar *interface_name, |
||
5288 | const gchar *method_name, |
||
5289 | GVariant *parameters, |
||
5290 | GDBusMethodInvocation *invocation, |
||
5291 | gpointer user_data) |
||
5292 | { |
||
5293 | RegisterObjectData *data = user_data; |
||
5294 | GValue params[] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; |
||
5295 | |||
5296 | g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION); |
||
5297 | g_value_set_object (¶ms[0], connection); |
||
5298 | |||
5299 | g_value_init (¶ms[1], G_TYPE_STRING); |
||
5300 | g_value_set_string (¶ms[1], sender); |
||
5301 | |||
5302 | g_value_init (¶ms[2], G_TYPE_STRING); |
||
5303 | g_value_set_string (¶ms[2], object_path); |
||
5304 | |||
5305 | g_value_init (¶ms[3], G_TYPE_STRING); |
||
5306 | g_value_set_string (¶ms[3], interface_name); |
||
5307 | |||
5308 | g_value_init (¶ms[4], G_TYPE_STRING); |
||
5309 | g_value_set_string (¶ms[4], method_name); |
||
5310 | |||
5311 | g_value_init (¶ms[5], G_TYPE_VARIANT); |
||
5312 | g_value_set_variant (¶ms[5], parameters); |
||
5313 | |||
5314 | g_value_init (¶ms[6], G_TYPE_DBUS_METHOD_INVOCATION); |
||
5315 | g_value_set_object (¶ms[6], invocation); |
||
5316 | |||
5317 | g_closure_invoke (data->method_call_closure, NULL, G_N_ELEMENTS (params), params, NULL); |
||
5318 | |||
5319 | g_value_unset (params + 0); |
||
5320 | g_value_unset (params + 1); |
||
5321 | g_value_unset (params + 2); |
||
5322 | g_value_unset (params + 3); |
||
5323 | g_value_unset (params + 4); |
||
5324 | g_value_unset (params + 5); |
||
5325 | g_value_unset (params + 6); |
||
5326 | } |
||
5327 | |||
5328 | static GVariant * |
||
5329 | register_with_closures_on_get_property (GDBusConnection *connection, |
||
5330 | const gchar *sender, |
||
5331 | const gchar *object_path, |
||
5332 | const gchar *interface_name, |
||
5333 | const gchar *property_name, |
||
5334 | GError **error, |
||
5335 | gpointer user_data) |
||
5336 | { |
||
5337 | RegisterObjectData *data = user_data; |
||
5338 | GValue params[] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; |
||
5339 | GValue result_value = G_VALUE_INIT; |
||
5340 | GVariant *result; |
||
5341 | |||
5342 | g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION); |
||
5343 | g_value_set_object (¶ms[0], connection); |
||
5344 | |||
5345 | g_value_init (¶ms[1], G_TYPE_STRING); |
||
5346 | g_value_set_string (¶ms[1], sender); |
||
5347 | |||
5348 | g_value_init (¶ms[2], G_TYPE_STRING); |
||
5349 | g_value_set_string (¶ms[2], object_path); |
||
5350 | |||
5351 | g_value_init (¶ms[3], G_TYPE_STRING); |
||
5352 | g_value_set_string (¶ms[3], interface_name); |
||
5353 | |||
5354 | g_value_init (¶ms[4], G_TYPE_STRING); |
||
5355 | g_value_set_string (¶ms[4], property_name); |
||
5356 | |||
5357 | g_value_init (&result_value, G_TYPE_VARIANT); |
||
5358 | |||
5359 | g_closure_invoke (data->get_property_closure, &result_value, G_N_ELEMENTS (params), params, NULL); |
||
5360 | |||
5361 | result = g_value_get_variant (&result_value); |
||
5362 | if (result) |
||
5363 | g_variant_ref (result); |
||
5364 | |||
5365 | g_value_unset (params + 0); |
||
5366 | g_value_unset (params + 1); |
||
5367 | g_value_unset (params + 2); |
||
5368 | g_value_unset (params + 3); |
||
5369 | g_value_unset (params + 4); |
||
5370 | g_value_unset (&result_value); |
||
5371 | |||
5372 | if (!result) |
||
5373 | g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, |
||
5374 | _("Unable to retrieve property %s.%s"), |
||
5375 | interface_name, property_name); |
||
5376 | |||
5377 | return result; |
||
5378 | } |
||
5379 | |||
5380 | static gboolean |
||
5381 | register_with_closures_on_set_property (GDBusConnection *connection, |
||
5382 | const gchar *sender, |
||
5383 | const gchar *object_path, |
||
5384 | const gchar *interface_name, |
||
5385 | const gchar *property_name, |
||
5386 | GVariant *value, |
||
5387 | GError **error, |
||
5388 | gpointer user_data) |
||
5389 | { |
||
5390 | RegisterObjectData *data = user_data; |
||
5391 | GValue params[] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; |
||
5392 | GValue result_value = G_VALUE_INIT; |
||
5393 | gboolean result; |
||
5394 | |||
5395 | g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION); |
||
5396 | g_value_set_object (¶ms[0], connection); |
||
5397 | |||
5398 | g_value_init (¶ms[1], G_TYPE_STRING); |
||
5399 | g_value_set_string (¶ms[1], sender); |
||
5400 | |||
5401 | g_value_init (¶ms[2], G_TYPE_STRING); |
||
5402 | g_value_set_string (¶ms[2], object_path); |
||
5403 | |||
5404 | g_value_init (¶ms[3], G_TYPE_STRING); |
||
5405 | g_value_set_string (¶ms[3], interface_name); |
||
5406 | |||
5407 | g_value_init (¶ms[4], G_TYPE_STRING); |
||
5408 | g_value_set_string (¶ms[4], property_name); |
||
5409 | |||
5410 | g_value_init (¶ms[5], G_TYPE_VARIANT); |
||
5411 | g_value_set_variant (¶ms[5], value); |
||
5412 | |||
5413 | g_value_init (&result_value, G_TYPE_BOOLEAN); |
||
5414 | |||
5415 | g_closure_invoke (data->set_property_closure, &result_value, G_N_ELEMENTS (params), params, NULL); |
||
5416 | |||
5417 | result = g_value_get_boolean (&result_value); |
||
5418 | |||
5419 | g_value_unset (params + 0); |
||
5420 | g_value_unset (params + 1); |
||
5421 | g_value_unset (params + 2); |
||
5422 | g_value_unset (params + 3); |
||
5423 | g_value_unset (params + 4); |
||
5424 | g_value_unset (params + 5); |
||
5425 | g_value_unset (&result_value); |
||
5426 | |||
5427 | if (!result) |
||
5428 | g_set_error (error, |
||
5429 | G_DBUS_ERROR, G_DBUS_ERROR_FAILED, |
||
5430 | _("Unable to set property %s.%s"), |
||
5431 | interface_name, property_name); |
||
5432 | |||
5433 | return result; |
||
5434 | } |
||
5435 | |||
5436 | /** |
||
5437 | * g_dbus_connection_register_object_with_closures: (rename-to g_dbus_connection_register_object) |
||
5438 | * @connection: A #GDBusConnection. |
||
5439 | * @object_path: The object path to register at. |
||
5440 | * @interface_info: Introspection data for the interface. |
||
5441 | * @method_call_closure: (nullable): #GClosure for handling incoming method calls. |
||
5442 | * @get_property_closure: (nullable): #GClosure for getting a property. |
||
5443 | * @set_property_closure: (nullable): #GClosure for setting a property. |
||
5444 | * @error: Return location for error or %NULL. |
||
5445 | * |
||
5446 | * Version of g_dbus_connection_register_object() using closures instead of a |
||
5447 | * #GDBusInterfaceVTable for easier binding in other languages. |
||
5448 | * |
||
5449 | * Returns: 0 if @error is set, otherwise a registration id (never 0) |
||
5450 | * that can be used with g_dbus_connection_unregister_object() . |
||
5451 | * |
||
5452 | * Since: 2.46 |
||
5453 | */ |
||
5454 | guint |
||
5455 | g_dbus_connection_register_object_with_closures (GDBusConnection *connection, |
||
5456 | const gchar *object_path, |
||
5457 | GDBusInterfaceInfo *interface_info, |
||
5458 | GClosure *method_call_closure, |
||
5459 | GClosure *get_property_closure, |
||
5460 | GClosure *set_property_closure, |
||
5461 | GError **error) |
||
5462 | { |
||
5463 | RegisterObjectData *data; |
||
5464 | GDBusInterfaceVTable vtable = |
||
5465 | { |
||
5466 | method_call_closure != NULL ? register_with_closures_on_method_call : NULL, |
||
5467 | get_property_closure != NULL ? register_with_closures_on_get_property : NULL, |
||
5468 | set_property_closure != NULL ? register_with_closures_on_set_property : NULL |
||
5469 | }; |
||
5470 | |||
5471 | data = register_object_data_new (method_call_closure, get_property_closure, set_property_closure); |
||
5472 | |||
5473 | return g_dbus_connection_register_object (connection, |
||
5474 | object_path, |
||
5475 | interface_info, |
||
5476 | &vtable, |
||
5477 | data, |
||
5478 | register_object_free_func, |
||
5479 | error); |
||
5480 | } |
||
5481 | |||
5482 | /* ---------------------------------------------------------------------------------------------------- */ |
||
5483 | |||
5484 | /** |
||
5485 | * g_dbus_connection_emit_signal: |
||
5486 | * @connection: a #GDBusConnection |
||
5487 | * @destination_bus_name: (allow-none): the unique bus name for the destination |
||
5488 | * for the signal or %NULL to emit to all listeners |
||
5489 | * @object_path: path of remote object |
||
5490 | * @interface_name: D-Bus interface to emit a signal on |
||
5491 | * @signal_name: the name of the signal to emit |
||
5492 | * @parameters: (allow-none): a #GVariant tuple with parameters for the signal |
||
5493 | * or %NULL if not passing parameters |
||
5494 | * @error: Return location for error or %NULL |
||
5495 | * |
||
5496 | * Emits a signal. |
||
5497 | * |
||
5498 | * If the parameters GVariant is floating, it is consumed. |
||
5499 | * |
||
5500 | * This can only fail if @parameters is not compatible with the D-Bus protocol. |
||
5501 | * |
||
5502 | * Returns: %TRUE unless @error is set |
||
5503 | * |
||
5504 | * Since: 2.26 |
||
5505 | */ |
||
5506 | gboolean |
||
5507 | g_dbus_connection_emit_signal (GDBusConnection *connection, |
||
5508 | const gchar *destination_bus_name, |
||
5509 | const gchar *object_path, |
||
5510 | const gchar *interface_name, |
||
5511 | const gchar *signal_name, |
||
5512 | GVariant *parameters, |
||
5513 | GError **error) |
||
5514 | { |
||
5515 | GDBusMessage *message; |
||
5516 | gboolean ret; |
||
5517 | |||
5518 | message = NULL; |
||
5519 | ret = FALSE; |
||
5520 | |||
5521 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
5522 | g_return_val_if_fail (destination_bus_name == NULL || g_dbus_is_name (destination_bus_name), FALSE); |
||
5523 | g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), FALSE); |
||
5524 | g_return_val_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name), FALSE); |
||
5525 | g_return_val_if_fail (signal_name != NULL && g_dbus_is_member_name (signal_name), FALSE); |
||
5526 | g_return_val_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), FALSE); |
||
5527 | g_return_val_if_fail (check_initialized (connection), FALSE); |
||
5528 | |||
5529 | if (G_UNLIKELY (_g_dbus_debug_emission ())) |
||
5530 | { |
||
5531 | _g_dbus_debug_print_lock (); |
||
5532 | g_print ("========================================================================\n" |
||
5533 | "GDBus-debug:Emission:\n" |
||
5534 | " >>>> SIGNAL EMISSION %s.%s()\n" |
||
5535 | " on object %s\n" |
||
5536 | " destination %s\n", |
||
5537 | interface_name, signal_name, |
||
5538 | object_path, |
||
5539 | destination_bus_name != NULL ? destination_bus_name : "(none)"); |
||
5540 | _g_dbus_debug_print_unlock (); |
||
5541 | } |
||
5542 | |||
5543 | message = g_dbus_message_new_signal (object_path, |
||
5544 | interface_name, |
||
5545 | signal_name); |
||
5546 | |||
5547 | if (destination_bus_name != NULL) |
||
5548 | g_dbus_message_set_header (message, |
||
5549 | G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION, |
||
5550 | g_variant_new_string (destination_bus_name)); |
||
5551 | |||
5552 | if (parameters != NULL) |
||
5553 | g_dbus_message_set_body (message, parameters); |
||
5554 | |||
5555 | ret = g_dbus_connection_send_message (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, error); |
||
5556 | g_object_unref (message); |
||
5557 | |||
5558 | return ret; |
||
5559 | } |
||
5560 | |||
5561 | static void |
||
5562 | add_call_flags (GDBusMessage *message, |
||
5563 | GDBusCallFlags flags) |
||
5564 | { |
||
5565 | GDBusMessageFlags msg_flags = 0; |
||
5566 | |||
5567 | if (flags & G_DBUS_CALL_FLAGS_NO_AUTO_START) |
||
5568 | msg_flags |= G_DBUS_MESSAGE_FLAGS_NO_AUTO_START; |
||
5569 | if (flags & G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION) |
||
5570 | msg_flags |= G_DBUS_MESSAGE_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION; |
||
5571 | if (msg_flags) |
||
5572 | g_dbus_message_set_flags (message, msg_flags); |
||
5573 | } |
||
5574 | |||
5575 | static GVariant * |
||
5576 | decode_method_reply (GDBusMessage *reply, |
||
5577 | const gchar *method_name, |
||
5578 | const GVariantType *reply_type, |
||
5579 | GUnixFDList **out_fd_list, |
||
5580 | GError **error) |
||
5581 | { |
||
5582 | GVariant *result; |
||
5583 | |||
5584 | result = NULL; |
||
5585 | switch (g_dbus_message_get_message_type (reply)) |
||
5586 | { |
||
5587 | case G_DBUS_MESSAGE_TYPE_METHOD_RETURN: |
||
5588 | result = g_dbus_message_get_body (reply); |
||
5589 | if (result == NULL) |
||
5590 | { |
||
5591 | result = g_variant_new ("()"); |
||
5592 | g_variant_ref_sink (result); |
||
5593 | } |
||
5594 | else |
||
5595 | { |
||
5596 | g_variant_ref (result); |
||
5597 | } |
||
5598 | |||
5599 | if (!g_variant_is_of_type (result, reply_type)) |
||
5600 | { |
||
5601 | gchar *type_string = g_variant_type_dup_string (reply_type); |
||
5602 | |||
5603 | g_set_error (error, |
||
5604 | G_IO_ERROR, |
||
5605 | G_IO_ERROR_INVALID_ARGUMENT, |
||
5606 | _("Method '%s' returned type '%s', but expected '%s'"), |
||
5607 | method_name, g_variant_get_type_string (result), type_string); |
||
5608 | |||
5609 | g_variant_unref (result); |
||
5610 | g_free (type_string); |
||
5611 | result = NULL; |
||
5612 | } |
||
5613 | |||
5614 | #ifdef G_OS_UNIX |
||
5615 | if (result != NULL) |
||
5616 | { |
||
5617 | if (out_fd_list != NULL) |
||
5618 | { |
||
5619 | *out_fd_list = g_dbus_message_get_unix_fd_list (reply); |
||
5620 | if (*out_fd_list != NULL) |
||
5621 | g_object_ref (*out_fd_list); |
||
5622 | } |
||
5623 | } |
||
5624 | #endif |
||
5625 | break; |
||
5626 | |||
5627 | case G_DBUS_MESSAGE_TYPE_ERROR: |
||
5628 | g_dbus_message_to_gerror (reply, error); |
||
5629 | break; |
||
5630 | |||
5631 | default: |
||
5632 | g_assert_not_reached (); |
||
5633 | break; |
||
5634 | } |
||
5635 | |||
5636 | return result; |
||
5637 | } |
||
5638 | |||
5639 | |||
5640 | typedef struct |
||
5641 | { |
||
5642 | GVariantType *reply_type; |
||
5643 | gchar *method_name; /* for error message */ |
||
5644 | guint32 serial; |
||
5645 | |||
5646 | GUnixFDList *fd_list; |
||
5647 | } CallState; |
||
5648 | |||
5649 | static void |
||
5650 | call_state_free (CallState *state) |
||
5651 | { |
||
5652 | g_variant_type_free (state->reply_type); |
||
5653 | g_free (state->method_name); |
||
5654 | |||
5655 | if (state->fd_list != NULL) |
||
5656 | g_object_unref (state->fd_list); |
||
5657 | g_slice_free (CallState, state); |
||
5658 | } |
||
5659 | |||
5660 | /* called in any thread, with the connection's lock not held */ |
||
5661 | static void |
||
5662 | g_dbus_connection_call_done (GObject *source, |
||
5663 | GAsyncResult *result, |
||
5664 | gpointer user_data) |
||
5665 | { |
||
5666 | GDBusConnection *connection = G_DBUS_CONNECTION (source); |
||
5667 | GTask *task = user_data; |
||
5668 | CallState *state = g_task_get_task_data (task); |
||
5669 | GError *error = NULL; |
||
5670 | GDBusMessage *reply; |
||
5671 | GVariant *value = NULL; |
||
5672 | |||
5673 | reply = g_dbus_connection_send_message_with_reply_finish (connection, |
||
5674 | result, |
||
5675 | &error); |
||
5676 | |||
5677 | if (G_UNLIKELY (_g_dbus_debug_call ())) |
||
5678 | { |
||
5679 | _g_dbus_debug_print_lock (); |
||
5680 | g_print ("========================================================================\n" |
||
5681 | "GDBus-debug:Call:\n" |
||
5682 | " <<<< ASYNC COMPLETE %s() (serial %d)\n" |
||
5683 | " ", |
||
5684 | state->method_name, |
||
5685 | state->serial); |
||
5686 | if (reply != NULL) |
||
5687 | { |
||
5688 | g_print ("SUCCESS\n"); |
||
5689 | } |
||
5690 | else |
||
5691 | { |
||
5692 | g_print ("FAILED: %s\n", |
||
5693 | error->message); |
||
5694 | } |
||
5695 | _g_dbus_debug_print_unlock (); |
||
5696 | } |
||
5697 | |||
5698 | if (reply != NULL) |
||
5699 | value = decode_method_reply (reply, state->method_name, state->reply_type, &state->fd_list, &error); |
||
5700 | |||
5701 | if (error != NULL) |
||
5702 | g_task_return_error (task, error); |
||
5703 | else |
||
5704 | g_task_return_pointer (task, value, (GDestroyNotify) g_variant_unref); |
||
5705 | |||
5706 | g_clear_object (&reply); |
||
5707 | g_object_unref (task); |
||
5708 | } |
||
5709 | |||
5710 | /* called in any thread, with the connection's lock not held */ |
||
5711 | static void |
||
5712 | g_dbus_connection_call_internal (GDBusConnection *connection, |
||
5713 | const gchar *bus_name, |
||
5714 | const gchar *object_path, |
||
5715 | const gchar *interface_name, |
||
5716 | const gchar *method_name, |
||
5717 | GVariant *parameters, |
||
5718 | const GVariantType *reply_type, |
||
5719 | GDBusCallFlags flags, |
||
5720 | gint timeout_msec, |
||
5721 | GUnixFDList *fd_list, |
||
5722 | GCancellable *cancellable, |
||
5723 | GAsyncReadyCallback callback, |
||
5724 | gpointer user_data) |
||
5725 | { |
||
5726 | GDBusMessage *message; |
||
5727 | guint32 serial; |
||
5728 | |||
5729 | g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); |
||
5730 | g_return_if_fail (bus_name == NULL || g_dbus_is_name (bus_name)); |
||
5731 | g_return_if_fail (object_path != NULL && g_variant_is_object_path (object_path)); |
||
5732 | g_return_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name)); |
||
5733 | g_return_if_fail (method_name != NULL && g_dbus_is_member_name (method_name)); |
||
5734 | g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1); |
||
5735 | g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE)); |
||
5736 | g_return_if_fail (check_initialized (connection)); |
||
5737 | #ifdef G_OS_UNIX |
||
5738 | g_return_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list)); |
||
5739 | #else |
||
5740 | g_return_if_fail (fd_list == NULL); |
||
5741 | #endif |
||
5742 | |||
5743 | message = g_dbus_message_new_method_call (bus_name, |
||
5744 | object_path, |
||
5745 | interface_name, |
||
5746 | method_name); |
||
5747 | add_call_flags (message, flags); |
||
5748 | if (parameters != NULL) |
||
5749 | g_dbus_message_set_body (message, parameters); |
||
5750 | |||
5751 | #ifdef G_OS_UNIX |
||
5752 | if (fd_list != NULL) |
||
5753 | g_dbus_message_set_unix_fd_list (message, fd_list); |
||
5754 | #endif |
||
5755 | |||
5756 | /* If the user has no callback then we can just send the message with |
||
5757 | * the G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED flag set and skip all |
||
5758 | * the logic for processing the reply. If the service sends the reply |
||
5759 | * anyway then it will just be ignored. |
||
5760 | */ |
||
5761 | if (callback != NULL) |
||
5762 | { |
||
5763 | CallState *state; |
||
5764 | GTask *task; |
||
5765 | |||
5766 | state = g_slice_new0 (CallState); |
||
5767 | state->method_name = g_strjoin (".", interface_name, method_name, NULL); |
||
5768 | |||
5769 | if (reply_type == NULL) |
||
5770 | reply_type = G_VARIANT_TYPE_ANY; |
||
5771 | |||
5772 | state->reply_type = g_variant_type_copy (reply_type); |
||
5773 | |||
5774 | task = g_task_new (connection, cancellable, callback, user_data); |
||
5775 | g_task_set_task_data (task, state, (GDestroyNotify) call_state_free); |
||
5776 | |||
5777 | g_dbus_connection_send_message_with_reply (connection, |
||
5778 | message, |
||
5779 | G_DBUS_SEND_MESSAGE_FLAGS_NONE, |
||
5780 | timeout_msec, |
||
5781 | &state->serial, |
||
5782 | cancellable, |
||
5783 | g_dbus_connection_call_done, |
||
5784 | task); |
||
5785 | serial = state->serial; |
||
5786 | } |
||
5787 | else |
||
5788 | { |
||
5789 | GDBusMessageFlags flags; |
||
5790 | |||
5791 | flags = g_dbus_message_get_flags (message); |
||
5792 | flags |= G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED; |
||
5793 | g_dbus_message_set_flags (message, flags); |
||
5794 | |||
5795 | g_dbus_connection_send_message (connection, |
||
5796 | message, |
||
5797 | G_DBUS_SEND_MESSAGE_FLAGS_NONE, |
||
5798 | &serial, NULL); |
||
5799 | } |
||
5800 | |||
5801 | if (G_UNLIKELY (_g_dbus_debug_call ())) |
||
5802 | { |
||
5803 | _g_dbus_debug_print_lock (); |
||
5804 | g_print ("========================================================================\n" |
||
5805 | "GDBus-debug:Call:\n" |
||
5806 | " >>>> ASYNC %s.%s()\n" |
||
5807 | " on object %s\n" |
||
5808 | " owned by name %s (serial %d)\n", |
||
5809 | interface_name, |
||
5810 | method_name, |
||
5811 | object_path, |
||
5812 | bus_name != NULL ? bus_name : "(none)", |
||
5813 | serial); |
||
5814 | _g_dbus_debug_print_unlock (); |
||
5815 | } |
||
5816 | |||
5817 | if (message != NULL) |
||
5818 | g_object_unref (message); |
||
5819 | } |
||
5820 | |||
5821 | /* called in any thread, with the connection's lock not held */ |
||
5822 | static GVariant * |
||
5823 | g_dbus_connection_call_finish_internal (GDBusConnection *connection, |
||
5824 | GUnixFDList **out_fd_list, |
||
5825 | GAsyncResult *res, |
||
5826 | GError **error) |
||
5827 | { |
||
5828 | GTask *task; |
||
5829 | CallState *state; |
||
5830 | GVariant *ret; |
||
5831 | |||
5832 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); |
||
5833 | g_return_val_if_fail (g_task_is_valid (res, connection), NULL); |
||
5834 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
5835 | |||
5836 | task = G_TASK (res); |
||
5837 | state = g_task_get_task_data (task); |
||
5838 | |||
5839 | ret = g_task_propagate_pointer (task, error); |
||
5840 | if (!ret) |
||
5841 | return NULL; |
||
5842 | |||
5843 | if (out_fd_list != NULL) |
||
5844 | *out_fd_list = state->fd_list != NULL ? g_object_ref (state->fd_list) : NULL; |
||
5845 | return ret; |
||
5846 | } |
||
5847 | |||
5848 | /* called in any user thread, with the connection's lock not held */ |
||
5849 | static GVariant * |
||
5850 | g_dbus_connection_call_sync_internal (GDBusConnection *connection, |
||
5851 | const gchar *bus_name, |
||
5852 | const gchar *object_path, |
||
5853 | const gchar *interface_name, |
||
5854 | const gchar *method_name, |
||
5855 | GVariant *parameters, |
||
5856 | const GVariantType *reply_type, |
||
5857 | GDBusCallFlags flags, |
||
5858 | gint timeout_msec, |
||
5859 | GUnixFDList *fd_list, |
||
5860 | GUnixFDList **out_fd_list, |
||
5861 | GCancellable *cancellable, |
||
5862 | GError **error) |
||
5863 | { |
||
5864 | GDBusMessage *message; |
||
5865 | GDBusMessage *reply; |
||
5866 | GVariant *result; |
||
5867 | GError *local_error; |
||
5868 | GDBusSendMessageFlags send_flags; |
||
5869 | |||
5870 | message = NULL; |
||
5871 | reply = NULL; |
||
5872 | result = NULL; |
||
5873 | |||
5874 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); |
||
5875 | g_return_val_if_fail (bus_name == NULL || g_dbus_is_name (bus_name), NULL); |
||
5876 | g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), NULL); |
||
5877 | g_return_val_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name), NULL); |
||
5878 | g_return_val_if_fail (method_name != NULL && g_dbus_is_member_name (method_name), NULL); |
||
5879 | g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); |
||
5880 | g_return_val_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL); |
||
5881 | #ifdef G_OS_UNIX |
||
5882 | g_return_val_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list), NULL); |
||
5883 | #else |
||
5884 | g_return_val_if_fail (fd_list == NULL, NULL); |
||
5885 | #endif |
||
5886 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
5887 | |||
5888 | if (!(flags & CALL_FLAGS_INITIALIZING)) |
||
5889 | g_return_val_if_fail (check_initialized (connection), FALSE); |
||
5890 | |||
5891 | if (reply_type == NULL) |
||
5892 | reply_type = G_VARIANT_TYPE_ANY; |
||
5893 | |||
5894 | message = g_dbus_message_new_method_call (bus_name, |
||
5895 | object_path, |
||
5896 | interface_name, |
||
5897 | method_name); |
||
5898 | add_call_flags (message, flags); |
||
5899 | if (parameters != NULL) |
||
5900 | g_dbus_message_set_body (message, parameters); |
||
5901 | |||
5902 | #ifdef G_OS_UNIX |
||
5903 | if (fd_list != NULL) |
||
5904 | g_dbus_message_set_unix_fd_list (message, fd_list); |
||
5905 | #endif |
||
5906 | |||
5907 | if (G_UNLIKELY (_g_dbus_debug_call ())) |
||
5908 | { |
||
5909 | _g_dbus_debug_print_lock (); |
||
5910 | g_print ("========================================================================\n" |
||
5911 | "GDBus-debug:Call:\n" |
||
5912 | " >>>> SYNC %s.%s()\n" |
||
5913 | " on object %s\n" |
||
5914 | " owned by name %s\n", |
||
5915 | interface_name, |
||
5916 | method_name, |
||
5917 | object_path, |
||
5918 | bus_name != NULL ? bus_name : "(none)"); |
||
5919 | _g_dbus_debug_print_unlock (); |
||
5920 | } |
||
5921 | |||
5922 | local_error = NULL; |
||
5923 | |||
5924 | send_flags = G_DBUS_SEND_MESSAGE_FLAGS_NONE; |
||
5925 | |||
5926 | /* translate from one flavour of flags to another... */ |
||
5927 | if (flags & CALL_FLAGS_INITIALIZING) |
||
5928 | send_flags |= SEND_MESSAGE_FLAGS_INITIALIZING; |
||
5929 | |||
5930 | reply = g_dbus_connection_send_message_with_reply_sync (connection, |
||
5931 | message, |
||
5932 | send_flags, |
||
5933 | timeout_msec, |
||
5934 | NULL, /* volatile guint32 *out_serial */ |
||
5935 | cancellable, |
||
5936 | &local_error); |
||
5937 | |||
5938 | if (G_UNLIKELY (_g_dbus_debug_call ())) |
||
5939 | { |
||
5940 | _g_dbus_debug_print_lock (); |
||
5941 | g_print ("========================================================================\n" |
||
5942 | "GDBus-debug:Call:\n" |
||
5943 | " <<<< SYNC COMPLETE %s.%s()\n" |
||
5944 | " ", |
||
5945 | interface_name, |
||
5946 | method_name); |
||
5947 | if (reply != NULL) |
||
5948 | { |
||
5949 | g_print ("SUCCESS\n"); |
||
5950 | } |
||
5951 | else |
||
5952 | { |
||
5953 | g_print ("FAILED: %s\n", |
||
5954 | local_error->message); |
||
5955 | } |
||
5956 | _g_dbus_debug_print_unlock (); |
||
5957 | } |
||
5958 | |||
5959 | if (reply == NULL) |
||
5960 | { |
||
5961 | if (error != NULL) |
||
5962 | *error = local_error; |
||
5963 | else |
||
5964 | g_error_free (local_error); |
||
5965 | goto out; |
||
5966 | } |
||
5967 | |||
5968 | result = decode_method_reply (reply, method_name, reply_type, out_fd_list, error); |
||
5969 | |||
5970 | out: |
||
5971 | if (message != NULL) |
||
5972 | g_object_unref (message); |
||
5973 | if (reply != NULL) |
||
5974 | g_object_unref (reply); |
||
5975 | |||
5976 | return result; |
||
5977 | } |
||
5978 | |||
5979 | /* ---------------------------------------------------------------------------------------------------- */ |
||
5980 | |||
5981 | /** |
||
5982 | * g_dbus_connection_call: |
||
5983 | * @connection: a #GDBusConnection |
||
5984 | * @bus_name: (allow-none): a unique or well-known bus name or %NULL if |
||
5985 | * @connection is not a message bus connection |
||
5986 | * @object_path: path of remote object |
||
5987 | * @interface_name: D-Bus interface to invoke method on |
||
5988 | * @method_name: the name of the method to invoke |
||
5989 | * @parameters: (allow-none): a #GVariant tuple with parameters for the method |
||
5990 | * or %NULL if not passing parameters |
||
5991 | * @reply_type: (allow-none): the expected type of the reply, or %NULL |
||
5992 | * @flags: flags from the #GDBusCallFlags enumeration |
||
5993 | * @timeout_msec: the timeout in milliseconds, -1 to use the default |
||
5994 | * timeout or %G_MAXINT for no timeout |
||
5995 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
5996 | * @callback: (allow-none): a #GAsyncReadyCallback to call when the request |
||
5997 | * is satisfied or %NULL if you don't care about the result of the |
||
5998 | * method invocation |
||
5999 | * @user_data: the data to pass to @callback |
||
6000 | * |
||
6001 | * Asynchronously invokes the @method_name method on the |
||
6002 | * @interface_name D-Bus interface on the remote object at |
||
6003 | * @object_path owned by @bus_name. |
||
6004 | * |
||
6005 | * If @connection is closed then the operation will fail with |
||
6006 | * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the operation will |
||
6007 | * fail with %G_IO_ERROR_CANCELLED. If @parameters contains a value |
||
6008 | * not compatible with the D-Bus protocol, the operation fails with |
||
6009 | * %G_IO_ERROR_INVALID_ARGUMENT. |
||
6010 | * |
||
6011 | * If @reply_type is non-%NULL then the reply will be checked for having this type and an |
||
6012 | * error will be raised if it does not match. Said another way, if you give a @reply_type |
||
6013 | * then any non-%NULL return value will be of this type. |
||
6014 | * |
||
6015 | * If the @parameters #GVariant is floating, it is consumed. This allows |
||
6016 | * convenient 'inline' use of g_variant_new(), e.g.: |
||
6017 | * |[<!-- language="C" --> |
||
6018 | * g_dbus_connection_call (connection, |
||
6019 | * "org.freedesktop.StringThings", |
||
6020 | * "/org/freedesktop/StringThings", |
||
6021 | * "org.freedesktop.StringThings", |
||
6022 | * "TwoStrings", |
||
6023 | * g_variant_new ("(ss)", |
||
6024 | * "Thing One", |
||
6025 | * "Thing Two"), |
||
6026 | * NULL, |
||
6027 | * G_DBUS_CALL_FLAGS_NONE, |
||
6028 | * -1, |
||
6029 | * NULL, |
||
6030 | * (GAsyncReadyCallback) two_strings_done, |
||
6031 | * NULL); |
||
6032 | * ]| |
||
6033 | * |
||
6034 | * This is an asynchronous method. When the operation is finished, |
||
6035 | * @callback will be invoked in the |
||
6036 | * [thread-default main context][g-main-context-push-thread-default] |
||
6037 | * of the thread you are calling this method from. You can then call |
||
6038 | * g_dbus_connection_call_finish() to get the result of the operation. |
||
6039 | * See g_dbus_connection_call_sync() for the synchronous version of this |
||
6040 | * function. |
||
6041 | * |
||
6042 | * If @callback is %NULL then the D-Bus method call message will be sent with |
||
6043 | * the %G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED flag set. |
||
6044 | * |
||
6045 | * Since: 2.26 |
||
6046 | */ |
||
6047 | void |
||
6048 | g_dbus_connection_call (GDBusConnection *connection, |
||
6049 | const gchar *bus_name, |
||
6050 | const gchar *object_path, |
||
6051 | const gchar *interface_name, |
||
6052 | const gchar *method_name, |
||
6053 | GVariant *parameters, |
||
6054 | const GVariantType *reply_type, |
||
6055 | GDBusCallFlags flags, |
||
6056 | gint timeout_msec, |
||
6057 | GCancellable *cancellable, |
||
6058 | GAsyncReadyCallback callback, |
||
6059 | gpointer user_data) |
||
6060 | { |
||
6061 | g_dbus_connection_call_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, NULL, cancellable, callback, user_data); |
||
6062 | } |
||
6063 | |||
6064 | /** |
||
6065 | * g_dbus_connection_call_finish: |
||
6066 | * @connection: a #GDBusConnection |
||
6067 | * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_call() |
||
6068 | * @error: return location for error or %NULL |
||
6069 | * |
||
6070 | * Finishes an operation started with g_dbus_connection_call(). |
||
6071 | * |
||
6072 | * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with |
||
6073 | * return values. Free with g_variant_unref(). |
||
6074 | * |
||
6075 | * Since: 2.26 |
||
6076 | */ |
||
6077 | GVariant * |
||
6078 | g_dbus_connection_call_finish (GDBusConnection *connection, |
||
6079 | GAsyncResult *res, |
||
6080 | GError **error) |
||
6081 | { |
||
6082 | return g_dbus_connection_call_finish_internal (connection, NULL, res, error); |
||
6083 | } |
||
6084 | |||
6085 | /** |
||
6086 | * g_dbus_connection_call_sync: |
||
6087 | * @connection: a #GDBusConnection |
||
6088 | * @bus_name: (allow-none): a unique or well-known bus name or %NULL if |
||
6089 | * @connection is not a message bus connection |
||
6090 | * @object_path: path of remote object |
||
6091 | * @interface_name: D-Bus interface to invoke method on |
||
6092 | * @method_name: the name of the method to invoke |
||
6093 | * @parameters: (allow-none): a #GVariant tuple with parameters for the method |
||
6094 | * or %NULL if not passing parameters |
||
6095 | * @reply_type: (allow-none): the expected type of the reply, or %NULL |
||
6096 | * @flags: flags from the #GDBusCallFlags enumeration |
||
6097 | * @timeout_msec: the timeout in milliseconds, -1 to use the default |
||
6098 | * timeout or %G_MAXINT for no timeout |
||
6099 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
6100 | * @error: return location for error or %NULL |
||
6101 | * |
||
6102 | * Synchronously invokes the @method_name method on the |
||
6103 | * @interface_name D-Bus interface on the remote object at |
||
6104 | * @object_path owned by @bus_name. |
||
6105 | * |
||
6106 | * If @connection is closed then the operation will fail with |
||
6107 | * %G_IO_ERROR_CLOSED. If @cancellable is canceled, the |
||
6108 | * operation will fail with %G_IO_ERROR_CANCELLED. If @parameters |
||
6109 | * contains a value not compatible with the D-Bus protocol, the operation |
||
6110 | * fails with %G_IO_ERROR_INVALID_ARGUMENT. |
||
6111 | |||
6112 | * If @reply_type is non-%NULL then the reply will be checked for having |
||
6113 | * this type and an error will be raised if it does not match. Said |
||
6114 | * another way, if you give a @reply_type then any non-%NULL return |
||
6115 | * value will be of this type. |
||
6116 | * |
||
6117 | * If the @parameters #GVariant is floating, it is consumed. |
||
6118 | * This allows convenient 'inline' use of g_variant_new(), e.g.: |
||
6119 | * |[<!-- language="C" --> |
||
6120 | * g_dbus_connection_call_sync (connection, |
||
6121 | * "org.freedesktop.StringThings", |
||
6122 | * "/org/freedesktop/StringThings", |
||
6123 | * "org.freedesktop.StringThings", |
||
6124 | * "TwoStrings", |
||
6125 | * g_variant_new ("(ss)", |
||
6126 | * "Thing One", |
||
6127 | * "Thing Two"), |
||
6128 | * NULL, |
||
6129 | * G_DBUS_CALL_FLAGS_NONE, |
||
6130 | * -1, |
||
6131 | * NULL, |
||
6132 | * &error); |
||
6133 | * ]| |
||
6134 | * |
||
6135 | * The calling thread is blocked until a reply is received. See |
||
6136 | * g_dbus_connection_call() for the asynchronous version of |
||
6137 | * this method. |
||
6138 | * |
||
6139 | * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with |
||
6140 | * return values. Free with g_variant_unref(). |
||
6141 | * |
||
6142 | * Since: 2.26 |
||
6143 | */ |
||
6144 | GVariant * |
||
6145 | g_dbus_connection_call_sync (GDBusConnection *connection, |
||
6146 | const gchar *bus_name, |
||
6147 | const gchar *object_path, |
||
6148 | const gchar *interface_name, |
||
6149 | const gchar *method_name, |
||
6150 | GVariant *parameters, |
||
6151 | const GVariantType *reply_type, |
||
6152 | GDBusCallFlags flags, |
||
6153 | gint timeout_msec, |
||
6154 | GCancellable *cancellable, |
||
6155 | GError **error) |
||
6156 | { |
||
6157 | return g_dbus_connection_call_sync_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, NULL, NULL, cancellable, error); |
||
6158 | } |
||
6159 | |||
6160 | /* ---------------------------------------------------------------------------------------------------- */ |
||
6161 | |||
6162 | #ifdef G_OS_UNIX |
||
6163 | |||
6164 | /** |
||
6165 | * g_dbus_connection_call_with_unix_fd_list: |
||
6166 | * @connection: a #GDBusConnection |
||
6167 | * @bus_name: (allow-none): a unique or well-known bus name or %NULL if |
||
6168 | * @connection is not a message bus connection |
||
6169 | * @object_path: path of remote object |
||
6170 | * @interface_name: D-Bus interface to invoke method on |
||
6171 | * @method_name: the name of the method to invoke |
||
6172 | * @parameters: (allow-none): a #GVariant tuple with parameters for the method |
||
6173 | * or %NULL if not passing parameters |
||
6174 | * @reply_type: (allow-none): the expected type of the reply, or %NULL |
||
6175 | * @flags: flags from the #GDBusCallFlags enumeration |
||
6176 | * @timeout_msec: the timeout in milliseconds, -1 to use the default |
||
6177 | * timeout or %G_MAXINT for no timeout |
||
6178 | * @fd_list: (allow-none): a #GUnixFDList or %NULL |
||
6179 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
6180 | * @callback: (allow-none): a #GAsyncReadyCallback to call when the request is |
||
6181 | * satisfied or %NULL if you don't * care about the result of the |
||
6182 | * method invocation |
||
6183 | * @user_data: The data to pass to @callback. |
||
6184 | * |
||
6185 | * Like g_dbus_connection_call() but also takes a #GUnixFDList object. |
||
6186 | * |
||
6187 | * This method is only available on UNIX. |
||
6188 | * |
||
6189 | * Since: 2.30 |
||
6190 | */ |
||
6191 | void |
||
6192 | g_dbus_connection_call_with_unix_fd_list (GDBusConnection *connection, |
||
6193 | const gchar *bus_name, |
||
6194 | const gchar *object_path, |
||
6195 | const gchar *interface_name, |
||
6196 | const gchar *method_name, |
||
6197 | GVariant *parameters, |
||
6198 | const GVariantType *reply_type, |
||
6199 | GDBusCallFlags flags, |
||
6200 | gint timeout_msec, |
||
6201 | GUnixFDList *fd_list, |
||
6202 | GCancellable *cancellable, |
||
6203 | GAsyncReadyCallback callback, |
||
6204 | gpointer user_data) |
||
6205 | { |
||
6206 | g_dbus_connection_call_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, fd_list, cancellable, callback, user_data); |
||
6207 | } |
||
6208 | |||
6209 | /** |
||
6210 | * g_dbus_connection_call_with_unix_fd_list_finish: |
||
6211 | * @connection: a #GDBusConnection |
||
6212 | * @out_fd_list: (out) (allow-none): return location for a #GUnixFDList or %NULL |
||
6213 | * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed to |
||
6214 | * g_dbus_connection_call_with_unix_fd_list() |
||
6215 | * @error: return location for error or %NULL |
||
6216 | * |
||
6217 | * Finishes an operation started with g_dbus_connection_call_with_unix_fd_list(). |
||
6218 | * |
||
6219 | * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with |
||
6220 | * return values. Free with g_variant_unref(). |
||
6221 | * |
||
6222 | * Since: 2.30 |
||
6223 | */ |
||
6224 | GVariant * |
||
6225 | g_dbus_connection_call_with_unix_fd_list_finish (GDBusConnection *connection, |
||
6226 | GUnixFDList **out_fd_list, |
||
6227 | GAsyncResult *res, |
||
6228 | GError **error) |
||
6229 | { |
||
6230 | return g_dbus_connection_call_finish_internal (connection, out_fd_list, res, error); |
||
6231 | } |
||
6232 | |||
6233 | /** |
||
6234 | * g_dbus_connection_call_with_unix_fd_list_sync: |
||
6235 | * @connection: a #GDBusConnection |
||
6236 | * @bus_name: (allow-none): a unique or well-known bus name or %NULL |
||
6237 | * if @connection is not a message bus connection |
||
6238 | * @object_path: path of remote object |
||
6239 | * @interface_name: D-Bus interface to invoke method on |
||
6240 | * @method_name: the name of the method to invoke |
||
6241 | * @parameters: (allow-none): a #GVariant tuple with parameters for |
||
6242 | * the method or %NULL if not passing parameters |
||
6243 | * @reply_type: (allow-none): the expected type of the reply, or %NULL |
||
6244 | * @flags: flags from the #GDBusCallFlags enumeration |
||
6245 | * @timeout_msec: the timeout in milliseconds, -1 to use the default |
||
6246 | * timeout or %G_MAXINT for no timeout |
||
6247 | * @fd_list: (allow-none): a #GUnixFDList or %NULL |
||
6248 | * @out_fd_list: (out) (allow-none): return location for a #GUnixFDList or %NULL |
||
6249 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
6250 | * @error: return location for error or %NULL |
||
6251 | * |
||
6252 | * Like g_dbus_connection_call_sync() but also takes and returns #GUnixFDList objects. |
||
6253 | * |
||
6254 | * This method is only available on UNIX. |
||
6255 | * |
||
6256 | * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with |
||
6257 | * return values. Free with g_variant_unref(). |
||
6258 | * |
||
6259 | * Since: 2.30 |
||
6260 | */ |
||
6261 | GVariant * |
||
6262 | g_dbus_connection_call_with_unix_fd_list_sync (GDBusConnection *connection, |
||
6263 | const gchar *bus_name, |
||
6264 | const gchar *object_path, |
||
6265 | const gchar *interface_name, |
||
6266 | const gchar *method_name, |
||
6267 | GVariant *parameters, |
||
6268 | const GVariantType *reply_type, |
||
6269 | GDBusCallFlags flags, |
||
6270 | gint timeout_msec, |
||
6271 | GUnixFDList *fd_list, |
||
6272 | GUnixFDList **out_fd_list, |
||
6273 | GCancellable *cancellable, |
||
6274 | GError **error) |
||
6275 | { |
||
6276 | return g_dbus_connection_call_sync_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, fd_list, out_fd_list, cancellable, error); |
||
6277 | } |
||
6278 | |||
6279 | #endif /* G_OS_UNIX */ |
||
6280 | |||
6281 | /* ---------------------------------------------------------------------------------------------------- */ |
||
6282 | |||
6283 | struct ExportedSubtree |
||
6284 | { |
||
6285 | guint id; |
||
6286 | gchar *object_path; |
||
6287 | GDBusConnection *connection; |
||
6288 | GDBusSubtreeVTable *vtable; |
||
6289 | GDBusSubtreeFlags flags; |
||
6290 | |||
6291 | GMainContext *context; |
||
6292 | gpointer user_data; |
||
6293 | GDestroyNotify user_data_free_func; |
||
6294 | }; |
||
6295 | |||
6296 | static void |
||
6297 | exported_subtree_free (ExportedSubtree *es) |
||
6298 | { |
||
6299 | call_destroy_notify (es->context, |
||
6300 | es->user_data_free_func, |
||
6301 | es->user_data); |
||
6302 | |||
6303 | g_main_context_unref (es->context); |
||
6304 | |||
6305 | _g_dbus_subtree_vtable_free (es->vtable); |
||
6306 | g_free (es->object_path); |
||
6307 | g_free (es); |
||
6308 | } |
||
6309 | |||
6310 | /* called without lock held in the thread where the caller registered |
||
6311 | * the subtree |
||
6312 | */ |
||
6313 | static gboolean |
||
6314 | handle_subtree_introspect (GDBusConnection *connection, |
||
6315 | ExportedSubtree *es, |
||
6316 | GDBusMessage *message) |
||
6317 | { |
||
6318 | GString *s; |
||
6319 | gboolean handled; |
||
6320 | GDBusMessage *reply; |
||
6321 | gchar **children; |
||
6322 | gboolean is_root; |
||
6323 | const gchar *sender; |
||
6324 | const gchar *requested_object_path; |
||
6325 | const gchar *requested_node; |
||
6326 | GDBusInterfaceInfo **interfaces; |
||
6327 | guint n; |
||
6328 | gchar **subnode_paths; |
||
6329 | gboolean has_properties_interface; |
||
6330 | gboolean has_introspectable_interface; |
||
6331 | |||
6332 | handled = FALSE; |
||
6333 | |||
6334 | requested_object_path = g_dbus_message_get_path (message); |
||
6335 | sender = g_dbus_message_get_sender (message); |
||
6336 | is_root = (g_strcmp0 (requested_object_path, es->object_path) == 0); |
||
6337 | |||
6338 | s = g_string_new (NULL); |
||
6339 | introspect_append_header (s); |
||
6340 | |||
6341 | /* Strictly we don't need the children in dynamic mode, but we avoid the |
||
6342 | * conditionals to preserve code clarity |
||
6343 | */ |
||
6344 | children = es->vtable->enumerate (es->connection, |
||
6345 | sender, |
||
6346 | es->object_path, |
||
6347 | es->user_data); |
||
6348 | |||
6349 | if (!is_root) |
||
6350 | { |
||
6351 | requested_node = strrchr (requested_object_path, '/') + 1; |
||
6352 | |||
6353 | /* Assert existence of object if we are not dynamic */ |
||
6354 | if (!(es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) && |
||
6355 | !_g_strv_has_string ((const gchar * const *) children, requested_node)) |
||
6356 | goto out; |
||
6357 | } |
||
6358 | else |
||
6359 | { |
||
6360 | requested_node = NULL; |
||
6361 | } |
||
6362 | |||
6363 | interfaces = es->vtable->introspect (es->connection, |
||
6364 | sender, |
||
6365 | es->object_path, |
||
6366 | requested_node, |
||
6367 | es->user_data); |
||
6368 | if (interfaces != NULL) |
||
6369 | { |
||
6370 | has_properties_interface = FALSE; |
||
6371 | has_introspectable_interface = FALSE; |
||
6372 | |||
6373 | for (n = 0; interfaces[n] != NULL; n++) |
||
6374 | { |
||
6375 | if (strcmp (interfaces[n]->name, "org.freedesktop.DBus.Properties") == 0) |
||
6376 | has_properties_interface = TRUE; |
||
6377 | else if (strcmp (interfaces[n]->name, "org.freedesktop.DBus.Introspectable") == 0) |
||
6378 | has_introspectable_interface = TRUE; |
||
6379 | } |
||
6380 | if (!has_properties_interface) |
||
6381 | g_string_append (s, introspect_properties_interface); |
||
6382 | if (!has_introspectable_interface) |
||
6383 | g_string_append (s, introspect_introspectable_interface); |
||
6384 | |||
6385 | for (n = 0; interfaces[n] != NULL; n++) |
||
6386 | { |
||
6387 | g_dbus_interface_info_generate_xml (interfaces[n], 2, s); |
||
6388 | g_dbus_interface_info_unref (interfaces[n]); |
||
6389 | } |
||
6390 | g_free (interfaces); |
||
6391 | } |
||
6392 | |||
6393 | /* then include <node> entries from the Subtree for the root */ |
||
6394 | if (is_root) |
||
6395 | { |
||
6396 | for (n = 0; children != NULL && children[n] != NULL; n++) |
||
6397 | g_string_append_printf (s, " <node name=\"%s\"/>\n", children[n]); |
||
6398 | } |
||
6399 | |||
6400 | /* finally include nodes registered below us */ |
||
6401 | subnode_paths = g_dbus_connection_list_registered (es->connection, requested_object_path); |
||
6402 | for (n = 0; subnode_paths != NULL && subnode_paths[n] != NULL; n++) |
||
6403 | g_string_append_printf (s, " <node name=\"%s\"/>\n", subnode_paths[n]); |
||
6404 | g_strfreev (subnode_paths); |
||
6405 | |||
6406 | g_string_append (s, "</node>\n"); |
||
6407 | |||
6408 | reply = g_dbus_message_new_method_reply (message); |
||
6409 | g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); |
||
6410 | g_dbus_connection_send_message (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
6411 | g_object_unref (reply); |
||
6412 | |||
6413 | handled = TRUE; |
||
6414 | |||
6415 | out: |
||
6416 | g_string_free (s, TRUE); |
||
6417 | g_strfreev (children); |
||
6418 | return handled; |
||
6419 | } |
||
6420 | |||
6421 | /* called without lock held in the thread where the caller registered |
||
6422 | * the subtree |
||
6423 | */ |
||
6424 | static gboolean |
||
6425 | handle_subtree_method_invocation (GDBusConnection *connection, |
||
6426 | ExportedSubtree *es, |
||
6427 | GDBusMessage *message) |
||
6428 | { |
||
6429 | gboolean handled; |
||
6430 | const gchar *sender; |
||
6431 | const gchar *interface_name; |
||
6432 | const gchar *member; |
||
6433 | const gchar *signature; |
||
6434 | const gchar *requested_object_path; |
||
6435 | const gchar *requested_node; |
||
6436 | gboolean is_root; |
||
6437 | GDBusInterfaceInfo *interface_info; |
||
6438 | const GDBusInterfaceVTable *interface_vtable; |
||
6439 | gpointer interface_user_data; |
||
6440 | guint n; |
||
6441 | GDBusInterfaceInfo **interfaces; |
||
6442 | gboolean is_property_get; |
||
6443 | gboolean is_property_set; |
||
6444 | gboolean is_property_get_all; |
||
6445 | |||
6446 | handled = FALSE; |
||
6447 | interfaces = NULL; |
||
6448 | |||
6449 | requested_object_path = g_dbus_message_get_path (message); |
||
6450 | sender = g_dbus_message_get_sender (message); |
||
6451 | interface_name = g_dbus_message_get_interface (message); |
||
6452 | member = g_dbus_message_get_member (message); |
||
6453 | signature = g_dbus_message_get_signature (message); |
||
6454 | is_root = (g_strcmp0 (requested_object_path, es->object_path) == 0); |
||
6455 | |||
6456 | is_property_get = FALSE; |
||
6457 | is_property_set = FALSE; |
||
6458 | is_property_get_all = FALSE; |
||
6459 | if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0) |
||
6460 | { |
||
6461 | if (g_strcmp0 (member, "Get") == 0 && g_strcmp0 (signature, "ss") == 0) |
||
6462 | is_property_get = TRUE; |
||
6463 | else if (g_strcmp0 (member, "Set") == 0 && g_strcmp0 (signature, "ssv") == 0) |
||
6464 | is_property_set = TRUE; |
||
6465 | else if (g_strcmp0 (member, "GetAll") == 0 && g_strcmp0 (signature, "s") == 0) |
||
6466 | is_property_get_all = TRUE; |
||
6467 | } |
||
6468 | |||
6469 | if (!is_root) |
||
6470 | { |
||
6471 | requested_node = strrchr (requested_object_path, '/') + 1; |
||
6472 | |||
6473 | if (~es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) |
||
6474 | { |
||
6475 | /* We don't want to dispatch to unenumerated |
||
6476 | * nodes, so ensure that the child exists. |
||
6477 | */ |
||
6478 | gchar **children; |
||
6479 | gboolean exists; |
||
6480 | |||
6481 | children = es->vtable->enumerate (es->connection, |
||
6482 | sender, |
||
6483 | es->object_path, |
||
6484 | es->user_data); |
||
6485 | |||
6486 | exists = _g_strv_has_string ((const gchar * const *) children, requested_node); |
||
6487 | g_strfreev (children); |
||
6488 | |||
6489 | if (!exists) |
||
6490 | goto out; |
||
6491 | } |
||
6492 | } |
||
6493 | else |
||
6494 | { |
||
6495 | requested_node = NULL; |
||
6496 | } |
||
6497 | |||
6498 | /* get introspection data for the node */ |
||
6499 | interfaces = es->vtable->introspect (es->connection, |
||
6500 | sender, |
||
6501 | requested_object_path, |
||
6502 | requested_node, |
||
6503 | es->user_data); |
||
6504 | |||
6505 | if (interfaces == NULL) |
||
6506 | goto out; |
||
6507 | |||
6508 | interface_info = NULL; |
||
6509 | for (n = 0; interfaces[n] != NULL; n++) |
||
6510 | { |
||
6511 | if (g_strcmp0 (interfaces[n]->name, interface_name) == 0) |
||
6512 | interface_info = interfaces[n]; |
||
6513 | } |
||
6514 | |||
6515 | /* dispatch the call if the user wants to handle it */ |
||
6516 | if (interface_info != NULL) |
||
6517 | { |
||
6518 | /* figure out where to dispatch the method call */ |
||
6519 | interface_user_data = NULL; |
||
6520 | interface_vtable = es->vtable->dispatch (es->connection, |
||
6521 | sender, |
||
6522 | es->object_path, |
||
6523 | interface_name, |
||
6524 | requested_node, |
||
6525 | &interface_user_data, |
||
6526 | es->user_data); |
||
6527 | if (interface_vtable == NULL) |
||
6528 | goto out; |
||
6529 | |||
6530 | CONNECTION_LOCK (connection); |
||
6531 | handled = validate_and_maybe_schedule_method_call (es->connection, |
||
6532 | message, |
||
6533 | 0, |
||
6534 | es->id, |
||
6535 | interface_info, |
||
6536 | interface_vtable, |
||
6537 | es->context, |
||
6538 | interface_user_data); |
||
6539 | CONNECTION_UNLOCK (connection); |
||
6540 | } |
||
6541 | /* handle org.freedesktop.DBus.Properties interface if not explicitly handled */ |
||
6542 | else if (is_property_get || is_property_set || is_property_get_all) |
||
6543 | { |
||
6544 | if (is_property_get) |
||
6545 | g_variant_get (g_dbus_message_get_body (message), "(&s&s)", &interface_name, NULL); |
||
6546 | else if (is_property_set) |
||
6547 | g_variant_get (g_dbus_message_get_body (message), "(&s&sv)", &interface_name, NULL, NULL); |
||
6548 | else if (is_property_get_all) |
||
6549 | g_variant_get (g_dbus_message_get_body (message), "(&s)", &interface_name, NULL, NULL); |
||
6550 | else |
||
6551 | g_assert_not_reached (); |
||
6552 | |||
6553 | /* see if the object supports this interface at all */ |
||
6554 | for (n = 0; interfaces[n] != NULL; n++) |
||
6555 | { |
||
6556 | if (g_strcmp0 (interfaces[n]->name, interface_name) == 0) |
||
6557 | interface_info = interfaces[n]; |
||
6558 | } |
||
6559 | |||
6560 | /* Fail with org.freedesktop.DBus.Error.InvalidArgs if the user-code |
||
6561 | * claims it won't support the interface |
||
6562 | */ |
||
6563 | if (interface_info == NULL) |
||
6564 | { |
||
6565 | GDBusMessage *reply; |
||
6566 | reply = g_dbus_message_new_method_error (message, |
||
6567 | "org.freedesktop.DBus.Error.InvalidArgs", |
||
6568 | _("No such interface '%s'"), |
||
6569 | interface_name); |
||
6570 | g_dbus_connection_send_message (es->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
6571 | g_object_unref (reply); |
||
6572 | handled = TRUE; |
||
6573 | goto out; |
||
6574 | } |
||
6575 | |||
6576 | /* figure out where to dispatch the property get/set/getall calls */ |
||
6577 | interface_user_data = NULL; |
||
6578 | interface_vtable = es->vtable->dispatch (es->connection, |
||
6579 | sender, |
||
6580 | es->object_path, |
||
6581 | interface_name, |
||
6582 | requested_node, |
||
6583 | &interface_user_data, |
||
6584 | es->user_data); |
||
6585 | if (interface_vtable == NULL) |
||
6586 | { |
||
6587 | g_warning ("The subtree introspection function indicates that '%s' " |
||
6588 | "is a valid interface name, but calling the dispatch " |
||
6589 | "function on that interface gave us NULL", interface_name); |
||
6590 | goto out; |
||
6591 | } |
||
6592 | |||
6593 | if (is_property_get || is_property_set) |
||
6594 | { |
||
6595 | CONNECTION_LOCK (connection); |
||
6596 | handled = validate_and_maybe_schedule_property_getset (es->connection, |
||
6597 | message, |
||
6598 | 0, |
||
6599 | es->id, |
||
6600 | is_property_get, |
||
6601 | interface_info, |
||
6602 | interface_vtable, |
||
6603 | es->context, |
||
6604 | interface_user_data); |
||
6605 | CONNECTION_UNLOCK (connection); |
||
6606 | } |
||
6607 | else if (is_property_get_all) |
||
6608 | { |
||
6609 | CONNECTION_LOCK (connection); |
||
6610 | handled = validate_and_maybe_schedule_property_get_all (es->connection, |
||
6611 | message, |
||
6612 | 0, |
||
6613 | es->id, |
||
6614 | interface_info, |
||
6615 | interface_vtable, |
||
6616 | es->context, |
||
6617 | interface_user_data); |
||
6618 | CONNECTION_UNLOCK (connection); |
||
6619 | } |
||
6620 | } |
||
6621 | |||
6622 | out: |
||
6623 | if (interfaces != NULL) |
||
6624 | { |
||
6625 | for (n = 0; interfaces[n] != NULL; n++) |
||
6626 | g_dbus_interface_info_unref (interfaces[n]); |
||
6627 | g_free (interfaces); |
||
6628 | } |
||
6629 | |||
6630 | return handled; |
||
6631 | } |
||
6632 | |||
6633 | typedef struct |
||
6634 | { |
||
6635 | GDBusMessage *message; |
||
6636 | ExportedSubtree *es; |
||
6637 | } SubtreeDeferredData; |
||
6638 | |||
6639 | static void |
||
6640 | subtree_deferred_data_free (SubtreeDeferredData *data) |
||
6641 | { |
||
6642 | g_object_unref (data->message); |
||
6643 | g_free (data); |
||
6644 | } |
||
6645 | |||
6646 | /* called without lock held in the thread where the caller registered the subtree */ |
||
6647 | static gboolean |
||
6648 | process_subtree_vtable_message_in_idle_cb (gpointer _data) |
||
6649 | { |
||
6650 | SubtreeDeferredData *data = _data; |
||
6651 | gboolean handled; |
||
6652 | |||
6653 | handled = FALSE; |
||
6654 | |||
6655 | if (g_strcmp0 (g_dbus_message_get_interface (data->message), "org.freedesktop.DBus.Introspectable") == 0 && |
||
6656 | g_strcmp0 (g_dbus_message_get_member (data->message), "Introspect") == 0 && |
||
6657 | g_strcmp0 (g_dbus_message_get_signature (data->message), "") == 0) |
||
6658 | handled = handle_subtree_introspect (data->es->connection, |
||
6659 | data->es, |
||
6660 | data->message); |
||
6661 | else |
||
6662 | handled = handle_subtree_method_invocation (data->es->connection, |
||
6663 | data->es, |
||
6664 | data->message); |
||
6665 | |||
6666 | if (!handled) |
||
6667 | { |
||
6668 | CONNECTION_LOCK (data->es->connection); |
||
6669 | handled = handle_generic_unlocked (data->es->connection, data->message); |
||
6670 | CONNECTION_UNLOCK (data->es->connection); |
||
6671 | } |
||
6672 | |||
6673 | /* if we couldn't handle the request, just bail with the UnknownMethod error */ |
||
6674 | if (!handled) |
||
6675 | { |
||
6676 | GDBusMessage *reply; |
||
6677 | reply = g_dbus_message_new_method_error (data->message, |
||
6678 | "org.freedesktop.DBus.Error.UnknownMethod", |
||
6679 | _("Method '%s' on interface '%s' with signature '%s' does not exist"), |
||
6680 | g_dbus_message_get_member (data->message), |
||
6681 | g_dbus_message_get_interface (data->message), |
||
6682 | g_dbus_message_get_signature (data->message)); |
||
6683 | g_dbus_connection_send_message (data->es->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
6684 | g_object_unref (reply); |
||
6685 | } |
||
6686 | |||
6687 | return FALSE; |
||
6688 | } |
||
6689 | |||
6690 | /* called in GDBusWorker thread with connection's lock held */ |
||
6691 | static gboolean |
||
6692 | subtree_message_func (GDBusConnection *connection, |
||
6693 | ExportedSubtree *es, |
||
6694 | GDBusMessage *message) |
||
6695 | { |
||
6696 | GSource *idle_source; |
||
6697 | SubtreeDeferredData *data; |
||
6698 | |||
6699 | data = g_new0 (SubtreeDeferredData, 1); |
||
6700 | data->message = g_object_ref (message); |
||
6701 | data->es = es; |
||
6702 | |||
6703 | /* defer this call to an idle handler in the right thread */ |
||
6704 | idle_source = g_idle_source_new (); |
||
6705 | g_source_set_priority (idle_source, G_PRIORITY_HIGH); |
||
6706 | g_source_set_callback (idle_source, |
||
6707 | process_subtree_vtable_message_in_idle_cb, |
||
6708 | data, |
||
6709 | (GDestroyNotify) subtree_deferred_data_free); |
||
6710 | g_source_set_name (idle_source, "[gio] process_subtree_vtable_message_in_idle_cb"); |
||
6711 | g_source_attach (idle_source, es->context); |
||
6712 | g_source_unref (idle_source); |
||
6713 | |||
6714 | /* since we own the entire subtree, handlers for objects not in the subtree have been |
||
6715 | * tried already by libdbus-1 - so we just need to ensure that we're always going |
||
6716 | * to reply to the message |
||
6717 | */ |
||
6718 | return TRUE; |
||
6719 | } |
||
6720 | |||
6721 | /** |
||
6722 | * g_dbus_connection_register_subtree: |
||
6723 | * @connection: a #GDBusConnection |
||
6724 | * @object_path: the object path to register the subtree at |
||
6725 | * @vtable: a #GDBusSubtreeVTable to enumerate, introspect and |
||
6726 | * dispatch nodes in the subtree |
||
6727 | * @flags: flags used to fine tune the behavior of the subtree |
||
6728 | * @user_data: data to pass to functions in @vtable |
||
6729 | * @user_data_free_func: function to call when the subtree is unregistered |
||
6730 | * @error: return location for error or %NULL |
||
6731 | * |
||
6732 | * Registers a whole subtree of dynamic objects. |
||
6733 | * |
||
6734 | * The @enumerate and @introspection functions in @vtable are used to |
||
6735 | * convey, to remote callers, what nodes exist in the subtree rooted |
||
6736 | * by @object_path. |
||
6737 | * |
||
6738 | * When handling remote calls into any node in the subtree, first the |
||
6739 | * @enumerate function is used to check if the node exists. If the node exists |
||
6740 | * or the #G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag is set |
||
6741 | * the @introspection function is used to check if the node supports the |
||
6742 | * requested method. If so, the @dispatch function is used to determine |
||
6743 | * where to dispatch the call. The collected #GDBusInterfaceVTable and |
||
6744 | * #gpointer will be used to call into the interface vtable for processing |
||
6745 | * the request. |
||
6746 | * |
||
6747 | * All calls into user-provided code will be invoked in the |
||
6748 | * [thread-default main context][g-main-context-push-thread-default] |
||
6749 | * of the thread you are calling this method from. |
||
6750 | * |
||
6751 | * If an existing subtree is already registered at @object_path or |
||
6752 | * then @error is set to #G_IO_ERROR_EXISTS. |
||
6753 | * |
||
6754 | * Note that it is valid to register regular objects (using |
||
6755 | * g_dbus_connection_register_object()) in a subtree registered with |
||
6756 | * g_dbus_connection_register_subtree() - if so, the subtree handler |
||
6757 | * is tried as the last resort. One way to think about a subtree |
||
6758 | * handler is to consider it a fallback handler for object paths not |
||
6759 | * registered via g_dbus_connection_register_object() or other bindings. |
||
6760 | * |
||
6761 | * Note that @vtable will be copied so you cannot change it after |
||
6762 | * registration. |
||
6763 | * |
||
6764 | * See this [server][gdbus-subtree-server] for an example of how to use |
||
6765 | * this method. |
||
6766 | * |
||
6767 | * Returns: 0 if @error is set, otherwise a subtree registration id (never 0) |
||
6768 | * that can be used with g_dbus_connection_unregister_subtree() . |
||
6769 | * |
||
6770 | * Since: 2.26 |
||
6771 | */ |
||
6772 | guint |
||
6773 | g_dbus_connection_register_subtree (GDBusConnection *connection, |
||
6774 | const gchar *object_path, |
||
6775 | const GDBusSubtreeVTable *vtable, |
||
6776 | GDBusSubtreeFlags flags, |
||
6777 | gpointer user_data, |
||
6778 | GDestroyNotify user_data_free_func, |
||
6779 | GError **error) |
||
6780 | { |
||
6781 | guint ret; |
||
6782 | ExportedSubtree *es; |
||
6783 | |||
6784 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); |
||
6785 | g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), 0); |
||
6786 | g_return_val_if_fail (vtable != NULL, 0); |
||
6787 | g_return_val_if_fail (error == NULL || *error == NULL, 0); |
||
6788 | g_return_val_if_fail (check_initialized (connection), 0); |
||
6789 | |||
6790 | ret = 0; |
||
6791 | |||
6792 | CONNECTION_LOCK (connection); |
||
6793 | |||
6794 | es = g_hash_table_lookup (connection->map_object_path_to_es, object_path); |
||
6795 | if (es != NULL) |
||
6796 | { |
||
6797 | g_set_error (error, |
||
6798 | G_IO_ERROR, |
||
6799 | G_IO_ERROR_EXISTS, |
||
6800 | _("A subtree is already exported for %s"), |
||
6801 | object_path); |
||
6802 | goto out; |
||
6803 | } |
||
6804 | |||
6805 | es = g_new0 (ExportedSubtree, 1); |
||
6806 | es->object_path = g_strdup (object_path); |
||
6807 | es->connection = connection; |
||
6808 | |||
6809 | es->vtable = _g_dbus_subtree_vtable_copy (vtable); |
||
6810 | es->flags = flags; |
||
6811 | es->id = _global_subtree_registration_id++; /* TODO: overflow etc. */ |
||
6812 | es->user_data = user_data; |
||
6813 | es->user_data_free_func = user_data_free_func; |
||
6814 | es->context = g_main_context_ref_thread_default (); |
||
6815 | |||
6816 | g_hash_table_insert (connection->map_object_path_to_es, es->object_path, es); |
||
6817 | g_hash_table_insert (connection->map_id_to_es, |
||
6818 | GUINT_TO_POINTER (es->id), |
||
6819 | es); |
||
6820 | |||
6821 | ret = es->id; |
||
6822 | |||
6823 | out: |
||
6824 | CONNECTION_UNLOCK (connection); |
||
6825 | |||
6826 | return ret; |
||
6827 | } |
||
6828 | |||
6829 | /* ---------------------------------------------------------------------------------------------------- */ |
||
6830 | |||
6831 | /** |
||
6832 | * g_dbus_connection_unregister_subtree: |
||
6833 | * @connection: a #GDBusConnection |
||
6834 | * @registration_id: a subtree registration id obtained from |
||
6835 | * g_dbus_connection_register_subtree() |
||
6836 | * |
||
6837 | * Unregisters a subtree. |
||
6838 | * |
||
6839 | * Returns: %TRUE if the subtree was unregistered, %FALSE otherwise |
||
6840 | * |
||
6841 | * Since: 2.26 |
||
6842 | */ |
||
6843 | gboolean |
||
6844 | g_dbus_connection_unregister_subtree (GDBusConnection *connection, |
||
6845 | guint registration_id) |
||
6846 | { |
||
6847 | ExportedSubtree *es; |
||
6848 | gboolean ret; |
||
6849 | |||
6850 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); |
||
6851 | g_return_val_if_fail (check_initialized (connection), FALSE); |
||
6852 | |||
6853 | ret = FALSE; |
||
6854 | |||
6855 | CONNECTION_LOCK (connection); |
||
6856 | |||
6857 | es = g_hash_table_lookup (connection->map_id_to_es, |
||
6858 | GUINT_TO_POINTER (registration_id)); |
||
6859 | if (es == NULL) |
||
6860 | goto out; |
||
6861 | |||
6862 | g_warn_if_fail (g_hash_table_remove (connection->map_id_to_es, GUINT_TO_POINTER (es->id))); |
||
6863 | g_warn_if_fail (g_hash_table_remove (connection->map_object_path_to_es, es->object_path)); |
||
6864 | |||
6865 | ret = TRUE; |
||
6866 | |||
6867 | out: |
||
6868 | CONNECTION_UNLOCK (connection); |
||
6869 | |||
6870 | return ret; |
||
6871 | } |
||
6872 | |||
6873 | /* ---------------------------------------------------------------------------------------------------- */ |
||
6874 | |||
6875 | /* may be called in any thread, with connection's lock held */ |
||
6876 | static void |
||
6877 | handle_generic_ping_unlocked (GDBusConnection *connection, |
||
6878 | const gchar *object_path, |
||
6879 | GDBusMessage *message) |
||
6880 | { |
||
6881 | GDBusMessage *reply; |
||
6882 | reply = g_dbus_message_new_method_reply (message); |
||
6883 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
6884 | g_object_unref (reply); |
||
6885 | } |
||
6886 | |||
6887 | /* may be called in any thread, with connection's lock held */ |
||
6888 | static void |
||
6889 | handle_generic_get_machine_id_unlocked (GDBusConnection *connection, |
||
6890 | const gchar *object_path, |
||
6891 | GDBusMessage *message) |
||
6892 | { |
||
6893 | GDBusMessage *reply; |
||
6894 | |||
6895 | reply = NULL; |
||
6896 | if (connection->machine_id == NULL) |
||
6897 | { |
||
6898 | GError *error; |
||
6899 | |||
6900 | error = NULL; |
||
6901 | connection->machine_id = _g_dbus_get_machine_id (&error); |
||
6902 | if (connection->machine_id == NULL) |
||
6903 | { |
||
6904 | reply = g_dbus_message_new_method_error_literal (message, |
||
6905 | "org.freedesktop.DBus.Error.Failed", |
||
6906 | error->message); |
||
6907 | g_error_free (error); |
||
6908 | } |
||
6909 | } |
||
6910 | |||
6911 | if (reply == NULL) |
||
6912 | { |
||
6913 | reply = g_dbus_message_new_method_reply (message); |
||
6914 | g_dbus_message_set_body (reply, g_variant_new ("(s)", connection->machine_id)); |
||
6915 | } |
||
6916 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
6917 | g_object_unref (reply); |
||
6918 | } |
||
6919 | |||
6920 | /* may be called in any thread, with connection's lock held */ |
||
6921 | static void |
||
6922 | handle_generic_introspect_unlocked (GDBusConnection *connection, |
||
6923 | const gchar *object_path, |
||
6924 | GDBusMessage *message) |
||
6925 | { |
||
6926 | guint n; |
||
6927 | GString *s; |
||
6928 | gchar **registered; |
||
6929 | GDBusMessage *reply; |
||
6930 | |||
6931 | /* first the header */ |
||
6932 | s = g_string_new (NULL); |
||
6933 | introspect_append_header (s); |
||
6934 | |||
6935 | registered = g_dbus_connection_list_registered_unlocked (connection, object_path); |
||
6936 | for (n = 0; registered != NULL && registered[n] != NULL; n++) |
||
6937 | g_string_append_printf (s, " <node name=\"%s\"/>\n", registered[n]); |
||
6938 | g_strfreev (registered); |
||
6939 | g_string_append (s, "</node>\n"); |
||
6940 | |||
6941 | reply = g_dbus_message_new_method_reply (message); |
||
6942 | g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); |
||
6943 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
6944 | g_object_unref (reply); |
||
6945 | g_string_free (s, TRUE); |
||
6946 | } |
||
6947 | |||
6948 | /* may be called in any thread, with connection's lock held */ |
||
6949 | static gboolean |
||
6950 | handle_generic_unlocked (GDBusConnection *connection, |
||
6951 | GDBusMessage *message) |
||
6952 | { |
||
6953 | gboolean handled; |
||
6954 | const gchar *interface_name; |
||
6955 | const gchar *member; |
||
6956 | const gchar *signature; |
||
6957 | const gchar *path; |
||
6958 | |||
6959 | CONNECTION_ENSURE_LOCK (connection); |
||
6960 | |||
6961 | handled = FALSE; |
||
6962 | |||
6963 | interface_name = g_dbus_message_get_interface (message); |
||
6964 | member = g_dbus_message_get_member (message); |
||
6965 | signature = g_dbus_message_get_signature (message); |
||
6966 | path = g_dbus_message_get_path (message); |
||
6967 | |||
6968 | if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Introspectable") == 0 && |
||
6969 | g_strcmp0 (member, "Introspect") == 0 && |
||
6970 | g_strcmp0 (signature, "") == 0) |
||
6971 | { |
||
6972 | handle_generic_introspect_unlocked (connection, path, message); |
||
6973 | handled = TRUE; |
||
6974 | } |
||
6975 | else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Peer") == 0 && |
||
6976 | g_strcmp0 (member, "Ping") == 0 && |
||
6977 | g_strcmp0 (signature, "") == 0) |
||
6978 | { |
||
6979 | handle_generic_ping_unlocked (connection, path, message); |
||
6980 | handled = TRUE; |
||
6981 | } |
||
6982 | else if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Peer") == 0 && |
||
6983 | g_strcmp0 (member, "GetMachineId") == 0 && |
||
6984 | g_strcmp0 (signature, "") == 0) |
||
6985 | { |
||
6986 | handle_generic_get_machine_id_unlocked (connection, path, message); |
||
6987 | handled = TRUE; |
||
6988 | } |
||
6989 | |||
6990 | return handled; |
||
6991 | } |
||
6992 | |||
6993 | /* ---------------------------------------------------------------------------------------------------- */ |
||
6994 | |||
6995 | /* called in GDBusWorker thread with connection's lock held */ |
||
6996 | static void |
||
6997 | distribute_method_call (GDBusConnection *connection, |
||
6998 | GDBusMessage *message) |
||
6999 | { |
||
7000 | GDBusMessage *reply; |
||
7001 | ExportedObject *eo; |
||
7002 | ExportedSubtree *es; |
||
7003 | const gchar *object_path; |
||
7004 | const gchar *interface_name; |
||
7005 | const gchar *member; |
||
7006 | const gchar *path; |
||
7007 | gchar *subtree_path; |
||
7008 | gchar *needle; |
||
7009 | |||
7010 | g_assert (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL); |
||
7011 | |||
7012 | interface_name = g_dbus_message_get_interface (message); |
||
7013 | member = g_dbus_message_get_member (message); |
||
7014 | path = g_dbus_message_get_path (message); |
||
7015 | subtree_path = g_strdup (path); |
||
7016 | needle = strrchr (subtree_path, '/'); |
||
7017 | if (needle != NULL && needle != subtree_path) |
||
7018 | { |
||
7019 | *needle = '\0'; |
||
7020 | } |
||
7021 | else |
||
7022 | { |
||
7023 | g_free (subtree_path); |
||
7024 | subtree_path = NULL; |
||
7025 | } |
||
7026 | |||
7027 | |||
7028 | if (G_UNLIKELY (_g_dbus_debug_incoming ())) |
||
7029 | { |
||
7030 | _g_dbus_debug_print_lock (); |
||
7031 | g_print ("========================================================================\n" |
||
7032 | "GDBus-debug:Incoming:\n" |
||
7033 | " <<<< METHOD INVOCATION %s.%s()\n" |
||
7034 | " on object %s\n" |
||
7035 | " invoked by name %s\n" |
||
7036 | " serial %d\n", |
||
7037 | interface_name, member, |
||
7038 | path, |
||
7039 | g_dbus_message_get_sender (message) != NULL ? g_dbus_message_get_sender (message) : "(none)", |
||
7040 | g_dbus_message_get_serial (message)); |
||
7041 | _g_dbus_debug_print_unlock (); |
||
7042 | } |
||
7043 | |||
7044 | object_path = g_dbus_message_get_path (message); |
||
7045 | g_assert (object_path != NULL); |
||
7046 | |||
7047 | eo = g_hash_table_lookup (connection->map_object_path_to_eo, object_path); |
||
7048 | if (eo != NULL) |
||
7049 | { |
||
7050 | if (obj_message_func (connection, eo, message)) |
||
7051 | goto out; |
||
7052 | } |
||
7053 | |||
7054 | es = g_hash_table_lookup (connection->map_object_path_to_es, object_path); |
||
7055 | if (es != NULL) |
||
7056 | { |
||
7057 | if (subtree_message_func (connection, es, message)) |
||
7058 | goto out; |
||
7059 | } |
||
7060 | |||
7061 | if (subtree_path != NULL) |
||
7062 | { |
||
7063 | es = g_hash_table_lookup (connection->map_object_path_to_es, subtree_path); |
||
7064 | if (es != NULL) |
||
7065 | { |
||
7066 | if (subtree_message_func (connection, es, message)) |
||
7067 | goto out; |
||
7068 | } |
||
7069 | } |
||
7070 | |||
7071 | if (handle_generic_unlocked (connection, message)) |
||
7072 | goto out; |
||
7073 | |||
7074 | /* if we end up here, the message has not been not handled - so return an error saying this */ |
||
7075 | reply = g_dbus_message_new_method_error (message, |
||
7076 | "org.freedesktop.DBus.Error.UnknownMethod", |
||
7077 | _("No such interface '%s' on object at path %s"), |
||
7078 | interface_name, |
||
7079 | object_path); |
||
7080 | g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); |
||
7081 | g_object_unref (reply); |
||
7082 | |||
7083 | out: |
||
7084 | g_free (subtree_path); |
||
7085 | } |
||
7086 | |||
7087 | /* ---------------------------------------------------------------------------------------------------- */ |
||
7088 | |||
7089 | /* Called in any user thread, with the message_bus_lock held. */ |
||
7090 | static GWeakRef * |
||
7091 | message_bus_get_singleton (GBusType bus_type, |
||
7092 | GError **error) |
||
7093 | { |
||
7094 | GWeakRef *ret; |
||
7095 | const gchar *starter_bus; |
||
7096 | |||
7097 | ret = NULL; |
||
7098 | |||
7099 | switch (bus_type) |
||
7100 | { |
||
7101 | case G_BUS_TYPE_SESSION: |
||
7102 | ret = &the_session_bus; |
||
7103 | break; |
||
7104 | |||
7105 | case G_BUS_TYPE_SYSTEM: |
||
7106 | ret = &the_system_bus; |
||
7107 | break; |
||
7108 | |||
7109 | case G_BUS_TYPE_STARTER: |
||
7110 | starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE"); |
||
7111 | if (g_strcmp0 (starter_bus, "session") == 0) |
||
7112 | { |
||
7113 | ret = message_bus_get_singleton (G_BUS_TYPE_SESSION, error); |
||
7114 | goto out; |
||
7115 | } |
||
7116 | else if (g_strcmp0 (starter_bus, "system") == 0) |
||
7117 | { |
||
7118 | ret = message_bus_get_singleton (G_BUS_TYPE_SYSTEM, error); |
||
7119 | goto out; |
||
7120 | } |
||
7121 | else |
||
7122 | { |
||
7123 | if (starter_bus != NULL) |
||
7124 | { |
||
7125 | g_set_error (error, |
||
7126 | G_IO_ERROR, |
||
7127 | G_IO_ERROR_INVALID_ARGUMENT, |
||
7128 | _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable" |
||
7129 | " - unknown value '%s'"), |
||
7130 | starter_bus); |
||
7131 | } |
||
7132 | else |
||
7133 | { |
||
7134 | g_set_error_literal (error, |
||
7135 | G_IO_ERROR, |
||
7136 | G_IO_ERROR_INVALID_ARGUMENT, |
||
7137 | _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment " |
||
7138 | "variable is not set")); |
||
7139 | } |
||
7140 | } |
||
7141 | break; |
||
7142 | |||
7143 | default: |
||
7144 | g_assert_not_reached (); |
||
7145 | break; |
||
7146 | } |
||
7147 | |||
7148 | out: |
||
7149 | return ret; |
||
7150 | } |
||
7151 | |||
7152 | /* Called in any user thread, without holding locks. */ |
||
7153 | static GDBusConnection * |
||
7154 | get_uninitialized_connection (GBusType bus_type, |
||
7155 | GCancellable *cancellable, |
||
7156 | GError **error) |
||
7157 | { |
||
7158 | GWeakRef *singleton; |
||
7159 | GDBusConnection *ret; |
||
7160 | |||
7161 | ret = NULL; |
||
7162 | |||
7163 | G_LOCK (message_bus_lock); |
||
7164 | singleton = message_bus_get_singleton (bus_type, error); |
||
7165 | if (singleton == NULL) |
||
7166 | goto out; |
||
7167 | |||
7168 | ret = g_weak_ref_get (singleton); |
||
7169 | |||
7170 | if (ret == NULL) |
||
7171 | { |
||
7172 | gchar *address; |
||
7173 | address = g_dbus_address_get_for_bus_sync (bus_type, cancellable, error); |
||
7174 | if (address == NULL) |
||
7175 | goto out; |
||
7176 | ret = g_object_new (G_TYPE_DBUS_CONNECTION, |
||
7177 | "address", address, |
||
7178 | "flags", G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | |
||
7179 | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, |
||
7180 | "exit-on-close", TRUE, |
||
7181 | NULL); |
||
7182 | |||
7183 | g_weak_ref_set (singleton, ret); |
||
7184 | g_free (address); |
||
7185 | } |
||
7186 | |||
7187 | g_assert (ret != NULL); |
||
7188 | |||
7189 | out: |
||
7190 | G_UNLOCK (message_bus_lock); |
||
7191 | return ret; |
||
7192 | } |
||
7193 | |||
7194 | /* May be called from any thread. Must not hold message_bus_lock. */ |
||
7195 | GDBusConnection * |
||
7196 | _g_bus_get_singleton_if_exists (GBusType bus_type) |
||
7197 | { |
||
7198 | GWeakRef *singleton; |
||
7199 | GDBusConnection *ret = NULL; |
||
7200 | |||
7201 | G_LOCK (message_bus_lock); |
||
7202 | singleton = message_bus_get_singleton (bus_type, NULL); |
||
7203 | if (singleton == NULL) |
||
7204 | goto out; |
||
7205 | |||
7206 | ret = g_weak_ref_get (singleton); |
||
7207 | |||
7208 | out: |
||
7209 | G_UNLOCK (message_bus_lock); |
||
7210 | return ret; |
||
7211 | } |
||
7212 | |||
7213 | /** |
||
7214 | * g_bus_get_sync: |
||
7215 | * @bus_type: a #GBusType |
||
7216 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
7217 | * @error: return location for error or %NULL |
||
7218 | * |
||
7219 | * Synchronously connects to the message bus specified by @bus_type. |
||
7220 | * Note that the returned object may shared with other callers, |
||
7221 | * e.g. if two separate parts of a process calls this function with |
||
7222 | * the same @bus_type, they will share the same object. |
||
7223 | * |
||
7224 | * This is a synchronous failable function. See g_bus_get() and |
||
7225 | * g_bus_get_finish() for the asynchronous version. |
||
7226 | * |
||
7227 | * The returned object is a singleton, that is, shared with other |
||
7228 | * callers of g_bus_get() and g_bus_get_sync() for @bus_type. In the |
||
7229 | * event that you need a private message bus connection, use |
||
7230 | * g_dbus_address_get_for_bus_sync() and |
||
7231 | * g_dbus_connection_new_for_address(). |
||
7232 | * |
||
7233 | * Note that the returned #GDBusConnection object will (usually) have |
||
7234 | * the #GDBusConnection:exit-on-close property set to %TRUE. |
||
7235 | * |
||
7236 | * Returns: (transfer full): a #GDBusConnection or %NULL if @error is set. |
||
7237 | * Free with g_object_unref(). |
||
7238 | * |
||
7239 | * Since: 2.26 |
||
7240 | */ |
||
7241 | GDBusConnection * |
||
7242 | g_bus_get_sync (GBusType bus_type, |
||
7243 | GCancellable *cancellable, |
||
7244 | GError **error) |
||
7245 | { |
||
7246 | GDBusConnection *connection; |
||
7247 | |||
7248 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
7249 | |||
7250 | connection = get_uninitialized_connection (bus_type, cancellable, error); |
||
7251 | if (connection == NULL) |
||
7252 | goto out; |
||
7253 | |||
7254 | if (!g_initable_init (G_INITABLE (connection), cancellable, error)) |
||
7255 | { |
||
7256 | g_object_unref (connection); |
||
7257 | connection = NULL; |
||
7258 | } |
||
7259 | |||
7260 | out: |
||
7261 | return connection; |
||
7262 | } |
||
7263 | |||
7264 | static void |
||
7265 | bus_get_async_initable_cb (GObject *source_object, |
||
7266 | GAsyncResult *res, |
||
7267 | gpointer user_data) |
||
7268 | { |
||
7269 | GTask *task = user_data; |
||
7270 | GError *error = NULL; |
||
7271 | |||
7272 | if (!g_async_initable_init_finish (G_ASYNC_INITABLE (source_object), |
||
7273 | res, |
||
7274 | &error)) |
||
7275 | { |
||
7276 | g_assert (error != NULL); |
||
7277 | g_task_return_error (task, error); |
||
7278 | g_object_unref (source_object); |
||
7279 | } |
||
7280 | else |
||
7281 | { |
||
7282 | g_task_return_pointer (task, source_object, g_object_unref); |
||
7283 | } |
||
7284 | g_object_unref (task); |
||
7285 | } |
||
7286 | |||
7287 | /** |
||
7288 | * g_bus_get: |
||
7289 | * @bus_type: a #GBusType |
||
7290 | * @cancellable: (allow-none): a #GCancellable or %NULL |
||
7291 | * @callback: a #GAsyncReadyCallback to call when the request is satisfied |
||
7292 | * @user_data: the data to pass to @callback |
||
7293 | * |
||
7294 | * Asynchronously connects to the message bus specified by @bus_type. |
||
7295 | * |
||
7296 | * When the operation is finished, @callback will be invoked. You can |
||
7297 | * then call g_bus_get_finish() to get the result of the operation. |
||
7298 | * |
||
7299 | * This is a asynchronous failable function. See g_bus_get_sync() for |
||
7300 | * the synchronous version. |
||
7301 | * |
||
7302 | * Since: 2.26 |
||
7303 | */ |
||
7304 | void |
||
7305 | g_bus_get (GBusType bus_type, |
||
7306 | GCancellable *cancellable, |
||
7307 | GAsyncReadyCallback callback, |
||
7308 | gpointer user_data) |
||
7309 | { |
||
7310 | GDBusConnection *connection; |
||
7311 | GTask *task; |
||
7312 | GError *error = NULL; |
||
7313 | |||
7314 | task = g_task_new (NULL, cancellable, callback, user_data); |
||
7315 | |||
7316 | connection = get_uninitialized_connection (bus_type, cancellable, &error); |
||
7317 | if (connection == NULL) |
||
7318 | { |
||
7319 | g_assert (error != NULL); |
||
7320 | g_task_return_error (task, error); |
||
7321 | g_object_unref (task); |
||
7322 | } |
||
7323 | else |
||
7324 | { |
||
7325 | g_async_initable_init_async (G_ASYNC_INITABLE (connection), |
||
7326 | G_PRIORITY_DEFAULT, |
||
7327 | cancellable, |
||
7328 | bus_get_async_initable_cb, |
||
7329 | task); |
||
7330 | } |
||
7331 | } |
||
7332 | |||
7333 | /** |
||
7334 | * g_bus_get_finish: |
||
7335 | * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed |
||
7336 | * to g_bus_get() |
||
7337 | * @error: return location for error or %NULL |
||
7338 | * |
||
7339 | * Finishes an operation started with g_bus_get(). |
||
7340 | * |
||
7341 | * The returned object is a singleton, that is, shared with other |
||
7342 | * callers of g_bus_get() and g_bus_get_sync() for @bus_type. In the |
||
7343 | * event that you need a private message bus connection, use |
||
7344 | * g_dbus_address_get_for_bus_sync() and |
||
7345 | * g_dbus_connection_new_for_address(). |
||
7346 | * |
||
7347 | * Note that the returned #GDBusConnection object will (usually) have |
||
7348 | * the #GDBusConnection:exit-on-close property set to %TRUE. |
||
7349 | * |
||
7350 | * Returns: (transfer full): a #GDBusConnection or %NULL if @error is set. |
||
7351 | * Free with g_object_unref(). |
||
7352 | * |
||
7353 | * Since: 2.26 |
||
7354 | */ |
||
7355 | GDBusConnection * |
||
7356 | g_bus_get_finish (GAsyncResult *res, |
||
7357 | GError **error) |
||
7358 | { |
||
7359 | g_return_val_if_fail (g_task_is_valid (res, NULL), NULL); |
||
7360 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
7361 | |||
7362 | return g_task_propagate_pointer (G_TASK (res), error); |
||
7363 | } |
||
7364 | |||
7365 | /* ---------------------------------------------------------------------------------------------------- */ |