]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Only store and save details for non-monitor sources
[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 = NULL;
172 char *name = NULL;
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 if (source->monitor_of)
209 return;
210
211 name = pa_sprintf_malloc("source:%s", source->name);
212
213 if ((old = read_entry(u, name)))
214 entry = *old;
215
216 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
217 }
218
219 if (old) {
220
221 if (entries_equal(old, &entry)) {
222 pa_xfree(old);
223 pa_xfree(name);
224 return;
225 }
226
227 pa_xfree(old);
228 }
229
230 key.data = name;
231 key.size = strlen(name);
232
233 data.data = &entry;
234 data.size = sizeof(entry);
235
236 pa_log_info("Storing device description for %s.", name);
237
238 pa_database_set(u->database, &key, &data, TRUE);
239
240 pa_xfree(name);
241
242 trigger_save(u);
243 }
244
245 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
246 char *name;
247 struct entry *e;
248
249 pa_assert(c);
250 pa_assert(new_data);
251 pa_assert(u);
252
253 name = pa_sprintf_malloc("sink:%s", new_data->name);
254
255 if ((e = read_entry(u, name))) {
256 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
257 pa_log_info("Restoring description for sink %s.", new_data->name);
258 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
259 }
260
261 pa_xfree(e);
262 }
263
264 pa_xfree(name);
265
266 return PA_HOOK_OK;
267 }
268
269 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
270 char *name;
271 struct entry *e;
272
273 pa_assert(c);
274 pa_assert(new_data);
275 pa_assert(u);
276
277 name = pa_sprintf_malloc("source:%s", new_data->name);
278
279 if ((e = read_entry(u, name))) {
280
281 if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
282 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
283 pa_log_info("Restoring description for sink %s.", new_data->name);
284 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
285 }
286
287 pa_xfree(e);
288 }
289
290 pa_xfree(name);
291
292 return PA_HOOK_OK;
293 }
294
295 static char *get_name(const char *key, const char *prefix) {
296 char *t;
297
298 if (strncmp(key, prefix, strlen(prefix)))
299 return NULL;
300
301 t = pa_xstrdup(key + strlen(prefix));
302 return t;
303 }
304
305 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
306 pa_sink *sink;
307 pa_source *source;
308 uint32_t idx;
309 char *n;
310
311 pa_assert(u);
312 pa_assert(name);
313 pa_assert(e);
314
315 if ((n = get_name(name, "sink:"))) {
316 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
317 if (!pa_streq(sink->name, n)) {
318 continue;
319 }
320
321 pa_log_info("Setting description for sink %s.", sink->name);
322 pa_sink_set_description(sink, e->description);
323 }
324 pa_xfree(n);
325 }
326 else if ((n = get_name(name, "source:"))) {
327 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
328 if (!pa_streq(source->name, n)) {
329 continue;
330 }
331
332 if (source->monitor_of) {
333 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
334 continue;
335 }
336
337 pa_log_info("Setting description for source %s.", source->name);
338 pa_source_set_description(source, e->description);
339 }
340 pa_xfree(n);
341 }
342 }
343
344 #define EXT_VERSION 1
345
346 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
347 struct userdata *u;
348 uint32_t command;
349 pa_tagstruct *reply = NULL;
350
351 pa_assert(p);
352 pa_assert(m);
353 pa_assert(c);
354 pa_assert(t);
355
356 u = m->userdata;
357
358 if (pa_tagstruct_getu32(t, &command) < 0)
359 goto fail;
360
361 reply = pa_tagstruct_new(NULL, 0);
362 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
363 pa_tagstruct_putu32(reply, tag);
364
365 switch (command) {
366 case SUBCOMMAND_TEST: {
367 if (!pa_tagstruct_eof(t))
368 goto fail;
369
370 pa_tagstruct_putu32(reply, EXT_VERSION);
371 break;
372 }
373
374 case SUBCOMMAND_READ: {
375 pa_datum key;
376 pa_bool_t done;
377
378 if (!pa_tagstruct_eof(t))
379 goto fail;
380
381 done = !pa_database_first(u->database, &key, NULL);
382
383 while (!done) {
384 pa_datum next_key;
385 struct entry *e;
386 char *name;
387
388 done = !pa_database_next(u->database, &key, &next_key, NULL);
389
390 name = pa_xstrndup(key.data, key.size);
391 pa_datum_free(&key);
392
393 if ((e = read_entry(u, name))) {
394 pa_tagstruct_puts(reply, name);
395 pa_tagstruct_puts(reply, e->description);
396
397 pa_xfree(e);
398 }
399
400 pa_xfree(name);
401
402 key = next_key;
403 }
404
405 break;
406 }
407
408 case SUBCOMMAND_WRITE: {
409 uint32_t mode;
410 pa_bool_t apply_immediately = FALSE;
411
412 if (pa_tagstruct_getu32(t, &mode) < 0 ||
413 pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
414 goto fail;
415
416 if (mode != PA_UPDATE_MERGE &&
417 mode != PA_UPDATE_REPLACE &&
418 mode != PA_UPDATE_SET)
419 goto fail;
420
421 if (mode == PA_UPDATE_SET)
422 pa_database_clear(u->database);
423
424 while (!pa_tagstruct_eof(t)) {
425 const char *name, *description;
426 struct entry entry;
427 pa_datum key, data;
428
429 pa_zero(entry);
430 entry.version = ENTRY_VERSION;
431
432 if (pa_tagstruct_gets(t, &name) < 0 ||
433 pa_tagstruct_gets(t, &description) < 0)
434 goto fail;
435
436 if (!name || !*name)
437 goto fail;
438
439 pa_strlcpy(entry.description, description, sizeof(entry.description));
440
441 key.data = (char*) name;
442 key.size = strlen(name);
443
444 data.data = &entry;
445 data.size = sizeof(entry);
446
447 if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
448 if (apply_immediately)
449 apply_entry(u, name, &entry);
450 }
451
452 trigger_save(u);
453
454 break;
455 }
456
457 case SUBCOMMAND_DELETE:
458
459 while (!pa_tagstruct_eof(t)) {
460 const char *name;
461 pa_datum key;
462
463 if (pa_tagstruct_gets(t, &name) < 0)
464 goto fail;
465
466 key.data = (char*) name;
467 key.size = strlen(name);
468
469 pa_database_unset(u->database, &key);
470 }
471
472 trigger_save(u);
473
474 break;
475
476 case SUBCOMMAND_SUBSCRIBE: {
477
478 pa_bool_t enabled;
479
480 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
481 !pa_tagstruct_eof(t))
482 goto fail;
483
484 if (enabled)
485 pa_idxset_put(u->subscribed, c, NULL);
486 else
487 pa_idxset_remove_by_data(u->subscribed, c, NULL);
488
489 break;
490 }
491
492 default:
493 goto fail;
494 }
495
496 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
497 return 0;
498
499 fail:
500
501 if (reply)
502 pa_tagstruct_free(reply);
503
504 return -1;
505 }
506
507 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
508 pa_assert(p);
509 pa_assert(c);
510 pa_assert(u);
511
512 pa_idxset_remove_by_data(u->subscribed, c, NULL);
513 return PA_HOOK_OK;
514 }
515
516 int pa__init(pa_module*m) {
517 pa_modargs *ma = NULL;
518 struct userdata *u;
519 char *fname;
520 pa_sink *sink;
521 pa_source *source;
522 uint32_t idx;
523
524 pa_assert(m);
525
526 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
527 pa_log("Failed to parse module arguments");
528 goto fail;
529 }
530
531 m->userdata = u = pa_xnew0(struct userdata, 1);
532 u->core = m->core;
533 u->module = m;
534 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
535
536 u->protocol = pa_native_protocol_get(m->core);
537 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
538
539 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);
540
541 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
542
543 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);
544 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);
545
546 if (!(fname = pa_state_path("device-manager", TRUE)))
547 goto fail;
548
549 if (!(u->database = pa_database_open(fname, TRUE))) {
550 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
551 pa_xfree(fname);
552 goto fail;
553 }
554
555 pa_log_info("Sucessfully opened database file '%s'.", fname);
556 pa_xfree(fname);
557
558 for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
559 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
560
561 for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
562 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
563
564 pa_modargs_free(ma);
565 return 0;
566
567 fail:
568 pa__done(m);
569
570 if (ma)
571 pa_modargs_free(ma);
572
573 return -1;
574 }
575
576 void pa__done(pa_module*m) {
577 struct userdata* u;
578
579 pa_assert(m);
580
581 if (!(u = m->userdata))
582 return;
583
584 if (u->subscription)
585 pa_subscription_free(u->subscription);
586
587 if (u->sink_new_hook_slot)
588 pa_hook_slot_free(u->sink_new_hook_slot);
589 if (u->source_new_hook_slot)
590 pa_hook_slot_free(u->source_new_hook_slot);
591
592 if (u->save_time_event)
593 u->core->mainloop->time_free(u->save_time_event);
594
595 if (u->database)
596 pa_database_close(u->database);
597
598 if (u->protocol) {
599 pa_native_protocol_remove_ext(u->protocol, m);
600 pa_native_protocol_unref(u->protocol);
601 }
602
603 if (u->subscribed)
604 pa_idxset_free(u->subscribed, NULL, NULL);
605
606 pa_xfree(u);
607 }