]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
d86c158d1c6c5eeed7df6c619e9df628aca8bc79
[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
34 #include <pulse/gccmacro.h>
35 #include <pulse/xmalloc.h>
36 #include <pulse/timeval.h>
37 #include <pulse/rtclock.h>
38
39 #include <pulsecore/core-error.h>
40 #include <pulsecore/module.h>
41 #include <pulsecore/core-util.h>
42 #include <pulsecore/modargs.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/core-subscribe.h>
45 #include <pulsecore/sink-input.h>
46 #include <pulsecore/source-output.h>
47 #include <pulsecore/namereg.h>
48 #include <pulsecore/protocol-native.h>
49 #include <pulsecore/pstream.h>
50 #include <pulsecore/pstream-util.h>
51 #include <pulsecore/database.h>
52 #include <pulsecore/tagstruct.h>
53
54 #include "module-device-manager-symdef.h"
55
56 PA_MODULE_AUTHOR("Colin Guthrie");
57 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
58 PA_MODULE_VERSION(PACKAGE_VERSION);
59 PA_MODULE_LOAD_ONCE(true);
60 PA_MODULE_USAGE(
61 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
62 "on_hotplug=<When new device becomes available, recheck streams?> "
63 "on_rescue=<When device becomes unavailable, recheck streams?>");
64
65 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
66 #define DUMP_DATABASE
67
68 static const char* const valid_modargs[] = {
69 "do_routing",
70 "on_hotplug",
71 "on_rescue",
72 NULL
73 };
74
75 #define NUM_ROLES 9
76 enum {
77 ROLE_NONE,
78 ROLE_VIDEO,
79 ROLE_MUSIC,
80 ROLE_GAME,
81 ROLE_EVENT,
82 ROLE_PHONE,
83 ROLE_ANIMATION,
84 ROLE_PRODUCTION,
85 ROLE_A11Y,
86 ROLE_MAX
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 bool on_hotplug;
124 bool on_rescue;
125 bool 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;
136 bool user_set_description;
137 char *icon;
138 role_indexes_t priority;
139 };
140
141 enum {
142 SUBCOMMAND_TEST,
143 SUBCOMMAND_READ,
144 SUBCOMMAND_RENAME,
145 SUBCOMMAND_DELETE,
146 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
147 SUBCOMMAND_REORDER,
148 SUBCOMMAND_SUBSCRIBE,
149 SUBCOMMAND_EVENT
150 };
151
152 /* Forward declarations */
153 #ifdef DUMP_DATABASE
154 static void dump_database(struct userdata *);
155 #endif
156 static void notify_subscribers(struct userdata *);
157
158 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
159 struct userdata *u = userdata;
160
161 pa_assert(a);
162 pa_assert(e);
163 pa_assert(u);
164
165 pa_assert(e == u->save_time_event);
166 u->core->mainloop->time_free(u->save_time_event);
167 u->save_time_event = NULL;
168
169 pa_database_sync(u->database);
170 pa_log_info("Synced.");
171
172 #ifdef DUMP_DATABASE
173 dump_database(u);
174 #endif
175 }
176
177 static void trigger_save(struct userdata *u) {
178
179 pa_assert(u);
180
181 notify_subscribers(u);
182
183 if (u->save_time_event)
184 return;
185
186 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
187 }
188
189 static struct entry* entry_new(void) {
190 struct entry *r = pa_xnew0(struct entry, 1);
191 r->version = ENTRY_VERSION;
192 return r;
193 }
194
195 static void entry_free(struct entry* e) {
196 pa_assert(e);
197
198 pa_xfree(e->description);
199 pa_xfree(e->icon);
200 pa_xfree(e);
201 }
202
203 static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
204 pa_tagstruct *t;
205 pa_datum key, data;
206 bool r;
207
208 pa_assert(u);
209 pa_assert(name);
210 pa_assert(e);
211
212 t = pa_tagstruct_new(NULL, 0);
213 pa_tagstruct_putu8(t, e->version);
214 pa_tagstruct_puts(t, e->description);
215 pa_tagstruct_put_boolean(t, e->user_set_description);
216 pa_tagstruct_puts(t, e->icon);
217 for (uint8_t i=0; i<ROLE_MAX; ++i)
218 pa_tagstruct_putu32(t, e->priority[i]);
219
220 key.data = (char *) name;
221 key.size = strlen(name);
222
223 data.data = (void*)pa_tagstruct_data(t, &data.size);
224
225 r = (pa_database_set(u->database, &key, &data, true) == 0);
226
227 pa_tagstruct_free(t);
228
229 return r;
230 }
231
232 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
233
234 #define LEGACY_ENTRY_VERSION 1
235 static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
236 struct legacy_entry {
237 uint8_t version;
238 char description[PA_NAME_MAX];
239 bool user_set_description;
240 char icon[PA_NAME_MAX];
241 role_indexes_t priority;
242 } PA_GCC_PACKED;
243 struct legacy_entry *le;
244 struct entry *e;
245
246 pa_assert(u);
247 pa_assert(data);
248
249 if (data->size != sizeof(struct legacy_entry)) {
250 pa_log_debug("Size does not match.");
251 return NULL;
252 }
253
254 le = (struct legacy_entry*)data->data;
255
256 if (le->version != LEGACY_ENTRY_VERSION) {
257 pa_log_debug("Version mismatch.");
258 return NULL;
259 }
260
261 if (!memchr(le->description, 0, sizeof(le->description))) {
262 pa_log_warn("Description has missing NUL byte.");
263 return NULL;
264 }
265
266 if (!le->description[0]) {
267 pa_log_warn("Description is empty.");
268 return NULL;
269 }
270
271 if (!memchr(le->icon, 0, sizeof(le->icon))) {
272 pa_log_warn("Icon has missing NUL byte.");
273 return NULL;
274 }
275
276 e = entry_new();
277 e->description = pa_xstrdup(le->description);
278 e->icon = pa_xstrdup(le->icon);
279 return e;
280 }
281 #endif
282
283 static struct entry* entry_read(struct userdata *u, const char *name) {
284 pa_datum key, data;
285 struct entry *e = NULL;
286 pa_tagstruct *t = NULL;
287 const char *description, *icon;
288
289 pa_assert(u);
290 pa_assert(name);
291
292 key.data = (char*) name;
293 key.size = strlen(name);
294
295 pa_zero(data);
296
297 if (!pa_database_get(u->database, &key, &data))
298 goto fail;
299
300 t = pa_tagstruct_new(data.data, data.size);
301 e = entry_new();
302
303 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
304 e->version > ENTRY_VERSION ||
305 pa_tagstruct_gets(t, &description) < 0 ||
306 pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
307 pa_tagstruct_gets(t, &icon) < 0) {
308
309 goto fail;
310 }
311
312 if (e->user_set_description && !description) {
313 pa_log("Entry has user_set_description set, but the description is NULL.");
314 goto fail;
315 }
316
317 if (e->user_set_description && !*description) {
318 pa_log("Entry has user_set_description set, but the description is empty.");
319 goto fail;
320 }
321
322 e->description = pa_xstrdup(description);
323 e->icon = pa_xstrdup(icon);
324
325 for (uint8_t i=0; i<ROLE_MAX; ++i) {
326 if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
327 goto fail;
328 }
329
330 if (!pa_tagstruct_eof(t))
331 goto fail;
332
333 pa_tagstruct_free(t);
334 pa_datum_free(&data);
335
336 return e;
337
338 fail:
339 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
340
341 if (e)
342 entry_free(e);
343 if (t)
344 pa_tagstruct_free(t);
345
346 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
347 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
348 if ((e = legacy_entry_read(u, &data))) {
349 pa_log_debug("Success. Saving new format for key: %s", name);
350 if (entry_write(u, name, e))
351 trigger_save(u);
352 pa_datum_free(&data);
353 return e;
354 } else
355 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
356 #endif
357
358 pa_datum_free(&data);
359 return NULL;
360 }
361
362 #ifdef DUMP_DATABASE
363 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, bool sink_mode) {
364 pa_assert(u);
365 pa_assert(human);
366
367 if (sink_mode) {
368 pa_sink *s;
369 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
370 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
371 else
372 pa_log_debug(" %s No sink specified", human);
373 } else {
374 pa_source *s;
375 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
376 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
377 else
378 pa_log_debug(" %s No source specified", human);
379 }
380 }
381
382 static void dump_database(struct userdata *u) {
383 pa_datum key;
384 bool done;
385
386 pa_assert(u);
387
388 done = !pa_database_first(u->database, &key, NULL);
389
390 pa_log_debug("Dumping database");
391 while (!done) {
392 char *name;
393 struct entry *e;
394 pa_datum next_key;
395
396 done = !pa_database_next(u->database, &key, &next_key, NULL);
397
398 name = pa_xstrndup(key.data, key.size);
399
400 if ((e = entry_read(u, name))) {
401 pa_log_debug(" Got entry: %s", name);
402 pa_log_debug(" Description: %s", e->description);
403 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
404 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
405 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
406 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
407 entry_free(e);
408 }
409
410 pa_xfree(name);
411
412 pa_datum_free(&key);
413 key = next_key;
414 }
415
416 if (u->do_routing) {
417 pa_log_debug(" Highest priority devices per-role:");
418
419 pa_log_debug(" Sinks:");
420 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
421 char name[13];
422 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
423 strncpy(name, role_names[role], len);
424 for (int i = len+1; i < 12; ++i) name[i] = ' ';
425 name[len] = ':'; name[0] -= 32; name[12] = '\0';
426 dump_database_helper(u, role, name, true);
427 }
428
429 pa_log_debug(" Sources:");
430 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
431 char name[13];
432 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
433 strncpy(name, role_names[role], len);
434 for (int i = len+1; i < 12; ++i) name[i] = ' ';
435 name[len] = ':'; name[0] -= 32; name[12] = '\0';
436 dump_database_helper(u, role, name, false);
437 }
438 }
439
440 pa_log_debug("Completed database dump");
441 }
442 #endif
443
444 static void notify_subscribers(struct userdata *u) {
445
446 pa_native_connection *c;
447 uint32_t idx;
448
449 pa_assert(u);
450
451 PA_IDXSET_FOREACH(c, u->subscribed, idx) {
452 pa_tagstruct *t;
453
454 t = pa_tagstruct_new(NULL, 0);
455 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
456 pa_tagstruct_putu32(t, 0);
457 pa_tagstruct_putu32(t, u->module->index);
458 pa_tagstruct_puts(t, u->module->name);
459 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
460
461 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
462 }
463 }
464
465 static bool entries_equal(const struct entry *a, const struct entry *b) {
466
467 pa_assert(a);
468 pa_assert(b);
469
470 if (!pa_streq(a->description, b->description)
471 || a->user_set_description != b->user_set_description
472 || !pa_streq(a->icon, b->icon))
473 return false;
474
475 for (int i=0; i < NUM_ROLES; ++i)
476 if (a->priority[i] != b->priority[i])
477 return false;
478
479 return true;
480 }
481
482 static char *get_name(const char *key, const char *prefix) {
483 char *t;
484
485 if (strncmp(key, prefix, strlen(prefix)))
486 return NULL;
487
488 t = pa_xstrdup(key + strlen(prefix));
489 return t;
490 }
491
492 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
493 struct entry *old;
494
495 pa_assert(u);
496 pa_assert(entry);
497 pa_assert(name);
498 pa_assert(prefix);
499
500 if ((old = entry_read(u, name))) {
501 *entry = *old;
502 entry->description = pa_xstrdup(old->description);
503 entry->icon = pa_xstrdup(old->icon);
504 } else {
505 /* This is a new device, so make sure we write it's priority list correctly */
506 role_indexes_t max_priority;
507 pa_datum key;
508 bool done;
509
510 pa_zero(max_priority);
511 done = !pa_database_first(u->database, &key, NULL);
512
513 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
514 while (!done) {
515 pa_datum next_key;
516
517 done = !pa_database_next(u->database, &key, &next_key, NULL);
518
519 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
520 char *name2;
521 struct entry *e;
522
523 name2 = pa_xstrndup(key.data, key.size);
524
525 if ((e = entry_read(u, name2))) {
526 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
527 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
528 }
529
530 entry_free(e);
531 }
532
533 pa_xfree(name2);
534 }
535 pa_datum_free(&key);
536 key = next_key;
537 }
538
539 /* Actually initialise our entry now we've calculated it */
540 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
541 entry->priority[i] = max_priority[i] + 1;
542 }
543 entry->user_set_description = false;
544 }
545
546 return old;
547 }
548
549 static uint32_t get_role_index(const char* role) {
550 pa_assert(role);
551
552 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
553 if (pa_streq(role, role_names[i]))
554 return i;
555
556 return PA_INVALID_INDEX;
557 }
558
559 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
560 role_indexes_t *indexes, highest_priority_available;
561 pa_datum key;
562 bool done, sink_mode;
563
564 pa_assert(u);
565 pa_assert(prefix);
566
567 sink_mode = pa_streq(prefix, "sink:");
568
569 if (sink_mode)
570 indexes = &u->preferred_sinks;
571 else
572 indexes = &u->preferred_sources;
573
574 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
575 (*indexes)[i] = PA_INVALID_INDEX;
576 }
577 pa_zero(highest_priority_available);
578
579 done = !pa_database_first(u->database, &key, NULL);
580
581 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
582 while (!done) {
583 pa_datum next_key;
584
585 done = !pa_database_next(u->database, &key, &next_key, NULL);
586
587 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
588 char *name, *device_name;
589 struct entry *e;
590
591 name = pa_xstrndup(key.data, key.size);
592 pa_assert_se(device_name = get_name(name, prefix));
593
594 if ((e = entry_read(u, name))) {
595 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
596 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
597 /* We've found a device with a higher priority than that we've currently got,
598 so see if it is currently available or not and update our list */
599 uint32_t idx;
600 bool found = false;
601
602 if (sink_mode) {
603 pa_sink *sink;
604
605 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
606 if ((pa_sink*) ignore_device == sink)
607 continue;
608 if (!PA_SINK_IS_LINKED(sink->state))
609 continue;
610 if (pa_streq(sink->name, device_name)) {
611 found = true;
612 idx = sink->index; /* Is this needed? */
613 break;
614 }
615 }
616 } else {
617 pa_source *source;
618
619 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
620 if ((pa_source*) ignore_device == source)
621 continue;
622 if (!PA_SOURCE_IS_LINKED(source->state))
623 continue;
624 if (pa_streq(source->name, device_name)) {
625 found = true;
626 idx = source->index; /* Is this needed? */
627 break;
628 }
629 }
630 }
631 if (found) {
632 highest_priority_available[i] = e->priority[i];
633 (*indexes)[i] = idx;
634 }
635
636 }
637 }
638
639 entry_free(e);
640 }
641
642 pa_xfree(name);
643 pa_xfree(device_name);
644 }
645
646 pa_datum_free(&key);
647 key = next_key;
648 }
649 }
650
651 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
652 const char *role;
653 uint32_t role_index, device_index;
654 pa_sink *sink;
655
656 pa_assert(u);
657 pa_assert(u->do_routing);
658
659 if (si->save_sink)
660 return;
661
662 /* Skip this if it is already in the process of being moved anyway */
663 if (!si->sink)
664 return;
665
666 /* It might happen that a stream and a sink are set up at the
667 same time, in which case we want to make sure we don't
668 interfere with that */
669 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
670 return;
671
672 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
673 role_index = get_role_index("none");
674 else
675 role_index = get_role_index(role);
676
677 if (PA_INVALID_INDEX == role_index)
678 return;
679
680 device_index = u->preferred_sinks[role_index];
681 if (PA_INVALID_INDEX == device_index)
682 return;
683
684 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
685 return;
686
687 if (si->sink != sink)
688 pa_sink_input_move_to(si, sink, false);
689 }
690
691 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
692 pa_sink_input *si;
693 uint32_t idx;
694
695 pa_assert(u);
696
697 if (!u->do_routing)
698 return PA_HOOK_OK;
699
700 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
701
702 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
703 route_sink_input(u, si);
704 }
705
706 return PA_HOOK_OK;
707 }
708
709 static void route_source_output(struct userdata *u, pa_source_output *so) {
710 const char *role;
711 uint32_t role_index, device_index;
712 pa_source *source;
713
714 pa_assert(u);
715 pa_assert(u->do_routing);
716
717 if (so->save_source)
718 return;
719
720 if (so->direct_on_input)
721 return;
722
723 /* Skip this if it is already in the process of being moved anyway */
724 if (!so->source)
725 return;
726
727 /* It might happen that a stream and a source are set up at the
728 same time, in which case we want to make sure we don't
729 interfere with that */
730 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
731 return;
732
733 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
734 role_index = get_role_index("none");
735 else
736 role_index = get_role_index(role);
737
738 if (PA_INVALID_INDEX == role_index)
739 return;
740
741 device_index = u->preferred_sources[role_index];
742 if (PA_INVALID_INDEX == device_index)
743 return;
744
745 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
746 return;
747
748 if (so->source != source)
749 pa_source_output_move_to(so, source, false);
750 }
751
752 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
753 pa_source_output *so;
754 uint32_t idx;
755
756 pa_assert(u);
757
758 if (!u->do_routing)
759 return PA_HOOK_OK;
760
761 update_highest_priority_device_indexes(u, "source:", ignore_source);
762
763 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
764 route_source_output(u, so);
765 }
766
767 return PA_HOOK_OK;
768 }
769
770 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
771 struct userdata *u = userdata;
772 struct entry *entry, *old = NULL;
773 char *name = NULL;
774
775 pa_assert(c);
776 pa_assert(u);
777
778 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
779 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
780 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
781 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
782
783 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
784 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
785 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
786 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
787 return;
788
789 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
790 pa_sink_input *si;
791
792 if (!u->do_routing)
793 return;
794 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
795 return;
796
797 /* The role may change mid-stream, so we reroute */
798 route_sink_input(u, si);
799
800 return;
801 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
802 pa_source_output *so;
803
804 if (!u->do_routing)
805 return;
806 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
807 return;
808
809 /* The role may change mid-stream, so we reroute */
810 route_source_output(u, so);
811
812 return;
813 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
814 pa_sink *sink;
815
816 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
817 return;
818
819 entry = entry_new();
820 name = pa_sprintf_malloc("sink:%s", sink->name);
821
822 old = load_or_initialize_entry(u, entry, name, "sink:");
823
824 if (!entry->user_set_description) {
825 pa_xfree(entry->description);
826 entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
827 } else if (!pa_streq(entry->description, pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
828 /* Warning: If two modules fight over the description, this could cause an infinite loop.
829 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
830 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
831 the description, this will fail... */
832 pa_sink_set_description(sink, entry->description);
833 }
834
835 pa_xfree(entry->icon);
836 entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
837
838 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
839 pa_source *source;
840
841 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
842
843 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
844 return;
845
846 if (source->monitor_of)
847 return;
848
849 entry = entry_new();
850 name = pa_sprintf_malloc("source:%s", source->name);
851
852 old = load_or_initialize_entry(u, entry, name, "source:");
853
854 if (!entry->user_set_description) {
855 pa_xfree(entry->description);
856 entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
857 } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
858 /* Warning: If two modules fight over the description, this could cause an infinite loop.
859 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
860 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
861 the description, this will fail... */
862 pa_source_set_description(source, entry->description);
863 }
864
865 pa_xfree(entry->icon);
866 entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
867 } else {
868 pa_assert_not_reached();
869 }
870
871 pa_assert(name);
872
873 if (old) {
874
875 if (entries_equal(old, entry)) {
876 entry_free(old);
877 entry_free(entry);
878 pa_xfree(name);
879
880 return;
881 }
882
883 entry_free(old);
884 }
885
886 pa_log_info("Storing device %s.", name);
887
888 if (entry_write(u, name, entry))
889 trigger_save(u);
890 else
891 pa_log_warn("Could not save device");;
892
893 entry_free(entry);
894 pa_xfree(name);
895 }
896
897 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
898 char *name;
899 struct entry *e;
900
901 pa_assert(c);
902 pa_assert(new_data);
903 pa_assert(u);
904
905 name = pa_sprintf_malloc("sink:%s", new_data->name);
906
907 if ((e = entry_read(u, name))) {
908 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
909 pa_log_info("Restoring description for sink %s.", new_data->name);
910 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
911 }
912
913 entry_free(e);
914 }
915
916 pa_xfree(name);
917
918 return PA_HOOK_OK;
919 }
920
921 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
922 char *name;
923 struct entry *e;
924
925 pa_assert(c);
926 pa_assert(new_data);
927 pa_assert(u);
928
929 name = pa_sprintf_malloc("source:%s", new_data->name);
930
931 if ((e = entry_read(u, name))) {
932 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
933 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
934 pa_log_info("Restoring description for source %s.", new_data->name);
935 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
936 }
937
938 entry_free(e);
939 }
940
941 pa_xfree(name);
942
943 return PA_HOOK_OK;
944 }
945
946 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
947 const char *role;
948 uint32_t role_index;
949
950 pa_assert(c);
951 pa_assert(new_data);
952 pa_assert(u);
953
954 if (!u->do_routing)
955 return PA_HOOK_OK;
956
957 if (new_data->sink)
958 pa_log_debug("Not restoring device for stream because already set.");
959 else {
960 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
961 role_index = get_role_index("none");
962 else
963 role_index = get_role_index(role);
964
965 if (PA_INVALID_INDEX != role_index) {
966 uint32_t device_index;
967
968 device_index = u->preferred_sinks[role_index];
969 if (PA_INVALID_INDEX != device_index) {
970 pa_sink *sink;
971
972 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
973 if (!pa_sink_input_new_data_set_sink(new_data, sink, false))
974 pa_log_debug("Not restoring device for stream because no supported format was found");
975 }
976 }
977 }
978 }
979
980 return PA_HOOK_OK;
981 }
982
983 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
984 const char *role;
985 uint32_t role_index;
986
987 pa_assert(c);
988 pa_assert(new_data);
989 pa_assert(u);
990
991 if (!u->do_routing)
992 return PA_HOOK_OK;
993
994 if (new_data->direct_on_input)
995 return PA_HOOK_OK;
996
997 if (new_data->source)
998 pa_log_debug("Not restoring device for stream because already set.");
999 else {
1000 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
1001 role_index = get_role_index("none");
1002 else
1003 role_index = get_role_index(role);
1004
1005 if (PA_INVALID_INDEX != role_index) {
1006 uint32_t device_index;
1007
1008 device_index = u->preferred_sources[role_index];
1009 if (PA_INVALID_INDEX != device_index) {
1010 pa_source *source;
1011
1012 if ((source = pa_idxset_get_by_index(u->core->sources, device_index)))
1013 if (!pa_source_output_new_data_set_source(new_data, source, false))
1014 pa_log_debug("Not restoring device for stream because no supported format was found");
1015 }
1016 }
1017 }
1018
1019 return PA_HOOK_OK;
1020 }
1021
1022 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
1023 pa_assert(c);
1024 pa_assert(u);
1025 pa_assert(u->core == c);
1026 pa_assert(u->on_hotplug);
1027
1028 notify_subscribers(u);
1029
1030 return route_sink_inputs(u, NULL);
1031 }
1032
1033 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
1034 pa_assert(c);
1035 pa_assert(u);
1036 pa_assert(u->core == c);
1037 pa_assert(u->on_hotplug);
1038
1039 notify_subscribers(u);
1040
1041 return route_source_outputs(u, NULL);
1042 }
1043
1044 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
1045 pa_assert(c);
1046 pa_assert(sink);
1047 pa_assert(u);
1048 pa_assert(u->core == c);
1049 pa_assert(u->on_rescue);
1050
1051 /* There's no point in doing anything if the core is shut down anyway */
1052 if (c->state == PA_CORE_SHUTDOWN)
1053 return PA_HOOK_OK;
1054
1055 notify_subscribers(u);
1056
1057 return route_sink_inputs(u, sink);
1058 }
1059
1060 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
1061 pa_assert(c);
1062 pa_assert(source);
1063 pa_assert(u);
1064 pa_assert(u->core == c);
1065 pa_assert(u->on_rescue);
1066
1067 /* There's no point in doing anything if the core is shut down anyway */
1068 if (c->state == PA_CORE_SHUTDOWN)
1069 return PA_HOOK_OK;
1070
1071 notify_subscribers(u);
1072
1073 return route_source_outputs(u, source);
1074 }
1075
1076 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
1077 uint32_t idx;
1078 char *n;
1079
1080 pa_assert(u);
1081 pa_assert(name);
1082 pa_assert(e);
1083
1084 if (!e->user_set_description)
1085 return;
1086
1087 if ((n = get_name(name, "sink:"))) {
1088 pa_sink *s;
1089 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1090 if (!pa_streq(s->name, n)) {
1091 continue;
1092 }
1093
1094 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
1095 pa_sink_set_description(s, e->description);
1096 }
1097 pa_xfree(n);
1098 }
1099 else if ((n = get_name(name, "source:"))) {
1100 pa_source *s;
1101 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1102 if (!pa_streq(s->name, n)) {
1103 continue;
1104 }
1105
1106 if (s->monitor_of) {
1107 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
1108 continue;
1109 }
1110
1111 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
1112 pa_source_set_description(s, e->description);
1113 }
1114 pa_xfree(n);
1115 }
1116 }
1117
1118 #define EXT_VERSION 1
1119
1120 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
1121 struct userdata *u;
1122 uint32_t command;
1123 pa_tagstruct *reply = NULL;
1124
1125 pa_assert(p);
1126 pa_assert(m);
1127 pa_assert(c);
1128 pa_assert(t);
1129
1130 u = m->userdata;
1131
1132 if (pa_tagstruct_getu32(t, &command) < 0)
1133 goto fail;
1134
1135 reply = pa_tagstruct_new(NULL, 0);
1136 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1137 pa_tagstruct_putu32(reply, tag);
1138
1139 switch (command) {
1140 case SUBCOMMAND_TEST: {
1141 if (!pa_tagstruct_eof(t))
1142 goto fail;
1143
1144 pa_tagstruct_putu32(reply, EXT_VERSION);
1145 break;
1146 }
1147
1148 case SUBCOMMAND_READ: {
1149 pa_datum key;
1150 bool done;
1151
1152 if (!pa_tagstruct_eof(t))
1153 goto fail;
1154
1155 done = !pa_database_first(u->database, &key, NULL);
1156
1157 while (!done) {
1158 pa_datum next_key;
1159 struct entry *e;
1160 char *name;
1161
1162 done = !pa_database_next(u->database, &key, &next_key, NULL);
1163
1164 name = pa_xstrndup(key.data, key.size);
1165 pa_datum_free(&key);
1166
1167 if ((e = entry_read(u, name))) {
1168 uint32_t idx;
1169 char *device_name;
1170 uint32_t found_index = PA_INVALID_INDEX;
1171
1172 if ((device_name = get_name(name, "sink:"))) {
1173 pa_sink* s;
1174 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1175 if (pa_streq(s->name, device_name)) {
1176 found_index = s->index;
1177 break;
1178 }
1179 }
1180 pa_xfree(device_name);
1181 } else if ((device_name = get_name(name, "source:"))) {
1182 pa_source* s;
1183 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1184 if (pa_streq(s->name, device_name)) {
1185 found_index = s->index;
1186 break;
1187 }
1188 }
1189 pa_xfree(device_name);
1190 }
1191
1192 pa_tagstruct_puts(reply, name);
1193 pa_tagstruct_puts(reply, e->description);
1194 pa_tagstruct_puts(reply, e->icon);
1195 pa_tagstruct_putu32(reply, found_index);
1196 pa_tagstruct_putu32(reply, NUM_ROLES);
1197
1198 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1199 pa_tagstruct_puts(reply, role_names[i]);
1200 pa_tagstruct_putu32(reply, e->priority[i]);
1201 }
1202
1203 entry_free(e);
1204 }
1205
1206 pa_xfree(name);
1207
1208 key = next_key;
1209 }
1210
1211 break;
1212 }
1213
1214 case SUBCOMMAND_RENAME: {
1215
1216 struct entry *e;
1217 const char *device, *description;
1218
1219 if (pa_tagstruct_gets(t, &device) < 0 ||
1220 pa_tagstruct_gets(t, &description) < 0)
1221 goto fail;
1222
1223 if (!device || !*device || !description || !*description)
1224 goto fail;
1225
1226 if ((e = entry_read(u, device))) {
1227 pa_xfree(e->description);
1228 e->description = pa_xstrdup(description);
1229 e->user_set_description = true;
1230
1231 if (entry_write(u, (char *)device, e)) {
1232 apply_entry(u, device, e);
1233
1234 trigger_save(u);
1235 }
1236 else
1237 pa_log_warn("Could not save device");
1238
1239 entry_free(e);
1240 }
1241 else
1242 pa_log_warn("Could not rename device %s, no entry in database", device);
1243
1244 break;
1245 }
1246
1247 case SUBCOMMAND_DELETE:
1248
1249 while (!pa_tagstruct_eof(t)) {
1250 const char *name;
1251 pa_datum key;
1252
1253 if (pa_tagstruct_gets(t, &name) < 0)
1254 goto fail;
1255
1256 key.data = (char*) name;
1257 key.size = strlen(name);
1258
1259 /** @todo: Reindex the priorities */
1260 pa_database_unset(u->database, &key);
1261 }
1262
1263 trigger_save(u);
1264
1265 break;
1266
1267 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1268
1269 bool enable;
1270
1271 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1272 goto fail;
1273
1274 if ((u->do_routing = enable)) {
1275 /* Update our caches */
1276 update_highest_priority_device_indexes(u, "sink:", NULL);
1277 update_highest_priority_device_indexes(u, "source:", NULL);
1278 }
1279
1280 break;
1281 }
1282
1283 case SUBCOMMAND_REORDER: {
1284
1285 const char *role;
1286 struct entry *e;
1287 uint32_t role_index, n_devices;
1288 pa_datum key;
1289 bool done, sink_mode = true;
1290 struct device_t { uint32_t prio; char *device; };
1291 struct device_t *device;
1292 struct device_t **devices;
1293 uint32_t i, idx, offset;
1294 pa_hashmap *h;
1295 /*void *state;*/
1296 bool first;
1297
1298 if (pa_tagstruct_gets(t, &role) < 0 ||
1299 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1300 n_devices < 1)
1301 goto fail;
1302
1303 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1304 goto fail;
1305
1306 /* Cycle through the devices given and make sure they exist */
1307 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1308 first = true;
1309 idx = 0;
1310 for (i = 0; i < n_devices; ++i) {
1311 const char *s;
1312 if (pa_tagstruct_gets(t, &s) < 0) {
1313 while ((device = pa_hashmap_steal_first(h))) {
1314 pa_xfree(device->device);
1315 pa_xfree(device);
1316 }
1317
1318 pa_hashmap_free(h);
1319 pa_log_error("Protocol error on reorder");
1320 goto fail;
1321 }
1322
1323 /* Ensure this is a valid entry */
1324 if (!(e = entry_read(u, s))) {
1325 while ((device = pa_hashmap_steal_first(h))) {
1326 pa_xfree(device->device);
1327 pa_xfree(device);
1328 }
1329
1330 pa_hashmap_free(h);
1331 pa_log_error("Client specified an unknown device in it's reorder list.");
1332 goto fail;
1333 }
1334 entry_free(e);
1335
1336 if (first) {
1337 first = false;
1338 sink_mode = (0 == strncmp("sink:", s, 5));
1339 } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
1340 while ((device = pa_hashmap_steal_first(h))) {
1341 pa_xfree(device->device);
1342 pa_xfree(device);
1343 }
1344
1345 pa_hashmap_free(h);
1346 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1347 goto fail;
1348 }
1349
1350 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1351 device = pa_xnew(struct device_t, 1);
1352 device->device = pa_xstrdup(s);
1353 if (pa_hashmap_put(h, device->device, device) == 0) {
1354 device->prio = idx;
1355 idx++;
1356 } else {
1357 pa_xfree(device->device);
1358 pa_xfree(device);
1359 }
1360 }
1361
1362 /*pa_log_debug("Hashmap contents (received from client)");
1363 PA_HASHMAP_FOREACH(device, h, state) {
1364 pa_log_debug(" - %s (%d)", device->device, device->prio);
1365 }*/
1366
1367 /* Now cycle through our list and add all the devices.
1368 This has the effect of adding in any in our DB,
1369 not specified in the device list (and thus will be
1370 tacked on at the end) */
1371 offset = idx;
1372 done = !pa_database_first(u->database, &key, NULL);
1373
1374 while (!done && idx < 256) {
1375 pa_datum next_key;
1376
1377 done = !pa_database_next(u->database, &key, &next_key, NULL);
1378
1379 device = pa_xnew(struct device_t, 1);
1380 device->device = pa_xstrndup(key.data, key.size);
1381 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1382 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1383
1384 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1385 if (pa_hashmap_put(h, device->device, device) == 0
1386 && (e = entry_read(u, device->device))) {
1387 /* We add offset on to the existing priority so that when we order, the
1388 existing entries are always lower priority than the new ones. */
1389 device->prio = (offset + e->priority[role_index]);
1390 pa_xfree(e);
1391 }
1392 else {
1393 pa_xfree(device->device);
1394 pa_xfree(device);
1395 }
1396 } else {
1397 pa_xfree(device->device);
1398 pa_xfree(device);
1399 }
1400
1401 pa_datum_free(&key);
1402
1403 key = next_key;
1404 }
1405
1406 /*pa_log_debug("Hashmap contents (combined with database)");
1407 PA_HASHMAP_FOREACH(device, h, state) {
1408 pa_log_debug(" - %s (%d)", device->device, device->prio);
1409 }*/
1410
1411 /* Now we put all the entries in a simple list for sorting it. */
1412 n_devices = pa_hashmap_size(h);
1413 devices = pa_xnew(struct device_t *, n_devices);
1414 idx = 0;
1415 while ((device = pa_hashmap_steal_first(h))) {
1416 devices[idx++] = device;
1417 }
1418 pa_hashmap_free(h);
1419
1420 /* Simple bubble sort */
1421 for (i = 0; i < n_devices; ++i) {
1422 for (uint32_t j = i; j < n_devices; ++j) {
1423 if (devices[i]->prio > devices[j]->prio) {
1424 struct device_t *tmp;
1425 tmp = devices[i];
1426 devices[i] = devices[j];
1427 devices[j] = tmp;
1428 }
1429 }
1430 }
1431
1432 /*pa_log_debug("Sorted device list");
1433 for (i = 0; i < n_devices; ++i) {
1434 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1435 }*/
1436
1437 /* Go through in order and write the new entry and cleanup our own list */
1438 idx = 1;
1439 first = true;
1440 for (i = 0; i < n_devices; ++i) {
1441 if ((e = entry_read(u, devices[i]->device))) {
1442 if (e->priority[role_index] == idx)
1443 idx++;
1444 else {
1445 e->priority[role_index] = idx;
1446
1447 if (entry_write(u, (char *) devices[i]->device, e)) {
1448 first = false;
1449 idx++;
1450 }
1451 }
1452
1453 pa_xfree(e);
1454 }
1455 pa_xfree(devices[i]->device);
1456 pa_xfree(devices[i]);
1457 }
1458
1459 pa_xfree(devices);
1460
1461 if (!first) {
1462 trigger_save(u);
1463
1464 if (sink_mode)
1465 route_sink_inputs(u, NULL);
1466 else
1467 route_source_outputs(u, NULL);
1468 }
1469
1470 break;
1471 }
1472
1473 case SUBCOMMAND_SUBSCRIBE: {
1474
1475 bool enabled;
1476
1477 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1478 !pa_tagstruct_eof(t))
1479 goto fail;
1480
1481 if (enabled)
1482 pa_idxset_put(u->subscribed, c, NULL);
1483 else
1484 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1485
1486 break;
1487 }
1488
1489 default:
1490 goto fail;
1491 }
1492
1493 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1494 return 0;
1495
1496 fail:
1497
1498 if (reply)
1499 pa_tagstruct_free(reply);
1500
1501 return -1;
1502 }
1503
1504 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1505 pa_assert(p);
1506 pa_assert(c);
1507 pa_assert(u);
1508
1509 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1510 return PA_HOOK_OK;
1511 }
1512
1513 struct prioritised_indexes {
1514 uint32_t index;
1515 int32_t priority;
1516 };
1517
1518 int pa__init(pa_module*m) {
1519 pa_modargs *ma = NULL;
1520 struct userdata *u;
1521 char *fname;
1522 pa_sink *sink;
1523 pa_source *source;
1524 uint32_t idx;
1525 bool do_routing = false, on_hotplug = true, on_rescue = true;
1526 uint32_t total_devices;
1527
1528 pa_assert(m);
1529
1530 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1531 pa_log("Failed to parse module arguments");
1532 goto fail;
1533 }
1534
1535 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1536 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1537 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1538 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1539 goto fail;
1540 }
1541
1542 m->userdata = u = pa_xnew0(struct userdata, 1);
1543 u->core = m->core;
1544 u->module = m;
1545 u->do_routing = do_routing;
1546 u->on_hotplug = on_hotplug;
1547 u->on_rescue = on_rescue;
1548 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1549
1550 u->protocol = pa_native_protocol_get(m->core);
1551 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1552
1553 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);
1554
1555 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);
1556
1557 /* Used to handle device description management */
1558 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);
1559 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);
1560
1561 /* The following slots are used to deal with routing */
1562 /* A little bit later than module-stream-restore, but before module-intended-roles */
1563 u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+5, (pa_hook_cb_t) sink_input_new_hook_callback, u);
1564 u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+5, (pa_hook_cb_t) source_output_new_hook_callback, u);
1565
1566 if (on_hotplug) {
1567 /* A little bit later than module-stream-restore, but before module-intended-roles */
1568 u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+5, (pa_hook_cb_t) sink_put_hook_callback, u);
1569 u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+5, (pa_hook_cb_t) source_put_hook_callback, u);
1570 }
1571
1572 if (on_rescue) {
1573 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1574 u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+5, (pa_hook_cb_t) sink_unlink_hook_callback, u);
1575 u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+5, (pa_hook_cb_t) source_unlink_hook_callback, u);
1576 }
1577
1578 if (!(fname = pa_state_path("device-manager", true)))
1579 goto fail;
1580
1581 if (!(u->database = pa_database_open(fname, true))) {
1582 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1583 pa_xfree(fname);
1584 goto fail;
1585 }
1586
1587 pa_log_info("Successfully opened database file '%s'.", fname);
1588 pa_xfree(fname);
1589
1590 /* Attempt to inject the devices into the list in priority order */
1591 total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
1592 if (total_devices > 0 && total_devices < 128) {
1593 uint32_t i;
1594 struct prioritised_indexes p_i[128];
1595
1596 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1597 i = 0;
1598 PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
1599 pa_log_debug("Found sink index %u", sink->index);
1600 p_i[i ].index = sink->index;
1601 p_i[i++].priority = sink->priority;
1602 }
1603 /* Bubble sort it (only really useful for first time creation) */
1604 if (i > 1)
1605 for (uint32_t j = 0; j < i; ++j)
1606 for (uint32_t k = 0; k < i; ++k)
1607 if (p_i[j].priority > p_i[k].priority) {
1608 struct prioritised_indexes tmp_pi = p_i[k];
1609 p_i[k] = p_i[j];
1610 p_i[j] = tmp_pi;
1611 }
1612 /* Register it */
1613 for (uint32_t j = 0; j < i; ++j)
1614 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1615
1616 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1617 i = 0;
1618 PA_IDXSET_FOREACH(source, m->core->sources, idx) {
1619 p_i[i ].index = source->index;
1620 p_i[i++].priority = source->priority;
1621 }
1622 /* Bubble sort it (only really useful for first time creation) */
1623 if (i > 1)
1624 for (uint32_t j = 0; j < i; ++j)
1625 for (uint32_t k = 0; k < i; ++k)
1626 if (p_i[j].priority > p_i[k].priority) {
1627 struct prioritised_indexes tmp_pi = p_i[k];
1628 p_i[k] = p_i[j];
1629 p_i[j] = tmp_pi;
1630 }
1631 /* Register it */
1632 for (uint32_t j = 0; j < i; ++j)
1633 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1634 }
1635 else if (total_devices > 0) {
1636 /* This user has a *lot* of devices... */
1637 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1638 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1639
1640 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1641 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1642 }
1643
1644 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1645 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1646 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1647 }
1648
1649 route_sink_inputs(u, NULL);
1650 route_source_outputs(u, NULL);
1651
1652 #ifdef DUMP_DATABASE
1653 dump_database(u);
1654 #endif
1655
1656 pa_modargs_free(ma);
1657 return 0;
1658
1659 fail:
1660 pa__done(m);
1661
1662 if (ma)
1663 pa_modargs_free(ma);
1664
1665 return -1;
1666 }
1667
1668 void pa__done(pa_module*m) {
1669 struct userdata* u;
1670
1671 pa_assert(m);
1672
1673 if (!(u = m->userdata))
1674 return;
1675
1676 if (u->subscription)
1677 pa_subscription_free(u->subscription);
1678
1679 if (u->sink_new_hook_slot)
1680 pa_hook_slot_free(u->sink_new_hook_slot);
1681 if (u->source_new_hook_slot)
1682 pa_hook_slot_free(u->source_new_hook_slot);
1683
1684 if (u->sink_input_new_hook_slot)
1685 pa_hook_slot_free(u->sink_input_new_hook_slot);
1686 if (u->source_output_new_hook_slot)
1687 pa_hook_slot_free(u->source_output_new_hook_slot);
1688
1689 if (u->sink_put_hook_slot)
1690 pa_hook_slot_free(u->sink_put_hook_slot);
1691 if (u->source_put_hook_slot)
1692 pa_hook_slot_free(u->source_put_hook_slot);
1693
1694 if (u->sink_unlink_hook_slot)
1695 pa_hook_slot_free(u->sink_unlink_hook_slot);
1696 if (u->source_unlink_hook_slot)
1697 pa_hook_slot_free(u->source_unlink_hook_slot);
1698
1699 if (u->connection_unlink_hook_slot)
1700 pa_hook_slot_free(u->connection_unlink_hook_slot);
1701
1702 if (u->save_time_event)
1703 u->core->mainloop->time_free(u->save_time_event);
1704
1705 if (u->database)
1706 pa_database_close(u->database);
1707
1708 if (u->protocol) {
1709 pa_native_protocol_remove_ext(u->protocol, m);
1710 pa_native_protocol_unref(u->protocol);
1711 }
1712
1713 if (u->subscribed)
1714 pa_idxset_free(u->subscribed, NULL);
1715
1716 pa_xfree(u);
1717 }