]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Fix the database write mode
[pulseaudio] / src / modules / module-device-manager.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2009 Colin Guthrie
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/rtclock.h>
40
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/module.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/modargs.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/core-subscribe.h>
47 #include <pulsecore/sink-input.h>
48 #include <pulsecore/source-output.h>
49 #include <pulsecore/namereg.h>
50 #include <pulsecore/protocol-native.h>
51 #include <pulsecore/pstream.h>
52 #include <pulsecore/pstream-util.h>
53 #include <pulsecore/database.h>
54
55 #include "module-device-manager-symdef.h"
56
57 PA_MODULE_AUTHOR("Colin Guthrie");
58 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present");
59 PA_MODULE_VERSION(PACKAGE_VERSION);
60 PA_MODULE_LOAD_ONCE(TRUE);
61 PA_MODULE_USAGE(
62 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
63 "on_hotplug=<When new device becomes available, recheck streams?> "
64 "on_rescue=<When device becomes unavailable, recheck streams?>");
65
66 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
67
68 static const char* const valid_modargs[] = {
69 "do_routing",
70 "on_hotplug",
71 "on_rescue",
72 NULL
73 };
74
75 struct userdata {
76 pa_core *core;
77 pa_module *module;
78 pa_subscription *subscription;
79 pa_hook_slot
80 *sink_new_hook_slot,
81 *source_new_hook_slot,
82 *sink_input_new_hook_slot,
83 *source_output_new_hook_slot,
84 *sink_put_hook_slot,
85 *source_put_hook_slot,
86 *sink_unlink_hook_slot,
87 *source_unlink_hook_slot,
88 *connection_unlink_hook_slot;
89 pa_time_event *save_time_event;
90 pa_database *database;
91
92 pa_native_protocol *protocol;
93 pa_idxset *subscribed;
94
95 pa_bool_t on_hotplug;
96 pa_bool_t on_rescue;
97 pa_bool_t do_routing;
98 };
99
100 #define ENTRY_VERSION 1
101
102 #define NUM_ROLES 9
103 enum {
104 ROLE_NONE,
105 ROLE_VIDEO,
106 ROLE_MUSIC,
107 ROLE_GAME,
108 ROLE_EVENT,
109 ROLE_PHONE,
110 ROLE_ANIMATION,
111 ROLE_PRODUCTION,
112 ROLE_A11Y,
113 };
114
115 typedef uint32_t role_indexes_t[NUM_ROLES];
116
117 struct entry {
118 uint8_t version;
119 char description[PA_NAME_MAX];
120 role_indexes_t priority;
121 } PA_GCC_PACKED;
122
123 enum {
124 SUBCOMMAND_TEST,
125 SUBCOMMAND_READ,
126 SUBCOMMAND_RENAME,
127 SUBCOMMAND_DELETE,
128 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
129 SUBCOMMAND_PREFER_DEVICE,
130 SUBCOMMAND_DEFER_DEVICE,
131 SUBCOMMAND_SUBSCRIBE,
132 SUBCOMMAND_EVENT
133 };
134
135 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
136 struct userdata *u = userdata;
137
138 pa_assert(a);
139 pa_assert(e);
140 pa_assert(u);
141
142 pa_assert(e == u->save_time_event);
143 u->core->mainloop->time_free(u->save_time_event);
144 u->save_time_event = NULL;
145
146 pa_database_sync(u->database);
147 pa_log_info("Synced.");
148 }
149
150 static struct entry* read_entry(struct userdata *u, const char *name) {
151 pa_datum key, data;
152 struct entry *e;
153
154 pa_assert(u);
155 pa_assert(name);
156
157 key.data = (char*) name;
158 key.size = strlen(name);
159
160 pa_zero(data);
161
162 if (!pa_database_get(u->database, &key, &data))
163 goto fail;
164
165 if (data.size != sizeof(struct entry)) {
166 pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
167 goto fail;
168 }
169
170 e = (struct entry*) data.data;
171
172 if (e->version != ENTRY_VERSION) {
173 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
174 goto fail;
175 }
176
177 if (!memchr(e->description, 0, sizeof(e->description))) {
178 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
179 goto fail;
180 }
181
182 return e;
183
184 fail:
185
186 pa_datum_free(&data);
187 return NULL;
188 }
189
190 static void trigger_save(struct userdata *u) {
191 pa_native_connection *c;
192 uint32_t idx;
193
194 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
195 pa_tagstruct *t;
196
197 t = pa_tagstruct_new(NULL, 0);
198 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
199 pa_tagstruct_putu32(t, 0);
200 pa_tagstruct_putu32(t, u->module->index);
201 pa_tagstruct_puts(t, u->module->name);
202 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
203
204 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
205 }
206
207 if (u->save_time_event)
208 return;
209
210 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
211 }
212
213 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
214 if (strncmp(a->description, b->description, sizeof(a->description)))
215 return FALSE;
216
217 return TRUE;
218 }
219
220 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
221 struct entry *old;
222
223 pa_assert(u);
224 pa_assert(entry);
225 pa_assert(name);
226 pa_assert(prefix);
227
228 if ((old = read_entry(u, name)))
229 *entry = *old;
230 else {
231 /* This is a new device, so make sure we write it's priority list correctly */
232 role_indexes_t max_priority;
233 pa_datum key;
234 pa_bool_t done;
235
236 pa_zero(max_priority);
237 done = !pa_database_first(u->database, &key, NULL);
238
239 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
240 while (!done) {
241 pa_datum next_key;
242
243 done = !pa_database_next(u->database, &key, &next_key, NULL);
244
245 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
246 char *name2;
247 struct entry *e;
248
249 name2 = pa_xstrndup(key.data, key.size);
250
251 if ((e = read_entry(u, name2))) {
252 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
253 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
254 }
255
256 pa_xfree(e);
257 }
258
259 pa_xfree(name2);
260 }
261 pa_datum_free(&key);
262 key = next_key;
263 }
264
265 /* Actually initialise our entry now we've calculated it */
266 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
267 entry->priority[i] = max_priority[i] + 1;
268 }
269 }
270
271 return old;
272 }
273
274 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
275 struct userdata *u = userdata;
276 struct entry entry, *old = NULL;
277 char *name = NULL;
278 pa_datum key, data;
279
280 pa_assert(c);
281 pa_assert(u);
282
283 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
284 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
285 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
286 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
287 return;
288
289 pa_zero(entry);
290 entry.version = ENTRY_VERSION;
291
292 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
293 pa_sink *sink;
294
295 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
296 return;
297
298 name = pa_sprintf_malloc("sink:%s", sink->name);
299
300 old = load_or_initialize_entry(u, &entry, name, "sink:");
301
302 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
303
304 } else {
305 pa_source *source;
306
307 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
308
309 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
310 return;
311
312 if (source->monitor_of)
313 return;
314
315 name = pa_sprintf_malloc("source:%s", source->name);
316
317 old = load_or_initialize_entry(u, &entry, name, "source:");
318
319 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
320 }
321
322 if (old) {
323
324 if (entries_equal(old, &entry)) {
325 pa_xfree(old);
326 pa_xfree(name);
327 return;
328 }
329
330 pa_xfree(old);
331 }
332
333 key.data = name;
334 key.size = strlen(name);
335
336 data.data = &entry;
337 data.size = sizeof(entry);
338
339 pa_log_info("Storing device %s.", name);
340
341 pa_database_set(u->database, &key, &data, TRUE);
342
343 pa_xfree(name);
344
345 trigger_save(u);
346 }
347
348 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
349 char *name;
350 struct entry *e;
351
352 pa_assert(c);
353 pa_assert(new_data);
354 pa_assert(u);
355
356 name = pa_sprintf_malloc("sink:%s", new_data->name);
357
358 if ((e = read_entry(u, name))) {
359 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
360 pa_log_info("Restoring description for sink %s.", new_data->name);
361 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
362 }
363
364 pa_xfree(e);
365 }
366
367 pa_xfree(name);
368
369 return PA_HOOK_OK;
370 }
371
372 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
373 char *name;
374 struct entry *e;
375
376 pa_assert(c);
377 pa_assert(new_data);
378 pa_assert(u);
379
380 name = pa_sprintf_malloc("source:%s", new_data->name);
381
382 if ((e = read_entry(u, name))) {
383 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
384 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
385 pa_log_info("Restoring description for source %s.", new_data->name);
386 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
387 }
388
389 pa_xfree(e);
390 }
391
392 pa_xfree(name);
393
394 return PA_HOOK_OK;
395 }
396
397 static char *get_name(const char *key, const char *prefix) {
398 char *t;
399
400 if (strncmp(key, prefix, strlen(prefix)))
401 return NULL;
402
403 t = pa_xstrdup(key + strlen(prefix));
404 return t;
405 }
406
407 static uint32_t get_role_index(const char* role) {
408 pa_assert(role);
409
410 if (strcmp(role, "") == 0)
411 return ROLE_NONE;
412 if (strcmp(role, "video") == 0)
413 return ROLE_VIDEO;
414 if (strcmp(role, "music") == 0)
415 return ROLE_MUSIC;
416 if (strcmp(role, "game") == 0)
417 return ROLE_GAME;
418 if (strcmp(role, "event") == 0)
419 return ROLE_EVENT;
420 if (strcmp(role, "phone") == 0)
421 return ROLE_PHONE;
422 if (strcmp(role, "animation") == 0)
423 return ROLE_ANIMATION;
424 if (strcmp(role, "production") == 0)
425 return ROLE_PRODUCTION;
426 if (strcmp(role, "a11y") == 0)
427 return ROLE_A11Y;
428 return PA_INVALID_INDEX;
429 }
430
431 static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) {
432 role_indexes_t *indexes, highest_priority_available;
433 pa_datum key;
434 pa_bool_t done, sink_mode;
435
436 pa_assert(u);
437 pa_assert(prefix);
438
439 indexes = pa_xnew(role_indexes_t, 1);
440 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
441 *indexes[i] = PA_INVALID_INDEX;
442 }
443 pa_zero(highest_priority_available);
444
445 sink_mode = (strcmp(prefix, "sink:") == 0);
446
447 done = !pa_database_first(u->database, &key, NULL);
448
449 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
450 while (!done) {
451 pa_datum next_key;
452
453 done = !pa_database_next(u->database, &key, &next_key, NULL);
454
455 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
456 char *name;
457 struct entry *e;
458
459 name = pa_xstrndup(key.data, key.size);
460
461 if ((e = read_entry(u, name))) {
462 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
463 if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) {
464 /* We've found a device with a higher priority than that we've currently got,
465 so see if it is currently available or not and update our list */
466 uint32_t idx;
467 pa_bool_t found = FALSE;
468 char *device_name = get_name(name, prefix);
469
470 if (sink_mode) {
471 pa_sink *sink;
472
473 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
474 if (strcmp(sink->name, device_name) == 0) {
475 found = TRUE;
476 idx = sink->index; /* Is this needed? */
477 break;
478 }
479 }
480 } else {
481 pa_source *source;
482
483 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
484 if (strcmp(source->name, device_name) == 0) {
485 found = TRUE;
486 idx = source->index; /* Is this needed? */
487 break;
488 }
489 }
490 }
491 if (found) {
492 highest_priority_available[i] = e->priority[i];
493 *indexes[i] = idx;
494 }
495
496 pa_xfree(device_name);
497 }
498 }
499
500 pa_xfree(e);
501 }
502
503 pa_xfree(name);
504 }
505
506 pa_datum_free(&key);
507 key = next_key;
508 }
509
510 return indexes;
511 }
512
513
514 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
515 pa_assert(c);
516 pa_assert(new_data);
517 pa_assert(u);
518
519 if (!u->do_routing)
520 return PA_HOOK_OK;
521
522 if (new_data->sink)
523 pa_log_debug("Not restoring device for stream, because already set.");
524 else {
525 const char *role;
526 uint32_t role_index;
527
528 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
529 role_index = get_role_index("");
530 else
531 role_index = get_role_index(role);
532
533 if (PA_INVALID_INDEX != role_index) {
534 role_indexes_t *indexes;
535 uint32_t device_index;
536
537 pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:"));
538
539 device_index = *indexes[role_index];
540 if (PA_INVALID_INDEX != device_index) {
541 pa_sink *sink;
542
543 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
544 new_data->sink = sink;
545 new_data->save_sink = TRUE;
546 }
547 }
548 }
549 }
550
551 return PA_HOOK_OK;
552 }
553
554 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
555 pa_assert(c);
556 pa_assert(new_data);
557 pa_assert(u);
558
559 if (!u->do_routing)
560 return PA_HOOK_OK;
561
562 if (new_data->direct_on_input)
563 return PA_HOOK_OK;
564
565 if (new_data->source)
566 pa_log_debug("Not restoring device for stream, because already set");
567 else {
568 const char *role;
569 uint32_t role_index;
570
571 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
572 role_index = get_role_index("");
573 else
574 role_index = get_role_index(role);
575
576 if (PA_INVALID_INDEX != role_index) {
577 role_indexes_t *indexes;
578 uint32_t device_index;
579
580 pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:"));
581
582 device_index = *indexes[role_index];
583 if (PA_INVALID_INDEX != device_index) {
584 pa_source *source;
585
586 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
587 new_data->source = source;
588 new_data->save_source = TRUE;
589 }
590 }
591 }
592 }
593
594 return PA_HOOK_OK;
595 }
596
597 static pa_hook_result_t reroute_sinks(struct userdata *u) {
598 pa_sink_input *si;
599 role_indexes_t *indexes;
600 uint32_t idx;
601
602 pa_assert(u);
603
604 if (!u->do_routing)
605 return PA_HOOK_OK;
606
607 pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:"));
608
609 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
610 const char *role;
611 uint32_t role_index, device_index;
612 pa_sink *sink;
613
614 if (si->save_sink)
615 continue;
616
617 /* Skip this if it is already in the process of being moved anyway */
618 if (!si->sink)
619 continue;
620
621 /* It might happen that a stream and a sink are set up at the
622 same time, in which case we want to make sure we don't
623 interfere with that */
624 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
625 continue;
626
627 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
628 role_index = get_role_index("");
629 else
630 role_index = get_role_index(role);
631
632 if (PA_INVALID_INDEX == role_index)
633 continue;
634
635 device_index = *indexes[role_index];
636 if (PA_INVALID_INDEX == device_index)
637 continue;
638
639 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
640 continue;
641
642 if (si->sink != sink)
643 pa_sink_input_move_to(si, sink, TRUE);
644 }
645
646 pa_xfree(indexes);
647
648 return PA_HOOK_OK;
649 }
650
651 static pa_hook_result_t reroute_sources(struct userdata *u) {
652 pa_source_output *so;
653 role_indexes_t *indexes;
654 uint32_t idx;
655
656 pa_assert(u);
657
658 if (!u->do_routing)
659 return PA_HOOK_OK;
660
661 pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:"));
662
663 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
664 const char *role;
665 uint32_t role_index, device_index;
666 pa_source *source;
667
668 if (so->save_source)
669 continue;
670
671 if (so->direct_on_input)
672 continue;
673
674 /* Skip this if it is already in the process of being moved anyway */
675 if (!so->source)
676 continue;
677
678 /* It might happen that a stream and a source are set up at the
679 same time, in which case we want to make sure we don't
680 interfere with that */
681 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
682 continue;
683
684 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
685 role_index = get_role_index("");
686 else
687 role_index = get_role_index(role);
688
689 if (PA_INVALID_INDEX == role_index)
690 continue;
691
692 device_index = *indexes[role_index];
693 if (PA_INVALID_INDEX == device_index)
694 continue;
695
696 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
697 continue;
698
699 if (so->source != source)
700 pa_source_output_move_to(so, source, TRUE);
701 }
702
703 pa_xfree(indexes);
704
705 return PA_HOOK_OK;
706 }
707
708 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
709 pa_assert(c);
710 pa_assert(u);
711 pa_assert(u->core == c);
712 pa_assert(u->on_hotplug);
713
714 return reroute_sinks(u);
715 }
716
717 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
718 pa_assert(c);
719 pa_assert(u);
720 pa_assert(u->core == c);
721 pa_assert(u->on_hotplug);
722
723 return reroute_sources(u);
724 }
725
726 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
727 pa_assert(c);
728 pa_assert(u);
729 pa_assert(u->core == c);
730 pa_assert(u->on_rescue);
731
732 /* There's no point in doing anything if the core is shut down anyway */
733 if (c->state == PA_CORE_SHUTDOWN)
734 return PA_HOOK_OK;
735
736 return reroute_sinks(u);
737 }
738
739 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
740 pa_assert(c);
741 pa_assert(u);
742 pa_assert(u->core == c);
743 pa_assert(u->on_rescue);
744
745 /* There's no point in doing anything if the core is shut down anyway */
746 if (c->state == PA_CORE_SHUTDOWN)
747 return PA_HOOK_OK;
748
749 return reroute_sinks(u);
750 }
751
752
753 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
754 pa_sink *sink;
755 pa_source *source;
756 uint32_t idx;
757 char *n;
758
759 pa_assert(u);
760 pa_assert(name);
761 pa_assert(e);
762
763 if ((n = get_name(name, "sink:"))) {
764 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
765 if (!pa_streq(sink->name, n)) {
766 continue;
767 }
768
769 pa_log_info("Setting description for sink %s.", sink->name);
770 pa_sink_set_description(sink, e->description);
771 }
772 pa_xfree(n);
773 }
774 else if ((n = get_name(name, "source:"))) {
775 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
776 if (!pa_streq(source->name, n)) {
777 continue;
778 }
779
780 if (source->monitor_of) {
781 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
782 continue;
783 }
784
785 pa_log_info("Setting description for source %s.", source->name);
786 pa_source_set_description(source, e->description);
787 }
788 pa_xfree(n);
789 }
790 }
791
792
793 #define EXT_VERSION 1
794
795 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
796 struct userdata *u;
797 uint32_t command;
798 pa_tagstruct *reply = NULL;
799
800 pa_assert(p);
801 pa_assert(m);
802 pa_assert(c);
803 pa_assert(t);
804
805 u = m->userdata;
806
807 if (pa_tagstruct_getu32(t, &command) < 0)
808 goto fail;
809
810 reply = pa_tagstruct_new(NULL, 0);
811 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
812 pa_tagstruct_putu32(reply, tag);
813
814 switch (command) {
815 case SUBCOMMAND_TEST: {
816 if (!pa_tagstruct_eof(t))
817 goto fail;
818
819 pa_tagstruct_putu32(reply, EXT_VERSION);
820 break;
821 }
822
823 case SUBCOMMAND_READ: {
824 pa_datum key;
825 pa_bool_t done;
826
827 if (!pa_tagstruct_eof(t))
828 goto fail;
829
830 done = !pa_database_first(u->database, &key, NULL);
831
832 while (!done) {
833 pa_datum next_key;
834 struct entry *e;
835 char *name;
836
837 done = !pa_database_next(u->database, &key, &next_key, NULL);
838
839 name = pa_xstrndup(key.data, key.size);
840 pa_datum_free(&key);
841
842 if ((e = read_entry(u, name))) {
843 pa_tagstruct_puts(reply, name);
844 pa_tagstruct_puts(reply, e->description);
845
846 pa_xfree(e);
847 }
848
849 pa_xfree(name);
850
851 key = next_key;
852 }
853
854 break;
855 }
856
857 case SUBCOMMAND_RENAME: {
858
859 struct entry *e;
860 const char *device, *description;
861
862 if (pa_tagstruct_gets(t, &device) < 0 ||
863 pa_tagstruct_gets(t, &description) < 0)
864 goto fail;
865
866 if (!device || !*device || !description || !*description)
867 goto fail;
868
869 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
870 pa_datum key, data;
871
872 pa_strlcpy(e->description, description, sizeof(e->description));
873
874 key.data = (char *) device;
875 key.size = strlen(device);
876
877 data.data = e;
878 data.size = sizeof(*e);
879
880 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
881 apply_entry(u, device, e);
882
883 trigger_save(u);
884 }
885 else
886 pa_log_warn("Could not save device");
887
888 pa_xfree(e);
889 }
890 else
891 pa_log_warn("Could not rename device %s, no entry in database", device);
892
893 break;
894 }
895
896 case SUBCOMMAND_DELETE:
897
898 while (!pa_tagstruct_eof(t)) {
899 const char *name;
900 pa_datum key;
901
902 if (pa_tagstruct_gets(t, &name) < 0)
903 goto fail;
904
905 key.data = (char*) name;
906 key.size = strlen(name);
907
908 /** @todo: Reindex the priorities */
909 pa_database_unset(u->database, &key);
910 }
911
912 trigger_save(u);
913
914 break;
915
916 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
917
918 pa_bool_t enable;
919
920 if (pa_tagstruct_get_boolean(t, &enable) < 0)
921 goto fail;
922
923 u->do_routing = enable;
924
925 break;
926 }
927
928 case SUBCOMMAND_PREFER_DEVICE:
929 case SUBCOMMAND_DEFER_DEVICE: {
930
931 const char *role, *device;
932 struct entry *e;
933 uint32_t role_index;
934
935 if (pa_tagstruct_gets(t, &role) < 0 ||
936 pa_tagstruct_gets(t, &device) < 0)
937 goto fail;
938
939 if (!role || !device || !*device)
940 goto fail;
941
942 role_index = get_role_index(role);
943 if (PA_INVALID_INDEX == role_index)
944 goto fail;
945
946 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
947 pa_datum key, data;
948 pa_bool_t done;
949 char* prefix = NULL;
950 uint32_t priority;
951 pa_bool_t haschanged = FALSE;
952
953 if (strncmp(device, "sink:", 5) == 0)
954 prefix = pa_xstrdup("sink:");
955 else if (strncmp(device, "source:", 7) == 0)
956 prefix = pa_xstrdup("source:");
957
958 if (!prefix)
959 goto fail;
960
961 priority = e->priority[role_index];
962
963 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
964
965 done = !pa_database_first(u->database, &key, NULL);
966
967 while (!done && !haschanged) {
968 pa_datum next_key;
969
970 done = !pa_database_next(u->database, &key, &next_key, NULL);
971
972 /* Only read devices with the right prefix */
973 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
974 char *name;
975 struct entry *e2;
976
977 name = pa_xstrndup(key.data, key.size);
978
979 if ((e2 = read_entry(u, name))) {
980 if (SUBCOMMAND_PREFER_DEVICE == command) {
981 /* PREFER */
982 if (e2->priority[role_index] == (priority - 1)) {
983 e2->priority[role_index]++;
984 haschanged = TRUE;
985 }
986 } else {
987 /* DEFER */
988 if (e2->priority[role_index] == (priority + 1)) {
989 e2->priority[role_index]--;
990 haschanged = TRUE;
991 }
992 }
993
994 if (haschanged) {
995 data.data = e2;
996 data.size = sizeof(*e2);
997
998 if (pa_database_set(u->database, &key, &data, TRUE))
999 pa_log_warn("Could not save device");
1000 }
1001
1002 pa_xfree(e2);
1003 }
1004
1005 pa_xfree(name);
1006 }
1007
1008 pa_datum_free(&key);
1009 key = next_key;
1010 }
1011
1012 /* Now write out our actual entry */
1013 if (haschanged) {
1014 if (SUBCOMMAND_PREFER_DEVICE == command)
1015 e->priority[role_index]--;
1016 else
1017 e->priority[role_index]++;
1018
1019 key.data = (char *) device;
1020 key.size = strlen(device);
1021
1022 data.data = e;
1023 data.size = sizeof(*e);
1024
1025 if (pa_database_set(u->database, &key, &data, TRUE))
1026 pa_log_warn("Could not save device");
1027
1028 trigger_save(u);
1029 }
1030
1031 pa_xfree(e);
1032
1033 pa_xfree(prefix);
1034 }
1035 else
1036 pa_log_warn("Could not reorder device %s, no entry in database", device);
1037
1038 break;
1039 }
1040
1041 case SUBCOMMAND_SUBSCRIBE: {
1042
1043 pa_bool_t enabled;
1044
1045 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1046 !pa_tagstruct_eof(t))
1047 goto fail;
1048
1049 if (enabled)
1050 pa_idxset_put(u->subscribed, c, NULL);
1051 else
1052 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1053
1054 break;
1055 }
1056
1057 default:
1058 goto fail;
1059 }
1060
1061 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1062 return 0;
1063
1064 fail:
1065
1066 if (reply)
1067 pa_tagstruct_free(reply);
1068
1069 return -1;
1070 }
1071
1072 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1073 pa_assert(p);
1074 pa_assert(c);
1075 pa_assert(u);
1076
1077 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1078 return PA_HOOK_OK;
1079 }
1080
1081 int pa__init(pa_module*m) {
1082 pa_modargs *ma = NULL;
1083 struct userdata *u;
1084 char *fname;
1085 pa_sink *sink;
1086 pa_source *source;
1087 pa_sink_input *si;
1088 pa_source_output *so;
1089 uint32_t idx;
1090 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1091
1092 pa_assert(m);
1093
1094 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1095 pa_log("Failed to parse module arguments");
1096 goto fail;
1097 }
1098
1099 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1100 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1101 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1102 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1103 goto fail;
1104 }
1105
1106 m->userdata = u = pa_xnew0(struct userdata, 1);
1107 u->core = m->core;
1108 u->module = m;
1109 u->do_routing = do_routing;
1110 u->on_hotplug = on_hotplug;
1111 u->on_rescue = on_rescue;
1112 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1113
1114 u->protocol = pa_native_protocol_get(m->core);
1115 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1116
1117 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);
1118
1119 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
1120
1121 /* Used to handle device description management */
1122 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);
1123 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);
1124
1125 /* The following slots are used to deal with routing */
1126 /* A little bit later than module-stream-restore, module-intended-roles */
1127 u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u);
1128 u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u);
1129
1130 if (on_hotplug) {
1131 /* A little bit later than module-stream-restore, module-intended-roles */
1132 u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_put_hook_callback, u);
1133 u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) source_put_hook_callback, u);
1134 }
1135
1136 if (on_rescue) {
1137 /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
1138 u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_unlink_hook_callback, u);
1139 u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) source_unlink_hook_callback, u);
1140 }
1141
1142 if (!(fname = pa_state_path("device-manager", TRUE)))
1143 goto fail;
1144
1145 if (!(u->database = pa_database_open(fname, TRUE))) {
1146 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1147 pa_xfree(fname);
1148 goto fail;
1149 }
1150
1151 pa_log_info("Sucessfully opened database file '%s'.", fname);
1152 pa_xfree(fname);
1153
1154 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1155 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1156
1157 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1158 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1159
1160 PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
1161 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
1162
1163 PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
1164 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
1165
1166 pa_modargs_free(ma);
1167 return 0;
1168
1169 fail:
1170 pa__done(m);
1171
1172 if (ma)
1173 pa_modargs_free(ma);
1174
1175 return -1;
1176 }
1177
1178 void pa__done(pa_module*m) {
1179 struct userdata* u;
1180
1181 pa_assert(m);
1182
1183 if (!(u = m->userdata))
1184 return;
1185
1186 if (u->subscription)
1187 pa_subscription_free(u->subscription);
1188
1189 if (u->sink_new_hook_slot)
1190 pa_hook_slot_free(u->sink_new_hook_slot);
1191 if (u->source_new_hook_slot)
1192 pa_hook_slot_free(u->source_new_hook_slot);
1193
1194 if (u->sink_input_new_hook_slot)
1195 pa_hook_slot_free(u->sink_input_new_hook_slot);
1196 if (u->source_output_new_hook_slot)
1197 pa_hook_slot_free(u->source_output_new_hook_slot);
1198
1199 if (u->sink_put_hook_slot)
1200 pa_hook_slot_free(u->sink_put_hook_slot);
1201 if (u->source_put_hook_slot)
1202 pa_hook_slot_free(u->source_put_hook_slot);
1203
1204 if (u->sink_unlink_hook_slot)
1205 pa_hook_slot_free(u->sink_unlink_hook_slot);
1206 if (u->source_unlink_hook_slot)
1207 pa_hook_slot_free(u->source_unlink_hook_slot);
1208
1209 if (u->save_time_event)
1210 u->core->mainloop->time_free(u->save_time_event);
1211
1212 if (u->database)
1213 pa_database_close(u->database);
1214
1215 if (u->protocol) {
1216 pa_native_protocol_remove_ext(u->protocol, m);
1217 pa_native_protocol_unref(u->protocol);
1218 }
1219
1220 if (u->subscribed)
1221 pa_idxset_free(u->subscribed, NULL, NULL);
1222
1223 pa_xfree(u);
1224 }