]> code.delx.au - pulseaudio/commitdiff
database: port restore modules to new database API
authorLennart Poettering <lennart@poettering.net>
Wed, 13 May 2009 23:24:26 +0000 (01:24 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 13 May 2009 23:24:26 +0000 (01:24 +0200)
configure.ac
src/Makefile.am
src/modules/module-card-restore.c
src/modules/module-device-restore.c
src/modules/module-stream-restore.c

index 6031ebd38ff8fc5109e538ab977ab6defe56cca1..16e6648c2262aaf4667e8b5e863d0c74c4318740 100644 (file)
@@ -392,9 +392,6 @@ AC_SEARCH_LIBS([connect], [socket])
 # build, disabling its ability to make dlls.
 AC_CHECK_FUNCS([getopt_long], [], [AC_CHECK_LIB([iberty], [getopt_long])])
 
-AC_CHECK_LIB(gdbm, gdbm_open)
-AC_CHECK_HEADERS(gdbm.h, [], [AC_MSG_ERROR([gdbm.h not found])])
-
 #### Check for functions ####
 
 # ISO
@@ -602,6 +599,65 @@ AC_SUBST(LIBSAMPLERATE_LIBS)
 AC_SUBST(HAVE_LIBSAMPLERATE)
 AM_CONDITIONAL([HAVE_LIBSAMPLERATE], [test "x$HAVE_LIBSAMPLERATE" = x1])
 
+#### Database support ####
+
+HAVE_TDB=0
+HAVE_GDBM=0
+
+AC_ARG_WITH(
+        [database],
+        AS_HELP_STRING([--with-database=auto|tdb|gdbm],[Choose database backend.]),[],[with_database=auto])
+
+if test "x${with_database}" == "xauto" -o "x${with_database}" == "xtdb" ; then
+    PKG_CHECK_MODULES(TDB, [ tdb ],
+        [
+            HAVE_TDB=1
+            with_database=tdb
+        ], [
+            if test "x${with_database}" == "xtdb" ; then
+                AC_MSG_ERROR([*** tdb not found])
+            fi
+        ])
+fi
+
+if test "x${with_database}" == "xauto" -o "x${with_database}" == "xgdbm" ; then
+   have_gdbm=yes
+
+   AC_CHECK_LIB(gdbm, gdbm_open, [], [have_gdbm=no])
+   AC_CHECK_HEADERS(gdbm.h, [], [have_gdbm=no])
+
+   if test "x${have_gdbm}" == "xyes" ; then
+       HAVE_GDBM=1
+       GDBM_CFLAGS=
+       GDBM_LIBS=-lgdbm
+       with_database=gdbm
+   elif test "x${with_database}" == "xgdbm"; then
+       AC_MSG_ERROR([*** gdbm not found])
+   fi
+fi
+
+if test "x${HAVE_TDB}" != x1 -a "x${HAVE_GDBM}" != x1; then
+   AC_MSG_ERROR([*** missing database backend])
+fi
+
+if test "x${HAVE_TDB}" = x1 ; then
+   AC_DEFINE([HAVE_TDB], 1, [Have tdb?])
+fi
+
+if test "x${HAVE_GDBM}" = x1 ; then
+   AC_DEFINE([HAVE_GDBM], 1, [Have gdbm?])
+fi
+
+AC_SUBST(TDB_CFLAGS)
+AC_SUBST(TDB_LIBS)
+AC_SUBST(HAVE_TDB)
+AM_CONDITIONAL([HAVE_TDB], [test "x$HAVE_TDB" = x1])
+
+AC_SUBST(GDBM_CFLAGS)
+AC_SUBST(GDBM_LIBS)
+AC_SUBST(HAVE_GDBM)
+AM_CONDITIONAL([HAVE_GDBM], [test "x$HAVE_GDBM" = x1])
+
 #### OSS support (optional) ####
 
 AC_ARG_ENABLE([oss],
@@ -1410,6 +1466,16 @@ if test "x${HAVE_POLKIT}" = "x1" ; then
    ENABLE_POLKIT=yes
 fi
 
+ENABLE_GDBM=no
+if test "x${HAVE_GDBM}" = "x1" ; then
+   ENABLE_GDBM=yes
+fi
+
+ENABLE_TDB=no
+if test "x${HAVE_TDB}" = "x1" ; then
+   ENABLE_TDB=yes
+fi
+
 ENABLE_OPENSSL=no
 if test "x${HAVE_OPENSSL}" = "x1" ; then
    ENABLE_OPENSSL=yes
@@ -1456,6 +1522,8 @@ echo "
     Enable PolicyKit:              ${ENABLE_POLKIT}
     Enable IPv6:                   ${ENABLE_IPV6}
     Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
+    Enable tdb:                    ${ENABLE_TDB}
+    Enable gdbm:                   ${ENABLE_GDBM}
 
     System User:                   ${PA_SYSTEM_USER}
     System Group:                  ${PA_SYSTEM_GROUP}
index b41ae3985b4bf6adde23da6edbf47592b996cdc6..49a0c69bea6c44b092ef8e988dc6f801c97e58a8 100644 (file)
@@ -816,7 +816,8 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \
                pulsecore/source.c pulsecore/source.h \
                pulsecore/start-child.c pulsecore/start-child.h \
                pulsecore/thread-mq.c pulsecore/thread-mq.h \
-               pulsecore/time-smoother.c pulsecore/time-smoother.h
+               pulsecore/time-smoother.c pulsecore/time-smoother.h \
+               pulsecore/database.h
 
 libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
 libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS = -avoid-version
@@ -828,13 +829,25 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(X11_CFLAGS)
 libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS += $(X11_LIBS)
 endif
 
-
 if HAVE_DBUS
 libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dbus-shared.c pulsecore/dbus-shared.h
 libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS)
 libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS)
 endif
 
+if HAVE_GDBM
+libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/database-gdbm.c
+libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(GDBM_CFLAGS)
+libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(GDBM_LIBS)
+endif
+
+if HAVE_TDB
+libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/database-tdb.c
+libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(TDB_CFLAGS)
+libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(TDB_LIBS)
+endif
+
+
 # We split the foreign code off to not be annoyed by warnings we don't care about
 noinst_LTLIBRARIES = libpulsecore-foreign.la
 
@@ -1441,19 +1454,19 @@ module_cork_music_on_phone_la_CFLAGS = $(AM_CFLAGS)
 # Device volume/muted restore module
 module_device_restore_la_SOURCES = modules/module-device-restore.c
 module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
 module_device_restore_la_CFLAGS = $(AM_CFLAGS)
 
 # Stream volume/muted/device restore module
 module_stream_restore_la_SOURCES = modules/module-stream-restore.c
 module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
 module_stream_restore_la_CFLAGS = $(AM_CFLAGS)
 
 # Card profile restore module
 module_card_restore_la_SOURCES = modules/module-card-restore.c
 module_card_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_card_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_card_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
 module_card_restore_la_CFLAGS = $(AM_CFLAGS)
 
 # Default sink/source restore module
index 17f1f8c395a06497882d15e0341e887a959acca5..85478d1264759b4d5b9077e8e8357eb30cb3f438 100644 (file)
@@ -30,7 +30,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
-#include <gdbm.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/volume.h>
@@ -45,6 +44,7 @@
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/card.h>
 #include <pulsecore/namereg.h>
+#include <pulsecore/database.h>
 
 #include "module-card-restore-symdef.h"
 
@@ -65,7 +65,7 @@ struct userdata {
     pa_subscription *subscription;
     pa_hook_slot *card_new_hook_slot;
     pa_time_event *save_time_event;
-    GDBM_FILE gdbm_file;
+    pa_database *database;
 };
 
 #define ENTRY_VERSION 1
@@ -87,31 +87,31 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
     u->core->mainloop->time_free(u->save_time_event);
     u->save_time_event = NULL;
 
-    gdbm_sync(u->gdbm_file);
+    pa_database_sync(u->database);
     pa_log_info("Synced.");
 }
 
 static struct entry* read_entry(struct userdata *u, const char *name) {
-    datum key, data;
+    pa_datum key, data;
     struct entry *e;
 
     pa_assert(u);
     pa_assert(name);
 
-    key.dptr = (char*) name;
-    key.dsize = (int) strlen(name);
+    key.data = (char*) name;
+    key.size = strlen(name);
 
-    data = gdbm_fetch(u->gdbm_file, key);
+    pa_zero(data);
 
-    if (!data.dptr)
+    if (!pa_database_get(u->database, &key, &data))
         goto fail;
 
-    if (data.dsize != sizeof(struct entry)) {
-        pa_log_debug("Database contains entry for card %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+    if (data.size != sizeof(struct entry)) {
+        pa_log_debug("Database contains entry for card %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
         goto fail;
     }
 
-    e = (struct entry*) data.dptr;
+    e = (struct entry*) data.data;
 
     if (e->version != ENTRY_VERSION) {
         pa_log_debug("Version of database entry for card %s doesn't match our version. Probably due to upgrade, ignoring.", name);
@@ -127,7 +127,7 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
 
 fail:
 
-    pa_xfree(data.dptr);
+    pa_datum_free(&data);
     return NULL;
 }
 
@@ -145,7 +145,7 @@ static void trigger_save(struct userdata *u) {
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
     struct entry entry, *old;
-    datum key, data;
+    pa_datum key, data;
     pa_card *card;
 
     pa_assert(c);
@@ -155,7 +155,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE))
         return;
 
-    memset(&entry, 0, sizeof(entry));
+    pa_zero(entry);
     entry.version = ENTRY_VERSION;
 
     if (!(card = pa_idxset_get_by_index(c->cards, idx)))
@@ -176,15 +176,15 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         pa_xfree(old);
     }
 
-    key.dptr = card->name;
-    key.dsize = (int) strlen(card->name);
+    key.data = card->name;
+    key.size = strlen(card->name);
 
-    data.dptr = (void*) &entry;
-    data.dsize = sizeof(entry);
+    data.data = &entry;
+    data.size = sizeof(entry);
 
     pa_log_info("Storing profile for card %s.", card->name);
 
-    gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
+    pa_database_set(u->database, &key, &data, TRUE);
 
     trigger_save(u);
 }
@@ -211,10 +211,9 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
-    char *fname, *fn;
+    char *fname;
     pa_card *card;
     uint32_t idx;
-    int gdbm_cache_size;
 
     pa_assert(m);
 
@@ -227,33 +226,21 @@ int pa__init(pa_module*m) {
     u->core = m->core;
     u->module = m;
     u->save_time_event = NULL;
-    u->gdbm_file = NULL;
+    u->database = NULL;
 
     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_CARD, subscribe_callback, u);
 
     u->card_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) card_new_hook_callback, u);
 
-    /* We include the host identifier in the file name because gdbm
-     * files are CPU dependant, and we don't want things to go wrong
-     * if we are on a multiarch system. */
-
-    fn = pa_sprintf_malloc("card-database."CANONICAL_HOST".gdbm");
-    fname = pa_state_path(fn, TRUE);
-    pa_xfree(fn);
-
-    if (!fname)
+    if (!(fname = pa_state_path("card-database", TRUE)))
         goto fail;
 
-    if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT|GDBM_NOLOCK, 0600, NULL))) {
-        pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
+    if (!(u->database = pa_database_open(fname, TRUE))) {
+        pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
         pa_xfree(fname);
         goto fail;
     }
 
-    /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
-    gdbm_cache_size = 10;
-    gdbm_setopt(u->gdbm_file, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
-
     pa_log_info("Sucessfully opened database file '%s'.", fname);
     pa_xfree(fname);
 
@@ -289,8 +276,8 @@ void pa__done(pa_module*m) {
     if (u->save_time_event)
         u->core->mainloop->time_free(u->save_time_event);
 
-    if (u->gdbm_file)
-        gdbm_close(u->gdbm_file);
+    if (u->database)
+        pa_database_close(u->database);
 
     pa_xfree(u);
 }
index 7d87ca0ff0136911de76c29127b077c2757f2f5d..ae21acd5fd3037fbce5a2f0f538d9cedbf676788 100644 (file)
@@ -30,7 +30,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
-#include <gdbm.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/volume.h>
@@ -46,6 +45,7 @@
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/namereg.h>
+#include <pulsecore/database.h>
 
 #include "module-device-restore-symdef.h"
 
@@ -73,7 +73,7 @@ struct userdata {
         *sink_fixate_hook_slot,
         *source_fixate_hook_slot;
     pa_time_event *save_time_event;
-    GDBM_FILE gdbm_file;
+    pa_database *database;
 
     pa_bool_t restore_volume:1;
     pa_bool_t restore_muted:1;
@@ -100,31 +100,31 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
     u->core->mainloop->time_free(u->save_time_event);
     u->save_time_event = NULL;
 
-    gdbm_sync(u->gdbm_file);
+    pa_database_sync(u->database);
     pa_log_info("Synced.");
 }
 
 static struct entry* read_entry(struct userdata *u, const char *name) {
-    datum key, data;
+    pa_datum key, data;
     struct entry *e;
 
     pa_assert(u);
     pa_assert(name);
 
-    key.dptr = (char*) name;
-    key.dsize = (int) strlen(name);
+    key.data = (char*) name;
+    key.size = strlen(name);
 
-    data = gdbm_fetch(u->gdbm_file, key);
+    pa_zero(data);
 
-    if (!data.dptr)
+    if (!pa_database_get(u->database, &key, &data))
         goto fail;
 
-    if (data.dsize != sizeof(struct entry)) {
-        pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+    if (data.size != sizeof(struct entry)) {
+        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));
         goto fail;
     }
 
-    e = (struct entry*) data.dptr;
+    e = (struct entry*) data.data;
 
     if (e->version != ENTRY_VERSION) {
         pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
@@ -150,7 +150,7 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
 
 fail:
 
-    pa_xfree(data.dptr);
+    pa_datum_free(&data);
     return NULL;
 }
 
@@ -169,7 +169,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
     struct userdata *u = userdata;
     struct entry entry, *old;
     char *name;
-    datum key, data;
+    pa_datum key, data;
 
     pa_assert(c);
     pa_assert(u);
@@ -180,7 +180,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
         return;
 
-    memset(&entry, 0, sizeof(entry));
+    pa_zero(entry);
     entry.version = ENTRY_VERSION;
 
     if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
@@ -221,15 +221,15 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         pa_xfree(old);
     }
 
-    key.dptr = name;
-    key.dsize = (int) strlen(name);
+    key.data = name;
+    key.size = strlen(name);
 
-    data.dptr = (void*) &entry;
-    data.dsize = sizeof(entry);
+    data.data = &entry;
+    data.size = sizeof(entry);
 
     pa_log_info("Storing volume/mute for device %s.", name);
 
-    gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
+    pa_database_set(u->database, &key, &data, TRUE);
 
     pa_xfree(name);
 
@@ -312,12 +312,11 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
-    char *fname, *fn;
+    char *fname;
     pa_sink *sink;
     pa_source *source;
     uint32_t idx;
     pa_bool_t restore_volume = TRUE, restore_muted = TRUE;
-    int gdbm_cache_size;
 
     pa_assert(m);
 
@@ -341,7 +340,7 @@ int pa__init(pa_module*m) {
     u->save_time_event = NULL;
     u->restore_volume = restore_volume;
     u->restore_muted = restore_muted;
-    u->gdbm_file = NULL;
+    u->database = NULL;
 
     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
 
@@ -350,27 +349,15 @@ int pa__init(pa_module*m) {
         u->source_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_fixate_hook_callback, u);
     }
 
-    /* We include the host identifier in the file name because gdbm
-     * files are CPU dependant, and we don't want things to go wrong
-     * if we are on a multiarch system. */
-
-    fn = pa_sprintf_malloc("device-volumes."CANONICAL_HOST".gdbm");
-    fname = pa_state_path(fn, TRUE);
-    pa_xfree(fn);
-
-    if (!fname)
+    if (!(fname = pa_state_path("device-volumes", TRUE)))
         goto fail;
 
-    if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT|GDBM_NOLOCK, 0600, NULL))) {
-        pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
+    if (!(u->database = pa_database_open(fname, TRUE))) {
+        pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
         pa_xfree(fname);
         goto fail;
     }
 
-    /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
-    gdbm_cache_size = 10;
-    gdbm_setopt(u->gdbm_file, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
-
     pa_log_info("Sucessfully opened database file '%s'.", fname);
     pa_xfree(fname);
 
@@ -411,8 +398,8 @@ void pa__done(pa_module*m) {
     if (u->save_time_event)
         u->core->mainloop->time_free(u->save_time_event);
 
-    if (u->gdbm_file)
-        gdbm_close(u->gdbm_file);
+    if (u->database)
+        pa_database_close(u->database);
 
     pa_xfree(u);
 }
index 70cd89a70a9d7b53154bbffcfd5be0999a9a203e..2de98f4e88c5d57ae53200ca5b18e0fe96dfa44b 100644 (file)
@@ -30,7 +30,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
-#include <gdbm.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/volume.h>
@@ -49,6 +48,7 @@
 #include <pulsecore/protocol-native.h>
 #include <pulsecore/pstream.h>
 #include <pulsecore/pstream-util.h>
+#include <pulsecore/database.h>
 
 #include "module-stream-restore-symdef.h"
 
@@ -81,7 +81,7 @@ struct userdata {
         *source_output_new_hook_slot,
         *connection_unlink_hook_slot;
     pa_time_event *save_time_event;
-    GDBM_FILE gdbm_file;
+    pa_database* database;
 
     pa_bool_t restore_device:1;
     pa_bool_t restore_volume:1;
@@ -123,7 +123,7 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
     u->core->mainloop->time_free(u->save_time_event);
     u->save_time_event = NULL;
 
-    gdbm_sync(u->gdbm_file);
+    pa_database_sync(u->database);
     pa_log_info("Synced.");
 }
 
@@ -153,28 +153,28 @@ static char *get_name(pa_proplist *p, const char *prefix) {
 }
 
 static struct entry* read_entry(struct userdata *u, const char *name) {
-    datum key, data;
+    pa_datum key, data;
     struct entry *e;
 
     pa_assert(u);
     pa_assert(name);
 
-    key.dptr = (char*) name;
-    key.dsize = (int) strlen(name);
+    key.data = (char*) name;
+    key.size = strlen(name);
 
-    data = gdbm_fetch(u->gdbm_file, key);
+    pa_zero(data);
 
-    if (!data.dptr)
+    if (!pa_database_get(u->database, &key, &data))
         goto fail;
 
-    if (data.dsize != sizeof(struct entry)) {
+    if (data.size != sizeof(struct entry)) {
         /* This is probably just a database upgrade, hence let's not
          * consider this more than a debug message */
-        pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+        pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
         goto fail;
     }
 
-    e = (struct entry*) data.dptr;
+    e = (struct entry*) data.data;
 
     if (e->version != ENTRY_VERSION) {
         pa_log_debug("Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring.", name);
@@ -205,7 +205,7 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
 
 fail:
 
-    pa_xfree(data.dptr);
+    pa_datum_free(&data);
     return NULL;
 }
 
@@ -261,7 +261,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
     struct userdata *u = userdata;
     struct entry entry, *old;
     char *name;
-    datum key, data;
+    pa_datum key, data;
 
     pa_assert(c);
     pa_assert(u);
@@ -272,7 +272,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
         return;
 
-    memset(&entry, 0, sizeof(entry));
+    pa_zero(entry);
     entry.version = ENTRY_VERSION;
 
     if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
@@ -334,15 +334,15 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         pa_xfree(old);
     }
 
-    key.dptr = name;
-    key.dsize = (int) strlen(name);
+    key.data = name;
+    key.size = strlen(name);
 
-    data.dptr = (void*) &entry;
-    data.dsize = sizeof(entry);
+    data.data = &entry;
+    data.size = sizeof(entry);
 
     pa_log_info("Storing volume/mute/device for stream %s.", name);
 
-    gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
+    pa_database_set(u->database, &key, &data, TRUE);
 
     pa_xfree(name);
 
@@ -471,25 +471,6 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
 
 #define EXT_VERSION 1
 
-static void clear_db(struct userdata *u) {
-    datum key;
-
-    pa_assert(u);
-
-    key = gdbm_firstkey(u->gdbm_file);
-    while (key.dptr) {
-        datum next_key;
-        next_key = gdbm_nextkey(u->gdbm_file, key);
-
-        gdbm_delete(u->gdbm_file, key);
-        pa_xfree(key.dptr);
-
-        key = next_key;
-    }
-
-    gdbm_reorganize(u->gdbm_file);
-}
-
 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
     pa_sink_input *si;
     pa_source_output *so;
@@ -559,18 +540,20 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
 
 #if 0
 static void dump_database(struct userdata *u) {
-    datum key;
+    pa_datum key;
+    pa_bool_t done;
 
-    key = gdbm_firstkey(u->gdbm_file);
-    while (key.dptr) {
-        datum next_key;
+    done = !pa_database_first(u->database, &key, NULL);
+
+    while (!done) {
+        pa_datum next_key;
         struct entry *e;
         char *name;
 
-        next_key = gdbm_nextkey(u->gdbm_file, key);
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
 
-        name = pa_xstrndup(key.dptr, key.dsize);
-        pa_xfree(key.dptr);
+        name = pa_xstrndup(key.data, key.size);
+        pa_datum_free(&key);
 
         if ((e = read_entry(u, name))) {
             char t[256];
@@ -618,21 +601,23 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         }
 
         case SUBCOMMAND_READ: {
-            datum key;
+            pa_datum key;
+            pa_bool_t done;
 
             if (!pa_tagstruct_eof(t))
                 goto fail;
 
-            key = gdbm_firstkey(u->gdbm_file);
-            while (key.dptr) {
-                datum next_key;
+            done = !pa_database_first(u->database, &key, NULL);
+
+            while (!done) {
+                pa_datum next_key;
                 struct entry *e;
                 char *name;
 
-                next_key = gdbm_nextkey(u->gdbm_file, key);
+                done = !pa_database_next(u->database, &key, &next_key, NULL);
 
-                name = pa_xstrndup(key.dptr, (size_t) key.dsize);
-                pa_xfree(key.dptr);
+                name = pa_xstrndup(key.data, key.size);
+                pa_datum_free(&key);
 
                 if ((e = read_entry(u, name))) {
                     pa_cvolume r;
@@ -669,16 +654,15 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                 goto fail;
 
             if (mode == PA_UPDATE_SET)
-                clear_db(u);
+                pa_database_clear(u->database);
 
             while (!pa_tagstruct_eof(t)) {
                 const char *name, *device;
                 pa_bool_t muted;
                 struct entry entry;
-                datum key, data;
-                int k;
+                pa_datum key, data;
 
-                memset(&entry, 0, sizeof(entry));
+                pa_zero(entry);
                 entry.version = ENTRY_VERSION;
 
                 if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -708,13 +692,13 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                     !pa_namereg_is_valid_name(entry.device))
                     goto fail;
 
-                key.dptr = (void*) name;
-                key.dsize = (int) strlen(name);
+                key.data = (char*) name;
+                key.size = strlen(name);
 
-                data.dptr = (void*) &entry;
-                data.dsize = sizeof(entry);
+                data.data = &entry;
+                data.size = sizeof(entry);
 
-                if ((k = gdbm_store(u->gdbm_file, key, data, mode == PA_UPDATE_REPLACE ? GDBM_REPLACE : GDBM_INSERT)) == 0)
+                if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
                     if (apply_immediately)
                         apply_entry(u, name, &entry);
             }
@@ -728,15 +712,15 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
 
             while (!pa_tagstruct_eof(t)) {
                 const char *name;
-                datum key;
+                pa_datum key;
 
                 if (pa_tagstruct_gets(t, &name) < 0)
                     goto fail;
 
-                key.dptr = (void*) name;
-                key.dsize = (int) strlen(name);
+                key.data = (char*) name;
+                key.size = strlen(name);
 
-                gdbm_delete(u->gdbm_file, key);
+                pa_database_unset(u->database, &key);
             }
 
             trigger_save(u);
@@ -786,12 +770,11 @@ static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_nati
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
-    char *fname, *fn;
+    char *fname;
     pa_sink_input *si;
     pa_source_output *so;
     uint32_t idx;
     pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE;
-    int gdbm_cache_size;
 
     pa_assert(m);
 
@@ -817,7 +800,7 @@ int pa__init(pa_module*m) {
     u->restore_device = restore_device;
     u->restore_volume = restore_volume;
     u->restore_muted = restore_muted;
-    u->gdbm_file = NULL;
+    u->database = NULL;
     u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
     u->protocol = pa_native_protocol_get(m->core);
@@ -835,27 +818,18 @@ int pa__init(pa_module*m) {
     if (restore_volume || restore_muted)
         u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
 
-    /* We include the host identifier in the file name because gdbm
-     * files are CPU dependant, and we don't want things to go wrong
-     * if we are on a multiarch system. */
 
-    fn = pa_sprintf_malloc("stream-volumes."CANONICAL_HOST".gdbm");
-    fname = pa_state_path(fn, TRUE);
-    pa_xfree(fn);
+    fname = pa_state_path("stream-volumes", TRUE);
 
     if (!fname)
         goto fail;
 
-    if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT|GDBM_NOLOCK, 0600, NULL))) {
-        pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
+    if (!(u->database = pa_database_open(fname, TRUE))) {
+        pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
         pa_xfree(fname);
         goto fail;
     }
 
-    /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
-    gdbm_cache_size = 10;
-    gdbm_setopt(u->gdbm_file, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
-
     pa_log_info("Sucessfully opened database file '%s'.", fname);
     pa_xfree(fname);
 
@@ -901,8 +875,8 @@ void pa__done(pa_module*m) {
     if (u->save_time_event)
         u->core->mainloop->time_free(u->save_time_event);
 
-    if (u->gdbm_file)
-        gdbm_close(u->gdbm_file);
+    if (u->database)
+        pa_database_close(u->database);
 
     if (u->protocol) {
         pa_native_protocol_remove_ext(u->protocol, m);