dbus-client.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. /*
  2. *
  3. * Embedded Linux library
  4. *
  5. * Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
  6. * Copyright (C) 2017 Codecoup. All rights reserved.
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  21. *
  22. */
  23. #ifdef HAVE_CONFIG_H
  24. #include <config.h>
  25. #endif
  26. #include "dbus.h"
  27. #include "dbus-client.h"
  28. #include "queue.h"
  29. #include "useful.h"
  30. #include "private.h"
  31. struct l_dbus_client {
  32. struct l_dbus *dbus;
  33. unsigned int watch;
  34. unsigned int added_watch;
  35. unsigned int removed_watch;
  36. char *service;
  37. uint32_t objects_call;
  38. l_dbus_watch_func_t connect_cb;
  39. void *connect_cb_data;
  40. l_dbus_destroy_func_t connect_cb_data_destroy;
  41. l_dbus_watch_func_t disconnect_cb;
  42. void *disconnect_cb_data;
  43. l_dbus_destroy_func_t disconnect_cb_data_destroy;
  44. l_dbus_client_ready_func_t ready_cb;
  45. void *ready_cb_data;
  46. l_dbus_destroy_func_t ready_cb_data_destroy;
  47. l_dbus_client_proxy_func_t proxy_added_cb;
  48. l_dbus_client_proxy_func_t proxy_removed_cb;
  49. l_dbus_client_property_function_t properties_changed_cb;
  50. void *proxy_cb_data;
  51. l_dbus_destroy_func_t proxy_cb_data_destroy;
  52. struct l_queue *proxies;
  53. };
  54. struct proxy_property {
  55. char *name;
  56. struct l_dbus_message *msg;
  57. };
  58. struct l_dbus_proxy {
  59. struct l_dbus_client *client;
  60. char *interface;
  61. char *path;
  62. uint32_t properties_watch;
  63. bool ready;
  64. struct l_queue *properties;
  65. struct l_queue *pending_calls;
  66. };
  67. LIB_EXPORT const char *l_dbus_proxy_get_path(struct l_dbus_proxy *proxy)
  68. {
  69. if (unlikely(!proxy))
  70. return NULL;
  71. return proxy->path;
  72. }
  73. LIB_EXPORT const char *l_dbus_proxy_get_interface(struct l_dbus_proxy *proxy)
  74. {
  75. if (unlikely(!proxy))
  76. return NULL;
  77. return proxy->interface;
  78. }
  79. static bool property_match_by_name(const void *a, const void *b)
  80. {
  81. const struct proxy_property *prop = a;
  82. const char *name = b;
  83. return !strcmp(prop->name, name);
  84. }
  85. static struct proxy_property *find_property(struct l_dbus_proxy *proxy,
  86. const char *name)
  87. {
  88. return l_queue_find(proxy->properties, property_match_by_name, name);
  89. }
  90. static struct proxy_property *get_property(struct l_dbus_proxy *proxy,
  91. const char *name)
  92. {
  93. struct proxy_property *prop;
  94. prop = find_property(proxy, name);
  95. if (prop)
  96. return prop;
  97. prop = l_new(struct proxy_property, 1);
  98. prop->name = l_strdup(name);
  99. l_queue_push_tail(proxy->properties, prop);
  100. return prop;
  101. }
  102. LIB_EXPORT bool l_dbus_proxy_get_property(struct l_dbus_proxy *proxy,
  103. const char *name,
  104. const char *signature, ...)
  105. {
  106. struct proxy_property *prop;
  107. va_list args;
  108. bool res;
  109. if (unlikely(!proxy))
  110. return false;
  111. prop = find_property(proxy, name);
  112. if (!prop)
  113. return false;
  114. va_start(args, signature);
  115. res = l_dbus_message_get_arguments_valist(prop->msg, signature, args);
  116. va_end(args);
  117. return res;
  118. }
  119. static void property_free(void *data)
  120. {
  121. struct proxy_property *prop = data;
  122. if (prop->msg)
  123. l_dbus_message_unref(prop->msg);
  124. l_free(prop->name);
  125. l_free(prop);
  126. }
  127. static void cancel_pending_calls(struct l_dbus_proxy *proxy)
  128. {
  129. const struct l_queue_entry *entry;
  130. for (entry = l_queue_get_entries(proxy->pending_calls); entry;
  131. entry = entry->next) {
  132. uint32_t call_id = L_PTR_TO_UINT(entry->data);
  133. l_dbus_cancel(proxy->client->dbus, call_id);
  134. }
  135. }
  136. static void dbus_proxy_destroy(struct l_dbus_proxy *proxy)
  137. {
  138. if (unlikely(!proxy))
  139. return;
  140. if (proxy->properties_watch)
  141. l_dbus_remove_signal_watch(proxy->client->dbus,
  142. proxy->properties_watch);
  143. cancel_pending_calls(proxy);
  144. l_queue_destroy(proxy->pending_calls, NULL);
  145. l_queue_destroy(proxy->properties, property_free);
  146. l_free(proxy->interface);
  147. l_free(proxy->path);
  148. l_free(proxy);
  149. }
  150. struct method_call_request
  151. {
  152. struct l_dbus_proxy *proxy;
  153. uint32_t call_id;
  154. l_dbus_message_func_t setup;
  155. l_dbus_client_proxy_result_func_t result;
  156. void *user_data;
  157. l_dbus_destroy_func_t destroy;
  158. };
  159. static void method_call_request_free(void *user_data)
  160. {
  161. struct method_call_request *req = user_data;
  162. l_queue_remove(req->proxy->pending_calls, L_UINT_TO_PTR(req->call_id));
  163. if (req->destroy)
  164. req->destroy(req->user_data);
  165. l_free(req);
  166. }
  167. static void method_call_setup(struct l_dbus_message *message, void *user_data)
  168. {
  169. struct method_call_request *req = user_data;
  170. if (req->setup)
  171. req->setup(message, req->user_data);
  172. else
  173. l_dbus_message_set_arguments(message, "");
  174. }
  175. static void method_call_reply(struct l_dbus_message *message, void *user_data)
  176. {
  177. struct method_call_request *req = user_data;
  178. if (req->result)
  179. req->result(req->proxy, message, req->user_data);
  180. }
  181. LIB_EXPORT bool l_dbus_proxy_set_property(struct l_dbus_proxy *proxy,
  182. l_dbus_client_proxy_result_func_t result,
  183. void *user_data, l_dbus_destroy_func_t destroy,
  184. const char *name, const char *signature, ...)
  185. {
  186. struct l_dbus_client *client = proxy->client;
  187. struct l_dbus_message_builder *builder;
  188. struct method_call_request *req;
  189. struct l_dbus_message *message;
  190. struct proxy_property *prop;
  191. va_list args;
  192. if (unlikely(!proxy))
  193. return false;
  194. prop = find_property(proxy, name);
  195. if (!prop)
  196. return false;
  197. if (strcmp(l_dbus_message_get_signature(prop->msg), signature))
  198. return false;
  199. message = l_dbus_message_new_method_call(client->dbus, client->service,
  200. proxy->path,
  201. L_DBUS_INTERFACE_PROPERTIES,
  202. "Set");
  203. if (!message)
  204. return false;
  205. builder = l_dbus_message_builder_new(message);
  206. if (!builder) {
  207. l_dbus_message_unref(message);
  208. return false;
  209. }
  210. l_dbus_message_builder_append_basic(builder, 's', proxy->interface);
  211. l_dbus_message_builder_append_basic(builder, 's', name);
  212. l_dbus_message_builder_enter_variant(builder, signature);
  213. va_start(args, signature);
  214. l_dbus_message_builder_append_from_valist(builder, signature, args);
  215. va_end(args);
  216. l_dbus_message_builder_leave_variant(builder);
  217. l_dbus_message_builder_finalize(builder);
  218. l_dbus_message_builder_destroy(builder);
  219. req = l_new(struct method_call_request, 1);
  220. req->proxy = proxy;
  221. req->result = result;
  222. req->user_data = user_data;
  223. req->destroy = destroy;
  224. req->call_id = l_dbus_send_with_reply(client->dbus, message,
  225. method_call_reply, req,
  226. method_call_request_free);
  227. if (!req->call_id) {
  228. l_free(req);
  229. return false;
  230. }
  231. l_queue_push_tail(proxy->pending_calls, L_UINT_TO_PTR(req->call_id));
  232. return true;
  233. }
  234. LIB_EXPORT uint32_t l_dbus_proxy_method_call(struct l_dbus_proxy *proxy,
  235. const char *method,
  236. l_dbus_message_func_t setup,
  237. l_dbus_client_proxy_result_func_t reply,
  238. void *user_data,
  239. l_dbus_destroy_func_t destroy)
  240. {
  241. struct method_call_request *req;
  242. if (unlikely(!proxy))
  243. return 0;
  244. req = l_new(struct method_call_request, 1);
  245. req->proxy = proxy;
  246. req->setup = setup;
  247. req->result = reply;
  248. req->user_data = user_data;
  249. req->destroy = destroy;
  250. req->call_id = l_dbus_method_call(proxy->client->dbus,
  251. proxy->client->service,
  252. proxy->path, proxy->interface,
  253. method, method_call_setup,
  254. method_call_reply, req,
  255. method_call_request_free);
  256. if (!req->call_id) {
  257. l_free(req);
  258. return 0;
  259. }
  260. l_queue_push_tail(proxy->pending_calls, L_UINT_TO_PTR(req->call_id));
  261. return req->call_id;
  262. }
  263. static void proxy_update_property(struct l_dbus_proxy *proxy,
  264. const char *name,
  265. struct l_dbus_message_iter *property)
  266. {
  267. struct l_dbus_message_builder *builder;
  268. struct proxy_property *prop = get_property(proxy, name);
  269. l_dbus_message_unref(prop->msg);
  270. if (!property) {
  271. prop->msg = NULL;
  272. goto done;
  273. }
  274. prop->msg = l_dbus_message_new_signal(proxy->client->dbus, proxy->path,
  275. proxy->interface, name);
  276. if (!prop->msg)
  277. return;
  278. builder = l_dbus_message_builder_new(prop->msg);
  279. l_dbus_message_builder_append_from_iter(builder, property);
  280. l_dbus_message_builder_finalize(builder);
  281. l_dbus_message_builder_destroy(builder);
  282. done:
  283. if (proxy->client->properties_changed_cb && proxy->ready)
  284. proxy->client->properties_changed_cb(proxy, name, prop->msg,
  285. proxy->client->proxy_cb_data);
  286. }
  287. static void proxy_invalidate_properties(struct l_dbus_proxy *proxy,
  288. struct l_dbus_message_iter* props)
  289. {
  290. const char *name;
  291. while (l_dbus_message_iter_next_entry(props, &name))
  292. proxy_update_property(proxy, name, NULL);
  293. }
  294. static void proxy_update_properties(struct l_dbus_proxy *proxy,
  295. struct l_dbus_message_iter* props)
  296. {
  297. struct l_dbus_message_iter variant;
  298. const char *name;
  299. while (l_dbus_message_iter_next_entry(props, &name, &variant))
  300. proxy_update_property(proxy, name, &variant);
  301. }
  302. static void properties_changed_callback(struct l_dbus_message *message,
  303. void *user_data)
  304. {
  305. struct l_dbus_proxy *proxy = user_data;
  306. const char *interface;
  307. struct l_dbus_message_iter changed;
  308. struct l_dbus_message_iter invalidated;
  309. if (!l_dbus_message_get_arguments(message, "sa{sv}as", &interface,
  310. &changed, &invalidated))
  311. return;
  312. proxy_update_properties(proxy, &changed);
  313. proxy_invalidate_properties(proxy, &invalidated);
  314. }
  315. static struct l_dbus_proxy *dbus_proxy_new(struct l_dbus_client *client,
  316. const char *path, const char *interface)
  317. {
  318. struct l_dbus_proxy *proxy = l_new(struct l_dbus_proxy, 1);
  319. proxy->properties_watch = l_dbus_add_signal_watch(client->dbus,
  320. client->service, path,
  321. L_DBUS_INTERFACE_PROPERTIES,
  322. "PropertiesChanged",
  323. L_DBUS_MATCH_ARGUMENT(0),
  324. interface, L_DBUS_MATCH_NONE,
  325. properties_changed_callback,
  326. proxy);
  327. if (!proxy->properties_watch) {
  328. l_free(proxy);
  329. return NULL;
  330. }
  331. proxy->client = client;
  332. proxy->interface = l_strdup(interface);
  333. proxy->path = l_strdup(path);
  334. proxy->properties = l_queue_new();
  335. proxy->pending_calls = l_queue_new();
  336. l_queue_push_tail(client->proxies, proxy);
  337. return proxy;
  338. }
  339. static bool is_ignorable(const char *interface)
  340. {
  341. static const struct {
  342. const char *interface;
  343. } interfaces_to_ignore[] = {
  344. { L_DBUS_INTERFACE_OBJECT_MANAGER },
  345. { L_DBUS_INTERFACE_INTROSPECTABLE },
  346. { L_DBUS_INTERFACE_PROPERTIES },
  347. };
  348. size_t i;
  349. for (i = 0; i < L_ARRAY_SIZE(interfaces_to_ignore); i++)
  350. if (!strcmp(interfaces_to_ignore[i].interface, interface))
  351. return true;
  352. return false;
  353. }
  354. static struct l_dbus_proxy *find_proxy(struct l_dbus_client *client,
  355. const char *path, const char *interface)
  356. {
  357. const struct l_queue_entry *entry;
  358. for (entry = l_queue_get_entries(client->proxies); entry;
  359. entry = entry->next) {
  360. struct l_dbus_proxy *proxy = entry->data;
  361. if (!strcmp(proxy->interface, interface) &&
  362. !strcmp(proxy->path, path))
  363. return proxy;
  364. }
  365. return NULL;
  366. }
  367. static void parse_interface(struct l_dbus_client *client, const char *path,
  368. const char *interface,
  369. struct l_dbus_message_iter *properties)
  370. {
  371. struct l_dbus_proxy *proxy;
  372. if (is_ignorable(interface))
  373. return;
  374. proxy = find_proxy(client, path, interface);
  375. if (!proxy)
  376. proxy = dbus_proxy_new(client, path, interface);
  377. if (!proxy)
  378. return;
  379. proxy_update_properties(proxy, properties);
  380. if (!proxy->ready) {
  381. proxy->ready = true;
  382. if (client->proxy_added_cb)
  383. client->proxy_added_cb(proxy, client->proxy_cb_data);
  384. }
  385. }
  386. static void parse_object(struct l_dbus_client *client, const char *path,
  387. struct l_dbus_message_iter *object)
  388. {
  389. const char *interface;
  390. struct l_dbus_message_iter properties;
  391. if (!path)
  392. return;
  393. while (l_dbus_message_iter_next_entry(object, &interface, &properties))
  394. parse_interface(client, path, interface, &properties);
  395. }
  396. static void interfaces_added_callback(struct l_dbus_message *message,
  397. void *user_data)
  398. {
  399. struct l_dbus_client *client = user_data;
  400. struct l_dbus_message_iter object;
  401. const char *path;
  402. if (!l_dbus_message_get_arguments(message, "oa{sa{sv}}", &path,
  403. &object))
  404. return;
  405. parse_object(client, path, &object);
  406. }
  407. static void interfaces_removed_callback(struct l_dbus_message *message,
  408. void *user_data)
  409. {
  410. struct l_dbus_client *client = user_data;
  411. struct l_dbus_message_iter interfaces;
  412. const char *interface;
  413. const char *path;
  414. if (!l_dbus_message_get_arguments(message, "oas", &path, &interfaces))
  415. return;
  416. while (l_dbus_message_iter_next_entry(&interfaces, &interface)) {
  417. struct l_dbus_proxy *proxy;
  418. proxy = find_proxy(client, path, interface);
  419. if (!proxy)
  420. continue;
  421. l_queue_remove(proxy->client->proxies, proxy);
  422. if (client->proxy_removed_cb)
  423. client->proxy_removed_cb(proxy, client->proxy_cb_data);
  424. dbus_proxy_destroy(proxy);
  425. }
  426. }
  427. static void get_managed_objects_reply(struct l_dbus_message *message,
  428. void *user_data)
  429. {
  430. struct l_dbus_client *client = user_data;
  431. struct l_dbus_message_iter objects;
  432. struct l_dbus_message_iter object;
  433. const char *path;
  434. client->objects_call = 0;
  435. if (l_dbus_message_is_error(message))
  436. return;
  437. if (!l_dbus_message_get_arguments(message, "a{oa{sa{sv}}}", &objects))
  438. return;
  439. while (l_dbus_message_iter_next_entry(&objects, &path, &object))
  440. parse_object(client, path, &object);
  441. client->added_watch = l_dbus_add_signal_watch(client->dbus,
  442. client->service, "/",
  443. L_DBUS_INTERFACE_OBJECT_MANAGER,
  444. "InterfacesAdded",
  445. L_DBUS_MATCH_NONE,
  446. interfaces_added_callback,
  447. client);
  448. client->removed_watch = l_dbus_add_signal_watch(client->dbus,
  449. client->service, "/",
  450. L_DBUS_INTERFACE_OBJECT_MANAGER,
  451. "InterfacesRemoved",
  452. L_DBUS_MATCH_NONE,
  453. interfaces_removed_callback,
  454. client);
  455. if (client->ready_cb)
  456. client->ready_cb(client, client->ready_cb_data);
  457. }
  458. static void service_appeared_callback(struct l_dbus *dbus, void *user_data)
  459. {
  460. struct l_dbus_client *client = user_data;
  461. /* TODO should we allow to set different root? */
  462. client->objects_call = l_dbus_method_call(dbus, client->service, "/",
  463. L_DBUS_INTERFACE_OBJECT_MANAGER,
  464. "GetManagedObjects", NULL,
  465. get_managed_objects_reply,
  466. client, NULL);
  467. if (client->connect_cb)
  468. client->connect_cb(client->dbus, client->connect_cb_data);
  469. }
  470. static void service_disappeared_callback(struct l_dbus *dbus, void *user_data)
  471. {
  472. struct l_dbus_client *client = user_data;
  473. if (client->disconnect_cb)
  474. client->disconnect_cb(client->dbus, client->disconnect_cb_data);
  475. l_queue_clear(client->proxies,
  476. (l_queue_destroy_func_t)dbus_proxy_destroy);
  477. }
  478. LIB_EXPORT struct l_dbus_client *l_dbus_client_new(struct l_dbus *dbus,
  479. const char *service, const char *path)
  480. {
  481. struct l_dbus_client *client = l_new(struct l_dbus_client, 1);
  482. client->dbus = dbus;
  483. client->watch = l_dbus_add_service_watch(dbus, service,
  484. service_appeared_callback,
  485. service_disappeared_callback,
  486. client, NULL);
  487. if (!client->watch) {
  488. l_free(client);
  489. return NULL;
  490. }
  491. client->service = l_strdup(service);
  492. client->proxies = l_queue_new();
  493. return client;
  494. }
  495. LIB_EXPORT void l_dbus_client_destroy(struct l_dbus_client *client)
  496. {
  497. if (unlikely(!client))
  498. return;
  499. if (client->watch)
  500. l_dbus_remove_signal_watch(client->dbus, client->watch);
  501. if (client->added_watch)
  502. l_dbus_remove_signal_watch(client->dbus, client->added_watch);
  503. if (client->removed_watch)
  504. l_dbus_remove_signal_watch(client->dbus, client->removed_watch);
  505. if (client->connect_cb_data_destroy)
  506. client->connect_cb_data_destroy(client->connect_cb_data);
  507. if (client->disconnect_cb_data_destroy)
  508. client->disconnect_cb_data_destroy(client->disconnect_cb_data);
  509. if (client->ready_cb_data_destroy)
  510. client->ready_cb_data_destroy(client->ready_cb_data);
  511. if (client->proxy_cb_data_destroy)
  512. client->proxy_cb_data_destroy(client->proxy_cb_data);
  513. if (client->objects_call)
  514. l_dbus_cancel(client->dbus, client->objects_call);
  515. l_queue_destroy(client->proxies,
  516. (l_queue_destroy_func_t)dbus_proxy_destroy);
  517. l_free(client->service);
  518. l_free(client);
  519. }
  520. LIB_EXPORT bool l_dbus_client_set_connect_handler(struct l_dbus_client *client,
  521. l_dbus_watch_func_t function,
  522. void *user_data,
  523. l_dbus_destroy_func_t destroy)
  524. {
  525. if (unlikely(!client))
  526. return false;
  527. if (client->connect_cb_data_destroy)
  528. client->connect_cb_data_destroy(client->connect_cb_data);
  529. client->connect_cb = function;
  530. client->connect_cb_data = user_data;
  531. client->connect_cb_data_destroy = destroy;
  532. return true;
  533. }
  534. LIB_EXPORT bool l_dbus_client_set_disconnect_handler(struct l_dbus_client *client,
  535. l_dbus_watch_func_t function,
  536. void *user_data,
  537. l_dbus_destroy_func_t destroy)
  538. {
  539. if (unlikely(!client))
  540. return false;
  541. if(client->disconnect_cb_data_destroy)
  542. client->disconnect_cb_data_destroy(client->disconnect_cb_data);
  543. client->disconnect_cb = function;
  544. client->disconnect_cb_data = user_data;
  545. client->disconnect_cb_data_destroy = destroy;
  546. return true;
  547. }
  548. LIB_EXPORT bool l_dbus_client_set_ready_handler(struct l_dbus_client *client,
  549. l_dbus_client_ready_func_t function,
  550. void *user_data,
  551. l_dbus_destroy_func_t destroy)
  552. {
  553. if (unlikely(!client))
  554. return false;
  555. if (client->ready_cb_data_destroy)
  556. client->ready_cb_data_destroy(client->ready_cb_data);
  557. client->ready_cb = function;
  558. client->ready_cb_data = user_data;
  559. client->ready_cb_data_destroy = destroy;
  560. return true;
  561. }
  562. LIB_EXPORT bool l_dbus_client_set_proxy_handlers(struct l_dbus_client *client,
  563. l_dbus_client_proxy_func_t proxy_added,
  564. l_dbus_client_proxy_func_t proxy_removed,
  565. l_dbus_client_property_function_t property_changed,
  566. void *user_data, l_dbus_destroy_func_t destroy)
  567. {
  568. if (unlikely(!client))
  569. return false;
  570. if (client->proxy_cb_data_destroy)
  571. client->proxy_cb_data_destroy(client->proxy_cb_data);
  572. client->proxy_added_cb = proxy_added;
  573. client->proxy_removed_cb = proxy_removed;
  574. client->properties_changed_cb = property_changed;
  575. client->proxy_cb_data = user_data;
  576. client->proxy_cb_data_destroy = destroy;
  577. return true;
  578. }