]> code.delx.au - pulseaudio/blob - src/modules/module-stream-restore.c
add missing usage strings
[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 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 #include <gdbm.h>
34
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.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
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
64 #define SAVE_INTERVAL 10
65
66 static const char* const valid_modargs[] = {
67 "restore_device",
68 "restore_volume",
69 "restore_muted",
70 NULL
71 };
72
73 struct userdata {
74 pa_core *core;
75 pa_module *module;
76 pa_subscription *subscription;
77 pa_hook_slot
78 *sink_input_new_hook_slot,
79 *sink_input_fixate_hook_slot,
80 *source_output_new_hook_slot,
81 *connection_unlink_hook_slot;
82 pa_time_event *save_time_event;
83 GDBM_FILE gdbm_file;
84
85 pa_bool_t restore_device:1;
86 pa_bool_t restore_volume:1;
87 pa_bool_t restore_muted:1;
88
89 pa_native_protocol *protocol;
90 pa_idxset *subscribed;
91 };
92
93 struct entry {
94 pa_channel_map channel_map;
95 char device[PA_NAME_MAX];
96 pa_cvolume relative_volume;
97 pa_cvolume absolute_volume;
98 pa_bool_t muted:1;
99 pa_bool_t device_valid:1, relative_volume_valid:1, absolute_volume_valid:1, muted_valid:1;
100 };
101
102
103 enum {
104 SUBCOMMAND_TEST,
105 SUBCOMMAND_READ,
106 SUBCOMMAND_WRITE,
107 SUBCOMMAND_DELETE,
108 SUBCOMMAND_SUBSCRIBE,
109 SUBCOMMAND_EVENT
110 };
111
112 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
113 struct userdata *u = userdata;
114
115 pa_assert(a);
116 pa_assert(e);
117 pa_assert(tv);
118 pa_assert(u);
119
120 pa_assert(e == u->save_time_event);
121 u->core->mainloop->time_free(u->save_time_event);
122 u->save_time_event = NULL;
123
124 gdbm_sync(u->gdbm_file);
125 pa_log_info("Synced.");
126 }
127
128 static char *get_name(pa_proplist *p, const char *prefix) {
129 const char *r;
130
131 if (!p)
132 return NULL;
133
134 if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE)))
135 return pa_sprintf_malloc("%s-by-media-role:%s", prefix, r);
136 else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_ID)))
137 return pa_sprintf_malloc("%s-by-application-id:%s", prefix, r);
138 else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME)))
139 return pa_sprintf_malloc("%s-by-application-name:%s", prefix, r);
140 else if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_NAME)))
141 return pa_sprintf_malloc("%s-by-media-name:%s", prefix, r);
142
143 return pa_sprintf_malloc("%s-fallback:%s", prefix, r);
144 }
145
146 static struct entry* read_entry(struct userdata *u, const char *name) {
147 datum key, data;
148 struct entry *e;
149
150 pa_assert(u);
151 pa_assert(name);
152
153 key.dptr = (char*) name;
154 key.dsize = (int) strlen(name);
155
156 data = gdbm_fetch(u->gdbm_file, key);
157
158 if (!data.dptr)
159 goto fail;
160
161 if (data.dsize != sizeof(struct entry)) {
162 /* This is probably just a database upgrade, hence let's not
163 * consider this more than a debug message */
164 pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
165 goto fail;
166 }
167
168 e = (struct entry*) data.dptr;
169
170 if (!memchr(e->device, 0, sizeof(e->device))) {
171 pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name);
172 goto fail;
173 }
174
175 if (!(pa_channel_map_valid(&e->channel_map))) {
176 pa_log_warn("Invalid channel map stored in database for stream %s", name);
177 goto fail;
178 }
179
180 if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
181 pa_log_warn("Invalid device name stored in database for stream %s", name);
182 goto fail;
183 }
184
185 if ((e->relative_volume_valid && (!pa_cvolume_valid(&e->relative_volume) || e->relative_volume.channels != e->channel_map.channels)) ||
186 (e->absolute_volume_valid && (!pa_cvolume_valid(&e->absolute_volume) || e->absolute_volume.channels != e->channel_map.channels))) {
187 pa_log_warn("Invalid volume stored in database for stream %s", name);
188 goto fail;
189 }
190
191 return e;
192
193 fail:
194
195 pa_xfree(data.dptr);
196 return NULL;
197 }
198
199 static void trigger_save(struct userdata *u) {
200 struct timeval tv;
201 pa_native_connection *c;
202 uint32_t idx;
203
204 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
205 pa_tagstruct *t;
206
207 t = pa_tagstruct_new(NULL, 0);
208 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
209 pa_tagstruct_putu32(t, 0);
210 pa_tagstruct_putu32(t, u->module->index);
211 pa_tagstruct_puts(t, u->module->name);
212 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
213
214 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
215 }
216
217 if (u->save_time_event)
218 return;
219
220 pa_gettimeofday(&tv);
221 tv.tv_sec += SAVE_INTERVAL;
222 u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
223 }
224
225 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
226 pa_cvolume t;
227
228 pa_assert(a);
229 pa_assert(b);
230
231 if (a->device_valid != b->device_valid ||
232 (a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
233 return FALSE;
234
235 if (a->muted_valid != b->muted_valid ||
236 (a->muted && (a->muted != b->muted)))
237 return FALSE;
238
239 t = b->relative_volume;
240 if (a->relative_volume_valid != b->relative_volume_valid ||
241 (a->relative_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->relative_volume)))
242 return FALSE;
243
244 t = b->absolute_volume;
245 if (a->absolute_volume_valid != b->absolute_volume_valid ||
246 (a->absolute_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->absolute_volume)))
247 return FALSE;
248
249 return TRUE;
250 }
251
252 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
253 struct userdata *u = userdata;
254 struct entry entry, *old;
255 char *name;
256 datum key, data;
257
258 pa_assert(c);
259 pa_assert(u);
260
261 if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
262 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
263 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
264 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
265 return;
266
267 memset(&entry, 0, sizeof(entry));
268
269 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
270 pa_sink_input *sink_input;
271
272 if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx)))
273 return;
274
275 if (!(name = get_name(sink_input->proplist, "sink-input")))
276 return;
277
278 entry.channel_map = sink_input->channel_map;
279
280 if (sink_input->sink->flags & PA_SINK_FLAT_VOLUME) {
281 entry.absolute_volume = *pa_sink_input_get_volume(sink_input);
282 entry.absolute_volume_valid = sink_input->save_volume;
283
284 pa_sw_cvolume_divide(&entry.relative_volume, &entry.absolute_volume, pa_sink_get_volume(sink_input->sink, FALSE));
285 entry.relative_volume_valid = sink_input->save_volume;
286 } else {
287 entry.relative_volume = *pa_sink_input_get_volume(sink_input);
288 entry.relative_volume_valid = sink_input->save_volume;
289 }
290
291 entry.muted = pa_sink_input_get_mute(sink_input);
292 entry.muted_valid = sink_input->save_muted;
293
294 pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
295 entry.device_valid = sink_input->save_sink;
296
297 } else {
298 pa_source_output *source_output;
299
300 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
301
302 if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx)))
303 return;
304
305 if (!(name = get_name(source_output->proplist, "source-output")))
306 return;
307
308 entry.channel_map = source_output->channel_map;
309
310 pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
311 entry.device_valid = source_output->save_source;
312 }
313
314 if ((old = read_entry(u, name))) {
315
316 if (entries_equal(old, &entry)) {
317 pa_xfree(old);
318 pa_xfree(name);
319 return;
320 }
321
322 pa_xfree(old);
323 }
324
325 key.dptr = name;
326 key.dsize = (int) strlen(name);
327
328 data.dptr = (void*) &entry;
329 data.dsize = sizeof(entry);
330
331 pa_log_info("Storing volume/mute/device for stream %s.", name);
332
333 gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
334
335 pa_xfree(name);
336
337 trigger_save(u);
338 }
339
340 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
341 char *name;
342 struct entry *e;
343
344 pa_assert(new_data);
345
346 if (!u->restore_device)
347 return PA_HOOK_OK;
348
349 if (!(name = get_name(new_data->proplist, "sink-input")))
350 return PA_HOOK_OK;
351
352 if ((e = read_entry(u, name))) {
353 pa_sink *s;
354
355 if (e->device_valid) {
356
357 if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
358 if (!new_data->sink) {
359 pa_log_info("Restoring device for stream %s.", name);
360 new_data->sink = s;
361 new_data->save_sink = TRUE;
362 } else
363 pa_log_info("Not restore device for stream %s, because already set.", name);
364 }
365 }
366
367 pa_xfree(e);
368 }
369
370 pa_xfree(name);
371
372 return PA_HOOK_OK;
373 }
374
375 static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
376 char *name;
377 struct entry *e;
378
379 pa_assert(new_data);
380
381 if (!u->restore_volume && !u->restore_muted)
382 return PA_HOOK_OK;
383
384 if (!(name = get_name(new_data->proplist, "sink-input")))
385 return PA_HOOK_OK;
386
387 if ((e = read_entry(u, name))) {
388
389 if (u->restore_volume) {
390
391 if (!new_data->virtual_volume_is_set) {
392 pa_cvolume v;
393 pa_cvolume_init(&v);
394
395 if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) {
396
397 if (e->absolute_volume_valid &&
398 e->device_valid &&
399 pa_streq(new_data->sink->name, e->device)) {
400
401 v = e->absolute_volume;
402 new_data->virtual_volume_is_absolute = TRUE;
403 } else if (e->relative_volume_valid) {
404
405 v = e->relative_volume;
406 new_data->virtual_volume_is_absolute = FALSE;
407 }
408
409 } else if (e->relative_volume_valid) {
410 v = e->relative_volume;
411 new_data->virtual_volume_is_absolute = FALSE;
412 }
413
414 if (v.channels > 0) {
415 pa_log_info("Restoring volume for sink input %s.", name);
416 pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map));
417 new_data->save_volume = TRUE;
418 }
419 } else
420 pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
421 }
422
423 if (u->restore_muted && e->muted_valid) {
424 if (!new_data->muted_is_set) {
425 pa_log_info("Restoring mute state for sink input %s.", name);
426 pa_sink_input_new_data_set_muted(new_data, e->muted);
427 new_data->save_muted = TRUE;
428 } else
429 pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
430 }
431
432 pa_xfree(e);
433 }
434
435 pa_xfree(name);
436
437 return PA_HOOK_OK;
438 }
439
440 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
441 char *name;
442 struct entry *e;
443
444 pa_assert(new_data);
445
446 if (!u->restore_device)
447 return PA_HOOK_OK;
448
449 if (new_data->direct_on_input)
450 return PA_HOOK_OK;
451
452 if (!(name = get_name(new_data->proplist, "source-output")))
453 return PA_HOOK_OK;
454
455 if ((e = read_entry(u, name))) {
456 pa_source *s;
457
458 if (e->device_valid) {
459 if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) {
460 if (!new_data->source) {
461 pa_log_info("Restoring device for stream %s.", name);
462 new_data->source = s;
463 new_data->save_source = TRUE;
464 } else
465 pa_log_info("Not restoring device for stream %s, because already set", name);
466 }
467 }
468
469 pa_xfree(e);
470 }
471
472 pa_xfree(name);
473
474 return PA_HOOK_OK;
475 }
476
477 #define EXT_VERSION 1
478
479 static void clear_db(struct userdata *u) {
480 datum key;
481
482 pa_assert(u);
483
484 key = gdbm_firstkey(u->gdbm_file);
485 while (key.dptr) {
486 datum next_key;
487 next_key = gdbm_nextkey(u->gdbm_file, key);
488
489 gdbm_delete(u->gdbm_file, key);
490 pa_xfree(key.dptr);
491
492 key = next_key;
493 }
494
495 gdbm_reorganize(u->gdbm_file);
496 }
497
498 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
499 pa_sink_input *si;
500 pa_source_output *so;
501 uint32_t idx;
502
503 pa_assert(u);
504 pa_assert(name);
505 pa_assert(e);
506
507 for (si = pa_idxset_first(u->core->sink_inputs, &idx); si; si = pa_idxset_next(u->core->sink_inputs, &idx)) {
508 char *n;
509 pa_sink *s;
510
511 if (!(n = get_name(si->proplist, "sink-input")))
512 continue;
513
514 if (strcmp(name, n)) {
515 pa_xfree(n);
516 continue;
517 }
518
519 if (u->restore_volume) {
520 pa_cvolume v;
521 pa_cvolume_init(&v);
522
523 if (si->sink->flags & PA_SINK_FLAT_VOLUME) {
524
525 if (e->absolute_volume_valid &&
526 e->device_valid &&
527 pa_streq(e->device, si->sink->name))
528 v = e->absolute_volume;
529 else if (e->relative_volume_valid) {
530 pa_cvolume t = si->sink->virtual_volume;
531 pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &si->sink->channel_map, &e->channel_map));
532 }
533 } else if (e->relative_volume_valid)
534 v = e->relative_volume;
535
536 if (v.channels > 0) {
537 pa_log_info("Restoring volume for sink input %s.", name);
538 pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map), TRUE);
539 }
540 }
541
542 if (u->restore_muted &&
543 e->muted_valid) {
544 pa_log_info("Restoring mute state for sink input %s.", name);
545 pa_sink_input_set_mute(si, e->muted, TRUE);
546 }
547
548 if (u->restore_device &&
549 e->device_valid &&
550 (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
551
552 pa_log_info("Restoring device for stream %s.", name);
553 pa_sink_input_move_to(si, s, TRUE);
554 }
555 }
556
557 for (so = pa_idxset_first(u->core->source_outputs, &idx); so; so = pa_idxset_next(u->core->source_outputs, &idx)) {
558 char *n;
559 pa_source *s;
560
561 if (!(n = get_name(so->proplist, "source-output")))
562 continue;
563
564 if (strcmp(name, n)) {
565 pa_xfree(n);
566 continue;
567 }
568
569 if (u->restore_device &&
570 e->device_valid &&
571 (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
572
573 pa_log_info("Restoring device for stream %s.", name);
574 pa_source_output_move_to(so, s, TRUE);
575 }
576 }
577 }
578
579 #if 0
580 static void dump_database(struct userdata *u) {
581 datum key;
582
583 key = gdbm_firstkey(u->gdbm_file);
584 while (key.dptr) {
585 datum next_key;
586 struct entry *e;
587 char *name;
588
589 next_key = gdbm_nextkey(u->gdbm_file, key);
590
591 name = pa_xstrndup(key.dptr, key.dsize);
592 pa_xfree(key.dptr);
593
594 if ((e = read_entry(u, name))) {
595 char t[256];
596 pa_log("name=%s", name);
597 pa_log("device=%s", e->device);
598 pa_log("channel_map=%s", pa_channel_map_snprint(t, sizeof(t), &e->channel_map));
599 pa_log("volume=%s", pa_cvolume_snprint(t, sizeof(t), &e->volume));
600 pa_log("mute=%s", pa_yes_no(e->muted));
601 pa_xfree(e);
602 }
603
604 pa_xfree(name);
605
606 key = next_key;
607 }
608 }
609 #endif
610
611 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
612 struct userdata *u;
613 uint32_t command;
614 pa_tagstruct *reply = NULL;
615
616 pa_assert(p);
617 pa_assert(m);
618 pa_assert(c);
619 pa_assert(t);
620
621 u = m->userdata;
622
623 if (pa_tagstruct_getu32(t, &command) < 0)
624 goto fail;
625
626 reply = pa_tagstruct_new(NULL, 0);
627 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
628 pa_tagstruct_putu32(reply, tag);
629
630 switch (command) {
631 case SUBCOMMAND_TEST: {
632 if (!pa_tagstruct_eof(t))
633 goto fail;
634
635 pa_tagstruct_putu32(reply, EXT_VERSION);
636 break;
637 }
638
639 case SUBCOMMAND_READ: {
640 datum key;
641
642 if (!pa_tagstruct_eof(t))
643 goto fail;
644
645 key = gdbm_firstkey(u->gdbm_file);
646 while (key.dptr) {
647 datum next_key;
648 struct entry *e;
649 char *name;
650
651 next_key = gdbm_nextkey(u->gdbm_file, key);
652
653 name = pa_xstrndup(key.dptr, (size_t) key.dsize);
654 pa_xfree(key.dptr);
655
656 if ((e = read_entry(u, name))) {
657 pa_cvolume r;
658
659 pa_tagstruct_puts(reply, name);
660 pa_tagstruct_put_channel_map(reply, &e->channel_map);
661 pa_tagstruct_put_cvolume(reply, e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r));
662 pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
663 pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
664
665 pa_xfree(e);
666 }
667
668 pa_xfree(name);
669
670 key = next_key;
671 }
672
673 break;
674 }
675
676 case SUBCOMMAND_WRITE: {
677 uint32_t mode;
678 pa_bool_t apply_immediately = FALSE;
679
680 if (pa_tagstruct_getu32(t, &mode) < 0 ||
681 pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
682 goto fail;
683
684 if (mode != PA_UPDATE_MERGE &&
685 mode != PA_UPDATE_REPLACE &&
686 mode != PA_UPDATE_SET)
687 goto fail;
688
689 if (mode == PA_UPDATE_SET)
690 clear_db(u);
691
692 while (!pa_tagstruct_eof(t)) {
693 const char *name, *device;
694 pa_bool_t muted;
695 struct entry entry;
696 datum key, data;
697 int k;
698
699 memset(&entry, 0, sizeof(entry));
700
701 if (pa_tagstruct_gets(t, &name) < 0 ||
702 pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
703 pa_tagstruct_get_cvolume(t, &entry.relative_volume) < 0 ||
704 pa_tagstruct_gets(t, &device) < 0 ||
705 pa_tagstruct_get_boolean(t, &muted) < 0)
706 goto fail;
707
708 entry.absolute_volume_valid = FALSE;
709 entry.relative_volume_valid = entry.relative_volume.channels > 0;
710
711 if (entry.relative_volume_valid &&
712 entry.channel_map.channels != entry.relative_volume.channels)
713 goto fail;
714
715 entry.muted = muted;
716 entry.muted_valid = TRUE;
717
718 if (device)
719 pa_strlcpy(entry.device, device, sizeof(entry.device));
720 entry.device_valid = !!entry.device[0];
721
722 if (entry.device_valid &&
723 !pa_namereg_is_valid_name(entry.device))
724 goto fail;
725
726 key.dptr = (void*) name;
727 key.dsize = (int) strlen(name);
728
729 data.dptr = (void*) &entry;
730 data.dsize = sizeof(entry);
731
732 if ((k = gdbm_store(u->gdbm_file, key, data, mode == PA_UPDATE_REPLACE ? GDBM_REPLACE : GDBM_INSERT)) == 0)
733 if (apply_immediately)
734 apply_entry(u, name, &entry);
735 }
736
737 trigger_save(u);
738
739 break;
740 }
741
742 case SUBCOMMAND_DELETE:
743
744 while (!pa_tagstruct_eof(t)) {
745 const char *name;
746 datum key;
747
748 if (pa_tagstruct_gets(t, &name) < 0)
749 goto fail;
750
751 key.dptr = (void*) name;
752 key.dsize = (int) strlen(name);
753
754 gdbm_delete(u->gdbm_file, key);
755 }
756
757 trigger_save(u);
758
759 break;
760
761 case SUBCOMMAND_SUBSCRIBE: {
762
763 pa_bool_t enabled;
764
765 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
766 !pa_tagstruct_eof(t))
767 goto fail;
768
769 if (enabled)
770 pa_idxset_put(u->subscribed, c, NULL);
771 else
772 pa_idxset_remove_by_data(u->subscribed, c, NULL);
773
774 break;
775 }
776
777 default:
778 goto fail;
779 }
780
781 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
782 return 0;
783
784 fail:
785
786 if (reply)
787 pa_tagstruct_free(reply);
788
789 return -1;
790 }
791
792 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
793 pa_assert(p);
794 pa_assert(c);
795 pa_assert(u);
796
797 pa_idxset_remove_by_data(u->subscribed, c, NULL);
798 return PA_HOOK_OK;
799 }
800
801 int pa__init(pa_module*m) {
802 pa_modargs *ma = NULL;
803 struct userdata *u;
804 char *fname, *fn;
805 pa_sink_input *si;
806 pa_source_output *so;
807 uint32_t idx;
808 pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE;
809 int gdbm_cache_size;
810
811 pa_assert(m);
812
813 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
814 pa_log("Failed to parse module arguments");
815 goto fail;
816 }
817
818 if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
819 pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
820 pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0) {
821 pa_log("restore_device=, restore_volume= and restore_muted= expect boolean arguments");
822 goto fail;
823 }
824
825 if (!restore_muted && !restore_volume && !restore_device)
826 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring device enabled!");
827
828 m->userdata = u = pa_xnew(struct userdata, 1);
829 u->core = m->core;
830 u->module = m;
831 u->save_time_event = NULL;
832 u->restore_device = restore_device;
833 u->restore_volume = restore_volume;
834 u->restore_muted = restore_muted;
835 u->gdbm_file = NULL;
836 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
837
838 u->protocol = pa_native_protocol_get(m->core);
839 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
840
841 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);
842
843 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
844
845 if (restore_device) {
846 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);
847 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);
848 }
849
850 if (restore_volume || restore_muted)
851 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);
852
853 /* We include the host identifier in the file name because gdbm
854 * files are CPU dependant, and we don't want things to go wrong
855 * if we are on a multiarch system. */
856
857 fn = pa_sprintf_malloc("stream-volumes."CANONICAL_HOST".gdbm");
858 fname = pa_state_path(fn, TRUE);
859 pa_xfree(fn);
860
861 if (!fname)
862 goto fail;
863
864 if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT|GDBM_NOLOCK, 0600, NULL))) {
865 pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
866 pa_xfree(fname);
867 goto fail;
868 }
869
870 /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
871 gdbm_cache_size = 10;
872 gdbm_setopt(u->gdbm_file, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
873
874 pa_log_info("Sucessfully opened database file '%s'.", fname);
875 pa_xfree(fname);
876
877 for (si = pa_idxset_first(m->core->sink_inputs, &idx); si; si = pa_idxset_next(m->core->sink_inputs, &idx))
878 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
879
880 for (so = pa_idxset_first(m->core->source_outputs, &idx); so; so = pa_idxset_next(m->core->source_outputs, &idx))
881 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
882
883 pa_modargs_free(ma);
884 return 0;
885
886 fail:
887 pa__done(m);
888
889 if (ma)
890 pa_modargs_free(ma);
891
892 return -1;
893 }
894
895 void pa__done(pa_module*m) {
896 struct userdata* u;
897
898 pa_assert(m);
899
900 if (!(u = m->userdata))
901 return;
902
903 if (u->subscription)
904 pa_subscription_free(u->subscription);
905
906 if (u->sink_input_new_hook_slot)
907 pa_hook_slot_free(u->sink_input_new_hook_slot);
908 if (u->sink_input_fixate_hook_slot)
909 pa_hook_slot_free(u->sink_input_fixate_hook_slot);
910 if (u->source_output_new_hook_slot)
911 pa_hook_slot_free(u->source_output_new_hook_slot);
912
913 if (u->connection_unlink_hook_slot)
914 pa_hook_slot_free(u->connection_unlink_hook_slot);
915
916 if (u->save_time_event)
917 u->core->mainloop->time_free(u->save_time_event);
918
919 if (u->gdbm_file)
920 gdbm_close(u->gdbm_file);
921
922 if (u->protocol) {
923 pa_native_protocol_remove_ext(u->protocol, m);
924 pa_native_protocol_unref(u->protocol);
925 }
926
927 if (u->subscribed)
928 pa_idxset_free(u->subscribed, NULL, NULL);
929
930 pa_xfree(u);
931 }