]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Misc fixes to co-exist with other stream management/routing modules.
[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_REORDER,
147 SUBCOMMAND_SUBSCRIBE,
148 SUBCOMMAND_EVENT
149 };
150
151
152 static struct entry* read_entry(struct userdata *u, const char *name) {
153 pa_datum key, data;
154 struct entry *e;
155
156 pa_assert(u);
157 pa_assert(name);
158
159 key.data = (char*) name;
160 key.size = strlen(name);
161
162 pa_zero(data);
163
164 if (!pa_database_get(u->database, &key, &data))
165 goto fail;
166
167 if (data.size != sizeof(struct entry)) {
168 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));
169 goto fail;
170 }
171
172 e = (struct entry*) data.data;
173
174 if (e->version != ENTRY_VERSION) {
175 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
176 goto fail;
177 }
178
179 if (!memchr(e->description, 0, sizeof(e->description))) {
180 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
181 goto fail;
182 }
183
184 return e;
185
186 fail:
187
188 pa_datum_free(&data);
189 return NULL;
190 }
191
192 #ifdef DUMP_DATABASE
193 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
194 pa_assert(u);
195 pa_assert(human);
196
197 if (sink_mode) {
198 pa_sink *s;
199 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
200 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
201 else
202 pa_log_debug(" %s No sink specified", human);
203 } else {
204 pa_source *s;
205 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
206 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
207 else
208 pa_log_debug(" %s No source specified", human);
209 }
210 }
211
212 static void dump_database(struct userdata *u) {
213 pa_datum key;
214 pa_bool_t done;
215
216 pa_assert(u);
217
218 done = !pa_database_first(u->database, &key, NULL);
219
220 pa_log_debug("Dumping database");
221 while (!done) {
222 char *name;
223 struct entry *e;
224 pa_datum next_key;
225
226 done = !pa_database_next(u->database, &key, &next_key, NULL);
227
228 name = pa_xstrndup(key.data, key.size);
229
230 if ((e = read_entry(u, name))) {
231 pa_log_debug(" Got entry: %s", name);
232 pa_log_debug(" Description: %s", e->description);
233 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
234 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
235 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
236 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
237 pa_xfree(e);
238 }
239
240 pa_xfree(name);
241
242 pa_datum_free(&key);
243 key = next_key;
244 }
245
246 if (u->do_routing) {
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
270 pa_log_debug("Completed database dump");
271 }
272 #endif
273
274 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
275 struct userdata *u = userdata;
276
277 pa_assert(a);
278 pa_assert(e);
279 pa_assert(u);
280
281 pa_assert(e == u->save_time_event);
282 u->core->mainloop->time_free(u->save_time_event);
283 u->save_time_event = NULL;
284
285 pa_database_sync(u->database);
286 pa_log_info("Synced.");
287
288 #ifdef DUMP_DATABASE
289 dump_database(u);
290 #endif
291 }
292
293 static void notify_subscribers(struct userdata *u) {
294
295 pa_native_connection *c;
296 uint32_t idx;
297
298 pa_assert(u);
299
300 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
301 pa_tagstruct *t;
302
303 t = pa_tagstruct_new(NULL, 0);
304 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
305 pa_tagstruct_putu32(t, 0);
306 pa_tagstruct_putu32(t, u->module->index);
307 pa_tagstruct_puts(t, u->module->name);
308 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
309
310 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
311 }
312 }
313
314 static void trigger_save(struct userdata *u) {
315
316 pa_assert(u);
317
318 notify_subscribers(u);
319
320 if (u->save_time_event)
321 return;
322
323 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
324 }
325
326 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
327
328 pa_assert(a);
329 pa_assert(b);
330
331 if (strncmp(a->description, b->description, sizeof(a->description))
332 || strncmp(a->icon, b->icon, sizeof(a->icon)))
333 return FALSE;
334
335 for (int i=0; i < NUM_ROLES; ++i)
336 if (a->priority[i] != b->priority[i])
337 return FALSE;
338
339 return TRUE;
340 }
341
342 static char *get_name(const char *key, const char *prefix) {
343 char *t;
344
345 if (strncmp(key, prefix, strlen(prefix)))
346 return NULL;
347
348 t = pa_xstrdup(key + strlen(prefix));
349 return t;
350 }
351
352 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
353 struct entry *old;
354
355 pa_assert(u);
356 pa_assert(entry);
357 pa_assert(name);
358 pa_assert(prefix);
359
360 if ((old = read_entry(u, name)))
361 *entry = *old;
362 else {
363 /* This is a new device, so make sure we write it's priority list correctly */
364 role_indexes_t max_priority;
365 pa_datum key;
366 pa_bool_t done;
367
368 pa_zero(max_priority);
369 done = !pa_database_first(u->database, &key, NULL);
370
371 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
372 while (!done) {
373 pa_datum next_key;
374
375 done = !pa_database_next(u->database, &key, &next_key, NULL);
376
377 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
378 char *name2;
379 struct entry *e;
380
381 name2 = pa_xstrndup(key.data, key.size);
382
383 if ((e = read_entry(u, name2))) {
384 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
385 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
386 }
387
388 pa_xfree(e);
389 }
390
391 pa_xfree(name2);
392 }
393 pa_datum_free(&key);
394 key = next_key;
395 }
396
397 /* Actually initialise our entry now we've calculated it */
398 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
399 entry->priority[i] = max_priority[i] + 1;
400 }
401 }
402
403 return old;
404 }
405
406 static uint32_t get_role_index(const char* role) {
407 pa_assert(role);
408
409 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
410 if (strcmp(role, role_names[i]) == 0)
411 return i;
412
413 return PA_INVALID_INDEX;
414 }
415
416 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
417 role_indexes_t *indexes, highest_priority_available;
418 pa_datum key;
419 pa_bool_t done, sink_mode;
420
421 pa_assert(u);
422 pa_assert(prefix);
423
424 sink_mode = (strcmp(prefix, "sink:") == 0);
425
426 if (sink_mode)
427 indexes = &u->preferred_sinks;
428 else
429 indexes = &u->preferred_sources;
430
431 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
432 (*indexes)[i] = PA_INVALID_INDEX;
433 }
434 pa_zero(highest_priority_available);
435
436 done = !pa_database_first(u->database, &key, NULL);
437
438 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
439 while (!done) {
440 pa_datum next_key;
441
442 done = !pa_database_next(u->database, &key, &next_key, NULL);
443
444 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
445 char *name, *device_name;
446 struct entry *e;
447
448 name = pa_xstrndup(key.data, key.size);
449 device_name = get_name(name, prefix);
450
451 if ((e = read_entry(u, name)) && ENTRY_VERSION == e->version) {
452 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
453 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
454 /* We've found a device with a higher priority than that we've currently got,
455 so see if it is currently available or not and update our list */
456 uint32_t idx;
457 pa_bool_t found = FALSE;
458
459 if (sink_mode) {
460 pa_sink *sink;
461
462 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
463 if ((pa_sink*) ignore_device == sink)
464 continue;
465 if (strcmp(sink->name, device_name) == 0) {
466 found = TRUE;
467 idx = sink->index; /* Is this needed? */
468 break;
469 }
470 }
471 } else {
472 pa_source *source;
473
474 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
475 if ((pa_source*) ignore_device == source)
476 continue;
477 if (strcmp(source->name, device_name) == 0) {
478 found = TRUE;
479 idx = source->index; /* Is this needed? */
480 break;
481 }
482 }
483 }
484 if (found) {
485 highest_priority_available[i] = e->priority[i];
486 (*indexes)[i] = idx;
487 }
488
489 }
490 }
491
492 pa_xfree(e);
493 }
494
495 pa_xfree(name);
496 pa_xfree(device_name);
497 }
498
499 pa_datum_free(&key);
500 key = next_key;
501 }
502 }
503
504
505 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
506 const char *role;
507 uint32_t role_index, device_index;
508 pa_sink *sink;
509
510 pa_assert(u);
511 pa_assert(u->do_routing);
512
513 /* Skip this if it is already in the process of being moved anyway */
514 if (!si->sink)
515 return;
516
517 /* It might happen that a stream and a sink are set up at the
518 same time, in which case we want to make sure we don't
519 interfere with that */
520 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
521 return;
522
523 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
524 role_index = get_role_index("none");
525 else
526 role_index = get_role_index(role);
527
528 if (PA_INVALID_INDEX == role_index)
529 return;
530
531 device_index = u->preferred_sinks[role_index];
532 if (PA_INVALID_INDEX == device_index)
533 return;
534
535 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
536 return;
537
538 if (si->sink != sink)
539 pa_sink_input_move_to(si, sink, TRUE);
540 }
541
542 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
543 pa_sink_input *si;
544 uint32_t idx;
545
546 pa_assert(u);
547
548 if (!u->do_routing)
549 return PA_HOOK_OK;
550
551 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
552
553 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
554 route_sink_input(u, si);
555 }
556
557 return PA_HOOK_OK;
558 }
559
560 static void route_source_output(struct userdata *u, pa_source_output *so) {
561 const char *role;
562 uint32_t role_index, device_index;
563 pa_source *source;
564
565 pa_assert(u);
566 pa_assert(u->do_routing);
567
568 if (so->direct_on_input)
569 return;
570
571 /* Skip this if it is already in the process of being moved anyway */
572 if (!so->source)
573 return;
574
575 /* It might happen that a stream and a source are set up at the
576 same time, in which case we want to make sure we don't
577 interfere with that */
578 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
579 return;
580
581 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
582 role_index = get_role_index("none");
583 else
584 role_index = get_role_index(role);
585
586 if (PA_INVALID_INDEX == role_index)
587 return;
588
589 device_index = u->preferred_sources[role_index];
590 if (PA_INVALID_INDEX == device_index)
591 return;
592
593 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
594 return;
595
596 if (so->source != source)
597 pa_source_output_move_to(so, source, TRUE);
598 }
599
600 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
601 pa_source_output *so;
602 uint32_t idx;
603
604 pa_assert(u);
605
606 if (!u->do_routing)
607 return PA_HOOK_OK;
608
609 update_highest_priority_device_indexes(u, "source:", ignore_source);
610
611 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
612 route_source_output(u, so);
613 }
614
615 return PA_HOOK_OK;
616 }
617
618 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
619 struct userdata *u = userdata;
620 struct entry entry, *old = NULL;
621 char *name = NULL;
622 pa_datum key, data;
623
624 pa_assert(c);
625 pa_assert(u);
626
627 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
628 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
629 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
630 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
631
632 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
633 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
634 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
635 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
636 return;
637
638 pa_zero(entry);
639 entry.version = ENTRY_VERSION;
640
641 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
642 pa_sink_input *si;
643
644 if (!u->do_routing)
645 return;
646 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
647 return;
648
649 /* The role may change mid-stream, so we reroute */
650 route_sink_input(u, si);
651
652 return;
653 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
654 pa_source_output *so;
655
656 if (!u->do_routing)
657 return;
658 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
659 return;
660
661 /* The role may change mid-stream, so we reroute */
662 route_source_output(u, so);
663
664 return;
665 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
666 pa_sink *sink;
667
668 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
669 return;
670
671 name = pa_sprintf_malloc("sink:%s", sink->name);
672
673 old = load_or_initialize_entry(u, &entry, name, "sink:");
674
675 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
676 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
677
678 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
679 pa_source *source;
680
681 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
682
683 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
684 return;
685
686 if (source->monitor_of)
687 return;
688
689 name = pa_sprintf_malloc("source:%s", source->name);
690
691 old = load_or_initialize_entry(u, &entry, name, "source:");
692
693 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
694 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
695 }
696
697 pa_assert(name);
698
699 if (old) {
700
701 if (entries_equal(old, &entry)) {
702 pa_xfree(old);
703 pa_xfree(name);
704
705 return;
706 }
707
708 pa_xfree(old);
709 }
710
711 key.data = name;
712 key.size = strlen(name);
713
714 data.data = &entry;
715 data.size = sizeof(entry);
716
717 pa_log_info("Storing device %s.", name);
718
719 pa_database_set(u->database, &key, &data, TRUE);
720
721 pa_xfree(name);
722
723 trigger_save(u);
724 }
725
726 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
727 char *name;
728 struct entry *e;
729
730 pa_assert(c);
731 pa_assert(new_data);
732 pa_assert(u);
733
734 name = pa_sprintf_malloc("sink:%s", new_data->name);
735
736 if ((e = read_entry(u, name))) {
737 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
738 pa_log_info("Restoring description for sink %s.", new_data->name);
739 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
740 }
741
742 pa_xfree(e);
743 }
744
745 pa_xfree(name);
746
747 return PA_HOOK_OK;
748 }
749
750 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
751 char *name;
752 struct entry *e;
753
754 pa_assert(c);
755 pa_assert(new_data);
756 pa_assert(u);
757
758 name = pa_sprintf_malloc("source:%s", new_data->name);
759
760 if ((e = read_entry(u, name))) {
761 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
762 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
763 pa_log_info("Restoring description for source %s.", new_data->name);
764 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
765 }
766
767 pa_xfree(e);
768 }
769
770 pa_xfree(name);
771
772 return PA_HOOK_OK;
773 }
774
775 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
776 const char *role;
777 uint32_t role_index;
778
779 pa_assert(c);
780 pa_assert(new_data);
781 pa_assert(u);
782
783 if (!u->do_routing)
784 return PA_HOOK_OK;
785
786 if (new_data->sink)
787 pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
788
789 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
790 role_index = get_role_index("none");
791 else
792 role_index = get_role_index(role);
793
794 if (PA_INVALID_INDEX != role_index) {
795 uint32_t device_index;
796
797 device_index = u->preferred_sinks[role_index];
798 if (PA_INVALID_INDEX != device_index) {
799 pa_sink *sink;
800
801 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
802 new_data->sink = sink;
803 }
804 }
805 }
806
807 return PA_HOOK_OK;
808 }
809
810 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
811 const char *role;
812 uint32_t role_index;
813
814 pa_assert(c);
815 pa_assert(new_data);
816 pa_assert(u);
817
818 if (!u->do_routing)
819 return PA_HOOK_OK;
820
821 if (new_data->direct_on_input)
822 return PA_HOOK_OK;
823
824 if (new_data->source)
825 pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
826
827 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
828 role_index = get_role_index("none");
829 else
830 role_index = get_role_index(role);
831
832 if (PA_INVALID_INDEX != role_index) {
833 uint32_t device_index;
834
835 device_index = u->preferred_sources[role_index];
836 if (PA_INVALID_INDEX != device_index) {
837 pa_source *source;
838
839 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
840 new_data->source = source;
841 }
842 }
843 }
844
845 return PA_HOOK_OK;
846 }
847
848
849 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
850 pa_assert(c);
851 pa_assert(u);
852 pa_assert(u->core == c);
853 pa_assert(u->on_hotplug);
854
855 notify_subscribers(u);
856
857 return route_sink_inputs(u, NULL);
858 }
859
860 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
861 pa_assert(c);
862 pa_assert(u);
863 pa_assert(u->core == c);
864 pa_assert(u->on_hotplug);
865
866 notify_subscribers(u);
867
868 return route_source_outputs(u, NULL);
869 }
870
871 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
872 pa_assert(c);
873 pa_assert(sink);
874 pa_assert(u);
875 pa_assert(u->core == c);
876 pa_assert(u->on_rescue);
877
878 /* There's no point in doing anything if the core is shut down anyway */
879 if (c->state == PA_CORE_SHUTDOWN)
880 return PA_HOOK_OK;
881
882 notify_subscribers(u);
883
884 return route_sink_inputs(u, sink);
885 }
886
887 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
888 pa_assert(c);
889 pa_assert(source);
890 pa_assert(u);
891 pa_assert(u->core == c);
892 pa_assert(u->on_rescue);
893
894 /* There's no point in doing anything if the core is shut down anyway */
895 if (c->state == PA_CORE_SHUTDOWN)
896 return PA_HOOK_OK;
897
898 notify_subscribers(u);
899
900 return route_source_outputs(u, source);
901 }
902
903
904 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
905 pa_sink *sink;
906 pa_source *source;
907 uint32_t idx;
908 char *n;
909
910 pa_assert(u);
911 pa_assert(name);
912 pa_assert(e);
913
914 if ((n = get_name(name, "sink:"))) {
915 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
916 if (!pa_streq(sink->name, n)) {
917 continue;
918 }
919
920 pa_log_info("Setting description for sink %s.", sink->name);
921 pa_sink_set_description(sink, e->description);
922 }
923 pa_xfree(n);
924 }
925 else if ((n = get_name(name, "source:"))) {
926 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
927 if (!pa_streq(source->name, n)) {
928 continue;
929 }
930
931 if (source->monitor_of) {
932 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
933 continue;
934 }
935
936 pa_log_info("Setting description for source %s.", source->name);
937 pa_source_set_description(source, e->description);
938 }
939 pa_xfree(n);
940 }
941 }
942
943
944 #define EXT_VERSION 1
945
946 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
947 struct userdata *u;
948 uint32_t command;
949 pa_tagstruct *reply = NULL;
950
951 pa_assert(p);
952 pa_assert(m);
953 pa_assert(c);
954 pa_assert(t);
955
956 u = m->userdata;
957
958 if (pa_tagstruct_getu32(t, &command) < 0)
959 goto fail;
960
961 reply = pa_tagstruct_new(NULL, 0);
962 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
963 pa_tagstruct_putu32(reply, tag);
964
965 switch (command) {
966 case SUBCOMMAND_TEST: {
967 if (!pa_tagstruct_eof(t))
968 goto fail;
969
970 pa_tagstruct_putu32(reply, EXT_VERSION);
971 break;
972 }
973
974 case SUBCOMMAND_READ: {
975 pa_datum key;
976 pa_bool_t done;
977
978 if (!pa_tagstruct_eof(t))
979 goto fail;
980
981 done = !pa_database_first(u->database, &key, NULL);
982
983 while (!done) {
984 pa_datum next_key;
985 struct entry *e;
986 char *name;
987
988 done = !pa_database_next(u->database, &key, &next_key, NULL);
989
990 name = pa_xstrndup(key.data, key.size);
991 pa_datum_free(&key);
992
993 if ((e = read_entry(u, name))) {
994 uint32_t idx;
995 char *devname;
996 pa_bool_t available = FALSE;
997
998 if ((devname = get_name(name, "sink:"))) {
999 pa_sink* s;
1000 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1001 if (strcmp(s->name, devname) == 0) {
1002 available = TRUE;
1003 break;
1004 }
1005 }
1006 pa_xfree(devname);
1007 } else if ((devname = get_name(name, "source:"))) {
1008 pa_source* s;
1009 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1010 if (strcmp(s->name, devname) == 0) {
1011 available = TRUE;
1012 break;
1013 }
1014 }
1015 pa_xfree(devname);
1016 }
1017
1018 pa_tagstruct_puts(reply, name);
1019 pa_tagstruct_puts(reply, e->description);
1020 pa_tagstruct_puts(reply, e->icon);
1021 pa_tagstruct_put_boolean(reply, available);
1022 pa_tagstruct_putu32(reply, NUM_ROLES);
1023
1024 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1025 pa_tagstruct_puts(reply, role_names[i]);
1026 pa_tagstruct_putu32(reply, e->priority[i]);
1027 }
1028
1029 pa_xfree(e);
1030 }
1031
1032 pa_xfree(name);
1033
1034 key = next_key;
1035 }
1036
1037 break;
1038 }
1039
1040 case SUBCOMMAND_RENAME: {
1041
1042 struct entry *e;
1043 const char *device, *description;
1044
1045 if (pa_tagstruct_gets(t, &device) < 0 ||
1046 pa_tagstruct_gets(t, &description) < 0)
1047 goto fail;
1048
1049 if (!device || !*device || !description || !*description)
1050 goto fail;
1051
1052 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1053 pa_datum key, data;
1054
1055 pa_strlcpy(e->description, description, sizeof(e->description));
1056
1057 key.data = (char *) device;
1058 key.size = strlen(device);
1059
1060 data.data = e;
1061 data.size = sizeof(*e);
1062
1063 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1064 apply_entry(u, device, e);
1065
1066 trigger_save(u);
1067 }
1068 else
1069 pa_log_warn("Could not save device");
1070
1071 pa_xfree(e);
1072 }
1073 else
1074 pa_log_warn("Could not rename device %s, no entry in database", device);
1075
1076 break;
1077 }
1078
1079 case SUBCOMMAND_DELETE:
1080
1081 while (!pa_tagstruct_eof(t)) {
1082 const char *name;
1083 pa_datum key;
1084
1085 if (pa_tagstruct_gets(t, &name) < 0)
1086 goto fail;
1087
1088 key.data = (char*) name;
1089 key.size = strlen(name);
1090
1091 /** @todo: Reindex the priorities */
1092 pa_database_unset(u->database, &key);
1093 }
1094
1095 trigger_save(u);
1096
1097 break;
1098
1099 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1100
1101 pa_bool_t enable;
1102
1103 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1104 goto fail;
1105
1106 if ((u->do_routing = enable)) {
1107 /* Update our caches */
1108 update_highest_priority_device_indexes(u, "sink:", NULL);
1109 update_highest_priority_device_indexes(u, "source:", NULL);
1110 }
1111
1112 break;
1113 }
1114
1115 case SUBCOMMAND_REORDER: {
1116
1117 const char *role;
1118 struct entry *e;
1119 uint32_t role_index, n_devices;
1120 pa_datum key, data;
1121 pa_bool_t done, sink_mode = TRUE;
1122 struct device_t { uint32_t prio; char *device; };
1123 struct device_t *device;
1124 struct device_t **devices;
1125 uint32_t i, idx, offset;
1126 pa_hashmap *h;
1127 /*void *state;*/
1128 pa_bool_t first;
1129
1130 if (pa_tagstruct_gets(t, &role) < 0 ||
1131 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1132 n_devices < 1)
1133 goto fail;
1134
1135 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1136 goto fail;
1137
1138 /* Cycle through the devices given and make sure they exist */
1139 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1140 first = TRUE;
1141 idx = 0;
1142 for (i = 0; i < n_devices; ++i) {
1143 const char *s;
1144 if (pa_tagstruct_gets(t, &s) < 0) {
1145 while ((device = pa_hashmap_steal_first(h))) {
1146 pa_xfree(device->device);
1147 pa_xfree(device);
1148 }
1149
1150 pa_hashmap_free(h, NULL, NULL);
1151 pa_log_error("Protocol error on reorder");
1152 goto fail;
1153 }
1154
1155 /* Ensure this is a valid entry */
1156 if (!(e = read_entry(u, s))) {
1157 while ((device = pa_hashmap_steal_first(h))) {
1158 pa_xfree(device->device);
1159 pa_xfree(device);
1160 }
1161
1162 pa_hashmap_free(h, NULL, NULL);
1163 pa_log_error("Client specified an unknown device in it's reorder list.");
1164 goto fail;
1165 }
1166 pa_xfree(e);
1167
1168 if (first) {
1169 first = FALSE;
1170 sink_mode = (0 == strncmp("sink:", s, 5));
1171 } else if ((sink_mode && 0 != strncmp("sink:", s, 5))
1172 || (!sink_mode && 0 != strncmp("source:", s, 7)))
1173 {
1174 while ((device = pa_hashmap_steal_first(h))) {
1175 pa_xfree(device->device);
1176 pa_xfree(device);
1177 }
1178
1179 pa_hashmap_free(h, NULL, NULL);
1180 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1181 goto fail;
1182 }
1183
1184 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1185 device = pa_xnew(struct device_t, 1);
1186 device->device = pa_xstrdup(s);
1187 if (pa_hashmap_put(h, device->device, device) == 0) {
1188 device->prio = idx;
1189 idx++;
1190 } else {
1191 pa_xfree(device->device);
1192 pa_xfree(device);
1193 }
1194 }
1195
1196 /*pa_log_debug("Hashmap contents (received from client)");
1197 PA_HASHMAP_FOREACH(device, h, state) {
1198 pa_log_debug(" - %s (%d)", device->device, device->prio);
1199 }*/
1200
1201 /* Now cycle through our list and add all the devices.
1202 This has the effect of addign in any in our DB,
1203 not specified in the device list (and thus will be
1204 tacked on at the end) */
1205 offset = idx;
1206 done = !pa_database_first(u->database, &key, NULL);
1207
1208 while (!done && idx < 256) {
1209 pa_datum next_key;
1210
1211 done = !pa_database_next(u->database, &key, &next_key, NULL);
1212
1213 device = pa_xnew(struct device_t, 1);
1214 device->device = pa_xstrndup(key.data, key.size);
1215 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1216 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1217
1218 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1219 if (pa_hashmap_put(h, device->device, device) == 0
1220 && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) {
1221 /* We add offset on to the existing priorirty so that when we order, the
1222 existing entries are always lower priority than the new ones. */
1223 device->prio = (offset + e->priority[role_index]);
1224 pa_xfree(e);
1225 }
1226 else {
1227 pa_xfree(device->device);
1228 pa_xfree(device);
1229 }
1230 } else {
1231 pa_xfree(device->device);
1232 pa_xfree(device);
1233 }
1234
1235 pa_datum_free(&key);
1236
1237 key = next_key;
1238 }
1239
1240 /*pa_log_debug("Hashmap contents (combined with database)");
1241 PA_HASHMAP_FOREACH(device, h, state) {
1242 pa_log_debug(" - %s (%d)", device->device, device->prio);
1243 }*/
1244
1245 /* Now we put all the entries in a simple list for sorting it. */
1246 n_devices = pa_hashmap_size(h);
1247 devices = pa_xnew(struct device_t *, n_devices);
1248 idx = 0;
1249 while ((device = pa_hashmap_steal_first(h))) {
1250 devices[idx++] = device;
1251 }
1252 pa_hashmap_free(h, NULL, NULL);
1253
1254 /* Simple bubble sort */
1255 for (i = 0; i < n_devices; ++i) {
1256 for (uint32_t j = i; j < n_devices; ++j) {
1257 if (devices[i]->prio > devices[j]->prio) {
1258 struct device_t *tmp;
1259 tmp = devices[i];
1260 devices[i] = devices[j];
1261 devices[j] = tmp;
1262 }
1263 }
1264 }
1265
1266 /*pa_log_debug("Sorted device list");
1267 for (i = 0; i < n_devices; ++i) {
1268 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1269 }*/
1270
1271 /* Go through in order and write the new entry and cleanup our own list */
1272 idx = 1;
1273 first = TRUE;
1274 for (i = 0; i < n_devices; ++i) {
1275 if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) {
1276 if (e->priority[role_index] == idx)
1277 idx++;
1278 else {
1279 e->priority[role_index] = idx;
1280
1281 key.data = (char *) devices[i]->device;
1282 key.size = strlen(devices[i]->device);
1283
1284 data.data = e;
1285 data.size = sizeof(*e);
1286
1287 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1288 first = FALSE;
1289 idx++;
1290 }
1291 }
1292
1293 pa_xfree(e);
1294 }
1295 pa_xfree(devices[i]->device);
1296 pa_xfree(devices[i]);
1297 }
1298
1299 if (!first) {
1300 trigger_save(u);
1301
1302 if (sink_mode)
1303 route_sink_inputs(u, NULL);
1304 else
1305 route_source_outputs(u, NULL);
1306 }
1307
1308 break;
1309 }
1310
1311 case SUBCOMMAND_SUBSCRIBE: {
1312
1313 pa_bool_t enabled;
1314
1315 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1316 !pa_tagstruct_eof(t))
1317 goto fail;
1318
1319 if (enabled)
1320 pa_idxset_put(u->subscribed, c, NULL);
1321 else
1322 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1323
1324 break;
1325 }
1326
1327 default:
1328 goto fail;
1329 }
1330
1331 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1332 return 0;
1333
1334 fail:
1335
1336 if (reply)
1337 pa_tagstruct_free(reply);
1338
1339 return -1;
1340 }
1341
1342 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1343 pa_assert(p);
1344 pa_assert(c);
1345 pa_assert(u);
1346
1347 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1348 return PA_HOOK_OK;
1349 }
1350
1351 int pa__init(pa_module*m) {
1352 pa_modargs *ma = NULL;
1353 struct userdata *u;
1354 char *fname;
1355 pa_sink *sink;
1356 pa_source *source;
1357 uint32_t idx;
1358 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1359
1360 pa_assert(m);
1361
1362 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1363 pa_log("Failed to parse module arguments");
1364 goto fail;
1365 }
1366
1367 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1368 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1369 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1370 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1371 goto fail;
1372 }
1373
1374 m->userdata = u = pa_xnew0(struct userdata, 1);
1375 u->core = m->core;
1376 u->module = m;
1377 u->do_routing = do_routing;
1378 u->on_hotplug = on_hotplug;
1379 u->on_rescue = on_rescue;
1380 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1381
1382 u->protocol = pa_native_protocol_get(m->core);
1383 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1384
1385 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);
1386
1387 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);
1388
1389 /* Used to handle device description management */
1390 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);
1391 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);
1392
1393 /* The following slots are used to deal with routing */
1394 /* A little bit later than module-stream-restore, module-intended-roles */
1395 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);
1396 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);
1397
1398 if (on_hotplug) {
1399 /* A little bit later than module-stream-restore, module-intended-roles */
1400 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);
1401 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);
1402 }
1403
1404 if (on_rescue) {
1405 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1406 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);
1407 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);
1408 }
1409
1410 if (!(fname = pa_state_path("device-manager", TRUE)))
1411 goto fail;
1412
1413 if (!(u->database = pa_database_open(fname, TRUE))) {
1414 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1415 pa_xfree(fname);
1416 goto fail;
1417 }
1418
1419 pa_log_info("Sucessfully opened database file '%s'.", fname);
1420 pa_xfree(fname);
1421
1422 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1423 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1424 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1425
1426 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1427 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1428
1429 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1430 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1431 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1432 }
1433
1434 route_sink_inputs(u, NULL);
1435 route_source_outputs(u, NULL);
1436
1437 #ifdef DUMP_DATABASE
1438 dump_database(u);
1439 #endif
1440
1441 pa_modargs_free(ma);
1442 return 0;
1443
1444 fail:
1445 pa__done(m);
1446
1447 if (ma)
1448 pa_modargs_free(ma);
1449
1450 return -1;
1451 }
1452
1453 void pa__done(pa_module*m) {
1454 struct userdata* u;
1455
1456 pa_assert(m);
1457
1458 if (!(u = m->userdata))
1459 return;
1460
1461 if (u->subscription)
1462 pa_subscription_free(u->subscription);
1463
1464 if (u->sink_new_hook_slot)
1465 pa_hook_slot_free(u->sink_new_hook_slot);
1466 if (u->source_new_hook_slot)
1467 pa_hook_slot_free(u->source_new_hook_slot);
1468
1469 if (u->sink_input_new_hook_slot)
1470 pa_hook_slot_free(u->sink_input_new_hook_slot);
1471 if (u->source_output_new_hook_slot)
1472 pa_hook_slot_free(u->source_output_new_hook_slot);
1473
1474 if (u->sink_put_hook_slot)
1475 pa_hook_slot_free(u->sink_put_hook_slot);
1476 if (u->source_put_hook_slot)
1477 pa_hook_slot_free(u->source_put_hook_slot);
1478
1479 if (u->sink_unlink_hook_slot)
1480 pa_hook_slot_free(u->sink_unlink_hook_slot);
1481 if (u->source_unlink_hook_slot)
1482 pa_hook_slot_free(u->source_unlink_hook_slot);
1483
1484 if (u->save_time_event)
1485 u->core->mainloop->time_free(u->save_time_event);
1486
1487 if (u->database)
1488 pa_database_close(u->database);
1489
1490 if (u->protocol) {
1491 pa_native_protocol_remove_ext(u->protocol, m);
1492 pa_native_protocol_unref(u->protocol);
1493 }
1494
1495 if (u->subscribed)
1496 pa_idxset_free(u->subscribed, NULL, NULL);
1497
1498 pa_xfree(u);
1499 }