database: Convert our use of database files to save in tagstruct format.

This has the advantage of allowing versioned updates in the future,
thus allowing us to be more user friendly going forward (as opposed
to just ignoring entries from old versions).

The primary motivation for this, however, is to allow variable length
storage in each entry which will be needed for upcoming work.

At present this commit will ignore any legacy entries but support
for reading and subsequently converting legacy entries will be added
shortly.
This commit is contained in:
Colin Guthrie 2011-06-07 23:21:04 +01:00
parent ec4fa4c668
commit 695d536380
4 changed files with 567 additions and 370 deletions

View file

@ -46,6 +46,7 @@
#include <pulsecore/card.h>
#include <pulsecore/namereg.h>
#include <pulsecore/database.h>
#include <pulsecore/tagstruct.h>
#include "module-card-restore-symdef.h"
@ -69,12 +70,12 @@ struct userdata {
pa_database *database;
};
#define ENTRY_VERSION 1
#define ENTRY_VERSION 2
struct entry {
uint8_t version;
char profile[PA_NAME_MAX];
} PA_GCC_PACKED ;
char *profile;
};
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
struct userdata *u = userdata;
@ -91,9 +92,24 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
pa_log_info("Synced.");
}
static struct entry* read_entry(struct userdata *u, const char *name) {
static struct entry* entry_new(void) {
struct entry *r = pa_xnew0(struct entry, 1);
r->version = ENTRY_VERSION;
return r;
}
static void entry_free(struct entry* e) {
pa_assert(e);
pa_xfree(e->profile);
pa_xfree(e);
}
static struct entry* entry_read(struct userdata *u, const char *name) {
pa_datum key, data;
struct entry *e;
struct entry *e = NULL;
pa_tagstruct *t = NULL;
const char* profile;
pa_assert(u);
pa_assert(name);
@ -106,31 +122,63 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
if (!pa_database_get(u->database, &key, &data))
goto fail;
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));
t = pa_tagstruct_new(data.data, data.size);
e = entry_new();
if (pa_tagstruct_getu8(t, &e->version) < 0 ||
e->version > ENTRY_VERSION ||
pa_tagstruct_gets(t, &profile) < 0) {
goto fail;
}
e = (struct entry*) data.data;
e->profile = pa_xstrdup(profile);
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);
if (!pa_tagstruct_eof(t))
goto fail;
}
if (!memchr(e->profile, 0, sizeof(e->profile))) {
pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name);
goto fail;
}
pa_tagstruct_free(t);
pa_datum_free(&data);
return e;
fail:
pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
if (e)
entry_free(e);
if (t)
pa_tagstruct_free(t);
pa_datum_free(&data);
return NULL;
}
static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
pa_tagstruct *t;
pa_datum key, data;
pa_bool_t r;
pa_assert(u);
pa_assert(name);
pa_assert(e);
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu8(t, e->version);
pa_tagstruct_puts(t, e->profile);
key.data = (char *) name;
key.size = strlen(name);
data.data = (void*)pa_tagstruct_data(t, &data.size);
r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
pa_tagstruct_free(t);
return r;
}
static void trigger_save(struct userdata *u) {
if (u->save_time_event)
return;
@ -140,8 +188,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;
pa_datum key, data;
struct entry *entry, *old;
pa_card *card;
pa_assert(c);
@ -151,8 +198,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;
pa_zero(entry);
entry.version = ENTRY_VERSION;
entry = entry_new();
if (!(card = pa_idxset_get_by_index(c->cards, idx)))
return;
@ -160,29 +206,25 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (!card->save_profile)
return;
pa_strlcpy(entry.profile, card->active_profile ? card->active_profile->name : "", sizeof(entry.profile));
entry->profile = pa_xstrdup(card->active_profile ? card->active_profile->name : "");
if ((old = read_entry(u, card->name))) {
if ((old = entry_read(u, card->name))) {
if (strncmp(old->profile, entry.profile, sizeof(entry.profile)) == 0) {
pa_xfree(old);
if (pa_streq(old->profile, entry->profile)) {
entry_free(old);
entry_free(entry);
return;
}
pa_xfree(old);
entry_free(old);
}
key.data = card->name;
key.size = strlen(card->name);
data.data = &entry;
data.size = sizeof(entry);
pa_log_info("Storing profile for card %s.", card->name);
pa_database_set(u->database, &key, &data, TRUE);
if (entry_write(u, card->name, entry))
trigger_save(u);
trigger_save(u);
entry_free(entry);
}
static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
@ -190,7 +232,7 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
pa_assert(new_data);
if ((e = read_entry(u, new_data->name)) && e->profile[0]) {
if ((e = entry_read(u, new_data->name)) && e->profile[0]) {
if (!new_data->active_profile) {
pa_log_info("Restoring profile for card %s.", new_data->name);
@ -199,7 +241,7 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
} else
pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
pa_xfree(e);
entry_free(e);
}
return PA_HOOK_OK;