]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
device-manager: Add an untested protocol extension.
[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, sizeof(prefix)))
295 return NULL;
296
297 t = pa_xstrdup(key + sizeof(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
306 pa_assert(u);
307 pa_assert(name);
308 pa_assert(e);
309
310 for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
311 char *n;
312
313 if (!(n = get_name(name, "sink")))
314 continue;
315
316 if (!pa_streq(sink->name, n)) {
317 pa_xfree(n);
318 continue;
319 }
320 pa_xfree(n);
321
322 pa_log_info("Restoring description for sink %s.", sink->name);
323 pa_proplist_sets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
324 }
325
326 for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
327 char *n;
328
329 if (!(n = get_name(name, "source")))
330 continue;
331
332 if (!pa_streq(source->name, n)) {
333 pa_xfree(n);
334 continue;
335 }
336 pa_xfree(n);
337
338 pa_log_info("Restoring description for source %s.", source->name);
339 pa_proplist_sets(source->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
340 }
341 }
342
343 #define EXT_VERSION 1
344
345 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
346 struct userdata *u;
347 uint32_t command;
348 pa_tagstruct *reply = NULL;
349
350 pa_assert(p);
351 pa_assert(m);
352 pa_assert(c);
353 pa_assert(t);
354
355 u = m->userdata;
356
357 if (pa_tagstruct_getu32(t, &command) < 0)
358 goto fail;
359
360 reply = pa_tagstruct_new(NULL, 0);
361 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
362 pa_tagstruct_putu32(reply, tag);
363
364 switch (command) {
365 case SUBCOMMAND_TEST: {
366 if (!pa_tagstruct_eof(t))
367 goto fail;
368
369 pa_tagstruct_putu32(reply, EXT_VERSION);
370 break;
371 }
372
373 case SUBCOMMAND_READ: {
374 pa_datum key;
375 pa_bool_t done;
376
377 if (!pa_tagstruct_eof(t))
378 goto fail;
379
380 done = !pa_database_first(u->database, &key, NULL);
381
382 while (!done) {
383 pa_datum next_key;
384 struct entry *e;
385 char *name;
386
387 done = !pa_database_next(u->database, &key, &next_key, NULL);
388
389 name = pa_xstrndup(key.data, key.size);
390 pa_datum_free(&key);
391
392 if ((e = read_entry(u, name))) {
393 pa_tagstruct_puts(reply, name);
394 pa_tagstruct_puts(reply, e->description);
395
396 pa_xfree(e);
397 }
398
399 pa_xfree(name);
400
401 key = next_key;
402 }
403
404 break;
405 }
406
407 case SUBCOMMAND_WRITE: {
408 uint32_t mode;
409 pa_bool_t apply_immediately = FALSE;
410
411 if (pa_tagstruct_getu32(t, &mode) < 0 ||
412 pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
413 goto fail;
414
415 if (mode != PA_UPDATE_MERGE &&
416 mode != PA_UPDATE_REPLACE &&
417 mode != PA_UPDATE_SET)
418 goto fail;
419
420 if (mode == PA_UPDATE_SET)
421 pa_database_clear(u->database);
422
423 while (!pa_tagstruct_eof(t)) {
424 const char *name, *description;
425 struct entry entry;
426 pa_datum key, data;
427
428 pa_zero(entry);
429 entry.version = ENTRY_VERSION;
430
431 if (pa_tagstruct_gets(t, &name) < 0 ||
432 pa_tagstruct_gets(reply, &description) < 0)
433 goto fail;
434
435 if (!name || !*name)
436 goto fail;
437
438 pa_strlcpy(entry.description, description, sizeof(entry.description));
439
440 key.data = (char*) name;
441 key.size = strlen(name);
442
443 data.data = &entry;
444 data.size = sizeof(entry);
445
446 if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
447 if (apply_immediately)
448 apply_entry(u, name, &entry);
449 }
450
451 trigger_save(u);
452
453 break;
454 }
455
456 case SUBCOMMAND_DELETE:
457
458 while (!pa_tagstruct_eof(t)) {
459 const char *name;
460 pa_datum key;
461
462 if (pa_tagstruct_gets(t, &name) < 0)
463 goto fail;
464
465 key.data = (char*) name;
466 key.size = strlen(name);
467
468 pa_database_unset(u->database, &key);
469 }
470
471 trigger_save(u);
472
473 break;
474
475 case SUBCOMMAND_SUBSCRIBE: {
476
477 pa_bool_t enabled;
478
479 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
480 !pa_tagstruct_eof(t))
481 goto fail;
482
483 if (enabled)
484 pa_idxset_put(u->subscribed, c, NULL);
485 else
486 pa_idxset_remove_by_data(u->subscribed, c, NULL);
487
488 break;
489 }
490
491 default:
492 goto fail;
493 }
494
495 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
496 return 0;
497
498 fail:
499
500 if (reply)
501 pa_tagstruct_free(reply);
502
503 return -1;
504 }
505
506 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
507 pa_assert(p);
508 pa_assert(c);
509 pa_assert(u);
510
511 pa_idxset_remove_by_data(u->subscribed, c, NULL);
512 return PA_HOOK_OK;
513 }
514
515 int pa__init(pa_module*m) {
516 pa_modargs *ma = NULL;
517 struct userdata *u;
518 char *fname;
519 pa_sink *sink;
520 pa_source *source;
521 uint32_t idx;
522
523 pa_assert(m);
524
525 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
526 pa_log("Failed to parse module arguments");
527 goto fail;
528 }
529
530 m->userdata = u = pa_xnew0(struct userdata, 1);
531 u->core = m->core;
532 u->module = m;
533 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
534
535 u->protocol = pa_native_protocol_get(m->core);
536 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
537
538 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);
539
540 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
541
542 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);
543 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);
544
545 if (!(fname = pa_state_path("device-manager", TRUE)))
546 goto fail;
547
548 if (!(u->database = pa_database_open(fname, TRUE))) {
549 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
550 pa_xfree(fname);
551 goto fail;
552 }
553
554 pa_log_info("Sucessfully opened database file '%s'.", fname);
555 pa_xfree(fname);
556
557 for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
558 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
559
560 for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
561 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
562
563 pa_modargs_free(ma);
564 return 0;
565
566 fail:
567 pa__done(m);
568
569 if (ma)
570 pa_modargs_free(ma);
571
572 return -1;
573 }
574
575 void pa__done(pa_module*m) {
576 struct userdata* u;
577
578 pa_assert(m);
579
580 if (!(u = m->userdata))
581 return;
582
583 if (u->subscription)
584 pa_subscription_free(u->subscription);
585
586 if (u->sink_new_hook_slot)
587 pa_hook_slot_free(u->sink_new_hook_slot);
588 if (u->source_new_hook_slot)
589 pa_hook_slot_free(u->source_new_hook_slot);
590
591 if (u->save_time_event)
592 u->core->mainloop->time_free(u->save_time_event);
593
594 if (u->database)
595 pa_database_close(u->database);
596
597 if (u->protocol) {
598 pa_native_protocol_remove_ext(u->protocol, m);
599 pa_native_protocol_unref(u->protocol);
600 }
601
602 if (u->subscribed)
603 pa_idxset_free(u->subscribed, NULL, NULL);
604
605 pa_xfree(u);
606 }