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