]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Don't notify clients on every subscription (it happens all the time).
[pulseaudio] / src / modules / module-device-manager.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2009 Colin Guthrie
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/rtclock.h>
40
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/module.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/modargs.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/core-subscribe.h>
47 #include <pulsecore/sink-input.h>
48 #include <pulsecore/source-output.h>
49 #include <pulsecore/namereg.h>
50 #include <pulsecore/protocol-native.h>
51 #include <pulsecore/pstream.h>
52 #include <pulsecore/pstream-util.h>
53 #include <pulsecore/database.h>
54
55 #include "module-device-manager-symdef.h"
56
57 PA_MODULE_AUTHOR("Colin Guthrie");
58 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
59 PA_MODULE_VERSION(PACKAGE_VERSION);
60 PA_MODULE_LOAD_ONCE(TRUE);
61 PA_MODULE_USAGE(
62 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
63 "on_hotplug=<When new device becomes available, recheck streams?> "
64 "on_rescue=<When device becomes unavailable, recheck streams?>");
65
66 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
67 #define DUMP_DATABASE
68
69 static const char* const valid_modargs[] = {
70 "do_routing",
71 "on_hotplug",
72 "on_rescue",
73 NULL
74 };
75
76 #define NUM_ROLES 9
77 enum {
78 ROLE_NONE,
79 ROLE_VIDEO,
80 ROLE_MUSIC,
81 ROLE_GAME,
82 ROLE_EVENT,
83 ROLE_PHONE,
84 ROLE_ANIMATION,
85 ROLE_PRODUCTION,
86 ROLE_A11Y,
87 };
88
89 typedef uint32_t role_indexes_t[NUM_ROLES];
90
91 static const char* role_names[NUM_ROLES] = {
92 "none",
93 "video",
94 "music",
95 "game",
96 "event",
97 "phone",
98 "animation",
99 "production",
100 "a11y",
101 };
102
103 struct userdata {
104 pa_core *core;
105 pa_module *module;
106 pa_subscription *subscription;
107 pa_hook_slot
108 *sink_new_hook_slot,
109 *source_new_hook_slot,
110 *sink_input_new_hook_slot,
111 *source_output_new_hook_slot,
112 *sink_put_hook_slot,
113 *source_put_hook_slot,
114 *sink_unlink_hook_slot,
115 *source_unlink_hook_slot,
116 *connection_unlink_hook_slot;
117 pa_time_event *save_time_event;
118 pa_database *database;
119
120 pa_native_protocol *protocol;
121 pa_idxset *subscribed;
122
123 pa_bool_t on_hotplug;
124 pa_bool_t on_rescue;
125 pa_bool_t do_routing;
126
127 role_indexes_t preferred_sinks;
128 role_indexes_t preferred_sources;
129 };
130
131 #define ENTRY_VERSION 1
132
133 struct entry {
134 uint8_t version;
135 char description[PA_NAME_MAX];
136 char icon[PA_NAME_MAX];
137 role_indexes_t priority;
138 } PA_GCC_PACKED;
139
140 enum {
141 SUBCOMMAND_TEST,
142 SUBCOMMAND_READ,
143 SUBCOMMAND_RENAME,
144 SUBCOMMAND_DELETE,
145 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
146 SUBCOMMAND_PREFER_DEVICE,
147 SUBCOMMAND_DEFER_DEVICE,
148 SUBCOMMAND_SUBSCRIBE,
149 SUBCOMMAND_EVENT
150 };
151
152
153 static struct entry* read_entry(struct userdata *u, const char *name) {
154 pa_datum key, data;
155 struct entry *e;
156
157 pa_assert(u);
158 pa_assert(name);
159
160 key.data = (char*) name;
161 key.size = strlen(name);
162
163 pa_zero(data);
164
165 if (!pa_database_get(u->database, &key, &data))
166 goto fail;
167
168 if (data.size != sizeof(struct entry)) {
169 pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
170 goto fail;
171 }
172
173 e = (struct entry*) data.data;
174
175 if (e->version != ENTRY_VERSION) {
176 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
177 goto fail;
178 }
179
180 if (!memchr(e->description, 0, sizeof(e->description))) {
181 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
182 goto fail;
183 }
184
185 return e;
186
187 fail:
188
189 pa_datum_free(&data);
190 return NULL;
191 }
192
193 #ifdef DUMP_DATABASE
194 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
195 pa_assert(u);
196 pa_assert(human);
197
198 if (sink_mode) {
199 pa_sink *s;
200 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
201 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
202 else
203 pa_log_debug(" %s No sink specified", human);
204 } else {
205 pa_source *s;
206 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
207 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
208 else
209 pa_log_debug(" %s No source specified", human);
210 }
211 }
212
213 static void dump_database(struct userdata *u) {
214 pa_datum key;
215 pa_bool_t done;
216
217 pa_assert(u);
218
219 done = !pa_database_first(u->database, &key, NULL);
220
221 pa_log_debug("Dumping database");
222 while (!done) {
223 char *name;
224 struct entry *e;
225 pa_datum next_key;
226
227 done = !pa_database_next(u->database, &key, &next_key, NULL);
228
229 name = pa_xstrndup(key.data, key.size);
230
231 if ((e = read_entry(u, name))) {
232 pa_log_debug(" Got entry: %s", name);
233 pa_log_debug(" Description: %s", e->description);
234 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
235 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
236 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
237 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
238 pa_xfree(e);
239 }
240
241 pa_xfree(name);
242
243 pa_datum_free(&key);
244 key = next_key;
245 }
246
247 pa_log_debug(" Highest priority devices per-role:");
248
249 pa_log_debug(" Sinks:");
250 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
251 char name[13];
252 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
253 strncpy(name, role_names[role], len);
254 for (int i = len+1; i < 12; ++i) name[i] = ' ';
255 name[len] = ':'; name[0] -= 32; name[12] = '\0';
256 dump_database_helper(u, role, name, TRUE);
257 }
258
259 pa_log_debug(" Sources:");
260 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
261 char name[13];
262 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
263 strncpy(name, role_names[role], len);
264 for (int i = len+1; i < 12; ++i) name[i] = ' ';
265 name[len] = ':'; name[0] -= 32; name[12] = '\0';
266 dump_database_helper(u, role, name, FALSE);
267 }
268
269 pa_log_debug("Completed database dump");
270 }
271 #endif
272
273 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
274 struct userdata *u = userdata;
275
276 pa_assert(a);
277 pa_assert(e);
278 pa_assert(u);
279
280 pa_assert(e == u->save_time_event);
281 u->core->mainloop->time_free(u->save_time_event);
282 u->save_time_event = NULL;
283
284 pa_database_sync(u->database);
285 pa_log_info("Synced.");
286
287 #ifdef DUMP_DATABASE
288 dump_database(u);
289 #endif
290 }
291
292 static void notify_subscribers(struct userdata *u) {
293
294 pa_native_connection *c;
295 uint32_t idx;
296
297 pa_assert(u);
298
299 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
300 pa_tagstruct *t;
301
302 t = pa_tagstruct_new(NULL, 0);
303 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
304 pa_tagstruct_putu32(t, 0);
305 pa_tagstruct_putu32(t, u->module->index);
306 pa_tagstruct_puts(t, u->module->name);
307 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
308
309 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
310 }
311 }
312
313 static void trigger_save(struct userdata *u) {
314
315 pa_assert(u);
316
317 notify_subscribers(u);
318
319 if (u->save_time_event)
320 return;
321
322 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
323 }
324
325 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
326
327 pa_assert(a);
328 pa_assert(b);
329
330 if (strncmp(a->description, b->description, sizeof(a->description))
331 || strncmp(a->icon, b->icon, sizeof(a->icon)))
332 return FALSE;
333
334 for (int i=0; i < NUM_ROLES; ++i)
335 if (a->priority[i] != b->priority[i])
336 return FALSE;
337
338 return TRUE;
339 }
340
341 static char *get_name(const char *key, const char *prefix) {
342 char *t;
343
344 if (strncmp(key, prefix, strlen(prefix)))
345 return NULL;
346
347 t = pa_xstrdup(key + strlen(prefix));
348 return t;
349 }
350
351 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
352 struct entry *old;
353
354 pa_assert(u);
355 pa_assert(entry);
356 pa_assert(name);
357 pa_assert(prefix);
358
359 if ((old = read_entry(u, name)))
360 *entry = *old;
361 else {
362 /* This is a new device, so make sure we write it's priority list correctly */
363 role_indexes_t max_priority;
364 pa_datum key;
365 pa_bool_t done;
366
367 pa_zero(max_priority);
368 done = !pa_database_first(u->database, &key, NULL);
369
370 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
371 while (!done) {
372 pa_datum next_key;
373
374 done = !pa_database_next(u->database, &key, &next_key, NULL);
375
376 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
377 char *name2;
378 struct entry *e;
379
380 name2 = pa_xstrndup(key.data, key.size);
381
382 if ((e = read_entry(u, name2))) {
383 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
384 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
385 }
386
387 pa_xfree(e);
388 }
389
390 pa_xfree(name2);
391 }
392 pa_datum_free(&key);
393 key = next_key;
394 }
395
396 /* Actually initialise our entry now we've calculated it */
397 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
398 entry->priority[i] = max_priority[i] + 1;
399 }
400 }
401
402 return old;
403 }
404
405 static uint32_t get_role_index(const char* role) {
406 pa_assert(role);
407
408 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
409 if (strcmp(role, role_names[i]) == 0)
410 return i;
411
412 return PA_INVALID_INDEX;
413 }
414
415 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
416 role_indexes_t *indexes, highest_priority_available;
417 pa_datum key;
418 pa_bool_t done, sink_mode;
419
420 pa_assert(u);
421 pa_assert(prefix);
422
423 sink_mode = (strcmp(prefix, "sink:") == 0);
424
425 if (sink_mode)
426 indexes = &u->preferred_sinks;
427 else
428 indexes = &u->preferred_sources;
429
430 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
431 *indexes[i] = PA_INVALID_INDEX;
432 }
433 pa_zero(highest_priority_available);
434
435 done = !pa_database_first(u->database, &key, NULL);
436
437 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
438 while (!done) {
439 pa_datum next_key;
440
441 done = !pa_database_next(u->database, &key, &next_key, NULL);
442
443 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
444 char *name;
445 struct entry *e;
446
447 name = pa_xstrndup(key.data, key.size);
448
449 if ((e = read_entry(u, name))) {
450 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
451 if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) {
452 /* We've found a device with a higher priority than that we've currently got,
453 so see if it is currently available or not and update our list */
454 uint32_t idx;
455 pa_bool_t found = FALSE;
456 char *device_name = get_name(name, prefix);
457
458 if (sink_mode) {
459 pa_sink *sink;
460
461 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
462 if ((pa_sink*) ignore_device == sink)
463 continue;
464 if (strcmp(sink->name, device_name) == 0) {
465 found = TRUE;
466 idx = sink->index; /* Is this needed? */
467 break;
468 }
469 }
470 } else {
471 pa_source *source;
472
473 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
474 if ((pa_source*) ignore_device == source)
475 continue;
476 if (strcmp(source->name, device_name) == 0) {
477 found = TRUE;
478 idx = source->index; /* Is this needed? */
479 break;
480 }
481 }
482 }
483 if (found) {
484 highest_priority_available[i] = e->priority[i];
485 *indexes[i] = idx;
486 }
487
488 pa_xfree(device_name);
489 }
490 }
491
492 pa_xfree(e);
493 }
494
495 pa_xfree(name);
496 }
497
498 pa_datum_free(&key);
499 key = next_key;
500 }
501 }
502
503
504 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
505 const char *role;
506 uint32_t role_index, device_index;
507 pa_sink *sink;
508
509 pa_assert(u);
510 pa_assert(u->do_routing);
511
512 if (si->save_sink)
513 return;
514
515 /* Skip this if it is already in the process of being moved anyway */
516 if (!si->sink)
517 return;
518
519 /* It might happen that a stream and a sink are set up at the
520 same time, in which case we want to make sure we don't
521 interfere with that */
522 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
523 return;
524
525 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
526 role_index = get_role_index("none");
527 else
528 role_index = get_role_index(role);
529
530 if (PA_INVALID_INDEX == role_index)
531 return;
532
533 device_index = u->preferred_sinks[role_index];
534 if (PA_INVALID_INDEX == device_index)
535 return;
536
537 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
538 return;
539
540 if (si->sink != sink)
541 pa_sink_input_move_to(si, sink, TRUE);
542 }
543
544 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
545 pa_sink_input *si;
546 uint32_t idx;
547
548 pa_assert(u);
549
550 if (!u->do_routing)
551 return PA_HOOK_OK;
552
553 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
554
555 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
556 route_sink_input(u, si);
557 }
558
559 return PA_HOOK_OK;
560 }
561
562 static void route_source_output(struct userdata *u, pa_source_output *so) {
563 const char *role;
564 uint32_t role_index, device_index;
565 pa_source *source;
566
567 pa_assert(u);
568 pa_assert(u->do_routing);
569
570 if (so->save_source)
571 return;
572
573 if (so->direct_on_input)
574 return;
575
576 /* Skip this if it is already in the process of being moved anyway */
577 if (!so->source)
578 return;
579
580 /* It might happen that a stream and a source are set up at the
581 same time, in which case we want to make sure we don't
582 interfere with that */
583 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
584 return;
585
586 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
587 role_index = get_role_index("none");
588 else
589 role_index = get_role_index(role);
590
591 if (PA_INVALID_INDEX == role_index)
592 return;
593
594 device_index = u->preferred_sources[role_index];
595 if (PA_INVALID_INDEX == device_index)
596 return;
597
598 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
599 return;
600
601 if (so->source != source)
602 pa_source_output_move_to(so, source, TRUE);
603 }
604
605 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
606 pa_source_output *so;
607 uint32_t idx;
608
609 pa_assert(u);
610
611 if (!u->do_routing)
612 return PA_HOOK_OK;
613
614 update_highest_priority_device_indexes(u, "source:", ignore_source);
615
616 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
617 route_source_output(u, so);
618 }
619
620 return PA_HOOK_OK;
621 }
622
623 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
624 struct userdata *u = userdata;
625 struct entry entry, *old = NULL;
626 char *name = NULL;
627 pa_datum key, data;
628
629 pa_assert(c);
630 pa_assert(u);
631
632 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
633 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
634 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
635 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
636
637 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
638 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
639 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
640 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
641 return;
642
643 pa_zero(entry);
644 entry.version = ENTRY_VERSION;
645
646 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
647 pa_sink_input *si;
648
649 if (!u->do_routing)
650 return;
651 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
652 return;
653
654 /* The role may change mid-stream, so we reroute */
655 route_sink_input(u, si);
656
657 return;
658 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
659 pa_source_output *so;
660
661 if (!u->do_routing)
662 return;
663 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
664 return;
665
666 /* The role may change mid-stream, so we reroute */
667 route_source_output(u, so);
668
669 return;
670 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
671 pa_sink *sink;
672
673 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
674 return;
675
676 name = pa_sprintf_malloc("sink:%s", sink->name);
677
678 old = load_or_initialize_entry(u, &entry, name, "sink:");
679
680 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
681 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
682
683 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
684 pa_source *source;
685
686 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
687
688 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
689 return;
690
691 if (source->monitor_of)
692 return;
693
694 name = pa_sprintf_malloc("source:%s", source->name);
695
696 old = load_or_initialize_entry(u, &entry, name, "source:");
697
698 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
699 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
700 }
701
702 pa_assert(name);
703
704 if (old) {
705
706 if (entries_equal(old, &entry)) {
707 pa_xfree(old);
708 pa_xfree(name);
709
710 return;
711 }
712
713 pa_xfree(old);
714 }
715
716 key.data = name;
717 key.size = strlen(name);
718
719 data.data = &entry;
720 data.size = sizeof(entry);
721
722 pa_log_info("Storing device %s.", name);
723
724 pa_database_set(u->database, &key, &data, TRUE);
725
726 pa_xfree(name);
727
728 trigger_save(u);
729 }
730
731 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
732 char *name;
733 struct entry *e;
734
735 pa_assert(c);
736 pa_assert(new_data);
737 pa_assert(u);
738
739 name = pa_sprintf_malloc("sink:%s", new_data->name);
740
741 if ((e = read_entry(u, name))) {
742 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
743 pa_log_info("Restoring description for sink %s.", new_data->name);
744 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
745 }
746
747 pa_xfree(e);
748 }
749
750 pa_xfree(name);
751
752 return PA_HOOK_OK;
753 }
754
755 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
756 char *name;
757 struct entry *e;
758
759 pa_assert(c);
760 pa_assert(new_data);
761 pa_assert(u);
762
763 name = pa_sprintf_malloc("source:%s", new_data->name);
764
765 if ((e = read_entry(u, name))) {
766 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
767 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
768 pa_log_info("Restoring description for source %s.", new_data->name);
769 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
770 }
771
772 pa_xfree(e);
773 }
774
775 pa_xfree(name);
776
777 return PA_HOOK_OK;
778 }
779
780 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
781 pa_assert(c);
782 pa_assert(new_data);
783 pa_assert(u);
784
785 if (!u->do_routing)
786 return PA_HOOK_OK;
787
788 if (new_data->sink)
789 pa_log_debug("Not restoring device for stream, because already set.");
790 else {
791 const char *role;
792 uint32_t role_index;
793
794 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
795 role_index = get_role_index("");
796 else
797 role_index = get_role_index(role);
798
799 if (PA_INVALID_INDEX != role_index) {
800 uint32_t device_index;
801
802 device_index = u->preferred_sinks[role_index];
803 if (PA_INVALID_INDEX != device_index) {
804 pa_sink *sink;
805
806 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
807 new_data->sink = sink;
808 new_data->save_sink = TRUE;
809 }
810 }
811 }
812 }
813
814 return PA_HOOK_OK;
815 }
816
817 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
818 pa_assert(c);
819 pa_assert(new_data);
820 pa_assert(u);
821
822 if (!u->do_routing)
823 return PA_HOOK_OK;
824
825 if (new_data->direct_on_input)
826 return PA_HOOK_OK;
827
828 if (new_data->source)
829 pa_log_debug("Not restoring device for stream, because already set");
830 else {
831 const char *role;
832 uint32_t role_index;
833
834 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
835 role_index = get_role_index("");
836 else
837 role_index = get_role_index(role);
838
839 if (PA_INVALID_INDEX != role_index) {
840 uint32_t device_index;
841
842 device_index = u->preferred_sources[role_index];
843 if (PA_INVALID_INDEX != device_index) {
844 pa_source *source;
845
846 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
847 new_data->source = source;
848 new_data->save_source = TRUE;
849 }
850 }
851 }
852 }
853
854 return PA_HOOK_OK;
855 }
856
857
858 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
859 pa_assert(c);
860 pa_assert(u);
861 pa_assert(u->core == c);
862 pa_assert(u->on_hotplug);
863
864 notify_subscribers(u);
865
866 return route_sink_inputs(u, NULL);
867 }
868
869 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
870 pa_assert(c);
871 pa_assert(u);
872 pa_assert(u->core == c);
873 pa_assert(u->on_hotplug);
874
875 notify_subscribers(u);
876
877 return route_source_outputs(u, NULL);
878 }
879
880 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
881 pa_assert(c);
882 pa_assert(sink);
883 pa_assert(u);
884 pa_assert(u->core == c);
885 pa_assert(u->on_rescue);
886
887 /* There's no point in doing anything if the core is shut down anyway */
888 if (c->state == PA_CORE_SHUTDOWN)
889 return PA_HOOK_OK;
890
891 notify_subscribers(u);
892
893 return route_sink_inputs(u, sink);
894 }
895
896 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
897 pa_assert(c);
898 pa_assert(source);
899 pa_assert(u);
900 pa_assert(u->core == c);
901 pa_assert(u->on_rescue);
902
903 /* There's no point in doing anything if the core is shut down anyway */
904 if (c->state == PA_CORE_SHUTDOWN)
905 return PA_HOOK_OK;
906
907 notify_subscribers(u);
908
909 return route_source_outputs(u, source);
910 }
911
912
913 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
914 pa_sink *sink;
915 pa_source *source;
916 uint32_t idx;
917 char *n;
918
919 pa_assert(u);
920 pa_assert(name);
921 pa_assert(e);
922
923 if ((n = get_name(name, "sink:"))) {
924 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
925 if (!pa_streq(sink->name, n)) {
926 continue;
927 }
928
929 pa_log_info("Setting description for sink %s.", sink->name);
930 pa_sink_set_description(sink, e->description);
931 }
932 pa_xfree(n);
933 }
934 else if ((n = get_name(name, "source:"))) {
935 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
936 if (!pa_streq(source->name, n)) {
937 continue;
938 }
939
940 if (source->monitor_of) {
941 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
942 continue;
943 }
944
945 pa_log_info("Setting description for source %s.", source->name);
946 pa_source_set_description(source, e->description);
947 }
948 pa_xfree(n);
949 }
950 }
951
952
953 #define EXT_VERSION 1
954
955 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
956 struct userdata *u;
957 uint32_t command;
958 pa_tagstruct *reply = NULL;
959
960 pa_assert(p);
961 pa_assert(m);
962 pa_assert(c);
963 pa_assert(t);
964
965 u = m->userdata;
966
967 if (pa_tagstruct_getu32(t, &command) < 0)
968 goto fail;
969
970 reply = pa_tagstruct_new(NULL, 0);
971 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
972 pa_tagstruct_putu32(reply, tag);
973
974 switch (command) {
975 case SUBCOMMAND_TEST: {
976 if (!pa_tagstruct_eof(t))
977 goto fail;
978
979 pa_tagstruct_putu32(reply, EXT_VERSION);
980 break;
981 }
982
983 case SUBCOMMAND_READ: {
984 pa_datum key;
985 pa_bool_t done;
986
987 if (!pa_tagstruct_eof(t))
988 goto fail;
989
990 done = !pa_database_first(u->database, &key, NULL);
991
992 while (!done) {
993 pa_datum next_key;
994 struct entry *e;
995 char *name;
996
997 done = !pa_database_next(u->database, &key, &next_key, NULL);
998
999 name = pa_xstrndup(key.data, key.size);
1000 pa_datum_free(&key);
1001
1002 if ((e = read_entry(u, name))) {
1003 uint32_t idx;
1004 char *devname;
1005 pa_bool_t available = FALSE;
1006
1007 if ((devname = get_name(name, "sink:"))) {
1008 pa_sink* s;
1009 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1010 if (strcmp(s->name, devname) == 0) {
1011 available = TRUE;
1012 break;
1013 }
1014 }
1015 pa_xfree(devname);
1016 } else if ((devname = get_name(name, "source:"))) {
1017 pa_source* s;
1018 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1019 if (strcmp(s->name, devname) == 0) {
1020 available = TRUE;
1021 break;
1022 }
1023 }
1024 pa_xfree(devname);
1025 }
1026
1027 pa_tagstruct_puts(reply, name);
1028 pa_tagstruct_puts(reply, e->description);
1029 pa_tagstruct_puts(reply, e->icon);
1030 pa_tagstruct_put_boolean(reply, available);
1031 pa_tagstruct_putu32(reply, NUM_ROLES);
1032
1033 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1034 pa_tagstruct_puts(reply, role_names[i]);
1035 pa_tagstruct_putu32(reply, e->priority[i]);
1036 }
1037
1038 pa_xfree(e);
1039 }
1040
1041 pa_xfree(name);
1042
1043 key = next_key;
1044 }
1045
1046 break;
1047 }
1048
1049 case SUBCOMMAND_RENAME: {
1050
1051 struct entry *e;
1052 const char *device, *description;
1053
1054 if (pa_tagstruct_gets(t, &device) < 0 ||
1055 pa_tagstruct_gets(t, &description) < 0)
1056 goto fail;
1057
1058 if (!device || !*device || !description || !*description)
1059 goto fail;
1060
1061 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1062 pa_datum key, data;
1063
1064 pa_strlcpy(e->description, description, sizeof(e->description));
1065
1066 key.data = (char *) device;
1067 key.size = strlen(device);
1068
1069 data.data = e;
1070 data.size = sizeof(*e);
1071
1072 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1073 apply_entry(u, device, e);
1074
1075 trigger_save(u);
1076 }
1077 else
1078 pa_log_warn("Could not save device");
1079
1080 pa_xfree(e);
1081 }
1082 else
1083 pa_log_warn("Could not rename device %s, no entry in database", device);
1084
1085 break;
1086 }
1087
1088 case SUBCOMMAND_DELETE:
1089
1090 while (!pa_tagstruct_eof(t)) {
1091 const char *name;
1092 pa_datum key;
1093
1094 if (pa_tagstruct_gets(t, &name) < 0)
1095 goto fail;
1096
1097 key.data = (char*) name;
1098 key.size = strlen(name);
1099
1100 /** @todo: Reindex the priorities */
1101 pa_database_unset(u->database, &key);
1102 }
1103
1104 trigger_save(u);
1105
1106 break;
1107
1108 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1109
1110 pa_bool_t enable;
1111
1112 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1113 goto fail;
1114
1115 if ((u->do_routing = enable)) {
1116 /* Update our caches */
1117 update_highest_priority_device_indexes(u, "sink:", NULL);
1118 update_highest_priority_device_indexes(u, "source:", NULL);
1119 }
1120
1121 break;
1122 }
1123
1124 case SUBCOMMAND_PREFER_DEVICE:
1125 case SUBCOMMAND_DEFER_DEVICE: {
1126
1127 const char *role, *device;
1128 struct entry *e;
1129 uint32_t role_index;
1130
1131 if (pa_tagstruct_gets(t, &role) < 0 ||
1132 pa_tagstruct_gets(t, &device) < 0)
1133 goto fail;
1134
1135 if (!role || !device || !*device)
1136 goto fail;
1137
1138 role_index = get_role_index(role);
1139 if (PA_INVALID_INDEX == role_index)
1140 goto fail;
1141
1142 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1143 pa_datum key, data;
1144 pa_bool_t done;
1145 char* prefix = NULL;
1146 uint32_t priority;
1147 pa_bool_t haschanged = FALSE;
1148
1149 if (strncmp(device, "sink:", 5) == 0)
1150 prefix = pa_xstrdup("sink:");
1151 else if (strncmp(device, "source:", 7) == 0)
1152 prefix = pa_xstrdup("source:");
1153
1154 if (!prefix)
1155 goto fail;
1156
1157 priority = e->priority[role_index];
1158
1159 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
1160
1161 done = !pa_database_first(u->database, &key, NULL);
1162
1163 while (!done && !haschanged) {
1164 pa_datum next_key;
1165
1166 done = !pa_database_next(u->database, &key, &next_key, NULL);
1167
1168 /* Only read devices with the right prefix */
1169 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
1170 char *name;
1171 struct entry *e2;
1172
1173 name = pa_xstrndup(key.data, key.size);
1174
1175 if ((e2 = read_entry(u, name))) {
1176 if (SUBCOMMAND_PREFER_DEVICE == command) {
1177 /* PREFER */
1178 if (e2->priority[role_index] == (priority - 1)) {
1179 e2->priority[role_index]++;
1180 haschanged = TRUE;
1181 }
1182 } else {
1183 /* DEFER */
1184 if (e2->priority[role_index] == (priority + 1)) {
1185 e2->priority[role_index]--;
1186 haschanged = TRUE;
1187 }
1188 }
1189
1190 if (haschanged) {
1191 data.data = e2;
1192 data.size = sizeof(*e2);
1193
1194 if (pa_database_set(u->database, &key, &data, TRUE))
1195 pa_log_warn("Could not save device");
1196 }
1197
1198 pa_xfree(e2);
1199 }
1200
1201 pa_xfree(name);
1202 }
1203
1204 pa_datum_free(&key);
1205 key = next_key;
1206 }
1207
1208 /* Now write out our actual entry */
1209 if (haschanged) {
1210 if (SUBCOMMAND_PREFER_DEVICE == command)
1211 e->priority[role_index]--;
1212 else
1213 e->priority[role_index]++;
1214
1215 key.data = (char *) device;
1216 key.size = strlen(device);
1217
1218 data.data = e;
1219 data.size = sizeof(*e);
1220
1221 if (pa_database_set(u->database, &key, &data, TRUE))
1222 pa_log_warn("Could not save device");
1223
1224 trigger_save(u);
1225 }
1226
1227 pa_xfree(e);
1228
1229 pa_xfree(prefix);
1230 }
1231 else
1232 pa_log_warn("Could not reorder device %s, no entry in database", device);
1233
1234 break;
1235 }
1236
1237 case SUBCOMMAND_SUBSCRIBE: {
1238
1239 pa_bool_t enabled;
1240
1241 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1242 !pa_tagstruct_eof(t))
1243 goto fail;
1244
1245 if (enabled)
1246 pa_idxset_put(u->subscribed, c, NULL);
1247 else
1248 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1249
1250 break;
1251 }
1252
1253 default:
1254 goto fail;
1255 }
1256
1257 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1258 return 0;
1259
1260 fail:
1261
1262 if (reply)
1263 pa_tagstruct_free(reply);
1264
1265 return -1;
1266 }
1267
1268 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1269 pa_assert(p);
1270 pa_assert(c);
1271 pa_assert(u);
1272
1273 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1274 return PA_HOOK_OK;
1275 }
1276
1277 int pa__init(pa_module*m) {
1278 pa_modargs *ma = NULL;
1279 struct userdata *u;
1280 char *fname;
1281 pa_sink *sink;
1282 pa_source *source;
1283 uint32_t idx;
1284 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1285
1286 pa_assert(m);
1287
1288 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1289 pa_log("Failed to parse module arguments");
1290 goto fail;
1291 }
1292
1293 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1294 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1295 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1296 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1297 goto fail;
1298 }
1299
1300 m->userdata = u = pa_xnew0(struct userdata, 1);
1301 u->core = m->core;
1302 u->module = m;
1303 u->do_routing = do_routing;
1304 u->on_hotplug = on_hotplug;
1305 u->on_rescue = on_rescue;
1306 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1307
1308 u->protocol = pa_native_protocol_get(m->core);
1309 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1310
1311 u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
1312
1313 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
1314
1315 /* Used to handle device description management */
1316 u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
1317 u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
1318
1319 /* The following slots are used to deal with routing */
1320 /* A little bit later than module-stream-restore, module-intended-roles */
1321 u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u);
1322 u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u);
1323
1324 if (on_hotplug) {
1325 /* A little bit later than module-stream-restore, module-intended-roles */
1326 u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_put_hook_callback, u);
1327 u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) source_put_hook_callback, u);
1328 }
1329
1330 if (on_rescue) {
1331 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1332 u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_unlink_hook_callback, u);
1333 u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) source_unlink_hook_callback, u);
1334 }
1335
1336 if (!(fname = pa_state_path("device-manager", TRUE)))
1337 goto fail;
1338
1339 if (!(u->database = pa_database_open(fname, TRUE))) {
1340 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1341 pa_xfree(fname);
1342 goto fail;
1343 }
1344
1345 pa_log_info("Sucessfully opened database file '%s'.", fname);
1346 pa_xfree(fname);
1347
1348 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1349 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1350 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1351
1352 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1353 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1354
1355 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1356 route_sink_inputs(u, NULL);
1357 route_source_outputs(u, NULL);
1358
1359 #ifdef DUMP_DATABASE
1360 dump_database(u);
1361 #endif
1362
1363 pa_modargs_free(ma);
1364 return 0;
1365
1366 fail:
1367 pa__done(m);
1368
1369 if (ma)
1370 pa_modargs_free(ma);
1371
1372 return -1;
1373 }
1374
1375 void pa__done(pa_module*m) {
1376 struct userdata* u;
1377
1378 pa_assert(m);
1379
1380 if (!(u = m->userdata))
1381 return;
1382
1383 if (u->subscription)
1384 pa_subscription_free(u->subscription);
1385
1386 if (u->sink_new_hook_slot)
1387 pa_hook_slot_free(u->sink_new_hook_slot);
1388 if (u->source_new_hook_slot)
1389 pa_hook_slot_free(u->source_new_hook_slot);
1390
1391 if (u->sink_input_new_hook_slot)
1392 pa_hook_slot_free(u->sink_input_new_hook_slot);
1393 if (u->source_output_new_hook_slot)
1394 pa_hook_slot_free(u->source_output_new_hook_slot);
1395
1396 if (u->sink_put_hook_slot)
1397 pa_hook_slot_free(u->sink_put_hook_slot);
1398 if (u->source_put_hook_slot)
1399 pa_hook_slot_free(u->source_put_hook_slot);
1400
1401 if (u->sink_unlink_hook_slot)
1402 pa_hook_slot_free(u->sink_unlink_hook_slot);
1403 if (u->source_unlink_hook_slot)
1404 pa_hook_slot_free(u->source_unlink_hook_slot);
1405
1406 if (u->save_time_event)
1407 u->core->mainloop->time_free(u->save_time_event);
1408
1409 if (u->database)
1410 pa_database_close(u->database);
1411
1412 if (u->protocol) {
1413 pa_native_protocol_remove_ext(u->protocol, m);
1414 pa_native_protocol_unref(u->protocol);
1415 }
1416
1417 if (u->subscribed)
1418 pa_idxset_free(u->subscribed, NULL, NULL);
1419
1420 pa_xfree(u);
1421 }