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