1/* GDBus - GLib D-Bus Library
2 *
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: David Zeuthen <davidz@redhat.com>
19 */
20
21#include "config.h"
22
23#include "gdbusobjectmanager.h"
24#include "gdbusobjectmanagerclient.h"
25#include "gdbusobject.h"
26#include "gdbusprivate.h"
27#include "gioenumtypes.h"
28#include "ginitable.h"
29#include "gasyncresult.h"
30#include "gasyncinitable.h"
31#include "gdbusconnection.h"
32#include "gdbusutils.h"
33#include "gdbusobject.h"
34#include "gdbusobjectproxy.h"
35#include "gdbusproxy.h"
36#include "gdbusinterface.h"
37
38#include "glibintl.h"
39#include "gmarshal-internal.h"
40
41/**
42 * SECTION:gdbusobjectmanagerclient
43 * @short_description: Client-side object manager
44 * @include: gio/gio.h
45 *
46 * #GDBusObjectManagerClient is used to create, monitor and delete object
47 * proxies for remote objects exported by a #GDBusObjectManagerServer (or any
48 * code implementing the
49 * [org.freedesktop.DBus.ObjectManager](http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager)
50 * interface).
51 *
52 * Once an instance of this type has been created, you can connect to
53 * the #GDBusObjectManager::object-added and
54 * #GDBusObjectManager::object-removed signals and inspect the
55 * #GDBusObjectProxy objects returned by
56 * g_dbus_object_manager_get_objects().
57 *
58 * If the name for a #GDBusObjectManagerClient is not owned by anyone at
59 * object construction time, the default behavior is to request the
60 * message bus to launch an owner for the name. This behavior can be
61 * disabled using the %G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START
62 * flag. It's also worth noting that this only works if the name of
63 * interest is activatable in the first place. E.g. in some cases it
64 * is not possible to launch an owner for the requested name. In this
65 * case, #GDBusObjectManagerClient object construction still succeeds but
66 * there will be no object proxies
67 * (e.g. g_dbus_object_manager_get_objects() returns the empty list) and
68 * the #GDBusObjectManagerClient:name-owner property is %NULL.
69 *
70 * The owner of the requested name can come and go (for example
71 * consider a system service being restarted) – #GDBusObjectManagerClient
72 * handles this case too; simply connect to the #GObject::notify
73 * signal to watch for changes on the #GDBusObjectManagerClient:name-owner
74 * property. When the name owner vanishes, the behavior is that
75 * #GDBusObjectManagerClient:name-owner is set to %NULL (this includes
76 * emission of the #GObject::notify signal) and then
77 * #GDBusObjectManager::object-removed signals are synthesized
78 * for all currently existing object proxies. Since
79 * #GDBusObjectManagerClient:name-owner is %NULL when this happens, you can
80 * use this information to disambiguate a synthesized signal from a
81 * genuine signal caused by object removal on the remote
82 * #GDBusObjectManager. Similarly, when a new name owner appears,
83 * #GDBusObjectManager::object-added signals are synthesized
84 * while #GDBusObjectManagerClient:name-owner is still %NULL. Only when all
85 * object proxies have been added, the #GDBusObjectManagerClient:name-owner
86 * is set to the new name owner (this includes emission of the
87 * #GObject::notify signal). Furthermore, you are guaranteed that
88 * #GDBusObjectManagerClient:name-owner will alternate between a name owner
89 * (e.g. `:1.42`) and %NULL even in the case where
90 * the name of interest is atomically replaced
91 *
92 * Ultimately, #GDBusObjectManagerClient is used to obtain #GDBusProxy
93 * instances. All signals (including the
94 * org.freedesktop.DBus.Properties::PropertiesChanged signal)
95 * delivered to #GDBusProxy instances are guaranteed to originate
96 * from the name owner. This guarantee along with the behavior
97 * described above, means that certain race conditions including the
98 * "half the proxy is from the old owner and the other half is from
99 * the new owner" problem cannot happen.
100 *
101 * To avoid having the application connect to signals on the returned
102 * #GDBusObjectProxy and #GDBusProxy objects, the
103 * #GDBusObject::interface-added,
104 * #GDBusObject::interface-removed,
105 * #GDBusProxy::g-properties-changed and
106 * #GDBusProxy::g-signal signals
107 * are also emitted on the #GDBusObjectManagerClient instance managing these
108 * objects. The signals emitted are
109 * #GDBusObjectManager::interface-added,
110 * #GDBusObjectManager::interface-removed,
111 * #GDBusObjectManagerClient::interface-proxy-properties-changed and
112 * #GDBusObjectManagerClient::interface-proxy-signal.
113 *
114 * Note that all callbacks and signals are emitted in the
115 * [thread-default main context][g-main-context-push-thread-default]
116 * that the #GDBusObjectManagerClient object was constructed
117 * in. Additionally, the #GDBusObjectProxy and #GDBusProxy objects
118 * originating from the #GDBusObjectManagerClient object will be created in
119 * the same context and, consequently, will deliver signals in the
120 * same main loop.
121 */
122
123struct _GDBusObjectManagerClientPrivate
124{
125 GMutex lock;
126
127 GBusType bus_type;
128 GDBusConnection *connection;
129 gchar *object_path;
130 gchar *name;
131 gchar *name_owner;
132 GDBusObjectManagerClientFlags flags;
133
134 GDBusProxy *control_proxy;
135
136 GHashTable *map_object_path_to_object_proxy;
137
138 guint signal_subscription_id;
139 gchar *match_rule;
140
141 GDBusProxyTypeFunc get_proxy_type_func;
142 gpointer get_proxy_type_user_data;
143 GDestroyNotify get_proxy_type_destroy_notify;
144
145 gulong name_owner_signal_id;
146 gulong signal_signal_id;
147};
148
149enum
150{
151 PROP_0,
152 PROP_BUS_TYPE,
153 PROP_CONNECTION,
154 PROP_FLAGS,
155 PROP_OBJECT_PATH,
156 PROP_NAME,
157 PROP_NAME_OWNER,
158 PROP_GET_PROXY_TYPE_FUNC,
159 PROP_GET_PROXY_TYPE_USER_DATA,
160 PROP_GET_PROXY_TYPE_DESTROY_NOTIFY
161};
162
163enum
164{
165 INTERFACE_PROXY_SIGNAL_SIGNAL,
166 INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL,
167 LAST_SIGNAL
168};
169
170static guint signals[LAST_SIGNAL] = { 0 };
171
172static void initable_iface_init (GInitableIface *initable_iface);
173static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface);
174static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface);
175
176G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerClient, g_dbus_object_manager_client, G_TYPE_OBJECT,
177 G_ADD_PRIVATE (GDBusObjectManagerClient)
178 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
179 G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
180 G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init))
181
182static void maybe_unsubscribe_signals (GDBusObjectManagerClient *manager);
183
184static void on_control_proxy_g_signal (GDBusProxy *proxy,
185 const gchar *sender_name,
186 const gchar *signal_name,
187 GVariant *parameters,
188 gpointer user_data);
189
190static void process_get_all_result (GDBusObjectManagerClient *manager,
191 GVariant *value,
192 const gchar *name_owner);
193
194static void
195g_dbus_object_manager_client_finalize (GObject *object)
196{
197 GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (object);
198
199 maybe_unsubscribe_signals (manager);
200
201 g_hash_table_unref (hash_table: manager->priv->map_object_path_to_object_proxy);
202
203 if (manager->priv->control_proxy != NULL && manager->priv->signal_signal_id != 0)
204 g_signal_handler_disconnect (instance: manager->priv->control_proxy,
205 handler_id: manager->priv->signal_signal_id);
206 manager->priv->signal_signal_id = 0;
207
208 if (manager->priv->control_proxy != NULL && manager->priv->name_owner_signal_id != 0)
209 g_signal_handler_disconnect (instance: manager->priv->control_proxy,
210 handler_id: manager->priv->name_owner_signal_id);
211 manager->priv->name_owner_signal_id = 0;
212
213 g_clear_object (&manager->priv->control_proxy);
214
215 if (manager->priv->connection != NULL)
216 g_object_unref (object: manager->priv->connection);
217 g_free (mem: manager->priv->object_path);
218 g_free (mem: manager->priv->name);
219 g_free (mem: manager->priv->name_owner);
220
221 if (manager->priv->get_proxy_type_destroy_notify != NULL)
222 manager->priv->get_proxy_type_destroy_notify (manager->priv->get_proxy_type_user_data);
223
224 g_mutex_clear (mutex: &manager->priv->lock);
225
226 if (G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize != NULL)
227 G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize (object);
228}
229
230static void
231g_dbus_object_manager_client_get_property (GObject *_object,
232 guint prop_id,
233 GValue *value,
234 GParamSpec *pspec)
235{
236 GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object);
237
238 switch (prop_id)
239 {
240 case PROP_CONNECTION:
241 g_value_set_object (value, v_object: g_dbus_object_manager_client_get_connection (manager));
242 break;
243
244 case PROP_OBJECT_PATH:
245 g_value_set_string (value, v_string: g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager)));
246 break;
247
248 case PROP_NAME:
249 g_value_set_string (value, v_string: g_dbus_object_manager_client_get_name (manager));
250 break;
251
252 case PROP_FLAGS:
253 g_value_set_flags (value, v_flags: g_dbus_object_manager_client_get_flags (manager));
254 break;
255
256 case PROP_NAME_OWNER:
257 g_value_take_string (value, v_string: g_dbus_object_manager_client_get_name_owner (manager));
258 break;
259
260 default:
261 G_OBJECT_WARN_INVALID_PROPERTY_ID (manager, prop_id, pspec);
262 break;
263 }
264}
265
266static void
267g_dbus_object_manager_client_set_property (GObject *_object,
268 guint prop_id,
269 const GValue *value,
270 GParamSpec *pspec)
271{
272 GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object);
273 const gchar *name;
274
275 switch (prop_id)
276 {
277 case PROP_BUS_TYPE:
278 manager->priv->bus_type = g_value_get_enum (value);
279 break;
280
281 case PROP_CONNECTION:
282 if (g_value_get_object (value) != NULL)
283 {
284 g_assert (manager->priv->connection == NULL);
285 g_assert (G_IS_DBUS_CONNECTION (g_value_get_object (value)));
286 manager->priv->connection = g_value_dup_object (value);
287 }
288 break;
289
290 case PROP_OBJECT_PATH:
291 g_assert (manager->priv->object_path == NULL);
292 g_assert (g_variant_is_object_path (g_value_get_string (value)));
293 manager->priv->object_path = g_value_dup_string (value);
294 break;
295
296 case PROP_NAME:
297 g_assert (manager->priv->name == NULL);
298 name = g_value_get_string (value);
299 g_assert (name == NULL || g_dbus_is_name (name));
300 manager->priv->name = g_strdup (str: name);
301 break;
302
303 case PROP_FLAGS:
304 manager->priv->flags = g_value_get_flags (value);
305 break;
306
307 case PROP_GET_PROXY_TYPE_FUNC:
308 manager->priv->get_proxy_type_func = g_value_get_pointer (value);
309 break;
310
311 case PROP_GET_PROXY_TYPE_USER_DATA:
312 manager->priv->get_proxy_type_user_data = g_value_get_pointer (value);
313 break;
314
315 case PROP_GET_PROXY_TYPE_DESTROY_NOTIFY:
316 manager->priv->get_proxy_type_destroy_notify = g_value_get_pointer (value);
317 break;
318
319 default:
320 G_OBJECT_WARN_INVALID_PROPERTY_ID (manager, prop_id, pspec);
321 break;
322 }
323}
324
325static void
326g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass)
327{
328 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
329
330 gobject_class->finalize = g_dbus_object_manager_client_finalize;
331 gobject_class->set_property = g_dbus_object_manager_client_set_property;
332 gobject_class->get_property = g_dbus_object_manager_client_get_property;
333
334 /**
335 * GDBusObjectManagerClient:connection:
336 *
337 * The #GDBusConnection to use.
338 *
339 * Since: 2.30
340 */
341 g_object_class_install_property (oclass: gobject_class,
342 property_id: PROP_CONNECTION,
343 pspec: g_param_spec_object (name: "connection",
344 nick: "Connection",
345 blurb: "The connection to use",
346 G_TYPE_DBUS_CONNECTION,
347 flags: G_PARAM_READABLE |
348 G_PARAM_WRITABLE |
349 G_PARAM_CONSTRUCT_ONLY |
350 G_PARAM_STATIC_STRINGS));
351
352 /**
353 * GDBusObjectManagerClient:bus-type:
354 *
355 * If this property is not %G_BUS_TYPE_NONE, then
356 * #GDBusObjectManagerClient:connection must be %NULL and will be set to the
357 * #GDBusConnection obtained by calling g_bus_get() with the value
358 * of this property.
359 *
360 * Since: 2.30
361 */
362 g_object_class_install_property (oclass: gobject_class,
363 property_id: PROP_BUS_TYPE,
364 pspec: g_param_spec_enum (name: "bus-type",
365 nick: "Bus Type",
366 blurb: "The bus to connect to, if any",
367 enum_type: G_TYPE_BUS_TYPE,
368 default_value: G_BUS_TYPE_NONE,
369 flags: G_PARAM_WRITABLE |
370 G_PARAM_CONSTRUCT_ONLY |
371 G_PARAM_STATIC_NAME |
372 G_PARAM_STATIC_BLURB |
373 G_PARAM_STATIC_NICK));
374
375 /**
376 * GDBusObjectManagerClient:flags:
377 *
378 * Flags from the #GDBusObjectManagerClientFlags enumeration.
379 *
380 * Since: 2.30
381 */
382 g_object_class_install_property (oclass: gobject_class,
383 property_id: PROP_FLAGS,
384 pspec: g_param_spec_flags (name: "flags",
385 nick: "Flags",
386 blurb: "Flags for the proxy manager",
387 flags_type: G_TYPE_DBUS_OBJECT_MANAGER_CLIENT_FLAGS,
388 default_value: G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
389 flags: G_PARAM_READABLE |
390 G_PARAM_WRITABLE |
391 G_PARAM_CONSTRUCT_ONLY |
392 G_PARAM_STATIC_NAME |
393 G_PARAM_STATIC_BLURB |
394 G_PARAM_STATIC_NICK));
395
396 /**
397 * GDBusObjectManagerClient:object-path:
398 *
399 * The object path the manager is for.
400 *
401 * Since: 2.30
402 */
403 g_object_class_install_property (oclass: gobject_class,
404 property_id: PROP_OBJECT_PATH,
405 pspec: g_param_spec_string (name: "object-path",
406 nick: "Object Path",
407 blurb: "The object path of the control object",
408 NULL,
409 flags: G_PARAM_READABLE |
410 G_PARAM_WRITABLE |
411 G_PARAM_CONSTRUCT_ONLY |
412 G_PARAM_STATIC_STRINGS));
413
414 /**
415 * GDBusObjectManagerClient:name:
416 *
417 * The well-known name or unique name that the manager is for.
418 *
419 * Since: 2.30
420 */
421 g_object_class_install_property (oclass: gobject_class,
422 property_id: PROP_NAME,
423 pspec: g_param_spec_string (name: "name",
424 nick: "Name",
425 blurb: "Name that the manager is for",
426 NULL,
427 flags: G_PARAM_READABLE |
428 G_PARAM_WRITABLE |
429 G_PARAM_CONSTRUCT_ONLY |
430 G_PARAM_STATIC_STRINGS));
431
432 /**
433 * GDBusObjectManagerClient:name-owner:
434 *
435 * The unique name that owns #GDBusObjectManagerClient:name or %NULL if
436 * no-one is currently owning the name. Connect to the
437 * #GObject::notify signal to track changes to this property.
438 *
439 * Since: 2.30
440 */
441 g_object_class_install_property (oclass: gobject_class,
442 property_id: PROP_NAME_OWNER,
443 pspec: g_param_spec_string (name: "name-owner",
444 nick: "Name Owner",
445 blurb: "The owner of the name we are watching",
446 NULL,
447 flags: G_PARAM_READABLE |
448 G_PARAM_STATIC_STRINGS));
449
450 /**
451 * GDBusObjectManagerClient:get-proxy-type-func:
452 *
453 * The #GDBusProxyTypeFunc to use when determining what #GType to
454 * use for interface proxies or %NULL.
455 *
456 * Since: 2.30
457 */
458 g_object_class_install_property (oclass: gobject_class,
459 property_id: PROP_GET_PROXY_TYPE_FUNC,
460 pspec: g_param_spec_pointer (name: "get-proxy-type-func",
461 nick: "GDBusProxyTypeFunc Function Pointer",
462 blurb: "The GDBusProxyTypeFunc pointer to use",
463 flags: G_PARAM_READABLE |
464 G_PARAM_WRITABLE |
465 G_PARAM_CONSTRUCT_ONLY |
466 G_PARAM_STATIC_STRINGS));
467
468 /**
469 * GDBusObjectManagerClient:get-proxy-type-user-data:
470 *
471 * The #gpointer user_data to pass to #GDBusObjectManagerClient:get-proxy-type-func.
472 *
473 * Since: 2.30
474 */
475 g_object_class_install_property (oclass: gobject_class,
476 property_id: PROP_GET_PROXY_TYPE_USER_DATA,
477 pspec: g_param_spec_pointer (name: "get-proxy-type-user-data",
478 nick: "GDBusProxyTypeFunc User Data",
479 blurb: "The GDBusProxyTypeFunc user_data",
480 flags: G_PARAM_READABLE |
481 G_PARAM_WRITABLE |
482 G_PARAM_CONSTRUCT_ONLY |
483 G_PARAM_STATIC_STRINGS));
484
485 /**
486 * GDBusObjectManagerClient:get-proxy-type-destroy-notify:
487 *
488 * A #GDestroyNotify for the #gpointer user_data in #GDBusObjectManagerClient:get-proxy-type-user-data.
489 *
490 * Since: 2.30
491 */
492 g_object_class_install_property (oclass: gobject_class,
493 property_id: PROP_GET_PROXY_TYPE_DESTROY_NOTIFY,
494 pspec: g_param_spec_pointer (name: "get-proxy-type-destroy-notify",
495 nick: "GDBusProxyTypeFunc user data free function",
496 blurb: "The GDBusProxyTypeFunc user data free function",
497 flags: G_PARAM_READABLE |
498 G_PARAM_WRITABLE |
499 G_PARAM_CONSTRUCT_ONLY |
500 G_PARAM_STATIC_STRINGS));
501
502 /**
503 * GDBusObjectManagerClient::interface-proxy-signal:
504 * @manager: The #GDBusObjectManagerClient emitting the signal.
505 * @object_proxy: The #GDBusObjectProxy on which an interface is emitting a D-Bus signal.
506 * @interface_proxy: The #GDBusProxy that is emitting a D-Bus signal.
507 * @sender_name: The sender of the signal or NULL if the connection is not a bus connection.
508 * @signal_name: The signal name.
509 * @parameters: A #GVariant tuple with parameters for the signal.
510 *
511 * Emitted when a D-Bus signal is received on @interface_proxy.
512 *
513 * This signal exists purely as a convenience to avoid having to
514 * connect signals to all interface proxies managed by @manager.
515 *
516 * This signal is emitted in the
517 * [thread-default main context][g-main-context-push-thread-default]
518 * that @manager was constructed in.
519 *
520 * Since: 2.30
521 */
522 signals[INTERFACE_PROXY_SIGNAL_SIGNAL] =
523 g_signal_new (I_("interface-proxy-signal"),
524 G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
525 signal_flags: G_SIGNAL_RUN_LAST,
526 G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_signal),
527 NULL,
528 NULL,
529 c_marshaller: _g_cclosure_marshal_VOID__OBJECT_OBJECT_STRING_STRING_VARIANT,
530 G_TYPE_NONE,
531 n_params: 5,
532 G_TYPE_DBUS_OBJECT_PROXY,
533 G_TYPE_DBUS_PROXY,
534 G_TYPE_STRING,
535 G_TYPE_STRING,
536 G_TYPE_VARIANT);
537 g_signal_set_va_marshaller (signal_id: signals[INTERFACE_PROXY_SIGNAL_SIGNAL],
538 G_TYPE_FROM_CLASS (klass),
539 va_marshaller: _g_cclosure_marshal_VOID__OBJECT_OBJECT_STRING_STRING_VARIANTv);
540
541 /**
542 * GDBusObjectManagerClient::interface-proxy-properties-changed:
543 * @manager: The #GDBusObjectManagerClient emitting the signal.
544 * @object_proxy: The #GDBusObjectProxy on which an interface has properties that are changing.
545 * @interface_proxy: The #GDBusProxy that has properties that are changing.
546 * @changed_properties: A #GVariant containing the properties that changed (type: `a{sv}`).
547 * @invalidated_properties: (array zero-terminated=1) (element-type utf8): A %NULL terminated
548 * array of properties that were invalidated.
549 *
550 * Emitted when one or more D-Bus properties on proxy changes. The
551 * local cache has already been updated when this signal fires. Note
552 * that both @changed_properties and @invalidated_properties are
553 * guaranteed to never be %NULL (either may be empty though).
554 *
555 * This signal exists purely as a convenience to avoid having to
556 * connect signals to all interface proxies managed by @manager.
557 *
558 * This signal is emitted in the
559 * [thread-default main context][g-main-context-push-thread-default]
560 * that @manager was constructed in.
561 *
562 * Since: 2.30
563 */
564 signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL] =
565 g_signal_new (I_("interface-proxy-properties-changed"),
566 G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
567 signal_flags: G_SIGNAL_RUN_LAST,
568 G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_properties_changed),
569 NULL,
570 NULL,
571 c_marshaller: _g_cclosure_marshal_VOID__OBJECT_OBJECT_VARIANT_BOXED,
572 G_TYPE_NONE,
573 n_params: 4,
574 G_TYPE_DBUS_OBJECT_PROXY,
575 G_TYPE_DBUS_PROXY,
576 G_TYPE_VARIANT,
577 G_TYPE_STRV);
578 g_signal_set_va_marshaller (signal_id: signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL],
579 G_TYPE_FROM_CLASS (klass),
580 va_marshaller: _g_cclosure_marshal_VOID__OBJECT_OBJECT_VARIANT_BOXEDv);
581}
582
583static void
584g_dbus_object_manager_client_init (GDBusObjectManagerClient *manager)
585{
586 manager->priv = g_dbus_object_manager_client_get_instance_private (self: manager);
587 g_mutex_init (mutex: &manager->priv->lock);
588 manager->priv->map_object_path_to_object_proxy = g_hash_table_new_full (hash_func: g_str_hash,
589 key_equal_func: g_str_equal,
590 key_destroy_func: g_free,
591 value_destroy_func: (GDestroyNotify) g_object_unref);
592}
593
594/* ---------------------------------------------------------------------------------------------------- */
595
596/**
597 * g_dbus_object_manager_client_new_sync:
598 * @connection: A #GDBusConnection.
599 * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
600 * @name: (nullable): The owner of the control object (unique or well-known name), or %NULL when not using a message bus connection.
601 * @object_path: The object path of the control object.
602 * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
603 * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
604 * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL.
605 * @cancellable: (nullable): A #GCancellable or %NULL
606 * @error: Return location for error or %NULL.
607 *
608 * Creates a new #GDBusObjectManagerClient object.
609 *
610 * This is a synchronous failable constructor - the calling thread is
611 * blocked until a reply is received. See g_dbus_object_manager_client_new()
612 * for the asynchronous version.
613 *
614 * Returns: (transfer full) (type GDBusObjectManagerClient): A
615 * #GDBusObjectManagerClient object or %NULL if @error is set. Free
616 * with g_object_unref().
617 *
618 * Since: 2.30
619 */
620GDBusObjectManager *
621g_dbus_object_manager_client_new_sync (GDBusConnection *connection,
622 GDBusObjectManagerClientFlags flags,
623 const gchar *name,
624 const gchar *object_path,
625 GDBusProxyTypeFunc get_proxy_type_func,
626 gpointer get_proxy_type_user_data,
627 GDestroyNotify get_proxy_type_destroy_notify,
628 GCancellable *cancellable,
629 GError **error)
630{
631 GInitable *initable;
632
633 g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
634 g_return_val_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) ||
635 g_dbus_is_name (name), NULL);
636 g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
637 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
638
639 initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
640 cancellable,
641 error,
642 first_property_name: "connection", connection,
643 "flags", flags,
644 "name", name,
645 "object-path", object_path,
646 "get-proxy-type-func", get_proxy_type_func,
647 "get-proxy-type-user-data", get_proxy_type_user_data,
648 "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify,
649 NULL);
650 if (initable != NULL)
651 return G_DBUS_OBJECT_MANAGER (initable);
652 else
653 return NULL;
654}
655
656/**
657 * g_dbus_object_manager_client_new:
658 * @connection: A #GDBusConnection.
659 * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
660 * @name: The owner of the control object (unique or well-known name).
661 * @object_path: The object path of the control object.
662 * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
663 * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
664 * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL.
665 * @cancellable: (nullable): A #GCancellable or %NULL
666 * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
667 * @user_data: The data to pass to @callback.
668 *
669 * Asynchronously creates a new #GDBusObjectManagerClient object.
670 *
671 * This is an asynchronous failable constructor. When the result is
672 * ready, @callback will be invoked in the
673 * [thread-default main context][g-main-context-push-thread-default]
674 * of the thread you are calling this method from. You can
675 * then call g_dbus_object_manager_client_new_finish() to get the result. See
676 * g_dbus_object_manager_client_new_sync() for the synchronous version.
677 *
678 * Since: 2.30
679 */
680void
681g_dbus_object_manager_client_new (GDBusConnection *connection,
682 GDBusObjectManagerClientFlags flags,
683 const gchar *name,
684 const gchar *object_path,
685 GDBusProxyTypeFunc get_proxy_type_func,
686 gpointer get_proxy_type_user_data,
687 GDestroyNotify get_proxy_type_destroy_notify,
688 GCancellable *cancellable,
689 GAsyncReadyCallback callback,
690 gpointer user_data)
691{
692 g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
693 g_return_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) ||
694 g_dbus_is_name (name));
695 g_return_if_fail (g_variant_is_object_path (object_path));
696
697 g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
698 G_PRIORITY_DEFAULT,
699 cancellable,
700 callback,
701 user_data,
702 first_property_name: "connection", connection,
703 "flags", flags,
704 "name", name,
705 "object-path", object_path,
706 "get-proxy-type-func", get_proxy_type_func,
707 "get-proxy-type-user-data", get_proxy_type_user_data,
708 "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify,
709 NULL);
710}
711
712/**
713 * g_dbus_object_manager_client_new_finish:
714 * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new().
715 * @error: Return location for error or %NULL.
716 *
717 * Finishes an operation started with g_dbus_object_manager_client_new().
718 *
719 * Returns: (transfer full) (type GDBusObjectManagerClient): A
720 * #GDBusObjectManagerClient object or %NULL if @error is set. Free
721 * with g_object_unref().
722 *
723 * Since: 2.30
724 */
725GDBusObjectManager *
726g_dbus_object_manager_client_new_finish (GAsyncResult *res,
727 GError **error)
728{
729 GObject *object;
730 GObject *source_object;
731
732 source_object = g_async_result_get_source_object (res);
733 g_assert (source_object != NULL);
734
735 object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
736 res,
737 error);
738 g_object_unref (object: source_object);
739
740 if (object != NULL)
741 return G_DBUS_OBJECT_MANAGER (object);
742 else
743 return NULL;
744}
745
746/* ---------------------------------------------------------------------------------------------------- */
747
748/**
749 * g_dbus_object_manager_client_new_for_bus_sync:
750 * @bus_type: A #GBusType.
751 * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
752 * @name: The owner of the control object (unique or well-known name).
753 * @object_path: The object path of the control object.
754 * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
755 * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
756 * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL.
757 * @cancellable: (nullable): A #GCancellable or %NULL
758 * @error: Return location for error or %NULL.
759 *
760 * Like g_dbus_object_manager_client_new_sync() but takes a #GBusType instead
761 * of a #GDBusConnection.
762 *
763 * This is a synchronous failable constructor - the calling thread is
764 * blocked until a reply is received. See g_dbus_object_manager_client_new_for_bus()
765 * for the asynchronous version.
766 *
767 * Returns: (transfer full) (type GDBusObjectManagerClient): A
768 * #GDBusObjectManagerClient object or %NULL if @error is set. Free
769 * with g_object_unref().
770 *
771 * Since: 2.30
772 */
773GDBusObjectManager *
774g_dbus_object_manager_client_new_for_bus_sync (GBusType bus_type,
775 GDBusObjectManagerClientFlags flags,
776 const gchar *name,
777 const gchar *object_path,
778 GDBusProxyTypeFunc get_proxy_type_func,
779 gpointer get_proxy_type_user_data,
780 GDestroyNotify get_proxy_type_destroy_notify,
781 GCancellable *cancellable,
782 GError **error)
783{
784 GInitable *initable;
785
786 g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, NULL);
787 g_return_val_if_fail (g_dbus_is_name (name), NULL);
788 g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
789 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
790
791 initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
792 cancellable,
793 error,
794 first_property_name: "bus-type", bus_type,
795 "flags", flags,
796 "name", name,
797 "object-path", object_path,
798 "get-proxy-type-func", get_proxy_type_func,
799 "get-proxy-type-user-data", get_proxy_type_user_data,
800 "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify,
801 NULL);
802 if (initable != NULL)
803 return G_DBUS_OBJECT_MANAGER (initable);
804 else
805 return NULL;
806}
807
808/**
809 * g_dbus_object_manager_client_new_for_bus:
810 * @bus_type: A #GBusType.
811 * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
812 * @name: The owner of the control object (unique or well-known name).
813 * @object_path: The object path of the control object.
814 * @get_proxy_type_func: (nullable): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
815 * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
816 * @get_proxy_type_destroy_notify: (nullable): Free function for @get_proxy_type_user_data or %NULL.
817 * @cancellable: (nullable): A #GCancellable or %NULL
818 * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
819 * @user_data: The data to pass to @callback.
820 *
821 * Like g_dbus_object_manager_client_new() but takes a #GBusType instead of a
822 * #GDBusConnection.
823 *
824 * This is an asynchronous failable constructor. When the result is
825 * ready, @callback will be invoked in the
826 * [thread-default main loop][g-main-context-push-thread-default]
827 * of the thread you are calling this method from. You can
828 * then call g_dbus_object_manager_client_new_for_bus_finish() to get the result. See
829 * g_dbus_object_manager_client_new_for_bus_sync() for the synchronous version.
830 *
831 * Since: 2.30
832 */
833void
834g_dbus_object_manager_client_new_for_bus (GBusType bus_type,
835 GDBusObjectManagerClientFlags flags,
836 const gchar *name,
837 const gchar *object_path,
838 GDBusProxyTypeFunc get_proxy_type_func,
839 gpointer get_proxy_type_user_data,
840 GDestroyNotify get_proxy_type_destroy_notify,
841 GCancellable *cancellable,
842 GAsyncReadyCallback callback,
843 gpointer user_data)
844{
845 g_return_if_fail (bus_type != G_BUS_TYPE_NONE);
846 g_return_if_fail (g_dbus_is_name (name));
847 g_return_if_fail (g_variant_is_object_path (object_path));
848
849 g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
850 G_PRIORITY_DEFAULT,
851 cancellable,
852 callback,
853 user_data,
854 first_property_name: "bus-type", bus_type,
855 "flags", flags,
856 "name", name,
857 "object-path", object_path,
858 "get-proxy-type-func", get_proxy_type_func,
859 "get-proxy-type-user-data", get_proxy_type_user_data,
860 "get-proxy-type-destroy-notify", get_proxy_type_destroy_notify,
861 NULL);
862}
863
864/**
865 * g_dbus_object_manager_client_new_for_bus_finish:
866 * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new_for_bus().
867 * @error: Return location for error or %NULL.
868 *
869 * Finishes an operation started with g_dbus_object_manager_client_new_for_bus().
870 *
871 * Returns: (transfer full) (type GDBusObjectManagerClient): A
872 * #GDBusObjectManagerClient object or %NULL if @error is set. Free
873 * with g_object_unref().
874 *
875 * Since: 2.30
876 */
877GDBusObjectManager *
878g_dbus_object_manager_client_new_for_bus_finish (GAsyncResult *res,
879 GError **error)
880{
881 GObject *object;
882 GObject *source_object;
883
884 source_object = g_async_result_get_source_object (res);
885 g_assert (source_object != NULL);
886
887 object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
888 res,
889 error);
890 g_object_unref (object: source_object);
891
892 if (object != NULL)
893 return G_DBUS_OBJECT_MANAGER (object);
894 else
895 return NULL;
896}
897
898/* ---------------------------------------------------------------------------------------------------- */
899
900/**
901 * g_dbus_object_manager_client_get_connection:
902 * @manager: A #GDBusObjectManagerClient
903 *
904 * Gets the #GDBusConnection used by @manager.
905 *
906 * Returns: (transfer none): A #GDBusConnection object. Do not free,
907 * the object belongs to @manager.
908 *
909 * Since: 2.30
910 */
911GDBusConnection *
912g_dbus_object_manager_client_get_connection (GDBusObjectManagerClient *manager)
913{
914 GDBusConnection *ret;
915 g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
916 g_mutex_lock (mutex: &manager->priv->lock);
917 ret = manager->priv->connection;
918 g_mutex_unlock (mutex: &manager->priv->lock);
919 return ret;
920}
921
922/**
923 * g_dbus_object_manager_client_get_name:
924 * @manager: A #GDBusObjectManagerClient
925 *
926 * Gets the name that @manager is for, or %NULL if not a message bus
927 * connection.
928 *
929 * Returns: A unique or well-known name. Do not free, the string
930 * belongs to @manager.
931 *
932 * Since: 2.30
933 */
934const gchar *
935g_dbus_object_manager_client_get_name (GDBusObjectManagerClient *manager)
936{
937 const gchar *ret;
938 g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
939 g_mutex_lock (mutex: &manager->priv->lock);
940 ret = manager->priv->name;
941 g_mutex_unlock (mutex: &manager->priv->lock);
942 return ret;
943}
944
945/**
946 * g_dbus_object_manager_client_get_flags:
947 * @manager: A #GDBusObjectManagerClient
948 *
949 * Gets the flags that @manager was constructed with.
950 *
951 * Returns: Zero of more flags from the #GDBusObjectManagerClientFlags
952 * enumeration.
953 *
954 * Since: 2.30
955 */
956GDBusObjectManagerClientFlags
957g_dbus_object_manager_client_get_flags (GDBusObjectManagerClient *manager)
958{
959 GDBusObjectManagerClientFlags ret;
960 g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE);
961 g_mutex_lock (mutex: &manager->priv->lock);
962 ret = manager->priv->flags;
963 g_mutex_unlock (mutex: &manager->priv->lock);
964 return ret;
965}
966
967/**
968 * g_dbus_object_manager_client_get_name_owner:
969 * @manager: A #GDBusObjectManagerClient.
970 *
971 * The unique name that owns the name that @manager is for or %NULL if
972 * no-one currently owns that name. You can connect to the
973 * #GObject::notify signal to track changes to the
974 * #GDBusObjectManagerClient:name-owner property.
975 *
976 * Returns: (nullable): The name owner or %NULL if no name owner
977 * exists. Free with g_free().
978 *
979 * Since: 2.30
980 */
981gchar *
982g_dbus_object_manager_client_get_name_owner (GDBusObjectManagerClient *manager)
983{
984 gchar *ret;
985 g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
986 g_mutex_lock (mutex: &manager->priv->lock);
987 ret = g_strdup (str: manager->priv->name_owner);
988 g_mutex_unlock (mutex: &manager->priv->lock);
989 return ret;
990}
991
992/* ---------------------------------------------------------------------------------------------------- */
993
994/* signal handler for all objects we manage - we dispatch signals
995 * from here to the objects
996 */
997static void
998signal_cb (GDBusConnection *connection,
999 const gchar *sender_name,
1000 const gchar *object_path,
1001 const gchar *interface_name,
1002 const gchar *signal_name,
1003 GVariant *parameters,
1004 gpointer user_data)
1005{
1006 GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (user_data);
1007 GDBusObjectProxy *object_proxy;
1008 GDBusInterface *interface;
1009
1010 g_mutex_lock (mutex: &manager->priv->lock);
1011 object_proxy = g_hash_table_lookup (hash_table: manager->priv->map_object_path_to_object_proxy, key: object_path);
1012 if (object_proxy == NULL)
1013 {
1014 g_mutex_unlock (mutex: &manager->priv->lock);
1015 goto out;
1016 }
1017 g_object_ref (object_proxy);
1018 g_mutex_unlock (mutex: &manager->priv->lock);
1019
1020 //g_debug ("yay, signal_cb %s %s: %s\n", signal_name, object_path, g_variant_print (parameters, TRUE));
1021
1022 g_object_ref (manager);
1023 if (g_strcmp0 (str1: interface_name, str2: "org.freedesktop.DBus.Properties") == 0)
1024 {
1025 if (g_strcmp0 (str1: signal_name, str2: "PropertiesChanged") == 0)
1026 {
1027 const gchar *interface_name;
1028 GVariant *changed_properties;
1029 const gchar **invalidated_properties;
1030
1031 g_variant_get (value: parameters,
1032 format_string: "(&s@a{sv}^a&s)",
1033 &interface_name,
1034 &changed_properties,
1035 &invalidated_properties);
1036
1037 interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), interface_name);
1038 if (interface != NULL)
1039 {
1040 GVariantIter property_iter;
1041 const gchar *property_name;
1042 GVariant *property_value;
1043 guint n;
1044
1045 /* update caches... */
1046 g_variant_iter_init (iter: &property_iter, value: changed_properties);
1047 while (g_variant_iter_next (iter: &property_iter,
1048 format_string: "{&sv}",
1049 &property_name,
1050 &property_value))
1051 {
1052 g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface),
1053 property_name,
1054 value: property_value);
1055 g_variant_unref (value: property_value);
1056 }
1057
1058 for (n = 0; invalidated_properties[n] != NULL; n++)
1059 {
1060 g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface),
1061 property_name: invalidated_properties[n],
1062 NULL);
1063 }
1064 /* ... and then synthesize the signal */
1065 g_signal_emit_by_name (instance: interface,
1066 detailed_signal: "g-properties-changed",
1067 changed_properties,
1068 invalidated_properties);
1069 g_signal_emit (instance: manager,
1070 signal_id: signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL],
1071 detail: 0,
1072 object_proxy,
1073 interface,
1074 changed_properties,
1075 invalidated_properties);
1076 g_object_unref (object: interface);
1077 }
1078 g_variant_unref (value: changed_properties);
1079 g_free (mem: invalidated_properties);
1080 }
1081 }
1082 else
1083 {
1084 /* regular signal - just dispatch it */
1085 interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), interface_name);
1086 if (interface != NULL)
1087 {
1088 g_signal_emit_by_name (instance: interface,
1089 detailed_signal: "g-signal",
1090 sender_name,
1091 signal_name,
1092 parameters);
1093 g_signal_emit (instance: manager,
1094 signal_id: signals[INTERFACE_PROXY_SIGNAL_SIGNAL],
1095 detail: 0,
1096 object_proxy,
1097 interface,
1098 sender_name,
1099 signal_name,
1100 parameters);
1101 g_object_unref (object: interface);
1102 }
1103 }
1104 g_object_unref (object: manager);
1105
1106 out:
1107 g_clear_object (&object_proxy);
1108}
1109
1110static void
1111subscribe_signals (GDBusObjectManagerClient *manager,
1112 const gchar *name_owner)
1113{
1114 GError *error = NULL;
1115 GVariant *ret;
1116
1117 g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager));
1118 g_return_if_fail (manager->priv->signal_subscription_id == 0);
1119 g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner));
1120
1121 if (name_owner != NULL)
1122 {
1123 /* Only add path_namespace if it's non-'/'. This removes a no-op key from
1124 * the match rule, and also works around a D-Bus bug where
1125 * path_namespace='/' matches nothing in D-Bus versions < 1.6.18.
1126 *
1127 * See: https://bugs.freedesktop.org/show_bug.cgi?id=70799 */
1128 if (g_str_equal (v1: manager->priv->object_path, v2: "/"))
1129 {
1130 manager->priv->match_rule = g_strdup_printf (format: "type='signal',sender='%s'",
1131 name_owner);
1132 }
1133 else
1134 {
1135 manager->priv->match_rule = g_strdup_printf (format: "type='signal',sender='%s',path_namespace='%s'",
1136 name_owner, manager->priv->object_path);
1137 }
1138
1139 /* The bus daemon may not implement path_namespace so gracefully
1140 * handle this by using a fallback triggered if @error is set. */
1141 ret = g_dbus_connection_call_sync (connection: manager->priv->connection,
1142 bus_name: "org.freedesktop.DBus",
1143 object_path: "/org/freedesktop/DBus",
1144 interface_name: "org.freedesktop.DBus",
1145 method_name: "AddMatch",
1146 parameters: g_variant_new (format_string: "(s)",
1147 manager->priv->match_rule),
1148 NULL, /* reply_type */
1149 flags: G_DBUS_CALL_FLAGS_NONE,
1150 timeout_msec: -1, /* default timeout */
1151 NULL, /* TODO: Cancellable */
1152 error: &error);
1153
1154 /* yay, bus daemon supports path_namespace */
1155 if (ret != NULL)
1156 g_variant_unref (value: ret);
1157 }
1158
1159 if (error == NULL)
1160 {
1161 /* still need to ask GDBusConnection for the callbacks */
1162 manager->priv->signal_subscription_id =
1163 g_dbus_connection_signal_subscribe (connection: manager->priv->connection,
1164 sender: name_owner,
1165 NULL, /* interface */
1166 NULL, /* member */
1167 NULL, /* path - TODO: really want wildcard support here */
1168 NULL, /* arg0 */
1169 flags: G_DBUS_SIGNAL_FLAGS_NONE |
1170 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
1171 callback: signal_cb,
1172 user_data: manager,
1173 NULL); /* user_data_free_func */
1174
1175 }
1176 else
1177 {
1178 /* TODO: we could report this to the user
1179 g_warning ("Message bus daemon does not support path_namespace: %s (%s %d)",
1180 error->message,
1181 g_quark_to_string (error->domain),
1182 error->code);
1183 */
1184
1185 g_error_free (error);
1186
1187 /* no need to call RemoveMatch when done since it didn't work */
1188 g_free (mem: manager->priv->match_rule);
1189 manager->priv->match_rule = NULL;
1190
1191 /* Fallback is to subscribe to *all* signals from the name owner which
1192 * is rather wasteful. It's probably not a big practical problem because
1193 * users typically want all objects that the name owner supplies.
1194 */
1195 manager->priv->signal_subscription_id =
1196 g_dbus_connection_signal_subscribe (connection: manager->priv->connection,
1197 sender: name_owner,
1198 NULL, /* interface */
1199 NULL, /* member */
1200 NULL, /* path - TODO: really want wildcard support here */
1201 NULL, /* arg0 */
1202 flags: G_DBUS_SIGNAL_FLAGS_NONE,
1203 callback: signal_cb,
1204 user_data: manager,
1205 NULL); /* user_data_free_func */
1206 }
1207}
1208
1209static void
1210maybe_unsubscribe_signals (GDBusObjectManagerClient *manager)
1211{
1212 g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager));
1213
1214 if (manager->priv->signal_subscription_id > 0)
1215 {
1216 g_dbus_connection_signal_unsubscribe (connection: manager->priv->connection,
1217 subscription_id: manager->priv->signal_subscription_id);
1218 manager->priv->signal_subscription_id = 0;
1219 }
1220
1221 if (manager->priv->match_rule != NULL)
1222 {
1223 /* Since the AddMatch call succeeded this is guaranteed to not
1224 * fail - therefore, don't bother checking the return value
1225 */
1226 g_dbus_connection_call (connection: manager->priv->connection,
1227 bus_name: "org.freedesktop.DBus",
1228 object_path: "/org/freedesktop/DBus",
1229 interface_name: "org.freedesktop.DBus",
1230 method_name: "RemoveMatch",
1231 parameters: g_variant_new (format_string: "(s)",
1232 manager->priv->match_rule),
1233 NULL, /* reply_type */
1234 flags: G_DBUS_CALL_FLAGS_NONE,
1235 timeout_msec: -1, /* default timeout */
1236 NULL, /* GCancellable */
1237 NULL, /* GAsyncReadyCallback */
1238 NULL); /* user data */
1239 g_free (mem: manager->priv->match_rule);
1240 manager->priv->match_rule = NULL;
1241 }
1242
1243}
1244
1245/* ---------------------------------------------------------------------------------------------------- */
1246
1247static void
1248on_notify_g_name_owner (GObject *object,
1249 GParamSpec *pspec,
1250 gpointer user_data)
1251{
1252 GWeakRef *manager_weak = user_data;
1253 GDBusObjectManagerClient *manager = NULL;
1254 gchar *old_name_owner;
1255 gchar *new_name_owner;
1256
1257 manager = G_DBUS_OBJECT_MANAGER_CLIENT (g_weak_ref_get (manager_weak));
1258 if (manager == NULL)
1259 return;
1260
1261 g_mutex_lock (mutex: &manager->priv->lock);
1262 old_name_owner = manager->priv->name_owner;
1263 new_name_owner = g_dbus_proxy_get_name_owner (proxy: manager->priv->control_proxy);
1264 manager->priv->name_owner = NULL;
1265
1266 if (g_strcmp0 (str1: old_name_owner, str2: new_name_owner) != 0)
1267 {
1268 GList *l;
1269 GList *proxies;
1270
1271 /* remote manager changed; nuke all local proxies */
1272 proxies = g_hash_table_get_values (hash_table: manager->priv->map_object_path_to_object_proxy);
1273 g_list_foreach (list: proxies, func: (GFunc) g_object_ref, NULL);
1274 g_hash_table_remove_all (hash_table: manager->priv->map_object_path_to_object_proxy);
1275
1276 g_mutex_unlock (mutex: &manager->priv->lock);
1277
1278 /* do the :name-owner notify with a NULL name - this way the user knows
1279 * the ::object-proxy-removed following is because the name owner went
1280 * away
1281 */
1282 g_object_notify (G_OBJECT (manager), property_name: "name-owner");
1283
1284 for (l = proxies; l != NULL; l = l->next)
1285 {
1286 GDBusObjectProxy *object_proxy = G_DBUS_OBJECT_PROXY (l->data);
1287 g_signal_emit_by_name (instance: manager, detailed_signal: "object-removed", object_proxy);
1288 }
1289 g_list_free_full (list: proxies, free_func: g_object_unref);
1290
1291 /* nuke local filter */
1292 maybe_unsubscribe_signals (manager);
1293 }
1294 else
1295 {
1296 g_mutex_unlock (mutex: &manager->priv->lock);
1297 }
1298
1299 if (new_name_owner != NULL)
1300 {
1301 GError *error;
1302 GVariant *value;
1303
1304 //g_debug ("repopulating for %s", new_name_owner);
1305
1306 /* TODO: do this async! */
1307 subscribe_signals (manager,
1308 name_owner: new_name_owner);
1309 error = NULL;
1310 value = g_dbus_proxy_call_sync (proxy: manager->priv->control_proxy,
1311 method_name: "GetManagedObjects",
1312 NULL, /* parameters */
1313 flags: G_DBUS_CALL_FLAGS_NONE,
1314 timeout_msec: -1,
1315 NULL,
1316 error: &error);
1317 if (value == NULL)
1318 {
1319 maybe_unsubscribe_signals (manager);
1320 g_warning ("Error calling GetManagedObjects() when name owner %s for name %s came back: %s",
1321 new_name_owner,
1322 manager->priv->name,
1323 error->message);
1324 g_error_free (error);
1325 }
1326 else
1327 {
1328 process_get_all_result (manager, value, name_owner: new_name_owner);
1329 g_variant_unref (value);
1330 }
1331
1332 /* do the :name-owner notify *AFTER* emitting ::object-proxy-added signals - this
1333 * way the user knows that the signals were emitted because the name owner came back
1334 */
1335 g_mutex_lock (mutex: &manager->priv->lock);
1336 manager->priv->name_owner = new_name_owner;
1337 g_mutex_unlock (mutex: &manager->priv->lock);
1338 g_object_notify (G_OBJECT (manager), property_name: "name-owner");
1339
1340 }
1341 g_free (mem: old_name_owner);
1342 g_object_unref (object: manager);
1343}
1344
1345static GWeakRef *
1346weak_ref_new (GObject *object)
1347{
1348 GWeakRef *weak_ref = g_new0 (GWeakRef, 1);
1349 g_weak_ref_init (weak_ref, object);
1350 return g_steal_pointer (&weak_ref);
1351}
1352
1353static void
1354weak_ref_free (GWeakRef *weak_ref)
1355{
1356 g_weak_ref_clear (weak_ref);
1357 g_free (mem: weak_ref);
1358}
1359
1360static gboolean
1361initable_init (GInitable *initable,
1362 GCancellable *cancellable,
1363 GError **error)
1364{
1365 GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (initable);
1366 gboolean ret;
1367 GVariant *value;
1368 GDBusProxyFlags proxy_flags;
1369
1370 ret = FALSE;
1371
1372 if (manager->priv->bus_type != G_BUS_TYPE_NONE)
1373 {
1374 g_assert (manager->priv->connection == NULL);
1375 manager->priv->connection = g_bus_get_sync (bus_type: manager->priv->bus_type, cancellable, error);
1376 if (manager->priv->connection == NULL)
1377 goto out;
1378 }
1379
1380 proxy_flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES;
1381 if (manager->priv->flags & G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START)
1382 proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
1383
1384 manager->priv->control_proxy = g_dbus_proxy_new_sync (connection: manager->priv->connection,
1385 flags: proxy_flags,
1386 NULL, /* GDBusInterfaceInfo* */
1387 name: manager->priv->name,
1388 object_path: manager->priv->object_path,
1389 interface_name: "org.freedesktop.DBus.ObjectManager",
1390 cancellable,
1391 error);
1392 if (manager->priv->control_proxy == NULL)
1393 goto out;
1394
1395 /* Use weak refs here. The @control_proxy will emit its signals in the current
1396 * #GMainContext (since we constructed it just above). However, the user may
1397 * drop the last external reference to this #GDBusObjectManagerClient in
1398 * another thread between a signal being emitted and scheduled in an idle
1399 * callback in this #GMainContext, and that idle callback being invoked. We
1400 * can’t use a strong reference here, as there’s no
1401 * g_dbus_object_manager_client_disconnect() (or similar) method to tell us
1402 * when the last external reference to this object has been dropped, so we
1403 * can’t break a strong reference count cycle. So use weak refs. */
1404 manager->priv->name_owner_signal_id =
1405 g_signal_connect_data (G_OBJECT (manager->priv->control_proxy),
1406 detailed_signal: "notify::g-name-owner",
1407 G_CALLBACK (on_notify_g_name_owner),
1408 data: weak_ref_new (G_OBJECT (manager)),
1409 destroy_data: (GClosureNotify) weak_ref_free,
1410 connect_flags: 0 /* flags */);
1411
1412 manager->priv->signal_signal_id =
1413 g_signal_connect_data (instance: manager->priv->control_proxy,
1414 detailed_signal: "g-signal",
1415 G_CALLBACK (on_control_proxy_g_signal),
1416 data: weak_ref_new (G_OBJECT (manager)),
1417 destroy_data: (GClosureNotify) weak_ref_free,
1418 connect_flags: 0 /* flags */);
1419
1420 manager->priv->name_owner = g_dbus_proxy_get_name_owner (proxy: manager->priv->control_proxy);
1421 if (manager->priv->name_owner == NULL && manager->priv->name != NULL)
1422 {
1423 /* it's perfectly fine if there's no name owner.. we're just going to
1424 * wait until one is ready
1425 */
1426 }
1427 else
1428 {
1429 /* yay, we can get the objects */
1430 subscribe_signals (manager,
1431 name_owner: manager->priv->name_owner);
1432 value = g_dbus_proxy_call_sync (proxy: manager->priv->control_proxy,
1433 method_name: "GetManagedObjects",
1434 NULL, /* parameters */
1435 flags: G_DBUS_CALL_FLAGS_NONE,
1436 timeout_msec: -1,
1437 cancellable,
1438 error);
1439 if (value == NULL)
1440 {
1441 maybe_unsubscribe_signals (manager);
1442
1443 g_warn_if_fail (manager->priv->signal_signal_id != 0);
1444 g_signal_handler_disconnect (instance: manager->priv->control_proxy,
1445 handler_id: manager->priv->signal_signal_id);
1446 manager->priv->signal_signal_id = 0;
1447
1448 g_warn_if_fail (manager->priv->name_owner_signal_id != 0);
1449 g_signal_handler_disconnect (instance: manager->priv->control_proxy,
1450 handler_id: manager->priv->name_owner_signal_id);
1451 manager->priv->name_owner_signal_id = 0;
1452
1453 g_object_unref (object: manager->priv->control_proxy);
1454 manager->priv->control_proxy = NULL;
1455
1456 goto out;
1457 }
1458
1459 process_get_all_result (manager, value, name_owner: manager->priv->name_owner);
1460 g_variant_unref (value);
1461 }
1462
1463 ret = TRUE;
1464
1465 out:
1466 return ret;
1467}
1468
1469static void
1470initable_iface_init (GInitableIface *initable_iface)
1471{
1472 initable_iface->init = initable_init;
1473}
1474
1475static void
1476async_initable_iface_init (GAsyncInitableIface *async_initable_iface)
1477{
1478 /* for now, just use default: run GInitable code in thread */
1479}
1480
1481/* ---------------------------------------------------------------------------------------------------- */
1482
1483static void
1484add_interfaces (GDBusObjectManagerClient *manager,
1485 const gchar *object_path,
1486 GVariant *ifaces_and_properties,
1487 const gchar *name_owner)
1488{
1489 GDBusObjectProxy *op;
1490 gboolean added;
1491 GVariantIter iter;
1492 const gchar *interface_name;
1493 GVariant *properties;
1494 GList *interface_added_signals, *l;
1495 GDBusProxy *interface_proxy;
1496
1497 g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner));
1498
1499 g_mutex_lock (mutex: &manager->priv->lock);
1500
1501 interface_added_signals = NULL;
1502 added = FALSE;
1503
1504 op = g_hash_table_lookup (hash_table: manager->priv->map_object_path_to_object_proxy, key: object_path);
1505 if (op == NULL)
1506 {
1507 GType object_proxy_type;
1508 if (manager->priv->get_proxy_type_func != NULL)
1509 {
1510 object_proxy_type = manager->priv->get_proxy_type_func (manager,
1511 object_path,
1512 NULL,
1513 manager->priv->get_proxy_type_user_data);
1514 g_warn_if_fail (g_type_is_a (object_proxy_type, G_TYPE_DBUS_OBJECT_PROXY));
1515 }
1516 else
1517 {
1518 object_proxy_type = G_TYPE_DBUS_OBJECT_PROXY;
1519 }
1520 op = g_object_new (object_type: object_proxy_type,
1521 first_property_name: "g-connection", manager->priv->connection,
1522 "g-object-path", object_path,
1523 NULL);
1524 added = TRUE;
1525 }
1526 g_object_ref (op);
1527
1528 g_variant_iter_init (iter: &iter, value: ifaces_and_properties);
1529 while (g_variant_iter_next (iter: &iter,
1530 format_string: "{&s@a{sv}}",
1531 &interface_name,
1532 &properties))
1533 {
1534 GError *error;
1535 GType interface_proxy_type;
1536
1537 if (manager->priv->get_proxy_type_func != NULL)
1538 {
1539 interface_proxy_type = manager->priv->get_proxy_type_func (manager,
1540 object_path,
1541 interface_name,
1542 manager->priv->get_proxy_type_user_data);
1543 g_warn_if_fail (g_type_is_a (interface_proxy_type, G_TYPE_DBUS_PROXY));
1544 }
1545 else
1546 {
1547 interface_proxy_type = G_TYPE_DBUS_PROXY;
1548 }
1549
1550 /* this is fine - there is no blocking IO because we pass DO_NOT_LOAD_PROPERTIES and
1551 * DO_NOT_CONNECT_SIGNALS and use a unique name
1552 */
1553 error = NULL;
1554 interface_proxy = g_initable_new (object_type: interface_proxy_type,
1555 NULL, /* GCancellable */
1556 error: &error,
1557 first_property_name: "g-connection", manager->priv->connection,
1558 "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
1559 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
1560 "g-name", name_owner,
1561 "g-object-path", object_path,
1562 "g-interface-name", interface_name,
1563 NULL);
1564 if (interface_proxy == NULL)
1565 {
1566 g_warning ("%s: Error constructing proxy for path %s and interface %s: %s",
1567 G_STRLOC,
1568 object_path,
1569 interface_name,
1570 error->message);
1571 g_error_free (error);
1572 }
1573 else
1574 {
1575 GVariantIter property_iter;
1576 const gchar *property_name;
1577 GVariant *property_value;
1578
1579 /* associate the interface proxy with the object */
1580 g_dbus_interface_set_object (G_DBUS_INTERFACE (interface_proxy),
1581 G_DBUS_OBJECT (op));
1582
1583 g_variant_iter_init (iter: &property_iter, value: properties);
1584 while (g_variant_iter_next (iter: &property_iter,
1585 format_string: "{&sv}",
1586 &property_name,
1587 &property_value))
1588 {
1589 g_dbus_proxy_set_cached_property (proxy: interface_proxy,
1590 property_name,
1591 value: property_value);
1592 g_variant_unref (value: property_value);
1593 }
1594
1595 _g_dbus_object_proxy_add_interface (proxy: op, interface_proxy);
1596 if (!added)
1597 interface_added_signals = g_list_append (list: interface_added_signals, g_object_ref (interface_proxy));
1598 g_object_unref (object: interface_proxy);
1599 }
1600 g_variant_unref (value: properties);
1601 }
1602
1603 if (added)
1604 {
1605 g_hash_table_insert (hash_table: manager->priv->map_object_path_to_object_proxy,
1606 key: g_strdup (str: object_path),
1607 value: op);
1608 }
1609
1610 g_mutex_unlock (mutex: &manager->priv->lock);
1611
1612 /* now that we don't hold the lock any more, emit signals */
1613 g_object_ref (manager);
1614 for (l = interface_added_signals; l != NULL; l = l->next)
1615 {
1616 interface_proxy = G_DBUS_PROXY (l->data);
1617 g_signal_emit_by_name (instance: manager, detailed_signal: "interface-added", op, interface_proxy);
1618 g_object_unref (object: interface_proxy);
1619 }
1620 g_list_free (list: interface_added_signals);
1621
1622 if (added)
1623 g_signal_emit_by_name (instance: manager, detailed_signal: "object-added", op);
1624
1625 g_object_unref (object: manager);
1626 g_object_unref (object: op);
1627}
1628
1629static void
1630remove_interfaces (GDBusObjectManagerClient *manager,
1631 const gchar *object_path,
1632 const gchar *const *interface_names)
1633{
1634 GDBusObjectProxy *op;
1635 GList *interfaces;
1636 guint n;
1637 guint num_interfaces;
1638 guint num_interfaces_to_remove;
1639
1640 g_mutex_lock (mutex: &manager->priv->lock);
1641
1642 op = g_hash_table_lookup (hash_table: manager->priv->map_object_path_to_object_proxy, key: object_path);
1643 if (op == NULL)
1644 {
1645 g_warning ("%s: Processing InterfaceRemoved signal for path %s but no object proxy exists",
1646 G_STRLOC,
1647 object_path);
1648 g_mutex_unlock (mutex: &manager->priv->lock);
1649 goto out;
1650 }
1651
1652 interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (op));
1653 num_interfaces = g_list_length (list: interfaces);
1654 g_list_free_full (list: interfaces, free_func: g_object_unref);
1655
1656 num_interfaces_to_remove = g_strv_length (str_array: (gchar **) interface_names);
1657
1658 /* see if we are going to completety remove the object */
1659 g_object_ref (manager);
1660 if (num_interfaces_to_remove == num_interfaces)
1661 {
1662 g_object_ref (op);
1663 g_warn_if_fail (g_hash_table_remove (manager->priv->map_object_path_to_object_proxy, object_path));
1664 g_mutex_unlock (mutex: &manager->priv->lock);
1665 g_signal_emit_by_name (instance: manager, detailed_signal: "object-removed", op);
1666 g_object_unref (object: op);
1667 }
1668 else
1669 {
1670 g_object_ref (op);
1671 g_mutex_unlock (mutex: &manager->priv->lock);
1672 for (n = 0; interface_names != NULL && interface_names[n] != NULL; n++)
1673 {
1674 GDBusInterface *interface;
1675 interface = g_dbus_object_get_interface (G_DBUS_OBJECT (op), interface_name: interface_names[n]);
1676 _g_dbus_object_proxy_remove_interface (proxy: op, interface_name: interface_names[n]);
1677 if (interface != NULL)
1678 {
1679 g_signal_emit_by_name (instance: manager, detailed_signal: "interface-removed", op, interface);
1680 g_object_unref (object: interface);
1681 }
1682 }
1683 g_object_unref (object: op);
1684 }
1685 g_object_unref (object: manager);
1686 out:
1687 ;
1688}
1689
1690static void
1691process_get_all_result (GDBusObjectManagerClient *manager,
1692 GVariant *value,
1693 const gchar *name_owner)
1694{
1695 GVariant *arg0;
1696 const gchar *object_path;
1697 GVariant *ifaces_and_properties;
1698 GVariantIter iter;
1699
1700 g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner));
1701
1702 arg0 = g_variant_get_child_value (value, index_: 0);
1703 g_variant_iter_init (iter: &iter, value: arg0);
1704 while (g_variant_iter_next (iter: &iter,
1705 format_string: "{&o@a{sa{sv}}}",
1706 &object_path,
1707 &ifaces_and_properties))
1708 {
1709 add_interfaces (manager, object_path, ifaces_and_properties, name_owner);
1710 g_variant_unref (value: ifaces_and_properties);
1711 }
1712 g_variant_unref (value: arg0);
1713}
1714
1715static void
1716on_control_proxy_g_signal (GDBusProxy *proxy,
1717 const gchar *sender_name,
1718 const gchar *signal_name,
1719 GVariant *parameters,
1720 gpointer user_data)
1721{
1722 GWeakRef *manager_weak = user_data;
1723 GDBusObjectManagerClient *manager = NULL;
1724 const gchar *object_path;
1725
1726 manager = G_DBUS_OBJECT_MANAGER_CLIENT (g_weak_ref_get (manager_weak));
1727 if (manager == NULL)
1728 return;
1729
1730 //g_debug ("yay, g_signal %s: %s\n", signal_name, g_variant_print (parameters, TRUE));
1731
1732 if (g_strcmp0 (str1: signal_name, str2: "InterfacesAdded") == 0)
1733 {
1734 GVariant *ifaces_and_properties;
1735 g_variant_get (value: parameters,
1736 format_string: "(&o@a{sa{sv}})",
1737 &object_path,
1738 &ifaces_and_properties);
1739 add_interfaces (manager, object_path, ifaces_and_properties, name_owner: manager->priv->name_owner);
1740 g_variant_unref (value: ifaces_and_properties);
1741 }
1742 else if (g_strcmp0 (str1: signal_name, str2: "InterfacesRemoved") == 0)
1743 {
1744 const gchar **ifaces;
1745 g_variant_get (value: parameters,
1746 format_string: "(&o^a&s)",
1747 &object_path,
1748 &ifaces);
1749 remove_interfaces (manager, object_path, interface_names: ifaces);
1750 g_free (mem: ifaces);
1751 }
1752
1753 g_object_unref (object: manager);
1754}
1755
1756/* ---------------------------------------------------------------------------------------------------- */
1757
1758static const gchar *
1759g_dbus_object_manager_client_get_object_path (GDBusObjectManager *_manager)
1760{
1761 GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager);
1762 return manager->priv->object_path;
1763}
1764
1765static GDBusObject *
1766g_dbus_object_manager_client_get_object (GDBusObjectManager *_manager,
1767 const gchar *object_path)
1768{
1769 GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager);
1770 GDBusObject *ret;
1771
1772 g_mutex_lock (mutex: &manager->priv->lock);
1773 ret = g_hash_table_lookup (hash_table: manager->priv->map_object_path_to_object_proxy, key: object_path);
1774 if (ret != NULL)
1775 g_object_ref (ret);
1776 g_mutex_unlock (mutex: &manager->priv->lock);
1777 return ret;
1778}
1779
1780static GDBusInterface *
1781g_dbus_object_manager_client_get_interface (GDBusObjectManager *_manager,
1782 const gchar *object_path,
1783 const gchar *interface_name)
1784{
1785 GDBusInterface *ret;
1786 GDBusObject *object;
1787
1788 ret = NULL;
1789
1790 object = g_dbus_object_manager_get_object (manager: _manager, object_path);
1791 if (object == NULL)
1792 goto out;
1793
1794 ret = g_dbus_object_get_interface (object, interface_name);
1795 g_object_unref (object);
1796
1797 out:
1798 return ret;
1799}
1800
1801static GList *
1802g_dbus_object_manager_client_get_objects (GDBusObjectManager *_manager)
1803{
1804 GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager);
1805 GList *ret;
1806
1807 g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
1808
1809 g_mutex_lock (mutex: &manager->priv->lock);
1810 ret = g_hash_table_get_values (hash_table: manager->priv->map_object_path_to_object_proxy);
1811 g_list_foreach (list: ret, func: (GFunc) g_object_ref, NULL);
1812 g_mutex_unlock (mutex: &manager->priv->lock);
1813
1814 return ret;
1815}
1816
1817
1818static void
1819dbus_object_manager_interface_init (GDBusObjectManagerIface *iface)
1820{
1821 iface->get_object_path = g_dbus_object_manager_client_get_object_path;
1822 iface->get_objects = g_dbus_object_manager_client_get_objects;
1823 iface->get_object = g_dbus_object_manager_client_get_object;
1824 iface->get_interface = g_dbus_object_manager_client_get_interface;
1825}
1826
1827/* ---------------------------------------------------------------------------------------------------- */
1828

source code of gtk/subprojects/glib/gio/gdbusobjectmanagerclient.c