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 : : #include <string.h>
27 : :
28 : : #include "gdbusutils.h"
29 : : #include "gdbusnamewatching.h"
30 : : #include "gdbuserror.h"
31 : : #include "gdbusprivate.h"
32 : : #include "gdbusconnection.h"
33 : :
34 : : #include "glibintl.h"
35 : :
36 : : G_LOCK_DEFINE_STATIC (lock);
37 : :
38 : : /* ---------------------------------------------------------------------------------------------------- */
39 : :
40 : : typedef enum
41 : : {
42 : : PREVIOUS_CALL_NONE = 0,
43 : : PREVIOUS_CALL_APPEARED,
44 : : PREVIOUS_CALL_VANISHED,
45 : : } PreviousCall;
46 : :
47 : : typedef struct
48 : : {
49 : : gint ref_count; /* (atomic) */
50 : : guint id;
51 : : gchar *name;
52 : : GBusNameWatcherFlags flags;
53 : : gchar *name_owner;
54 : : GBusNameAppearedCallback name_appeared_handler;
55 : : GBusNameVanishedCallback name_vanished_handler;
56 : : gpointer user_data;
57 : : GDestroyNotify user_data_free_func;
58 : : GMainContext *main_context;
59 : :
60 : : GDBusConnection *connection;
61 : : gulong disconnected_signal_handler_id;
62 : : guint name_owner_changed_subscription_id;
63 : :
64 : : PreviousCall previous_call;
65 : :
66 : : gboolean cancelled;
67 : : gboolean initialized;
68 : : } Client;
69 : :
70 : : /* Must be accessed atomically. */
71 : : static guint next_global_id = 1; /* (atomic) */
72 : :
73 : : /* Must be accessed with @lock held. */
74 : : static GHashTable *map_id_to_client = NULL;
75 : :
76 : : static Client *
77 : 60 : client_ref (Client *client)
78 : : {
79 : 60 : g_atomic_int_inc (&client->ref_count);
80 : 60 : return client;
81 : : }
82 : :
83 : : static gboolean
84 : 17 : free_user_data_cb (gpointer user_data)
85 : : {
86 : : /* The user data is actually freed by the GDestroyNotify for the idle source */
87 : 17 : return G_SOURCE_REMOVE;
88 : : }
89 : :
90 : : static void
91 : 84 : client_unref (Client *client)
92 : : {
93 : 84 : if (g_atomic_int_dec_and_test (&client->ref_count))
94 : : {
95 : 26 : if (client->connection != NULL)
96 : : {
97 : 15 : if (client->name_owner_changed_subscription_id > 0)
98 : 15 : g_dbus_connection_signal_unsubscribe (client->connection, g_steal_handle_id (&client->name_owner_changed_subscription_id));
99 : 15 : if (client->disconnected_signal_handler_id > 0)
100 : 15 : g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
101 : 15 : g_object_unref (client->connection);
102 : : }
103 : 26 : g_free (client->name);
104 : 26 : g_free (client->name_owner);
105 : :
106 : 26 : if (client->user_data_free_func != NULL)
107 : : {
108 : : /* Ensure client->user_data_free_func() is called from the right thread */
109 : 19 : if (client->main_context != g_main_context_get_thread_default ())
110 : : {
111 : 17 : GSource *idle_source = g_idle_source_new ();
112 : 17 : g_source_set_callback (idle_source, free_user_data_cb,
113 : : client->user_data,
114 : : client->user_data_free_func);
115 : 17 : g_source_set_name (idle_source, "[gio, gdbusnamewatching.c] free_user_data_cb");
116 : 17 : g_source_attach (idle_source, client->main_context);
117 : 17 : g_source_unref (idle_source);
118 : : }
119 : : else
120 : 2 : client->user_data_free_func (client->user_data);
121 : : }
122 : :
123 : 26 : g_main_context_unref (client->main_context);
124 : :
125 : 26 : g_free (client);
126 : : }
127 : 84 : }
128 : :
129 : : /* ---------------------------------------------------------------------------------------------------- */
130 : :
131 : : typedef enum
132 : : {
133 : : CALL_TYPE_NAME_APPEARED,
134 : : CALL_TYPE_NAME_VANISHED
135 : : } CallType;
136 : :
137 : : typedef struct
138 : : {
139 : : Client *client;
140 : :
141 : : /* keep this separate because client->connection may
142 : : * be set to NULL after scheduling the call
143 : : */
144 : : GDBusConnection *connection;
145 : :
146 : : /* ditto */
147 : : gchar *name_owner;
148 : :
149 : : CallType call_type;
150 : : } CallHandlerData;
151 : :
152 : : static void
153 : 2 : call_handler_data_free (CallHandlerData *data)
154 : : {
155 : 2 : if (data->connection != NULL)
156 : 0 : g_object_unref (data->connection);
157 : 2 : g_free (data->name_owner);
158 : 2 : client_unref (data->client);
159 : 2 : g_free (data);
160 : 2 : }
161 : :
162 : : static void
163 : 35 : actually_do_call (Client *client, GDBusConnection *connection, const gchar *name_owner, CallType call_type)
164 : : {
165 : : /* The client might have been cancelled (g_bus_unwatch_name()) while we were
166 : : * sitting in the #GMainContext dispatch queue. */
167 : 35 : if (client->cancelled)
168 : 1 : return;
169 : :
170 : 34 : switch (call_type)
171 : : {
172 : 15 : case CALL_TYPE_NAME_APPEARED:
173 : 15 : if (client->name_appeared_handler != NULL)
174 : : {
175 : 15 : client->name_appeared_handler (connection,
176 : 15 : client->name,
177 : : name_owner,
178 : : client->user_data);
179 : : }
180 : 15 : break;
181 : :
182 : 19 : case CALL_TYPE_NAME_VANISHED:
183 : 19 : if (client->name_vanished_handler != NULL)
184 : : {
185 : 19 : client->name_vanished_handler (connection,
186 : 19 : client->name,
187 : : client->user_data);
188 : : }
189 : 19 : break;
190 : :
191 : 0 : default:
192 : : g_assert_not_reached ();
193 : : break;
194 : : }
195 : : }
196 : :
197 : : static gboolean
198 : 2 : call_in_idle_cb (gpointer _data)
199 : : {
200 : 2 : CallHandlerData *data = _data;
201 : 2 : actually_do_call (data->client, data->connection, data->name_owner, data->call_type);
202 : 2 : return FALSE;
203 : : }
204 : :
205 : : static void
206 : 2 : schedule_call_in_idle (Client *client, CallType call_type)
207 : : {
208 : : CallHandlerData *data;
209 : : GSource *idle_source;
210 : :
211 : 2 : data = g_new0 (CallHandlerData, 1);
212 : 2 : data->client = client_ref (client);
213 : 2 : data->connection = client->connection != NULL ? g_object_ref (client->connection) : NULL;
214 : 2 : data->name_owner = g_strdup (client->name_owner);
215 : 2 : data->call_type = call_type;
216 : :
217 : 2 : idle_source = g_idle_source_new ();
218 : 2 : g_source_set_priority (idle_source, G_PRIORITY_HIGH);
219 : 2 : g_source_set_callback (idle_source,
220 : : call_in_idle_cb,
221 : : data,
222 : : (GDestroyNotify) call_handler_data_free);
223 : 2 : g_source_set_static_name (idle_source, "[gio, gdbusnamewatching.c] call_in_idle_cb");
224 : 2 : g_source_attach (idle_source, client->main_context);
225 : 2 : g_source_unref (idle_source);
226 : 2 : }
227 : :
228 : : static void
229 : 35 : do_call (Client *client, CallType call_type)
230 : : {
231 : : GMainContext *current_context;
232 : :
233 : : /* only schedule in idle if we're not in the right thread */
234 : 35 : current_context = g_main_context_ref_thread_default ();
235 : 35 : if (current_context != client->main_context)
236 : 2 : schedule_call_in_idle (client, call_type);
237 : : else
238 : 33 : actually_do_call (client, client->connection, client->name_owner, call_type);
239 : 35 : g_main_context_unref (current_context);
240 : 35 : }
241 : :
242 : : static void
243 : 21 : call_appeared_handler (Client *client)
244 : : {
245 : 21 : if (client->previous_call != PREVIOUS_CALL_APPEARED)
246 : : {
247 : 21 : client->previous_call = PREVIOUS_CALL_APPEARED;
248 : 21 : if (!client->cancelled && client->name_appeared_handler != NULL)
249 : : {
250 : 15 : do_call (client, CALL_TYPE_NAME_APPEARED);
251 : : }
252 : : }
253 : 21 : }
254 : :
255 : : static void
256 : 22 : call_vanished_handler (Client *client)
257 : : {
258 : 22 : if (client->previous_call != PREVIOUS_CALL_VANISHED)
259 : : {
260 : 22 : client->previous_call = PREVIOUS_CALL_VANISHED;
261 : 22 : if (!client->cancelled && client->name_vanished_handler != NULL)
262 : : {
263 : 20 : do_call (client, CALL_TYPE_NAME_VANISHED);
264 : : }
265 : : }
266 : 22 : }
267 : :
268 : : /* ---------------------------------------------------------------------------------------------------- */
269 : :
270 : : /* Return a reference to the #Client for @watcher_id, or %NULL if it’s been
271 : : * unwatched. This is safe to call from any thread. */
272 : : static Client *
273 : 18 : dup_client (guint watcher_id)
274 : : {
275 : : Client *client;
276 : :
277 : 18 : G_LOCK (lock);
278 : :
279 : 18 : g_assert (watcher_id != 0);
280 : 18 : g_assert (map_id_to_client != NULL);
281 : :
282 : 18 : client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id));
283 : :
284 : 18 : if (client != NULL)
285 : 18 : client_ref (client);
286 : :
287 : 18 : G_UNLOCK (lock);
288 : :
289 : 18 : return client;
290 : : }
291 : :
292 : : /* Could be called from any thread, so it could be called after client_unref()
293 : : * has started finalising the #Client. Avoid that by looking up the #Client
294 : : * atomically. */
295 : : static void
296 : 6 : on_connection_disconnected (GDBusConnection *connection,
297 : : gboolean remote_peer_vanished,
298 : : GError *error,
299 : : gpointer user_data)
300 : : {
301 : 6 : guint watcher_id = GPOINTER_TO_UINT (user_data);
302 : 6 : Client *client = NULL;
303 : :
304 : 6 : client = dup_client (watcher_id);
305 : 6 : if (client == NULL)
306 : 0 : return;
307 : :
308 : 6 : if (client->name_owner_changed_subscription_id > 0)
309 : 6 : g_dbus_connection_signal_unsubscribe (client->connection, g_steal_handle_id (&client->name_owner_changed_subscription_id));
310 : 6 : if (client->disconnected_signal_handler_id > 0)
311 : 6 : g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
312 : 6 : g_object_unref (client->connection);
313 : 6 : client->disconnected_signal_handler_id = 0;
314 : 6 : client->connection = NULL;
315 : :
316 : 6 : call_vanished_handler (client);
317 : :
318 : 6 : client_unref (client);
319 : : }
320 : :
321 : : /* ---------------------------------------------------------------------------------------------------- */
322 : :
323 : : /* Will always be called from the thread which acquired client->main_context. */
324 : : static void
325 : 12 : on_name_owner_changed (GDBusConnection *connection,
326 : : const gchar *sender_name,
327 : : const gchar *object_path,
328 : : const gchar *interface_name,
329 : : const gchar *signal_name,
330 : : GVariant *parameters,
331 : : gpointer user_data)
332 : : {
333 : 12 : guint watcher_id = GPOINTER_TO_UINT (user_data);
334 : 12 : Client *client = NULL;
335 : : const gchar *name;
336 : : const gchar *old_owner;
337 : : const gchar *new_owner;
338 : :
339 : 12 : client = dup_client (watcher_id);
340 : 12 : if (client == NULL)
341 : 0 : return;
342 : :
343 : 12 : if (!client->initialized)
344 : 1 : goto out;
345 : :
346 : 22 : if (g_strcmp0 (object_path, DBUS_PATH_DBUS) != 0 ||
347 : 22 : g_strcmp0 (interface_name, DBUS_INTERFACE_DBUS) != 0 ||
348 : 11 : g_strcmp0 (sender_name, DBUS_SERVICE_DBUS) != 0)
349 : 0 : goto out;
350 : :
351 : 11 : g_variant_get (parameters,
352 : : "(&s&s&s)",
353 : : &name,
354 : : &old_owner,
355 : : &new_owner);
356 : :
357 : : /* we only care about a specific name */
358 : 11 : if (g_strcmp0 (name, client->name) != 0)
359 : 0 : goto out;
360 : :
361 : 11 : if ((old_owner != NULL && strlen (old_owner) > 0) && client->name_owner != NULL)
362 : : {
363 : 4 : g_free (client->name_owner);
364 : 4 : client->name_owner = NULL;
365 : 4 : call_vanished_handler (client);
366 : : }
367 : :
368 : 11 : if (new_owner != NULL && strlen (new_owner) > 0)
369 : : {
370 : 7 : g_warn_if_fail (client->name_owner == NULL);
371 : 7 : g_free (client->name_owner);
372 : 7 : client->name_owner = g_strdup (new_owner);
373 : 7 : call_appeared_handler (client);
374 : : }
375 : :
376 : 4 : out:
377 : 12 : client_unref (client);
378 : : }
379 : :
380 : : /* ---------------------------------------------------------------------------------------------------- */
381 : :
382 : : static void
383 : 21 : get_name_owner_cb (GObject *source_object,
384 : : GAsyncResult *res,
385 : : gpointer user_data)
386 : : {
387 : 21 : Client *client = user_data;
388 : : GVariant *result;
389 : : const char *name_owner;
390 : :
391 : 21 : name_owner = NULL;
392 : 21 : result = NULL;
393 : :
394 : 21 : result = g_dbus_connection_call_finish (client->connection,
395 : : res,
396 : : NULL);
397 : 21 : if (result != NULL)
398 : : {
399 : 14 : g_variant_get (result, "(&s)", &name_owner);
400 : : }
401 : :
402 : 21 : if (name_owner != NULL)
403 : : {
404 : 14 : g_warn_if_fail (client->name_owner == NULL);
405 : 14 : client->name_owner = g_strdup (name_owner);
406 : 14 : call_appeared_handler (client);
407 : : }
408 : : else
409 : : {
410 : 7 : call_vanished_handler (client);
411 : : }
412 : :
413 : 21 : client->initialized = TRUE;
414 : :
415 : 21 : if (result != NULL)
416 : 14 : g_variant_unref (result);
417 : 21 : client_unref (client);
418 : 21 : }
419 : :
420 : : /* ---------------------------------------------------------------------------------------------------- */
421 : :
422 : : static void
423 : 21 : invoke_get_name_owner (Client *client)
424 : : {
425 : 21 : g_dbus_connection_call (client->connection,
426 : : DBUS_SERVICE_DBUS,
427 : : DBUS_PATH_DBUS,
428 : : DBUS_INTERFACE_DBUS,
429 : : "GetNameOwner", /* method name */
430 : : g_variant_new ("(s)", client->name),
431 : : G_VARIANT_TYPE ("(s)"),
432 : : G_DBUS_CALL_FLAGS_NONE,
433 : : -1,
434 : : NULL,
435 : : (GAsyncReadyCallback) get_name_owner_cb,
436 : 21 : client_ref (client));
437 : 21 : }
438 : :
439 : : /* ---------------------------------------------------------------------------------------------------- */
440 : :
441 : : static void
442 : 6 : start_service_by_name_cb (GObject *source_object,
443 : : GAsyncResult *res,
444 : : gpointer user_data)
445 : : {
446 : 6 : Client *client = user_data;
447 : : GVariant *result;
448 : :
449 : 6 : result = NULL;
450 : :
451 : 6 : result = g_dbus_connection_call_finish (client->connection,
452 : : res,
453 : : NULL);
454 : 6 : if (result != NULL)
455 : : {
456 : : guint32 start_service_result;
457 : 2 : g_variant_get (result, "(u)", &start_service_result);
458 : :
459 : 2 : if (start_service_result == DBUS_START_REPLY_SUCCESS)
460 : : {
461 : 1 : invoke_get_name_owner (client);
462 : : }
463 : 1 : else if (start_service_result == DBUS_START_REPLY_ALREADY_RUNNING)
464 : : {
465 : 1 : invoke_get_name_owner (client);
466 : : }
467 : : else
468 : : {
469 : 0 : g_warning ("Unexpected reply %d from StartServiceByName() method", start_service_result);
470 : 0 : call_vanished_handler (client);
471 : 0 : client->initialized = TRUE;
472 : : }
473 : : }
474 : : else
475 : : {
476 : : /* Errors are not unexpected; the bus will reply e.g.
477 : : *
478 : : * org.freedesktop.DBus.Error.ServiceUnknown: The name org.gnome.Epiphany2
479 : : * was not provided by any .service files
480 : : *
481 : : * This doesn't mean that the name doesn't have an owner, just
482 : : * that it's not provided by a .service file. So proceed to
483 : : * invoke GetNameOwner().
484 : : */
485 : 4 : invoke_get_name_owner (client);
486 : : }
487 : :
488 : 6 : if (result != NULL)
489 : 2 : g_variant_unref (result);
490 : 6 : client_unref (client);
491 : 6 : }
492 : :
493 : : /* ---------------------------------------------------------------------------------------------------- */
494 : :
495 : : static void
496 : 21 : has_connection (Client *client)
497 : : {
498 : : /* listen for disconnection */
499 : 21 : client->disconnected_signal_handler_id = g_signal_connect (client->connection,
500 : : "closed",
501 : : G_CALLBACK (on_connection_disconnected),
502 : : GUINT_TO_POINTER (client->id));
503 : :
504 : : /* start listening to NameOwnerChanged messages immediately */
505 : 42 : client->name_owner_changed_subscription_id = g_dbus_connection_signal_subscribe (client->connection,
506 : : DBUS_SERVICE_DBUS,
507 : : DBUS_INTERFACE_DBUS,
508 : : "NameOwnerChanged", /* signal */
509 : : DBUS_PATH_DBUS,
510 : 21 : client->name,
511 : : G_DBUS_SIGNAL_FLAGS_NONE,
512 : : on_name_owner_changed,
513 : 21 : GUINT_TO_POINTER (client->id),
514 : : NULL);
515 : :
516 : 21 : if (client->flags & G_BUS_NAME_WATCHER_FLAGS_AUTO_START)
517 : : {
518 : 6 : g_dbus_connection_call (client->connection,
519 : : DBUS_SERVICE_DBUS,
520 : : DBUS_PATH_DBUS,
521 : : DBUS_INTERFACE_DBUS,
522 : : "StartServiceByName", /* method name */
523 : : g_variant_new ("(su)", client->name, 0),
524 : : G_VARIANT_TYPE ("(u)"),
525 : : G_DBUS_CALL_FLAGS_NONE,
526 : : -1,
527 : : NULL,
528 : : (GAsyncReadyCallback) start_service_by_name_cb,
529 : 6 : client_ref (client));
530 : : }
531 : : else
532 : : {
533 : : /* check owner */
534 : 15 : invoke_get_name_owner (client);
535 : : }
536 : 21 : }
537 : :
538 : :
539 : : static void
540 : 11 : connection_get_cb (GObject *source_object,
541 : : GAsyncResult *res,
542 : : gpointer user_data)
543 : : {
544 : 11 : Client *client = user_data;
545 : :
546 : 11 : client->connection = g_bus_get_finish (res, NULL);
547 : 11 : if (client->connection == NULL)
548 : : {
549 : 5 : call_vanished_handler (client);
550 : 5 : goto out;
551 : : }
552 : :
553 : 6 : has_connection (client);
554 : :
555 : 11 : out:
556 : 11 : client_unref (client);
557 : 11 : }
558 : :
559 : : /* ---------------------------------------------------------------------------------------------------- */
560 : :
561 : : /**
562 : : * g_bus_watch_name:
563 : : * @bus_type: The type of bus to watch a name on.
564 : : * @name: The name (well-known or unique) to watch.
565 : : * @flags: Flags from the #GBusNameWatcherFlags enumeration.
566 : : * @name_appeared_handler: (nullable) (scope notified): Handler to invoke when
567 : : * @name is known to exist or %NULL.
568 : : * @name_vanished_handler: (nullable) (scope notified): Handler to invoke when
569 : : * @name is known to not exist or %NULL.
570 : : * @user_data: User data to pass to handlers.
571 : : * @user_data_free_func: (nullable): Function for freeing @user_data or %NULL.
572 : : *
573 : : * Starts watching @name on the bus specified by @bus_type and calls
574 : : * @name_appeared_handler and @name_vanished_handler when the name is
575 : : * known to have an owner respectively known to lose its
576 : : * owner. Callbacks will be invoked in the
577 : : * [thread-default main context][g-main-context-push-thread-default]
578 : : * of the thread you are calling this function from.
579 : : *
580 : : * You are guaranteed that one of the handlers will be invoked after
581 : : * calling this function. When you are done watching the name, just
582 : : * call g_bus_unwatch_name() with the watcher id this function
583 : : * returns.
584 : : *
585 : : * If the name vanishes or appears (for example the application owning
586 : : * the name could restart), the handlers are also invoked. If the
587 : : * #GDBusConnection that is used for watching the name disconnects, then
588 : : * @name_vanished_handler is invoked since it is no longer
589 : : * possible to access the name.
590 : : *
591 : : * Another guarantee is that invocations of @name_appeared_handler
592 : : * and @name_vanished_handler are guaranteed to alternate; that
593 : : * is, if @name_appeared_handler is invoked then you are
594 : : * guaranteed that the next time one of the handlers is invoked, it
595 : : * will be @name_vanished_handler. The reverse is also true.
596 : : *
597 : : * This behavior makes it very simple to write applications that want
598 : : * to take action when a certain [name exists][gdbus-watching-names].
599 : : * Basically, the application should create object proxies in
600 : : * @name_appeared_handler and destroy them again (if any) in
601 : : * @name_vanished_handler.
602 : : *
603 : : * Returns: An identifier (never 0) that can be used with
604 : : * g_bus_unwatch_name() to stop watching the name.
605 : : *
606 : : * Since: 2.26
607 : : */
608 : : guint
609 : 13 : g_bus_watch_name (GBusType bus_type,
610 : : const gchar *name,
611 : : GBusNameWatcherFlags flags,
612 : : GBusNameAppearedCallback name_appeared_handler,
613 : : GBusNameVanishedCallback name_vanished_handler,
614 : : gpointer user_data,
615 : : GDestroyNotify user_data_free_func)
616 : : {
617 : : Client *client;
618 : :
619 : 13 : g_return_val_if_fail (g_dbus_is_name (name), 0);
620 : :
621 : 13 : G_LOCK (lock);
622 : :
623 : 13 : client = g_new0 (Client, 1);
624 : 13 : client->ref_count = 1;
625 : 13 : client->id = (guint) g_atomic_int_add (&next_global_id, 1); /* TODO: uh oh, handle overflow */
626 : 13 : client->name = g_strdup (name);
627 : 13 : client->flags = flags;
628 : 13 : client->name_appeared_handler = name_appeared_handler;
629 : 13 : client->name_vanished_handler = name_vanished_handler;
630 : 13 : client->user_data = user_data;
631 : 13 : client->user_data_free_func = user_data_free_func;
632 : 13 : client->main_context = g_main_context_ref_thread_default ();
633 : :
634 : 13 : if (map_id_to_client == NULL)
635 : : {
636 : 4 : map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
637 : : }
638 : 13 : g_hash_table_insert (map_id_to_client,
639 : 13 : GUINT_TO_POINTER (client->id),
640 : : client);
641 : :
642 : 13 : g_bus_get (bus_type,
643 : : NULL,
644 : : connection_get_cb,
645 : 13 : client_ref (client));
646 : :
647 : 13 : G_UNLOCK (lock);
648 : :
649 : 13 : return client->id;
650 : : }
651 : :
652 : : /**
653 : : * g_bus_watch_name_on_connection:
654 : : * @connection: A #GDBusConnection.
655 : : * @name: The name (well-known or unique) to watch.
656 : : * @flags: Flags from the #GBusNameWatcherFlags enumeration.
657 : : * @name_appeared_handler: (nullable) (scope notified): Handler to invoke when
658 : : * @name is known to exist or %NULL.
659 : : * @name_vanished_handler: (nullable) (scope notified): Handler to invoke when
660 : : * @name is known to not exist or %NULL.
661 : : * @user_data: User data to pass to handlers.
662 : : * @user_data_free_func: (nullable): Function for freeing @user_data or %NULL.
663 : : *
664 : : * Like g_bus_watch_name() but takes a #GDBusConnection instead of a
665 : : * #GBusType.
666 : : *
667 : : * Returns: An identifier (never 0) that can be used with
668 : : * g_bus_unwatch_name() to stop watching the name.
669 : : *
670 : : * Since: 2.26
671 : : */
672 : 15 : guint g_bus_watch_name_on_connection (GDBusConnection *connection,
673 : : const gchar *name,
674 : : GBusNameWatcherFlags flags,
675 : : GBusNameAppearedCallback name_appeared_handler,
676 : : GBusNameVanishedCallback name_vanished_handler,
677 : : gpointer user_data,
678 : : GDestroyNotify user_data_free_func)
679 : : {
680 : : Client *client;
681 : :
682 : 15 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
683 : 15 : g_return_val_if_fail (g_dbus_is_name (name), 0);
684 : :
685 : 15 : G_LOCK (lock);
686 : :
687 : 15 : client = g_new0 (Client, 1);
688 : 15 : client->ref_count = 1;
689 : 15 : client->id = (guint) g_atomic_int_add (&next_global_id, 1); /* TODO: uh oh, handle overflow */
690 : 15 : client->name = g_strdup (name);
691 : 15 : client->flags = flags;
692 : 15 : client->name_appeared_handler = name_appeared_handler;
693 : 15 : client->name_vanished_handler = name_vanished_handler;
694 : 15 : client->user_data = user_data;
695 : 15 : client->user_data_free_func = user_data_free_func;
696 : 15 : client->main_context = g_main_context_ref_thread_default ();
697 : :
698 : 15 : if (map_id_to_client == NULL)
699 : 5 : map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
700 : :
701 : 15 : g_hash_table_insert (map_id_to_client,
702 : 15 : GUINT_TO_POINTER (client->id),
703 : : client);
704 : :
705 : 15 : client->connection = g_object_ref (connection);
706 : 15 : G_UNLOCK (lock);
707 : :
708 : 15 : has_connection (client);
709 : :
710 : 15 : return client->id;
711 : : }
712 : :
713 : : typedef struct {
714 : : GClosure *name_appeared_closure;
715 : : GClosure *name_vanished_closure;
716 : : } WatchNameData;
717 : :
718 : : static WatchNameData *
719 : 4 : watch_name_data_new (GClosure *name_appeared_closure,
720 : : GClosure *name_vanished_closure)
721 : : {
722 : : WatchNameData *data;
723 : :
724 : 4 : data = g_new0 (WatchNameData, 1);
725 : :
726 : 4 : if (name_appeared_closure != NULL)
727 : : {
728 : 4 : data->name_appeared_closure = g_closure_ref (name_appeared_closure);
729 : 4 : g_closure_sink (name_appeared_closure);
730 : 4 : if (G_CLOSURE_NEEDS_MARSHAL (name_appeared_closure))
731 : 4 : g_closure_set_marshal (name_appeared_closure, g_cclosure_marshal_generic);
732 : : }
733 : :
734 : 4 : if (name_vanished_closure != NULL)
735 : : {
736 : 4 : data->name_vanished_closure = g_closure_ref (name_vanished_closure);
737 : 4 : g_closure_sink (name_vanished_closure);
738 : 4 : if (G_CLOSURE_NEEDS_MARSHAL (name_vanished_closure))
739 : 4 : g_closure_set_marshal (name_vanished_closure, g_cclosure_marshal_generic);
740 : : }
741 : :
742 : 4 : return data;
743 : : }
744 : :
745 : : static void
746 : 4 : watch_with_closures_on_name_appeared (GDBusConnection *connection,
747 : : const gchar *name,
748 : : const gchar *name_owner,
749 : : gpointer user_data)
750 : : {
751 : 4 : WatchNameData *data = user_data;
752 : 4 : GValue params[3] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT };
753 : :
754 : 4 : g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION);
755 : 4 : g_value_set_object (¶ms[0], connection);
756 : :
757 : 4 : g_value_init (¶ms[1], G_TYPE_STRING);
758 : 4 : g_value_set_string (¶ms[1], name);
759 : :
760 : 4 : g_value_init (¶ms[2], G_TYPE_STRING);
761 : 4 : g_value_set_string (¶ms[2], name_owner);
762 : :
763 : 4 : g_closure_invoke (data->name_appeared_closure, NULL, 3, params, NULL);
764 : :
765 : 4 : g_value_unset (params + 0);
766 : 4 : g_value_unset (params + 1);
767 : 4 : g_value_unset (params + 2);
768 : 4 : }
769 : :
770 : : static void
771 : 4 : watch_with_closures_on_name_vanished (GDBusConnection *connection,
772 : : const gchar *name,
773 : : gpointer user_data)
774 : : {
775 : 4 : WatchNameData *data = user_data;
776 : 4 : GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
777 : :
778 : 4 : g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION);
779 : 4 : g_value_set_object (¶ms[0], connection);
780 : :
781 : 4 : g_value_init (¶ms[1], G_TYPE_STRING);
782 : 4 : g_value_set_string (¶ms[1], name);
783 : :
784 : 4 : g_closure_invoke (data->name_vanished_closure, NULL, 2, params, NULL);
785 : :
786 : 4 : g_value_unset (params + 0);
787 : 4 : g_value_unset (params + 1);
788 : 4 : }
789 : :
790 : : static void
791 : 4 : bus_watch_name_free_func (gpointer user_data)
792 : : {
793 : 4 : WatchNameData *data = user_data;
794 : :
795 : 4 : if (data->name_appeared_closure != NULL)
796 : 4 : g_closure_unref (data->name_appeared_closure);
797 : :
798 : 4 : if (data->name_vanished_closure != NULL)
799 : 4 : g_closure_unref (data->name_vanished_closure);
800 : :
801 : 4 : g_free (data);
802 : 4 : }
803 : :
804 : : /**
805 : : * g_bus_watch_name_with_closures: (rename-to g_bus_watch_name)
806 : : * @bus_type: The type of bus to watch a name on.
807 : : * @name: The name (well-known or unique) to watch.
808 : : * @flags: Flags from the #GBusNameWatcherFlags enumeration.
809 : : * @name_appeared_closure: (nullable): #GClosure to invoke when @name is known
810 : : * to exist or %NULL.
811 : : * @name_vanished_closure: (nullable): #GClosure to invoke when @name is known
812 : : * to not exist or %NULL.
813 : : *
814 : : * Version of g_bus_watch_name() using closures instead of callbacks for
815 : : * easier binding in other languages.
816 : : *
817 : : * Returns: An identifier (never 0) that can be used with
818 : : * g_bus_unwatch_name() to stop watching the name.
819 : : *
820 : : * Since: 2.26
821 : : */
822 : : guint
823 : 2 : g_bus_watch_name_with_closures (GBusType bus_type,
824 : : const gchar *name,
825 : : GBusNameWatcherFlags flags,
826 : : GClosure *name_appeared_closure,
827 : : GClosure *name_vanished_closure)
828 : : {
829 : 2 : return g_bus_watch_name (bus_type,
830 : : name,
831 : : flags,
832 : : name_appeared_closure != NULL ? watch_with_closures_on_name_appeared : NULL,
833 : : name_vanished_closure != NULL ? watch_with_closures_on_name_vanished : NULL,
834 : 2 : watch_name_data_new (name_appeared_closure, name_vanished_closure),
835 : : bus_watch_name_free_func);
836 : : }
837 : :
838 : : /**
839 : : * g_bus_watch_name_on_connection_with_closures: (rename-to g_bus_watch_name_on_connection)
840 : : * @connection: A #GDBusConnection.
841 : : * @name: The name (well-known or unique) to watch.
842 : : * @flags: Flags from the #GBusNameWatcherFlags enumeration.
843 : : * @name_appeared_closure: (nullable): #GClosure to invoke when @name is known
844 : : * to exist or %NULL.
845 : : * @name_vanished_closure: (nullable): #GClosure to invoke when @name is known
846 : : * to not exist or %NULL.
847 : : *
848 : : * Version of g_bus_watch_name_on_connection() using closures instead of callbacks for
849 : : * easier binding in other languages.
850 : : *
851 : : * Returns: An identifier (never 0) that can be used with
852 : : * g_bus_unwatch_name() to stop watching the name.
853 : : *
854 : : * Since: 2.26
855 : : */
856 : 2 : guint g_bus_watch_name_on_connection_with_closures (
857 : : GDBusConnection *connection,
858 : : const gchar *name,
859 : : GBusNameWatcherFlags flags,
860 : : GClosure *name_appeared_closure,
861 : : GClosure *name_vanished_closure)
862 : : {
863 : 2 : return g_bus_watch_name_on_connection (connection,
864 : : name,
865 : : flags,
866 : : name_appeared_closure != NULL ? watch_with_closures_on_name_appeared : NULL,
867 : : name_vanished_closure != NULL ? watch_with_closures_on_name_vanished : NULL,
868 : 2 : watch_name_data_new (name_appeared_closure, name_vanished_closure),
869 : : bus_watch_name_free_func);
870 : : }
871 : :
872 : : /**
873 : : * g_bus_unwatch_name:
874 : : * @watcher_id: An identifier obtained from g_bus_watch_name()
875 : : *
876 : : * Stops watching a name.
877 : : *
878 : : * Note that there may still be D-Bus traffic to process (relating to watching
879 : : * and unwatching the name) in the current thread-default #GMainContext after
880 : : * this function has returned. You should continue to iterate the #GMainContext
881 : : * until the #GDestroyNotify function passed to g_bus_watch_name() is called, in
882 : : * order to avoid memory leaks through callbacks queued on the #GMainContext
883 : : * after it’s stopped being iterated.
884 : : *
885 : : * Since: 2.26
886 : : */
887 : : void
888 : 26 : g_bus_unwatch_name (guint watcher_id)
889 : : {
890 : : Client *client;
891 : :
892 : 26 : g_return_if_fail (watcher_id > 0);
893 : :
894 : 26 : client = NULL;
895 : :
896 : 26 : G_LOCK (lock);
897 : 26 : if (watcher_id == 0 ||
898 : 52 : map_id_to_client == NULL ||
899 : 26 : (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL)
900 : : {
901 : 0 : g_warning ("Invalid id %d passed to g_bus_unwatch_name()", watcher_id);
902 : 0 : goto out;
903 : : }
904 : :
905 : 26 : client->cancelled = TRUE;
906 : 26 : g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id)));
907 : :
908 : 26 : out:
909 : 26 : G_UNLOCK (lock);
910 : :
911 : : /* do callback without holding lock */
912 : 26 : if (client != NULL)
913 : : {
914 : 26 : client_unref (client);
915 : : }
916 : : }
|