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