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