]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Let subscribed clients know when something changes.
[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 #define NUM_ROLES 9
91 enum {
92 ROLE_NONE,
93 ROLE_VIDEO,
94 ROLE_MUSIC,
95 ROLE_GAME,
96 ROLE_EVENT,
97 ROLE_PHONE,
98 ROLE_ANIMATION,
99 ROLE_PRODUCTION,
100 ROLE_A11Y,
101 };
102
103 struct entry {
104 uint8_t version;
105 char description[PA_NAME_MAX];
106 uint32_t priority[NUM_ROLES];
107 } PA_GCC_PACKED;
108
109 enum {
110 SUBCOMMAND_TEST,
111 SUBCOMMAND_READ,
112 SUBCOMMAND_RENAME,
113 SUBCOMMAND_DELETE,
114 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
115 SUBCOMMAND_PREFER_DEVICE,
116 SUBCOMMAND_DEFER_DEVICE,
117 SUBCOMMAND_SUBSCRIBE,
118 SUBCOMMAND_EVENT
119 };
120
121 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
122 struct userdata *u = userdata;
123
124 pa_assert(a);
125 pa_assert(e);
126 pa_assert(u);
127
128 pa_assert(e == u->save_time_event);
129 u->core->mainloop->time_free(u->save_time_event);
130 u->save_time_event = NULL;
131
132 pa_database_sync(u->database);
133 pa_log_info("Synced.");
134 }
135
136 static struct entry* read_entry(struct userdata *u, const char *name) {
137 pa_datum key, data;
138 struct entry *e;
139
140 pa_assert(u);
141 pa_assert(name);
142
143 key.data = (char*) name;
144 key.size = strlen(name);
145
146 pa_zero(data);
147
148 if (!pa_database_get(u->database, &key, &data))
149 goto fail;
150
151 if (data.size != sizeof(struct entry)) {
152 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));
153 goto fail;
154 }
155
156 e = (struct entry*) data.data;
157
158 if (e->version != ENTRY_VERSION) {
159 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
160 goto fail;
161 }
162
163 if (!memchr(e->description, 0, sizeof(e->description))) {
164 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
165 goto fail;
166 }
167
168 return e;
169
170 fail:
171
172 pa_datum_free(&data);
173 return NULL;
174 }
175
176 static void trigger_save(struct userdata *u) {
177 pa_native_connection *c;
178 uint32_t idx;
179
180 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
181 pa_tagstruct *t;
182
183 t = pa_tagstruct_new(NULL, 0);
184 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
185 pa_tagstruct_putu32(t, 0);
186 pa_tagstruct_putu32(t, u->module->index);
187 pa_tagstruct_puts(t, u->module->name);
188 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
189
190 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
191 }
192
193 if (u->save_time_event)
194 return;
195
196 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
197 }
198
199 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
200 if (strncmp(a->description, b->description, sizeof(a->description)))
201 return FALSE;
202
203 return TRUE;
204 }
205
206 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
207 struct userdata *u = userdata;
208 struct entry entry, *old = NULL;
209 char *name = NULL;
210 pa_datum key, data;
211
212 pa_assert(c);
213 pa_assert(u);
214
215 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
216 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
217 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
218 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
219 return;
220
221 pa_zero(entry);
222 entry.version = ENTRY_VERSION;
223
224 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
225 pa_sink *sink;
226
227 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
228 return;
229
230 name = pa_sprintf_malloc("sink:%s", sink->name);
231
232 if ((old = read_entry(u, name)))
233 entry = *old;
234
235 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
236
237 } else {
238 pa_source *source;
239
240 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
241
242 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
243 return;
244
245 if (source->monitor_of)
246 return;
247
248 name = pa_sprintf_malloc("source:%s", source->name);
249
250 if ((old = read_entry(u, name)))
251 entry = *old;
252
253 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
254 }
255
256 if (old) {
257
258 if (entries_equal(old, &entry)) {
259 pa_xfree(old);
260 pa_xfree(name);
261 return;
262 }
263
264 pa_xfree(old);
265 }
266
267 key.data = name;
268 key.size = strlen(name);
269
270 data.data = &entry;
271 data.size = sizeof(entry);
272
273 pa_log_info("Storing device description for %s.", name);
274
275 pa_database_set(u->database, &key, &data, TRUE);
276
277 pa_xfree(name);
278
279 trigger_save(u);
280 }
281
282 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
283 char *name;
284 struct entry *e;
285
286 pa_assert(c);
287 pa_assert(new_data);
288 pa_assert(u);
289
290 name = pa_sprintf_malloc("sink:%s", new_data->name);
291
292 if ((e = read_entry(u, name))) {
293 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
294 pa_log_info("Restoring description for sink %s.", new_data->name);
295 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
296 }
297
298 pa_xfree(e);
299 }
300
301 pa_xfree(name);
302
303 return PA_HOOK_OK;
304 }
305
306 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
307 char *name;
308 struct entry *e;
309
310 pa_assert(c);
311 pa_assert(new_data);
312 pa_assert(u);
313
314 name = pa_sprintf_malloc("source:%s", new_data->name);
315
316 if ((e = read_entry(u, name))) {
317
318 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
319 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
320 pa_log_info("Restoring description for sink %s.", new_data->name);
321 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
322 }
323
324 pa_xfree(e);
325 }
326
327 pa_xfree(name);
328
329 return PA_HOOK_OK;
330 }
331
332 static char *get_name(const char *key, const char *prefix) {
333 char *t;
334
335 if (strncmp(key, prefix, strlen(prefix)))
336 return NULL;
337
338 t = pa_xstrdup(key + strlen(prefix));
339 return t;
340 }
341
342 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
343 pa_sink *sink;
344 pa_source *source;
345 uint32_t idx;
346 char *n;
347
348 pa_assert(u);
349 pa_assert(name);
350 pa_assert(e);
351
352 if ((n = get_name(name, "sink:"))) {
353 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
354 if (!pa_streq(sink->name, n)) {
355 continue;
356 }
357
358 pa_log_info("Setting description for sink %s.", sink->name);
359 pa_sink_set_description(sink, e->description);
360 }
361 pa_xfree(n);
362 }
363 else if ((n = get_name(name, "source:"))) {
364 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
365 if (!pa_streq(source->name, n)) {
366 continue;
367 }
368
369 if (source->monitor_of) {
370 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
371 continue;
372 }
373
374 pa_log_info("Setting description for source %s.", source->name);
375 pa_source_set_description(source, e->description);
376 }
377 pa_xfree(n);
378 }
379 }
380
381
382 static uint32_t get_role_index(const char* role) {
383 pa_assert(role);
384
385 if (strcmp(role, "") == 0)
386 return ROLE_NONE;
387 if (strcmp(role, "video") == 0)
388 return ROLE_VIDEO;
389 if (strcmp(role, "music") == 0)
390 return ROLE_MUSIC;
391 if (strcmp(role, "game") == 0)
392 return ROLE_GAME;
393 if (strcmp(role, "event") == 0)
394 return ROLE_EVENT;
395 if (strcmp(role, "phone") == 0)
396 return ROLE_PHONE;
397 if (strcmp(role, "animation") == 0)
398 return ROLE_ANIMATION;
399 if (strcmp(role, "production") == 0)
400 return ROLE_PRODUCTION;
401 if (strcmp(role, "a11y") == 0)
402 return ROLE_A11Y;
403 return PA_INVALID_INDEX;
404 }
405
406 #define EXT_VERSION 1
407
408 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
409 struct userdata *u;
410 uint32_t command;
411 pa_tagstruct *reply = NULL;
412
413 pa_assert(p);
414 pa_assert(m);
415 pa_assert(c);
416 pa_assert(t);
417
418 u = m->userdata;
419
420 if (pa_tagstruct_getu32(t, &command) < 0)
421 goto fail;
422
423 reply = pa_tagstruct_new(NULL, 0);
424 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
425 pa_tagstruct_putu32(reply, tag);
426
427 switch (command) {
428 case SUBCOMMAND_TEST: {
429 if (!pa_tagstruct_eof(t))
430 goto fail;
431
432 pa_tagstruct_putu32(reply, EXT_VERSION);
433 break;
434 }
435
436 case SUBCOMMAND_READ: {
437 pa_datum key;
438 pa_bool_t done;
439
440 if (!pa_tagstruct_eof(t))
441 goto fail;
442
443 done = !pa_database_first(u->database, &key, NULL);
444
445 while (!done) {
446 pa_datum next_key;
447 struct entry *e;
448 char *name;
449
450 done = !pa_database_next(u->database, &key, &next_key, NULL);
451
452 name = pa_xstrndup(key.data, key.size);
453 pa_datum_free(&key);
454
455 if ((e = read_entry(u, name))) {
456 pa_tagstruct_puts(reply, name);
457 pa_tagstruct_puts(reply, e->description);
458
459 pa_xfree(e);
460 }
461
462 pa_xfree(name);
463
464 key = next_key;
465 }
466
467 break;
468 }
469
470 case SUBCOMMAND_RENAME: {
471
472 struct entry *e;
473 const char *device, *description;
474
475 if (pa_tagstruct_gets(t, &device) < 0 ||
476 pa_tagstruct_gets(t, &description) < 0)
477 goto fail;
478
479 if (!device || !*device || !description || !*description)
480 goto fail;
481
482 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
483 pa_datum key, data;
484
485 pa_strlcpy(e->description, description, sizeof(e->description));
486
487 key.data = (char *) device;
488 key.size = strlen(device);
489
490 data.data = e;
491 data.size = sizeof(*e);
492
493 if (pa_database_set(u->database, &key, &data, FALSE) == 0) {
494 apply_entry(u, device, e);
495
496 trigger_save(u);
497 }
498 else
499 pa_log_warn("Could not save device");
500
501 pa_xfree(e);
502 }
503 else
504 pa_log_warn("Could not rename device %s, no entry in database", device);
505
506 break;
507 }
508
509 case SUBCOMMAND_DELETE:
510
511 while (!pa_tagstruct_eof(t)) {
512 const char *name;
513 pa_datum key;
514
515 if (pa_tagstruct_gets(t, &name) < 0)
516 goto fail;
517
518 key.data = (char*) name;
519 key.size = strlen(name);
520
521 pa_database_unset(u->database, &key);
522 }
523
524 trigger_save(u);
525
526 break;
527
528 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
529
530 pa_bool_t enable;
531 uint32_t sridx = PA_INVALID_INDEX;
532 uint32_t idx;
533 pa_module *module;
534
535 if (pa_tagstruct_get_boolean(t, &enable) < 0)
536 goto fail;
537
538 /* If this is the first run, check for stream restore module */
539 if (!u->checked_stream_restore) {
540 u->checked_stream_restore = TRUE;
541
542 for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
543 if (strcmp(module->name, "module-stream-restore") == 0) {
544 pa_log_debug("Detected module-stream-restore is currently in use");
545 u->stream_restore_used = TRUE;
546 sridx = module->index;
547 }
548 }
549 }
550
551 u->role_device_priority_routing = enable;
552 if (enable) {
553 if (u->stream_restore_used) {
554 if (PA_INVALID_INDEX == sridx) {
555 /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */
556 for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
557 if (strcmp(module->name, "module-stream-restore") == 0) {
558 sridx = module->index;
559 }
560 }
561 }
562 if (PA_INVALID_INDEX != sridx) {
563 pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing");
564 pa_module_unload_request_by_index(u->core, sridx, TRUE);
565 }
566 }
567 } else if (u->stream_restore_used) {
568 /* We want to reload module-stream-restore */
569 if (!pa_module_load(u->core, "module-stream-restore", ""))
570 pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing");
571 }
572
573 break;
574 }
575
576 case SUBCOMMAND_PREFER_DEVICE:
577 case SUBCOMMAND_DEFER_DEVICE: {
578
579 const char *role, *device;
580 struct entry *e;
581 uint32_t role_index;
582
583 if (pa_tagstruct_gets(t, &role) < 0 ||
584 pa_tagstruct_gets(t, &device) < 0)
585 goto fail;
586
587 if (!role || !device || !*device)
588 goto fail;
589
590 role_index = get_role_index(role);
591 if (PA_INVALID_INDEX == role_index)
592 goto fail;
593
594 if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
595 pa_datum key;
596 pa_datum data;
597 pa_bool_t done;
598 char* prefix;
599 uint32_t priority;
600 pa_bool_t haschanged = FALSE;
601
602 if (strncmp(device, "sink:", 5) == 0)
603 prefix = pa_xstrdup("sink:");
604 else
605 prefix = pa_xstrdup("source:");
606
607 priority = e->priority[role_index];
608
609 /* Now we need to load up all the other entries of this type and shuffle the priroities around */
610
611 done = !pa_database_first(u->database, &key, NULL);
612
613 while (!done && !haschanged) {
614 pa_datum next_key;
615
616 done = !pa_database_next(u->database, &key, &next_key, NULL);
617
618 /* Only read devices with the right prefix */
619 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
620 char *name;
621 struct entry *e2;
622
623 name = pa_xstrndup(key.data, key.size);
624 pa_datum_free(&key);
625
626 if ((e2 = read_entry(u, name))) {
627 if (SUBCOMMAND_PREFER_DEVICE == command) {
628 /* PREFER */
629 if (e2->priority[role_index] == (priority - 1)) {
630 e2->priority[role_index]++;
631 haschanged = TRUE;
632 }
633 } else {
634 /* DEFER */
635 if (e2->priority[role_index] == (priority + 1)) {
636 e2->priority[role_index]--;
637 haschanged = TRUE;
638 }
639 }
640
641 if (haschanged) {
642 data.data = e2;
643 data.size = sizeof(*e2);
644
645 if (pa_database_set(u->database, &key, &data, FALSE))
646 pa_log_warn("Could not save device");
647 }
648 pa_xfree(e2);
649 }
650
651 pa_xfree(name);
652 }
653
654 key = next_key;
655 }
656
657 /* Now write out our actual entry */
658 if (haschanged) {
659 if (SUBCOMMAND_PREFER_DEVICE == command)
660 e->priority[role_index]--;
661 else
662 e->priority[role_index]++;
663
664 key.data = (char *) device;
665 key.size = strlen(device);
666
667 data.data = e;
668 data.size = sizeof(*e);
669
670 if (pa_database_set(u->database, &key, &data, FALSE))
671 pa_log_warn("Could not save device");
672
673 trigger_save(u);
674 }
675
676 pa_xfree(e);
677
678 pa_xfree(prefix);
679 }
680 else
681 pa_log_warn("Could not reorder device %s, no entry in database", device);
682
683 break;
684 }
685
686 case SUBCOMMAND_SUBSCRIBE: {
687
688 pa_bool_t enabled;
689
690 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
691 !pa_tagstruct_eof(t))
692 goto fail;
693
694 if (enabled)
695 pa_idxset_put(u->subscribed, c, NULL);
696 else
697 pa_idxset_remove_by_data(u->subscribed, c, NULL);
698
699 break;
700 }
701
702 default:
703 goto fail;
704 }
705
706 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
707 return 0;
708
709 fail:
710
711 if (reply)
712 pa_tagstruct_free(reply);
713
714 return -1;
715 }
716
717 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
718 pa_assert(p);
719 pa_assert(c);
720 pa_assert(u);
721
722 pa_idxset_remove_by_data(u->subscribed, c, NULL);
723 return PA_HOOK_OK;
724 }
725
726 int pa__init(pa_module*m) {
727 pa_modargs *ma = NULL;
728 struct userdata *u;
729 char *fname;
730 pa_sink *sink;
731 pa_source *source;
732 uint32_t idx;
733
734 pa_assert(m);
735
736 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
737 pa_log("Failed to parse module arguments");
738 goto fail;
739 }
740
741 m->userdata = u = pa_xnew0(struct userdata, 1);
742 u->core = m->core;
743 u->module = m;
744 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
745
746 u->protocol = pa_native_protocol_get(m->core);
747 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
748
749 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);
750
751 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
752
753 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);
754 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);
755
756 if (!(fname = pa_state_path("device-manager", TRUE)))
757 goto fail;
758
759 if (!(u->database = pa_database_open(fname, TRUE))) {
760 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
761 pa_xfree(fname);
762 goto fail;
763 }
764
765 pa_log_info("Sucessfully opened database file '%s'.", fname);
766 pa_xfree(fname);
767
768 for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
769 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
770
771 for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
772 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
773
774 pa_modargs_free(ma);
775 return 0;
776
777 fail:
778 pa__done(m);
779
780 if (ma)
781 pa_modargs_free(ma);
782
783 return -1;
784 }
785
786 void pa__done(pa_module*m) {
787 struct userdata* u;
788
789 pa_assert(m);
790
791 if (!(u = m->userdata))
792 return;
793
794 if (u->subscription)
795 pa_subscription_free(u->subscription);
796
797 if (u->sink_new_hook_slot)
798 pa_hook_slot_free(u->sink_new_hook_slot);
799 if (u->source_new_hook_slot)
800 pa_hook_slot_free(u->source_new_hook_slot);
801
802 if (u->save_time_event)
803 u->core->mainloop->time_free(u->save_time_event);
804
805 if (u->database)
806 pa_database_close(u->database);
807
808 if (u->protocol) {
809 pa_native_protocol_remove_ext(u->protocol, m);
810 pa_native_protocol_unref(u->protocol);
811 }
812
813 if (u->subscribed)
814 pa_idxset_free(u->subscribed, NULL, NULL);
815
816 pa_xfree(u);
817 }