• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2009 Tanu Kaskinen
5 
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10 
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <dbus/dbus.h>
25 
26 #include <pulse/xmalloc.h>
27 
28 #include <pulsecore/core-util.h>
29 #include <pulsecore/dbus-util.h>
30 #include <pulsecore/hashmap.h>
31 #include <pulsecore/idxset.h>
32 #include <pulsecore/shared.h>
33 #include <pulsecore/strbuf.h>
34 
35 #include "protocol-dbus.h"
36 
37 struct pa_dbus_protocol {
38     PA_REFCNT_DECLARE;
39 
40     pa_core *core;
41     pa_hashmap *objects; /* Object path -> struct object_entry */
42     pa_hashmap *connections; /* DBusConnection -> struct connection_entry */
43     pa_idxset *extensions; /* Strings */
44 
45     pa_hook hooks[PA_DBUS_PROTOCOL_HOOK_MAX];
46 };
47 
48 struct object_entry {
49     char *path;
50     pa_hashmap *interfaces; /* Interface name -> struct interface_entry */
51     char *introspection;
52 };
53 
54 struct connection_entry {
55     DBusConnection *connection;
56     pa_client *client;
57 
58     bool listening_for_all_signals;
59 
60     /* Contains object paths. If this is empty, then signals from all objects
61      * are accepted. Only used when listening_for_all_signals == true. */
62     pa_idxset *all_signals_objects;
63 
64     /* Signal name -> signal paths entry. The entries contain object paths. If
65      * a path set is empty, then that signal is accepted from all objects. This
66      * variable is only used when listening_for_all_signals == false. */
67     pa_hashmap *listening_signals;
68 };
69 
70 /* Only used in connection entries' listening_signals hashmap. */
71 struct signal_paths_entry {
72     char *signal;
73     pa_idxset *paths;
74 };
75 
76 struct interface_entry {
77     char *name;
78     pa_hashmap *method_handlers;
79     pa_hashmap *method_signatures; /* Derived from method_handlers. Contains only "in" arguments. */
80     pa_hashmap *property_handlers;
81     pa_dbus_receive_cb_t get_all_properties_cb;
82     pa_dbus_signal_info *signals;
83     unsigned n_signals;
84     void *userdata;
85 };
86 
pa_get_dbus_address_from_server_type(pa_server_type_t server_type)87 char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
88     char *address = NULL;
89     char *runtime_path = NULL;
90     char *escaped_path = NULL;
91 
92     switch (server_type) {
93         case PA_SERVER_TYPE_USER:
94             pa_assert_se((runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME)));
95             pa_assert_se((escaped_path = dbus_address_escape_value(runtime_path)));
96             address = pa_sprintf_malloc("unix:path=%s", escaped_path);
97             break;
98 
99         case PA_SERVER_TYPE_SYSTEM:
100             pa_assert_se((escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH)));
101             address = pa_sprintf_malloc("unix:path=%s", escaped_path);
102             break;
103 
104         case PA_SERVER_TYPE_NONE:
105             address = pa_xnew0(char, 1);
106             break;
107 
108         default:
109             pa_assert_not_reached();
110     }
111 
112     pa_xfree(runtime_path);
113     dbus_free(escaped_path);
114 
115     return address;
116 }
117 
dbus_protocol_new(pa_core * c)118 static pa_dbus_protocol *dbus_protocol_new(pa_core *c) {
119     pa_dbus_protocol *p;
120     unsigned i;
121 
122     pa_assert(c);
123 
124     p = pa_xnew(pa_dbus_protocol, 1);
125     PA_REFCNT_INIT(p);
126     p->core = c;
127     p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
128     p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
129     p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
130 
131     for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
132         pa_hook_init(&p->hooks[i], p);
133 
134     pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0);
135 
136     return p;
137 }
138 
pa_dbus_protocol_get(pa_core * c)139 pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c) {
140     pa_dbus_protocol *p;
141 
142     if ((p = pa_shared_get(c, "dbus-protocol")))
143         return pa_dbus_protocol_ref(p);
144 
145     return dbus_protocol_new(c);
146 }
147 
pa_dbus_protocol_ref(pa_dbus_protocol * p)148 pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) {
149     pa_assert(p);
150     pa_assert(PA_REFCNT_VALUE(p) >= 1);
151 
152     PA_REFCNT_INC(p);
153 
154     return p;
155 }
156 
pa_dbus_protocol_unref(pa_dbus_protocol * p)157 void pa_dbus_protocol_unref(pa_dbus_protocol *p) {
158     unsigned i;
159 
160     pa_assert(p);
161     pa_assert(PA_REFCNT_VALUE(p) >= 1);
162 
163     if (PA_REFCNT_DEC(p) > 0)
164         return;
165 
166     pa_assert(pa_hashmap_isempty(p->objects));
167     pa_assert(pa_hashmap_isempty(p->connections));
168     pa_assert(pa_idxset_isempty(p->extensions));
169 
170     pa_hashmap_free(p->objects);
171     pa_hashmap_free(p->connections);
172     pa_idxset_free(p->extensions, NULL);
173 
174     for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
175         pa_hook_done(&p->hooks[i]);
176 
177     pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0);
178 
179     pa_xfree(p);
180 }
181 
update_introspection(struct object_entry * oe)182 static void update_introspection(struct object_entry *oe) {
183     pa_strbuf *buf;
184     void *interfaces_state = NULL;
185     struct interface_entry *iface_entry = NULL;
186 
187     pa_assert(oe);
188 
189     buf = pa_strbuf_new();
190     pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
191     pa_strbuf_puts(buf, "<node>\n");
192 
193     PA_HASHMAP_FOREACH(iface_entry, oe->interfaces, interfaces_state) {
194         pa_dbus_method_handler *method_handler;
195         pa_dbus_property_handler *property_handler;
196         void *handlers_state = NULL;
197         unsigned i;
198         unsigned j;
199 
200         pa_strbuf_printf(buf, " <interface name=\"%s\">\n", iface_entry->name);
201 
202         PA_HASHMAP_FOREACH(method_handler, iface_entry->method_handlers, handlers_state) {
203             pa_strbuf_printf(buf, "  <method name=\"%s\">\n", method_handler->method_name);
204 
205             for (i = 0; i < method_handler->n_arguments; ++i)
206                 pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n",
207                                  method_handler->arguments[i].name,
208                                  method_handler->arguments[i].type,
209                                  method_handler->arguments[i].direction);
210 
211             pa_strbuf_puts(buf, "  </method>\n");
212         }
213 
214         handlers_state = NULL;
215 
216         PA_HASHMAP_FOREACH(property_handler, iface_entry->property_handlers, handlers_state)
217             pa_strbuf_printf(buf, "  <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n",
218                              property_handler->property_name,
219                              property_handler->type,
220                              property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write");
221 
222         for (i = 0; i < iface_entry->n_signals; ++i) {
223             pa_strbuf_printf(buf, "  <signal name=\"%s\">\n", iface_entry->signals[i].name);
224 
225             for (j = 0; j < iface_entry->signals[i].n_arguments; ++j)
226                 pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\"/>\n", iface_entry->signals[i].arguments[j].name,
227                                                                              iface_entry->signals[i].arguments[j].type);
228 
229             pa_strbuf_puts(buf, "  </signal>\n");
230         }
231 
232         pa_strbuf_puts(buf, " </interface>\n");
233     }
234 
235     pa_strbuf_puts(buf, " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
236                         "  <method name=\"Introspect\">\n"
237                         "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
238                         "  </method>\n"
239                         " </interface>\n"
240                         " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
241                         "  <method name=\"Get\">\n"
242                         "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
243                         "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
244                         "   <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
245                         "  </method>\n"
246                         "  <method name=\"Set\">\n"
247                         "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
248                         "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
249                         "   <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
250                         "  </method>\n"
251                         "  <method name=\"GetAll\">\n"
252                         "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
253                         "   <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
254                         "  </method>\n"
255                         " </interface>\n");
256 
257     pa_strbuf_puts(buf, "</node>\n");
258 
259     pa_xfree(oe->introspection);
260     oe->introspection = pa_strbuf_to_string_free(buf);
261 }
262 
263 /* Return value of find_handler() and its subfunctions. */
264 enum find_result_t {
265     /* The received message is a valid .Get call. */
266     FOUND_GET_PROPERTY,
267 
268     /* The received message is a valid .Set call. */
269     FOUND_SET_PROPERTY,
270 
271     /* The received message is a valid .GetAll call. */
272     FOUND_GET_ALL,
273 
274     /* The received message is a valid method call. */
275     FOUND_METHOD,
276 
277     /* The interface of the received message hasn't been registered for the
278      * destination object. */
279     NO_SUCH_INTERFACE,
280 
281     /* No property handler was found for the received .Get or .Set call. */
282     NO_SUCH_PROPERTY,
283 
284     /* The interface argument of a property call didn't match any registered
285      * interface. */
286     NO_SUCH_PROPERTY_INTERFACE,
287 
288     /* The received message called .Get or .Set for a property whose access
289      * mode doesn't match the call. */
290     PROPERTY_ACCESS_DENIED,
291 
292     /* The new value signature of a .Set call didn't match the expected
293      * signature. */
294     INVALID_PROPERTY_SIG,
295 
296     /* No method handler was found for the received message. */
297     NO_SUCH_METHOD,
298 
299     /* The signature of the received message didn't match the expected
300      * signature. Despite the name, this can also be returned for a property
301      * call if its message signature is invalid. */
302     INVALID_METHOD_SIG
303 };
304 
305 /* Data for resolving the correct reaction to a received message. */
306 struct call_info {
307     DBusMessage *message; /* The received message. */
308     struct object_entry *obj_entry;
309     const char *interface; /* Destination interface name (extracted from the message). */
310     struct interface_entry *iface_entry;
311 
312     const char *property; /* Property name (extracted from the message). */
313     const char *property_interface; /* The interface argument of a property call is stored here. */
314     pa_dbus_property_handler *property_handler;
315     const char *expected_property_sig; /* Property signature from the introspection data. */
316     char *property_sig; /* The signature of the new value in the received .Set message. */
317     DBusMessageIter variant_iter; /* Iterator pointing to the beginning of the new value variant of a .Set call. */
318 
319     const char *method; /* Method name (extracted from the message). */
320     pa_dbus_method_handler *method_handler;
321     const char *expected_method_sig; /* Method signature from the introspection data. */
322     const char *method_sig; /* The signature of the received message. */
323 };
324 
325 /* Called when call_info->property has been set and the property interface has
326  * not been given. In case of a Set call, call_info->property_sig is also set,
327  * which is checked against the expected value in this function. */
find_handler_by_property(struct call_info * call_info)328 static enum find_result_t find_handler_by_property(struct call_info *call_info) {
329     void *state = NULL;
330 
331     pa_assert(call_info);
332 
333     PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
334         if ((call_info->property_handler = pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
335             if (pa_streq(call_info->method, "Get"))
336                 return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
337 
338             else if (pa_streq(call_info->method, "Set")) {
339                 call_info->expected_property_sig = call_info->property_handler->type;
340 
341                 if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
342                     return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
343                 else
344                     return INVALID_PROPERTY_SIG;
345 
346             } else
347                 pa_assert_not_reached();
348         }
349     }
350 
351     return NO_SUCH_PROPERTY;
352 }
353 
find_handler_by_method(struct call_info * call_info)354 static enum find_result_t find_handler_by_method(struct call_info *call_info) {
355     void *state = NULL;
356 
357     pa_assert(call_info);
358 
359     PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
360         if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) {
361             pa_assert_se(call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method));
362 
363             if (pa_streq(call_info->method_sig, call_info->expected_method_sig))
364                 return FOUND_METHOD;
365             else
366                 return INVALID_METHOD_SIG;
367         }
368     }
369 
370     return NO_SUCH_METHOD;
371 }
372 
find_handler_from_properties_call(struct call_info * call_info)373 static enum find_result_t find_handler_from_properties_call(struct call_info *call_info) {
374     pa_assert(call_info);
375 
376     if (pa_streq(call_info->method, "GetAll")) {
377         call_info->expected_method_sig = "s";
378         if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
379             return INVALID_METHOD_SIG;
380 
381         pa_assert_se(dbus_message_get_args(call_info->message, NULL,
382                                            DBUS_TYPE_STRING, &call_info->property_interface,
383                                            DBUS_TYPE_INVALID));
384 
385         if (*call_info->property_interface) {
386             if ((call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
387                 return FOUND_GET_ALL;
388             else
389                 return NO_SUCH_PROPERTY_INTERFACE;
390 
391         } else {
392             pa_assert_se(call_info->iface_entry = pa_hashmap_first(call_info->obj_entry->interfaces));
393             return FOUND_GET_ALL;
394         }
395 
396     } else if (pa_streq(call_info->method, "Get")) {
397         call_info->expected_method_sig = "ss";
398         if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
399             return INVALID_METHOD_SIG;
400 
401         pa_assert_se(dbus_message_get_args(call_info->message, NULL,
402                                            DBUS_TYPE_STRING, &call_info->property_interface,
403                                            DBUS_TYPE_STRING, &call_info->property,
404                                            DBUS_TYPE_INVALID));
405 
406         if (*call_info->property_interface) {
407             if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
408                 return NO_SUCH_PROPERTY_INTERFACE;
409             else if ((call_info->property_handler =
410                         pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property)))
411                 return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
412             else
413                 return NO_SUCH_PROPERTY;
414 
415         } else
416             return find_handler_by_property(call_info);
417 
418     } else if (pa_streq(call_info->method, "Set")) {
419         DBusMessageIter msg_iter;
420 
421         call_info->expected_method_sig = "ssv";
422         if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
423             return INVALID_METHOD_SIG;
424 
425         pa_assert_se(dbus_message_iter_init(call_info->message, &msg_iter));
426 
427         dbus_message_iter_get_basic(&msg_iter, &call_info->property_interface);
428         pa_assert_se(dbus_message_iter_next(&msg_iter));
429         dbus_message_iter_get_basic(&msg_iter, &call_info->property);
430         pa_assert_se(dbus_message_iter_next(&msg_iter));
431 
432         dbus_message_iter_recurse(&msg_iter, &call_info->variant_iter);
433 
434         pa_assert_se(call_info->property_sig = dbus_message_iter_get_signature(&call_info->variant_iter));
435 
436         if (*call_info->property_interface) {
437             if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
438                 return NO_SUCH_PROPERTY_INTERFACE;
439 
440             else if ((call_info->property_handler =
441                         pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
442                 call_info->expected_property_sig = call_info->property_handler->type;
443 
444                 if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
445                     return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
446                 else
447                     return INVALID_PROPERTY_SIG;
448 
449             } else
450                 return NO_SUCH_PROPERTY;
451 
452         } else
453             return find_handler_by_property(call_info);
454 
455     } else
456         pa_assert_not_reached();
457 }
458 
find_handler(struct call_info * call_info)459 static enum find_result_t find_handler(struct call_info *call_info) {
460     pa_assert(call_info);
461 
462     if (call_info->interface) {
463         if (pa_streq(call_info->interface, DBUS_INTERFACE_PROPERTIES))
464             return find_handler_from_properties_call(call_info);
465 
466         else if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->interface)))
467             return NO_SUCH_INTERFACE;
468 
469         else if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) {
470             pa_assert_se(call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method));
471 
472             if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
473                 return INVALID_METHOD_SIG;
474 
475             return FOUND_METHOD;
476 
477         } else
478             return NO_SUCH_METHOD;
479 
480     } else { /* The method call doesn't contain an interface. */
481         if (pa_streq(call_info->method, "Get") || pa_streq(call_info->method, "Set") || pa_streq(call_info->method, "GetAll")) {
482             if (find_handler_by_method(call_info) == FOUND_METHOD)
483                 /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
484                 return FOUND_METHOD;
485             else
486                 /* Assume this is a .Properties call. */
487                 return find_handler_from_properties_call(call_info);
488 
489         } else /* This is not a .Properties call. */
490             return find_handler_by_method(call_info);
491     }
492 }
493 
handle_message_cb(DBusConnection * connection,DBusMessage * message,void * user_data)494 static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
495     pa_dbus_protocol *p = user_data;
496     struct call_info call_info;
497     call_info.property_sig = NULL;
498 
499     pa_assert(connection);
500     pa_assert(message);
501     pa_assert(p);
502     pa_assert(p->objects);
503 
504     if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
505         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
506 
507     pa_log_debug("Received message: destination = %s, interface = %s, member = %s",
508                  dbus_message_get_path(message),
509                  dbus_message_get_interface(message),
510                  dbus_message_get_member(message));
511 
512     call_info.message = message;
513     pa_assert_se(call_info.obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)));
514     call_info.interface = dbus_message_get_interface(message);
515     pa_assert_se(call_info.method = dbus_message_get_member(message));
516     pa_assert_se(call_info.method_sig = dbus_message_get_signature(message));
517 
518     if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") ||
519         (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
520         pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection);
521         goto finish;
522     }
523 
524     switch (find_handler(&call_info)) {
525         case FOUND_GET_PROPERTY:
526             call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata);
527             break;
528 
529         case FOUND_SET_PROPERTY:
530             call_info.property_handler->set_cb(connection, message, &call_info.variant_iter, call_info.iface_entry->userdata);
531             break;
532 
533         case FOUND_METHOD:
534             call_info.method_handler->receive_cb(connection, message, call_info.iface_entry->userdata);
535             break;
536 
537         case FOUND_GET_ALL:
538             if (call_info.iface_entry->get_all_properties_cb)
539                 call_info.iface_entry->get_all_properties_cb(connection, message, call_info.iface_entry->userdata);
540             else {
541                 DBusMessage *dummy_reply = NULL;
542                 DBusMessageIter msg_iter;
543                 DBusMessageIter dict_iter;
544 
545                 pa_assert_se(dummy_reply = dbus_message_new_method_return(message));
546                 dbus_message_iter_init_append(dummy_reply, &msg_iter);
547                 pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
548                 pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
549                 pa_assert_se(dbus_connection_send(connection, dummy_reply, NULL));
550                 dbus_message_unref(dummy_reply);
551             }
552             break;
553 
554         case PROPERTY_ACCESS_DENIED:
555             pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED,
556                                "%s access denied for property %s", call_info.method, call_info.property);
557             break;
558 
559         case NO_SUCH_METHOD:
560             pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "No such method: %s", call_info.method);
561             break;
562 
563         case NO_SUCH_INTERFACE:
564             pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_INTERFACE, "No such interface: %s", call_info.interface);
565             break;
566 
567         case NO_SUCH_PROPERTY:
568             pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property: %s", call_info.property);
569             break;
570 
571         case NO_SUCH_PROPERTY_INTERFACE:
572             pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_INTERFACE, "No such property interface: %s", call_info.property_interface);
573             break;
574 
575         case INVALID_METHOD_SIG:
576             pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
577                                "Invalid signature for method %s: '%s'. Expected '%s'.",
578                                call_info.method, call_info.method_sig, call_info.expected_method_sig);
579             break;
580 
581         case INVALID_PROPERTY_SIG:
582             pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
583                                "Invalid signature for property %s: '%s'. Expected '%s'.",
584                                call_info.property, call_info.property_sig, call_info.expected_property_sig);
585             break;
586 
587         default:
588             pa_assert_not_reached();
589     }
590 
591 finish:
592     if (call_info.property_sig)
593         dbus_free(call_info.property_sig);
594 
595     return DBUS_HANDLER_RESULT_HANDLED;
596 }
597 
598 static DBusObjectPathVTable vtable = {
599     .unregister_function = NULL,
600     .message_function = handle_message_cb,
601     .dbus_internal_pad1 = NULL,
602     .dbus_internal_pad2 = NULL,
603     .dbus_internal_pad3 = NULL,
604     .dbus_internal_pad4 = NULL
605 };
606 
register_object(pa_dbus_protocol * p,struct object_entry * obj_entry)607 static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
608     struct connection_entry *conn_entry;
609     void *state = NULL;
610 
611     pa_assert(p);
612     pa_assert(obj_entry);
613 
614     PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
615         pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p));
616 }
617 
copy_args(const pa_dbus_arg_info * src,unsigned n)618 static pa_dbus_arg_info *copy_args(const pa_dbus_arg_info *src, unsigned n) {
619     pa_dbus_arg_info *dst;
620     unsigned i;
621 
622     if (n == 0)
623         return NULL;
624 
625     pa_assert(src);
626 
627     dst = pa_xnew0(pa_dbus_arg_info, n);
628 
629     for (i = 0; i < n; ++i) {
630         dst[i].name = pa_xstrdup(src[i].name);
631         dst[i].type = pa_xstrdup(src[i].type);
632         dst[i].direction = pa_xstrdup(src[i].direction);
633     }
634 
635     return dst;
636 }
637 
method_handler_free(pa_dbus_method_handler * h)638 static void method_handler_free(pa_dbus_method_handler *h) {
639     unsigned i;
640 
641     pa_assert(h);
642 
643     pa_xfree((char *) h->method_name);
644 
645     for (i = 0; i < h->n_arguments; ++i) {
646         pa_xfree((char *) h->arguments[i].name);
647         pa_xfree((char *) h->arguments[i].type);
648         pa_xfree((char *) h->arguments[i].direction);
649     }
650 
651     pa_xfree((pa_dbus_arg_info *) h->arguments);
652     pa_xfree(h);
653 }
654 
create_method_handlers(const pa_dbus_interface_info * info)655 static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) {
656     pa_hashmap *handlers;
657     unsigned i;
658 
659     pa_assert(info);
660     pa_assert(info->method_handlers || info->n_method_handlers == 0);
661 
662     handlers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) method_handler_free);
663 
664     for (i = 0; i < info->n_method_handlers; ++i) {
665         pa_dbus_method_handler *h = pa_xnew(pa_dbus_method_handler, 1);
666         h->method_name = pa_xstrdup(info->method_handlers[i].method_name);
667         h->arguments = copy_args(info->method_handlers[i].arguments, info->method_handlers[i].n_arguments);
668         h->n_arguments = info->method_handlers[i].n_arguments;
669         h->receive_cb = info->method_handlers[i].receive_cb;
670 
671         pa_hashmap_put(handlers, (char *) h->method_name, h);
672     }
673 
674     return handlers;
675 }
676 
extract_method_signatures(pa_hashmap * method_handlers)677 static pa_hashmap *extract_method_signatures(pa_hashmap *method_handlers) {
678     pa_hashmap *signatures = NULL;
679     pa_dbus_method_handler *handler = NULL;
680     void *state = NULL;
681     pa_strbuf *sig_buf = NULL;
682     unsigned i = 0;
683 
684     pa_assert(method_handlers);
685 
686     signatures = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
687 
688     PA_HASHMAP_FOREACH(handler, method_handlers, state) {
689         sig_buf = pa_strbuf_new();
690 
691         for (i = 0; i < handler->n_arguments; ++i) {
692             if (pa_streq(handler->arguments[i].direction, "in"))
693                 pa_strbuf_puts(sig_buf, handler->arguments[i].type);
694         }
695 
696         pa_hashmap_put(signatures, (char *) handler->method_name, pa_strbuf_to_string_free(sig_buf));
697     }
698 
699     return signatures;
700 }
701 
property_handler_free(pa_dbus_property_handler * h)702 static void property_handler_free(pa_dbus_property_handler *h) {
703     pa_assert(h);
704 
705     pa_xfree((char *) h->property_name);
706     pa_xfree((char *) h->type);
707 
708     pa_xfree(h);
709 }
710 
create_property_handlers(const pa_dbus_interface_info * info)711 static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) {
712     pa_hashmap *handlers;
713     unsigned i = 0;
714 
715     pa_assert(info);
716     pa_assert(info->property_handlers || info->n_property_handlers == 0);
717 
718     handlers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) property_handler_free);
719 
720     for (i = 0; i < info->n_property_handlers; ++i) {
721         pa_dbus_property_handler *h = pa_xnew(pa_dbus_property_handler, 1);
722         h->property_name = pa_xstrdup(info->property_handlers[i].property_name);
723         h->type = pa_xstrdup(info->property_handlers[i].type);
724         h->get_cb = info->property_handlers[i].get_cb;
725         h->set_cb = info->property_handlers[i].set_cb;
726 
727         pa_hashmap_put(handlers, (char *) h->property_name, h);
728     }
729 
730     return handlers;
731 }
732 
copy_signals(const pa_dbus_interface_info * info)733 static pa_dbus_signal_info *copy_signals(const pa_dbus_interface_info *info) {
734     pa_dbus_signal_info *dst;
735     unsigned i;
736 
737     pa_assert(info);
738 
739     if (info->n_signals == 0)
740         return NULL;
741 
742     pa_assert(info->signals);
743 
744     dst = pa_xnew(pa_dbus_signal_info, info->n_signals);
745 
746     for (i = 0; i < info->n_signals; ++i) {
747         dst[i].name = pa_xstrdup(info->signals[i].name);
748         dst[i].arguments = copy_args(info->signals[i].arguments, info->signals[i].n_arguments);
749         dst[i].n_arguments = info->signals[i].n_arguments;
750     }
751 
752     return dst;
753 }
754 
pa_dbus_protocol_add_interface(pa_dbus_protocol * p,const char * path,const pa_dbus_interface_info * info,void * userdata)755 int pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
756                                    const char *path,
757                                    const pa_dbus_interface_info *info,
758                                    void *userdata) {
759     struct object_entry *obj_entry;
760     struct interface_entry *iface_entry;
761     bool obj_entry_created = false;
762 
763     pa_assert(p);
764     pa_assert(path);
765     pa_assert(info);
766     pa_assert(info->name);
767     pa_assert(info->method_handlers || info->n_method_handlers == 0);
768     pa_assert(info->property_handlers || info->n_property_handlers == 0);
769     pa_assert(info->get_all_properties_cb || info->n_property_handlers == 0);
770     pa_assert(info->signals || info->n_signals == 0);
771 
772     if (!(obj_entry = pa_hashmap_get(p->objects, path))) {
773         obj_entry = pa_xnew(struct object_entry, 1);
774         obj_entry->path = pa_xstrdup(path);
775         obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
776         obj_entry->introspection = NULL;
777 
778         pa_hashmap_put(p->objects, obj_entry->path, obj_entry);
779         obj_entry_created = true;
780     }
781 
782     if (pa_hashmap_get(obj_entry->interfaces, info->name) != NULL)
783         goto fail; /* The interface was already registered. */
784 
785     iface_entry = pa_xnew(struct interface_entry, 1);
786     iface_entry->name = pa_xstrdup(info->name);
787     iface_entry->method_handlers = create_method_handlers(info);
788     iface_entry->method_signatures = extract_method_signatures(iface_entry->method_handlers);
789     iface_entry->property_handlers = create_property_handlers(info);
790     iface_entry->get_all_properties_cb = info->get_all_properties_cb;
791     iface_entry->signals = copy_signals(info);
792     iface_entry->n_signals = info->n_signals;
793     iface_entry->userdata = userdata;
794     pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry);
795 
796     update_introspection(obj_entry);
797 
798     if (obj_entry_created)
799         register_object(p, obj_entry);
800 
801     pa_log_debug("Interface %s added for object %s", iface_entry->name, obj_entry->path);
802 
803     return 0;
804 
805 fail:
806     return -1;
807 }
808 
unregister_object(pa_dbus_protocol * p,struct object_entry * obj_entry)809 static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
810     struct connection_entry *conn_entry;
811     void *state = NULL;
812 
813     pa_assert(p);
814     pa_assert(obj_entry);
815 
816     PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
817         pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path));
818 }
819 
pa_dbus_protocol_remove_interface(pa_dbus_protocol * p,const char * path,const char * interface)820 int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) {
821     struct object_entry *obj_entry;
822     struct interface_entry *iface_entry;
823     unsigned i;
824 
825     pa_assert(p);
826     pa_assert(path);
827     pa_assert(interface);
828 
829     if (!(obj_entry = pa_hashmap_get(p->objects, path)))
830         return -1;
831 
832     if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface)))
833         return -1;
834 
835     update_introspection(obj_entry);
836 
837     pa_log_debug("Interface %s removed from object %s", iface_entry->name, obj_entry->path);
838 
839     pa_xfree(iface_entry->name);
840     pa_hashmap_free(iface_entry->method_signatures);
841     pa_hashmap_free(iface_entry->method_handlers);
842     pa_hashmap_free(iface_entry->property_handlers);
843 
844     for (i = 0; i < iface_entry->n_signals; ++i) {
845         unsigned j;
846 
847         pa_xfree((char *) iface_entry->signals[i].name);
848 
849         for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) {
850             pa_xfree((char *) iface_entry->signals[i].arguments[j].name);
851             pa_xfree((char *) iface_entry->signals[i].arguments[j].type);
852             pa_assert(iface_entry->signals[i].arguments[j].direction == NULL);
853         }
854 
855         pa_xfree((pa_dbus_arg_info *) iface_entry->signals[i].arguments);
856     }
857 
858     pa_xfree(iface_entry->signals);
859     pa_xfree(iface_entry);
860 
861     if (pa_hashmap_isempty(obj_entry->interfaces)) {
862         unregister_object(p, obj_entry);
863 
864         pa_hashmap_remove(p->objects, path);
865         pa_xfree(obj_entry->path);
866         pa_hashmap_free(obj_entry->interfaces);
867         pa_xfree(obj_entry->introspection);
868         pa_xfree(obj_entry);
869     }
870 
871     return 0;
872 }
873 
register_all_objects(pa_dbus_protocol * p,DBusConnection * conn)874 static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
875     struct object_entry *obj_entry;
876     void *state = NULL;
877 
878     pa_assert(p);
879     pa_assert(conn);
880 
881     PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
882         pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p));
883 }
884 
signal_paths_entry_free(struct signal_paths_entry * e)885 static void signal_paths_entry_free(struct signal_paths_entry *e) {
886     pa_assert(e);
887 
888     pa_xfree(e->signal);
889     pa_idxset_free(e->paths, pa_xfree);
890     pa_xfree(e);
891 }
892 
pa_dbus_protocol_register_connection(pa_dbus_protocol * p,DBusConnection * conn,pa_client * client)893 int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client) {
894     struct connection_entry *conn_entry;
895 
896     pa_assert(p);
897     pa_assert(conn);
898     pa_assert(client);
899 
900     if (pa_hashmap_get(p->connections, conn))
901         return -1; /* The connection was already registered. */
902 
903     register_all_objects(p, conn);
904 
905     conn_entry = pa_xnew(struct connection_entry, 1);
906     conn_entry->connection = dbus_connection_ref(conn);
907     conn_entry->client = client;
908     conn_entry->listening_for_all_signals = false;
909     conn_entry->all_signals_objects = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
910     conn_entry->listening_signals = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
911                                                         (pa_free_cb_t) signal_paths_entry_free);
912 
913     pa_hashmap_put(p->connections, conn, conn_entry);
914 
915     return 0;
916 }
917 
unregister_all_objects(pa_dbus_protocol * p,DBusConnection * conn)918 static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
919     struct object_entry *obj_entry;
920     void *state = NULL;
921 
922     pa_assert(p);
923     pa_assert(conn);
924 
925     PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
926         pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path));
927 }
928 
signal_paths_entry_new(const char * signal_name)929 static struct signal_paths_entry *signal_paths_entry_new(const char *signal_name) {
930     struct signal_paths_entry *e = NULL;
931 
932     pa_assert(signal_name);
933 
934     e = pa_xnew0(struct signal_paths_entry, 1);
935     e->signal = pa_xstrdup(signal_name);
936     e->paths = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
937 
938     return e;
939 }
940 
pa_dbus_protocol_unregister_connection(pa_dbus_protocol * p,DBusConnection * conn)941 int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn) {
942     struct connection_entry *conn_entry = NULL;
943 
944     pa_assert(p);
945     pa_assert(conn);
946 
947     if (!(conn_entry = pa_hashmap_remove(p->connections, conn)))
948         return -1;
949 
950     unregister_all_objects(p, conn);
951 
952     dbus_connection_unref(conn_entry->connection);
953     pa_idxset_free(conn_entry->all_signals_objects, pa_xfree);
954     pa_hashmap_free(conn_entry->listening_signals);
955     pa_xfree(conn_entry);
956 
957     return 0;
958 }
959 
pa_dbus_protocol_get_client(pa_dbus_protocol * p,DBusConnection * conn)960 pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) {
961     struct connection_entry *conn_entry;
962 
963     pa_assert(p);
964     pa_assert(conn);
965 
966     if (!(conn_entry = pa_hashmap_get(p->connections, conn)))
967         return NULL;
968 
969     return conn_entry->client;
970 }
971 
pa_dbus_protocol_add_signal_listener(pa_dbus_protocol * p,DBusConnection * conn,const char * signal_name,char ** objects,unsigned n_objects)972 void pa_dbus_protocol_add_signal_listener(
973         pa_dbus_protocol *p,
974         DBusConnection *conn,
975         const char *signal_name,
976         char **objects,
977         unsigned n_objects) {
978     struct connection_entry *conn_entry = NULL;
979     struct signal_paths_entry *signal_paths_entry = NULL;
980     unsigned i = 0;
981 
982     pa_assert(p);
983     pa_assert(conn);
984     pa_assert(objects || n_objects == 0);
985 
986     pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
987 
988     /* all_signals_objects will either be emptied or replaced with new objects,
989      * so we empty it here unconditionally. If listening_for_all_signals is
990      * currently false, the idxset is empty already so this does nothing. */
991     pa_idxset_remove_all(conn_entry->all_signals_objects, pa_xfree);
992 
993     if (signal_name) {
994         conn_entry->listening_for_all_signals = false;
995 
996         /* Replace the old signal paths entry for this signal with a new
997          * one. */
998         pa_hashmap_remove_and_free(conn_entry->listening_signals, signal_name);
999         signal_paths_entry = signal_paths_entry_new(signal_name);
1000 
1001         for (i = 0; i < n_objects; ++i)
1002             pa_idxset_put(signal_paths_entry->paths, pa_xstrdup(objects[i]), NULL);
1003 
1004         pa_hashmap_put(conn_entry->listening_signals, signal_paths_entry->signal, signal_paths_entry);
1005 
1006     } else {
1007         conn_entry->listening_for_all_signals = true;
1008 
1009         /* We're not interested in individual signals anymore, so let's empty
1010          * listening_signals. */
1011         pa_hashmap_remove_all(conn_entry->listening_signals);
1012 
1013         for (i = 0; i < n_objects; ++i)
1014             pa_idxset_put(conn_entry->all_signals_objects, pa_xstrdup(objects[i]), NULL);
1015     }
1016 }
1017 
pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol * p,DBusConnection * conn,const char * signal_name)1018 void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal_name) {
1019     struct connection_entry *conn_entry = NULL;
1020     struct signal_paths_entry *signal_paths_entry = NULL;
1021 
1022     pa_assert(p);
1023     pa_assert(conn);
1024 
1025     pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
1026 
1027     if (signal_name) {
1028         if ((signal_paths_entry = pa_hashmap_remove(conn_entry->listening_signals, signal_name)))
1029             signal_paths_entry_free(signal_paths_entry);
1030 
1031     } else {
1032         conn_entry->listening_for_all_signals = false;
1033         pa_idxset_remove_all(conn_entry->all_signals_objects, pa_xfree);
1034         pa_hashmap_remove_all(conn_entry->listening_signals);
1035     }
1036 }
1037 
pa_dbus_protocol_send_signal(pa_dbus_protocol * p,DBusMessage * signal_msg)1038 void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal_msg) {
1039     struct connection_entry *conn_entry;
1040     struct signal_paths_entry *signal_paths_entry;
1041     void *state = NULL;
1042     DBusMessage *signal_copy;
1043     char *signal_string;
1044 
1045     pa_assert(p);
1046     pa_assert(signal_msg);
1047     pa_assert(dbus_message_get_type(signal_msg) == DBUS_MESSAGE_TYPE_SIGNAL);
1048     pa_assert(dbus_message_get_path(signal_msg));
1049     pa_assert(dbus_message_get_interface(signal_msg));
1050     pa_assert(dbus_message_get_member(signal_msg));
1051 
1052     signal_string = pa_sprintf_malloc("%s.%s", dbus_message_get_interface(signal_msg), dbus_message_get_member(signal_msg));
1053 
1054     PA_HASHMAP_FOREACH(conn_entry, p->connections, state) {
1055         if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */
1056              && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal_msg), NULL)
1057                  || pa_idxset_isempty(conn_entry->all_signals_objects)))
1058 
1059             || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */
1060                 && (signal_paths_entry = pa_hashmap_get(conn_entry->listening_signals, signal_string))
1061                 && (pa_idxset_get_by_data(signal_paths_entry->paths, dbus_message_get_path(signal_msg), NULL)
1062                     || pa_idxset_isempty(signal_paths_entry->paths)))) {
1063 
1064             pa_assert_se(signal_copy = dbus_message_copy(signal_msg));
1065             pa_assert_se(dbus_connection_send(conn_entry->connection, signal_copy, NULL));
1066             dbus_message_unref(signal_copy);
1067         }
1068     }
1069 
1070     pa_xfree(signal_string);
1071 }
1072 
pa_dbus_protocol_get_extensions(pa_dbus_protocol * p,unsigned * n)1073 const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
1074     const char **extensions;
1075     const char *ext_name;
1076     void *state = NULL;
1077     unsigned i = 0;
1078 
1079     pa_assert(p);
1080     pa_assert(n);
1081 
1082     *n = pa_idxset_size(p->extensions);
1083 
1084     if (*n <= 0)
1085         return NULL;
1086 
1087     extensions = pa_xnew(const char *, *n);
1088 
1089     while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL)))
1090         extensions[i++] = ext_name;
1091 
1092     return extensions;
1093 }
1094 
pa_dbus_protocol_register_extension(pa_dbus_protocol * p,const char * name)1095 int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name) {
1096     char *internal_name;
1097 
1098     pa_assert(p);
1099     pa_assert(name);
1100 
1101     internal_name = pa_xstrdup(name);
1102 
1103     if (pa_idxset_put(p->extensions, internal_name, NULL) < 0) {
1104         pa_xfree(internal_name);
1105         return -1;
1106     }
1107 
1108     pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED], internal_name);
1109 
1110     return 0;
1111 }
1112 
pa_dbus_protocol_unregister_extension(pa_dbus_protocol * p,const char * name)1113 int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) {
1114     char *internal_name;
1115 
1116     pa_assert(p);
1117     pa_assert(name);
1118 
1119     if (!(internal_name = pa_idxset_remove_by_data(p->extensions, name, NULL)))
1120         return -1;
1121 
1122     pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED], internal_name);
1123 
1124     pa_xfree(internal_name);
1125 
1126     return 0;
1127 }
1128 
pa_dbus_protocol_hook_connect(pa_dbus_protocol * p,pa_dbus_protocol_hook_t hook,pa_hook_priority_t prio,pa_hook_cb_t cb,void * data)1129 pa_hook_slot *pa_dbus_protocol_hook_connect(
1130         pa_dbus_protocol *p,
1131         pa_dbus_protocol_hook_t hook,
1132         pa_hook_priority_t prio,
1133         pa_hook_cb_t cb,
1134         void *data) {
1135     pa_assert(p);
1136     pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX);
1137     pa_assert(cb);
1138 
1139     return pa_hook_connect(&p->hooks[hook], prio, cb, data);
1140 }
1141