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