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
52PA_MODULE_AUTHOR("Colin Guthrie");
53PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
54PA_MODULE_VERSION(PACKAGE_VERSION);
55PA_MODULE_LOAD_ONCE(true);
56PA_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
64static const char* const valid_modargs[] = {
65    "do_routing",
66    "on_hotplug",
67    "on_rescue",
68    NULL
69};
70
71#define NUM_ROLES 9
72enum {
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
85typedef uint32_t role_indexes_t[NUM_ROLES];
86
87static 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
99struct 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
129struct entry {
130    uint8_t version;
131    char *description;
132    bool user_set_description;
133    char *icon;
134    role_indexes_t priority;
135};
136
137enum {
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
150static void dump_database(struct userdata *);
151#endif
152static void notify_subscribers(struct userdata *);
153
154static 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
173static 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
185static struct entry* entry_new(void) {
186    struct entry *r = pa_xnew0(struct entry, 1);
187    r->version = ENTRY_VERSION;
188    return r;
189}
190
191static 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
199static 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
231static 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
279static 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
336fail:
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
361static 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
380static 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
442static 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
463static 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
480static 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
490static 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
547static 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
557static 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
649static 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
703static 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
721static 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
778static 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
796static 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
923static 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
947static 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
972static 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
1009static 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
1048static 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
1059static 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
1070static 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
1086static 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
1102static 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
1146static 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
1530static 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
1539struct prioritised_indexes {
1540    uint32_t index;
1541    int32_t priority;
1542};
1543
1544int 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
1683fail:
1684    pa__done(m);
1685
1686    if (ma)
1687        pa_modargs_free(ma);
1688
1689    return -1;
1690}
1691
1692void 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