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.1 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 | #include "config.h" |
22 | |
23 | #include <stdlib.h> |
24 | |
25 | #include "gdbusutils.h" |
26 | #include "gdbusnameowning.h" |
27 | #include "gdbuserror.h" |
28 | #include "gdbusprivate.h" |
29 | #include "gdbusconnection.h" |
30 | |
31 | #include "glibintl.h" |
32 | |
33 | /** |
34 | * SECTION:gdbusnameowning |
35 | * @title: Owning Bus Names |
36 | * @short_description: Simple API for owning bus names |
37 | * @include: gio/gio.h |
38 | * |
39 | * Convenience API for owning bus names. |
40 | * |
41 | * A simple example for owning a name can be found in |
42 | * [gdbus-example-own-name.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-example-own-name.c) |
43 | */ |
44 | |
45 | G_LOCK_DEFINE_STATIC (lock); |
46 | |
47 | /* ---------------------------------------------------------------------------------------------------- */ |
48 | |
49 | typedef enum |
50 | { |
51 | PREVIOUS_CALL_NONE = 0, |
52 | PREVIOUS_CALL_ACQUIRED, |
53 | PREVIOUS_CALL_LOST, |
54 | } PreviousCall; |
55 | |
56 | typedef struct |
57 | { |
58 | gint ref_count; /* (atomic) */ |
59 | guint id; |
60 | GBusNameOwnerFlags flags; |
61 | gchar *name; |
62 | GBusAcquiredCallback bus_acquired_handler; |
63 | GBusNameAcquiredCallback name_acquired_handler; |
64 | GBusNameLostCallback name_lost_handler; |
65 | gpointer user_data; |
66 | GDestroyNotify user_data_free_func; |
67 | GMainContext *main_context; |
68 | |
69 | PreviousCall previous_call; |
70 | |
71 | GDBusConnection *connection; |
72 | gulong disconnected_signal_handler_id; |
73 | guint name_acquired_subscription_id; |
74 | guint name_lost_subscription_id; |
75 | |
76 | gboolean cancelled; /* must hold lock when reading or modifying */ |
77 | |
78 | gboolean needs_release; |
79 | } Client; |
80 | |
81 | static guint next_global_id = 1; |
82 | static GHashTable *map_id_to_client = NULL; |
83 | |
84 | |
85 | static Client * |
86 | client_ref (Client *client) |
87 | { |
88 | g_atomic_int_inc (&client->ref_count); |
89 | return client; |
90 | } |
91 | |
92 | static void |
93 | client_unref (Client *client) |
94 | { |
95 | if (g_atomic_int_dec_and_test (&client->ref_count)) |
96 | { |
97 | if (client->connection != NULL) |
98 | { |
99 | if (client->disconnected_signal_handler_id > 0) |
100 | g_signal_handler_disconnect (instance: client->connection, handler_id: client->disconnected_signal_handler_id); |
101 | if (client->name_acquired_subscription_id > 0) |
102 | g_dbus_connection_signal_unsubscribe (connection: client->connection, subscription_id: client->name_acquired_subscription_id); |
103 | if (client->name_lost_subscription_id > 0) |
104 | g_dbus_connection_signal_unsubscribe (connection: client->connection, subscription_id: client->name_lost_subscription_id); |
105 | g_object_unref (object: client->connection); |
106 | } |
107 | g_main_context_unref (context: client->main_context); |
108 | g_free (mem: client->name); |
109 | if (client->user_data_free_func != NULL) |
110 | client->user_data_free_func (client->user_data); |
111 | g_free (mem: client); |
112 | } |
113 | } |
114 | |
115 | /* ---------------------------------------------------------------------------------------------------- */ |
116 | |
117 | |
118 | typedef enum |
119 | { |
120 | CALL_TYPE_NAME_ACQUIRED, |
121 | CALL_TYPE_NAME_LOST |
122 | } CallType; |
123 | |
124 | typedef struct |
125 | { |
126 | Client *client; |
127 | |
128 | /* keep this separate because client->connection may |
129 | * be set to NULL after scheduling the call |
130 | */ |
131 | GDBusConnection *connection; |
132 | |
133 | /* set to TRUE to call acquired */ |
134 | CallType call_type; |
135 | } CallHandlerData; |
136 | |
137 | static void |
138 | call_handler_data_free (CallHandlerData *data) |
139 | { |
140 | if (data->connection != NULL) |
141 | g_object_unref (object: data->connection); |
142 | client_unref (client: data->client); |
143 | g_free (mem: data); |
144 | } |
145 | |
146 | static void |
147 | actually_do_call (Client *client, GDBusConnection *connection, CallType call_type) |
148 | { |
149 | switch (call_type) |
150 | { |
151 | case CALL_TYPE_NAME_ACQUIRED: |
152 | if (client->name_acquired_handler != NULL) |
153 | { |
154 | client->name_acquired_handler (connection, |
155 | client->name, |
156 | client->user_data); |
157 | } |
158 | break; |
159 | |
160 | case CALL_TYPE_NAME_LOST: |
161 | if (client->name_lost_handler != NULL) |
162 | { |
163 | client->name_lost_handler (connection, |
164 | client->name, |
165 | client->user_data); |
166 | } |
167 | break; |
168 | |
169 | default: |
170 | g_assert_not_reached (); |
171 | break; |
172 | } |
173 | } |
174 | |
175 | static gboolean |
176 | call_in_idle_cb (gpointer _data) |
177 | { |
178 | CallHandlerData *data = _data; |
179 | actually_do_call (client: data->client, connection: data->connection, call_type: data->call_type); |
180 | return FALSE; |
181 | } |
182 | |
183 | static void |
184 | schedule_call_in_idle (Client *client, CallType call_type) |
185 | { |
186 | CallHandlerData *data; |
187 | GSource *idle_source; |
188 | |
189 | data = g_new0 (CallHandlerData, 1); |
190 | data->client = client_ref (client); |
191 | data->connection = client->connection != NULL ? g_object_ref (client->connection) : NULL; |
192 | data->call_type = call_type; |
193 | |
194 | idle_source = g_idle_source_new (); |
195 | g_source_set_priority (source: idle_source, G_PRIORITY_HIGH); |
196 | g_source_set_callback (source: idle_source, |
197 | func: call_in_idle_cb, |
198 | data, |
199 | notify: (GDestroyNotify) call_handler_data_free); |
200 | g_source_set_name (source: idle_source, name: "[gio, gdbusnameowning.c] call_in_idle_cb" ); |
201 | g_source_attach (source: idle_source, context: client->main_context); |
202 | g_source_unref (source: idle_source); |
203 | } |
204 | |
205 | static void |
206 | do_call (Client *client, CallType call_type) |
207 | { |
208 | GMainContext *current_context; |
209 | |
210 | /* only schedule in idle if we're not in the right thread */ |
211 | current_context = g_main_context_ref_thread_default (); |
212 | if (current_context != client->main_context) |
213 | schedule_call_in_idle (client, call_type); |
214 | else |
215 | actually_do_call (client, connection: client->connection, call_type); |
216 | g_main_context_unref (context: current_context); |
217 | } |
218 | |
219 | static void |
220 | call_acquired_handler (Client *client) |
221 | { |
222 | G_LOCK (lock); |
223 | if (client->previous_call != PREVIOUS_CALL_ACQUIRED) |
224 | { |
225 | client->previous_call = PREVIOUS_CALL_ACQUIRED; |
226 | if (!client->cancelled) |
227 | { |
228 | G_UNLOCK (lock); |
229 | do_call (client, call_type: CALL_TYPE_NAME_ACQUIRED); |
230 | goto out; |
231 | } |
232 | } |
233 | G_UNLOCK (lock); |
234 | out: |
235 | ; |
236 | } |
237 | |
238 | static void |
239 | call_lost_handler (Client *client) |
240 | { |
241 | G_LOCK (lock); |
242 | if (client->previous_call != PREVIOUS_CALL_LOST) |
243 | { |
244 | client->previous_call = PREVIOUS_CALL_LOST; |
245 | if (!client->cancelled) |
246 | { |
247 | G_UNLOCK (lock); |
248 | do_call (client, call_type: CALL_TYPE_NAME_LOST); |
249 | goto out; |
250 | } |
251 | } |
252 | G_UNLOCK (lock); |
253 | out: |
254 | ; |
255 | } |
256 | |
257 | /* ---------------------------------------------------------------------------------------------------- */ |
258 | |
259 | static void |
260 | on_name_lost_or_acquired (GDBusConnection *connection, |
261 | const gchar *sender_name, |
262 | const gchar *object_path, |
263 | const gchar *interface_name, |
264 | const gchar *signal_name, |
265 | GVariant *parameters, |
266 | gpointer user_data) |
267 | { |
268 | Client *client = user_data; |
269 | const gchar *name; |
270 | |
271 | if (g_strcmp0 (str1: object_path, str2: "/org/freedesktop/DBus" ) != 0 || |
272 | g_strcmp0 (str1: interface_name, str2: "org.freedesktop.DBus" ) != 0 || |
273 | g_strcmp0 (str1: sender_name, str2: "org.freedesktop.DBus" ) != 0) |
274 | goto out; |
275 | |
276 | if (!g_variant_is_of_type (value: parameters, G_VARIANT_TYPE ("(s)" ))) |
277 | { |
278 | g_warning ("%s signal had unexpected signature %s" , signal_name, |
279 | g_variant_get_type_string (parameters)); |
280 | goto out; |
281 | } |
282 | |
283 | if (g_strcmp0 (str1: signal_name, str2: "NameLost" ) == 0) |
284 | { |
285 | g_variant_get (value: parameters, format_string: "(&s)" , &name); |
286 | if (g_strcmp0 (str1: name, str2: client->name) == 0) |
287 | { |
288 | call_lost_handler (client); |
289 | } |
290 | } |
291 | else if (g_strcmp0 (str1: signal_name, str2: "NameAcquired" ) == 0) |
292 | { |
293 | g_variant_get (value: parameters, format_string: "(&s)" , &name); |
294 | if (g_strcmp0 (str1: name, str2: client->name) == 0) |
295 | { |
296 | call_acquired_handler (client); |
297 | } |
298 | } |
299 | out: |
300 | ; |
301 | } |
302 | |
303 | /* ---------------------------------------------------------------------------------------------------- */ |
304 | |
305 | static void |
306 | request_name_cb (GObject *source_object, |
307 | GAsyncResult *res, |
308 | gpointer user_data) |
309 | { |
310 | Client *client = user_data; |
311 | GVariant *result; |
312 | guint32 request_name_reply; |
313 | gboolean unsubscribe; |
314 | |
315 | request_name_reply = 0; |
316 | result = NULL; |
317 | |
318 | /* don't use client->connection - it may be NULL already */ |
319 | result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), |
320 | res, |
321 | NULL); |
322 | if (result != NULL) |
323 | { |
324 | g_variant_get (value: result, format_string: "(u)" , &request_name_reply); |
325 | g_variant_unref (value: result); |
326 | } |
327 | |
328 | unsubscribe = FALSE; |
329 | |
330 | switch (request_name_reply) |
331 | { |
332 | case 1: /* DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER */ |
333 | /* We got the name - now listen for NameLost and NameAcquired */ |
334 | call_acquired_handler (client); |
335 | break; |
336 | |
337 | case 2: /* DBUS_REQUEST_NAME_REPLY_IN_QUEUE */ |
338 | /* Waiting in line - listen for NameLost and NameAcquired */ |
339 | call_lost_handler (client); |
340 | break; |
341 | |
342 | default: |
343 | /* assume we couldn't get the name - explicit fallthrough */ |
344 | case 3: /* DBUS_REQUEST_NAME_REPLY_EXISTS */ |
345 | case 4: /* DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER */ |
346 | /* Some other part of the process is already owning the name */ |
347 | call_lost_handler (client); |
348 | unsubscribe = TRUE; |
349 | client->needs_release = FALSE; |
350 | break; |
351 | } |
352 | |
353 | /* If we’re not the owner and not in the queue, there’s no point in continuing |
354 | * to listen to NameAcquired or NameLost. */ |
355 | if (unsubscribe) |
356 | { |
357 | GDBusConnection *connection = NULL; |
358 | |
359 | /* make sure we use a known good Connection object since it may be set to |
360 | * NULL at any point after being cancelled |
361 | */ |
362 | G_LOCK (lock); |
363 | if (!client->cancelled) |
364 | connection = g_object_ref (client->connection); |
365 | G_UNLOCK (lock); |
366 | |
367 | if (connection != NULL) |
368 | { |
369 | if (client->name_acquired_subscription_id > 0) |
370 | g_dbus_connection_signal_unsubscribe (connection: client->connection, subscription_id: client->name_acquired_subscription_id); |
371 | if (client->name_lost_subscription_id > 0) |
372 | g_dbus_connection_signal_unsubscribe (connection: client->connection, subscription_id: client->name_lost_subscription_id); |
373 | client->name_acquired_subscription_id = 0; |
374 | client->name_lost_subscription_id = 0; |
375 | |
376 | g_object_unref (object: connection); |
377 | } |
378 | } |
379 | |
380 | client_unref (client); |
381 | } |
382 | |
383 | /* ---------------------------------------------------------------------------------------------------- */ |
384 | |
385 | static void |
386 | on_connection_disconnected (GDBusConnection *connection, |
387 | gboolean remote_peer_vanished, |
388 | GError *error, |
389 | gpointer user_data) |
390 | { |
391 | Client *client = user_data; |
392 | |
393 | if (client->disconnected_signal_handler_id > 0) |
394 | g_signal_handler_disconnect (instance: client->connection, handler_id: client->disconnected_signal_handler_id); |
395 | if (client->name_acquired_subscription_id > 0) |
396 | g_dbus_connection_signal_unsubscribe (connection: client->connection, subscription_id: client->name_acquired_subscription_id); |
397 | if (client->name_lost_subscription_id > 0) |
398 | g_dbus_connection_signal_unsubscribe (connection: client->connection, subscription_id: client->name_lost_subscription_id); |
399 | g_object_unref (object: client->connection); |
400 | client->disconnected_signal_handler_id = 0; |
401 | client->name_acquired_subscription_id = 0; |
402 | client->name_lost_subscription_id = 0; |
403 | client->connection = NULL; |
404 | |
405 | call_lost_handler (client); |
406 | } |
407 | |
408 | /* ---------------------------------------------------------------------------------------------------- */ |
409 | |
410 | static void |
411 | has_connection (Client *client) |
412 | { |
413 | /* listen for disconnection */ |
414 | client->disconnected_signal_handler_id = g_signal_connect (client->connection, |
415 | "closed" , |
416 | G_CALLBACK (on_connection_disconnected), |
417 | client); |
418 | |
419 | /* Start listening to NameLost and NameAcquired messages. We hold |
420 | * references to the Client in the signal closures, since it’s possible |
421 | * for a signal to be in-flight after unsubscribing the signal handler. |
422 | * This creates a reference count cycle, but that’s explicitly broken by |
423 | * disconnecting the signal handlers before calling client_unref() in |
424 | * g_bus_unown_name(). |
425 | * |
426 | * Subscribe to NameLost and NameAcquired before calling RequestName() to |
427 | * avoid the potential race of losing the name between receiving a reply to |
428 | * RequestName() and subscribing to NameLost. The #PreviousCall state will |
429 | * ensure that the user callbacks get called an appropriate number of times. */ |
430 | client->name_lost_subscription_id = |
431 | g_dbus_connection_signal_subscribe (connection: client->connection, |
432 | sender: "org.freedesktop.DBus" , |
433 | interface_name: "org.freedesktop.DBus" , |
434 | member: "NameLost" , |
435 | object_path: "/org/freedesktop/DBus" , |
436 | arg0: client->name, |
437 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
438 | callback: on_name_lost_or_acquired, |
439 | user_data: client_ref (client), |
440 | user_data_free_func: (GDestroyNotify) client_unref); |
441 | client->name_acquired_subscription_id = |
442 | g_dbus_connection_signal_subscribe (connection: client->connection, |
443 | sender: "org.freedesktop.DBus" , |
444 | interface_name: "org.freedesktop.DBus" , |
445 | member: "NameAcquired" , |
446 | object_path: "/org/freedesktop/DBus" , |
447 | arg0: client->name, |
448 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
449 | callback: on_name_lost_or_acquired, |
450 | user_data: client_ref (client), |
451 | user_data_free_func: (GDestroyNotify) client_unref); |
452 | |
453 | /* attempt to acquire the name */ |
454 | client->needs_release = TRUE; |
455 | g_dbus_connection_call (connection: client->connection, |
456 | bus_name: "org.freedesktop.DBus" , /* bus name */ |
457 | object_path: "/org/freedesktop/DBus" , /* object path */ |
458 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
459 | method_name: "RequestName" , /* method name */ |
460 | parameters: g_variant_new (format_string: "(su)" , |
461 | client->name, |
462 | client->flags), |
463 | G_VARIANT_TYPE ("(u)" ), |
464 | flags: G_DBUS_CALL_FLAGS_NONE, |
465 | timeout_msec: -1, |
466 | NULL, |
467 | callback: (GAsyncReadyCallback) request_name_cb, |
468 | user_data: client_ref (client)); |
469 | } |
470 | |
471 | |
472 | static void |
473 | connection_get_cb (GObject *source_object, |
474 | GAsyncResult *res, |
475 | gpointer user_data) |
476 | { |
477 | Client *client = user_data; |
478 | |
479 | /* must not do anything if already cancelled */ |
480 | G_LOCK (lock); |
481 | if (client->cancelled) |
482 | { |
483 | G_UNLOCK (lock); |
484 | goto out; |
485 | } |
486 | G_UNLOCK (lock); |
487 | |
488 | client->connection = g_bus_get_finish (res, NULL); |
489 | if (client->connection == NULL) |
490 | { |
491 | call_lost_handler (client); |
492 | goto out; |
493 | } |
494 | |
495 | /* No need to schedule this in idle as we're already in the thread |
496 | * that the user called g_bus_own_name() from. This is because |
497 | * g_bus_get() guarantees that. |
498 | * |
499 | * Also, we need to ensure that the handler is invoked *before* |
500 | * we call RequestName(). Otherwise there is a race. |
501 | */ |
502 | if (client->bus_acquired_handler != NULL) |
503 | { |
504 | client->bus_acquired_handler (client->connection, |
505 | client->name, |
506 | client->user_data); |
507 | } |
508 | |
509 | has_connection (client); |
510 | |
511 | out: |
512 | client_unref (client); |
513 | } |
514 | |
515 | /* ---------------------------------------------------------------------------------------------------- */ |
516 | |
517 | /** |
518 | * g_bus_own_name_on_connection: |
519 | * @connection: a #GDBusConnection |
520 | * @name: the well-known name to own |
521 | * @flags: a set of flags from the #GBusNameOwnerFlags enumeration |
522 | * @name_acquired_handler: (nullable): handler to invoke when @name is acquired or %NULL |
523 | * @name_lost_handler: (nullable): handler to invoke when @name is lost or %NULL |
524 | * @user_data: user data to pass to handlers |
525 | * @user_data_free_func: (nullable): function for freeing @user_data or %NULL |
526 | * |
527 | * Like g_bus_own_name() but takes a #GDBusConnection instead of a |
528 | * #GBusType. |
529 | * |
530 | * Returns: an identifier (never 0) that can be used with |
531 | * g_bus_unown_name() to stop owning the name |
532 | * |
533 | * Since: 2.26 |
534 | */ |
535 | guint |
536 | g_bus_own_name_on_connection (GDBusConnection *connection, |
537 | const gchar *name, |
538 | GBusNameOwnerFlags flags, |
539 | GBusNameAcquiredCallback name_acquired_handler, |
540 | GBusNameLostCallback name_lost_handler, |
541 | gpointer user_data, |
542 | GDestroyNotify user_data_free_func) |
543 | { |
544 | Client *client; |
545 | |
546 | g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); |
547 | g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), 0); |
548 | |
549 | G_LOCK (lock); |
550 | |
551 | client = g_new0 (Client, 1); |
552 | client->ref_count = 1; |
553 | client->id = next_global_id++; /* TODO: uh oh, handle overflow */ |
554 | client->name = g_strdup (str: name); |
555 | client->flags = flags; |
556 | client->name_acquired_handler = name_acquired_handler; |
557 | client->name_lost_handler = name_lost_handler; |
558 | client->user_data = user_data; |
559 | client->user_data_free_func = user_data_free_func; |
560 | client->main_context = g_main_context_ref_thread_default (); |
561 | |
562 | client->connection = g_object_ref (connection); |
563 | |
564 | if (map_id_to_client == NULL) |
565 | { |
566 | map_id_to_client = g_hash_table_new (hash_func: g_direct_hash, key_equal_func: g_direct_equal); |
567 | } |
568 | g_hash_table_insert (hash_table: map_id_to_client, |
569 | GUINT_TO_POINTER (client->id), |
570 | value: client); |
571 | |
572 | G_UNLOCK (lock); |
573 | |
574 | has_connection (client); |
575 | |
576 | return client->id; |
577 | } |
578 | |
579 | /** |
580 | * g_bus_own_name: |
581 | * @bus_type: the type of bus to own a name on |
582 | * @name: the well-known name to own |
583 | * @flags: a set of flags from the #GBusNameOwnerFlags enumeration |
584 | * @bus_acquired_handler: (nullable): handler to invoke when connected to the bus of type @bus_type or %NULL |
585 | * @name_acquired_handler: (nullable): handler to invoke when @name is acquired or %NULL |
586 | * @name_lost_handler: (nullable): handler to invoke when @name is lost or %NULL |
587 | * @user_data: user data to pass to handlers |
588 | * @user_data_free_func: (nullable): function for freeing @user_data or %NULL |
589 | * |
590 | * Starts acquiring @name on the bus specified by @bus_type and calls |
591 | * @name_acquired_handler and @name_lost_handler when the name is |
592 | * acquired respectively lost. Callbacks will be invoked in the |
593 | * [thread-default main context][g-main-context-push-thread-default] |
594 | * of the thread you are calling this function from. |
595 | * |
596 | * You are guaranteed that one of the @name_acquired_handler and @name_lost_handler |
597 | * callbacks will be invoked after calling this function - there are three |
598 | * possible cases: |
599 | * |
600 | * - @name_lost_handler with a %NULL connection (if a connection to the bus |
601 | * can't be made). |
602 | * |
603 | * - @bus_acquired_handler then @name_lost_handler (if the name can't be |
604 | * obtained) |
605 | * |
606 | * - @bus_acquired_handler then @name_acquired_handler (if the name was |
607 | * obtained). |
608 | * |
609 | * When you are done owning the name, just call g_bus_unown_name() |
610 | * with the owner id this function returns. |
611 | * |
612 | * If the name is acquired or lost (for example another application |
613 | * could acquire the name if you allow replacement or the application |
614 | * currently owning the name exits), the handlers are also invoked. |
615 | * If the #GDBusConnection that is used for attempting to own the name |
616 | * closes, then @name_lost_handler is invoked since it is no longer |
617 | * possible for other processes to access the process. |
618 | * |
619 | * You cannot use g_bus_own_name() several times for the same name (unless |
620 | * interleaved with calls to g_bus_unown_name()) - only the first call |
621 | * will work. |
622 | * |
623 | * Another guarantee is that invocations of @name_acquired_handler |
624 | * and @name_lost_handler are guaranteed to alternate; that |
625 | * is, if @name_acquired_handler is invoked then you are |
626 | * guaranteed that the next time one of the handlers is invoked, it |
627 | * will be @name_lost_handler. The reverse is also true. |
628 | * |
629 | * If you plan on exporting objects (using e.g. |
630 | * g_dbus_connection_register_object()), note that it is generally too late |
631 | * to export the objects in @name_acquired_handler. Instead, you can do this |
632 | * in @bus_acquired_handler since you are guaranteed that this will run |
633 | * before @name is requested from the bus. |
634 | * |
635 | * This behavior makes it very simple to write applications that wants |
636 | * to [own names][gdbus-owning-names] and export objects. |
637 | * Simply register objects to be exported in @bus_acquired_handler and |
638 | * unregister the objects (if any) in @name_lost_handler. |
639 | * |
640 | * Returns: an identifier (never 0) that can be used with |
641 | * g_bus_unown_name() to stop owning the name. |
642 | * |
643 | * Since: 2.26 |
644 | */ |
645 | guint |
646 | g_bus_own_name (GBusType bus_type, |
647 | const gchar *name, |
648 | GBusNameOwnerFlags flags, |
649 | GBusAcquiredCallback bus_acquired_handler, |
650 | GBusNameAcquiredCallback name_acquired_handler, |
651 | GBusNameLostCallback name_lost_handler, |
652 | gpointer user_data, |
653 | GDestroyNotify user_data_free_func) |
654 | { |
655 | Client *client; |
656 | |
657 | g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), 0); |
658 | |
659 | G_LOCK (lock); |
660 | |
661 | client = g_new0 (Client, 1); |
662 | client->ref_count = 1; |
663 | client->id = next_global_id++; /* TODO: uh oh, handle overflow */ |
664 | client->name = g_strdup (str: name); |
665 | client->flags = flags; |
666 | client->bus_acquired_handler = bus_acquired_handler; |
667 | client->name_acquired_handler = name_acquired_handler; |
668 | client->name_lost_handler = name_lost_handler; |
669 | client->user_data = user_data; |
670 | client->user_data_free_func = user_data_free_func; |
671 | client->main_context = g_main_context_ref_thread_default (); |
672 | |
673 | if (map_id_to_client == NULL) |
674 | { |
675 | map_id_to_client = g_hash_table_new (hash_func: g_direct_hash, key_equal_func: g_direct_equal); |
676 | } |
677 | g_hash_table_insert (hash_table: map_id_to_client, |
678 | GUINT_TO_POINTER (client->id), |
679 | value: client); |
680 | |
681 | g_bus_get (bus_type, |
682 | NULL, |
683 | callback: connection_get_cb, |
684 | user_data: client_ref (client)); |
685 | |
686 | G_UNLOCK (lock); |
687 | |
688 | return client->id; |
689 | } |
690 | |
691 | typedef struct { |
692 | GClosure *bus_acquired_closure; |
693 | GClosure *name_acquired_closure; |
694 | GClosure *name_lost_closure; |
695 | } OwnNameData; |
696 | |
697 | static OwnNameData * |
698 | own_name_data_new (GClosure *bus_acquired_closure, |
699 | GClosure *name_acquired_closure, |
700 | GClosure *name_lost_closure) |
701 | { |
702 | OwnNameData *data; |
703 | |
704 | data = g_new0 (OwnNameData, 1); |
705 | |
706 | if (bus_acquired_closure != NULL) |
707 | { |
708 | data->bus_acquired_closure = g_closure_ref (closure: bus_acquired_closure); |
709 | g_closure_sink (closure: bus_acquired_closure); |
710 | if (G_CLOSURE_NEEDS_MARSHAL (bus_acquired_closure)) |
711 | g_closure_set_marshal (closure: bus_acquired_closure, marshal: g_cclosure_marshal_generic); |
712 | } |
713 | |
714 | if (name_acquired_closure != NULL) |
715 | { |
716 | data->name_acquired_closure = g_closure_ref (closure: name_acquired_closure); |
717 | g_closure_sink (closure: name_acquired_closure); |
718 | if (G_CLOSURE_NEEDS_MARSHAL (name_acquired_closure)) |
719 | g_closure_set_marshal (closure: name_acquired_closure, marshal: g_cclosure_marshal_generic); |
720 | } |
721 | |
722 | if (name_lost_closure != NULL) |
723 | { |
724 | data->name_lost_closure = g_closure_ref (closure: name_lost_closure); |
725 | g_closure_sink (closure: name_lost_closure); |
726 | if (G_CLOSURE_NEEDS_MARSHAL (name_lost_closure)) |
727 | g_closure_set_marshal (closure: name_lost_closure, marshal: g_cclosure_marshal_generic); |
728 | } |
729 | |
730 | return data; |
731 | } |
732 | |
733 | static void |
734 | own_with_closures_on_bus_acquired (GDBusConnection *connection, |
735 | const gchar *name, |
736 | gpointer user_data) |
737 | { |
738 | OwnNameData *data = user_data; |
739 | GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT }; |
740 | |
741 | g_value_init (value: ¶ms[0], G_TYPE_DBUS_CONNECTION); |
742 | g_value_set_object (value: ¶ms[0], v_object: connection); |
743 | |
744 | g_value_init (value: ¶ms[1], G_TYPE_STRING); |
745 | g_value_set_string (value: ¶ms[1], v_string: name); |
746 | |
747 | g_closure_invoke (closure: data->bus_acquired_closure, NULL, n_param_values: 2, param_values: params, NULL); |
748 | |
749 | g_value_unset (value: params + 0); |
750 | g_value_unset (value: params + 1); |
751 | } |
752 | |
753 | static void |
754 | own_with_closures_on_name_acquired (GDBusConnection *connection, |
755 | const gchar *name, |
756 | gpointer user_data) |
757 | { |
758 | OwnNameData *data = user_data; |
759 | GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT }; |
760 | |
761 | g_value_init (value: ¶ms[0], G_TYPE_DBUS_CONNECTION); |
762 | g_value_set_object (value: ¶ms[0], v_object: connection); |
763 | |
764 | g_value_init (value: ¶ms[1], G_TYPE_STRING); |
765 | g_value_set_string (value: ¶ms[1], v_string: name); |
766 | |
767 | g_closure_invoke (closure: data->name_acquired_closure, NULL, n_param_values: 2, param_values: params, NULL); |
768 | |
769 | g_value_unset (value: params + 0); |
770 | g_value_unset (value: params + 1); |
771 | } |
772 | |
773 | static void |
774 | own_with_closures_on_name_lost (GDBusConnection *connection, |
775 | const gchar *name, |
776 | gpointer user_data) |
777 | { |
778 | OwnNameData *data = user_data; |
779 | GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT }; |
780 | |
781 | g_value_init (value: ¶ms[0], G_TYPE_DBUS_CONNECTION); |
782 | g_value_set_object (value: ¶ms[0], v_object: connection); |
783 | |
784 | g_value_init (value: ¶ms[1], G_TYPE_STRING); |
785 | g_value_set_string (value: ¶ms[1], v_string: name); |
786 | |
787 | g_closure_invoke (closure: data->name_lost_closure, NULL, n_param_values: 2, param_values: params, NULL); |
788 | |
789 | g_value_unset (value: params + 0); |
790 | g_value_unset (value: params + 1); |
791 | } |
792 | |
793 | static void |
794 | bus_own_name_free_func (gpointer user_data) |
795 | { |
796 | OwnNameData *data = user_data; |
797 | |
798 | if (data->bus_acquired_closure != NULL) |
799 | g_closure_unref (closure: data->bus_acquired_closure); |
800 | |
801 | if (data->name_acquired_closure != NULL) |
802 | g_closure_unref (closure: data->name_acquired_closure); |
803 | |
804 | if (data->name_lost_closure != NULL) |
805 | g_closure_unref (closure: data->name_lost_closure); |
806 | |
807 | g_free (mem: data); |
808 | } |
809 | |
810 | /** |
811 | * g_bus_own_name_with_closures: (rename-to g_bus_own_name) |
812 | * @bus_type: the type of bus to own a name on |
813 | * @name: the well-known name to own |
814 | * @flags: a set of flags from the #GBusNameOwnerFlags enumeration |
815 | * @bus_acquired_closure: (nullable): #GClosure to invoke when connected to |
816 | * the bus of type @bus_type or %NULL |
817 | * @name_acquired_closure: (nullable): #GClosure to invoke when @name is |
818 | * acquired or %NULL |
819 | * @name_lost_closure: (nullable): #GClosure to invoke when @name is lost or |
820 | * %NULL |
821 | * |
822 | * Version of g_bus_own_name() using closures instead of callbacks for |
823 | * easier binding in other languages. |
824 | * |
825 | * Returns: an identifier (never 0) that can be used with |
826 | * g_bus_unown_name() to stop owning the name. |
827 | * |
828 | * Since: 2.26 |
829 | */ |
830 | guint |
831 | g_bus_own_name_with_closures (GBusType bus_type, |
832 | const gchar *name, |
833 | GBusNameOwnerFlags flags, |
834 | GClosure *bus_acquired_closure, |
835 | GClosure *name_acquired_closure, |
836 | GClosure *name_lost_closure) |
837 | { |
838 | return g_bus_own_name (bus_type, |
839 | name, |
840 | flags, |
841 | bus_acquired_handler: bus_acquired_closure != NULL ? own_with_closures_on_bus_acquired : NULL, |
842 | name_acquired_handler: name_acquired_closure != NULL ? own_with_closures_on_name_acquired : NULL, |
843 | name_lost_handler: name_lost_closure != NULL ? own_with_closures_on_name_lost : NULL, |
844 | user_data: own_name_data_new (bus_acquired_closure, |
845 | name_acquired_closure, |
846 | name_lost_closure), |
847 | user_data_free_func: bus_own_name_free_func); |
848 | } |
849 | |
850 | /** |
851 | * g_bus_own_name_on_connection_with_closures: (rename-to g_bus_own_name_on_connection) |
852 | * @connection: a #GDBusConnection |
853 | * @name: the well-known name to own |
854 | * @flags: a set of flags from the #GBusNameOwnerFlags enumeration |
855 | * @name_acquired_closure: (nullable): #GClosure to invoke when @name is |
856 | * acquired or %NULL |
857 | * @name_lost_closure: (nullable): #GClosure to invoke when @name is lost |
858 | * or %NULL |
859 | * |
860 | * Version of g_bus_own_name_on_connection() using closures instead of |
861 | * callbacks for easier binding in other languages. |
862 | * |
863 | * Returns: an identifier (never 0) that can be used with |
864 | * g_bus_unown_name() to stop owning the name. |
865 | * |
866 | * Since: 2.26 |
867 | */ |
868 | guint |
869 | g_bus_own_name_on_connection_with_closures (GDBusConnection *connection, |
870 | const gchar *name, |
871 | GBusNameOwnerFlags flags, |
872 | GClosure *name_acquired_closure, |
873 | GClosure *name_lost_closure) |
874 | { |
875 | return g_bus_own_name_on_connection (connection, |
876 | name, |
877 | flags, |
878 | name_acquired_handler: name_acquired_closure != NULL ? own_with_closures_on_name_acquired : NULL, |
879 | name_lost_handler: name_lost_closure != NULL ? own_with_closures_on_name_lost : NULL, |
880 | user_data: own_name_data_new (NULL, |
881 | name_acquired_closure, |
882 | name_lost_closure), |
883 | user_data_free_func: bus_own_name_free_func); |
884 | } |
885 | |
886 | /** |
887 | * g_bus_unown_name: |
888 | * @owner_id: an identifier obtained from g_bus_own_name() |
889 | * |
890 | * Stops owning a name. |
891 | * |
892 | * Note that there may still be D-Bus traffic to process (relating to owning |
893 | * and unowning the name) in the current thread-default #GMainContext after |
894 | * this function has returned. You should continue to iterate the #GMainContext |
895 | * until the #GDestroyNotify function passed to g_bus_own_name() is called, in |
896 | * order to avoid memory leaks through callbacks queued on the #GMainContext |
897 | * after it’s stopped being iterated. |
898 | * |
899 | * Since: 2.26 |
900 | */ |
901 | void |
902 | g_bus_unown_name (guint owner_id) |
903 | { |
904 | Client *client; |
905 | |
906 | g_return_if_fail (owner_id > 0); |
907 | |
908 | client = NULL; |
909 | |
910 | G_LOCK (lock); |
911 | if (owner_id == 0 || map_id_to_client == NULL || |
912 | (client = g_hash_table_lookup (hash_table: map_id_to_client, GUINT_TO_POINTER (owner_id))) == NULL) |
913 | { |
914 | g_warning ("Invalid id %d passed to g_bus_unown_name()" , owner_id); |
915 | goto out; |
916 | } |
917 | |
918 | client->cancelled = TRUE; |
919 | g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (owner_id))); |
920 | |
921 | out: |
922 | G_UNLOCK (lock); |
923 | |
924 | /* do callback without holding lock */ |
925 | if (client != NULL) |
926 | { |
927 | /* Release the name if needed */ |
928 | if (client->needs_release && |
929 | client->connection != NULL && |
930 | !g_dbus_connection_is_closed (connection: client->connection)) |
931 | { |
932 | GVariant *result; |
933 | GError *error; |
934 | guint32 release_name_reply; |
935 | |
936 | /* TODO: it kinda sucks having to do a sync call to release the name - but if |
937 | * we don't, then a subsequent grab of the name will make the bus daemon return |
938 | * IN_QUEUE which will trigger name_lost(). |
939 | * |
940 | * I believe this is a bug in the bus daemon. |
941 | */ |
942 | error = NULL; |
943 | result = g_dbus_connection_call_sync (connection: client->connection, |
944 | bus_name: "org.freedesktop.DBus" , /* bus name */ |
945 | object_path: "/org/freedesktop/DBus" , /* object path */ |
946 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
947 | method_name: "ReleaseName" , /* method name */ |
948 | parameters: g_variant_new (format_string: "(s)" , client->name), |
949 | G_VARIANT_TYPE ("(u)" ), |
950 | flags: G_DBUS_CALL_FLAGS_NONE, |
951 | timeout_msec: -1, |
952 | NULL, |
953 | error: &error); |
954 | if (result == NULL) |
955 | { |
956 | g_warning ("Error releasing name %s: %s" , client->name, error->message); |
957 | g_error_free (error); |
958 | } |
959 | else |
960 | { |
961 | g_variant_get (value: result, format_string: "(u)" , &release_name_reply); |
962 | if (release_name_reply != 1 /* DBUS_RELEASE_NAME_REPLY_RELEASED */) |
963 | { |
964 | g_warning ("Unexpected reply %d when releasing name %s" , release_name_reply, client->name); |
965 | } |
966 | else |
967 | { |
968 | client->needs_release = FALSE; |
969 | } |
970 | g_variant_unref (value: result); |
971 | } |
972 | } |
973 | |
974 | if (client->disconnected_signal_handler_id > 0) |
975 | g_signal_handler_disconnect (instance: client->connection, handler_id: client->disconnected_signal_handler_id); |
976 | if (client->name_acquired_subscription_id > 0) |
977 | g_dbus_connection_signal_unsubscribe (connection: client->connection, subscription_id: client->name_acquired_subscription_id); |
978 | if (client->name_lost_subscription_id > 0) |
979 | g_dbus_connection_signal_unsubscribe (connection: client->connection, subscription_id: client->name_lost_subscription_id); |
980 | client->disconnected_signal_handler_id = 0; |
981 | client->name_acquired_subscription_id = 0; |
982 | client->name_lost_subscription_id = 0; |
983 | if (client->connection != NULL) |
984 | { |
985 | g_object_unref (object: client->connection); |
986 | client->connection = NULL; |
987 | } |
988 | |
989 | client_unref (client); |
990 | } |
991 | } |
992 | |