]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-restore: Fix the application of an entry to allow changing the name of devices.
[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
84 #define ENTRY_VERSION 1
85
86 struct entry {
87 uint8_t version;
88 char description[PA_NAME_MAX];
89 } PA_GCC_PACKED;
90
91 enum {
92 SUBCOMMAND_TEST,
93 SUBCOMMAND_READ,
94 SUBCOMMAND_WRITE,
95 SUBCOMMAND_DELETE,
96 SUBCOMMAND_SUBSCRIBE,
97 SUBCOMMAND_EVENT
98 };
99
100 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
101 struct userdata *u = userdata;
102
103 pa_assert(a);
104 pa_assert(e);
105 pa_assert(u);
106
107 pa_assert(e == u->save_time_event);
108 u->core->mainloop->time_free(u->save_time_event);
109 u->save_time_event = NULL;
110
111 pa_database_sync(u->database);
112 pa_log_info("Synced.");
113 }
114
115 static struct entry* read_entry(struct userdata *u, const char *name) {
116 pa_datum key, data;
117 struct entry *e;
118
119 pa_assert(u);
120 pa_assert(name);
121
122 key.data = (char*) name;
123 key.size = strlen(name);
124
125 pa_zero(data);
126
127 if (!pa_database_get(u->database, &key, &data))
128 goto fail;
129
130 if (data.size != sizeof(struct entry)) {
131 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));
132 goto fail;
133 }
134
135 e = (struct entry*) data.data;
136
137 if (e->version != ENTRY_VERSION) {
138 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
139 goto fail;
140 }
141
142 if (!memchr(e->description, 0, sizeof(e->description))) {
143 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
144 goto fail;
145 }
146
147 return e;
148
149 fail:
150
151 pa_datum_free(&data);
152 return NULL;
153 }
154
155 static void trigger_save(struct userdata *u) {
156 if (u->save_time_event)
157 return;
158
159 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
160 }
161
162 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
163 if (strncmp(a->description, b->description, sizeof(a->description)))
164 return FALSE;
165
166 return TRUE;
167 }
168
169 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
170 struct userdata *u = userdata;
171 struct entry entry, *old;
172 char *name;
173 pa_datum key, data;
174
175 pa_assert(c);
176 pa_assert(u);
177
178 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
179 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
180 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
181 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
182 return;
183
184 pa_zero(entry);
185 entry.version = ENTRY_VERSION;
186
187 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
188 pa_sink *sink;
189
190 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
191 return;
192
193 name = pa_sprintf_malloc("sink:%s", sink->name);
194
195 if ((old = read_entry(u, name)))
196 entry = *old;
197
198 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
199
200 } else {
201 pa_source *source;
202
203 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
204
205 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
206 return;
207
208 name = pa_sprintf_malloc("source:%s", source->name);
209
210 if ((old = read_entry(u, name)))
211 entry = *old;
212
213 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
214 }
215
216 if (old) {
217
218 if (entries_equal(old, &entry)) {
219 pa_xfree(old);
220 pa_xfree(name);
221 return;
222 }
223
224 pa_xfree(old);
225 }
226
227 key.data = name;
228 key.size = strlen(name);
229
230 data.data = &entry;
231 data.size = sizeof(entry);
232
233 pa_log_info("Storing device description for %s.", name);
234
235 pa_database_set(u->database, &key, &data, TRUE);
236
237 pa_xfree(name);
238
239 trigger_save(u);
240 }
241
242 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
243 char *name;
244 struct entry *e;
245
246 pa_assert(c);
247 pa_assert(new_data);
248 pa_assert(u);
249
250 name = pa_sprintf_malloc("sink:%s", new_data->name);
251
252 if ((e = read_entry(u, name))) {
253 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
254 pa_log_info("Restoring description for sink %s.", name);
255 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
256 }
257
258 pa_xfree(e);
259 }
260
261 pa_xfree(name);
262
263 return PA_HOOK_OK;
264 }
265
266 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
267 char *name;
268 struct entry *e;
269
270 pa_assert(c);
271 pa_assert(new_data);
272 pa_assert(u);
273
274 name = pa_sprintf_malloc("source:%s", new_data->name);
275
276 if ((e = read_entry(u, name))) {
277
278 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
279 pa_log_info("Restoring description for sink %s.", name);
280 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
281 }
282
283 pa_xfree(e);
284 }
285
286 pa_xfree(name);
287
288 return PA_HOOK_OK;
289 }
290
291 static char *get_name(const char *key, const char *prefix) {
292 char *t;
293
294 if (strncmp(key, prefix, strlen(prefix)))
295 return NULL;
296
297 t = pa_xstrdup(key + strlen(prefix));
298 return t;
299 }
300
301 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
302 pa_sink *sink;
303 pa_source *source;
304 uint32_t idx;
305 char *n;
306
307 pa_assert(u);
308 pa_assert(name);
309 pa_assert(e);
310
311 if ((n = get_name(name, "sink:"))) {
312 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
313 if (!pa_streq(sink->name, n)) {
314 continue;
315 }
316
317 pa_log_info("Setting description for sink %s.", sink->name);
318 pa_sink_set_description(sink, e->description);
319 }
320 pa_xfree(n);
321 }
322 else if ((n = get_name(name, "source:"))) {
323 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
324 if (!pa_streq(source->name, n)) {
325 continue;
326 }
327
328 pa_log_info("Setting description for source %s.", source->name);
329 pa_source_set_description(source, e->description);
330 }
331 pa_xfree(n);
332 }
333 }
334
335 #define EXT_VERSION 1
336
337 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
338 struct userdata *u;
339 uint32_t command;
340 pa_tagstruct *reply = NULL;
341
342 pa_assert(p);
343 pa_assert(m);
344 pa_assert(c);
345 pa_assert(t);
346
347 u = m->userdata;
348
349 if (pa_tagstruct_getu32(t, &command) < 0)
350 goto fail;
351
352 reply = pa_tagstruct_new(NULL, 0);
353 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
354 pa_tagstruct_putu32(reply, tag);
355
356 switch (command) {
357 case SUBCOMMAND_TEST: {
358 if (!pa_tagstruct_eof(t))
359 goto fail;
360
361 pa_tagstruct_putu32(reply, EXT_VERSION);
362 break;
363 }
364
365 case SUBCOMMAND_READ: {
366 pa_datum key;
367 pa_bool_t done;
368
369 if (!pa_tagstruct_eof(t))
370 goto fail;
371
372 done = !pa_database_first(u->database, &key, NULL);
373
374 while (!done) {
375 pa_datum next_key;
376 struct entry *e;
377 char *name;
378
379 done = !pa_database_next(u->database, &key, &next_key, NULL);
380
381 name = pa_xstrndup(key.data, key.size);
382 pa_datum_free(&key);
383
384 if ((e = read_entry(u, name))) {
385 pa_tagstruct_puts(reply, name);
386 pa_tagstruct_puts(reply, e->description);
387
388 pa_xfree(e);
389 }
390
391 pa_xfree(name);
392
393 key = next_key;
394 }
395
396 break;
397 }
398
399 case SUBCOMMAND_WRITE: {
400 uint32_t mode;
401 pa_bool_t apply_immediately = FALSE;
402
403 if (pa_tagstruct_getu32(t, &mode) < 0 ||
404 pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
405 goto fail;
406
407 if (mode != PA_UPDATE_MERGE &&
408 mode != PA_UPDATE_REPLACE &&
409 mode != PA_UPDATE_SET)
410 goto fail;
411
412 if (mode == PA_UPDATE_SET)
413 pa_database_clear(u->database);
414
415 while (!pa_tagstruct_eof(t)) {
416 const char *name, *description;
417 struct entry entry;
418 pa_datum key, data;
419
420 pa_zero(entry);
421 entry.version = ENTRY_VERSION;
422
423 if (pa_tagstruct_gets(t, &name) < 0 ||
424 pa_tagstruct_gets(t, &description) < 0)
425 goto fail;
426
427 if (!name || !*name)
428 goto fail;
429
430 pa_strlcpy(entry.description, description, sizeof(entry.description));
431
432 key.data = (char*) name;
433 key.size = strlen(name);
434
435 data.data = &entry;
436 data.size = sizeof(entry);
437
438 if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
439 if (apply_immediately)
440 apply_entry(u, name, &entry);
441 }
442
443 trigger_save(u);
444
445 break;
446 }
447
448 case SUBCOMMAND_DELETE:
449
450 while (!pa_tagstruct_eof(t)) {
451 const char *name;
452 pa_datum key;
453
454 if (pa_tagstruct_gets(t, &name) < 0)
455 goto fail;
456
457 key.data = (char*) name;
458 key.size = strlen(name);
459
460 pa_database_unset(u->database, &key);
461 }
462
463 trigger_save(u);
464
465 break;
466
467 case SUBCOMMAND_SUBSCRIBE: {
468
469 pa_bool_t enabled;
470
471 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
472 !pa_tagstruct_eof(t))
473 goto fail;
474
475 if (enabled)
476 pa_idxset_put(u->subscribed, c, NULL);
477 else
478 pa_idxset_remove_by_data(u->subscribed, c, NULL);
479
480 break;
481 }
482
483 default:
484 goto fail;
485 }
486
487 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
488 return 0;
489
490 fail:
491
492 if (reply)
493 pa_tagstruct_free(reply);
494
495 return -1;
496 }
497
498 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
499 pa_assert(p);
500 pa_assert(c);
501 pa_assert(u);
502
503 pa_idxset_remove_by_data(u->subscribed, c, NULL);
504 return PA_HOOK_OK;
505 }
506
507 int pa__init(pa_module*m) {
508 pa_modargs *ma = NULL;
509 struct userdata *u;
510 char *fname;
511 pa_sink *sink;
512 pa_source *source;
513 uint32_t idx;
514
515 pa_assert(m);
516
517 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
518 pa_log("Failed to parse module arguments");
519 goto fail;
520 }
521
522 m->userdata = u = pa_xnew0(struct userdata, 1);
523 u->core = m->core;
524 u->module = m;
525 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
526
527 u->protocol = pa_native_protocol_get(m->core);
528 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
529
530 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);
531
532 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
533
534 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);
535 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);
536
537 if (!(fname = pa_state_path("device-manager", TRUE)))
538 goto fail;
539
540 if (!(u->database = pa_database_open(fname, TRUE))) {
541 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
542 pa_xfree(fname);
543 goto fail;
544 }
545
546 pa_log_info("Sucessfully opened database file '%s'.", fname);
547 pa_xfree(fname);
548
549 for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
550 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
551
552 for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
553 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
554
555 pa_modargs_free(ma);
556 return 0;
557
558 fail:
559 pa__done(m);
560
561 if (ma)
562 pa_modargs_free(ma);
563
564 return -1;
565 }
566
567 void pa__done(pa_module*m) {
568 struct userdata* u;
569
570 pa_assert(m);
571
572 if (!(u = m->userdata))
573 return;
574
575 if (u->subscription)
576 pa_subscription_free(u->subscription);
577
578 if (u->sink_new_hook_slot)
579 pa_hook_slot_free(u->sink_new_hook_slot);
580 if (u->source_new_hook_slot)
581 pa_hook_slot_free(u->source_new_hook_slot);
582
583 if (u->save_time_event)
584 u->core->mainloop->time_free(u->save_time_event);
585
586 if (u->database)
587 pa_database_close(u->database);
588
589 if (u->protocol) {
590 pa_native_protocol_remove_ext(u->protocol, m);
591 pa_native_protocol_unref(u->protocol);
592 }
593
594 if (u->subscribed)
595 pa_idxset_free(u->subscribed, NULL, NULL);
596
597 pa_xfree(u);
598 }