]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Misc fixes.
[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 if (si->save_sink)
514 return;
515
516 /* Skip this if it is already in the process of being moved anyway */
517 if (!si->sink)
518 return;
519
520 /* It might happen that a stream and a sink are set up at the
521 same time, in which case we want to make sure we don't
522 interfere with that */
523 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
524 return;
525
526 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
527 role_index = get_role_index("none");
528 else
529 role_index = get_role_index(role);
530
531 if (PA_INVALID_INDEX == role_index)
532 return;
533
534 device_index = u->preferred_sinks[role_index];
535 if (PA_INVALID_INDEX == device_index)
536 return;
537
538 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
539 return;
540
541 if (si->sink != sink)
542 pa_sink_input_move_to(si, sink, TRUE);
543 }
544
545 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
546 pa_sink_input *si;
547 uint32_t idx;
548
549 pa_assert(u);
550
551 if (!u->do_routing)
552 return PA_HOOK_OK;
553
554 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
555
556 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
557 route_sink_input(u, si);
558 }
559
560 return PA_HOOK_OK;
561 }
562
563 static void route_source_output(struct userdata *u, pa_source_output *so) {
564 const char *role;
565 uint32_t role_index, device_index;
566 pa_source *source;
567
568 pa_assert(u);
569 pa_assert(u->do_routing);
570
571 if (so->save_source)
572 return;
573
574 if (so->direct_on_input)
575 return;
576
577 /* Skip this if it is already in the process of being moved anyway */
578 if (!so->source)
579 return;
580
581 /* It might happen that a stream and a source are set up at the
582 same time, in which case we want to make sure we don't
583 interfere with that */
584 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
585 return;
586
587 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
588 role_index = get_role_index("none");
589 else
590 role_index = get_role_index(role);
591
592 if (PA_INVALID_INDEX == role_index)
593 return;
594
595 device_index = u->preferred_sources[role_index];
596 if (PA_INVALID_INDEX == device_index)
597 return;
598
599 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
600 return;
601
602 if (so->source != source)
603 pa_source_output_move_to(so, source, TRUE);
604 }
605
606 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
607 pa_source_output *so;
608 uint32_t idx;
609
610 pa_assert(u);
611
612 if (!u->do_routing)
613 return PA_HOOK_OK;
614
615 update_highest_priority_device_indexes(u, "source:", ignore_source);
616
617 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
618 route_source_output(u, so);
619 }
620
621 return PA_HOOK_OK;
622 }
623
624 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
625 struct userdata *u = userdata;
626 struct entry entry, *old = NULL;
627 char *name = NULL;
628 pa_datum key, data;
629
630 pa_assert(c);
631 pa_assert(u);
632
633 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
634 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
635 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
636 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
637
638 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
639 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
640 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
641 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
642 return;
643
644 pa_zero(entry);
645 entry.version = ENTRY_VERSION;
646
647 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
648 pa_sink_input *si;
649
650 if (!u->do_routing)
651 return;
652 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
653 return;
654
655 /* The role may change mid-stream, so we reroute */
656 route_sink_input(u, si);
657
658 return;
659 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
660 pa_source_output *so;
661
662 if (!u->do_routing)
663 return;
664 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
665 return;
666
667 /* The role may change mid-stream, so we reroute */
668 route_source_output(u, so);
669
670 return;
671 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
672 pa_sink *sink;
673
674 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
675 return;
676
677 name = pa_sprintf_malloc("sink:%s", sink->name);
678
679 old = load_or_initialize_entry(u, &entry, name, "sink:");
680
681 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
682 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
683
684 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
685 pa_source *source;
686
687 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
688
689 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
690 return;
691
692 if (source->monitor_of)
693 return;
694
695 name = pa_sprintf_malloc("source:%s", source->name);
696
697 old = load_or_initialize_entry(u, &entry, name, "source:");
698
699 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
700 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
701 }
702
703 pa_assert(name);
704
705 if (old) {
706
707 if (entries_equal(old, &entry)) {
708 pa_xfree(old);
709 pa_xfree(name);
710
711 return;
712 }
713
714 pa_xfree(old);
715 }
716
717 key.data = name;
718 key.size = strlen(name);
719
720 data.data = &entry;
721 data.size = sizeof(entry);
722
723 pa_log_info("Storing device %s.", name);
724
725 pa_database_set(u->database, &key, &data, TRUE);
726
727 pa_xfree(name);
728
729 trigger_save(u);
730 }
731
732 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
733 char *name;
734 struct entry *e;
735
736 pa_assert(c);
737 pa_assert(new_data);
738 pa_assert(u);
739
740 name = pa_sprintf_malloc("sink:%s", new_data->name);
741
742 if ((e = read_entry(u, name))) {
743 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
744 pa_log_info("Restoring description for sink %s.", new_data->name);
745 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
746 }
747
748 pa_xfree(e);
749 }
750
751 pa_xfree(name);
752
753 return PA_HOOK_OK;
754 }
755
756 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
757 char *name;
758 struct entry *e;
759
760 pa_assert(c);
761 pa_assert(new_data);
762 pa_assert(u);
763
764 name = pa_sprintf_malloc("source:%s", new_data->name);
765
766 if ((e = read_entry(u, name))) {
767 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
768 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
769 pa_log_info("Restoring description for source %s.", new_data->name);
770 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
771 }
772
773 pa_xfree(e);
774 }
775
776 pa_xfree(name);
777
778 return PA_HOOK_OK;
779 }
780
781 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
782 pa_assert(c);
783 pa_assert(new_data);
784 pa_assert(u);
785
786 if (!u->do_routing)
787 return PA_HOOK_OK;
788
789 if (new_data->sink)
790 pa_log_debug("Not restoring device for stream, because already set.");
791 else {
792 const char *role;
793 uint32_t role_index;
794
795 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
796 role_index = get_role_index("");
797 else
798 role_index = get_role_index(role);
799
800 if (PA_INVALID_INDEX != role_index) {
801 uint32_t device_index;
802
803 device_index = u->preferred_sinks[role_index];
804 if (PA_INVALID_INDEX != device_index) {
805 pa_sink *sink;
806
807 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
808 new_data->sink = sink;
809 new_data->save_sink = TRUE;
810 }
811 }
812 }
813 }
814
815 return PA_HOOK_OK;
816 }
817
818 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
819 pa_assert(c);
820 pa_assert(new_data);
821 pa_assert(u);
822
823 if (!u->do_routing)
824 return PA_HOOK_OK;
825
826 if (new_data->direct_on_input)
827 return PA_HOOK_OK;
828
829 if (new_data->source)
830 pa_log_debug("Not restoring device for stream, because already set");
831 else {
832 const char *role;
833 uint32_t role_index;
834
835 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
836 role_index = get_role_index("");
837 else
838 role_index = get_role_index(role);
839
840 if (PA_INVALID_INDEX != role_index) {
841 uint32_t device_index;
842
843 device_index = u->preferred_sources[role_index];
844 if (PA_INVALID_INDEX != device_index) {
845 pa_source *source;
846
847 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
848 new_data->source = source;
849 new_data->save_source = TRUE;
850 }
851 }
852 }
853 }
854
855 return PA_HOOK_OK;
856 }
857
858
859 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
860 pa_assert(c);
861 pa_assert(u);
862 pa_assert(u->core == c);
863 pa_assert(u->on_hotplug);
864
865 notify_subscribers(u);
866
867 return route_sink_inputs(u, NULL);
868 }
869
870 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
871 pa_assert(c);
872 pa_assert(u);
873 pa_assert(u->core == c);
874 pa_assert(u->on_hotplug);
875
876 notify_subscribers(u);
877
878 return route_source_outputs(u, NULL);
879 }
880
881 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
882 pa_assert(c);
883 pa_assert(sink);
884 pa_assert(u);
885 pa_assert(u->core == c);
886 pa_assert(u->on_rescue);
887
888 /* There's no point in doing anything if the core is shut down anyway */
889 if (c->state == PA_CORE_SHUTDOWN)
890 return PA_HOOK_OK;
891
892 notify_subscribers(u);
893
894 return route_sink_inputs(u, sink);
895 }
896
897 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
898 pa_assert(c);
899 pa_assert(source);
900 pa_assert(u);
901 pa_assert(u->core == c);
902 pa_assert(u->on_rescue);
903
904 /* There's no point in doing anything if the core is shut down anyway */
905 if (c->state == PA_CORE_SHUTDOWN)
906 return PA_HOOK_OK;
907
908 notify_subscribers(u);
909
910 return route_source_outputs(u, source);
911 }
912
913
914 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
915 pa_sink *sink;
916 pa_source *source;
917 uint32_t idx;
918 char *n;
919
920 pa_assert(u);
921 pa_assert(name);
922 pa_assert(e);
923
924 if ((n = get_name(name, "sink:"))) {
925 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
926 if (!pa_streq(sink->name, n)) {
927 continue;
928 }
929
930 pa_log_info("Setting description for sink %s.", sink->name);
931 pa_sink_set_description(sink, e->description);
932 }
933 pa_xfree(n);
934 }
935 else if ((n = get_name(name, "source:"))) {
936 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
937 if (!pa_streq(source->name, n)) {
938 continue;
939 }
940
941 if (source->monitor_of) {
942 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
943 continue;
944 }
945
946 pa_log_info("Setting description for source %s.", source->name);
947 pa_source_set_description(source, e->description);
948 }
949 pa_xfree(n);
950 }
951 }
952
953
954 #define EXT_VERSION 1
955
956 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
957 struct userdata *u;
958 uint32_t command;
959 pa_tagstruct *reply = NULL;
960
961 pa_assert(p);
962 pa_assert(m);
963 pa_assert(c);
964 pa_assert(t);
965
966 u = m->userdata;
967
968 if (pa_tagstruct_getu32(t, &command) < 0)
969 goto fail;
970
971 reply = pa_tagstruct_new(NULL, 0);
972 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
973 pa_tagstruct_putu32(reply, tag);
974
975 switch (command) {
976 case SUBCOMMAND_TEST: {
977 if (!pa_tagstruct_eof(t))
978 goto fail;
979
980 pa_tagstruct_putu32(reply, EXT_VERSION);
981 break;
982 }
983
984 case SUBCOMMAND_READ: {
985 pa_datum key;
986 pa_bool_t done;
987
988 if (!pa_tagstruct_eof(t))
989 goto fail;
990
991 done = !pa_database_first(u->database, &key, NULL);
992
993 while (!done) {
994 pa_datum next_key;
995 struct entry *e;
996 char *name;
997
998 done = !pa_database_next(u->database, &key, &next_key, NULL);
999
1000 name = pa_xstrndup(key.data, key.size);
1001 pa_datum_free(&key);
1002
1003 if ((e = read_entry(u, name))) {
1004 uint32_t idx;
1005 char *devname;
1006 pa_bool_t available = FALSE;
1007
1008 if ((devname = get_name(name, "sink:"))) {
1009 pa_sink* s;
1010 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1011 if (strcmp(s->name, devname) == 0) {
1012 available = TRUE;
1013 break;
1014 }
1015 }
1016 pa_xfree(devname);
1017 } else if ((devname = get_name(name, "source:"))) {
1018 pa_source* s;
1019 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1020 if (strcmp(s->name, devname) == 0) {
1021 available = TRUE;
1022 break;
1023 }
1024 }
1025 pa_xfree(devname);
1026 }
1027
1028 pa_tagstruct_puts(reply, name);
1029 pa_tagstruct_puts(reply, e->description);
1030 pa_tagstruct_puts(reply, e->icon);
1031 pa_tagstruct_put_boolean(reply, available);
1032 pa_tagstruct_putu32(reply, NUM_ROLES);
1033
1034 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1035 pa_tagstruct_puts(reply, role_names[i]);
1036 pa_tagstruct_putu32(reply, e->priority[i]);
1037 }
1038
1039 pa_xfree(e);
1040 }
1041
1042 pa_xfree(name);
1043
1044 key = next_key;
1045 }
1046
1047 break;
1048 }
1049
1050 case SUBCOMMAND_RENAME: {
1051
1052 struct entry *e;
1053 const char *device, *description;
1054
1055 if (pa_tagstruct_gets(t, &device) < 0 ||
1056 pa_tagstruct_gets(t, &description) < 0)
1057 goto fail;
1058
1059 if (!device || !*device || !description || !*description)
1060 goto fail;
1061
1062 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
1063 pa_datum key, data;
1064
1065 pa_strlcpy(e->description, description, sizeof(e->description));
1066
1067 key.data = (char *) device;
1068 key.size = strlen(device);
1069
1070 data.data = e;
1071 data.size = sizeof(*e);
1072
1073 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1074 apply_entry(u, device, e);
1075
1076 trigger_save(u);
1077 }
1078 else
1079 pa_log_warn("Could not save device");
1080
1081 pa_xfree(e);
1082 }
1083 else
1084 pa_log_warn("Could not rename device %s, no entry in database", device);
1085
1086 break;
1087 }
1088
1089 case SUBCOMMAND_DELETE:
1090
1091 while (!pa_tagstruct_eof(t)) {
1092 const char *name;
1093 pa_datum key;
1094
1095 if (pa_tagstruct_gets(t, &name) < 0)
1096 goto fail;
1097
1098 key.data = (char*) name;
1099 key.size = strlen(name);
1100
1101 /** @todo: Reindex the priorities */
1102 pa_database_unset(u->database, &key);
1103 }
1104
1105 trigger_save(u);
1106
1107 break;
1108
1109 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1110
1111 pa_bool_t enable;
1112
1113 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1114 goto fail;
1115
1116 if ((u->do_routing = enable)) {
1117 /* Update our caches */
1118 update_highest_priority_device_indexes(u, "sink:", NULL);
1119 update_highest_priority_device_indexes(u, "source:", NULL);
1120 }
1121
1122 break;
1123 }
1124
1125 case SUBCOMMAND_REORDER: {
1126
1127 const char *role;
1128 struct entry *e;
1129 uint32_t role_index, n_devices;
1130 pa_datum key, data;
1131 pa_bool_t done, sink_mode = TRUE;
1132 struct device_t { uint32_t prio; char *device; };
1133 struct device_t *device;
1134 struct device_t **devices;
1135 uint32_t i, idx, offset;
1136 pa_hashmap *h;
1137 /*void *state;*/
1138 pa_bool_t first;
1139
1140 if (pa_tagstruct_gets(t, &role) < 0 ||
1141 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1142 n_devices < 1)
1143 goto fail;
1144
1145 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1146 goto fail;
1147
1148 /* Cycle through the devices given and make sure they exist */
1149 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1150 first = TRUE;
1151 idx = 0;
1152 for (i = 0; i < n_devices; ++i) {
1153 const char *s;
1154 if (pa_tagstruct_gets(t, &s) < 0) {
1155 while ((device = pa_hashmap_steal_first(h))) {
1156 pa_xfree(device->device);
1157 pa_xfree(device);
1158 }
1159
1160 pa_hashmap_free(h, NULL, NULL);
1161 pa_log_error("Protocol error on reorder");
1162 goto fail;
1163 }
1164
1165 /* Ensure this is a valid entry */
1166 if (!(e = read_entry(u, s))) {
1167 while ((device = pa_hashmap_steal_first(h))) {
1168 pa_xfree(device->device);
1169 pa_xfree(device);
1170 }
1171
1172 pa_hashmap_free(h, NULL, NULL);
1173 pa_log_error("Client specified an unknown device in it's reorder list.");
1174 goto fail;
1175 }
1176 pa_xfree(e);
1177
1178 if (first) {
1179 first = FALSE;
1180 sink_mode = (0 == strncmp("sink:", s, 5));
1181 } else if ((sink_mode && 0 != strncmp("sink:", s, 5))
1182 || (!sink_mode && 0 != strncmp("source:", s, 7)))
1183 {
1184 while ((device = pa_hashmap_steal_first(h))) {
1185 pa_xfree(device->device);
1186 pa_xfree(device);
1187 }
1188
1189 pa_hashmap_free(h, NULL, NULL);
1190 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1191 goto fail;
1192 }
1193
1194 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1195 device = pa_xnew(struct device_t, 1);
1196 device->device = pa_xstrdup(s);
1197 if (pa_hashmap_put(h, device->device, device) == 0) {
1198 device->prio = idx;
1199 idx++;
1200 } else {
1201 pa_xfree(device->device);
1202 pa_xfree(device);
1203 }
1204 }
1205
1206 /*pa_log_debug("Hashmap contents (received from client)");
1207 PA_HASHMAP_FOREACH(device, h, state) {
1208 pa_log_debug(" - %s (%d)", device->device, device->prio);
1209 }*/
1210
1211 /* Now cycle through our list and add all the devices.
1212 This has the effect of addign in any in our DB,
1213 not specified in the device list (and thus will be
1214 tacked on at the end) */
1215 offset = idx;
1216 done = !pa_database_first(u->database, &key, NULL);
1217
1218 while (!done && idx < 256) {
1219 pa_datum next_key;
1220
1221 done = !pa_database_next(u->database, &key, &next_key, NULL);
1222
1223 device = pa_xnew(struct device_t, 1);
1224 device->device = pa_xstrndup(key.data, key.size);
1225 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1226 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1227
1228 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1229 if (pa_hashmap_put(h, device->device, device) == 0
1230 && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) {
1231 /* We add offset on to the existing priorirty so that when we order, the
1232 existing entries are always lower priority than the new ones. */
1233 device->prio = (offset + e->priority[role_index]);
1234 pa_xfree(e);
1235 }
1236 else {
1237 pa_xfree(device->device);
1238 pa_xfree(device);
1239 }
1240 } else {
1241 pa_xfree(device->device);
1242 pa_xfree(device);
1243 }
1244
1245 pa_datum_free(&key);
1246
1247 key = next_key;
1248 }
1249
1250 /*pa_log_debug("Hashmap contents (combined with database)");
1251 PA_HASHMAP_FOREACH(device, h, state) {
1252 pa_log_debug(" - %s (%d)", device->device, device->prio);
1253 }*/
1254
1255 /* Now we put all the entries in a simple list for sorting it. */
1256 n_devices = pa_hashmap_size(h);
1257 devices = pa_xnew(struct device_t *, n_devices);
1258 idx = 0;
1259 while ((device = pa_hashmap_steal_first(h))) {
1260 devices[idx++] = device;
1261 }
1262 pa_hashmap_free(h, NULL, NULL);
1263
1264 /* Simple bubble sort */
1265 for (i = 0; i < n_devices; ++i) {
1266 for (uint32_t j = i; j < n_devices; ++j) {
1267 if (devices[i]->prio > devices[j]->prio) {
1268 struct device_t *tmp;
1269 tmp = devices[i];
1270 devices[i] = devices[j];
1271 devices[j] = tmp;
1272 }
1273 }
1274 }
1275
1276 /*pa_log_debug("Sorted device list");
1277 for (i = 0; i < n_devices; ++i) {
1278 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1279 }*/
1280
1281 /* Go through in order and write the new entry and cleanup our own list */
1282 idx = 1;
1283 first = TRUE;
1284 for (i = 0; i < n_devices; ++i) {
1285 if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) {
1286 if (e->priority[role_index] == idx)
1287 idx++;
1288 else {
1289 e->priority[role_index] = idx;
1290
1291 key.data = (char *) devices[i]->device;
1292 key.size = strlen(devices[i]->device);
1293
1294 data.data = e;
1295 data.size = sizeof(*e);
1296
1297 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1298 first = FALSE;
1299 idx++;
1300 }
1301 }
1302
1303 pa_xfree(e);
1304 }
1305 pa_xfree(devices[i]->device);
1306 pa_xfree(devices[i]);
1307 }
1308
1309 if (!first) {
1310 trigger_save(u);
1311
1312 if (sink_mode)
1313 route_sink_inputs(u, NULL);
1314 else
1315 route_source_outputs(u, NULL);
1316 }
1317
1318 break;
1319 }
1320
1321 case SUBCOMMAND_SUBSCRIBE: {
1322
1323 pa_bool_t enabled;
1324
1325 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1326 !pa_tagstruct_eof(t))
1327 goto fail;
1328
1329 if (enabled)
1330 pa_idxset_put(u->subscribed, c, NULL);
1331 else
1332 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1333
1334 break;
1335 }
1336
1337 default:
1338 goto fail;
1339 }
1340
1341 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1342 return 0;
1343
1344 fail:
1345
1346 if (reply)
1347 pa_tagstruct_free(reply);
1348
1349 return -1;
1350 }
1351
1352 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1353 pa_assert(p);
1354 pa_assert(c);
1355 pa_assert(u);
1356
1357 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1358 return PA_HOOK_OK;
1359 }
1360
1361 int pa__init(pa_module*m) {
1362 pa_modargs *ma = NULL;
1363 struct userdata *u;
1364 char *fname;
1365 pa_sink *sink;
1366 pa_source *source;
1367 uint32_t idx;
1368 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1369
1370 pa_assert(m);
1371
1372 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1373 pa_log("Failed to parse module arguments");
1374 goto fail;
1375 }
1376
1377 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1378 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1379 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1380 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1381 goto fail;
1382 }
1383
1384 m->userdata = u = pa_xnew0(struct userdata, 1);
1385 u->core = m->core;
1386 u->module = m;
1387 u->do_routing = do_routing;
1388 u->on_hotplug = on_hotplug;
1389 u->on_rescue = on_rescue;
1390 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1391
1392 u->protocol = pa_native_protocol_get(m->core);
1393 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1394
1395 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);
1396
1397 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);
1398
1399 /* Used to handle device description management */
1400 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);
1401 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);
1402
1403 /* The following slots are used to deal with routing */
1404 /* A little bit later than module-stream-restore, module-intended-roles */
1405 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);
1406 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);
1407
1408 if (on_hotplug) {
1409 /* A little bit later than module-stream-restore, module-intended-roles */
1410 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);
1411 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);
1412 }
1413
1414 if (on_rescue) {
1415 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1416 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);
1417 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);
1418 }
1419
1420 if (!(fname = pa_state_path("device-manager", TRUE)))
1421 goto fail;
1422
1423 if (!(u->database = pa_database_open(fname, TRUE))) {
1424 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1425 pa_xfree(fname);
1426 goto fail;
1427 }
1428
1429 pa_log_info("Sucessfully opened database file '%s'.", fname);
1430 pa_xfree(fname);
1431
1432 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1433 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1434 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1435
1436 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1437 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1438
1439 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1440 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1441 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1442 }
1443
1444 route_sink_inputs(u, NULL);
1445 route_source_outputs(u, NULL);
1446
1447 #ifdef DUMP_DATABASE
1448 dump_database(u);
1449 #endif
1450
1451 pa_modargs_free(ma);
1452 return 0;
1453
1454 fail:
1455 pa__done(m);
1456
1457 if (ma)
1458 pa_modargs_free(ma);
1459
1460 return -1;
1461 }
1462
1463 void pa__done(pa_module*m) {
1464 struct userdata* u;
1465
1466 pa_assert(m);
1467
1468 if (!(u = m->userdata))
1469 return;
1470
1471 if (u->subscription)
1472 pa_subscription_free(u->subscription);
1473
1474 if (u->sink_new_hook_slot)
1475 pa_hook_slot_free(u->sink_new_hook_slot);
1476 if (u->source_new_hook_slot)
1477 pa_hook_slot_free(u->source_new_hook_slot);
1478
1479 if (u->sink_input_new_hook_slot)
1480 pa_hook_slot_free(u->sink_input_new_hook_slot);
1481 if (u->source_output_new_hook_slot)
1482 pa_hook_slot_free(u->source_output_new_hook_slot);
1483
1484 if (u->sink_put_hook_slot)
1485 pa_hook_slot_free(u->sink_put_hook_slot);
1486 if (u->source_put_hook_slot)
1487 pa_hook_slot_free(u->source_put_hook_slot);
1488
1489 if (u->sink_unlink_hook_slot)
1490 pa_hook_slot_free(u->sink_unlink_hook_slot);
1491 if (u->source_unlink_hook_slot)
1492 pa_hook_slot_free(u->source_unlink_hook_slot);
1493
1494 if (u->save_time_event)
1495 u->core->mainloop->time_free(u->save_time_event);
1496
1497 if (u->database)
1498 pa_database_close(u->database);
1499
1500 if (u->protocol) {
1501 pa_native_protocol_remove_ext(u->protocol, m);
1502 pa_native_protocol_unref(u->protocol);
1503 }
1504
1505 if (u->subscribed)
1506 pa_idxset_free(u->subscribed, NULL, NULL);
1507
1508 pa_xfree(u);
1509 }