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