]> code.delx.au - pulseaudio/blob - src/modules/module-stream-restore.c
modules: pass properly initialized userdata pointers to various hooks
[pulseaudio] / src / modules / module-stream-restore.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2008 Lennart Poettering
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33
34 #include <pulse/xmalloc.h>
35 #include <pulse/volume.h>
36 #include <pulse/timeval.h>
37 #include <pulse/util.h>
38 #include <pulse/rtclock.h>
39
40 #include <pulsecore/core-error.h>
41 #include <pulsecore/module.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/modargs.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/core-subscribe.h>
46 #include <pulsecore/sink-input.h>
47 #include <pulsecore/source-output.h>
48 #include <pulsecore/namereg.h>
49 #include <pulsecore/protocol-native.h>
50 #include <pulsecore/pstream.h>
51 #include <pulsecore/pstream-util.h>
52 #include <pulsecore/database.h>
53
54 #include "module-stream-restore-symdef.h"
55
56 PA_MODULE_AUTHOR("Lennart Poettering");
57 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute/device state of streams");
58 PA_MODULE_VERSION(PACKAGE_VERSION);
59 PA_MODULE_LOAD_ONCE(TRUE);
60 PA_MODULE_USAGE(
61 "restore_device=<Save/restore sinks/sources?> "
62 "restore_volume=<Save/restore volumes?> "
63 "restore_muted=<Save/restore muted states?> "
64 "on_hotplug=<When new device becomes available, recheck streams?> "
65 "on_rescue=<When device becomes unavailable, recheck streams?>");
66
67 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
68 #define IDENTIFICATION_PROPERTY "module-stream-restore.id"
69
70 static const char* const valid_modargs[] = {
71 "restore_device",
72 "restore_volume",
73 "restore_muted",
74 "on_hotplug",
75 "on_rescue",
76 NULL
77 };
78
79 struct userdata {
80 pa_core *core;
81 pa_module *module;
82 pa_subscription *subscription;
83 pa_hook_slot
84 *sink_input_new_hook_slot,
85 *sink_input_fixate_hook_slot,
86 *source_output_new_hook_slot,
87 *sink_put_hook_slot,
88 *source_put_hook_slot,
89 *sink_unlink_hook_slot,
90 *source_unlink_hook_slot,
91 *connection_unlink_hook_slot;
92 pa_time_event *save_time_event;
93 pa_database* database;
94
95 pa_bool_t restore_device:1;
96 pa_bool_t restore_volume:1;
97 pa_bool_t restore_muted:1;
98 pa_bool_t on_hotplug:1;
99 pa_bool_t on_rescue:1;
100
101 pa_native_protocol *protocol;
102 pa_idxset *subscribed;
103 };
104
105 #define ENTRY_VERSION 2
106
107 struct entry {
108 uint8_t version;
109 pa_bool_t muted_valid:1, volume_valid:1, device_valid:1;
110 pa_bool_t muted:1;
111 pa_channel_map channel_map;
112 pa_cvolume volume;
113 char device[PA_NAME_MAX];
114 } PA_GCC_PACKED;
115
116 enum {
117 SUBCOMMAND_TEST,
118 SUBCOMMAND_READ,
119 SUBCOMMAND_WRITE,
120 SUBCOMMAND_DELETE,
121 SUBCOMMAND_SUBSCRIBE,
122 SUBCOMMAND_EVENT
123 };
124
125 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
126 struct userdata *u = userdata;
127
128 pa_assert(a);
129 pa_assert(e);
130 pa_assert(u);
131
132 pa_assert(e == u->save_time_event);
133 u->core->mainloop->time_free(u->save_time_event);
134 u->save_time_event = NULL;
135
136 pa_database_sync(u->database);
137 pa_log_info("Synced.");
138 }
139
140 static char *get_name(pa_proplist *p, const char *prefix) {
141 const char *r;
142 char *t;
143
144 if (!p)
145 return NULL;
146
147 if ((r = pa_proplist_gets(p, IDENTIFICATION_PROPERTY)))
148 return pa_xstrdup(r);
149
150 if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE)))
151 t = pa_sprintf_malloc("%s-by-media-role:%s", prefix, r);
152 else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_ID)))
153 t = pa_sprintf_malloc("%s-by-application-id:%s", prefix, r);
154 else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME)))
155 t = pa_sprintf_malloc("%s-by-application-name:%s", prefix, r);
156 else if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_NAME)))
157 t = pa_sprintf_malloc("%s-by-media-name:%s", prefix, r);
158 else
159 t = pa_sprintf_malloc("%s-fallback:%s", prefix, r);
160
161 pa_proplist_sets(p, IDENTIFICATION_PROPERTY, t);
162 return t;
163 }
164
165 static struct entry* read_entry(struct userdata *u, const char *name) {
166 pa_datum key, data;
167 struct entry *e;
168
169 pa_assert(u);
170 pa_assert(name);
171
172 key.data = (char*) name;
173 key.size = strlen(name);
174
175 pa_zero(data);
176
177 if (!pa_database_get(u->database, &key, &data))
178 goto fail;
179
180 if (data.size != sizeof(struct entry)) {
181 /* This is probably just a database upgrade, hence let's not
182 * consider this more than a debug message */
183 pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
184 goto fail;
185 }
186
187 e = (struct entry*) data.data;
188
189 if (e->version != ENTRY_VERSION) {
190 pa_log_debug("Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring.", name);
191 goto fail;
192 }
193
194 if (!memchr(e->device, 0, sizeof(e->device))) {
195 pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name);
196 goto fail;
197 }
198
199 if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
200 pa_log_warn("Invalid device name stored in database for stream %s", name);
201 goto fail;
202 }
203
204 if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
205 pa_log_warn("Invalid channel map stored in database for stream %s", name);
206 goto fail;
207 }
208
209 if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) {
210 pa_log_warn("Invalid volume stored in database for stream %s", name);
211 goto fail;
212 }
213
214 return e;
215
216 fail:
217
218 pa_datum_free(&data);
219 return NULL;
220 }
221
222 static void trigger_save(struct userdata *u) {
223 pa_native_connection *c;
224 uint32_t idx;
225
226 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
227 pa_tagstruct *t;
228
229 t = pa_tagstruct_new(NULL, 0);
230 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
231 pa_tagstruct_putu32(t, 0);
232 pa_tagstruct_putu32(t, u->module->index);
233 pa_tagstruct_puts(t, u->module->name);
234 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
235
236 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
237 }
238
239 if (u->save_time_event)
240 return;
241
242 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
243 }
244
245 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
246 pa_cvolume t;
247
248 pa_assert(a);
249 pa_assert(b);
250
251 if (a->device_valid != b->device_valid ||
252 (a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
253 return FALSE;
254
255 if (a->muted_valid != b->muted_valid ||
256 (a->muted_valid && (a->muted != b->muted)))
257 return FALSE;
258
259 t = b->volume;
260 if (a->volume_valid != b->volume_valid ||
261 (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
262 return FALSE;
263
264 return TRUE;
265 }
266
267 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
268 struct userdata *u = userdata;
269 struct entry entry, *old;
270 char *name;
271 pa_datum key, data;
272
273 pa_assert(c);
274 pa_assert(u);
275
276 if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
277 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
278 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
279 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
280 return;
281
282 pa_zero(entry);
283 entry.version = ENTRY_VERSION;
284
285 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
286 pa_sink_input *sink_input;
287
288 if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx)))
289 return;
290
291 if (!(name = get_name(sink_input->proplist, "sink-input")))
292 return;
293
294 if ((old = read_entry(u, name)))
295 entry = *old;
296
297 if (sink_input->save_volume) {
298 entry.channel_map = sink_input->channel_map;
299 pa_sink_input_get_volume(sink_input, &entry.volume, FALSE);
300 entry.volume_valid = TRUE;
301 }
302
303 if (sink_input->save_muted) {
304 entry.muted = pa_sink_input_get_mute(sink_input);
305 entry.muted_valid = TRUE;
306 }
307
308 if (sink_input->save_sink) {
309 pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
310 entry.device_valid = TRUE;
311 }
312
313 } else {
314 pa_source_output *source_output;
315
316 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
317
318 if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx)))
319 return;
320
321 if (!(name = get_name(source_output->proplist, "source-output")))
322 return;
323
324 if ((old = read_entry(u, name)))
325 entry = *old;
326
327 if (source_output->save_source) {
328 pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
329 entry.device_valid = source_output->save_source;
330 }
331 }
332
333 if (old) {
334
335 if (entries_equal(old, &entry)) {
336 pa_xfree(old);
337 pa_xfree(name);
338 return;
339 }
340
341 pa_xfree(old);
342 }
343
344 key.data = name;
345 key.size = strlen(name);
346
347 data.data = &entry;
348 data.size = sizeof(entry);
349
350 pa_log_info("Storing volume/mute/device for stream %s.", name);
351
352 pa_database_set(u->database, &key, &data, TRUE);
353
354 pa_xfree(name);
355
356 trigger_save(u);
357 }
358
359 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
360 char *name;
361 struct entry *e;
362
363 pa_assert(c);
364 pa_assert(new_data);
365 pa_assert(u);
366 pa_assert(u->restore_device);
367
368 if (!(name = get_name(new_data->proplist, "sink-input")))
369 return PA_HOOK_OK;
370
371 if ((e = read_entry(u, name))) {
372
373 if (e->device_valid) {
374 pa_sink *s;
375
376 if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
377 if (!new_data->sink) {
378 pa_log_info("Restoring device for stream %s.", name);
379 new_data->sink = s;
380 new_data->save_sink = TRUE;
381 } else
382 pa_log_debug("Not restoring device for stream %s, because already set.", name);
383 }
384 }
385
386 pa_xfree(e);
387 }
388
389 pa_xfree(name);
390
391 return PA_HOOK_OK;
392 }
393
394 static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
395 char *name;
396 struct entry *e;
397
398 pa_assert(c);
399 pa_assert(new_data);
400 pa_assert(u);
401 pa_assert(u->restore_volume || u->restore_muted);
402
403 if (!(name = get_name(new_data->proplist, "sink-input")))
404 return PA_HOOK_OK;
405
406 if ((e = read_entry(u, name))) {
407
408 if (u->restore_volume && e->volume_valid) {
409
410 if (!new_data->volume_is_set) {
411 pa_cvolume v;
412
413 pa_log_info("Restoring volume for sink input %s.", name);
414
415 v = e->volume;
416 pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
417 pa_sink_input_new_data_set_volume(new_data, &v);
418
419 new_data->volume_is_absolute = FALSE;
420 new_data->save_volume = TRUE;
421 } else
422 pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
423 }
424
425 if (u->restore_muted && e->muted_valid) {
426
427 if (!new_data->muted_is_set) {
428 pa_log_info("Restoring mute state for sink input %s.", name);
429 pa_sink_input_new_data_set_muted(new_data, e->muted);
430 new_data->save_muted = TRUE;
431 } else
432 pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
433 }
434
435 pa_xfree(e);
436 }
437
438 pa_xfree(name);
439
440 return PA_HOOK_OK;
441 }
442
443 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
444 char *name;
445 struct entry *e;
446
447 pa_assert(c);
448 pa_assert(new_data);
449 pa_assert(u);
450 pa_assert(u->restore_device);
451
452 if (new_data->direct_on_input)
453 return PA_HOOK_OK;
454
455 if (!(name = get_name(new_data->proplist, "source-output")))
456 return PA_HOOK_OK;
457
458 if ((e = read_entry(u, name))) {
459 pa_source *s;
460
461 if (e->device_valid) {
462 if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) {
463 if (!new_data->source) {
464 pa_log_info("Restoring device for stream %s.", name);
465 new_data->source = s;
466 new_data->save_source = TRUE;
467 } else
468 pa_log_debug("Not restoring device for stream %s, because already set", name);
469 }
470 }
471
472 pa_xfree(e);
473 }
474
475 pa_xfree(name);
476
477 return PA_HOOK_OK;
478 }
479
480 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
481 pa_sink_input *si;
482 uint32_t idx;
483
484 pa_assert(c);
485 pa_assert(sink);
486 pa_assert(u);
487 pa_assert(u->on_hotplug && u->restore_device);
488
489 PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
490 char *name;
491 struct entry *e;
492
493 if (si->sink == sink)
494 continue;
495
496 if (si->save_sink)
497 continue;
498
499 if (!(name = get_name(si->proplist, "sink-input")))
500 continue;
501
502 if ((e = read_entry(u, name))) {
503 if (e->device_valid && pa_streq(e->device, sink->name))
504 pa_sink_input_move_to(si, sink, TRUE);
505
506 pa_xfree(e);
507 }
508
509 pa_xfree(name);
510 }
511
512 return PA_HOOK_OK;
513 }
514
515 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
516 pa_source_output *so;
517 uint32_t idx;
518
519 pa_assert(c);
520 pa_assert(source);
521 pa_assert(u);
522 pa_assert(u->on_hotplug && u->restore_device);
523
524 PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
525 char *name;
526 struct entry *e;
527
528 if (so->source == source)
529 continue;
530
531 if (so->save_source)
532 continue;
533
534 if (so->direct_on_input)
535 continue;
536
537 if (!(name = get_name(so->proplist, "source-input")))
538 continue;
539
540 if ((e = read_entry(u, name))) {
541 if (e->device_valid && pa_streq(e->device, source->name))
542 pa_source_output_move_to(so, source, TRUE);
543
544 pa_xfree(e);
545 }
546
547 pa_xfree(name);
548 }
549
550 return PA_HOOK_OK;
551 }
552
553 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
554 pa_sink_input *si;
555 uint32_t idx;
556
557 pa_assert(c);
558 pa_assert(sink);
559 pa_assert(u);
560 pa_assert(u->on_rescue && u->restore_device);
561
562 /* There's no point in doing anything if the core is shut down anyway */
563 if (c->state == PA_CORE_SHUTDOWN)
564 return PA_HOOK_OK;
565
566 PA_IDXSET_FOREACH(si, sink->inputs, idx) {
567 char *name;
568 struct entry *e;
569
570 if (!(name = get_name(si->proplist, "sink-input")))
571 continue;
572
573 if ((e = read_entry(u, name))) {
574
575 if (e->device_valid) {
576 pa_sink *d;
577
578 if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) && d != sink)
579 pa_sink_input_move_to(si, d, TRUE);
580 }
581
582 pa_xfree(e);
583 }
584
585 pa_xfree(name);
586 }
587
588 return PA_HOOK_OK;
589 }
590
591 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
592 pa_source_output *so;
593 uint32_t idx;
594
595 pa_assert(c);
596 pa_assert(source);
597 pa_assert(u);
598 pa_assert(u->on_rescue && u->restore_device);
599
600 /* There's no point in doing anything if the core is shut down anyway */
601 if (c->state == PA_CORE_SHUTDOWN)
602 return PA_HOOK_OK;
603
604 PA_IDXSET_FOREACH(so, source->outputs, idx) {
605 char *name;
606 struct entry *e;
607
608 if (!(name = get_name(so->proplist, "source-output")))
609 continue;
610
611 if ((e = read_entry(u, name))) {
612
613 if (e->device_valid) {
614 pa_source *d;
615
616 if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) && d != source)
617 pa_source_output_move_to(so, d, TRUE);
618 }
619
620 pa_xfree(e);
621 }
622
623 pa_xfree(name);
624 }
625
626 return PA_HOOK_OK;
627 }
628
629 #define EXT_VERSION 1
630
631 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
632 pa_sink_input *si;
633 pa_source_output *so;
634 uint32_t idx;
635
636 pa_assert(u);
637 pa_assert(name);
638 pa_assert(e);
639
640 for (si = pa_idxset_first(u->core->sink_inputs, &idx); si; si = pa_idxset_next(u->core->sink_inputs, &idx)) {
641 char *n;
642 pa_sink *s;
643
644 if (!(n = get_name(si->proplist, "sink-input")))
645 continue;
646
647 if (!pa_streq(name, n)) {
648 pa_xfree(n);
649 continue;
650 }
651 pa_xfree(n);
652
653 if (u->restore_volume && e->volume_valid) {
654 pa_cvolume v;
655
656 v = e->volume;
657 pa_log_info("Restoring volume for sink input %s.", name);
658 pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map), FALSE, FALSE);
659 }
660
661 if (u->restore_muted && e->muted_valid) {
662 pa_log_info("Restoring mute state for sink input %s.", name);
663 pa_sink_input_set_mute(si, e->muted, FALSE);
664 }
665
666 if (u->restore_device &&
667 e->device_valid &&
668 (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
669
670 pa_log_info("Restoring device for stream %s.", name);
671 pa_sink_input_move_to(si, s, FALSE);
672 }
673 }
674
675 for (so = pa_idxset_first(u->core->source_outputs, &idx); so; so = pa_idxset_next(u->core->source_outputs, &idx)) {
676 char *n;
677 pa_source *s;
678
679 if (!(n = get_name(so->proplist, "source-output")))
680 continue;
681
682 if (!pa_streq(name, n)) {
683 pa_xfree(n);
684 continue;
685 }
686 pa_xfree(n);
687
688 if (u->restore_device &&
689 e->device_valid &&
690 (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
691
692 pa_log_info("Restoring device for stream %s.", name);
693 pa_source_output_move_to(so, s, FALSE);
694 }
695 }
696 }
697
698 #if 0
699 static void dump_database(struct userdata *u) {
700 pa_datum key;
701 pa_bool_t done;
702
703 done = !pa_database_first(u->database, &key, NULL);
704
705 while (!done) {
706 pa_datum next_key;
707 struct entry *e;
708 char *name;
709
710 done = !pa_database_next(u->database, &key, &next_key, NULL);
711
712 name = pa_xstrndup(key.data, key.size);
713 pa_datum_free(&key);
714
715 if ((e = read_entry(u, name))) {
716 char t[256];
717 pa_log("name=%s", name);
718 pa_log("device=%s %s", e->device, pa_yes_no(e->device_valid));
719 pa_log("channel_map=%s", pa_channel_map_snprint(t, sizeof(t), &e->channel_map));
720 pa_log("volume=%s %s", pa_cvolume_snprint(t, sizeof(t), &e->volume), pa_yes_no(e->volume_valid));
721 pa_log("mute=%s %s", pa_yes_no(e->muted), pa_yes_no(e->volume_valid));
722 pa_xfree(e);
723 }
724
725 pa_xfree(name);
726
727 key = next_key;
728 }
729 }
730 #endif
731
732 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
733 struct userdata *u;
734 uint32_t command;
735 pa_tagstruct *reply = NULL;
736
737 pa_assert(p);
738 pa_assert(m);
739 pa_assert(c);
740 pa_assert(t);
741
742 u = m->userdata;
743
744 if (pa_tagstruct_getu32(t, &command) < 0)
745 goto fail;
746
747 reply = pa_tagstruct_new(NULL, 0);
748 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
749 pa_tagstruct_putu32(reply, tag);
750
751 switch (command) {
752 case SUBCOMMAND_TEST: {
753 if (!pa_tagstruct_eof(t))
754 goto fail;
755
756 pa_tagstruct_putu32(reply, EXT_VERSION);
757 break;
758 }
759
760 case SUBCOMMAND_READ: {
761 pa_datum key;
762 pa_bool_t done;
763
764 if (!pa_tagstruct_eof(t))
765 goto fail;
766
767 done = !pa_database_first(u->database, &key, NULL);
768
769 while (!done) {
770 pa_datum next_key;
771 struct entry *e;
772 char *name;
773
774 done = !pa_database_next(u->database, &key, &next_key, NULL);
775
776 name = pa_xstrndup(key.data, key.size);
777 pa_datum_free(&key);
778
779 if ((e = read_entry(u, name))) {
780 pa_cvolume r;
781 pa_channel_map cm;
782
783 pa_tagstruct_puts(reply, name);
784 pa_tagstruct_put_channel_map(reply, e->volume_valid ? &e->channel_map : pa_channel_map_init(&cm));
785 pa_tagstruct_put_cvolume(reply, e->volume_valid ? &e->volume : pa_cvolume_init(&r));
786 pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
787 pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
788
789 pa_xfree(e);
790 }
791
792 pa_xfree(name);
793
794 key = next_key;
795 }
796
797 break;
798 }
799
800 case SUBCOMMAND_WRITE: {
801 uint32_t mode;
802 pa_bool_t apply_immediately = FALSE;
803
804 if (pa_tagstruct_getu32(t, &mode) < 0 ||
805 pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
806 goto fail;
807
808 if (mode != PA_UPDATE_MERGE &&
809 mode != PA_UPDATE_REPLACE &&
810 mode != PA_UPDATE_SET)
811 goto fail;
812
813 if (mode == PA_UPDATE_SET)
814 pa_database_clear(u->database);
815
816 while (!pa_tagstruct_eof(t)) {
817 const char *name, *device;
818 pa_bool_t muted;
819 struct entry entry;
820 pa_datum key, data;
821
822 pa_zero(entry);
823 entry.version = ENTRY_VERSION;
824
825 if (pa_tagstruct_gets(t, &name) < 0 ||
826 pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
827 pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
828 pa_tagstruct_gets(t, &device) < 0 ||
829 pa_tagstruct_get_boolean(t, &muted) < 0)
830 goto fail;
831
832 if (!name || !*name)
833 goto fail;
834
835 entry.volume_valid = entry.volume.channels > 0;
836
837 if (entry.volume_valid)
838 if (!pa_cvolume_compatible_with_channel_map(&entry.volume, &entry.channel_map))
839 goto fail;
840
841 entry.muted = muted;
842 entry.muted_valid = TRUE;
843
844 if (device)
845 pa_strlcpy(entry.device, device, sizeof(entry.device));
846 entry.device_valid = !!entry.device[0];
847
848 if (entry.device_valid &&
849 !pa_namereg_is_valid_name(entry.device))
850 goto fail;
851
852 key.data = (char*) name;
853 key.size = strlen(name);
854
855 data.data = &entry;
856 data.size = sizeof(entry);
857
858 if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
859 if (apply_immediately)
860 apply_entry(u, name, &entry);
861 }
862
863 trigger_save(u);
864
865 break;
866 }
867
868 case SUBCOMMAND_DELETE:
869
870 while (!pa_tagstruct_eof(t)) {
871 const char *name;
872 pa_datum key;
873
874 if (pa_tagstruct_gets(t, &name) < 0)
875 goto fail;
876
877 key.data = (char*) name;
878 key.size = strlen(name);
879
880 pa_database_unset(u->database, &key);
881 }
882
883 trigger_save(u);
884
885 break;
886
887 case SUBCOMMAND_SUBSCRIBE: {
888
889 pa_bool_t enabled;
890
891 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
892 !pa_tagstruct_eof(t))
893 goto fail;
894
895 if (enabled)
896 pa_idxset_put(u->subscribed, c, NULL);
897 else
898 pa_idxset_remove_by_data(u->subscribed, c, NULL);
899
900 break;
901 }
902
903 default:
904 goto fail;
905 }
906
907 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
908 return 0;
909
910 fail:
911
912 if (reply)
913 pa_tagstruct_free(reply);
914
915 return -1;
916 }
917
918 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
919 pa_assert(p);
920 pa_assert(c);
921 pa_assert(u);
922
923 pa_idxset_remove_by_data(u->subscribed, c, NULL);
924 return PA_HOOK_OK;
925 }
926
927 int pa__init(pa_module*m) {
928 pa_modargs *ma = NULL;
929 struct userdata *u;
930 char *fname;
931 pa_sink_input *si;
932 pa_source_output *so;
933 uint32_t idx;
934 pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE, on_hotplug = TRUE, on_rescue = TRUE;
935
936 pa_assert(m);
937
938 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
939 pa_log("Failed to parse module arguments");
940 goto fail;
941 }
942
943 if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
944 pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
945 pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 ||
946 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
947 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
948 pa_log("restore_device=, restore_volume=, restore_muted=, on_hotplug= and on_rescue= expect boolean arguments");
949 goto fail;
950 }
951
952 if (!restore_muted && !restore_volume && !restore_device)
953 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring device enabled!");
954
955 m->userdata = u = pa_xnew0(struct userdata, 1);
956 u->core = m->core;
957 u->module = m;
958 u->restore_device = restore_device;
959 u->restore_volume = restore_volume;
960 u->restore_muted = restore_muted;
961 u->on_hotplug = on_hotplug;
962 u->on_rescue = on_rescue;
963 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
964
965 u->protocol = pa_native_protocol_get(m->core);
966 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
967
968 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);
969
970 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
971
972 if (restore_device) {
973 /* A little bit earlier than module-intended-roles ... */
974 u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
975 u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
976 }
977
978 if (restore_device && on_hotplug) {
979 /* A little bit earlier than module-intended-roles ... */
980 u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_callback, u);
981 u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) source_put_hook_callback, u);
982 }
983
984 if (restore_device && on_rescue) {
985 /* A little bit earlier than module-intended-roles, module-rescue-streams, ... */
986 u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_unlink_hook_callback, u);
987 u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_unlink_hook_callback, u);
988 }
989
990 if (restore_volume || restore_muted)
991 u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
992
993 if (!(fname = pa_state_path("stream-volumes", TRUE)))
994 goto fail;
995
996 if (!(u->database = pa_database_open(fname, TRUE))) {
997 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
998 pa_xfree(fname);
999 goto fail;
1000 }
1001
1002 pa_log_info("Sucessfully opened database file '%s'.", fname);
1003 pa_xfree(fname);
1004
1005 PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
1006 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
1007
1008 PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
1009 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
1010
1011 pa_modargs_free(ma);
1012 return 0;
1013
1014 fail:
1015 pa__done(m);
1016
1017 if (ma)
1018 pa_modargs_free(ma);
1019
1020 return -1;
1021 }
1022
1023 void pa__done(pa_module*m) {
1024 struct userdata* u;
1025
1026 pa_assert(m);
1027
1028 if (!(u = m->userdata))
1029 return;
1030
1031 if (u->subscription)
1032 pa_subscription_free(u->subscription);
1033
1034 if (u->sink_input_new_hook_slot)
1035 pa_hook_slot_free(u->sink_input_new_hook_slot);
1036 if (u->sink_input_fixate_hook_slot)
1037 pa_hook_slot_free(u->sink_input_fixate_hook_slot);
1038 if (u->source_output_new_hook_slot)
1039 pa_hook_slot_free(u->source_output_new_hook_slot);
1040
1041 if (u->sink_put_hook_slot)
1042 pa_hook_slot_free(u->sink_put_hook_slot);
1043 if (u->source_put_hook_slot)
1044 pa_hook_slot_free(u->source_put_hook_slot);
1045
1046 if (u->sink_unlink_hook_slot)
1047 pa_hook_slot_free(u->sink_unlink_hook_slot);
1048 if (u->source_unlink_hook_slot)
1049 pa_hook_slot_free(u->source_unlink_hook_slot);
1050
1051 if (u->connection_unlink_hook_slot)
1052 pa_hook_slot_free(u->connection_unlink_hook_slot);
1053
1054 if (u->save_time_event)
1055 u->core->mainloop->time_free(u->save_time_event);
1056
1057 if (u->database)
1058 pa_database_close(u->database);
1059
1060 if (u->protocol) {
1061 pa_native_protocol_remove_ext(u->protocol, m);
1062 pa_native_protocol_unref(u->protocol);
1063 }
1064
1065 if (u->subscribed)
1066 pa_idxset_free(u->subscribed, NULL, NULL);
1067
1068 pa_xfree(u);
1069 }