device-manager: Fix tagstruct description extraction (copy+paste blunder)
[profile/ivi/pulseaudio.git] / 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(t, &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 }