]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Fix copy+paste code that looped over the tagstruct when not necessary
[pulseaudio] / src / modules / module-device-manager.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2009 Colin Guthrie
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/rtclock.h>
40
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/module.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/modargs.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/core-subscribe.h>
47 #include <pulsecore/sink-input.h>
48 #include <pulsecore/source-output.h>
49 #include <pulsecore/namereg.h>
50 #include <pulsecore/protocol-native.h>
51 #include <pulsecore/pstream.h>
52 #include <pulsecore/pstream-util.h>
53 #include <pulsecore/database.h>
54
55 #include "module-device-manager-symdef.h"
56
57 PA_MODULE_AUTHOR("Colin Guthrie");
58 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present");
59 PA_MODULE_VERSION(PACKAGE_VERSION);
60 PA_MODULE_LOAD_ONCE(TRUE);
61 PA_MODULE_USAGE("This module does not take any arguments");
62
63 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
64
65 static const char* const valid_modargs[] = {
66 NULL
67 };
68
69 struct userdata {
70 pa_core *core;
71 pa_module *module;
72 pa_subscription *subscription;
73 pa_hook_slot
74 *sink_new_hook_slot,
75 *source_new_hook_slot,
76 *connection_unlink_hook_slot;
77 pa_time_event *save_time_event;
78 pa_database *database;
79
80 pa_native_protocol *protocol;
81 pa_idxset *subscribed;
82
83 pa_bool_t role_device_priority_routing;
84 pa_bool_t stream_restore_used;
85 pa_bool_t checked_stream_restore;
86 };
87
88 #define ENTRY_VERSION 1
89
90 struct entry {
91 uint8_t version;
92 char description[PA_NAME_MAX];
93 } PA_GCC_PACKED;
94
95 enum {
96 SUBCOMMAND_TEST,
97 SUBCOMMAND_READ,
98 SUBCOMMAND_WRITE,
99 SUBCOMMAND_DELETE,
100 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
101 SUBCOMMAND_SUBSCRIBE,
102 SUBCOMMAND_EVENT
103 };
104
105 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
106 struct userdata *u = userdata;
107
108 pa_assert(a);
109 pa_assert(e);
110 pa_assert(u);
111
112 pa_assert(e == u->save_time_event);
113 u->core->mainloop->time_free(u->save_time_event);
114 u->save_time_event = NULL;
115
116 pa_database_sync(u->database);
117 pa_log_info("Synced.");
118 }
119
120 static struct entry* read_entry(struct userdata *u, const char *name) {
121 pa_datum key, data;
122 struct entry *e;
123
124 pa_assert(u);
125 pa_assert(name);
126
127 key.data = (char*) name;
128 key.size = strlen(name);
129
130 pa_zero(data);
131
132 if (!pa_database_get(u->database, &key, &data))
133 goto fail;
134
135 if (data.size != sizeof(struct entry)) {
136 pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
137 goto fail;
138 }
139
140 e = (struct entry*) data.data;
141
142 if (e->version != ENTRY_VERSION) {
143 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
144 goto fail;
145 }
146
147 if (!memchr(e->description, 0, sizeof(e->description))) {
148 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
149 goto fail;
150 }
151
152 return e;
153
154 fail:
155
156 pa_datum_free(&data);
157 return NULL;
158 }
159
160 static void trigger_save(struct userdata *u) {
161 if (u->save_time_event)
162 return;
163
164 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
165 }
166
167 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
168 if (strncmp(a->description, b->description, sizeof(a->description)))
169 return FALSE;
170
171 return TRUE;
172 }
173
174 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
175 struct userdata *u = userdata;
176 struct entry entry, *old = NULL;
177 char *name = NULL;
178 pa_datum key, data;
179
180 pa_assert(c);
181 pa_assert(u);
182
183 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
184 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
185 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
186 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
187 return;
188
189 pa_zero(entry);
190 entry.version = ENTRY_VERSION;
191
192 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
193 pa_sink *sink;
194
195 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
196 return;
197
198 name = pa_sprintf_malloc("sink:%s", sink->name);
199
200 if ((old = read_entry(u, name)))
201 entry = *old;
202
203 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
204
205 } else {
206 pa_source *source;
207
208 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
209
210 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
211 return;
212
213 if (source->monitor_of)
214 return;
215
216 name = pa_sprintf_malloc("source:%s", source->name);
217
218 if ((old = read_entry(u, name)))
219 entry = *old;
220
221 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
222 }
223
224 if (old) {
225
226 if (entries_equal(old, &entry)) {
227 pa_xfree(old);
228 pa_xfree(name);
229 return;
230 }
231
232 pa_xfree(old);
233 }
234
235 key.data = name;
236 key.size = strlen(name);
237
238 data.data = &entry;
239 data.size = sizeof(entry);
240
241 pa_log_info("Storing device description for %s.", name);
242
243 pa_database_set(u->database, &key, &data, TRUE);
244
245 pa_xfree(name);
246
247 trigger_save(u);
248 }
249
250 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
251 char *name;
252 struct entry *e;
253
254 pa_assert(c);
255 pa_assert(new_data);
256 pa_assert(u);
257
258 name = pa_sprintf_malloc("sink:%s", new_data->name);
259
260 if ((e = read_entry(u, name))) {
261 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
262 pa_log_info("Restoring description for sink %s.", new_data->name);
263 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
264 }
265
266 pa_xfree(e);
267 }
268
269 pa_xfree(name);
270
271 return PA_HOOK_OK;
272 }
273
274 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
275 char *name;
276 struct entry *e;
277
278 pa_assert(c);
279 pa_assert(new_data);
280 pa_assert(u);
281
282 name = pa_sprintf_malloc("source:%s", new_data->name);
283
284 if ((e = read_entry(u, name))) {
285
286 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
287 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
288 pa_log_info("Restoring description for sink %s.", new_data->name);
289 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
290 }
291
292 pa_xfree(e);
293 }
294
295 pa_xfree(name);
296
297 return PA_HOOK_OK;
298 }
299
300 static char *get_name(const char *key, const char *prefix) {
301 char *t;
302
303 if (strncmp(key, prefix, strlen(prefix)))
304 return NULL;
305
306 t = pa_xstrdup(key + strlen(prefix));
307 return t;
308 }
309
310 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
311 pa_sink *sink;
312 pa_source *source;
313 uint32_t idx;
314 char *n;
315
316 pa_assert(u);
317 pa_assert(name);
318 pa_assert(e);
319
320 if ((n = get_name(name, "sink:"))) {
321 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
322 if (!pa_streq(sink->name, n)) {
323 continue;
324 }
325
326 pa_log_info("Setting description for sink %s.", sink->name);
327 pa_sink_set_description(sink, e->description);
328 }
329 pa_xfree(n);
330 }
331 else if ((n = get_name(name, "source:"))) {
332 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
333 if (!pa_streq(source->name, n)) {
334 continue;
335 }
336
337 if (source->monitor_of) {
338 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
339 continue;
340 }
341
342 pa_log_info("Setting description for source %s.", source->name);
343 pa_source_set_description(source, e->description);
344 }
345 pa_xfree(n);
346 }
347 }
348
349 #define EXT_VERSION 1
350
351 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
352 struct userdata *u;
353 uint32_t command;
354 pa_tagstruct *reply = NULL;
355
356 pa_assert(p);
357 pa_assert(m);
358 pa_assert(c);
359 pa_assert(t);
360
361 u = m->userdata;
362
363 if (pa_tagstruct_getu32(t, &command) < 0)
364 goto fail;
365
366 reply = pa_tagstruct_new(NULL, 0);
367 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
368 pa_tagstruct_putu32(reply, tag);
369
370 switch (command) {
371 case SUBCOMMAND_TEST: {
372 if (!pa_tagstruct_eof(t))
373 goto fail;
374
375 pa_tagstruct_putu32(reply, EXT_VERSION);
376 break;
377 }
378
379 case SUBCOMMAND_READ: {
380 pa_datum key;
381 pa_bool_t done;
382
383 if (!pa_tagstruct_eof(t))
384 goto fail;
385
386 done = !pa_database_first(u->database, &key, NULL);
387
388 while (!done) {
389 pa_datum next_key;
390 struct entry *e;
391 char *name;
392
393 done = !pa_database_next(u->database, &key, &next_key, NULL);
394
395 name = pa_xstrndup(key.data, key.size);
396 pa_datum_free(&key);
397
398 if ((e = read_entry(u, name))) {
399 pa_tagstruct_puts(reply, name);
400 pa_tagstruct_puts(reply, e->description);
401
402 pa_xfree(e);
403 }
404
405 pa_xfree(name);
406
407 key = next_key;
408 }
409
410 break;
411 }
412
413 case SUBCOMMAND_WRITE: {
414 uint32_t mode;
415 pa_bool_t apply_immediately = FALSE;
416
417 if (pa_tagstruct_getu32(t, &mode) < 0 ||
418 pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
419 goto fail;
420
421 if (mode != PA_UPDATE_MERGE &&
422 mode != PA_UPDATE_REPLACE &&
423 mode != PA_UPDATE_SET)
424 goto fail;
425
426 if (mode == PA_UPDATE_SET)
427 pa_database_clear(u->database);
428
429 while (!pa_tagstruct_eof(t)) {
430 const char *name, *description;
431 struct entry entry;
432 pa_datum key, data;
433
434 pa_zero(entry);
435 entry.version = ENTRY_VERSION;
436
437 if (pa_tagstruct_gets(t, &name) < 0 ||
438 pa_tagstruct_gets(t, &description) < 0)
439 goto fail;
440
441 if (!name || !*name)
442 goto fail;
443
444 pa_strlcpy(entry.description, description, sizeof(entry.description));
445
446 key.data = (char*) name;
447 key.size = strlen(name);
448
449 data.data = &entry;
450 data.size = sizeof(entry);
451
452 if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
453 if (apply_immediately)
454 apply_entry(u, name, &entry);
455 }
456
457 trigger_save(u);
458
459 break;
460 }
461
462 case SUBCOMMAND_DELETE:
463
464 while (!pa_tagstruct_eof(t)) {
465 const char *name;
466 pa_datum key;
467
468 if (pa_tagstruct_gets(t, &name) < 0)
469 goto fail;
470
471 key.data = (char*) name;
472 key.size = strlen(name);
473
474 pa_database_unset(u->database, &key);
475 }
476
477 trigger_save(u);
478
479 break;
480
481 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
482
483 pa_bool_t enable;
484 uint32_t sridx = PA_INVALID_INDEX;
485 uint32_t idx;
486 pa_module *module;
487
488 if (pa_tagstruct_get_boolean(t, &enable) < 0)
489 goto fail;
490
491 /* If this is the first run, check for stream restore module */
492 if (!u->checked_stream_restore) {
493 u->checked_stream_restore = TRUE;
494
495 for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
496 if (strcmp(module->name, "module-stream-restore") == 0) {
497 pa_log_debug("Detected module-stream-restore is currently in use");
498 u->stream_restore_used = TRUE;
499 sridx = module->index;
500 }
501 }
502 }
503
504 u->role_device_priority_routing = enable;
505 if (enable) {
506 if (u->stream_restore_used) {
507 if (PA_INVALID_INDEX == sridx) {
508 /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */
509 for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
510 if (strcmp(module->name, "module-stream-restore") == 0) {
511 sridx = module->index;
512 }
513 }
514 }
515 if (PA_INVALID_INDEX != sridx) {
516 pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing");
517 pa_module_unload_request_by_index(u->core, sridx, TRUE);
518 }
519 }
520 } else if (u->stream_restore_used) {
521 /* We want to reload module-stream-restore */
522 if (!pa_module_load(u->core, "module-stream-restore", ""))
523 pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing");
524 }
525
526 break;
527 }
528
529 case SUBCOMMAND_SUBSCRIBE: {
530
531 pa_bool_t enabled;
532
533 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
534 !pa_tagstruct_eof(t))
535 goto fail;
536
537 if (enabled)
538 pa_idxset_put(u->subscribed, c, NULL);
539 else
540 pa_idxset_remove_by_data(u->subscribed, c, NULL);
541
542 break;
543 }
544
545 default:
546 goto fail;
547 }
548
549 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
550 return 0;
551
552 fail:
553
554 if (reply)
555 pa_tagstruct_free(reply);
556
557 return -1;
558 }
559
560 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
561 pa_assert(p);
562 pa_assert(c);
563 pa_assert(u);
564
565 pa_idxset_remove_by_data(u->subscribed, c, NULL);
566 return PA_HOOK_OK;
567 }
568
569 int pa__init(pa_module*m) {
570 pa_modargs *ma = NULL;
571 struct userdata *u;
572 char *fname;
573 pa_sink *sink;
574 pa_source *source;
575 uint32_t idx;
576
577 pa_assert(m);
578
579 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
580 pa_log("Failed to parse module arguments");
581 goto fail;
582 }
583
584 m->userdata = u = pa_xnew0(struct userdata, 1);
585 u->core = m->core;
586 u->module = m;
587 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
588
589 u->protocol = pa_native_protocol_get(m->core);
590 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
591
592 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);
593
594 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
595
596 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);
597 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);
598
599 if (!(fname = pa_state_path("device-manager", TRUE)))
600 goto fail;
601
602 if (!(u->database = pa_database_open(fname, TRUE))) {
603 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
604 pa_xfree(fname);
605 goto fail;
606 }
607
608 pa_log_info("Sucessfully opened database file '%s'.", fname);
609 pa_xfree(fname);
610
611 for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
612 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
613
614 for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
615 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
616
617 pa_modargs_free(ma);
618 return 0;
619
620 fail:
621 pa__done(m);
622
623 if (ma)
624 pa_modargs_free(ma);
625
626 return -1;
627 }
628
629 void pa__done(pa_module*m) {
630 struct userdata* u;
631
632 pa_assert(m);
633
634 if (!(u = m->userdata))
635 return;
636
637 if (u->subscription)
638 pa_subscription_free(u->subscription);
639
640 if (u->sink_new_hook_slot)
641 pa_hook_slot_free(u->sink_new_hook_slot);
642 if (u->source_new_hook_slot)
643 pa_hook_slot_free(u->source_new_hook_slot);
644
645 if (u->save_time_event)
646 u->core->mainloop->time_free(u->save_time_event);
647
648 if (u->database)
649 pa_database_close(u->database);
650
651 if (u->protocol) {
652 pa_native_protocol_remove_ext(u->protocol, m);
653 pa_native_protocol_unref(u->protocol);
654 }
655
656 if (u->subscribed)
657 pa_idxset_free(u->subscribed, NULL, NULL);
658
659 pa_xfree(u);
660 }